#include #include #include #include #include #include #include #include #include #include #include #include #include "usuals.h" #include "httpauthd.h" #include "defaults.h" #include "sock_any.h" /* * This shouldn't be used by handlers, * they should return HA_FAILED instead. */ #define HA_SERVER_ERROR 500 /* ----------------------------------------------------------------------- * Handlers Registered Here */ extern ha_handler_t simple_handler; extern ha_handler_t ldap_handler; extern ha_handler_t ntlm_handler; /* This is the list of all available handlers */ ha_handler_t* g_handlerlist[] = { &simple_handler, &ldap_handler, &ntlm_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", NULL }; /* The command definitions */ const httpauth_command_t kCommands[] = { { "auth", REQTYPE_AUTH, 4, kAuthHeaders }, { "set", REQTYPE_SET, 2, 0 }, { "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_console = 0; /* debug mode read write from console */ int g_debuglevel = LOG_ERR; /* what gets logged to console */ 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 */ static int usage(); static void* httpauth_thread(void* arg); static int httpauth_processor(int ifd, int ofd); static int httpauth_respond(int ofd, int scode, int ccode, const char* msg); static int process_auth(ha_request_t* req, ha_response_t* resp, ha_buffer_t* outb); static int config_parse(const char* file, ha_buffer_t* buf); static void on_quit(int signal); /* ----------------------------------------------------------------------- * Main Program */ int main(int argc, char* argv[]) { const char* conf = DEFAULT_CONFIG; httpauth_thread_t* threads = NULL; 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, "d:f:X")) != -1) { switch(ch) { /* Don't daemonize */ case 'd': { char* t; daemonize = 0; g_debuglevel = strtol(optarg, &t, 10); if(*t || g_debuglevel > 4) errx(1, "invalid debug log level"); g_debuglevel += LOG_ERR; } break; /* The configuration file */ case 'f': conf = optarg; break; /* Process console input instead */ case 'X': g_console = 1; daemonize = 0; break; /* Usage information */ case '?': default: return usage(); break; } } argc -= optind; argv += optind; if(argc != 0) return usage(); ha_messagex(LOG_DEBUG, "starting up..."); /* 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_console) { struct sockaddr_any sany; /* Create the thread buffers */ threads = (httpauth_thread_t*)malloc(sizeof(httpauth_thread_t) * g_maxthreads); if(!threads) errx(1, "out of memory"); /* Get the socket type */ if(sock_any_pton(g_socket, &sany, DEFAULT_PORT) == -1) errx(1, "invalid socket name or ip: %s", g_socket); /* Create the socket */ sock = socket(SANY_TYPE(sany), 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); if(bind(sock, SANY_ADDR(sany), SANY_LEN(sany)) != 0) err(1, "couldn't bind to socket: %s", g_socket); /* Let 5 connections queue up */ if(listen(sock, 5) != 0) err(1, "couldn't listen on socket"); ha_messagex(LOG_DEBUG, "created socket: %s", g_socket); } /* 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_console) { ha_messagex(LOG_DEBUG, "processing from console"); r = httpauth_processor(0, 1); goto finally; } /* This is the daemon section of the code */ else { if(daemonize) { /* Fork a daemon nicely here */ if(daemon(0, 0) == -1) { ha_message(LOG_ERR, "couldn't run httpauth as daemon"); exit(1); } g_daemonized = 1; } /* Handle some signals */ signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); /* signal(SIGINT, on_quit); signal(SIGTERM, on_quit); */ /* Open the system log */ openlog("httpauthd", 0, LOG_AUTHPRIV); ha_messagex(LOG_DEBUG, "accepting connections"); /* Now loop and accept the connections */ while(!g_quit) { int fd; fd = accept(sock, 0, 0); if(fd == -1) { switch(errno) { case EINTR: case EAGAIN: break; case ECONNABORTED: 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; } ha_messagex(LOG_INFO, "accepted connection: %d", fd); /* 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 == -1) { 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; } ha_messagex(LOG_DEBUG, "created thread for connection: %d", fd); fd = -1; break; } } /* Check to make sure we have a thread */ if(fd != -1) { ha_messagex(LOG_ERR, "too many connections open (max %d)", g_maxthreads); httpauth_respond(fd, HA_SERVER_ERROR, 0, "too many connections"); shutdown(fd, SHUT_RDWR); } } ha_messagex(LOG_INFO, "waiting for threads to quit"); /* Quit all threads here */ for(i = 0; i < g_maxthreads; i++) { /* Clean up quit threads */ if(threads[i].tid != 0) pthread_join(threads[i].tid, NULL); } 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; } static void on_quit(int signal) { g_quit = 1; } static int usage() { fprintf(stderr, "usage: httpauthd [-d level] [-X] [-f conffile]\n"); return 2; } static void* httpauth_thread(void* arg) { httpauth_thread_t* thread = (httpauth_thread_t*)arg; int r; ASSERT(thread); ASSERT(thread->fd != -1); /* call the processor */ r = httpauth_processor(thread->fd, thread->fd); /* mark this as done */ thread->fd = -1; return (void*)r; } /* ----------------------------------------------------------------------- * Logging */ void log_request(ha_request_t* req, ha_buffer_t* buf, int fd) { const httpauth_command_t* cmd; const char* t; const char* t2; int i; if(g_debuglevel < LOG_DEBUG) return; if(req->type == REQTYPE_IGNORE || req->type == -1) return; ha_bufcpy(buf, ""); for(i = 0; i < HA_MAX_ARGS; i++) { if(req->args[i]) { ha_bufjoin(buf); ha_bufmcat(buf, ha_buflen(buf) > 0 ? ", " : "", req->args[i], NULL); } } t = ha_bufdata(buf); t2 = NULL; /* Figure out which command it is */ for(cmd = kCommands; cmd->name; cmd++) { if(cmd->code == req->type) { t2 = cmd->name; break; } } ASSERT(t2); ha_messagex(LOG_DEBUG, "received request (from %d): " "[ type: %s / args: %s ]", fd, t2, t); for(i = 0; i < HA_MAX_HEADERS; i++) { if(req->headers[i].name) { ASSERT(req->headers[i].data); ha_messagex(LOG_DEBUG, "received header (from %d): [ %s: %s ]", fd, req->headers[i].name, req->headers[i].data); } } } void log_response(ha_response_t* resp, int fd) { int i; if(g_debuglevel < LOG_DEBUG) return; ha_messagex(LOG_DEBUG, "sending response (to %d): " "[ code: 200 / ccode: %d / detail: %s ]", fd, resp->code, resp->detail ? resp->detail : ""); for(i = 0; i < HA_MAX_HEADERS; i++) { if(resp->headers[i].name) { ASSERT(resp->headers[i].data); ha_messagex(LOG_DEBUG, "sending header (to %d): [ %s: %s ]", fd, resp->headers[i].name, resp->headers[i].data); } } } void log_respcode(int code, const char* msg, int fd) { if(g_debuglevel < LOG_DEBUG) return; ha_messagex(LOG_DEBUG, "sending response (to %d): " "[ code: %d / detail: %s ]", fd, code, msg ? msg : ""); } /* ----------------------------------------------------------------------- * Command Parsing and Handling */ static 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; ASSERT(req && buf); ASSERT(ifd != -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_bufreadline(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_bufparseword(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; } } } else { req->type = REQTYPE_IGNORE; return more; } /* 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_bufparseword(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_bufreadline(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_bufparseline(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 < HA_MAX_HEADERS) { t = ha_bufparseword(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_bufparseline(buf, 1); i++; } valid = (t != NULL) ? 1 : 0; } } ha_bufskip(buf); } } return more; } static int write_data(int ofd, const char* data) { int r; ASSERT(data); ASSERT(ofd != -1); while(*data != 0) { r = write(ofd, data, strlen(data)); if(r > 0) data += r; else if(r == -1) { if(errno == EAGAIN) continue; /* The other end closed. no message */ if(errno != EPIPE) ha_message(LOG_ERR, "couldn't write data"); return HA_CRITERROR; } } return 0; } static int httpauth_respond(int ofd, int scode, int ccode, const char* msg) { char num[16]; ASSERT(ofd != -1); ASSERT(scode > 99 && scode < 1000); ASSERT(ccode == 0 || (ccode > 99 && ccode < 1000)); /* Can only have a client code when server code is 200 */ ASSERT(ccode == 0 || scode == HA_SERVER_OK); sprintf(num, "%d ", scode); if(write_data(ofd, num) < 0) return HA_CRITERROR; if(ccode != 0) { sprintf(num, "%d ", ccode); if(write_data(ofd, num) < 0) return HA_CRITERROR; } if(!msg) { switch(scode) { case HA_SERVER_ERROR: msg = "Internal Error "; break; case HA_SERVER_BADREQ: msg = "Bad Request "; break; case HA_SERVER_DECLINE: msg = "Unauthorized "; break; default: msg = NULL; break; }; } if(msg && write_data(ofd, msg) < 0) return HA_CRITERROR; /* When the client code is 0, then caller should log */ if(ccode == 0) log_respcode(scode, msg, ofd); return write_data(ofd, "\n"); } const char kHeaderDelimiter[] = ": "; static int httpauth_write(int ofd, ha_response_t* resp) { int i; int wrote = 0; ASSERT(ofd != -1); ASSERT(resp); if(httpauth_respond(ofd, HA_SERVER_OK, resp->code, resp->detail) < 0) return HA_CRITERROR; for(i = 0; i < HA_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(write_data(ofd, "\n") == -1) return -1; log_response(resp, ofd); return 0; } static int httpauth_error(int ofd, int r) { int scode = 0; const char* msg = NULL; ASSERT(r < 0); switch(r) { case HA_BADREQ: scode = HA_SERVER_BADREQ; break; case HA_CRITERROR: msg = "Critical Error"; /* fall through */ case HA_FAILED: scode = HA_SERVER_ERROR; break; default: ASSERT(0 && "invalid error code"); break; } return httpauth_respond(ofd, scode, 0, msg); } static int httpauth_ready(int ofd, ha_buffer_t* buf) { const char* t; httpauth_loaded_t* h; ASSERT(ofd != -1); ASSERT(buf); /* We send a ready banner to our client */ if(ha_buferr(buf)) return httpauth_error(ofd, HA_CRITERROR); else return httpauth_respond(ofd, HA_SERVER_READY, 0, "HTTPAUTH/1.0"); } static int httpauth_auth(int ofd, ha_request_t* req, ha_response_t* resp, ha_buffer_t* outb) { httpauth_loaded_t* h; int processed = 0; int r; ASSERT(req && resp && outb); /* Clear out our response */ memset(resp, 0, sizeof(*resp)); /* Check our connection argument */ if(!req->args[AUTH_ARG_CONN] || !(req->args[AUTH_ARG_CONN][0])) { ha_messagex(LOG_ERR, "missing connection ID in request"); return HA_BADREQ; } /* Check our uri argument */ if(!req->args[AUTH_ARG_URI] || !(req->args[AUTH_ARG_URI][0])) { ha_messagex(LOG_ERR, "missing URI in request"); return HA_BADREQ; } /* Check our connection arguments */ if(!req->args[AUTH_ARG_METHOD] || !(req->args[AUTH_ARG_METHOD][0])) { ha_messagex(LOG_ERR, "missing method in request"); return HA_BADREQ; } /* Find a handler for this type */ for(h = g_handlers; h; h = h->next) { if(strcasecmp(h->ctx.name, req->args[0]) == 0) { ha_messagex(LOG_INFO, "processing request with method: %s (%s)", h->ctx.name, h->ctx.handler->type); /* Now let the handler handle it */ ASSERT(h->ctx.handler->f_process); processed = 1; r = (h->ctx.handler->f_process)(&(h->ctx), req, resp, outb); if(r < 0) return r; } } if(!processed) { ha_messagex(LOG_ERR, "unknown authentication type: %s", req->args[0]); return HA_BADREQ; } if(httpauth_write(ofd, resp) < 0) return HA_CRITERROR; return HA_OK; } static int httpauth_set(int ofd, ha_request_t* req, ha_request_opts_t* opts, ha_buffer_t* outb) { const char* name = req->args[0]; const char* value = req->args[1]; /* Check our name argument */ if(!name || !*name) { ha_messagex(LOG_ERR, "missing name in SET request"); return HA_BADREQ; } if(strcasecmp(name, "Domain") == 0) { opts->digest_domains = value ? value : ""; } else { ha_messagex(LOG_ERR, "bad option in SET request"); return HA_BADREQ; } return httpauth_respond(ofd, HA_SERVER_ACCEPTED, 0, NULL); } static int httpauth_processor(int ifd, int ofd) { ha_buffer_t inb; ha_buffer_t outb; ha_request_t req; ha_response_t resp; ha_request_opts_t opts; int result = -1; int r; ASSERT(ifd != -1); ASSERT(ofd != -1); /* Initialize the memory buffers */ ha_bufinit(&inb); ha_bufinit(&outb); /* Initialize default options */ memset(&opts, 0, sizeof(opts)); opts.digest_domains = ""; if(httpauth_ready(ofd, &outb) == -1) { result = 1; goto finally; } /* Now loop and handle the commands */ while(result == -1 && !g_quit) { ha_bufreset(&outb); ha_bufreset(&inb); r = httpauth_read(ifd, &req, &inb); if(g_quit) continue; if(ha_buferr(&inb)) r = HA_CRITERROR; if(r < 0) { httpauth_error(ofd, r); result = 1; break; } log_request(&req, &inb, ifd); if(r == 0) result = 0; req.opts = &opts; switch(req.type) { case REQTYPE_AUTH: r = httpauth_auth(ofd, &req, &resp, &outb); break; case REQTYPE_SET: r = httpauth_set(ofd, &req, &opts, &outb); break; case REQTYPE_QUIT: r = HA_OK; result = 0; break; case REQTYPE_IGNORE: r = HA_FALSE; break; default: ha_messagex(LOG_WARNING, "received unknown command from client: %d", ifd); r = httpauth_respond(ofd, HA_SERVER_BADREQ, 0, "Unknown command"); break; }; if(g_quit) continue; if(ha_buferr(&outb)) r = HA_CRITERROR; if(r < 0) { httpauth_error(ofd, r); if(r == HA_CRITERROR) result = 1; } } if(ifd == ofd) shutdown(ofd, SHUT_RDWR); else close(ofd); finally: ha_buffree(&outb); ha_buffree(&inb); return result; } /* ----------------------------------------------------------------------- * Configuration */ static ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias, ha_handler_t* handler, const ha_context_opts_t* defaults) { httpauth_loaded_t* loaded; ha_context_opts_t* opts; int len; ASSERT(buf && alias && handler && defaults); len = sizeof(httpauth_loaded_t) + sizeof(ha_context_opts_t) + handler->context_size; loaded = (httpauth_loaded_t*)ha_bufmalloc(buf, len); if(!loaded) errx(1, "out of memory"); memset(loaded, 0, len); /* Setup the options */ opts = (ha_context_opts_t*)(((unsigned char*)loaded) + sizeof(httpauth_loaded_t)); memcpy(opts, defaults, sizeof(ha_context_opts_t)); loaded->ctx.opts = opts; if(handler->context_size) { void* mem = ((unsigned char*)(opts)) + sizeof(ha_context_opts_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 method section for '%s'", alias); if(!(l->next)) break; l = l->next; } l->next = loaded; } ha_messagex(LOG_DEBUG, "configuration: method: %s (%s)", alias, handler->type); return &(loaded->ctx); } static int config_parse(const char* file, ha_buffer_t* buf) { ha_context_opts_t defaults; ha_context_t* ctx = NULL; int line = 0; int fd; const char** t; char* name; char* value; int more = 1; int recog; int r, i; ASSERT(file && buf); /* 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.cache_timeout = DEFAULT_TIMEOUT; /* Timeout for cache */ defaults.cache_max = DEFAULT_CACHEMAX; defaults.realm = ""; ha_bufreset(buf); /* Read each line and process */ while(more) { ha_bufskip(buf); if((more = ha_bufreadline(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) == '[') { ha_handler_t* handler = NULL; const char* x; ha_bufeat(buf); name = ha_bufparseline(buf, 1); if(!name || name[strlen(name) - 1] != ']') errx(1, "method 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 method type '%s' (line %d)", name, line); /* If we had a last handler then add it to handlers */ ctx = config_addhandler(buf, name, handler, &defaults); /* Rest doesn't apply to handler headers */ continue; } /* Parse out the name */ name = ha_bufparseword(buf, ":"); if(!name || !name[0]) errx(1, "configuration file invalid (line %d)", line); strlwr(name); /* And get the rest of the line */ value = ha_bufparseline(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(name, value, 1, 256, &g_maxthreads) == -1) exit(1); recog = 1; } } /* Otherwise we're in a handler */ else { if(strcmp("alias", name) == 0) { ctx->name = value; recog = 1; } if(ctx->handler->f_config) { r = (ctx->handler->f_config)(ctx, name, value); if(r < 0) return r; if(!recog && r == HA_OK) recog = 1; } } /* Options that are legal in both global and internal sections */ if(!recog) { ha_context_opts_t* opts = ctx ? (ha_context_opts_t*)(ctx->opts) : &defaults; ASSERT(opts); if(strcmp(name, "cachetimeout") == 0) { int v; if(ha_confint(name, value, 0, 86400, &v) < 0) exit(1); /* Message already printed */ opts->cache_timeout = v; recog = 1; } else if(strcmp(name, "cachemax") == 0) { int v; if(ha_confint(name, value, 0, 0x7FFFFFFF, &v) < 0) exit(1); /* Message already printed */ opts->cache_max = 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); opts->types = types; recog = 1; } else if(strcmp(name, "realm") == 0) { opts->realm = value; recog = 1; } else if(strcmp(name, "digestignoreuri") == 0) { int v; if(ha_confbool(name, value, &v) < 0) exit(1); /* Message already printed */ opts->digest_ignoreuri = v; recog = 1; } else if(strcmp(name, "digestignorenc") == 0) { int v; if(ha_confbool(name, value, &v) < 0) exit(1); /* Message already printed */ opts->digest_ignorenc = v; recog = 1; } #ifdef _DEBUG else if(strcmp(name, "digestdebugnonce") == 0) { opts->digest_debugnonce = value; recog = 1; } #endif } if(!recog) errx(1, "unrecognized configuration setting '%s' (line %d)", name, line); else ha_messagex(LOG_DEBUG, "configuration: setting: [ %s: %s ]", name, value); } if(!g_handlers) ha_messagex(LOG_WARNING, "configuration: no methods found in configuration file"); return 0; }