db-passwd-file.c revision b4ed649cad1f149bb8fc058eb2eb44f0278a729b
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "auth-common.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
7e209b78ca757294dbbc15604c88673b3a6b0c39Timo Sirainen#if defined (USERDB_PASSWD_FILE) || defined(PASSDB_PASSWD_FILE)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "userdb.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "db-passwd-file.h"
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "array.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "buffer.h"
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen#include "istream.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "hash.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "str.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "eacces-error.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "ioloop.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <unistd.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <fcntl.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <time.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <sys/stat.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#define PARSE_TIME_STARTUP_WARN_SECS 60
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen#define PARSE_TIME_RELOAD_WARN_SECS 10
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainenstatic struct db_passwd_file *passwd_files;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic void ATTR_NULL(3)
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainenpasswd_file_add(struct passwd_file *pw, const char *username,
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen const char *pass, const char *const *args)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* args = uid, gid, user info, home dir, shell, extra_fields */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct passwd_user *pu;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const char *extra_fields = NULL;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen char *user;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen size_t len;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (hash_table_lookup(pw->users, username) != NULL) {
6b265a8a9d1ce3b3a8033445e99c9035d62ffbc7Timo Sirainen i_error("passwd-file %s: User %s exists more than once",
6b265a8a9d1ce3b3a8033445e99c9035d62ffbc7Timo Sirainen pw->path, username);
6b265a8a9d1ce3b3a8033445e99c9035d62ffbc7Timo Sirainen return;
6b265a8a9d1ce3b3a8033445e99c9035d62ffbc7Timo Sirainen }
6b265a8a9d1ce3b3a8033445e99c9035d62ffbc7Timo Sirainen
6b265a8a9d1ce3b3a8033445e99c9035d62ffbc7Timo Sirainen pu = p_new(pw->pool, struct passwd_user, 1);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen user = p_strdup(pw->pool, username);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen len = pass == NULL ? 0 : strlen(pass);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (len > 4 && pass[0] != '{' && pass[0] != '$' &&
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pass[len-1] == ']' && pass[len-4] == '[') {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* password[type] - we're being libpam-pwdfile compatible
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen here. it uses 13 = DES and 34 = MD5. For backwards
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen comaptibility with ourself, we have also 56 = Digest-MD5. */
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen int num = (pass[len-3] - '0') * 10 + (pass[len-2] - '0');
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen pass = t_strndup(pass, len-4);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (num == 34) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->password = p_strconcat(pw->pool, "{PLAIN-MD5}",
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pass, NULL);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else if (num == 56) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->password = p_strconcat(pw->pool, "{DIGEST-MD5}",
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pass, NULL);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (strlen(pu->password) != 32 + 12) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_error("passwd-file %s: User %s "
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen "has invalid password",
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pw->path, username);
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen return;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else {
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen pu->password = p_strconcat(pw->pool, "{CRYPT}",
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen pass, NULL);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen } else {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->password = p_strdup(pw->pool, pass);
7e209b78ca757294dbbc15604c88673b3a6b0c39Timo Sirainen }
7e209b78ca757294dbbc15604c88673b3a6b0c39Timo Sirainen
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen pu->uid = (uid_t)-1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->gid = (gid_t)-1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (*args == NULL)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen else if (!pw->db->userdb || **args == '\0') {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen args++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen pu->uid = userdb_parse_uid(NULL, *args);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen if (pu->uid == 0 || pu->uid == (uid_t)-1) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_error("passwd-file %s: User %s has invalid UID '%s'",
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pw->path, username, *args);
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen return;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen }
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen args++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen if (*args == NULL) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (pw->db->userdb_warn_missing) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_error("passwd-file %s: User %s is missing "
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen "userdb info", pw->path, username);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* don't allow userdb lookups */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->uid = 0;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->gid = 0;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else if (!pw->db->userdb || **args == '\0')
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen args++;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen else {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen pu->gid = userdb_parse_gid(NULL, *args);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (pu->gid == 0 || pu->gid == (gid_t)-1) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_error("passwd-file %s: User %s has invalid GID '%s'",
67770c0874918e3b73bbd1ff75b5ec91790d51dcTimo Sirainen pw->path, username, *args);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen args++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* user info */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (*args != NULL)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen args++;
bcdb6c0bd2e7dbb34b306d3d8c2383a7d7654612Timo Sirainen
bcdb6c0bd2e7dbb34b306d3d8c2383a7d7654612Timo Sirainen /* home */
bcdb6c0bd2e7dbb34b306d3d8c2383a7d7654612Timo Sirainen if (*args != NULL) {
bcdb6c0bd2e7dbb34b306d3d8c2383a7d7654612Timo Sirainen if (pw->db->userdb)
bcdb6c0bd2e7dbb34b306d3d8c2383a7d7654612Timo Sirainen pu->home = p_strdup_empty(pw->pool, *args);
bcdb6c0bd2e7dbb34b306d3d8c2383a7d7654612Timo Sirainen args++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* shell */
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (*args != NULL)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen args++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
fb5b342aab25d6dc96df14774eb5215dc5481546Timo Sirainen if (*args != NULL && **args == '\0') {
1330f999b8076b2f8eed2572c667f7482a555c1bTimo Sirainen /* old format, this field is empty and next field may
1330f999b8076b2f8eed2572c667f7482a555c1bTimo Sirainen contain MAIL */
1330f999b8076b2f8eed2572c667f7482a555c1bTimo Sirainen args++;
899ebb8ba3fbebaafd3e431943283719ce2b106dTimo Sirainen if (*args != NULL && **args != '\0' && pw->db->userdb) {
899ebb8ba3fbebaafd3e431943283719ce2b106dTimo Sirainen extra_fields =
899ebb8ba3fbebaafd3e431943283719ce2b106dTimo Sirainen t_strconcat("userdb_mail=",
899ebb8ba3fbebaafd3e431943283719ce2b106dTimo Sirainen t_strarray_join(args, ":"), NULL);
899ebb8ba3fbebaafd3e431943283719ce2b106dTimo Sirainen }
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen } else if (*args != NULL) {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen /* new format, contains a space separated list of
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen extra fields */
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen extra_fields = t_strarray_join(args, ":");
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen }
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen if (extra_fields != NULL) {
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen pu->extra_fields =
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen p_strsplit_spaces(pw->pool, extra_fields, " ");
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen }
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen hash_table_insert(pw->users, user, pu);
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen}
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic struct passwd_file *
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenpasswd_file_new(struct db_passwd_file *db, const char *expanded_path)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct passwd_file *pw;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen pw = i_new(struct passwd_file, 1);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen pw->db = db;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen pw->path = i_strdup(expanded_path);
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen pw->fd = -1;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (hash_table_is_created(db->files))
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen hash_table_insert(db->files, pw->path, pw);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen return pw;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen}
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainenstatic int passwd_file_open(struct passwd_file *pw, bool startup,
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen const char **error_r)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen{
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen const char *no_args = NULL;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen struct istream *input;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen const char *line;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen struct stat st;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen time_t start_time, end_time;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen unsigned int time_secs;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen int fd;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen fd = open(pw->path, O_RDONLY);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (fd == -1) {
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (errno == EACCES)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen *error_r = eacces_error_get("open", pw->path);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen else {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen *error_r = t_strdup_printf("open(%s) failed: %m",
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->path);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen }
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen return -1;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen if (fstat(fd, &st) != 0) {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen *error_r = t_strdup_printf("fstat(%s) failed: %m",
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen pw->path);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen i_close_fd(&fd);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen return -1;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->fd = fd;
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->stamp = st.st_mtime;
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->size = st.st_size;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->pool = pool_alloconly_create(MEMPOOL_GROWING"passwd_file", 10240);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen hash_table_create(&pw->users, pw->pool, 0, str_hash, strcmp);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen start_time = time(NULL);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen input = i_stream_create_fd(pw->fd, (size_t)-1);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen i_stream_set_return_partial_line(input, TRUE);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen if (*line == '\0' || *line == ':' || *line == '#')
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen continue; /* no username or comment */
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen T_BEGIN {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen const char *const *args = t_strsplit(line, ":");
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen if (args[1] != NULL) {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen /* at least username+password */
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen passwd_file_add(pw, args[0], args[1], args+2);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen } else {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen /* only username */
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen passwd_file_add(pw, args[0], NULL, &no_args);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen }
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen } T_END;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen }
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen i_stream_destroy(&input);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen end_time = time(NULL);
7f3b826a89bcb7a72759912e99f574b28309fe1bTimo Sirainen time_secs = end_time - start_time;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen if ((time_secs > PARSE_TIME_STARTUP_WARN_SECS && startup) ||
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen (time_secs > PARSE_TIME_RELOAD_WARN_SECS && !startup)) {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen i_warning("passwd-file %s: Reading %u users took %u secs",
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->path, hash_table_count(pw->users), time_secs);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen } else if (pw->db->debug) {
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen i_debug("passwd-file %s: Read %u users in %u secs",
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->path, hash_table_count(pw->users), time_secs);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen return 0;
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen}
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainenstatic void passwd_file_close(struct passwd_file *pw)
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen{
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (pw->fd != -1) {
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (close(pw->fd) < 0)
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen i_error("passwd-file %s: close() failed: %m", pw->path);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pw->fd = -1;
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (hash_table_is_created(pw->users))
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen hash_table_destroy(&pw->users);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (pw->pool != NULL)
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen pool_unref(&pw->pool);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen}
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainenstatic void passwd_file_free(struct passwd_file *pw)
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen{
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (hash_table_is_created(pw->db->files))
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen hash_table_remove(pw->db->files, pw->path);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen passwd_file_close(pw);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen i_free(pw->path);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen i_free(pw);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainenstatic int passwd_file_sync(struct auth_request *request,
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen struct passwd_file *pw)
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen{
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen struct stat st;
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen const char *error;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (pw->last_sync_time == ioloop_time)
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen return hash_table_is_created(pw->users) ? 0 : -1;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen pw->last_sync_time = ioloop_time;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
5e203e846133e28d0e36ca70ef1c30e72a922c14Timo Sirainen if (stat(pw->path, &st) < 0) {
5e203e846133e28d0e36ca70ef1c30e72a922c14Timo Sirainen /* with variables don't give hard errors, or errors about
5e203e846133e28d0e36ca70ef1c30e72a922c14Timo Sirainen nonexistent files */
5e203e846133e28d0e36ca70ef1c30e72a922c14Timo Sirainen if (errno == EACCES) {
5e203e846133e28d0e36ca70ef1c30e72a922c14Timo Sirainen auth_request_log_error(request, AUTH_SUBSYS_DB,
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen "%s", eacces_error_get("stat", pw->path));
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen } else {
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen auth_request_log_error(request, AUTH_SUBSYS_DB,
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen "stat(%s) failed: %m", pw->path);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen if (pw->db->default_file != pw)
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen passwd_file_free(pw);
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen return -1;
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (st.st_mtime != pw->stamp || st.st_size != pw->size) {
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen passwd_file_close(pw);
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen if (passwd_file_open(pw, FALSE, &error) < 0) {
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen auth_request_log_error(request, AUTH_SUBSYS_DB,
107f6e87c6edcb29cf87195357144dea3fdda9afTimo Sirainen "%s", error);
107f6e87c6edcb29cf87195357144dea3fdda9afTimo Sirainen return -1;
107f6e87c6edcb29cf87195357144dea3fdda9afTimo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen }
834f2b9f60d93e4c7dfc6992e692f5b7213a1b11Timo Sirainen return 0;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen}
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainenstatic struct db_passwd_file *db_passwd_file_find(const char *path)
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen{
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen struct db_passwd_file *f;
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen for (f = passwd_files; f != NULL; f = f->next) {
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen if (strcmp(f->path, path) == 0)
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen return f;
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen }
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen return NULL;
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen}
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainenstatic void db_passwd_file_set_userdb(struct db_passwd_file *db)
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen{
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen db->userdb = TRUE;
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen /* warn about missing userdb fields only when there aren't any other
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen userdbs. */
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen db->userdb_warn_missing =
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen array_is_created(&global_auth_settings->userdbs) &&
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen array_count(&global_auth_settings->userdbs) == 1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstruct db_passwd_file *
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainendb_passwd_file_init(const char *path, bool userdb, bool debug)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct db_passwd_file *db;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const char *p;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen bool percents = FALSE;
7e209b78ca757294dbbc15604c88673b3a6b0c39Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db = db_passwd_file_find(path);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (db != NULL) {
075a53973bbdf15cc3bd2ba4872f96f3f2f00574Timo Sirainen db->refcount++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (userdb)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db_passwd_file_set_userdb(db);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return db;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db = i_new(struct db_passwd_file, 1);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db->refcount = 1;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (userdb)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db_passwd_file_set_userdb(db);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db->debug = debug;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen for (p = path; *p != '\0'; p++) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (*p == '%' && p[1] != '\0') {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (var_get_key(++p) == '%')
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen percents = TRUE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen else
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db->vars = TRUE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
d4854db53e9c141db3d02821ed857bea101b1cc2Timo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (percents && !db->vars) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* just extra escaped % chars. remove them. */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct var_expand_table empty_table[1];
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen string_t *dest;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen empty_table[0].key = '\0';
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen dest = t_str_new(256);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen var_expand(dest, path, empty_table);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen path = str_c(dest);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen db->path = i_strdup(path);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (db->vars) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen hash_table_create(&db->files, default_pool, 0,
867990944f0bda3a5bd70895d5a5ebfa611d0505Timo Sirainen str_hash, strcmp);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else {
867990944f0bda3a5bd70895d5a5ebfa611d0505Timo Sirainen db->default_file = passwd_file_new(db, path);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
867990944f0bda3a5bd70895d5a5ebfa611d0505Timo Sirainen
867990944f0bda3a5bd70895d5a5ebfa611d0505Timo Sirainen db->next = passwd_files;
867990944f0bda3a5bd70895d5a5ebfa611d0505Timo Sirainen passwd_files = db;
867990944f0bda3a5bd70895d5a5ebfa611d0505Timo Sirainen return db;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainenvoid db_passwd_file_parse(struct db_passwd_file *db)
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen{
111a7dda02defa4d612468cfc3c40da5240645afTimo Sirainen const char *error;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (db->default_file != NULL && db->default_file->stamp == 0) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* no variables, open the file immediately */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (passwd_file_open(db->default_file, TRUE, &error) < 0)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_error("passwd-file: %s", error);
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen }
3b1bf68d26c9f2fe4a649f40cf375d52acffc81cTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenvoid db_passwd_file_unref(struct db_passwd_file **_db)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct db_passwd_file *db = *_db;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen struct db_passwd_file **p;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen struct hash_iterate_context *iter;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen char *path;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen struct passwd_file *file;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen *_db = NULL;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen i_assert(db->refcount >= 0);
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen if (--db->refcount > 0)
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen return;
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen
66c0f96d704f09c88dd03b0ee13a3e9711ffe593Timo Sirainen for (p = &passwd_files; *p != NULL; p = &(*p)->next) {
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen if (*p == db) {
*p = db->next;
break;
}
}
if (db->default_file != NULL)
passwd_file_free(db->default_file);
else {
iter = hash_table_iterate_init(db->files);
while (hash_table_iterate(iter, db->files, &path, &file))
passwd_file_free(file);
hash_table_iterate_deinit(&iter);
hash_table_destroy(&db->files);
}
i_free(db->path);
i_free(db);
}
static const char *
path_fix(const char *path,
const struct auth_request *auth_request ATTR_UNUSED)
{
const char *p;
p = strchr(path, '/');
if (p == NULL)
return path;
/* most likely this is an invalid request. just cut off the '/' and
everything after it. */
return t_strdup_until(path, p);
}
int db_passwd_file_lookup(struct db_passwd_file *db,
struct auth_request *request,
const char *username_format,
struct passwd_user **user_r)
{
struct passwd_file *pw;
string_t *username, *dest;
if (!db->vars)
pw = db->default_file;
else {
dest = t_str_new(256);
auth_request_var_expand(dest, db->path, request, path_fix);
pw = hash_table_lookup(db->files, str_c(dest));
if (pw == NULL) {
/* doesn't exist yet. create lookup for it. */
pw = passwd_file_new(db, str_c(dest));
}
}
if (passwd_file_sync(request, pw) < 0) {
/* pw may be freed now */
return -1;
}
username = t_str_new(256);
auth_request_var_expand(username, username_format, request,
auth_request_str_escape);
auth_request_log_debug(request, AUTH_SUBSYS_DB,
"lookup: user=%s file=%s",
str_c(username), pw->path);
*user_r = hash_table_lookup(pw->users, str_c(username));
if (*user_r == NULL) {
auth_request_log_unknown_user(request, AUTH_SUBSYS_DB);
return 0;
}
return 1;
}
#endif