pid_t proc_run(struct privsep *ps, struct privsep_proc *p, struct privsep_proc *procs, u_int nproc, void (*init)(struct privsep *, struct privsep_proc *, void *), void *arg) { pid_t pid; struct passwd *pw; const char *root; struct control_sock *rcs; u_int n; if (ps->ps_noaction) return (0); proc_open(ps, p, procs, nproc); /* Fork child handlers */ switch (pid = fork()) { case -1: fatal("proc_run: cannot fork"); case 0: /* Set the process group of the current process */ setpgrp(0, getpid()); break; default: return (pid); } pw = ps->ps_pw; if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { if (control_init(ps, &ps->ps_csock) == -1) fatalx(p->p_title); TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) if (control_init(ps, rcs) == -1) fatalx(p->p_title); } /* Change root directory */ if (p->p_chroot != NULL) root = p->p_chroot; else root = pw->pw_dir; if (chroot(root) == -1) fatal("proc_run: chroot"); if (chdir("/") == -1) fatal("proc_run: chdir(\"/\")"); privsep_process = p->p_id; setproctitle("%s", p->p_title); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("proc_run: cannot drop privileges"); /* Fork child handlers */ for (n = 1; n < ps->ps_instances[p->p_id]; n++) { if (fork() == 0) { ps->ps_instance = p->p_instance = n; break; } } #ifdef DEBUG log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title, ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); #endif event_init(); signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); signal_add(&ps->ps_evsigint, NULL); signal_add(&ps->ps_evsigterm, NULL); signal_add(&ps->ps_evsigchld, NULL); signal_add(&ps->ps_evsighup, NULL); signal_add(&ps->ps_evsigpipe, NULL); signal_add(&ps->ps_evsigusr1, NULL); proc_listen(ps, procs, nproc); if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { TAILQ_INIT(&ctl_conns); if (control_listen(&ps->ps_csock) == -1) fatalx(p->p_title); TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) if (control_listen(rcs) == -1) fatalx(p->p_title); }
int main(int argc, char *argv[]) { struct sockaddr_in from; struct stat st; char path[64]; int on = 1; char *cp; struct sockaddr_in soin; uid_t unpriv_uid; gid_t unpriv_gid; if (getuid()) errx(1, "not super user"); run_as(&unpriv_uid, &unpriv_gid); argv++; argc--; while (argc > 0 && *argv[0] == '-') { if (strcmp(*argv, "-m") == 0) { if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; multicast_mode = SCOPED_MULTICAST; multicast_scope = atoi(*argv); if (multicast_scope > MAX_MULTICAST_SCOPE) errx(1, "ttl must not exceed %u", MAX_MULTICAST_SCOPE); } else multicast_mode = PER_INTERFACE_MULTICAST; } else if (strcmp(*argv, "-i") == 0) insecure_mode = 1; else if (strcmp(*argv, "-l") == 0) quiet_mode = 1; else if (strcmp(*argv, "-p") == 0) iff_flag = 0; else usage(); argv++, argc--; } if (argc > 0) usage(); #ifndef DEBUG daemon(1, 0); #endif (void) signal(SIGHUP, getboottime); openlog("rwhod", LOG_PID, LOG_DAEMON); sp = getservbyname("who", "udp"); if (sp == NULL) { syslog(LOG_ERR, "who/udp: unknown service"); exit(1); } if (chdir(_PATH_RWHODIR) < 0) { syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR); exit(1); } /* * Establish host name as returned by system. */ if (gethostname(myname, sizeof(myname) - 1) < 0) { syslog(LOG_ERR, "gethostname: %m"); exit(1); } if ((cp = index(myname, '.')) != NULL) *cp = '\0'; strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0'; #ifdef __APPLE__ utmpf = open(_PATH_UTMPX, O_RDONLY|O_CREAT, 0644); #else utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644); #endif if (utmpf < 0) { #ifdef __APPLE__ syslog(LOG_ERR, "%s: %m", _PATH_UTMPX); #else syslog(LOG_ERR, "%s: %m", _PATH_UTMP); #endif exit(1); } getboottime(0); if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "socket: %m"); exit(1); } if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); exit(1); } memset(&soin, 0, sizeof(soin)); soin.sin_len = sizeof(soin); soin.sin_family = AF_INET; soin.sin_port = sp->s_port; if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) { syslog(LOG_ERR, "bind: %m"); exit(1); } setgid(unpriv_gid); setgroups(1, &unpriv_gid); /* XXX BOGUS groups[0] = egid */ setuid(unpriv_uid); if (!configure(s)) exit(1); if (!quiet_mode) { signal(SIGALRM, onalrm); onalrm(0); } for (;;) { struct whod wd; socklen_t len = sizeof(from); int cc, whod; time_t t; cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, (struct sockaddr *)&from, &len); if (cc <= 0) { if (cc < 0 && errno != EINTR) syslog(LOG_WARNING, "recv: %m"); continue; } if (from.sin_port != sp->s_port && !insecure_mode) { syslog(LOG_WARNING, "%d: bad source port from %s", ntohs(from.sin_port), inet_ntoa(from.sin_addr)); continue; } if (cc < WHDRSIZE) { syslog(LOG_WARNING, "short packet from %s", inet_ntoa(from.sin_addr)); continue; } if (wd.wd_vers != WHODVERSION) continue; if (wd.wd_type != WHODTYPE_STATUS) continue; if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) { syslog(LOG_WARNING, "malformed host name from %s", inet_ntoa(from.sin_addr)); continue; } (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname); /* * Rather than truncating and growing the file each time, * use ftruncate if size is less than previous size. */ whod = open(path, O_WRONLY | O_CREAT, 0644); if (whod < 0) { syslog(LOG_WARNING, "%s: %m", path); continue; } #if ENDIAN != BIG_ENDIAN { int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); struct whoent *we; /* undo header byte swapping before writing to file */ wd.wd_sendtime = ntohl(wd.wd_sendtime); for (i = 0; i < 3; i++) wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); wd.wd_boottime = ntohl(wd.wd_boottime); we = wd.wd_we; for (i = 0; i < n; i++) { we->we_idle = ntohl(we->we_idle); we->we_utmp.out_time = ntohl(we->we_utmp.out_time); we++; } } #endif (void) time(&t); wd.wd_recvtime = _time_to_int(t); (void) write(whod, (char *)&wd, cc); if (fstat(whod, &st) < 0 || st.st_size > cc) ftruncate(whod, cc); (void) close(whod); } }
int daemonize_trqauthd(char *server_ip, int server_port, void *(*process_meth)(void *)) { int gid; pid_t pid; int rc; char error_buf[1024]; umask(022); gid = getgid(); /* secure supplemental groups */ if(setgroups(1, (gid_t *)&gid) != 0) { fprintf(stderr, "Unable to drop secondary groups. Some MAC framework is active?\n"); snprintf(error_buf, sizeof(error_buf), "setgroups(group = %lu) failed: %s\n", (unsigned long)gid, strerror(errno)); fprintf(stderr, "%s\n", error_buf); return(1); } if (getenv("PBSDEBUG") != NULL) debug_mode = TRUE; if (debug_mode == FALSE) { pid = fork(); if(pid > 0) { /* parent. We are done */ return(0); } else if (pid < 0) { /* something went wrong */ fprintf(stderr, "fork failed. errno = %d\n", errno); return(PBSE_RMSYSTEM); } else { fprintf(stderr, "trqauthd daemonized - port %d\n", server_port); /* If I made it here I am the child */ fclose(stdin); fclose(stdout); fclose(stderr); /* We closed 0 (stdin), 1 (stdout), and 2 (stderr). fopen should give us 0, 1 and 2 in that order. this is a UNIX practice */ (void)fopen("/dev/null", "r"); (void)fopen("/dev/null", "r"); (void)fopen("/dev/null", "r"); } } else { fprintf(stderr, "trqauthd port: %d\n", server_port); } /* start the listener */ rc = start_listener(server_ip, server_port, process_meth); if(rc != PBSE_NONE) { openlog("daemonize_trqauthd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); syslog(LOG_ALERT, "trqauthd could not start: %d\n", rc); exit(-1); } exit(0); }
int main (int argc, char **argv) { server *srv = NULL; int print_config = 0; int test_config = 0; int i_am_root; int o; int num_childs = 0; int pid_fd = -1, fd; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; #endif #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif #ifdef USE_ALARM struct itimerval interval; interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif /* for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } /* init structs done */ srv->srvconf.port = 0; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); #else i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { case 'f': if (config_read(srv, optarg)) { server_free(srv); return -1; } break; case 'm': buffer_copy_string(srv->srvconf.modules_dir, optarg); break; case 'p': print_config = 1; break; case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; case 'V': show_features(); return 0; case 'h': show_help(); return 0; default: show_help(); server_free(srv); return -1; } } if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); server_free(srv); return -1; } if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); fprintf(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); } } if (test_config) { printf("Syntax OK\n"); } if (test_config || print_config) { server_free(srv); return 0; } /* close stdin and stdout, as they are not needed */ openDevNull(STDIN_FILENO); openDevNull(STDOUT_FILENO); if (0 != config_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } /* UID handling */ #ifdef HAVE_GETUID if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { /* we are setuid-root */ log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); server_free(srv); return -1; } #endif /* check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); server_free(srv); return -1; } if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); plugins_free(srv); server_free(srv); return -1; } /* open pid file BEFORE chroot */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { struct stat st; if (errno != EEXIST) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * * as it is a hard limit and will lead to a segfault we add some safety * */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; int use_rlimit = 1; #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } if (use_rlimit && srv->srvconf.max_fds) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; rlim.rlim_max = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > FD_SETSIZE - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } #ifdef HAVE_PWD_H /* set user and group */ if (srv->srvconf.username->used) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } if (srv->srvconf.groupname->used) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } if (grp->gr_gid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set gid to 0\n"); return -1; } } #endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } #ifdef HAVE_PWD_H /* * Change group before chroot, when we have access * to /etc/group * */ if (srv->srvconf.groupname->used) { setgid(grp->gr_gid); setgroups(0, NULL); if (srv->srvconf.username->used) { initgroups(srv->srvconf.username->ptr, grp->gr_gid); } } #endif #ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); if (-1 == chroot(srv->srvconf.changeroot->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); return -1; } if (-1 == chdir("/")) { log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); return -1; } } #endif #ifdef HAVE_PWD_H /* drop root privs */ if (srv->srvconf.username->used) { setuid(pwd->pw_uid); } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) /** * on IRIX 6.5.30 they have prctl() but no DUMPABLE */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } else { #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } /** * we are not root can can't increase the fd-limit, but we can reduce it */ if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > FD_SETSIZE - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } } /* set max-conns */ if (srv->srvconf.max_conns > srv->max_fds) { /* we can't have more connections than max-fds */ srv->max_conns = srv->max_fds; } else if (srv->srvconf.max_conns) { /* otherwise respect the wishes of the user */ srv->max_conns = srv->srvconf.max_conns; } else { /* or use the default */ srv->max_conns = srv->max_fds; } if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_FORK /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); /* write pid file */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } /* Close stderr ASAP in the child process to make sure that nothing * is being written to that fd which may not be valid anymore. */ if (-1 == log_error_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *)srv->config_context->data[i])->value; size_t j; for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; /* all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } if (srv->config_unsupported) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains unsupported keys. Going down."); } if (srv->config_deprecated) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); } if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGUSR1, &act, NULL); # if defined(SA_SIGINFO) act.sa_sigaction = sigaction_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; # else act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; # endif sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) /* ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif #ifdef USE_ALARM signal(SIGALRM, signal_handler); /* setup periodic timer (1 second) */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } getitimer(ITIMER_REAL, &interval); #endif #ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else { int status; if (-1 != wait(&status)) { /** * one of our workers went away */ num_childs++; } else { switch (errno) { case EINTR: /** * if we receive a SIGHUP we have to close our logs ourself as we don't * have the mainloop who can help us here */ if (handle_sig_hup) { handle_sig_hup = 0; log_error_cycle(srv); /** * forward to all procs in the process-group * * we also send it ourself */ if (!forwarded_sig_hup) { forwarded_sig_hup = 1; kill(0, SIGHUP); } } break; default: break; } } } } /** * for the parent this is the exit-point */ if (!child) { /** * kill all children too */ if (graceful_shutdown) { kill(0, SIGINT); } else if (srv_shutdown) { kill(0, SIGTERM); } log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; } } #endif if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } /* * kqueue() is called here, select resets its internals, * all server sockets get their handlers * * */ if (0 != network_register_fdevents(srv)) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* might fail if user is using fam (not gamin) and famd isn't running */ if (NULL == (srv->stat_cache = stat_cache_init())) { log_error_write(srv, __FILE__, __LINE__, "s", "stat-cache could not be setup, dieing."); return -1; } #ifdef HAVE_FAM_H /* setup FAM */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { log_error_write(srv, __FILE__, __LINE__, "s", "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS FAMNoExists(srv->stat_cache->fam); #endif srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif /* get the current number of FDs */ srv->cur_fds = open("/dev/null", O_RDONLY); close(srv->cur_fds); for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); return -1; } } /* main-loop */ while (!srv_shutdown) { int n; size_t ndx; time_t min_ts; if (handle_sig_hup) { handler_t r; /* reset notification */ handle_sig_hup = 0; /* cycle logfiles */ switch(r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); return -1; } else { #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "logfiles cycled UID =", last_sighup_info.si_uid, "PID =", last_sighup_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "logfiles cycled"); #endif } } if (handle_sig_alarm) { /* a new second */ #ifdef USE_ALARM /* reset notification */ handle_sig_alarm = 0; #endif /* get current time */ min_ts = time(NULL); if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; case HANDLER_ERROR: log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } /* trigger waitpid */ srv->cur_ts = min_ts; /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** * check all connections for timeouts * */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; con = conns->ptr[ndx]; if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { if (con->request_count == 1) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { if (srv->cur_ts - con->read_idle_ts > con->conf.max_keep_alive_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } } if ((con->state == CON_STATE_WRITE) && (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* time - out */ #if 1 log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", con->bytes_written, "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; if (con->traffic_limit_reached && (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* enable connection again */ con->traffic_limit_reached = 0; changed = 1; } if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif } if (cs == 1) fprintf(stderr, "\n"); } } if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ if ((srv->cur_fds + srv->want_fds < srv->max_fds * 0.8) && /* we have enough unused fds */ (srv->conns->used < srv->max_conns * 0.9) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); srv->sockets_disabled = 0; } } else { if ((srv->cur_fds + srv->want_fds > srv->max_fds * 0.9) || /* out of fds */ (srv->conns->used > srv->max_conns) || /* out of connections */ (graceful_shutdown)) { /* graceful_shutdown */ /* disable server-fds */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); if (graceful_shutdown) { /* we don't want this socket anymore, * * closing it right away will make it possible for * the next lighttpd to take over (graceful restart) * */ fdevent_unregister(srv->ev, srv_socket->fd); close(srv_socket->fd); srv_socket->fd = -1; /* network_close() will cleanup after us */ if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } } } if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); } else if (srv->conns->used > srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } srv->sockets_disabled = 1; } } if (graceful_shutdown && srv->conns->used == 0) { /* we are in graceful shutdown phase and all connections are closed * we are ready to terminate without harming anyone */ srv_shutdown = 1; } /* we still have some fds to share */ if (srv->want_fds) { /* check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); srv->want_fds--; } } if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* n is the number of events */ int revents; int fd_ndx; #if 0 if (n > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } #endif fd_ndx = -1; do { fdevent_handler handler; void *context; handler_t r; fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); revents = fdevent_event_get_revent (srv->ev, fd_ndx); fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); /* connection_handle_fdevent needs a joblist_append */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); #endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } } while (--n > 0); } else if (n < 0 && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ss", "fdevent_poll failed:", strerror(errno)); } for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; connection_state_machine(srv, con); switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } con->in_joblist = 0; } srv->joblist->used = 0; } if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0 && 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "server stopped by UID =", last_sigterm_info.si_uid, "PID =", last_sigterm_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); #endif /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; }
int main(int argc, char **argv) { char *nameserv_host; char *topdomain; char *errormsg; #ifndef WINDOWS32 struct passwd *pw; #endif char *username; char password[33]; int foreground; char *newroot; char *context; char *device; char *pidfile; int choice; int tun_fd; int dns_fd; int max_downstream_frag_size; int autodetect_frag_size; int retval; int raw_mode; int lazymode; int selecttimeout; int hostname_maxlen; #ifdef OPENBSD int rtable = 0; #endif struct sockaddr_storage nameservaddr; int nameservaddr_len; int nameserv_family; nameserv_host = NULL; topdomain = NULL; errormsg = NULL; #ifndef WINDOWS32 pw = NULL; #endif username = NULL; memset(password, 0, 33); srand(time(NULL)); foreground = 0; newroot = NULL; context = NULL; device = NULL; pidfile = NULL; autodetect_frag_size = 1; max_downstream_frag_size = 3072; retval = 0; raw_mode = 1; lazymode = 1; selecttimeout = 4; hostname_maxlen = 0xFF; nameserv_family = AF_UNSPEC; #ifdef WINDOWS32 WSAStartup(req_version, &wsa_data); #endif srand((unsigned) time(NULL)); client_init(); #if !defined(BSD) && !defined(__GLIBC__) __progname = strrchr(argv[0], '/'); if (__progname == NULL) __progname = argv[0]; else __progname++; #endif while ((choice = getopt(argc, argv, "46vfhru:t:d:R:P:m:M:F:T:O:L:I:")) != -1) { switch(choice) { case '4': nameserv_family = AF_INET; break; case '6': nameserv_family = AF_INET6; break; case 'v': version(); /* NOTREACHED */ break; case 'f': foreground = 1; break; case 'h': help(); /* NOTREACHED */ break; case 'r': raw_mode = 0; break; case 'u': username = optarg; break; case 't': newroot = optarg; break; case 'd': device = optarg; break; #ifdef OPENBSD case 'R': rtable = atoi(optarg); break; #endif case 'P': strncpy(password, optarg, sizeof(password)); password[sizeof(password)-1] = 0; /* XXX: find better way of cleaning up ps(1) */ memset(optarg, 0, strlen(optarg)); break; case 'm': autodetect_frag_size = 0; max_downstream_frag_size = atoi(optarg); break; case 'M': hostname_maxlen = atoi(optarg); if (hostname_maxlen > 255) hostname_maxlen = 255; if (hostname_maxlen < 10) hostname_maxlen = 10; break; case 'z': context = optarg; break; case 'F': pidfile = optarg; break; case 'T': if (client_set_qtype(optarg)) errx(5, "Invalid query type '%s'", optarg); break; case 'O': /* not -D, is Debug in server */ client_set_downenc(optarg); break; case 'L': lazymode = atoi(optarg); if (lazymode > 1) lazymode = 1; if (lazymode < 0) lazymode = 0; if (!lazymode) selecttimeout = 1; break; case 'I': selecttimeout = atoi(optarg); if (selecttimeout < 1) selecttimeout = 1; break; default: usage(); /* NOTREACHED */ } } check_superuser(usage); argc -= optind; argv += optind; switch (argc) { case 1: nameserv_host = get_resolvconf_addr(); topdomain = strdup(argv[0]); break; case 2: nameserv_host = argv[0]; topdomain = strdup(argv[1]); break; default: usage(); /* NOTREACHED */ } if (max_downstream_frag_size < 1 || max_downstream_frag_size > 0xffff) { warnx("Use a max frag size between 1 and 65535 bytes.\n"); usage(); /* NOTREACHED */ } if (nameserv_host) { nameservaddr_len = get_addr(nameserv_host, DNS_PORT, nameserv_family, 0, &nameservaddr); if (nameservaddr_len < 0) { errx(1, "Cannot lookup nameserver '%s': %s ", nameserv_host, gai_strerror(nameservaddr_len)); } client_set_nameserver(&nameservaddr, nameservaddr_len); } else { warnx("No nameserver found - not connected to any network?\n"); usage(); /* NOTREACHED */ } if(check_topdomain(topdomain, &errormsg)) { warnx("Invalid topdomain: %s", errormsg); usage(); /* NOTREACHED */ } client_set_selecttimeout(selecttimeout); client_set_lazymode(lazymode); client_set_topdomain(topdomain); client_set_hostname_maxlen(hostname_maxlen); if (username != NULL) { #ifndef WINDOWS32 if ((pw = getpwnam(username)) == NULL) { warnx("User %s does not exist!\n", username); usage(); /* NOTREACHED */ } #endif } if (strlen(password) == 0) { if (NULL != getenv(PASSWORD_ENV_VAR)) snprintf(password, sizeof(password), "%s", getenv(PASSWORD_ENV_VAR)); else read_password(password, sizeof(password)); } client_set_password(password); if ((tun_fd = open_tun(device)) == -1) { retval = 1; goto cleanup1; } if ((dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) { retval = 1; goto cleanup2; } #ifdef OPENBSD if (rtable > 0) socket_setrtable(dns_fd, rtable); #endif signal(SIGINT, sighandler); signal(SIGTERM, sighandler); fprintf(stderr, "Sending DNS queries for %s to %s\n", topdomain, format_addr(&nameservaddr, nameservaddr_len)); if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) { retval = 1; goto cleanup2; } if (client_get_conn() == CONN_RAW_UDP) { fprintf(stderr, "Sending raw traffic directly to %s\n", client_get_raw_addr()); } fprintf(stderr, "Connection setup complete, transmitting data.\n"); if (foreground == 0) do_detach(); if (pidfile != NULL) do_pidfile(pidfile); if (newroot != NULL) do_chroot(newroot); if (username != NULL) { #ifndef WINDOWS32 gid_t gids[1]; gids[0] = pw->pw_gid; if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { warnx("Could not switch to user %s!\n", username); usage(); /* NOTREACHED */ } #endif } if (context != NULL) do_setcon(context); client_tunnel(tun_fd, dns_fd); cleanup2: close_dns(dns_fd); close_tun(tun_fd); cleanup1: return retval; }
/* * User shell for authenticating gateways. Sole purpose is to allow * a user to ssh to a gateway, and have the gateway modify packet * filters to allow access, then remove access when the user finishes * up. Meant to be used only from ssh(1) connections. */ int main(int argc, char *argv[]) { int lockcnt = 0, n, pidfd; FILE *config; struct in6_addr ina; struct passwd *pw; char *cp; gid_t gid; uid_t uid; char *shell; login_cap_t *lc; config = fopen(PATH_CONFFILE, "r"); if (config == NULL) { syslog(LOG_ERR, "can not open %s (%m)", PATH_CONFFILE); exit(1); } if ((cp = getenv("SSH_TTY")) == NULL) { syslog(LOG_ERR, "non-interactive session connection for authpf"); exit(1); } if ((cp = getenv("SSH_CLIENT")) == NULL) { syslog(LOG_ERR, "cannot determine connection source"); exit(1); } if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) { syslog(LOG_ERR, "SSH_CLIENT variable too long"); exit(1); } cp = strchr(ipsrc, ' '); if (!cp) { syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc); exit(1); } *cp = '\0'; if (inet_pton(AF_INET, ipsrc, &ina) != 1 && inet_pton(AF_INET6, ipsrc, &ina) != 1) { syslog(LOG_ERR, "cannot determine IP from SSH_CLIENT %s", ipsrc); exit(1); } /* open the pf device */ dev = open(PATH_DEVFILE, O_RDWR); if (dev == -1) { syslog(LOG_ERR, "cannot open packet filter device (%m)"); goto die; } uid = getuid(); pw = getpwuid(uid); if (pw == NULL) { syslog(LOG_ERR, "cannot find user for uid %u", uid); goto die; } if ((lc = login_getclass(pw->pw_class)) != NULL) shell = login_getcapstr(lc, "shell", pw->pw_shell, pw->pw_shell); else shell = pw->pw_shell; login_close(lc); if (strcmp(shell, PATH_AUTHPF_SHELL)) { syslog(LOG_ERR, "wrong shell for user %s, uid %u", pw->pw_name, pw->pw_uid); if (shell != pw->pw_shell) free(shell); goto die; } if (shell != pw->pw_shell) free(shell); /* * Paranoia, but this data _does_ come from outside authpf, and * truncation would be bad. */ if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) { syslog(LOG_ERR, "username too long: %s", pw->pw_name); goto die; } if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)", luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) { syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld", luser, (long)getpid(), (long)getpid()); if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld", (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) { syslog(LOG_ERR, "pid too large for ruleset name"); goto die; } } /* Make our entry in /var/authpf as /var/authpf/ipaddr */ n = snprintf(pidfile, sizeof(pidfile), "%s/%s", PATH_PIDFILE, ipsrc); if (n < 0 || (u_int)n >= sizeof(pidfile)) { syslog(LOG_ERR, "path to pidfile too long"); goto die; } /* * If someone else is already using this ip, then this person * wants to switch users - so kill the old process and exit * as well. * * Note, we could print a message and tell them to log out, but the * usual case of this is that someone has left themselves logged in, * with the authenticated connection iconized and someone else walks * up to use and automatically logs in before using. If this just * gets rid of the old one silently, the new user never knows they * could have used someone else's old authentication. If we * tell them to log out before switching users it is an invitation * for abuse. */ do { int save_errno, otherpid = -1; char otherluser[MAXLOGNAME]; if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 || (pidfp = fdopen(pidfd, "r+")) == NULL) { if (pidfd != -1) close(pidfd); syslog(LOG_ERR, "cannot open or create %s: %s", pidfile, strerror(errno)); goto die; } if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0) break; save_errno = errno; /* Mark our pid, and username to our file. */ rewind(pidfp); /* 31 == MAXLOGNAME - 1 */ if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2) otherpid = -1; syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s", pidfile, otherpid, strerror(save_errno)); if (otherpid > 0) { syslog(LOG_INFO, "killing prior auth (pid %d) of %s by user %s", otherpid, ipsrc, otherluser); if (kill((pid_t) otherpid, SIGTERM) == -1) { syslog(LOG_INFO, "could not kill process %d: (%m)", otherpid); } } /* * we try to kill the previous process and acquire the lock * for 10 seconds, trying once a second. if we can't after * 10 attempts we log an error and give up */ if (++lockcnt > 10) { syslog(LOG_ERR, "cannot kill previous authpf (pid %d)", otherpid); fclose(pidfp); pidfp = NULL; goto dogdeath; } sleep(1); /* re-open, and try again. The previous authpf process * we killed above should unlink the file and release * it's lock, giving us a chance to get it now */ fclose(pidfp); pidfp = NULL; } while (1); /* whack the group list */ gid = getegid(); if (setgroups(1, &gid) == -1) { syslog(LOG_INFO, "setgroups: %s", strerror(errno)); do_death(0); } /* revoke privs */ uid = getuid(); #if defined(__OpenBSD__) if (setresuid(uid, uid, uid) == -1) { syslog(LOG_INFO, "setresuid: %s", strerror(errno)); do_death(0); } #else /* defined(__OpenBSD__) */ /* NetBSD */ if (setuid(uid) == -1) { syslog(LOG_INFO, "setresuid: %s", strerror(errno)); do_death(0); } #endif /* defined(__OpenBSD__) */ openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON); if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) { syslog(LOG_INFO, "user %s prohibited", luser); do_death(0); } if (read_config(config)) { syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE); do_death(0); } if (remove_stale_rulesets()) { syslog(LOG_INFO, "error removing stale rulesets"); do_death(0); } /* We appear to be making headway, so actually mark our pid */ rewind(pidfp); fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser); fflush(pidfp); (void) ftruncate(fileno(pidfp), ftello(pidfp)); if (change_filter(1, luser, ipsrc) == -1) { printf("Unable to modify filters\r\n"); do_death(0); } if (change_table(1, ipsrc) == -1) { printf("Unable to modify table\r\n"); change_filter(0, luser, ipsrc); do_death(0); } signal(SIGTERM, need_death); signal(SIGINT, need_death); signal(SIGALRM, need_death); signal(SIGPIPE, need_death); signal(SIGHUP, need_death); signal(SIGQUIT, need_death); signal(SIGTSTP, need_death); while (1) { printf("\r\nHello %s. ", luser); printf("You are authenticated from host \"%s\"\r\n", ipsrc); setproctitle("%s@%s", luser, ipsrc); print_message(PATH_MESSAGE); while (1) { sleep(10); if (want_death) do_death(1); } } /* NOTREACHED */ dogdeath: printf("\r\n\r\nSorry, this service is currently unavailable due to "); printf("technical difficulties\r\n\r\n"); print_message(PATH_PROBLEM); printf("\r\nYour authentication process (pid %ld) was unable to run\n", (long)getpid()); sleep(180); /* them lusers read reaaaaal slow */ die: do_death(0); return (0); }
int main (int argc, char *argv[]) { int i, pidfd; int blocked_signals[] = {SIGPIPE, 0}; int cc; char *oom_value; uint32_t slurmd_uid = 0; uint32_t curr_uid = 0; char time_stamp[256]; log_options_t lopts = LOG_OPTS_INITIALIZER; /* NOTE: logfile is NULL at this point */ log_init(argv[0], lopts, LOG_DAEMON, NULL); /* * Make sure we have no extra open files which * would be propagated to spawned tasks. */ cc = sysconf(_SC_OPEN_MAX); for (i = 3; i < cc; i++) close(i); /* * Drop supplementary groups. */ if (geteuid() == 0) { if (setgroups(0, NULL) != 0) { fatal("Failed to drop supplementary groups, " "setgroups: %m"); } } else { debug("Not running as root. Can't drop supplementary groups"); } /* * Create and set default values for the slurmd global * config variable "conf" */ conf = xmalloc(sizeof(slurmd_conf_t)); _init_conf(); conf->argv = &argv; conf->argc = &argc; if (_slurmd_init() < 0) { error( "slurmd initialization failed" ); fflush( NULL ); exit(1); } slurmd_uid = slurm_get_slurmd_user_id(); curr_uid = getuid(); if (curr_uid != slurmd_uid) { struct passwd *pw = NULL; char *slurmd_user = NULL; char *curr_user = NULL; /* since when you do a getpwuid you get a pointer to a * structure you have to do a xstrdup on the first * call or your information will just get over * written. This is a memory leak, but a fatal is * called right after so it isn't that big of a deal. */ if ((pw=getpwuid(slurmd_uid))) slurmd_user = xstrdup(pw->pw_name); if ((pw=getpwuid(curr_uid))) curr_user = pw->pw_name; fatal("You are running slurmd as something " "other than user %s(%d). If you want to " "run as this user add SlurmdUser=%s " "to the slurm.conf file.", slurmd_user, slurmd_uid, curr_user); } init_setproctitle(argc, argv); xsignal(SIGTERM, &_term_handler); xsignal(SIGINT, &_term_handler); xsignal(SIGHUP, &_hup_handler ); xsignal_block(blocked_signals); debug3("slurmd initialization successful"); /* * Become a daemon if desired. * Do not chdir("/") or close all fd's */ if (conf->daemonize) { if (daemon(1,1) == -1) error("Couldn't daemonize slurmd: %m"); } test_core_limit(); info("slurmd version %s started", SLURM_VERSION_STRING); debug3("finished daemonize"); if ((oom_value = getenv("SLURMD_OOM_ADJ"))) { i = atoi(oom_value); debug("Setting slurmd oom_adj to %d", i); set_oom_adj(i); } _kill_old_slurmd(); if (conf->mlock_pages) { /* * Call mlockall() if available to ensure slurmd * doesn't get swapped out */ #ifdef _POSIX_MEMLOCK if (mlockall (MCL_FUTURE | MCL_CURRENT) < 0) error ("failed to mlock() slurmd pages: %m"); #else error ("mlockall() system call does not appear to be available"); #endif /* _POSIX_MEMLOCK */ } /* * Restore any saved revoked credential information */ if (!conf->cleanstart && (_restore_cred_state(conf->vctx) < 0)) return SLURM_FAILURE; if (jobacct_gather_init() != SLURM_SUCCESS) fatal("Unable to initialize jobacct_gather"); if (job_container_init() < 0) fatal("Unable to initialize job_container plugin."); if (container_g_restore(conf->spooldir, !conf->cleanstart)) error("Unable to restore job_container state."); if (switch_g_node_init() < 0) fatal("Unable to initialize interconnect."); if (conf->cleanstart && switch_g_clear_node_state()) fatal("Unable to clear interconnect state."); switch_g_slurmd_init(); _create_msg_socket(); conf->pid = getpid(); /* This has to happen after daemon(), which closes all fd's, so we keep the write lock of the pidfile. */ pidfd = create_pidfile(conf->pidfile, 0); rfc2822_timestamp(time_stamp, sizeof(time_stamp)); info("%s started on %s", slurm_prog_name, time_stamp); _install_fork_handlers(); list_install_fork_handlers(); slurm_conf_install_fork_handlers(); /* * Initialize any plugins */ if (slurmd_plugstack_init()) fatal("failed to initialize slurmd_plugstack"); _spawn_registration_engine(); _msg_engine(); /* * Close fd here, otherwise we'll deadlock since create_pidfile() * flocks the pidfile. */ if (pidfd >= 0) /* valid pidfd, non-error */ (void) close(pidfd); /* Ignore errors */ if (unlink(conf->pidfile) < 0) error("Unable to remove pidfile `%s': %m", conf->pidfile); _wait_for_all_threads(120); _slurmd_fini(); _destroy_conf(); slurm_crypto_fini(); /* must be after _destroy_conf() */ info("Slurmd shutdown completing"); log_fini(); return 0; }
/*-------------------------------------------------------------------------*/ int Daemonize(const char* pidfile) /* Turn the calling process into a daemon (detach from tty setuid(), etc */ /* */ /* Returns: zero on success non-zero if slpd could not daemonize (or if */ /* slpd is already running . */ /*-------------------------------------------------------------------------*/ { pid_t pid; FILE* fd; struct passwd* pwent; char pidstr[13]; if(G_SlpdCommandLine.detach) { /*-------------------------------------------*/ /* Release the controlling tty and std files */ /*-------------------------------------------*/ switch(fork()) { case -1: return -1; case 0: /* child lives */ break; default: /* parent dies */ exit(0); } close(0); close(1); close(2); setsid(); /* will only fail if we are already the process group leader */ } /*------------------------------------------*/ /* make sure that we're not running already */ /*------------------------------------------*/ /* read the pid from the file */ fd = fopen(pidfile,"r"); if(fd) { fread(pidstr,13,1,fd); fclose(fd); pid = atoi(pidstr); if(pid) { if(kill(pid,0) == 0) { /* we are already running */ SLPFatal("slpd daemon is already running\n"); return -1; } } } /* write my pid to the pidfile */ fd = fopen(pidfile,"w"); if(fd) { sprintf(pidstr,"%i",getpid()); fwrite(pidstr,strlen(pidstr),1,fd); fclose(fd); } /*----------------*/ /* suid to daemon */ /*----------------*/ /* TODO: why do the following lines mess up my signal handlers? */ pwent = getpwnam("daemon"); if(pwent) { if( setgroups(1, &pwent->pw_gid) < 0 || setgid(pwent->pw_gid) < 0 || setuid(pwent->pw_uid) < 0 ) { /* TODO: should we log here and return fail */ } } return 0; }
main(int argc, char *argv[]) #endif { char options[128] = "aAc:CdD::fhHI:l:L:m:M:n:p:P:qrsS:UvV-:Y:"; int arg, i, ret; int dont_fork = 0, do_help = 0; int log_set = 0; int uid = 0, gid = 0; int agent_mode = -1; char *pid_file = NULL; char option_compatability[] = "-Le"; #if HAVE_GETPID int fd; FILE *PID; #endif #if HAVE_GETPWNAM && HAVE_PWD_H struct passwd *info; #endif #if HAVE_UNISTD_H const char *persistent_dir; #endif #ifndef WIN32 /* * close all non-standard file descriptors we may have * inherited from the shell. */ for (i = getdtablesize() - 1; i > 2; --i) { (void) close(i); } #endif /* #WIN32 */ /* * register signals ASAP to prevent default action (usually core) * for signals during startup... */ #ifdef SIGTERM DEBUGMSGTL(("signal", "registering SIGTERM signal handler\n")); signal(SIGTERM, SnmpdShutDown); #endif #ifdef SIGINT DEBUGMSGTL(("signal", "registering SIGINT signal handler\n")); signal(SIGINT, SnmpdShutDown); #endif #ifdef SIGHUP signal(SIGHUP, SIG_IGN); /* do not terminate on early SIGHUP */ #endif #ifdef SIGUSR1 DEBUGMSGTL(("signal", "registering SIGUSR1 signal handler\n")); signal(SIGUSR1, SnmpdDump); #endif #ifdef SIGPIPE DEBUGMSGTL(("signal", "registering SIGPIPE signal handler\n")); signal(SIGPIPE, SIG_IGN); /* 'Inline' failure of wayward readers */ #endif #ifdef SIGXFSZ signal(SIGXFSZ, SnmpdCatchRandomSignal); #endif #ifdef NETSNMP_NO_ROOT_ACCESS /* * Default to no. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); #endif /* * Default to NOT running an AgentX master. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_MASTER, 0); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_TIMEOUT, -1); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_RETRIES, -1); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_CACHE_TIMEOUT, 5); /* * Add some options if they are available. */ #if HAVE_UNISTD_H strcat(options, "g:u:"); #endif #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) strcat(options, "x:"); #endif #ifdef USING_AGENTX_SUBAGENT_MODULE strcat(options, "X"); #endif /* * This is incredibly ugly, but it's probably the simplest way * to handle the old '-L' option as well as the new '-Lx' style */ for (i=0; i<argc; i++) { if (!strcmp(argv[i], "-L")) argv[i] = option_compatability; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG #ifdef WIN32 snmp_log_syslogname(app_name_long); #else snmp_log_syslogname(app_name); #endif #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, app_name); /* * Now process options normally. */ while ((arg = getopt(argc, argv, options)) != EOF) { switch (arg) { case '-': if (strcasecmp(optarg, "help") == 0) { usage(argv[0]); } if (strcasecmp(optarg, "version") == 0) { version(); } handle_long_opt(optarg); break; case 'a': log_addresses++; break; case 'A': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES, 1); break; case 'c': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OPTIONALCONFIG, optarg); } else { usage(argv[0]); } break; case 'C': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1); break; case 'd': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, ++snmp_dump_packet); break; case 'D': debug_register_tokens(optarg); snmp_set_do_debugging(1); break; case 'f': dont_fork = 1; break; #if HAVE_UNISTD_H case 'g': if (optarg != NULL) { char *ecp; int gid; gid = strtoul(optarg, &ecp, 10); if (*ecp) { #if HAVE_GETPWNAM && HAVE_PWD_H struct group *info; info = getgrnam(optarg); if (info) { gid = info->gr_gid; } else { #endif fprintf(stderr, "Bad group id: %s\n", optarg); exit(1); #if HAVE_GETPWNAM && HAVE_PWD_H } #endif } netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID, gid); } else { usage(argv[0]); } break; #endif case 'h': usage(argv[0]); break; case 'H': do_help = 1; break; case 'I': if (optarg != NULL) { add_to_init_list(optarg); } else { usage(argv[0]); } break; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE case 'l': printf("Warning: -l option is deprecated, use -Lf <file> instead\n"); if (optarg != NULL) { if (strlen(optarg) > PATH_MAX) { fprintf(stderr, "%s: logfile path too long (limit %d chars)\n", argv[0], PATH_MAX); exit(1); } snmp_enable_filelog(optarg, netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES)); log_set = 1; } else { usage(argv[0]); } break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ case 'L': if (snmp_log_options( optarg, argc, argv ) < 0 ) { usage(argv[0]); } log_set = 1; break; case 'm': if (optarg != NULL) { setenv("MIBS", optarg, 1); } else { usage(argv[0]); } break; case 'M': if (optarg != NULL) { setenv("MIBDIRS", optarg, 1); } else { usage(argv[0]); } break; case 'n': if (optarg != NULL) { app_name = optarg; netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, app_name); } else { usage(argv[0]); } break; case 'P': printf("Warning: -P option is deprecated, use -p instead\n"); case 'p': if (optarg != NULL) { pid_file = optarg; } else { usage(argv[0]); } break; case 'q': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1); break; case 'r': netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS); break; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG case 's': printf("Warning: -s option is deprecated, use -Lsd instead\n"); snmp_enable_syslog(); log_set = 1; break; case 'S': printf("Warning: -S option is deprecated, use -Ls <facility> instead\n"); if (optarg != NULL) { switch (*optarg) { case 'd': case 'D': Facility = LOG_DAEMON; break; case 'i': case 'I': Facility = LOG_INFO; break; case '0': Facility = LOG_LOCAL0; break; case '1': Facility = LOG_LOCAL1; break; case '2': Facility = LOG_LOCAL2; break; case '3': Facility = LOG_LOCAL3; break; case '4': Facility = LOG_LOCAL4; break; case '5': Facility = LOG_LOCAL5; break; case '6': Facility = LOG_LOCAL6; break; case '7': Facility = LOG_LOCAL7; break; default: fprintf(stderr, "invalid syslog facility: -S%c\n",*optarg); usage(argv[0]); } snmp_enable_syslog_ident(snmp_log_syslogname(NULL), Facility); log_set = 1; } else { fprintf(stderr, "no syslog facility specified\n"); usage(argv[0]); } break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ case 'U': netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_LEAVE_PIDFILE); break; #if HAVE_UNISTD_H case 'u': if (optarg != NULL) { char *ecp; int uid; uid = strtoul(optarg, &ecp, 10); if (*ecp) { #if HAVE_GETPWNAM && HAVE_PWD_H info = getpwnam(optarg); if (info) { uid = info->pw_uid; } else { #endif fprintf(stderr, "Bad user id: %s\n", optarg); exit(1); #if HAVE_GETPWNAM && HAVE_PWD_H } #endif } netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID, uid); } else { usage(argv[0]); } break; #endif case 'v': version(); case 'V': netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE, 1); break; #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) case 'x': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, optarg); } else { usage(argv[0]); } netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_MASTER, 1); break; #endif case 'X': #if defined(USING_AGENTX_SUBAGENT_MODULE) agent_mode = SUB_AGENT; #else fprintf(stderr, "%s: Illegal argument -X:" "AgentX support not compiled in.\n", argv[0]); usage(argv[0]); exit(1); #endif break; case 'Y': netsnmp_config_remember(optarg); break; default: usage(argv[0]); break; } } if (do_help) { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); init_agent(app_name); /* register our .conf handlers */ init_mib_modules(); init_snmp(app_name); fprintf(stderr, "Configuration directives understood:\n"); read_config_print_usage(" "); exit(0); } if (optind < argc) { #ifndef NETSNMP_NO_LISTEN_SUPPORT /* * There are optional transport addresses on the command line. */ DEBUGMSGTL(("snmpd/main", "optind %d, argc %d\n", optind, argc)); for (i = optind; i < argc; i++) { char *c, *astring; if ((c = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS))) { astring = (char*)malloc(strlen(c) + 2 + strlen(argv[i])); if (astring == NULL) { fprintf(stderr, "malloc failure processing argv[%d]\n", i); exit(1); } sprintf(astring, "%s,%s", c, argv[i]); netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS, astring); SNMP_FREE(astring); } else { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS, argv[i]); } } DEBUGMSGTL(("snmpd/main", "port spec: %s\n", netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS))); #else /* NETSNMP_NO_LISTEN_SUPPORT */ fprintf(stderr, "You specified ports to open; this agent was built to only send notifications\n"); exit(1); #endif /* NETSNMP_NO_LISTEN_SUPPORT */ } #ifdef NETSNMP_LOGFILE #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE if (0 == log_set) snmp_enable_filelog(NETSNMP_LOGFILE, netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES)); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #endif #ifdef USING_UTIL_FUNCS_RESTART_MODULE { /* * Initialize a argv set to the current for restarting the agent. */ char *cptr, **argvptr; argvrestartp = (char **)malloc((argc + 2) * sizeof(char *)); argvptr = argvrestartp; for (i = 0, ret = 1; i < argc; i++) { ret += strlen(argv[i]) + 1; } argvrestart = (char *) malloc(ret); argvrestartname = (char *) malloc(strlen(argv[0]) + 1); if (!argvrestartp || !argvrestart || !argvrestartname) { fprintf(stderr, "malloc failure processing argvrestart\n"); exit(1); } strcpy(argvrestartname, argv[0]); for (cptr = argvrestart, i = 0; i < argc; i++) { strcpy(cptr, argv[i]); *(argvptr++) = cptr; cptr += strlen(argv[i]) + 1; } } #endif /* USING_UTIL_FUNCS_RESTART_MODULE */ if (agent_mode == -1) { if (strstr(argv[0], "agentxd") != NULL) { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, SUB_AGENT); } else { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, MASTER_AGENT); } } else { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, agent_mode); } SOCK_STARTUP; init_agent(app_name); /* do what we need to do first. */ init_mib_modules(); /* * start library */ init_snmp(app_name); if ((ret = init_master_agent()) != 0) { /* * Some error opening one of the specified agent transports. */ snmp_log(LOG_ERR, "Server Exiting with code 1\n"); exit(1); } /* * Initialize the world. Detach from the shell. Create initial user. */ if(!dont_fork) { int quit = ! netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY); ret = netsnmp_daemonize(quit, #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO snmp_stderrlog_status() #else /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ 0 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ ); /* * xxx-rks: do we care if fork fails? I think we should... */ if(ret != 0) { snmp_log(LOG_ERR, "Server Exiting with code 1\n"); exit(1); } } #if HAVE_GETPID if (pid_file != NULL) { /* * unlink the pid_file, if it exists, prior to open. Without * doing this the open will fail if the user specified pid_file * already exists. */ unlink(pid_file); fd = open(pid_file, O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd == -1) { snmp_log_perror(pid_file); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } else { if ((PID = fdopen(fd, "w")) == NULL) { snmp_log_perror(pid_file); exit(1); } else { fprintf(PID, "%d\n", (int) getpid()); fclose(PID); } #ifndef _MSC_VER /* The sequence open()/fdopen()/fclose()/close() makes MSVC crash, hence skip the close() call when using the MSVC runtime. */ close(fd); #endif } } #endif #if HAVE_UNISTD_H persistent_dir = get_persistent_directory(); mkdirhier( persistent_dir, NETSNMP_AGENT_DIRECTORY_MODE, 0 ); uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID); gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID); #ifdef HAVE_CHOWN if ( uid != 0 || gid != 0 ) chown( persistent_dir, uid, gid ); #endif #ifdef HAVE_SETGID if ((gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID)) != 0) { DEBUGMSGTL(("snmpd/main", "Changing gid to %d.\n", gid)); if (setgid(gid) == -1 #ifdef HAVE_SETGROUPS || setgroups(1, (gid_t *)&gid) == -1 #endif ) { snmp_log_perror("setgid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #ifdef HAVE_SETUID if ((uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID)) != 0) { #if HAVE_GETPWNAM && HAVE_PWD_H && HAVE_INITGROUPS /* * Set supplementary groups before changing UID * (which probably involves giving up privileges) */ info = getpwuid(uid); if (info) { DEBUGMSGTL(("snmpd/main", "Supplementary groups for %s.\n", info->pw_name)); if (initgroups(info->pw_name, (gid != 0 ? (gid_t)gid : info->pw_gid)) == -1) { snmp_log_perror("initgroups failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif DEBUGMSGTL(("snmpd/main", "Changing uid to %d.\n", uid)); if (setuid(uid) == -1) { snmp_log_perror("setuid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #endif /* * Store persistent data immediately in case we crash later. */ snmp_store(app_name); #ifdef SIGHUP DEBUGMSGTL(("signal", "registering SIGHUP signal handler\n")); signal(SIGHUP, SnmpdReconfig); #endif /* * Send coldstart trap if possible. */ send_easy_trap(0, 0); /* * We're up, log our version number. */ snmp_log(LOG_INFO, "NET-SNMP version %s\n", netsnmp_get_version()); #ifdef WIN32SERVICE agent_status = AGENT_RUNNING; #endif netsnmp_addrcache_initialise(); /* * Forever monitor the dest_port for incoming PDUs. */ DEBUGMSGTL(("snmpd/main", "We're up. Starting to process data.\n")); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY)) receive(); DEBUGMSGTL(("snmpd/main", "sending shutdown trap\n")); SnmpTrapNodeDown(); DEBUGMSGTL(("snmpd/main", "Bye...\n")); snmp_shutdown(app_name); shutdown_master_agent(); shutdown_agent(); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_LEAVE_PIDFILE) && (pid_file != NULL)) { unlink(pid_file); } #ifdef WIN32SERVICE agent_status = AGENT_STOPPED; #endif #ifdef USING_UTIL_FUNCS_RESTART_MODULE SNMP_FREE(argvrestartname); SNMP_FREE(argvrestart); SNMP_FREE(argvrestartp); #endif /* USING_UTIL_FUNCS_RESTART_MODULE */ SOCK_CLEANUP; return 0; } /* End main() -- snmpd */
/*++ * Function: BecomeNonRoot * * Purpose: If we are running as root, attempt to change that. * * Parameters: nada * * Returns: 0 on success * -1 on failure * * Authors: Dave McMurtrie <*****@*****.**> * * Notes: Relies on global copy of ProxyConfig_Struct "PC_Struct". * Also worth mentioning that instead of just becoming non-root * this function is now also responsible for chown()ing * the global statistics file. I had to choose * between doing a passwd and group lookup twice (and further * cluttering main) or doing the chown here where it * doesn't logically belong. I chose to put it here, but at * least I documented it... * * In addition to becoming non-root, this function now also * does a chroot() if so configured. Soon I'll rename this * function to something more fitting... *-- */ extern int BecomeNonRoot( void ) { char *fn = "BecomeNonRoot()"; struct passwd *pwent; /* ptr to a passwd file entry */ struct group *gp; /* ptr to a group file entry */ uid_t newuid; /* uid we want to run as */ gid_t newgid; /* gid we want to run as */ if ((pwent = getpwnam( PC_Struct.proc_username )) == NULL) { syslog(LOG_WARNING, "%s: getpwnam(%s) failed.", fn, PC_Struct.proc_username); return(-1); } else { newuid = pwent->pw_uid; } /* * Since the whole purpose here is to not run as root, make sure that * we don't get UID 0 back for username. */ if ( newuid == 0 ) { syslog( LOG_ERR, "%s: getpwnam returned UID 0 for '%s'.", fn, PC_Struct.proc_username ); return(-1); } if ((gp = getgrnam( PC_Struct.proc_groupname )) == NULL) { syslog(LOG_WARNING, "%s: getgrnam(%s) failed.", fn, PC_Struct.proc_groupname); return(-1); } else { newgid = gp->gr_gid; } /* * The chown() call gets stuck here. I hate it, but * once in a while there are going to be things in life that I hate. */ if ( chown( PC_Struct.stat_filename, newuid, newgid ) < 0 ) { syslog( LOG_WARNING, "%s: chown() failed to set ownership of file '%s' to '%s:%s': %s", fn, PC_Struct.stat_filename, PC_Struct.proc_username, PC_Struct.proc_groupname, strerror( errno ) ); return( -1 ); } /* * Now the whole reason this function exists... setgid and setuid. * * Patch by Jarno Huuskonen -- also drop any supplementary groups. */ syslog( LOG_INFO, "%s: Process will run as uid %d (%s) and gid %d (%s).", fn, newuid, PC_Struct.proc_username, newgid, PC_Struct.proc_groupname ); if ( setgroups( 0, NULL ) < 0 ) { syslog( LOG_WARNING, "%s: setgroups() failed: %s", fn, strerror( errno ) ); return( -1 ); } if ((setgid(newgid)) < 0 ) { syslog(LOG_WARNING, "%s: setgid(%d) failed: %s", fn, newgid, strerror(errno)); return(-1); } /* * Patch originally by Dave Steinberg, and later modified by * Jarno Huuskonen -- chroot() if so configured. */ if ( PC_Struct.chroot_directory ) { if ( chroot( PC_Struct.chroot_directory ) < 0 || chdir( "/" ) < 0 ) { syslog( LOG_WARNING, "%s: chroot(%s) failed: %s", fn, PC_Struct.chroot_directory, strerror( errno ) ); return( -1 ); } syslog( LOG_INFO, "%s: Process chrooted in %s", fn, PC_Struct.chroot_directory ); } if ((setuid(newuid)) < 0 ) { syslog(LOG_WARNING,"%s: setuid(%d) failed: %s", fn, newuid, strerror(errno)); return(-1); } return(0); }
int main(int argc, char **argv) { int i; sigset_t set; #if ENABLE_LINUXDVB uint32_t adapter_mask; #endif int log_level = LOG_INFO; int log_options = TVHLOG_OPT_MILLIS | TVHLOG_OPT_STDERR | TVHLOG_OPT_SYSLOG; const char *log_debug = NULL, *log_trace = NULL; char buf[512]; main_tid = pthread_self(); /* Setup global mutexes */ pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_init(&atomic_lock, NULL); pthread_cond_init(>imer_cond, NULL); /* Defaults */ tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; tvheadend_htsp_port_extra = 0; /* Command line options */ int opt_help = 0, opt_version = 0, opt_fork = 0, opt_firstrun = 0, opt_stderr = 0, opt_syslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, opt_fileline = 0, opt_threadid = 0, opt_ipv6 = 0, opt_tsfile_tuner = 0, opt_dump = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, *opt_logpath = NULL, *opt_log_debug = NULL, *opt_log_trace = NULL, *opt_pidpath = "/var/run/tvheadend.pid", #if ENABLE_LINUXDVB *opt_dvb_adapters = NULL, #endif *opt_bindaddr = NULL, *opt_subscribe = NULL; str_list_t opt_tsfile = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; cmdline_opt_t cmdline_opts[] = { { 0, NULL, "Generic Options", OPT_BOOL, NULL }, { 'h', "help", "Show this page", OPT_BOOL, &opt_help }, { 'v', "version", "Show version infomation", OPT_BOOL, &opt_version }, { 0, NULL, "Service Configuration", OPT_BOOL, NULL }, { 'c', "config", "Alternate config path", OPT_STR, &opt_config }, { 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork }, { 'u', "user", "Run as user", OPT_STR, &opt_user }, { 'g', "group", "Run as group", OPT_STR, &opt_group }, { 'p', "pid", "Alternate pid path", OPT_STR, &opt_pidpath }, { 'C', "firstrun", "If no user account exists then create one with\n" "no username and no password. Use with care as\n" "it will allow world-wide administrative access\n" "to your Tvheadend installation until you edit\n" "the access-control from within the Tvheadend UI", OPT_BOOL, &opt_firstrun }, #if ENABLE_LINUXDVB { 'a', "adapters", "Only use specified DVB adapters (comma separated)", OPT_STR, &opt_dvb_adapters }, #endif { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, { '6', "ipv6", "Listen on IPv6", OPT_BOOL, &opt_ipv6 }, { 'b', "bindaddr", "Specify bind address", OPT_STR, &opt_bindaddr}, { 0, "http_port", "Specify alternative http port", OPT_INT, &tvheadend_webui_port }, { 0, "http_root", "Specify alternative http webroot", OPT_STR, &tvheadend_webroot }, { 0, "htsp_port", "Specify alternative htsp port", OPT_INT, &tvheadend_htsp_port }, { 0, "htsp_port2", "Specify extra htsp port", OPT_INT, &tvheadend_htsp_port_extra }, { 0, NULL, "Debug Options", OPT_BOOL, NULL }, { 'd', "stderr", "Enable debug on stderr", OPT_BOOL, &opt_stderr }, { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, { 'l', "logfile", "Enable debug to file", OPT_STR, &opt_logpath }, { 0, "debug", "Enable debug subsystems", OPT_STR, &opt_log_debug }, #if ENABLE_TRACE { 0, "trace", "Enable trace subsystems", OPT_STR, &opt_log_trace }, #endif { 0, "fileline", "Add file and line numbers to debug", OPT_BOOL, &opt_fileline }, { 0, "threadid", "Add the thread ID to debug", OPT_BOOL, &opt_threadid }, { 0, "uidebug", "Enable webUI debug (non-minified JS)", OPT_BOOL, &opt_uidebug }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, { 'D', "dump", "Enable coredumps for daemon", OPT_BOOL, &opt_dump }, { 0, "noacl", "Disable all access control checks", OPT_BOOL, &opt_noacl }, { 'j', "join", "Subscribe to a service permanently", OPT_STR, &opt_subscribe }, { 0, NULL, "TODO: testing", OPT_BOOL, NULL }, { 0, "tsfile_tuners", "Number of tsfile tuners", OPT_INT, &opt_tsfile_tuner }, { 0, "tsfile", "tsfile input (mux file)", OPT_STR_LIST, &opt_tsfile }, }; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); /* Set locale */ setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* make sure the timezone is set */ tzset(); /* Process command line */ for (i = 1; i < argc; i++) { /* Find option */ cmdline_opt_t *opt = cmdline_opt_find(cmdline_opts, ARRAY_SIZE(cmdline_opts), argv[i]); if (!opt) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), "invalid option specified [%s]", argv[i]); /* Process */ if (opt->type == OPT_BOOL) *((int*)opt->param) = 1; else if (++i == argc) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), "option %s requires a value", opt->lopt); else if (opt->type == OPT_INT) *((int*)opt->param) = atoi(argv[i]); else if (opt->type == OPT_STR_LIST) { str_list_t *strl = opt->param; if (strl->num < strl->max) strl->str[strl->num++] = argv[i]; } else *((char**)opt->param) = argv[i]; /* Stop processing */ if (opt_help) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), NULL); if (opt_version) show_version(argv[0]); } /* Additional cmdline processing */ #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; } else { char *p, *e; char *r = NULL; char *dvb_adapters = strdup(opt_dvb_adapters); adapter_mask = 0x0; p = strtok_r(dvb_adapters, ",", &r); while (p) { int a = strtol(p, &e, 10); if (*e != 0 || a < 0 || a > 31) { tvhlog(LOG_ERR, "START", "Invalid adapter number '%s'", p); free(dvb_adapters); return 1; } adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } free(dvb_adapters); if (!adapter_mask) { tvhlog(LOG_ERR, "START", "No adapters specified!"); return 1; } } #endif if (tvheadend_webroot) { char *tmp; if (*tvheadend_webroot == '/') tmp = strdup(tvheadend_webroot); else { tmp = malloc(strlen(tvheadend_webroot)+2); *tmp = '/'; strcpy(tmp+1, tvheadend_webroot); } if (tmp[strlen(tmp)-1] == '/') tmp[strlen(tmp)-1] = '\0'; tvheadend_webroot = tmp; } tvheadend_webui_debug = opt_uidebug; /* Setup logging */ if (isatty(2)) log_options |= TVHLOG_OPT_DECORATE; if (opt_stderr || opt_syslog || opt_logpath) { if (!opt_log_trace && !opt_log_debug) log_debug = "all"; log_level = LOG_DEBUG; if (opt_stderr) log_options |= TVHLOG_OPT_DBG_STDERR; if (opt_syslog) log_options |= TVHLOG_OPT_DBG_SYSLOG; if (opt_logpath) log_options |= TVHLOG_OPT_DBG_FILE; } if (opt_fileline) log_options |= TVHLOG_OPT_FILELINE; if (opt_threadid) log_options |= TVHLOG_OPT_THREAD; if (opt_log_trace) { log_level = LOG_TRACE; log_trace = opt_log_trace; } if (opt_log_debug) log_debug = opt_log_debug; tvhlog_init(log_level, log_options, opt_logpath); tvhlog_set_debug(log_debug); tvhlog_set_trace(log_trace); signal(SIGPIPE, handle_sigpipe); // will be redundant later /* Daemonise */ if(opt_fork) { const char *homedir; gid_t gid; uid_t uid; struct group *grp = getgrnam(opt_group ?: "video"); struct passwd *pw = opt_user ? getpwnam(opt_user) : NULL; FILE *pidfile = fopen(opt_pidpath, "w+"); if(grp != NULL) { gid = grp->gr_gid; } else { gid = 1; } if (pw != NULL) { if (getuid() != pw->pw_uid) { gid_t glist[10]; int gnum; gnum = get_user_groups(pw, glist, 10); if (setgroups(gnum, glist)) { tvhlog(LOG_ALERT, "START", "setgroups() failed, do you have permission?"); return 1; } } uid = pw->pw_uid; homedir = pw->pw_dir; setenv("HOME", homedir, 1); } else { uid = 1; } if ((getgid() != gid) && setgid(gid)) { tvhlog(LOG_ALERT, "START", "setgid() failed, do you have permission?"); return 1; } if ((getuid() != uid) && setuid(uid)) { tvhlog(LOG_ALERT, "START", "setuid() failed, do you have permission?"); return 1; } if(daemon(0, 0)) { exit(2); } if(pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } /* Make dumpable */ if (opt_dump) { #ifdef PLATFORM_LINUX if (chdir("/tmp")) tvhwarn("START", "failed to change cwd to /tmp"); prctl(PR_SET_DUMPABLE, 1); #else tvhwarn("START", "Coredumps not implemented on your platform"); #endif } umask(0); } tvheadend_running = 1; /* Start log thread (must be done post fork) */ tvhlog_start(); /* Alter logging */ if (opt_fork) tvhlog_options &= ~TVHLOG_OPT_STDERR; if (!isatty(2)) tvhlog_options &= ~TVHLOG_OPT_DECORATE; /* Initialise clock */ pthread_mutex_lock(&global_lock); time(&dispatch_clock); /* Signal handling */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); trap_init(argv[0]); /* Initialise configuration */ uuid_init(); idnode_init(); config_init(opt_config); /** * Initialize subsystems */ api_init(); fsmonitor_init(); #if ENABLE_LIBAV libav_init(); transcoding_init(); #endif imagecache_init(); service_init(); #if ENABLE_TSFILE if(opt_tsfile.num) { tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num); for (i = 0; i < opt_tsfile.num; i++) tsfile_add_file(opt_tsfile.str[i]); } #endif #if ENABLE_MPEGTS_DVB dvb_network_init(); #endif #if ENABLE_IPTV iptv_init(); #endif #if ENABLE_LINUXDVB linuxdvb_init(adapter_mask); #endif channel_init(); subscription_init(); access_init(opt_firstrun, opt_noacl); #if ENABLE_TIMESHIFT timeshift_init(); #endif http_client_init(); tcp_server_init(opt_ipv6); http_server_init(opt_bindaddr); webui_init(); service_mapper_init(); descrambler_init(); epggrab_init(); epg_init(); dvr_init(); htsp_init(opt_bindaddr); if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); avahi_init(); epg_updated(); // cleanup now all prev ref's should have been created pthread_mutex_unlock(&global_lock); /** * Wait for SIGTERM / SIGINT, but only in this thread */ sigemptyset(&set); sigaddset(&set, SIGTERM); sigaddset(&set, SIGINT); signal(SIGTERM, doexit); signal(SIGINT, doexit); pthread_sigmask(SIG_UNBLOCK, &set, NULL); tvhlog(LOG_NOTICE, "START", "HTS Tvheadend version %s started, " "running as PID:%d UID:%d GID:%d, CWD:%s CNF:%s", tvheadend_version, getpid(), getuid(), getgid(), getcwd(buf, sizeof(buf)), hts_settings_get_root()); if(opt_abort) abort(); mainloop(); tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); tvhftrace("main", http_client_done); tvhftrace("main", fsmonitor_done); #if ENABLE_MPEGTS_DVB tvhftrace("main", dvb_network_done); #endif #if ENABLE_IPTV tvhftrace("main", iptv_done); #endif #if ENABLE_LINUXDVB tvhftrace("main", linuxdvb_done); #endif #if ENABLE_TSFILE tvhftrace("main", tsfile_done); #endif // Note: the locking is obviously a bit redundant, but without // we need to disable the gtimer_arm call in epg_save() pthread_mutex_lock(&global_lock); tvhftrace("main", epg_save); #if ENABLE_TIMESHIFT tvhftrace("main", timeshift_term); #endif pthread_mutex_unlock(&global_lock); tvhftrace("main", epggrab_done); tvhftrace("main", tcp_server_done); tvhftrace("main", descrambler_done); tvhftrace("main", service_mapper_done); tvhftrace("main", service_done); tvhftrace("main", channel_done); tvhftrace("main", dvr_done); tvhftrace("main", subscription_done); tvhftrace("main", access_done); tvhftrace("main", epg_done); tvhftrace("main", avahi_done); tvhftrace("main", imagecache_done); tvhftrace("main", idnode_done); tvhftrace("main", lang_code_done); tvhftrace("main", api_done); tvhftrace("main", config_done); tvhftrace("main", hts_settings_done); tvhftrace("main", dvb_done); tvhftrace("main", lang_str_done); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); tvhlog_end(); if(opt_fork) unlink(opt_pidpath); free(opt_tsfile.str); return 0; }
int table_api_dispatch(void) { #if 0 struct passwd *pw; #endif ssize_t n; #if 0 pw = getpwnam(user); if (pw == NULL) { log_warn("table-api: getpwnam"); fatalx("table-api: exiting"); } if (rootpath) { if (chroot(rootpath) == -1) { log_warn("table-api: chroot"); fatalx("table-api: exiting"); } if (chdir("/") == -1) { log_warn("table-api: chdir"); fatalx("table-api: exiting"); } } if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) { log_warn("table-api: cannot drop privileges"); fatalx("table-api: exiting"); } #endif imsg_init(&ibuf, 0); while (1) { n = imsg_get(&ibuf, &imsg); if (n == -1) { log_warn("warn: table-api: imsg_get"); break; } if (n) { rdata = imsg.data; rlen = imsg.hdr.len - IMSG_HEADER_SIZE; table_msg_dispatch(); if (quit) break; imsg_flush(&ibuf); continue; } n = imsg_read(&ibuf); if (n == -1 && errno != EAGAIN) { log_warn("warn: table-api: imsg_read"); break; } if (n == 0) { log_warnx("warn: table-api: pipe closed"); break; } } return (1); }
pid_t pony(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("pony: cannot fork"); case 0: post_fork(PROC_PONY); break; default: return (pid); } mda_postfork(); mta_postfork(); smtp_postfork(); filter_postfork(); /* do not purge listeners and pki, they are purged * in smtp_configure() */ purge_config(PURGE_TABLES|PURGE_RULES); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); if (chroot(PATH_CHROOT) == -1) fatal("pony: chroot"); if (chdir("/") == -1) fatal("pony: chdir(\"/\")"); config_process(PROC_PONY); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("pony: cannot drop privileges"); imsg_callback = pony_imsg; event_init(); mda_postprivdrop(); mta_postprivdrop(); smtp_postprivdrop(); signal_set(&ev_sigint, SIGINT, pony_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, pony_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); config_peer(PROC_PARENT); config_peer(PROC_QUEUE); config_peer(PROC_LKA); config_peer(PROC_CONTROL); config_peer(PROC_CA); config_done(); ca_engine_init(); if (event_dispatch() < 0) fatal("event_dispatch"); pony_shutdown(); return (0); }
int main(int argc, char **argv) { enum { NNP = CHAR_MAX + 1, RUID, EUID, RGID, EGID, REUID, REGID, CLEAR_GROUPS, KEEP_GROUPS, GROUPS, INHCAPS, LISTCAPS, CAPBSET, SECUREBITS, SELINUX_LABEL, APPARMOR_PROFILE }; static const struct option longopts[] = { {"dump", no_argument, 0, 'd'}, {"nnp", no_argument, 0, NNP}, {"no-new-privs", no_argument, 0, NNP}, {"inh-caps", required_argument, 0, INHCAPS}, {"list-caps", no_argument, 0, LISTCAPS}, {"ruid", required_argument, 0, RUID}, {"euid", required_argument, 0, EUID}, {"rgid", required_argument, 0, RGID}, {"egid", required_argument, 0, EGID}, {"reuid", required_argument, 0, REUID}, {"regid", required_argument, 0, REGID}, {"clear-groups", no_argument, 0, CLEAR_GROUPS}, {"keep-groups", no_argument, 0, KEEP_GROUPS}, {"groups", required_argument, 0, GROUPS}, {"bounding-set", required_argument, 0, CAPBSET}, {"securebits", required_argument, 0, SECUREBITS}, {"selinux-label", required_argument, 0, SELINUX_LABEL}, {"apparmor-profile", required_argument, 0, APPARMOR_PROFILE}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {NULL, 0, 0, 0} }; static const ul_excl_t excl[] = { /* keep in same order with enum definitions */ {CLEAR_GROUPS, KEEP_GROUPS, GROUPS}, {0} }; int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; int c; struct privctx opts; int dumplevel = 0; int total_opts = 0; int list_caps = 0; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); atexit(close_stdout); memset(&opts, 0, sizeof(opts)); while ((c = getopt_long(argc, argv, "+dhV", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); total_opts++; switch (c) { case 'd': dumplevel++; break; case NNP: if (opts.nnp) errx(EXIT_FAILURE, _("duplicate --no-new-privs option")); opts.nnp = 1; break; case RUID: if (opts.have_ruid) errx(EXIT_FAILURE, _("duplicate ruid")); opts.have_ruid = 1; opts.ruid = get_user(optarg, _("failed to parse ruid")); break; case EUID: if (opts.have_euid) errx(EXIT_FAILURE, _("duplicate euid")); opts.have_euid = 1; opts.euid = get_user(optarg, _("failed to parse euid")); break; case REUID: if (opts.have_ruid || opts.have_euid) errx(EXIT_FAILURE, _("duplicate ruid or euid")); opts.have_ruid = opts.have_euid = 1; opts.ruid = opts.euid = get_user(optarg, _("failed to parse reuid")); break; case RGID: if (opts.have_rgid) errx(EXIT_FAILURE, _("duplicate rgid")); opts.have_rgid = 1; opts.rgid = get_group(optarg, _("failed to parse rgid")); break; case EGID: if (opts.have_egid) errx(EXIT_FAILURE, _("duplicate egid")); opts.have_egid = 1; opts.egid = get_group(optarg, _("failed to parse egid")); break; case REGID: if (opts.have_rgid || opts.have_egid) errx(EXIT_FAILURE, _("duplicate rgid or egid")); opts.have_rgid = opts.have_egid = 1; opts.rgid = opts.egid = get_group(optarg, _("failed to parse regid")); break; case CLEAR_GROUPS: if (opts.clear_groups) errx(EXIT_FAILURE, _("duplicate --clear-groups option")); opts.clear_groups = 1; break; case KEEP_GROUPS: if (opts.keep_groups) errx(EXIT_FAILURE, _("duplicate --keep-groups option")); opts.keep_groups = 1; break; case GROUPS: if (opts.have_groups) errx(EXIT_FAILURE, _("duplicate --groups option")); parse_groups(&opts, optarg); break; case LISTCAPS: list_caps = 1; break; case INHCAPS: if (opts.caps_to_inherit) errx(EXIT_FAILURE, _("duplicate --inh-caps option")); opts.caps_to_inherit = optarg; break; case CAPBSET: if (opts.bounding_set) errx(EXIT_FAILURE, _("duplicate --bounding-set option")); opts.bounding_set = optarg; break; case SECUREBITS: if (opts.have_securebits) errx(EXIT_FAILURE, _("duplicate --securebits option")); parse_securebits(&opts, optarg); break; case SELINUX_LABEL: if (opts.selinux_label) errx(EXIT_FAILURE, _("duplicate --selinux-label option")); opts.selinux_label = optarg; break; case APPARMOR_PROFILE: if (opts.apparmor_profile) errx(EXIT_FAILURE, _("duplicate --apparmor-profile option")); opts.apparmor_profile = optarg; break; case 'h': usage(stdout); case 'V': printf(UTIL_LINUX_VERSION); return EXIT_SUCCESS; case '?': usage(stderr); default: errx(EXIT_FAILURE, _("unrecognized option '%c'"), c); } } if (dumplevel) { if (total_opts != dumplevel || optind < argc) errx(EXIT_FAILURE, _("--dump is incompatible with all other options")); dump(dumplevel); return EXIT_SUCCESS; } if (list_caps) { if (total_opts != 1 || optind < argc) errx(EXIT_FAILURE, _("--list-caps must be specified alone")); list_known_caps(); return EXIT_SUCCESS; } if (argc <= optind) errx(EXIT_FAILURE, _("No program specified")); if ((opts.have_rgid || opts.have_egid) && !opts.keep_groups && !opts.clear_groups && !opts.have_groups) errx(EXIT_FAILURE, _("--[re]gid requires --keep-groups, --clear-groups, or --groups")); if (opts.nnp) if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) err(EXIT_FAILURE, _("disallow granting new privileges failed")); if (opts.selinux_label) do_selinux_label(opts.selinux_label); if (opts.apparmor_profile) do_apparmor_profile(opts.apparmor_profile); if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) err(EXIT_FAILURE, _("keep process capabilities failed")); /* We're going to want CAP_SETPCAP, CAP_SETUID, and CAP_SETGID if * possible. */ bump_cap(CAP_SETPCAP); bump_cap(CAP_SETUID); bump_cap(CAP_SETGID); if (capng_apply(CAPNG_SELECT_CAPS) != 0) err(SETPRIV_EXIT_PRIVERR, _("activate capabilities")); if (opts.have_ruid || opts.have_euid) { do_setresuid(&opts); /* KEEPCAPS doesn't work for the effective mask. */ if (capng_apply(CAPNG_SELECT_CAPS) != 0) err(SETPRIV_EXIT_PRIVERR, _("reactivate capabilities")); } if (opts.have_rgid || opts.have_egid) do_setresgid(&opts); if (opts.have_groups) { if (setgroups(opts.num_groups, opts.groups) != 0) err(SETPRIV_EXIT_PRIVERR, _("setgroups failed")); } else if (opts.clear_groups) { gid_t x = 0; if (setgroups(0, &x) != 0) err(SETPRIV_EXIT_PRIVERR, _("setgroups failed")); } if (opts.have_securebits) if (prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0) err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed")); if (opts.bounding_set) { do_caps(CAPNG_BOUNDING_SET, opts.bounding_set); errno = EPERM; /* capng doesn't set errno if we're missing CAP_SETPCAP */ if (capng_apply(CAPNG_SELECT_BOUNDS) != 0) err(SETPRIV_EXIT_PRIVERR, _("apply bounding set")); } if (opts.caps_to_inherit) { do_caps(CAPNG_INHERITABLE, opts.caps_to_inherit); if (capng_apply(CAPNG_SELECT_CAPS) != 0) err(SETPRIV_EXIT_PRIVERR, _("apply capabilities")); } execvp(argv[optind], argv + optind); err(EXIT_FAILURE, _("cannot execute: %s"), argv[optind]); }
static void f_create_process( INT32 args ) { struct perishables storage; struct array *cmd = 0; struct mapping *optional = 0; struct svalue *tmp; int e; int stds[3]; int *fds; int num_fds = 3; int wanted_gid=0, wanted_uid=0; int gid_request=0, uid_request=0; char *tmp_cwd = NULL; pid_t pid=-2; extern char **environ; fds = stds; storage.env = NULL; storage.argv = NULL; storage.disabled = 0; storage.fds = NULL; storage.limits = NULL; check_all_args("create_process",args, BIT_ARRAY, BIT_MAPPING | BIT_VOID, 0); switch(args) { default: optional=Pike_sp[1-args].u.mapping; mapping_fix_type_field(optional); if(m_ind_types(optional) & ~BIT_STRING) Pike_error("Bad index type in argument 2 to Caudium.create_process()\n"); case 1: cmd=Pike_sp[-args].u.array; if(cmd->size < 1) Pike_error("Too few elements in argument array.\n"); for(e=0; e<cmd->size; e++) if(ITEM(cmd)[e].type!=T_STRING) Pike_error("Argument is not a string.\n"); array_fix_type_field(cmd); if(cmd->type_field & ~BIT_STRING) Pike_error("Bad argument 1 to Caudium.create_process().\n"); } if (optional) { if ((tmp = simple_mapping_string_lookup(optional, "gid"))) { switch(tmp->type) { case T_INT: wanted_gid = tmp->u.integer; gid_request = 1; break; default: Pike_error("Invalid argument for gid."); break; } } if ((tmp = simple_mapping_string_lookup(optional, "uid"))) { switch(tmp->type) { case T_INT: wanted_uid = tmp->u.integer; uid_request = 1; break; default: Pike_error("Invalid argument for uid."); break; } } if((tmp = simple_mapping_string_lookup( optional, "cwd" )) && tmp->type == T_STRING && !tmp->u.string->size_shift) tmp_cwd = tmp->u.string->str; if((tmp = simple_mapping_string_lookup( optional, "stdin" )) && tmp->type == T_OBJECT) { fds[0] = fd_from_object( tmp->u.object ); if(fds[0] == -1) Pike_error("Invalid stdin file\n"); } if((tmp = simple_mapping_string_lookup( optional, "stdout" )) && tmp->type == T_OBJECT) { fds[1] = fd_from_object( tmp->u.object ); if(fds[1] == -1) Pike_error("Invalid stdout file\n"); } if((tmp = simple_mapping_string_lookup( optional, "stderr" )) && tmp->type == T_OBJECT) { fds[2] = fd_from_object( tmp->u.object ); if(fds[2] == -1) Pike_error("Invalid stderr file\n"); } if((tmp=simple_mapping_string_lookup(optional, "rlimit"))) { struct svalue *tmp2; if(tmp->type != T_MAPPING) Pike_error("Wrong type of argument for the 'rusage' option. " "Should be mapping.\n"); #define ADD_LIMIT(X,Y,Z) internal_add_limit(&storage,X,Y,Z); #ifdef RLIMIT_NPROC if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "nproc"))) ADD_LIMIT( "nproc", RLIMIT_NPROC, tmp2 ); #endif #ifdef RLIMIT_MEMLOCK if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "memlock"))) ADD_LIMIT( "memlock", RLIMIT_MEMLOCK, tmp2 ); #endif #ifdef RLIMIT_RSS if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "rss"))) ADD_LIMIT( "rss", RLIMIT_RSS, tmp2 ); #endif #ifdef RLIMIT_CORE if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "core"))) ADD_LIMIT( "core", RLIMIT_CORE, tmp2 ); #endif #ifdef RLIMIT_CPU if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "cpu"))) ADD_LIMIT( "cpu", RLIMIT_CPU, tmp2 ); #endif #ifdef RLIMIT_DATA if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "data"))) ADD_LIMIT( "data", RLIMIT_DATA, tmp2 ); #endif #ifdef RLIMIT_FSIZE if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "fsize"))) ADD_LIMIT( "fsize", RLIMIT_FSIZE, tmp2 ); #endif #ifdef RLIMIT_NOFILE if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "nofile"))) ADD_LIMIT( "nofile", RLIMIT_NOFILE, tmp2 ); #endif #ifdef RLIMIT_STACK if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "stack"))) ADD_LIMIT( "stack", RLIMIT_STACK, tmp2 ); #endif #ifdef RLIMIT_VMEM if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "map_mem")) ||(tmp2=simple_mapping_string_lookup(tmp->u.mapping, "vmem"))) ADD_LIMIT( "map_mem", RLIMIT_VMEM, tmp2 ); #endif #ifdef RLIMIT_AS if((tmp2=simple_mapping_string_lookup(tmp->u.mapping, "as")) ||(tmp2=simple_mapping_string_lookup(tmp->u.mapping, "mem"))) ADD_LIMIT( "mem", RLIMIT_AS, tmp2 ); #endif #undef ADD_LIMIT } } if((tmp=simple_mapping_string_lookup(optional, "env"))) { if(tmp->type == T_MAPPING) { struct mapping *m=tmp->u.mapping; struct array *i,*v; int ptr=0; i=mapping_indices(m); v=mapping_values(m); storage.env=(char **)xalloc((1+m_sizeof(m)) * sizeof(char *)); for(e=0;e<i->size;e++) { if(ITEM(i)[e].type == T_STRING && ITEM(v)[e].type == T_STRING) { check_stack(3); ref_push_string(ITEM(i)[e].u.string); push_string(make_shared_string("=")); ref_push_string(ITEM(v)[e].u.string); f_add(3); storage.env[ptr++]=Pike_sp[-1].u.string->str; } } storage.env[ptr++]=0; free_array(i); free_array(v); } } storage.argv = (char **)xalloc((1 + cmd->size) * sizeof(char *)); for (e = 0; e < cmd->size; e++) storage.argv[e] = ITEM(cmd)[e].u.string->str; storage.argv[e] = 0; th_atfork_prepare(); pid = fork(); if (pid) { th_atfork_parent(); } else { th_atfork_child(); } if (pid == -1) { Pike_error("Caudium.create_process() failed."); } else if (pid) { pop_n_elems(args); push_int(pid); return; } else { if(storage.limits) { struct plimit *l = storage.limits; while(l) { int tmpres = setrlimit( l->resource, &l->rlp ); l = l->next; } } if(storage.env) environ = storage.env; chdir(tmp_cwd); seteuid(0); setegid(0); setgroups(0, NULL); if (gid_request) setgid(wanted_gid); if (uid_request) setuid(wanted_uid); dup2(fds[0], 0); dup2(fds[1], 1); dup2(fds[2], 2); set_close_on_exec(0,0); set_close_on_exec(1,0); set_close_on_exec(2,0); execvp(storage.argv[0],storage.argv); exit(99); } pop_n_elems(args); push_int(0); }
static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { int close_fd; int use_fd; int fd; if (options->flags & UV_PROCESS_DETACHED) setsid(); for (fd = 0; fd < stdio_count; fd++) { close_fd = pipes[fd][0]; use_fd = pipes[fd][1]; if (use_fd < 0) { if (fd >= 3) continue; else { /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is * set */ use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; if (use_fd == -1) { uv__write_int(error_fd, -errno); _exit(127); } } } if (fd == use_fd) uv__cloexec(use_fd, 0); else dup2(use_fd, fd); if (fd <= 2) uv__nonblock(fd, 0); if (close_fd >= stdio_count) uv__close(close_fd); } for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd >= 0 && fd != use_fd) close(use_fd); } if (options->cwd != NULL && chdir(options->cwd)) { uv__write_int(error_fd, -errno); _exit(127); } if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will * remove any extraneous groups. If we don't call this, then * even though our uid has dropped, we may still have groups * that enable us to do super-user things. This will fail if we * aren't root, so don't bother checking the return value, this * is just done as an optimistic privilege dropping function. */ SAVE_ERRNO(setgroups(0, NULL)); } if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { uv__write_int(error_fd, -errno); _exit(127); } if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { uv__write_int(error_fd, -errno); _exit(127); } if (options->env != NULL) { environ = options->env; } execvp(options->file, options->args); uv__write_int(error_fd, -errno); _exit(127); }
static int attach_child_main(void* data) { struct attach_clone_payload* payload = (struct attach_clone_payload*)data; int ipc_socket = payload->ipc_socket; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; #if HAVE_SYS_PERSONALITY_H long new_personality; #endif int ret; int status; int expected; long flags; int fd; int lsm_labelfd; uid_t new_uid; gid_t new_gid; /* Wait for the initial thread to signal us that it's ready for us to * start initializing. */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 0: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* A description of the purpose of this functionality is provided in the * lxc-attach(1) manual page. We have to remount here and not in the * parent process, otherwise /proc may not properly reflect the new pid * namespace. */ if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) { shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Now perform additional attachments. */ #if HAVE_SYS_PERSONALITY_H if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { ret = personality(new_personality); if (ret < 0) { SYSERROR("Could not ensure correct architecture."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) { ERROR("Could not drop privileges."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) * if you want this to be a no-op). */ ret = lxc_attach_set_environment(options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) { ERROR("Could not set initial environment for attached process."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Set {u,g}id. */ new_uid = 0; new_gid = 0; /* Ignore errors, we will fall back to root in that case (/proc was not * mounted etc.). */ if (options->namespaces & CLONE_NEWUSER) lxc_attach_get_init_uidgid(&new_uid, &new_gid); if (options->uid != (uid_t)-1) new_uid = options->uid; if (options->gid != (gid_t)-1) new_gid = options->gid; /* Setup the controlling tty. */ if (options->stdin_fd && isatty(options->stdin_fd)) { if (setsid() < 0) { SYSERROR("Unable to setsid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) { SYSERROR("Unable to set TIOCSTTY."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Try to set the {u,g}id combination. */ if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) { if (setgid(new_gid) || setgroups(0, NULL)) { SYSERROR("Switching to container gid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { SYSERROR("Switching to container uid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Tell initial process it may now put us into cgroups. */ status = 1; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret != sizeof(status)) { ERROR("Intended to send sequence number 1: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Wait for the initial thread to signal us that it has done everything * for us when it comes to cgroups etc. */ expected = 2; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 2: %s", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->no_new_privs) || (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. " "Process can use execve() gainable " "privileges."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() " "gainable privileges."); } /* Tell the (grand)parent to send us LSM label fd. */ status = 3; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret <= 0) { ERROR("Intended to send sequence number 3: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec; /* Receive fd for LSM security module. */ ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0); if (ret <= 0) { ERROR("Expected to receive file descriptor: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; if (lsm_set_label_at(lsm_labelfd, on_exec, init_ctx->lsm_label) < 0) { SYSERROR("Failed to set LSM label."); shutdown(ipc_socket, SHUT_RDWR); close(lsm_labelfd); rexit(-1); } close(lsm_labelfd); } if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp && (lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) { ERROR("Failed to load seccomp policy."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } shutdown(ipc_socket, SHUT_RDWR); close(ipc_socket); lxc_proc_put_context_info(init_ctx); /* The following is done after the communication socket is shut down. * That way, all errors that might (though unlikely) occur up until this * point will have their messages printed to the original stderr (if * logging is so configured) and not the fd the user supplied, if any. */ /* Fd handling for stdin, stdout and stderr; ignore errors here, user * may want to make sure the fds are closed, for example. */ if (options->stdin_fd >= 0 && options->stdin_fd != 0) dup2(options->stdin_fd, 0); if (options->stdout_fd >= 0 && options->stdout_fd != 1) dup2(options->stdout_fd, 1); if (options->stderr_fd >= 0 && options->stderr_fd != 2) dup2(options->stderr_fd, 2); /* close the old fds */ if (options->stdin_fd > 2) close(options->stdin_fd); if (options->stdout_fd > 2) close(options->stdout_fd); if (options->stderr_fd > 2) close(options->stderr_fd); /* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also * here, ignore errors. */ for (fd = 0; fd <= 2; fd++) { flags = fcntl(fd, F_GETFL); if (flags < 0) continue; if (flags & FD_CLOEXEC) if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0) SYSERROR("Unable to clear FD_CLOEXEC from file descriptor."); } /* We're done, so we can now do whatever the user intended us to do. */ rexit(payload->exec_function(payload->exec_payload)); }
static int process_security_set_cap(request_rec *r) { int ncap; cap_t cap; cap_value_t capval[3]; gid_t gid; uid_t uid; ncap = 2; process_security_dir_config_t *dconf = ap_get_module_config(r->per_dir_config, &process_security_module); process_security_config_t *conf = ap_get_module_config(r->server->module_config, &process_security_module); if (dconf->process_security_mode==PS_MODE_STAT || dconf->process_security_mode==PS_MODE_UNDEFINED) { gid = r->finfo.group; uid = r->finfo.user; } else { gid = conf->default_gid; uid = conf->default_uid; } cap = cap_init(); capval[0] = CAP_SETUID; capval[1] = CAP_SETGID; cap_set_flag(cap, CAP_PERMITTED, ncap, capval, CAP_SET); if (cap_set_proc(cap) != 0) ap_log_error(APLOG_MARK , APLOG_ERR , 0 , NULL , "%s ERROR %s:cap_set_proc failed" , MODULE_NAME , __func__ ); cap_free(cap); coredump = prctl(PR_GET_DUMPABLE); cap = cap_get_proc(); cap_set_flag(cap, CAP_EFFECTIVE, ncap, capval, CAP_SET); if (cap_set_proc(cap) != 0) ap_log_error (APLOG_MARK , APLOG_ERR , 0 , NULL , "%s ERROR %s:cap_set_proc failed before setuid" , MODULE_NAME , __func__ ); cap_free(cap); setgroups(0, NULL); setgid(gid); setuid(uid); cap = cap_get_proc(); cap_set_flag(cap, CAP_EFFECTIVE, ncap, capval, CAP_CLEAR); cap_set_flag(cap, CAP_PERMITTED, ncap, capval, CAP_CLEAR); if (cap_set_proc(cap) != 0) { ap_log_error (APLOG_MARK , APLOG_ERR , 0 , NULL , "%s ERROR %s:cap_set_proc failed after setuid" , MODULE_NAME , __func__ ); } cap_free(cap); if (coredump) prctl(PR_SET_DUMPABLE, 1); return OK; }
int main(int argc, char **argv) { char *my_socket, *pt; const struct optstruct *opt; struct optstruct *opts; time_t currtime; int ret; memset(&descr, 0, sizeof(struct smfiDesc)); descr.xxfi_name = "ClamAV"; /* filter name */ descr.xxfi_version = SMFI_VERSION; /* milter version */ descr.xxfi_flags = SMFIF_QUARANTINE; /* flags */ descr.xxfi_connect = clamfi_connect; /* connection info filter */ descr.xxfi_envfrom = clamfi_envfrom; /* envelope sender filter */ descr.xxfi_envrcpt = clamfi_envrcpt; /* envelope recipient filter */ descr.xxfi_header = clamfi_header; /* header filter */ descr.xxfi_body = clamfi_body; /* body block */ descr.xxfi_eom = clamfi_eom; /* end of message */ descr.xxfi_abort = clamfi_abort; /* message aborted */ opts = optparse(NULL, argc, argv, 1, OPT_MILTER, 0, NULL); if (!opts) { mprintf("!Can't parse command line options\n"); return 1; } if(optget(opts, "help")->enabled) { printf("Usage: %s [-c <config-file>]\n\n", argv[0]); printf(" --help -h Show this help\n"); printf(" --version -V Show version and exit\n"); printf(" --config-file <file> -c Read configuration from file\n\n"); optfree(opts); return 0; } if(opts->filename) { int x; for(x = 0; opts->filename[x]; x++) mprintf("^Ignoring option %s\n", opts->filename[x]); } if(optget(opts, "version")->enabled) { printf("clamav-milter %s\n", get_version()); optfree(opts); return 0; } pt = strdup(optget(opts, "config-file")->strarg); if((opts = optparse(pt, 0, NULL, 1, OPT_MILTER, 0, opts)) == NULL) { printf("%s: cannot parse config file %s\n", argv[0], pt); free(pt); return 1; } free(pt); if((opt = optget(opts, "Chroot"))->enabled) { if(chdir(opt->strarg) != 0) { logg("!Cannot change directory to %s\n", opt->strarg); return 1; } if(chroot(opt->strarg) != 0) { logg("!chroot to %s failed. Are you root?\n", opt->strarg); return 1; } } if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) { struct passwd *user = NULL; if((user = getpwnam(opt->strarg)) == NULL) { fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg); optfree(opts); return 1; } if(optget(opts, "AllowSupplementaryGroups")->enabled) { #ifdef HAVE_INITGROUPS if(initgroups(opt->strarg, user->pw_gid)) { fprintf(stderr, "ERROR: initgroups() failed.\n"); optfree(opts); return 1; } #else mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n"); optfree(opts); return 1; #endif } else { #ifdef HAVE_SETGROUPS if(setgroups(1, &user->pw_gid)) { fprintf(stderr, "ERROR: setgroups() failed.\n"); optfree(opts); return 1; } #endif } if(setgid(user->pw_gid)) { fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid); optfree(opts); return 1; } if(setuid(user->pw_uid)) { fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid); optfree(opts); return 1; } } logg_lock = !optget(opts, "LogFileUnlock")->enabled; logg_time = optget(opts, "LogTime")->enabled; logg_size = optget(opts, "LogFileMaxSize")->numarg; logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled; if((opt = optget(opts, "LogFile"))->enabled) { logg_file = opt->strarg; if(strlen(logg_file) < 2 || logg_file[0] != '/') { fprintf(stderr, "ERROR: LogFile requires full path.\n"); logg_close(); optfree(opts); return 1; } } else logg_file = NULL; #if defined(USE_SYSLOG) && !defined(C_AIX) if(optget(opts, "LogSyslog")->enabled) { int fac; opt = optget(opts, "LogFacility"); if((fac = logg_facility(opt->strarg)) == -1) { logg("!LogFacility: %s: No such facility.\n", opt->strarg); logg_close(); optfree(opts); return 1; } openlog("clamav-milter", LOG_PID, fac); logg_syslog = 1; } #endif time(&currtime); if(logg("#+++ Started at %s", ctime(&currtime))) { fprintf(stderr, "ERROR: Can't initialize the internal logger\n"); logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "TemporaryDirectory"))->enabled) tempdir = opt->strarg; if(localnets_init(opts) || init_actions(opts)) { logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "Whitelist"))->enabled && whitelist_init(opt->strarg)) { localnets_free(); logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "SkipAuthenticated"))->enabled && smtpauth_init(opt->strarg)) { localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } pt = optget(opts, "AddHeader")->strarg; if(strcasecmp(pt, "No")) { char myname[255]; if(!gethostname(myname, sizeof(myname))) { myname[sizeof(myname)-1] = '\0'; snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s", get_version(), myname); xvirushdr[sizeof(xvirushdr)-1] = '\0'; } else { snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s", get_version()); xvirushdr[sizeof(xvirushdr)-1] = '\0'; } descr.xxfi_flags |= SMFIF_ADDHDRS; if(strcasecmp(pt, "Add")) { /* Replace or Yes */ descr.xxfi_flags |= SMFIF_CHGHDRS; addxvirus = 1; } else { /* Add */ addxvirus = 2; } } if(!(my_socket = optget(opts, "MilterSocket")->strarg)) { logg("!Please configure the MilterSocket directive\n"); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } if(!optget(opts, "Foreground")->enabled) { if(daemonize() == -1) { logg("!daemonize() failed\n"); localnets_free(); whitelist_free(); cpool_free(); logg_close(); optfree(opts); return 1; } if(chdir("/") == -1) logg("^Can't change current working directory to root\n"); } if(smfi_setconn(my_socket) == MI_FAILURE) { logg("!smfi_setconn failed\n"); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } if(smfi_register(descr) == MI_FAILURE) { logg("!smfi_register failed\n"); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } opt = optget(opts, "FixStaleSocket"); if(smfi_opensocket(opt->enabled) == MI_FAILURE) { logg("!Failed to create socket %s\n", my_socket); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } maxfilesize = optget(opts, "MaxFileSize")->numarg; readtimeout = optget(opts, "ReadTimeout")->numarg; cpool_init(opts); if (!cp) { logg("!Failed to init the socket pool\n"); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "PidFile"))->enabled) { FILE *fd; mode_t old_umask = umask(0006); if((fd = fopen(opt->strarg, "w")) == NULL) { logg("!Can't save PID in file %s\n", opt->strarg); } else { if (fprintf(fd, "%u", (unsigned int)getpid())<0) { logg("!Can't save PID in file %s\n", opt->strarg); } fclose(fd); } umask(old_umask); } ret = smfi_main(); optfree(opts); logg_close(); cpool_free(); localnets_free(); whitelist_free(); return ret; }
int main(int argc, char *argv[]) { int userdir = 0; /* ~userdir flag */ uid_t uid; /* user information */ gid_t gid, primary_gid; /* target group placeholder */ char *target_uname; /* target user name */ char *target_gname; /* target group name */ char *target_homedir; /* target home directory */ char *actual_uname; /* actual user name */ char *actual_gname; /* actual group name */ char *prog; /* name of this program */ char *cmd; /* command to be executed */ char cwd[AP_MAXPATH]; /* current working directory */ char dwd[AP_MAXPATH]; /* docroot working directory */ #ifdef TESTBED char cmdpath[AP_MAXPATH]; /* full path to command */ int grouplist[NGROUPS], ngroups = 0; #endif struct passwd *pw; /* password entry holder */ struct group *gr; /* group entry holder */ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ #ifdef TESTBED /* * For the Testbed, we run this from php3, and it loses all * outout sent to stderr, so make sure it all goes to stdout. */ dup2(1, 2); { int i, max; /* * Close all other descriptors. I think this is the wrong * place for this since a program can be run from the web * server without going through suexec! */ max = getdtablesize(); for (i = 3; i < max; i++) (void) close(i); } #endif /* * If there are a proper number of arguments, set * all of them to variables. Otherwise, error out. */ prog = argv[0]; if (argc < 4) { log_err("alert: too few arguments\n"); exit(101); } target_uname = argv[1]; target_gname = argv[2]; cmd = argv[3]; /* * Check existence/validity of the UID of the user * running this program. Error out if invalid. */ uid = getuid(); if ((pw = getpwuid(uid)) == NULL) { log_err("crit: invalid uid: (%ld)\n", uid); exit(102); } /* * Check to see if the user running this program * is the user allowed to do so as defined in * suexec.h. If not the allowed user, error out. */ #ifdef _OSD_POSIX /* User name comparisons are case insensitive on BS2000/OSD */ if (strcasecmp(HTTPD_USER, pw->pw_name)) { log_err("crit: calling user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); exit(103); } #else /* _OSD_POSIX */ if (strcmp(HTTPD_USER, pw->pw_name)) { log_err("crit: calling user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); exit(103); } #endif /* _OSD_POSIX */ /* * Check for a leading '/' (absolute path) in the command to be executed, * or attempts to back up out of the current directory, * to protect against attacks. If any are * found, error out. Naughty naughty crackers. */ if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3)) || (strstr(cmd, "/../") != NULL)) { log_err("error: invalid command (%s)\n", cmd); exit(104); } #ifndef TESTBED /* * Check to see if this is a ~userdir request. If * so, set the flag, and remove the '~' from the * target username. */ if (!strncmp("~", target_uname, 1)) { target_uname++; userdir = 1; } #endif /* * Error out if the target username is invalid. */ if ((pw = getpwnam(target_uname)) == NULL) { log_err("crit: invalid target user name: (%s)\n", target_uname); exit(105); } #ifdef TESTBED { char *cp, *bp, *rname, *temp_gname = strdup(target_gname); gid_t rgid; int i; actual_gname = NULL; bp = temp_gname; while ((bp = strsep(&temp_gname, ",")) != NULL) { if (!*bp) continue; /* * Error out if the target group name is invalid. */ if (strspn(bp, "1234567890") != strlen(bp)) { if ((gr = getgrnam(bp)) == NULL) { log_err("crit: invalid target group name: (%s)\n", bp); exit(106); } rgid = gr->gr_gid; rname = gr->gr_name; } else { rgid = atoi(bp); rname = bp; } /* Watch for duplicates */ for (i = 0; i < ngroups; i++) { if (grouplist[i] == rgid) goto skip; } grouplist[ngroups++] = rgid; /* * Error out if attempt is made to execute as root group * or as a GID less than GID_MIN. Tsk tsk. */ if ((rgid == 0) || (rgid < GID_MIN)) { log_err("crit: cannot run as forbidden gid (%d/%s)\n", gid, cmd); exit(108); } /* see below; need room for primary group in first two slots */ if (ngroups >= (NGROUPS - 2)) { log_err("crit: Too many groups: (%s)\n", bp); exit(106); } if (actual_gname) { cp = (char *) malloc(strlen(actual_gname) + strlen(rname) + 2); strcpy(cp, actual_gname); strcat(cp, ","); strcat(cp, rname); free(actual_gname); actual_gname = cp; } else { actual_gname = strdup(rname); } skip: ; } } #else /* * Error out if the target group name is invalid. */ if (strspn(target_gname, "1234567890") != strlen(target_gname)) { if ((gr = getgrnam(target_gname)) == NULL) { log_err("crit: invalid target group name: (%s)\n", target_gname); exit(106); } gid = gr->gr_gid; actual_gname = strdup(gr->gr_name); } else { gid = atoi(target_gname); actual_gname = strdup(target_gname); } #endif #ifdef _OSD_POSIX /* * Initialize BS2000 user environment */ { pid_t pid; int status; switch (pid = ufork(target_uname)) { case -1: /* Error */ log_err("emerg: failed to setup bs2000 environment for user " "%s: %s\n", target_uname, strerror(errno)); exit(150); case 0: /* Child */ break; default: /* Father */ while (pid != waitpid(pid, &status, 0)) ; /* @@@ FIXME: should we deal with STOP signals as well? */ if (WIFSIGNALED(status)) { kill (getpid(), WTERMSIG(status)); } exit(WEXITSTATUS(status)); } } #endif /* _OSD_POSIX */ /* * Save these for later since initgroups will hose the struct */ uid = pw->pw_uid; primary_gid = pw->pw_gid; actual_uname = strdup(pw->pw_name); target_homedir = strdup(pw->pw_dir); /* * Log the transaction here to be sure we have an open log * before we setuid(). */ { char argbuf[2*BUFSIZ], *bp = argbuf; int i, size = sizeof(argbuf) - 1; *bp = '\0'; for (i = 4; i < argc; i++) { int count = snprintf(bp, size, "%s ", argv[i]); if (count >= size) break; size -= count; bp += count; } log_err("info: (target/actual) uid: (%s/%s) gid: (%s/%s) cmd: %s %s\n", target_uname, actual_uname, target_gname, actual_gname, cmd, argbuf); } /* * Error out if attempt is made to execute as root or as * a UID less than UID_MIN. Tsk tsk. */ if ((uid == 0) || (uid < UID_MIN)) { log_err("crit: cannot run as forbidden uid (%d/%s)\n", uid, cmd); exit(107); } #ifdef TESTBED { gid_t groups[NGROUPS]; int i, idx = 0; groups[idx++] = primary_gid; /* duplicate has something to do with effective gid */ groups[idx++] = primary_gid; /* Move over the grouplist from above. */ for (i = 0; i < ngroups; i++) { if (grouplist[i] != primary_gid) groups[idx++] = grouplist[i]; } if (setgid(primary_gid) != 0) { log_err("emerg: failed to setgid (%ld: %s)\n", primary_gid, cmd); exit(109); } if (setgroups(idx, groups) != 0) { log_err("emerg: failed to setgroups (%s: %s)\n", actual_gname,cmd); exit(109); } } #else /* * Error out if attempt is made to execute as root group * or as a GID less than GID_MIN. Tsk tsk. */ if ((gid == 0) || (gid < GID_MIN)) { log_err("crit: cannot run as forbidden gid (%d/%s)\n", gid, cmd); exit(108); } /* * Change UID/GID here so that the following tests work over NFS. * * Initialize the group access list for the target user, * and setgid() to the target group. If unsuccessful, error out. */ if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) { log_err("emerg: failed to setgid (%ld: %s)\n", gid, cmd); exit(109); } #endif /* * setuid() to the target user. Error out on fail. */ if ((setuid(uid)) != 0) { log_err("emerg: failed to setuid (%ld: %s)\n", uid, cmd); exit(110); } #ifdef TESTBED /* * All programs exist in the testbed bin directory, and we won't be * running those programs from that directory (cwd will not be the * bin directory, but rather someplace else). So, fake things * up so the rest of this code works okay. */ strcpy(cwd, DOC_ROOT); strcpy(cmdpath, DOC_ROOT); strcat(cmdpath, cmd); cmd = cmdpath; #else /* * Get the current working directory, as well as the proper * document root (dependant upon whether or not it is a * ~userdir request). Error out if we cannot get either one, * or if the current working directory is not in the docroot. * Use chdir()s and getcwd()s to avoid problems with symlinked * directories. Yuck. */ if (getcwd(cwd, AP_MAXPATH) == NULL) { log_err("emerg: cannot get current working directory\n"); exit(111); } if (userdir) { if (((chdir(target_homedir)) != 0) || ((chdir(USERDIR_SUFFIX)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("emerg: cannot get docroot information (%s)\n", target_homedir); exit(112); } } else { if (((chdir(DOC_ROOT)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("emerg: cannot get docroot information (%s)\n", DOC_ROOT); exit(113); } } if ((strncmp(cwd, dwd, strlen(dwd))) != 0) { log_err("error: command not in docroot (%s/%s)\n", cwd, cmd); exit(114); } #endif /* * Stat the cwd and verify it is a directory, or error out. */ if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) { log_err("error: cannot stat directory: (%s)\n", cwd); exit(115); } /* * Error out if cwd is writable by others. */ #ifdef TESTBED if (dir_info.st_mode & S_IWOTH) { #else if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) { #endif log_err("error: directory is writable by group or other: (%s)\n", cwd); exit(116); } /* * Error out if we cannot stat the program. */ if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { log_err("error: cannot stat program: (%s)\n", cmd); exit(117); } /* * Error out if the program is writable by others. */ if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { log_err("error: file is writable by group or other: (%s/%s)\n", cwd, cmd); exit(118); } #ifndef TESTBED /* * Error out if the file is setuid or setgid. */ if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) { log_err("error: file is either setuid or setgid: (%s/%s)\n", cwd, cmd); exit(119); } /* * Error out if the target name/group is different from * the name/group of the cwd or the program. */ if ((uid != dir_info.st_uid) || (gid != dir_info.st_gid) || (uid != prg_info.st_uid) || (gid != prg_info.st_gid)) { log_err("error: target uid/gid (%ld/%ld) mismatch " "with directory (%ld/%ld) or program (%ld/%ld)\n", uid, gid, dir_info.st_uid, dir_info.st_gid, prg_info.st_uid, prg_info.st_gid); exit(120); } #endif /* * Error out if the program is not executable for the user. * Otherwise, she won't find any error in the logs except for * "[error] Premature end of script headers: ..." */ if (!(prg_info.st_mode & S_IXUSR)) { log_err("error: file has no execute permission: (%s/%s)\n", cwd, cmd); exit(121); } #ifdef SUEXEC_UMASK /* * umask() uses inverse logic; bits are CLEAR for allowed access. */ if ((~SUEXEC_UMASK) & 0022) { log_err("notice: SUEXEC_UMASK of %03o allows " "write permission to group and/or other\n", SUEXEC_UMASK); } umask(SUEXEC_UMASK); #endif /* SUEXEC_UMASK */ clean_env(); /* * Be sure to close the log file so the CGI can't * mess with it. If the exec fails, it will be reopened * automatically when log_err is called. Note that the log * might not actually be open if LOG_EXEC isn't defined. * However, the "log" cell isn't ifdef'd so let's be defensive * and assume someone might have done something with it * outside an ifdef'd LOG_EXEC block. */ if (log != NULL) { fclose(log); log = NULL; } /* * Execute the command, replacing our image with its own. */ #ifdef NEED_HASHBANG_EMUL /* We need the #! emulation when we want to execute scripts */ { extern char **environ; ap_execve(cmd, &argv[3], environ); } #else /*NEED_HASHBANG_EMUL*/ execv(cmd, &argv[3]); #endif /*NEED_HASHBANG_EMUL*/ /* * (I can't help myself...sorry.) * * Uh oh. Still here. Where's the kaboom? There was supposed to be an * EARTH-shattering kaboom! * * Oh well, log the failure and error out. */ log_err("emerg: (%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd); exit(255); }
int main(int argc, char **argv) { char *fcgi_app = NULL, *changeroot = NULL, *username = NULL, *groupname = NULL, *unixsocket = NULL, *pid_file = NULL, *addr = NULL; char **fcgi_app_argv = { NULL }; unsigned short port = 0; int child_count = 5; int i_am_root, o; int pid_fd = -1; int nofork = 0; struct sockaddr_un un; const size_t sun_path_len = sizeof(un.sun_path); i_am_root = (getuid() == 0); while(-1 != (o = getopt(argc, argv, "c:f:g:hna:p:u:vC:s:P:"))) { switch(o) { case 'f': fcgi_app = optarg; break; case 'a': addr = optarg;/* ip addr */ break; case 'p': port = strtol(optarg, NULL, 10);/* port */ break; case 'C': child_count = strtol(optarg, NULL, 10);/* */ break; case 's': unixsocket = optarg; /* unix-domain socket */ break; case 'c': if (i_am_root) { changeroot = optarg; }/* chroot() */ break; case 'u': if (i_am_root) { username = optarg; } /* set user */ break; case 'g': if (i_am_root) { groupname = optarg; } /* set group */ break; case 'n': nofork = 1; break; case 'P': pid_file = optarg; /* PID file */ break; case 'v': show_version(); return 0; case 'h': show_help(); return 0; default: show_help(); return -1; } } if (optind < argc) { fcgi_app_argv = &argv[optind]; } if ((fcgi_app == NULL && fcgi_app_argv == NULL) || (port == 0 && unixsocket == NULL)) { show_help(); return -1; } if (unixsocket && port) { fprintf(stderr, "%s.%d: %s\n", __FILE__, __LINE__, "either a unix domain socket or a tcp-port, but not both\n"); return -1; } if (unixsocket && strlen(unixsocket) > sun_path_len - 1) { fprintf(stderr, "%s.%d: %s\n", __FILE__, __LINE__, "path of the unix socket is too long\n"); return -1; } /* UID handling */ if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { /* we are setuid-root */ fprintf(stderr, "%s.%d: %s\n", __FILE__, __LINE__, "Are you nuts ? Don't apply a SUID bit to this binary\n"); return -1; } if (pid_file && (-1 == (pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)))) { struct stat st; if (errno != EEXIST) { fprintf(stderr, "%s.%d: opening pid-file '%s' failed: %s\n", __FILE__, __LINE__, pid_file, strerror(errno)); return -1; } /* ok, file exists */ if (0 != stat(pid_file, &st)) { fprintf(stderr, "%s.%d: stating pid-file '%s' failed: %s\n", __FILE__, __LINE__, pid_file, strerror(errno)); return -1; } /* is it a regular file ? */ if (!S_ISREG(st.st_mode)) { fprintf(stderr, "%s.%d: pid-file exists and isn't regular file: '%s'\n", __FILE__, __LINE__, pid_file); return -1; } if (-1 == (pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { fprintf(stderr, "%s.%d: opening pid-file '%s' failed: %s\n", __FILE__, __LINE__, pid_file, strerror(errno)); return -1; } } if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; /* set user and group */ if (username) { if (NULL == (pwd = getpwnam(username))) { fprintf(stderr, "%s.%d: %s, %s\n", __FILE__, __LINE__, "can't find username", username); return -1; } if (pwd->pw_uid == 0) { fprintf(stderr, "%s.%d: %s\n", __FILE__, __LINE__, "I will not set uid to 0\n"); return -1; } } if (groupname) { if (NULL == (grp = getgrnam(groupname))) { fprintf(stderr, "%s.%d: %s %s\n", __FILE__, __LINE__, "can't find groupname", groupname); return -1; } if (grp->gr_gid == 0) { fprintf(stderr, "%s.%d: %s\n", __FILE__, __LINE__, "I will not set gid to 0\n"); return -1; } } /* * Change group before chroot, when we have access * to /etc/group */ if (groupname) { setgid(grp->gr_gid); setgroups(0, NULL); if (username) { initgroups(username, grp->gr_gid); } } if (changeroot) { if (-1 == chroot(changeroot)) { fprintf(stderr, "%s.%d: %s %s\n", __FILE__, __LINE__, "chroot failed: ", strerror(errno)); return -1; } if (-1 == chdir("/")) { fprintf(stderr, "%s.%d: %s %s\n", __FILE__, __LINE__, "chdir failed: ", strerror(errno)); return -1; } } /* drop root privs */ if (username) { setuid(pwd->pw_uid); } } return fcgi_spawn_connection(fcgi_app, fcgi_app_argv, addr, port, unixsocket, child_count, pid_fd, nofork); }
/* change a Flag(*) value; takes care of special actions */ void change_flag(enum sh_flag f, int what, bool newset) { unsigned char oldval; unsigned char newval = (newset ? 1 : 0); if (f == FXTRACE) { change_xtrace(newval, true); return; } oldval = Flag(f); Flag(f) = newval = (newset ? 1 : 0); #ifndef MKSH_UNEMPLOYED if (f == FMONITOR) { if (what != OF_CMDLINE && newval != oldval) j_change(); } else #endif #ifndef MKSH_NO_CMDLINE_EDITING if (( #if !MKSH_S_NOVI f == FVI || #endif f == FEMACS || f == FGMACS) && newval) { #if !MKSH_S_NOVI Flag(FVI) = #endif Flag(FEMACS) = Flag(FGMACS) = 0; Flag(f) = newval; } else #endif if (f == FPRIVILEGED && oldval && !newval) { /* Turning off -p? */ /*XXX this can probably be optimised */ kshegid = kshgid = getgid(); ksheuid = kshuid = getuid(); #if HAVE_SETRESUGID DO_SETUID(setresgid, (kshegid, kshegid, kshegid)); #if HAVE_SETGROUPS /* setgroups doesn't EAGAIN on Linux */ setgroups(1, &kshegid); #endif DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid)); #else /* !HAVE_SETRESUGID */ /* setgid, setegid, seteuid don't EAGAIN on Linux */ setgid(kshegid); #ifndef MKSH__NO_SETEUGID setegid(kshegid); #endif DO_SETUID(setuid, (ksheuid)); #ifndef MKSH__NO_SETEUGID seteuid(ksheuid); #endif #endif /* !HAVE_SETRESUGID */ } else if ((f == FPOSIX || f == FSH) && newval) { /* Turning on -o posix or -o sh? */ Flag(FBRACEEXPAND) = 0; } else if (f == FTALKING) { /* Changing interactive flag? */ if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) Flag(FTALKING_I) = newval; } }
int main(int argc, char **argv) { int ch; mode_t omask; const char *oconn = OCONN; const char *user = USER; const char *group = ""; sfsistat r = MI_FAILURE; const char *ofile = NULL; tzset(); openlog("sa2scl-milter", LOG_PID | LOG_NDELAY, LOG_DAEMON); while ((ch = getopt(argc, argv, "dg:p:u:")) != -1) { switch (ch) { case 'd': debug = 1; break; case 'g': group = optarg; break; case 'p': oconn = optarg; break; case 'u': user = optarg; break; default: usage(argv[0]); } } if (argc != optind) { fprintf(stderr, "unknown command line argument: %s ...", argv[optind]); usage(argv[0]); } if (!strncmp(oconn, "unix:", 5)) ofile = oconn + 5; else if (!strncmp(oconn, "local:", 6)) ofile = oconn + 6; if (ofile != NULL) unlink(ofile); /* drop privileges */ if (!getuid()) { struct passwd *pw; struct group *gr; if ((pw = getpwnam(user)) == NULL) { fprintf(stderr, "getpwnam: %s: %s\n", user, strerror(errno)); return (1); } if (strlen(group) == 0 || (gr = getgrnam(group)) == NULL) { /* use primary group of user */ setgroups(1, &pw->pw_gid); if (setegid(pw->pw_gid) || setgid(pw->pw_gid)) { fprintf(stderr, "setgid: %s\n", strerror(errno)); return (1); } omask = 0177; } else { /* custom group */ setgroups(1, &gr->gr_gid); if (setegid(gr->gr_gid) || setgid(gr->gr_gid)) { fprintf(stderr, "setgid: %s\n", strerror(errno)); return (1); } omask = 0117; } if ( #if ! ( __linux__ ) seteuid(pw->pw_uid) || #endif setuid(pw->pw_uid)) { fprintf(stderr, "setuid: %s\n", strerror(errno)); return (1); } } if (smfi_setconn((char *)oconn) != MI_SUCCESS) { fprintf(stderr, "smfi_setconn: %s: failed\n", oconn); goto done; } if (smfi_register(smfilter) != MI_SUCCESS) { fprintf(stderr, "smfi_register: failed\n"); goto done; } /* daemonize (detach from controlling terminal) */ if (!debug && daemon(0, 0)) { fprintf(stderr, "daemon: %s\n", strerror(errno)); goto done; } umask(omask); msg(LOG_INFO, NULL, "smfi_main: started"); r = smfi_main(); if (r != MI_SUCCESS) msg(LOG_ERR, NULL, "smfi_main: terminating due to error"); else msg(LOG_INFO, NULL, "smfi_main: terminating without error"); done: return (r); }
int main(void) { struct passwd *credentials; pid_t pid; int result; char *const argv[] = { "/usr/local/share/rivoluz/run.sh", NULL }; char *const envp[] = { "USER="******"LOGNAME=" UNPRIV_USER, "HOME=/", "PATH=/usr/bin:/bin", }; /* * Drop privileges */ credentials = getpwnam(UNPRIV_USER); if (credentials == NULL) err(1, "getpwnam"); result = setresgid(credentials->pw_gid, credentials->pw_gid, credentials->pw_gid); if (result != 0) err(1, "setresgid"); result = setgroups(0, NULL); if (result != 0) err(1, "setgroups"); result = setresuid(credentials->pw_uid, credentials->pw_uid, credentials->pw_uid); if (result != 0) err(1, "setresuid"); /* * Become a daemon */ pid = fork(); if (pid == -1) err(1, "fork"); if (pid != 0) exit(0); pid = setsid(); if (pid == -1) err(1, "setsid"); pid = fork(); if (pid == -1) err(1, "fork"); if (pid != 0) exit(0); /* * Redirect stdio */ (void)close(0); result = open("/dev/null", O_RDONLY); if (result == -1) err(1, "open"); (void)close(1); result = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP); if (result == -1) err(1, "open"); (void)close(2); result = dup(1); if (result == -1) exit(1); /* * Execve */ result = execve(argv[0], argv, envp); if (result != 0) err(1, "execve"); /* NOTREACHED */ exit(1); }
int main(int argc, char **argv) { #ifdef __FreeBSD__ FILE *fpid = NULL; #endif int ch; struct passwd *pw; pcap_handler phandler = logpkt_handler; int syncfd = 0; struct servent *ent; char *sync_iface = NULL; char *sync_baddr = NULL; if ((ent = getservbyname("spamd-sync", "udp")) == NULL) errx(1, "Can't find service \"spamd-sync\" in /etc/services"); sync_port = ntohs(ent->s_port); #ifndef __FreeBSD__ while ((ch = getopt(argc, argv, "DIi:l:Y:")) != -1) { #else while ((ch = getopt(argc, argv, "DIi:l:Y:m:")) != -1) { #endif switch (ch) { case 'D': flag_debug = 1; break; case 'I': flag_inbound = 1; break; case 'i': networkif = optarg; break; case 'l': pflogif = optarg; break; case 'Y': if (sync_addhost(optarg, sync_port) != 0) sync_iface = optarg; syncsend++; break; #ifdef __FreeBSD__ case 'm': if (strcmp(optarg, "ipfw") == 0) use_pf=0; break; #endif default: usage(); /* NOTREACHED */ } } signal(SIGINT , sighandler_close); signal(SIGQUIT, sighandler_close); signal(SIGTERM, sighandler_close); logmsg(LOG_DEBUG, "Listening on %s for %s %s", pflogif, (networkif == NULL) ? "all interfaces." : networkif, (flag_inbound) ? "Inbound direction only." : ""); if (init_pcap() == -1) err(1, "couldn't initialize pcap"); if (syncsend) { syncfd = sync_init(sync_iface, sync_baddr, sync_port); if (syncfd == -1) err(1, "sync init"); } #ifdef __FreeBSD__ /* open the pid file just before switch the user */ fpid = fopen(pid_file, "w"); if (fpid == NULL) { syslog(LOG_ERR, "exiting (couldn't create pid file %s)", pid_file); err(1, "couldn't create pid file \"%s\"", pid_file); } #endif /* privdrop */ pw = getpwnam("_spamd"); if (pw == NULL) errx(1, "User '_spamd' not found! "); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) err(1, "failed to drop privs"); if (!flag_debug) { if (daemon(0, 0) == -1) err(1, "daemon"); tzset(); openlog_r("spamlogd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata); } #ifdef __FreeBSD__ /* after switch user and daemon write and close the pid file */ if (fpid) { fprintf(fpid, "%ld\n", (long) getpid()); if (fclose(fpid) == EOF) { syslog(LOG_ERR, "exiting (couldn't close pid file %s)", pid_file); exit (1); } } #endif pcap_loop(hpcap, -1, phandler, NULL); logmsg(LOG_NOTICE, "exiting"); if (!flag_debug) closelog_r(&sdata); exit(0); }
pid_t control(void) { pid_t pid; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; switch (pid = fork()) { case -1: fatal("control: cannot fork"); case 0: post_fork(PROC_CONTROL); break; default: return (pid); } purge_config(PURGE_EVERYTHING); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); stat_backend = env->sc_stat; stat_backend->init(); if (chroot(PATH_CHROOT) == -1) fatal("control: chroot"); if (chdir("/") == -1) fatal("control: chdir(\"/\")"); config_process(PROC_CONTROL); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("control: cannot drop privileges"); imsg_callback = control_imsg; event_init(); signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); tree_init(&ctl_conns); memset(&digest, 0, sizeof digest); digest.startup = time(NULL); config_peer(PROC_SCHEDULER); config_peer(PROC_QUEUE); config_peer(PROC_SMTP); config_peer(PROC_MFA); config_peer(PROC_PARENT); config_peer(PROC_LKA); config_peer(PROC_MDA); config_peer(PROC_MTA); config_done(); control_listen(); if (event_dispatch() < 0) fatal("event_dispatch"); control_shutdown(); return (0); }
int daemonize_trqauthd(const char *server_ip, int server_port, void *(*process_meth)(void *)) { int gid; pid_t pid; int rc; char error_buf[MAX_BUF]; char msg_trqauthddown[MAX_BUF]; char path_log[MAXPATHLEN + 1]; char unix_socket_name[MAXPATHLEN + 1]; char *log_file=NULL; int eventclass = PBS_EVENTCLASS_TRQAUTHD; const char *path_home = PBS_SERVER_HOME; umask(022); gid = getgid(); /* secure supplemental groups */ if(setgroups(1, (gid_t *)&gid) != 0) { fprintf(stderr, "Unable to drop secondary groups. Some MAC framework is active?\n"); snprintf(error_buf, sizeof(error_buf), "setgroups(group = %lu) failed: %s\n", (unsigned long)gid, strerror(errno)); fprintf(stderr, "%s\n", error_buf); return(1); } if (getenv("PBSDEBUG") != NULL) debug_mode = TRUE; if (debug_mode == FALSE) { pid = fork(); if(pid > 0) { /* parent. We are done */ return(0); } else if (pid < 0) { /* something went wrong */ fprintf(stderr, "fork failed. errno = %d\n", errno); return(PBSE_RMSYSTEM); } else { fprintf(stderr, "trqauthd daemonized - port %d\n", server_port); /* If I made it here I am the child */ fclose(stdin); fclose(stdout); fclose(stderr); /* We closed 0 (stdin), 1 (stdout), and 2 (stderr). fopen should give us 0, 1 and 2 in that order. this is a UNIX practice */ if (fopen("/dev/null", "r") == NULL) perror(__func__); if (fopen("/dev/null", "r") == NULL) perror(__func__); if (fopen("/dev/null", "r") == NULL) perror(__func__); } } else { fprintf(stderr, "trqauthd port: %d\n", server_port); } log_init(NULL, NULL); log_get_set_eventclass(&eventclass, SETV); initialize_globals_for_log(server_port); sprintf(path_log, "%s/%s", path_home, TRQ_LOGFILES); if ((mkdir(path_log, 0755) == -1) && (errno != EEXIST)) { openlog("daemonize_trqauthd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); syslog(LOG_ALERT, "Failed to create client_logs directory: errno: %d", errno); log_err(errno,"daemonize_trqauthd", "Failed to create client_logs directory"); closelog(); } pthread_mutex_lock(log_mutex); log_open(log_file, path_log); pthread_mutex_unlock(log_mutex); /* start the listener */ snprintf(unix_socket_name, sizeof(unix_socket_name), "%s/%s", TRQAUTHD_SOCK_DIR, TRQAUTHD_SOCK_NAME); rc = start_domainsocket_listener(unix_socket_name, process_meth); if(rc != PBSE_NONE) { openlog("daemonize_trqauthd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); syslog(LOG_ALERT, "trqauthd could not start: %d\n", rc); log_err(rc, "daemonize_trqauthd", (char *)"trqauthd could not start"); pthread_mutex_lock(log_mutex); log_close(1); pthread_mutex_unlock(log_mutex); if (changed_msg_daem && msg_daemonname) { free(msg_daemonname); } clean_log_init_mutex(); exit(-1); } snprintf(msg_trqauthddown, sizeof(msg_trqauthddown), "TORQUE authd daemon shut down and no longer listening on IP:port %s:%d", server_ip, server_port); log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_TRQAUTHD, msg_daemonname, msg_trqauthddown); pthread_mutex_lock(log_mutex); log_close(1); pthread_mutex_unlock(log_mutex); if (changed_msg_daem && msg_daemonname) { free(msg_daemonname); } clean_log_init_mutex(); exit(0); }
static int ruid_set_perm (request_rec *r, const char *from_func) { ruid_config_t *conf = ap_get_module_config(r->server->module_config, &ruid2_module); ruid_dir_config_t *dconf = ap_get_module_config(r->per_dir_config, &ruid2_module); int retval = DECLINED; gid_t gid; uid_t uid; gid_t groups[RUID_MAXGROUPS]; int groupsnr; cap_t cap; cap_value_t capval[3]; cap=cap_get_proc(); capval[0]=CAP_SETUID; capval[1]=CAP_SETGID; cap_set_flag(cap,CAP_EFFECTIVE,2,capval,CAP_SET); if (cap_set_proc(cap)!=0) { ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s>%s:cap_set_proc failed before setuid", MODULE_NAME, from_func, __func__); } cap_free(cap); if (dconf->ruid_mode==RUID_MODE_STAT) { /* set uid,gid to uid,gid of file * if file does not exist, finfo.user and finfo.group is set to uid,gid of parent directory */ gid=r->finfo.group; uid=r->finfo.user; /* if uid of filename is less than conf->min_uid then set to conf->default_uid */ if (uid < conf->min_uid) { uid=conf->default_uid; } if (gid < conf->min_gid) { gid=conf->default_gid; } } else { gid=(dconf->ruid_gid == UNSET) ? ap_unixd_config.group_id : dconf->ruid_gid; uid=(dconf->ruid_uid == UNSET) ? ap_unixd_config.user_id : dconf->ruid_uid; } /* set supplementary groups */ if ((dconf->groupsnr == UNSET) && (startup_groupsnr > 0)) { memcpy(groups, startup_groups, sizeof(groups)); groupsnr = startup_groupsnr; } else if (dconf->groupsnr > 0) { for (groupsnr = 0; groupsnr < dconf->groupsnr; groupsnr++) { groups[groupsnr] = dconf->groups[groupsnr]; } } else { groupsnr = 0; } setgroups(groupsnr, groups); /* final set[ug]id */ if (setgid(gid) != 0) { ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s %s %s %s>%s:setgid(%d) failed. getgid=%d getuid=%d", MODULE_NAME, ap_get_server_name(r), r->the_request, from_func, __func__, dconf->ruid_gid, getgid(), getuid()); retval = HTTP_FORBIDDEN; } else { if (setuid(uid) != 0) { ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s %s %s %s>%s:setuid(%d) failed. getuid=%d", MODULE_NAME, ap_get_server_name(r), r->the_request, from_func, __func__, dconf->ruid_uid, getuid()); retval = HTTP_FORBIDDEN; } } /* set httpd process dumpable after setuid */ if (coredump) { prctl(PR_SET_DUMPABLE,1); } /* clear capabilties from effective set */ cap=cap_get_proc(); capval[0]=CAP_SETUID; capval[1]=CAP_SETGID; capval[2]=CAP_DAC_READ_SEARCH; cap_set_flag(cap,CAP_EFFECTIVE,3,capval,CAP_CLEAR); if (cap_set_proc(cap)!=0) { ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "%s CRITICAL ERROR %s>%s:cap_set_proc failed after setuid", MODULE_NAME, from_func, __func__); retval = HTTP_FORBIDDEN; } cap_free(cap); return retval; }
void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; char *scon = NULL; int rc; /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->time_started = 0; /* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless * they are ONESHOT */ if (svc->flags & SVC_RUNNING) { #ifdef MTK_INIT ERROR("service '%s' still running, return directly\n", svc->name); #endif return; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]); svc->flags |= SVC_DISABLED; return; } if (is_selinux_enabled() > 0) { if (svc->seclabel) { scon = strdup(svc->seclabel); if (!scon) { ERROR("Out of memory while starting '%s'\n", svc->name); return; } } else { char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'\n", svc->args[0]); rc = getcon(&mycon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); return; } rc = getfilecon(svc->args[0], &fcon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); freecon(mycon); return; } rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); if (rc == 0 && !strcmp(scon, mycon)) { ERROR("Warning! Service %s needs a SELinux domain defined; please fix!\n", svc->name); } freecon(mycon); freecon(fcon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); return; } } } NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; umask(077); if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); } for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid, si->socketcon ?: scon); if (s >= 0) { publish_socket(si->name, s); } } freecon(scon); scon = NULL; if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno)); } } if (needs_console) { setsid(); open_console(); } else { zap_stdio(); } #if 0 for (n = 0; svc->args[n]; n++) { INFO("args[%d] = '%s'\n", n, svc->args[n]); } for (n = 0; ENV[n]; n++) { INFO("env[%d] = '%s'\n", n, ENV[n]); } #endif setpgid(0, getpid()); /* as requested, set our gid, supplemental gids, and uid */ if (svc->gid) { if (setgid(svc->gid) != 0) { ERROR("setgid failed: %s service name %s\n", strerror(errno),svc->name); _exit(127); } } if (svc->nr_supp_gids) { if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) { ERROR("setgroups failed: %s service name %s\n", strerror(errno),svc->name); _exit(127); } } if (svc->uid) { if (setuid(svc->uid) != 0) { ERROR("setuid failed: %s service name %s\n", strerror(errno),svc->name); _exit(127); } } if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno)); _exit(127); } } if (!dynamic_args) { if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { char *arg_ptrs[INIT_PARSER_MAXARGS+1]; int arg_idx = svc->nargs; char *tmp = strdup(dynamic_args); char *next = tmp; char *bword; /* Copy the static arguments */ memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *))); while((bword = strsep(&next, " "))) { arg_ptrs[arg_idx++] = bword; if (arg_idx == INIT_PARSER_MAXARGS) break; } arg_ptrs[arg_idx] = '\0'; execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); } _exit(127); } freecon(scon); if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); svc->pid = 0; return; } svc->time_started = gettime(); svc->pid = pid; svc->flags |= SVC_RUNNING; #ifdef MTK_INIT //record the usage of serivce svc->flags |= SVC_MT_GOTLIFE; #endif if (properties_inited()) notify_service_state(svc->name, "running"); }
void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; /* starting a service removes it from the disabled * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING)); svc->time_started = 0; /* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless * they are ONESHOT */ if (svc->flags & SVC_RUNNING) { return; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]); svc->flags |= SVC_DISABLED; return; } NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); } for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid); if (s >= 0) { publish_socket(si->name, s); } } if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno)); } } if (needs_console) { setsid(); open_console(); } else { zap_stdio(); } #if 0 for (n = 0; svc->args[n]; n++) { INFO("args[%d] = '%s'\n", n, svc->args[n]); } for (n = 0; ENV[n]; n++) { INFO("env[%d] = '%s'\n", n, ENV[n]); } #endif setpgid(0, getpid()); /* as requested, set our gid, supplemental gids, and uid */ if (svc->gid) { setgid(svc->gid); } if (svc->nr_supp_gids) { setgroups(svc->nr_supp_gids, svc->supp_gids); } if (svc->uid) { setuid(svc->uid); } if (!dynamic_args) { if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { char *arg_ptrs[INIT_PARSER_MAXARGS+1]; int arg_idx = svc->nargs; char *tmp = strdup(dynamic_args); char *next = tmp; char *bword; /* Copy the static arguments */ memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *))); while((bword = strsep(&next, " "))) { arg_ptrs[arg_idx++] = bword; if (arg_idx == INIT_PARSER_MAXARGS) break; } arg_ptrs[arg_idx] = '\0'; execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); } _exit(127); } if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); svc->pid = 0; return; } svc->time_started = gettime(); svc->pid = pid; svc->flags |= SVC_RUNNING; if (properties_inited()) notify_service_state(svc->name, "running"); }