Modify FastCGI server to use MySQL for auth
authorSam Moore <[email protected]>
Sun, 20 Oct 2013 08:36:01 +0000 (16:36 +0800)
committerSam Moore <[email protected]>
Sun, 20 Oct 2013 08:36:01 +0000 (16:36 +0800)
Made some general improvements to Login_Handler and how the "auth_uri" is dealt with.
Called strsep, but it's not (that) evil since it doesn't do anything I wouldn't have done with boilerplate anyway.

MySQL for auth tested and appears to work. Using SHA6 instead of md5 (!) hashes.

13 files changed:
.gitignore
server/Makefile
server/fastcgi.c
server/login.c
server/main.c
server/options.h
server/parameters
server/run.sh
testing/MCTXWeb/public_html/users/admin_upload_users.php
testing/MCTXWeb/public_html/users/login.php
testing/MCTXWeb/public_html/users/models/db-settings.php
testing/MCTXWeb/public_html/users/models/funcs.php
testing/MCTXWeb/public_html/users/models/header.php

index ae7115d..770ed60 100644 (file)
@@ -17,3 +17,5 @@ server/win32
 **/nbproject/private/
 
 *jquery-ui*
+
+*password*
index 39a71fb..c314b20 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile for server software
 CXX = gcc
-FLAGS = -std=gnu99 -Wall -pedantic -g -I/usr/include/opencv -I/usr/include/opencv2/highgui -L/usr/lib
-LIB = -lfcgi -lssl -lcrypto -lpthread -lm -lopencv_highgui -lopencv_core -lopencv_ml -lopencv_imgproc -lldap -lcrypt
+FLAGS = -std=gnu99 -Wall -pedantic -g -I/usr/include/opencv -I/usr/include/opencv2/highgui -L/usr/lib `mysql_config --cflags`
+LIB = -lfcgi -lssl -lcrypto -lpthread -lm -lopencv_highgui -lopencv_core -lopencv_ml -lopencv_imgproc -lldap -lcrypt `mysql_config --libs`
 OBJ = log.o control.o data.o fastcgi.o main.o sensor.o actuator.o image.o bbb_pin.o pin_test.o login.o sensors/sensors.a actuators/actuators.a
 RM = rm -f
 
index 0a6783f..d7e057b 100644 (file)
@@ -520,8 +520,23 @@ void * FCGI_RequestLoop (void *data)
                snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
                snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
 
+               
+               //char cookies[BUFSIZ];
+               //snprintf(cookies, BUFSIZ, "%s", getenv("COOKIE_STRING"));
+               //Log(LOGDEBUG, "ALL cookies %s", cookies); //mmmm
+
                //Hack to get the nameless cookie only
+               // (works as long as browsers send the nameless cookie first...)
                snprintf(control_key, CONTROL_KEY_BUFSIZ, "%s", getenv("COOKIE_STRING"));
+               // Ignore any other cookies if the nameless cookie is empty
+               for (int i = 0; i < CONTROL_KEY_BUFSIZ; ++i)
+               {
+                       if (control_key[i] == ';')
+                       {
+                               control_key[i] = '\0';
+                               break;
+                       }
+               }
 
                Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params);
                Log(LOGDEBUG, "Control key: %s", control_key);
@@ -559,8 +574,8 @@ void * FCGI_RequestLoop (void *data)
                
                if (module_handler) 
                {
-                       //if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler)
-                       if (false) // Testing
+                       if (g_options.auth_method != AUTH_NONE && module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler)
+                       //if (false) // Testing
                        {
                                if (!FCGI_HasControl(&context, control_key))
                                {
index 78e31f9..c1d981f 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);
+       }
+
+
+       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)
@@ -227,15 +330,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 +363,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 ...
+                       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!");
index fadcd3a..38fc9f0 100644 (file)
@@ -47,7 +47,7 @@ void ParseArguments(int argc, char ** argv)
 
        g_options.auth_method = AUTH_NONE;  // Don't use authentication
        g_options.auth_uri = ""; // 
-       g_options.ldap_base_dn = "";
+       g_options.auth_options = "";
        g_options.experiment_dir = ".";
        
        for (int i = 1; i < argc; ++i)
@@ -72,14 +72,10 @@ void ParseArguments(int argc, char ** argv)
                        case 'p':
                                g_options.enable_pin = !(strtol(argv[++i], &end, 10));
                                break;
-                       // LDAP URI
+                       // Authentication URI and options
                        case 'A':
                                g_options.auth_uri = argv[++i];
                                break;
-                       // LDAP DN
-                       case 'd':
-                               g_options.ldap_base_dn = argv[++i];
-                               break;
                        case 'e':
                        // Experiments directory
                                g_options.experiment_dir = argv[++i];
@@ -93,12 +89,7 @@ void ParseArguments(int argc, char ** argv)
                        Fatal("argv[%d] -%c requires an integer (got \"%s\" instead)", i-1, argv[i-1][0], argv[i]);
        }       
 
-       Log(LOGDEBUG, "Verbosity: %d", g_options.verbosity);
-       Log(LOGDEBUG, "Pin Module Enabled: %d", g_options.enable_pin);
-       Log(LOGDEBUG, "Auth URI: %s", g_options.auth_uri);
-       Log(LOGDEBUG, "LDAP Base DN: %s", g_options.ldap_base_dn);
-       //Log(LOGDEBUG, "Root directory: %s", g_options.root_dir);
-       Log(LOGDEBUG, "Experiment directory: %s", g_options.experiment_dir);
+
 
        if (!DirExists(g_options.experiment_dir))
        {
@@ -107,12 +98,58 @@ void ParseArguments(int argc, char ** argv)
 
        if (g_options.auth_uri[0] != '\0')
        {
-               //HACK...
-               if (PathExists(g_options.auth_uri))
-                       g_options.auth_method = AUTH_SHADOW;
-               else
-                       g_options.auth_method = AUTH_LDAP;
+               // Get the options part of the URI if it exists
+               char * c = (char*)g_options.auth_uri;
+               while (*(++c) != '\0' && *c != '#');
+               
+               if (*(c++) == '#')
+               {
+                       *(c-1) = '\0';
+                       g_options.auth_options = c;
+               }
+
+               // Use the first part of the URI to identify the protocol:
+               c = (char*)g_options.auth_uri;
+               while (*(++c) != '\0' && *c != ':');
+
+               if (*c == '\0') // No ':' means no protocol; use plaintext file
+               {
+                       g_options.auth_method = AUTH_SHADOW;                    
+               }
+               else if (*c == ':' && *(c+1) == '/' && *(c+2) == '/')
+               {
+                       
+                       *c = '\0';
+                       if (strcmp(g_options.auth_uri, "ldap") == 0 || strcmp(g_options.auth_uri, "ldaps") == 0)
+                       {
+                               *c = ':'; // LDAP URI's require the prodocol as part of the string
+                               g_options.auth_method = AUTH_LDAP;
+                       }
+                       else if (strcmp(g_options.auth_uri, "mysql") == 0)
+                       {
+                               g_options.auth_uri = c+3; // MySQL doesn't (just a hostname)
+                               g_options.auth_method = AUTH_MYSQL;
+                       }
+                       else
+                       {
+                               Fatal("Unsupported authentication method %s", g_options.auth_uri);
+                       }
+               }
        }
+       else
+       {
+               Log(LOGWARN, "No authentication method.");
+       }
+
+       Log(LOGDEBUG, "Verbosity: %d", g_options.verbosity);
+       Log(LOGDEBUG, "Pin Module Enabled: %d", g_options.enable_pin);
+       Log(LOGDEBUG, "Auth method: %d", g_options.auth_method);
+       Log(LOGDEBUG, "Auth URI: %s", g_options.auth_uri);
+       Log(LOGDEBUG, "Auth Options: %s", g_options.auth_options);
+       //Log(LOGDEBUG, "Root directory: %s", g_options.root_dir);
+       Log(LOGDEBUG, "Experiment directory: %s", g_options.experiment_dir);
+
+
        
 }
 
index 0e0b598..88496cc 100644 (file)
@@ -25,11 +25,11 @@ typedef struct
        /** URI for authentication **/
        const char * auth_uri;
 
-       /** Base DN for LDAP authentication **/
-       const char * ldap_base_dn;
+       /** Additional options for authentication (to be parsed in Login_Handler) **/
+       const char * auth_options;
 
        /** Authentication method **/
-       enum {AUTH_NONE, AUTH_LDAP, AUTH_SHADOW} auth_method;
+       enum {AUTH_NONE, AUTH_LDAP, AUTH_SHADOW, AUTH_MYSQL} auth_method;
 
        /** Experiments directory **/
        const char *experiment_dir;
index da48239..99b3f7e 100644 (file)
@@ -17,18 +17,16 @@ LOGDEBUG=4
 verbosity="$LOGDEBUG"
 
 # Set to 1/0 to enable/disable the pin module (gives direct control over GPIO/ADC/PWM)
+#TODO: This option isn't actually implemented yet...
 pin_test="0"
 
 # Set to the URI to use authentication
-#auth_uri="ldap://192.168.1.1"
-#auth_uri="ldaps://ldap.pheme.uwa.edu.au" #UWA
-#auth_uri="/etc/shadow"
-auth_uri="shadow"
-
-# Set to the dn of the LDAP server
-ldap_base_dn="ou=People,dc=daedalus" # Testing
-#ldap_base_dn="ou=Users,ou=UWA,dc=uwads,dc=uwa,dc=edu,dc=au" #UWA
+#auth_uri="ldap://192.168.1.1#ou=People,dc=daedalus"
+#auth_uri="ldaps://ldap.pheme.uwa.edu.au#ou=Users,ou=UWA,dc=uwads,dc=uwa,dc=edu,dc=au" #UWA
+auth_uri="/etc/shadow"
+#auth_uri="shadow"
+#auth_uri="mysql://localhost#root,$(cat mysql_password)"
 
 
 ## OPTIONS TO BE PASSED TO SERVER; DO NOT EDIT
