Stop help talking about units' ability to attack relative to non-native
[freeciv.git] / server / auth.c
blob23c5065eeafcde3d57786a841919f1e0a523fc53
1 /**********************************************************************
2 Freeciv - Copyright (C) 2005 - M.C. Kaufman
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 /* utility */
23 #include "fcintl.h"
24 #include "log.h"
25 #include "md5.h"
26 #include "registry.h"
27 #include "shared.h"
28 #include "support.h"
30 /* common */
31 #include "connection.h"
32 #include "packets.h"
34 /* common/scripting */
35 #include "luascript_types.h"
37 /* server */
38 #include "connecthand.h"
39 #include "fcdb.h"
40 #include "notify.h"
41 #include "sernet.h"
42 #include "srv_main.h"
44 /* server/scripting */
45 #include "script_fcdb.h"
47 #include "auth.h"
49 #define GUEST_NAME "guest"
51 #define MIN_PASSWORD_LEN 6 /* minimum length of password */
52 #define MIN_PASSWORD_CAPS 0 /* minimum number of capital letters required */
53 #define MIN_PASSWORD_NUMS 0 /* minimum number of numbers required */
55 #define MAX_AUTH_TRIES 3
56 #define MAX_WAIT_TIME 300 /* max time we'll wait on a password */
58 /* after each wrong guess for a password, the server waits this
59 * many seconds to reply to the client */
60 static const int auth_fail_wait[] = { 1, 1, 2, 3 };
62 static bool auth_check_password(struct connection *pconn,
63 const char *password, int len);
64 static bool is_guest_name(const char *name);
65 static void get_unique_guest_name(char *name);
66 static bool is_good_password(const char *password, char *msg);
68 /****************************************************************************
69 Handle authentication of a user; called by handle_login_request() if
70 authentication is enabled.
72 If the connection is rejected right away, return FALSE, otherwise this
73 function will return TRUE.
74 ****************************************************************************/
75 bool auth_user(struct connection *pconn, char *username)
77 char tmpname[MAX_LEN_NAME] = "\0";
79 /* assign the client a unique guest name/reject if guests aren't allowed */
80 if (is_guest_name(username)) {
81 if (srvarg.auth_allow_guests) {
83 sz_strlcpy(tmpname, username);
84 get_unique_guest_name(username);
86 if (strncmp(tmpname, username, MAX_LEN_NAME) != 0) {
87 notify_conn(pconn->self, NULL, E_CONNECTION, ftc_warning,
88 _("Warning: the guest name '%s' has been "
89 "taken, renaming to user '%s'."), tmpname, username);
91 sz_strlcpy(pconn->username, username);
92 establish_new_connection(pconn);
93 } else {
94 reject_new_connection(_("Guests are not allowed on this server. "
95 "Sorry."), pconn);
96 log_normal(_("%s was rejected: Guests not allowed."), username);
97 return FALSE;
99 } else {
100 /* we are not a guest, we need an extra check as to whether a
101 * connection can be established: the client must authenticate itself */
102 char buffer[MAX_LEN_MSG];
104 sz_strlcpy(pconn->username, username);
106 switch(script_fcdb_call("user_load", 1, API_TYPE_CONNECTION, pconn)) {
107 case FCDB_ERROR:
108 if (srvarg.auth_allow_guests) {
109 sz_strlcpy(tmpname, pconn->username);
110 get_unique_guest_name(tmpname); /* don't pass pconn->username here */
111 sz_strlcpy(pconn->username, tmpname);
113 log_error("Error reading database; connection -> guest");
114 notify_conn(pconn->self, NULL, E_CONNECTION, ftc_warning,
115 _("There was an error reading the user "
116 "database, logging in as guest connection '%s'."),
117 pconn->username);
118 establish_new_connection(pconn);
119 } else {
120 reject_new_connection(_("There was an error reading the user database "
121 "and guest logins are not allowed. Sorry"),
122 pconn);
123 log_normal(_("%s was rejected: Database error and guests not "
124 "allowed."), pconn->username);
125 return FALSE;
127 break;
128 case FCDB_SUCCESS_TRUE:
129 /* we found a user */
130 fc_snprintf(buffer, sizeof(buffer), _("Enter password for %s:"),
131 pconn->username);
132 dsend_packet_authentication_req(pconn, AUTH_LOGIN_FIRST, buffer);
133 pconn->server.auth_settime = time(NULL);
134 pconn->server.status = AS_REQUESTING_OLD_PASS;
135 break;
136 case FCDB_SUCCESS_FALSE:
137 /* we couldn't find the user, he is new */
138 if (srvarg.auth_allow_newusers) {
139 sz_strlcpy(buffer, _("Enter a new password (and remember it)."));
140 dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer);
141 pconn->server.auth_settime = time(NULL);
142 pconn->server.status = AS_REQUESTING_NEW_PASS;
143 } else {
144 reject_new_connection(_("This server allows only preregistered "
145 "users. Sorry."), pconn);
146 log_normal(_("%s was rejected: Only preregistered users allowed."),
147 pconn->username);
149 return FALSE;
151 break;
152 default:
153 fc_assert(FALSE);
154 break;
156 return TRUE;
159 return TRUE;
162 /****************************************************************************
163 Receives a password from a client and verifies it.
164 ****************************************************************************/
165 bool auth_handle_reply(struct connection *pconn, char *password)
167 char msg[MAX_LEN_MSG];
169 if (pconn->server.status == AS_REQUESTING_NEW_PASS) {
171 /* check if the new password is acceptable */
172 if (!is_good_password(password, msg)) {
173 if (pconn->server.auth_tries++ >= MAX_AUTH_TRIES) {
174 reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
175 log_normal(_("%s was rejected: Too many wrong password "
176 "verifies for new user."), pconn->username);
178 return FALSE;
179 } else {
180 dsend_packet_authentication_req(pconn, AUTH_NEWUSER_RETRY, msg);
181 return TRUE;
185 /* the new password is good, create a database entry for
186 * this user; we establish the connection in handle_db_lookup */
187 create_md5sum((unsigned char *)password, strlen(password),
188 pconn->server.password);
190 if (script_fcdb_call("user_save", 1, API_TYPE_CONNECTION, pconn)
191 != FCDB_SUCCESS_TRUE) {
192 notify_conn(pconn->self, NULL, E_CONNECTION, ftc_warning,
193 _("Warning: There was an error in saving to the database. "
194 "Continuing, but your stats will not be saved."));
195 log_error("Error writing to database for: %s", pconn->username);
198 establish_new_connection(pconn);
199 } else if (pconn->server.status == AS_REQUESTING_OLD_PASS) {
200 if (auth_check_password(pconn, password, strlen(password)) == 1) {
201 establish_new_connection(pconn);
202 } else {
203 pconn->server.status = AS_FAILED;
204 pconn->server.auth_tries++;
205 pconn->server.auth_settime = time(NULL)
206 + auth_fail_wait[pconn->server.auth_tries];
208 } else {
209 log_verbose("%s is sending unrequested auth packets", pconn->username);
210 return FALSE;
213 return TRUE;
216 /****************************************************************************
217 Checks on where in the authentication process we are.
218 ****************************************************************************/
219 void auth_process_status(struct connection *pconn)
221 switch(pconn->server.status) {
222 case AS_NOT_ESTABLISHED:
223 /* nothing, we're not ready to do anything here yet. */
224 break;
225 case AS_FAILED:
226 /* the connection gave the wrong password, we kick 'em off or
227 * we're throttling the connection to avoid password guessing */
228 if (pconn->server.auth_settime > 0
229 && time(NULL) >= pconn->server.auth_settime) {
231 if (pconn->server.auth_tries >= MAX_AUTH_TRIES) {
232 pconn->server.status = AS_NOT_ESTABLISHED;
233 reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
234 log_normal(_("%s was rejected: Too many wrong password tries."),
235 pconn->username);
236 connection_close_server(pconn, _("auth failed"));
237 } else {
238 struct packet_authentication_req request;
240 pconn->server.status = AS_REQUESTING_OLD_PASS;
241 request.type = AUTH_LOGIN_RETRY;
242 sz_strlcpy(request.message,
243 _("Your password is incorrect. Try again."));
244 send_packet_authentication_req(pconn, &request);
247 break;
248 case AS_REQUESTING_OLD_PASS:
249 case AS_REQUESTING_NEW_PASS:
250 /* waiting on the client to send us a password... don't wait too long */
251 if (time(NULL) >= pconn->server.auth_settime + MAX_WAIT_TIME) {
252 pconn->server.status = AS_NOT_ESTABLISHED;
253 reject_new_connection(_("Sorry, your connection timed out..."), pconn);
254 log_normal(_("%s was rejected: Connection timeout waiting for "
255 "password."), pconn->username);
256 connection_close_server(pconn, _("auth failed"));
258 break;
259 case AS_ESTABLISHED:
260 /* this better fail bigtime */
261 fc_assert(pconn->server.status != AS_ESTABLISHED);
262 break;
266 /**************************************************************************
267 Check if the password with length len matches the hashed one in
268 pconn->server.password.
269 ***************************************************************************/
270 static bool auth_check_password(struct connection *pconn,
271 const char *password, int len)
273 bool ok = FALSE;
274 char checksum[MD5_HEX_BYTES + 1];
276 /* do the password checking right here */
277 create_md5sum((const unsigned char *)password, len, checksum);
278 ok = (strncmp(checksum, pconn->server.password, MD5_HEX_BYTES) == 0)
279 ? TRUE : FALSE;
281 script_fcdb_call("user_log", 2, API_TYPE_CONNECTION, pconn, API_TYPE_BOOL,
282 ok);
284 return ok;
287 /****************************************************************************
288 See if the name qualifies as a guest login name
289 ****************************************************************************/
290 static bool is_guest_name(const char *name)
292 return (fc_strncasecmp(name, GUEST_NAME, strlen(GUEST_NAME)) == 0);
295 /****************************************************************************
296 Return a unique guest name
297 WARNING: do not pass pconn->username to this function: it won't return!
298 ****************************************************************************/
299 static void get_unique_guest_name(char *name)
301 unsigned int i;
303 /* first see if the given name is suitable */
304 if (is_guest_name(name) && !conn_by_user(name)) {
305 return;
308 /* next try bare guest name */
309 fc_strlcpy(name, GUEST_NAME, MAX_LEN_NAME);
310 if (!conn_by_user(name)) {
311 return;
314 /* bare name is taken, append numbers */
315 for (i = 1; ; i++) {
316 fc_snprintf(name, MAX_LEN_NAME, "%s%u", GUEST_NAME, i);
318 /* attempt to find this name; if we can't we're good to go */
319 if (!conn_by_user(name)) {
320 break;
323 /* Prevent endless loops. */
324 fc_assert_ret(i < 2 * MAX_NUM_PLAYERS);
328 /****************************************************************************
329 Verifies that a password is valid. Does some [very] rudimentary safety
330 checks. TODO: do we want to frown on non-printing characters?
331 Fill the msg (length MAX_LEN_MSG) with any worthwhile information that
332 the client ought to know.
333 ****************************************************************************/
334 static bool is_good_password(const char *password, char *msg)
336 int i, num_caps = 0, num_nums = 0;
338 /* check password length */
339 if (strlen(password) < MIN_PASSWORD_LEN) {
340 fc_snprintf(msg, MAX_LEN_MSG,
341 _("Your password is too short, the minimum length is %d. "
342 "Try again."), MIN_PASSWORD_LEN);
343 return FALSE;
346 fc_snprintf(msg, MAX_LEN_MSG,
347 _("The password must have at least %d capital letters, %d "
348 "numbers, and be at minimum %d [printable] characters long. "
349 "Try again."),
350 MIN_PASSWORD_CAPS, MIN_PASSWORD_NUMS, MIN_PASSWORD_LEN);
352 for (i = 0; i < strlen(password); i++) {
353 if (fc_isupper(password[i])) {
354 num_caps++;
356 if (fc_isdigit(password[i])) {
357 num_nums++;
361 /* check number of capital letters */
362 if (num_caps < MIN_PASSWORD_CAPS) {
363 return FALSE;
366 /* check number of numbers */
367 if (num_nums < MIN_PASSWORD_NUMS) {
368 return FALSE;
371 if (!is_ascii_name(password)) {
372 return FALSE;
375 return TRUE;
378 /**************************************************************************
379 Get username for connection
380 **************************************************************************/
381 const char *auth_get_username(struct connection *pconn)
383 fc_assert_ret_val(pconn != NULL, NULL);
385 return pconn->username;
388 /**************************************************************************
389 Get connection ip address
390 **************************************************************************/
391 const char *auth_get_ipaddr(struct connection *pconn)
393 fc_assert_ret_val(pconn != NULL, NULL);
395 return pconn->server.ipaddr;
398 /**************************************************************************
399 Set password for connection
400 **************************************************************************/
401 bool auth_set_password(struct connection *pconn, const char *password)
403 fc_assert_ret_val(pconn != NULL, FALSE);
404 fc_assert_ret_val(password != NULL, FALSE);
406 sz_strlcpy(pconn->server.password, password);
408 return TRUE;
411 /**************************************************************************
412 Get connection password
413 **************************************************************************/
414 const char *auth_get_password(struct connection *pconn)
416 fc_assert_ret_val(pconn != NULL, NULL);
418 return pconn->server.password;