[Freeciv-Dev] (PR#14851) remove userdb, add mysql to server for authenti
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14851 >
here's patches for both 2.0 and trunk to remove userdb/ and add mysql
authentication to the server.
To apply, pick your version.
If 2.0:
install mysql (table creation instruction can be found in auth.c)
patch -p0 < auth_mysql-2.0.diff
patch < auth.c2auth-2.0.c.diff
add auth.[ch] to server/
add md5.[ch] to utility/ (use the files in trunk)
./autogen.sh --enable-auth
if trunk:
install mysql
patch -p0 < auth_mysql.diff
add auth.[ch] to server/ (use the same auth.h)
add md5.[ch] to utility/
./autogen.sh --enable-auth
-mike
Index: m4/auth.m4
===================================================================
--- m4/auth.m4 (revision 11340)
+++ m4/auth.m4 (working copy)
@@ -4,16 +4,33 @@
AC_DEFUN([FC_CHECK_AUTH],
[
- # If the user calls --without-auth this will fail.
- AC_ARG_WITH([auth],
- [ --with-auth=lib Specify authentication database
library],
- [USER_DB_LIB="$withval"], [USER_DB_LIB="yes"])
+ dnl no=do not compile in authentication, yes=compile in auth, *=error
+ AC_ARG_ENABLE(auth,
+ [ --enable-auth do not compile in authentication],
+ [case "${enableval}" in
+ yes) auth=true ;;
+ no) auth=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-auth) ;;
+ esac], [auth=false])
- if test "$USER_DB_LIB" = "yes" || test "$USER_DB_LIB" = "no"; then
- USER_DB_LIB="userdb/libuserdb.a"
+ if test x$auth = xtrue; then
+
+ AC_CHECK_HEADER(mysql/mysql.h, ,
+ [AC_MSG_WARN([couldn't find mysql header: disabling auth]);
+ auth=false])
+
+ AC_CHECK_LIB(mysqlclient, mysql_query,
+ [AUTH_LIBS="-lmysqlclient $AUTH_LIBS";
+ AUTH_DEPS="$AUTH_LIBS"],
+ [AC_MSG_WARN([couldn't find mysql libs: disabling auth]);
+ auth=false])
+
+ AC_SUBST(AUTH_LIBS)
+ AC_SUBST(AUTH_DEPS)
fi
- USER_DB_DEP="$USER_DB_LIB"
- AC_SUBST(USER_DB_LIB)
- AC_SUBST(USER_DB_DEP)
+ if test x$auth = xtrue; then
+ AC_DEFINE(HAVE_AUTH, 1, [can compile with authentication])
+ fi
+
])
Index: configure.ac
===================================================================
--- configure.ac (revision 11340)
+++ configure.ac (working copy)
@@ -704,7 +704,6 @@
client/gui-stub/Makefile
server/Makefile
server/generator/Makefile
- server/userdb/Makefile
server/scripting/Makefile
manual/Makefile
intl/Makefile
Index: server/civserver.c
===================================================================
--- server/civserver.c (revision 11340)
+++ server/civserver.c (working copy)
@@ -163,12 +163,14 @@
break;
}
free(option);
+#ifdef HAVE_AUTH
} else if (is_option("--auth", argv[inx])) {
srvarg.auth_enabled = TRUE;
} else if (is_option("--Guests", argv[inx])) {
srvarg.auth_allow_guests = TRUE;
} else if (is_option("--Newusers", argv[inx])) {
srvarg.auth_allow_newusers = TRUE;
+#endif
} else if ((option = get_option_malloc("--Serverid", argv, &inx, argc))) {
sz_strlcpy(srvarg.serverid, option);
free(option);
@@ -195,11 +197,13 @@
if (showhelp) {
fc_fprintf(stderr,
_("Usage: %s [option ...]\nValid options are:\n"), argv[0]);
+#ifdef HAVE_AUTH
fc_fprintf(stderr, _(" -a --auth\t\tEnable server authentication.\n"));
fc_fprintf(stderr, _(" -G --Guests\t\tAllow guests to "
"login if auth is enabled.\n"));
fc_fprintf(stderr, _(" -N --Newusers\tAllow new users to "
"login if auth is enabled.\n"));
+#endif
fc_fprintf(stderr, _(" -b --bind ADDR\tListen for clients on ADDR\n"));
#ifdef DEBUG
fc_fprintf(stderr, _(" -d, --debug NUM\tSet debug log level (0 to 4,"
Index: server/srv_main.c
===================================================================
--- server/srv_main.c (revision 11340)
+++ server/srv_main.c (working copy)
@@ -69,6 +69,7 @@
#include "timing.h"
#include "version.h"
+#include "auth.h"
#include "barbarian.h"
#include "cityhand.h"
#include "citytools.h"
Index: server/connecthand.c
===================================================================
--- server/connecthand.c (revision 11340)
+++ server/connecthand.c (working copy)
@@ -29,7 +29,7 @@
#include "support.h"
#include "version.h"
-#include "user_db.h"
+#include "auth.h"
#include "diplhand.h"
#include "gamehand.h"
#include "gamelog.h"
@@ -43,25 +43,6 @@
#include "connecthand.h"
-#define GUEST_NAME "guest"
-
-#define MIN_PASSWORD_LEN 6 /* minimum length of password */
-#define MIN_PASSWORD_CAPS 0 /* minimum number of capital letters required */
-#define MIN_PASSWORD_NUMS 0 /* minimum number of numbers required */
-
-#define MAX_AUTHENTICATION_TRIES 3
-
-/* after each wrong guess for a password, the server waits this
- * many seconds to reply to the client */
-static const int auth_fail_period[] = { 1, 1, 2, 3 };
-
-static void establish_new_connection(struct connection *pconn);
-static void reject_new_connection(const char *msg, struct connection *pconn);
-
-static bool is_guest_name(const char *name);
-static void get_unique_guest_name(char *name);
-static bool is_good_password(const char *password, char *msg);
-
/**************************************************************************
This is used when a new player joins a server, before the game
has started. If pconn is NULL, is an AI, else a client.
@@ -69,7 +50,7 @@
N.B. this only attachs a connection to a player if
pconn->name == player->username
**************************************************************************/
-static void establish_new_connection(struct connection *pconn)
+void establish_new_connection(struct connection *pconn)
{
struct conn_list *dest = pconn->self;
struct player *pplayer;
@@ -203,7 +184,7 @@
/**************************************************************************
send the rejection packet to the client.
**************************************************************************/
-static void reject_new_connection(const char *msg, struct connection *pconn)
+void reject_new_connection(const char *msg, struct connection *pconn)
{
struct packet_server_join_reply packet;
@@ -295,256 +276,15 @@
} conn_list_iterate_end;
if (srvarg.auth_enabled) {
- /* assign the client a unique guest name/reject if guests aren't allowed */
- if (is_guest_name(req->username)) {
- if (srvarg.auth_allow_guests) {
- char old_guest_name[MAX_LEN_NAME];
-
- sz_strlcpy(old_guest_name, req->username);
- get_unique_guest_name(req->username);
-
- if (strncmp(old_guest_name, req->username, MAX_LEN_NAME) != 0) {
- notify_conn(pconn->self, NULL, E_CONNECTION,
- _("Warning: the guest name '%s' has been "
- "taken, renaming to user '%s'."),
- old_guest_name, req->username);
- }
- } else {
- reject_new_connection(_("Guests are not allowed on this server. "
- "Sorry."), pconn);
- return FALSE;
- }
- } else {
- /* we are not a guest, we need an extra check as to whether a
- * connection can be established: the client must authenticate itself */
- char tmpname[MAX_LEN_NAME] = "\0";
- char buffer[MAX_LEN_MSG];
-
- sz_strlcpy(pconn->username, req->username);
-
- switch(user_db_load(pconn)) {
- case USER_DB_ERROR:
- if (srvarg.auth_allow_guests) {
- sz_strlcpy(tmpname, pconn->username);
- get_unique_guest_name(tmpname); /* don't pass pconn->username here */
- sz_strlcpy(pconn->username, tmpname);
-
- freelog(LOG_ERROR, "Error reading database; connection -> guest");
- notify_conn(pconn->self, NULL, E_CONNECTION,
- _("There was an error reading the user "
- "database, logging in as guest "
- "connection '%s'."), pconn->username);
- establish_new_connection(pconn);
- } else {
- reject_new_connection(_("There was an error reading the user "
- "database and guest logins are not "
- "allowed. Sorry"), pconn);
- return FALSE;
- }
- break;
- case USER_DB_SUCCESS:
- /* we found a user */
- my_snprintf(buffer, sizeof(buffer), _("Enter password for %s:"),
- pconn->username);
- dsend_packet_authentication_req(pconn, AUTH_LOGIN_FIRST, buffer);
- pconn->server.status = AS_REQUESTING_OLD_PASS;
- break;
- case USER_DB_NOT_FOUND:
- /* we couldn't find the user, he is new */
- if (srvarg.auth_allow_newusers) {
- sz_strlcpy(buffer, _("Enter a new password (and remember it)."));
- dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer);
- pconn->server.status = AS_REQUESTING_NEW_PASS;
- } else {
- reject_new_connection(_("This server allows only preregistered "
- "users. Sorry."), pconn);
- return FALSE;
- }
- break;
- default:
- assert(0);
- break;
- }
- return TRUE;
- }
- }
-
- sz_strlcpy(pconn->username, req->username);
- establish_new_connection(pconn);
- return TRUE;
-}
-
-/**************************************************************************
- sniff_packets calls this when pconn->server.authentication_stop == time(NULL)
- after an authentication fails
-**************************************************************************/
-void unfail_authentication(struct connection *pconn)
-{
- assert(pconn->server.status == AS_FAILED);
-
- if (pconn->server.authentication_tries >= MAX_AUTHENTICATION_TRIES) {
- pconn->server.status = AS_NOT_ESTABLISHED;
- reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
- close_connection(pconn);
+ return authenticate_user(pconn, req->username);
} else {
- struct packet_authentication_req request;
-
- pconn->server.status = AS_REQUESTING_OLD_PASS;
- request.type = AUTH_LOGIN_RETRY;
- sz_strlcpy(request.message,
- _("Your password is incorrect. Try again."));
- send_packet_authentication_req(pconn, &request);
- }
-}
-
-/**************************************************************************
- Receives a password from a client and verifies it.
-**************************************************************************/
-bool handle_authentication_reply(struct connection *pconn, char *password)
-{
- char msg[MAX_LEN_MSG];
-
- if (pconn->server.status == AS_REQUESTING_NEW_PASS) {
-
- /* check if the new password is acceptable */
- if (!is_good_password(password, msg)) {
- if (pconn->server.authentication_tries++ >= MAX_AUTHENTICATION_TRIES) {
- reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
- return FALSE;
- } else {
- dsend_packet_authentication_req(pconn, AUTH_NEWUSER_RETRY,msg);
- return TRUE;
- }
- }
-
- /* the new password is good, create a database entry for
- * this user; we establish the connection in handle_db_lookup */
- sz_strlcpy(pconn->server.password, password);
-
- switch(user_db_save(pconn)) {
- case USER_DB_SUCCESS:
- break;
- case USER_DB_ERROR:
- notify_conn(pconn->self, NULL, E_CONNECTION,
- _("Warning: There was an error in saving "
- "to the database. Continuing, but your "
- "stats will not be saved."));
- freelog(LOG_ERROR, "Error writing to database for %s", pconn->username);
- break;
- default:
- assert(0);
- }
-
+ sz_strlcpy(pconn->username, req->username);
establish_new_connection(pconn);
- } else if (pconn->server.status == AS_REQUESTING_OLD_PASS) {
- if (userdb_check_password(pconn, password, strlen(password)) == 1) {
- pconn->server.status = AS_ESTABLISHED;
- establish_new_connection(pconn);
- } else {
- pconn->server.status = AS_FAILED;
- pconn->server.authentication_tries++;
- pconn->server.authentication_stop = time(NULL) +
- auth_fail_period[pconn->server.authentication_tries];
- }
- } else {
- freelog(LOG_VERBOSE, "%s is sending unrequested auth packets",
- pconn->username);
- return FALSE;
+ return TRUE;
}
-
- return TRUE;
}
/**************************************************************************
- see if the name qualifies as a guest login name
-**************************************************************************/
-static bool is_guest_name(const char *name)
-{
- return (mystrncasecmp(name, GUEST_NAME, strlen(GUEST_NAME)) == 0);
-}
-
-/**************************************************************************
- return a unique guest name
- WARNING: do not pass pconn->username to this function: it won't return!
-**************************************************************************/
-static void get_unique_guest_name(char *name)
-{
- unsigned int i;
-
- /* first see if the given name is suitable */
- if (is_guest_name(name) && !find_conn_by_user(name)) {
- return;
- }
-
- /* next try bare guest name */
- mystrlcpy(name, GUEST_NAME, MAX_LEN_NAME);
- if (!find_conn_by_user(name)) {
- return;
- }
-
- /* bare name is taken, append numbers */
- for (i = 1; ; i++) {
- my_snprintf(name, MAX_LEN_NAME, "%s%u", GUEST_NAME, i);
-
-
- /* attempt to find this name; if we can't we're good to go */
- if (!find_conn_by_user(name)) {
- break;
- }
- }
-}
-
-/**************************************************************************
- Verifies that a password is valid. Does some [very] rudimentary safety
- checks. TODO: do we want to frown on non-printing characters?
- Fill the msg (length MAX_LEN_MSG) with any worthwhile information that
- the client ought to know.
-**************************************************************************/
-static bool is_good_password(const char *password, char *msg)
-{
- int i, num_caps = 0, num_nums = 0;
-
- /* check password length */
- if (strlen(password) < MIN_PASSWORD_LEN) {
- my_snprintf(msg, MAX_LEN_MSG,
- _("Your password is too short, the minimum length is %d. "
- "Try again."), MIN_PASSWORD_LEN);
- return FALSE;
- }
-
- my_snprintf(msg, MAX_LEN_MSG,
- _("The password must have at least %d capital letters, %d "
- "numbers, and be at minimum %d [printable] characters long. "
- "Try again."),
- MIN_PASSWORD_CAPS, MIN_PASSWORD_NUMS, MIN_PASSWORD_LEN);
-
- for (i = 0; i < strlen(password); i++) {
- if (my_isupper(password[i])) {
- num_caps++;
- }
- if (my_isdigit(password[i])) {
- num_nums++;
- }
- }
-
- /* check number of capital letters */
- if (num_caps < MIN_PASSWORD_CAPS) {
- return FALSE;
- }
-
- /* check number of numbers */
- if (num_nums < MIN_PASSWORD_NUMS) {
- return FALSE;
- }
-
- if (!is_ascii_name(password)) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**************************************************************************
High-level server stuff when connection to client is closed or lost.
Reports loss to log, and to other players if the connection was a
player. Also removes player if in pregame, applies auto_toggle, and
Index: server/connecthand.h
===================================================================
--- server/connecthand.h (revision 11340)
+++ server/connecthand.h (working copy)
@@ -23,11 +23,12 @@
struct packet_login_request;
struct packet_server_join_req;
+void establish_new_connection(struct connection *pconn);
+void reject_new_connection(const char *msg, struct connection *pconn);
+
bool handle_login_request(struct connection *pconn,
struct packet_server_join_req *req);
-void unfail_authentication(struct connection *pconn);
-
void lost_connection_to_client(struct connection *pconn);
void send_conn_info(struct conn_list *src, struct conn_list *dest);
@@ -36,6 +37,5 @@
bool attach_connection_to_player(struct connection *pconn,
struct player *pplayer);
bool unattach_connection_from_player(struct connection *pconn);
-bool handle_authentication_reply(struct connection *pc, char *password);
#endif /* FC__CONNECTHAND_H */
Index: server/sernet.c
===================================================================
--- server/sernet.c (revision 11340)
+++ server/sernet.c (working copy)
@@ -74,6 +74,7 @@
#include "support.h"
#include "timing.h"
+#include "auth.h"
#include "connecthand.h"
#include "console.h"
#include "meta.h"
@@ -546,10 +547,8 @@
/* if we've waited long enough after a failure, respond to the client */
conn_list_iterate(game.all_connections, pconn) {
- if (pconn->server.status == AS_FAILED
- && pconn->server.authentication_stop > 0
- && time(NULL) >= pconn->server.authentication_stop) {
- unfail_authentication(pconn);
+ if (srvarg.auth_enabled && pconn->server.status != AS_ESTABLISHED) {
+ process_authentication_status(pconn);
}
} conn_list_iterate_end
@@ -811,8 +810,8 @@
pconn->is_server = TRUE;
pconn->server.currently_processed_request_id = 0;
pconn->server.last_request_id_seen = 0;
- pconn->server.authentication_tries = 0;
- pconn->server.authentication_stop = 0;
+ pconn->server.auth_tries = 0;
+ pconn->server.auth_settime = 0;
pconn->server.status = AS_NOT_ESTABLISHED;
pconn->server.ping_timers =
fc_malloc(sizeof(*pconn->server.ping_timers));
Index: server/Makefile.am
===================================================================
--- server/Makefile.am (revision 11340)
+++ server/Makefile.am (working copy)
@@ -1,12 +1,12 @@
## Process this file with automake to produce Makefile.in
-SUBDIRS= userdb generator scripting
+SUBDIRS= generator scripting
bin_PROGRAMS = civserver
noinst_LIBRARIES = libcivserver.a
AM_CPPFLAGS = \
-I$(top_srcdir)/utility -I$(srcdir)/../common -I$(srcdir)/../ai \
- -I../intl -I$(top_srcdir)/common/aicore -I$(srcdir)/userdb \
+ -I../intl -I$(top_srcdir)/common/aicore \
-I$(srcdir)/generator -I$(srcdir)/scripting
## Above, note -I../intl instead of -I$(top_srdir/intl) is deliberate.
@@ -18,6 +18,8 @@
libcivserver_a_SOURCES = \
airgoto.c \
airgoto.h \
+ auth.c \
+ auth.h \
barbarian.c \
barbarian.h \
cityhand.c \
@@ -88,7 +90,7 @@
../dependencies/lua/src/liblua.a \
../dependencies/lua/src/lib/liblualib.a \
../dependencies/tolua/libtolua.a \
- ./generator/libgenerator.a $(USER_DB_DEP)
+ ./generator/libgenerator.a $(AUTH_DEPS)
civserver_LDADD = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ./libcivserver.a @INTLLIBS@ \
../utility/libcivutility.a ../common/libcivcommon.a ../ai/libcivai.a \
@@ -98,7 +100,7 @@
../dependencies/lua/src/liblua.a \
../dependencies/lua/src/lib/liblualib.a \
../dependencies/tolua/libtolua.a \
- $(USER_DB_LIB) $(SERVER_LIBS)
+ $(AUTH_LIBS) $(SERVER_LIBS)
Index: common/connection.h
===================================================================
--- common/connection.h (revision 11340)
+++ common/connection.h (working copy)
@@ -187,11 +187,12 @@
struct timer_list *ping_timers;
/* Holds number of tries for authentication from client. */
- int authentication_tries;
+ int auth_tries;
- /* the time that the server will reply after receiving an auth reply.
- * this is used to throttle the connection. */
- time_t authentication_stop;
+ /* the time that the server will respond after receiving an auth reply.
+ * this is used to throttle the connection. Also used to reject a
+ * connection if we've waited too long for a password. */
+ time_t auth_settime;
/* used to follow where the connection is in the authentication process */
enum auth_status status;
Index: manual/Makefile.am
===================================================================
--- manual/Makefile.am (revision 11340)
+++ manual/Makefile.am (working copy)
@@ -1,7 +1,7 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = civmanual
-AM_CPPFLAGS = -I$(top_srcdir)/server -I$(top_srcdir)/utility
-I$(top_srcdir)/common -I$(top_srcdir)/ai -I../intl
-I$(top_srcdir)/common/aicore -I$(top_srcdir)/server/userdb
-I$(top_srcdir)/server/generator -I$(top_srcdir)/client
-I$(top_srcdir)/client/include
+AM_CPPFLAGS = -I$(top_srcdir)/server -I$(top_srcdir)/utility
-I$(top_srcdir)/common -I$(top_srcdir)/ai -I../intl
-I$(top_srcdir)/common/aicore -I$(top_srcdir)/server/generator
-I$(top_srcdir)/client -I$(top_srcdir)/client/include
## Above, note -I../intl instead of -I$(top_srcdir/intl) is deliberate.
@@ -13,22 +13,21 @@
civmanual_DEPENDENCIES = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ../server/libcivserver.a \
../utility/libcivutility.a ../common/aicore/libaicore.a \
- ../server/userdb/libuserdb.a ../client/helpdata.o \
+ ../client/helpdata.o \
../server/scripting/libscripting.a \
../dependencies/lua/src/liblua.a \
../dependencies/lua/src/lib/liblualib.a \
../dependencies/tolua/libtolua.a \
- ../server/generator/libgenerator.a
+ ../server/generator/libgenerator.a $(AUTH_DEPS)
civmanual_LDADD = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ../server/libcivserver.a \
@INTLLIBS@ ../client/helpdata.o \
../utility/libcivutility.a ../common/libcivcommon.a ../ai/libcivai.a \
../utility/libcivutility.a ../server/libcivserver.a \
- ../server/userdb/libuserdb.a \
../utility/libcivutility.a ../common/aicore/libaicore.a \
../server/scripting/libscripting.a \
../dependencies/lua/src/liblua.a \
../dependencies/lua/src/lib/liblualib.a \
../dependencies/tolua/libtolua.a \
../server/generator/libgenerator.a \
- $(SERVER_LIBS)
+ $(SERVER_LIBS) $(AUTH_LIBS)
Index: m4/auth.m4
===================================================================
--- m4/auth.m4 (revision 11345)
+++ m4/auth.m4 (working copy)
@@ -4,16 +4,33 @@
AC_DEFUN([FC_CHECK_AUTH],
[
- # If the user calls --without-auth this will fail.
- AC_ARG_WITH([auth],
- [ --with-auth=lib Specify authentication database
library],
- [USER_DB_LIB="$withval"], [USER_DB_LIB="yes"])
+ dnl no=do not compile in authentication, yes=compile in auth, *=error
+ AC_ARG_ENABLE(auth,
+ [ --enable-auth do not compile in authentication],
+ [case "${enableval}" in
+ yes) auth=true ;;
+ no) auth=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-auth) ;;
+ esac], [auth=false])
- if test "$USER_DB_LIB" = "yes" || test "$USER_DB_LIB" = "no"; then
- USER_DB_LIB="userdb/libuserdb.a"
+ if test x$auth = xtrue; then
+
+ AC_CHECK_HEADER(mysql/mysql.h, ,
+ [AC_MSG_WARN([couldn't find mysql header: disabling auth]);
+ auth=false])
+
+ AC_CHECK_LIB(mysqlclient, mysql_query,
+ [AUTH_LIBS="-lmysqlclient $AUTH_LIBS";
+ AUTH_DEPS="$AUTH_LIBS"],
+ [AC_MSG_WARN([couldn't find mysql libs: disabling auth]);
+ auth=false])
+
+ AC_SUBST(AUTH_LIBS)
+ AC_SUBST(AUTH_DEPS)
fi
- USER_DB_DEP="$USER_DB_LIB"
- AC_SUBST(USER_DB_LIB)
- AC_SUBST(USER_DB_DEP)
+ if test x$auth = xtrue; then
+ AC_DEFINE(HAVE_AUTH, 1, [can compile with authentication])
+ fi
+
])
Index: configure.ac
===================================================================
--- configure.ac (revision 11345)
+++ configure.ac (working copy)
@@ -671,7 +671,6 @@
client/gui-stub/Makefile
server/Makefile
server/generator/Makefile
- server/userdb/Makefile
manual/Makefile
intl/Makefile
po/Makefile.in
Index: utility/Makefile.am
===================================================================
--- utility/Makefile.am (revision 11345)
+++ utility/Makefile.am (working copy)
@@ -48,4 +48,6 @@
support.c \
support.h \
timing.c \
- timing.h
+ timing.h \
+ md5.c \
+ md5.h
Index: server/civserver.c
===================================================================
--- server/civserver.c (revision 11345)
+++ server/civserver.c (working copy)
@@ -122,12 +122,14 @@
showhelp = TRUE;
break;
}
+#ifdef HAVE_AUTH
} else if (is_option("--auth", argv[inx])) {
srvarg.auth_enabled = TRUE;
} else if (is_option("--Guests", argv[inx])) {
srvarg.auth_allow_guests = TRUE;
} else if (is_option("--Newusers", argv[inx])) {
srvarg.auth_allow_newusers = TRUE;
+#endif
} else if ((option = get_option("--Serverid", argv, &inx, argc))) {
sz_strlcpy(srvarg.serverid, option);
} else if ((option = get_option("--saves", argv, &inx, argc))) {
@@ -153,11 +155,13 @@
if (showhelp) {
fc_fprintf(stderr,
_("Usage: %s [option ...]\nValid options are:\n"), argv[0]);
+#ifdef HAVE_AUTH
fc_fprintf(stderr, _(" -a --auth\t\tEnable server authentication.\n"));
fc_fprintf(stderr, _(" -G --Guests\t\tAllow guests to "
"login if auth is enabled.\n"));
fc_fprintf(stderr, _(" -N --Newusers\tAllow new users to "
"login if auth is enabled.\n"));
+#endif
fc_fprintf(stderr, _(" -b --bind ADDR\tListen for clients on ADDR\n"));
#ifdef DEBUG
fc_fprintf(stderr, _(" -d, --debug NUM\tSet debug log level (0 to 4,"
Index: server/srv_main.c
===================================================================
--- server/srv_main.c (revision 11345)
+++ server/srv_main.c (working copy)
@@ -68,6 +68,7 @@
#include "timing.h"
#include "version.h"
+#include "auth.h"
#include "autoattack.h"
#include "barbarian.h"
#include "cityhand.h"
Index: server/connecthand.c
===================================================================
--- server/connecthand.c (revision 11345)
+++ server/connecthand.c (working copy)
@@ -28,7 +28,7 @@
#include "support.h"
#include "version.h"
-#include "user_db.h"
+#include "auth.h"
#include "diplhand.h"
#include "gamehand.h"
#include "gamelog.h"
@@ -42,25 +42,6 @@
#include "connecthand.h"
-#define GUEST_NAME "guest"
-
-#define MIN_PASSWORD_LEN 6 /* minimum length of password */
-#define MIN_PASSWORD_CAPS 0 /* minimum number of capital letters required */
-#define MIN_PASSWORD_NUMS 0 /* minimum number of numbers required */
-
-#define MAX_AUTHENTICATION_TRIES 3
-
-/* after each wrong guess for a password, the server waits this
- * many seconds to reply to the client */
-static const int auth_fail_period[] = { 1, 1, 2, 3 };
-
-static void establish_new_connection(struct connection *pconn);
-static void reject_new_connection(const char *msg, struct connection *pconn);
-
-static bool is_guest_name(const char *name);
-static void get_unique_guest_name(char *name);
-static bool is_good_password(const char *password, char *msg);
-
/**************************************************************************
This is used when a new player joins a server, before the game
has started. If pconn is NULL, is an AI, else a client.
@@ -68,7 +49,7 @@
N.B. this only attachs a connection to a player if
pconn->name == player->username
**************************************************************************/
-static void establish_new_connection(struct connection *pconn)
+void establish_new_connection(struct connection *pconn)
{
struct conn_list *dest = &pconn->self;
struct player *pplayer;
@@ -188,7 +169,7 @@
/**************************************************************************
send the rejection packet to the client.
**************************************************************************/
-static void reject_new_connection(const char *msg, struct connection *pconn)
+void reject_new_connection(const char *msg, struct connection *pconn)
{
struct packet_server_join_reply packet;
@@ -280,253 +261,15 @@
} conn_list_iterate_end;
if (srvarg.auth_enabled) {
- /* assign the client a unique guest name/reject if guests aren't allowed */
- if (is_guest_name(req->username)) {
- if (srvarg.auth_allow_guests) {
- char old_guest_name[MAX_LEN_NAME];
-
- sz_strlcpy(old_guest_name, req->username);
- get_unique_guest_name(req->username);
-
- if (strncmp(old_guest_name, req->username, MAX_LEN_NAME) != 0) {
- notify_conn(&pconn->self, _("Warning: the guest name '%s' has been "
- "taken, renaming to user '%s'."),
- old_guest_name, req->username);
- }
- } else {
- reject_new_connection(_("Guests are not allowed on this server. "
- "Sorry."), pconn);
- return FALSE;
- }
- } else {
- /* we are not a guest, we need an extra check as to whether a
- * connection can be established: the client must authenticate itself */
- char tmpname[MAX_LEN_NAME] = "\0";
- char buffer[MAX_LEN_MSG];
-
- sz_strlcpy(pconn->username, req->username);
-
- switch(user_db_load(pconn)) {
- case USER_DB_ERROR:
- if (srvarg.auth_allow_guests) {
- sz_strlcpy(tmpname, pconn->username);
- get_unique_guest_name(tmpname); /* don't pass pconn->username here */
- sz_strlcpy(pconn->username, tmpname);
-
- freelog(LOG_ERROR, "Error reading database; connection -> guest");
- notify_conn(&pconn->self, _("There was an error reading the user "
- "database, logging in as guest "
- "connection '%s'."), pconn->username);
- establish_new_connection(pconn);
- } else {
- reject_new_connection(_("There was an error reading the user "
- "database and guest logins are not "
- "allowed. Sorry"), pconn);
- return FALSE;
- }
- break;
- case USER_DB_SUCCESS:
- /* we found a user */
- my_snprintf(buffer, sizeof(buffer), _("Enter password for %s:"),
- pconn->username);
- dsend_packet_authentication_req(pconn, AUTH_LOGIN_FIRST, buffer);
- pconn->server.status = AS_REQUESTING_OLD_PASS;
- break;
- case USER_DB_NOT_FOUND:
- /* we couldn't find the user, he is new */
- if (srvarg.auth_allow_newusers) {
- sz_strlcpy(buffer, _("Enter a new password (and remember it)."));
- dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer);
- pconn->server.status = AS_REQUESTING_NEW_PASS;
- } else {
- reject_new_connection(_("This server allows only preregistered "
- "users. Sorry."), pconn);
- return FALSE;
- }
- break;
- default:
- assert(0);
- break;
- }
- return TRUE;
- }
- }
-
- sz_strlcpy(pconn->username, req->username);
- establish_new_connection(pconn);
- return TRUE;
-}
-
-/**************************************************************************
- sniff_packets calls this when pconn->server.authentication_stop == time(NULL)
- after an authentication fails
-**************************************************************************/
-void unfail_authentication(struct connection *pconn)
-{
- assert(pconn->server.status == AS_FAILED);
-
- if (pconn->server.authentication_tries >= MAX_AUTHENTICATION_TRIES) {
- pconn->server.status = AS_NOT_ESTABLISHED;
- reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
- close_connection(pconn);
+ return authenticate_user(pconn, req->username);
} else {
- struct packet_authentication_req request;
-
- pconn->server.status = AS_REQUESTING_OLD_PASS;
- request.type = AUTH_LOGIN_RETRY;
- sz_strlcpy(request.message,
- _("Your password is incorrect. Try again."));
- send_packet_authentication_req(pconn, &request);
- }
-}
-
-/**************************************************************************
- Receives a password from a client and verifies it.
-**************************************************************************/
-bool handle_authentication_reply(struct connection *pconn, char *password)
-{
- char msg[MAX_LEN_MSG];
-
- if (pconn->server.status == AS_REQUESTING_NEW_PASS) {
-
- /* check if the new password is acceptable */
- if (!is_good_password(password, msg)) {
- if (pconn->server.authentication_tries++ >= MAX_AUTHENTICATION_TRIES) {
- reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
- return FALSE;
- } else {
- dsend_packet_authentication_req(pconn, AUTH_NEWUSER_RETRY,msg);
- return TRUE;
- }
- }
-
- /* the new password is good, create a database entry for
- * this user; we establish the connection in handle_db_lookup */
- sz_strlcpy(pconn->server.password, password);
-
- switch(user_db_save(pconn)) {
- case USER_DB_SUCCESS:
- break;
- case USER_DB_ERROR:
- notify_conn(&pconn->self, _("Warning: There was an error in saving "
- "to the database. Continuing, but your "
- "stats will not be saved."));
- freelog(LOG_ERROR, "Error writing to database for %s", pconn->username);
- break;
- default:
- assert(0);
- }
-
+ sz_strlcpy(pconn->username, req->username);
establish_new_connection(pconn);
- } else if (pconn->server.status == AS_REQUESTING_OLD_PASS) {
- if (userdb_check_password(pconn, password, strlen(password)) == 1) {
- pconn->server.status = AS_ESTABLISHED;
- establish_new_connection(pconn);
- } else {
- pconn->server.status = AS_FAILED;
- pconn->server.authentication_tries++;
- pconn->server.authentication_stop = time(NULL) +
- auth_fail_period[pconn->server.authentication_tries];
- }
- } else {
- freelog(LOG_VERBOSE, "%s is sending unrequested auth packets",
- pconn->username);
- return FALSE;
+ return TRUE;
}
-
- return TRUE;
}
/**************************************************************************
- see if the name qualifies as a guest login name
-**************************************************************************/
-static bool is_guest_name(const char *name)
-{
- return (mystrncasecmp(name, GUEST_NAME, strlen(GUEST_NAME)) == 0);
-}
-
-/**************************************************************************
- return a unique guest name
- WARNING: do not pass pconn->username to this function: it won't return!
-**************************************************************************/
-static void get_unique_guest_name(char *name)
-{
- unsigned int i;
-
- /* first see if the given name is suitable */
- if (is_guest_name(name) && !find_conn_by_user(name)) {
- return;
- }
-
- /* next try bare guest name */
- mystrlcpy(name, GUEST_NAME, MAX_LEN_NAME);
- if (!find_conn_by_user(name)) {
- return;
- }
-
- /* bare name is taken, append numbers */
- for (i = 1; ; i++) {
- my_snprintf(name, MAX_LEN_NAME, "%s%u", GUEST_NAME, i);
-
-
- /* attempt to find this name; if we can't we're good to go */
- if (!find_conn_by_user(name)) {
- break;
- }
- }
-}
-
-/**************************************************************************
- Verifies that a password is valid. Does some [very] rudimentary safety
- checks. TODO: do we want to frown on non-printing characters?
- Fill the msg (length MAX_LEN_MSG) with any worthwhile information that
- the client ought to know.
-**************************************************************************/
-static bool is_good_password(const char *password, char *msg)
-{
- int i, num_caps = 0, num_nums = 0;
-
- /* check password length */
- if (strlen(password) < MIN_PASSWORD_LEN) {
- my_snprintf(msg, MAX_LEN_MSG,
- _("Your password is too short, the minimum length is %d. "
- "Try again."), MIN_PASSWORD_LEN);
- return FALSE;
- }
-
- my_snprintf(msg, MAX_LEN_MSG,
- _("The password must have at least %d capital letters, %d "
- "numbers, and be at minimum %d [printable] characters long. "
- "Try again."),
- MIN_PASSWORD_CAPS, MIN_PASSWORD_NUMS, MIN_PASSWORD_LEN);
-
- for (i = 0; i < strlen(password); i++) {
- if (my_isupper(password[i])) {
- num_caps++;
- }
- if (my_isdigit(password[i])) {
- num_nums++;
- }
- }
-
- /* check number of capital letters */
- if (num_caps < MIN_PASSWORD_CAPS) {
- return FALSE;
- }
-
- /* check number of numbers */
- if (num_nums < MIN_PASSWORD_NUMS) {
- return FALSE;
- }
-
- if (!is_ascii_name(password)) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**************************************************************************
High-level server stuff when connection to client is closed or lost.
Reports loss to log, and to other players if the connection was a
player. Also removes player if in pregame, applies auto_toggle, and
Index: server/connecthand.h
===================================================================
--- server/connecthand.h (revision 11345)
+++ server/connecthand.h (working copy)
@@ -23,11 +23,12 @@
struct packet_login_request;
struct packet_server_join_req;
+void establish_new_connection(struct connection *pconn);
+void reject_new_connection(const char *msg, struct connection *pconn);
+
bool handle_login_request(struct connection *pconn,
struct packet_server_join_req *req);
-void unfail_authentication(struct connection *pconn);
-
void lost_connection_to_client(struct connection *pconn);
void send_conn_info(struct conn_list *src, struct conn_list *dest);
@@ -36,6 +37,5 @@
bool attach_connection_to_player(struct connection *pconn,
struct player *pplayer);
bool unattach_connection_from_player(struct connection *pconn);
-bool handle_authentication_reply(struct connection *pc, char *password);
#endif /* FC__CONNECTHAND_H */
Index: server/sernet.c
===================================================================
--- server/sernet.c (revision 11345)
+++ server/sernet.c (working copy)
@@ -74,6 +74,7 @@
#include "support.h"
#include "timing.h"
+#include "auth.h"
#include "connecthand.h"
#include "console.h"
#include "meta.h"
@@ -465,10 +466,8 @@
/* if we've waited long enough after a failure, respond to the client */
conn_list_iterate(game.all_connections, pconn) {
- if (pconn->server.status == AS_FAILED
- && pconn->server.authentication_stop > 0
- && time(NULL) >= pconn->server.authentication_stop) {
- unfail_authentication(pconn);
+ if (srvarg.auth_enabled && pconn->server.status != AS_ESTABLISHED) {
+ process_authentication_status(pconn);
}
} conn_list_iterate_end
@@ -764,8 +763,8 @@
pconn->notify_of_writable_data = NULL;
pconn->server.currently_processed_request_id = 0;
pconn->server.last_request_id_seen = 0;
- pconn->server.authentication_tries = 0;
- pconn->server.authentication_stop = 0;
+ pconn->server.auth_tries = 0;
+ pconn->server.auth_settime = 0;
pconn->server.status = AS_NOT_ESTABLISHED;
pconn->server.ping_timers =
fc_malloc(sizeof(*pconn->server.ping_timers));
Index: server/Makefile.am
===================================================================
--- server/Makefile.am (revision 11345)
+++ server/Makefile.am (working copy)
@@ -1,10 +1,10 @@
## Process this file with automake to produce Makefile.in
-SUBDIRS= userdb generator
+SUBDIRS= generator
bin_PROGRAMS = civserver
noinst_LIBRARIES = libcivserver.a
-AM_CPPFLAGS = -I$(top_srcdir)/utility -I$(srcdir)/../common -I$(srcdir)/../ai
-I../intl -I$(top_srcdir)/common/aicore -I$(srcdir)/userdb -I$(srcdir)/generator
+AM_CPPFLAGS = -I$(top_srcdir)/utility -I$(srcdir)/../common -I$(srcdir)/../ai
-I../intl -I$(top_srcdir)/common/aicore -I$(srcdir)/generator
## Above, note -I../intl instead of -I$(top_srdir/intl) is deliberate.
@@ -16,6 +16,8 @@
libcivserver_a_SOURCES = \
airgoto.c \
airgoto.h \
+ auth.c \
+ auth.h \
autoattack.c \
autoattack.h \
barbarian.c \
@@ -83,11 +85,11 @@
civserver_DEPENDENCIES = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ./libcivserver.a \
../utility/libcivutility.a ../common/aicore/libaicore.a \
- ./generator/libgenerator.a $(USER_DB_DEP)
+ ./generator/libgenerator.a $(AUTH_DEPS)
civserver_LDADD = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ./libcivserver.a @INTLLIBS@ \
../utility/libcivutility.a ../common/libcivcommon.a ../ai/libcivai.a \
../utility/libcivutility.a ./libcivserver.a ../utility/libcivutility.a \
../common/aicore/libaicore.a ./generator/libgenerator.a \
- $(USER_DB_LIB) $(SERVER_LIBS)
+ $(AUTH_LIBS) $(SERVER_LIBS)
Index: common/connection.h
===================================================================
--- common/connection.h (revision 11345)
+++ common/connection.h (working copy)
@@ -184,11 +184,12 @@
struct timer_list *ping_timers;
/* Holds number of tries for authentication from client. */
- int authentication_tries;
+ int auth_tries;
- /* the time that the server will reply after receiving an auth reply.
- * this is used to throttle the connection. */
- time_t authentication_stop;
+ /* the time that the server will respond after receiving an auth reply.
+ * this is used to throttle the connection. Also used to reject a
+ * connection if we've waited too long for a password. */
+ time_t auth_settime;
/* used to follow where the connection is in the authentication process */
enum auth_status status;
Index: manual/Makefile.am
===================================================================
--- manual/Makefile.am (revision 11345)
+++ manual/Makefile.am (working copy)
@@ -1,7 +1,7 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = civmanual
-AM_CPPFLAGS = -I$(top_srcdir)/server -I$(top_srcdir)/utility
-I$(top_srcdir)/common -I$(top_srcdir)/ai -I../intl
-I$(top_srcdir)/common/aicore -I$(top_srcdir)/server/userdb
-I$(top_srcdir)/server/generator -I$(top_srcdir)/client
-I$(top_srcdir)/client/include
+AM_CPPFLAGS = -I$(top_srcdir)/server -I$(top_srcdir)/utility
-I$(top_srcdir)/common -I$(top_srcdir)/ai -I../intl
-I$(top_srcdir)/common/aicore -I$(top_srcdir)/server/generator
-I$(top_srcdir)/client -I$(top_srcdir)/client/include
## Above, note -I../intl instead of -I$(top_srcdir/intl) is deliberate.
@@ -13,15 +13,14 @@
civmanual_DEPENDENCIES = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ../server/libcivserver.a \
../utility/libcivutility.a ../common/aicore/libaicore.a \
- ../server/userdb/libuserdb.a ../client/helpdata.o \
- ../server/generator/libgenerator.a
+ ../client/helpdata.o \
+ ../server/generator/libgenerator.a $(AUTH_DEPS)
civmanual_LDADD = ../utility/libcivutility.a ../common/libcivcommon.a \
../ai/libcivai.a ../utility/libcivutility.a ../server/libcivserver.a \
@INTLLIBS@ ../client/helpdata.o \
../utility/libcivutility.a ../common/libcivcommon.a ../ai/libcivai.a \
../utility/libcivutility.a ../server/libcivserver.a \
- ../server/userdb/libuserdb.a \
../utility/libcivutility.a \
../common/aicore/libaicore.a \
../server/generator/libgenerator.a \
- $(SERVER_LIBS)
+ $(SERVER_LIBS) $(AUTH_LIBS)
/**********************************************************************
Freeciv - Copyright (C) 2005 - M.C. Kaufman
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
***********************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_AUTH
#include <mysql/mysql.h>
#endif
#include "auth.h"
#include "connection.h"
#include "fcintl.h"
#include "log.h"
#include "md5.h"
#include "packets.h"
#include "registry.h"
#include "shared.h"
#include "support.h"
#include "auth.h"
#include "connecthand.h"
#include "plrhand.h"
#include "sernet.h"
#include "srv_main.h"
/* where our mysql database is located and how to get to it */
#define HOST "localhost"
#define USER "anonymous"
#define PASSWORD ""
/* the database where our table is located */
#define DATABASE "test"
/* the tables where we will do our lookups and inserts.
* the tables can be created with the following:
*
* CREATE TABLE auth (
* id int(11) NOT NULL auto_increment,
* name varchar(32) default NULL,
* password varchar(32) default NULL,
* email varchar(128) default NULL,
* createtime int(11) default NULL,
* accesstime int(11) default NULL,
* address varchar(255) default NULL,
* createaddress varchar(15) default NULL,
* logincount int(11) default '0',
* PRIMARY KEY (id),
* UNIQUE KEY name (name)
* ) TYPE=MyISAM;
*
* CREATE TABLE loginlog (
* id int(11) NOT NULL auto_increment,
* name varchar(32) default NULL,
* logintime int(11) default NULL,
* address varchar(255) default NULL,
* succeed enum('S','F') default 'S',
* PRIMARY KEY (id)
* ) TYPE=MyISAM;
*
* N.B. if the tables are not of this format, then the select,insert,
* and update syntax in the auth_db_* functions below must be changed.
*/
#define AUTH_TABLE "auth"
#define LOGIN_TABLE "loginlog"
#define GUEST_NAME "guest"
#define MIN_PASSWORD_LEN 6 /* minimum length of password */
#define MIN_PASSWORD_CAPS 0 /* minimum number of capital letters required */
#define MIN_PASSWORD_NUMS 0 /* minimum number of numbers required */
#define MAX_AUTH_TRIES 3
#define MAX_WAIT_TIME 300 /* max time we'll wait on a password */
/* after each wrong guess for a password, the server waits this
* many seconds to reply to the client */
static const int auth_fail_wait[] = { 1, 1, 2, 3 };
static bool is_good_password(const char *password, char *msg);
static bool authdb_check_password(struct connection *pconn,
const char *password, int len);
static enum authdb_status auth_db_load(struct connection *pconn);
static bool auth_db_save(struct connection *pconn);
/**************************************************
The auth db statuses are:
1: an error occurred, possibly we couldn't access the database file.
2: we were able to successfully insert an entry. or we found the entry
we were searching for
3: the user we were searching for was not found.
***************************************************/
enum authdb_status {
AUTH_DB_ERROR,
AUTH_DB_SUCCESS,
AUTH_DB_NOT_FOUND
};
/**************************************************************************
handle authentication of a user; called by handle_login_request()
if authentication is enabled.
if the connection is rejected right away, return FALSE, otherwise return TRUE
**************************************************************************/
bool authenticate_user(struct connection *pconn, char *username)
{
char tmpname[MAX_LEN_NAME] = "\0";
/* assign the client a unique guest name/reject if guests aren't allowed */
if (is_guest_name(username)) {
if (srvarg.auth_allow_guests) {
sz_strlcpy(tmpname, username);
get_unique_guest_name(username);
if (strncmp(tmpname, username, MAX_LEN_NAME) != 0) {
notify_conn(pconn->self, NULL, E_CONNECTION,
_("Warning: the guest name '%s' has been "
"taken, renaming to user '%s'."), tmpname, username);
}
sz_strlcpy(pconn->username, username);
establish_new_connection(pconn);
} else {
reject_new_connection(_("Guests are not allowed on this server. "
"Sorry."), pconn);
return FALSE;
}
} else {
/* we are not a guest, we need an extra check as to whether a
* connection can be established: the client must authenticate itself */
char buffer[MAX_LEN_MSG];
sz_strlcpy(pconn->username, username);
switch(auth_db_load(pconn)) {
case AUTH_DB_ERROR:
if (srvarg.auth_allow_guests) {
sz_strlcpy(tmpname, pconn->username);
get_unique_guest_name(tmpname); /* don't pass pconn->username here */
sz_strlcpy(pconn->username, tmpname);
freelog(LOG_ERROR, "Error reading database; connection -> guest");
notify_conn(pconn->self, NULL, E_CONNECTION,
_("There was an error reading the user "
"database, logging in as guest connection '%s'."),
pconn->username);
establish_new_connection(pconn);
} else {
reject_new_connection(_("There was an error reading the user database "
"and guest logins are not allowed. Sorry"),
pconn);
return FALSE;
}
break;
case AUTH_DB_SUCCESS:
/* we found a user */
my_snprintf(buffer, sizeof(buffer), _("Enter password for %s:"),
pconn->username);
dsend_packet_authentication_req(pconn, AUTH_LOGIN_FIRST, buffer);
pconn->server.auth_settime = time(NULL);
pconn->server.status = AS_REQUESTING_OLD_PASS;
break;
case AUTH_DB_NOT_FOUND:
/* we couldn't find the user, he is new */
if (srvarg.auth_allow_newusers) {
sz_strlcpy(buffer, _("Enter a new password (and remember it)."));
dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer);
pconn->server.auth_settime = time(NULL);
pconn->server.status = AS_REQUESTING_NEW_PASS;
} else {
reject_new_connection(_("This server allows only preregistered "
"users. Sorry."), pconn);
return FALSE;
}
break;
default:
assert(0);
break;
}
return TRUE;
}
return TRUE;
}
/**************************************************************************
Receives a password from a client and verifies it.
**************************************************************************/
bool handle_authentication_reply(struct connection *pconn, char *password)
{
char msg[MAX_LEN_MSG];
if (pconn->server.status == AS_REQUESTING_NEW_PASS) {
/* check if the new password is acceptable */
if (!is_good_password(password, msg)) {
if (pconn->server.auth_tries++ >= MAX_AUTH_TRIES) {
reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
return FALSE;
} else {
dsend_packet_authentication_req(pconn, AUTH_NEWUSER_RETRY, msg);
return TRUE;
}
}
/* the new password is good, create a database entry for
* this user; we establish the connection in handle_db_lookup */
sz_strlcpy(pconn->server.password, password);
if (!auth_db_save(pconn)) {
notify_conn(pconn->self, NULL, E_CONNECTION,
_("Warning: There was an error in saving to the database. "
"Continuing, but your stats will not be saved."));
freelog(LOG_ERROR, "Error writing to database for: %s", pconn->username);
}
establish_new_connection(pconn);
} else if (pconn->server.status == AS_REQUESTING_OLD_PASS) {
if (authdb_check_password(pconn, password, strlen(password)) == 1) {
pconn->server.status = AS_ESTABLISHED;
establish_new_connection(pconn);
} else {
pconn->server.status = AS_FAILED;
pconn->server.auth_tries++;
pconn->server.auth_settime = time(NULL)
+ auth_fail_wait[pconn->server.auth_tries];
}
} else {
freelog(LOG_VERBOSE, "%s is sending unrequested auth packets",
pconn->username);
return FALSE;
}
return TRUE;
}
/**************************************************************************
checks on where in the authentication process we are.
**************************************************************************/
void process_authentication_status(struct connection *pconn)
{
switch(pconn->server.status) {
case AS_NOT_ESTABLISHED:
/* nothing, we're not ready to do anything here yet. */
break;
case AS_FAILED:
/* the connection gave the wrong password, we kick 'em off or
* we're throttling the connection to avoid password guessing */
if (pconn->server.auth_settime > 0
&& time(NULL) >= pconn->server.auth_settime) {
if (pconn->server.auth_tries >= MAX_AUTH_TRIES) {
pconn->server.status = AS_NOT_ESTABLISHED;
reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
close_connection(pconn);
} else {
struct packet_authentication_req request;
pconn->server.status = AS_REQUESTING_OLD_PASS;
request.type = AUTH_LOGIN_RETRY;
sz_strlcpy(request.message,
_("Your password is incorrect. Try again."));
send_packet_authentication_req(pconn, &request);
}
}
break;
case AS_REQUESTING_OLD_PASS:
case AS_REQUESTING_NEW_PASS:
/* waiting on the client to send us a password... don't wait too long */
if (time(NULL) >= pconn->server.auth_settime + MAX_WAIT_TIME) {
pconn->server.status = AS_NOT_ESTABLISHED;
reject_new_connection(_("Sorry, your connection timed out..."), pconn);
close_connection(pconn);
}
break;
case AS_ESTABLISHED:
/* this better fail bigtime */
assert(pconn->server.status != AS_ESTABLISHED);
break;
}
}
/**************************************************************************
see if the name qualifies as a guest login name
**************************************************************************/
bool is_guest_name(const char *name)
{
return (mystrncasecmp(name, GUEST_NAME, strlen(GUEST_NAME)) == 0);
}
/**************************************************************************
return a unique guest name
WARNING: do not pass pconn->username to this function: it won't return!
**************************************************************************/
void get_unique_guest_name(char *name)
{
unsigned int i;
/* first see if the given name is suitable */
if (is_guest_name(name) && !find_conn_by_user(name)) {
return;
}
/* next try bare guest name */
mystrlcpy(name, GUEST_NAME, MAX_LEN_NAME);
if (!find_conn_by_user(name)) {
return;
}
/* bare name is taken, append numbers */
for (i = 1; ; i++) {
my_snprintf(name, MAX_LEN_NAME, "%s%u", GUEST_NAME, i);
/* attempt to find this name; if we can't we're good to go */
if (!find_conn_by_user(name)) {
break;
}
}
}
/**************************************************************************
Verifies that a password is valid. Does some [very] rudimentary safety
checks. TODO: do we want to frown on non-printing characters?
Fill the msg (length MAX_LEN_MSG) with any worthwhile information that
the client ought to know.
**************************************************************************/
static bool is_good_password(const char *password, char *msg)
{
int i, num_caps = 0, num_nums = 0;
/* check password length */
if (strlen(password) < MIN_PASSWORD_LEN) {
my_snprintf(msg, MAX_LEN_MSG,
_("Your password is too short, the minimum length is %d. "
"Try again."), MIN_PASSWORD_LEN);
return FALSE;
}
my_snprintf(msg, MAX_LEN_MSG,
_("The password must have at least %d capital letters, %d "
"numbers, and be at minimum %d [printable] characters long. "
"Try again."),
MIN_PASSWORD_CAPS, MIN_PASSWORD_NUMS, MIN_PASSWORD_LEN);
for (i = 0; i < strlen(password); i++) {
if (my_isupper(password[i])) {
num_caps++;
}
if (my_isdigit(password[i])) {
num_nums++;
}
}
/* check number of capital letters */
if (num_caps < MIN_PASSWORD_CAPS) {
return FALSE;
}
/* check number of numbers */
if (num_nums < MIN_PASSWORD_NUMS) {
return FALSE;
}
if (!is_ascii_name(password)) {
return FALSE;
}
return TRUE;
}
/**************************************************************************
Check if the password with length len matches that given in
pconn->server.password.
***************************************************************************/
static bool authdb_check_password(struct connection *pconn,
const char *password, int len)
{
#ifdef HAVE_AUTH
bool ok = FALSE;
char buffer[512] = "";
unsigned char checksum[DIGEST_HEX_BYTES];
MYSQL *sock, mysql;
/* do the password checking right here */
create_md5sum(password, len, checksum);
ok = (strncmp(checksum, pconn->server.password, DIGEST_HEX_BYTES) == 0)
? TRUE : FALSE;
/* we don't really need the stuff below here to
* verify password, this is just logging */
mysql_init(&mysql);
/* attempt to connect to the server */
if ((sock = mysql_real_connect(&mysql, HOST, USER, PASSWORD,
DATABASE, 0, NULL, 0))) {
/* insert an entry into our log */
my_snprintf(buffer, sizeof(buffer),
"insert into %s (name, logintime, address, succeed) "
"values ('%s',unix_timestamp(),'%s','%s')", LOGIN_TABLE,
pconn->username, pconn->server.ipaddr, ok ? "S" : "F");
if (mysql_query(sock, buffer)) {
freelog(LOG_ERROR, "check_pass insert loginlog failed for user: %s (%s)",
pconn->username, mysql_error(sock));
}
mysql_close(sock);
} else {
freelog(LOG_ERROR, "Can't connect to server! (%s)", mysql_error(&mysql));
}
return ok;
#else
return TRUE;
#endif
}
/**************************************************************************
Loads a user from the database.
**************************************************************************/
static enum authdb_status auth_db_load(struct connection *pconn)
{
#ifdef HAVE_AUTH
char buffer[512] = "";
int num_rows = 0;
MYSQL *sock, mysql;
MYSQL_RES *res;
MYSQL_ROW row;
mysql_init(&mysql);
/* attempt to connect to the server */
if (!(sock = mysql_real_connect(&mysql, HOST, USER, PASSWORD, DATABASE,
0, NULL, 0))) {
freelog(LOG_ERROR, "Can't connect to server! (%s)", mysql_error(&mysql));
return AUTH_DB_ERROR;
}
/* select the password from the entry */
my_snprintf(buffer, sizeof(buffer),
"select password from %s where name = '%s'",
AUTH_TABLE, pconn->username);
if (mysql_query(sock, buffer)) {
freelog(LOG_ERROR, "db_load query failed for user: %s (%s)",
pconn->username, mysql_error(sock));
return AUTH_DB_ERROR;
}
res = mysql_store_result(sock);
num_rows = mysql_num_rows(res);
/* if num_rows = 0, then we could find no such user */
if (num_rows < 1) {
mysql_free_result(res);
mysql_close(sock);
return AUTH_DB_NOT_FOUND;
}
/* if there are more than one row that matches this name, it's an error
* continue anyway though */
if (num_rows > 1) {
freelog(LOG_ERROR, "db_load query found multiple entries (%d) for user: %s",
num_rows, pconn->username);
}
/* if there are rows, then fetch them and use the first one */
row = mysql_fetch_row(res);
mystrlcpy(pconn->server.password, row[0], sizeof(pconn->server.password));
mysql_free_result(res);
/* update the access time for this user */
memset(buffer, 0, sizeof(buffer));
my_snprintf(buffer, sizeof(buffer),
"update %s set accesstime=unix_timestamp(), address='%s', "
"logincount=logincount+1 where strcmp(name, '%s') = 0",
AUTH_TABLE, pconn->server.ipaddr, pconn->username);
if (mysql_query(sock, buffer)) {
freelog(LOG_ERROR, "db_load update accesstime failed for user: %s (%s)",
pconn->username, mysql_error(sock));
}
mysql_close(sock);
#endif
return AUTH_DB_SUCCESS;
}
/**************************************************************************
Saves pconn fields to the database. If the username already exists,
replace the data.
**************************************************************************/
static bool auth_db_save(struct connection *pconn)
{
#ifdef HAVE_AUTH
char buffer[1024] = "";
MYSQL *sock, mysql;
mysql_init(&mysql);
/* attempt to connect to the server */
if (!(sock = mysql_real_connect(&mysql, HOST, USER, PASSWORD, DATABASE,
0, NULL, 0))) {
freelog(LOG_ERROR, "Can't connect to server! (%s)", mysql_error(&mysql));
return FALSE;
}
/* insert new user into table. we insert the following things: name
* md5sum of the password, the creation time in seconds, the accesstime
* also in seconds from 1970, the users address (twice) and the logincount */
my_snprintf(buffer, sizeof(buffer),
"insert into %s values "
"(NULL, '%s', md5('%s'), NULL, unix_timestamp(), unix_timestamp(),"
"'%s', '%s', 0)", AUTH_TABLE, pconn->username,
pconn->server.password, pconn->server.ipaddr, pconn->server.ipaddr);
if (mysql_query(sock, buffer)) {
freelog(LOG_ERROR, "db_save insert failed for new user: %s (%s)",
pconn->username, mysql_error(sock));
mysql_close(sock);
return FALSE;
}
/* insert an entry into our log */
memset(buffer, 0, sizeof(buffer));
my_snprintf(buffer, sizeof(buffer),
"insert into %s (name, logintime, address, succeed) "
"values ('%s',unix_timestamp(),'%s', 'S')", LOGIN_TABLE,
pconn->username, pconn->server.ipaddr);
if (mysql_query(sock, buffer)) {
freelog(LOG_ERROR, "db_load insert loginlog failed for user: %s (%s)",
pconn->username, mysql_error(sock));
}
mysql_close(sock);
#endif
return TRUE;
}
/**********************************************************************
Freeciv - Copyright (C) 2005 - M.C. Kaufman
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
***********************************************************************/
#ifndef FC__AUTH_H
#define FC__AUTH_H
#include "shared.h"
struct connection;
bool is_guest_name(const char *name);
void get_unique_guest_name(char *name);
bool authenticate_user(struct connection *pconn, char *username);
void process_authentication_status(struct connection *pconn);
bool handle_authentication_reply(struct connection *pc, char *password);
#endif /* FC__AUTH_H */
--- auth.c 2005-12-12 22:18:03.000000000 -0600
+++ auth-2.0.c 2005-12-12 23:25:12.000000000 -0600
@@ -132,7 +132,7 @@
get_unique_guest_name(username);
if (strncmp(tmpname, username, MAX_LEN_NAME) != 0) {
- notify_conn(pconn->self, NULL, E_CONNECTION,
+ notify_conn(&pconn->self,
_("Warning: the guest name '%s' has been "
"taken, renaming to user '%s'."), tmpname, username);
}
@@ -158,7 +158,7 @@
sz_strlcpy(pconn->username, tmpname);
freelog(LOG_ERROR, "Error reading database; connection -> guest");
- notify_conn(pconn->self, NULL, E_CONNECTION,
+ notify_conn(&pconn->self,
_("There was an error reading the user "
"database, logging in as guest connection '%s'."),
pconn->username);
@@ -226,7 +226,7 @@
sz_strlcpy(pconn->server.password, password);
if (!auth_db_save(pconn)) {
- notify_conn(pconn->self, NULL, E_CONNECTION,
+ notify_conn(&pconn->self,
_("Warning: There was an error in saving to the database. "
"Continuing, but your stats will not be saved."));
freelog(LOG_ERROR, "Error writing to database for: %s", pconn->username);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Freeciv-Dev] (PR#14851) remove userdb, add mysql to server for authentication,
Mike Kaufman <=
|
|