-
Stephan Bosch authoredStephan Bosch authored
managesieve-login-settings.c 7.07 KiB
/* 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 */