mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-25 05:11:35 +00:00
Update DTLS stuff, fixing certificate pinning bugs.
We should now support dtls1.2 under win10 (otherwise win7+8 is still stuck with 1.0). SSQC can now query client certificate info via infokey - *cert_sha1 or *cert_dn Server addresses can be postfixed with eg ip:port?fp=BASE64 to provide a fingerprint to verify the server without depending on cert authorities.
This commit is contained in:
parent
4d06516fb2
commit
f2d54f30d8
17 changed files with 1338 additions and 405 deletions
|
@ -289,15 +289,6 @@ static struct
|
|||
int numadr;
|
||||
int nextadr;
|
||||
netadr_t adr[8]; //addresses that we're trying to transfer to, one entry per dns result, eg both ::1 AND 127.0.0.1
|
||||
#ifdef HAVE_DTLS
|
||||
enum
|
||||
{ //not relevant when given a direct dtls address.
|
||||
DTLS_DISABLE,
|
||||
DTLS_TRY,
|
||||
DTLS_REQUIRE,
|
||||
DTLS_ACTIVE,
|
||||
} dtlsupgrade;
|
||||
#endif
|
||||
int protocol; //nq/qw/q2/q3. guessed based upon server replies
|
||||
int subprotocol; //the monkeys are trying to eat me.
|
||||
struct
|
||||
|
@ -320,13 +311,20 @@ static struct
|
|||
enum coninfomode_e
|
||||
{
|
||||
CIM_DEFAULT, //sends both a qw getchallenge and nq connect (also with postfixed getchallenge so modified servers can force getchallenge)
|
||||
CIM_NQONLY, //disables getchallenge (so fte servers treat us as an nq server). should not be used for dpp7 servers.
|
||||
CIM_NQONLY, //disables getchallenge (so fte servers treat us as an nq client). should not be used for dpp7 servers.
|
||||
CIM_QEONLY, //forces dtls and uses a different nq netchan version
|
||||
} mode;
|
||||
enum coninfospec_e
|
||||
{
|
||||
CIS_DEFAULT, //default
|
||||
CIS_JOIN, //force join
|
||||
CIS_OBSERVE, //force observe
|
||||
} spec;
|
||||
int defaultport;
|
||||
int tries; //increased each try, every fourth trys nq connect packets.
|
||||
unsigned char guid[64]; //client->server guid (so doesn't change with transfers)
|
||||
// qbyte fingerprint[5*4]; //sha1 hash of accepted dtls certs
|
||||
|
||||
struct dtlspeercred_s peercred;
|
||||
} connectinfo;
|
||||
|
||||
qboolean nomaster;
|
||||
|
@ -595,6 +593,29 @@ char *CL_GUIDString(netadr_t *adr)
|
|||
return connectinfo.guid;
|
||||
}
|
||||
|
||||
static void CL_ConnectAbort(const char *format, ...)
|
||||
{ //stops trying to connect, doesn't affect the _current_ connection, so usable for transfers.
|
||||
va_list argptr;
|
||||
char reason[1024];
|
||||
|
||||
if (format)
|
||||
{
|
||||
va_start (argptr, format);
|
||||
Q_vsnprintfz (reason, sizeof(reason), format,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Cvar_Set(&cl_disconnectreason, reason);
|
||||
Con_Printf (CON_ERROR"%s\n", reason);
|
||||
}
|
||||
#ifdef HAVE_DTLS
|
||||
while (connectinfo.numadr)
|
||||
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[--connectinfo.numadr]);
|
||||
#endif
|
||||
connectinfo.numadr = 0;
|
||||
SCR_EndLoadingPlaque();
|
||||
connectinfo.trying = false;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
CL_SendConnectPacket
|
||||
|
@ -658,13 +679,27 @@ static void CL_SendConnectPacket (netadr_t *to)
|
|||
|
||||
t1 = Sys_DoubleTime ();
|
||||
|
||||
if (connectinfo.peercred.hash && net_enable_dtls.ival>0)
|
||||
{
|
||||
char cert[8192];
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
int sz = NET_GetConnectionCertificate(cls.sockets, to, QCERT_PEERCERTIFICATE, cert, sizeof(cert));
|
||||
if (sz <= 0 || memcmp(connectinfo.peercred.digest, digest, CalcHash(connectinfo.peercred.hash, digest, sizeof(digest), cert, sz)))
|
||||
{ //FIXME: we may have already pinned the bad cert, which may cause issues when reconnecting without FP info later.
|
||||
if (NET_GetConnectionCertificate(cls.sockets, to, QCERT_ISENCRYPTED, NULL, 0)<0)
|
||||
CL_ConnectAbort ("Fingerprint specified, but server did not report any certificate\n");
|
||||
else
|
||||
CL_ConnectAbort ("Server certificate does not match specified fingerprint\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!to)
|
||||
{
|
||||
to = &addr;
|
||||
if (!NET_StringToAdr (cls.servername, PORT_DEFAULTSERVER, to))
|
||||
{
|
||||
Con_TPrintf ("CL_SendConnectPacket: Bad server address \"%s\"\n", cls.servername);
|
||||
connectinfo.trying = false;
|
||||
CL_ConnectAbort ("CL_SendConnectPacket: Bad server address \"%s\"\n", cls.servername);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -675,8 +710,7 @@ static void CL_SendConnectPacket (netadr_t *to)
|
|||
|
||||
if (!NET_IsClientLegal(to))
|
||||
{
|
||||
Con_TPrintf ("Illegal server address\n");
|
||||
connectinfo.trying = false;
|
||||
CL_ConnectAbort("Illegal server address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -715,10 +749,12 @@ static void CL_SendConnectPacket (netadr_t *to)
|
|||
Q_strncatz(data, va("\\prx\\%s", cls.servername), sizeof(data));
|
||||
*a = '@';
|
||||
}
|
||||
if (connectinfo.spec==CIS_OBSERVE)
|
||||
Q_strncatz(data, "\\spectator\\1", sizeof(data));
|
||||
//the info itself
|
||||
{
|
||||
static const char *prioritykeys[] = {"name", "password", "spectator", "lang", "rate", "team", "topcolor", "bottomcolor", "skin", "_", "*", NULL};
|
||||
static const char *ignorekeys[] = {"prx", "*z_ext", NULL};
|
||||
const char *ignorekeys[] = {"prx", "*z_ext", (connectinfo.spec!=CIS_DEFAULT)?"spectator":NULL, NULL};
|
||||
InfoBuf_ToString(&cls.userinfo[0], data+strlen(data), sizeof(data)-strlen(data), prioritykeys, ignorekeys, NULL, &cls.userinfosync, &cls.userinfo[0]);
|
||||
}
|
||||
if (connectinfo.protocol == CP_QUAKEWORLD) //zquake extension info.
|
||||
|
@ -808,32 +844,18 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)
|
|||
|
||||
if (!ctx->found)
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("Bad server address \"%s\"", ctx->servername));
|
||||
Con_TPrintf ("Bad server address \"%s\"\n", ctx->servername);
|
||||
connectinfo.trying = false;
|
||||
SCR_EndLoadingPlaque();
|
||||
CL_ConnectAbort("Bad server address \"%s\"\n", ctx->servername);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
for (i = 0; i < ctx->found; i++)
|
||||
{
|
||||
if (connectinfo.dtlsupgrade == DTLS_ACTIVE || connectinfo.mode==CIM_QEONLY)
|
||||
if (net_enable_dtls.ival>=4 || connectinfo.mode==CIM_QEONLY)// || (connectinfo.peercred.hash && net_enable_dtls.ival >= 1))
|
||||
{ //if we've already established a dtls connection, stick with it
|
||||
if (ctx->adr[i].prot == NP_DGRAM)
|
||||
ctx->adr[i].prot = NP_DTLS;
|
||||
}
|
||||
else if (connectinfo.adr[i].prot == NP_DTLS)
|
||||
{ //dtls connections start out with regular udp, and upgrade to dtls once its established that the server supports it.
|
||||
//FIXME: remove this block once our new netcode is better established.
|
||||
connectinfo.dtlsupgrade = DTLS_REQUIRE;
|
||||
ctx->adr[i].prot = NP_DGRAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
//hostname didn't specify dtls. upgrade if we're allowed, but don't mandate it.
|
||||
//connectinfo.dtlsupgrade = DTLS_TRY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -846,11 +868,11 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)
|
|||
static void CL_ResolveServer(void *vctx, void *data, size_t a, size_t b)
|
||||
{
|
||||
struct resolvectx_s *ctx = vctx;
|
||||
const char *host = strrchr(cls.servername+1, '@');
|
||||
const char *host = strrchr(ctx->servername+1, '@');
|
||||
if (host)
|
||||
host++;
|
||||
else
|
||||
host = cls.servername;
|
||||
host = ctx->servername;
|
||||
|
||||
ctx->found = NET_StringToAdr2 (host, connectinfo.defaultport, ctx->adr, countof(ctx->adr), NULL);
|
||||
|
||||
|
@ -1109,9 +1131,7 @@ void CL_CheckForResend (void)
|
|||
connectinfo.nextadr = 0;
|
||||
if (!connectinfo.numadr)
|
||||
{
|
||||
Con_TPrintf ("CL_CheckForResend: Bad server address \"%s\"\n", cls.servername);
|
||||
connectinfo.trying = false;
|
||||
SCR_EndLoadingPlaque();
|
||||
CL_ConnectAbort("CL_CheckForResend: Bad server address \"%s\"\n", cls.servername);
|
||||
return;
|
||||
}
|
||||
NET_AdrToString(data, sizeof(data), &connectinfo.adr[connectinfo.nextadr]);
|
||||
|
@ -1200,6 +1220,9 @@ void CL_CheckForResend (void)
|
|||
else
|
||||
connectinfo.clogged = false; //do the prints and everything.
|
||||
|
||||
if (!cls.sockets) //only if its needed... we don't want to keep using a new port unless we have to
|
||||
NET_InitClient(false);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS)
|
||||
{ //get through the handshake first, instead of waiting for a 5-sec timeout between polls.
|
||||
|
@ -1208,17 +1231,16 @@ void CL_CheckForResend (void)
|
|||
case NETERR_CLOGGED: //temporary failure
|
||||
connectinfo.clogged = true;
|
||||
return;
|
||||
case NETERR_DISCONNECTED:
|
||||
CL_ConnectAbort("DTLS Certificate Verification Failure\n");
|
||||
break;
|
||||
case NETERR_NOROUTE: //not an error here, just means we need to send a new handshake.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connectinfo.dtlsupgrade != DTLS_ACTIVE)
|
||||
#endif
|
||||
{
|
||||
if (!cls.sockets) //only if its needed... we don't want to keep using a new port unless we have to
|
||||
NET_InitClient(false);
|
||||
}
|
||||
|
||||
t1 = Sys_DoubleTime ();
|
||||
if (!connectinfo.istransfer)
|
||||
|
@ -1246,10 +1268,7 @@ void CL_CheckForResend (void)
|
|||
to = &connectinfo.adr[connectinfo.nextadr%connectinfo.numadr];
|
||||
if (!NET_IsClientLegal(to))
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("Illegal server address"));
|
||||
Con_TPrintf ("Illegal server address\n");
|
||||
SCR_EndLoadingPlaque();
|
||||
connectinfo.trying = false;
|
||||
CL_ConnectAbort ("Illegal server address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1274,12 +1293,9 @@ void CL_CheckForResend (void)
|
|||
connectinfo.clogged = false;
|
||||
|
||||
if (connectinfo.tries == 0 && connectinfo.nextadr < connectinfo.numadr)
|
||||
if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, to))
|
||||
if (!NET_EnsureRoute(cls.sockets, "conn", &connectinfo.peercred, to))
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("Unable to establish connection to %s\n", cls.servername));
|
||||
Con_Printf ("Unable to establish connection to %s\n", cls.servername);
|
||||
connectinfo.trying = false;
|
||||
SCR_EndLoadingPlaque();
|
||||
CL_ConnectAbort ("Unable to establish connection to %s\n", cls.servername);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1377,18 +1393,77 @@ void CL_CheckForResend (void)
|
|||
}
|
||||
else
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("No route to \"%s\", giving up\n", cls.servername));
|
||||
Con_TPrintf ("No route to host, giving up\n");
|
||||
connectinfo.trying = false;
|
||||
SCR_EndLoadingPlaque();
|
||||
CL_ConnectAbort ("No route to host, giving up\n");
|
||||
|
||||
NET_CloseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CL_BeginServerConnect(const char *host, int port, qboolean noproxy, enum coninfomode_e mode)
|
||||
static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum coninfomode_e mode, enum coninfospec_e spec)
|
||||
{
|
||||
const char *schemeend = strstr(host, "://");
|
||||
char *arglist;
|
||||
|
||||
Q_strncpyz(cls.serverurl, host, sizeof(cls.serverurl));
|
||||
|
||||
if (schemeend)
|
||||
{
|
||||
const char *schemestart = strchr(host, ':');
|
||||
int schemelen;
|
||||
//if its one of our explicit protocols then use the url as-is
|
||||
const char *netschemes[] = {"udp", "udp4", "udp6", "ipx", "tcp", "tcp4", "tcp6", /*ipx*/"spx", "ws", "wss", "tls", "dtls", "ice", "rtc", "ices", "rtcs", "irc", "udg", "unix"};
|
||||
int i;
|
||||
size_t slen;
|
||||
|
||||
if (!schemestart || schemestart==schemeend)
|
||||
schemestart = host;
|
||||
else
|
||||
schemestart++;
|
||||
schemelen = schemeend-schemestart;
|
||||
|
||||
Q_strncpyz (cls.servername, "", sizeof(cls.servername));
|
||||
for (i = 0; i < countof(netschemes); i++)
|
||||
{
|
||||
slen = strlen(netschemes[i]);
|
||||
if (schemelen == slen && !strncmp(schemestart, netschemes[i], slen))
|
||||
{
|
||||
Q_strncpyz (cls.servername, host, sizeof(cls.servername)); //oh. will probably be okay then
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!*cls.servername)
|
||||
{ //not some '/foo' name, not rtc:// either...
|
||||
char *sl = strchr(schemeend+3, '/');
|
||||
if (sl)
|
||||
{
|
||||
if (!strncmp(sl, "/observe", 8))
|
||||
{
|
||||
if (spec == CIS_DEFAULT)
|
||||
spec = CIS_OBSERVE;
|
||||
else if (spec != CIS_OBSERVE)
|
||||
Con_Printf("Ignoring 'observe'\n");
|
||||
memmove(sl, sl+8, strlen(sl+8)+1);
|
||||
}
|
||||
else if (!strncmp(sl, "/join", 5))
|
||||
{
|
||||
if (spec == CIS_DEFAULT)
|
||||
spec = CIS_JOIN;
|
||||
else if (spec != CIS_OBSERVE)
|
||||
Con_Printf("Ignoring 'join'\n");
|
||||
memmove(sl, sl+5, strlen(sl+5)+1);
|
||||
}
|
||||
else if (!strncmp(sl, "/", 1) && (sl[1] == 0 || sl[1]=='?'))
|
||||
{
|
||||
//current spectator mode
|
||||
memmove(sl, sl+1, strlen(sl+1)+1);
|
||||
}
|
||||
}
|
||||
Q_strncpyz (cls.servername, schemeend+3, sizeof(cls.servername)); //probably some game-specific mess that we don't know
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strncmp(host, "localhost", 9))
|
||||
noproxy = true; //FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses.
|
||||
|
||||
|
@ -1396,13 +1471,14 @@ static void CL_BeginServerConnect(const char *host, int port, qboolean noproxy,
|
|||
Q_strncpyz (cls.servername, host, sizeof(cls.servername));
|
||||
else
|
||||
Q_snprintfz(cls.servername, sizeof(cls.servername), "%s@%s", host, cl_proxyaddr.string);
|
||||
}
|
||||
|
||||
arglist = strchr(cls.servername, '?');
|
||||
|
||||
if (!port)
|
||||
port = cl_defaultport.value;
|
||||
#ifdef HAVE_DTLS
|
||||
while (connectinfo.numadr)
|
||||
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[--connectinfo.numadr]);
|
||||
#endif
|
||||
|
||||
CL_ConnectAbort(NULL);
|
||||
memset(&connectinfo, 0, sizeof(connectinfo));
|
||||
if (*cl_disconnectreason.string)
|
||||
Cvar_Set(&cl_disconnectreason, "");
|
||||
|
@ -1410,15 +1486,31 @@ static void CL_BeginServerConnect(const char *host, int port, qboolean noproxy,
|
|||
connectinfo.defaultport = port;
|
||||
connectinfo.protocol = CP_UNKNOWN;
|
||||
connectinfo.mode = mode;
|
||||
connectinfo.spec = spec;
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (net_enable_dtls.ival >= 3)
|
||||
connectinfo.dtlsupgrade = DTLS_REQUIRE;
|
||||
else if (net_enable_dtls.ival >= 2)
|
||||
connectinfo.dtlsupgrade = DTLS_TRY;
|
||||
connectinfo.peercred.name = cls.servername;
|
||||
if (arglist)
|
||||
{
|
||||
*arglist++ = 0;
|
||||
while (*arglist)
|
||||
{
|
||||
char *e = strchr(arglist, '&');
|
||||
if (e)
|
||||
*e=0;
|
||||
if (!strncasecmp(arglist, "fp=", 3))
|
||||
{
|
||||
Base64_DecodeBlock(arglist+3, arglist+strlen(arglist), connectinfo.peercred.digest, sizeof(connectinfo.peercred.digest));
|
||||
connectinfo.peercred.hash = &hash_sha1;
|
||||
}
|
||||
else
|
||||
connectinfo.dtlsupgrade = DTLS_DISABLE;
|
||||
#endif
|
||||
Con_Printf(CON_WARNING"uri arg not known: \"%s\"\n", arglist);
|
||||
|
||||
if (e)
|
||||
arglist=e+1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SCR_SetLoadingStage(LS_CONNECTION);
|
||||
CL_CheckForResend();
|
||||
|
@ -1434,9 +1526,11 @@ void CL_BeginServerReconnect(void)
|
|||
}
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
if (connectinfo.numadr>0)
|
||||
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[0]);
|
||||
connectinfo.dtlsupgrade = 0;
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < connectinfo.numadr; i++)
|
||||
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[i]);
|
||||
}
|
||||
#endif
|
||||
#ifdef SUPPORT_ICE
|
||||
while (connectinfo.numadr) //remove any ICE addresses. probably we'll end up with no addresses left leaving us free to re-resolve giving us the original(ish) rtc connection.
|
||||
|
@ -1474,11 +1568,11 @@ void CL_Transfer_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
CL_ConnectAbort(NULL);
|
||||
server = Cmd_Argv (1);
|
||||
if (!*server)
|
||||
{
|
||||
//if they didn't specify a server, abort any active transfer/connection.
|
||||
connectinfo.trying = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1529,7 +1623,7 @@ void CL_Connect_f (void)
|
|||
#endif
|
||||
CL_Disconnect_f ();
|
||||
|
||||
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
|
||||
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_DEFAULT);
|
||||
}
|
||||
#if defined(CL_MASTER) && defined(HAVE_PACKET)
|
||||
static void CL_ConnectBestRoute_f (void)
|
||||
|
@ -1564,7 +1658,7 @@ static void CL_ConnectBestRoute_f (void)
|
|||
else
|
||||
#endif
|
||||
CL_Disconnect_f ();
|
||||
CL_BeginServerConnect(server, 0, true, CIM_DEFAULT);
|
||||
CL_BeginServerConnect(server, 0, true, CIM_DEFAULT, CIS_DEFAULT);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1591,9 +1685,7 @@ static void CL_Join_f (void)
|
|||
|
||||
CL_Disconnect_f ();
|
||||
|
||||
Cvar_Set(&spectator, "0");
|
||||
|
||||
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
|
||||
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_JOIN);
|
||||
}
|
||||
|
||||
void CL_Observe_f (void)
|
||||
|
@ -1621,7 +1713,7 @@ void CL_Observe_f (void)
|
|||
|
||||
Cvar_Set(&spectator, "1");
|
||||
|
||||
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
|
||||
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_OBSERVE);
|
||||
}
|
||||
|
||||
#ifdef NQPROT
|
||||
|
@ -1646,7 +1738,7 @@ void CLNQ_Connect_f (void)
|
|||
|
||||
CL_Disconnect_f ();
|
||||
|
||||
CL_BeginServerConnect(server, 26000, true, mode);
|
||||
CL_BeginServerConnect(server, 26000, true, mode, CIS_DEFAULT/*doesn't really do spec/join stuff, but if the server asks for our info later...*/);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2242,9 +2334,7 @@ void CL_Disconnect_f (void)
|
|||
#endif
|
||||
|
||||
CL_Disconnect (NULL);
|
||||
|
||||
connectinfo.trying = false;
|
||||
|
||||
CL_ConnectAbort(NULL);
|
||||
NET_CloseClient();
|
||||
|
||||
(void)CSQC_UnconnectedInit();
|
||||
|
@ -2930,6 +3020,7 @@ void CL_Packet_f (void)
|
|||
int i, l;
|
||||
char *in, *out;
|
||||
netadr_t adr;
|
||||
struct dtlspeercred_s cred = {Cmd_Argv(1)};
|
||||
|
||||
if (Cmd_Argc() != 3)
|
||||
{
|
||||
|
@ -3019,7 +3110,7 @@ void CL_Packet_f (void)
|
|||
|
||||
if (!cls.sockets)
|
||||
NET_InitClient(false);
|
||||
if (!NET_EnsureRoute(cls.sockets, "packet", Cmd_Argv(1), &adr))
|
||||
if (!NET_EnsureRoute(cls.sockets, "packet", &cred, &adr))
|
||||
return;
|
||||
NET_SendPacket (cls.sockets, out-send, send, &adr);
|
||||
|
||||
|
@ -3340,7 +3431,8 @@ void CL_ConnectionlessPacket (void)
|
|||
{
|
||||
if (CL_IsPendingServerAddress(&net_from))
|
||||
{
|
||||
if (!NET_EnsureRoute(cls.sockets, "redir", cls.servername, &adr))
|
||||
struct dtlspeercred_s cred = {cls.servername}; //FIXME
|
||||
if (!NET_EnsureRoute(cls.sockets, "redir", &cred, &adr))
|
||||
Con_Printf (CON_ERROR"Unable to redirect to %s\n", data);
|
||||
else
|
||||
{
|
||||
|
@ -3358,29 +3450,20 @@ void CL_ConnectionlessPacket (void)
|
|||
else if (!strcmp(s, "reject"))
|
||||
{ //generic rejection. stop trying.
|
||||
char *data = MSG_ReadStringLine();
|
||||
Con_Printf ("reject\n%s\n", data);
|
||||
Con_Printf ("reject\n");
|
||||
if (CL_IsPendingServerAddress(&net_from))
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("%s\n", data));
|
||||
connectinfo.trying = false;
|
||||
}
|
||||
CL_ConnectAbort("%s\n", data);
|
||||
return;
|
||||
}
|
||||
else if (!strcmp(s, "badname"))
|
||||
{ //rejected purely because of player name
|
||||
if (CL_IsPendingServerAddress(&net_from))
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("bad player name\n"));
|
||||
connectinfo.trying = false;
|
||||
}
|
||||
CL_ConnectAbort("bad player name\n");
|
||||
}
|
||||
else if (!strcmp(s, "badaccount"))
|
||||
{ //rejected because username or password is wrong
|
||||
if (CL_IsPendingServerAddress(&net_from))
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("invalid username or password\n"));
|
||||
connectinfo.trying = false;
|
||||
}
|
||||
CL_ConnectAbort("invalid username or password\n");
|
||||
}
|
||||
|
||||
Con_Printf ("f%s\n", s);
|
||||
|
@ -3606,27 +3689,29 @@ void CL_ConnectionlessPacket (void)
|
|||
}
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (candtls && net_from.prot == NP_DGRAM && (connectinfo.dtlsupgrade || candtls > 1) && !NET_IsEncrypted(&net_from))
|
||||
if ((candtls && net_enable_dtls.ival) && net_from.prot == NP_DGRAM && (net_enable_dtls.ival>1 || candtls > 1) && !NET_IsEncrypted(&net_from))
|
||||
{
|
||||
//c2s getchallenge
|
||||
//s2c c%u\0DTLS=$candtls
|
||||
//c2s getchallenge <no client details
|
||||
//s2c c%u\0DTLS=$candtls <may leak server details>
|
||||
//<<YOU ARE HERE>>
|
||||
//c2s dtlsconnect %u
|
||||
//s2c dtlsopened
|
||||
//c2s dtlsconnect %u [REALTARGET] <FIXME: target server is plain text, not entirely unlike tls1.2, but still worse than a vpn and could be improved>
|
||||
//s2c dtlsopened <no details at all, other than that the server is now willing to accept dtls handshakes etc>
|
||||
//c2s DTLS(getchallenge)
|
||||
//DTLS(etc)
|
||||
|
||||
//NOTE: the dtlsconnect/dtlsopened parts are redundant and the non-dtls parts are entirely optional (and should be skipped the client requries/knows the server supports dtls)
|
||||
//NOTE: the dtlsconnect/dtlsopened parts are redundant and the non-dtls parts are now entirely optional (and should be skipped if the client requries/knows the server supports dtls)
|
||||
//the challenge response includes server capabilities, so we still need the getchallenge/response part of the handshake despite dtls making the actual challenge part redundant.
|
||||
|
||||
//getchallenge has to be done twice, with the outer one only reporting whether dtls can/should be used.
|
||||
//this means the actual connect packet is already over dtls, which protects the user's userinfo.
|
||||
//FIXME: do rcon via dtls too, but requires tracking pending rcon packets until the handshake completes.
|
||||
|
||||
//server says it can do dtls, but will still need to ask it to allocate extra resources for us.
|
||||
//server says it can do dtls, but will still need to ask it to allocate extra resources for us (I hadn't gotten dtls cookies working properly at that point).
|
||||
|
||||
if (net_enable_dtls.ival>0)
|
||||
{
|
||||
char *pkt;
|
||||
//qwfwd proxy routing
|
||||
//qwfwd proxy routing. it doesn't support it yet, but hey, if its willing to forward the dtls packets its all good.
|
||||
char *at;
|
||||
if ((at = strrchr(cls.servername, '@')))
|
||||
{
|
||||
|
@ -3639,7 +3724,15 @@ void CL_ConnectionlessPacket (void)
|
|||
NET_SendPacket (cls.sockets, strlen(pkt), pkt, &net_from);
|
||||
return;
|
||||
}
|
||||
if (connectinfo.dtlsupgrade == DTLS_REQUIRE && !NET_IsEncrypted(&net_from))
|
||||
else if (candtls >= 3)
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("DTLS is disabled, but server requires it. not connecting\n"));
|
||||
connectinfo.trying = false;
|
||||
Con_Printf("DTLS is disabled, but server requires it. Set ^[/net_enable_dtls 1^] before connecting again.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (net_enable_dtls.ival>=3 && !NET_IsEncrypted(&net_from))
|
||||
{
|
||||
Cvar_Set(&cl_disconnectreason, va("Server does not support/allow dtls. not connecting\n"));
|
||||
connectinfo.trying = false;
|
||||
|
@ -3802,10 +3895,9 @@ void CL_ConnectionlessPacket (void)
|
|||
return;
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
cred.peer.name = cls.servername;
|
||||
cred.peer = connectinfo.peercred;
|
||||
if (NET_DTLS_Create(cls.sockets, &net_from, &cred))
|
||||
{
|
||||
connectinfo.dtlsupgrade = DTLS_ACTIVE;
|
||||
connectinfo.numadr = 1; //fixate on this resolved address.
|
||||
connectinfo.adr[0] = net_from;
|
||||
connectinfo.adr[0].prot = NP_DTLS;
|
||||
|
@ -3813,11 +3905,7 @@ void CL_ConnectionlessPacket (void)
|
|||
connectinfo.time = 0; //send a new challenge NOW.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connectinfo.dtlsupgrade == DTLS_TRY)
|
||||
connectinfo.dtlsupgrade = DTLS_DISABLE;
|
||||
Con_Printf ("unable to establish dtls route\n");
|
||||
}
|
||||
CL_ConnectAbort("Unable to initialise dtls driver. You may need to adjust tls_provider or disable dtls with ^[/net_enable_dtls 0^]\n"); //this is a local issue, and not a result on remote packets.
|
||||
#else
|
||||
Con_Printf ("dtlsopened (unsupported)\n");
|
||||
#endif
|
||||
|
@ -4112,7 +4200,7 @@ void CLNQ_ConnectionlessPacket(void)
|
|||
else
|
||||
{
|
||||
//send a dummy packet.
|
||||
//this makes our local nat think we initialised the conversation, so that we can receive the.
|
||||
//this makes our local firewall think we initialised the conversation, so that we can receive their packets. however this only works if our nat uses the same public port for private ports.
|
||||
Netchan_Transmit(&cls.netchan, 1, "\x01", 2500);
|
||||
}
|
||||
return;
|
||||
|
@ -5422,9 +5510,9 @@ NORETURN void VARGS Host_EndGame (const char *message, ...)
|
|||
SCR_EndLoadingPlaque();
|
||||
|
||||
CL_Disconnect (string);
|
||||
CL_ConnectAbort(NULL);
|
||||
|
||||
SV_UnspawnServer();
|
||||
connectinfo.trying = false;
|
||||
|
||||
Cvar_Set(&cl_shownet, "0");
|
||||
|
||||
|
@ -6215,7 +6303,7 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
|
|||
// "quake2:rtc://broker:port/game"
|
||||
// "qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay
|
||||
//we'll chop off any non-auth prefix, its just so we can handle multiple protocols via a single uri scheme.
|
||||
char *t, *cmd;
|
||||
char *t, *cmd, *args;
|
||||
const char *url;
|
||||
char buffer[8192];
|
||||
const char *schemestart = strchr(fname, ':');
|
||||
|
@ -6261,6 +6349,15 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
|
|||
t[urilen] = 0;
|
||||
url = t+schemelen;
|
||||
|
||||
*buffer = 0;
|
||||
for (args = t+schemelen; *args; args++)
|
||||
{
|
||||
if (*args == '?')
|
||||
{
|
||||
*args++ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (cmd = t+schemelen; *cmd; cmd++)
|
||||
{
|
||||
if (*cmd == '/')
|
||||
|
|
|
@ -492,7 +492,8 @@ typedef struct
|
|||
infobuf_t userinfo[MAX_SPLITS];
|
||||
infosync_t userinfosync;
|
||||
|
||||
char servername[MAX_OSPATH]; // name of server from original connect
|
||||
char serverurl[MAX_OSPATH*4]; // eg qw://foo:27500/join?fp=blah
|
||||
char servername[MAX_OSPATH]; // internal parsing, eg dtls://foo:27500
|
||||
|
||||
struct ftenet_connections_s *sockets;
|
||||
|
||||
|
|
|
@ -774,6 +774,20 @@ void M_Menu_GameOptions_f (void)
|
|||
y+=4;
|
||||
info->hostnameedit = MC_AddEdit (menu, 64, 160, y, "Hostname", name.string);y+=info->hostnameedit->common.height;
|
||||
info->publicgame = MC_AddCombo (menu, 64, 160, y, "Public", publicoptions, bound(0, sv_public.ival+1, 4));y+=8;
|
||||
#if !defined(FTE_TARGET_WEB) && defined(HAVE_DTLS)
|
||||
{
|
||||
extern cvar_t net_enable_dtls;
|
||||
static const char *encoptions[] =
|
||||
{
|
||||
"None",
|
||||
"Accept",
|
||||
"Request",
|
||||
"Require",
|
||||
NULL
|
||||
};
|
||||
MC_AddCvarCombo (menu, 64, 160, y, "DTLS Encryption", &net_enable_dtls, encoptions, NULL);y+=8;
|
||||
}
|
||||
#endif
|
||||
y+=4;
|
||||
|
||||
for (players = 0; players < sizeof(numplayeroptions)/ sizeof(numplayeroptions[0]); players++)
|
||||
|
@ -1110,6 +1124,16 @@ void M_Menu_Network_f (void)
|
|||
"Smooth Demos Only",
|
||||
NULL
|
||||
};
|
||||
#ifdef HAVE_DTLS
|
||||
extern cvar_t net_enable_dtls;
|
||||
static const char *dtlsopts[] = {
|
||||
"Disabled",
|
||||
"Accept",
|
||||
"Request",
|
||||
"Require",
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
static const char *smoothingvalues[] = {"0", "1", "2", NULL};
|
||||
extern cvar_t cl_download_csprogs, cl_download_redirection, requiredownloads, cl_solid_players;
|
||||
extern cvar_t cl_predict_players, cl_lerp_smooth, cl_predict_extrapolate;
|
||||
|
@ -1122,6 +1146,9 @@ void M_Menu_Network_f (void)
|
|||
MB_EDITCVARSLIM("Network FPS", "cl_netfps", "Sets ammount of FPS used to communicate with server (sent and received)"),
|
||||
MB_EDITCVARSLIM("Rate", "rate", "Maximum bytes per second that the server should send to the client"),
|
||||
MB_EDITCVARSLIM("Download Rate", "drate", "Maximum bytes per second that the server should send maps and demos to the client"),
|
||||
#ifdef HAVE_DTLS
|
||||
MB_COMBOCVAR("DTLS Encryption", net_enable_dtls, dtlsopts, NULL, "Use this to avoid snooping. Certificates will be pinned."),
|
||||
#endif
|
||||
MB_SPACING(4),
|
||||
MB_CHECKBOXCVARTIP("Require Download", requiredownloads, 0, "Ignore downloaded content sent to the client and connect immediately"),
|
||||
MB_CHECKBOXCVARTIP("Redirect Download", cl_download_redirection, 0, "Whether the client will ignore download redirection from servers"),
|
||||
|
|
|
@ -5,6 +5,7 @@ clientside master queries and server ping/polls
|
|||
|
||||
#include "quakedef.h"
|
||||
#include "cl_master.h"
|
||||
#include "netinc.h"
|
||||
|
||||
#define FAVOURITESFILE "favourites.txt"
|
||||
|
||||
|
@ -496,7 +497,10 @@ static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
|
|||
{
|
||||
//tcp masters require a route
|
||||
if (NET_AddrIsReliable(na))
|
||||
NET_EnsureRoute(svs.sockets, master->cv.name, master->cv.string, na);
|
||||
{
|
||||
struct dtlspeercred_s cred = {master->cv.string};
|
||||
NET_EnsureRoute(svs.sockets, master->cv.name, &cred, na);
|
||||
}
|
||||
|
||||
//q2+qw masters are given a ping to verify that they're still up
|
||||
switch (master->protocol)
|
||||
|
|
|
@ -6891,9 +6891,9 @@ static int Base64_Decode(char inp)
|
|||
return (inp-'a') + 26;
|
||||
if (inp >= '0' && inp <= '9')
|
||||
return (inp-'0') + 52;
|
||||
if (inp == '+')
|
||||
if (inp == '+' || inp == '-')
|
||||
return 62;
|
||||
if (inp == '/')
|
||||
if (inp == '/' || inp == '_')
|
||||
return 63;
|
||||
//if (inp == '=') //padding char
|
||||
return 0; //invalid
|
||||
|
@ -6929,6 +6929,23 @@ size_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outs
|
|||
*out = 0;
|
||||
return out-start;
|
||||
}
|
||||
size_t Base64_EncodeBlockURI(const qbyte *in, size_t length, char *out, size_t outsize)
|
||||
{ //special uri-safe version (also trims)
|
||||
outsize = Base64_EncodeBlock(in, length, out, outsize);
|
||||
for (length = 0; length < outsize; length++)
|
||||
{
|
||||
if (out[length] == '+')
|
||||
out[length] = '-';
|
||||
else if (out[length] == '/')
|
||||
out[length] = '_';
|
||||
else if (out[length] == '=')
|
||||
{ //truncate it here.
|
||||
out[length] = 0;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
return outsize;
|
||||
}
|
||||
size_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize)
|
||||
{
|
||||
qbyte *start = out;
|
||||
|
|
|
@ -695,7 +695,10 @@ static void CertLog_Write(void)
|
|||
VFS_PUTS(f, certhex);
|
||||
VFS_PRINTF(f, "\" %i\n", l->trusted?true:false);
|
||||
}
|
||||
VFS_CLOSE(f);
|
||||
}
|
||||
else
|
||||
Con_Printf(CON_ERROR"Unable to write %s\n", CERTLOG_FILENAME);
|
||||
}
|
||||
static void CertLog_Purge(void)
|
||||
{
|
||||
|
@ -753,6 +756,7 @@ static void CertLog_Import(const char *filename)
|
|||
}
|
||||
CertLog_Update(addressstring, certdata, certsize, atoi(trusted));
|
||||
}
|
||||
VFS_CLOSE(f);
|
||||
}
|
||||
static void CertLog_UntrustAll_f(void)
|
||||
{
|
||||
|
@ -795,6 +799,8 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
|
|||
extern cvar_t net_enable_dtls;
|
||||
struct certlog_s *l;
|
||||
qboolean trusted = (net_enable_dtls.ival >= 2);
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
char fp[DIGEST_MAXSIZE*2+1];
|
||||
|
||||
if (certlog_curprompt)
|
||||
return false;
|
||||
|
@ -808,11 +814,20 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
|
|||
{ //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);
|
||||
if (certsize)
|
||||
Base64_EncodeBlockURI(digest, CalcHash(&hash_sha1, digest, sizeof(digest), cert, certsize), fp, sizeof(fp));
|
||||
else
|
||||
strcpy(fp, "<No Certificate>");
|
||||
Con_Printf(S_COLOR_GRAY" fp: %s\n", fp);
|
||||
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 (certsize)
|
||||
Base64_EncodeBlockURI(digest, CalcHash(&hash_sha1, digest, sizeof(digest), cert, certsize), fp, sizeof(fp));
|
||||
else
|
||||
strcpy(fp, "<No Certificate>");
|
||||
if (qrenderer)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -820,7 +835,7 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
|
|||
char *text;
|
||||
const char *accepttext;
|
||||
const char *lines[] = {
|
||||
va(localtext("Certificate for %s\n"), hostname),
|
||||
va(localtext("Certificate for %s\n(fp:"S_COLOR_GRAY"%s"S_COLOR_WHITE")\n"), hostname, fp),
|
||||
(certlogproblems&CERTLOG_WRONGHOST)?localtext("^1Certificate does not match host\n"):"",
|
||||
((certlogproblems&(CERTLOG_MISSINGCA|CERTLOG_WRONGHOST))==CERTLOG_MISSINGCA)?localtext("^1Certificate authority is untrusted.\n"):"",
|
||||
(certlogproblems&CERTLOG_EXPIRED)?localtext("^1Expired Certificate\n"):"",
|
||||
|
|
|
@ -151,7 +151,8 @@ neterr_t NET_SendPacket (struct ftenet_connections_s *col, int length, const voi
|
|||
int NET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_t *remote, netadr_t *local, int idx);
|
||||
void NET_PrintAddresses(struct ftenet_connections_s *collection);
|
||||
qboolean NET_AddressSmellsFunny(netadr_t *a);
|
||||
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, char *host, netadr_t *adr);
|
||||
struct dtlspeercred_s;
|
||||
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, const struct dtlspeercred_s *peerinfo, netadr_t *adr);
|
||||
void NET_TerminateRoute(struct ftenet_connections_s *collection, netadr_t *adr);
|
||||
void NET_PrintConnectionsStatus(struct ftenet_connections_s *collection);
|
||||
|
||||
|
@ -191,9 +192,13 @@ qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *na
|
|||
|
||||
enum certprops_e
|
||||
{
|
||||
QCERT_PEERFINGERPRINT
|
||||
QCERT_ISENCRYPTED, //0 or error
|
||||
QCERT_PEERSUBJECT, //null terminated. should be a hash of the primary cert, ignoring chain.
|
||||
QCERT_PEERCERTIFICATE, //should be the primary cert, ignoring chain. no fixed maximum size required, mostly 2k but probably best to allow at leasy 5k.. or 8k.
|
||||
|
||||
QCERT_LOCALCERTIFICATE, //the cert we're using/advertising. may have no context. to tell people what fp to expect.
|
||||
};
|
||||
size_t NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);
|
||||
int NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
struct dtlscred_s;
|
||||
|
@ -207,7 +212,7 @@ extern cvar_t dtls_psk_hint, dtls_psk_user, dtls_psk_key;
|
|||
#ifdef SUPPORT_ICE
|
||||
neterr_t ICE_SendPacket(size_t length, const void *data, netadr_t *to);
|
||||
void ICE_Terminate(netadr_t *to); //if we kicked the client/etc, kill their ICE too.
|
||||
qboolean ICE_IsEncrypted(netadr_t *to);
|
||||
int ICE_GetPeerCertificate(netadr_t *to, enum certprops_e prop, char *out, size_t outsize);
|
||||
void ICE_Init(void);
|
||||
#endif
|
||||
extern cvar_t timeout;
|
||||
|
|
|
@ -2102,11 +2102,14 @@ static qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const ch
|
|||
Con_Printf(S_COLOR_GRAY"[%s]: ice state inactive\n", con->friendlyname);
|
||||
}
|
||||
else if (!strcmp(value, STRINGIFY(ICE_FAILED)))
|
||||
{
|
||||
if (con->state != ICE_FAILED)
|
||||
{
|
||||
con->state = ICE_FAILED;
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: ice state failed\n", con->friendlyname);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(value, STRINGIFY(ICE_CONNECTED)))
|
||||
{
|
||||
con->state = ICE_CONNECTED;
|
||||
|
@ -4805,7 +4808,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
return false;
|
||||
}
|
||||
#ifdef SUPPORT_ICE
|
||||
qboolean ICE_IsEncrypted(netadr_t *to)
|
||||
int ICE_GetPeerCertificate(netadr_t *to, enum certprops_e prop, char *out, size_t outsize)
|
||||
{
|
||||
#ifdef HAVE_DTLS
|
||||
struct icestate_s *con;
|
||||
|
@ -4813,12 +4816,14 @@ qboolean ICE_IsEncrypted(netadr_t *to)
|
|||
{
|
||||
if (NET_CompareAdr(to, &con->qadr))
|
||||
{
|
||||
if (con->dtlsstate)
|
||||
return true;
|
||||
if (con->dtlsstate && con->dtlsfuncs->GetPeerCertificate)
|
||||
return con->dtlsfuncs->GetPeerCertificate(con->dtlsstate, prop, out, outsize);
|
||||
else if (prop==QCERT_ISENCRYPTED && con->dtlsstate)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
void ICE_Terminate(netadr_t *to)
|
||||
{
|
||||
|
@ -5243,14 +5248,16 @@ handleerror:
|
|||
if (cl == -1)
|
||||
{
|
||||
b->error = true;
|
||||
// Con_Printf("Broker closed connection: %s\n", data);
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Broker lost connection: %s\n", b->ice?b->ice->friendlyname:"?", *data?data:"<NO REASON>");
|
||||
}
|
||||
else if (cl >= 0 && cl < b->numclients)
|
||||
{
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Broker lost connection: %s\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?", *data?data:"<NO REASON>");
|
||||
if (b->clients[cl].ice)
|
||||
iceapi.Close(b->clients[cl].ice, false);
|
||||
b->clients[cl].ice = NULL;
|
||||
// Con_Printf("Broker closing connection: %s\n", data);
|
||||
}
|
||||
break;
|
||||
case ICEMSG_NAMEINUSE:
|
||||
|
@ -5273,13 +5280,23 @@ handleerror:
|
|||
Z_ReallocElements((void**)&b->clients, &b->numclients, cl+1, sizeof(b->clients[0]));
|
||||
}
|
||||
if (cl >= 0 && cl < b->numclients)
|
||||
{
|
||||
FTENET_ICE_Establish(b, cl, &b->clients[cl].ice);
|
||||
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: New client spotted...\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?");
|
||||
}
|
||||
else if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: New client spotted, but index is unusable\n", "?");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Con_DPrintf("Server found: %s\n", data);
|
||||
FTENET_ICE_Establish(b, cl, &b->ice);
|
||||
b->serverid = cl;
|
||||
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Relay to server now open\n", b->ice?b->ice->friendlyname:"?");
|
||||
}
|
||||
break;
|
||||
case ICEMSG_OFFER: //we received an offer from a client
|
||||
|
@ -5299,19 +5316,31 @@ handleerror:
|
|||
{
|
||||
if (cl >= 0 && cl < b->numclients && b->clients[cl].ice)
|
||||
{
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Got offer:\n%s\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?", data);
|
||||
iceapi.Set(b->clients[cl].ice, "sdpoffer", data);
|
||||
iceapi.Set(b->clients[cl].ice, "state", STRINGIFY(ICE_CONNECTING));
|
||||
|
||||
FTENET_ICE_SendOffer(b, cl, b->clients[cl].ice, "sdpanswer");
|
||||
break;
|
||||
}
|
||||
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Got bad offer/answer:\n%s\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?", data);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b->ice)
|
||||
{
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Got answer:\n%s\n", b->ice?b->ice->friendlyname:"?", data);
|
||||
iceapi.Set(b->ice, "sdpanswer", data);
|
||||
iceapi.Set(b->ice, "state", STRINGIFY(ICE_CONNECTING));
|
||||
break;
|
||||
}
|
||||
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Got bad offer/answer:\n%s\n", b->ice?b->ice->friendlyname:"?", data);
|
||||
}
|
||||
break;
|
||||
case ICEMSG_CANDIDATE:
|
||||
|
@ -5327,13 +5356,21 @@ handleerror:
|
|||
if (b->generic.islisten)
|
||||
{
|
||||
if (cl >= 0 && cl < b->numclients && b->clients[cl].ice)
|
||||
{
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Got candidate:\n%s\n", b->clients[cl].ice->friendlyname, data);
|
||||
iceapi.Set(b->clients[cl].ice, "sdp", data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b->ice)
|
||||
{
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Got candidate:\n%s\n", b->ice->friendlyname, data);
|
||||
iceapi.Set(b->ice, "sdp", data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
|
||||
|
||||
#define GNUTLS_X509_STUFF \
|
||||
GNUTLS_FUNC(gnutls_certificate_server_set_request,void,(gnutls_session_t session, gnutls_certificate_request_t req)) \
|
||||
GNUTLS_FUNC(gnutls_sec_param_to_pk_bits,unsigned int,(gnutls_pk_algorithm_t algo, gnutls_sec_param_t param)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_init,int,(gnutls_x509_crt_t * cert)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_deinit,void,(gnutls_x509_crt_t cert)) \
|
||||
|
@ -108,6 +109,7 @@
|
|||
GNUTLS_FUNC(gnutls_x509_crt_set_expiration_time,int,(gnutls_x509_crt_t cert, time_t exp_time)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_set_serial,int,(gnutls_x509_crt_t cert, const void *serial, size_t serial_size)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_set_dn,int,(gnutls_x509_crt_t crt, const char *dn, const char **err)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_get_dn3,int,(gnutls_x509_crt_t crt, gnutls_datum_t * dn, unsigned flags)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_set_issuer_dn,int,(gnutls_x509_crt_t crt,const char *dn, const char **err)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_set_key,int,(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key)) \
|
||||
GNUTLS_FUNC(gnutls_x509_crt_export2,int,(gnutls_x509_crt_t cert, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)) \
|
||||
|
@ -123,7 +125,9 @@
|
|||
GNUTLS_FUNC(gnutls_pubkey_init,int,(gnutls_pubkey_t * key)) \
|
||||
GNUTLS_FUNC(gnutls_pubkey_deinit,void,(gnutls_pubkey_t key)) \
|
||||
GNUTLS_FUNC(gnutls_pubkey_import_x509,int,(gnutls_pubkey_t key, gnutls_x509_crt_t crt, unsigned int flags)) \
|
||||
GNUTLS_FUNC(gnutls_pubkey_verify_hash2,int,(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo, unsigned int flags, const gnutls_datum_t * hash, const gnutls_datum_t * signature))
|
||||
GNUTLS_FUNC(gnutls_pubkey_verify_hash2,int,(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo, unsigned int flags, const gnutls_datum_t * hash, const gnutls_datum_t * signature)) \
|
||||
GNUTLS_FUNC(gnutls_certificate_get_ours,const gnutls_datum_t*,(gnutls_session_t session)) \
|
||||
GNUTLS_FUNC(gnutls_certificate_get_crt_raw,int,(gnutls_certificate_credentials_t sc, unsigned idx1, unsigned idx2, gnutls_datum_t * cert))
|
||||
|
||||
|
||||
#define GNUTLS_FUNCS \
|
||||
|
@ -548,7 +552,7 @@ static int QDECL SSL_CheckFingerprint(gnutls_session_t session)
|
|||
if (!memcmp(digest, file->peerdigest, file->peerhashfunc->digestsize))
|
||||
return 0;
|
||||
}
|
||||
Con_DPrintf(CON_ERROR "%s: rejecting certificate\n", file->certname);
|
||||
Con_Printf(CON_ERROR "%s: rejecting certificate\n", file->certname);
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
@ -1084,7 +1088,10 @@ qboolean SSL_InitGlobal(qboolean isserver)
|
|||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
qgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert);
|
||||
// qgnutls_certificate_set_retrieve_function (xcred[isserver], SSL_FindClientCert);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
@ -1151,7 +1158,7 @@ static int GetPSKForServer(gnutls_session_t sess, char **username, gnutls_datum_
|
|||
if (strcmp(svhint, dtls_psk_user.string) || CalcHashInt(&hash_sha1, dtls_psk_key.string, strlen(dtls_psk_key.string)) != 0x3dd348e4)
|
||||
{
|
||||
Con_Printf(CON_WARNING "Possible QEx Server, please set your ^[%s\\type\\%s^] and ^[%s\\type\\%s^] cvars correctly, their current values are likely to crash the server.\n", dtls_psk_user.name,dtls_psk_user.name, dtls_psk_key.name,dtls_psk_key.name);
|
||||
return 0; //don't report anything.
|
||||
return -1; //don't report anything.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1174,7 +1181,10 @@ static int GetPSKForServer(gnutls_session_t sess, char **username, gnutls_datum_
|
|||
static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboolean datagram)
|
||||
{
|
||||
// Initialize TLS session
|
||||
qgnutls_init (&newf->session, GNUTLS_NONBLOCK|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)|(datagram?GNUTLS_DATAGRAM:0));
|
||||
qgnutls_init (&newf->session, ((newf->certcred)?GNUTLS_FORCE_CLIENT_CERT:0)
|
||||
|GNUTLS_NONBLOCK
|
||||
|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)
|
||||
|(datagram?GNUTLS_DATAGRAM:0));
|
||||
|
||||
if (!isserver)
|
||||
qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname));
|
||||
|
@ -1182,6 +1192,7 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
|
|||
|
||||
if (newf->certcred)
|
||||
{
|
||||
qgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_REQUIRE); //we will need to validate their fingerprint.
|
||||
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, newf->certcred);
|
||||
qgnutls_set_default_priority (newf->session);
|
||||
}
|
||||
|
@ -1192,6 +1203,8 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
|
|||
qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]);
|
||||
#else
|
||||
#ifdef HAVE_DTLS
|
||||
qgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_REQUEST); //request a cert, we'll use it for fingerprints.
|
||||
|
||||
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;
|
||||
|
@ -1402,17 +1415,23 @@ static void *GNUDTLS_CreateContext(const dtlscred_t *credinfo, void *cbctx, nete
|
|||
|
||||
// Sys_Printf("DTLS_CreateContext: server=%i\n", isserver);
|
||||
|
||||
if (credinfo && credinfo->local.cert && credinfo->local.key && credinfo->peer.hash)
|
||||
if (credinfo && ((credinfo->local.cert && credinfo->local.key) || credinfo->peer.hash))
|
||||
{
|
||||
qgnutls_certificate_allocate_credentials (&newf->certcred);
|
||||
if (credinfo->local.cert && credinfo->local.key)
|
||||
{
|
||||
gnutls_datum_t pub = {credinfo->local.cert, credinfo->local.certsize},
|
||||
priv = {credinfo->local.key, credinfo->local.keysize};
|
||||
qgnutls_certificate_allocate_credentials (&newf->certcred);
|
||||
qgnutls_certificate_set_x509_key_mem(newf->certcred, &pub, &priv, GNUTLS_X509_FMT_DER);
|
||||
}
|
||||
|
||||
if (credinfo->peer.hash)
|
||||
{
|
||||
newf->peerhashfunc = credinfo->peer.hash;
|
||||
memcpy(newf->peerdigest, credinfo->peer.digest, newf->peerhashfunc->digestsize);
|
||||
qgnutls_certificate_set_verify_function (newf->certcred, SSL_CheckFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
SSL_SetCertificateName(newf, credinfo?credinfo->peer.name:NULL);
|
||||
|
||||
|
@ -1442,6 +1461,9 @@ static neterr_t GNUDTLS_Transmit(void *ctx, const qbyte *data, size_t datasize)
|
|||
return NETERR_DISCONNECTED;
|
||||
}
|
||||
|
||||
if (!datasize)
|
||||
return NETERR_SENT;
|
||||
|
||||
ret = qgnutls_record_send(f->session, data, datasize);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
@ -1462,17 +1484,39 @@ static neterr_t GNUDTLS_Received(void *ctx, sizebuf_t *message)
|
|||
|
||||
if (f->challenging)
|
||||
{
|
||||
int cli_addr = 0xdeadbeef; //FIXME: replace with client's IP:port.
|
||||
size_t asize;
|
||||
safeswitch (net_from.type)
|
||||
{
|
||||
case NA_LOOPBACK: asize = 0; break;
|
||||
case NA_IP: asize = sizeof(net_from.address.ip); break;
|
||||
case NA_IPV6: asize = sizeof(net_from.address.ip6); break;
|
||||
case NA_IPX: asize = sizeof(net_from.address.ipx); break;
|
||||
#ifdef UNIXSOCKETS
|
||||
case NA_UNIX: asize = (qbyte*)&net_from.address.un.path[net_from.address.un.len]-(qbyte*)&net_from.address; break; //unlikely to be spoofed...
|
||||
#endif
|
||||
#ifdef IRCCONNECT
|
||||
//case NA_IRC:
|
||||
#endif
|
||||
#ifdef HAVE_WEBSOCKCL
|
||||
//case NA_WEBSOCKET: //basically web browser.
|
||||
#endif
|
||||
#ifdef SUPPORT_ICE
|
||||
case NA_ICE: asize = strlen(net_from.address.icename); break;
|
||||
#endif
|
||||
case NA_INVALID:
|
||||
safedefault: return NETERR_NOROUTE;
|
||||
}
|
||||
|
||||
memset(&f->prestate, 0, sizeof(f->prestate));
|
||||
ret = qgnutls_dtls_cookie_verify(&cookie_key,
|
||||
&cli_addr, sizeof(cli_addr),
|
||||
&net_from.address, asize,
|
||||
message->data, message->cursize,
|
||||
&f->prestate);
|
||||
|
||||
if (ret == GNUTLS_E_BAD_COOKIE)
|
||||
{
|
||||
qgnutls_dtls_cookie_send(&cookie_key,
|
||||
&cli_addr, sizeof(cli_addr),
|
||||
&net_from.address, asize,
|
||||
&f->prestate,
|
||||
(gnutls_transport_ptr_t)f, DTLS_Push);
|
||||
return NETERR_CLOGGED;
|
||||
|
@ -1599,6 +1643,74 @@ static neterr_t GNUDTLS_Timeouts(void *ctx)
|
|||
return NETERR_SENT;
|
||||
}
|
||||
|
||||
static int GNUDTLS_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)
|
||||
{
|
||||
gnutlsfile_t *f = (gnutlsfile_t *)ctx;
|
||||
if (f && (f->challenging || f->handshaking))
|
||||
return -1; //no cert locked down yet...
|
||||
safeswitch(prop)
|
||||
{
|
||||
case QCERT_ISENCRYPTED:
|
||||
return 0; //well, should be...
|
||||
case QCERT_PEERSUBJECT:
|
||||
{
|
||||
unsigned int certcount;
|
||||
const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(f->session, &certcount);
|
||||
if (certlist)
|
||||
{
|
||||
gnutls_x509_crt_t cert = NULL;
|
||||
gnutls_datum_t dn={NULL};
|
||||
qgnutls_x509_crt_init(&cert);
|
||||
qgnutls_x509_crt_import(cert, certlist, GNUTLS_X509_FMT_DER);
|
||||
qgnutls_x509_crt_get_dn3(cert, &dn, 0);
|
||||
if (dn.size >= outsize)
|
||||
dn.size = -1; //too big...
|
||||
else
|
||||
{
|
||||
memcpy(out, dn.data, dn.size);
|
||||
out[dn.size] = 0;
|
||||
}
|
||||
(*qgnutls_free)(dn.data);
|
||||
qgnutls_x509_crt_deinit(cert);
|
||||
return (int)dn.size;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
case QCERT_PEERCERTIFICATE:
|
||||
{
|
||||
unsigned int certcount;
|
||||
const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(f->session, &certcount);
|
||||
if (certlist && certlist->size <= outsize)
|
||||
{
|
||||
memcpy(out, certlist->data, certlist->size);
|
||||
return certlist->size;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
case QCERT_LOCALCERTIFICATE:
|
||||
{
|
||||
const gnutls_datum_t *cert;
|
||||
gnutls_datum_t d;
|
||||
|
||||
if (f)
|
||||
cert = qgnutls_certificate_get_ours(f->session);
|
||||
else //no actual context? get our default dtls server cert.
|
||||
{
|
||||
qgnutls_certificate_get_crt_raw (xcred[true], 0/*first chain*/, 0/*primary one*/, &d);
|
||||
cert = &d;
|
||||
}
|
||||
if (cert->size <= outsize)
|
||||
{
|
||||
memcpy(out, cert->data, cert->size);
|
||||
return cert->size;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
safedefault:
|
||||
return -1; //dunno what you want from me.
|
||||
}
|
||||
}
|
||||
|
||||
static qboolean GNUDTLS_GenTempCertificate(const char *subject, struct dtlslocalcred_s *qcred)
|
||||
{
|
||||
gnutls_datum_t priv = {NULL}, pub = {NULL};
|
||||
|
@ -1681,7 +1793,7 @@ static const dtlsfuncs_t dtlsfuncs_gnutls =
|
|||
GNUDTLS_Transmit,
|
||||
GNUDTLS_Received,
|
||||
GNUDTLS_Timeouts,
|
||||
NULL,
|
||||
GNUDTLS_GetPeerCertificate,
|
||||
GNUDTLS_GenTempCertificate
|
||||
};
|
||||
static const dtlsfuncs_t *GNUDTLS_InitServer(void)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -155,6 +155,9 @@ cvar_t net_enable_websockets = CVARD("net_enable_websockets", "1", "If enabled,
|
|||
#if defined(HAVE_SERVER)
|
||||
static void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue)
|
||||
{
|
||||
char cert[8192];
|
||||
int certsize;
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
var->ival = var->value;
|
||||
//set up the default value
|
||||
if (!*var->string)
|
||||
|
@ -165,6 +168,12 @@ static void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue)
|
|||
svs.sockets->dtlsfuncs = (var->ival)?DTLS_InitServer():NULL;
|
||||
if (!svs.sockets->dtlsfuncs && var->ival >= 2)
|
||||
Con_Printf("%sUnable to set %s to \"%s\", no DTLS provider available.\n", (var->ival >= 2)?CON_ERROR:CON_WARNING, var->name, var->string);
|
||||
|
||||
certsize = svs.sockets->dtlsfuncs?svs.sockets->dtlsfuncs->GetPeerCertificate(NULL, QCERT_LOCALCERTIFICATE, cert, sizeof(cert)):-1;
|
||||
if (certsize > 0)
|
||||
InfoBuf_SetStarBlobKey(&svs.info, "*fp", cert, Base64_EncodeBlockURI(digest, CalcHash(&hash_sha1, digest, sizeof(digest), cert, certsize), cert, sizeof(cert)));
|
||||
else
|
||||
InfoBuf_SetStarKey(&svs.info, "*fp", "");
|
||||
}
|
||||
}
|
||||
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).");
|
||||
|
@ -1565,6 +1574,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
|
|||
netproto_t prot;
|
||||
netadrtype_t afhint;
|
||||
char *path;
|
||||
char *args;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -1793,6 +1803,10 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
|
|||
}
|
||||
}
|
||||
|
||||
args = strchr(s, '?');
|
||||
if (args)
|
||||
*args=0;
|
||||
|
||||
path = strchr(s, '/');
|
||||
#if !defined(HAVE_WEBSOCKCL) && defined(SUPPORT_ICE)
|
||||
if (path == s && fs_manifest->rtcbroker && *fs_manifest->rtcbroker)
|
||||
|
@ -1829,6 +1843,9 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
|
|||
a[i].prot = prot;
|
||||
}
|
||||
|
||||
if (args)
|
||||
*args='?';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2132,7 +2149,7 @@ qboolean NET_IsEncrypted(netadr_t *adr)
|
|||
if (adr->type == NA_LOOPBACK)
|
||||
return true; //might as well claim it, others can't snoop on it so...
|
||||
#ifdef SUPPORT_ICE
|
||||
if (adr->type == NA_ICE && ICE_IsEncrypted(adr))
|
||||
if (adr->type == NA_ICE && ICE_GetPeerCertificate(adr, QCERT_ISENCRYPTED, NULL, 0)==0)
|
||||
return true;
|
||||
#endif
|
||||
#if defined(FTE_TARGET_WEB)
|
||||
|
@ -3230,6 +3247,7 @@ qboolean NET_DTLS_Decode(ftenet_connections_t *col)
|
|||
switch(peer->funcs->Received(peer->dtlsstate, &net_message))
|
||||
{
|
||||
case NETERR_DISCONNECTED:
|
||||
if (col->islisten)
|
||||
NET_DTLS_DisconnectPeer(col, peer);
|
||||
net_message.cursize = 0;
|
||||
break;
|
||||
|
@ -3254,17 +3272,16 @@ qboolean NET_DTLS_Decode(ftenet_connections_t *col)
|
|||
#endif
|
||||
|
||||
|
||||
size_t NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize)
|
||||
int NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize)
|
||||
{
|
||||
if (!col)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
switch(prop)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case QCERT_PEERFINGERPRINT:
|
||||
#if 0//def HAVE_DTLS
|
||||
#ifdef SUPPORT_ICE
|
||||
if (a->type == NA_ICE)
|
||||
return ICE_GetPeerCertificate(a, prop, out, outsize);
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
if (a->prot == NP_DTLS)
|
||||
{
|
||||
struct dtlspeer_s *peer;
|
||||
|
@ -3277,13 +3294,11 @@ size_t NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *
|
|||
}
|
||||
a->prot = NP_DTLS;
|
||||
}
|
||||
if (peer)
|
||||
return peer->funcs->GetPeerCertificate(peer->dtlsstate, data, length);
|
||||
if (peer && peer->funcs->GetPeerCertificate)
|
||||
return peer->funcs->GetPeerCertificate(peer->dtlsstate, prop, out, outsize);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -8212,7 +8227,7 @@ neterr_t NET_SendPacket (ftenet_connections_t *collection, int length, const voi
|
|||
return NET_SendPacketCol (collection, length, data, to);
|
||||
}
|
||||
|
||||
qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char *host, netadr_t *adr)
|
||||
qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, const struct dtlspeercred_s *peerinfo, netadr_t *adr)
|
||||
{
|
||||
switch(adr->prot)
|
||||
{
|
||||
|
@ -8225,11 +8240,11 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
|
|||
case NP_DTLS:
|
||||
#ifdef HAVE_DTLS
|
||||
adr->prot = NP_DGRAM;
|
||||
if (NET_EnsureRoute(collection, routename, host, adr))
|
||||
if (NET_EnsureRoute(collection, routename, peerinfo, adr))
|
||||
{
|
||||
dtlscred_t cred;
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
cred.peer.name = host;
|
||||
cred.peer = *peerinfo;
|
||||
if (NET_DTLS_Create(collection, adr, &cred))
|
||||
{
|
||||
adr->prot = NP_DTLS;
|
||||
|
@ -8243,14 +8258,14 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
|
|||
case NP_WSS:
|
||||
case NP_TLS:
|
||||
case NP_STREAM:
|
||||
if (!FTENET_AddToCollection(collection, routename, host, adr->type, adr->prot))
|
||||
if (!FTENET_AddToCollection(collection, routename, peerinfo->name, adr->type, adr->prot))
|
||||
return false;
|
||||
Con_Printf("Establishing connection to %s\n", host);
|
||||
Con_Printf("Establishing connection to %s\n", peerinfo->name);
|
||||
break;
|
||||
#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)
|
||||
case NP_RTC_TCP:
|
||||
case NP_RTC_TLS:
|
||||
if (!FTENET_AddToCollection(collection, routename, host, adr->type, adr->prot))
|
||||
if (!FTENET_AddToCollection(collection, routename, peerinfo->name, adr->type, adr->prot))
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
|
@ -9478,7 +9493,10 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
|
|||
{
|
||||
trying = VFSTCP_IsStillConnecting(tf->sock);
|
||||
if (trying < 0)
|
||||
{
|
||||
tf->readaborted = trying;
|
||||
tf->writeaborted = true;
|
||||
}
|
||||
else if (trying)
|
||||
return 0;
|
||||
tf->conpending = false;
|
||||
|
@ -9509,18 +9527,22 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
|
|||
case NET_ENOTCONN:
|
||||
Con_Printf("connection to \"%s\" failed\n", tf->peer);
|
||||
tf->readaborted = VFS_ERROR_NORESPONSE;
|
||||
tf->writeaborted = true;
|
||||
break;
|
||||
case NET_ECONNABORTED:
|
||||
Con_DPrintf("connection to \"%s\" aborted\n", tf->peer);
|
||||
tf->readaborted = VFS_ERROR_NORESPONSE;
|
||||
tf->writeaborted = true;
|
||||
break;
|
||||
case NET_ETIMEDOUT:
|
||||
Con_Printf("connection to \"%s\" timed out\n", tf->peer);
|
||||
tf->readaborted = VFS_ERROR_NORESPONSE;
|
||||
tf->writeaborted = true;
|
||||
break;
|
||||
case NET_ECONNREFUSED:
|
||||
Con_DPrintf("connection to \"%s\" refused\n", tf->peer);
|
||||
tf->readaborted = VFS_ERROR_REFUSED;
|
||||
tf->writeaborted = true;
|
||||
break;
|
||||
case NET_ECONNRESET:
|
||||
Con_DPrintf("connection to \"%s\" reset\n", tf->peer);
|
||||
|
@ -9579,7 +9601,7 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
|
|||
tf->conpending = false;
|
||||
}
|
||||
|
||||
len = send(tf->sock, buffer, bytestoread, 0);
|
||||
len = send(tf->sock, buffer, bytestoread, MSG_NOSIGNAL);
|
||||
if (len == -1 || len == 0)
|
||||
{
|
||||
int reason = VFS_ERROR_UNSPECIFIED;
|
||||
|
@ -9591,9 +9613,13 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
|
|||
return 0; //nothing available yet.
|
||||
case NET_ETIMEDOUT:
|
||||
Con_Printf("connection to \"%s\" timed out\n", tf->peer);
|
||||
tf->writeaborted = true;
|
||||
tf->conpending = false;
|
||||
return VFS_ERROR_NORESPONSE; //don't bother trying to read if we never connected.
|
||||
case NET_ECONNREFUSED: //peer sent a reset instead of accepting a new connection
|
||||
Con_DPrintf("connection to \"%s\" refused\n", tf->peer);
|
||||
tf->writeaborted = true;
|
||||
tf->conpending = false;
|
||||
return VFS_ERROR_REFUSED; //don't bother trying to read if we never connected.
|
||||
case NET_ECONNABORTED: //peer closed its socket
|
||||
Con_Printf("connection to \"%s\" aborted\n", tf->peer);
|
||||
|
@ -9608,6 +9634,8 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
|
|||
case EPIPE:
|
||||
#endif
|
||||
Con_Printf("connection to \"%s\" failed\n", tf->peer);
|
||||
tf->writeaborted = true;
|
||||
tf->conpending = false;
|
||||
return VFS_ERROR_NORESPONSE; //don't bother trying to read if we never connected.
|
||||
default:
|
||||
Sys_Printf("tcp socket error %i (%s)\n", e, tf->peer);
|
||||
|
|
|
@ -223,6 +223,9 @@
|
|||
#ifndef INVALID_SOCKET
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0 //available on linux, no idea about other unixes. don't bug out too much... (d)tls needs this to not get constant SIGPIPE errors
|
||||
#endif
|
||||
|
||||
#ifndef INADDR_LOOPBACK
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
|
@ -372,7 +375,7 @@ typedef struct dtlsfuncs_s
|
|||
neterr_t (*Transmit)(void *ctx, const qbyte *data, size_t datasize);
|
||||
neterr_t (*Received)(void *ctx, sizebuf_t *message); //operates in-place...
|
||||
neterr_t (*Timeouts)(void *ctx);
|
||||
void (*GetPeerCertificate)(void *ctx);
|
||||
int (*GetPeerCertificate)(void *ctx, enum certprops_e prop, char *out, size_t outsize);
|
||||
qboolean (*GenTempCertificate)(const char *subject, struct dtlslocalcred_s *cred);
|
||||
} dtlsfuncs_t;
|
||||
#ifdef HAVE_DTLS
|
||||
|
|
|
@ -6462,6 +6462,18 @@ char *PF_infokey_Internal (int entnum, const char *key)
|
|||
sprintf(ov, "%d", SV_CalcPing (&svs.clients[entnum-1], true));
|
||||
else if (!strcmp(key, "guid"))
|
||||
sprintf(ov, "%s", pl->guid);
|
||||
else if (!strcmp(key, "*cert_sha1"))
|
||||
{
|
||||
char buf[8192];
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
int certsize = NET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERCERTIFICATE, buf, sizeof(buf));
|
||||
if (certsize <= 0)
|
||||
value = "";
|
||||
else
|
||||
Base64_EncodeBlockURI(digest,CalcHash(&hash_sha1, digest, sizeof(digest), buf, certsize), ov, sizeof(ov));
|
||||
}
|
||||
else if (!strcmp(key, "*cert_dn"))
|
||||
NET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERSUBJECT, ov, sizeof(ov));
|
||||
else if (!strcmp(key, "challenge"))
|
||||
sprintf(ov, "%u", pl->challenge);
|
||||
else if (!strcmp(key, "*userid"))
|
||||
|
@ -12567,7 +12579,7 @@ void PR_ResetBuiltins(progstype_t type) //fix all nulls to PF_FIXME and add any
|
|||
}
|
||||
}
|
||||
if (!BuiltinList[i].name)
|
||||
Con_Printf("Failed to map builtin %s to %i specified in fte_bimap.dat\n", com_token, binum);
|
||||
Con_Printf("Failed to map builtin %s to %i specified in fte_bimap.txt\n", com_token, binum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13688,6 +13700,8 @@ void PR_DumpPlatform_f(void)
|
|||
{"INFOKEY_P_CSQCACTIVE","const string", QW|NQ, D("Client has csqc enabled. CSQC ents etc will be sent to this player."), 0, "\"csqcactive\""},
|
||||
{"INFOKEY_P_SVPING", "const string", QW|NQ, NULL, 0, "\"svping\""},
|
||||
{"INFOKEY_P_GUID", "const string", QW|NQ, D("Some hash string which should be reasonably unique to this player's quake installation."), 0, "\"guid\""},
|
||||
{"INFOKEY_P_CERT_SHA1", "const string", QW|NQ, D("Obtains the client's (d)tls certificate's fingerprint."), 0, "\"*cert_sha1\""},
|
||||
{"INFOKEY_P_CERT_DN", "const string", QW|NQ, D("Obtains the client's (d)tls certificate's Distinguished Name string."), 0, "\"*cert_dn\""},
|
||||
{"INFOKEY_P_CHALLENGE", "const string", QW|NQ, NULL, 0, "\"challenge\""},
|
||||
{"INFOKEY_P_USERID", "const string", QW|NQ, NULL, 0, "\"*userid\""},
|
||||
{"INFOKEY_P_DOWNLOADPCT","const string",QW|NQ, D("The client's download percentage for the current file. Additional files are not known."), 0, "\"download\""},
|
||||
|
|
|
@ -1649,7 +1649,7 @@ typedef struct
|
|||
{
|
||||
qboolean hasauthed;
|
||||
qboolean isreverse;
|
||||
char challenge[64];
|
||||
char challenge[64]; //aka nonce
|
||||
} qtvpendingstate_t;
|
||||
int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p);
|
||||
#endif
|
||||
|
|
|
@ -2139,7 +2139,7 @@ static void SV_Status_f (void)
|
|||
int i;
|
||||
client_t *cl;
|
||||
float cpu;
|
||||
char *s, *p;
|
||||
char *s, *p, *sec;
|
||||
char adr[MAX_ADR_SIZE];
|
||||
float pi, po, bi, bo;
|
||||
|
||||
|
@ -2209,6 +2209,10 @@ static void SV_Status_f (void)
|
|||
else
|
||||
s = "private";
|
||||
Con_TPrintf("public : %s\n", s);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
Con_TPrintf("fingerprint : "S_COLOR_GRAY"%s\n", InfoBuf_ValueForKey(&svs.info, "*fp"));
|
||||
#endif
|
||||
switch(svs.gametype)
|
||||
{
|
||||
#ifdef Q3SERVER
|
||||
|
@ -2223,38 +2227,38 @@ static void SV_Status_f (void)
|
|||
#endif
|
||||
|
||||
default:
|
||||
Con_TPrintf("client types :%s", sv_listen_qw.ival?" QW":"");
|
||||
Con_TPrintf("client types :%s", sv_listen_qw.ival?" ^[QW\\tip\\This is "FULLENGINENAME"'s standard protocol.^]":"");
|
||||
#ifdef NQPROT
|
||||
Con_TPrintf("%s%s", (sv_listen_nq.ival==2)?" -NQ":(sv_listen_nq.ival?" NQ":""), sv_listen_dp.ival?" DP":"");
|
||||
Con_TPrintf("%s%s", (sv_listen_nq.ival==2)?" ^[-NQ\\tip\\Allows 'Net'/'Normal' Quake clients to connect, with cookies and extensions that might confuse some old clients^]":(sv_listen_nq.ival?" ^[NQ\\tip\\Vanilla/Normal Quake protocol with maximum compatibility^]":""), sv_listen_dp.ival?" ^[DP\\tip\\Explicitly recognise connection requests from DP clients.^]":"");
|
||||
#endif
|
||||
#ifdef QWOVERQ3
|
||||
if (sv_listen_q3.ival) Con_Printf(" Q3");
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
if (net_enable_dtls.ival >= 3)
|
||||
Con_Printf(" DTLS-only");
|
||||
Con_Printf(" ^[DTLS-only\\tip\\Insecure clients (those without support for DTLS) will be barred from connecting.^]");
|
||||
else if (net_enable_dtls.ival)
|
||||
Con_Printf(" DTLS");
|
||||
Con_Printf(" ^[DTLS\\tip\\Clients may optionally connect via DTLS for added security^]");
|
||||
#endif
|
||||
Con_Printf("\n");
|
||||
#if defined(TCPCONNECT) && !defined(CLIENTONLY)
|
||||
Con_TPrintf("tcp services :");
|
||||
#if defined(HAVE_SSL)
|
||||
if (net_enable_tls.ival)
|
||||
Con_Printf(" TLS");
|
||||
Con_Printf(" ^[TLS\\tip\\Clients are able to connect with Transport Layer Security for the other services, allowing for the use of tls://, wss:// or https:// schemes when their underlaying protocol is enabled.^]");
|
||||
#endif
|
||||
#ifdef HAVE_HTTPSV
|
||||
if (net_enable_http.ival)
|
||||
Con_Printf(" HTTP");
|
||||
Con_Printf(" ^[HTTP\\tip\\This server also acts as a web server. This might be useful to allow hosting demos or stats.^]");
|
||||
if (net_enable_rtcbroker.ival)
|
||||
Con_Printf(" RTC");
|
||||
Con_Printf(" ^[RTC\\tip\\This server is set up to act as a webrtc broker, allowing clients+servers to locate each other instead of playing on this server.^]");
|
||||
if (net_enable_websockets.ival)
|
||||
Con_Printf(" WebSocket");
|
||||
Con_Printf(" ^[WebSocket\\tip\\Clients can use the ws:// or possibly wss:// schemes to connect to this server, potentially from browser ports. This may be laggy.^]");
|
||||
#endif
|
||||
if (net_enable_qizmo.ival)
|
||||
Con_Printf(" Qizmo");
|
||||
Con_Printf(" ^[Qizmo\\tip\\Compatible with the tcp connection feature of qizmo, equivelent to 'connect tcp://ip:port' in FTE.^]");
|
||||
if (net_enable_qtv.ival)
|
||||
Con_Printf(" QTV");
|
||||
Con_Printf(" ^[QTV\\tip\\Allows receiving streamed mvd data from this server.^]");
|
||||
Con_Printf("\n");
|
||||
#endif
|
||||
break;
|
||||
|
@ -2269,7 +2273,7 @@ static void SV_Status_f (void)
|
|||
Con_TPrintf("map uptime : %s\n", ShowTime(sv.world.physicstime));
|
||||
//show the current map+name (but hide name if its too long or would be ugly)
|
||||
if (columns >= 80 && *sv.mapname && strlen(sv.mapname) < 45 && !strchr(sv.mapname, '\n'))
|
||||
Con_TPrintf ("current map : %s (%s)\n", svs.name, sv.mapname);
|
||||
Con_TPrintf ("current map : %s "S_COLOR_GRAY"(%s)\n", svs.name, sv.mapname);
|
||||
else
|
||||
Con_TPrintf ("current map : %s\n", svs.name);
|
||||
|
||||
|
@ -2367,7 +2371,7 @@ static void SV_Status_f (void)
|
|||
#define COLUMNS C_FRAGS C_USERID C_ADDRESS C_NAME C_RATE C_PING C_DROP C_DLP C_DLS C_PROT C_ADDRESS2
|
||||
#define C_FRAGS COLUMN(0, "frags", if (cl->spectator==1)Con_Printf("%-5s ", "spec"); else Con_Printf("%5i ", (int)cl->old_frags))
|
||||
#define C_USERID COLUMN(1, "userid", Con_Printf("%6i ", (int)cl->userid))
|
||||
#define C_ADDRESS COLUMN(2, "address ", Con_Printf("%-16.16s", s))
|
||||
#define C_ADDRESS COLUMN(2, "address ", Con_Printf("%s%-16.16s", sec, s))
|
||||
#define C_NAME COLUMN(3, "name ", Con_Printf("%-16.16s", cl->name))
|
||||
#define C_RATE COLUMN(4, " hz", Con_Printf("%4i ", (cl->frameunion.frames&&cl->netchan.frame_rate>0)?(int)(0.5f+1/cl->netchan.frame_rate):0))
|
||||
#define C_PING COLUMN(5, "ping", Con_Printf("%4i ", (int)SV_CalcPing (cl, false)))
|
||||
|
@ -2437,6 +2441,13 @@ static void SV_Status_f (void)
|
|||
else
|
||||
s = NET_BaseAdrToString (adr, sizeof(adr), &cl->netchan.remote_address);
|
||||
|
||||
if (NET_IsLoopBackAddress(&cl->netchan.remote_address))
|
||||
sec = "";
|
||||
else if (NET_IsEncrypted(&cl->netchan.remote_address))
|
||||
sec = S_COLOR_GREEN;
|
||||
else
|
||||
sec = S_COLOR_RED;
|
||||
|
||||
safeswitch(cl->protocol)
|
||||
{
|
||||
case SCP_BAD: p = "-----"; break;
|
||||
|
@ -2728,7 +2739,9 @@ void SV_User_f (void)
|
|||
client_t *cl;
|
||||
int clnum=-1;
|
||||
unsigned int u;
|
||||
char buf[256];
|
||||
char buf[8192];
|
||||
qbyte digest[DIGEST_MAXSIZE];
|
||||
int certsize;
|
||||
extern cvar_t sv_userinfo_bytelimit, sv_userinfo_keylimit;
|
||||
static const char *pext1names[32] = { "setview", "scale", "lightstylecol", "trans", "view2", "builletens", "accuratetimings", "sounddbl",
|
||||
"fatness", "hlbsp", "bullet", "hullsize", "modeldbl", "entitydbl", "entitydbl2", "floatcoords",
|
||||
|
@ -2813,7 +2826,16 @@ void SV_User_f (void)
|
|||
Con_Printf("\n");
|
||||
}
|
||||
|
||||
Con_Printf("ip: %s\n", NET_AdrToString(buf, sizeof(buf), &cl->netchan.remote_address));
|
||||
Con_Printf("ip: %s%s\n", NET_IsEncrypted(&cl->netchan.remote_address)?S_COLOR_GREEN:S_COLOR_RED, NET_AdrToString(buf, sizeof(buf), &cl->netchan.remote_address));
|
||||
certsize = NET_GetConnectionCertificate(svs.sockets, &cl->netchan.remote_address, QCERT_PEERCERTIFICATE, buf, sizeof(buf));
|
||||
if (certsize <= 0)
|
||||
strcpy(buf, "<no certificate>");
|
||||
else
|
||||
Base64_EncodeBlockURI(digest,CalcHash(&hash_sha1, digest, sizeof(digest), buf, certsize), buf, sizeof(buf));
|
||||
Con_Printf("fp: %s\n", buf);
|
||||
if (NET_GetConnectionCertificate(svs.sockets, &cl->netchan.remote_address, QCERT_PEERSUBJECT, buf, sizeof(buf)) < 0)
|
||||
strcpy(buf, "<unavailable>");
|
||||
Con_Printf("dn: %s\n", buf);
|
||||
switch(cl->realip_status)
|
||||
{
|
||||
case 1:
|
||||
|
|
|
@ -1127,13 +1127,61 @@ CONNECTIONLESS COMMANDS
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
const char *SV_ProtocolNameForClient(client_t *cl)
|
||||
{
|
||||
//okay, that failed...
|
||||
safeswitch (cl->protocol)
|
||||
{
|
||||
case SCP_QUAKEWORLD:
|
||||
if (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
|
||||
return "fteqw"; //changes enough to be significant. assumed to include csqc.
|
||||
return "quakeworld";
|
||||
case SCP_BAD:
|
||||
return "bot";
|
||||
case SCP_QUAKE2:
|
||||
return "quake2";
|
||||
case SCP_QUAKE3:
|
||||
return "quake3";
|
||||
case SCP_NETQUAKE:
|
||||
if (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
|
||||
return "ftenq"; //changes enough to be significant. assumed to include csqc.
|
||||
if (cl->qex)
|
||||
return "qex";
|
||||
if (cl->proquake_angles_hack)
|
||||
return "proquake";
|
||||
return "vanilla";
|
||||
case SCP_BJP3:
|
||||
return "bjp3";
|
||||
case SCP_FITZ666:
|
||||
//this gets messy... probably we should distinguish more
|
||||
if (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
|
||||
return "ftenq"; //changes enough to be significant. assumed to include csqc.
|
||||
if (cl->qex)
|
||||
return "qex";
|
||||
if (cl->netchan.netprim.coordtype != COORDTYPE_FIXED_13_3 || cl->netchan.netprim.anglesize != 1)
|
||||
return "rmq"; //while fte tends not to care, most people consider them separate.
|
||||
return "fitz";
|
||||
case SCP_DARKPLACES6:
|
||||
return "dp6";
|
||||
case SCP_DARKPLACES7:
|
||||
return "dp7";
|
||||
safedefault:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
char *SV_PlayerPublicAddress(client_t *cl)
|
||||
{ //returns a string containing the client's IP address, as permitted for viewing by other clients.
|
||||
//if something useful is actually returned, it should be masked.
|
||||
return "private";
|
||||
//we hide it entirely out of private info caution. most nq clients expect a #.#.#.INVALID type address.
|
||||
//it should be fine to put other stuff here though, we put client version instead, if we know it.
|
||||
const char *ver = InfoBuf_ValueForKey(&cl->userinfo, "*ver");
|
||||
const char *prot = SV_ProtocolNameForClient(cl);
|
||||
|
||||
return va("prot %s %s", prot, ver); //something so they can't confuse ip parsing so easily nor pass them off as some other protocol.
|
||||
}
|
||||
|
||||
#define STATUS_OLDSTYLE 0
|
||||
#define STATUS_OLDSTYLE 0 //equivelent to STATUS_SERVERINFO|STATUS_PLAYERS
|
||||
#define STATUS_SERVERINFO 1
|
||||
#define STATUS_PLAYERS 2
|
||||
#define STATUS_SPECTATORS 4
|
||||
|
@ -1768,14 +1816,16 @@ qboolean SVC_GetChallenge (qboolean respond_dp)
|
|||
#endif
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (net_enable_dtls.ival/* || !*net_enable_dtls.string*/)
|
||||
if (net_enable_dtls.ival>0/* || !*net_enable_dtls.string*/ && svs.sockets->dtlsfuncs)
|
||||
{
|
||||
lng = LittleLong(PROTOCOL_VERSION_DTLSUPGRADE);
|
||||
memcpy(over, &lng, sizeof(lng));
|
||||
over+=sizeof(lng);
|
||||
|
||||
if (net_enable_dtls.ival >= 2)
|
||||
lng = LittleLong(2); //required
|
||||
if (net_enable_dtls.ival >= 3)
|
||||
lng = LittleLong(3); //required
|
||||
else if (net_enable_dtls.ival >= 2)
|
||||
lng = LittleLong(2); //encouraged
|
||||
else
|
||||
lng = LittleLong(1); //supported
|
||||
memcpy(over, &lng, sizeof(lng));
|
||||
|
@ -2606,7 +2656,8 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
|
|||
{
|
||||
if (spectator_password.string[0] &&
|
||||
stricmp(spectator_password.string, "none") &&
|
||||
strcmp(spectator_password.string, s) )
|
||||
strcmp(spectator_password.string, s) &&
|
||||
!NET_IsLoopBackAddress(&info->adr))
|
||||
{ // failed
|
||||
Con_TPrintf ("%s:spectator password failed\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));
|
||||
SV_RejectMessage (info->protocol, "requires a spectator password\n\n");
|
||||
|
@ -2621,7 +2672,8 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
|
|||
s = Info_ValueForKey (info->userinfo, "password");
|
||||
if (password.string[0] &&
|
||||
stricmp(password.string, "none") &&
|
||||
strcmp(password.string, s) )
|
||||
strcmp(password.string, s) &&
|
||||
!NET_IsLoopBackAddress(&info->adr))
|
||||
{
|
||||
Con_TPrintf ("%s:password failed\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));
|
||||
SV_RejectMessage (info->protocol, "server requires a password\n\n");
|
||||
|
@ -3056,10 +3108,15 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
|
|||
//this is the upper bound of the mtu, if its too high we'll get EMSGSIZE and we'll reduce it.
|
||||
//however, if it drops below newcl->netchan.message.maxsize then we'll start to see undeliverable reliables, which means dropped clients.
|
||||
newcl->netchan.mtu = MAX_DATAGRAM; //vanilla qw clients are assumed to have an mtu of this size.
|
||||
if (info->mtu >= 64)
|
||||
if (info->mtu >= 300) //anything smaller is someone being intentionally malicious.
|
||||
{ //if we support application fragmenting, then we can send massive reliables without too much issue
|
||||
newcl->netchan.mtu = info->mtu;
|
||||
newcl->netchan.message.maxsize = sizeof(newcl->netchan.message_buf);
|
||||
|
||||
if (info->adr.type == NA_ICE)
|
||||
newcl->netchan.mtu -= 48+12; //dtls+sctp overhead
|
||||
else if (info->adr.prot == NP_DTLS || info->adr.prot == NP_TLS)
|
||||
newcl->netchan.mtu -= 48; //dtls overhead
|
||||
}
|
||||
else
|
||||
{ //otherwise we can't fragment the packets, and the only way to honour the mtu is to send less data. yay for more round-trips.
|
||||
|
@ -3665,9 +3722,6 @@ void SVC_DirectConnect(int expectedreliablesequence)
|
|||
}
|
||||
msg_badread=false;
|
||||
|
||||
if (!*info.guid)
|
||||
NET_GetConnectionCertificate(svs.sockets, &net_from, QCERT_PEERFINGERPRINT, info.guid, sizeof(info.guid));
|
||||
|
||||
info.adr = net_from;
|
||||
if (MSV_ClusterLogin(&info))
|
||||
return;
|
||||
|
@ -4500,16 +4554,18 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
/*dual-stack client, supporting either DP or QW protocols*/
|
||||
SVC_GetChallenge (false);
|
||||
}
|
||||
#ifdef HAVE_DTLS
|
||||
else 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 && !NET_IsEncrypted(&net_from))
|
||||
{
|
||||
SV_RejectMessage (SCP_NETQUAKE, "This server requires the use of DTLS/TLS/WSS.\n");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{ //legacy pure-nq (though often DP).
|
||||
if (progstype == PROG_H2)
|
||||
{
|
||||
SZ_Clear(&sb);
|
||||
MSG_WriteLong(&sb, 0);
|
||||
MSG_WriteByte(&sb, CCREP_REJECT);
|
||||
MSG_WriteString(&sb, "NQ clients are not supported with hexen2 gamecode\n");
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
SV_RejectMessage (SCP_NETQUAKE, "NQ clients are not supported with hexen2 gamecode\n");
|
||||
return true; //not our version...
|
||||
}
|
||||
if (NET_WasSpecialPacket(svs.sockets))
|
||||
|
|
|
@ -21,6 +21,7 @@ struct fte_certctx_s
|
|||
{
|
||||
const char *peername;
|
||||
qboolean dtls;
|
||||
qboolean failure;
|
||||
|
||||
hashfunc_t *hash; //if set peer's cert MUST match the specified digest (with this hash function)
|
||||
qbyte digest[DIGEST_MAXSIZE];
|
||||
|
@ -44,6 +45,8 @@ static int OSSL_Bio_FWrite(BIO *h, const char *buf, int size)
|
|||
BIO_set_retry_write(h);
|
||||
r = -1; //paranoia
|
||||
}
|
||||
// else if (r < 0)
|
||||
// Con_DPrintf("ossl Error: %i\n", r);
|
||||
return r;
|
||||
}
|
||||
static int OSSL_Bio_FRead(BIO *h, char *buf, int size)
|
||||
|
@ -242,7 +245,12 @@ static int OSSL_Verify_Peer(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
uctx->hash->terminate(digest, hctx);
|
||||
|
||||
//return 1 for success
|
||||
return !memcmp(digest, uctx->digest, uctx->hash->digestsize);
|
||||
if (memcmp(digest, uctx->digest, uctx->hash->digestsize))
|
||||
{
|
||||
uctx->failure = true;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(preverify_ok == 0)
|
||||
|
@ -914,7 +922,22 @@ static void OSSL_DestroyContext(void *ctx)
|
|||
static neterr_t OSSL_Transmit(void *ctx, const qbyte *data, size_t datasize)
|
||||
{ //we're sending data
|
||||
ossldtls_t *o = (ossldtls_t*)ctx;
|
||||
int r = BIO_write(o->bio, data, datasize);
|
||||
int r;
|
||||
if (datasize == 0) //liveness test. return clogged while handshaking and sent when finished. openssl doesn't like 0-byte writes.
|
||||
{
|
||||
if (o->cert.failure)
|
||||
return NETERR_DISCONNECTED; //actual security happens elsewhere.
|
||||
else if (SSL_is_init_finished(o->ssl))
|
||||
return NETERR_SENT; //go on, send stuff.
|
||||
else if (BIO_should_retry(o->bio))
|
||||
{
|
||||
if (BIO_do_handshake(o->bio) == 1)
|
||||
return NETERR_SENT;
|
||||
}
|
||||
return NETERR_CLOGGED; //can't send yet.
|
||||
}
|
||||
else
|
||||
r = BIO_write(o->bio, data, datasize);
|
||||
if (r <= 0)
|
||||
{
|
||||
if (BIO_should_io_special(o->bio))
|
||||
|
@ -941,7 +964,7 @@ static neterr_t OSSL_Received(void *ctx, sizebuf_t *message)
|
|||
int r;
|
||||
|
||||
if (!message)
|
||||
r = 0;
|
||||
r = BIO_read(o->bio, NULL, 0);
|
||||
else
|
||||
{
|
||||
o->pending = message->data;
|
||||
|
@ -981,7 +1004,52 @@ static neterr_t OSSL_Timeouts(void *ctx)
|
|||
return OSSL_Received(ctx, NULL);
|
||||
}
|
||||
|
||||
qboolean OSSL_GenTempCertificate(const char *subject, struct dtlslocalcred_s *cred)
|
||||
static int OSSL_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)
|
||||
{
|
||||
ossldtls_t *o = (ossldtls_t*)ctx;
|
||||
X509 *cert;
|
||||
|
||||
safeswitch(prop)
|
||||
{
|
||||
case QCERT_ISENCRYPTED:
|
||||
return 0; //not an error.
|
||||
case QCERT_PEERCERTIFICATE:
|
||||
cert = SSL_get_peer_certificate(o->ssl);
|
||||
goto returncert;
|
||||
case QCERT_LOCALCERTIFICATE:
|
||||
cert = vhost.servercert;
|
||||
goto returncert;
|
||||
returncert:
|
||||
if (cert)
|
||||
{
|
||||
size_t blobsize = i2d_X509(cert, NULL);
|
||||
qbyte *end = out;
|
||||
if (blobsize <= outsize)
|
||||
i2d_X509(cert, &end);
|
||||
return end-(qbyte*)out;
|
||||
}
|
||||
return -1;
|
||||
case QCERT_PEERSUBJECT:
|
||||
{
|
||||
int r;
|
||||
X509 *cert = SSL_get_peer_certificate(o->ssl);
|
||||
if (cert)
|
||||
{
|
||||
X509_NAME *iname = X509_get_subject_name(cert);
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
X509_NAME_print_ex(bio, iname, 0, XN_FLAG_RFC2253);
|
||||
r = BIO_read(bio, out, outsize-1);
|
||||
out[(r<0)?0:r] = 0;
|
||||
BIO_free(bio);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
safedefault:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
static qboolean OSSL_GenTempCertificate(const char *subject, struct dtlslocalcred_s *cred)
|
||||
{
|
||||
EVP_PKEY*pkey = EVP_PKEY_new();
|
||||
RSA *rsa = RSA_new();
|
||||
|
@ -1033,7 +1101,7 @@ static dtlsfuncs_t ossl_dtlsfuncs =
|
|||
OSSL_Transmit,
|
||||
OSSL_Received,
|
||||
OSSL_Timeouts,
|
||||
NULL,
|
||||
OSSL_GetPeerCertificate,
|
||||
OSSL_GenTempCertificate,
|
||||
};
|
||||
static const dtlsfuncs_t *OSSL_InitClient(void)
|
||||
|
@ -1172,6 +1240,8 @@ static void OSSL_PluginShutdown(void)
|
|||
EVP_PKEY_free(vhost.privatekey);
|
||||
BIO_meth_free(biometh_vfs);
|
||||
BIO_meth_free(biometh_dtls);
|
||||
|
||||
memset(&vhost, 0, sizeof(vhost));
|
||||
}
|
||||
static qboolean OSSL_PluginMayShutdown(void)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue