db-passwd-file.c revision 76b43e4417bab52e913da39b5f5bc2a130d3f149
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "common.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#if defined (USERDB_PASSWD_FILE) || defined(PASSDB_PASSWD_FILE)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "userdb.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "db-passwd-file.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "buffer.h"
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi#include "istream.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "hash.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "str.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include "var-expand.h"
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include <stdlib.h>
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include <unistd.h>
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#include <fcntl.h>
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch#include <sys/stat.h>
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic struct db_passwd_file *passwd_files;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic void passwd_file_add(struct passwd_file *pw, const char *username,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *pass, const char *const *args)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* args = uid, gid, user info, home dir, shell, extra_fields */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct passwd_user *pu;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *extra_fields = NULL;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen char *user;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen size_t len;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (hash_lookup(pw->users, username) != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: User %s exists more than once",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->path, username);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen return;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu = p_new(pw->pool, struct passwd_user, 1);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen user = p_strdup(pw->pool, username);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen len = pass == NULL ? 0 : strlen(pass);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (len > 4 && pass[0] != '{' && pass[0] != '$' &&
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pass[len-1] == ']' && pass[len-4] == '[') {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* password[type] - we're being libpam-pwdfile compatible
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen here. it uses 13 = DES and 34 = MD5. For backwards
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen comaptibility with ourself, we have also 56 = Digest-MD5. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen int num = (pass[len-3] - '0') * 10 + (pass[len-2] - '0');
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pass = t_strndup(pass, len-4);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (num == 34) {
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi pu->password = p_strconcat(pw->pool, "{PLAIN-MD5}",
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi pass, NULL);
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi } else if (num == 56) {
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi pu->password = p_strconcat(pw->pool, "{DIGEST-MD5}",
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi pass, NULL);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (strlen(pu->password) != 32 + 12) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: User %s "
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "has invalid password",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->path, username);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen } else {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->password = p_strconcat(pw->pool, "{CRYPT}",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pass, NULL);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen } else {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->password = p_strdup(pw->pool, pass);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->uid = (uid_t)-1;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->gid = (gid_t)-1;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args == NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen ;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen else if (!pw->db->userdb || **args == '\0') {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen } else {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen pu->uid = userdb_parse_uid(NULL, *args);
9644b7914445f0fb1098038218bfcb7d135a8698Timo Sirainen if (pu->uid == 0 || pu->uid == (uid_t)-1) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: User %s has invalid UID '%s'",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->path, username, *args);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args == NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->db->userdb) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: User %s is missing "
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "userdb info", pw->path, username);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen } else if (!pw->db->userdb || **args == '\0')
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen else {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->gid = userdb_parse_gid(NULL, *args);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pu->gid == 0 || pu->gid == (gid_t)-1) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: User %s has invalid GID '%s'",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->path, username, *args);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* user info */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* home */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->db->userdb)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->home = p_strdup_empty(pw->pool, *args);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* shell */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args != NULL && **args == '\0') {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* old format, this field is empty and next field may
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen contain MAIL */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen args++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*args != NULL && **args != '\0' && pw->db->userdb) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen extra_fields =
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen t_strconcat("userdb_mail=",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen t_strarray_join(args, ":"), NULL);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen } else if (*args != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* new format, contains a space separated list of
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen extra fields */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen extra_fields = t_strarray_join(args, ":");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (extra_fields != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu->extra_fields =
1d738cce754bc64bbc66d3355ebdaf3f6eac55f1Timo Sirainen p_strsplit_spaces(pw->pool, extra_fields, " ");
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen hash_insert(pw->users, user, pu);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic struct passwd_file *
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenpasswd_file_new(struct db_passwd_file *db, const char *expanded_path)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct passwd_file *pw;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw = i_new(struct passwd_file, 1);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->db = db;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->path = i_strdup(expanded_path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->fd = -1;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (db->files != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen hash_insert(db->files, pw->path, pw);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return pw;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic bool passwd_file_open(struct passwd_file *pw)
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *no_args = NULL;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen struct istream *input;
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen const char *line;
9644b7914445f0fb1098038218bfcb7d135a8698Timo Sirainen struct stat st;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen int fd;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen fd = open(pw->path, O_RDONLY);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (fd == -1) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: Can't open file: %m", pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return FALSE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
f6d5c9fbdac9af5c4d3f467f828dc6f056309d5eTimo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (fstat(fd, &st) != 0) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: fstat() failed: %m", pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen (void)close(fd);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return FALSE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->fd = fd;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->stamp = st.st_mtime;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->size = st.st_size;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->pool = pool_alloconly_create("passwd_file", 10240);;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->users = hash_create(default_pool, pw->pool, 100,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen str_hash, (hash_cmp_callback_t *)strcmp);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen input = i_stream_create_fd(pw->fd, 4096, FALSE);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*line == '\0' || *line == ':' || *line == '#')
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen continue; /* no username or comment */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen T_FRAME(
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *const *args = t_strsplit(line, ":");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (args[1] != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* at least username+password */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen passwd_file_add(pw, args[0], args[1], args+2);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen } else {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* only username */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen passwd_file_add(pw, args[0], NULL, &no_args);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen );
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_stream_destroy(&input);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->db->debug) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_info("passwd-file %s: Read %u users",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->path, hash_count(pw->users));
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return TRUE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic void passwd_file_close(struct passwd_file *pw)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->fd != -1) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (close(pw->fd) < 0)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: close() failed: %m", pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw->fd = -1;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->users != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen hash_destroy(&pw->users);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->pool != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pool_unref(&pw->pool);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic void passwd_file_free(struct passwd_file *pw)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->db->files != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen hash_remove(pw->db->files, pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen passwd_file_close(pw);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_free(pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_free(pw);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic bool passwd_file_sync(struct passwd_file *pw)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct stat st;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (stat(pw->path, &st) < 0) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* with variables don't give hard errors, or errors about
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen nonexisting files */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (errno != ENOENT)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_error("passwd-file %s: stat() failed: %m", pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw->db->default_file != pw)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen passwd_file_free(pw);
1af0c5d8814c2304d09d8ca844a84f0b9b0c1f61Timo Sirainen return FALSE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (st.st_mtime != pw->stamp || st.st_size != pw->size) {
b71e08759c02a49d02cdfb28343351ad28fc10b5Timo Sirainen passwd_file_close(pw);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return passwd_file_open(pw);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen return TRUE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic struct db_passwd_file *db_passwd_file_find(const char *path)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct db_passwd_file *f;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen for (f = passwd_files; f != NULL; f = f->next) {
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen if (strcmp(f->path, path) == 0)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return f;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return NULL;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstruct db_passwd_file *
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainendb_passwd_file_init(const char *path, const char *username_format,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen bool userdb, bool debug)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct db_passwd_file *db;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *p;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen bool percents = FALSE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db = db_passwd_file_find(path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (db != NULL) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->refcount++;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->userdb = TRUE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return db;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db = i_new(struct db_passwd_file, 1);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->refcount = 1;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->userdb = userdb;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->debug = debug;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->username_format = username_format;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen for (p = path; *p != '\0'; p++) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*p == '%' && p[1] != '\0') {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (var_get_key(++p) == '%')
85da8c055280cd45553b6b335e9fb226d6e2801eTimo Sirainen percents = TRUE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen else
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->vars = TRUE;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (percents && !db->vars) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* just extra escaped % chars. remove them. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct var_expand_table empty_table[1];
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen string_t *dest;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen empty_table[0].key = '\0';
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen dest = t_str_new(256);
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen var_expand(dest, path, empty_table);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen path = str_c(dest);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->path = i_strdup(path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (db->vars) {
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen db->files = hash_create(default_pool, default_pool, 100,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen str_hash,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen (hash_cmp_callback_t *)strcmp);
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen } else {
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen db->default_file = passwd_file_new(db, path);
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen db->next = passwd_files;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen passwd_files = db;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return db;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenvoid db_passwd_file_parse(struct db_passwd_file *db)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
48325adac125d7ff275ec69b05b7a92be9637630Timo Sirainen if (db->default_file != NULL && db->default_file->stamp == 0) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* no variables, open the file immediately */
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if (!passwd_file_open(db->default_file))
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen exit(FATAL_DEFAULT);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenvoid db_passwd_file_unref(struct db_passwd_file **_db)
85da8c055280cd45553b6b335e9fb226d6e2801eTimo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct db_passwd_file *db = *_db;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct db_passwd_file **p;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct hash_iterate_context *iter;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen void *key, *value;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen *_db = NULL;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_assert(db->refcount >= 0);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (--db->refcount > 0)
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen return;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen for (p = &passwd_files; *p != NULL; p = &(*p)->next) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (*p == db) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen *p = db->next;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen break;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
b71e08759c02a49d02cdfb28343351ad28fc10b5Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (db->default_file != NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen passwd_file_free(db->default_file);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen else {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen iter = hash_iterate_init(db->files);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen while (hash_iterate(iter, &key, &value)) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct passwd_file *file = value;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen passwd_file_free(file);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen hash_iterate_deinit(&iter);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen hash_destroy(&db->files);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen }
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen i_free(db->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen i_free(db);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic const char *
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenpath_fix(const char *path,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const struct auth_request *auth_request ATTR_UNUSED)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *p;
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen p = strchr(path, '/');
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (p == NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return path;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* most likely this is an invalid request. just cut off the '/' and
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen everything after it. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return t_strdup_until(path, p);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstruct passwd_user *
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainendb_passwd_file_lookup(struct db_passwd_file *db, struct auth_request *request)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen{
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct passwd_file *pw;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct passwd_user *pu;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const struct var_expand_table *table;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen string_t *username;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const char *path;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (!db->vars)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw = db->default_file;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen else {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen const struct var_expand_table *table;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen string_t *dest;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen table = auth_request_get_var_expand_table(request, path_fix);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen dest = t_str_new(256);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen var_expand(dest, db->path, table);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw = hash_lookup(db->files, str_c(dest));
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pw == NULL) {
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen /* doesn't exist yet. create lookup for it. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pw = passwd_file_new(db, str_c(dest));
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen path = t_strdup(pw->path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (!passwd_file_sync(pw)) {
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen /* pw may be freed now */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen auth_request_log_info(request, "passwd-file",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "no passwd file: %s", path);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return NULL;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen }
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen username = t_str_new(256);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen table = auth_request_get_var_expand_table(request,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen auth_request_str_escape);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen var_expand(username, db->username_format, table);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen auth_request_log_debug(request, "passwd-file",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "lookup: user=%s file=%s",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen str_c(username), pw->path);
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen pu = hash_lookup(pw->users, str_c(username));
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pu == NULL)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen auth_request_log_info(request, "passwd-file", "unknown user");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen return pu;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen}
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen#endif
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen