diff options
author | Stef Walter <stef@memberwebs.com> | 2004-04-21 17:37:06 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2004-04-21 17:37:06 +0000 |
commit | ff76efc3e5e1b0e4ca3b10b7402406f619509bba (patch) | |
tree | c3b1e49235f67eabd22d31ebfc14934743b70858 /daemon/ldap.c | |
parent | 01430fca169c1b8d7b1b4f1bdd529aa6bc4be80b (diff) |
Initial Import
Diffstat (limited to 'daemon/ldap.c')
-rw-r--r-- | daemon/ldap.c | 1114 |
1 files changed, 1114 insertions, 0 deletions
diff --git a/daemon/ldap.c b/daemon/ldap.c new file mode 100644 index 0000000..2927b1d --- /dev/null +++ b/daemon/ldap.c @@ -0,0 +1,1114 @@ + +/* TODO: Include attribution for ideas, and code from mod_auth_digest */ + +#include "usuals.h" +#include "httpauthd.h" +#include "hash.h" +#include "defaults.h" + +#include <syslog.h> + +/* LDAP library */ +#include <ldap.h> + +/* ------------------------------------------------------------------------------- + * Defaults and Constants + */ + +/* This needs to be the same as an MD5 hash length */ +#define LDAP_HASH_KEY_LEN 16 +#define LDAP_ESTABLISHED (void*)1 + +/* TODO: We need to support more password types */ +#define LDAP_PW_CLEAR 0 +#define LDAP_PW_CRYPT 1 +#define LDAP_PW_MD5 2 +#define LDAP_PW_SHA 3 +#define LDAP_PW_UNKNOWN -1 + +typedef struct ldap_pw_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 } +}; + + +/* ------------------------------------------------------------------------------- + * Structures + */ + +/* 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* realm; /* The realm to use in authentication */ + 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 pending_max; /* Maximum number of connections at once */ + int pending_timeout; /* Timeout for authentication (in seconds) */ + int ldap_timeout; /* Timeout for LDAP operations */ + + /* Context ----------------------------------------------------------- */ + hash_t* pending; /* Pending connections */ + hash_t* established; /* Established connections */ + LDAP** pool; /* Pool of available connections */ + int pool_mark; /* Amount of connections allocated */ +} +ldap_context_t; + + +/* The defaults for the context */ +static const ldap_defaults = +{XXXXX + NULL, NULL, "", "userPassword", NULL, NULL, NULL, "", NULL + LDAP_SCOPE_DEFAULT, 1, DEFAULT_PENDING_MAX, DEFAULT_PENDING_TIMEOUT, + 30, NULL, NULL, NULL +}; + + +/* ------------------------------------------------------------------------------- + * Internal Functions + */ + +static void make_digest_ha1(unsigned char* digest, const char* user, + const char* realm, const char* password) +{ + struct MD5Context md5; + MD5_Init(&md5); + MD5_Update(&md5, user, strlen(user)); + MD5_Update(&md5, ":", 1); + MD5_Update(&md5, realm, strlen(realm)); + MD5_Update(&md5, ":", 1); + MD5_Update(&md5, password, strlen(pasword)); + MD5_Final(digest, &md5); +} + +static const char* make_password_md5(ha_buffer_t* buf, const char* clearpw) +{ + struct MD5Context md5; + unsigned char digest[MD5_LEN]; + + MD5_Init(&md5); + MD5_Update(&md5, clearpw, strlen(clearpw)); + MD5_Final(digest, &md5); + + ha_bufnext(buf); + ha_bufenc64(buf, digest, MD5_LEN); + return ha_bufdata(buf); +} + +static const char* make_password_sha(ha_buffer_t* buf, const char* clearpw) +{ + struct SHA1Context sha; + unsigned char digest[SHA1_LEN]; + + SHA1_Init(&sha); + SHA1_Update(&sha, clearpw, strlen(clearpw)); + SHA1_Final(digest, &sha); + + ha_bufnext(buf); + ha_bufenc64(buf, digest, SHA1_LEN); + return ha_bufdata(buf); +} + +static int parse_ldap_password(const char** password) +{ + const char* pw; + const char* scheme; + int i; + + ASSERT(password && *password); + + pw = *password; + + /* zero length passwords are clear */ + if(strlen(pw) == 0) + return LDAP_PW_CLEAR; + + /* passwords without a scheme are clear */ + if(pw[0] != '{') + return LDAP_PW_CLEAR; + + pw++; + scheme = pw; + + while(*pw && (isalpha(*pw) || isdigit(*pw) || *pw == '-')) + pw++; + + /* scheme should end in a brace */ + if(pw != '}') + return LDAP_PW_CLEAR; + + *password = pw + 1; + + /* find a scheme in our map */ + for(i = 0; i < countof(kLDAPPWTypes); i++) + { + if(strncasecmp(kLDAPSchemes[i].name, scheme, pw - scheme)) + return kLDAPSchemes[i].type; + } + + return LDAP_PW_UNKNOWN; +} + +static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws) +{ + ha_bufnext(buf); + + for(; pws && *pws; pws++) + { + const char* pw = *pws; + + if(parse_ldap_password(&pw) == LDAP_PW_CLEAR) + return pw; + } + + return NULL; +} + + +static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha1) +{ + /* Raw binary */ + if(bv->bv_len == MD5_LEN) + { + memcpy(ha1, bv->bv_len, MD5_LEN); + return HA_OK; + } + + /* Hex encoded */ + else if(bv->bv_len == (MD5_LEN * 2)) + { + ha_bufnext(buf); + ha_bufdechex(buf, bv->bv_val, MD5_LEN * 2); + + if(!ha_bufdata(buf)) + return HA_ERROR; + + if(ha_buflen(buf) == MD5_LEN) + { + memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN); + return HA_OK; + } + } + + /* B64 Encoded */ + else + { + ha_bufnext(buf); + ha_bufdec64(buf, (*pws)->bv_val, (*pws)->bv_len); + + if(!ha_bufdata(buf)) + return HA_ERROR; + + if(ha_buflen(buf) == MD5_LEN) + { + memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN); + return HA_OK; + } + } + + return 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) +{ + const char** pws; + 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( ; *pws; pws++) + { + pw = *pws; + 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_ERROR; + break; + } + + if(strcmp(pw, p) == 0) + { + res = HA_OK; + break; + } + } + + ldap_free_values(pws); + } + + if(res == HA_FALSE && unknown) + ha_messagex(LOG_ERR, "LDAP does not contain any compatible passwords for user: %s", user); + + return res; +} + +static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry, + ha_buffer_t* buf, const char* user, const char* clearpw) +{ + struct berval** ha1s; + unsigned char key[MD5_LEN]; + unsigned char k[MD5_LEN]; + int r, first = 1; + int res = HA_FALSE; + + if(!ctx->ha1_attr) + return HA_FALSE; + + ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr); + + if(ha1s) + { + make_digest_ha1(key, user, ctx->realm, clearpw); + + for( ; *ha1s; ha1s++) + { + r = parse_ldap_h1(buf, *ha1s, k); + if(r == HA_ERROR) + { + res = r; + break; + } + + if(r == HA_FALSE) + { + if(first) + ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user); + + first = 0; + continue; + } + + if(memcmp(key, k, MD5_LEN) == 0) + { + res = HA_OK; + break; + } + } + + ldap_free_values_len(ha1s); + } + + return res; +} + +static LDAP* get_ldap_connection(ldap_context_t* ctx) +{ + LDAP* ld; + int i, r; + + for(i = 0; i < ctx->pending_max; i++) + { + /* An open connection in the pool */ + if(ctx->pool[i]) + { + ld = ctx->pool[i]; + ctx->pool[i]; + return ld; + } + } + + if(ctx->pool_mark >= ctx->pending_max) + { + ha_messagex("too many open connections to LDAP"); + return NULL; + } + + ld = ldap_init(ctx->servers, ctx->port); + if(!ld) + { + ha_message("couldn't initialize ldap 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) + { + report_ldap(r, NULL); + ldap_unbind_s(ld); + return NULL; + } + } + + return ld; +} + +static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld) +{ + int i; + + if(!ld) + return; + + /* Make sure it's worth saving */ + switch(ld_errno(ld)) + { + case LDAP_SERVER_DOWN: + case LDAP_LOCAL_ERROR: + case LDAP_NO_MEMORY: + break; + + default: + for(i = 0; i < ctx->pending_max; i++) + { + /* An open connection in the pool */ + if(!ctx->pool[i]) + { + ctx->pool[i] = ld; + ld = NULL; + break; + } + } + + break; + }; + + if(ld != NULL) + ldap_unbind_s(ld); +} + +static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec, + const char* user) +{ + LDAP* ld = NULL; /* freed in finally */ + LDAPMessage* results = NULL; /* freed in finally */ + LDAPMessage* entry = NULL; /* no need to free */ + struct berval** pws; /* freed manually */ + int ret = HA_FALSE; + + /* Hash in the user name */ + ha_md5string(user, rec->userhash); + + + ld = get_ldap_connection(ctx); + if(!ld) + 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, buf, user, ctx->dnmap); + if(!dn) + { + ret = HA_ERROR; + goto finally; + } + } + + /* Okay now we contact the LDAP server. */ + r = retrieve_user_entry(ctx, buf, user, &dn, &entry, &results); + if(r != HA_OK) + { + ret = r; + goto finally; + } + + /* Figure out the users ha1 */ + if(ctx->ha1_attr) + pws = ldap_get_values_len(ld, entry, ctx->ha1_attr); + + if(pws) + { + if(*pws) + { + r = parse_ldap_ha1(buf, *pws, rec->ha1); + if(r != HA_OK) + { + ret = r + + if(ret != HA_FALSE) + ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user); + } + } + + ldap_free_values_len(pws); + goto finally; + } + + /* If no ha1 set or none found, use password and make a HA1 */ + pws = ldap_get_values_len(ld, entry, ctx->pw_attr); + + if(pws) + { + /* Find a cleartext password */ + const char* t = find_cleartext_password(buf, pws); + + ldap_free_values_len(pws); + + if(t) + { + make_digest_ha1(rec->ha1, user, ctx->realm, t); + ret = HA_OK; + goto finally; + } + } + + ha_messagex(LOG_ERROR, "LDAP contains no cleartext password for user: %s", user); + +finally: + + if(ld) + save_ldap_connection(ctx, ld); + + if(results) + ldap_msgfree(results); + + return ret; +} + +static int retrieve_user_entry(ldap_context_t* ctx, buffer_t* buf, LDAP* ld, + const char* user, const char** dn, + LDAPMessage** entry, LDAPMessage** result) +{ + timeval tv; + const char* filter; + const char* attrs[3]; + + if(ctx->filter) + { + /* Filters can also have %u and %r */ + filter = substitute_params(ctx, buf, user, ctx->filter); + if(!filter) + return HA_ERROR; + } + 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; + + r = ldap_search_st(ld, *dn ? *dn : ctx->base, + *dn ? LDAP_SCOPE_BASE : ctx->scope, + filter, attrs, 0, &tv, result); + + if(r != LDAP_SUCCESS) + return report_ldap(r, resp, &ret); + + + /* 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); + return HA_OK; + + case 0: + ha_messagex(LOG_WARNING, "user not found in LDAP: %s", basic.user); + break; + + default: + ha_messagex(LOG_WARNING, "more than one user found for filter: %s", filter); + break; + }; + + ldap_msg_free(*result); + return HA_FALSE; +} + +static int basic_ldap_response(ldap_context_t* ctx, const char* header, + ha_response_t* resp, ha_buffer_t* buf) +{ + ha_basic_header_t basic; + LDAP* ld = NULL; + LDAPMessage* entry = NULL; + LDAPMessage* results = NULL; + const char* dn; + int ret = HA_FALSE; + int found = 0; + int r; + + ASSERT(buf && header && resp && buf); + + if(ha_parsebasic(header, buf, &basic) == HA_ERROR) + return HA_ERROR; + + /* Past this point we don't return directly */ + + /* Check and see if this connection is in the cache */ + ha_lock(NULL); + + if(hash_get(ctx->established, key) == BASIC_ESTABLISHED) + { + found = 1; + ret = HA_OK; + goto finally: + } + + ha_unlock(NULL); + + + /* If we have a user name and password */ + if(!basic.user || !basic.user[0] || + !basic.password || !basic.password[0]) + goto finally; + + + ld = get_ldap_connection(); + if(!ld) + { + resp->code = HA_SERVER_ERROR; + 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, buf, basic.user, ctx->dnmap); + if(!dn) + { + ret = HA_ERROR; + goto finally; + } + } + + + /** + * 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, buf, basic.user, &dn, &entry, &results); + if(r != HA_OK) + { + ret = r; + goto finally; + } + } + + + /* 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) + { + if(r == LDAP_INVALID_CREDENTIALS) + ha_messagex(LOG_WARNING, "invalid login for: %s", basic.user); + else + report_ldap(r, resp, &ret); + + goto finally; + } + + /* It worked! */ + resp->code = HA_SERVER_ACCEPT; + } + + + /* Otherwise we compare the password attribute */ + else + { + ret = validate_ldap_password(ctx, ld, entry, buf, basic.user, basic.password); + if(ret == HA_FALSE) + ret = validate_ldap_ha1(ctx, ld, entry, buf, basic.user, basic.password); + + if(ret == HA_OK) + resp->code = HA_SERVER_ACCEPT; + + else + ha_messagex(LOG_WARNING, "invalid or unrecognized password for user: %s", basic.user); + } + + +finally: + + if(ld) + save_ldap_connection(ctx, ld); + + if(results) + ldap_msgfree(results); + + if(resp->code == HA_SERVER_ACCEPT) + { + resp->details = basic.user; + + /* We put this connection into the successful connections */ + if(!hash_set(ctx->established, basic.key, LDAP_ESTABLISHED)) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + } + + return ret; +} + + +static int digest_ldap_response(ldap_context_t* ctx, const char* header, + const char* method, const char* uri, + ha_response_t* resp, ha_buffer_t* buf) +{ + ha_digest_header_t dg; + digest_rec_t* rec = NULL; + int ret = HA_FALSE; + int stale = 0; + int pending = 0; + + /* We use this below to send a default response */ + resp->code = -1; + + if(ha_parsedigest(header, buf, &rec) == HA_ERROR) + return HA_ERROR; + + /* Lookup our digest context based on the nonce */ + if(!dg.nonce || strlen(dg.nonce) != DIGEST_NONCE_LEN) + { + ha_messagex(LOG_WARNING, "digest response contains invalid nonce"); + goto finally; + } + + ha_lock(NULL); + + rec = (digest_rec_t*)hash_get(ctx->pending, dg.nonce) + if(rec) + { + pending = 1; + hash_rem(ctx->pending, dg.nonce); + } + + else + { + rec = (digest_rec_t*)hash_get(ctx->established, dg.nonce); + } + + ha_unlock(NULL); + + /* + * If nothing was found for this nonce, then it might + * be a stale nonce. In any case prompt the client + * to reauthenticate. + */ + if(!rec) + { + stale = 1; + goto finally; + } + + /* + * If we got a response from the pending table, then + * we need to lookup the user name and figure out + * who the dude is. + */ + if(pending) + { + ASSERT(rec); + + r = complete_digest_ha1(ctx, rec, dg->username); + if(r != HA_OK) + { + ret = r; + goto finally; + } + } + + /* Increment our nonce count */ + rec->nc++; + + ret = ha_digestcheck(ctx->realm, method, uri, buf, &dg, rec); + + if(ret == HA_OK) + { + resp->code = HA_SERVER_ACCEPT; + resp->details = dg->username; + + /* Put the connection back into established */ + + ha_lock(NULL); + + if(hash_set(ctx->established, dg.nonce, rec)) + { + rec = NULL; + } + else + { + ha_messagex(LOG_CRIT, "out of memory"); + ret = HA_ERROR; + } + + ha_unlock(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 digest_ldap_challenge(ctx, resp, buf, stale); + + return ret; +} + + + + +/* ------------------------------------------------------------------------------- + * Handler Functions + */ + +int ldap_config(ha_context_t* context, const char* name, const char* value) +{ + ldap_context_t* ctx = (ldap_context_t*)(context.data); + + 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; + } + + else if(strcmp(name, "realm") == 0) + { + ctx->realm = value; + return HA_OK; + } + + 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 + { + messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name); + return HA_ERROR; + } + + return HA_OK; + } + + else if(strcmp(name, "ldapdobind") == 0) + { + return ha_confbool(name, value, &(ctx->dobind)); + } + + else if(strcmp(name, "pendingmax") == 0) + { + return ha_confint(name, value, 1, 256, &(ctx->pending_max)); + } + + else if(strcmp(name, "pendingtimeout") == 0) + { + return ha_confint(name, value, 0, 86400, &(ctx->pending_timeout)); + } + + else if(strcmp(name, "ldaptimeout") == 0) + { + return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout)); + } + + return HA_FALSE; +} + +int ldap_initialize(ha_context_t* context) +{ + /* No global initialization */ + if(!context) + return HA_OK; + + ldap_context_t* ctx = (ldap_context_t*)(context.data); + + + /* Make sure there are some types of authentication we can do */ + if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST))) + { + ha_messagex(LOG_ERR, "Digest module configured, but does not implement any " + "configured authentication type."); + return HA_ERROR; + } + + /* Check for mandatory configuration */ + if(!ctx->servers || (!ctx->dnmap || !ctx->filter)) + { + ha_messagex(LOG_ERR, "Digest LDAP configuration incomplete. " + "Must have LDAPServers and either LDAPFilter or LDAPDNMap."); + return HA_ERROR; + } + + + /* The hash tables */ + if(!(ctx->pending = hash_create(LDAP_HASH_KEY_LEN)) || + !(ctx->established = hash_create(LDAP_HASH_KEY_LEN))) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + + /* + * 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. + */ + XXXXX + ctx->pool = (LDAP**)malloc(sizeof(LDAP*) * ctx->pending_max); + if(!ctx->pool) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + + memset(ctx->pool, 0, sizeof(LDAP*) * ctx->pending_max); + + return HA_OK; +} + +void ldap_destroy(ha_context_t* context) +{ + int i; + + if(!context) + return HA_OK; + + ldap_context_t* ctx = (digest_ldap_context_t*)(context.data); + + /* Note: We don't need to be thread safe here anymore */ + hash_free(ctx->pending); + hash_free(ctx->established); + + XXXXX + /* Close any connections we have open */ + for(i = 0; i < ctx->pending_max; i++) + { + if(ctx->pool[i]) + ldap_unbind_s(ctx->pool[i]); + } + + /* And free the connection pool */ + free(ctx->pool); +} + + +int ldap_process(ha_context_t* context, ha_request_t* req, + ha_response_t* resp, ha_buffer_t* buf) +{ + ldap_context_t* ctx = (ldap_context_t*)context; + time_t t = time(NULL); + const char* header = NULL; + int ret; + + ha_lock(NULL); + + XXXXXX + /* + * Purge out stale connection stuff. This includes + * authenticated connections which have expired as + * well as half open connections which expire. + */ + hash_purge(ctx->pending, t - ctx->pending_timeout); + hash_purge(ctx->established, t - ctx->timeout); + + ha_unlock(NULL); + + + /* 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(ctx->types & HA_TYPE_DIGEST) + { + header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST); + if(header) + { + ret = digest_ldap_response(ctx, header, resp, buf); + if(ret == HA_ERROR) + return ret; + } + } + + /* Or a basic authentication */ + if(!header && ctx->types & HA_TYPE_BASIC) + { + header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC); + if(header) + { + ret = basic_ldap_response(ctx, header, resp, buf); + if(ret == HA_ERROR) + return ret; + } + } + + + /* Send a default response if that's what we need */ + if(resp->code == -1) + { + resp->code = HA_SERVER_DECLINE; + + if(ctx->types & HA_TYPE_DIGEST) + { + ret = digest_ldap_challenge(ctx, resp, buf, 0); + if(ret == HA_ERROR) + return ret; + } + + if(ctx->types & HA_TYPE_BASIC) + { + ha_bufnext(buf); + ha_bufcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL); + + ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); + } + } + + return ret; +} + + +/* ------------------------------------------------------------------------------- + * Handler Definition + */ + +ha_handler_t digest_ldap_handler = +{ + "LDAP", /* The type */ + ldap_initialize, /* Initialization function */ + ldap_destroy, /* Uninitialization routine */ + ldap_config, /* Config routine */ + ldap_process, /* Processing routine */ + &ldap_defaults, /* The context defaults */ + sizeof(ldap_context_t) +}; |