-parameters="-v $verbosity -p $pin_test -A $auth_uri -d $ldap_base_dn"
+parameters="-v $verbosity -p $pin_test -A $auth_uri"
index 76a7a48..1890059 100755 (executable)
@@ -6,13 +6,16 @@ if [ ! -e "server" ]; then
         exit 1
 fi
 
+# Get the parameters
+. parameters
+echo "Parameters are: $parameters"
 
 if [[ "$(uname -m)" != *arm*  ]]; then
         echo Not running on the BBB
         # Use this to quickly test run the server in valgrind
         #spawn-fcgi -p9005 -n ./valgrind.sh
         # Use this to run the server normally
-        spawn-fcgi -p9005 -n ./server
+        spawn-fcgi -p9005 -n -- ./server $parameters
         exit 0
 fi
 
@@ -75,10 +78,7 @@ fi;
 #adc_device_path=$(dirname $(find /sys -name *AIN0))
 
 
-# Get the parameters
-. parameters
 
-echo "Parameters are: $parameters"
 
 # Run the program with parameters
 # TODO: Can tell spawn-fcgi to run the program as an unprivelaged user?
index dd4f28c..d068f71 100644 (file)
@@ -4,6 +4,7 @@ require_once("models/config.php");
 if (!securePage($_SERVER['PHP_SELF'])){die();}
 
 require_once("models/header.php");
+createPage("User Upload");
 
 
 if (!empty($_POST))
index f0168da..2c45d1f 100644 (file)
@@ -47,6 +47,8 @@ if(!empty($_POST))
                        {\r
                                //Hash the password and use the salt from the database to compare the password.\r
                                $entered_pass = generateHash($password,$userdetails["password"]);\r
+\r
+                               echo "".$userdetails["password"];\r
                                \r
                                if($entered_pass != $userdetails["password"])\r
                                {\r
index 6027314..0df3b94 100644 (file)
@@ -8,7 +8,7 @@ http://usercake.com
 $db_host = "localhost"; //Host address (most likely localhost)\r
 $db_name = "users"; //Name of Database\r
 $db_user = "root"; //Name of database user\r
-$db_pass = "NOT_THE_PASSWORD"; //Password for database user\r
+$db_pass = file_get_contents("mysql_password"); // TODO: Create that file or replace this line.\r
 $db_table_prefix = "uc_";\r
 \r
 GLOBAL $errors;\r
index 021f3a0..07af426 100644 (file)
@@ -71,14 +71,13 @@ function generateHash($plainText, $salt = null)
 {\r
        if ($salt === null)\r
        {\r
-               $salt = substr(md5(uniqid(rand(), true)), 0, 25);\r
+               //$salt = substr(md5(uniqid(rand(), true)), 0, 25); // Original UserCake\r
+               $random = file_get_contents("/dev/urandom", false, null, 0, 25); // Get random number\r
+               $salt = '$6$'.bin2hex($random).'$'; // Make hex salt\r
+               \r
        }\r
-       else\r
-       {\r
-               $salt = substr($salt, 0, 25);\r
-       }\r
-       \r
-       return $salt . sha1($salt . $plainText);\r
+       //return $salt . sha1($salt . $plainText); // Original UserCake\r
+       return crypt($plainText, $salt);\r
 }\r
 \r
 //Checks if an email is valid\r
index ee78a54..239a10a 100644 (file)
@@ -4,14 +4,35 @@ UserCake Version: 2.0.2
 http://usercake.com\r
 */\r
 echo "\r
-<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\r
-<html xmlns='http://www.w3.org/1999/xhtml'>\r
-<head>\r
-<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\r
-<title>".$websiteName."</title>\r
-<link href='".$template."' rel='stylesheet' type='text/css' />\r
-<script src='models/funcs.js' type='text/javascript'>\r
-</script>\r
-</head>";\r
+  <!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\r
+  <html xmlns='http://www.w3.org/1999/xhtml'>\r
+  <head>\r
+  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\r
+  <title>".$websiteName."</title>\r
+  <link href='".$template."' rel='stylesheet' type='text/css' />\r
+  <script src='models/funcs.js' type='text/javascript'>\r
+  </script>\r
+  </head>\r
+  <body>";\r
+\r
+/**\r
+ * Make the remainder of the page\r
+ */\r
+function createPage($title)\r
+{\r
+  echo "\r
+  <div id='wrapper'>\r
+  <div id='top'><div id='logo'></div></div>\r
+  <div id='content'>\r
+  <h1>User Managment</h1>\r
+  <h2>$title</h2>\r
+  <div id='left-nav'>";\r
+\r
+  include("left-nav.php");\r
+\r
+  echo "\r
+  </div>\r
+  <div id='main'>";\r
+}\r
 \r
 ?>\r

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