diff options
author | Stef Walter <stef@memberwebs.com> | 2004-08-09 16:57:46 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2004-08-09 16:57:46 +0000 |
commit | b0e50bbeb12e6247dd52dfd9e44c62f558c8a3a0 (patch) | |
tree | 20863017407586634b5fedf5ccf793d1cb5176a4 | |
parent | 6be6d1dd25f2e7f2f1de6c0091e9aeae2ea1918c (diff) |
- Seperated core Basic/Digest functionality into a base handler
-rw-r--r-- | daemon/bd.c | 511 | ||||
-rw-r--r-- | daemon/bd.h | 71 | ||||
-rw-r--r-- | daemon/ldap.c | 1931 | ||||
-rw-r--r-- | daemon/simple.c | 865 |
4 files changed, 1524 insertions, 1854 deletions
diff --git a/daemon/bd.c b/daemon/bd.c new file mode 100644 index 0000000..916c453 --- /dev/null +++ b/daemon/bd.c @@ -0,0 +1,511 @@ + +/* TODO: Include attribution for ideas, and code from mod_auth_digest */ + +#include "usuals.h" +#include "httpauthd.h" +#include "hash.h" +#include "defaults.h" +#include "digest.h" +#include "basic.h" +#include "md5.h" +#include "bd.h" + +static unsigned char g_digest_secret[DIGEST_SECRET_LEN]; + +/* ------------------------------------------------------------------------------- + * Defaults and Constants + */ + +#define BASIC_ESTABLISHED (void*)1 + + +/* ------------------------------------------------------------------------------- + * Internal Functions + */ + +static void free_hash_object(void* arg, void* val) +{ + if(val && val != BASIC_ESTABLISHED) + free(val); +} + +static digest_record_t* get_cached_digest(bd_context_t* ctx, ha_context_t* c, + unsigned char* nonce) +{ + digest_record_t* rec; + + ASSERT(ctx && c && nonce); + + if(c->cache_max == 0) + return NULL; + + ha_lock(NULL); + + rec = (digest_record_t*)hsh_get(ctx->cache, nonce); + + /* Just in case it's a basic :) */ + if(rec && rec != BASIC_ESTABLISHED) + hsh_rem(ctx->cache, nonce); + + ha_unlock(NULL); + + ASSERT(!rec || memcmp(nonce, rec->nonce, DIGEST_NONCE_LEN) == 0); + return rec; +} + +static int have_cached_basic(bd_context_t* ctx, unsigned char* key) +{ + int ret = 0; + + ASSERT(ctx && key); + + ha_lock(NULL); + + ret = (hsh_get(ctx->cache, key) == BASIC_ESTABLISHED); + + ha_unlock(NULL); + + return ret; +} + +static int save_cached_digest(bd_context_t* ctx, ha_context_t* c, + digest_record_t* rec) +{ + int r; + + ASSERT(ctx && rec); + + if(c->cache_max == 0) + { + free_hash_object(NULL, rec); + return HA_FALSE; + } + + ha_lock(NULL); + + while(hsh_count(ctx->cache) >= c->cache_max) + hsh_bump(ctx->cache); + + r = hsh_set(ctx->cache, rec->nonce, rec); + + ha_unlock(NULL); + + if(!r) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_CRITERROR; + } + + return HA_OK; +} + +static int add_cached_basic(bd_context_t* ctx, ha_context_t* c, + unsigned char* key) +{ + int r; + + ASSERT(ctx && c && key); + + if(c->cache_max == 0) + return HA_FALSE; + + ha_lock(NULL); + + while(hsh_count(ctx->cache) >= c->cache_max) + hsh_bump(ctx->cache); + + r = hsh_set(ctx->cache, key, BASIC_ESTABLISHED); + + ha_unlock(NULL); + + if(!r) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_CRITERROR; + } + + return HA_OK; +} + +static int do_basic_response(bd_context_t* ctx, const char* header, + const ha_request_t* req, ha_response_t* resp) +{ + basic_header_t basic; + int ret = HA_FALSE; + + ASSERT(header && resp && req); + + if((ret = basic_parse(header, req->buf, &basic)) < 0) + return ret; + + /* Past this point we don't return directly */ + + /* Check and see if this connection is in the cache */ + if(have_cached_basic(ctx, basic.key)) + { + ha_messagex(LOG_NOTICE, "bd: validated basic user against cache: %s", + basic.user); + ret = HA_OK; + goto finally; + } + + /* If we have a user name and password */ + if(!basic.user || !basic.user[0] || + !basic.password || !basic.password[0]) + { + ha_messagex(LOG_NOTICE, "bd: no valid basic auth info"); + ret = HA_FALSE; + goto finally; + } + + ASSERT(ctx->f_validate_basic); + ret = ctx->f_validate_basic(req, basic.user, basic.password); + +finally: + + if(ret == HA_OK) + { + resp->code = HA_SERVER_OK; + resp->detail = basic.user; + + /* We put this connection into the successful connections */ + ret = add_cached_basic(ctx, req->context, basic.key); + } + + return ret; +} + +static int do_digest_challenge(bd_context_t* ctx, const ha_request_t* req, + ha_response_t* resp, int stale) +{ + unsigned char nonce[DIGEST_NONCE_LEN]; + const char* nonce_str; + const char* header; + + ASSERT(ctx && resp && req); + +#ifdef _DEBUG + if(req->context->digest_debugnonce) + { + nonce_str = req->context->digest_debugnonce; + ha_messagex(LOG_WARNING, "bd: using debug nonce. security non-existant."); + } + else +#endif + { + unsigned char nonce[DIGEST_NONCE_LEN]; + digest_makenonce(nonce, g_digest_secret, NULL); + + nonce_str = ha_bufenchex(req->buf, nonce, DIGEST_NONCE_LEN); + if(!nonce_str) + return HA_CRITERROR; + } + + /* Now generate a message to send */ + header = digest_challenge(req->buf, nonce_str, req->context->realm, + req->digest_domain, stale); + + if(!header) + return HA_CRITERROR; + + /* And append it nicely */ + resp->code = HA_SERVER_DECLINE; + ha_addheader(resp, "WWW-Authenticate", header); + + ha_messagex(LOG_DEBUG, "bd: created digest challenge with nonce: %s", nonce_str); + return HA_OK; +} + +static int do_digest_response(bd_context_t* ctx, const char* header, + const ha_request_t* req, ha_response_t* resp) +{ + unsigned char nonce[DIGEST_NONCE_LEN]; + digest_header_t dg; + digest_record_t* rec = NULL; + const char* t; + time_t expiry; + int ret = HA_FALSE; + int stale = 0; + int r; + + ASSERT(ctx && header && req && resp); + + /* We use this below to send a default response */ + resp->code = -1; + + if((r = digest_parse(header, req->buf, &dg, nonce)) < 0) + return r; + +#ifdef _DEBUG + if(req->context->digest_debugnonce) + { + if(dg.nonce && strcmp(dg.nonce, req->context->digest_debugnonce) != 0) + { + ret = HA_FALSE; + ha_messagex(LOG_WARNING, "bd: digest response contains invalid nonce"); + goto finally; + } + + /* Do a rough hash into the real nonce, for use as a key */ + md5_string(nonce, req->context->digest_debugnonce); + + /* Debug nonce's never expire */ + expiry = time(NULL); + } + else +#endif + { + r = digest_checknonce(nonce, g_digest_secret, &expiry); + if(r != HA_OK) + { + if(r == HA_FALSE) + ha_messagex(LOG_WARNING, "bd: digest response contains invalid nonce"); + + goto finally; + } + } + + rec = get_cached_digest(ctx, req->context, nonce); + + /* Check to see if we're stale */ + if((expiry + req->context->cache_timeout) <= time(NULL)) + { + ha_messagex(LOG_INFO, "bd: nonce expired, sending stale challenge: %s", + dg.username); + + stale = 1; + goto finally; + } + + if(!rec) + { + ha_messagex(LOG_INFO, "bd: no record in cache, creating one: %s", dg.username); + + /* + * If we're valid but don't have a record in the + * cache then complete the record properly. + */ + + rec = digest_makerec(nonce, dg.username); + if(!rec) + { + ret = HA_CRITERROR; + goto finally; + } + + ASSERT(ctx->f_complete_digest); + r = ctx->f_complete_digest(req, dg.username, rec->ha1); + if(r != HA_OK) + { + ret = r; + goto finally; + } + } + + /* We had a record so ... */ + else + { + rec->nc++; + } + + ret = digest_check(&dg, rec, req->context, req->buf, + req->args[AUTH_ARG_METHOD], req->args[AUTH_ARG_URI]); + + if(ret == HA_BADREQ) + { + ret = HA_FALSE; + resp->code = HA_SERVER_BADREQ; + } + + else if(ret == HA_OK) + { + resp->code = HA_SERVER_OK; + resp->detail = dg.username; + + /* Figure out if we need a new nonce */ + if((expiry + (req->context->cache_timeout - + (req->context->cache_timeout / 8))) < time(NULL)) + { + ha_messagex(LOG_INFO, "bd: nonce almost expired, creating new one: %s", + dg.username); + + digest_makenonce(nonce, g_digest_secret, NULL); + stale = 1; + } + + t = digest_respond(req->buf, &dg, rec, stale ? nonce : NULL); + if(!t) + { + ret = HA_CRITERROR; + goto finally; + } + + if(t[0]) + ha_addheader(resp, "Authentication-Info", t); + + ha_messagex(LOG_NOTICE, "bd: validated digest user: %s", dg.username); + + /* Put the connection into the cache */ + if((r = save_cached_digest(ctx, req->context, rec)) < 0) + ret = r; + + rec = NULL; + } + +finally: + + /* If the record wasn't stored away then free it */ + if(rec) + free(rec); + + /* If nobody above responded then challenge the client again */ + if(resp->code == -1) + return do_digest_challenge(ctx, req, resp, stale); + + return ret; +} + + +/* ------------------------------------------------------------------------------- + * Handler Functions + */ + +int bd_init(ha_context_t* context) +{ + /* Global initialization */ + if(!context) + { + ha_messagex(LOG_DEBUG, "bd: generating secret"); + return ha_genrandom(g_digest_secret, DIGEST_SECRET_LEN); + } + + /* Context specific initialization */ + else + { + bd_context_t* ctx = (bd_context_t*)(context->ctx_data); + hsh_table_calls_t htc; + + ASSERT(ctx); + + /* Make sure there are some types of authentication we can do */ + if(!(context->allowed_types & (HA_TYPE_BASIC | HA_TYPE_DIGEST))) + { + ha_messagex(LOG_ERR, "bd: module configured, but does not implement any " + "configured authentication type."); + return HA_FAILED; + } + + /* The cache for digest records and basic */ + if(!(ctx->cache = hsh_create(MD5_LEN))) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_CRITERROR; + } + + htc.f_freeval = free_hash_object; + htc.arg = NULL; + hsh_set_table_calls(ctx->cache, &htc); + + ha_messagex(LOG_INFO, "ldap: initialized handler"); + } + + return HA_OK; +} + +void bd_destroy(ha_context_t* context) +{ + bd_context_t* ctx; + int i; + + if(!context) + return; + + /* Note: We don't need to be thread safe here anymore */ + ctx = (bd_context_t*)(context->ctx_data); + + ASSERT(ctx); + + if(ctx->cache) + hsh_free(ctx->cache); + + ha_messagex(LOG_INFO, "bd: uninitialized handler"); +} + +int bd_process(const ha_request_t* req, ha_response_t* resp) +{ + bd_context_t* ctx = (bd_context_t*)req->context->ctx_data; + time_t t = time(NULL); + const char* header = NULL; + int ret, r; + + ASSERT(req && resp); + ASSERT(req->args[AUTH_ARG_METHOD]); + ASSERT(req->args[AUTH_ARG_URI]); + + ha_lock(NULL); + + /* Purge out stale connection stuff. */ + r = hsh_purge(ctx->cache, t - req->context->cache_timeout); + + ha_unlock(NULL); + + if(r > 0) + ha_messagex(LOG_DEBUG, "ldap: purged cache records: %d", r); + + /* We use this below to detect whether to send a default response */ + resp->code = -1; + + /* Check the headers and see if we got a response thingy */ + if(req->context->allowed_types & HA_TYPE_DIGEST) + { + header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST); + if(header) + { + ha_messagex(LOG_DEBUG, "ldap: processing digest auth header"); + ret = do_digest_response(ctx, header, req, resp); + if(ret < 0) + return ret; + } + } + + /* Or a basic authentication */ + if(!header && req->context->allowed_types & HA_TYPE_BASIC) + { + header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC); + if(header) + { + ha_messagex(LOG_DEBUG, "bd: processing basic auth header"); + ret = do_basic_response(ctx, header, req, resp); + if(ret < 0) + return ret; + } + } + + + /* Send a default response if that's what we need */ + if(resp->code == -1) + { + resp->code = HA_SERVER_DECLINE; + + if(req->context->allowed_types & HA_TYPE_BASIC) + { + ha_bufmcat(req->buf, "BASIC realm=\"", req->context->realm , "\"", NULL); + + if(ha_buferr(req->buf)) + return HA_CRITERROR; + + ha_addheader(resp, "WWW-Authenticate", ha_bufdata(req->buf)); + ha_messagex(LOG_DEBUG, "bd: sent basic auth request"); + } + + if(req->context->allowed_types & HA_TYPE_DIGEST) + { + ret = do_digest_challenge(ctx, req, resp, 0); + if(ret < 0) + return ret; + } + } + + return ret; +} + diff --git a/daemon/bd.h b/daemon/bd.h new file mode 100644 index 0000000..450c8e1 --- /dev/null +++ b/daemon/bd.h @@ -0,0 +1,71 @@ + +#ifndef BD_H +#define BD_H + +#include "hash.h" + +/* ---------------------------------------------------------------------------------- + * Callbacks + */ + +/* + * A callback for completing a given user's digest ha1. In this function + * you're required to fill in the digest ha1 hash for the user. ha1 will + * always be an array of MD5_LEN bytes. + * + * Return Values + * HA_OK: completed successfully + * HA_FAILED: error retrieving hash (should have logged error) + */ +typedef int (*bd_complete_digest)(const ha_request_t* req, + const char* user, unsigned char* ha1); + +/* + * A callback for validating a given user's password. + * + * Return Values + * HA_OK: good password + * HA_FALSE: invalid password + * HA_FAILED: error validating (should have logged error) + */ +typedef int (*bd_validate_basic)(const ha_request_t* req, + const char* user, const char* password); + +/* ---------------------------------------------------------------------------------- + * Base Context + */ +typedef struct bd_context +{ + hsh_t* cache; /* Some cached records or basic */ + + bd_complete_digest f_complete_digest; + bd_validate_basic f_validate_basic; +} +bd_context_t; + +#define BD_CALLBACKS(a, b) { NULL, (a), (b) } +#define BD_DEFAULTS { NULL, NULL, NULL } + +/* ---------------------------------------------------------------------------------- + * Base Handler Functions + */ + +/* + * The base/digest initialize function. Call this from your + * 'derived' handler initialize function. + */ +int bd_init(ha_context_t* context); + +/* + * The base/digest uninitialize function. Call this from your + * 'derived' handler uninitialize function. + */ +void bd_destroy(ha_context_t* context); + +/* + * The base/digest processer for requests. Call this from your + * 'derived' handler process function. + */ +int bd_process(const ha_request_t* req, ha_response_t* resp); + +#endif /* BD_H */ diff --git a/daemon/ldap.c b/daemon/ldap.c index 4398dc0..e0349fb 100644 --- a/daemon/ldap.c +++ b/daemon/ldap.c @@ -1,32 +1,21 @@ /* TODO: Include attribution for ideas, and code from mod_auth_digest */ -#define _XOPEN_SOURCE - #include "usuals.h" #include "httpauthd.h" -#include "hash.h" -#include "defaults.h" -#include "digest.h" -#include "basic.h" #include "md5.h" #include "sha1.h" +#include "bd.h" #include <sys/time.h> -#include <unistd.h> -#include <syslog.h> /* LDAP library */ #include <ldap.h> -unsigned char g_ldap_secret[DIGEST_SECRET_LEN]; - /* ------------------------------------------------------------------------------- * Defaults and Constants */ -#define BASIC_ESTABLISHED (void*)1 - /* TODO: We need to support more password types */ #define LDAP_PW_CLEAR 0 #define LDAP_PW_CRYPT 1 @@ -36,17 +25,17 @@ unsigned char g_ldap_secret[DIGEST_SECRET_LEN]; typedef struct ldap_pw_type { - const char* name; - int type; + const char* name; + int type; } ldap_pw_type_t; static const ldap_pw_type_t kLDAPPWTypes[] = { - { "cleartext", LDAP_PW_CLEAR }, - { "crypt", LDAP_PW_CRYPT }, - { "md5", LDAP_PW_MD5 }, - { "sha", LDAP_PW_SHA } + { "cleartext", LDAP_PW_CLEAR }, + { "crypt", LDAP_PW_CRYPT }, + { "md5", LDAP_PW_MD5 }, + { "sha", LDAP_PW_SHA } }; @@ -57,50 +46,54 @@ static const ldap_pw_type_t kLDAPPWTypes[] = /* Our hanler context */ typedef struct ldap_context { - /* Settings ---------------------------------------------------------- */ - const char* servers; /* Servers to authenticate against (required) */ - const char* filter; /* Filter (either this or dnmap must be set) */ - const char* base; /* Base for the filter */ - const char* pw_attr; /* The clear password attribute */ - const char* ha1_attr; /* Password for an encrypted Digest H(A1) */ - const char* user; /* User to bind as */ - const char* password; /* Password to bind with */ - const char* dnmap; /* For mapping users to dns */ - int port; /* Port to connect to LDAP server on */ - int scope; /* Scope for filter */ - - int dobind; /* Bind to do simple authentication */ - int ldap_max; /* Number of open connections allowed */ - int ldap_timeout; /* Maximum amount of time to dedicate to an ldap query */ - - /* Context ----------------------------------------------------------- */ - hsh_t* cache; /* Some cached records or basic */ - - LDAP** pool; /* Pool of available connections */ - int pool_mark; /* Amount of connections allocated */ + /* Base Handler ------------------------------------------------------ */ + bd_context_t bd; + + /* Settings ---------------------------------------------------------- */ + const char* servers; /* Servers to authenticate against (required) */ + const char* filter; /* Filter (either this or dnmap must be set) */ + const char* base; /* Base for the filter */ + const char* pw_attr; /* The clear password attribute */ + const char* ha1_attr; /* Password for an encrypted Digest H(A1) */ + const char* user; /* User to bind as */ + const char* password; /* Password to bind with */ + const char* dnmap; /* For mapping users to dns */ + int port; /* Port to connect to LDAP server on */ + int scope; /* Scope for filter */ + + int dobind; /* Bind to do simple authentication */ + int ldap_max; /* Number of open connections allowed */ + int ldap_timeout; /* Maximum amount of time to dedicate to an ldap query */ + + /* Context ----------------------------------------------------------- */ + LDAP** pool; /* Pool of available connections */ + int pool_mark; /* Amount of connections allocated */ } ldap_context_t; +/* Forward declarations for callbacks */ +static int complete_digest(const ha_request_t* req, const char* user, unsigned char* ha1); +static int validate_basic(const ha_request_t* req, const char* user, const char* password); /* The defaults for the context */ static const ldap_context_t ldap_defaults = { - NULL, /* servers */ - NULL, /* filter */ - NULL, /* base */ - "userPassword", /* pw_attr */ - NULL, /* ha1_attr */ - NULL, /* user */ - NULL, /* password */ - NULL, /* dnmap */ - 389, /* port */ - LDAP_SCOPE_SUBTREE, /* scope */ - 1, /* dobind */ - 10, /* ldap_max */ - 30, /* ldap_timeout */ - NULL, /* cache */ - NULL, /* pool */ - 0 /* pool_mark */ + BD_CALLBACKS(complete_digest, validate_basic), + NULL, /* servers */ + NULL, /* filter */ + NULL, /* base */ + "userPassword", /* pw_attr */ + NULL, /* ha1_attr */ + NULL, /* user */ + NULL, /* password */ + NULL, /* dnmap */ + 389, /* port */ + LDAP_SCOPE_SUBTREE, /* scope */ + 1, /* dobind */ + 10, /* ldap_max */ + 30, /* ldap_timeout */ + NULL, /* pool */ + 0 /* pool_mark */ }; @@ -108,127 +101,22 @@ static const ldap_context_t ldap_defaults = * Internal Functions */ -static void free_hash_object(void* arg, void* val) -{ - if(val && val != BASIC_ESTABLISHED) - free(val); -} - static int report_ldap(const char* msg, int code) { - ASSERT(code != LDAP_SUCCESS); - - switch(code) - { - case LDAP_NO_MEMORY: - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; - - default: - if(!msg) - msg = "error"; - - ha_messagex(LOG_ERR, "ldap: %s: %s", msg, ldap_err2string(code)); - return HA_FAILED; - }; -} - -static digest_record_t* get_cached_digest(ldap_context_t* ctx, ha_context_t* c, - unsigned char* nonce) -{ - digest_record_t* rec; - - ASSERT(ctx && c && nonce); - - if(c->cache_max == 0) - return NULL; - - ha_lock(NULL); - - rec = (digest_record_t*)hsh_get(ctx->cache, nonce); - - /* Just in case it's a basic :) */ - if(rec && rec != BASIC_ESTABLISHED) - hsh_rem(ctx->cache, nonce); - - ha_unlock(NULL); - - ASSERT(!rec || memcmp(nonce, rec->nonce, DIGEST_NONCE_LEN) == 0); - return rec; -} - -static int have_cached_basic(ldap_context_t* ctx, unsigned char* key) -{ - int ret = 0; - - ASSERT(ctx && key); - - ha_lock(NULL); - - ret = (hsh_get(ctx->cache, key) == BASIC_ESTABLISHED); - - ha_unlock(NULL); - - return ret; -} - -static int save_cached_digest(ldap_context_t* ctx, ha_context_t* c, - digest_record_t* rec) -{ - int r; - - ASSERT(ctx && rec); - - if(c->cache_max == 0) - { - free_hash_object(NULL, rec); - return HA_FALSE; - } - - ha_lock(NULL); - - while(hsh_count(ctx->cache) >= c->cache_max) - hsh_bump(ctx->cache); - - r = hsh_set(ctx->cache, rec->nonce, rec); - - ha_unlock(NULL); - - if(!r) - { - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; - } - - return HA_OK; -} - -static int add_cached_basic(ldap_context_t* ctx, ha_context_t* c, - unsigned char* key) -{ - int r; - - ASSERT(ctx && c && key); - - if(c->cache_max == 0) - return HA_FALSE; - - ha_lock(NULL); - - while(hsh_count(ctx->cache) >= c->cache_max) - hsh_bump(ctx->cache); - - r = hsh_set(ctx->cache, key, BASIC_ESTABLISHED); - - ha_unlock(NULL); + ASSERT(code != LDAP_SUCCESS); - if(!r) - { - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; - } + switch(code) + { + case LDAP_NO_MEMORY: + ha_messagex(LOG_CRIT, "out of memory"); + return HA_CRITERROR; - return HA_OK; + default: + if(!msg) + msg = "error"; + ha_messagex(LOG_ERR, "ldap: %s: %s", msg, ldap_err2string(code)); + return HA_FAILED; + }; } #define LDAP_NO_ESCAPE "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_" @@ -236,982 +124,757 @@ static int add_cached_basic(ldap_context_t* ctx, ha_context_t* c, static const char* escape_ldap(ha_buffer_t* buf, const char* str) { - const char* t = str; - size_t pos; - - ha_bufcpy(buf, ""); + const char* t = str; + size_t pos; - while(*t) - { - pos = strspn(t, LDAP_NO_ESCAPE); + ha_bufcpy(buf, ""); - if(pos > 0) + while(*t) { - ha_bufjoin(buf); - ha_bufncpy(buf, t, pos); + pos = strspn(t, LDAP_NO_ESCAPE); - t += pos; - } + if(pos > 0) + { + ha_bufjoin(buf); + ha_bufncpy(buf, t, pos); - while(*t && !strchr(LDAP_NO_ESCAPE, *t)) - { - char hex[4]; - hex[0] = '\\'; - hex[1] = LDAP_HEX[*t >> 4 & 0xf]; - hex[2] = LDAP_HEX[*t & 0xf]; - hex[3] = '\0'; + t += pos; + } - ha_bufjoin(buf); - ha_bufcpy(buf, hex); + while(*t && !strchr(LDAP_NO_ESCAPE, *t)) + { + char hex[4]; + hex[0] = '\\'; + hex[1] = LDAP_HEX[*t >> 4 & 0xf]; + hex[2] = LDAP_HEX[*t & 0xf]; + hex[3] = '\0'; - t++; + ha_bufjoin(buf); + ha_bufcpy(buf, hex); + + t++; + } } - } - return ha_bufdata(buf); + return ha_bufdata(buf); } static const char* substitute_params(ldap_context_t* ctx, const ha_request_t* req, const char* user, const char* str) { - const char* t; + const char* t; - ASSERT(ctx && req && user && str); + ASSERT(ctx && req && user && str); - /* TODO: We need to be escaping the user and realm properly */ - /* This starts a new block to join */ - ha_bufcpy(req->buf, ""); + /* TODO: We need to be escaping the user and realm properly */ + /* This starts a new block to join */ + ha_bufcpy(req->buf, ""); - while(str[0]) - { - t = strchr(str, '%'); - if(!t) + while(str[0]) { - ha_bufjoin(req->buf); - ha_bufcpy(req->buf, str); - break; + t = strchr(str, '%'); + if(!t) + { + ha_bufjoin(req->buf); + ha_bufcpy(req->buf, str); + break; + } + + ha_bufjoin(req->buf); + ha_bufncpy(req->buf, str, t - str); + + t++; + + switch(t[0]) + { + case 'u': + ha_bufjoin(req->buf); + escape_ldap(req->buf, user); + t++; + break; + + case 'r': + ha_bufjoin(req->buf); + escape_ldap(req->buf, req->context->realm); + t++; + break; + + case '%': + ha_bufjoin(req->buf); + ha_bufcpy(req->buf, "%"); + t++; + break; + }; + + str = t; } - ha_bufjoin(req->buf); - ha_bufncpy(req->buf, str, t - str); - - t++; - - switch(t[0]) - { - case 'u': - ha_bufjoin(req->buf); - escape_ldap(req->buf, user); - t++; - break; - - case 'r': - ha_bufjoin(req->buf); - escape_ldap(req->buf, req->context->realm); - t++; - break; - - case '%': - ha_bufjoin(req->buf); - ha_bufcpy(req->buf, "%"); - t++; - break; - }; - - str = t; - } - - return ha_bufdata(req->buf); + return ha_bufdata(req->buf); } static const char* make_password_md5(ha_buffer_t* buf, const char* clearpw) { - md5_ctx_t md5; - unsigned char digest[MD5_LEN]; + md5_ctx_t md5; + unsigned char digest[MD5_LEN]; - ASSERT(buf && clearpw); + ASSERT(buf && clearpw); - md5_init(&md5); - md5_update(&md5, clearpw, strlen(clearpw)); - md5_final(digest, &md5); + md5_init(&md5); + md5_update(&md5, clearpw, strlen(clearpw)); + md5_final(digest, &md5); - return ha_bufenc64(buf, digest, MD5_LEN); + return ha_bufenc64(buf, digest, MD5_LEN); } static const char* make_password_sha(ha_buffer_t* buf, const char* clearpw) { - sha1_ctx_t sha; - unsigned char digest[SHA1_LEN]; + sha1_ctx_t sha; + unsigned char digest[SHA1_LEN]; - ASSERT(buf && clearpw); + ASSERT(buf && clearpw); - sha1_init(&sha); - sha1_update(&sha, clearpw, strlen(clearpw)); - sha1_final(digest, &sha); + sha1_init(&sha); + sha1_update(&sha, clearpw, strlen(clearpw)); + sha1_final(digest, &sha); - return ha_bufenc64(buf, digest, SHA1_LEN); + return ha_bufenc64(buf, digest, SHA1_LEN); } static int parse_ldap_password(const char** password) { - const char* pw; - const char* scheme; - int i; - - ASSERT(password && *password); + const char* pw; + const char* scheme; + int i; - pw = *password; + ASSERT(password && *password); - /* zero length passwords are clear */ - if(strlen(pw) == 0) - return LDAP_PW_CLEAR; + pw = *password; - /* passwords without a scheme are clear */ - if(pw[0] != '{') - return LDAP_PW_CLEAR; + /* zero length passwords are clear */ + if(strlen(pw) == 0) + return LDAP_PW_CLEAR; - pw++; - scheme = pw; + /* passwords without a scheme are clear */ + if(pw[0] != '{') + return LDAP_PW_CLEAR; - while(*pw && (isalpha(*pw) || isdigit(*pw) || *pw == '-')) pw++; + scheme = pw; - /* scheme should end in a brace */ - if(*pw != '}') - return LDAP_PW_CLEAR; + while(*pw && (isalpha(*pw) || isdigit(*pw) || *pw == '-')) + pw++; - *password = pw + 1; + /* scheme should end in a brace */ + if(*pw != '}') + return LDAP_PW_CLEAR; - /* find a scheme in our map */ - for(i = 0; i < countof(kLDAPPWTypes); i++) - { - if(strncasecmp(kLDAPPWTypes[i].name, scheme, pw - scheme) == 0) - return kLDAPPWTypes[i].type; - } + *password = pw + 1; - return LDAP_PW_UNKNOWN; + /* find a scheme in our map */ + for(i = 0; i < countof(kLDAPPWTypes); i++) + { + if(strncasecmp(kLDAPPWTypes[i].name, scheme, pw - scheme) == 0) + return kLDAPPWTypes[i].type; + } + + return LDAP_PW_UNKNOWN; } static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws) { - ASSERT(buf); + ASSERT(buf); - for(; pws && *pws; pws++) - { - const char* pw = *pws; + for(; pws && *pws; pws++) + { + const char* pw = *pws; - if(parse_ldap_password(&pw) == LDAP_PW_CLEAR) - return pw; - } + if(parse_ldap_password(&pw) == LDAP_PW_CLEAR) + return pw; + } - return NULL; + return NULL; } static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha1) { - size_t len; - void* d; + size_t len; + void* d; - ASSERT(buf && bv && ha1); + ASSERT(buf && bv && ha1); - /* Raw binary */ - if(bv->bv_len == MD5_LEN) - { - ha_messagex(LOG_DEBUG, "ldap: found ha1 in raw binary format"); - memcpy(ha1, bv->bv_val, MD5_LEN); - return HA_OK; - } - - /* Hex encoded */ - else if(bv->bv_len == (MD5_LEN * 2)) - { - len = MD5_LEN; - d = ha_bufdechex(buf, bv->bv_val, &len); - - if(d && len == MD5_LEN) + /* Raw binary */ + if(bv->bv_len == MD5_LEN) { - ha_messagex(LOG_DEBUG, "ldap: found ha1 in hex encoded format"); - memcpy(ha1, d, MD5_LEN); - return HA_OK; + ha_messagex(LOG_DEBUG, "ldap: found ha1 in raw binary format"); + memcpy(ha1, bv->bv_val, MD5_LEN); + return HA_OK; } - } - /* B64 Encoded */ - else - { - len = MD5_LEN; - d = ha_bufdec64(buf, bv->bv_val, &len); + /* Hex encoded */ + else if(bv->bv_len == (MD5_LEN * 2)) + { + len = MD5_LEN; + d = ha_bufdechex(buf, bv->bv_val, &len); + + if(d && len == MD5_LEN) + { + ha_messagex(LOG_DEBUG, "ldap: found ha1 in hex encoded format"); + memcpy(ha1, d, MD5_LEN); + return HA_OK; + } + } - if(d && len == MD5_LEN) + /* B64 Encoded */ + else { - ha_messagex(LOG_DEBUG, "ldap: found ha1 in b64 encoded format"); - memcpy(ha1, ha_bufdata(buf), MD5_LEN); - return HA_OK; + len = MD5_LEN; + d = ha_bufdec64(buf, bv->bv_val, &len); + + if(d && len == MD5_LEN) + { + ha_messagex(LOG_DEBUG, "ldap: found ha1 in b64 encoded format"); + memcpy(ha1, ha_bufdata(buf), MD5_LEN); + return HA_OK; + } } - } - return ha_buferr(buf) ? HA_CRITERROR : HA_FALSE; + return ha_buferr(buf) ? HA_CRITERROR : HA_FALSE; } static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry, ha_buffer_t* buf, const char* user, const char* clearpw) { - char** pws; - char** t; - const char* pw; - const char* p; - int type; - int res = HA_FALSE; - int unknown = 0; - - ASSERT(entry && ld && ctx && clearpw); - - ASSERT(ctx->pw_attr); - pws = ldap_get_values(ld, entry, ctx->pw_attr); - - if(pws) - { - for(t = pws ; *t; t++) - { - pw = *t; - type = parse_ldap_password(&pw); - - switch(type) - { - case LDAP_PW_CLEAR: - p = clearpw; - break; + char** pws; + char** t; + const char* pw; + const char* p; + int type; + int res = HA_FALSE; + int unknown = 0; - case LDAP_PW_MD5: - p = make_password_md5(buf, clearpw); - break; - - case LDAP_PW_CRYPT: + ASSERT(entry && ld && ctx && clearpw); - /* Not sure if crypt is thread safe */ - ha_lock(NULL); - p = crypt(clearpw, pw); - ha_unlock(NULL); - break; + ASSERT(ctx->pw_attr); + pws = ldap_get_values(ld, entry, ctx->pw_attr); - case LDAP_PW_SHA: - p = make_password_sha(buf, clearpw); - break; - - case LDAP_PW_UNKNOWN: - unknown = 1; - continue; - - default: - /* Not reached */ - ASSERT(0); - }; - - if(!p) - { - res = HA_CRITERROR; - break; - } - - if(strcmp(pw, p) == 0) - { - ha_messagex(LOG_DEBUG, "ldap: successful validate against password"); - res = HA_OK; - break; - } + if(pws) + { + for(t = pws ; *t; t++) + { + pw = *t; + type = parse_ldap_password(&pw); + + switch(type) + { + case LDAP_PW_CLEAR: + p = clearpw; + break; + + case LDAP_PW_MD5: + p = make_password_md5(buf, clearpw); + break; + + case LDAP_PW_CRYPT: + /* Not sure if crypt is thread safe */ + ha_lock(NULL); + p = crypt(clearpw, pw); + ha_unlock(NULL); + break; + + case LDAP_PW_SHA: + p = make_password_sha(buf, clearpw); + break; + + case LDAP_PW_UNKNOWN: + unknown = 1; + continue; + + default: + /* Not reached */ + ASSERT(0); + }; + + if(!p) + { + res = HA_CRITERROR; + break; + } + + if(strcmp(pw, p) == 0) + { + ha_messagex(LOG_DEBUG, "ldap: successful validate against password"); + res = HA_OK; + break; + } + } + + ldap_value_free(pws); } - ldap_value_free(pws); - } - - if(res == HA_FALSE && unknown) - ha_messagex(LOG_ERR, "ldap: server does not contain any compatible passwords for user: %s", user); + if(res == HA_FALSE && unknown) + ha_messagex(LOG_ERR, "ldap: server does not contain any compatible passwords for user: %s", user); - return res; + return res; } static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry, ha_buffer_t* buf, const char* user, const char* realm, const char* clearpw) { - struct berval** ha1s; - struct berval** b; - unsigned char key[MD5_LEN]; - unsigned char k[MD5_LEN]; - int r, first = 1; - int res = HA_FALSE; + struct berval** ha1s; + struct berval** b; + unsigned char key[MD5_LEN]; + unsigned char k[MD5_LEN]; + int r, first = 1; + int res = HA_FALSE; - ASSERT(ctx && ld && entry && buf && user && clearpw); + ASSERT(ctx && ld && entry && buf && user && clearpw); - if(!ctx->ha1_attr) - return HA_FALSE; - - ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr); + if(!ctx->ha1_attr) + return HA_FALSE; - if(ha1s) - { - digest_makeha1(key, user, realm, clearpw); + ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr); - for(b = ha1s ; *b; b++) + if(ha1s) { - r = parse_ldap_ha1(buf, *b, k); - if(r < 0) - { - res = r; - break; - } - - if(r == HA_FALSE) - { - if(first) - ha_messagex(LOG_ERR, "ldap: server contains invalid HA1 digest hash for user: %s", user); - - first = 0; - continue; - } - - if(memcmp(key, k, MD5_LEN) == 0) - { - ha_messagex(LOG_DEBUG, "ldap: successful validate against ha1"); - res = HA_OK; - break; - } + digest_makeha1(key, user, realm, clearpw); + + for(b = ha1s ; *b; b++) + { + r = parse_ldap_ha1(buf, *b, k); + if(r < 0) + { + res = r; + break; + } + + if(r == HA_FALSE) + { + if(first) + ha_messagex(LOG_ERR, "ldap: server contains invalid HA1 digest hash for user: %s", user); + + first = 0; + continue; + } + + if(memcmp(key, k, MD5_LEN) == 0) + { + ha_messagex(LOG_DEBUG, "ldap: successful validate against ha1"); + res = HA_OK; + break; + } + } + + ldap_value_free_len(ha1s); } - ldap_value_free_len(ha1s); - } - - return res; + return res; } static LDAP* get_ldap_connection(ldap_context_t* ctx) { - LDAP* ld; - int i, r; + LDAP* ld; + int i, r; - ASSERT(ctx); + ASSERT(ctx); - for(i = 0; i < ctx->ldap_max; i++) - { - /* An open connection in the pool */ - if(ctx->pool[i]) + for(i = 0; i < ctx->ldap_max; i++) { - ha_messagex(LOG_DEBUG, "ldap: using cached connection"); - ld = ctx->pool[i]; - ctx->pool[i] = NULL; - return ld; + /* An open connection in the pool */ + if(ctx->pool[i]) + { + ha_messagex(LOG_DEBUG, "ldap: using cached connection"); + ld = ctx->pool[i]; + ctx->pool[i] = NULL; + return ld; + } } - } - if(ctx->pool_mark >= ctx->ldap_max) - { - ha_messagex(LOG_ERR, "ldap: too many open connections"); - return NULL; - } + if(ctx->pool_mark >= ctx->ldap_max) + { + ha_messagex(LOG_ERR, "ldap: too many open connections"); + return NULL; + } - ld = ldap_init(ctx->servers, ctx->port); - if(!ld) - { - ha_message(LOG_ERR, "ldap: couldn't initialize connection"); - return NULL; - } + ld = ldap_init(ctx->servers, ctx->port); + if(!ld) + { + ha_message(LOG_ERR, "ldap: couldn't initialize connection"); + return NULL; + } - if(ctx->user || ctx->password) - { - r = ldap_simple_bind_s(ld, ctx->user ? ctx->user : "", - ctx->password ? ctx->password : ""); - if(r != LDAP_SUCCESS) + if(ctx->user || ctx->password) { - report_ldap("ldap: couldn't bind to LDAP server", r); - ldap_unbind_s(ld); - return NULL; + r = ldap_simple_bind_s(ld, ctx->user ? ctx->user : "", + ctx->password ? ctx->password : ""); + if(r != LDAP_SUCCESS) + { + report_ldap("ldap: couldn't bind to LDAP server", r); + ldap_unbind_s(ld); + return NULL; + } } - } - ctx->pool_mark++; - ha_messagex(LOG_DEBUG, "ldap: opened new connection (total %d)", ctx->pool_mark); - return ld; + ctx->pool_mark++; + ha_messagex(LOG_DEBUG, "ldap: opened new connection (total %d)", ctx->pool_mark); + return ld; } static void discard_ldap_connection(ldap_context_t* ctx, LDAP* ld) { - ldap_unbind_s(ld); - ctx->pool_mark--; - ha_messagex(LOG_DEBUG, "ldap: discarding connection (total %d)", ctx->pool_mark); + ldap_unbind_s(ld); + ctx->pool_mark--; + ha_messagex(LOG_DEBUG, "ldap: discarding connection (total %d)", ctx->pool_mark); } static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld) { - int i, e; + int i, e; - ASSERT(ctx); - - if(!ld) - return; + ASSERT(ctx); - ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &e); + if(!ld) + return; - /* Make sure it's worth saving */ - switch(e) - { - case LDAP_SERVER_DOWN: - case LDAP_LOCAL_ERROR: - case LDAP_NO_MEMORY: - discard_ldap_connection(ctx, ld); - break; + ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &e); - default: - for(i = 0; i < ctx->ldap_max; i++) + /* Make sure it's worth saving */ + switch(e) { - /* An open connection in the pool */ - if(!ctx->pool[i]) - { - ha_messagex(LOG_DEBUG, "ldap: caching connection for later use"); - ctx->pool[i] = ld; - ld = NULL; + case LDAP_SERVER_DOWN: + case LDAP_LOCAL_ERROR: + case LDAP_NO_MEMORY: + discard_ldap_connection(ctx, ld); break; - } - } - break; - }; + default: + for(i = 0; i < ctx->ldap_max; i++) + { + /* An open connection in the pool */ + if(!ctx->pool[i]) + { + ha_messagex(LOG_DEBUG, "ldap: caching connection for later use"); + ctx->pool[i] = ld; + ld = NULL; + break; + } + } + break; + }; } static int retrieve_user_entry(ldap_context_t* ctx, const ha_request_t* req, LDAP* ld, const char* user, const char** dn, LDAPMessage** entry, LDAPMessage** result) { - struct timeval tv; - const char* filter; - const char* attrs[3]; - const char* base; - int scope; - int r; - - ASSERT(ctx && req && ld && user && dn && entry && result); - - if(ctx->filter) - { - /* Filters can also have %u and %r */ - filter = substitute_params(ctx, req, user, ctx->filter); - if(!filter) - return HA_CRITERROR; - } - else - { - filter = "(objectClass=*)"; - } - - attrs[0] = ctx->dobind ? NULL : ctx->pw_attr; - attrs[1] = ctx->dobind ? NULL : ctx->ha1_attr; - attrs[2] = NULL; - - tv.tv_sec = ctx->ldap_timeout; - tv.tv_usec = 0; - - base = *dn ? *dn : ctx->base; - scope = *dn ? LDAP_SCOPE_BASE : ctx->scope; - - ha_messagex(LOG_DEBUG, "ldap: performing search: [ base: %s / scope: %d / filter: %s ]", - base, scope, filter); - - r = ldap_search_st(ld, base, scope, filter, (char**)attrs, 0, &tv, result); - - if(r != LDAP_SUCCESS) - { - if(r == LDAP_NO_SUCH_OBJECT) - { - ha_messagex(LOG_WARNING, "ldap: user not found in LDAP: %s", user); - return HA_FALSE; - } + struct timeval tv; + const char* filter; + const char* attrs[3]; + const char* base; + int scope; + int r; - return report_ldap("couldn't search LDAP server", r); - } + ASSERT(ctx && req && ld && user && dn && entry && result); - - /* Only one result should exist */ - switch(r = ldap_count_entries(ld, *result)) - { - case 1: - *entry = ldap_first_entry(ld, *result); - if(!(*dn)) + if(ctx->filter) { - *dn = ldap_get_dn(ld, *entry); - ha_messagex(LOG_DEBUG, "ldap: found entry for user: %s", *dn); + /* Filters can also have %u and %r */ + filter = substitute_params(ctx, req, user, ctx->filter); + if(!filter) + return HA_CRITERROR; } - return HA_OK; - - case 0: - ha_messagex(LOG_WARNING, "ldap: user not found in LDAP: %s", user); - break; - - default: - ha_messagex(LOG_WARNING, "ldap: more than one user found for filter: %s", filter); - break; - }; - - return HA_FALSE; -} - -static int complete_digest_ha1(ldap_context_t* ctx, digest_record_t* rec, - const ha_request_t* req, const char* user) -{ - LDAP* ld = NULL; /* freed in finally */ - LDAPMessage* results = NULL; /* freed in finally */ - LDAPMessage* entry = NULL; /* no need to free */ - struct berval** ha1s = NULL; /* freed manually */ - char** pws; - int ret = HA_FALSE; - const char* dn = NULL; - int r; - - ASSERT(ctx && req && rec && user); - - ld = get_ldap_connection(ctx); - if(!ld) - { - ret = HA_FAILED; - goto finally; - } - - /* - * Discover the DN of the user. If there's a DN map string - * then we can do this really quickly here without querying - * the LDAP tree - */ - if(ctx->dnmap) - { - /* The map can have %u and %r to denote user and realm */ - dn = substitute_params(ctx, req, user, ctx->dnmap); - if(!dn) + else { - ret = HA_CRITERROR; - goto finally; + filter = "(objectClass=*)"; } - ha_messagex(LOG_INFO, "ldap: mapped %s to %s", user, dn); - } - - /* Okay now we contact the LDAP server. */ - r = retrieve_user_entry(ctx, req, ld, user, &dn, &entry, &results); - if(r != HA_OK) - { - ret = r; - goto finally; - } + attrs[0] = ctx->dobind ? NULL : ctx->pw_attr; + attrs[1] = ctx->dobind ? NULL : ctx->ha1_attr; + attrs[2] = NULL; - /* Figure out the users ha1 */ - if(ctx->ha1_attr) - ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr); - - if(ha1s) - { - if(*ha1s) - { - ret = parse_ldap_ha1(req->buf, *ha1s, rec->ha1); - if(ret != HA_OK) - { - if(ret == HA_FALSE) - ha_messagex(LOG_ERR, "ldap: server contains invalid HA1 for user: %s", user); - } - } + tv.tv_sec = ctx->ldap_timeout; + tv.tv_usec = 0; - ha_messagex(LOG_DEBUG, "ldap: using HA1 from ldap"); - ldap_value_free_len(ha1s); - goto finally; - } + base = *dn ? *dn : ctx->base; + scope = *dn ? LDAP_SCOPE_BASE : ctx->scope; - /* If no ha1 set or none found, use password and make a HA1 */ - pws = ldap_get_values(ld, entry, ctx->pw_attr); + ha_messagex(LOG_DEBUG, "ldap: performing search: [ base: %s / scope: %d / filter: %s ]", + base, scope, filter); - if(pws) - { - /* Find a cleartext password */ - const char* t = find_cleartext_password(req->buf, (const char**)pws); + r = ldap_search_st(ld, base, scope, filter, (char**)attrs, 0, &tv, result); - if(t) + if(r != LDAP_SUCCESS) { - digest_makeha1(rec->ha1, user, req->context->realm, t); - ret = HA_OK; - } + if(r == LDAP_NO_SUCH_OBJECT) + { + ha_messagex(LOG_WARNING, "ldap: user not found in LDAP: %s", user); + return HA_FALSE; + } - ldap_value_free(pws); - - if(ret == HA_OK) - { - ha_messagex(LOG_DEBUG, "ldap: using clear password from ldap"); - goto finally; + return report_ldap("couldn't search LDAP server", r); } - } - ha_messagex(LOG_ERR, "ldap: server contains no clear password or HA1 for user: %s", user); -finally: - - if(ld) - save_ldap_connection(ctx, ld); + /* Only one result should exist */ + switch(r = ldap_count_entries(ld, *result)) + { + case 1: + *entry = ldap_first_entry(ld, *result); + if(!(*dn)) + { + *dn = ldap_get_dn(ld, *entry); + ha_messagex(LOG_DEBUG, "ldap: found entry for user: %s", *dn); + } + return HA_OK; + + case 0: + ha_messagex(LOG_WARNING, "ldap: user not found in LDAP: %s", user); + break; - if(results) - ldap_msgfree(results); + default: + ha_messagex(LOG_WARNING, "ldap: more than one user found for filter: %s", filter); + break; + }; - return ret; + return HA_FALSE; } -static int basic_ldap_response(ldap_context_t* ctx, const char* header, - const ha_request_t* req, ha_response_t* resp) +static int complete_digest(const ha_request_t* req, const char* user, unsigned char* ha1) { - basic_header_t basic; - LDAP* ld = NULL; - LDAPMessage* entry = NULL; - LDAPMessage* results = NULL; - const char* dn = NULL; - int ret = HA_FALSE; - int found = 0; - int r; - - ASSERT(header && resp && req); - - if((r = basic_parse(header, req->buf, &basic)) < 0) - return r; - - /* Past this point we don't return directly */ - - /* Check and see if this connection is in the cache */ - if(have_cached_basic(ctx, basic.key)) - { - ha_messagex(LOG_NOTICE, "ldap: validated basic user against cache: %s", - basic.user); - found = 1; - ret = HA_OK; - goto finally; - } - - /* If we have a user name and password */ - if(!basic.user || !basic.user[0] || - !basic.password || !basic.password[0]) - { - ha_messagex(LOG_NOTICE, "ldap: no valid basic auth info"); - goto finally; - } - - - ld = get_ldap_connection(ctx); - if(!ld) - { - ret = HA_FAILED; - goto finally; - } - - - /* - * Discover the DN of the user. If there's a DN map string - * then we can do this really quickly here without querying - * the LDAP tree - */ - if(ctx->dnmap) - { - /* The map can have %u and %r to denote user and realm */ - dn = substitute_params(ctx, req, basic.user, ctx->dnmap); - if(!dn) + ldap_context_t* ctx = (ldap_context_t*)req->context->ctx_data; + LDAP* ld = NULL; /* freed in finally */ + LDAPMessage* results = NULL; /* freed in finally */ + LDAPMessage* entry = NULL; /* no need to free */ + struct berval** ha1s = NULL; /* freed manually */ + char** pws; + int ret = HA_FALSE; + const char* dn = NULL; + int r; + + ASSERT(req && ha1 && user); + + ld = get_ldap_connection(ctx); + if(!ld) { - ret = HA_CRITERROR; - goto finally; + ret = HA_FAILED; + goto finally; } - ha_messagex(LOG_INFO, "ldap: mapped %s to %s", basic.user, dn); - } - - - /** - * Okay now we contact the LDAP server. There are many ways - * this is used for different authentication modes: - * - * - If a dn has been mapped above, this can apply a - * configured filter to narrow things down. - * - If no dn has been mapped, then this maps out a dn - * by using the single object the filter returns. - * - If not in 'dobind' mode we also retrieve the password - * here. - * - * All this results in only one query to the LDAP server, - * except for the case of dobind without a dnmap. - */ - - if(!ctx->dobind || !dn || ctx->filter) - { - r = retrieve_user_entry(ctx, req, ld, basic.user, &dn, &entry, &results); - if(r != HA_OK) + /* + * Discover the DN of the user. If there's a DN map string + * then we can do this really quickly here without querying + * the LDAP tree + */ + if(ctx->dnmap) { - ret = r; - goto finally; + /* The map can have %u and %r to denote user and realm */ + dn = substitute_params(ctx, req, user, ctx->dnmap); + if(!dn) + { + ret = HA_FAILED; + goto finally; + } + + ha_messagex(LOG_INFO, "ldap: mapped %s to %s", user, dn); } - } - - - /* Now if in bind mode we try to bind as that user */ - if(ctx->dobind) - { - ASSERT(dn); - r = ldap_simple_bind_s(ld, dn, basic.password); - if(r != LDAP_SUCCESS) + /* Okay now we contact the LDAP server. */ + r = retrieve_user_entry(ctx, req, ld, user, &dn, &entry, &results); + if(r != HA_OK) { - if(r == LDAP_INVALID_CREDENTIALS) - ha_messagex(LOG_WARNING, "ldap: basic authentication (via bind) failed for user: %s", - basic.user); - - else - report_ldap("couldn't bind to LDAP server", r); - - goto finally; + ret = r; + goto finally; } - /* It worked! */ - ha_messagex(LOG_NOTICE, "ldap: validated basic user using bind: %s", basic.user); - found = 1; + /* Figure out the users ha1 */ + if(ctx->ha1_attr) + ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr); - /* Now we have to rebind the connection back to the main user */ - r = ldap_simple_bind_s(ld, ctx->user ? ctx->user : "", - ctx->password ? ctx->password : ""); - if(r != LDAP_SUCCESS) + if(ha1s) { - report_ldap("ldap: couldn't rebind LDAP connection back to auth credentials", r); - - /* Discard the connection since it's useless to us */ - discard_ldap_connection(ctx, ld); - ld = NULL; + if(*ha1s) + { + ret = parse_ldap_ha1(req->buf, *ha1s, ha1); + if(ret != HA_OK) + { + if(ret == HA_FALSE) + ha_messagex(LOG_ERR, "ldap: server contains invalid HA1 for user: %s", user); + } + } + + ha_messagex(LOG_DEBUG, "ldap: using HA1 from ldap"); + ldap_value_free_len(ha1s); + goto finally; } - } - - - /* Otherwise we compare the password attribute */ - else - { - ret = validate_ldap_password(ctx, ld, entry, req->buf, basic.user, basic.password); - if(ret == HA_FALSE) - ret = validate_ldap_ha1(ctx, ld, entry, req->buf, basic.user, - req->context->realm, basic.password); - if(ret == HA_OK) - { - ha_messagex(LOG_NOTICE, "ldap: validated basic user password/ha1: %s", basic.user); - found = 1; - } + /* If no ha1 set or none found, use password and make a HA1 */ + pws = ldap_get_values(ld, entry, ctx->pw_attr); - else + if(pws) { - ha_messagex(LOG_WARNING, "ldap: invalid, unreadable or unrecognized password for user: %s", basic.user); + /* Find a cleartext password */ + const char* t = find_cleartext_password(req->buf, (const char**)pws); + + if(t) + { + digest_makeha1(ha1, user, req->context->realm, t); + ret = HA_OK; + } + + ldap_value_free(pws); + + if(ret == HA_OK) + { + ha_messagex(LOG_DEBUG, "ldap: using clear password from ldap"); + goto finally; + } } - } + ha_messagex(LOG_ERR, "ldap: server contains no clear password or HA1 for user: %s", user); finally: - if(ld) - save_ldap_connection(ctx, ld); - - if(results) - ldap_msgfree(results); + if(ld) + save_ldap_connection(ctx, ld); - if(found && ret >= 0) - { - resp->code = HA_SERVER_OK; - resp->detail = basic.user; + if(results) + ldap_msgfree(results); - /* We put this connection into the successful connections */ - ret = add_cached_basic(ctx, req->context, basic.key); - } - - return ret; -} - -static int digest_ldap_challenge(ldap_context_t* ctx, const ha_request_t* req, - ha_response_t* resp, int stale) -{ - unsigned char nonce[DIGEST_NONCE_LEN]; - const char* nonce_str; - const char* header; - - ASSERT(ctx && resp && req); - -#ifdef _DEBUG - if(req->context->digest_debugnonce) - { - nonce_str = req->context->digest_debugnonce; - ha_messagex(LOG_WARNING, "ldap: using debug nonce. security non-existant."); - } - else -#endif - { - unsigned char nonce[DIGEST_NONCE_LEN]; - digest_makenonce(nonce, g_ldap_secret, NULL); - - nonce_str = ha_bufenchex(req->buf, nonce, DIGEST_NONCE_LEN); - if(!nonce_str) - return HA_CRITERROR; - } - - /* Now generate a message to send */ - header = digest_challenge(req->buf, nonce_str, req->context->realm, - req->digest_domain, stale); - - if(!header) - return HA_CRITERROR; - - /* And append it nicely */ - resp->code = HA_SERVER_DECLINE; - ha_addheader(resp, "WWW-Authenticate", header); - - ha_messagex(LOG_DEBUG, "ldap: created digest challenge with nonce: %s", nonce_str); - return HA_OK; + return ret; } -static int digest_ldap_response(ldap_context_t* ctx, const char* header, - const ha_request_t* req, ha_response_t* resp) +static int validate_basic(const ha_request_t* req, const char* user, const char* password) { - unsigned char nonce[DIGEST_NONCE_LEN]; - digest_header_t dg; - digest_record_t* rec = NULL; - const char* t; - time_t expiry; - int ret = HA_FALSE; - int stale = 0; - int r; - - ASSERT(ctx && header && req && resp); - - /* We use this below to send a default response */ - resp->code = -1; - - if((r = digest_parse(header, req->buf, &dg, nonce)) < 0) - return r; - -#ifdef _DEBUG - if(req->context->digest_debugnonce) - { - if(dg.nonce && strcmp(dg.nonce, req->context->digest_debugnonce) != 0) + ldap_context_t* ctx = (ldap_context_t*)req->context->ctx_data; + LDAP* ld = NULL; + LDAPMessage* entry = NULL; + LDAPMessage* results = NULL; + const char* dn = NULL; + int ret = HA_FALSE; + int found = 0; + int r; + + ASSERT(req && user && password); + + ld = get_ldap_connection(ctx); + if(!ld) { - ret = HA_FALSE; - ha_messagex(LOG_WARNING, "ldap: digest response contains invalid nonce"); - goto finally; + ret = HA_FAILED; + goto finally; } - /* Do a rough hash into the real nonce, for use as a key */ - md5_string(nonce, req->context->digest_debugnonce); - /* Debug nonce's never expire */ - expiry = time(NULL); - } - else -#endif - { - r = digest_checknonce(nonce, g_ldap_secret, &expiry); - if(r != HA_OK) + /* + * Discover the DN of the user. If there's a DN map string + * then we can do this really quickly here without querying + * the LDAP tree + */ + if(ctx->dnmap) { - if(r == HA_FALSE) - ha_messagex(LOG_WARNING, "ldap: digest response contains invalid nonce"); - - goto finally; + /* The map can have %u and %r to denote user and realm */ + dn = substitute_params(ctx, req, user, ctx->dnmap); + if(!dn) + { + ret = HA_CRITERROR; + goto finally; + } + + ha_messagex(LOG_INFO, "ldap: mapped %s to %s", user, dn); } - } - - rec = get_cached_digest(ctx, req->context, nonce); - /* Check to see if we're stale */ - if((expiry + req->context->cache_timeout) <= time(NULL)) - { - ha_messagex(LOG_INFO, "ldap: nonce expired, sending stale challenge: %s", - dg.username); - stale = 1; - goto finally; - } - - if(!rec) - { - ha_messagex(LOG_INFO, "ldap: no record in cache, creating one: %s", dg.username); - - /* - * If we're valid but don't have a record in the - * cache then complete the record properly. + /** + * Okay now we contact the LDAP server. There are many ways + * this is used for different authentication modes: + * + * - If a dn has been mapped above, this can apply a + * configured filter to narrow things down. + * - If no dn has been mapped, then this maps out a dn + * by using the single object the filter returns. + * - If not in 'dobind' mode we also retrieve the password + * here. + * + * All this results in only one query to the LDAP server, + * except for the case of dobind without a dnmap. */ - rec = digest_makerec(nonce, dg.username); - if(!rec) + if(!ctx->dobind || !dn || ctx->filter) { - ret = HA_CRITERROR; - goto finally; + r = retrieve_user_entry(ctx, req, ld, user, &dn, &entry, &results); + if(r != HA_OK) + { + ret = r; + goto finally; + } } - r = complete_digest_ha1(ctx, rec, req, dg.username); - if(r != HA_OK) + + /* Now if in bind mode we try to bind as that user */ + if(ctx->dobind) { - ret = r; - goto finally; + ASSERT(dn); + + r = ldap_simple_bind_s(ld, dn, password); + if(r != LDAP_SUCCESS) + { + if(r == LDAP_INVALID_CREDENTIALS) + ha_messagex(LOG_WARNING, "ldap: basic authentication (via bind) failed for user: %s", + user); + + else + report_ldap("couldn't bind to LDAP server", r); + + goto finally; + } + + /* It worked! */ + ha_messagex(LOG_NOTICE, "ldap: validated basic user using bind: %s", user); + found = 1; + + /* Now we have to rebind the connection back to the main user */ + r = ldap_simple_bind_s(ld, ctx->user ? ctx->user : "", + ctx->password ? ctx->password : ""); + if(r != LDAP_SUCCESS) + { + report_ldap("ldap: couldn't rebind LDAP connection back to auth credentials", r); + + /* Discard the connection since it's useless to us */ + discard_ldap_connection(ctx, ld); + ld = NULL; + } } - } - - /* We had a record so ... */ - else - { - rec->nc++; - } - - ret = digest_check(&dg, rec, req->context, req->buf, - req->args[AUTH_ARG_METHOD], req->args[AUTH_ARG_URI]); - - if(ret == HA_BADREQ) - { - ret = HA_FALSE; - resp->code = HA_SERVER_BADREQ; - } - - else if(ret == HA_OK) - { - resp->code = HA_SERVER_OK; - resp->detail = dg.username; - - /* Figure out if we need a new nonce */ - if((expiry + (req->context->cache_timeout - - (req->context->cache_timeout / 8))) < time(NULL)) - { - ha_messagex(LOG_INFO, "ldap: nonce almost expired, creating new one: %s", - dg.username); - digest_makenonce(nonce, g_ldap_secret, NULL); - stale = 1; - } - t = digest_respond(req->buf, &dg, rec, stale ? nonce : NULL); - if(!t) + /* Otherwise we compare the password attribute */ + else { - ret = HA_CRITERROR; - goto finally; + ret = validate_ldap_password(ctx, ld, entry, req->buf, user, password); + if(ret == HA_FALSE) + ret = validate_ldap_ha1(ctx, ld, entry, req->buf, user, + req->context->realm, password); + + if(ret == HA_OK) + { + ha_messagex(LOG_NOTICE, "ldap: validated basic user password/ha1: %s", user); + found = 1; + } + + else + { + ha_messagex(LOG_WARNING, "ldap: invalid, unreadable or unrecognized password for user: %s", user); + } } - if(t[0]) - ha_addheader(resp, "Authentication-Info", t); - - ha_messagex(LOG_NOTICE, "ldap: validated digest user: %s", dg.username); - - /* Put the connection into the cache */ - if((r = save_cached_digest(ctx, req->context, rec)) < 0) - ret = r; - - rec = NULL; - } - finally: - /* If the record wasn't stored away then free it */ - if(rec) - free(rec); + if(ld) + save_ldap_connection(ctx, ld); - /* If nobody above responded then challenge the client again */ - if(resp->code == -1) - return digest_ldap_challenge(ctx, req, resp, stale); + if(results) + ldap_msgfree(results); - return ret; + /* TODO: Check to make sure this fits within the return values */ + return ret; } @@ -1221,292 +884,184 @@ finally: int ldap_config(ha_context_t* context, const char* name, const char* value) { - ldap_context_t* ctx = (ldap_context_t*)(context->ctx_data); - - ASSERT(name && value && value[0]); - - if(strcmp(name, "ldapservers") == 0) - { - ctx->servers = value; - return HA_OK; - } - - else if(strcmp(name, "ldapfilter") == 0) - { - ctx->filter = value; - return HA_OK; - } - - else if(strcmp(name, "ldapbase") == 0) - { - ctx->base = value; - return HA_OK; - } - - else if(strcmp(name, "ldappwattr") == 0) - { - ctx->pw_attr = value; - return HA_OK; - } - - else if(strcmp(name, "ldapha1attr") == 0) - { - ctx->ha1_attr = value; - return HA_OK; - } - - else if(strcmp(name, "ldapuser") == 0) - { - ctx->user = value; - return HA_OK; - } - - else if(strcmp(name, "ldappassword") == 0) - { - ctx->password = value; - return HA_OK; - } - - else if(strcmp(name, "ldapdnmap") == 0) - { - ctx->dnmap = value; - return HA_OK; - } + ldap_context_t* ctx = (ldap_context_t*)(context->ctx_data); - else if(strcmp(name, "ldapscope") == 0) - { - if(strcmp(value, "sub") == 0 || strcmp(value, "subtree") == 0) - ctx->scope = LDAP_SCOPE_SUBTREE; - else if(strcmp(value, "base") == 0) - ctx->scope = LDAP_SCOPE_BASE; - else if(strcmp(value, "one") == 0 || strcmp(value, "onelevel") == 0) - ctx->scope = LDAP_SCOPE_ONELEVEL; + ASSERT(name && value && value[0]); - else + if(strcmp(name, "ldapservers") == 0) { - ha_messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name); - return HA_FAILED; + ctx->servers = value; + return HA_OK; } - return HA_OK; - } - - else if(strcmp(name, "ldapdobind") == 0) - { - return ha_confbool(name, value, &(ctx->dobind)); - } - - else if(strcmp(name, "ldapmax") == 0) - { - return ha_confint(name, value, 1, 256, &(ctx->ldap_max)); - } - - else if(strcmp(name, "ldaptimeout") == 0) - { - return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout)); - } - - return HA_FALSE; -} - -int ldap_inithand(ha_context_t* context) -{ - /* Global initialization */ - if(!context) - { - ha_messagex(LOG_DEBUG, "ldap: generating secret"); - return ha_genrandom(g_ldap_secret, DIGEST_SECRET_LEN); - } - - - /* Context specific initialization */ - else - { - ldap_context_t* ctx = (ldap_context_t*)(context->ctx_data); - hsh_table_calls_t htc; - - ASSERT(ctx); - - /* Make sure there are some types of authentication we can do */ - if(!(context->allowed_types & (HA_TYPE_BASIC | HA_TYPE_DIGEST))) + else if(strcmp(name, "ldapfilter") == 0) { - ha_messagex(LOG_ERR, "ldap: module configured, but does not implement any " - "configured authentication type."); - return HA_FAILED; + ctx->filter = value; + return HA_OK; } - /* Check for mandatory configuration */ - if(!ctx->servers) + else if(strcmp(name, "ldapbase") == 0) { - ha_messagex(LOG_ERR, "ldap: configuration incomplete. " - "Must have LDAPServers."); - return HA_FAILED; + ctx->base = value; + return HA_OK; } - if(!ctx->dnmap && (!ctx->filter || !ctx->base)) + else if(strcmp(name, "ldappwattr") == 0) { - ha_messagex(LOG_ERR, "ldap: configuration incomplete. " - "When not using LDAPDNMap must specify LDAPBase and LDAPFilter."); - return HA_FAILED; + ctx->pw_attr = value; + return HA_OK; } - /* The cache for digest records and basic */ - if(!(ctx->cache = hsh_create(MD5_LEN))) + else if(strcmp(name, "ldapha1attr") == 0) { - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; + ctx->ha1_attr = value; + return HA_OK; } - htc.f_freeval = free_hash_object; - htc.arg = NULL; - hsh_set_table_calls(ctx->cache, &htc); - - ASSERT(!ctx->pool); - ASSERT(ctx->ldap_max > 0); - - /* - * Our connection pool. It's the size of our maximum - * amount of pending connections as that's the max - * we'd be able to use at a time anyway. - */ - ctx->pool = (LDAP**)malloc(sizeof(LDAP*) * ctx->ldap_max); - if(!ctx->pool) + else if(strcmp(name, "ldapuser") == 0) { - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; + ctx->user = value; + return HA_OK; } - memset(ctx->pool, 0, sizeof(LDAP*) * ctx->ldap_max); - - ha_messagex(LOG_INFO, "ldap: initialized handler"); - } - - return HA_OK; -} - -void ldap_destroy(ha_context_t* context) -{ - ldap_context_t* ctx; - int i; - - if(!context) - return; - - /* Note: We don't need to be thread safe here anymore */ - ctx = (ldap_context_t*)(context->ctx_data); - - ASSERT(ctx); - - if(ctx->cache) - hsh_free(ctx->cache); - - if(ctx->pool) - { - /* Close any connections we have open */ - for(i = 0; i < ctx->ldap_max; i++) + else if(strcmp(name, "ldappassword") == 0) { - if(ctx->pool[i]) - ldap_unbind_s(ctx->pool[i]); + ctx->password = value; + return HA_OK; } - /* And free the connection pool */ - free(ctx->pool); - } - - ha_messagex(LOG_INFO, "ldap: uninitialized handler"); -} - -int ldap_process(const ha_request_t* req, ha_response_t* resp) -{ - ldap_context_t* ctx = (ldap_context_t*)req->context->ctx_data; - time_t t = time(NULL); - const char* header = NULL; - int ret, r; - - ASSERT(req && resp); - ASSERT(req->args[AUTH_ARG_METHOD]); - ASSERT(req->args[AUTH_ARG_URI]); - - ha_lock(NULL); - - /* Purge out stale connection stuff. */ - r = hsh_purge(ctx->cache, t - req->context->cache_timeout); - - ha_unlock(NULL); - - if(r > 0) - ha_messagex(LOG_DEBUG, "ldap: purged cache records: %d", r); + else if(strcmp(name, "ldapdnmap") == 0) + { + ctx->dnmap = value; + return HA_OK; + } - /* We use this below to detect whether to send a default response */ - resp->code = -1; + else if(strcmp(name, "ldapscope") == 0) + { + if(strcmp(value, "sub") == 0 || strcmp(value, "subtree") == 0) + ctx->scope = LDAP_SCOPE_SUBTREE; + else if(strcmp(value, "base") == 0) + ctx->scope = LDAP_SCOPE_BASE; + else if(strcmp(value, "one") == 0 || strcmp(value, "onelevel") == 0) + ctx->scope = LDAP_SCOPE_ONELEVEL; + else + { + ha_messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name); + return HA_FAILED; + } + + return HA_OK; + } + else if(strcmp(name, "ldapdobind") == 0) + { + return ha_confbool(name, value, &(ctx->dobind)); + } - /* Check the headers and see if we got a response thingy */ - if(req->context->allowed_types & HA_TYPE_DIGEST) - { - header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST); - if(header) + else if(strcmp(name, "ldapmax") == 0) { - ha_messagex(LOG_DEBUG, "ldap: processing digest auth header"); - ret = digest_ldap_response(ctx, header, req, resp); - if(ret < 0) - return ret; + return ha_confint(name, value, 1, 256, &(ctx->ldap_max)); } - } - /* Or a basic authentication */ - if(!header && req->context->allowed_types & HA_TYPE_BASIC) - { - header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC); - if(header) + else if(strcmp(name, "ldaptimeout") == 0) { - ha_messagex(LOG_DEBUG, "ldap: processing basic auth header"); - ret = basic_ldap_response(ctx, header, req, resp); - if(ret < 0) - return ret; + return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout)); } - } + return HA_FALSE; +} - /* Send a default response if that's what we need */ - if(resp->code == -1) - { - resp->code = HA_SERVER_DECLINE; +int ldap_inithand(ha_context_t* context) +{ + int r; - if(req->context->allowed_types & HA_TYPE_BASIC) - { - ha_bufmcat(req->buf, "BASIC realm=\"", req->context->realm , "\"", NULL); + if((r = bd_init(context)) != HA_OK) + return r; - if(ha_buferr(req->buf)) - return HA_CRITERROR; + /* Context specific initialization */ + if(context) + { + ldap_context_t* ctx = (ldap_context_t*)(context->ctx_data); + ASSERT(ctx); - ha_addheader(resp, "WWW-Authenticate", ha_bufdata(req->buf)); - ha_messagex(LOG_DEBUG, "ldap: sent basic auth request"); + /* Check for mandatory configuration */ + if(!ctx->servers) + { + ha_messagex(LOG_ERR, "ldap: configuration incomplete. " + "Must have LDAPServers."); + return HA_FAILED; + } + + if(!ctx->dnmap && (!ctx->filter || !ctx->base)) + { + ha_messagex(LOG_ERR, "ldap: configuration incomplete. " + "When not using LDAPDNMap must specify LDAPBase and LDAPFilter."); + return HA_FAILED; + } + + ASSERT(!ctx->pool); + ASSERT(ctx->ldap_max > 0); + + /* + * Our connection pool. It's the size of our maximum + * amount of pending connections as that's the max + * we'd be able to use at a time anyway. + */ + ctx->pool = (LDAP**)malloc(sizeof(LDAP*) * ctx->ldap_max); + if(!ctx->pool) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_CRITERROR; + } + + memset(ctx->pool, 0, sizeof(LDAP*) * ctx->ldap_max); + ha_messagex(LOG_INFO, "ldap: initialized handler"); } - if(req->context->allowed_types & HA_TYPE_DIGEST) + return HA_OK; +} + +void ldap_destroy(ha_context_t* context) +{ + if(context) { - ret = digest_ldap_challenge(ctx, req, resp, 0); - if(ret < 0) - return ret; + /* Note: We don't need to be thread safe here anymore */ + ldap_context_t* ctx = (ldap_context_t*)(context->ctx_data); + int i; + + ASSERT(ctx); + + if(ctx->pool) + { + /* Close any connections we have open */ + for(i = 0; i < ctx->ldap_max; i++) + { + if(ctx->pool[i]) + ldap_unbind_s(ctx->pool[i]); + } + + /* And free the connection pool */ + free(ctx->pool); + } } - } - return ret; + bd_destroy(context); + ha_messagex(LOG_INFO, "ldap: uninitialized handler"); } + /* ------------------------------------------------------------------------------- * Handler Definition */ ha_handler_t ldap_handler = { - "LDAP", /* The type */ - ldap_inithand, /* Initialization function */ - ldap_destroy, /* Uninitialization routine */ - ldap_config, /* Config routine */ - ldap_process, /* Processing routine */ - &ldap_defaults, /* The context defaults */ - sizeof(ldap_context_t) + "LDAP", /* The type */ + ldap_inithand, /* Initialization function */ + ldap_destroy, /* Uninitialization routine */ + ldap_config, /* Config routine */ + bd_process, /* Processing routine */ + &ldap_defaults, /* The context defaults */ + sizeof(ldap_context_t) }; diff --git a/daemon/simple.c b/daemon/simple.c index ee4fafa..d570a25 100644 --- a/daemon/simple.c +++ b/daemon/simple.c @@ -5,19 +5,14 @@ #include "usuals.h" #include "httpauthd.h" #include "defaults.h" -#include "basic.h" -#include "digest.h" #include "hash.h" +#include "bd.h" #include "md5.h" -#include <syslog.h> +#include <stdio.h> #include <fcntl.h> -#include <unistd.h> - -unsigned char g_simple_secret[DIGEST_SECRET_LEN]; #define SIMPLE_MAXLINE 256 -#define BASIC_ESTABLISHED (void*)1 /* ------------------------------------------------------------------------------- * Structures @@ -25,736 +20,274 @@ unsigned char g_simple_secret[DIGEST_SECRET_LEN]; typedef struct simple_context { - /* Settings ----------------------------------------------------------- */ - const char* filename; /* The file name with the user names */ + /* Base Handler ------------------------------------------------------ */ + bd_context_t bd; - /* Context ----------------------------------------------------------- */ - hsh_t* cache; /* Some cached records or basic */ + /* Settings ---------------------------------------------------------- */ + const char* filename; /* The file name with the user names */ } simple_context_t; +/* Forward declarations for callbacks */ +static int complete_digest(const ha_request_t* req, const char* user, unsigned char* ha1); +static int validate_basic(const ha_request_t* req, const char* user, const char* password); + +/* The defaults for the context */ +static const simple_context_t simple_defaults = +{ + BD_CALLBACKS(complete_digest, validate_basic), + NULL /* filename */ +}; /* ------------------------------------------------------------------------------- * Internal Functions */ -static void free_hash_object(void* arg, void* val) -{ - if(val && val != BASIC_ESTABLISHED) - free(val); -} - -static digest_record_t* get_cached_digest(simple_context_t* ctx, ha_context_t* c, - unsigned char* nonce) -{ - digest_record_t* rec; - - ASSERT(ctx && c && nonce); - - if(c->cache_max == 0) - return NULL; - - ha_lock(NULL); - - rec = (digest_record_t*)hsh_get(ctx->cache, nonce); - - /* Just in case it's a basic :) */ - if(rec && rec != BASIC_ESTABLISHED) - hsh_rem(ctx->cache, nonce); - - ha_unlock(NULL); - - ASSERT(!rec || memcmp(nonce, rec->nonce, DIGEST_NONCE_LEN) == 0); - return rec; -} - -static int have_cached_basic(simple_context_t* ctx, unsigned char* key) -{ - int ret = 0; - - ASSERT(ctx && key); - - ha_lock(NULL); - - ret = (hsh_get(ctx->cache, key) == BASIC_ESTABLISHED); - - ha_unlock(NULL); - - return ret; -} - -static int save_cached_digest(simple_context_t* ctx, ha_context_t* c, - digest_record_t* rec) +static int complete_digest(const ha_request_t* req, const char* user, unsigned char* ha1) { - int r; - - ASSERT(ctx && c && rec); - - if(c->cache_max == 0) - { - free_hash_object(NULL, rec); - return HA_FALSE; - } - - ha_lock(NULL); - - while(hsh_count(ctx->cache) >= c->cache_max) - hsh_bump(ctx->cache); - - r = hsh_set(ctx->cache, rec->nonce, rec); - - ha_unlock(NULL); - - if(!r) - { - free_hash_object(NULL, rec); - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; - } - - return HA_OK; -} - -static int add_cached_basic(simple_context_t* ctx, ha_context_t* c, - unsigned char* key) -{ - int r; - - ASSERT(ctx && c && key); - - if(c->cache_max == 0) - return HA_FALSE; - - ha_lock(NULL); - - while(hsh_count(ctx->cache) >= c->cache_max) - hsh_bump(ctx->cache); - - r = hsh_set(ctx->cache, key, BASIC_ESTABLISHED); - - ha_unlock(NULL); - - if(!r) - { - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; - } - - return HA_OK; -} - -static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec, - const ha_request_t* req, const char* user) -{ - FILE* f; - char* t; - char* t2; - size_t len; - char line[SIMPLE_MAXLINE]; - int ret = HA_FALSE; - - ASSERT(ctx && rec && req && user && user[0]); - ha_messagex(LOG_DEBUG, "searching password file for user's ha1: %s", user); - - f = fopen(ctx->filename, "r"); - if(!f) - { - ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename); - return HA_FAILED; - } - - /* - * 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, SIMPLE_MAXLINE, f); - - if(ferror(f)) - { - ha_message(LOG_ERR, "simple: error reading basic password file"); - ret = HA_FAILED; - break; - } - - t = strchr(line, ':'); - if(t) - { - /* Split the line */ - *t = 0; - t++; - - /* Check the user */ - if(strcmp(line, user) == 0) - { - /* Otherwise it might be a digest type ha1. */ - t2 = strchr(t, ':'); - if(t2) - { - *t2 = 0; - t2++; - - /* Check the realm */ - if(strcmp(t, req->context->realm) == 0) - { - len = MD5_LEN; - - /* Now try antd decode the ha1 */ - t = ha_bufdechex(req->buf, t2, &len); - if(t && len == MD5_LEN) - { - ha_messagex(LOG_DEBUG, "simple: found ha1 for user: %s", user); - memcpy(rec->ha1, t, MD5_LEN); - ret = HA_OK; - break; - } - } - } - - if(!t2 || ret != HA_OK) - ha_messagex(LOG_WARNING, "simple: user '%s' found in file, but password not in digest format", user); - } - } - } - - fclose(f); - - if(ha_buferr(req->buf)) - return HA_CRITERROR; - - return ret; -} - -static int validate_user_password(simple_context_t* ctx, const ha_request_t* req, - const char* user, const char* clearpw) -{ - FILE* f; - char line[SIMPLE_MAXLINE]; - unsigned char ha1[MD5_LEN]; - char* t; - char* t2; - size_t len; - int ret = HA_FALSE; - - ASSERT(ctx && req); - ASSERT(user && user[0] && clearpw); - ha_messagex(LOG_DEBUG, "simple: validating user against password file: %s", user); - - f = fopen(ctx->filename, "r"); - if(!f) - { - ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename); - return HA_FAILED; - } - - digest_makeha1(ha1, user, req->context->realm, clearpw); - - /* - * 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, SIMPLE_MAXLINE, f); - - if(ferror(f)) + simple_context_t* ctx = (simple_context_t*)req->context->ctx_data; + FILE* f; + char* t; + char* t2; + size_t len; + char line[SIMPLE_MAXLINE]; + int ret = HA_FALSE; + + ASSERT(ctx && req && user && user[0]); + ha_messagex(LOG_DEBUG, "searching password file for user's ha1: %s", user); + + f = fopen(ctx->filename, "r"); + if(!f) { - ha_message(LOG_ERR, "simple: error reading basic password file"); - ret = HA_FAILED; - break; + ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename); + return HA_FAILED; } - /* Take white space off end of line */ - trim_end(line); + /* + * Note: There should be no returns or jumps between + * this point and the closing of the file below. + */ - t = strchr(line, ':'); - if(t) + /* Now look through the whole file */ + while(!feof(f)) { - /* Split the line */ - *t = 0; - t++; - - /* Check the user */ - if(strcmp(line, user) == 0) - { - /* Not sure if crypt is thread safe so we lock */ - ha_lock(NULL); + fgets(line, SIMPLE_MAXLINE, f); - /* Check the password */ - t2 = crypt(clearpw, t); - - ha_unlock(NULL); - - if(strcmp(crypt(clearpw, t), t) == 0) + if(ferror(f)) { - ha_messagex(LOG_DEBUG, "simple: found valid crypt password for user: %s", user); - ret = HA_OK; - break; + ha_message(LOG_ERR, "simple: error reading basic password file"); + ret = HA_FAILED; + break; } - /* Otherwise it might be a digest type ha1. */ - t2 = strchr(t, ':'); - if(t2) + t = strchr(line, ':'); + if(t) { - *t2 = 0; - t2++; - - /* Check the realm */ - if(strcmp(t, req->context->realm) == 0) - { - len = MD5_LEN; + /* Split the line */ + *t = 0; + t++; - /* Now try antd decode the ha1 */ - t = ha_bufdechex(req->buf, t2, &len); - if(t && len == MD5_LEN && memcmp(ha1, t, MD5_LEN) == 0) + /* Check the user */ + if(strcmp(line, user) == 0) { - ha_messagex(LOG_DEBUG, "simple: found valid ha1 for user: %s", user); - ret = HA_OK; - break; + /* Otherwise it might be a digest type ha1. */ + t2 = strchr(t, ':'); + if(t2) + { + *t2 = 0; + t2++; + + /* Check the realm */ + if(strcmp(t, req->context->realm) == 0) + { + len = MD5_LEN; + + /* Now try and decode the ha1 */ + t = ha_bufdechex(req->buf, t2, &len); + if(t && len == MD5_LEN) + { + ha_messagex(LOG_DEBUG, "simple: found ha1 for user: %s", user); + memcpy(ha1, t, MD5_LEN); + ret = HA_OK; + break; + } + } + } + + if(!t2 || ret != HA_OK) + ha_messagex(LOG_WARNING, "simple: user '%s' found in file, but password not in digest format", user); } - } } - - if(ha_buferr(req->buf)) - break; - } } - } - - fclose(f); - - if(ha_buferr(req->buf)) - return HA_CRITERROR; - - return ret; -} - -static int simple_basic_response(simple_context_t* ctx, const char* header, - const ha_request_t* req, ha_response_t* resp) -{ - basic_header_t basic; - int ret = HA_FALSE; - int found = 0; - int r; - - ASSERT(header && req && resp); - - if((r = basic_parse(header, req->buf, &basic)) < 0) - return r; - - /* Past this point we don't return directly */ - - /* Check and see if this connection is in the cache */ - if(have_cached_basic(ctx, basic.key)) - { - ha_messagex(LOG_NOTICE, "simple: validated basic user against cache: %s", basic.user); - found = 1; - ret = HA_OK; - goto finally; - } - - /* If we have a user name and password */ - if(!basic.user || !basic.user[0] || - !basic.password || !basic.password[0]) - goto finally; - - - ret = validate_user_password(ctx, req, basic.user, basic.password); - - if(ret == HA_OK) - ha_messagex(LOG_NOTICE, "simple: validated basic user against file: %s", basic.user); - - else - ha_messagex(LOG_WARNING, "simple: basic authentication failed for user: %s", basic.user); -finally: + fclose(f); - if(ret == HA_OK) - { - resp->code = HA_SERVER_OK; - resp->detail = basic.user; - - /* We put this connection into the successful connections */ - ret = add_cached_basic(ctx, req->context, basic.key); - } - - return ret; -} - -static int simple_digest_challenge(simple_context_t* ctx, const ha_request_t* req, - ha_response_t* resp, int stale) -{ - const char* nonce_str; - const char* header; - - ASSERT(ctx && req && resp); - - /* Generate an nonce */ - -#ifdef _DEBUG - if(req->context->digest_debugnonce) - { - nonce_str = req->context->digest_debugnonce; - ha_messagex(LOG_WARNING, "simple: using debug nonce. security non-existant."); - } - else -#endif - { - unsigned char nonce[DIGEST_NONCE_LEN]; - digest_makenonce(nonce, g_simple_secret, NULL); - - nonce_str = ha_bufenchex(req->buf, nonce, DIGEST_NONCE_LEN); - if(!nonce_str) - return HA_CRITERROR; - } - - - /* Now generate a message to send */ - header = digest_challenge(req->buf, nonce_str, req->context->realm, - req->digest_domain, stale); - - if(!header) - return HA_CRITERROR; - - /* And append it nicely */ - resp->code = HA_SERVER_DECLINE; - ha_addheader(resp, "WWW-Authenticate", header); + if(ha_buferr(req->buf)) + return HA_CRITERROR; - ha_messagex(LOG_DEBUG, "simple: created digest challenge with nonce: %s", nonce_str); - return HA_OK; + return ret; } -static int simple_digest_response(simple_context_t* ctx, const char* header, - const ha_request_t* req, ha_response_t* resp) +static int validate_basic(const ha_request_t* req, const char* user, const char* password) { - unsigned char nonce[DIGEST_NONCE_LEN]; - digest_header_t dg; - digest_record_t* rec = NULL; - const char* t; - time_t expiry; - int ret = HA_FALSE; - int stale = 0; - int r; - - ASSERT(ctx && header && req && resp); - - /* We use this below to send a default response */ - resp->code = -1; - - if((r = digest_parse(header, req->buf, &dg, nonce)) < 0) - return r; - -#ifdef _DEBUG - if(req->context->digest_debugnonce) - { - if(dg.nonce && strcmp(dg.nonce, req->context->digest_debugnonce) != 0) - { - ret = HA_FALSE; - ha_messagex(LOG_WARNING, "simple: digest response contains invalid nonce"); - goto finally; - } - - /* Do a rough hash into the real nonce, for use as a key */ - md5_string(nonce, req->context->digest_debugnonce); - - /* Debug nonce's never expire */ - expiry = time(NULL); - } - else -#endif - { - r = digest_checknonce(nonce, g_simple_secret, &expiry); - if(r != HA_OK) + simple_context_t* ctx = (simple_context_t*)req->context->ctx_data; + FILE* f; + char line[SIMPLE_MAXLINE]; + unsigned char ha1[MD5_LEN]; + char* t; + char* t2; + size_t len; + int ret = HA_FALSE; + + ASSERT(ctx && req); + ASSERT(user && user[0] && password); + ha_messagex(LOG_DEBUG, "simple: validating user against password file: %s", user); + + f = fopen(ctx->filename, "r"); + if(!f) { - if(r == HA_FALSE) - ha_messagex(LOG_WARNING, "simple: digest response contains invalid nonce"); - - ret = r; - goto finally; + ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename); + return HA_FAILED; } - } - rec = get_cached_digest(ctx, req->context, nonce); - - /* Check to see if we're stale */ - if((expiry + req->context->cache_timeout) <= time(NULL)) - { - ha_messagex(LOG_INFO, "simple: nonce expired, sending stale challenge: %s", - dg.username); - - stale = 1; - goto finally; - } - - if(!rec) - { - ha_messagex(LOG_INFO, "simple: no record in cache, creating one: %s", dg.username); + digest_makeha1(ha1, user, req->context->realm, password); /* - * If we're valid but don't have a record in the - * cache then complete the record properly. + * Note: There should be no returns or jumps between + * this point and the closing of the file below. */ - rec = digest_makerec(nonce, dg.username); - if(!rec) + /* Now look through the whole file */ + while(!feof(f)) { - ret = HA_CRITERROR; - goto finally; - } + fgets(line, SIMPLE_MAXLINE, f); - r = complete_digest_ha1(ctx, rec, req, dg.username); - if(r != HA_OK) - { - ret = r; - goto finally; - } - } - - /* We had a record so ... */ - else - { - rec->nc++; - } - - ret = digest_check(&dg, rec, req->context, req->buf, - req->args[AUTH_ARG_METHOD], req->args[AUTH_ARG_URI]); - - if(ret == HA_BADREQ) - { - ret = HA_FALSE; - resp->code = HA_SERVER_BADREQ; - } - - else if(ret == HA_OK) - { - resp->code = HA_SERVER_OK; - resp->detail = dg.username; - - /* Figure out if we need a new nonce */ - if((expiry + (req->context->cache_timeout - - (req->context->cache_timeout / 8))) < time(NULL)) - { - ha_messagex(LOG_INFO, "simple: nonce almost expired, creating new one: %s", - dg.username); - - digest_makenonce(nonce, g_simple_secret, NULL); - stale = 1; - } - - t = digest_respond(req->buf, &dg, rec, stale ? nonce : NULL); - if(!t) - { - ret = HA_CRITERROR; - goto finally; - } - - if(t[0]) - ha_addheader(resp, "Authentication-Info", t); - - ha_messagex(LOG_NOTICE, "simple: validated digest user: %s", dg.username); + if(ferror(f)) + { + ha_message(LOG_ERR, "simple: error reading basic password file"); + ret = HA_FAILED; + break; + } - /* Put the connection into the cache */ - if((r = save_cached_digest(ctx, req->context, rec)) < 0) - ret = r; + /* Take white space off end of line */ + trim_end(line); - rec = NULL; - } + t = strchr(line, ':'); + if(t) + { + /* Split the line */ + *t = 0; + t++; -finally: + /* Check the user */ + if(strcmp(line, user) == 0) + { + /* Not sure if crypt is thread safe so we lock */ + ha_lock(NULL); + + /* Check the password */ + t2 = crypt(password, t); + + ha_unlock(NULL); + + if(strcmp(t2, t) == 0) + { + ha_messagex(LOG_DEBUG, "simple: found valid crypt password for user: %s", user); + ret = HA_OK; + break; + } + + /* Otherwise it might be a digest type ha1. */ + t2 = strchr(t, ':'); + if(t2) + { + *t2 = 0; + t2++; + + /* Check the realm */ + if(strcmp(t, req->context->realm) == 0) + { + len = MD5_LEN; + + /* Now try antd decode the ha1 */ + t = ha_bufdechex(req->buf, t2, &len); + if(t && len == MD5_LEN && memcmp(ha1, t, MD5_LEN) == 0) + { + ha_messagex(LOG_DEBUG, "simple: found valid ha1 for user: %s", user); + ret = HA_OK; + break; + } + } + } + + if(ha_buferr(req->buf)) + break; + } + } + } - /* If the record wasn't stored away then free it */ - if(rec) - free(rec); + fclose(f); - /* If nobody above responded then challenge the client again */ - if(resp->code == -1) - return simple_digest_challenge(ctx, req, resp, stale); + if(ha_buferr(req->buf)) + return HA_CRITERROR; - return ret; + return ret; } - /* ------------------------------------------------------------------------------- * Handler Functions */ int simple_config(ha_context_t* context, const char* name, const char* value) { - simple_context_t* ctx = (simple_context_t*)(context->ctx_data); - - ASSERT(name && name[0] && value && value[0]); - - if(strcmp(name, "passwordfile") == 0) - { - ctx->filename = value; - return HA_OK; - } - - return HA_FALSE; -} - -int simple_init(ha_context_t* context) -{ - /* Global initialization */ - if(!context) - { - ha_messagex(LOG_DEBUG, "simple: generating secret"); - return ha_genrandom(g_simple_secret, DIGEST_SECRET_LEN); - } - - /* Context specific initialization */ - else - { simple_context_t* ctx = (simple_context_t*)(context->ctx_data); - hsh_table_calls_t htc; - int fd; - - ASSERT(ctx); - - /* Make sure there are some types of authentication we can do */ - if(!(context->allowed_types & (HA_TYPE_BASIC | HA_TYPE_DIGEST))) - { - ha_messagex(LOG_ERR, "simple: module configured, but does not implement any " - "configured authentication type."); - return HA_FAILED; - } - - /* Check to make sure the file exists */ - if(!ctx->filename) - { - ha_messagex(LOG_ERR, "simple: configuration incomplete. " - "Must have a PasswordFile configured."); - return HA_FAILED; - } + ASSERT(name && name[0] && value && value[0]); - fd = open(ctx->filename, O_RDONLY); - if(fd == -1) + if(strcmp(name, "passwordfile") == 0) { - ha_message(LOG_ERR, "simple: can't open file for authentication: %s", ctx->filename); - return HA_FAILED; + ctx->filename = value; + return HA_OK; } - close(fd); - - ASSERT(!ctx->cache); - - /* The cache for digest records and basic */ - if(!(ctx->cache = hsh_create(MD5_LEN))) - { - ha_messagex(LOG_CRIT, "out of memory"); - return HA_CRITERROR; - } - - htc.f_freeval = free_hash_object; - htc.arg = NULL; - hsh_set_table_calls(ctx->cache, &htc); - - ha_messagex(LOG_INFO, "simple: initialized handler"); - } - - return HA_OK; -} - -void simple_destroy(ha_context_t* context) -{ - /* Per context destroy */ - if(context) - { - /* Note: We don't need to be thread safe here anymore */ - simple_context_t* ctx = (simple_context_t*)(context->ctx_data); - - if(ctx->cache) - hsh_free(ctx->cache); - - ha_messagex(LOG_INFO, "simple: uninitialized handler"); - } + return HA_FALSE; } -int simple_process(const ha_request_t* req, ha_response_t* resp) +int simple_init(ha_context_t* context) { - simple_context_t* ctx = (simple_context_t*)(req->context->ctx_data); - const char* header = NULL; - int ret = HA_FALSE; - int found = 0; - basic_header_t basic; - int r; - - ASSERT(req && resp); - ASSERT(req->args[AUTH_ARG_METHOD]); - ASSERT(req->args[AUTH_ARG_URI]); - - ha_lock(NULL); - - /* Purge the cache */ - r = hsh_purge(ctx->cache, time(NULL) - req->context->cache_timeout); + int r; - ha_unlock(NULL); + if((r = bd_init(context)) != HA_OK) + return r; - if(r > 0) - ha_messagex(LOG_DEBUG, "simple: purged cache records: %d", r); - - /* We use this below to detect whether to send a default response */ - resp->code = -1; - - - /* Check the headers and see if we got a response thingy */ - if(req->context->allowed_types & HA_TYPE_DIGEST) - { - header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST); - if(header) - { - ha_messagex(LOG_DEBUG, "simple: processing digest auth header"); - ret = simple_digest_response(ctx, header, req, resp); - if(ret < 0) - return ret; - } - } - - /* Or a basic authentication */ - if(!header && req->context->allowed_types & HA_TYPE_BASIC) - { - ha_messagex(LOG_DEBUG, "simple: processing basic auth header"); - header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC); - if(header) + if(context) { - ret = simple_basic_response(ctx, header, req, resp); - if(ret < 0) - return ret; - } - } - + simple_context_t* ctx = (simple_context_t*)(context->ctx_data); + int fd; - /* Send a default response if that's what we need */ - if(resp->code == -1) - { - resp->code = HA_SERVER_DECLINE; + ASSERT(ctx); - if(req->context->allowed_types & HA_TYPE_BASIC) - { - ha_bufmcat(req->buf, "BASIC realm=\"", req->context->realm , "\"", NULL); + /* Check to make sure the file exists */ + if(!ctx->filename) + { + ha_messagex(LOG_ERR, "simple: configuration incomplete. " + "Must have a PasswordFile configured."); + return HA_FAILED; + } - if(ha_buferr(req->buf)) - return HA_CRITERROR; + fd = open(ctx->filename, O_RDONLY); + if(fd == -1) + { + ha_message(LOG_ERR, "simple: can't open file for authentication: %s", ctx->filename); + return HA_FAILED; + } - ha_addheader(resp, "WWW-Authenticate", ha_bufdata(req->buf)); - ha_messagex(LOG_DEBUG, "simple: sent basic auth request"); - } + close(fd); - if(req->context->allowed_types & HA_TYPE_DIGEST) - { - ret = simple_digest_challenge(ctx, req, resp, 0); - if(ret < 0) - return ret; + ha_messagex(LOG_INFO, "simple: initialized handler"); } - } - return ret; + return HA_OK; } @@ -766,9 +299,9 @@ ha_handler_t simple_handler = { "SIMPLE", /* The type */ simple_init, /* Initialization function */ - simple_destroy, /* Uninitialization routine */ + bd_destroy, /* Uninitialization routine */ simple_config, /* Config routine */ - simple_process, /* Processing routine */ + bd_process, /* Processing routine */ NULL, /* A default context */ sizeof(simple_context_t) /* Size of the context */ }; |