Work on Authentication System(s)
authorSam Moore <[email protected]>
Tue, 1 Oct 2013 10:28:39 +0000 (18:28 +0800)
committerSam Moore <[email protected]>
Tue, 1 Oct 2013 10:28:39 +0000 (18:28 +0800)
Login_Handler can currently use one of two methods. LDAP, or unix style authentication.

Unix style authentication parses a file formatted like /etc/shadow. Yeah, you can use /etc/shadow... our program is already running as root anyway :S
LDAP style authentication attempts to bind to an LDAP server.

If Authentication is successful, the key generated by FCGI_LockControl is passed to the browser as a cookie.

In the FCGI_Loop function, we check for a valid key in the cookies.

I've tested LDAP using a LDAP server I setup on my laptop.
We may have issues formatting the DN correctly, but other than that it's fine.

12 files changed:
server/Makefile
server/bbb_pin.c
server/common.h
server/control.c
server/fastcgi.c
server/image.c
server/log.h
server/login.c
server/main.c
server/options.h
server/parameters [new file with mode: 0644]
server/run.sh

index a87fdcd..244490b 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile for server software
 CXX = gcc
 FLAGS = -std=c99 -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
+LIB = -lfcgi -lssl -lcrypto -lpthread -lm -lopencv_highgui -lopencv_core -lopencv_ml -lopencv_imgproc -lldap -lcrypt
 OBJ = log.o control.o data.o fastcgi.o main.o sensor.o actuator.o image.o bbb_pin.o bbb_pin_defines.o pin_test.o login.o
 RM = rm -f
 
index 4d1276b..7248c5a 100644 (file)
@@ -272,7 +272,7 @@ bool ADC_Export(int pin)
                return true;
        }
 
