Mercurial > dovecot > original-hg > dovecot-1.2
view src/deliver/deliver.c @ 3338:e5ce49c8524a HEAD
USER auth command requires now service parameter and supports also others
parameters. Fixes a crash in dovecot-auth with deliver+mysql.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 26 Apr 2005 14:43:30 +0300 |
parents | 5949ce51755b |
children | 70c200deddf7 |
line wrap: on
line source
/* Copyright (C) 2005 Timo Sirainen */ /* FIXME: pretty ugly thing. */ #include "lib.h" #include "lib-signals.h" #include "ioloop.h" #include "env-util.h" #include "network.h" #include "restrict-access.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "var-expand.h" #include "mail-storage.h" #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <pwd.h> #include <sysexits.h> #define DEFAULT_CONFIG_FILE SYSCONFDIR"/dovecot-deliver.conf" #define DEFAULT_AUTH_SOCKET_PATH "/var/run/dovecot/auth-master" #define MAX_INBUF_SIZE 8192 #define MAX_OUTBUF_SIZE 512 struct auth_connection { int fd; struct io *io; struct istream *input; struct ostream *output; unsigned int handshaked:1; }; static struct ioloop *ioloop; static int return_value = EX_SOFTWARE; static void sig_quit(int signo __attr_unused__) { io_loop_stop(ioloop); } static int sync_quick(struct mailbox *box) { struct mailbox_sync_context *ctx; struct mailbox_sync_rec sync_rec; struct mailbox_status status; ctx = mailbox_sync_init(box, 0); while (mailbox_sync_next(ctx, &sync_rec) > 0) ; return mailbox_sync_deinit(ctx, &status); } struct save_mail_context { struct mail_save_context *save_ctx; struct istream *input; int ret; }; static void save_mail_input(void *context) { struct save_mail_context *ctx = context; if (ctx->input->closed || mailbox_save_continue(ctx->save_ctx) < 0) io_loop_stop(ioloop); else if (ctx->input->eof) { ctx->ret = 0; io_loop_stop(ioloop); } } static int save_mail(struct mail_storage *storage, const char *mailbox, struct istream *input) { struct mailbox *box; struct mailbox_transaction_context *t; struct save_mail_context ctx; struct io *io; int ret = 0; box = mailbox_open(storage, mailbox, NULL, MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); if (box == NULL) return FALSE; if (sync_quick(box) < 0) { mailbox_close(box); return FALSE; } t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); memset(&ctx, 0, sizeof(ctx)); ctx.ret = -1; ctx.input = input; ctx.save_ctx = mailbox_save_init(t, 0, NULL, (time_t)-1, 0, NULL, input, FALSE); io = io_add(i_stream_get_fd(input), IO_READ, save_mail_input, &ctx); io_loop_run(ioloop); io_remove(io); ret = ctx.ret; if (ret < 0) mailbox_save_cancel(ctx.save_ctx); else ret = mailbox_save_finish(ctx.save_ctx, NULL); if (ret < 0) mailbox_transaction_rollback(t); else ret = mailbox_transaction_commit(t, 0); mailbox_close(box); return ret; } static void auth_connection_destroy(struct auth_connection *conn) { io_loop_stop(ioloop); io_remove(conn->io); i_stream_unref(conn->input); o_stream_unref(conn->output); i_free(conn); } static void auth_parse_input(const char *args) { const char *const *tmp; for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) { if (strncmp(*tmp, "uid=", 4) == 0) { env_put(t_strconcat("RESTRICT_SETUID=", *tmp + 4, NULL)); } else if (strncmp(*tmp, "gid=", 4) == 0) { env_put(t_strconcat("RESTRICT_SETGID=", *tmp + 4, NULL)); } else if (strncmp(*tmp, "chroot=", 7) == 0) { env_put(t_strconcat("RESTRICT_CHROOT=", *tmp + 7, NULL)); } else if (strncmp(*tmp, "home=", 5) == 0) env_put(t_strconcat("HOME=", *tmp + 5, NULL)); } restrict_access_by_env(TRUE); return_value = EX_OK; } static void auth_input(void *context) { struct auth_connection *conn = context; const char *line; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_connection_destroy(conn); return; case -2: /* buffer full */ i_error("BUG: Auth master sent us more than %d bytes", MAX_INBUF_SIZE); auth_connection_destroy(conn); return; } if (!conn->handshaked) { while ((line = i_stream_next_line(conn->input)) != NULL) { if (strncmp(line, "VERSION\t", 8) == 0) { if (strncmp(line + 8, "1\t", 2) != 0) { i_error("Auth master version mismatch"); auth_connection_destroy(conn); return; } } else if (strncmp(line, "SPID\t", 5) == 0) { conn->handshaked = TRUE; break; } } } line = i_stream_next_line(conn->input); if (line != NULL) { if (strncmp(line, "USER\t1\t", 7) == 0) { auth_parse_input(line + 7); } else if (strcmp(line, "NOTFOUND\t1") == 0) return_value = EX_NOUSER; else if (strncmp(line, "FAIL\t1\t", 7) == 0) return_value = EX_TEMPFAIL; else { i_error("BUG: Unexpected input from auth master: %s", line); } auth_connection_destroy(conn); } } static struct auth_connection *auth_connection_new(const char *auth_socket) { struct auth_connection *conn; int fd; fd = net_connect_unix(auth_socket); if (fd < 0) { i_error("net_connect(%s) failed: %m", auth_socket); return NULL; } conn = i_new(struct auth_connection, 1); conn->fd = fd; conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE); conn->io = io_add(fd, IO_READ, auth_input, conn); return conn; } static int user_init(const char *auth_socket, const char *destination) { struct auth_connection *conn; conn = auth_connection_new(auth_socket); if (conn == NULL) return EX_TEMPFAIL; o_stream_send_str(conn->output, t_strconcat("VERSION\t1\t0\n" "USER\t1\t", destination, "\t" "service=deliver\n", NULL)); io_loop_run(ioloop); return return_value; } static void config_file_init(const char *path) { struct istream *input; const char *line, *p, *key, *value; int fd; fd = open(path, O_RDONLY); if (fd < 0) i_fatal_status(EX_CONFIG, "open(%s) failed: %m", path); t_push(); input = i_stream_create_file(fd, default_pool, 1024, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) { while (*line == ' ') line++; if (*line == '#') continue; value = p = strchr(line, '='); if (value == NULL) continue; while (p > line && p[-1] == ' ') p--; key = t_strdup_until(line, p); do { value++; } while (*value == ' '); env_put(t_strconcat(t_str_ucase(key), "=", value, NULL)); } i_stream_unref(input); t_pop(); } static const struct var_expand_table * get_var_expand_table(const char *user, const char *home) { static struct var_expand_table static_tab[] = { { 'u', NULL }, { 'n', NULL }, { 'd', NULL }, { 's', NULL }, { 'h', NULL }, { 'l', NULL }, { 'r', NULL }, { 'p', NULL }, { '\0', NULL } }; struct var_expand_table *tab; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = user; tab[1].value = t_strcut(user, '@'); tab[2].value = strchr(user, '@'); if (tab[2].value != NULL) tab[2].value++; tab[3].value = "DELIVER"; tab[4].value = home; tab[5].value = NULL; tab[6].value = NULL; tab[7].value = dec2str(getpid()); return tab; } static const char * expand_mail_env(const char *env, const struct var_expand_table *table) { string_t *str; const char *p; str = t_str_new(256); /* it's either type:data or just data */ p = strchr(env, ':'); if (p != NULL) { while (env != p) { str_append_c(str, *env); env++; } str_append_c(str, *env++); } if (env[0] == '~' && env[1] == '/') { /* expand home */ env = t_strconcat("%h", env+1, NULL); } /* expand %vars */ var_expand(str, env, table); return str_c(str); } int main(int argc, char *argv[]) { const char *auth_socket = DEFAULT_AUTH_SOCKET_PATH; const char *destination, *mail; const struct var_expand_table *table; enum mail_storage_flags flags; enum mail_storage_lock_method lock_method; struct mail_storage *storage; struct istream *input; int i, ret; const char *str; lib_init(); lib_init_signals(sig_quit); ioloop = io_loop_create(default_pool); destination = NULL; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-d") == 0) { /* destination user */ i++; if (i == argc) { i_fatal_status(EX_USAGE, "Missing destination argument"); } destination = argv[i]; } else if (strcmp(argv[i], "-a") == 0) { /* auth master socket path */ i++; if (i == argc) { i_fatal_status(EX_USAGE, "Missing auth socket path argument"); } auth_socket = argv[i]; } else { i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[1]); } } config_file_init(DEFAULT_CONFIG_FILE); if (destination != NULL) { ret = user_init(auth_socket, destination); if (ret != 0) return ret; } else if (geteuid() != 0) { /* we're non-root. get our username. */ struct passwd *pw; pw = getpwuid(geteuid()); if (pw != NULL) destination = t_strdup(pw->pw_name); } if (destination == NULL) { i_fatal_status(EX_USAGE, "destination user parameter (-d user) not given"); } mail_storage_init(); mail_storage_register_all(); mail = getenv("MAIL"); if (mail == NULL) i_fatal_status(EX_CONFIG, "mail setting not given"); table = get_var_expand_table(destination, getenv("HOME")); mail = expand_mail_env(mail, table); flags = 0; if (getenv("FULL_FILESYSTEM_ACCESS") != NULL) flags |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS; if (getenv("DEBUG") != NULL) flags |= MAIL_STORAGE_FLAG_DEBUG; if (getenv("MMAP_DISABLE") != NULL) flags |= MAIL_STORAGE_FLAG_MMAP_DISABLE; if (getenv("MMAP_NO_WRITE") != NULL) flags |= MAIL_STORAGE_FLAG_MMAP_NO_WRITE; if (getenv("MAIL_READ_MMAPED") != NULL) flags |= MAIL_STORAGE_FLAG_MMAP_MAILS; if (getenv("MAIL_SAVE_CRLF") != NULL) flags |= MAIL_STORAGE_FLAG_SAVE_CRLF; /*FIXME:if ((uidl_keymask & UIDL_MD5) != 0) flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;*/ str = getenv("LOCK_METHOD"); if (str == NULL || strcmp(str, "fcntl") == 0) lock_method = MAIL_STORAGE_LOCK_FCNTL; else if (strcmp(str, "flock") == 0) lock_method = MAIL_STORAGE_LOCK_FLOCK; else if (strcmp(str, "dotlock") == 0) lock_method = MAIL_STORAGE_LOCK_DOTLOCK; else i_fatal("Unknown lock_method: %s", str); /* FIXME: how should we handle namespaces? */ storage = mail_storage_create_with_data(mail, destination, flags, lock_method); if (storage == NULL) { i_fatal_status(EX_CONFIG, "Failed to create storage for '%s' with mail '%s'", destination, mail == NULL ? "(null)" : mail); } net_set_nonblock(0, TRUE); input = i_stream_create_file(0, default_pool, 8192, FALSE); if (save_mail(storage, "INBOX", input) < 0) return EX_TEMPFAIL; i_stream_unref(input); mail_storage_destroy(storage); mail_storage_deinit(); io_loop_destroy(ioloop); lib_deinit(); return EX_OK; }