changeset 1135:81930fff13cf HEAD

passdb ldap added. fixes to userdb ldap.
author Timo Sirainen <tss@iki.fi>
date Tue, 11 Feb 2003 11:55:58 +0200
parents bd3dabaa598e
children ad6343bd4479
files src/auth/Makefile.am src/auth/db-ldap.c src/auth/db-ldap.h src/auth/passdb-ldap.c src/auth/passdb.c src/auth/passdb.h src/auth/userdb-ldap.c
diffstat 7 files changed, 275 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/Makefile.am	Mon Feb 10 09:56:23 2003 +0200
+++ b/src/auth/Makefile.am	Tue Feb 11 11:55:58 2003 +0200
@@ -26,6 +26,7 @@
 	mech-digest-md5.c \
 	mycrypt.c \
 	passdb.c \
+	passdb-ldap.c \
 	passdb-passwd.c \
 	passdb-passwd-file.c \
 	passdb-pam.c \
--- a/src/auth/db-ldap.c	Mon Feb 10 09:56:23 2003 +0200
+++ b/src/auth/db-ldap.c	Tue Feb 11 11:55:58 2003 +0200
@@ -27,6 +27,7 @@
 	DEF(SET_STR, dn),
 	DEF(SET_STR, dnpass),
 	DEF(SET_STR, deref),
+	DEF(SET_STR, scope),
 	DEF(SET_STR, base),
 	DEF(SET_STR, attrs),
 	DEF(SET_STR, filter)
@@ -37,6 +38,7 @@
 	MEMBER(dn) NULL,
 	MEMBER(dnpass) NULL,
 	MEMBER(deref) "never",
+	MEMBER(scope) "subtree",
 	MEMBER(base) NULL,
 	MEMBER(attrs) NULL,
 	MEMBER(filter) NULL
@@ -58,6 +60,18 @@
 	i_fatal("LDAP: Unknown deref option '%s'", str);
 }
 
