From e6772c4d68849cfdb4547a59bc51cb6dd0acd2c6 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 24 Nov 2009 13:38:52 +0200 Subject: Implement parsing of inetd.conf files. * src/Makefile.am (pies_SOURCES): Add inetd.c. * src/inetd.c: New file. * src/pies.c (inetd_mode): New global. (str_to_socket_type): New function. (_cb_socket_type): Use str_to_socket_type. (_cm_include_meta1): Rename to _cb_include_meta1. (_cb_include_inetd): New function. (pies_keywords): New keyword "include-inetd". (options): New option --inetd. (main): Handle inetd mode. * src/pies.h (str_to_socket_type): New proto. (disable_socket, enable_socket): Fix return type. * src/progman.c (redirect_to_file): Avoid coredump on privs.user == NULL. (open_redirector,env_setup) (progman_start,run_command): Remove unneeded variable. (progman_start): Fix diagnostic message. * src/socket.c (disable_socket, enable_socket): Fix return type. Do nothing if fd < 0. * src/userprivs.c (switch_to_privs): Allow to pass uid==0. --- src/Makefile.am | 1 + src/inetd.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pies.c | 108 +++++++++++++------ src/pies.h | 12 ++- src/progman.c | 11 +- src/socket.c | 8 +- src/userprivs.c | 6 -- 7 files changed, 417 insertions(+), 49 deletions(-) create mode 100644 src/inetd.c diff --git a/src/Makefile.am b/src/Makefile.am index 63043f0..8735607 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,6 +21,7 @@ pies_SOURCES = \ addrfmt.c\ depmap.c\ diag.c\ + inetd.c\ limits.c\ meta.c\ meta1gram.y\ diff --git a/src/inetd.c b/src/inetd.c new file mode 100644 index 0000000..ac40a5a --- /dev/null +++ b/src/inetd.c @@ -0,0 +1,320 @@ +/* This file is part of GNU Pies. + Copyright (C) 2009 Sergey Poznyakoff + + GNU Pies 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 Pies 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 Pies. If not, see . */ + +#include "pies.h" +#include +#include + +#define IFLD_SERVICE 0 /* service name */ +#define IFLD_SOCKET 1 /* socket type */ +#define IFLD_PROTOCOL 2 /* protocol */ +#define IFLD_WAIT 3 /* wait/nowait */ +#define IFLD_USER 4 /* user */ +#define IFLD_SERVER_PATH 5 /* server program path */ +#define IFLD_SERVER_ARGS 6 /* server program arguments */ + +#define IFLD_MIN_COUNT 6 /* Minimum number of fields in entry */ + +/* FIXME: Duplicated in grex-lex.l */ +static void +listel_dispose(const void *el) +{ + free((void*)el); +} + +static int +inetd_conf_file (const char *file) +{ + FILE *fp; + size_t size = 0; + char *buf = NULL; + size_t line_no = 0; + struct wordsplit ws; + char *dfl_address = NULL; + + fp = fopen (file, "r"); + if (!fp) + { + logmsg (LOG_ERR, + _("cannot open configuration file %s: %s"), + file, strerror (errno)); + return 1; + } + + while (getline (&buf, &size, fp) >= 0) + { + char *p; + struct component *comp; + int socket_type; + struct pies_url *url; + size_t max_instances = 0; + int flags = 0; + char *str; + char *user = NULL; + char *group = NULL; + char *address; + char *service; + size_t len; + + if (line_no) + wordsplit_free (&ws); + + line_no++; + for (p = buf; *p && c_isblank (*p); p++) + ; + + if (!p || *p == '\n' || *p == '#') + continue; + + if (wordsplit (p, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_WS | + WRDSF_SQUEEZE_DELIMS)) + { + logmsg (LOG_ERR, "wordsplit: %s", strerror (errno)); + continue; + } + + if (ws.ws_wordc == 1) + { + size_t len = strlen (ws.ws_wordv[IFLD_SERVICE]); + if (len > 0 && ws.ws_wordv[IFLD_SERVICE][len-1] == ':') + { + free (dfl_address); + + if (len == 2 && ws.ws_wordv[IFLD_SERVICE][0] == '*') + dfl_address = NULL; + else + { + dfl_address = xmalloc (len); + memcpy (dfl_address, ws.ws_wordv[IFLD_SERVICE], len-1); + dfl_address[len-1] = 0; + } + continue; + } + } + + if (ws.ws_wordc < IFLD_MIN_COUNT) + { + logmsg (LOG_ERR, "%s:%lu: too few fields", file, line_no); + continue; + } + + /* Parse service */ + str = strchr (ws.ws_wordv[IFLD_SERVICE], ':'); + if (str) + { + *str++ = 0; + address = ws.ws_wordv[IFLD_SERVICE]; + service = str; + } + else + { + address = dfl_address; + service = ws.ws_wordv[IFLD_SERVICE]; + } + + /* Parse socket type */ + if (str_to_socket_type (ws.ws_wordv[IFLD_SOCKET], &socket_type)) + { + logmsg (LOG_ERR, "%s:%lu: %s", + file, line_no, _("bad socket type")); + continue; + } + + /* Create URL from protocol and service fields. */ + str = xasprintf ("inet+%s://%s:%s", + ws.ws_wordv[IFLD_PROTOCOL], + address ? address : "0.0.0.0", + service); + if (pies_url_create (&url, str)) + { + /* FIXME: Better error message */ + logmsg (LOG_ERR, "%s:%lu: %s", + file, line_no, _("invalid socket address")); + continue; + } + + free (str); + + /* Parse wait/nowait field */ + str = strchr (ws.ws_wordv[IFLD_WAIT], '.'); + if (str) + { + size_t n; + + *str++ = 0; + n = strtoul(str, &p, 10); + if (*p) + logmsg (LOG_WARNING, "%s:%lu: invalid number (near %s)", + file, line_no, p); + else + max_instances = n; + } + + if (strcmp (ws.ws_wordv[IFLD_WAIT], "wait") == 0) + flags |= CF_WAIT; + else if (strcmp (ws.ws_wordv[IFLD_WAIT], "nowait")) + { + logmsg (LOG_ERR, "%s:%lu: %s", + file, line_no, _("invalid wait field")); + pies_url_destroy(&url); + continue; + } + + /* Parse user/group */ + len = strcspn (ws.ws_wordv[IFLD_USER], ":."); + if (ws.ws_wordv[IFLD_USER][len]) + { + ws.ws_wordv[IFLD_USER][len] = 0; + group = ws.ws_wordv[IFLD_USER] + len + 1; + } + user = ws.ws_wordv[IFLD_USER]; + + /* Create the component */ + if (address) + { + str = xmalloc (strlen (address) + 1 + strlen (service) + 1); + strcpy (str, address); + strcat (str, ":"); + strcat (str, service); + comp = component_create (str); + free (str); + } + else + comp = component_create (service); + + comp->mode = pies_comp_inetd; + comp->socket_type = socket_type; + comp->socket_url = url; + comp->max_instances = max_instances; + comp->flags = flags; + comp->privs.user = xstrdup (user); /* FIXME: memory leak */ + if (group) + { + comp->privs.groups = + gl_list_create_empty (&gl_linked_list_implementation, + NULL, + NULL, + listel_dispose, + true); + gl_list_add_last (comp->privs.groups, xstrdup (group)); + } + + comp->program = xstrdup (ws.ws_wordv[IFLD_SERVER_PATH]); + + if (ws.ws_wordc > IFLD_MIN_COUNT) + { + size_t i, j; + + comp->argc = ws.ws_wordc - IFLD_MIN_COUNT; + comp->argv = xcalloc (comp->argc + 1, sizeof (comp->argv[0])); + for (i = IFLD_SERVER_ARGS, j = 0; i < ws.ws_wordc; i++, j++) + comp->argv[j] = xstrdup (ws.ws_wordv[i]); + } + else + { + comp->argc = 1; + comp->argv = xcalloc (comp->argc + 1, sizeof (comp->argv[0])); + comp->argv[0] = xstrdup (comp->program); + } + + if (progman_lookup_component (comp->tag) == NULL) + register_prog (comp); + } + + if (line_no) + wordsplit_free (&ws); + free (dfl_address); + free (buf); + fclose (fp); + return 0; +} + +#ifndef S_ISLNK +# define S_ISLNK(m) 0 +#endif +#define NAME_INIT_ALLOC 16 + +static int +inetd_conf_dir (const char *name) +{ + DIR *dir; + struct dirent *ent; + int errs = 0; + char *namebuf; + size_t namebufsize; + size_t namelen; + + dir = opendir (name); + if (!dir) + { + logmsg (LOG_ERR, _("cannot open directory %s: %s"), + name, strerror (errno)); + return 1; + } + + namelen = strlen (name); + namebufsize = namelen; + if (name[namelen-1] != '/') + namebufsize++; + namebufsize += NAME_INIT_ALLOC; + namebuf = xmalloc (namebufsize); + memcpy (namebuf, name, namelen); + if (name[namelen-1] != '/') + namebuf[namelen++] = 0; + + while ((ent = readdir (dir))) + { + struct stat st; + + if (stat (ent->d_name, &st)) + { + logmsg (LOG_ERR, _("cannot stat %s/%s: %s"), + name, ent->d_name, strerror (errno)); + errs |= 1; + } + else if (S_ISREG (st.st_mode) || S_ISLNK (st.st_mode)) + { + size_t len = strlen (ent->d_name); + if (namelen + len >= namebufsize) + { + namebufsize = namelen + len + 1; + namebuf = xrealloc (namebuf, namebufsize); + } + strcpy (namebuf + namelen, ent->d_name); + errs |= inetd_conf_file (namebuf); + } + } + free (namebuf); + closedir (dir); + return errs; +} + +int +inetd_parse_conf (const char *file) +{ + struct stat st; + + if (stat (file, &st)) + { + logmsg (LOG_ERR, _("cannot stat %s: %s"), file, strerror (errno)); + return 1; + } + if (S_ISDIR (st.st_mode)) + return inetd_conf_dir (file); + + return inetd_conf_file (file); +} diff --git a/src/pies.c b/src/pies.c index 2bf127c..fd83e04 100644 --- a/src/pies.c +++ b/src/pies.c @@ -28,6 +28,7 @@ char *log_tag; struct pies_privs pies_privs; int foreground; int command; +int inetd_mode; char *pidfile = LOCALSTATEDIR "/pies.pid"; char *ctlfile = LOCALSTATEDIR "/pies.ctl"; char *statfile = LOCALSTATEDIR "/pies.stat"; @@ -712,33 +713,35 @@ _cb_url (enum grecs_callback_command cmd, return 0; } +int +str_to_socket_type (const char *str, int *pret) +{ + if (strcmp (str, "stream") == 0) + *pret = SOCK_STREAM; + else if (strcmp (str, "dgram") == 0) + *pret = SOCK_DGRAM; + else if (strcmp (str, "rdm") == 0) + *pret = SOCK_RDM; + else if (strcmp (str, "seqpacket") == 0) + *pret = SOCK_SEQPACKET; + else if (strcmp (str, "raw") == 0) + *pret = SOCK_RAW; + else + return 1; + return 0; +} + static int _cb_socket_type (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { - int t; - if (assert_scalar_stmt (locus, cmd) || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) return 1; - if (strcmp (value->v.string, "stream") == 0) - t = SOCK_STREAM; - else if (strcmp (value->v.string, "dgram") == 0) - t = SOCK_DGRAM; - else if (strcmp (value->v.string, "rdm") == 0) - t = SOCK_RDM; - else if (strcmp (value->v.string, "seqpacket") == 0) - t = SOCK_SEQPACKET; - else if (strcmp (value->v.string, "raw") == 0) - t = SOCK_RAW; - else - { - grecs_error (locus, 0, _("bad socket type")); - return 0; - } - *(int*)varptr = t; + if (str_to_socket_type (value->v.string, varptr)) + grecs_error (locus, 0, _("bad socket type")); return 0; } @@ -1207,7 +1210,7 @@ static struct grecs_keyword syslog_kw[] = { struct component default_component; static int -_cm_include_meta1 (enum grecs_callback_command cmd, +_cb_include_meta1 (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { @@ -1217,6 +1220,16 @@ _cm_include_meta1 (enum grecs_callback_command cmd, return 0; } +static int +_cb_include_inetd (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + return inetd_parse_conf (value->v.string); +} + struct grecs_keyword pies_keywords[] = { {"component", N_(""), @@ -1303,11 +1316,16 @@ struct grecs_keyword pies_keywords[] = { N_("Define an ACL."), grecs_type_section, NULL, 0, defacl_section_parser, NULL, acl_keywords}, + {"include-inetd", + N_("file-or-dir: string"), + N_("Include inetd configuration file or directory"), + grecs_type_string, NULL, 0, + _cb_include_inetd }, {"include-meta1", N_("file: string"), N_("Include components from the specified MeTA1 configuration file."), grecs_type_string, NULL, 0, - _cm_include_meta1, + _cb_include_meta1, }, {"meta1-queue-dir", NULL, @@ -1391,6 +1409,8 @@ static struct argp_option options[] = { N_("parse configuration file and exit"), GRP + 1}, {NULL, 'E', NULL, 0, N_("preprocess config and exit"), GRP + 1}, + {"inetd", 'i', NULL, 0, + N_("run in inetd mode"), GRP + 1}, {"config-file", 'c', N_("FILE"), 0, N_("use FILE instead of the default configuration"), GRP + 1}, {"config-help", OPT_CONFIG_HELP, NULL, 0, @@ -1453,6 +1473,10 @@ parse_opt (int key, char *arg, struct argp_state *state) preprocess_only = 1; break; + case 'i': + inetd_mode = 1; + break; + case 't': log_to_stderr_only = 1; lint_mode = 1; @@ -1938,20 +1962,44 @@ main (int argc, char **argv) if (argp_parse (&argp, argc, argv, 0, &index, NULL)) exit (EX_USAGE); - if (!DEFAULT_PREPROCESSOR) - grecs_preprocessor = NULL; - else + if (inetd_mode) { - grecs_preprocessor = obstack_finish (&pp_stk); - free (pp_qopt); + if (index == argc) + { + if (inetd_parse_conf (SYSCONFDIR "/inetd.conf")) + exit (EX_CONFIG); + } + else + { + int i; + + for (i = index; i < argc; i++) + { + if (inetd_parse_conf (argv[i])) + exit (EX_CONFIG); + } + } + + index = argc; } + else + { + if (!DEFAULT_PREPROCESSOR) + grecs_preprocessor = NULL; + else + { + grecs_preprocessor = obstack_finish (&pp_stk); + free (pp_qopt); + } - if (preprocess_only) - exit (grecs_preproc_run (conffile, grecs_preprocessor) ? EX_CONFIG : 0); - - if (grecs_parse (conffile)) - exit (EX_CONFIG); + if (preprocess_only) + exit (grecs_preproc_run (conffile, grecs_preprocessor) + ? EX_CONFIG : 0); + if (grecs_parse (conffile)) + exit (EX_CONFIG); + } + set_mailer_argcv (); if (lint_mode) diff --git a/src/pies.h b/src/pies.h index 68a2ff5..e387a56 100644 --- a/src/pies.h +++ b/src/pies.h @@ -162,7 +162,7 @@ struct component struct pies_url *socket_url; /* Socket to listen on (if mode != pies_comp_exec) */ char *pass_fd_socket; /* Socket to pass fd on - (if mode == pies_comp_pass_fd) */ + (if mode == pies_comp_pass_fd) */ unsigned pass_fd_timeout; /* Maximum time to wait for pass_fd socket to become available. */ pies_acl_t acl; @@ -223,6 +223,8 @@ size_t depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos); int assert_grecs_value_type (grecs_locus_t *locus, const grecs_value_t *value, int type); +int str_to_socket_type (const char *str, int *pret); + struct component *component_create (const char *name); void component_finish (struct component *comp, grecs_locus_t *locus); struct grecs_keyword *find_component_keyword (const char *ident); @@ -254,8 +256,8 @@ int register_socket (int socktype, int fd); int pass_fd (const char *socket, int fd, unsigned time_out); int create_socket (struct pies_url *url, int socket_type, const char *user, mode_t umask); -int disable_socket (int fd); -int enable_socket (int fd); +void disable_socket (int fd); +void enable_socket (int fd); int parse_limits (limits_record_t *plrec, char *str, char **endp); @@ -324,4 +326,6 @@ int switch_to_privs (uid_t uid, gid_t gid, gl_list_t retain_groups); void pies_priv_setup (struct pies_privs *); void pies_epriv_setup (struct pies_privs *); - + +/* inetd.c */ +int inetd_parse_conf (const char *file); diff --git a/src/progman.c b/src/progman.c index 85a5068..227b438 100644 --- a/src/progman.c +++ b/src/progman.c @@ -382,8 +382,8 @@ redirect_to_file (struct prog *master, int stream) return -1; } /* Fix file ownership */ - pw = getpwnam (master->v.p.comp->privs.user); - if (pw) + if (master->v.p.comp->privs.user + && (pw = getpwnam (master->v.p.comp->privs.user)) != NULL) chown (master->v.p.comp->redir[stream].v.file, pw->pw_uid, pw->pw_gid); return fd; } @@ -409,7 +409,7 @@ open_redirector (struct prog *master, int stream) char *buf = NULL; size_t size = 0; pid_t pid; - int i, prio; + int prio; char *tag; fd_set fdset; @@ -613,7 +613,6 @@ env_setup (char **env) static void prog_start (struct prog *prog) { - int i; pid_t pid; time_t now; int redir[2]; @@ -1191,7 +1190,7 @@ progman_start () struct prog *prog; recompute_alarm = 0; - debug (1, ("Starting components")); + debug (1, ("starting components")); for (prog = proghead; prog; prog = prog->next) if (IS_COMPONENT (prog) && ((prog->v.p.status == status_enabled && prog->pid == 0) @@ -1706,8 +1705,6 @@ run_command (struct action *act, struct prog *prog, unsigned retcode, if (pid == 0) { - int i; - debug (1, (_("executing %s"), act->command)); /* Child */ setenv ("PIES_VERSION", PACKAGE_VERSION, 1); diff --git a/src/socket.c b/src/socket.c index 8b25073..6c56817 100644 --- a/src/socket.c +++ b/src/socket.c @@ -423,16 +423,20 @@ register_socket (int socktype, int fd) return 0; } -int +void disable_socket (int fd) { + if (fd < 0) + return; debug (2, (_("disabling fd %d"), fd)); FD_CLR (fd, &listenset); } -int +void enable_socket (int fd) { + if (fd < 0) + return; debug (2, (_("enabling fd %d"), fd)); FD_SET (fd, &listenset); } diff --git a/src/userprivs.c b/src/userprivs.c index 399e229..ba7494f 100644 --- a/src/userprivs.c +++ b/src/userprivs.c @@ -89,12 +89,6 @@ switch_to_privs (uid_t uid, gid_t gid, gl_list_t retain_groups) gid_t *emptygidset; size_t size = 1, j = 1; - if (uid == 0) - { - logmsg (LOG_ERR, _("refusing to run as root")); - return 1; - } - /* Create a list of supplementary groups */ size = 1 + (retain_groups ? gl_list_size (retain_groups) : 0); emptygidset = xcalloc (size, sizeof emptygidset[0]); -- cgit v1.2.1