From f858232d7c564f14e6d2fb9d616f8e12a1ec9171 Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Tue, 1 Oct 2013 18:28:39 +0800 Subject: [PATCH] Work on Authentication System(s) 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. --- server/Makefile | 2 +- server/bbb_pin.c | 4 +- server/common.h | 13 +++- server/control.c | 5 +- server/fastcgi.c | 19 ++++- server/image.c | 12 ++++ server/log.h | 2 +- server/login.c | 173 ++++++++++++++++++++++++++++++++++++---------- server/main.c | 51 +++++++++++--- server/options.h | 18 +++-- server/parameters | 31 +++++++++ server/run.sh | Bin 2637 -> 3000 bytes 12 files changed, 270 insertions(+), 60 deletions(-) create mode 100644 server/parameters diff --git a/server/Makefile b/server/Makefile index a87fdcd..244490b 100644 --- a/server/Makefile +++ b/server/Makefile @@ -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 diff --git a/server/bbb_pin.c b/server/bbb_pin.c index 4d1276b..7248c5a 100644 --- a/server/bbb_pin.c +++ b/server/bbb_pin.c @@ -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 diff --git a/server/common.h b/server/common.h index 0092598..d4defd9 100644 --- a/server/common.h +++ b/server/common.h @@ -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 #include #include @@ -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 diff --git a/server/control.c b/server/control.c index 5c8a410..4b4a1e6 100644 --- a/server/control.c +++ b/server/control.c @@ -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 +} diff --git a/server/fastcgi.c b/server/fastcgi.c index 0252f78..08c413b 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -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 diff --git a/server/image.c b/server/image.c index 263ce07..dafe375 100644 --- a/server/image.c +++ b/server/image.c @@ -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? diff --git a/server/log.h b/server/log.h index cc038a0..e4fd17f 100644 --- a/server/log.h +++ b/server/log.h @@ -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 diff --git a/server/login.c b/server/login.c index 615be9e..a616af2 100644 --- a/server/login.c +++ b/server/login.c @@ -3,47 +3,110 @@ * @brief Implementation of Login related functionality */ -#define _BSD_SOURCE + + #include "login.h" +#include "options.h" #include +#include #define LDAP_DEPRECATED 1 // Required to use ldap_simple_bind_s #include -#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); + } diff --git a/server/main.c b/server/main.c index 2ad9dcc..ecdf068 100644 --- a/server/main.c +++ b/server/main.c @@ -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; + } + } /** diff --git a/server/options.h b/server/options.h index 05f7ddf..839bc4d 100644 --- a/server/options.h +++ b/server/options.h @@ -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 index 0000000..9f33848 --- /dev/null +++ b/server/parameters @@ -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" diff --git a/server/run.sh b/server/run.sh index 4318b398230178066c2e385d7cc789ead34976e6..c2bd504f95067bf1df36d47c75566ba7dd18c43b 100755 GIT binary patch delta 709 zcmX>rvO|2r0S*Nyn0QbI!MuFq}8L0{diA9OIsU@jJ#aw#G94<(l1i+;g5{pu;6jTuE zl_uwM+(uaM$*EY6B+JFgnOanoUu3Id54SioO(9xANd+vfq+n~Spk$z=5Ni!`M;=@e z-1`-oB?<;`;j~NyGchx#*j7bDLqR1C#8l8$FjUagYaRu(+c6Rznq?oTmT<| BnmYgh delta 320 zcmdlXepY0{0Y=V=CuD#Gj~OH9WGzNzATv#kk5gGszqq6*H8EE~jf->gbVfG?PL0&$ zjC=*9pwc`A6$6FDVuhmo{1QDSO$9?cH6x%RZYF1-5n)X7jGUWmnN%5poK?)0K=L)S z3Q$O#WjRRb7^^N5C+B2lHggc=#iqu=nVM6a$_3=~vgvSgre#_K*+Ap&K~$)++aXjm zu$u#o*w5|>Bn3Fk#5uuEa?MLj%E`=2S8#IDb#!qC>dof(GI<&2b)c$NE=7A~-9!Z? zmBf_f_>|PL%;ePgg2a*xB`(g)G=*rOguRl2t*wHRfs#V3wL(coY97%3)QZd!1p_Xi ZYk;Qla7zG9(&4rOl6l;o>