From 7a4e3504c33ac6210066af56db0c5f08c774f83c Mon Sep 17 00:00:00 2001 From: Matthew Mondor Date: Tue, 6 Apr 2004 16:35:23 +0000 Subject: [PATCH] Warning: In an inconsistent state since undertaking major modifications --- mmsoftware/mmspawnd/mmspawnd.c | 223 +++++++++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 63 deletions(-) diff --git a/mmsoftware/mmspawnd/mmspawnd.c b/mmsoftware/mmspawnd/mmspawnd.c index 28709b9..1044433 100644 --- a/mmsoftware/mmspawnd/mmspawnd.c +++ b/mmsoftware/mmspawnd/mmspawnd.c @@ -1,4 +1,4 @@ -/* $Id: mmspawnd.c,v 1.18 2004/03/14 09:26:13 mmondor Exp $ */ +/* $Id: mmspawnd.c,v 1.19 2004/04/06 16:35:23 mmondor Exp $ */ /* * Copyright (C) 2003, Matthew Mondor @@ -59,6 +59,24 @@ * a library like libevent or libio which could take adventage of features * like kqueue and epoll where available. Using poll(2) wouldn't be too * bad however... + * - Think properly. We have a recursion prevention lock, which should use + * a lock path outside of the chroot(2) if any. We have the ALOCK_PATH, + * which can also be outside the chroot(2), since only the parent locks it. + * However, there is the LOCK_PATH which should be inside the chroot(2) if + * any, because both the parent, cache manager and children processes are + * locking it. These files also need to be readable by the user or group + * under which mmspawnd(8) runs, so that shlocks_reopen() works. Hmm these + * are not used in the children processes however, only by the cache manager + * process after launch... Either we have chroot(2) called in each, in which + * case the files can be anywhere on the filesystem, or we cause those lock + * files to be created after chroot(2). Also, because we drop privileges + * after this, both can write and open the locks fine permission-wise. + * So plz hlp wut 2 doo 2 nut teh errer kthx!!11111 u teh their!?1// + * Argh!!! children processes call client_resolve()! Hence they also need + * to reopen lock file(s)... So LOCK_PATH HAS to be within the chroot(2), + * and chroot(2) needs to be called before calling shlocks_init()! + * - Fix client_resolve() so that getnameinfo(3) can be called concurrently. + * - Update man page about lock paths requirements etc! */ @@ -106,20 +124,20 @@ MMCOPYRIGHT("@(#) Copyright (c) 2003\n\ \tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmspawnd.c,v 1.18 2004/03/14 09:26:13 mmondor Exp $"); +MMRCSID("$Id: mmspawnd.c,v 1.19 2004/04/06 16:35:23 mmondor Exp $"); /* DEFINITIONS */ #define DAEMON_NAME "mmspawnd" -#define DAEMON_VERSION "0.0.1/mmondor" +#define DAEMON_VERSION "0.0.2/mmondor" struct configuration { - char CHROOT_DIR[256], LOCK_PATH[256], PID_PATH[256], - USER[32], GROUPS[256], LOG_FACILITY[32], LISTEN_ADDRESSES[1024], - COMMAND[256], ALOCK_PATH[256], ALOCK_USER[32], ALOCK_GROUP[32], - ALOCK_MODE[4], PROCTITLE[64], STDERR_FILE[256]; + char CHROOT_DIR[256], LOCKS_PATH[256], LOCKS_USER[32], LOCKS_GROUP[32], + LOCKS_MODE[4], PID_PATH[256], USER[32], GROUPS[256], LOG_FACILITY[32], + LISTEN_ADDRESSES[1024], COMMAND[256], ALOCK_PATH[256], ALOCK_USER[32], + ALOCK_GROUP[32], ALOCK_MODE[4], PROCTITLE[64], STDERR_FILE[256]; long LISTEN_PORT, MAX_CONNECTIONS, MAX_ADDRESSES, MAX_PER_ADDRESS, CONNECTION_RATE, CONNECTION_PERIOD, MAX_ARGUMENTS, COMMAND_TIMEOUT, LIMIT_CORE, LIMIT_CPU, LIMIT_DATA, LIMIT_FSIZE, LIMIT_MEMLOCK, @@ -202,7 +220,7 @@ struct ipaddr_expire_process_iterator_udata { syslog(LOG_NOTICE, "* setrlimit(%s, %ld) - (%s)", \ (restxt), (limit), strerror(errno)); \ } \ -} while (/* CONSTCOND */0) ; +} while (/* CONSTCOND */0) @@ -231,8 +249,13 @@ static int key_cmp32(const void *, const void *, size_t); static int key_pidcmp(const void *, const void *, size_t); static u_int32_t key_pidhash(const void *, size_t); +void launch_server_child_init(const char *, uid_t, gid_t *, int); + +static pid_t launch_server_process(uid_t, gid_t *, int); +static void server_process_main(void); + static pid_t launch_ipaddr_expire_process(uid_t, gid_t *, int); -static void ipaddr_expire_process(void); +static void ipaddr_expire_process_main(void); static bool ipaddr_expire_process_iterator(hashnode_t *, void *); @@ -277,7 +300,10 @@ int main(int argc, char **argv) carg_t *cargp; carg_t cargs[] = { {CAT_STR, CAF_NONE, 0, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, + {CAT_STR, CAF_NONE, 1, 255, "LOCKS_PATH", CONF.LOCKS_PATH}, + {CAT_STR, CAF_NONE, 1, 31, "LOCKS_USER", CONF.LOCKS_USER}, + {CAT_STR, CAF_NONE, 1, 31, "LOCKS_GROUP", CONF.LOCKS_GROUP}, + {CAT_STR, CAF_NONE, 1, 3, "LOCKS_MODE", CONF.LOCKS_MODE}, {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, @@ -340,7 +366,10 @@ int main(int argc, char **argv) /* Set harmless defaults */ *CONF.CHROOT_DIR = '\0'; - (void) mm_strcpy(CONF.LOCK_PATH, "/var/run/mmspawnd.lock"); + (void) mm_strcpy(CONF.LOCKS_PATH, "/var/run/mmspawnd.lock"); + (void) mm_strcpy(CONF.LOCKS_USER, "mmspawnd"); + (void) mm_strcpy(CONF.LOCKS_GROUP, "mmspawnd"); + (void) mm_strcpy(CONF.LOCKS_MODE, "400"); (void) mm_strcpy(CONF.PID_PATH, "/var/run/mmspawnd.pid"); (void) mm_strcpy(CONF.USER, "mmspawnd"); (void) mm_strcpy(CONF.GROUPS, "mmspawnd"); @@ -409,6 +438,7 @@ int main(int argc, char **argv) CONF.GROUPS); exit(EXIT_FAILURE); } + /* Initialize alock */ if (*CONF.ALOCK_PATH != '\0') { uid_t u; gid_t g; @@ -442,6 +472,29 @@ int main(int argc, char **argv) } } else server.alock = -1; + /* Initialize shlocks */ + if (CONF.SHLOCKS_PATH != '\0') { /* Actually should always be true */ + uid_t u; + gid_t g; + mode_t m; + + if ((u = mmgetuid(CONF.LOCKS_USER)) == -1) { + (void) fprintf(stderr, "\nUnknown LOCKS_USER '%s'\n\n", + CONF.ALOCK_USER); + exit(EXIT_FAILURE); + } + if ((g = mmgetgid(CONF.LOCKS_GROUP)) == -1) { + (void) fprintf(stderr, "\nUnknown LOCKS_GROUP '%s'\n\n", + CONF.ALOCK_GROUP); + exit(EXIT_FAILURE); + } + (void) sscanf(CONF.LOCKS_MODE, "%o", &m); + if (m == 0 || m > 0770) { + (void) fprintf(stderr, "\nIllegal LOCKS_MODE '%03o'\n\n", m); + exit(EXIT_FAILURE); + } + /* XXX */ + } if ((ifaces = ifaces_parse(CONF.LISTEN_ADDRESSES)) == NULL) { (void) fprintf(stderr, "\nInvalid LISTEN_ADDRESSES\n\n"); exit(EXIT_FAILURE); @@ -506,6 +559,9 @@ MAX_ARGUMENTS)\n\n"); /* Create our locks for shared memory synchronization. These lock files * (LOCK_PATH) should also be available under the new root since they * need to be reopened by our children. + * XXX Should be done after chroot(2), with proper permissions so that + * we may reopen the files in children processes, Moreover, the + * lock_check() lock path should be independent (since outside the chroot)? */ if (!shlocks_init(CONF.LOCK_PATH, locks, SHLOCK_MAX, TRUE)) { syslog(LOG_NOTICE, "main() - shlocks_init()"); @@ -600,7 +656,7 @@ MAX_ARGUMENTS)\n\n"); * Note that the following function also causes the child to become * unprivileged. */ - if (launch_ipaddr_expire_process(uid, gids, ngids) < 1) { + if (launch_ipaddr_expire_process(uid, gids, ngids, CONF.CHROOT_PATH) < 1) { syslog(LOG_NOTICE, "main() - launch_ipaddr_expire_process()"); exit(EXIT_FAILURE); } @@ -768,7 +824,7 @@ listening for connections on port %d. Service is '%s'.", /* We create our own process group so that the * process after execve(2) could not kill(0) - * causing the listerner process to exit. + * causing the listener process to exit. * It also allows us to optionally not interrupt * currently served client when we restart. */ @@ -791,7 +847,8 @@ listening for connections on port %d. Service is '%s'.", (void) sigaction(SIGPIPE, &act, NULL); /* Close bound sockets which this process shouldn't - * have access to + * have access to. XXX We could use fcntl(3) to + * change socket inheritance at execve(2)... */ for (tif = ifaces; *(tif->address); tif++) { if (tif->sock != -1) { @@ -811,7 +868,7 @@ listening for connections on port %d. Service is '%s'.", /* sock always >2 */ (void) close(sock); - /* Let children know and remember these */ + /* Let child know and remember these */ client.ipn = ipn; client.saddr = addr; client.ipaddr = (const char *)ipaddr; @@ -828,12 +885,12 @@ listening for connections on port %d. Service is '%s'.", /* We do not need to hold the lock files any * longer, and in fact we do not want to let * the process access them, especially if the - * adminstrator sets up the service to not kill + * administrator sets up the service to not kill * currently running children if the service is * shut down. */ - shlocks_destroy(CONF.LOCK_PATH, locks, - (enum shlocks)SHLOCK_MAX, FALSE); + (void) shlocks_destroy(CONF.LOCK_PATH, locks, + (enum shlocks)SHLOCK_MAX, FALSE); (void) close(server.runlock); (void) close(server.alock); @@ -1426,45 +1483,6 @@ static void ipaddr_close(struct ipaddr_node *ipn) } -/* Launches the ipaddress-hostname-rate cache entry expiration process, - * a kind of asynchroneous garbage collector. - */ -static pid_t launch_ipaddr_expire_process(uid_t uid, gid_t *gids, int ngids) -{ - pid_t pid = -1; - - if ((pid = fork()) == 0) { - struct sigaction act; - - /* Reset default signal action */ - act.sa_handler = SIG_DFL; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGSEGV, &act, NULL); - - /* Release runlock which only our parent should hold */ - (void) close(server.runlock); - - /* Drop privileges */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "launch_ipaddr_expire_process() - Cannot \ -change uid and gids to safe privs"); - (void) kill(0, SIGTERM); - (void) exit(EXIT_FAILURE); - } - ipaddr_expire_process(); - /* NOTREACHED */ - } - - /* Parent */ - return pid; -} - - /* A quick hashtable_hash() and memcmp() replacements which already deal with * unique 32-bit values. */ @@ -1498,10 +1516,93 @@ static int key_pidcmp(const void *src, const void *dst, size_t len) } -/* This process can run independantly and manage the ipaddr cache entry +/* Useful function for use by launch_server_process() and + * launch_ipaddress_expire_process() initialization + */ +void launch_server_child_init(const char *func, uid_t uid, gid_t *gids, + int ngids) +{ + /* Reopen lock files as needed for flock(2) */ + if (shlocks_reopen(CONF.LOCK_PATH, locks, SHLOCK_MAX) == -1) { + syslog(LOG_NOTICE, "%s - Could not reopen lock files - %s", + func, strerror(errno)); + (void) kill(getppid(), SIGTERM); + exit(EXIT_FAILURE); + } + + /* chroot(2) if necessary */ + if (CONF.CHROOT_PATH != '\0') { + if ((chroot(chroot_path)) == -1) { + syslog(LOG_NOTICE, "%s - Could not chroot(2) to '%s' - %s", + func, CONF.CHROOT_PATH, strerror(errno)); + (void) kill(getppid(), SIGTERM); + exit(EXIT_FAILURE); + } + (void) chdir("/"); + } + + /* Drop privileges */ + if (!mmdropprivs(uid, gids, ngids)) { + syslog(LOG_NOTICE, "%s - Cannot change uid and gids to safe privs", + func); + (void) kill(getppid(), SIGTERM); + (void) exit(EXIT_FAILURE); + } +} + + +/* Launches the main server program, listening for connections and dispatching + * them to children processes. + */ +static pid_t launch_server_process(uid_t uid, gid_t *gids, int ngids) +{ +} + +static void server_process_main(void) +{ +} + + +/* Launches the ipaddress-hostname-rate cache entry expiration process, + * a kind of asynchroneous garbage collector. + */ +static pid_t launch_ipaddr_expire_process(uid_t uid, gid_t *gids, int ngids) +{ + pid_t pid = -1; + + if ((pid = fork()) == 0) { + struct sigaction act; + + /* Reset default signal action */ + act.sa_handler = SIG_DFL; + act.sa_flags = SA_NOCLDSTOP; + (void) sigemptyset(&act.sa_mask); + + (void) sigaction(SIGTERM, &act, NULL); + (void) sigaction(SIGINT, &act, NULL); + (void) sigaction(SIGCHLD, &act, NULL); + (void) sigaction(SIGSEGV, &act, NULL); + + /* Release runlock which only our parent should hold */ + (void) close(server.runlock); + + /* Will reopen lock files, chroot(2) and drop privileges */ + launch_server_child_init("launch_ipaddr_expire_process()", + uid, gids, ngids); + + /* Finally lanch main process loop */ + ipaddr_expire_process_main(); + /* NOTREACHED */ + } + + /* Parent */ + return pid; +} + +/* This process can run independently and manage the ipaddr cache entry * expiration events. */ -static void ipaddr_expire_process(void) +static void ipaddr_expire_process_main(void) { struct ipaddr_expire_process_iterator_udata data; @@ -1509,9 +1610,6 @@ static void ipaddr_expire_process(void) setproctitle("%s Cache service process", CONF.PROCTITLE); #endif /* __GLIBC__ */ - /* Reopen lock files as needed for flock(2) */ - (void) shlocks_init(CONF.LOCK_PATH, locks, SHLOCK_MAX, FALSE); - /* Set initial timeout to maximum allowed */ data.soonest = CONF.CONNECTION_PERIOD; for (;;) { @@ -1539,7 +1637,6 @@ static void ipaddr_expire_process(void) /* NOTREACHED */ } - /* Internally used by the cache events expiration process */ static bool ipaddr_expire_process_iterator(hashnode_t *hnod, void *udata) { -- 2.9.0