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_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
);
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_early(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 /* 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
;
145 reject_new_connection(_("This server allows only preregistered "
146 "users. Sorry."), pconn
);
147 log_normal(_("%s was rejected: Only preregistered users allowed."),
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
);
181 dsend_packet_authentication_req(pconn
, AUTH_NEWUSER_RETRY
, msg
);
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
);
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
];
210 log_verbose("%s is sending unrequested auth packets", pconn
->username
);
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. */
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."),
237 connection_close_server(pconn
, _("auth failed"));
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
);
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"));
261 /* this better fail bigtime */
262 fc_assert(pconn
->server
.status
!= AS_ESTABLISHED
);
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
)
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)
282 script_fcdb_call("user_log", 2, API_TYPE_CONNECTION
, pconn
, API_TYPE_BOOL
,
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
)
304 /* first see if the given name is suitable */
305 if (is_guest_name(name
) && !conn_by_user(name
)) {
309 /* next try bare guest name */
310 fc_strlcpy(name
, GUEST_NAME
, MAX_LEN_NAME
);
311 if (!conn_by_user(name
)) {
315 /* bare name is taken, append numbers */
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
)) {
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
);
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. "
351 MIN_PASSWORD_CAPS
, MIN_PASSWORD_NUMS
, MIN_PASSWORD_LEN
);
353 for (i
= 0; i
< strlen(password
); i
++) {
354 if (fc_isupper(password
[i
])) {
357 if (fc_isdigit(password
[i
])) {
362 /* check number of capital letters */
363 if (num_caps
< MIN_PASSWORD_CAPS
) {
367 /* check number of numbers */
368 if (num_nums
< MIN_PASSWORD_NUMS
) {
372 if (!is_ascii_name(password
)) {
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
);
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
;