-       sprintf(g_buffer, "%s/in_voltage%d_raw", g_options.adc_device_path, pin);
+       sprintf(g_buffer, "%s/in_voltage%d_raw", ADC_DEVICE_PATH, pin);
        g_adc[pin].fd_value = open(g_buffer, O_RDONLY);
        if (g_adc[pin].fd_value <0)
        {
@@ -486,4 +486,4 @@ bool ADC_Read(int id, int *value)
 bool True_Stub(void *arg, ...) { return true; }
 bool ADC_Read_Stub(int *val, ...) { *val = 0; return true; }
 bool GPIO_Read_Stub(bool *val, ...) { *val = false; return true; }
-#endif
\ No newline at end of file
+#endif
index 0092598..d4defd9 100644 (file)
@@ -6,18 +6,24 @@
 #ifndef _COMMON_H
 #define _COMMON_H
 
+/** Defines required to allow various C standard functions to be used **/
 #define _POSIX_C_SOURCE 200809L
 #define _BSD_SOURCE
 #define _XOPEN_SOURCE 600
 
 /** Determine if we're running on the BBB **/
 #ifdef __arm__
-#define _BBB
-#endif
+       #define _BBB
+#else
+       //#warning This software was designed for the BeagleBone Black. Some features may not work.
+#endif //__arm__
 
 /** The current API version **/
 #define API_VERSION 0
 
+
+
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdbool.h>
@@ -39,6 +45,9 @@
 #define TIMEVAL_DIFF(tv1, tv2) ((tv1).tv_sec - (tv2).tv_sec + 1e-6 * ((tv1).tv_usec - (tv2).tv_usec))
 
 
+extern bool PathExists(const char * path);
+
+
 
 
 #endif //_COMMON_H
index 5c8a410..4b4a1e6 100644 (file)
@@ -15,7 +15,8 @@ typedef struct ControlData {
 
 ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
 
-static bool PathExists(const char *path) {
+bool PathExists(const char *path) 
+{
        FILE *fp = fopen(path, "r");
        if (fp) {
                fclose(fp);
@@ -177,4 +178,4 @@ const char * Control_GetModeName() {
  */
 const struct timeval* Control_GetStartTime() {
        return &g_controls.start_time;
-}
\ No newline at end of file
+}
index 0252f78..08c413b 100644 (file)
@@ -441,14 +441,15 @@ void * FCGI_RequestLoop (void *data)
        while (FCGI_Accept() >= 0) {
                
                ModuleHandler module_handler = NULL;
-               char module[BUFSIZ], params[BUFSIZ], hack[BUFSIZ];
+               char module[BUFSIZ], params[BUFSIZ], cookie[BUFSIZ];
                
                //strncpy doesn't zero-truncate properly
                snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
                snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
-               snprintf(hack, BUFSIZ, "%s", getenv("QUERY_STRING"));
+               snprintf(cookie, BUFSIZ, "%s", getenv("COOKIE_STRING"));
 
                Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params);
+               Log(LOGDEBUG, "Cookie: %s", cookie);
 
 
                
@@ -489,6 +490,20 @@ void * FCGI_RequestLoop (void *data)
 
                if (module_handler) 
                {
+                       if (module_handler != Login_Handler)
+                       {
+                               if (cookie[0] == '\0')
+                               {
+                                       FCGI_RejectJSON(&context, "Please login.");
+                                       continue;
+                               }
+                               if (!FCGI_HasControl(&context, cookie))
+                               {
+                                       FCGI_RejectJSON(&context, "Invalid control key.");
+                                       continue;       
+                               }
+                       }
+
                        module_handler(&context, params);
                } 
                else 
index 263ce07..dafe375 100644 (file)
@@ -16,6 +16,18 @@ void Image_Handler(FCGIContext * context, char * params)
 
        IplImage * frame = cvQueryFrame(capture);
        assert(frame != NULL);
+
+//        CvMat stub;
+ //       CvMat * background = cvGetMat(frame, &stub, 0, 0);
+
+//     CvMat *cv8u = cvCreateMat(frame->width, frame->height, CV_8U);
+//     double min, max;
+//     CvPoint a,b;    
+//     cvMinMaxLoc(background, &min, &max, &a, &b, 0);
+       
+//     double ccscale = 255.0/(max-min);
+//     double ccshift = -min;
+       //cvCvtScale(frame, cv8u, ccscale, ccshift);
        CvMat * jpg = cvEncodeImage(".jpg", frame, p);
 
        // Will this work?
index cc038a0..e4fd17f 100644 (file)
@@ -14,7 +14,7 @@
 #define Abort(...) { LogEx(LOGERR, __func__, __FILE__, __LINE__, __VA_ARGS__); return; }
 #define AbortBool(...) { LogEx(LOGERR, __func__, __FILE__, __LINE__, __VA_ARGS__); return false; }
 
-// An enum to make the severity of log messages human readable in code
+/** An enum to make the severity of log messages human readable in code **/
 enum {LOGERR=0, LOGWARN=1, LOGNOTE=2, LOGINFO=3,LOGDEBUG=4};
 
 extern void LogEx(int level, const char * funct, const char * file, int line,  ...); // General function for printing log messages to stderr
index 615be9e..a616af2 100644 (file)
  * @brief Implementation of Login related functionality
  */
 
-#define _BSD_SOURCE
+
+
 
 #include "login.h"
+#include "options.h"
 #include <ctype.h>
+#include <unistd.h>
 
 #define LDAP_DEPRECATED 1 // Required to use ldap_simple_bind_s
 #include <ldap.h>
 
-#define LDAP_URI "ldaps://ldap.pheme.uwa.edu.au"
-#define LDAP_DN_BASE "ou=Users,ou=UWA,dc=uwads,dc=uwa,dc=edu,dc=au"    
-
 /**
- * Attempt to bind to the LDAP_URI
+ * Attempt to login using a file formatted like /etc/shadow
+ * This is here for horrible hack purposes
  * @param user - The username
  * @param pass - The password
- * @returns An error code according to libldap; LDAP_SUCCESS if everything worked
+ * @returns True if the login was successful, false otherwise
  */
-int Login_LDAP_Bind(const char * user, const char * pass)
+bool Login_Shadow(const char * user, const char * pass, const char * shadow)
 {
+       if (strlen(user) + strlen(pass) >= BUFSIZ-1)
+       {
+               Log(LOGERR, "User/Password too long!\n");
+               return false;
+       }
 
+       FILE * f = fopen(shadow, "r");
+       if (f == NULL)
+       {
+               Log(LOGERR,"Can't open %s - %s\n", shadow, strerror(errno));
+               return false;
+       }
 
-       Log(LOGINFO, "Username: \"%s\"", user);
+       char buffer[BUFSIZ];
+       int passwd_index = -1;
 
-       char dn[BUFSIZ]; // Fill with the DN
+       while (fgets(buffer, BUFSIZ, f) != NULL) // NOTE: Restrict username+password strings to BUFSIZ... what could possibly go wrong?
+       {
 
-       const char * user_type = "Students";
+               Log(LOGDEBUG,"Scanning %d: %s", strlen(buffer), buffer);
+       
+               for (int i = 0; i < BUFSIZ-1; ++i)
+               {
+                       if (buffer[i] == ':')
+                       {
+                               buffer[i] = '\0';
+                               passwd_index = i+1;
+                               break;
+                       }
+               }
 
-       // Staff members have numbers starting in zero
-       if (user[0] == '0') 
+               if (strcmp(user,buffer) == 0)
+               {
+                       Log(LOGDEBUG,"User matches! %s\n", buffer);
+                       break;
+               } 
+               passwd_index = -1;
+       }
+
+       if (passwd_index <= 0)
+       {
+               Log(LOGDEBUG,"No user found matching %s\n", user);
+               return false;
+       }
+
+       for (int i = passwd_index; i < BUFSIZ-1; ++i)
        {
-               user_type = "Staff";    
+               if (buffer[i] == ':' || buffer[i] == '\n')
+               {
+                       buffer[i] = '\0';
+                       
+               }
        }
        
-       if (sprintf(dn, "cn=%s,ou=%s,%s", user, user_type, LDAP_DN_BASE) >= BUFSIZ)
+       // Determine the salt
+       char salt[BUFSIZ];
+       int s = 0; int count = 0;
+       for (int i = passwd_index; i < BUFSIZ-1; ++i)
        {
-               Log(LOGERR,"DN too long; recompile with increased BUFSIZ");     
+               salt[s++] = buffer[i];
+               if (salt[s] == '$' && ++count >= 3)
+                       break;
        }
 
+       Log(LOGDEBUG,"Salted Entry: %s\n", buffer+passwd_index);
+       Log(LOGDEBUG,"Salted Attempt: %s\n", crypt(pass, salt));
+       
+       return (strcmp(crypt(pass, salt), buffer+passwd_index) == 0);
+}
+
+/**
+ * Attempt to bind to a LDAP uri
+ * @param uri - The uri
+ * @param dn - The DN
+ * @param pass - The password
+ * @returns An error code according to libldap; LDAP_SUCCESS if everything worked
+ */
+int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass)
+{
+       Log(LOGDEBUG, "Bind to %s with dn %s and pass %s", uri, dn, pass);
+
        // Initialise LDAP; prepares to connect to the server
        LDAP * ld = NULL;
-       int err = ldap_initialize(&ld, LDAP_URI);
+       int err = ldap_initialize(&ld, uri);
        if (err != LDAP_SUCCESS || ld == NULL)
        {
                Log(LOGERR,"ldap_initialize failed - %s (ld = %p)", ldap_err2string(err), ld);
@@ -68,13 +131,14 @@ int Login_LDAP_Bind(const char * user, const char * pass)
        }
        else
        {
-               Log(LOGDEBUG, "Successfully bound to %s with username %s", LDAP_URI, user);
+               Log(LOGDEBUG, "Successfully bound to %s with dn %s", uri, dn);
        }
 
-       err = ldap_unbind_s(ld);
-       if (err != LDAP_SUCCESS)
+       int err2 = ldap_unbind_s(ld);
+       if (err2 != LDAP_SUCCESS)
        {
-               Log(LOGERR, "ldap_unbind_s failed - %s", ldap_err2string(err));
+               Log(LOGERR, "ldap_unbind_s failed - %s", ldap_err2string(err2));
+               err = err2;
        }
        return err;
 }
@@ -100,7 +164,7 @@ void Login_Handler(FCGIContext * context, char * params)
 
        if (context->control_key[0] != '\0')
        {
-               FCGI_RejectJSON(context, "Already logged in");
+               FCGI_RejectJSON(context, "Already logged in.");
                return;
        }
 
@@ -136,26 +200,65 @@ void Login_Handler(FCGIContext * context, char * params)
        for (i = 0; i < BUFSIZ && isalnum(user[i]) && user[i] != '\0'; ++i);
        user[i] = '\0';
 
-       if (strlen(pass) <= 0)
+
+
+       
+       bool authenticated = true;
+       
+       switch (g_options.auth_method)
        {
-               FCGI_RejectJSON(context, "No password supplied.");
-               return;
-       }
 
-       // Try to authenticate
-       int err = Login_LDAP_Bind(user, pass);
+               case AUTH_LDAP:
+               {
+                       if (strlen(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);
+       
+                       // At UWA (hooray)
+                       //char * user_type = (user[0] != '0') : "Students" ? "Staff";
+                       //int len = sprintf(dn, "cn=%s,ou=%s", user, user_type, g_options.ldap_dn_base);
+               
+
+                       if (len >= BUFSIZ)
+                       {
+                               FCGI_RejectJSON(context, "DN too long! Recompile with increased BUFSIZ");
+                       }
+               
+                       authenticated = (Login_LDAP_Bind(g_options.auth_uri, dn, pass) == LDAP_SUCCESS);
+                       break;
+               }
+               case AUTH_SHADOW:
+               {
+                       authenticated = Login_Shadow(user, pass, g_options.auth_uri);
+                       break;
+               }
+               default:
+               {
+                       Log(LOGWARN, "No authentication!");
+                       break;
+               }
+       }
+               
        // error check  
        
-       if (err == LDAP_SUCCESS)
+       if (!authenticated)
        {
-               FCGI_LockControl(context, false);
+               FCGI_RejectJSON(context, "Authentication failure.");
+               return;
        }
-       
 
-       FCGI_BeginJSON(context, STATUS_OK);
-       FCGI_JSONPair("user", user);
-       FCGI_JSONPair("login", ldap_err2string(err));
-       FCGI_JSONPair("key", context->control_key);
-       FCGI_EndJSON();
+       FCGI_LockControl(context, false);
+       
+       // Give the user a cookie
+       FCGI_PrintRaw("Content-type: text\r\n");
+       FCGI_PrintRaw("Set-Cookie: %s\r\n\r\n", context->control_key);
+       
 }
index 2ad9dcc..ecdf068 100644 (file)
@@ -28,15 +28,18 @@ Options g_options; // options passed to program through command line arguments
  */
 void ParseArguments(int argc, char ** argv)
 {
-       // horrible horrible hacks
-       g_options.argc = argc;
-       g_options.argv = argv;
+
 
        g_options.program = argv[0]; // program name
        g_options.verbosity = LOGDEBUG; // default log level
        gettimeofday(&(g_options.start_time), NULL); // Start time
-       g_options.adc_device_path = ADC_DEVICE_PATH;
-       Log(LOGDEBUG, "Called as %s with %d arguments.", g_options.program, argc);
+
+
+       g_options.auth_method = AUTH_NONE;  // Don't use authentication
+       g_options.auth_uri = ""; // 
+       g_options.ldap_base_dn = "";
+       
+
 
        for (int i = 1; i < argc; ++i)
        {
@@ -49,18 +52,48 @@ void ParseArguments(int argc, char ** argv)
                if (strlen(argv[i]) > 2)
                        Fatal("Human readable switches are not supported.");
 
+               char * end = NULL;
                switch (argv[i][1])
                {
-                       case 'a':
-                               g_options.adc_device_path = argv[i+1];
-                               Log(LOGINFO, "ADC Device Path: %s", argv[i+1]);
-                               ++i;
+                       // Set program verbosity
+                       case 'v':
+                               g_options.verbosity = strtol(argv[++i], &end, 10);
+                               break;
+                       // Enable/Disable pin test
+                       case 'p':
+                               g_options.enable_pin = !(strtol(argv[++i], &end, 10));
+                               break;
+                       // LDAP URI
+                       case 'A':
+                               g_options.auth_uri = argv[++i];
+                               break;
+                       // LDAP DN
+                       case 'd':
+                               g_options.ldap_base_dn = argv[++i];
                                break;
                        default:
                                Fatal("Unrecognised switch %s", argv[i]);
                                break;
                }
+
+               if (end != NULL && *end != '\0')
+                       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);
+
+       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;
+       }
+       
 }
 
 /**
index 05f7ddf..839bc4d 100644 (file)
@@ -19,13 +19,19 @@ typedef struct
        /** Time at which program exits **/
        struct timeval end_time;
 
-       /** Path to ADC files **/
-       const char * adc_device_path;
+       /** Whether or not to enable the pin_test module **/
+       bool enable_pin;
+       
+       /** URI for authentication **/
+       const char * auth_uri;
 
-       /*** Horrible horrible hack ***/
-       int argc;
-       /*** Horrible horrible hack ***/
-       char ** argv;
+       /** Base DN for LDAP authentication **/
+       const char * ldap_base_dn;
+
+       /** Authentication method **/
+       enum {AUTH_NONE, AUTH_LDAP, AUTH_SHADOW} auth_method;
+
+       
 
 } Options;
 
diff --git a/server/parameters b/server/parameters
new file mode 100644 (file)
index 0000000..9f33848
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# This script sets the options interpreted by the server at runtime.
+# Enclose any settings that have whitespace with "quotation marks".
+
+
+## DO NOT EDIT THESE OPTIONS
+LOGERR=0
+LOGWARN=1
+LOGNOTE=2
+LOGINFO=3
+LOGDEBUG=4
+
+## OPTIONS PASSED TO SERVER
+
+# Set the verbosity of log messages
+verbosity="$LOGDEBUG"
+
+# Set to 1/0 to enable/disable the pin module (gives direct control over GPIO/ADC/PWM)
+pin_test="0"
+
+# Set to the URI to use authentication
+auth_uri="ldap://192.168.1.1"
+#auth_uri="/etc/shadow"
+
+# Set to the dn of the LDAP server
+ldap_base_dn="ou=People,dc=daedalus"
+
+
+## OPTIONS TO BE PASSED TO SERVER; DO NOT EDIT
+parameters="-v $verbosity -p $pin_test -A $auth_uri -d $ldap_base_dn"
index 4318b39..c2bd504 100755 (executable)
Binary files a/server/run.sh and b/server/run.sh differ

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