/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "buffer.h" #include "env-util.h" #include "fd-close-on-exec.h" #include "execv-const.h" #include "settings-parser.h" #include "service-settings.h" #include "login-settings.h" #include "managesieve-login-settings.h" #include <stddef.h> #include <unistd.h> #include <sys/wait.h> #include <sysexits.h> /* <settings checks> */ #ifdef _CONFIG_PLUGIN static bool managesieve_login_settings_verify (void *_set, pool_t pool, const char **error_r); #endif static struct inet_listener_settings managesieve_login_inet_listeners_array[] = { { "sieve", "", 4190, FALSE }, }; static struct inet_listener_settings *managesieve_login_inet_listeners[] = { &managesieve_login_inet_listeners_array[0] }; static buffer_t managesieve_login_inet_listeners_buf = { managesieve_login_inet_listeners, sizeof(managesieve_login_inet_listeners), { 0, } }; /* </settings checks> */ struct service_settings managesieve_login_settings_service_settings = { .name = "managesieve-login", .protocol = "sieve", .type = "login", .executable = "managesieve-login", .user = "$default_login_user", .group = "", .privileged_group = "", .extra_groups = "", .chroot = "login", .drop_priv_before_exec = FALSE, .process_min_avail = 0, .process_limit = 0, .client_limit = 0, .service_count = 1, .vsz_limit = 64, .unix_listeners = ARRAY_INIT, .fifo_listeners = ARRAY_INIT, .inet_listeners = { { &managesieve_login_inet_listeners_buf, sizeof(managesieve_login_inet_listeners[0]) } } }; #undef DEF #define DEF(type, name) \ { type, #name, offsetof(struct managesieve_login_settings, name), NULL } static const struct setting_define managesieve_login_setting_defines[] = { DEF(SET_STR, managesieve_implementation_string), DEF(SET_STR, managesieve_sieve_capability), DEF(SET_STR, managesieve_notify_capability), SETTING_DEFINE_LIST_END }; static const struct managesieve_login_settings managesieve_login_default_settings = { .managesieve_implementation_string = PACKAGE_NAME, .managesieve_sieve_capability = "", .managesieve_notify_capability = "" }; static const struct setting_parser_info *managesieve_login_setting_dependencies[] = { &login_setting_parser_info, NULL }; static const struct setting_parser_info managesieve_login_setting_parser_info = { .module_name = "managesieve-login", .defines = managesieve_login_setting_defines, .defaults = &managesieve_login_default_settings, .type_offset = (size_t)-1, .struct_size = sizeof(struct managesieve_login_settings), .parent_offset = (size_t)-1, .parent = NULL, /* Only compiled in the doveconf plugin */ #ifdef _CONFIG_PLUGIN // .check_func = managesieve_login_settings_verify, #endif .dependencies = managesieve_login_setting_dependencies }; const struct setting_parser_info *managesieve_login_settings_set_roots[] = { &login_setting_parser_info, &managesieve_login_setting_parser_info, NULL }; /* * Dynamic ManageSieve capability determination * Only compiled in the doveconf plugin */ #ifdef _CONFIG_PLUGIN typedef enum { CAP_SIEVE, CAP_NOTIFY } capability_type_t; static char *capability_sieve = NULL; static char *capability_notify = NULL; void managesieve_login_settings_deinit(void) { if ( capability_sieve != NULL ) i_free(capability_sieve); if ( capability_notify != NULL ) i_free(capability_notify); } static void capability_store(capability_type_t cap_type, const char *value) { switch ( cap_type ) { case CAP_SIEVE: capability_sieve = i_strdup(value); break; case CAP_NOTIFY: capability_notify = i_strdup(value); break; } } static void capability_parse(const char *cap_string) { capability_type_t cap_type = CAP_SIEVE; const char *p = cap_string; string_t *part = t_str_new(256); if ( cap_string == NULL || *cap_string == '\0' ) { i_warning("managesieve-login: capability string is empty."); return; } while ( *p != '\0' ) { if ( *p == '\\' ) { p++; if ( *p != '\0' ) { str_append_c(part, *p); p++; } else break; } else if ( *p == ':' ) { if ( strcasecmp(str_c(part), "SIEVE") == 0 ) cap_type = CAP_SIEVE; else if ( strcasecmp(str_c(part), "NOTIFY") == 0 ) cap_type = CAP_NOTIFY; else i_warning("managesieve-login: unknown capability '%s' listed in " "capability string (ignored).", str_c(part)); str_truncate(part, 0); } else if ( *p == ',' ) { capability_store(cap_type, str_c(part)); str_truncate(part, 0); } else { /* Append character, but omit leading spaces */ if ( str_len(part) > 0 || *p != ' ' ) str_append_c(part, *p); } p++; } if ( str_len(part) > 0 ) { capability_store(cap_type, str_c(part)); } } static bool capability_dump(void) { char buf[4096]; int fd[2], status; ssize_t ret; unsigned int pos; pid_t pid; if ( pipe(fd) < 0 ) { i_error("managesieve-login: dump-capability pipe() failed: %m"); return FALSE; } fd_close_on_exec(fd[0], TRUE); fd_close_on_exec(fd[1], TRUE); if ( (pid = fork()) == (pid_t)-1 ) { (void)close(fd[0]); (void)close(fd[1]); i_error("managesieve-login: dump-capability fork() failed: %m"); return FALSE; } if ( pid == 0 ) { const char *argv[2]; /* Child */ (void)close(fd[0]); if (dup2(fd[1], STDOUT_FILENO) < 0) i_fatal("managesieve-login: dump-capability dup2() failed: %m"); env_put("DUMP_CAPABILITY=1"); argv[0] = PKG_LIBEXECDIR"/managesieve"; /* BAD */ argv[1] = NULL; execv_const(argv[0], argv); i_fatal("managesieve-login: dump-capability execv(%s) failed: %m", argv[0]); } (void)close(fd[1]); alarm(5); if (wait(&status) == -1) { i_error("managesieve-login: dump-capability failed: process %d got stuck", (int)pid); return FALSE; } alarm(0); if (status != 0) { (void)close(fd[0]); if (WIFSIGNALED(status)) { i_error("managesieve-login: dump-capability process " "killed with signal %d", WTERMSIG(status)); } else { i_error("managesieve-login: dump-capability process returned %d", WIFEXITED(status) ? WEXITSTATUS(status) : status); } return FALSE; } pos = 0; while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0) pos += ret; if (ret < 0) { i_error("managesieve-login: read(dump-capability process) failed: %m"); (void)close(fd[0]); return FALSE; } (void)close(fd[0]); if (pos == 0 || buf[pos-1] != '\n') { i_error("managesieve-login: dump-capability: Couldn't read capability " "(got %u bytes)", pos); return FALSE; } buf[pos-1] = '\0'; capability_parse(buf); return TRUE; } /* <settings checks> */ static bool managesieve_login_settings_verify (void *_set, pool_t pool ATTR_UNUSED, const char **error_r ATTR_UNUSED) { struct managesieve_login_settings *set = _set; if ( capability_sieve == NULL ) { if ( !capability_dump() ) { capability_sieve = ""; } } if ( *set->managesieve_sieve_capability == '\0' && capability_sieve != NULL ) set->managesieve_sieve_capability = capability_sieve; if ( *set->managesieve_notify_capability == '\0' && capability_notify != NULL ) set->managesieve_notify_capability = capability_notify; return TRUE; } /* </settings checks> */ #endif /* _CONFIG_PLUGIN */