summaryrefslogtreecommitdiff
path: root/daemon/httpauthd.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/httpauthd.c')
-rw-r--r--daemon/httpauthd.c1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c
new file mode 100644
index 0000000..8af5d7f
--- /dev/null
+++ b/daemon/httpauthd.c
@@ -0,0 +1,1031 @@
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <err.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+
+#include "usuals.h"
+#include "httpauthd.h"
+
+/* -----------------------------------------------------------------------
+ * Handlers Registered Here
+ */
+
+extern ha_handler_t basic_handler;
+
+/* This is the list of all available handlers */
+const ha_handler_t* g_handlerlist[] =
+{
+ &basic_handler
+};
+
+typedef struct httpauth_loaded
+{
+ ha_context_t ctx;
+ struct httpauth_loaded* next;
+}
+httpauth_loaded_t;
+
+/* The list of handlers in use */
+httpauth_loaded_t* g_handlers = NULL;
+
+/* -----------------------------------------------------------------------
+ * Structures and Constants
+ */
+
+/* A command definition. Used in parsing */
+typedef struct httpauth_command
+{
+ const char* name;
+ int code;
+ int args;
+ const char** headers;
+}
+httpauth_command_t;
+
+/* The various valid headers for the auth command */
+const char* kAuthHeaders[] =
+{
+ "Authorization",
+ "Proxy-Authorization",
+ NULL
+};
+
+/* The command definitions */
+const httpauth_command_t kCommands[] =
+{
+ { "auth", REQTYPE_AUTH, 2, kAuthHeaders },
+ { "quit", REQTYPE_QUIT, 0, 0 },
+ { NULL, -1, -1 }
+};
+
+typedef struct httpauth_thread
+{
+ pthread_t tid;
+ int fd;
+}
+httpauth_thread_t;
+
+/* -----------------------------------------------------------------------
+ * Default Settings
+ */
+
+#define DEFAULT_CONFIG "/usr/local/etc/httpauthd.conf"
+#define DEFAULT_SOCKET "/var/run/httpauthd.sock"
+#define DEFAULT_MAXTHREADS 32
+
+
+/* -----------------------------------------------------------------------
+ * Globals
+ */
+
+int g_daemonized = 0; /* Currently running as a daemon */
+int g_debugging = 0; /* In debug mode */
+const char* g_socket = DEFAULT_SOCKET; /* The socket to communicate on */
+int g_maxthreads = DEFAULT_MAXTHREADS; /* The maximum number of threads */
+
+/* For main loop and signal handlers */
+int g_quit = 0;
+
+/* The main thread */
+pthread_t g_mainthread;
+
+/* The main mutex */
+pthread_mutex_t g_mutex;
+pthread_mutexattr_t g_mutexattr;
+
+/* -----------------------------------------------------------------------
+ * Forward Declarations
+ */
+
+int usage();
+void* httpauth_thread(void* arg);
+int httpauth_processor(int ifd, int ofd);
+int process_auth(ha_request_t* req, ha_response_t* resp,
+ ha_buffer_t* outb);
+int config_parse(const char* file, ha_buffer_t* buf);
+
+
+/* -----------------------------------------------------------------------
+ * Main Program
+ */
+
+int main(int argc, char* argv[])
+{
+ const char* conf = DEFAULT_CONFIG;
+ httpauth_thread_t* threads = NULL;
+ const httpauth_loaded_t* h;
+ int daemonize = 1;
+ ha_buffer_t cbuf;
+ int r, i, sock;
+ int ch = 0;
+
+ /* Keep note of the main thread */
+ g_mainthread = pthread_self();
+
+ /* Create the main mutex */
+ if(pthread_mutexattr_init(&g_mutexattr) != 0 ||
+ pthread_mutexattr_settype(&g_mutexattr, HA_MUTEX_TYPE) ||
+ pthread_mutex_init(&g_mutex, &g_mutexattr) != 0)
+ errx(1, "threading problem. can't create mutex");
+
+ /* Parse the arguments nicely */
+ while((ch = getopt(argc, argv, "df:X")) != -1)
+ {
+ switch(ch)
+ {
+ /* Don't daemonize */
+ case 'd':
+ daemonize = 0;
+ break;
+
+ /* The configuration file */
+ case 'f':
+ conf = optarg;
+ break;
+
+ /* Process console input instead */
+ case 'X':
+ g_debugging = 1;
+ break;
+
+ /* Usage information */
+ case '?':
+ default:
+ return usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 0)
+ return usage();
+
+
+ /* From here on out we need to quit in an orderly fashion */
+
+ /* Run global initialization on all handlers */
+ for(i = 0; i < countof(g_handlerlist); i++)
+ {
+ if(g_handlerlist[i]->f_init)
+ {
+ if((r = (g_handlerlist[i]->f_init)(NULL)) == -1)
+ goto finally;
+ }
+ }
+
+ /* Initialize our configuration buffer */
+ ha_bufinit(&cbuf);
+
+
+ /* Parse the configuration */
+ config_parse(conf, &cbuf);
+
+
+ if(!g_debugging)
+ {
+ struct sockaddr_un sau;
+
+ /* Create the thread buffers */
+ threads = (httpauth_thread_t*)malloc(sizeof(httpauth_thread_t) * g_maxthreads);
+ if(!threads)
+ errx(1, "out of memory");
+
+ /* Create the socket */
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(sock < 0)
+ err(1, "couldn't open socket");
+
+
+ /* Unlink the socket file if it exists */
+ /* TODO: Is this safe? */
+ unlink(g_socket);
+
+ /* Setup the socket */
+ strncmp(sau.sun_path, g_socket, sizeof(sau.sun_path));
+ sau.sun_path[sizeof(sau.sun_path) - 1] = 0;
+
+ /* Bind to the socket */
+ if(bind(sock, (struct sockaddr*)&sau,
+ sizeof(sau) - (sizeof(sau.sun_path) - strlen(sau.sun_path))))
+ err(1, "couldn't bind to socket: %s", sau.sun_path);
+
+
+ /* Let 5 connections queue up */
+ if(listen(sock, 5) != 0)
+ err(1, "couldn't listen on socket");
+ }
+
+
+ /* TODO: Enable signal processing here */
+
+ /* Initialize all the handlers */
+ for(h = g_handlers; h; h = h->next)
+ {
+ if(h->ctx.handler->f_init)
+ {
+ if((r = (h->ctx.handler->f_init)(&(h->ctx))) == -1)
+ goto finally;
+ }
+ }
+
+
+ /* This is for debugging the internal processes */
+ if(g_debugging)
+ {
+ r = httpauth_processor(0, 1);
+ goto finally;
+ }
+
+
+ /* This is the daemon section of the code */
+ else
+ {
+ /* TODO: Daemonize here, and disconnect from terminal */
+
+ /* Open the system log */
+ openlog("httpauthd", 0, LOG_AUTHPRIV);
+ g_daemonized = 1;
+
+
+ /* Now loop and accept the connections */
+ while(!g_quit)
+ {
+ int fd;
+
+ /* TODO: A nice way to break out of the loop here */
+
+ fd = accept(sock, 0, 0);
+ if(fd == -1)
+ {
+ switch(errno)
+ {
+ case EAGAIN:
+ case EINTR:
+ break;
+
+ case ECONNABORTED:
+ case EPROTO:
+ ha_message(LOG_ERR, "couldn't accept a connection");
+ break;
+
+ default:
+ ha_message(LOG_ERR, "couldn't accept a connection");
+ g_quit = 1;
+ break;
+ };
+
+ continue;
+ }
+
+ /* Look for thread and also clean up others */
+ for(i = 0; i < g_maxthreads; i++)
+ {
+ /* Clean up quit threads */
+ if(threads[i].tid != 0)
+ {
+ if(threads[i].fd == 0)
+ {
+ pthread_join(threads[i].tid, NULL);
+ threads[i].tid = 0;
+ }
+ }
+
+ /* Start a new thread if neccessary */
+ if(fd != 0 && threads[i].tid == 0)
+ {
+ threads[i].fd = fd;
+ r = pthread_create(&(threads[i].tid), NULL, httpauth_thread,
+ (void*)(threads + i));
+ if(r != 0)
+ {
+ errno = r;
+ ha_message(LOG_ERR, "couldn't create thread");
+ g_quit = 1;
+ break;
+ }
+
+ fd = 0;
+ }
+ }
+
+ /* Check to make sure we have a thread */
+ if(fd != 0)
+ {
+ ha_messagex(LOG_ERR, "too many connections open (max %d)", g_maxthreads);
+ httpauth_respond(fd, HA_SERVER_ERROR, "too many connections");
+ shutdown(fd, SHUT_RDWR);
+ }
+ }
+
+ /* TODO: Quit all threads here */
+
+ r = 0;
+ }
+
+finally:
+
+ /* Uninitialize all the handlers */
+ for(h = g_handlers; h; h = h->next)
+ {
+ if(h->ctx.handler->f_destroy)
+ (h->ctx.handler->f_destroy)(&(h->ctx));
+ }
+
+ /* Run global destroy for all handlers */
+ for(i = 0; i < countof(g_handlerlist); i++)
+ {
+ if(g_handlerlist[i]->f_destroy)
+ (g_handlerlist[i]->f_destroy)(NULL);
+ }
+
+ /* Clean up memory and stuff */
+ ha_buffree(&cbuf);
+
+
+ /* Close the mutex */
+ pthread_mutex_destroy(&g_mutex);
+ pthread_mutexattr_destroy(&g_mutexattr);
+ return r == -1 ? 1 : 0;
+}
+
+int usage()
+{
+ fprintf(stderr, "usage: httpauthd [-dX] [-f conffile]\n");
+ return 2;
+}
+
+void* httpauth_thread(void* arg)
+{
+ int fd = (int)arg;
+ int r = httpauth_processor(fd, fd);
+ return (void*)r;
+}
+
+/* -----------------------------------------------------------------------
+ * Command Parsing and Handling
+ */
+
+int httpauth_read(int ifd, ha_request_t* req,
+ ha_buffer_t* buf)
+{
+ const httpauth_command_t* cmd;
+ char* t;
+ int i, r;
+ int more = 1;
+
+ /* Clean up the request header */
+ memset(req, 0, sizeof(*req));
+ req->type = -1;
+
+ /* This guarantees a bit of memory allocated, and resets buffer */
+ ha_bufreset(buf);
+
+ r = ha_readline(ifd, buf);
+ if(r == -1)
+ return -1;
+
+ /* Check if this is the last line */
+ if(r == 0)
+ more = 0;
+
+ /* Check to see if we got anything */
+ if(ha_buflen(buf) == 0)
+ {
+ req->type = REQTYPE_IGNORE;
+ return more;
+ }
+
+ /* Find the first space in the line */
+ t = ha_parseword(buf, " \t");
+
+ if(t)
+ {
+ /* Figure out which command it is */
+ for(cmd = kCommands; cmd->name; cmd++)
+ {
+ if(strcasecmp(t, cmd->name) == 0)
+ {
+ req->type = cmd->code;
+ break;
+ }
+ }
+ }
+
+ /* Check for invalid command */
+ if(req->type == -1)
+ return more;
+
+ /* Now parse the arguments if any */
+ for(i = 0; i < cmd->args; i++)
+ req->args[i] = ha_parseword(buf, " \t");
+
+
+ /* Now skip anything else we have in the buffer */
+ ha_bufskip(buf);
+
+
+ /* If we need headers, then read them now */
+ if(cmd->headers)
+ {
+ const char** head; /* For iterating through valid headers */
+ int valid = 0; /* The last header was valid */
+ i = 0; /* The header we're working with */
+
+ for(;;)
+ {
+ /* Make sure we have more data */
+ if(!more)
+ break;
+
+ r = ha_readline(ifd, buf);
+ if(r == -1)
+ return -1;
+
+ /* Check if this is the last line */
+ if(r == 0)
+ more = 0;
+
+ /* An empty line is the end of the headers */
+ if(ha_buflen(buf) == 0)
+ break;
+
+ /* Check if the header starts with a space */
+ if(isspace(ha_bufchar(buf)))
+ {
+ /* Skip all the spaces */
+ while(ha_buflen(buf) > 0 && isspace(ha_bufchar(buf)))
+ ha_bufeat(buf);
+
+ /* An empty line is the end of the headers
+ even if that line has spaces on it */
+ if(ha_buflen(buf) == 0)
+ break;
+
+ /* A header that has data on it but started
+ with a space continues the previous header */
+ if(valid && i > 0)
+ {
+ t = ha_parseline(buf, 0);
+ if(t)
+ {
+ char* t2 = (char*)req->headers[i - 1].data + strlen(req->headers[i - 1].data);
+
+ /* Fill the area between the end of the last
+ valid header and this with spaces */
+ memset(t2, ' ', t - t2);
+ }
+ }
+ }
+ else
+ {
+ if(i < MAX_HEADERS)
+ {
+ t = ha_parseword(buf, ":");
+
+ if(t)
+ {
+ for(head = cmd->headers; ; head++)
+ {
+ if(!(*head))
+ {
+ t = NULL;
+ break;
+ }
+
+ if(strcasecmp(t, *head) == 0)
+ break;
+ }
+ }
+
+ if(t)
+ {
+ req->headers[i].name = t;
+ req->headers[i].data = ha_parseline(buf, 1);
+ i++;
+ }
+
+ valid = (t != NULL) ? 1 : 0;
+ }
+ }
+
+ ha_bufskip(buf);
+ }
+ }
+
+ return more;
+}
+
+int write_data(int ofd, const char* data)
+{
+ int r;
+
+ while(*data != 0)
+ {
+ r = write(ofd, data, strlen(data));
+
+ if(r > 0)
+ data += r;
+
+ else if(r == -1)
+ {
+ if(errno == EAGAIN || errno == EINTR)
+ continue;
+
+ /* The other end closed. no message */
+ if(errno != EPIPE)
+ ha_message(LOG_ERR, "couldn't write data");
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int httpauth_respond(int ofd, int code, const char* msg)
+{
+ const char* t;
+ char num[16];
+
+ sprintf(num, "%d", code);
+
+ if(write_data(ofd, num) == -1 ||
+ write_data(ofd, " ") == -1)
+ return -1;
+
+ switch(code)
+ {
+ case HA_SERVER_ERROR:
+ t = "Internal Error ";
+ break;
+ case HA_SERVER_BADREQ:
+ t = "Bad Request ";
+ break;
+ case HA_SERVER_DECLINE:
+ t = "Unauthorized ";
+ break;
+ default:
+ t = NULL;
+ break;
+ };
+
+ if(t && write_data(ofd, t) == -1)
+ return -1;
+
+ if(msg)
+ {
+ if(write_data(ofd, "[") == -1 ||
+ write_data(ofd, msg) == -1 ||
+ write_data(ofd, "]") == -1)
+ return -1;
+ }
+
+ return write_data(ofd, "\n");
+}
+
+const char kHeaderDelimiter[] = ": ";
+
+int httpauth_write(int ofd, ha_response_t* resp)
+{
+ int i;
+ int wrote = 0;
+
+ if(httpauth_respond(ofd, resp->code, resp->detail) == -1)
+ return -1;
+
+ for(i = 0; i < MAX_HEADERS; i++)
+ {
+ if(resp->headers[i].name)
+ {
+ if(write_data(ofd, resp->headers[i].name) == -1 ||
+ write_data(ofd, kHeaderDelimiter) == -1 ||
+ write_data(ofd, resp->headers[i].data) == -1 ||
+ write_data(ofd, "\n") == -1)
+ return -1;
+
+ wrote = 1;
+ }
+ }
+
+ if(wrote && write_data(ofd, "\n") == -1)
+ return -1;
+
+ return 0;
+}
+
+int httpauth_ready(int ofd, ha_buffer_t* buf)
+{
+ const char* t;
+ httpauth_loaded_t* h;
+
+ /* We send a ready banner to our client */
+ ha_bufnext(buf);
+
+ for(h = g_handlers; h; h = h->next)
+ ha_bufcat(buf, (h == g_handlers) ? "" : " ",
+ h->ctx.name, NULL);
+
+ if(ha_buferr(buf))
+ return httpauth_respond(ofd, HA_SERVER_ERROR, NULL);
+ else
+ return httpauth_respond(ofd, HA_SERVER_READY, ha_bufdata(buf));
+}
+
+int httpauth_processor(int ifd, int ofd)
+{
+ ha_buffer_t inb;
+ ha_buffer_t outb;
+ ha_request_t req;
+ ha_response_t resp;
+ int result = -1;
+ int r;
+
+ /* Initialize the memory buffers */
+ ha_bufinit(&inb);
+ ha_bufinit(&outb);
+
+ if(httpauth_ready(ofd, &outb) == -1)
+ {
+ result = 1;
+ goto finally;
+ }
+
+ /* Now loop and handle the commands */
+ while(result == -1)
+ {
+ ha_bufreset(&outb);
+ ha_bufreset(&inb);
+
+ r = httpauth_read(ifd, &req, &inb);
+ if(r == -1 || ha_buferr(&inb))
+ {
+ httpauth_respond(ofd, HA_SERVER_ERROR, NULL);
+ result = 1;
+ continue;
+ }
+
+ if(r == 0)
+ result = 0;
+
+ switch(req.type)
+ {
+ case REQTYPE_AUTH:
+
+ r = process_auth(&req, &resp, &outb);
+ if(r == -1 || ha_buferr(&outb))
+ {
+ httpauth_respond(ofd, HA_SERVER_ERROR, NULL);
+ result = 1;
+ continue;
+ }
+
+ if(httpauth_write(ofd, &resp) == -1)
+ {
+ result = 1;
+ continue;
+ }
+
+ break;
+
+
+ case REQTYPE_QUIT:
+ result = 0;
+ break;
+
+
+ default:
+ if(httpauth_respond(ofd, HA_SERVER_BADREQ, "Unknown command") == -1)
+ {
+ result = -1;
+ continue;
+ }
+
+ break;
+ };
+ }
+
+ if(ifd == ofd)
+ shutdown(ofd, SHUT_RDWR);
+ else
+ close(ofd);
+
+finally:
+ ha_buffree(&outb);
+ ha_buffree(&inb);
+
+ return result;
+}
+
+int process_auth(ha_request_t* req, ha_response_t* resp,
+ ha_buffer_t* outb)
+{
+ const httpauth_loaded_t* h;
+
+ /* Clear out our response */
+ memset(resp, 0, sizeof(*resp));
+
+ /* Check our connection argument */
+ if(!req->args[1] || !(req->args[1][0]))
+ {
+ ha_messagex(LOG_ERR, "Missing connection ID in request");
+ resp->detail = "Missing connection ID";
+ resp->code = HA_SERVER_BADREQ;
+ return 0;
+ }
+
+ /* Find a handler for this type */
+ for(h = g_handlers; h; h = h->next)
+ {
+ if(strcasecmp(h->ctx.name, req->args[0]) == 0)
+ {
+ /* Now let the handler handle it */
+ if(h->ctx.handler->f_process)
+ return (h->ctx.handler->f_process)(&(h->ctx), req, resp, outb);
+
+ return 0;
+ }
+ }
+
+ ha_messagex(LOG_ERR, "Unknown authentication type: %s", req->args[0]);
+ resp->detail = "Unknown authentication type";
+ resp->code = HA_SERVER_BADREQ;
+ return -1;
+}
+
+/* -----------------------------------------------------------------------
+ * Configuration
+ */
+
+ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
+ const ha_handler_t* handler, const ha_context_t* defaults)
+{
+ httpauth_loaded_t* loaded;
+ int len = sizeof(httpauth_loaded_t) + handler->context_size;
+
+ loaded = (httpauth_loaded_t*)ha_bufmalloc(buf, len);
+ if(!loaded)
+ errx(1, "out of memory");
+
+ memset(loaded, 0, len);
+ memcpy(&(loaded->ctx), defaults, sizeof(ha_context_t));
+
+ if(handler->context_size)
+ {
+ void* mem = ((unsigned char*)loaded) + sizeof(httpauth_loaded_t);
+
+ /* Initialize the defaults properly */
+ if(handler->context_default)
+ memcpy(mem, handler->context_default, handler->context_size);
+
+ loaded->ctx.data = mem;
+ }
+
+ else
+ {
+ loaded->ctx.data = NULL;
+ }
+
+ loaded->ctx.name = (char*)alias;
+ loaded->ctx.handler = handler;
+
+ if(!g_handlers)
+ {
+ g_handlers = loaded;
+ }
+ else
+ {
+ httpauth_loaded_t* l = g_handlers;
+
+ for(;;)
+ {
+ if(strcasecmp(alias, l->ctx.name) == 0)
+ errx(1, "duplicate handler section for '%s'", alias);
+
+ if(!(l->next))
+ break;
+
+ l = l->next;
+ }
+
+ l->next = loaded;
+ }
+
+ return &(loaded->ctx);
+}
+
+
+int config_parse(const char* file, ha_buffer_t* buf)
+{
+ ha_context_t defaults;
+ ha_context_t* ctx = NULL;
+ int line = 0;
+ int fd;
+ const char** t;
+ char* name;
+ const char* value;
+ int more = 1;
+ int recog;
+ int r, i;
+
+ /* Open the configuration file */
+ fd = open(file, O_RDONLY);
+ if(fd == -1)
+ err(1, "couldn't open configuration file: %s", file);
+
+ /* These are the default options for the contexts */
+ memset(&defaults, 0, sizeof(defaults));
+ defaults.types = 0xFFFFFFFF; /* All types by default */
+ defaults.timeout = DEFAULT_TIMEOUT; /* Timeout for cache */
+
+ ha_bufreset(buf);
+
+ /* Read each line and process */
+ while(more)
+ {
+ ha_bufskip(buf);
+
+ if((more = ha_readline(fd, buf)) == -1)
+ return -1;
+
+ line++;
+
+ /* Eat all white space at beginning of line */
+ while(ha_buflen(buf) && isspace(ha_bufchar(buf)))
+ ha_bufeat(buf);
+
+ /* Skip blank lines */
+ if(ha_buflen(buf) == 0)
+ continue;
+
+ /* Skip comment lines */
+ if(ha_bufchar(buf) == '#')
+ continue;
+
+ /* Check for a handler */
+ if(ha_bufchar(buf) == '[')
+ {
+ const ha_handler_t* handler = NULL;
+ const char* x;
+
+ ha_bufeat(buf);
+ name = ha_parseline(buf, 1);
+
+ if(!name || name[strlen(name) - 1] != ']')
+ errx(1, "configuration section invalid (line %d)", line);
+
+
+ /* remove the bracket */
+ name[strlen(name) - 1] = 0;
+
+
+ /* Look for a handler with this type */
+ for(i = 0; i < countof(g_handlerlist); i++)
+ {
+ if(g_handlerlist[i] && g_handlerlist[i]->type &&
+ strcasecmp(name, g_handlerlist[i]->type) == 0)
+ {
+ handler = g_handlerlist[i];
+ break;
+ }
+ }
+
+ if(handler == NULL)
+ errx(1, "unknown handler type '%s' (line %d)", name, line);
+
+ /* If we had a last handler then add it to handlers */
+ loaded = config_addhandler(buf, name, handler, defaults);
+
+ /* Rest doesn't apply to handler headers */
+ continue;
+ }
+
+
+ /* Parse out the name */
+ name = ha_parseword(buf, ":");
+ if(!name || !name[0])
+ errx(1, "configuration file invalid (line %d)", line);
+
+ strlwr(name);
+
+ /* And get the rest of the line */
+ value = ha_parseline(buf, 1);
+ if(value == NULL)
+ errx(1, "configuration missing value at (line %d)", line);
+
+
+ recog = 0;
+
+ /* Is this the global section? */
+ if(!ctx)
+ {
+ /* Look and see if that's a name we want */
+ if(strcmp("socket", name) == 0)
+ {
+ g_socket = value;
+ recog = 1;
+ }
+
+ else if(strcmp("maxthreads", name) == 0)
+ {
+ if(ha_confint(value, 1, 256, &g_maxthreads) == -1)
+ errx(1, "invalid value for '%s'. must be a number between 1 and 256", name);
+ recog = 1;
+ }
+
+ }
+
+ /* Otherwise we're in a handler */
+ else
+ {
+ if(strcmp("alias", name) == 0)
+ {
+ loaded->alias = value;
+ recog = 1;
+ }
+
+ if(loaded->ctx.handler->f_config)
+ {
+ r = (loaded->ctx.handler->f_config)(&(loaded->ctx), name, value);
+ if(r == -1)
+ return -1;
+
+ if(!recog && r)
+ recog = 1;
+ }
+ }
+
+ /* Options that are legal in both global and internal sections */
+ if(!recog)
+ {
+ if(strcmp(name, "cachetimeout") == 0)
+ {
+ int v;
+ if(ha_confint(name, value, 0, 86400, &v) == HA_ERROR)
+ exit(1); /* Message already printed */
+
+ (ctx ? ctx : &defaults)->timeout = v;
+ recog = 1;
+ }
+
+ else if(strcmp(name, "authtypes") == 0)
+ {
+ int types = 0;
+ char* t;
+
+ strlwr(value);
+
+ /* Split the line into tokens at the spaces */
+ while(*value)
+ {
+ while(*value && isspace(*value))
+ value++;
+
+ t = value;
+
+ while(*t && !isspace(*t))
+ t++;
+
+ if(strncmp(value, "basic", 5) == 0)
+ types |= HA_TYPE_BASIC;
+
+ else if(strncmp(value, "digest", 6) == 0)
+ types |= HA_TYPE_DIGEST;
+
+ else if(strncmp(value, "ntlm", 4) == 0)
+ types |= HA_TYPE_NTLM;
+
+ else
+ errx(1, "invalid type for '%s': %s (line %d)", name, value, line);
+
+ value = t;
+ }
+
+ if(types == 0)
+ errx(1, "no authentication types for '%s' (line %d)", name, line);
+
+ (ctx ? ctx : &defaults)->types = types;
+ recog = 1;
+ }
+ }
+
+ if(!recog)
+ errx(1, "unrecognized configuration setting '%s' (line %d)", name, line);
+ }
+
+ if(!g_handlers)
+ ha_messagex(LOG_INFO, "no handlers found in configuration file");
+
+ return 0;
+}