summaryrefslogtreecommitdiff
path: root/daemon/basic.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/basic.c')
-rw-r--r--daemon/basic.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/daemon/basic.c b/daemon/basic.c
new file mode 100644
index 0000000..a8325af
--- /dev/null
+++ b/daemon/basic.c
@@ -0,0 +1,243 @@
+
+/* On some linux this is required to get crypt to show itself */
+#define _XOPEN_SOURCE
+
+#include "usuals.h"
+#include "httpauthd.h"
+#include "defaults.h"
+
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define BASIC_MAXLINE 128
+
+/* This needs to be the same as an MD5 hash length */
+#define BASIC_HASH_KEY_LEN 16
+#define BASIC_ESTABLISHED (void*)1
+
+/* -------------------------------------------------------------------------------
+ * Structures
+ */
+
+typedef struct basic_context
+{
+ const char* filename; /* The file name with the user names */
+ const char* realm; /* The realm for basic authentication */
+
+ /* Context ----------------------------------------------------------- */
+ hash_t* established; /* Established connections */
+}
+basic_context_t;
+
+/* -------------------------------------------------------------------------------
+ * Handler Functions
+ */
+
+int basic_config(ha_context_t* context, const char* name, const char* value)
+{
+ basic_context_t* ctx = (basic_context_t*)(context.data);
+
+ if(strcmp(name, "basicfile") == 0)
+ {
+ ctx->filename = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "realm") == 0)
+ {
+ ctx->realm = value;
+ return HA_OK;
+ }
+
+ return HA_FALSE;
+}
+
+int basic_init(ha_context_t* context)
+{
+ /* Don't do global init */
+ if(!context)
+ return HA_OK;
+
+ basic_context_t* ctx = (basic_context_t*)(context.data);
+ int fd;
+
+ /* Make sure there are some types of authentication we can do */
+ if(!(context->types & HA_TYPE_BASIC))
+ {
+ ha_messagex(LOG_ERR, "Basic module configured, but does not implement any "
+ "configured authentication type.");
+ return HA_ERROR;
+ }
+
+ /* Check to make sure the file exists */
+ if(!ctx->filename)
+ {
+ ha_messagex(LOG_ERR, "Basic configuration incomplete. "
+ "Must have a BasicFile configured.");
+ return HA_ERROR;
+ }
+
+ fd = open(ctx->filename, O_RDONLY);
+ if(fd == -1)
+ {
+ ha_message(LOG_ERR, "can't open file for basic authentication: %s", ctx->filename);
+ return HA_ERROR;
+ }
+
+ close(fd);
+
+ /* Initialize our cache table */
+ if(!(ctx->established = hash_create(BASIC_HASH_KEY_LEN)))
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+int basic_process(ha_context_t* context, ha_request_t* req,
+ ha_response_t* resp, ha_buffer_t* buf)
+{
+ basic_context_t* ctx = (basic_context_t*)(context.data);
+ const char* header;
+ char* t;
+ int found = 0;
+ ha_basic_header_t basic;
+
+ memset(&basic, 0, sizeof(basic));
+
+ ha_lock(NULL);
+
+ /* Purge the cache */
+ hash_purge(ctx->established, time(NULL) - context->timeout);
+
+ ha_unlock(NULL);
+
+
+ /*
+ * Init checked and made sure basic auth is enabled, so we
+ * can take that for granted here.
+ */
+
+ header = ha_getheader(req, "Authorization", BASIC_PREFIX);
+ if(header)
+ {
+ if(ha_parsebasic(header, buf, &basic) == HA_ERROR)
+ return HA_ERROR;
+
+ /* Check and see if this connection is in the cache */
+ ha_lock(NULL);
+
+ if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED)
+ {
+ hash_touch(ctx->established, basic.key);
+ found = 1;
+ }
+
+ ha_unlock(NULL);
+ }
+
+
+ /* If we have a user name and password that wasn't in the cache */
+ if(!found && basic.user && basic.user[0] &&
+ basic.password && basic.password[0])
+ {
+ FILE* f;
+ char line[BASIC_MAXLINE];
+
+ f = fopen(ctx->filename, "r");
+ if(!f)
+ {
+ ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
+ resp->code = HA_SERVER_ERROR;
+ return HA_FALSE;
+ }
+
+ /*
+ * Note: There should be no returns or jumps between
+ * this point and the closing of the file below.
+ */
+
+ /* Now look through the whole file */
+ while(!feof(f))
+ {
+ fgets(line, BASIC_MAXLINE, f);
+
+ if(ferror(f))
+ ha_message(LOG_ERR, "error reading basic password file");
+
+ t = strchr(line, ':');
+ if(t)
+ {
+ /* Split the line */
+ *t = 0;
+ t++;
+
+ /* Check the user */
+ if(strcmp(line, basic.user) == 0)
+ {
+ /* Not sure if crypt is thread safe so we lock */
+ ha_lock();
+
+ /* Check the password */
+ if(strcmp(crypt(basic.password, t), t) == 0)
+ found = 1;
+
+ ha_unlock();
+
+ if(found)
+ break;
+ }
+ }
+ }
+
+ fclose(f);
+ }
+
+ /* If we found a user then tell them what it was */
+ if(found)
+ {
+ resp->code = HA_SERVER_ACCEPT;
+ resp->detail = basic.user;
+
+ /* We put this connection into the successful connections */
+ if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED))
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+ }
+
+ /* Otherwise make the browser authenticate */
+ else
+ {
+ resp->code = HA_SERVER_DECLINE;
+
+ ha_bufnext(buf);
+ ha_bufcat(buf, "BASIC realm=\"", ctx->realm ? ctx->realm : "",
+ "\"", NULL);
+
+ ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
+ }
+
+ return HA_OK;
+}
+
+
+/* -------------------------------------------------------------------------------
+ * Handler Definition
+ */
+
+ha_handler_t basic_handler =
+{
+ "Basic", /* The type */
+ basic_init, /* Initialization function */
+ basic_destroy, /* Uninitialization routine */
+ basic_config, /* Config routine */
+ basic_process, /* Processing routine */
+ NULL, /* A default context */
+ sizeof(basic_context_t) /* Size of the context */
+};
+