summaryrefslogtreecommitdiff
path: root/daemon/ldap.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/ldap.c')
-rw-r--r--daemon/ldap.c768
1 files changed, 474 insertions, 294 deletions
diff --git a/daemon/ldap.c b/daemon/ldap.c
index 2927b1d..59af797 100644
--- a/daemon/ldap.c
+++ b/daemon/ldap.c
@@ -1,23 +1,31 @@
/* 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 <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
*/
-/* This needs to be the same as an MD5 hash length */
-#define LDAP_HASH_KEY_LEN 16
-#define LDAP_ESTABLISHED (void*)1
+#define BASIC_ESTABLISHED (void*)1
/* TODO: We need to support more password types */
#define LDAP_PW_CLEAR 0
@@ -57,18 +65,21 @@ typedef struct ldap_context
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 */
+
+ const char* realm; /* The realm to use in authentication */
+ const char* domains; /* Domains for which digest auth is valid */
+
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 */
+ int cache_max; /* Maximum number of connections at once */
+ int ldap_max; /* Number of open connections allowed */
+ int ldap_timeout; /* Maximum amount of time to dedicate to an ldap query */
/* Context ----------------------------------------------------------- */
- hash_t* pending; /* Pending connections */
- hash_t* established; /* Established connections */
+ hash_t* cache; /* Some cached records or basic */
+
LDAP** pool; /* Pool of available connections */
int pool_mark; /* Amount of connections allocated */
}
@@ -76,11 +87,27 @@ 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
+static const ldap_context_t ldap_defaults =
+{
+ NULL, /* servers */
+ NULL, /* filter */
+ "", /* base */
+ "userPassword", /* pw_attr */
+ NULL, /* ha1_attr */
+ NULL, /* user */
+ NULL, /* password */
+ NULL, /* dnmap */
+ 389, /* port */
+ LDAP_SCOPE_DEFAULT, /* scope */
+ "", /* realm */
+ NULL, /* domains */
+ 1, /* dobind */
+ 1000, /* cache_max */
+ 10, /* ldap_max */
+ 30, /* ldap_timeout */
+ NULL, /* cache */
+ NULL, /* pool */
+ 0 /* pool_mark */
};
@@ -88,45 +115,182 @@ static const ldap_defaults =
* Internal Functions
*/
-static void make_digest_ha1(unsigned char* digest, const char* user,
- const char* realm, const char* password)
+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, ha_response_t* resp)
+{
+ if(!msg)
+ msg = "ldap error";
+
+ ha_messagex(LOG_ERR, "%s: %s", msg, ldap_err2string(code));
+
+ switch(code)
+ {
+ case LDAP_NO_MEMORY:
+ return HA_ERROR;
+
+ default:
+ if(resp)
+ resp->code = HA_SERVER_ERROR;
+
+ return HA_FALSE;
+ };
+}
+
+static digest_record_t* get_cached_digest(ldap_context_t* ctx, unsigned char* nonce)
+{
+ digest_record_t* rec;
+
+ if(ctx->cache_max == 0)
+ return NULL;
+
+ ha_lock(NULL);
+
+ rec = (digest_record_t*)hash_get(ctx->cache, nonce);
+
+ /* Just in case it's a basic :) */
+ if(rec && rec != BASIC_ESTABLISHED)
+ hash_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;
+
+ ha_lock(NULL);
+
+ ret = (hash_get(ctx->cache, key) == BASIC_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ return ret;
+}
+
+static int save_cached_digest(ldap_context_t* ctx, digest_record_t* rec)
+{
+ int r;
+
+ if(ctx->cache_max == 0)
+ return HA_FALSE;
+
+ ha_lock(NULL);
+
+ while(hash_count(ctx->cache) >= ctx->cache_max)
+ hash_bump(ctx->cache);
+
+ r = hash_set(ctx->cache, rec->nonce, rec);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+static int add_cached_basic(ldap_context_t* ctx, unsigned char* key)
+{
+ int r;
+
+ if(ctx->cache_max == 0)
+ return HA_FALSE;
+
+ ha_lock(NULL);
+
+ while(hash_count(ctx->cache) >= ctx->cache_max)
+ hash_bump(ctx->cache);
+
+ r = hash_set(ctx->cache, key, BASIC_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+static const char* substitute_params(ldap_context_t* ctx, ha_buffer_t* buf,
+ const char* user, const char* str)
{
- 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);
+ const char* t;
+
+ /* This starts a new block to join */
+ ha_bufcpy(buf, "");
+
+ while(str[0])
+ {
+ t = strchr(str, '%');
+ if(!t)
+ {
+ ha_bufjoin(buf);
+ ha_bufcpy(buf, str);
+ break;
+ }
+
+ ha_bufjoin(buf);
+ ha_bufncpy(buf, str, t - str);
+
+ t++;
+
+ switch(t[0])
+ {
+ case 'u':
+ ha_bufjoin(buf);
+ ha_bufcpy(buf, user);
+ t++;
+ break;
+
+ case 'r':
+ ha_bufjoin(buf);
+ ha_bufcpy(buf, ctx->realm);
+ t++;
+ break;
+ };
+
+ str = t;
+ }
+
+ return ha_bufdata(buf);
}
static const char* make_password_md5(ha_buffer_t* buf, const char* clearpw)
{
- struct MD5Context md5;
+ md5_ctx_t md5;
unsigned char digest[MD5_LEN];
- 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);
- ha_bufnext(buf);
- ha_bufenc64(buf, digest, MD5_LEN);
- return ha_bufdata(buf);
+ return ha_bufenc64(buf, digest, MD5_LEN);
}
static const char* make_password_sha(ha_buffer_t* buf, const char* clearpw)
{
- struct SHA1Context sha;
+ sha1_ctx_t sha;
unsigned char digest[SHA1_LEN];
- 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);
- ha_bufnext(buf);
- ha_bufenc64(buf, digest, SHA1_LEN);
- return ha_bufdata(buf);
+ return ha_bufenc64(buf, digest, SHA1_LEN);
}
static int parse_ldap_password(const char** password)
@@ -154,7 +318,7 @@ static int parse_ldap_password(const char** password)
pw++;
/* scheme should end in a brace */
- if(pw != '}')
+ if(*pw != '}')
return LDAP_PW_CLEAR;
*password = pw + 1;
@@ -162,8 +326,8 @@ static int parse_ldap_password(const char** password)
/* 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;
+ if(strncasecmp(kLDAPPWTypes[i].name, scheme, pw - scheme))
+ return kLDAPPWTypes[i].type;
}
return LDAP_PW_UNKNOWN;
@@ -171,8 +335,6 @@ static int parse_ldap_password(const char** password)
static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws)
{
- ha_bufnext(buf);
-
for(; pws && *pws; pws++)
{
const char* pw = *pws;
@@ -184,28 +346,23 @@ static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws)
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);
+ memcpy(ha1, bv->bv_val, 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);
+ void* d = ha_bufdechex(buf, bv->bv_val, MD5_LEN);
- if(!ha_bufdata(buf))
- return HA_ERROR;
-
- if(ha_buflen(buf) == MD5_LEN)
+ if(d)
{
- memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN);
+ memcpy(ha1, d, MD5_LEN);
return HA_OK;
}
}
@@ -213,26 +370,22 @@ static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha
/* B64 Encoded */
else
{
- ha_bufnext(buf);
- ha_bufdec64(buf, (*pws)->bv_val, (*pws)->bv_len);
+ void* d = ha_bufdec64(buf, bv->bv_val, MD5_LEN);
- if(!ha_bufdata(buf))
- return HA_ERROR;
-
- if(ha_buflen(buf) == MD5_LEN)
+ if(d)
{
- memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN);
+ memcpy(ha1, ha_bufdata(buf), MD5_LEN);
return HA_OK;
}
}
- return HA_FALSE;
+ return ha_buferr(buf) ? HA_ERROR : 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;
+ char** pws;
const char* pw;
const char* p;
int type;
@@ -295,7 +448,7 @@ static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* en
}
}
- ldap_free_values(pws);
+ ldap_value_free(pws);
}
if(res == HA_FALSE && unknown)
@@ -320,11 +473,11 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(ha1s)
{
- make_digest_ha1(key, user, ctx->realm, clearpw);
+ digest_makeha1(key, user, ctx->realm, clearpw);
for( ; *ha1s; ha1s++)
{
- r = parse_ldap_h1(buf, *ha1s, k);
+ r = parse_ldap_ha1(buf, *ha1s, k);
if(r == HA_ERROR)
{
res = r;
@@ -334,7 +487,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(r == HA_FALSE)
{
if(first)
- ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user);
+ ha_messagex(LOG_ERR, "LDAP contains invalid HA1 digest hash for user: %s", user);
first = 0;
continue;
@@ -347,7 +500,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
}
}
- ldap_free_values_len(ha1s);
+ ldap_value_free_len(ha1s);
}
return res;
@@ -358,7 +511,7 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
LDAP* ld;
int i, r;
- for(i = 0; i < ctx->pending_max; i++)
+ for(i = 0; i < ctx->ldap_max; i++)
{
/* An open connection in the pool */
if(ctx->pool[i])
@@ -369,16 +522,16 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
}
}
- if(ctx->pool_mark >= ctx->pending_max)
+ if(ctx->pool_mark >= ctx->ldap_max)
{
- ha_messagex("too many open connections to LDAP");
+ ha_messagex(LOG_ERR, "too many open connections to LDAP");
return NULL;
}
ld = ldap_init(ctx->servers, ctx->port);
if(!ld)
{
- ha_message("couldn't initialize ldap connection");
+ ha_message(LOG_ERR, "couldn't initialize ldap connection");
return NULL;
}
@@ -388,10 +541,12 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
ctx->password ? ctx->password : "");
if(r != LDAP_SUCCESS)
{
- report_ldap(r, NULL);
+ report_ldap("couldn't bind to LDAP server", r, NULL);
ldap_unbind_s(ld);
return NULL;
}
+
+ ctx->pool_mark++;
}
return ld;
@@ -399,13 +554,15 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
{
- int i;
+ int i, e;
if(!ld)
return;
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &e);
+
/* Make sure it's worth saving */
- switch(ld_errno(ld))
+ switch(e)
{
case LDAP_SERVER_DOWN:
case LDAP_LOCAL_ERROR:
@@ -413,7 +570,7 @@ static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
break;
default:
- for(i = 0; i < ctx->pending_max; i++)
+ for(i = 0; i < ctx->ldap_max; i++)
{
/* An open connection in the pool */
if(!ctx->pool[i])
@@ -428,25 +585,88 @@ static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
};
if(ld != NULL)
+ {
ldap_unbind_s(ld);
+ ctx->pool_mark--;
+ }
+}
+
+static int retrieve_user_entry(ldap_context_t* ctx, ha_buffer_t* buf, LDAP* ld,
+ const char* user, const char** dn,
+ LDAPMessage** entry, LDAPMessage** result)
+{
+ struct timeval tv;
+ const char* filter;
+ const char* attrs[3];
+ int r;
+
+ 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, (char**)attrs, 0, &tv, result);
+
+ if(r != LDAP_SUCCESS)
+ return report_ldap("couldn't search LDAP server", r, NULL);
+
+
+ /* 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", user);
+ break;
+
+ default:
+ ha_messagex(LOG_WARNING, "more than one user found for filter: %s", filter);
+ break;
+ };
+
+ ldap_msgfree(*result);
+ return HA_FALSE;
}
-static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec,
- const char* user)
+static int complete_digest_ha1(ldap_context_t* ctx, digest_record_t* rec,
+ ha_buffer_t* buf, const char* user, int* code)
{
LDAP* ld = NULL; /* freed in finally */
LDAPMessage* results = NULL; /* freed in finally */
LDAPMessage* entry = NULL; /* no need to free */
- struct berval** pws; /* freed manually */
+ struct berval** ha1s; /* freed manually */
+ char** pws;
int ret = HA_FALSE;
-
- /* Hash in the user name */
- ha_md5string(user, rec->userhash);
-
+ const char* dn;
+ int r;
ld = get_ldap_connection(ctx);
if(!ld)
+ {
+ *code = HA_SERVER_ERROR;
goto finally;
+ }
/*
* Discover the DN of the user. If there's a DN map string
@@ -465,7 +685,7 @@ static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec,
}
/* Okay now we contact the LDAP server. */
- r = retrieve_user_entry(ctx, buf, user, &dn, &entry, &results);
+ r = retrieve_user_entry(ctx, buf, ld, user, &dn, &entry, &results);
if(r != HA_OK)
{
ret = r;
@@ -474,45 +694,45 @@ static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec,
/* Figure out the users ha1 */
if(ctx->ha1_attr)
- pws = ldap_get_values_len(ld, entry, ctx->ha1_attr);
+ ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr);
- if(pws)
+ if(ha1s)
{
- if(*pws)
+ if(*ha1s)
{
- r = parse_ldap_ha1(buf, *pws, rec->ha1);
+ r = parse_ldap_ha1(buf, *ha1s, rec->ha1);
if(r != HA_OK)
{
- ret = r
+ ret = r;
if(ret != HA_FALSE)
- ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user);
+ ha_messagex(LOG_ERR, "LDAP contains invalid HA1 digest hash for user: %s", user);
}
}
- ldap_free_values_len(pws);
+ ldap_value_free_len(ha1s);
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);
+ pws = ldap_get_values(ld, entry, ctx->pw_attr);
if(pws)
{
/* Find a cleartext password */
- const char* t = find_cleartext_password(buf, pws);
+ const char* t = find_cleartext_password(buf, (const char**)pws);
- ldap_free_values_len(pws);
+ ldap_value_free(pws);
if(t)
{
- make_digest_ha1(rec->ha1, user, ctx->realm, t);
+ digest_makeha1(rec->ha1, user, ctx->realm, t);
ret = HA_OK;
goto finally;
}
}
- ha_messagex(LOG_ERROR, "LDAP contains no cleartext password for user: %s", user);
+ ha_messagex(LOG_ERR, "LDAP contains no cleartext password for user: %s", user);
finally:
@@ -525,67 +745,10 @@ finally:
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;
+ basic_header_t basic;
LDAP* ld = NULL;
LDAPMessage* entry = NULL;
LDAPMessage* results = NULL;
@@ -596,23 +759,18 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
ASSERT(buf && header && resp && buf);
- if(ha_parsebasic(header, buf, &basic) == HA_ERROR)
+ if(basic_parse(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(have_cached_basic(ctx, basic.key))
+ {
+ found = 1;
+ ret = HA_OK;
+ goto finally;
+ }
/* If we have a user name and password */
if(!basic.user || !basic.user[0] ||
@@ -620,7 +778,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
goto finally;
- ld = get_ldap_connection();
+ ld = get_ldap_connection(ctx);
if(!ld)
{
resp->code = HA_SERVER_ERROR;
@@ -662,7 +820,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
if(!ctx->dobind || !dn || ctx->filter)
{
- r = retrieve_user_entry(ctx, buf, basic.user, &dn, &entry, &results);
+ r = retrieve_user_entry(ctx, buf, ld, basic.user, &dn, &entry, &results);
if(r != HA_OK)
{
ret = r;
@@ -682,7 +840,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
if(r == LDAP_INVALID_CREDENTIALS)
ha_messagex(LOG_WARNING, "invalid login for: %s", basic.user);
else
- report_ldap(r, resp, &ret);
+ report_ldap("couldn't bind to LDAP server", r, resp);
goto finally;
}
@@ -717,80 +875,90 @@ finally:
if(resp->code == HA_SERVER_ACCEPT)
{
- resp->details = basic.user;
+ resp->detail = 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;
- }
+ ret = add_cached_basic(ctx, basic.key);
}
return ret;
}
+static int digest_ldap_challenge(ldap_context_t* ctx, ha_response_t* resp,
+ ha_buffer_t* buf, int stale)
+{
+ unsigned char nonce[DIGEST_NONCE_LEN];
+ const char* header;
+
+ /* Generate an nonce */
+ digest_makenonce(nonce, g_ldap_secret, NULL);
+
+ /* Now generate a message to send */
+ header = digest_challenge(buf, nonce, ctx->realm, ctx->domains, stale);
+
+ if(!header)
+ return HA_ERROR;
+
+ /* And append it nicely */
+ resp->code = HA_SERVER_DECLINE;
+ ha_addheader(resp, "WWW-Authenticate", header);
+
+ return HA_OK;
+}
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)
+ const char* method, const char* uri, int timeout,
+ ha_response_t* resp, ha_buffer_t* buf)
{
- ha_digest_header_t dg;
- digest_rec_t* rec = NULL;
+ 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 pending = 0;
+ int r;
/* We use this below to send a default response */
resp->code = -1;
- if(ha_parsedigest(header, buf, &rec) == HA_ERROR)
+ if(digest_parse(header, buf, &dg, nonce) == HA_ERROR)
return HA_ERROR;
- /* Lookup our digest context based on the nonce */
- if(!dg.nonce || strlen(dg.nonce) != DIGEST_NONCE_LEN)
+ r = digest_checknonce(nonce, g_ldap_secret, &expiry);
+ if(r != HA_OK)
{
- ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+ if(r == HA_FALSE)
+ ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+
+ ret = r;
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);
- }
+ rec = get_cached_digest(ctx, 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)
+ /* Check to see if we're stale */
+ if((expiry + timeout) <= time(NULL))
{
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)
+ if(!rec)
{
- ASSERT(rec);
+ /*
+ * 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_ERROR;
+ goto finally;
+ }
- r = complete_digest_ha1(ctx, rec, dg->username);
+ r = complete_digest_ha1(ctx, rec, buf, dg.username, &(resp->code));
if(r != HA_OK)
{
ret = r;
@@ -801,28 +969,35 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
/* Increment our nonce count */
rec->nc++;
- ret = ha_digestcheck(ctx->realm, method, uri, buf, &dg, rec);
+ ret = digest_check(ctx->realm, method, uri, buf, &dg, rec);
if(ret == HA_OK)
{
resp->code = HA_SERVER_ACCEPT;
- resp->details = dg->username;
+ resp->detail = dg.username;
- /* Put the connection back into established */
+ /* Figure out if we need a new nonce */
+ if((expiry + (timeout - (timeout / 8))) < time(NULL))
+ {
+ digest_makenonce(nonce, g_ldap_secret, NULL);
+ stale = 1;
+ }
- ha_lock(NULL);
+ t = digest_respond(buf, &dg, rec, stale ? nonce : NULL);
+ if(!t)
+ {
+ ret = HA_ERROR;
+ goto finally;
+ }
- if(hash_set(ctx->established, dg.nonce, rec))
- {
- rec = NULL;
- }
- else
- {
- ha_messagex(LOG_CRIT, "out of memory");
- ret = HA_ERROR;
- }
+ if(t[0])
+ ha_addheader(resp, "Authentication-Info", t);
- ha_unlock(NULL);
+ /* Put the connection into the cache */
+ if(save_cached_digest(ctx, rec) == HA_ERROR)
+ ret = HA_ERROR;
+ else
+ rec = NULL;
}
finally:
@@ -847,7 +1022,7 @@ finally:
int ldap_config(ha_context_t* context, const char* name, const char* value)
{
- ldap_context_t* ctx = (ldap_context_t*)(context.data);
+ ldap_context_t* ctx = (ldap_context_t*)(context->data);
if(strcmp(name, "ldapservers") == 0)
{
@@ -903,6 +1078,12 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return HA_OK;
}
+ else if(strcmp(name, "digestdomains") == 0)
+ {
+ ctx->domains = value;
+ return HA_OK;
+ }
+
else if(strcmp(name, "ldapscope") == 0)
{
if(strcmp(value, "sub") == 0 || strcmp(value, "subtree") == 0)
@@ -914,7 +1095,7 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
else
{
- messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name);
+ ha_messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name);
return HA_ERROR;
}
@@ -926,72 +1107,75 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return ha_confbool(name, value, &(ctx->dobind));
}
- else if(strcmp(name, "pendingmax") == 0)
+ else if(strcmp(name, "ldapmax") == 0)
{
- return ha_confint(name, value, 1, 256, &(ctx->pending_max));
+ return ha_confint(name, value, 1, 256, &(ctx->ldap_max));
}
- else if(strcmp(name, "pendingtimeout") == 0)
+ else if(strcmp(name, "ldaptimeout") == 0)
{
- return ha_confint(name, value, 0, 86400, &(ctx->pending_timeout));
+ return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout));
}
- else if(strcmp(name, "ldaptimeout") == 0)
+ else if(strcmp(name, "cachemax") == 0)
{
- return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout));
+ return ha_confint(name, value, 0, 0x7FFFFFFF, &(ctx->cache_max));
}
return HA_FALSE;
}
-int ldap_initialize(ha_context_t* context)
+int ldap_inithand(ha_context_t* context)
{
- /* No global initialization */
+ /* 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;
+ return ha_genrandom(g_ldap_secret, DIGEST_SECRET_LEN);
}
- /* Check for mandatory configuration */
- if(!ctx->servers || (!ctx->dnmap || !ctx->filter))
+
+ /* Context specific initialization */
+ else
{
- ha_messagex(LOG_ERR, "Digest LDAP configuration incomplete. "
- "Must have LDAPServers and either LDAPFilter or LDAPDNMap.");
- return HA_ERROR;
- }
+ 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, "LDAP module configured, but does not implement any "
+ "configured authentication type.");
+ 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;
- }
+ /* 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;
+ }
- /*
- * 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;
- }
+ /* The cache for digest records and basic */
+ if(!(ctx->cache = hash_create(MD5_LEN, free_hash_object, NULL)))
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
- memset(ctx->pool, 0, sizeof(LDAP*) * ctx->pending_max);
+ /*
+ * 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_ERROR;
+ }
+
+ memset(ctx->pool, 0, sizeof(LDAP*) * ctx->ldap_max);
+ }
return HA_OK;
}
@@ -1001,17 +1185,15 @@ void ldap_destroy(ha_context_t* context)
int i;
if(!context)
- return HA_OK;
+ return;
- ldap_context_t* ctx = (digest_ldap_context_t*)(context.data);
+ ldap_context_t* ctx = (ldap_context_t*)(context->data);
/* Note: We don't need to be thread safe here anymore */
- hash_free(ctx->pending);
- hash_free(ctx->established);
+ hash_free(ctx->cache);
- XXXXX
/* Close any connections we have open */
- for(i = 0; i < ctx->pending_max; i++)
+ for(i = 0; i < ctx->ldap_max; i++)
{
if(ctx->pool[i])
ldap_unbind_s(ctx->pool[i]);
@@ -1032,14 +1214,8 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
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);
+ /* Purge out stale connection stuff. */
+ hash_purge(ctx->cache, t - context->timeout);
ha_unlock(NULL);
@@ -1049,19 +1225,21 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
/* Check the headers and see if we got a response thingy */
- if(ctx->types & HA_TYPE_DIGEST)
+ if(context->types & HA_TYPE_DIGEST)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST);
if(header)
{
- ret = digest_ldap_response(ctx, header, resp, buf);
+ ret = digest_ldap_response(ctx, header, req->args[AUTH_ARG_METHOD],
+ req->args[AUTH_ARG_URI], context->timeout,
+ resp, buf);
if(ret == HA_ERROR)
return ret;
}
}
/* Or a basic authentication */
- if(!header && ctx->types & HA_TYPE_BASIC)
+ if(!header && context->types & HA_TYPE_BASIC)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
if(header)
@@ -1078,17 +1256,19 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
{
resp->code = HA_SERVER_DECLINE;
- if(ctx->types & HA_TYPE_DIGEST)
+ if(context->types & HA_TYPE_DIGEST)
{
ret = digest_ldap_challenge(ctx, resp, buf, 0);
if(ret == HA_ERROR)
return ret;
}
- if(ctx->types & HA_TYPE_BASIC)
+ if(context->types & HA_TYPE_BASIC)
{
- ha_bufnext(buf);
- ha_bufcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL);
+ ha_bufmcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL);
+
+ if(ha_buferr(buf))
+ return HA_ERROR;
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
}
@@ -1102,10 +1282,10 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
* Handler Definition
*/
-ha_handler_t digest_ldap_handler =
+ha_handler_t ldap_handler =
{
"LDAP", /* The type */
- ldap_initialize, /* Initialization function */
+ ldap_inithand, /* Initialization function */
ldap_destroy, /* Uninitialization routine */
ldap_config, /* Config routine */
ldap_process, /* Processing routine */