]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
bump a few limits in DP_SMALLMEMORY (cachedpics was too low for even
[xonotic/darkplaces.git] / netconn.c
index a66e2f323da0925e79cbe75db4885b3f51dd4075..2ddf6a380dd7f317cfceb69893da813031e41e63 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -84,7 +84,7 @@ static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist
 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
 static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
 static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
-static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode"};
+static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
 extern cvar_t rcon_secure;
@@ -1579,7 +1579,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        int i, j;
                        for (j = 0;j < MAX_RCONS;j++)
                        {
-                               i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
+                               i = (cls.rcon_ringpos + j) % MAX_RCONS;
                                if(cls.rcon_commands[i][0])
                                        if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
                                                break;
@@ -1588,9 +1588,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        {
                                char buf[1500];
                                char argbuf[1500];
+                               const char *e;
+                               int n;
                                dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
                                memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
-                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
+
+                               e = strchr(rcon_password.string, ' ');
+                               n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
+
+                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
                                {
                                        buf[45] = ' ';
                                        strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
@@ -2382,8 +2388,9 @@ qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char
 
        // validate the challenge
        for (i = 0;i < MAX_CHALLENGES;i++)
-               if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
-                       break;
+               if(challenge[i].time > 0)
+                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
+                               break;
        // if the challenge is not recognized, drop the packet
        if (i == MAX_CHALLENGES)
                return false;
@@ -2395,13 +2402,7 @@ qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char
                return false;
 
        // unmark challenge to prevent replay attacks
-       // FIXME as there is currently no unmark facility, let's invalidate it
-       // as much as possible
-       challenge[i].string[0] = '\\'; // not allowed in infostrings, so connects cannot match
-       NetConn_BuildChallengeString(challenge[i].string + 1, sizeof(challenge[i].string) - 1);
        challenge[i].time = 0;
-       LHNETADDRESS_FromString(&challenge[i].address, "local:42", 42); // no rcon will come from there for sure
-       challenge[i].address = *peeraddress;
 
        return true;
 }
@@ -2414,15 +2415,51 @@ qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, c
 /// returns a string describing the user level, or NULL for auth failure
 const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
 {
-       const char *text;
+       const char *text, *userpass_start, *userpass_end, *userpass_startpass;
+       char buf[MAX_INPUTLINE];
        qboolean hasquotes;
+       qboolean restricted = false;
+       qboolean have_usernames = false;
+
+       userpass_start = rcon_password.string;
+       while((userpass_end = strchr(userpass_start, ' ')))
+       {
+               have_usernames = true;
+               strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
+               if(buf[0])
+                       if(comparator(peeraddress, buf, password, cs, cslen))
+                               goto allow;
+               userpass_start = userpass_end + 1;
+       }
+       if(userpass_start[0])
+       {
+               userpass_end = userpass_start + strlen(userpass_start);
+               if(comparator(peeraddress, userpass_start, password, cs, cslen))
+                       goto allow;
+       }
 
-       if(comparator(peeraddress, rcon_password.string, password, cs, cslen))
-               return "rcon";
+       restricted = true;
+       have_usernames = false;
+       userpass_start = rcon_restricted_password.string;
+       while((userpass_end = strchr(userpass_start, ' ')))
+       {
+               have_usernames = true;
+               strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
+               if(buf[0])
+                       if(comparator(peeraddress, buf, password, cs, cslen))
+                               goto check;
+               userpass_start = userpass_end + 1;
+       }
+       if(userpass_start[0])
+       {
+               userpass_end = userpass_start + strlen(userpass_start);
+               if(comparator(peeraddress, userpass_start, password, cs, cslen))
+                       goto check;
+       }
        
-       if(!comparator(peeraddress, rcon_restricted_password.string, password, cs, cslen))
-               return NULL;
+       return NULL; // DENIED
 
+check:
        for(text = s; text != endpos; ++text)
                if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
                        return NULL; // block possible exploits against the parser/alias expansion
@@ -2465,6 +2502,13 @@ match:
                s += l + 1;
        }
 
+allow:
+       userpass_startpass = strchr(userpass_start, ':');
+       if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
+               return va("%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
+       else
+               return va("%srcon", restricted ? "restricted " : "");
+
        return "restricted rcon";
 }
 
@@ -2551,8 +2595,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
                        {
-                               if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
-                                       break;
+                               if(challenge[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
+                                               break;
                                if (besttime > challenge[i].time)
                                        besttime = challenge[best = i].time;
                        }
@@ -2578,8 +2623,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                return true;
                        // validate the challenge
                        for (i = 0;i < MAX_CHALLENGES;i++)
-                               if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
-                                       break;
+                               if(challenge[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
+                                               break;
                        // if the challenge is not recognized, drop the packet
                        if (i == MAX_CHALLENGES)
                                return true;
@@ -2725,7 +2771,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
 
-                       if(rcon_secure.integer)
+                       if(rcon_secure.integer > 0)
                                return true;
 
                        for (i = 0;!ISWHITESPACE(*s);s++)