X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=server%2Flogin.c;h=f3209facc74f15c2de5e692941893569ef456920;hb=15a32ab1123375e1a52c319fca71ab8d02c58261;hp=78e31f9f0c4f071ab14d6c7e6c6bf0578d799b84;hpb=f7e1e1e4b7c22ef34702cff9b01025612809aab8;p=matches%2FMCTX3420.git diff --git a/server/login.c b/server/login.c index 78e31f9..f3209fa 100644 --- a/server/login.c +++ b/server/login.c @@ -4,21 +4,129 @@ */ - - -#include "login.h" +#include "common.h" #include "options.h" #include #include +// LDAP stuff #define LDAP_DEPRECATED 1 // Required to use ldap_simple_bind_s #include +// MySQL stuff +#undef _GNU_SOURCE // HACK to silence compiler warning on redefinition in my_global.h +#include +#include + + + + + +/** + * Attempt to login by searching a MySQL database + * @param user - Username + * @param pass - Password + * @param db_host - Host running the DataBase + * @param db_user - User to search the database as + * @param db_pass - Password for the database user + * @param db_name - Name of the database to use + * @param db_table - Table to search in + * @returns Privelage level of the user or USER_UNAUTH for failure to authenticate + */ +UserType Login_MySQL(const char * user, const char * pass, + const char * db_host, const char * db_user, const char * db_pass, const char * db_name, const char * db_table) +{ + MYSQL * con = mysql_init(NULL); + if (con == NULL) + { + Log(LOGERR, "mysql_init failed - %s", mysql_error(con)); + return USER_UNAUTH; + } + + if (mysql_real_connect(con, db_host, db_user, db_pass, NULL, 0, NULL, 0) == NULL) + { + Log(LOGERR, "mysql_real_connect failed - %s", mysql_error(con)); + mysql_close(con); + return USER_UNAUTH; + } + + char buffer[BUFSIZ]; + + // Select the database + sprintf(buffer, "USE %s;", db_name); + if (mysql_query(con, buffer)) + { + Log(LOGERR, "mysql_query failed - %s", mysql_error(con)); + mysql_close(con); + return USER_UNAUTH; + } + + // Search for the user + sprintf(buffer, "SELECT password FROM %s WHERE user_name = \"%s\";", db_table, user); + if (mysql_query(con, buffer)) + { + Log(LOGERR, "mysql_query failed - %s", mysql_error(con)); + mysql_close(con); + return USER_UNAUTH; + } + + // Process the result + MYSQL_RES * result = mysql_store_result(con); + if (result == NULL) + { + Log(LOGERR, "mysql_store_result failed - %s", mysql_error(con)); + mysql_close(con); + return USER_UNAUTH; + } + + int num_fields = mysql_num_fields(result); + if (num_fields != 1) + { + Log(LOGERR, "The database may be corrupt; %d fields found, expected %d", num_fields, 1); + mysql_close(con); + return USER_UNAUTH; + } + + UserType user_type = USER_UNAUTH; + MYSQL_ROW row; + + // Get first row + if ((row = mysql_fetch_row(result))) + { + if (strcmp(crypt(pass, row[0]), row[0]) == 0) + { + user_type = USER_NORMAL; + } + + // There should only be one row. Through a hissy fit if we see any more. + if ((row = mysql_fetch_row(result))) + { + Log(LOGERR, "Too many rows found."); + user_type = USER_UNAUTH; + } + } + else + { + Log(LOGERR, "No user matching %s", user); + } + + //TODO: Handle administrator users somehow better than this + // UserCake stores the permission level in a seperate table to the username/password, which is annoying + if (user_type != USER_UNAUTH && strcmp(user, "admin") == 0) + { + user_type = USER_ADMIN; + } + mysql_free_result(result); + mysql_close(con); + return user_type; +} + /** * Attempt to login using a file formatted like /etc/shadow * This is here... because all better options have been exhausted * @param user - The username * @param pass - The password + * @param shadow - The file to use * @returns Privelage level of the user or USER_UNAUTH for failure to authenticate */ UserType Login_Shadow(const char * user, const char * pass, const char * shadow) @@ -62,6 +170,8 @@ UserType Login_Shadow(const char * user, const char * pass, const char * shadow) passwd_index = -1; } + fclose(f); + if (passwd_index <= 0) { //Log(LOGDEBUG,"No user found matching %s\n", user); @@ -170,14 +280,18 @@ int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass) void Logout_Handler(FCGIContext * context, char * params) { FCGI_ReleaseControl(context); - FCGI_AcceptJSON(context, "Logged out", "0"); + FCGI_SendControlCookie(context, false); //Unset the cookie + FCGI_AcceptJSON(context, "Logged out"); } /** * Handle a Login Request * @param context - The context - * @param params - Parameter string, should contain username and password + * @param params - Parameter string, should contain username and password. + * NOTE: Care should be taken when using params, as it is + * completely unescaped. Do not log or use it without + * suitable escaping. */ void Login_Handler(FCGIContext * context, char * params) { @@ -219,7 +333,7 @@ void Login_Handler(FCGIContext * context, char * params) case AUTH_LDAP: { - if (strlen(pass) <= 0) + if (*pass == '\0') { FCGI_RejectJSON(context, "No password supplied."); return; @@ -227,15 +341,17 @@ void Login_Handler(FCGIContext * context, char * params) //TODO: Generate the DN in some sane way char dn[BUFSIZ]; + + // On a simple LDAP server: - //int len = sprintf(dn, "uid=%s,%s", user, g_options.ldap_base_dn); + //int len = sprintf(dn, "uid=%s,%s", user, g_options.auth_options); // At UWA (hooray) char * user_group = "Students"; if (user[0] == '0') user_group = "Staff"; - int len = sprintf(dn, "cn=%s,ou=%s,%s", user, user_group, g_options.ldap_base_dn); + int len = sprintf(dn, "cn=%s,ou=%s,%s", user, user_group, g_options.auth_options); if (len >= BUFSIZ) @@ -258,6 +374,35 @@ void Login_Handler(FCGIContext * context, char * params) user_type = Login_Shadow(user, pass, g_options.auth_uri); break; } + case AUTH_MYSQL: + { + //WARNING: C string manipulation code approaching! + // Non reentrent; uses strsep and modifies g_options.auth_options + // If problems happen, try strdup first ... + static char * db_opts[] = {"root", "", "users", "uc_users"}; + static bool db_init_opts = false; + if (!db_init_opts) + { + db_init_opts = true; + db_opts[0] = (char*)g_options.auth_options; + for (int i = 1; i < sizeof(db_opts)/sizeof(char*); ++i) + { + char * def = db_opts[i]; + db_opts[i] = db_opts[i-1]; + + strsep(db_opts+i, ","); + if (db_opts[i] == NULL) + { + db_opts[i] = def; + break; + } + } + //Log(LOGDEBUG, "MySQL: user %s pass %s name %s table %s", db_opts[0], db_opts[1], db_opts[2], db_opts[3]); + } + + user_type = Login_MySQL(user, pass, g_options.auth_uri, db_opts[0],db_opts[1], db_opts[2], db_opts[3]); + break; + } default: { Log(LOGWARN, "No authentication!"); @@ -280,7 +425,8 @@ void Login_Handler(FCGIContext * context, char * params) { FCGI_EscapeText(context->user_name); //Don't break javascript pls // Give the user a cookie - FCGI_AcceptJSON(context, "Logged in", context->control_key); + FCGI_SendControlCookie(context, true); //Send the control key + FCGI_AcceptJSON(context, "Logged in"); Log(LOGDEBUG, "Successful authentication for %s", user); } else