*/
-
-
-#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)
/**
* 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)
{
case AUTH_LDAP:
{
- if (strlen(pass) <= 0)
+ if (*pass == '\0')
{
FCGI_RejectJSON(context, "No password supplied.");
return;
//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)
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!");
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)
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];
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))
{
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);
+
+
}
{
Log(LOGDEBUG, "Begin cleanup.");
Sensor_Cleanup();
- //Actuator_Cleanup();
+ Actuator_Cleanup();
Log(LOGDEBUG, "Finish cleanup.");
}
- Sensor_Init();
- Actuator_Init();
Pin_Init();
// Try and start things
- const char *ret;
- if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL)
- Fatal("Control_SetMode failed with '%s'", ret);
+ //const char *ret;
+ //if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL)
+ // Fatal("Control_SetMode failed with '%s'", ret);
// run request thread in the main thread
FCGI_RequestLoop(NULL);
- if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL)
- Fatal("Control_SetMode failed with '%s'", ret);
+ Control_SetMode(CONTROL_STOP, NULL);
+ //if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL)
+ // Fatal("Control_SetMode failed with '%s'", ret);
//Sensor_StopAll();
//Actuator_StopAll();