summaryrefslogtreecommitdiff
path: root/daemon/ldap.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/ldap.c')
-rw-r--r--daemon/ldap.c1931
1 files changed, 743 insertions, 1188 deletions
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)
};