/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999-2021 Free Software Foundation, Inc. GNU Mailutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Mailutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Mailutils. If not, see . */ #include "imap4d.h" #include #include #include #include "tcpwrap.h" mu_m_server_t server; unsigned int idle_timeout = 1800; int imap4d_transcript; mu_mailbox_t mbox; /* Current mailbox */ char *real_homedir; /* Homedir as returned by user database */ int state = STATE_NONAUTH; /* Current IMAP4 state */ struct mu_auth_data *auth_data; int login_disabled; /* Disable LOGIN command */ int create_home_dir; /* Create home directory if it does not exist */ mu_list_t user_retain_groups; int home_dir_mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; /* Saved command line. */ int imap4d_argc; char **imap4d_argv; enum imap4d_preauth preauth_mode; char *preauth_program; int preauth_only; int ident_port; char *ident_keyfile; int ident_encrypt_only; int test_mode; const char *program_version = "imap4d (" PACKAGE_STRING ")"; static void set_foreground (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { mu_m_server_set_foreground (server, 1); } static void set_inetd_mode (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { mu_m_server_set_mode (server, MODE_INTERACTIVE); } static void set_daemon_mode (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { mu_m_server_set_mode (server, MODE_DAEMON); if (arg) { size_t max_children; char *errmsg; int rc = mu_str_to_c (arg, mu_c_size, &max_children, &errmsg); if (rc) { mu_parseopt_error (po, _("%s: bad argument"), arg); exit (po->po_exit_error); } mu_m_server_set_max_children (server, max_children); } } static void set_preauth (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { preauth_mode = preauth_stdio; } static struct mu_option imap4d_options[] = { { "foreground", 0, NULL, MU_OPTION_DEFAULT, N_("remain in foreground"), mu_c_bool, NULL, set_foreground }, { "inetd", 'i', NULL, MU_OPTION_DEFAULT, N_("run in inetd mode"), mu_c_bool, NULL, set_inetd_mode }, { "daemon", 'd', N_("NUMBER"), MU_OPTION_ARG_OPTIONAL, N_("runs in daemon mode with a maximum of NUMBER children"), mu_c_string, NULL, set_daemon_mode }, { "test", 0, NULL, MU_OPTION_DEFAULT, N_("run in test mode"), mu_c_bool, &test_mode }, { "preauth", 0, NULL, MU_OPTION_DEFAULT, N_("start in preauth mode"), mu_c_string, NULL, set_preauth }, MU_OPTION_END }, *options[] = { imap4d_options, NULL }; static char *capa[] = { "auth", "debug", "mailbox", "locking", "logging", NULL }; static int cb_mode (void *data, mu_config_value_t *val) { char *p; if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; home_dir_mode = strtoul (val->v.string, &p, 8); if (p[0] || (home_dir_mode & ~0777)) mu_error (_("invalid mode specification: %s"), val->v.string); return 0; } int parse_preauth_scheme (const char *scheme, mu_url_t url) { int rc = 0; if (strcmp (scheme, "stdio") == 0) preauth_mode = preauth_stdio; else if (strcmp (scheme, "prog") == 0) { char *path; rc = mu_url_aget_path (url, &path); if (rc) { mu_error (_("URL error: cannot get path: %s"), mu_strerror (rc)); return 1; } preauth_program = path; preauth_mode = preauth_prog; } else if (strcmp (scheme, "ident") == 0) { struct servent *sp; unsigned n; if (url && mu_url_get_port (url, &n) == 0) ident_port = (short) n; else if ((sp = getservbyname ("auth", "tcp"))) ident_port = ntohs (sp->s_port); else ident_port = 113; preauth_mode = preauth_ident; } else { mu_error (_("unknown preauth scheme")); rc = 1; } return rc; } /* preauth prog:///usr/sbin/progname preauth ident[://:port] preauth stdio */ static int cb_preauth (void *data, mu_config_value_t *val) { if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; if (strcmp (val->v.string, "stdio") == 0) preauth_mode = preauth_stdio; else if (strcmp (val->v.string, "ident") == 0) return parse_preauth_scheme (val->v.string, NULL); else if (val->v.string[0] == '/') { preauth_program = mu_strdup (val->v.string); preauth_mode = preauth_prog; } else { mu_url_t url; char *scheme; int rc = mu_url_create (&url, val->v.string); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_url_create", val->v.string, rc); return 1; } rc = mu_url_aget_scheme (url, &scheme); if (rc) { mu_url_destroy (&url); mu_error (_("URL error: %s"), mu_strerror (rc)); return 1; } rc = parse_preauth_scheme (scheme, url); mu_url_destroy (&url); free (scheme); return rc; } return 0; } static int cb_mailbox_mode (void *data, mu_config_value_t *val) { const char *p; if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; if (mu_parse_stream_perm_string ((int *)data, val->v.string, &p)) mu_error (_("invalid mode string near %s"), p); return 0; } static int cb2_group (const char *gname, void *data) { mu_list_t list = data; struct group *group; group = getgrnam (gname); if (!group) mu_error (_("unknown group: %s"), gname); else mu_list_append (list, (void*) (intptr_t) group->gr_gid); return 0; } static int cb_group (void *data, mu_config_value_t *arg) { mu_list_t *plist = data; if (!*plist) mu_list_create (plist); return mu_cfg_string_value_cb (arg, cb2_group, *plist); } static int cb_tls (void *data, mu_config_value_t *val) { int *res = data; static struct mu_kwd tls_kwd[] = { { "no", tls_no }, { "false", tls_no }, { "off", tls_no }, { "0", tls_no }, { "ondemand", tls_ondemand }, { "stls", tls_ondemand }, { "required", tls_required }, { "connection", tls_connection }, /* For compatibility with prior versions: */ { "yes", tls_connection }, { "true", tls_connection }, { "on", tls_connection }, { "1", tls_connection }, { NULL } }; if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; if (mu_kwd_xlat_name (tls_kwd, val->v.string, res)) mu_error (_("not a valid tls keyword: %s"), val->v.string); return 0; } static mu_list_t auth_deny_user_list, auth_allow_user_list; static mu_list_t auth_deny_group_list, auth_allow_group_list; static int check_user_groups (void *item, void *data) { char *gname = item; struct group *gp; char **p; gp = getgrnam (gname); if (!gp) return 0; if (gp->gr_gid == auth_data->gid) return MU_ERR_USER0; for (p = gp->gr_mem; *p; p++) if (strcmp (*p, auth_data->name) == 0) return MU_ERR_USER0; return 0; } static int imap_check_group_list (mu_list_t l) { int rc = mu_list_foreach (l, check_user_groups, NULL); if (rc == MU_ERR_USER0) return 0; else if (rc == 0) return MU_ERR_NOENT; return rc; } static int cb_prefix_delim (void *data, mu_config_value_t *val) { if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; if (val->v.string[0] == 0) mu_error (_("delimiter cannot be empty")); else if (val->v.string[1] != 0) mu_error (_("delimiter must be a single character")); else *(int*) data = val->v.string[0]; return 0; } static int cb_prefix_scheme (void *data, mu_config_value_t *val) { struct namespace_prefix *pfx = data; char *scheme; mu_record_t rec; int rc; int (*mbx) (mu_mailbox_t); if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; scheme = mu_strdup (val->v.string); rc = mu_registrar_lookup_scheme (scheme, &rec); if (rc == MU_ERR_NOENT) { mu_error (_("unknown mailbox type")); return 1; } else if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_registrar_lookup_scheme", scheme, rc); return 1; } rc = mu_record_get_mailbox (rec, &mbx); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_record_get_mailbox", scheme, rc); return 1; } if (!mbx || !mu_record_is_local (rec)) { mu_error (_("not a local mailbox type")); return 1; } pfx->scheme = scheme; pfx->record = rec; return 0; } static struct mu_cfg_param prefix_param[] = { { "directory", mu_c_string, NULL, mu_offsetof (struct namespace_prefix, dir), NULL, N_("Directory in the file system") }, { "delimiter", mu_cfg_callback, NULL, mu_offsetof (struct namespace_prefix, delim), cb_prefix_delim, N_("Hierarchy delimiter character"), N_("arg: character") }, { "mailbox-type", mu_cfg_callback, NULL, 0, cb_prefix_scheme, N_("Type of mailboxes residing under this prefix"), N_("type: string") }, { NULL } }; static struct mu_cfg_param namespace_param[] = { { "mailbox-mode", mu_cfg_callback, NULL, mu_offsetof (struct namespace, mode), cb_mailbox_mode, N_("File mode for newly created mailboxes in this namespace"), N_("mode: g(+|=)[wr]+,o(+|=)[wr]+") }, { "prefix", mu_cfg_section }, { NULL } }; static int prefix_section_parser (enum mu_cfg_section_stage stage, const mu_cfg_node_t *node, const char *section_label, void **section_data, void *call_data, mu_cfg_tree_t *tree) { struct namespace_prefix *pfx; switch (stage) { case mu_cfg_section_start: { struct namespace *nspace = *section_data; if (node->label == NULL || node->label->type != MU_CFG_STRING) return 1; pfx = mu_zalloc (sizeof (*pfx)); pfx->prefix = mu_strdup (node->label->v.string); mu_list_append (nspace->prefixes, pfx); *section_data = pfx; } break; case mu_cfg_section_end: pfx = *section_data; if (!pfx->delim) pfx->delim = '/'; if (!pfx->dir) { if (pfx->prefix) pfx->dir = mu_strdup (pfx->prefix); else { mu_error (_("bad prefix definition")); return 1; } } else if (!pfx->prefix) { pfx->prefix = namespace_encode_delim (pfx, pfx->dir); } } return 0; } static int namespace_section_parser (enum mu_cfg_section_stage stage, const mu_cfg_node_t *node, const char *section_label, void **section_data, void *call_data, mu_cfg_tree_t *tree) { if (stage == mu_cfg_section_start) { struct namespace *ns; if (node->label == NULL || node->label->type != MU_CFG_STRING) return 1; ns = namespace_lookup (node->label->v.string); if (!ns) { mu_error (_("unknown namespace")); return 0; } *section_data = ns; } return 0; } static void namespace_cfg_init (void) { struct mu_cfg_section *section; if (mu_create_canned_section ("prefix", §ion)) abort (); section->docstring = N_("Define a single prefix"); section->label = N_("string"); mu_cfg_section_add_params (section, prefix_param); section->parser = prefix_section_parser; if (mu_create_canned_section ("namespace", §ion)) abort (); section->docstring = N_("Define a namespace"); section->label = "personal | other | shared"; section->parser = namespace_section_parser; mu_cfg_section_add_params (section, namespace_param); } static struct mu_cfg_param imap4d_srv_param[] = { { "tls-mode", mu_cfg_callback, NULL, mu_offsetof (struct imap4d_srv_config, tls_mode), cb_tls, N_("Kind of TLS encryption to use for this server"), /* TRANSLATORS: translate only arg:, the rest are keywords */ N_("arg: false|true|ondemand|stls|required|connection") }, { "tls", mu_cfg_section, NULL, mu_offsetof (struct imap4d_srv_config, tls_conf) }, { NULL } }; static struct mu_cfg_param imap4d_cfg_param[] = { { "allow-users", MU_CFG_LIST_OF(mu_c_string), &auth_allow_user_list, 0, NULL, N_("Allow access to users from this list.") }, { "deny-users", MU_CFG_LIST_OF(mu_c_string), &auth_deny_user_list, 0, NULL, N_("Deny access to users from this list.") }, { "allow-groups", MU_CFG_LIST_OF(mu_c_string), &auth_allow_group_list, 0, NULL, N_("Allow access if the user group is in this list.") }, { "deny-groups", MU_CFG_LIST_OF(mu_c_string), &auth_deny_group_list, 0, NULL, N_("Deny access if the user group is in this list.") }, { "namespace", mu_cfg_section }, { "login-disabled", mu_c_bool, &login_disabled, 0, NULL, N_("Disable LOGIN command.") }, { "create-home-dir", mu_c_bool, &create_home_dir, 0, NULL, N_("If true, create non-existing user home directories.") }, { "home-dir-mode", mu_cfg_callback, NULL, 0, cb_mode, N_("File mode for creating user home directories."), N_("mode: octal") }, { "retain-groups", mu_cfg_callback, &user_retain_groups, 0, cb_group, N_("Retain these supplementary groups when switching to user privileges"), N_("groups: list of string") }, { "tls", mu_cfg_section, &global_tls_conf }, { "tls-mode", mu_cfg_callback, &global_tls_mode, 0, cb_tls, N_("Kind of TLS encryption to use for the inetd server" " and all server blocks that lack the tls-mode statement."), /* TRANSLATORS: words to the right of : are keywords - do not translate */ N_("arg: false|true|ondemand|stls|requred|connection") }, { "preauth", mu_cfg_callback, NULL, 0, cb_preauth, N_("Configure PREAUTH mode. is one of:\n" " prog:///\n" " ident[://:]\n" " stdio"), N_("mode: value") }, { "preauth-only", mu_c_bool, &preauth_only, 0, NULL, N_("Use only preauth mode. If unable to setup it, disconnect " "immediately.") }, { "ident-keyfile", mu_c_string, &ident_keyfile, 0, NULL, N_("Name of DES keyfile for decoding encrypted ident responses.") }, { "ident-encrypt-only", mu_c_bool, &ident_encrypt_only, 0, NULL, N_("Use only encrypted ident responses.") }, { "id-fields", MU_CFG_LIST_OF(mu_c_string), &imap4d_id_list, 0, NULL, N_("List of fields to return in response to ID command.") }, { "mandatory-locking", mu_cfg_section }, { ".server", mu_cfg_section, NULL, 0, NULL, N_("Server configuration.") }, { "transcript", mu_c_bool, &imap4d_transcript, 0, NULL, N_("Set global transcript mode.") }, TCP_WRAPPERS_CONFIG { NULL } }; struct mu_cli_setup cli = { .optv = options, .cfg = imap4d_cfg_param, .prog_doc = N_("GNU imap4d -- the IMAP4D daemon."), .server = 1 }; int mu_get_user_groups (const char *user, mu_list_t retain, mu_list_t *pgrouplist) { int rc; struct group *gr; mu_list_t list; if (!*pgrouplist) { rc = mu_list_create (pgrouplist); if (rc) { mu_error(_("%s: cannot create list: %s"), "mu_get_user_groups", mu_strerror (rc)); return rc; } } list = *pgrouplist; setgrent (); for (rc = 0; rc == 0 && (gr = getgrent ()); ) { char **p; for (p = gr->gr_mem; *p; p++) if (strcmp (*p, user) == 0) { if (retain && mu_list_locate (retain, (void*) (intptr_t) gr->gr_gid, NULL)) continue; /* FIXME: Avoid duplicating gids */ rc = mu_list_append (list, (void*) (intptr_t) gr->gr_gid); if (rc) mu_error(_("%s: cannot append to list: %s"), "mu_get_user_groups", mu_strerror (rc)); break; } } endgrent (); return rc; } int imap4d_session_setup0 () { if (auth_deny_user_list && mu_list_locate (auth_deny_user_list, auth_data->name, NULL) == 0) { mu_error (_("%s is in deny-users, rejecting"), auth_data->name); return 1; } if (auth_allow_user_list && mu_list_locate (auth_allow_user_list, auth_data->name, NULL)) { mu_error (_("%s is not in allow-users, rejecting"), auth_data->name); return 1; } if (auth_deny_group_list && imap_check_group_list (auth_deny_group_list) == 0) { mu_error (_("%s is in deny-groups, rejecting"), auth_data->name); return 1; } if (auth_allow_group_list && imap_check_group_list (auth_allow_group_list)) { mu_error (_("%s is not in allow-groups, rejecting"), auth_data->name); return 1; } real_homedir = mu_normalize_path (mu_strdup (auth_data->dir)); if (imap4d_check_home_dir (real_homedir, auth_data->uid, auth_data->gid)) return 1; if (auth_data->change_uid) { struct group *gr; mu_list_t groups = NULL; int rc; uid_t uid; uid = getuid (); if (uid != auth_data->uid) { rc = mu_list_create (&groups); if (rc) { mu_error(_("cannot create list: %s"), mu_strerror (rc)); free (real_homedir); return 1; } mu_list_append (groups, (void*) (intptr_t) auth_data->gid); rc = mu_get_user_groups (auth_data->name, user_retain_groups, &groups); if (rc) { /* FIXME: When mu_get_user_groups goes to the library, add a diag message here */ free (real_homedir); return 1; } gr = getgrnam ("mail"); rc = mu_switch_to_privs (auth_data->uid, gr->gr_gid, groups); mu_list_destroy (&groups); if (rc) { mu_error (_("can't switch to user %s privileges: %s"), auth_data->name, mu_strerror (rc)); free (real_homedir); return 1; } } } util_chdir (real_homedir); mu_diag_output (MU_DIAG_INFO, _("user `%s' logged in (source: %s)"), auth_data->name, auth_data->source); if (auth_data->quota) quota_setup (); return 0; } int imap4d_session_setup (char *username) { auth_data = mu_get_auth_by_name (username); if (auth_data == NULL) { mu_diag_output (MU_DIAG_INFO, _("user `%s' nonexistent"), username); return 1; } return imap4d_session_setup0 (); } void imap4d_child_signal_setup (RETSIGTYPE (*handler) (int signo)) { static int sigtab[] = { SIGPIPE, SIGABRT, SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGALRM }; mu_set_signals (handler, sigtab, MU_ARRAY_SIZE (sigtab)); } static int imap4d_mainloop (int ifd, int ofd, struct imap4d_srv_config *cfg) { imap4d_tokbuf_t tokp; char *text; int signo; struct imap4d_session session; if (!test_mode) test_mode = isatty (ifd); if ((signo = setjmp (child_jmp))) { mu_diag_output (MU_DIAG_CRIT, _("got signal `%s'"), strsignal (signo)); switch (signo) { case SIGTERM: case SIGHUP: signo = ERR_TERMINATE; break; case SIGALRM: signo = ERR_TIMEOUT; break; case SIGPIPE: signo = ERR_NO_OFILE; break; default: signo = ERR_SIGNAL; } imap4d_bye (signo); } else { /* Restore default handling for these signals: */ static int defsigtab[] = { SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSTOP }; mu_set_signals (SIG_DFL, defsigtab, MU_ARRAY_SIZE (defsigtab)); /* Set child-specific signal handlers */ imap4d_child_signal_setup (imap4d_child_signal); } switch (cfg->tls_mode) { case tls_required: imap4d_capability_add (IMAP_CAPA_XTLSREQUIRED); imap4d_capability_add (IMAP_CAPA_STARTTLS); break; case tls_ondemand: imap4d_capability_add (IMAP_CAPA_STARTTLS); break; default: break; } session.tls_mode = cfg->tls_mode; session.tls_conf = &cfg->tls_conf; io_setio (ifd, ofd, cfg->tls_mode == tls_connection ? &cfg->tls_conf : NULL); if (cfg->tls_mode == tls_connection) tls_encryption_on (&session); if (imap4d_preauth_setup (ifd) == 0) { if (test_mode) { mu_diag_output (MU_DIAG_INFO, _("started in test mode")); text = "IMAP4rev1 Test mode"; } else text = "IMAP4rev1"; } else { io_flush (); return 0; } /* Greetings. */ io_untagged_response ((state == STATE_AUTH) ? RESP_PREAUTH : RESP_OK, "%s", text); io_flush (); set_xscript_level ((state == STATE_AUTH) ? MU_XSCRIPT_NORMAL : MU_XSCRIPT_SECURE); tokp = imap4d_tokbuf_init (); while (1) { if (idle_timeout && io_wait_input (idle_timeout) != 1) imap4d_bye (ERR_TIMEOUT); imap4d_readline (tokp); /* check for updates */ imap4d_sync (); util_do_command (&session, tokp); imap4d_sync (); io_flush (); } return 0; } int imap4d_connection (int fd, struct sockaddr *sa, int salen, struct mu_srv_config *pconf, void *data) { struct imap4d_srv_config *cfg = (struct imap4d_srv_config *) pconf; idle_timeout = cfg->m_cfg.timeout; imap4d_transcript = cfg->m_cfg.transcript; imap4d_mainloop (fd, fd, cfg); return 0; } int imap4d_check_home_dir (const char *dir, uid_t uid, gid_t gid) { struct stat st; if (stat (dir, &st)) { if (errno == ENOENT && create_home_dir) { mode_t mode = umask (0); int rc = mkdir (dir, home_dir_mode); umask (mode); if (rc) { mu_error ("Cannot create home directory `%s': %s", dir, mu_strerror (errno)); return 1; } if (chown (dir, uid, gid)) { mu_error ("Cannot set owner for home directory `%s': %s", dir, mu_strerror (errno)); return 1; } } } return 0; } jmp_buf master_jmp; RETSIGTYPE imap4d_master_signal (int signo) { longjmp (master_jmp, signo); } static void imap4d_alloc_die () { imap4d_bye (ERR_NO_MEM); } int main (int argc, char **argv) { struct group *gr; int status = 0; static int sigtab[] = { SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSTOP, SIGPIPE, SIGABRT }; imap4d_argc = argc; imap4d_argv = argv; /* Native Language Support */ MU_APP_INIT_NLS (); state = STATE_NONAUTH; /* Starting state in non-auth. */ MU_AUTH_REGISTER_ALL_MODULES (); /* Register the desired formats. */ mu_register_local_mbox_formats (); imap4d_capability_init (); mu_tcpwrapper_cfg_init (); manlock_cfg_init (); mu_acl_cfg_init (); mu_tls_cfg_init (); namespace_cfg_init (); mu_m_server_create (&server, program_version); mu_m_server_set_config_size (server, sizeof (struct imap4d_srv_config)); mu_m_server_set_conn (server, imap4d_connection); mu_m_server_set_prefork (server, mu_tcp_wrapper_prefork); mu_m_server_set_mode (server, MODE_INTERACTIVE); mu_m_server_set_max_children (server, 20); /* FIXME mu_m_server_set_pidfile (); */ mu_m_server_set_default_port (server, 143); mu_m_server_set_timeout (server, 1800); /* RFC2060: 30 minutes. */ mu_m_server_set_strexit (server, mu_strexit); mu_m_server_cfg_init (server, imap4d_srv_param); mu_alloc_die_hook = imap4d_alloc_die; mu_log_syslog = 1; mu_cli (argc, argv, &cli, capa, server, &argc, &argv); if (argc) { mu_error (_("too many arguments")); exit (EX_USAGE); } namespace_init (); if (test_mode) mu_m_server_set_mode (server, MODE_INTERACTIVE); if (login_disabled) imap4d_capability_add (IMAP_CAPA_LOGINDISABLED); #ifdef USE_LIBPAM if (!mu_pam_service) mu_pam_service = "gnu-imap4d"; #endif if (mu_m_server_mode (server) == MODE_DAEMON) { /* Normal operation: */ /* First we want our group to be mail so we can access the spool. */ errno = 0; gr = getgrnam ("mail"); if (gr == NULL) { if (errno == 0 || errno == ENOENT) { mu_error (_("%s: no such group"), "mail"); exit (EX_CONFIG); } else { mu_diag_funcall (MU_DIAG_ERROR, "getgrnam", "mail", errno); exit (EX_OSERR); } } if (setgid (gr->gr_gid) == -1) { mu_error (_("error setting mail group: %s"), mu_strerror (errno)); //exit (EX_OSERR); } } /* Set the signal handlers. */ if ((status = setjmp (master_jmp))) { int code; mu_diag_output (MU_DIAG_CRIT, _("MASTER: exiting on signal (%s)"), strsignal (status)); switch (status) { case SIGTERM: case SIGHUP: case SIGQUIT: case SIGINT: code = EX_OK; break; default: code = EX_SOFTWARE; break; } exit (code); } mu_set_signals (imap4d_master_signal, sigtab, MU_ARRAY_SIZE (sigtab)); mu_stdstream_strerr_setup (mu_log_syslog ? MU_STRERR_SYSLOG : MU_STRERR_STDERR); umask (S_IROTH | S_IWOTH | S_IXOTH); /* 007 */ /* Check TLS environment, i.e. cert and key files */ mu_m_server_set_preflight (server, starttls_init); /* Actually run the daemon. */ if (mu_m_server_mode (server) == MODE_DAEMON) { if (mu_gsasl_enabled ()) { auth_gssapi_init (); auth_gsasl_init (); } mu_m_server_begin (server); status = mu_m_server_run (server); mu_m_server_end (server); mu_m_server_destroy (&server); } else { struct imap4d_srv_config cfg; memset (&cfg, 0, sizeof cfg); idle_timeout = mu_m_server_timeout (server); if (mu_gsasl_enabled ()) { auth_gssapi_init (); auth_gsasl_init (); } switch (starttls_server_check (&cfg, "")) { case MU_TLS_CONFIG_OK: if (mu_init_tls_libs ()) status = EX_OK; else { mu_error (_("TLS is not configured, but requested in the " "configuration")); exit (EX_CONFIG); } break; case MU_TLS_CONFIG_NULL: break; case MU_TLS_CONFIG_UNSAFE: exit (EX_CONFIG); default: exit (EX_UNAVAILABLE); } /* Make sure we are in the root directory. */ chdir ("/"); status = imap4d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, &cfg); } if (status) mu_error (_("main loop status: %s"), mu_strerror (status)); /* Close the syslog connection and exit. */ closelog (); return status ? EX_SOFTWARE : EX_OK; }