Do not build gtk3.22-client by default
[freeciv.git] / server / auth.c
bloba2aa1bba175f03b1fc1de62bea326fddf921a22b
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_early(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_early(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 /* TRANS: Try not to make the translation much longer than the original. */
140 sz_strlcpy(buffer, _("First time login. Set a new password and confirm it."));
141 dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer);
142 pconn->server.auth_settime = time(NULL);
143 pconn->server.status = AS_REQUESTING_NEW_PASS;
144 } else {
145 reject_new_connection(_("This server allows only preregistered "
146 "users. Sorry."), pconn);
147 log_normal(_("%s was rejected: Only preregistered users allowed."),
148 pconn->username);
150 return FALSE;
152 break;
153 default:
154 fc_assert(FALSE);
155 break;
157 return TRUE;
160 return TRUE;
163 /****************************************************************************
164 Receives a password from a client and verifies it.
165 ****************************************************************************/
166 bool auth_handle_reply(struct connection *pconn, char *password)
168 char msg[MAX_LEN_MSG];
170 if (pconn->server.status == AS_REQUESTING_NEW_PASS) {
172 /* check if the new password is acceptable */
173 if (!is_good_password(password, msg)) {
174 if (pconn->server.auth_tries++ >= MAX_AUTH_TRIES) {
175 reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
176 log_normal(_("%s was rejected: Too many wrong password "
177 "verifies for new user."), pconn->username);
179 return FALSE;
180 } else {
181 dsend_packet_authentication_req(pconn, AUTH_NEWUSER_RETRY, msg);
182 return TRUE;
186 /* the new password is good, create a database entry for
187 * this user; we establish the connection in handle_db_lookup */
188 create_md5sum((unsigned char *)password, strlen(password),
189 pconn->server.password);
191 if (script_fcdb_call("user_save", 1, API_TYPE_CONNECTION, pconn)
192 != FCDB_SUCCESS_TRUE) {
193 notify_conn(pconn->self, NULL, E_CONNECTION, ftc_warning,
194 _("Warning: There was an error in saving to the database. "
195 "Continuing, but your stats will not be saved."));
196 log_error("Error writing to database for: %s", pconn->username);
199 establish_new_connection(pconn);
200 } else if (pconn->server.status == AS_REQUESTING_OLD_PASS) {
201 if (auth_check_password(pconn, password, strlen(password)) == 1) {
202 establish_new_connection(pconn);
203 } else {
204 pconn->server.status = AS_FAILED;
205 pconn->server.auth_tries++;
206 pconn->server.auth_settime = time(NULL)
207 + auth_fail_wait[pconn->server.auth_tries];
209 } else {
210 log_verbose("%s is sending unrequested auth packets", pconn->username);
211 return FALSE;
214 return TRUE;
217 /****************************************************************************
218 Checks on where in the authentication process we are.
219 ****************************************************************************/
220 void auth_process_status(struct connection *pconn)
222 switch(pconn->server.status) {
223 case AS_NOT_ESTABLISHED:
224 /* nothing, we're not ready to do anything here yet. */
225 break;
226 case AS_FAILED:
227 /* the connection gave the wrong password, we kick 'em off or
228 * we're throttling the connection to avoid password guessing */
229 if (pconn->server.auth_settime > 0
230 && time(NULL) >= pconn->server.auth_settime) {
232 if (pconn->server.auth_tries >= MAX_AUTH_TRIES) {
233 pconn->server.status = AS_NOT_ESTABLISHED;
234 reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
235 log_normal(_("%s was rejected: Too many wrong password tries."),
236 pconn->username);
237 connection_close_server(pconn, _("auth failed"));
238 } else {
239 struct packet_authentication_req request;
241 pconn->server.status = AS_REQUESTING_OLD_PASS;
242 request.type = AUTH_LOGIN_RETRY;
243 sz_strlcpy(request.message,
244 _("Your password is incorrect. Try again."));
245 send_packet_authentication_req(pconn, &request);
248 break;
249 case AS_REQUESTING_OLD_PASS:
250 case AS_REQUESTING_NEW_PASS:
251 /* waiting on the client to send us a password... don't wait too long */
252 if (time(NULL) >= pconn->server.auth_settime + MAX_WAIT_TIME) {
253 pconn->server.status = AS_NOT_ESTABLISHED;
254 reject_new_connection(_("Sorry, your connection timed out..."), pconn);
255 log_normal(_("%s was rejected: Connection timeout waiting for "
256 "password."), pconn->username);
257 connection_close_server(pconn, _("auth failed"));
259 break;
260 case AS_ESTABLISHED:
261 /* this better fail bigtime */
262 fc_assert(pconn->server.status != AS_ESTABLISHED);
263 break;
267 /**************************************************************************
268 Check if the password with length len matches the hashed one in
269 pconn->server.password.
270 ***************************************************************************/
271 static bool auth_check_password(struct connection *pconn,
272 const char *password, int len)
274 bool ok = FALSE;
275 char checksum[MD5_HEX_BYTES + 1];
277 /* do the password checking right here */
278 create_md5sum((const unsigned char *)password, len, checksum);
279 ok = (strncmp(checksum, pconn->server.password, MD5_HEX_BYTES) == 0)
280 ? TRUE : FALSE;
282 script_fcdb_call("user_log", 2, API_TYPE_CONNECTION, pconn, API_TYPE_BOOL,
283 ok);
285 return ok;
288 /****************************************************************************
289 See if the name qualifies as a guest login name
290 ****************************************************************************/
291 static bool is_guest_name(const char *name)
293 return (fc_strncasecmp(name, GUEST_NAME, strlen(GUEST_NAME)) == 0);
296 /****************************************************************************
297 Return a unique guest name
298 WARNING: do not pass pconn->username to this function: it won't return!
299 ****************************************************************************/
300 static void get_unique_guest_name(char *name)
302 unsigned int i;
304 /* first see if the given name is suitable */
305 if (is_guest_name(name) && !conn_by_user(name)) {
306 return;
309 /* next try bare guest name */
310 fc_strlcpy(name, GUEST_NAME, MAX_LEN_NAME);
311 if (!conn_by_user(name)) {
312 return;
315 /* bare name is taken, append numbers */
316 for (i = 1; ; i++) {
317 fc_snprintf(name, MAX_LEN_NAME, "%s%u", GUEST_NAME, i);
319 /* attempt to find this name; if we can't we're good to go */
320 if (!conn_by_user(name)) {
321 break;
324 /* Prevent endless loops. */
325 fc_assert_ret(i < 2 * MAX_NUM_PLAYERS);
329 /****************************************************************************
330 Verifies that a password is valid. Does some [very] rudimentary safety
331 checks. TODO: do we want to frown on non-printing characters?
332 Fill the msg (length MAX_LEN_MSG) with any worthwhile information that
333 the client ought to know.
334 ****************************************************************************/
335 static bool is_good_password(const char *password, char *msg)
337 int i, num_caps = 0, num_nums = 0;
339 /* check password length */
340 if (strlen(password) < MIN_PASSWORD_LEN) {
341 fc_snprintf(msg, MAX_LEN_MSG,
342 _("Your password is too short, the minimum length is %d. "
343 "Try again."), MIN_PASSWORD_LEN);
344 return FALSE;
347 fc_snprintf(msg, MAX_LEN_MSG,
348 _("The password must have at least %d capital letters, %d "
349 "numbers, and be at minimum %d [printable] characters long. "
350 "Try again."),
351 MIN_PASSWORD_CAPS, MIN_PASSWORD_NUMS, MIN_PASSWORD_LEN);
353 for (i = 0; i < strlen(password); i++) {
354 if (fc_isupper(password[i])) {
355 num_caps++;
357 if (fc_isdigit(password[i])) {
358 num_nums++;
362 /* check number of capital letters */
363 if (num_caps < MIN_PASSWORD_CAPS) {
364 return FALSE;
367 /* check number of numbers */
368 if (num_nums < MIN_PASSWORD_NUMS) {
369 return FALSE;
372 if (!is_ascii_name(password)) {
373 return FALSE;
376 return TRUE;
379 /**************************************************************************
380 Get username for connection
381 **************************************************************************/
382 const char *auth_get_username(struct connection *pconn)
384 fc_assert_ret_val(pconn != NULL, NULL);
386 return pconn->username;
389 /**************************************************************************
390 Get connection ip address
391 **************************************************************************/
392 const char *auth_get_ipaddr(struct connection *pconn)
394 fc_assert_ret_val(pconn != NULL, NULL);
396 return pconn->server.ipaddr;
399 /**************************************************************************
400 Set password for connection
401 **************************************************************************/
402 bool auth_set_password(struct connection *pconn, const char *password)
404 fc_assert_ret_val(pconn != NULL, FALSE);
405 fc_assert_ret_val(password != NULL, FALSE);
407 sz_strlcpy(pconn->server.password, password);
409 return TRUE;
412 /**************************************************************************
413 Get connection password
414 **************************************************************************/
415 const char *auth_get_password(struct connection *pconn)
417 fc_assert_ret_val(pconn != NULL, NULL);
419 return pconn->server.password;