mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-25 13:21:36 +00:00
Add support for PSK for DTLS via gnutls and openssl. User needs to supply the keys.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6161 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
0085e5e8d8
commit
2e627df7b5
9 changed files with 439 additions and 87 deletions
|
@ -1164,6 +1164,7 @@ void CL_CheckForResend (void)
|
|||
switch(NET_SendPacket (cls.sockets, 0, NULL, &connectinfo.adr[0]))
|
||||
{
|
||||
case NETERR_CLOGGED: //temporary failure
|
||||
connectinfo.clogged = true;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
|
@ -1195,7 +1196,7 @@ void CL_CheckForResend (void)
|
|||
|
||||
Cvar_ForceSet(&cl_servername, cls.servername);
|
||||
|
||||
if (!connectinfo.numadr)
|
||||
if (!connectinfo.numadr || !cls.sockets)
|
||||
return; //nothing to do yet...
|
||||
if (!connectinfo.clogged)
|
||||
connectinfo.time = realtime+t2-t1; // for retransmit requests
|
||||
|
@ -1220,7 +1221,7 @@ void CL_CheckForResend (void)
|
|||
#endif
|
||||
|
||||
if (connectinfo.istransfer || connectinfo.numadr>1)
|
||||
Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), to));
|
||||
Con_TPrintf ("Connecting to %s" S_COLOR_GRAY "(%s)" S_COLOR_WHITE "...\n", cls.servername, NET_AdrToString(data, sizeof(data), to));
|
||||
else
|
||||
Con_TPrintf ("Connecting to %s...\n", cls.servername);
|
||||
}
|
||||
|
@ -1365,7 +1366,7 @@ void CL_BeginServerReconnect(void)
|
|||
connectinfo.istransfer = false;
|
||||
connectinfo.time = 0;
|
||||
connectinfo.tries = 0; //re-ensure routes.
|
||||
connectinfo.nextadr = 0; //should at least be consistent, other than packetloss. yay. :\
|
||||
connectinfo.nextadr = 0; //should at least be consistent, other than packetloss. yay.
|
||||
|
||||
NET_InitClient(false);
|
||||
}
|
||||
|
@ -3689,7 +3690,7 @@ void CL_ConnectionlessPacket (void)
|
|||
if (!CL_IsPendingServerAddress(&net_from))
|
||||
return;
|
||||
|
||||
if (NET_DTLS_Create(cls.sockets, &net_from))
|
||||
if (NET_DTLS_Create(cls.sockets, &net_from, cls.servername))
|
||||
{
|
||||
connectinfo.dtlsupgrade = DTLS_ACTIVE;
|
||||
connectinfo.numadr = 1; //fixate on this resolved address.
|
||||
|
|
|
@ -29,7 +29,7 @@ cvar_t log_dosformat = CVARF("log_dosformat", "0", CVAR_NOTFROMSERVER);
|
|||
qboolean log_newline[LOG_TYPES];
|
||||
|
||||
#ifdef IPLOG
|
||||
cvar_t iplog_autodump = CVARFD("ipautodump", "1", CVAR_NOTFROMSERVER, "Enables dumping the 'iplog.txt' file, which contains a log of usernames seen for a given IP, which is useful for detecting fake-nicks.");
|
||||
cvar_t iplog_autodump = CVARFD("ipautodump", "0", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "Enables dumping the 'iplog.txt' file, which contains a log of usernames seen for a given IP, which is useful for detecting fake-nicks.");
|
||||
#endif
|
||||
|
||||
static char log_dir[MAX_OSPATH];
|
||||
|
@ -637,6 +637,8 @@ struct certlog_s
|
|||
{
|
||||
link_t l;
|
||||
char *hostname;
|
||||
qboolean trusted; //when true, the user has given explicit trust
|
||||
//when false we buldozed straight through and will only complain when it changes (legacy mode).
|
||||
size_t certsize;
|
||||
qbyte cert[1];
|
||||
};
|
||||
|
@ -654,7 +656,7 @@ static struct certlog_s *CertLog_Find(const char *hostname)
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
static void CertLog_Update(const char *hostname, const void *cert, size_t certsize)
|
||||
static void CertLog_Update(const char *hostname, const void *cert, size_t certsize, qboolean trusted)
|
||||
{
|
||||
struct certlog_s *l = CertLog_Find(hostname);
|
||||
if (l)
|
||||
|
@ -675,7 +677,7 @@ static void CertLog_Write(void)
|
|||
vfsfile_t *f = FS_OpenVFS(CERTLOG_FILENAME, "wb", FS_ROOT);
|
||||
if (f)
|
||||
{
|
||||
VFS_PRINTF(f, "version 1.0\n");
|
||||
VFS_PRINTF(f, "version 1.1\n");
|
||||
|
||||
for (l=(struct certlog_s*)certlog.next ; l != (struct certlog_s*)&certlog ; l = (struct certlog_s*)l->l.next)
|
||||
{
|
||||
|
@ -690,7 +692,7 @@ static void CertLog_Write(void)
|
|||
certhex[i*2] = 0;
|
||||
VFS_PRINTF(f, "%s \"", l->hostname);
|
||||
VFS_PUTS(f, certhex);
|
||||
VFS_PRINTF(f, "\"\n");
|
||||
VFS_PRINTF(f, "\" %i\n", l->trusted?true:false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -720,6 +722,7 @@ static void CertLog_Import(const char *filename)
|
|||
char addressstring[512];
|
||||
char certhex[32768];
|
||||
char certdata[16384];
|
||||
char trusted[16];
|
||||
char line[65536], *l;
|
||||
size_t i, certsize;
|
||||
vfsfile_t *f;
|
||||
|
@ -738,6 +741,7 @@ static void CertLog_Import(const char *filename)
|
|||
l = line;
|
||||
l = COM_ParseOut(l, addressstring, sizeof(addressstring));
|
||||
l = COM_ParseOut(l, certhex, sizeof(certhex));
|
||||
l = COM_ParseOut(l, trusted, sizeof(trusted));
|
||||
|
||||
certsize = 0;
|
||||
for (i = 0; certsize < sizeof(certdata); i++)
|
||||
|
@ -746,7 +750,7 @@ static void CertLog_Import(const char *filename)
|
|||
break;
|
||||
certdata[certsize++] = (hexdecode(certhex[(i<<1)+0])<<4)|hexdecode(certhex[(i<<1)+1]);
|
||||
}
|
||||
CertLog_Update(addressstring, certdata, certsize);
|
||||
CertLog_Update(addressstring, certdata, certsize, atoi(trusted));
|
||||
}
|
||||
}
|
||||
static void CertLog_UntrustAll_f(void)
|
||||
|
@ -775,7 +779,7 @@ static void CertLog_Add_Prompted(void *vctx, promptbutton_t button)
|
|||
struct certprompt_s *ctx = vctx;
|
||||
if (button == PROMPT_YES) //button_yes / button_left
|
||||
{
|
||||
CertLog_Update(ctx->hostname, ctx->cert, ctx->certsize);
|
||||
CertLog_Update(ctx->hostname, ctx->cert, ctx->certsize, true);
|
||||
CertLog_Write();
|
||||
|
||||
CL_BeginServerReconnect();
|
||||
|
@ -786,8 +790,10 @@ static void CertLog_Add_Prompted(void *vctx, promptbutton_t button)
|
|||
certlog_curprompt = NULL;
|
||||
}
|
||||
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems)
|
||||
{
|
||||
{ //this is specifically for dtls certs.
|
||||
extern cvar_t net_enable_dtls;
|
||||
struct certlog_s *l;
|
||||
qboolean trusted = (net_enable_dtls.ival >= 2);
|
||||
|
||||
if (certlog_curprompt)
|
||||
return false;
|
||||
|
@ -797,7 +803,14 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
|
|||
CertLog_Import(NULL);
|
||||
l = CertLog_Find(hostname);
|
||||
|
||||
if (!l || l->certsize != certsize || memcmp(l->cert, cert, certsize))
|
||||
if (!l && !trusted)
|
||||
{ //cert is new, but we don't care about full trust. don't bother to prompt when the user doesn't much care.
|
||||
//(but do pin so we at least know when its MITMed after the fact)
|
||||
Con_Printf(CON_WARNING"Auto-Pinning certificate for %s."CON_DEFAULT" ^[/seta %s 2^]+ for actual security.\n", hostname, net_enable_dtls.name);
|
||||
CertLog_Update(hostname, cert, certsize, false);
|
||||
CertLog_Write();
|
||||
}
|
||||
else if (!l || l->certsize != certsize || memcmp(l->cert, cert, certsize) || (trusted && !l->trusted))
|
||||
{ //new or different
|
||||
if (qrenderer)
|
||||
{
|
||||
|
@ -839,6 +852,8 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
|
|||
}
|
||||
return false; //can't connect yet...
|
||||
}
|
||||
else if (!l->trusted)
|
||||
Con_Printf(CON_WARNING"Server certificate for %s was previously auto-pinned."CON_DEFAULT" ^[/seta %s 2^]+ for actual security.\n", hostname, net_enable_dtls.name);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -187,10 +187,11 @@ enum certprops_e
|
|||
size_t NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to);
|
||||
qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to, const char *hostname);
|
||||
qboolean NET_DTLS_Decode(struct ftenet_connections_s *col);
|
||||
qboolean NET_DTLS_Disconnect(struct ftenet_connections_s *col, netadr_t *to);
|
||||
void NET_DTLS_Timeouts(struct ftenet_connections_s *col);
|
||||
extern cvar_t dtls_psk_hint, dtls_psk_user, dtls_psk_key;
|
||||
#endif
|
||||
extern cvar_t timeout;
|
||||
extern cvar_t tls_ignorecertificateerrors; //evil evil evil.
|
||||
|
|
|
@ -154,6 +154,8 @@ static int (VARGS *qgnutls_credentials_set)(gnutls_session_t, gnutls_credentials
|
|||
static int (VARGS *qgnutls_init)(gnutls_session_t * session, gnutls_connection_end_t con_end);
|
||||
static void (VARGS *qgnutls_deinit)(gnutls_session_t session);
|
||||
static int (VARGS *qgnutls_set_default_priority)(gnutls_session_t session);
|
||||
static int (VARGS *qgnutls_set_default_priority_append)(gnutls_session_t session, const char *add_prio, const char **err_pos, unsigned flags);
|
||||
|
||||
static int (VARGS *qgnutls_certificate_allocate_credentials)(gnutls_certificate_credentials_t *sc);
|
||||
static int (VARGS *qgnutls_anon_allocate_client_credentials)(gnutls_anon_client_credentials_t *sc);
|
||||
static int (VARGS *qgnutls_global_init)(void);
|
||||
|
@ -193,8 +195,12 @@ static int (VARGS *qgnutls_dtls_cookie_send)(gnutls_datum_t * key, void *client_
|
|||
static void (VARGS *qgnutls_dtls_prestate_set)(gnutls_session_t session, gnutls_dtls_prestate_st * prestate);
|
||||
static void (VARGS *qgnutls_dtls_set_mtu)(gnutls_session_t session, unsigned int mtu);
|
||||
|
||||
//static int (VARGS *qgnutls_psk_allocate_client_credentials)(gnutls_psk_client_credentials_t *sc);
|
||||
//static int (VARGS *qgnutls_psk_set_client_credentials)(gnutls_psk_client_credentials_t res, const char *username, const gnutls_datum_t * key, gnutls_psk_key_flags flags);
|
||||
static int (VARGS *qgnutls_psk_allocate_server_credentials)(gnutls_psk_server_credentials_t *sc);
|
||||
static void (VARGS *qgnutls_psk_set_server_credentials_function)(gnutls_psk_server_credentials_t cred, gnutls_psk_server_credentials_function *func);
|
||||
static int (VARGS *qgnutls_psk_set_server_credentials_hint)(gnutls_psk_server_credentials_t res, const char *hint);
|
||||
static const char *(VARGS *qgnutls_psk_client_get_hint)(gnutls_session_t session);
|
||||
static int (VARGS *qgnutls_psk_allocate_client_credentials)(gnutls_psk_client_credentials_t *sc);
|
||||
static void (VARGS *qgnutls_psk_set_client_credentials_function)(gnutls_psk_client_credentials_t cred, gnutls_psk_client_credentials_function *func);
|
||||
#endif
|
||||
|
||||
static unsigned int (VARGS *qgnutls_sec_param_to_pk_bits)(gnutls_pk_algorithm_t algo, gnutls_sec_param_t param);
|
||||
|
@ -254,9 +260,12 @@ static qboolean Init_GNUTLS(void)
|
|||
GNUTLS_FUNC(gnutls_dtls_cookie_verify) \
|
||||
GNUTLS_FUNC(gnutls_dtls_cookie_send) \
|
||||
GNUTLS_FUNC(gnutls_dtls_prestate_set) \
|
||||
GNUTLS_FUNC(gnutls_dtls_set_mtu)
|
||||
// GNUTLS_FUNC(gnutls_psk_allocate_client_credentials)
|
||||
// GNUTLS_FUNC(gnutls_psk_set_client_credentials)
|
||||
GNUTLS_FUNC(gnutls_dtls_set_mtu) \
|
||||
GNUTLS_FUNC(gnutls_psk_allocate_server_credentials) \
|
||||
GNUTLS_FUNC(gnutls_psk_set_server_credentials_function) \
|
||||
GNUTLS_FUNC(gnutls_psk_set_server_credentials_hint) \
|
||||
GNUTLS_FUNC(gnutls_psk_allocate_client_credentials) \
|
||||
GNUTLS_FUNC(gnutls_psk_set_client_credentials_function)
|
||||
#else
|
||||
#define GNUTLS_DTLS_STUFF
|
||||
#endif
|
||||
|
@ -288,6 +297,8 @@ static qboolean Init_GNUTLS(void)
|
|||
|
||||
#define GNUTLS_FUNCS \
|
||||
GNUTLS_FUNC(gnutls_bye) \
|
||||
GNUTLS_FUNC(gnutls_alert_get) \
|
||||
GNUTLS_FUNC(gnutls_alert_get_name) \
|
||||
GNUTLS_FUNC(gnutls_perror) \
|
||||
GNUTLS_FUNC(gnutls_handshake) \
|
||||
GNUTLS_FUNC(gnutls_transport_set_ptr) \
|
||||
|
@ -340,6 +351,7 @@ static qboolean Init_GNUTLS(void)
|
|||
{(void**)&qgnutls_init, "gnutls_init"},
|
||||
{(void**)&qgnutls_deinit, "gnutls_deinit"},
|
||||
{(void**)&qgnutls_set_default_priority, "gnutls_set_default_priority"},
|
||||
{(void**)&qgnutls_set_default_priority_append, "gnutls_set_default_priority_append"},
|
||||
{(void**)&qgnutls_certificate_allocate_credentials, "gnutls_certificate_allocate_credentials"},
|
||||
{(void**)&qgnutls_anon_allocate_client_credentials, "gnutls_anon_allocate_client_credentials"},
|
||||
{(void**)&qgnutls_global_init, "gnutls_global_init"},
|
||||
|
@ -378,8 +390,13 @@ static qboolean Init_GNUTLS(void)
|
|||
{(void**)&qgnutls_dtls_cookie_send, "gnutls_dtls_cookie_send"},
|
||||
{(void**)&qgnutls_dtls_prestate_set, "gnutls_dtls_prestate_set"},
|
||||
{(void**)&qgnutls_dtls_set_mtu, "gnutls_dtls_set_mtu"},
|
||||
// {(void**)&qgnutls_psk_allocate_client_credentials, "gnutls_psk_allocate_client_credentials"},
|
||||
// {(void**)&qgnutls_psk_set_client_credentials, "gnutls_psk_set_client_credentials"},
|
||||
|
||||
{(void**)&qgnutls_psk_allocate_server_credentials, "gnutls_psk_allocate_server_credentials"},
|
||||
{(void**)&qgnutls_psk_set_server_credentials_function, "gnutls_psk_set_server_credentials_function"},
|
||||
{(void**)&qgnutls_psk_set_server_credentials_hint, "gnutls_psk_set_server_credentials_hint"},
|
||||
{(void**)&qgnutls_psk_client_get_hint, "gnutls_psk_client_get_hint"},
|
||||
{(void**)&qgnutls_psk_allocate_client_credentials, "gnutls_psk_allocate_client_credentials"},
|
||||
{(void**)&qgnutls_psk_set_client_credentials_function, "gnutls_psk_set_client_credentials_function"},
|
||||
#endif
|
||||
|
||||
{(void**)&qgnutls_sec_param_to_pk_bits, "gnutls_sec_param_to_pk_bits"},
|
||||
|
@ -459,6 +476,65 @@ typedef struct
|
|||
// int mtu;
|
||||
} gnutlsfile_t;
|
||||
|
||||
static void SSL_SetCertificateName(gnutlsfile_t *f, const char *hostname)
|
||||
{
|
||||
int i;
|
||||
if (hostname)
|
||||
{
|
||||
const char *host = strstr(hostname, "://");
|
||||
if (host)
|
||||
hostname = host+3;
|
||||
//any dtls:// prefix will have been stripped now.
|
||||
if (*hostname == '[')
|
||||
{ //eg: [::1]:foo - skip the lead [ and strip the ] and any trailing data (hopefully just a :port or nothing)
|
||||
hostname++;
|
||||
host = strchr(hostname, ']');
|
||||
if (host && host-hostname < sizeof(f->certname))
|
||||
{
|
||||
memcpy(f->certname, hostname, host-hostname);
|
||||
f->certname[host-hostname] = 0;
|
||||
hostname = f->certname;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //eg: 127.0.0.1:port - strip the port number if specified.
|
||||
host = strchr(hostname, ':');
|
||||
if (host && host-hostname < sizeof(f->certname))
|
||||
{
|
||||
memcpy(f->certname, hostname, host-hostname);
|
||||
f->certname[host-hostname] = 0;
|
||||
hostname = f->certname;
|
||||
}
|
||||
}
|
||||
for (i = 0; hostname[i]; i++)
|
||||
{
|
||||
if (hostname[i] >= 'a' && hostname[i] <= 'z')
|
||||
;
|
||||
else if (hostname[i] >= 'A' && hostname[i] <= 'Z')
|
||||
;
|
||||
else if (hostname[i] >= '0' && hostname[i] <= '9')
|
||||
;
|
||||
else if (hostname[i] == '-' || hostname[i] == '.')
|
||||
;
|
||||
else
|
||||
{
|
||||
hostname = NULL; //something invalid. bum.
|
||||
break;
|
||||
}
|
||||
}
|
||||
//we should have a cleaned up host name now, ready for (ab)use in certificates.
|
||||
}
|
||||
|
||||
if (!hostname)
|
||||
*f->certname = 0;
|
||||
else if (hostname == f->certname)
|
||||
;
|
||||
else if (strlen(hostname) >= sizeof(f->certname))
|
||||
*f->certname = 0;
|
||||
else
|
||||
memcpy(f->certname, hostname, strlen(hostname)+1);
|
||||
}
|
||||
|
||||
#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
static void SSL_Close(vfsfile_t *vfs)
|
||||
|
@ -703,6 +779,7 @@ static int SSL_DoHandshake(gnutlsfile_t *file)
|
|||
{
|
||||
case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
|
||||
case GNUTLS_E_CERTIFICATE_ERROR: err = VFS_ERROR_UNTRUSTED; break;
|
||||
case GNUTLS_E_SESSION_EOF:
|
||||
case GNUTLS_E_PREMATURE_TERMINATION: err = VFS_ERROR_EOF; break;
|
||||
case GNUTLS_E_PUSH_ERROR: err = file->pusherror; break;
|
||||
case GNUTLS_E_PULL_ERROR: err = file->pullerror; break;
|
||||
|
@ -920,6 +997,7 @@ static int DTLS_Pull_Timeout(gnutls_transport_ptr_t p, unsigned int timeout)
|
|||
static gnutls_anon_client_credentials_t anoncred[2];
|
||||
#else
|
||||
static gnutls_certificate_credentials_t xcred[2];
|
||||
static qboolean servercertfail;
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
static gnutls_datum_t cookie_key;
|
||||
|
@ -987,7 +1065,7 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
|
|||
memset(&priv, 0, sizeof(priv));
|
||||
memset(&pub, 0, sizeof(pub));
|
||||
|
||||
if ((!privf || !pubf) && hostname)
|
||||
if ((!privf || !pubf))// && hostname)
|
||||
{ //not found? generate a new one.
|
||||
//FIXME: how to deal with race conditions with multiple servers on the same host?
|
||||
//delay till the first connection? we at least write both files at the sameish time.
|
||||
|
@ -1170,7 +1248,7 @@ qboolean SSL_InitGlobal(qboolean isserver)
|
|||
{
|
||||
#if 1
|
||||
if (!SSL_LoadPrivateCert(xcred[isserver]))
|
||||
initstatus[isserver] = -1;
|
||||
servercertfail = true;
|
||||
#else
|
||||
int ret = -1;
|
||||
char keyfile[MAX_OSPATH];
|
||||
|
@ -1202,12 +1280,39 @@ qboolean SSL_InitGlobal(qboolean isserver)
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
#if 0
|
||||
#ifdef HAVE_DTLS
|
||||
static int GetPSKForUser(gnutls_session_t sess, const char *username, gnutls_datum_t * key)
|
||||
{
|
||||
Con_Printf("GetPSKForUser: %s\n", username);
|
||||
key->size = 0;
|
||||
key->data = key->size?gnutls_malloc(key->size):0;
|
||||
{ //serverside. name must match what we expect (this isn't very secure), and we return the key we require for that user name.
|
||||
if (!strcmp(username, dtls_psk_user.string))
|
||||
{
|
||||
key->size = (strlen(dtls_psk_key.string)+1)/2;
|
||||
key->data = (*qgnutls_malloc)(key->size);
|
||||
key->size = Base16_DecodeBlock(dtls_psk_key.string, key->data, key->size);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int GetPSKForServer(gnutls_session_t sess, char **username, gnutls_datum_t *key)
|
||||
{ //clientside. return the appropriate username for the hint, along with the matching key.
|
||||
//this could be made more fancy with a database, but we'll keep it simple with cvars.
|
||||
const char *svhint = qgnutls_psk_client_get_hint(sess);
|
||||
|
||||
if (!svhint)
|
||||
svhint = "";
|
||||
|
||||
if ((!*dtls_psk_hint.string&&*dtls_psk_user.string) || (*dtls_psk_hint.string&&!strcmp(svhint, dtls_psk_hint.string)))
|
||||
{ //okay, hints match (or ours is unset), report our user as appropriate.
|
||||
*username = strcpy((*qgnutls_malloc)(strlen(dtls_psk_user.string)+1), dtls_psk_user.string);
|
||||
|
||||
key->size = (strlen(dtls_psk_key.string)+1)/2;
|
||||
key->data = (*qgnutls_malloc)(key->size);
|
||||
key->size = Base16_DecodeBlock(dtls_psk_key.string, key->data, key->size);
|
||||
return 0;
|
||||
}
|
||||
else if (!*dtls_psk_user.string && !*dtls_psk_hint.string)
|
||||
Con_Printf(CON_ERROR"Server requires a Pre-Shared Key (hint: \"%s\"). Please set %s, %s, and %s accordingly.\n", svhint, dtls_psk_hint.name, dtls_psk_user.name, dtls_psk_key.name);
|
||||
else
|
||||
Con_Printf(CON_ERROR"Server requires different Pre-Shared Key credentials (hint: \"%s\", expected \"%s\"). Please set %s, %s, and %s accordingly.\n", svhint, dtls_psk_hint.string, dtls_psk_hint.name, dtls_psk_user.name, dtls_psk_key.name);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
@ -1224,39 +1329,36 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
|
|||
//qgnutls_kx_set_priority (newf->session, kx_prio);
|
||||
qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]);
|
||||
#else
|
||||
#if 0//def HAVE_DTLS
|
||||
if (datagram)
|
||||
{ //use some arbitrary PSK for dtls clients.
|
||||
if (isserver)
|
||||
{
|
||||
#ifdef HAVE_DTLS
|
||||
if (datagram && !isserver)
|
||||
{ //do psk as needed. we can still do the cert stuff if the server isn't doing psk.
|
||||
gnutls_psk_client_credentials_t pskcred;
|
||||
qgnutls_psk_allocate_client_credentials(&pskcred);
|
||||
qgnutls_psk_set_client_credentials_function(pskcred, GetPSKForServer);
|
||||
|
||||
qgnutls_set_default_priority_append (newf->session, "+ECDHE-PSK:+DHE-PSK:+PSK", NULL, 0);
|
||||
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
|
||||
}
|
||||
else if (datagram && isserver && (*dtls_psk_user.string || servercertfail))
|
||||
{ //offer some arbitrary PSK for dtls clients.
|
||||
gnutls_psk_server_credentials_t pskcred;
|
||||
qgnutls_psk_allocate_server_credentials(&pskcred);
|
||||
qgnutls_psk_set_server_credentials_function(pskcred, GetPSKForUser);
|
||||
qgnutls_psk_set_server_credentials_hint(pskcred, "id-quake-ex-dtls");
|
||||
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_CLIENT
|
||||
extern cvar_t name;
|
||||
const char *namestr = name.string;
|
||||
#else
|
||||
const char *namestr = "Anonymous";
|
||||
#endif
|
||||
gnutls_psk_client_credentials_t pskcred;
|
||||
const gnutls_datum_t key = { (void *) "deadbeef", 0 };
|
||||
qgnutls_psk_allocate_client_credentials(&pskcred);
|
||||
qgnutls_psk_set_client_credentials(pskcred, namestr, &key, GNUTLS_PSK_KEY_HEX);
|
||||
if (*dtls_psk_hint.string)
|
||||
qgnutls_psk_set_server_credentials_hint(pskcred, dtls_psk_hint.string);
|
||||
|
||||
qgnutls_set_default_priority_append (newf->session, ("-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK")+(servercertfail?0:8), NULL, 0);
|
||||
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]);
|
||||
#endif
|
||||
// Use default priorities
|
||||
{
|
||||
// Use default priorities for regular tls sessions
|
||||
qgnutls_set_default_priority (newf->session);
|
||||
}
|
||||
#endif
|
||||
if (xcred[isserver])
|
||||
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]);
|
||||
|
||||
// tell gnutls how to send/receive data
|
||||
qgnutls_transport_set_ptr (newf->session, newf);
|
||||
|
@ -1297,10 +1399,7 @@ static vfsfile_t *GNUTLS_OpenVFS(const char *hostname, vfsfile_t *source, qboole
|
|||
newf->funcs.Tell = SSL_Tell;
|
||||
newf->funcs.seekstyle = SS_UNSEEKABLE;
|
||||
|
||||
if (hostname)
|
||||
Q_strncpyz(newf->certname, hostname, sizeof(newf->certname));
|
||||
else
|
||||
Q_strncpyz(newf->certname, "", sizeof(newf->certname));
|
||||
SSL_SetCertificateName(newf, hostname);
|
||||
|
||||
if (!SSL_InitConnection(newf, isserver, false))
|
||||
{
|
||||
|
@ -1433,7 +1532,7 @@ static void *GNUDTLS_CreateContext(const char *remotehost, void *cbctx, neterr_t
|
|||
|
||||
// Sys_Printf("DTLS_CreateContext: server=%i\n", isserver);
|
||||
|
||||
Q_strncpyz(newf->certname, remotehost?remotehost:"", sizeof(newf->certname));
|
||||
SSL_SetCertificateName(newf, remotehost);
|
||||
|
||||
if (!SSL_InitConnection(newf, isserver, true))
|
||||
{
|
||||
|
@ -1587,7 +1686,7 @@ static qboolean GNUDTLS_CheckConnection(void *cbctx, void *peeraddr, size_t peer
|
|||
|
||||
//and this is the result...
|
||||
qgnutls_dtls_prestate_set(f->session, &prestate);
|
||||
qgnutls_dtls_set_mtu(f->session, 1440);
|
||||
qgnutls_dtls_set_mtu(f->session, 1400);
|
||||
|
||||
//still need to do the whole certificate thing though.
|
||||
f->handshaking = true;
|
||||
|
@ -1628,6 +1727,8 @@ static const dtlsfuncs_t *GNUDTLS_InitServer(void)
|
|||
{
|
||||
if (!SSL_InitGlobal(true))
|
||||
return NULL; //unable to init a server certificate. don't allow dtls to init.
|
||||
if (servercertfail && !*dtls_psk_user.string)
|
||||
return NULL;
|
||||
return &dtlsfuncs_gnutls;
|
||||
}
|
||||
static const dtlsfuncs_t *GNUDTLS_InitClient(void)
|
||||
|
|
|
@ -165,13 +165,18 @@ static void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue)
|
|||
if (!svs.sockets->dtlsfuncs)
|
||||
{
|
||||
if (var->ival >= 2)
|
||||
Con_Printf("%sUnable to set %s to \"%s\", no DTLS certificate available.\n", (var->ival >= 2)?CON_ERROR:CON_WARNING, var->name, var->string);
|
||||
Con_Printf("%sUnable to set %s to \"%s\", no DTLS provider available.\n", (var->ival >= 2)?CON_ERROR:CON_WARNING, var->name, var->string);
|
||||
var->ival = 0; //disable the cvar (internally) if we don't have a usable certificate. this allows us to default the cvar to enabled without it breaking otherwise.
|
||||
}
|
||||
}
|
||||
}
|
||||
cvar_t net_enable_dtls = CVARAFCD("net_enable_dtls", "", "sv_listen_dtls", 0, NET_Enable_DTLS_Changed, "Controls serverside dtls support.\n0: dtls blocked, not advertised.\n1: clientside choice.\n2: used where possible (recommended setting).\n3: disallow non-dtls clients (sv_port_tcp should be eg tls://[::]:27500 to also disallow unencrypted tcp connections).");
|
||||
#endif
|
||||
#if defined(HAVE_DTLS)
|
||||
cvar_t dtls_psk_hint = CVARFD("dtls_psk_hint", "", CVAR_NOUNSAFEEXPAND, "For DTLS-PSK handshakes. This specifies the public server identity.");
|
||||
cvar_t dtls_psk_user = CVARFD("dtls_psk_user", "", CVAR_NOUNSAFEEXPAND, "For DTLS-PSK handshakes. This specifies the username to use when the client+server's hints match.");
|
||||
cvar_t dtls_psk_key = CVARFD("dtls_psk_key", "", CVAR_NOUNSAFEEXPAND, "For DTLS-PSK handshakes. This specifies the hexadecimal key which must match between client+server. Will only be used when client+server's hint settings match.");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CLIENT
|
||||
static void QDECL cl_delay_packets_Announce(cvar_t *var, char *oldval)
|
||||
|
@ -2997,7 +3002,7 @@ static neterr_t FTENET_DTLS_DoSendPacket(void *cbctx, const qbyte *data, size_t
|
|||
struct dtlspeer_s *peer = cbctx;
|
||||
return NET_SendPacketCol(peer->col, length, data, &peer->addr);
|
||||
}
|
||||
qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to)
|
||||
qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to, const char *hostname)
|
||||
{
|
||||
extern cvar_t timeout;
|
||||
struct dtlspeer_s *peer;
|
||||
|
@ -3010,7 +3015,6 @@ qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to)
|
|||
}
|
||||
if (!peer)
|
||||
{
|
||||
char hostname[256];
|
||||
peer = Z_Malloc(sizeof(*peer));
|
||||
peer->addr = *to;
|
||||
peer->col = col;
|
||||
|
@ -3020,7 +3024,7 @@ qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to)
|
|||
else
|
||||
peer->funcs = DTLS_InitClient();
|
||||
if (peer->funcs)
|
||||
peer->dtlsstate = peer->funcs->CreateContext(NET_BaseAdrToString(hostname, sizeof(hostname), to), peer, FTENET_DTLS_DoSendPacket, col->islisten);
|
||||
peer->dtlsstate = peer->funcs->CreateContext(hostname, peer, FTENET_DTLS_DoSendPacket, col->islisten);
|
||||
|
||||
peer->timeout = realtime+timeout.value;
|
||||
if (peer->dtlsstate)
|
||||
|
@ -8009,14 +8013,14 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
|
|||
break;
|
||||
case NP_DTLS:
|
||||
adr->prot = NP_DGRAM;
|
||||
NET_EnsureRoute(collection, routename, host, adr);
|
||||
if (NET_DTLS_Create(collection, adr))
|
||||
if (NET_EnsureRoute(collection, routename, host, adr))
|
||||
if (NET_DTLS_Create(collection, adr, host))
|
||||
{
|
||||
adr->prot = NP_DTLS;
|
||||
return true;
|
||||
}
|
||||
adr->prot = NP_DTLS;
|
||||
break;
|
||||
return false;
|
||||
case NP_WS:
|
||||
case NP_WSS:
|
||||
case NP_TLS:
|
||||
|
@ -9034,6 +9038,11 @@ void SVNET_RegisterCvars(void)
|
|||
#if defined(HAVE_DTLS) && defined(HAVE_SERVER)
|
||||
Cvar_Register (&net_enable_dtls, "networking");
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
Cvar_Register (&dtls_psk_hint, "networking");
|
||||
Cvar_Register (&dtls_psk_user, "networking");
|
||||
Cvar_Register (&dtls_psk_key, "networking");
|
||||
#endif
|
||||
}
|
||||
|
||||
void NET_CloseServer(void)
|
||||
|
|
|
@ -1896,6 +1896,14 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
|
|||
Plug_Net_Close,
|
||||
Plug_Net_SetTLSClient,
|
||||
Plug_Net_GetTLSBinding,
|
||||
|
||||
Sys_RandomBytes,
|
||||
TLS_GetKnownCertificate,
|
||||
#ifdef HAVE_CLIENT
|
||||
CertLog_ConnectOkay,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
};
|
||||
if (structsize == sizeof(funcs))
|
||||
return &funcs;
|
||||
|
|
|
@ -217,7 +217,6 @@ char cvargroup_servercontrol[] = "server control variables";
|
|||
|
||||
vfsfile_t *sv_fraglogfile;
|
||||
|
||||
void SV_AcceptClient (netadr_t *adr, int userid, char *userinfo);
|
||||
void PRH2_SetPlayerClass(client_t *cl, int classnum, qboolean fromqc);
|
||||
void SV_DeDupeName(const char *val, client_t *cl, char *newname, size_t newnamesize);
|
||||
|
||||
|
@ -3424,12 +3423,16 @@ void SVC_DirectConnect(int expectedreliablesequence)
|
|||
|
||||
// note an extra qbyte is needed to replace spectator key
|
||||
Q_strncpyz (info.userinfo, Cmd_Argv(4), sizeof(info.userinfo)-1);
|
||||
if (info.protocol == SCP_NETQUAKE)
|
||||
if (info.protocol >= SCP_NETQUAKE)
|
||||
{
|
||||
Info_RemoveKey(info.userinfo, "mod"); //its served its purpose.
|
||||
Info_RemoveKey(info.userinfo, "modver"); //its served its purpose.
|
||||
Info_RemoveKey(info.userinfo, "flags"); //its served its purpose.
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (net_enable_dtls.ival > 2 && (net_from.prot == NP_DGRAM || net_from.prot == NP_STREAM || net_from.prot == NP_WS))
|
||||
if (net_enable_dtls.ival > 2 && (net_from.prot == NP_DGRAM || net_from.prot == NP_STREAM || net_from.prot == NP_WS) && net_from.type != NA_LOOPBACK)
|
||||
{
|
||||
SV_RejectMessage (info.protocol, "This server requires the use of DTLS/TLS/WSS.\n");
|
||||
return;
|
||||
|
@ -4118,7 +4121,7 @@ qboolean SV_ConnectionlessPacket (void)
|
|||
else
|
||||
{
|
||||
//NET_DTLS_Disconnect(svs.sockets, &net_from);
|
||||
if (NET_DTLS_Create(svs.sockets, &net_from))
|
||||
if (NET_DTLS_Create(svs.sockets, &net_from, NULL))
|
||||
Netchan_OutOfBandPrint(NS_SERVER, &net_from, "dtlsopened");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include "plugin.h"
|
||||
#include "netinc.h"
|
||||
|
||||
plugfsfuncs_t *fsfuncs;
|
||||
static plugfsfuncs_t *fsfuncs;
|
||||
static plugnetfuncs_t *netfuncs;
|
||||
|
||||
static cvar_t *pdtls_psk_hint, *pdtls_psk_user, *pdtls_psk_key;
|
||||
|
||||
#undef SHA1
|
||||
#undef HMAC
|
||||
|
@ -230,7 +233,7 @@ static int OSSL_Verify_Peer(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
if (err == X509_V_ERR_CERT_HAS_EXPIRED || err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
|
||||
{
|
||||
size_t knownsize;
|
||||
qbyte *knowndata = TLS_GetKnownCertificate(uctx->peername, &knownsize);
|
||||
qbyte *knowndata = netfuncs->TLS_GetKnownCertificate(uctx->peername, &knownsize);
|
||||
if (knowndata)
|
||||
{ //check
|
||||
size_t blobsize;
|
||||
|
@ -274,7 +277,7 @@ static int OSSL_Verify_Peer(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
probs |= CERTLOG_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
if (CertLog_ConnectOkay(uctx->peername, blob, blobsize, probs))
|
||||
if (netfuncs->CertLog_ConnectOkay && netfuncs->CertLog_ConnectOkay(uctx->peername, blob, blobsize, probs))
|
||||
return 1; //ignore the errors...
|
||||
}
|
||||
#endif
|
||||
|
@ -399,6 +402,65 @@ static void OSSL_OpenPubKey(void)
|
|||
BIO_free(bio);
|
||||
}
|
||||
|
||||
static char *OSSL_SetCertificateName(char *out, const char *hostname)
|
||||
{ //glorified strcpy...
|
||||
int i;
|
||||
if (hostname)
|
||||
{
|
||||
const char *host = strstr(hostname, "://");
|
||||
if (host)
|
||||
hostname = host+3;
|
||||
//any dtls:// prefix will have been stripped now.
|
||||
if (*hostname == '[')
|
||||
{ //eg: [::1]:foo - skip the lead [ and strip the ] and any trailing data (hopefully just a :port or nothing)
|
||||
hostname++;
|
||||
host = strchr(hostname, ']');
|
||||
if (host)
|
||||
{
|
||||
memcpy(out, hostname, host-hostname);
|
||||
out[host-hostname] = 0;
|
||||
hostname = out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //eg: 127.0.0.1:port - strip the port number if specified.
|
||||
host = strchr(hostname, ':');
|
||||
if (host)
|
||||
{
|
||||
memcpy(out, hostname, host-hostname);
|
||||
out[host-hostname] = 0;
|
||||
hostname = out;
|
||||
}
|
||||
}
|
||||
for (i = 0; hostname[i]; i++)
|
||||
{
|
||||
if (hostname[i] >= 'a' && hostname[i] <= 'z')
|
||||
;
|
||||
else if (hostname[i] >= 'A' && hostname[i] <= 'Z')
|
||||
;
|
||||
else if (hostname[i] >= '0' && hostname[i] <= '9')
|
||||
;
|
||||
else if (hostname[i] == '-' || hostname[i] == '.')
|
||||
;
|
||||
else
|
||||
{
|
||||
hostname = NULL; //something invalid. bum.
|
||||
break;
|
||||
}
|
||||
}
|
||||
//we should have a cleaned up host name now, ready for (ab)use in certificates.
|
||||
}
|
||||
|
||||
if (!hostname)
|
||||
*out = 0;
|
||||
else if (hostname == out)
|
||||
;
|
||||
else
|
||||
memcpy(out, hostname, strlen(hostname)+1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static vfsfile_t *OSSL_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver)
|
||||
{
|
||||
BIO *sink;
|
||||
|
@ -406,6 +468,9 @@ static vfsfile_t *OSSL_OpenVFS(const char *hostname, vfsfile_t *source, qboolean
|
|||
if (!OSSL_Init())
|
||||
return NULL; //FAIL!
|
||||
|
||||
if (!hostname)
|
||||
hostname = "";
|
||||
|
||||
n = calloc(sizeof(*n) + strlen(hostname)+1, 1);
|
||||
|
||||
n->funcs.ReadBytes = OSSL_FRead;
|
||||
|
@ -417,7 +482,7 @@ static vfsfile_t *OSSL_OpenVFS(const char *hostname, vfsfile_t *source, qboolean
|
|||
n->funcs.Flush = NULL;
|
||||
n->funcs.seekstyle = SS_UNSEEKABLE;
|
||||
|
||||
n->cert.peername = strcpy((char*)(n+1), hostname);
|
||||
n->cert.peername = OSSL_SetCertificateName((char*)(n+1), hostname);
|
||||
n->cert.dtls = false;
|
||||
|
||||
ERR_print_errors_cb(OSSL_PrintError_CB, NULL);
|
||||
|
@ -489,6 +554,9 @@ typedef struct {
|
|||
// BIO *sink;
|
||||
qbyte *pending;
|
||||
size_t pendingsize;
|
||||
|
||||
void *peeraddr;
|
||||
size_t peeraddrsize;
|
||||
} ossldtls_t;
|
||||
static int OSSL_Bio_DWrite(BIO *h, const char *buf, int size)
|
||||
{
|
||||
|
@ -539,11 +607,15 @@ static long OSSL_Bio_DCtrl(BIO *h, int cmd, long arg1, void *arg2)
|
|||
return 1;
|
||||
|
||||
|
||||
case BIO_CTRL_DGRAM_GET_PEER:
|
||||
return 0;
|
||||
|
||||
case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: //we're non-blocking, so this doesn't affect us.
|
||||
case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
|
||||
case BIO_CTRL_WPENDING:
|
||||
case BIO_CTRL_DGRAM_QUERY_MTU:
|
||||
case BIO_CTRL_DGRAM_SET_MTU:
|
||||
case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
|
||||
return 0;
|
||||
|
||||
|
||||
|
@ -576,17 +648,61 @@ static int OSSL_Bio_DDestroy(BIO *h)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int dehex(int i)
|
||||
{
|
||||
if (i >= '0' && i <= '9')
|
||||
return (i-'0');
|
||||
else if (i >= 'A' && i <= 'F')
|
||||
return (i-'A'+10);
|
||||
else
|
||||
return (i-'a'+10);
|
||||
}
|
||||
static size_t Base16_DecodeBlock_(const char *in, qbyte *out, size_t outsize)
|
||||
{
|
||||
qbyte *start = out;
|
||||
if (!out)
|
||||
return ((strlen(in)+1)/2) + 1;
|
||||
|
||||
for (; ishexcode(in[0]) && ishexcode(in[1]) && outsize > 0; outsize--, in+=2)
|
||||
*out++ = (dehex(in[0])<<4) | dehex(in[1]);
|
||||
return out-start;
|
||||
}
|
||||
|
||||
static unsigned int OSSL_SV_Validate_PSK(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len)
|
||||
{
|
||||
if (!strcmp(identity, pdtls_psk_user->string))
|
||||
{ //Yay! We know this one!
|
||||
return Base16_DecodeBlock_(pdtls_psk_key->string, psk, max_psk_len);
|
||||
}
|
||||
return 0; //0 for error, or something.
|
||||
}
|
||||
unsigned int OSSL_CL_Validate_PSK(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len)
|
||||
{ //if our hint cvar matches, then report our user+key cvars to the server
|
||||
if ((!*hint && *pdtls_psk_user->string && !*pdtls_psk_hint->string) || (*hint && !strcmp(hint, pdtls_psk_hint->string)))
|
||||
{
|
||||
//FIXME: avoid crashing QE
|
||||
|
||||
Q_strlcpy(identity, pdtls_psk_user->string, max_identity_len);
|
||||
return Base16_DecodeBlock_(pdtls_psk_key->string, psk, max_psk_len);
|
||||
}
|
||||
return 0; //we don't know what to report.
|
||||
}
|
||||
|
||||
static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)
|
||||
{ //if remotehost is null then their certificate will not be validated.
|
||||
ossldtls_t *n = calloc(sizeof(*n) + strlen(remotehost)+1, 1);
|
||||
ossldtls_t *n;
|
||||
BIO *sink;
|
||||
|
||||
if (!remotehost)
|
||||
remotehost = "";
|
||||
n = calloc(sizeof(*n) + strlen(remotehost)+1, 1);
|
||||
|
||||
n->cbctx = cbctx;
|
||||
n->push = push;
|
||||
|
||||
n->ctx = SSL_CTX_new(isserver?DTLS_server_method():DTLS_client_method());
|
||||
|
||||
n->cert.peername = strcpy((char*)(n+1), remotehost);
|
||||
n->cert.peername = OSSL_SetCertificateName((char*)(n+1), remotehost);
|
||||
n->cert.dtls = true;
|
||||
|
||||
if (n->ctx)
|
||||
|
@ -599,6 +715,28 @@ static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*p
|
|||
SSL_CTX_set_verify_depth(n->ctx, 5);
|
||||
SSL_CTX_set_options(n->ctx, SSL_OP_NO_COMPRESSION); //compression allows guessing the contents of the stream somehow.
|
||||
|
||||
if (isserver)
|
||||
{
|
||||
if (*pdtls_psk_user->string)
|
||||
{
|
||||
if (*pdtls_psk_user->string)
|
||||
SSL_CTX_use_psk_identity_hint(n->ctx, pdtls_psk_hint->string);
|
||||
SSL_CTX_set_psk_server_callback(n->ctx, OSSL_SV_Validate_PSK);
|
||||
}
|
||||
|
||||
if (vhost.servercert && vhost.privatekey)
|
||||
{
|
||||
SSL_CTX_use_certificate(n->ctx, vhost.servercert);
|
||||
SSL_CTX_use_PrivateKey(n->ctx, vhost.privatekey);
|
||||
assert(1==SSL_CTX_check_private_key(n->ctx));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*pdtls_psk_user->string)
|
||||
SSL_CTX_set_psk_client_callback(n->ctx, OSSL_CL_Validate_PSK);
|
||||
}
|
||||
|
||||
//SSL_CTX_use_certificate_file
|
||||
//FIXME: SSL_CTX_use_certificate_file aka SSL_CTX_use_certificate(PEM_read_bio_X509)
|
||||
//FIXME: SSL_CTX_use_PrivateKey_file aka SSL_CTX_use_PrivateKey(PEM_read_bio_PrivateKey)
|
||||
|
@ -619,8 +757,10 @@ static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*p
|
|||
}
|
||||
|
||||
BIO_get_ssl(n->bio, &n->ssl);
|
||||
SSL_set_app_data(n->ssl, n);
|
||||
SSL_set_ex_data(n->ssl, ossl_fte_certctx, &n->cert);
|
||||
SSL_set_tlsext_host_name(n->ssl, remotehost); //let the server know which cert to send
|
||||
if (*n->cert.peername)
|
||||
SSL_set_tlsext_host_name(n->ssl, n->cert.peername); //let the server know which cert to send
|
||||
BIO_do_connect(n->bio);
|
||||
ERR_print_errors_cb(OSSL_PrintError_CB, NULL);
|
||||
return n;
|
||||
|
@ -629,6 +769,71 @@ static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*p
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char dtlscookiekey[16];
|
||||
static int OSSL_GenCookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
|
||||
{
|
||||
ossldtls_t *f = SSL_get_app_data(ssl);
|
||||
|
||||
SHA_CTX ctx;
|
||||
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, dtlscookiekey, sizeof(dtlscookiekey));
|
||||
SHA1_Update(&ctx, "somethinghashy", 15);
|
||||
SHA1_Update(&ctx, f->peeraddr, f->peeraddrsize);
|
||||
SHA1_Final(cookie, &ctx);
|
||||
*cookie_len = SHA_DIGEST_LENGTH;
|
||||
|
||||
return 1;
|
||||
}
|
||||
static int OSSL_VerifyCookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)
|
||||
{
|
||||
unsigned char match[DTLS1_COOKIE_LENGTH];
|
||||
unsigned int matchsize;
|
||||
if (OSSL_GenCookie(ssl, match, &matchsize))
|
||||
if (cookie_len == matchsize && !memcmp(cookie, match, matchsize))
|
||||
return 1;
|
||||
return 0; //not valid.
|
||||
}
|
||||
qboolean OSSL_CheckConnection(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state))
|
||||
{
|
||||
int ret;
|
||||
static ossldtls_t *pending;
|
||||
BIO_ADDR *bioaddr = BIO_ADDR_new();
|
||||
|
||||
if (!pending)
|
||||
{
|
||||
pending = OSSL_CreateContext("localhost", cbctx, push, true);
|
||||
|
||||
SSL_CTX_set_cookie_generate_cb(pending->ctx, OSSL_GenCookie);
|
||||
SSL_CTX_set_cookie_verify_cb(pending->ctx, OSSL_VerifyCookie);
|
||||
}
|
||||
|
||||
SSL_set_app_data(pending->ssl, pending);
|
||||
|
||||
//make sure its kept current...
|
||||
pending->cbctx = cbctx;
|
||||
pending->push = push;
|
||||
|
||||
pending->pending = indata;
|
||||
pending->pendingsize = insize;
|
||||
ret = DTLSv1_listen(pending->ssl, bioaddr);
|
||||
|
||||
BIO_ADDR_free(bioaddr);
|
||||
|
||||
if (ret >= 1)
|
||||
{
|
||||
pending->pending = NULL;
|
||||
pending->pendingsize = 0;
|
||||
|
||||
EstablishTrueContext(&pending->cbctx, pending);
|
||||
pending = NULL; //returned to called. next request gets a new one.
|
||||
return true;
|
||||
}
|
||||
//0 = nonfatal
|
||||
//-1 = fatal
|
||||
return false;
|
||||
}
|
||||
static void OSSL_DestroyContext(void *ctx)
|
||||
{
|
||||
ossldtls_t *o = (ossldtls_t*)ctx;
|
||||
|
@ -709,7 +914,7 @@ static neterr_t OSSL_Timeouts(void *ctx)
|
|||
static dtlsfuncs_t ossl_dtlsfuncs =
|
||||
{
|
||||
OSSL_CreateContext,
|
||||
NULL,
|
||||
OSSL_CheckConnection,
|
||||
OSSL_DestroyContext,
|
||||
OSSL_Transmit,
|
||||
OSSL_Received,
|
||||
|
@ -841,12 +1046,15 @@ static ftecrypto_t crypto_openssl =
|
|||
NULL,
|
||||
};
|
||||
|
||||
void *TLS_GetKnownCertificate(const char *certname, size_t *size){return NULL;}
|
||||
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems){return false;}
|
||||
qboolean Plug_Init(void)
|
||||
{
|
||||
fsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));
|
||||
if (!fsfuncs)
|
||||
netfuncs = plugfuncs->GetEngineInterface(plugnetfuncs_name, sizeof(*netfuncs));
|
||||
if (!fsfuncs || !netfuncs)
|
||||
return false;
|
||||
pdtls_psk_hint = cvarfuncs->GetNVFDG("dtls_psk_hint", "", 0, NULL, "DTLS stuff");
|
||||
pdtls_psk_user = cvarfuncs->GetNVFDG("dtls_psk_user", "", 0, NULL, "DTLS stuff");
|
||||
pdtls_psk_key = cvarfuncs->GetNVFDG("dtls_psk_key", "", 0, NULL, "DTLS stuff");
|
||||
netfuncs->RandomBytes(dtlscookiekey, sizeof(dtlscookiekey)); //something random so people can't guess cookies for arbitrary victim IPs.
|
||||
return plugfuncs->ExportInterface("Crypto", &crypto_openssl, sizeof(crypto_openssl)); //export a named interface struct to the engine
|
||||
}
|
|
@ -374,6 +374,12 @@ typedef struct //for when you need basic socket access, hopefully rare...
|
|||
F(void, Close, (qhandle_t socket));
|
||||
F(int, SetTLSClient, (qhandle_t sock, const char *certhostname)); //adds a tls layer to the socket (and specifies the peer's required hostname)
|
||||
F(int, GetTLSBinding, (qhandle_t sock, char *outdata, int *datalen)); //to avoid MITM attacks with compromised cert authorities
|
||||
|
||||
//for (d)tls plugins to use.
|
||||
F(qboolean, RandomBytes, (qbyte *string, int len));
|
||||
F(void *, TLS_GetKnownCertificate, (const char *certname, size_t *size));
|
||||
F(qboolean, CertLog_ConnectOkay, (const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems));
|
||||
|
||||
#define N_WOULDBLOCK -1
|
||||
#define N_FATALERROR -2
|
||||
#define NET_CLIENTPORT -1
|
||||
|
|
Loading…
Reference in a new issue