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.
# 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
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)
{
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
#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>
#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
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);
*/
const struct timeval* Control_GetStartTime() {
return &g_controls.start_time;
-}
\ No newline at end of file
+}
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);
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
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?
#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
* @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);
}
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;
}
if (context->control_key[0] != '\0')
{
- FCGI_RejectJSON(context, "Already logged in");
+ FCGI_RejectJSON(context, "Already logged in.");
return;
}
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);
+
}
*/
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)
{
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;
+ }
+
}
/**
/** 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;
--- /dev/null
+#!/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"