+static int scope2str(const char *str)
+{
+	if (strcasecmp(str, "base") == 0)
+		return LDAP_SCOPE_BASE;
+	if (strcasecmp(str, "onelevel") == 0)
+		return LDAP_SCOPE_ONELEVEL;
+	if (strcasecmp(str, "subtree") == 0)
+		return LDAP_SCOPE_SUBTREE;
+
+	i_fatal("LDAP: Unknown scope option '%s'", str);
+}
+
 static const char *get_ldap_error(struct ldap_connection *conn)
 {
 	int ret, err;
@@ -251,14 +265,11 @@
 	conn->set = default_ldap_settings;
 	settings_read(config_path, parse_setting, conn);
 
-	/*if (conn->set.dnuser == NULL)
-		i_fatal("LDAP: No user given");
-	if (conn->set.dnpass == NULL)
-		i_fatal("LDAP: No password given");*/
 	if (conn->set.base == NULL)
 		i_fatal("LDAP: No base given");
 
         conn->set.ldap_deref = deref2str(conn->set.deref);
+        conn->set.ldap_scope = scope2str(conn->set.scope);
 
 	(void)ldap_conn_open(conn);
 	return conn;
--- a/src/auth/db-ldap.h	Mon Feb 10 09:56:23 2003 +0200
+++ b/src/auth/db-ldap.h	Tue Feb 11 11:55:58 2003 +0200
@@ -15,11 +15,12 @@
 	const char *dn;
 	const char *dnpass;
 	const char *deref;
+	const char *scope;
 	const char *base;
 	const char *attrs;
 	const char *filter;
 
-	int ldap_deref;
+	int ldap_deref, ldap_scope;
 };
 
 struct ldap_connection {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-ldap.c	Tue Feb 11 11:55:58 2003 +0200
@@ -0,0 +1,234 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_LDAP
+
+#include "common.h"
+#include "str.h"
+#include "var-expand.h"
+#include "mycrypt.h"
+#include "db-ldap.h"
+#include "passdb.h"
+
+#include <ldap.h>
+#include <stdlib.h>
+
+/* using posixAccount */
+#define DEFAULT_ATTRIBUTES "uid,userPassword"
+
+enum ldap_user_attr {
+	ATTR_VIRTUAL_USER = 0,
+	ATTR_PASSWORD,
+
+	ATTR_COUNT
+};
+
+struct passdb_ldap_connection {
+	struct ldap_connection *conn;
+
+        unsigned int *attrs;
+	char **attr_names;
+};
+
+struct passdb_ldap_request {
+	struct ldap_request request;
+
+	enum passdb_credentials credentials;
+	union {
+		verify_plain_callback_t *verify_plain;
+                lookup_credentials_callback_t *lookup_credentials;
+	} callback;
+
+	char password[1]; /* variable width */
+};
+
+static struct passdb_ldap_connection *passdb_ldap_conn;
+
+static void handle_request(struct ldap_connection *conn,
+			   struct ldap_request *request, LDAPMessage *res)
+{
+	struct passdb_ldap_request *ldap_request =
+		(struct passdb_ldap_request *) request;
+        struct auth_request *auth_request = request->context;
+	LDAPMessage *entry;
+	BerElement *ber;
+	char *attr, **vals;
+	const char *user, *password;
+
+	if (auth_request->realm == NULL)
+		user = auth_request->user;
+	else {
+		user = t_strconcat(auth_request->user, "@",
+				   auth_request->realm, NULL);
+	}
+
+	password = NULL;
+
+	entry = ldap_first_entry(conn->ld, res);
+	if (entry == NULL)
+		i_error("ldap(%s): unknown user", user);
+	else {
+		attr = ldap_first_attribute(conn->ld, entry, &ber);
+		while (attr != NULL) {
+			vals = ldap_get_values(conn->ld, entry, attr);
+			if (vals != NULL && vals[0] != NULL &&
+			    vals[1] == NULL) {
+				if (strcasecmp(attr, passdb_ldap_conn->
+					       attr_names[ATTR_PASSWORD]) == 0)
+					password = t_strdup(vals[0]);
+			}
+			ldap_value_free(vals);
+			ldap_memfree(attr);
+
+			attr = ldap_next_attribute(conn->ld, entry, ber);
+		}
+
+		if (password == NULL)
+			i_error("ldap(%s): No password in reply", user);
+		else if (ldap_next_entry(conn->ld, entry) != NULL) {
+			i_error("ldap(%s): Multiple password replies", user);
+			password = NULL;
+		}
+	}
+
+	switch (ldap_request->credentials) {
+	case -1:
+		/* verify_plain */
+		if (password == NULL) {
+			ldap_request->callback.
+				verify_plain(PASSDB_RESULT_USER_UNKNOWN,
+					     auth_request);
+			break;
+		}
+
+		if (strncasecmp(password, "{crypt}", 7) == 0)
+			password += 7;
+
+		if (strcmp(mycrypt(password, ldap_request->password),
+			   ldap_request->password) != 0) {
+			if (verbose)
+				i_info("ldap(%s): password mismatch", user);
+			ldap_request->callback.
+				verify_plain(PASSDB_RESULT_PASSWORD_MISMATCH,
+					     auth_request);
+		} else {
+			ldap_request->callback.verify_plain(PASSDB_RESULT_OK,
+							    auth_request);
+		}
+		break;
+	case PASSDB_CREDENTIALS_PLAINTEXT:
+		if (password != NULL &&
+		    strncasecmp(password, "{plain}", 7) == 0)
+			password += 7;
+		else
+			password = NULL;
+
+		ldap_request->callback.lookup_credentials(password,
+							  auth_request);
+		break;
+	case PASSDB_CREDENTIALS_CRYPT:
+		ldap_request->callback.lookup_credentials(password,
+							  auth_request);
+		break;
+	case PASSDB_CREDENTIALS_DIGEST_MD5:
+		if (password != NULL &&
+		    strncasecmp(password, "{digest-md5}", 12) == 0)
+			password += 12;
+		else
+			password = NULL;
+
+		ldap_request->callback.lookup_credentials(password,
+							  auth_request);
+		break;
+	}
+}
+
+static void ldap_lookup_pass(struct auth_request *auth_request,
+			     struct ldap_request *ldap_request)
+{
+	struct ldap_connection *conn = passdb_ldap_conn->conn;
+	const char *user, *filter;
+	string_t *str;
+
+	if (auth_request->realm == NULL)
+		user = auth_request->user;
+	else {
+		user = t_strconcat(auth_request->user, "@",
+				   auth_request->realm, NULL);
+	}
+
+	if (conn->set.filter == NULL) {
+		filter = t_strdup_printf("(&(objectClass=posixAccount)(%s=%s))",
+			passdb_ldap_conn->attr_names[ATTR_VIRTUAL_USER], user);
+	} else {
+		str = t_str_new(512);
+		var_expand(str, conn->set.filter, user, NULL);
+		filter = str_c(str);
+	}
+
+	ldap_request->callback = handle_request;
+	ldap_request->context = auth_request;
+
+	db_ldap_search(conn, conn->set.base, conn->set.ldap_scope,
+		       filter, passdb_ldap_conn->attr_names,
+		       ldap_request);
+}
+
+static void
+ldap_verify_plain(struct auth_request *request, const char *password,
+		  verify_plain_callback_t *callback)
+{
+	struct passdb_ldap_request *ldap_request;
+
+	ldap_request = i_malloc(sizeof(struct passdb_ldap_request) +
+				strlen(password));
+	ldap_request->credentials = -1;
+	ldap_request->callback.verify_plain = callback;
+	strcpy(ldap_request->password, password);
+
+        ldap_lookup_pass(request, &ldap_request->request);
+}
+
+static void ldap_lookup_credentials(struct auth_request *request,
+				    enum passdb_credentials credentials,
+				    lookup_credentials_callback_t *callback)
+{
+	struct passdb_ldap_request *ldap_request;
+
+	ldap_request = i_new(struct passdb_ldap_request, 1);
+	ldap_request->credentials = credentials;
+	ldap_request->callback.lookup_credentials = callback;
+
+        ldap_lookup_pass(request, &ldap_request->request);
+}
+
+static void passdb_ldap_init(const char *args)
+{
+	struct ldap_connection *conn;
+
+	passdb_ldap_conn = i_new(struct passdb_ldap_connection, 1);
+	passdb_ldap_conn->conn = conn = db_ldap_init(args);
+
+	db_ldap_set_attrs(conn, conn->set.attrs ?
+			  conn->set.attrs : DEFAULT_ATTRIBUTES,
+			  &passdb_ldap_conn->attrs,
+			  &passdb_ldap_conn->attr_names);
+}
+
+static void passdb_ldap_deinit(void)
+{
+	db_ldap_unref(passdb_ldap_conn->conn);
+	i_free(passdb_ldap_conn);
+}
+
+struct passdb_module passdb_ldap = {
+	passdb_ldap_init,
+	passdb_ldap_deinit,
+
+	ldap_verify_plain,
+	ldap_lookup_credentials
+};
+
+#endif
--- a/src/auth/passdb.c	Mon Feb 10 09:56:23 2003 +0200
+++ b/src/auth/passdb.c	Tue Feb 11 11:55:58 2003 +0200
@@ -11,8 +11,12 @@
 const char *passdb_credentials_to_str(enum passdb_credentials credentials)
 {
 	switch (credentials) {
+	case _PASSDB_CREDENTIALS_INTERNAL:
+		break;
 	case PASSDB_CREDENTIALS_PLAINTEXT:
 		return "plaintext";
+	case PASSDB_CREDENTIALS_CRYPT:
+		return "crypt";
 	case PASSDB_CREDENTIALS_DIGEST_MD5:
 		return "digest-md5";
 	}
@@ -53,6 +57,10 @@
 	if (strcasecmp(name, "vpopmail") == 0)
 		passdb = &passdb_vpopmail;
 #endif
+#ifdef USERDB_LDAP
+	if (strcasecmp(name, "ldap") == 0)
+		passdb = &passdb_ldap;
+#endif
 
 	if (passdb == NULL)
 		i_fatal("Unknown passdb type '%s'", name);
--- a/src/auth/passdb.h	Mon Feb 10 09:56:23 2003 +0200
+++ b/src/auth/passdb.h	Tue Feb 11 11:55:58 2003 +0200
@@ -7,7 +7,10 @@
 	((pass)[0] != '\0' && (pass)[0] != '*' && (pass)[0] != '!')
 
 enum passdb_credentials {
+	_PASSDB_CREDENTIALS_INTERNAL = -1,
+
 	PASSDB_CREDENTIALS_PLAINTEXT,
+	PASSDB_CREDENTIALS_CRYPT,
 	PASSDB_CREDENTIALS_DIGEST_MD5
 };
 
@@ -49,6 +52,7 @@
 extern struct passdb_module passdb_passwd_file;
 extern struct passdb_module passdb_pam;
 extern struct passdb_module passdb_vpopmail;
+extern struct passdb_module passdb_ldap;
 
 void passdb_init(void);
 void passdb_deinit(void);
--- a/src/auth/userdb-ldap.c	Mon Feb 10 09:56:23 2003 +0200
+++ b/src/auth/userdb-ldap.c	Tue Feb 11 11:55:58 2003 +0200
@@ -6,6 +6,8 @@
 #ifdef USERDB_LDAP
 
 #include "common.h"
+#include "str.h"
+#include "var-expand.h"
 #include "db-ldap.h"
 #include "userdb.h"
 
@@ -98,6 +100,7 @@
 	entry = ldap_first_entry(conn->ld, res);
 	if (entry == NULL) {
 		i_error("LDAP: ldap_first_entry failed()");
+		urequest->userdb_callback(NULL, request->context);
 		return;
 	}
 
@@ -115,9 +118,10 @@
 		attr = ldap_next_attribute(conn->ld, entry, ber);
 	}
 
-	if (user.virtual_user == NULL)
+	if (user.virtual_user == NULL) {
 		i_error("LDAP: No username in reply");
-	else {
+		urequest->userdb_callback(NULL, request->context);
+	} else {
 		if (ldap_next_entry(conn->ld, entry) != NULL) {
 			i_error("LDAP: Multiple replies found for user %s",
 				user.virtual_user);
@@ -135,6 +139,7 @@
 	struct ldap_connection *conn = userdb_ldap_conn->conn;
 	struct userdb_ldap_request *request;
 	const char *filter;
+	string_t *str;
 
 	if (realm != NULL)
 		user = t_strconcat(user, "@", realm, NULL);
@@ -143,8 +148,9 @@
 		filter = t_strdup_printf("(&(objectClass=posixAccount)(%s=%s))",
 			userdb_ldap_conn->attr_names[ATTR_VIRTUAL_USER], user);
 	} else {
-		filter = t_strdup_printf("(&%s(%s=%s))", conn->set.filter,
-			userdb_ldap_conn->attr_names[ATTR_VIRTUAL_USER], user);
+		str = t_str_new(512);
+		var_expand(str, conn->set.filter, user, NULL);
+		filter = str_c(str);
 	}
 
 	request = i_new(struct userdb_ldap_request, 1);
@@ -152,7 +158,7 @@
 	request->request.context = context;
 	request->userdb_callback = callback;
 
-	db_ldap_search(conn, conn->set.base, LDAP_SCOPE_SUBTREE,
+	db_ldap_search(conn, conn->set.base, conn->set.ldap_scope,
 		       filter, userdb_ldap_conn->attr_names,
 		       &request->request);
 }