db-passwd-file.c revision 5f21795890064f8e1ebebdff752b67f15ea59e37
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "auth-common.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#if defined (USERDB_PASSWD_FILE) || defined(PASSDB_PASSWD_FILE)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "userdb.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "db-passwd-file.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "buffer.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "istream.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "hash.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "str.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "var-expand.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <stdlib.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <unistd.h>
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen#include <fcntl.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <sys/stat.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstatic struct db_passwd_file *passwd_files;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstatic void passwd_file_add(struct passwd_file *pw, const char *username,
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen const char *pass, const char *const *args)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen{
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* args = uid, gid, user info, home dir, shell, extra_fields */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct passwd_user *pu;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen const char *extra_fields = NULL;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen char *user;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen size_t len;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (hash_table_lookup(pw->users, username) != NULL) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen i_error("passwd-file %s: User %s exists more than once",
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen pw->path, username);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pu = p_new(pw->pool, struct passwd_user, 1);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen user = p_strdup(pw->pool, username);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen len = pass == NULL ? 0 : strlen(pass);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (len > 4 && pass[0] != '{' && pass[0] != '$' &&
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pass[len-1] == ']' && pass[len-4] == '[') {
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen /* password[type] - we're being libpam-pwdfile compatible
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen here. it uses 13 = DES and 34 = MD5. For backwards
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen comaptibility with ourself, we have also 56 = Digest-MD5. */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen int num = (pass[len-3] - '0') * 10 + (pass[len-2] - '0');
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pass = t_strndup(pass, len-4);
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen if (num == 34) {
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen pu->password = p_strconcat(pw->pool, "{PLAIN-MD5}",
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen pass, NULL);
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen } else if (num == 56) {
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen pu->password = p_strconcat(pw->pool, "{DIGEST-MD5}",
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen pass, NULL);
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen if (strlen(pu->password) != 32 + 12) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen i_error("passwd-file %s: User %s "
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen "has invalid password",
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pw->path, username);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen } else {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pu->password = p_strconcat(pw->pool, "{CRYPT}",
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen pass, NULL);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen } else {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen pu->password = p_strdup(pw->pool, pass);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pu->uid = (uid_t)-1;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pu->gid = (gid_t)-1;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (*args == NULL)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen ;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen else if (!pw->db->userdb || **args == '\0') {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen args++;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen } else {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pu->uid = userdb_parse_uid(NULL, *args);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (pu->uid == 0 || pu->uid == (uid_t)-1) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen i_error("passwd-file %s: User %s has invalid UID '%s'",
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pw->path, username, *args);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen args++;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (*args == NULL) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (pw->db->userdb) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen i_error("passwd-file %s: User %s is missing "
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen "userdb info", pw->path, username);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen } else if (!pw->db->userdb || **args == '\0')
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen args++;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen else {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pu->gid = userdb_parse_gid(NULL, *args);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (pu->gid == 0 || pu->gid == (gid_t)-1) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_error("passwd-file %s: User %s has invalid GID '%s'",
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen pw->path, username, *args);
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen return;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen args++;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* user info */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (*args != NULL)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen args++;
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen /* home */
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen if (*args != NULL) {
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen if (pw->db->userdb)
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen pu->home = p_strdup_empty(pw->pool, *args);
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen args++;
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen /* shell */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (*args != NULL)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen args++;
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen if (*args != NULL && **args == '\0') {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen /* old format, this field is empty and next field may
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen contain MAIL */
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen args++;
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen if (*args != NULL && **args != '\0' && pw->db->userdb) {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen extra_fields =
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen t_strconcat("userdb_mail=",
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen t_strarray_join(args, ":"), NULL);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen } else if (*args != NULL) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* new format, contains a space separated list of
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen extra fields */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen extra_fields = t_strarray_join(args, ":");
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (extra_fields != NULL) {
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen pu->extra_fields =
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen p_strsplit_spaces(pw->pool, extra_fields, " ");
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen hash_table_insert(pw->users, user, pu);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic struct passwd_file *
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenpasswd_file_new(struct db_passwd_file *db, const char *expanded_path)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen{
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen struct passwd_file *pw;
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen pw = i_new(struct passwd_file, 1);
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen pw->db = db;
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen pw->path = i_strdup(expanded_path);
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen pw->fd = -1;
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (db->files != NULL)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen hash_table_insert(db->files, pw->path, pw);
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen return pw;
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen}
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenstatic bool passwd_file_open(struct passwd_file *pw)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen{
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen const char *no_args = NULL;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct istream *input;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen const char *line;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct stat st;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen int fd;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen fd = open(pw->path, O_RDONLY);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (fd == -1) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen i_error("passwd-file %s: Can't open file: %m", pw->path);
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen return FALSE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen if (fstat(fd, &st) != 0) {
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen i_error("passwd-file %s: fstat() failed: %m", pw->path);
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen (void)close(fd);
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen return FALSE;
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen }
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen pw->fd = fd;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen pw->stamp = st.st_mtime;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen pw->size = st.st_size;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen pw->pool = pool_alloconly_create(MEMPOOL_GROWING"passwd_file", 10240);
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen pw->users = hash_table_create(default_pool, pw->pool, 100,
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen str_hash, (hash_cmp_callback_t *)strcmp);
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen input = i_stream_create_fd(pw->fd, 4096, FALSE);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_stream_set_return_partial_line(input, TRUE);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen if (*line == '\0' || *line == ':' || *line == '#')
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen continue; /* no username or comment */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen T_BEGIN {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen const char *const *args = t_strsplit(line, ":");
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (args[1] != NULL) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* at least username+password */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen passwd_file_add(pw, args[0], args[1], args+2);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen } else {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* only username */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen passwd_file_add(pw, args[0], NULL, &no_args);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen } T_END;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_stream_destroy(&input);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (pw->db->debug) {
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen i_debug("passwd-file %s: Read %u users",
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen pw->path, hash_table_count(pw->users));
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return TRUE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen}
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainenstatic void passwd_file_close(struct passwd_file *pw)
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen{
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (pw->fd != -1) {
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (close(pw->fd) < 0)
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen i_error("passwd-file %s: close() failed: %m", pw->path);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen pw->fd = -1;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (pw->users != NULL)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen hash_table_destroy(&pw->users);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (pw->pool != NULL)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen pool_unref(&pw->pool);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen}
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstatic void passwd_file_free(struct passwd_file *pw)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen{
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (pw->db->files != NULL)
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen hash_table_remove(pw->db->files, pw->path);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen passwd_file_close(pw);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_free(pw->path);
294f1a51763015cda0e2d874c5027d6fe7a2cd54Timo Sirainen i_free(pw);
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen}
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainenstatic bool passwd_file_sync(struct passwd_file *pw)
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen{
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen struct stat st;
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen if (stat(pw->path, &st) < 0) {
18634dae6e304bac982bb1e0ff1b6b88fc448dbcTimo Sirainen /* with variables don't give hard errors, or errors about
294f1a51763015cda0e2d874c5027d6fe7a2cd54Timo Sirainen nonexisting files */
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (errno != ENOENT)
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen i_error("passwd-file %s: stat() failed: %m", pw->path);
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen if (pw->db->default_file != pw)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen passwd_file_free(pw);
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen return FALSE;
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen }
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (st.st_mtime != pw->stamp || st.st_size != pw->size) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen passwd_file_close(pw);
return passwd_file_open(pw);
}
return TRUE;
}
static struct db_passwd_file *db_passwd_file_find(const char *path)
{
struct db_passwd_file *f;
for (f = passwd_files; f != NULL; f = f->next) {
if (strcmp(f->path, path) == 0)
return f;
}
return NULL;
}
struct db_passwd_file *
db_passwd_file_init(const char *path, bool userdb, bool debug)
{
struct db_passwd_file *db;
const char *p;
bool percents = FALSE;
db = db_passwd_file_find(path);
if (db != NULL) {
db->refcount++;
db->userdb = TRUE;
return db;
}
db = i_new(struct db_passwd_file, 1);
db->refcount = 1;
db->userdb = userdb;
db->debug = debug;
for (p = path; *p != '\0'; p++) {
if (*p == '%' && p[1] != '\0') {
if (var_get_key(++p) == '%')
percents = TRUE;
else
db->vars = TRUE;
}
}
if (percents && !db->vars) {
/* just extra escaped % chars. remove them. */
struct var_expand_table empty_table[1];
string_t *dest;
empty_table[0].key = '\0';
dest = t_str_new(256);
var_expand(dest, path, empty_table);
path = str_c(dest);
}
db->path = i_strdup(path);
if (db->vars) {
db->files = hash_table_create(default_pool, default_pool, 100,
str_hash,
(hash_cmp_callback_t *)strcmp);
} else {
db->default_file = passwd_file_new(db, path);
}
db->next = passwd_files;
passwd_files = db;
return db;
}
void db_passwd_file_parse(struct db_passwd_file *db)
{
if (db->default_file != NULL && db->default_file->stamp == 0) {
/* no variables, open the file immediately */
if (!passwd_file_open(db->default_file))
exit(FATAL_DEFAULT);
}
}
void db_passwd_file_unref(struct db_passwd_file **_db)
{
struct db_passwd_file *db = *_db;
struct db_passwd_file **p;
struct hash_iterate_context *iter;
void *key, *value;
*_db = NULL;
i_assert(db->refcount >= 0);
if (--db->refcount > 0)
return;
for (p = &passwd_files; *p != NULL; p = &(*p)->next) {
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, &key, &value)) {
struct passwd_file *file = value;
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);
}
struct passwd_user *
db_passwd_file_lookup(struct db_passwd_file *db, struct auth_request *request,
const char *username_format)
{
struct passwd_file *pw;
struct passwd_user *pu;
const struct var_expand_table *table;
string_t *username, *dest;
const char *path;
if (!db->vars)
pw = db->default_file;
else {
table = auth_request_get_var_expand_table(request, path_fix);
dest = t_str_new(256);
var_expand(dest, db->path, table);
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));
}
}
path = t_strdup(pw->path);
if (!passwd_file_sync(pw)) {
/* pw may be freed now */
auth_request_log_info(request, "passwd-file",
"no passwd file: %s", path);
return NULL;
}
username = t_str_new(256);
table = auth_request_get_var_expand_table(request,
auth_request_str_escape);
var_expand(username, username_format, table);
auth_request_log_debug(request, "passwd-file",
"lookup: user=%s file=%s",
str_c(username), pw->path);
pu = hash_table_lookup(pw->users, str_c(username));
if (pu == NULL)
auth_request_log_info(request, "passwd-file", "unknown user");
return pu;
}
#endif