Mercurial > dovecot > original-hg > dovecot-1.2
view src/auth/userinfo-passwd-file.c @ 363:567e932cdc66 HEAD
Added autoclose_fd-flag for io_buffer_create_file() and
io_buffer_create_mmap().
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 06 Oct 2002 06:09:36 +0300 |
parents | c2269ed9ffcb |
children | ea958a5b9de1 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #ifndef _XOPEN_SOURCE_EXTENDED # define _XOPEN_SOURCE_EXTENDED #endif #define _XOPEN_SOURCE 4 #define _XPG4_2 #include "common.h" #ifdef USERINFO_PASSWD_FILE #include "iobuffer.h" #include "hash.h" #include "hex-binary.h" #include "md5.h" #include "userinfo.h" #include "userinfo-passwd.h" #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> typedef struct { Pool pool; char *path; time_t stamp; int fd; HashTable *users; } PasswdFile; typedef enum { PASSWORD_DES, PASSWORD_MD5, PASSWORD_DIGEST_MD5 } PasswordType; typedef struct { char *user_realm; /* user:realm */ const char *realm; /* NULL or points to user_realm */ char *password; char *home; char *mail; uid_t uid; gid_t gid; PasswordType password_type; unsigned int chroot:1; } PasswdUser; static PasswdFile *passwd_file; static int get_reply_data(PasswdUser *pu, AuthCookieReplyData *reply) { const char *user; struct passwd *pw; if (pu->uid == 0 || pu->gid == 0 || (pu->home == NULL && pu->mail == NULL)) { /* all required information was not set in passwd file, check from system's passwd */ user = pu->realm == NULL ? pu->user_realm : t_strndup(pu->user_realm, strlen(pu->user_realm) - strlen(pu->realm) - 1); pw = getpwnam(user); if (pw == NULL) return FALSE; passwd_fill_cookie_reply(pw, reply); } if (pu->uid != 0) reply->uid = pu->uid; if (pu->gid != 0) reply->gid = pu->gid; if (pu->home != NULL) { i_assert(sizeof(reply->home) > strlen(pu->home)); strcpy(reply->home, pu->home); } if (pu->mail != NULL) { i_assert(sizeof(reply->mail) > strlen(pu->mail)); strcpy(reply->mail, pu->mail); } reply->chroot = pu->chroot; return TRUE; } static int passwd_file_verify_plain(const char *user, const char *password, AuthCookieReplyData *reply) { PasswdUser *pu; char *const *tmp; unsigned char digest[16]; const char *str; /* find it from all realms */ pu = hash_lookup(passwd_file->users, user); if (pu == NULL) { t_push(); for (tmp = auth_realms; *tmp != NULL; tmp++) { str = t_strconcat(user, ":", *tmp, NULL); pu = hash_lookup(passwd_file->users, str); } t_pop(); } if (pu == NULL) return FALSE; /* verify that password matches */ switch (pu->password_type) { case PASSWORD_DES: if (strcmp(crypt(password, pu->password), pu->password) != 0) return FALSE; break; case PASSWORD_MD5: md5_get_digest(password, strlen(password), digest); str = binary_to_hex(digest, sizeof(digest)); if (strcmp(str, pu->password) != 0) return FALSE; break; case PASSWORD_DIGEST_MD5: /* user:realm:passwd */ str = t_strconcat(pu->user_realm, pu->realm == NULL ? ":" : "", ":", password, NULL); md5_get_digest(str, strlen(str), digest); str = binary_to_hex(digest, sizeof(digest)); if (strcmp(str, pu->password) != 0) return FALSE; break; default: i_assert(0); } /* found */ return get_reply_data(pu, reply); } static int passwd_file_lookup_digest_md5(const char *user, const char *realm, unsigned char digest[16], AuthCookieReplyData *reply) { const char *id; PasswdUser *pu; /* FIXME: we simply ignore UTF8 setting.. */ id = realm == NULL || *realm == '\0' ? user : t_strconcat(user, ":", realm, NULL); pu = hash_lookup(passwd_file->users, id); if (pu == NULL) return FALSE; /* found */ i_assert(strlen(pu->password) == 32); if (!hex_to_binary(pu->password, digest)) return FALSE; return get_reply_data(pu, reply); } static void passwd_file_add(PasswdFile *pw, const char *username, const char *pass, char *const *args) { /* args = uid, gid, user info, home dir, shell, realm, mail, chroot */ PasswdUser *pu; const char *p; if (strlen(username) >= AUTH_MAX_USER_LEN) { i_error("Username %s is too long (max. %d chars) in password " "file %s", username, AUTH_MAX_USER_LEN, pw->path); return; } pu = p_new(pw->pool, PasswdUser, 1); p = strchr(pass, '['); if (p == NULL) { pu->password = p_strdup(pw->pool, pass); pu->password_type = PASSWORD_DES; } else { /* password[type] - we're being libpam-pwdfile compatible here. it uses 13 = DES and 34 = MD5. We add 56 = Digest-MD5. */ pu->password = p_strndup(pw->pool, pass, (size_t) (p-pass)); if (p[1] == '3' && p[2] == '4') { pu->password_type = PASSWORD_MD5; str_lcase(pu->password); } else if (p[1] == '5' && p[2] == '6') { pu->password_type = PASSWORD_DIGEST_MD5; if (strlen(pu->password) != 32) { i_error("User %s has invalid password in " "file %s", username, pw->path); return; } str_lcase(pu->password); } else { pu->password_type = PASSWORD_DES; } } if (args[0] != NULL) { pu->uid = atoi(args[0]); if (pu->uid == 0) { i_error("User %s has UID 0 in password file %s", username, pw->path); return; } args++; } if (args[0] != NULL) { pu->gid = atoi(args[0]); if (pu->gid == 0) { i_error("User %s has GID 0 in password file %s", username, pw->path); return; } args++; } /* user info */ if (args[0] != NULL) args++; /* home */ if (args[0] != NULL) { if (strlen(args[0]) >= AUTH_MAX_HOME_LEN) { i_error("User %s has too long home directory in " "password file %s", username, pw->path); return; } pu->home = p_strdup(pw->pool, args[0]); args++; } /* shell */ if (args[0] != NULL) args++; /* realm */ if (args[0] == NULL || *args[0] == '\0') { pu->user_realm = p_strdup(pw->pool, username); if (hash_lookup(pw->users, username) != NULL) { i_error("User %s already exists in password file %s", username, pw->path); return; } } else { pu->user_realm = p_strconcat(pw->pool, username, ":", args[0], NULL); pu->realm = pu->user_realm + strlen(username)+1; if (hash_lookup(pw->users, pu->user_realm) != NULL) { i_error("User %s already exists in realm %s in " "password file %s", username, args[0], pw->path); return; } } /* mail storage */ if (args[0] != NULL) { if (strlen(args[0]) >= AUTH_MAX_MAIL_LEN) { i_error("User %s has too long mail storage in " "password file %s", username, pw->path); return; } pu->mail = p_strdup(pw->pool, args[0]); args++; } /* chroot */ if (args[0] != NULL && strstr(args[0], "chroot") != NULL) pu->chroot = TRUE; hash_insert(pw->users, pu->user_realm, pu); } static void passwd_file_parse_file(PasswdFile *pw) { IOBuffer *inbuf; char *const *args; char *line; inbuf = io_buffer_create_file(pw->fd, default_pool, 2048, FALSE); for (;;) { line = io_buffer_next_line(inbuf); if (line == NULL) { if (io_buffer_read(inbuf) <= 0) break; continue; } if (*line == '\0' || *line == ':') continue; /* no username */ t_push(); args = t_strsplit(line, ":"); if (args[1] != NULL && IS_VALID_PASSWD(args[1])) { /* valid user/pass */ passwd_file_add(pw, args[0], args[1], args+2); } t_pop(); } io_buffer_destroy(inbuf); } static PasswdFile *passwd_file_parse(const char *path) { PasswdFile *pw; Pool pool; struct stat st; int fd; fd = open(path, O_RDONLY); if (fd == -1) { i_fatal("Can't open passwd-file %s: %m", path); } if (fstat(fd, &st) != 0) i_fatal("fstat() failed for passwd-file %s: %m", path); pool = pool_create("PasswdFile", 10240, FALSE); pw = p_new(pool, PasswdFile, 1); pw->pool = pool; pw->path = p_strdup(pool, path); pw->stamp = st.st_mtime; pw->fd = fd; pw->users = hash_create(pool, 100, str_hash, (HashCompareFunc) strcmp); passwd_file_parse_file(pw); return pw; } static void passwd_file_free(PasswdFile *pw) { pool_unref(pw->pool); } static void passwd_file_init(const char *args) { passwd_file = passwd_file_parse(args); } static void passwd_file_deinit(void) { passwd_file_free(passwd_file); } UserInfoModule userinfo_passwd_file = { passwd_file_init, passwd_file_deinit, passwd_file_verify_plain, passwd_file_lookup_digest_md5 }; #endif