// Returns 0 on success, -1 on failure // If a failure occurs the password will contain a description of the error int Keygen(char const *_sysInfoData, int _sysInfoLen, char const *_userInfoFilename, char const **_password) { int userInfoLen; char *userInfoData = ReadFileToBuf(_userInfoFilename, &userInfoLen); if (!userInfoData) { *_password = "******"; return -1; } char const *validationResult = ValidateSysInfo(_sysInfoData, _sysInfoLen); if (validationResult) { *_password = validationResult; return -1; } validationResult = ValidateUserInfo(userInfoData, userInfoLen); if (validationResult) { *_password = validationResult; return -1; } unsigned int sysKey = GenerateKey(_sysInfoData, _sysInfoLen); unsigned int userKey = GenerateKey(userInfoData, userInfoLen); MakePasswordFromKey(userKey, sysKey, _password); delete [] userInfoData; return 0; }
/* * Sv_UserInfoChanged * * Enforces safe user_info data before passing onto game module. */ void Sv_UserInfoChanged(sv_client_t *cl) { char *val; size_t i; if (*cl->user_info == '\0') { // catch empty user_info Com_Print("Empty user_info from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Bad user info"); return; } if (strchr(cl->user_info, '\xFF')) { // catch end of message exploit Com_Print("Illegal user_info contained xFF from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Bad user info"); return; } if (!ValidateUserInfo(cl->user_info)) { // catch otherwise invalid user_info Com_Print("Invalid user_info from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Bad user info"); return; } val = GetUserInfo(cl->user_info, "skin"); if (strstr(val, "..")) // catch malformed skins SetUserInfo(cl->user_info, "skin", "enforcer/qforcer"); // call game code to allow overrides svs.game->ClientUserInfoChanged(cl->edict, cl->user_info); // name for C code, mask off high bit strncpy(cl->name, GetUserInfo(cl->user_info, "name"), sizeof(cl->name) - 1); for (i = 0; i < sizeof(cl->name); i++) { cl->name[i] &= 127; } // rate command val = GetUserInfo(cl->user_info, "rate"); if (*val != '\0') { cl->rate = atoi(val); if (cl->rate > CLIENT_RATE_MAX) cl->rate = CLIENT_RATE_MAX; else if (cl->rate < CLIENT_RATE_MIN) cl->rate = CLIENT_RATE_MIN; } // limit the print messages the client receives val = GetUserInfo(cl->user_info, "message_level"); if (*val != '\0') { cl->message_level = atoi(val); } // start/stop sending view angles for demo recording val = GetUserInfo(cl->user_info, "recording"); cl->recording = atoi(val) == 1; }
/* * @brief Enforces safe user_info data before passing onto game module. */ void Sv_UserInfoChanged(sv_client_t *cl) { char *val; size_t i; if (*cl->user_info == '\0') { // catch empty user_info Com_Print("Empty user_info from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Bad user info"); return; } if (strchr(cl->user_info, '\xFF')) { // catch end of message exploit Com_Print("Illegal user_info contained xFF from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Bad user info"); return; } if (!ValidateUserInfo(cl->user_info)) { // catch otherwise invalid user_info Com_Print("Invalid user_info from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Bad user info"); return; } // call game code to allow overrides svs.game->ClientUserInfoChanged(cl->entity, cl->user_info); // name for C code, mask off high bit g_strlcpy(cl->name, GetUserInfo(cl->user_info, "name"), sizeof(cl->name)); for (i = 0; i < sizeof(cl->name); i++) { cl->name[i] &= 127; } // rate command val = GetUserInfo(cl->user_info, "rate"); if (*val != '\0') { cl->rate = strtoul(val, NULL, 10); if (cl->rate > 0 && cl->rate < CLIENT_RATE_MIN) { cl->rate = CLIENT_RATE_MIN; } } // limit the print messages the client receives val = GetUserInfo(cl->user_info, "message_level"); if (*val != '\0') { cl->message_level = strtoul(val, NULL, 10); } }
ECode CURI::ParseAuthority( /* [in] */ Boolean forceServer) { if (mAuthority.IsNull()) { return NOERROR; } String tempUserInfo; String temp = mAuthority; Int32 index = temp.IndexOf('@'); Int32 hostIndex = 0; if (index != -1) { // remove user info tempUserInfo = temp.Substring(0, index); FAIL_RETURN(ValidateUserInfo(mAuthority, tempUserInfo, 0)); temp = temp.Substring(index + 1); // host[:port] is left hostIndex = index + 1; } index = temp.LastIndexOf(':'); Int32 endIndex = temp.IndexOf(']'); String tempHost; Int32 tempPort = -1; if (index != -1 && endIndex < index) { // determine port and host tempHost = temp.Substring(0, index); Char32 firstPortChar = temp.GetChar(index + 1); if (firstPortChar >= '0' && firstPortChar <= '9') { // allow only digits, no signs ECode ec = StringUtils::Parse(temp.Substring(index + 1), &tempPort); if (ec == (ECode)E_NUMBER_FORMAT_EXCEPTION) { if (forceServer) { ALOGE("%s Invalid port number %d", mAuthority.string(), hostIndex + index + 1); return E_URI_SYNTAX_EXCEPTION; } return NOERROR; } } else { if (forceServer) { ALOGE("%s Invalid port number %d", mAuthority.string(), hostIndex + index + 1); return E_URI_SYNTAX_EXCEPTION; } return NOERROR; } } else { tempHost = temp; } if (tempHost.IsEmpty()) { if (forceServer) { return E_URI_SYNTAX_EXCEPTION; } return NOERROR; } Boolean isValid = FALSE; FAIL_RETURN(IsValidHost(forceServer, tempHost, &isValid)); if (!isValid) { return NOERROR; } // this is a server based uri, // fill in the userInfo, host and port fields mUserInfo = tempUserInfo; mHost = tempHost; mPort = tempPort; mServerAuthority = TRUE; return NOERROR; }
/* * G_ClientUserInfoChanged */ void G_ClientUserInfoChanged(g_edict_t *ent, const char *user_info) { const char *s; char *c; char name[MAX_NET_NAME]; int player_num, i; boolean_t color; g_client_t *cl; // check for malformed or illegal info strings if (!ValidateUserInfo(user_info)) { user_info = "\\name\\newbie\\skin\\qforcer/enforcer"; } cl = ent->client; // set name, use a temp buffer to compute length and crutch up bad names s = GetUserInfo(user_info, "name"); strncpy(name, s, sizeof(name) - 1); name[sizeof(name) - 1] = 0; color = false; c = name; i = 0; // trim to 15 printable chars while (i < 15) { if (!*c) break; if (IS_COLOR(c)) { color = true; c += 2; continue; } c++; i++; } name[c - name] = 0; if (!i) // name had nothing printable strcpy(name, "newbie"); if (color) // reset to white strcat(name, "^7"); if (strncmp(cl->persistent.net_name, name, sizeof(cl->persistent.net_name))) { if (*cl->persistent.net_name != '\0') gi.BroadcastPrint(PRINT_MEDIUM, "%s changed name to %s\n", cl->persistent.net_name, name); strncpy(cl->persistent.net_name, name, sizeof(cl->persistent.net_name) - 1); cl->persistent.net_name[sizeof(cl->persistent.net_name) - 1] = 0; } #ifdef HAVE_MYSQL if(mysql != NULL) { // escape name for safe db insertions StripColor(cl->persistent.net_name, name); mysql_real_escape_string(mysql, name, cl->persistent.sql_name, sizeof(cl->persistent.sql_name)); } #endif // set skin if ((g_level.teams || g_level.ctf) && cl->persistent.team) // players must use team_skin to change s = cl->persistent.team->skin; else s = GetUserInfo(user_info, "skin"); if (*s != '\0') // something valid-ish was provided strncpy(cl->persistent.skin, s, sizeof(cl->persistent.skin) - 1); else { strcpy(cl->persistent.skin, "qforcer/enforcer"); cl->persistent.skin[sizeof(cl->persistent.skin) - 1] = 0; } // set color s = GetUserInfo(user_info, "color"); cl->persistent.color = ColorByName(s, 243); player_num = ent - g_game.edicts - 1; // combine name and skin into a config_string gi.ConfigString(CS_CLIENT_INFO + player_num, va("%s\\%s", cl->persistent.net_name, cl->persistent.skin)); // save off the user_info in case we want to check something later strncpy(ent->client->persistent.user_info, user_info, sizeof(ent->client->persistent.user_info) - 1); }
/* * @brief A connection request that did not come from the master. */ static void Svc_Connect(void) { char user_info[MAX_USER_INFO_STRING]; sv_client_t *cl, *client; int32_t i; Com_Debug("Svc_Connect()\n"); net_addr_t *addr = &net_from; const int32_t version = strtol(Cmd_Argv(1), NULL, 0); // resolve protocol if (version != PROTOCOL) { Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nServer is version %d.\n", PROTOCOL); return; } const uint8_t qport = strtoul(Cmd_Argv(2), NULL, 0); const uint32_t challenge = strtoul(Cmd_Argv(3), NULL, 0); // copy user_info, leave room for ip stuffing g_strlcpy(user_info, Cmd_Argv(4), sizeof(user_info) - 25); if (*user_info == '\0') { // catch empty user_info Com_Print("Empty user_info from %s\n", Net_NetaddrToString(addr)); Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n"); return; } if (strchr(user_info, '\xFF')) { // catch end of message in string exploit Com_Print("Illegal user_info contained xFF from %s\n", Net_NetaddrToString(addr)); Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n"); return; } if (strlen(GetUserInfo(user_info, "ip"))) { // catch spoofed ips Com_Print("Illegal user_info contained ip from %s\n", Net_NetaddrToString(addr)); Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n"); return; } if (!ValidateUserInfo(user_info)) { // catch otherwise invalid user_info Com_Print("Invalid user_info from %s\n", Net_NetaddrToString(addr)); Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n"); return; } // force the ip so the game can filter on it SetUserInfo(user_info, "ip", Net_NetaddrToString(addr)); // enforce a valid challenge to avoid denial of service attack for (i = 0; i < MAX_CHALLENGES; i++) { if (Net_CompareClientNetaddr(addr, &svs.challenges[i].addr)) { if (challenge == svs.challenges[i].challenge) { svs.challenges[i].challenge = 0; break; // good } Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nBad challenge\n"); return; } } if (i == MAX_CHALLENGES) { Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nNo challenge for address\n"); return; } // resolve the client slot client = NULL; // first check for an ungraceful reconnect (client crashed, perhaps) for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) { const net_chan_t *ch = &cl->net_chan; if (cl->state == SV_CLIENT_FREE) // not in use, not interested continue; // the base address and either the qport or real port must match if (Net_CompareClientNetaddr(addr, &ch->remote_address)) { if (addr->port == ch->remote_address.port || qport == ch->qport) { client = cl; break; } } } // otherwise, treat as a fresh connect to a new slot if (!client) { for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) { if (cl->state == SV_CLIENT_FREE && !cl->edict->ai) { // we have a free one client = cl; break; } } } // no soup for you, next!! if (!client) { Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nServer is full\n"); Com_Debug("Rejected a connection\n"); return; } // give the game a chance to reject this connection or modify the user_info if (!(svs.game->ClientConnect(client->edict, user_info))) { const char *rejmsg = GetUserInfo(user_info, "rejmsg"); if (strlen(rejmsg)) { Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\n%s\nConnection refused\n", rejmsg); } else { Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n"); } Com_Debug("Game rejected a connection\n"); return; } // parse some info from the info strings g_strlcpy(client->user_info, user_info, sizeof(client->user_info)); Sv_UserInfoChanged(client); // send the connect packet to the client Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "client_connect %s", sv_download_url->string); Netchan_Setup(NS_UDP_SERVER, &client->net_chan, addr, qport); Mem_InitBuffer(&client->datagram.buffer, client->datagram.data, sizeof(client->datagram.data)); client->datagram.buffer.allow_overflow = true; client->last_message = svs.real_time; // don't timeout client->state = SV_CLIENT_CONNECTED; }