/* This file is part of Mailfromd. Copyright (C) 2006, 2007, 2008 Sergey Poznyakoff This program 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. This program 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 this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FIXME */ #include #include #include #include #include #include #include "inttostr.h" #include "libmf.h" int smap_reload; unsigned int smap_timeout; int smap_transcript; struct mf_privs smap_privs; char *syslog_tag; char *negative_reply; char *positive_reply; char * expand_reply_text (const char *arg, const char *key, struct mu_auth_data *auth) { int rc; mu_vartab_t vtab; char *reply = NULL; char buf[INT_BUFSIZE_BOUND (uintmax_t)]; if (!arg) return NULL; mu_vartab_create (&vtab); mu_vartab_define (vtab, "key", key, 0); mu_vartab_define (vtab, MU_AUTH_NAME, auth ? auth->name : "", 0); mu_vartab_define (vtab, MU_AUTH_PASSWD, auth ? auth->passwd : "", 0); mu_vartab_define (vtab, MU_AUTH_UID, auth ? umaxtostr (auth->uid, buf) : "-1", 0); mu_vartab_define (vtab, MU_AUTH_GID, auth ? umaxtostr (auth->gid, buf) : "-1", 0); mu_vartab_define (vtab, MU_AUTH_GECOS, auth ? auth->gecos : "", 0); mu_vartab_define (vtab, MU_AUTH_DIR, auth ? auth->dir : "", 0); mu_vartab_define (vtab, MU_AUTH_SHELL, auth ? auth->shell : "", 0); mu_vartab_define (vtab, MU_AUTH_MAILBOX, auth ? auth->mailbox : "", 0); mu_vartab_define (vtab, MU_AUTH_QUOTA, auth ? umaxtostr (auth->quota, buf) : "NONE", 0); rc = mu_vartab_expand (vtab, arg, &reply); if (rc) mu_error (_("cannot expand string `%s': %s"), arg, mu_strerror (rc)); mu_vartab_destroy (&vtab); return reply; } int read_delim (FILE *fp, int delim, char *buf, size_t bufsize) { int c; int len = 0; alarm (smap_timeout); bufsize--; while (len < bufsize && ((c = fgetc (fp))) != delim) { if (c == EOF) { if (len) { if (smap_transcript && len) mu_diag_output (MU_DIAG_INFO, "recv: %.*s:", len, buf); mu_error (_("unexpected end of file on input")); exit (1); } return 0; } buf[len++] = c; } alarm (0); buf[len] = 0; if (smap_transcript) mu_diag_output (MU_DIAG_INFO, "recv: %s:", buf); return len; } void smap_reply (FILE *fp, const char *code, const char *result) { size_t len = strlen (code); if (result) len += 1 + strlen (result); if (result) { if (smap_transcript) mu_diag_output (MU_DIAG_INFO, "send: %lu:%s %s,", (unsigned long) len, code, result); fprintf (fp, "%lu:%s %s,", (unsigned long) len, code, result); } else { if (smap_transcript) mu_diag_output (MU_DIAG_INFO, "send: %lu:%s,", (unsigned long) len, code); fprintf (fp, "%lu:%s,", (unsigned long) len, code); } } int smap_loop (FILE *in, FILE *out) { char buf[80]; size_t len; char *key, *p; struct mu_auth_data *auth; mf_epriv_setup (NULL); mf_priv_setup (&smap_privs); setvbuf (in, NULL, _IONBF, 0); setvbuf (out, NULL, _IONBF, 0); /* Read input: */ while (read_delim (in, ':', buf, sizeof buf)) { char *reply_txt = NULL; len = strtoul (buf, &p, 10); if (*p != 0) { mu_error (_("protocol error: expected packet length, but found %s"), buf); exit (1); } if (len > sizeof buf - 1) { mu_error (_("protocol error: packet length too big")); exit (1); } if (fread (buf, 1, len, in) != len) { mu_error (_("protocol error: short read")); exit (1); } buf[len] = 0; if (smap_transcript) mu_diag_output (MU_DIAG_INFO, "recv: %s,", buf); if (getc (in) != ',') { mu_error (_("protocol error: missing terminating comma")); exit (1); } buf[len] = 0; key = strchr (buf, ' '); if (!key) { mu_error (_("protocol error: missing map name")); exit (1); } key++; len = strlen (key); if (key[len-1] == '*') key[len-1] = 0; auth = mu_get_auth_by_name (key); /* Reply: */ if (!auth) { reply_txt = expand_reply_text (negative_reply, key, NULL); smap_reply (out, "NOTFOUND", reply_txt); } else { reply_txt = expand_reply_text (positive_reply, key, auth); smap_reply (out, "OK", reply_txt); mu_auth_data_free (auth); } free (reply_txt); } /* Cleanup and exit */ fclose (in); fclose (out); return 0; } int smap_connection (int fd, struct sockaddr *sa, int salen, void *data, mu_ip_server_t srv, time_t timeout, int transcript) { smap_timeout = timeout; smap_transcript = transcript; smap_loop (fdopen (fd, "r"), fdopen (fd, "w")); return 0; } /* Command line and configuration */ const char *program_version = "smap (" PACKAGE_STRING ")"; static char doc[] = N_("General-purpose remote map for MeTA1"); static char args_doc[] = ""; #define OPTION_GECOS 256 #define OPTION_POSITIVE_REPLY 257 #define OPTION_NEGATIVE_REPLY 258 #define OPTION_NEGATIVE_OK 259 #define OPTION_FOREGROUND 260 static struct argp_option options[] = { #define GRP 0 { "foreground", OPTION_FOREGROUND, 0, 0, N_("Remain in foreground."), GRP+1}, { "inetd", 'i', 0, 0, N_("Run in inetd mode"), GRP+1}, { "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL, N_("Runs in daemon mode with a maximum of NUMBER children"), GRP+1 }, #undef GRP #define GRP 10 { "user", 'U', "NAME", 0, N_("Run as user NAME"), GRP+1 }, { "gecos", OPTION_GECOS, NULL, 0, N_("Same as '--positive-reply=${gecos}'"), GRP+1 }, /* FIXME-MU: */ { "log-tag", 'L', N_("STRING"), 0, N_("Set syslog tag"), GRP+1 }, { "positive-reply", OPTION_POSITIVE_REPLY, N_("STRING"), 0, N_("Set positive reply text"), GRP+1 }, { "negative-reply", OPTION_NEGATIVE_REPLY, N_("STRING"), 0, N_("Set negative reply text"), GRP+1 }, #undef GRP { NULL } }; static error_t parse_opt (int key, char *arg, struct argp_state *state); static struct argp argp = { options, parse_opt, args_doc, doc, NULL, NULL, NULL }; static const char *argp_capa[] = { "auth", "common", "debug", "license", "logging", NULL }; static error_t parse_opt (int key, char *arg, struct argp_state *state) { static struct mu_argp_node_list lst; switch (key) { case 'd': mu_argp_node_list_new (&lst, "mode", "daemon"); if (arg) mu_argp_node_list_new (&lst, "max-children", arg); break; case 'i': mu_argp_node_list_new (&lst, "mode", "inetd"); break; case OPTION_FOREGROUND: mu_argp_node_list_new (&lst, "foreground", "yes"); break; case OPTION_NEGATIVE_REPLY: negative_reply = arg; break; case OPTION_POSITIVE_REPLY: positive_reply = arg; break; case 'L': syslog_tag = arg; break; case 'U': mu_argp_node_list_new (&lst, "user", arg); break; case OPTION_GECOS: positive_reply = "${gecos}"; break; default: return ARGP_ERR_UNKNOWN; case ARGP_KEY_INIT: mu_argp_node_list_init (&lst); break; case ARGP_KEY_FINI: mu_argp_node_list_finish (&lst, NULL, NULL); break; case ARGP_KEY_ERROR: exit (EX_USAGE); } return 0; } static struct mu_cfg_param cfg_param[] = { { "user", mu_cfg_string, &smap_privs.user, 0, NULL, N_("Run with this user privileges.") }, { "group", MU_CFG_LIST_OF(mu_cfg_string), &smap_privs.groups, 0, NULL, N_("Retain supplementary group.") }, { "allgroups", mu_cfg_bool, &smap_privs.allgroups, 0, NULL, N_("Retain all supplementary groups of which user is a member.") }, { "positive-reply", mu_cfg_string, &positive_reply, 0, NULL, N_("Set positive reply text.") }, { "negative-reply", mu_cfg_string, &negative_reply, 0, NULL, N_("Set negative reply text.") }, { ".server", mu_cfg_section, NULL, 0, NULL, N_("Server configuration.") }, { NULL } }; void logging_setup () { mu_debug_t debug; if (syslog_tag) mu_log_tag = syslog_tag; openlog (MU_LOG_TAG (), LOG_PID, mu_log_facility); mu_diag_get_debug (&debug); mu_debug_set_print (debug, mu_diag_syslog_printer, NULL); mu_debug_default_printer = mu_debug_syslog_printer; } static RETSIGTYPE smap_hup (int sig) { mu_m_server_stop (1); smap_reload = 1; } static void version (FILE *stream, struct argp_state *state) { mailfromd_version ("smap", stream); } int main (int argc, char **argv) { int status; mu_m_server_t server; mf_init_nls (); MU_AUTH_REGISTER_ALL_MODULES (); mu_acl_cfg_init (); mu_m_server_cfg_init (); mu_m_server_create (&server, program_version); mu_m_server_set_conn (server, smap_connection); mu_m_server_set_mode (server, MODE_INTERACTIVE); mu_m_server_set_max_children (server, 20); /* FIXME: mu_m_server_set_pidfile (); */ /* FIXME: mu_m_server_set_default_port (server, ??); */ mu_m_server_set_timeout (server, 600); mu_argp_init (program_version, "<" PACKAGE_BUGREPORT ">"); argp_program_version_hook = version; if (mu_app_init (&argp, argp_capa, cfg_param, argc, argv, 0, NULL, server)) exit (1); logging_setup (); if (mu_m_server_mode (server) == MODE_DAEMON) { if (argv[0][0] != '/') mu_diag_output (MU_DIAG_NOTICE, _("Program name is not absolute; reloading will not " "be possible")); else { sigset_t set; mu_m_server_get_sigset (server, &set); sigdelset (&set, SIGHUP); mu_m_server_set_sigset (server, &set); signal (SIGHUP, smap_hup); } mf_epriv_setup (&smap_privs); mu_m_server_begin (server); status = mu_m_server_run (server); mu_m_server_end (server); mu_m_server_destroy (&server); mf_epriv_setup (NULL); if (smap_reload) { mu_diag_output (MU_DIAG_NOTICE, _("Restarting")); execvp (argv[0], argv); } } else status = smap_loop (stdin, stdout); exit (status == 0 ? 0 : EX_SOFTWARE); } void xalloc_die () { mu_error ("not enough memory"); abort (); } /* Local Variables: c-file-style: "gnu" End: */ /* EOF */