From: Timo Sirainen Date: Sun, 30 May 2004 03:57:15 +0000 (+0300) Subject: Added support for password scheme plugins. auth module dir defaults under X-Git-Tag: 1.1.alpha1~4022 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c0a05ba822189ff4f2c86645c38a5a904943192a;p=thirdparty%2Fdovecot%2Fcore.git Added support for password scheme plugins. auth module dir defaults under module_dir now. --HG-- branch : HEAD --- diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index fbc91d162a..1126d0c932 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -5,7 +5,7 @@ pkglibexec_PROGRAMS = dovecot-auth INCLUDES = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ - -DAUTH_MODULE_DIR=\""$(libdir)/dovecot/auth"\" \ + -DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \ $(AUTH_CFLAGS) dovecot_auth_LDADD = \ diff --git a/src/auth/main.c b/src/auth/main.c index f03432f55a..cf24101fe3 100644 --- a/src/auth/main.c +++ b/src/auth/main.c @@ -11,6 +11,7 @@ #include "mech.h" #include "userdb.h" #include "passdb.h" +#include "password-scheme.h" #include "auth-master-connection.h" #include "auth-client-connection.h" @@ -93,6 +94,7 @@ static void main_init(void) mech_init(); userdb_init(); passdb_init(); + password_schemes_init(); masters_buf = buffer_create_dynamic(default_pool, 64, (size_t)-1); @@ -161,6 +163,7 @@ static void main_deinit(void) auth_master_connection_free(master[i]); } + password_schemes_deinit(); passdb_deinit(); userdb_deinit(); mech_deinit(); diff --git a/src/auth/password-scheme.c b/src/auth/password-scheme.c index f955125599..c7e08da13f 100644 --- a/src/auth/password-scheme.c +++ b/src/auth/password-scheme.c @@ -1,9 +1,11 @@ /* Copyright (C) 2003 Timo Sirainen */ #include "lib.h" +#include "buffer.h" #include "base64.h" #include "hex-binary.h" #include "md5.h" +#include "module-dir.h" #include "mycrypt.h" #include "randgen.h" #include "str.h" @@ -16,61 +18,23 @@ static const char *salt_chars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +static buffer_t *schemes_buf; +static const struct password_scheme *schemes; +#ifdef HAVE_MODULES +static struct module *scheme_modules; +#endif + int password_verify(const char *plaintext, const char *password, const char *scheme, const char *user) { - unsigned char md5_digest[16]; - const char *realm, *str; + const struct password_scheme *s; if (password == NULL) return 0; - if (strcasecmp(scheme, "CRYPT") == 0) - return strcmp(mycrypt(plaintext, password), password) == 0; - - if (strcasecmp(scheme, "MD5") == 0) { - str = password_generate_md5_crypt(plaintext, password); - return strcmp(str, password) == 0; - } -#ifdef HAVE_OPENSSL_SHA1 - if (strcasecmp(scheme, "SHA") == 0 || - strcasecmp(scheme, "SHA1") == 0) { - unsigned char sha1_digest[SHA_DIGEST_LENGTH]; - string_t *str; - - SHA1(plaintext, strlen(plaintext), sha1_digest); - - str = t_str_new(64); - base64_encode(sha1_digest, sizeof(sha1_digest), str); - return strcasecmp(str_c(str), password) == 0; - } -#endif - - if (strcasecmp(scheme, "PLAIN") == 0) - return strcmp(password, plaintext) == 0; - - if (strcasecmp(scheme, "HMAC-MD5") == 0) { - str = password_generate_cram_md5(plaintext); - return strcmp(str, password) == 0; - } - - if (strcasecmp(scheme, "DIGEST-MD5") == 0) { - /* user:realm:passwd */ - realm = strchr(user, '@'); - if (realm != NULL) realm++; else realm = ""; - - str = t_strconcat(t_strcut(user, '@'), ":", realm, ":", - plaintext, NULL); - md5_get_digest(str, strlen(str), md5_digest); - str = binary_to_hex(md5_digest, sizeof(md5_digest)); - - return strcasecmp(str, password) == 0; - } - - if (strcasecmp(scheme, "PLAIN-MD5") == 0) { - md5_get_digest(plaintext, strlen(plaintext), md5_digest); - str = binary_to_hex(md5_digest, sizeof(md5_digest)); - return strcasecmp(str, password) == 0; + for (s = schemes; s->name != NULL; s++) { + if (strcasecmp(s->name, scheme) == 0) + return s->password_verify(plaintext, password, user); } return -1; @@ -110,48 +74,197 @@ const char *password_get_scheme(const char **password) const char *password_generate(const char *plaintext, const char *user, const char *scheme) { - const char *realm, *str; - unsigned char digest[16]; + const struct password_scheme *s; + + for (s = schemes; s->name != NULL; s++) { + if (strcasecmp(s->name, scheme) == 0) + return s->password_generate(plaintext, user); + } + + return NULL; +} + +static int crypt_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + return strcmp(mycrypt(plaintext, password), password) == 0; +} + +static const char *crypt_generate(const char *plaintext, + const char *user __attr_unused__) +{ + char salt[9]; + + random_fill(salt, 2); + salt[0] = salt_chars[salt[0] % (sizeof(salt_chars)-1)]; + salt[1] = salt_chars[salt[1] % (sizeof(salt_chars)-1)]; + salt[2] = '\0'; + return t_strdup(mycrypt(plaintext, salt)); +} + +static int md5_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + const char *str; + + str = password_generate_md5_crypt(plaintext, password); + return strcmp(str, password) == 0; +} + +static const char *md5_generate(const char *plaintext, + const char *user __attr_unused__) +{ char salt[9]; int i; - if (strcasecmp(scheme, "CRYPT") == 0) { - random_fill(salt, 2); - salt[0] = salt_chars[salt[0] % (sizeof(salt_chars)-1)]; - salt[1] = salt_chars[salt[1] % (sizeof(salt_chars)-1)]; - salt[2] = '\0'; - return t_strdup(mycrypt(plaintext, salt)); - } + random_fill(salt, 8); + for (i = 0; i < 8; i++) + salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)]; + salt[8] = '\0'; + return password_generate_md5_crypt(plaintext, salt); +} - if (strcasecmp(scheme, "MD5") == 0) { - random_fill(salt, 8); - for (i = 0; i < 8; i++) - salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)]; - salt[8] = '\0'; - return password_generate_md5_crypt(plaintext, salt); - } +#ifdef HAVE_OPENSSL_SHA1 +static int sha_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + unsigned char digest[SHA_DIGEST_LENGTH]; + string_t *str; + + SHA1(plaintext, strlen(plaintext), digest); + + str = t_str_new(64); + base64_encode(digest, sizeof(digest), str); + return strcasecmp(str_c(str), password) == 0; +} +#endif - if (strcasecmp(scheme, "PLAIN") == 0) - return plaintext; +static int plain_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + return strcmp(password, plaintext) == 0; +} + +static const char *plain_generate(const char *plaintext, + const char *user __attr_unused__) +{ + return plaintext; +} - if (strcasecmp(scheme, "HMAC-MD5") == 0) - return password_generate_cram_md5(plaintext); +static int hmac_md5_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + return strcmp(password_generate_cram_md5(plaintext), password) == 0; +} - if (strcasecmp(scheme, "DIGEST-MD5") == 0) { - /* user:realm:passwd */ - realm = strchr(user, '@'); - if (realm != NULL) realm++; else realm = ""; +static const char *hmac_md5_generate(const char *plaintext, + const char *user __attr_unused__) +{ + return password_generate_cram_md5(plaintext); +} - str = t_strconcat(t_strcut(user, '@'), ":", realm, ":", - plaintext, NULL); - md5_get_digest(str, strlen(str), digest); - return binary_to_hex(digest, sizeof(digest)); - } +static int digest_md5_verify(const char *plaintext, const char *password, + const char *user) +{ + unsigned char digest[16]; + const char *realm, *str; + + /* user:realm:passwd */ + realm = strchr(user, '@'); + if (realm != NULL) realm++; else realm = ""; + + str = t_strconcat(t_strcut(user, '@'), ":", realm, ":", + plaintext, NULL); + md5_get_digest(str, strlen(str), digest); + str = binary_to_hex(digest, sizeof(digest)); + + return strcasecmp(str, password) == 0; +} + +static const char *digest_md5_generate(const char *plaintext, const char *user) +{ + const char *realm, *str; + unsigned char digest[16]; + + /* user:realm:passwd */ + realm = strchr(user, '@'); + if (realm != NULL) realm++; else realm = ""; + + str = t_strconcat(t_strcut(user, '@'), ":", realm, ":", + plaintext, NULL); + md5_get_digest(str, strlen(str), digest); + return binary_to_hex(digest, sizeof(digest)); +} - if (strcasecmp(scheme, "PLAIN-MD5") == 0) { - md5_get_digest(plaintext, strlen(plaintext), digest); - return binary_to_hex(digest, sizeof(digest)); +static int plain_md5_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + unsigned char digest[16]; + const char *str; + + md5_get_digest(plaintext, strlen(plaintext), digest); + str = binary_to_hex(digest, sizeof(digest)); + return strcasecmp(str, password) == 0; +} + +static const char *plain_md5_generate(const char *plaintext, + const char *user __attr_unused__) +{ + unsigned char digest[16]; + + md5_get_digest(plaintext, strlen(plaintext), digest); + return binary_to_hex(digest, sizeof(digest)); +} + +static const struct password_scheme default_schemes[] = { + { "CRYPT", crypt_verify, crypt_generate }, + { "MD5", md5_verify, md5_generate }, +#ifdef HAVE_OPENSSL_SHA1 + { "SHA", sha_verify, NULL }, + { "SHA1", sha_verify, NULL }, +#endif + { "PLAIN", plain_verify, plain_generate }, + { "HMAC-MD5", hmac_md5_verify, hmac_md5_generate }, + { "DIGEST-MD5", digest_md5_verify, digest_md5_generate }, + { "PLAIN-MD5", plain_md5_verify, plain_md5_generate }, + { NULL, NULL, NULL } +}; + +void password_schemes_init(void) +{ + static const struct password_scheme null_scheme = { NULL, NULL, NULL }; + const struct password_scheme *s; +#ifdef HAVE_MODULES + struct module *mod; + const char *symbol; +#endif + + schemes_buf = buffer_create_dynamic(default_pool, 128, (size_t)-1); + for (s = default_schemes; s->name != NULL; s++) + buffer_append(schemes_buf, s, sizeof(*s)); + +#ifdef HAVE_MODULES + scheme_modules = module_dir_load(AUTH_MODULE_DIR"/password", FALSE); + for (mod = scheme_modules; mod != NULL; mod = mod->next) { + t_push(); + symbol = t_strconcat(mod->name, "_scheme", NULL); + s = module_get_symbol(mod, symbol); + if (s != NULL) + buffer_append(schemes_buf, s, sizeof(*s)); + t_pop(); } +#endif - return NULL; + buffer_append(schemes_buf, &null_scheme, sizeof(null_scheme)); + schemes = buffer_get_data(schemes_buf, NULL); +} + +void password_schemes_deinit(void) +{ +#ifdef HAVE_MODULES + module_dir_unload(scheme_modules); +#endif + + buffer_free(schemes_buf); + schemes = NULL; } diff --git a/src/auth/password-scheme.h b/src/auth/password-scheme.h index 0cd7750bb2..e24c4fcbf4 100644 --- a/src/auth/password-scheme.h +++ b/src/auth/password-scheme.h @@ -1,6 +1,15 @@ #ifndef __PASSWORD_SCHEME_H #define __PASSWORD_SCHEME_H +struct password_scheme { + const char *name; + + int (*password_verify)(const char *plaintext, const char *password, + const char *user); + const char *(*password_generate)(const char *plaintext, + const char *user); +}; + /* Returns 1 = matched, 0 = didn't match, -1 = unknown scheme */ int password_verify(const char *plaintext, const char *password, const char *scheme, const char *user); @@ -12,6 +21,9 @@ const char *password_get_scheme(const char **password); const char *password_generate(const char *plaintext, const char *user, const char *scheme); +void password_schemes_init(void); +void password_schemes_deinit(void); + /* INTERNAL: */ const char *password_generate_md5_crypt(const char *pw, const char *salt); const char *password_generate_cram_md5(const char *pw); diff --git a/src/imap/main.c b/src/imap/main.c index edf4a62e7b..ede12e2578 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -111,7 +111,7 @@ static void main_init(void) commands_init(); modules = getenv("MODULE_DIR") == NULL ? NULL : - module_dir_load(getenv("MODULE_DIR")); + module_dir_load(getenv("MODULE_DIR"), TRUE); str = getenv("IMAP_MAX_LINE_LENGTH"); imap_max_line_length = str != NULL ? diff --git a/src/lib/module-dir.c b/src/lib/module-dir.c index 1f98a99115..e159c9058e 100644 --- a/src/lib/module-dir.c +++ b/src/lib/module-dir.c @@ -17,28 +17,40 @@ # define RTLD_NOW 0 #endif -static void *get_symbol(const char *path, void *handle, const char *symbol) +void *module_get_symbol(struct module *module, const char *symbol) { const char *error; void *ret; /* get our init func */ - ret = dlsym(handle, symbol); + ret = dlsym(module->handle, symbol); error = dlerror(); if (error != NULL) { - i_error("module %s: dlsym(%s) failed: %s", path, symbol, error); + i_error("module %s: dlsym(%s) failed: %s", + module->path, symbol, error); ret = NULL; } return ret; } -static struct module *module_load(const char *path, const char *name) +static void module_free(struct module *module) +{ + if (module->deinit != NULL) + module->deinit(); + if (dlclose(module->handle) != 0) + i_error("dlclose(%s) failed: %m", module->path); + i_free(module->path); + i_free(module->name); + i_free(module); +} + +static struct module * +module_load(const char *path, const char *name, int require_init_funcs) { void *handle; void (*init)(void); - void (*deinit)(void); struct module *module; handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); @@ -47,27 +59,29 @@ static struct module *module_load(const char *path, const char *name) return NULL; } + module = i_new(struct module, 1); + module->path = i_strdup(path); + module->name = i_strdup(name); + module->handle = handle; + /* get our init func */ - init = (void (*)()) get_symbol(path, handle, - t_strconcat(name, "_init", NULL)); - deinit = init == NULL ? NULL : - (void (*)()) get_symbol(path, handle, - t_strconcat(name, "_deinit", NULL)); - - if (init == NULL || deinit == NULL) { - (void)dlclose(handle); + init = (void (*)()) + module_get_symbol(module, t_strconcat(name, "_init", NULL)); + module->deinit = init == NULL ? NULL : (void (*)()) + module_get_symbol(module, t_strconcat(name, "_deinit", NULL)); + + if ((init == NULL || module->deinit == NULL) && require_init_funcs) { + module->deinit = NULL; + module_free(module); return NULL; } - init(); - - module = i_new(struct module, 1); - module->handle = handle; - module->deinit = deinit; + if (init != NULL) + init(); return module; } -struct module *module_dir_load(const char *dir) +struct module *module_dir_load(const char *dir, int require_init_funcs) { DIR *dirp; struct dirent *d; @@ -97,7 +111,7 @@ struct module *module_dir_load(const char *dir) t_push(); name = t_strdup_until(d->d_name, p); path = t_strconcat(dir, "/", d->d_name, NULL); - module = module_load(path, name); + module = module_load(path, name, require_init_funcs); t_pop(); if (module != NULL) { @@ -118,10 +132,7 @@ void module_dir_unload(struct module *modules) while (modules != NULL) { next = modules->next; - modules->deinit(); - if (dlclose(modules->handle) != 0) - i_error("dlclose() failed: %m"); - i_free(modules); + module_free(modules); modules = next; } } diff --git a/src/lib/module-dir.h b/src/lib/module-dir.h index 53b293c61b..615fd4169b 100644 --- a/src/lib/module-dir.h +++ b/src/lib/module-dir.h @@ -2,6 +2,8 @@ #define __MODULE_DIR_H struct module { + char *path, *name; + void *handle; void (*deinit)(void); @@ -9,8 +11,10 @@ struct module { }; /* Load all modules in given directory. */ -struct module *module_dir_load(const char *dir); +struct module *module_dir_load(const char *dir, int require_init_funcs); /* Unload all modules */ void module_dir_unload(struct module *modules); +void *module_get_symbol(struct module *module, const char *symbol); + #endif diff --git a/src/pop3/main.c b/src/pop3/main.c index b254f47880..e66724944f 100644 --- a/src/pop3/main.c +++ b/src/pop3/main.c @@ -86,7 +86,7 @@ static int main_init(void) clients_init(); modules = getenv("MODULE_DIR") == NULL ? NULL : - module_dir_load(getenv("MODULE_DIR")); + module_dir_load(getenv("MODULE_DIR"), TRUE); mail = getenv("MAIL"); if (mail == NULL) {