Update Titlepage with links to individual sections
[matches/MCTX3420.git] / server / login.c
index 78e31f9..f3209fa 100644 (file)
  */
 
 
-
-
-#include "login.h"
+#include "common.h"
 #include "options.h"
 #include <ctype.h>
 #include <unistd.h>
 
+// LDAP stuff
 #define LDAP_DEPRECATED 1 // Required to use ldap_simple_bind_s
 #include <ldap.h>
 
+// MySQL stuff
+#undef _GNU_SOURCE // HACK to silence compiler warning on redefinition in my_global.h
+#include <my_global.h>
+#include <mysql.h>
+
+
+
+
+
+/**
+ * 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

UCC git Repository :: git.ucc.asn.au