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)
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 ***********************************************************************/
15 #include <fc_config.h>
31 #include "connection.h"
34 /* common/scripting */
35 #include "luascript_types.h"
38 #include "connecthand.h"
44 /* server/scripting */
45 #include "script_fcdb.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
);
94 reject_new_connection(_("Guests are not allowed on this server. "
96 log_normal(_("%s was rejected: Guests not allowed."), username
);
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
)) {
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'."),
118 establish_new_connection(pconn
);
120 reject_new_connection(_("There was an error reading the user database "
121 "and guest logins are not allowed. Sorry"),
123 log_normal(_("%s was rejected: Database error and guests not "
124 "allowed."), pconn
->username
);
128 case FCDB_SUCCESS_TRUE
:
129 /* we found a user */
130 fc_snprintf(buffer
, sizeof(buffer
), _("Enter password for %s:"),
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
;
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
;
144 reject_new_connection(_("This server allows only preregistered "
145 "users. Sorry."), pconn
);
146 log_normal(_("%s was rejected: Only preregistered users allowed."),
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
);
180 dsend_packet_authentication_req(pconn
, AUTH_NEWUSER_RETRY
, msg
);
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
);
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
];
209 log_verbose("%s is sending unrequested auth packets", pconn
->username
);
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. */
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."),
236 connection_close_server(pconn
, _("auth failed"));
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
);
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"));
260 /* this better fail bigtime */
261 fc_assert(pconn
->server
.status
!= AS_ESTABLISHED
);
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
)
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)
281 script_fcdb_call("user_log", 2, API_TYPE_CONNECTION
, pconn
, API_TYPE_BOOL
,
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
)
303 /* first see if the given name is suitable */
304 if (is_guest_name(name
) && !conn_by_user(name
)) {
308 /* next try bare guest name */
309 fc_strlcpy(name
, GUEST_NAME
, MAX_LEN_NAME
);
310 if (!conn_by_user(name
)) {
314 /* bare name is taken, append numbers */
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
)) {
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
);
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. "
350 MIN_PASSWORD_CAPS
, MIN_PASSWORD_NUMS
, MIN_PASSWORD_LEN
);
352 for (i
= 0; i
< strlen(password
); i
++) {
353 if (fc_isupper(password
[i
])) {
356 if (fc_isdigit(password
[i
])) {
361 /* check number of capital letters */
362 if (num_caps
< MIN_PASSWORD_CAPS
) {
366 /* check number of numbers */
367 if (num_nums
< MIN_PASSWORD_NUMS
) {
371 if (!is_ascii_name(password
)) {
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
);
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
;