// // Change to Root // bool Cyhttpd::Configure() { if (!getuid()) // you must be root to do that! { // Get user and group data #ifdef Y_CONFIG_FEATURE_HTTPD_USER struct passwd *pwd = NULL; struct group *grp = NULL; std::string username = ConfigList["server.user_name"]; std::string groupname= ConfigList["server.group_name"]; // get user data if(username != "") { if((pwd = getpwnam(username.c_str())) == NULL) { dperror("Dont know user to set uid\n"); return false; } } // get group data if(groupname != "") { if((grp = getgrnam(groupname.c_str())) == NULL) { aprintf("Can not get Group-Information. Group: %s\n", groupname.c_str()); return false; } } #endif // change root directory #ifdef Y_CONFIG_FEATURE_CHROOT if(!ConfigList["server.chroot"].empty()) { log_level_printf(2, "do chroot to dir:%s\n", ConfigList["server.chroot"].c_str() ); // do change Root if(chroot(ConfigList["server.chroot"].c_str()) == -1) { dperror("Change Root failed\n"); return false; } // Set Working Dir if(chdir("/") == -1) { dperror("Change Directory to Root failed\n"); return false; } } #endif #ifdef Y_CONFIG_FEATURE_HTTPD_USER if(username != "" && pwd != NULL && grp != NULL) { log_level_printf(2, "set user and groups\n"); // drop root privileges setgid(grp->gr_gid); setgroups(0, NULL); // set user group if(groupname != "") initgroups(username.c_str(), grp->gr_gid); // set user if(setuid(pwd->pw_uid) == -1) { dperror("Change User Context failed\n"); return false; } } #endif } return true; }
void swWorker_onStart(swServer *serv) { /** * Release other worker process */ swWorker *worker; if (SwooleWG.id >= serv->worker_num) { SwooleG.process_type = SW_PROCESS_TASKWORKER; } else { SwooleG.process_type = SW_PROCESS_WORKER; } int is_root = !geteuid(); struct passwd *passwd = NULL; struct group *group = NULL; if (is_root) { //get group info if (SwooleG.group) { group = getgrnam(SwooleG.group); if (!group) { swSysError("get group [%s] info failed.", SwooleG.group); } } //get user info if (SwooleG.user) { passwd = getpwnam(SwooleG.user); if (!passwd) { swSysError("get user [%s] info failed.", SwooleG.user); } } //chroot if (SwooleG.chroot) { if (0 > chroot(SwooleG.chroot)) { swSysError("chroot to [%s] failed.", SwooleG.chroot); } } //set process group if (SwooleG.group && group) { if (setgid(group->gr_gid) < 0) { swSysError("setgid to [%s] failed.", SwooleG.group); } } //set process user if (SwooleG.user && passwd) { if (setuid(passwd->pw_uid) < 0) { swSysError("setuid to [%s] failed.", SwooleG.user); } } } SwooleWG.worker = swServer_get_worker(serv, SwooleWG.id); int i; for (i = 0; i < serv->worker_num + SwooleG.task_worker_num; i++) { worker = swServer_get_worker(serv, i); if (SwooleWG.id == i) { continue; } else { swWorker_free(worker); } if (swIsWorker()) { swSetNonBlock(worker->pipe_master); } } if (serv->onWorkerStart) { serv->onWorkerStart(serv, SwooleWG.id); } }
/* based on syslogd privsep */ int priv_init(void) { int i, fd, socks[2], cmd; int snaplen, ret, olderrno; struct passwd *pw; for (i = 1; i < _NSIG; i++) signal(i, SIG_DFL); /* Create sockets */ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) err(1, "socketpair() failed"); pw = getpwnam("_pflogd"); if (pw == NULL) errx(1, "unknown user _pflogd"); endpwent(); child_pid = fork(); if (child_pid < 0) err(1, "fork() failed"); if (!child_pid) { gid_t gidset[1]; /* Child - drop privileges and return */ if (chroot(pw->pw_dir) != 0) err(1, "unable to chroot"); if (chdir("/") != 0) err(1, "unable to chdir"); gidset[0] = pw->pw_gid; if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) err(1, "setresgid() failed"); if (setgroups(1, gidset) == -1) err(1, "setgroups() failed"); if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) err(1, "setresuid() failed"); close(socks[0]); priv_fd = socks[1]; return 0; } /* Father */ /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */ signal(SIGALRM, sig_pass_to_chld); signal(SIGTERM, sig_pass_to_chld); signal(SIGHUP, sig_pass_to_chld); signal(SIGINT, sig_pass_to_chld); signal(SIGQUIT, sig_pass_to_chld); signal(SIGCHLD, sig_chld); setproctitle("[priv]"); close(socks[1]); while (!gotsig_chld) { if (may_read(socks[0], &cmd, sizeof(int))) break; switch (cmd) { case PRIV_SET_SNAPLEN: logmsg(LOG_DEBUG, "[priv]: msg PRIV_SET_SNAPLENGTH received"); must_read(socks[0], &snaplen, sizeof(int)); ret = set_snaplen(snaplen); if (ret) { logmsg(LOG_NOTICE, "[priv]: set_snaplen failed for snaplen %d", snaplen); } must_write(socks[0], &ret, sizeof(int)); break; case PRIV_OPEN_LOG: logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_LOG received"); /* create or append logs but do not follow symlinks */ fd = open(filename, O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 0600); olderrno = errno; send_fd(socks[0], fd); if (fd < 0) logmsg(LOG_NOTICE, "[priv]: failed to open %s: %s", filename, strerror(olderrno)); else close(fd); break; case PRIV_MOVE_LOG: logmsg(LOG_DEBUG, "[priv]: msg PRIV_MOVE_LOG received"); ret = move_log(filename); must_write(socks[0], &ret, sizeof(int)); break; default: logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); _exit(1); /* NOTREACHED */ } } _exit(1); }
int main(int argc, char **argv) { const char *user; const char *logname; const struct passwd *pw; char **newargv; //char envsh[BUFSIZ]; //char envhome[BUFSIZ]; int j; //char *neweviron[10]; char *shell; char *argv0; char *newargv0; #ifdef DEBUG_PRINTS fp = fopen("/var/log/chrootshell.log", "a"); #endif user = getenv("USER"); if (!user) die("USER not set?!"); logname = getenv("LOGNAME"); if (logname && *logname && strcmp(user, logname)) die("USER does not match LOGNAME\n"); /* Look up user in outer /etc/passwd */ pw = getpwnam(user); if (!pw) die2("no such user %s\n", user); shell = strrchr(pw->pw_shell, '/'); if (!shell) die("shell contains no / ?"); shell++; /* skip slash */ argv0 = argv[0]; if (*argv0 == '-') { /* it's a login shell */ argv0++; /* skip dash */ } if (strcmp(shell, argv0)) { fprintf(fp, "shell '%s', argv[0] '%s'\n", shell, argv[0]); die2("%s not chrootshell\n", shell); } /* Enter jail */ if (chdir(pw->pw_dir)) die2("chdir(%s) fails", pw->pw_dir); if (chroot(pw->pw_dir)) die2("chroot(%s) fails", pw->pw_dir); /* Permanently discard root privs */ if (setuid(pw->pw_uid)) die2("setuid(%d) fails", (void *)pw->pw_uid); /* Look up user in jail's /etc/passwd */ endpwent(); pw = getpwnam(user); if (!pw) die2("no such user %s in jail\n", user); /* Go to his home directory */ if (chdir(pw->pw_dir)) die2("chdir(%s) fails", pw->pw_dir); /* Fix up the environment. * Clear the whole thing out for security reasons, and give him a minimal one. */ mysetenv("SHELL", pw->pw_shell); mysetenv("HOME", pw->pw_dir); mysetenv("PATH", "/bin:/usr/bin"); mysetenv("USER", user); /* Note: rshd does not set LOGNAME, as the user hasn't really logged in... */ if (logname && *logname) mysetenv("LOGNAME", user); myenv[myenv_used] = 0; /* yes, this is the posix way of replacing the entire environment */ environ = myenv; /* Close the handle to the jail's /etc/passwd */ endpwent(); /* Finally, run the original command. */ newargv = malloc((argc + 3) * sizeof(argv[0])); if (!newargv) die("malloc fails?!\n"); j = 0; if (argc == 1) { /* Case 1: interactive login; argv[0] is the shell, there are no args */ char *buf = malloc(strlen(pw->pw_shell) + 2); newargv0 = pw->pw_shell; sprintf(buf, "-%s", pw->pw_shell); newargv[j++] = buf; } else if (argc > 1 && !strcmp(argv[1], "-c")) { /* Case 2: non-interactive; argv[0] is the shell, argv[1] is -c, argv[2] is the command */ newargv[j++] = pw->pw_shell; newargv0 = pw->pw_shell; newargv[j++] = "-c"; newargv[j++] = argv[2]; } else die("Expected argc==1 || (argc==3 && argv[1] == '-c')"); newargv[j++] = 0; execvp(newargv0, newargv); die2("exec %s fails", newargv[0]); /*notreached*/ return 1; }
/* * Do chroot, if requested. * * Switch UID and GID to what is specified in the config file */ static int switch_users(CONF_SECTION *cs) { /* * Get the current maximum for core files. Do this * before anything else so as to ensure it's properly * initialized. */ if (fr_set_dumpable_init() < 0) { fr_perror("radiusd"); return 0; } /* * Don't do chroot/setuid/setgid if we're in debugging * as non-root. */ if (debug_flag && (getuid() != 0)) return 1; if (cf_section_parse(cs, NULL, bootstrap_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n"); return 0; } #ifdef HAVE_GRP_H /* Set GID. */ if (gid_name) { struct group *gr; gr = getgrnam(gid_name); if (gr == NULL) { fprintf(stderr, "%s: Cannot get ID for group %s: %s\n", progname, gid_name, fr_syserror(errno)); return 0; } server_gid = gr->gr_gid; } else { server_gid = getgid(); } #endif #ifdef HAVE_PWD_H /* Set UID. */ if (uid_name) { struct passwd *pw; pw = getpwnam(uid_name); if (pw == NULL) { fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n", progname, uid_name, fr_syserror(errno)); return 0; } if (getuid() == pw->pw_uid) { uid_name = NULL; } else { server_uid = pw->pw_uid; #ifdef HAVE_INITGROUPS if (initgroups(uid_name, server_gid) < 0) { fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n", progname, uid_name, fr_syserror(errno)); return 0; } #endif } } else { server_uid = getuid(); } #endif if (chroot_dir) { if (chroot(chroot_dir) < 0) { fprintf(stderr, "%s: Failed to perform chroot %s: %s", progname, chroot_dir, fr_syserror(errno)); return 0; } /* * Note that we leave chdir alone. It may be * OUTSIDE of the root. This allows us to read * the configuration from "-d ./etc/raddb", with * the chroot as "./chroot/" for example. After * the server has been loaded, it does a "cd * ${logdir}" below, so that core files (if any) * go to a logging directory. * * This also allows the configuration of the * server to be outside of the chroot. If the * server is statically linked, then the only * things needed inside of the chroot are the * logging directories. */ } #ifdef HAVE_GRP_H /* Set GID. */ if (gid_name && (setgid(server_gid) < 0)) { fprintf(stderr, "%s: Failed setting group to %s: %s", progname, gid_name, fr_syserror(errno)); return 0; } #endif #ifdef HAVE_SETUID /* * Just before losing root permissions, ensure that the * log files have the correct owner && group. * * We have to do this because the log file MAY have been * specified on the command-line. */ if (uid_name || gid_name) { if ((default_log.dst == L_DST_FILES) && (default_log.fd < 0)) { default_log.fd = open(main_config.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (default_log.fd < 0) { fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno)); return 0; } if (chown(main_config.log_file, server_uid, server_gid) < 0) { fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n", progname, main_config.log_file, fr_syserror(errno)); return 0; } } } if (uid_name) { doing_setuid = true; fr_suid_down(); } #endif /* * This also clears the dumpable flag if core dumps * aren't allowed. */ if (fr_set_dumpable(allow_core_dumps) < 0) { ERROR("%s", fr_strerror()); } if (allow_core_dumps) { INFO("Core dumps are enabled"); } return 1; }
int main (int argc, char **argv) { mode_t old_umask; cleanup_free char *base_path = NULL; int clone_flags; char *old_cwd = NULL; pid_t pid; int event_fd = -1; const char *new_cwd; uid_t ns_uid; gid_t ns_gid; /* Get the (optional) capabilities we need, drop root */ acquire_caps (); /* Never gain any more privs during exec */ if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) die_with_error ("prctl(PR_SET_NO_NEW_CAPS) failed"); /* The initial code is run with high permissions (i.e. CAP_SYS_ADMIN), so take lots of care. */ argv0 = argv[0]; if (isatty (1)) host_tty_dev = ttyname (1); argv++; argc--; if (argc == 0) usage (EXIT_FAILURE); parse_args (&argc, &argv); /* We have to do this if we weren't installed setuid, so let's just DWIM */ if (!is_privileged) opt_unshare_user = TRUE; if (argc == 0) usage (EXIT_FAILURE); __debug__(("Creating root mount point\n")); uid = getuid (); if (opt_sandbox_uid == -1) opt_sandbox_uid = uid; gid = getgid (); if (opt_sandbox_gid == -1) opt_sandbox_gid = gid; if (!opt_unshare_user && opt_sandbox_uid != uid) die ("Specifying --uid requires --unshare-user"); if (!opt_unshare_user && opt_sandbox_gid != gid) die ("Specifying --gid requires --unshare-user"); /* We need to read stuff from proc during the pivot_root dance, etc. Lets keep a fd to it open */ proc_fd = open ("/proc", O_RDONLY | O_PATH); if (proc_fd == -1) die_with_error ("Can't open /proc"); /* We need *some* mountpoint where we can mount the root tmpfs. We first try in /run, and if that fails, try in /tmp. */ base_path = xasprintf ("/run/user/%d/.bubblewrap", uid); if (mkdir (base_path, 0755) && errno != EEXIST) { free (base_path); base_path = xasprintf ("/tmp/.bubblewrap-%d", uid); if (mkdir (base_path, 0755) && errno != EEXIST) die_with_error ("Creating root mountpoint failed"); } __debug__(("creating new namespace\n")); if (opt_unshare_pid) { event_fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK); if (event_fd == -1) die_with_error ("eventfd()"); } /* We block sigchild here so that we can use signalfd in the monitor. */ block_sigchild (); clone_flags = SIGCHLD | CLONE_NEWNS; if (opt_unshare_user) clone_flags |= CLONE_NEWUSER; if (opt_unshare_pid) clone_flags |= CLONE_NEWPID; if (opt_unshare_net) clone_flags |= CLONE_NEWNET; if (opt_unshare_ipc) clone_flags |= CLONE_NEWIPC; if (opt_unshare_uts) clone_flags |= CLONE_NEWUTS; pid = raw_clone (clone_flags, NULL); if (pid == -1) { if (opt_unshare_user) { if (errno == EINVAL) die ("Creating new namespace failed, likely because the kernel does not support user namespaces. bwrap must be installed setuid on such systems."); else if (errno == EPERM && !is_privileged) die ("No permissions to creating new namespace, likely because the kernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'."); } die_with_error ("Creating new namespace failed"); } if (pid != 0) { /* Initial launched process, wait for exec:ed command to exit */ /* We don't need any caps in the launcher, drop them immediately. */ drop_caps (); monitor_child (event_fd); exit (0); /* Should not be reached, but better safe... */ } if (opt_unshare_net && loopback_setup () != 0) die ("Can't create loopback device"); ns_uid = opt_sandbox_uid; ns_gid = opt_sandbox_gid; if (opt_unshare_user) { if (opt_needs_devpts) { /* This is a bit hacky, but we need to first map the real uid/gid to 0, otherwise we can't mount the devpts filesystem because root is not mapped. Later we will create another child user namespace and map back to the real uid */ ns_uid = 0; ns_gid = 0; } write_uid_gid_map (ns_uid, uid, ns_gid, gid, TRUE); } old_umask = umask (0); /* Mark everything as slave, so that we still * receive mounts from the real root, but don't * propagate mounts to the real root. */ if (mount (NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) die_with_error ("Failed to make / slave"); /* Create a tmpfs which we will use as / in the namespace */ if (mount ("", base_path, "tmpfs", MS_NODEV|MS_NOSUID, NULL) != 0) die_with_error ("Failed to mount tmpfs"); old_cwd = get_current_dir_name (); /* Chdir to the new root tmpfs mount. This will be the CWD during the entire setup. Access old or new root via "oldroot" and "newroot". */ if (chdir (base_path) != 0) die_with_error ("chdir base_path"); /* We create a subdir "$base_path/newroot" for the new root, that * way we can pivot_root to base_path, and put the old root at * "$base_path/oldroot". This avoids problems accessing the oldroot * dir if the user requested to bind mount something over / */ if (mkdir ("newroot", 0755)) die_with_error ("Creating newroot failed"); if (mkdir ("oldroot", 0755)) die_with_error ("Creating oldroot failed"); if (pivot_root (base_path, "oldroot")) die_with_error ("pivot_root"); if (chdir ("/") != 0) die_with_error ("chdir / (base path)"); if (is_privileged) { pid_t child; int privsep_sockets[2]; if (socketpair (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, privsep_sockets) != 0) die_with_error ("Can't create privsep socket"); child = fork (); if (child == -1) die_with_error ("Can't fork unprivileged helper"); if (child == 0) { /* Unprivileged setup process */ drop_caps (); close (privsep_sockets[0]); setup_newroot (opt_unshare_pid, privsep_sockets[1]); exit (0); } else { uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ uint32_t op, flags; const char *arg1, *arg2; cleanup_fd int unpriv_socket = -1; unpriv_socket = privsep_sockets[0]; close (privsep_sockets[1]); do { op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer), &flags, &arg1, &arg2); privileged_op (-1, op, flags, arg1, arg2); if (write (unpriv_socket, buffer, 1) != 1) die ("Can't write to op_socket"); } while (op != PRIV_SEP_OP_DONE); /* Continue post setup */ } } else setup_newroot (opt_unshare_pid, -1); /* The old root better be rprivate or we will send unmount events to the parent namespace */ if (mount ("oldroot", "oldroot", NULL, MS_REC|MS_PRIVATE, NULL) != 0) die_with_error ("Failed to make old root rprivate"); if (umount2 ("oldroot", MNT_DETACH)) die_with_error ("unmount old root"); if (opt_unshare_user && (ns_uid != opt_sandbox_uid || ns_gid != opt_sandbox_gid)) { /* Now that devpts is mounted and we've no need for mount permissions we can create a new userspace and map our uid 1:1 */ if (unshare (CLONE_NEWUSER)) die_with_error ("unshare user ns"); write_uid_gid_map (opt_sandbox_uid, ns_uid, opt_sandbox_gid, ns_gid, FALSE); } /* Now make /newroot the real root */ if (chdir ("/newroot") != 0) die_with_error ("chdir newroot"); if (chroot ("/newroot") != 0) die_with_error ("chroot /newroot"); if (chdir ("/") != 0) die_with_error ("chdir /"); /* Now we have everything we need CAP_SYS_ADMIN for, so drop it */ drop_caps (); if (opt_seccomp_fd != -1) { cleanup_free char *seccomp_data = NULL; size_t seccomp_len; struct sock_fprog prog; seccomp_data = load_file_data (opt_seccomp_fd, &seccomp_len); if (seccomp_data == NULL) die_with_error ("Can't read seccomp data"); if (seccomp_len % 8 != 0) die ("Invalide seccomp data, must be multiple of 8"); prog.len = seccomp_len / 8; prog.filter = (struct sock_filter *)seccomp_data; close (opt_seccomp_fd); if (prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) die_with_error ("prctl(PR_SET_SECCOMP)"); } umask (old_umask); new_cwd = "/"; if (opt_chdir_path) { if (chdir (opt_chdir_path)) die_with_error ("Can't chdir to %s", opt_chdir_path); new_cwd = opt_chdir_path; } else if (chdir (old_cwd) == 0) { /* If the old cwd is mapped in the sandbox, go there */ new_cwd = old_cwd; } else { /* If the old cwd is not mapped, go to home */ const char *home = getenv ("HOME"); if (home != NULL && chdir (home) == 0) new_cwd = home; } xsetenv ("PWD", new_cwd, 1); free (old_cwd); __debug__(("forking for child\n")); if (opt_unshare_pid || lock_files != NULL || opt_sync_fd != -1) { /* We have to have a pid 1 in the pid namespace, because * otherwise we'll get a bunch of zombies as nothing reaps * them. Alternatively if we're using sync_fd or lock_files we * need some process to own these. */ pid = fork (); if (pid == -1) die_with_error("Can't fork for pid 1"); if (pid != 0) { /* Close fds in pid 1, except stdio and optionally event_fd (for syncing pid 2 lifetime with monitor_child) and opt_sync_fd (for syncing sandbox lifetime with outside process). Any other fds will been passed on to the child though. */ { int dont_close[3]; int j = 0; if (event_fd != -1) dont_close[j++] = event_fd; if (opt_sync_fd != -1) dont_close[j++] = opt_sync_fd; dont_close[j++] = -1; fdwalk (proc_fd, close_extra_fds, dont_close); } return do_init (event_fd, pid); } } __debug__(("launch executable %s\n", argv[0])); if (proc_fd != -1) close (proc_fd); if (opt_sync_fd != -1) close (opt_sync_fd); /* We want sigchild in the child */ unblock_sigchild (); if (label_exec (opt_exec_label) == -1) die_with_error ("label_exec %s", argv[0]); if (execvp (argv[0], argv) == -1) die_with_error ("execvp %s", argv[0]); return 0; }
int main(int argc, char *argv[]) { struct group *gp; struct passwd *pw; char *endp, *p, *user, *group, *grouplist; const char *shell; gid_t gid, *gidlist; uid_t uid; int ch, gids; long ngroups_max; gid = 0; uid = 0; user = group = grouplist = NULL; while ((ch = getopt(argc, argv, "G:g:u:")) != -1) { switch(ch) { case 'u': user = optarg; if (*user == '\0') usage(); break; case 'g': group = optarg; if (*group == '\0') usage(); break; case 'G': grouplist = optarg; if (*grouplist == '\0') usage(); break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc < 1) usage(); if (group != NULL) { if (isdigit((unsigned char)*group)) { gid = (gid_t)strtoul(group, &endp, 0); if (*endp != '\0') goto getgroup; } else { getgroup: if ((gp = getgrnam(group)) != NULL) gid = gp->gr_gid; else errx(1, "no such group `%s'", group); } } ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL) err(1, "malloc"); for (gids = 0; (p = strsep(&grouplist, ",")) != NULL && gids < ngroups_max; ) { if (*p == '\0') continue; if (isdigit((unsigned char)*p)) { gidlist[gids] = (gid_t)strtoul(p, &endp, 0); if (*endp != '\0') goto getglist; } else { getglist: if ((gp = getgrnam(p)) != NULL) gidlist[gids] = gp->gr_gid; else errx(1, "no such group `%s'", p); } gids++; } if (p != NULL && gids == ngroups_max) errx(1, "too many supplementary groups provided"); if (user != NULL) { if (isdigit((unsigned char)*user)) { uid = (uid_t)strtoul(user, &endp, 0); if (*endp != '\0') goto getuser; } else { getuser: if ((pw = getpwnam(user)) != NULL) uid = pw->pw_uid; else errx(1, "no such user `%s'", user); } } if (chdir(argv[0]) == -1 || chroot(".") == -1) err(1, "%s", argv[0]); if (gids && setgroups(gids, gidlist) == -1) err(1, "setgroups"); if (group && setgid(gid) == -1) err(1, "setgid"); if (user && setuid(uid) == -1) err(1, "setuid"); if (argv[1]) { execvp(argv[1], &argv[1]); err(1, "%s", argv[1]); } if (!(shell = getenv("SHELL"))) shell = _PATH_BSHELL; execlp(shell, shell, "-i", (char *)NULL); err(1, "%s", shell); /* NOTREACHED */ }
void cmd_chroot(int argc, char * argv[]) { if(chroot(argv[1]) == -1) perror("chroot"); }
int main(int argc, char **argv) { static struct option long_options[] = { {"pidfile\0\tSet pid file name", required_argument, NULL, 'p'}, {"foreground\0\t\tDon't fork into background", no_argument, NULL, 'f'}, {"chroot\0\t\tChroot to directory", required_argument, NULL, 'C'}, {"prompt-prog\0Program to execute for user prompt", required_argument, NULL, 'R'}, {"version\0\t\t\tShow version information", no_argument, NULL, 'V'}, {"channel\0\tCommunications channel (netlink or miscdev)", required_argument, NULL, 'd'}, {"help\0\t\t\tShow usage information", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; static char *short_options = "p:fC:R:Vd:h"; int long_options_ret; struct rlimit core = {0, 0}; int foreground = 0; char *chrootdir = NULL; char *tty = NULL; uint32_t channel_type = ECRYPTFS_DEFAULT_MESSAGING_TYPE; int messaging_type_specified = 0; int rc = 0; while ((long_options_ret = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (long_options_ret) { case 'p': pidfile = strdup(optarg); break; case 'f': foreground = 1; break; case 'C': chrootdir = strdup(optarg); break; case 'R': prompt_prog = strdup(optarg); break; case 'V': printf(("%s (%s) %s\n" "\n" "This is free software. You may " "redistribute copies of it under the " "terms of\n" "the GNU General Public License " "<http://www.gnu.org/licenses/" "gpl.html>.\n" "There is NO WARRANTY, to the extent " "permitted by law.\n"), basename(argv[0]), PACKAGE_NAME, PACKAGE_VERSION); exit(0); break; case 'd': messaging_type_specified = 1; if (strcmp(optarg, "netlink") == 0) channel_type = ECRYPTFS_MESSAGING_TYPE_NETLINK; else if (strcmp(optarg, "miscdev") == 0) channel_type = ECRYPTFS_MESSAGING_TYPE_MISCDEV; break; case 'h': default: usage(basename(argv[0]), long_options, short_options); exit(1); break; } } openlog(argv[0], LOG_PID | (foreground ? LOG_PERROR : 0), 0); if (!messaging_type_specified) { uint32_t version; rc = ecryptfs_get_version(&version); if (rc) { syslog(LOG_WARNING, "%s: Unable to retrieve versioning " "info from kernel module; falling back on " "default values\n", __FUNCTION__); } else { if (version & ECRYPTFS_VERSIONING_MISCDEV) channel_type = ECRYPTFS_MESSAGING_TYPE_MISCDEV; else channel_type = ECRYPTFS_MESSAGING_TYPE_NETLINK; } } tty = ttyname(0); /* We may need the tty name later */ if (tty != NULL) setenv ("TERM_DEVICE", tty, 0); if (!foreground) daemonize(); /* This will exit if cannot be completed */ /* Disallow core file; secret values may be in it */ if (setrlimit(RLIMIT_CORE, &core) == -1) { rc = -errno; syslog(LOG_ERR, "Cannot setrlimit: %m"); goto daemon_out; } if (chrootdir != NULL) { if (chroot(chrootdir) == -1) { rc = -errno; syslog(LOG_ERR, "Failed to chroot to '%s': %m\n", chrootdir); goto daemon_out; } free(chrootdir); chrootdir = NULL; } if (pidfile != NULL) { FILE *fp = fopen(pidfile, "w"); if (fp == NULL) { rc = -errno; syslog(LOG_ERR, "Failed to open pid file '%s': %m\n", pidfile); goto daemon_out; } fprintf(fp, "%d", (int)getpid()); fclose(fp); } if (signal(SIGTERM, sigterm_handler) == SIG_ERR) { rc = -ENOTSUP; syslog(LOG_ERR, "Failed to attach handler to SIGTERM"); goto daemon_out; } if (signal(SIGINT, sigterm_handler) == SIG_ERR) { rc = -ENOTSUP; syslog(LOG_ERR, "Failed to attach handler to SIGINT"); goto daemon_out; } cryptfs_get_ctx_opts()->prompt = prompt_callback; pthread_mutex_init(&mctx_mux, NULL); pthread_mutex_lock(&mctx_mux); rc = ecryptfs_init_messaging(&mctx, channel_type); if (rc) { syslog(LOG_ERR, "%s: Failed to initialize messaging; rc = " "[%d]\n", __FUNCTION__, rc); pthread_mutex_unlock(&mctx_mux); goto daemon_out; } rc = ecryptfs_send_message(&mctx, NULL, ECRYPTFS_MSG_HELO, 0, 0); if (rc) { syslog(LOG_ERR, "%s: Error attempting to send message to " "eCryptfs kernel module via transport of type " "[0x%.8x]; rc = [%d]\n", __FUNCTION__, mctx.type, rc); pthread_mutex_unlock(&mctx_mux); goto daemon_out; } mctx.state |= ECRYPTFS_MESSAGING_STATE_LISTENING; pthread_mutex_unlock(&mctx_mux); rc = ecryptfs_run_daemon(&mctx); pthread_mutex_lock(&mctx_mux); mctx.state &= ~ECRYPTFS_MESSAGING_STATE_LISTENING; pthread_mutex_unlock(&mctx_mux); daemon_out: ecryptfsd_exit(&mctx, rc); return rc; }
int main(int argc, char *argv[]) { fd_set *fdsr = NULL, *fdsw = NULL; struct sockaddr_in sin; struct sockaddr_in lin; int ch, s, s2, conflisten = 0, syncfd = 0, i, omax = 0, one = 1; socklen_t sinlen; u_short port; struct servent *ent; struct rlimit rlp; char *bind_address = NULL; const char *errstr; char *sync_iface = NULL; char *sync_baddr = NULL; tzset(); openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata); if ((ent = getservbyname("spamd", "tcp")) == NULL) errx(1, "Can't find service \"spamd\" in /etc/services"); port = ntohs(ent->s_port); if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL) errx(1, "Can't find service \"spamd-cfg\" in /etc/services"); cfg_port = ntohs(ent->s_port); if ((ent = getservbyname("spamd-sync", "udp")) == NULL) errx(1, "Can't find service \"spamd-sync\" in /etc/services"); sync_port = ntohs(ent->s_port); if (gethostname(hostname, sizeof hostname) == -1) err(1, "gethostname"); maxfiles = get_maxfiles(); if (maxcon > maxfiles) maxcon = maxfiles; if (maxblack > maxfiles) maxblack = maxfiles; while ((ch = getopt(argc, argv, "45l:c:B:p:bdG:h:r:s:S:M:n:vw:y:Y:")) != -1) { switch (ch) { case '4': nreply = "450"; break; case '5': nreply = "550"; break; case 'l': bind_address = optarg; break; case 'B': i = atoi(optarg); maxblack = i; break; case 'c': i = atoi(optarg); if (i > maxfiles) { fprintf(stderr, "%d > system max of %d connections\n", i, maxfiles); usage(); } maxcon = i; break; case 'p': i = atoi(optarg); port = i; break; case 'd': debug = 1; break; case 'b': greylist = 0; break; case 'G': if (sscanf(optarg, "%d:%d:%d", &passtime, &greyexp, &whiteexp) != 3) usage(); /* convert to seconds from minutes */ passtime *= 60; /* convert to seconds from hours */ whiteexp *= (60 * 60); /* convert to seconds from hours */ greyexp *= (60 * 60); break; case 'h': bzero(&hostname, sizeof(hostname)); if (strlcpy(hostname, optarg, sizeof(hostname)) >= sizeof(hostname)) errx(1, "-h arg too long"); break; case 's': i = atoi(optarg); if (i < 0 || i > 10) usage(); stutter = i; break; case 'S': i = strtonum(optarg, 0, 90, &errstr); if (errstr) usage(); grey_stutter = i; break; case 'M': low_prio_mx_ip = optarg; break; case 'n': spamd = optarg; break; case 'v': verbose = 1; break; case 'w': window = atoi(optarg); if (window <= 0) usage(); break; case 'Y': if (sync_addhost(optarg, sync_port) != 0) sync_iface = optarg; syncsend++; break; case 'y': sync_baddr = optarg; syncrecv++; break; default: usage(); break; } } setproctitle("[priv]%s%s", greylist ? " (greylist)" : "", (syncrecv || syncsend) ? " (sync)" : ""); if (!greylist) maxblack = maxcon; else if (maxblack > maxcon) usage(); rlp.rlim_cur = rlp.rlim_max = maxcon + 15; if (setrlimit(RLIMIT_NOFILE, &rlp) == -1) err(1, "setrlimit"); con = calloc(maxcon, sizeof(*con)); if (con == NULL) err(1, "calloc"); con->obuf = malloc(8192); if (con->obuf == NULL) err(1, "malloc"); con->osize = 8192; for (i = 0; i < maxcon; i++) con[i].fd = -1; signal(SIGPIPE, SIG_IGN); s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) err(1, "socket"); if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) return (-1); conflisten = socket(AF_INET, SOCK_STREAM, 0); if (conflisten == -1) err(1, "socket"); if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) return (-1); memset(&sin, 0, sizeof sin); sin.sin_len = sizeof(sin); if (bind_address) { if (inet_pton(AF_INET, bind_address, &sin.sin_addr) != 1) err(1, "inet_pton"); } else sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_family = AF_INET; sin.sin_port = htons(port); if (bind(s, (struct sockaddr *)&sin, sizeof sin) == -1) err(1, "bind"); memset(&lin, 0, sizeof sin); lin.sin_len = sizeof(sin); lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); lin.sin_family = AF_INET; lin.sin_port = htons(cfg_port); if (bind(conflisten, (struct sockaddr *)&lin, sizeof lin) == -1) err(1, "bind local"); if (syncsend || syncrecv) { syncfd = sync_init(sync_iface, sync_baddr, sync_port); if (syncfd == -1) err(1, "sync init"); } pw = getpwnam("_spamd"); if (!pw) pw = getpwnam("nobody"); if (debug == 0) { if (daemon(1, 1) == -1) err(1, "daemon"); } if (greylist) { pfdev = open("/dev/pf", O_RDWR); if (pfdev == -1) { syslog_r(LOG_ERR, &sdata, "open /dev/pf: %m"); exit(1); } maxblack = (maxblack >= maxcon) ? maxcon - 100 : maxblack; if (maxblack < 0) maxblack = 0; /* open pipe to talk to greylister */ if (pipe(greypipe) == -1) { syslog(LOG_ERR, "pipe (%m)"); exit(1); } /* open pipe to recieve spamtrap configs */ if (pipe(trappipe) == -1) { syslog(LOG_ERR, "pipe (%m)"); exit(1); } jail_pid = fork(); switch (jail_pid) { case -1: syslog(LOG_ERR, "fork (%m)"); exit(1); case 0: /* child - continue */ signal(SIGPIPE, SIG_IGN); grey = fdopen(greypipe[1], "w"); if (grey == NULL) { syslog(LOG_ERR, "fdopen (%m)"); _exit(1); } close(greypipe[0]); trapfd = trappipe[0]; trapcfg = fdopen(trappipe[0], "r"); if (trapcfg == NULL) { syslog(LOG_ERR, "fdopen (%m)"); _exit(1); } close(trappipe[1]); goto jail; } /* parent - run greylister */ grey = fdopen(greypipe[0], "r"); if (grey == NULL) { syslog(LOG_ERR, "fdopen (%m)"); exit(1); } close(greypipe[1]); trapcfg = fdopen(trappipe[1], "w"); if (trapcfg == NULL) { syslog(LOG_ERR, "fdopen (%m)"); exit(1); } close(trappipe[0]); return (greywatcher()); /* NOTREACHED */ } jail: if (chroot("/var/empty") == -1 || chdir("/") == -1) { syslog(LOG_ERR, "cannot chdir to /var/empty."); exit(1); } if (pw) #ifdef __OpenBSD__ 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"); #else /* TODO */ setgroups(1, &pw->pw_gid); setegid(pw->pw_gid); setgid(pw->pw_gid); seteuid(pw->pw_uid); setuid(pw->pw_uid); #endif if (listen(s, 10) == -1) err(1, "listen"); if (listen(conflisten, 10) == -1) err(1, "listen"); if (debug != 0) printf("listening for incoming connections.\n"); syslog_r(LOG_WARNING, &sdata, "listening for incoming connections."); while (1) { struct timeval tv, *tvp; int max, n; int writers; max = MAX(s, conflisten); if (syncrecv) max = MAX(max, syncfd); max = MAX(max, conffd); max = MAX(max, trapfd); time(&t); for (i = 0; i < maxcon; i++) if (con[i].fd != -1) max = MAX(max, con[i].fd); if (max > omax) { free(fdsr); fdsr = NULL; free(fdsw); fdsw = NULL; fdsr = (fd_set *)calloc(howmany(max+1, NFDBITS), sizeof(fd_mask)); if (fdsr == NULL) err(1, "calloc"); fdsw = (fd_set *)calloc(howmany(max+1, NFDBITS), sizeof(fd_mask)); if (fdsw == NULL) err(1, "calloc"); omax = max; } else { memset(fdsr, 0, howmany(max+1, NFDBITS) * sizeof(fd_mask)); memset(fdsw, 0, howmany(max+1, NFDBITS) * sizeof(fd_mask)); } writers = 0; for (i = 0; i < maxcon; i++) { if (con[i].fd != -1 && con[i].r) { if (con[i].r + MAXTIME <= t) { closecon(&con[i]); continue; } FD_SET(con[i].fd, fdsr); } if (con[i].fd != -1 && con[i].w) { if (con[i].w + MAXTIME <= t) { closecon(&con[i]); continue; } if (con[i].w <= t) FD_SET(con[i].fd, fdsw); writers = 1; } } FD_SET(s, fdsr); /* only one active config conn at a time */ if (conffd == -1) FD_SET(conflisten, fdsr); else FD_SET(conffd, fdsr); if (trapfd != -1) FD_SET(trapfd, fdsr); if (syncrecv) FD_SET(syncfd, fdsr); if (writers == 0) { tvp = NULL; } else { tv.tv_sec = 1; tv.tv_usec = 0; tvp = &tv; } n = select(max+1, fdsr, fdsw, NULL, tvp); if (n == -1) { if (errno != EINTR) err(1, "select"); continue; } if (n == 0) continue; for (i = 0; i < maxcon; i++) { if (con[i].fd != -1 && FD_ISSET(con[i].fd, fdsr)) handler(&con[i]); if (con[i].fd != -1 && FD_ISSET(con[i].fd, fdsw)) handlew(&con[i], clients + 5 < maxcon); } if (FD_ISSET(s, fdsr)) { sinlen = sizeof(sin); s2 = accept(s, (struct sockaddr *)&sin, &sinlen); if (s2 == -1) /* accept failed, they may try again */ continue; for (i = 0; i < maxcon; i++) if (con[i].fd == -1) break; if (i == maxcon) close(s2); else { initcon(&con[i], s2, (struct sockaddr *)&sin); syslog_r(LOG_INFO, &sdata, "%s: connected (%d/%d)%s%s", con[i].addr, clients, blackcount, ((con[i].lists == NULL) ? "" : ", lists:"), ((con[i].lists == NULL) ? "": con[i].lists)); } } if (FD_ISSET(conflisten, fdsr)) { sinlen = sizeof(lin); conffd = accept(conflisten, (struct sockaddr *)&lin, &sinlen); if (conffd == -1) /* accept failed, they may try again */ continue; else if (ntohs(lin.sin_port) >= IPPORT_RESERVED) { close(conffd); conffd = -1; } } if (conffd != -1 && FD_ISSET(conffd, fdsr)) do_config(); if (trapfd != -1 && FD_ISSET(trapfd, fdsr)) read_configline(trapcfg); if (syncrecv && FD_ISSET(syncfd, fdsr)) sync_recv(); } exit(1); }
/* label decision engine */ pid_t lde(struct ldpd_conf *xconf, int pipe_parent2lde[2], int pipe_ldpe2lde[2], int pipe_parent2ldpe[2]) { struct event ev_sigint, ev_sigterm; struct timeval now; struct passwd *pw; pid_t pid; switch (pid = fork()) { case -1: fatal("cannot fork"); /* NOTREACHED */ case 0: break; default: return (pid); } ldeconf = xconf; if ((pw = getpwnam(LDPD_USER)) == NULL) fatal("getpwnam"); if (chroot(pw->pw_dir) == -1) fatal("chroot"); if (chdir("/") == -1) fatal("chdir(\"/\")"); setproctitle("label decision engine"); ldpd_process = PROC_LDE_ENGINE; 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("can't drop privileges"); event_init(); /* setup signal handler */ signal_set(&ev_sigint, SIGINT, lde_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, lde_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); /* setup pipes */ close(pipe_ldpe2lde[0]); close(pipe_parent2lde[0]); close(pipe_parent2ldpe[0]); close(pipe_parent2ldpe[1]); if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL || (iev_main = malloc(sizeof(struct imsgev))) == NULL) fatal(NULL); imsg_init(&iev_ldpe->ibuf, pipe_ldpe2lde[1]); iev_ldpe->handler = lde_dispatch_imsg; imsg_init(&iev_main->ibuf, pipe_parent2lde[1]); iev_main->handler = lde_dispatch_parent; /* setup event handler */ iev_ldpe->events = EV_READ; event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd, iev_ldpe->events, iev_ldpe->handler, iev_ldpe); event_add(&iev_ldpe->ev, NULL); iev_main->events = EV_READ; event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, iev_main->handler, iev_main); event_add(&iev_main->ev, NULL); gettimeofday(&now, NULL); ldeconf->uptime = now.tv_sec; event_dispatch(); lde_shutdown(); /* NOTREACHED */ return (0); }
/* * The mother of all processes. */ int main(int argc, char *argv[]) { state_t initial_transition = runcom; char kenv_value[PATH_MAX]; int c; struct sigaction sa; sigset_t mask; /* Dispose of random users. */ if (getuid() != 0) errx(1, "%s", strerror(EPERM)); /* System V users like to reexec init. */ if (getpid() != 1) { #ifdef COMPAT_SYSV_INIT /* So give them what they want */ if (argc > 1) { if (strlen(argv[1]) == 1) { char runlevel = *argv[1]; int sig; switch (runlevel) { case '0': /* halt + poweroff */ sig = SIGUSR2; break; case '1': /* single-user */ sig = SIGTERM; break; case '6': /* reboot */ sig = SIGINT; break; case 'c': /* block further logins */ sig = SIGTSTP; break; case 'q': /* rescan /etc/ttys */ sig = SIGHUP; break; default: goto invalid; } kill(1, sig); _exit(0); } else invalid: errx(1, "invalid run-level ``%s''", argv[1]); } else #endif errx(1, "already running"); } /* * Note that this does NOT open a file... * Does 'init' deserve its own facility number? */ openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); /* * Create an initial session. */ if (setsid() < 0) warning("initial setsid() failed: %m"); /* * Establish an initial user so that programs running * single user do not freak out and die (like passwd). */ if (setlogin("root") < 0) warning("setlogin() failed: %m"); /* * This code assumes that we always get arguments through flags, * never through bits set in some random machine register. */ while ((c = getopt(argc, argv, "dsf")) != -1) switch (c) { case 'd': devfs = 1; break; case 's': initial_transition = single_user; break; case 'f': runcom_mode = FASTBOOT; break; default: warning("unrecognized flag '-%c'", c); break; } if (optind != argc) warning("ignoring excess arguments"); /* * We catch or block signals rather than ignore them, * so that they get reset on exec. */ handle(badsys, SIGSYS, 0); handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ, 0); handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGUSR1, SIGUSR2, 0); handle(alrm_handler, SIGALRM, 0); sigfillset(&mask); delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, SIGUSR1, SIGUSR2, 0); sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGTTIN, &sa, (struct sigaction *)0); sigaction(SIGTTOU, &sa, (struct sigaction *)0); /* * Paranoia. */ close(0); close(1); close(2); if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) { state_func_t next_transition; if ((next_transition = run_script(kenv_value)) != 0) initial_transition = (state_t) next_transition; } if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) { if (chdir(kenv_value) != 0 || chroot(".") != 0) warning("Can't chroot to %s: %m", kenv_value); } /* * Additional check if devfs needs to be mounted: * If "/" and "/dev" have the same device number, * then it hasn't been mounted yet. */ if (!devfs) { struct stat stst; dev_t root_devno; stat("/", &stst); root_devno = stst.st_dev; if (stat("/dev", &stst) != 0) warning("Can't stat /dev: %m"); else if (stst.st_dev == root_devno) devfs++; } if (devfs) { struct iovec iov[4]; char *s; int i; char _fstype[] = "fstype"; char _devfs[] = "devfs"; char _fspath[] = "fspath"; char _path_dev[]= _PATH_DEV; iov[0].iov_base = _fstype; iov[0].iov_len = sizeof(_fstype); iov[1].iov_base = _devfs; iov[1].iov_len = sizeof(_devfs); iov[2].iov_base = _fspath; iov[2].iov_len = sizeof(_fspath); /* * Try to avoid the trailing slash in _PATH_DEV. * Be *very* defensive. */ s = strdup(_PATH_DEV); if (s != NULL) { i = strlen(s); if (i > 0 && s[i - 1] == '/') s[i - 1] = '\0'; iov[3].iov_base = s; iov[3].iov_len = strlen(s) + 1; } else { iov[3].iov_base = _path_dev; iov[3].iov_len = sizeof(_path_dev); } nmount(iov, 4, 0); if (s != NULL) free(s); } /* * Start the state machine. */ transition(initial_transition); /* * Should never reach here. */ return 1; }
/* Try to authenticate the user based on: - PAM if the system has it, else it checks: - pwdauth if the system supports it. - conventional auth (check salt on /etc/passwd, crypt, and compare - try to contact the local ftp server and login (if -f flag used) */ static int do_auth (const char *username, const char *password) { int auth = 0; struct passwd *this; if (strcmp (username, "anonymous") == 0) username = "******"; #ifdef HAVE_PAM if (mc_pam_auth (username, password) == 0) auth = 1; #else /* if there is no pam */ #ifdef HAVE_PWDAUTH if (pwdauth (username, password) == 0) auth = 1; else #endif #ifdef HAVE_CRYPT if (do_classic_auth (username, password)) auth = 1; else #endif if (ftp) auth = do_ftp_auth (username, password); #endif /* not pam */ if (!auth) return 0; this = getpwnam (username); if (this == 0) return 0; if (chdir (this->pw_dir) == -1) return 0; if (this->pw_dir[strlen (this->pw_dir) - 1] == '/') home_dir = strdup (this->pw_dir); else { char *new_home_dir = malloc (strlen (this->pw_dir) + 2); if (new_home_dir) { strcpy (new_home_dir, this->pw_dir); strcat (new_home_dir, "/"); home_dir = new_home_dir; } else home_dir = "/"; } if (setgid (this->pw_gid) == -1) return 0; #ifdef HAVE_INITGROUPS #ifdef NGROUPS_MAX if (NGROUPS_MAX > 1 && initgroups (this->pw_name, this->pw_gid)) return 0; #endif #endif #if defined (HAVE_SETUID) if (setuid (this->pw_uid)) return 0; #elif defined (HAVE_SETREUID) if (setreuid (this->pw_uid, this->pw_uid)) return 0; #endif /* If the setuid call failed, then deny access */ /* This should fix the problem on those machines with strange setups */ if (getuid () != this->pw_uid) return 0; if (strcmp (username, "ftp") == 0) chroot (this->pw_dir); endpwent (); return auth; }
int HTTPServerSetUserContext(HTTPSession *Session) { char *ChrootDir=NULL, *Tempstr=NULL; Session->StartDir=CopyStr(Session->StartDir,Settings.DefaultDir); ChrootDir=CopyStr(ChrootDir,Settings.DefaultDir); LogToFile(Settings.LogPath,"SETCTX: %d %s home=%s",Session->MethodID,ChrootDir,Session->HomeDir); if (IsProxyMethod(Session->MethodID)) { //Do not chroot for proxy commands } else { if (Settings.Flags & FLAG_CHHOME) ChrootDir=CopyStr(ChrootDir,Session->HomeDir); //if (Settings.Flags & FLAG_LOG_VERBOSE) LogToFile(Settings.LogPath,"ChRoot to: %s home=%s",ChrootDir,Session->HomeDir); if (chdir(ChrootDir) !=0) { LogToFile(Settings.LogPath,"ERROR: CHDIR FAILED: %d %s %s",getuid(),ChrootDir,strerror(errno)); HTTPServerSendHTML(Session->S, Session, "500 Internal Server Error","Problem switching to home-directory"); LogFileFlushAll(TRUE); _exit(1); } chroot("."); Session->StartDir=CopyStr(Session->StartDir,"/"); } /* /Not working yet else if (Settings.Flags & FLAG_CHSHARE) { chdir(Settings.DefaultDir); chroot("."); if (strncmp(Session->StartDir,Settings.DefaultDir,StrLen(Settings.DefaultDir))==0) { Tempstr=MCopyStr(Tempstr,"/",Session->StartDir+StrLen(Settings.DefaultDir),NULL); chdir(Tempstr); Session->StartDir=CopyStr(Session->StartDir,Tempstr); } } */ Session->StartDir=SlashTerminateDirectoryPath(Session->StartDir); LogToFile(Settings.LogPath,"User Context: Chroot: %s, StartDir: %s, HomeDir: %s, UserID: %d, GroupID: %d,",ChrootDir, Session->StartDir, Session->HomeDir, Session->RealUserUID,Session->GroupID); if (Session->GroupID > 0) { if (setgid(Session->GroupID) != 0) { HTTPServerSendHTML(Session->S, Session, "500 Internal Server Error","Problem switching to configured user-group"); LogToFile(Settings.LogPath,"ERROR: Failed to switch group to %s/%d. Exiting",Session->RealUser,Session->RealUserUID); _exit(1); } } else if (Settings.DefaultGroupID > 0) { if (setgid(Settings.DefaultGroupID) != 0) { HTTPServerSendHTML(Session->S, Session, "500 Internal Server Error","Problem switching to configured user-group"); LogToFile(Settings.LogPath,"ERROR: Failed to switch group to %s/%d. Exiting",Session->RealUser,Session->RealUserUID); _exit(1); } } DropCapabilities(CAPS_LEVEL_CHROOTED); if (setresuid(Session->RealUserUID,Session->RealUserUID,Session->RealUserUID)!=0) { HTTPServerSendHTML(Session->S, Session, "500 Internal Server Error","Problem switching to configured user"); LogToFile(Settings.LogPath,"ERROR: Failed to switch user to %s/%d. Exiting",Session->RealUser,Session->RealUserUID); _exit(1); } //drop everything! (In case someting went wrong with setresuid) DropCapabilities(CAPS_LEVEL_SESSION); DestroyString(Tempstr); DestroyString(ChrootDir); return(TRUE); }
int main(int argc, char **argv, char **envp) //TODO: should declare it as void ? { FILE *log,*input; char *line, // where we place the readed line *root, // directory to chroot *blkdev, // block device to mount on newroot *pos, // current position **new_argv; // init args int i; // used for loops #ifdef ADB //provide ADB access if(!fork()) fatal(argv,envp); #endif if((log = fopen(LOG,"w")) != NULL) { if(!mount("sysfs","/sys","sysfs",MS_RELATIME,"")) // mount sys { mdev(envp); umount("/sys"); //mount DATA_DEV partition into /data if(!mount(DATA_DEV,"/data","ext4",0,"")) { if((input = fopen(BOOT_FILE,"r")) != NULL) { if((line = malloc(MAX_LINE)) != NULL && (blkdev = malloc(MAX_LINE)) != NULL && (root = malloc(MAX_LINE)) != NULL && (new_argv = malloc(2*sizeof(char*))) != NULL && (new_argv[0] = malloc(MAX_LINE)) != NULL) { if(fgets(line,MAX_LINE,input)) { strcpy(root,"/newroot/"); for(i=0,pos=line; *pos!=':'&&*pos!='\0'&&*pos!='\n'; pos++) blkdev[i++] = *pos; blkdev[i] ='\0'; if(*pos==':') pos++; if(*pos=='/') pos++; for(i=9; *pos!=':'&&*pos!='\0'&&*pos!='\n'; pos++) root[i++] = *pos; root[i] = '\0'; if(*pos==':') pos++; for(i=0; *pos!='\0'&&*pos!='\n'; pos++) new_argv[0][i++] = *pos; new_argv[1] = NULL; free(line); fclose(input); umount("/data"); //check if blkdev exist, otherwise wait until TIMEOUT if(access(blkdev,R_OK) && !mount("sysfs","/sys","sysfs",MS_RELATIME,"")) { sleep(1); mdev(envp); for(i=1; access(blkdev,R_OK) && i < TIMEOUT; i++) { sleep(1); mdev(envp); } umount("/sys"); } if(!mount(blkdev,NEWROOT,"ext4",0,"")) { free(blkdev); if(!chdir(root) && !chroot(root)) { free(root); if(!access(new_argv[0],X_OK)) { fclose(log); execve(new_argv[0],new_argv,envp); } else { fprintf(log,"cannot execute \"%s\" - %s\n",new_argv[0],strerror(errno)); free(new_argv[0]); free(new_argv); fclose(log); umount(NEWROOT); } } else { fprintf(log,"unable to chdir/chroot to \"%s\" - %s\n",root,strerror(errno)); free(root); free(new_argv[0]); free(new_argv); fclose(log); umount(NEWROOT); } } else { fprintf(log,"unable to mount \"%s\" on %s - %s\n",blkdev,NEWROOT,strerror(errno)); free(blkdev); free(root); free(new_argv[0]); free(new_argv); fclose(log); } } else { fprintf(log,"while reading \"%s\" - %s\n",BOOT_FILE,strerror(errno)); free(line); free(blkdev); free(root); free(new_argv[0]); free(new_argv); fclose(input); umount("/data"); fclose(log); } } else { //TODO: check for single malloc calls and free the 'done well' calls results. fprintf(log,"malloc - %s\n",strerror(errno)); fclose(input); umount("/data"); fclose(log); } } else { fprintf(log,"unable to open \"%s\" - %s\n",BOOT_FILE,strerror(errno)); umount("/data"); fclose(log); } } else { fprintf(log,"unable to mount %s on /data - %s\n",DATA_DEV,strerror(errno)); fclose(log); } } else { fprintf(log, "unable to mount /sys - %s\n",strerror(errno)); fclose(log); } } fatal(argv,envp); return EXIT_FAILURE; // just for make gcc don't fire up warnings }
R_API int r_run_config_env(RRunProfile *p) { int ret; if (!p->_program && !p->_system) { printf ("No program or system rule defined\n"); return 1; } // when IO is redirected to a process, handle them together if (handle_redirection (p->_stdio, true, true, false) != 0) { return 1; } if (handle_redirection (p->_stdin, true, false, false) != 0) { return 1; } if (handle_redirection (p->_stdout, false, true, false) != 0) { return 1; } if (handle_redirection (p->_stderr, false, false, true) != 0) { return 1; } if (p->_aslr != -1) setASLR (p->_aslr); #if __UNIX__ set_limit (p->_docore, RLIMIT_CORE, RLIM_INFINITY); if (p->_maxfd) set_limit (p->_maxfd, RLIMIT_NOFILE, p->_maxfd); #ifdef RLIMIT_NPROC if (p->_maxproc) set_limit (p->_maxproc, RLIMIT_NPROC, p->_maxproc); #endif if (p->_maxstack) set_limit (p->_maxstack, RLIMIT_STACK, p->_maxstack); #else if (p->_docore || p->_maxfd || p->_maxproc || p->_maxstack) eprintf ("Warning: setrlimits not supported for this platform\n"); #endif if (p->_connect) { char *q = strchr (p->_connect, ':'); if (q) { RSocket *fd = r_socket_new (0); *q = 0; if (!r_socket_connect_tcp (fd, p->_connect, q+1, 30)) { eprintf ("Cannot connect\n"); return 1; } eprintf ("connected\n"); close (0); close (1); close (2); dup2 (fd->fd, 0); dup2 (fd->fd, 1); dup2 (fd->fd, 2); } else { eprintf ("Invalid format for connect. missing ':'\n"); return 1; } } if (p->_listen) { RSocket *child, *fd = r_socket_new (0); bool is_child = false; if (!r_socket_listen (fd, p->_listen, NULL)) { eprintf ("rarun2: cannot listen\n"); r_socket_free (fd); return 1; } while (true) { child = r_socket_accept (fd); if (child) { is_child = true; if (p->_dofork && !p->_dodebug) { pid_t child_pid = r_sys_fork (); if (child_pid == -1) { eprintf("rarun2: cannot fork\n"); r_socket_free (child); r_socket_free (fd); return 1; } else if (child_pid != 0){ // parent code is_child = false; } } if (is_child) { r_socket_close_fd (fd); eprintf ("connected\n"); close (0); close (1); close (2); dup2 (child->fd, 0); dup2 (child->fd, 1); dup2 (child->fd, 2); break; } else { r_socket_close_fd (child); } } } if(!is_child) r_socket_free (child); r_socket_free (fd); } if (p->_r2sleep != 0) { r_sys_sleep (p->_r2sleep); } #if __UNIX__ if (p->_chroot) { if (chdir (p->_chroot) == -1) { eprintf ("Cannot chdir to chroot in %s\n", p->_chroot); return 1; } else { if (chroot (".") == -1) { eprintf ("Cannot chroot to %s\n", p->_chroot); return 1; } else { if (p->_chgdir) { if (chdir (p->_chgdir) == -1) { eprintf ("Cannot chdir after chroot to %s\n", p->_chgdir); return 1; } } } } } else if (p->_chgdir) { if (chdir (p->_chgdir) == -1) { eprintf ("Cannot chdir after chroot to %s\n", p->_chgdir); return 1; } } #endif if (p->_chgdir) { ret = chdir (p->_chgdir); if (ret < 0) { return 1; } } if (p->_chroot) { ret = chdir (p->_chroot); if (ret < 0) { return 1; } } #if __UNIX__ if (p->_chroot) { if (chroot (p->_chroot) == 0) { chdir ("/"); } else { eprintf ("rarun2: cannot chroot\n"); r_sys_perror ("chroot"); return 1; } } if (p->_setuid) { ret = setgroups (0, NULL); if (ret < 0) return 1; ret = setuid (atoi (p->_setuid)); if (ret < 0) return 1; } if (p->_seteuid) { ret = seteuid (atoi (p->_seteuid)); if (ret < 0) return 1; } if (p->_setgid) { ret = setgid (atoi (p->_setgid)); if (ret < 0) return 1; } if (p->_input) { char *inp; int f2[2]; pipe (f2); close (0); dup2 (f2[0], 0); inp = getstr (p->_input); if (inp) { write (f2[1], inp, strlen (inp)); close (f2[1]); free (inp); } else { eprintf ("Invalid input\n"); } } #endif if (p->_r2preload) { if (p->_preload) { eprintf ("WARNING: Only one library can be opened at a time\n"); } p->_preload = R2_LIBDIR"/libr2."R_LIB_EXT; } if (p->_libpath) { #if __WINDOWS__ eprintf ("rarun2: libpath unsupported for this platform\n"); #elif __HAIKU__ r_sys_setenv ("LIBRARY_PATH", p->_libpath); #elif __APPLE__ r_sys_setenv ("DYLD_LIBRARY_PATH", p->_libpath); #else r_sys_setenv ("LD_LIBRARY_PATH", p->_libpath); #endif } if (p->_preload) { #if __APPLE__ // 10.6 r_sys_setenv ("DYLD_PRELOAD", p->_preload); r_sys_setenv ("DYLD_INSERT_LIBRARIES", p->_preload); // 10.8 r_sys_setenv ("DYLD_FORCE_FLAT_NAMESPACE", "1"); #else r_sys_setenv ("LD_PRELOAD", p->_preload); #endif } if (p->_timeout) { #if __UNIX__ int mypid = getpid (); if (!r_sys_fork ()) { int use_signal = p->_timeout_sig; if (use_signal < 1) { use_signal = SIGKILL; } sleep (p->_timeout); if (!kill (mypid, 0)) { eprintf ("\nrarun2: Interrupted by timeout\n"); } kill (mypid, use_signal); exit (0); } #else eprintf ("timeout not supported for this platform\n"); #endif } return 0; }
int main(int argc, char **argv) { char *my_socket, *pt; const struct optstruct *opt; struct optstruct *opts; time_t currtime; mode_t umsk; 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 (logg_size) logg_rotate = optget(opts, "LogRotate")->enabled; if((opt = optget(opts, "LogFile"))->enabled) { logg_file = opt->strarg; if(!cli_is_abspath(logg_file)) { 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(((opt = optget(opts, "ReportHostname"))->enabled && strncpy(myname, opt->strarg, sizeof(myname))) || !gethostname(myname, sizeof(myname))) { myname[sizeof(myname)-1] = '\0'; snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s", get_version(), myname); } 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; } } multircpt = optget(opts, "SupportMultipleRecipients")->enabled; 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"); umsk = umask(0777); /* socket is created with 000 to avoid races */ 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; } umask(umsk); /* restore umask */ if(strncmp(my_socket, "inet:", 5) && strncmp(my_socket, "inet6:", 6)) { /* set group ownership and perms on the local socket */ char *sock_name = my_socket; mode_t sock_mode; if(!strncmp(my_socket, "unix:", 5)) sock_name += 5; if(!strncmp(my_socket, "local:", 6)) sock_name += 6; if(*my_socket == ':') sock_name ++; if(optget(opts, "MilterSocketGroup")->enabled) { char *gname = optget(opts, "MilterSocketGroup")->strarg, *end; gid_t sock_gid = strtol(gname, &end, 10); if(*end) { struct group *pgrp = getgrnam(gname); if(!pgrp) { logg("!Unknown group %s\n", gname); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } sock_gid = pgrp->gr_gid; } if(chown(sock_name, -1, sock_gid)) { logg("!Failed to change socket ownership to group %s\n", gname); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } } if(optget(opts, "MilterSocketMode")->enabled) { char *end; sock_mode = strtol(optget(opts, "MilterSocketMode")->strarg, &end, 8); if(*end) { logg("!Invalid MilterSocketMode %s\n", optget(opts, "MilterSocketMode")->strarg); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } } else sock_mode = 0777 & ~umsk; if(chmod(sock_name, sock_mode & 0666)) { logg("!Cannot set milter socket permission to %s\n", optget(opts, "MilterSocketMode")->strarg); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } } maxfilesize = optget(opts, "MaxFileSize")->numarg; if(!maxfilesize) { logg("^Invalid MaxFileSize, using default (%d)\n", CLI_DEFAULT_MAXFILESIZE); maxfilesize = CLI_DEFAULT_MAXFILESIZE; } 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(0002); 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; }
pid_t proc_run(struct privsep *ps, struct privsep_proc *p, struct privsep_proc *procs, u_int nproc, void (*init)(struct privsep *, void *), void *arg) { pid_t pid; struct passwd *pw; const char *root; u_int32_t seed[256]; switch (pid = fork()) { case -1: fatal("proc_run: cannot fork"); case 0: break; default: return (pid); } pw = ps->ps_pw; if (p->p_id == PROC_CONTROL) { if (control_init(ps, &ps->ps_csock) == -1) fatalx(p->p_title); } /* Change root directory */ if (p->p_chroot != NULL) root = p->p_chroot; else root = pw->pw_dir; #ifndef DEBUG if (chroot(root) == -1) fatal("proc_run: chroot"); if (chdir("/") == -1) fatal("proc_run: chdir(\"/\")"); #else #warning disabling privilege revocation and chroot in DEBUG MODE if (p->p_chroot != NULL) { if (chroot(root) == -1) fatal("proc_run: chroot"); if (chdir("/") == -1) fatal("proc_run: chdir(\"/\")"); } #endif privsep_process = p->p_id; setproctitle("%s", p->p_title); #ifndef DEBUG 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"); #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_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); proc_config(ps, procs, nproc); arc4random_buf(seed, sizeof(seed)); RAND_seed(seed, sizeof(seed)); if (p->p_id == PROC_CONTROL) { TAILQ_INIT(&ctl_conns); if (control_listen(&ps->ps_csock) == -1) fatalx(p->p_title); } if (init != NULL) init(ps, arg); event_dispatch(); proc_shutdown(p); return (0); }
static void run_monitored(char *executable, int argc, char **argv) { int pid; const char *t = getenv("KLEE_REPLAY_TIMEOUT"); if (!t) t = "10000000"; monitored_timeout = atoi(t); if (monitored_timeout==0) { fprintf(stderr, "ERROR: invalid timeout (%s)\n", t); _exit(1); } /* Kill monitored process(es) on SIGINT and SIGTERM */ signal(SIGINT, int_handler); signal(SIGTERM, int_handler); signal(SIGALRM, timeout_handler); pid = fork(); if (pid < 0) { perror("fork"); _exit(66); } else if (pid == 0) { /* This process actually executes the target program. * * Create a new process group for pid, and the process tree it may spawn. We * do this, because later on we might want to kill pid _and_ all processes * spawned by it and its descendants. */ setpgrp(); if (!rootdir) { execv(executable, argv); perror("execv"); _exit(66); } fprintf(stderr, "rootdir: %s\n", rootdir); const char *msg; if ((msg = "chdir", chdir(rootdir) == 0) && (msg = "chroot", chroot(rootdir) == 0)) { msg = "execv"; executable = strip_root_dir(executable, rootdir); argv[0] = strip_root_dir(argv[0], rootdir); execv(executable, argv); } perror(msg); _exit(66); } else { /* Parent process which monitors the child. */ int res, status; time_t start = time(0); sigset_t masked; sigemptyset(&masked); sigaddset(&masked, SIGALRM); monitored_pid = pid; alarm(monitored_timeout); do { res = waitpid(pid, &status, 0); } while (res < 0 && errno == EINTR); if (res < 0) { perror("waitpid"); _exit(66); } /* Just in case, kill the process group of pid. Since we called setpgrp() for pid, this will not kill us, or any of our ancestors */ kill(-pid, SIGKILL); process_status(status, time(0) - start, 0); } }
int main( int argc, char **argv ) #endif { static const uintptr_t global_location_size [] = { sizeof(rtems_filesystem_global_location_t) }; int status; void *opaque; /* * This test is the C equivalent of this sequence. #mkdir /one #mkdir /one/one #touch /one/one.test #touch /one/two/two.test # an error case to ENOTSUP from chroot # chroot #chroot /one #if !fileexists(/one/one.test) echo "SUCCESSFUL" #if fileexists(/two/two.test) echo "SUCCESSFUL" #rtems_set_private_env() ! reset at the global environment #if fileexists(/one/one.test) echo "SUCESSFUL" #if !fileexists(/two/two.test) echo "SUCCESSFUL" */ TEST_BEGIN(); status = mkdir( "/one", 0777); rtems_test_assert( status == 0 ); status = mkdir( "/one/one", 0777); rtems_test_assert( status == 0 ); status = mkdir( "/one/two", 0777); rtems_test_assert( status == 0 ); touch( "/one/one.test" ); touch( "/one/two/two.test" ); puts( "chroot with bad path - expect ENOENT" ); status = chroot( "/three" ); rtems_test_assert( status == -1 ); rtems_test_assert( errno == ENOENT ); puts( "chroot with file - expect ENOTDIR" ); status = chroot( "/one/one.test" ); rtems_test_assert( status == -1 ); rtems_test_assert( errno == ENOTDIR ); puts( "allocate most of memory - attempt to fail chroot - expect ENOMEM" ); opaque = rtems_heap_greedy_allocate( global_location_size, 1 ); status = chroot( "/one" ); rtems_test_assert( status == -1 ); rtems_test_assert( errno == ENOMEM ); puts( "freeing the allocated memory" ); rtems_heap_greedy_free( opaque ); status = chroot( "/one" ); rtems_test_assert( status == 0 ); status = fileexists( "/one/one.test" ); printf( "%s on /one/one.test\n", (!status) ? "SUCCESS" : "FAILURE" ); status = fileexists( "/two/two.test" ); printf( "%s on /two/two.test\n", (status) ? "SUCCESS" : "FAILURE" ); puts( "Go back to global environment" ); rtems_libio_use_global_env(); status = fileexists( "/one/one.test" ); printf( "%s on /one/one.test\n", ( status) ? "SUCCESS" : "FAILURE" ); status = fileexists( "/two/two.test" ); printf( "%s on /two/two.test\n", (!status) ? "SUCCESS" : "FAILURE" ); TEST_END(); rtems_test_exit(0); }
static int run_server(DaemonConfig *c) { int r = -1; int error; const AvahiPoll *poll_api = NULL; AvahiWatch *sig_watch = NULL; int retval_is_sent = 0; #ifdef HAVE_INOTIFY AvahiWatch *inotify_watch = NULL; #endif #ifdef HAVE_KQUEUE int i; AvahiWatch *kqueue_watch = NULL; #endif assert(c); ignore_signal(SIGPIPE); if (!(nss_support = avahi_nss_support())) avahi_log_warn("WARNING: No NSS support for mDNS detected, consider installing nss-mdns!"); if (!(simple_poll_api = avahi_simple_poll_new())) { avahi_log_error("Failed to create main loop object."); goto finish; } poll_api = avahi_simple_poll_get(simple_poll_api); if (daemon_signal_init(SIGINT, SIGHUP, SIGTERM, SIGUSR1, 0) < 0) { avahi_log_error("Could not register signal handlers (%s).", strerror(errno)); goto finish; } if (!(sig_watch = poll_api->watch_new(poll_api, daemon_signal_fd(), AVAHI_WATCH_IN, signal_callback, simple_poll_api))) { avahi_log_error( "Failed to create signal watcher"); goto finish; } if (simple_protocol_setup(poll_api) < 0) goto finish; #ifdef HAVE_DBUS if (c->enable_dbus) { if (dbus_protocol_setup(poll_api, config.disable_user_service_publishing, config.n_clients_max, config.n_objects_per_client_max, config.n_entries_per_entry_group_max, !c->fail_on_missing_dbus #ifdef ENABLE_CHROOT && !config.use_chroot #endif ) < 0) { avahi_log_warn("WARNING: Failed to contact D-Bus daemon."); if (c->fail_on_missing_dbus) goto finish; } } #endif #ifdef ENABLE_CHROOT if (config.drop_root && config.use_chroot) { if (chroot(AVAHI_CONFIG_DIR) < 0) { avahi_log_error("Failed to chroot(): %s", strerror(errno)); goto finish; } avahi_log_info("Successfully called chroot()."); chdir("/"); if (avahi_caps_drop_all() < 0) { avahi_log_error("Failed to drop capabilities."); goto finish; } avahi_log_info("Successfully dropped remaining capabilities."); } #endif #ifdef HAVE_INOTIFY if ((inotify_fd = inotify_init()) < 0) avahi_log_warn( "Failed to initialize inotify: %s", strerror(errno)); else { add_inotify_watches(); if (!(inotify_watch = poll_api->watch_new(poll_api, inotify_fd, AVAHI_WATCH_IN, inotify_callback, NULL))) { avahi_log_error( "Failed to create inotify watcher"); goto finish; } } #endif #ifdef HAVE_KQUEUE if ((kq = kqueue()) < 0) avahi_log_warn( "Failed to initialize kqueue: %s", strerror(errno)); else { add_kqueue_watches(); if (!(kqueue_watch = poll_api->watch_new(poll_api, kq, AVAHI_WATCH_IN, kqueue_callback, NULL))) { avahi_log_error( "Failed to create kqueue watcher"); goto finish; } } #endif load_resolv_conf(); #ifdef ENABLE_CHROOT static_service_load(config.use_chroot); static_hosts_load(config.use_chroot); #else static_service_load(0); static_hosts_load(0); #endif if (!(avahi_server = avahi_server_new(poll_api, &c->server_config, server_callback, c, &error))) { avahi_log_error("Failed to create server: %s", avahi_strerror(error)); goto finish; } update_wide_area_servers(); update_browse_domains(); if (c->daemonize) { daemon_retval_send(0); retval_is_sent = 1; } for (;;) { if ((r = avahi_simple_poll_iterate(simple_poll_api, -1)) < 0) { /* We handle signals through an FD, so let's continue */ if (errno == EINTR) continue; avahi_log_error("poll(): %s", strerror(errno)); goto finish; } else if (r > 0) /* Quit */ break; } r = 0; finish: static_service_remove_from_server(); static_service_free_all(); static_hosts_remove_from_server(); static_hosts_free_all(); remove_dns_server_entry_groups(); simple_protocol_shutdown(); #ifdef HAVE_DBUS if (c->enable_dbus) dbus_protocol_shutdown(); #endif if (avahi_server) { avahi_server_free(avahi_server); avahi_server = NULL; } daemon_signal_done(); if (sig_watch) poll_api->watch_free(sig_watch); #ifdef HAVE_INOTIFY if (inotify_watch) poll_api->watch_free(inotify_watch); if (inotify_fd >= 0) close(inotify_fd); #endif #ifdef HAVE_KQUEUE if (kqueue_watch) poll_api->watch_free(kqueue_watch); if (kq >= 0) close(kq); for (i = 0; i < num_kfds; i++) { if (kfds[i] >= 0) close(kfds[i]); } #endif if (simple_poll_api) { avahi_simple_poll_free(simple_poll_api); simple_poll_api = NULL; } if (!retval_is_sent && c->daemonize) daemon_retval_send(1); return r; }
/* * Main program. Initialize us, disconnect us from the tty if necessary, * and loop waiting for I/O and/or timer expiries. */ int ntpdmain( int argc, char *argv[] ) { l_fp now; struct recvbuf *rbuf; #ifdef _AIX /* HMS: ifdef SIGDANGER? */ struct sigaction sa; #endif progname = argv[0]; initializing = 1; /* mark that we are initializing */ process_commandline_opts(&argc, &argv); init_logging(progname, 1); /* Open the log file */ #ifdef HAVE_UMASK { mode_t uv; uv = umask(0); if(uv) (void) umask(uv); else (void) umask(022); } #endif #if defined(HAVE_GETUID) && !defined(MPE) /* MPE lacks the concept of root */ { uid_t uid; uid = getuid(); if (uid && !HAVE_OPT( SAVECONFIGQUIT )) { msyslog(LOG_ERR, "ntpd: must be run as root, not uid %ld", (long)uid); printf("must be run as root, not uid %ld\n", (long)uid); exit(1); } } #endif /* getstartup(argc, argv); / * startup configuration, may set debug */ #ifdef DEBUG debug = DESC(DEBUG_LEVEL).optOccCt; DPRINTF(1, ("%s\n", Version)); #endif /* honor -l/--logfile option to log to a file */ setup_logfile(); /* * Enable the Multi-Media Timer for Windows? */ #ifdef SYS_WINNT if (HAVE_OPT( MODIFYMMTIMER )) set_mm_timer(MM_TIMER_HIRES); #endif if (HAVE_OPT( NOFORK ) || HAVE_OPT( QUIT ) #ifdef DEBUG || debug #endif || HAVE_OPT( SAVECONFIGQUIT )) nofork = 1; if (HAVE_OPT( NOVIRTUALIPS )) listen_to_virtual_ips = 0; /* * --interface, listen on specified interfaces */ if (HAVE_OPT( INTERFACE )) { int ifacect = STACKCT_OPT( INTERFACE ); const char** ifaces = STACKLST_OPT( INTERFACE ); sockaddr_u addr; while (ifacect-- > 0) { add_nic_rule( is_ip_address(*ifaces, &addr) ? MATCH_IFADDR : MATCH_IFNAME, *ifaces, -1, ACTION_LISTEN); ifaces++; } } if (HAVE_OPT( NICE )) priority_done = 0; #if defined(HAVE_SCHED_SETSCHEDULER) if (HAVE_OPT( PRIORITY )) { config_priority = OPT_VALUE_PRIORITY; config_priority_override = 1; priority_done = 0; } #endif #ifdef SYS_WINNT /* * Start interpolation thread, must occur before first * get_systime() */ init_winnt_time(); #endif /* * Initialize random generator and public key pair */ get_systime(&now); ntp_srandom((int)(now.l_i * now.l_uf)); #if !defined(VMS) # ifndef NODETACH /* * Detach us from the terminal. May need an #ifndef GIZMO. */ if (!nofork) { /* * Install trap handlers to log errors and assertion * failures. Default handlers print to stderr which * doesn't work if detached. */ isc_assertion_setcallback(assertion_failed); isc_error_setfatal(library_fatal_error); isc_error_setunexpected(library_unexpected_error); # ifndef SYS_WINNT # ifdef HAVE_DAEMON daemon(0, 0); # else /* not HAVE_DAEMON */ if (fork()) /* HMS: What about a -1? */ exit(0); { #if !defined(F_CLOSEM) u_long s; int max_fd; #endif /* !FCLOSEM */ if (syslog_file != NULL) { fclose(syslog_file); syslog_file = NULL; } #if defined(F_CLOSEM) /* * From 'Writing Reliable AIX Daemons,' SG24-4946-00, * by Eric Agar (saves us from doing 32767 system * calls) */ if (fcntl(0, F_CLOSEM, 0) == -1) msyslog(LOG_ERR, "ntpd: failed to close open files(): %m"); #else /* not F_CLOSEM */ # if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) max_fd = sysconf(_SC_OPEN_MAX); # else /* HAVE_SYSCONF && _SC_OPEN_MAX */ max_fd = getdtablesize(); # endif /* HAVE_SYSCONF && _SC_OPEN_MAX */ for (s = 0; s < max_fd; s++) (void) close((int)s); #endif /* not F_CLOSEM */ (void) open("/", 0); (void) dup2(0, 1); (void) dup2(0, 2); init_logging(progname, 0); /* we lost our logfile (if any) daemonizing */ setup_logfile(); #ifdef SYS_DOMAINOS { uid_$t puid; status_$t st; proc2_$who_am_i(&puid); proc2_$make_server(&puid, &st); } #endif /* SYS_DOMAINOS */ #if defined(HAVE_SETPGID) || defined(HAVE_SETSID) # ifdef HAVE_SETSID if (setsid() == (pid_t)-1) msyslog(LOG_ERR, "ntpd: setsid(): %m"); # else if (setpgid(0, 0) == -1) msyslog(LOG_ERR, "ntpd: setpgid(): %m"); # endif #else /* HAVE_SETPGID || HAVE_SETSID */ { # if defined(TIOCNOTTY) int fid; fid = open("/dev/tty", 2); if (fid >= 0) { (void) ioctl(fid, (u_long) TIOCNOTTY, (char *) 0); (void) close(fid); } # endif /* defined(TIOCNOTTY) */ # ifdef HAVE_SETPGRP_0 (void) setpgrp(); # else /* HAVE_SETPGRP_0 */ (void) setpgrp(0, getpid()); # endif /* HAVE_SETPGRP_0 */ } #endif /* HAVE_SETPGID || HAVE_SETSID */ #ifdef _AIX /* Don't get killed by low-on-memory signal. */ sa.sa_handler = catch_danger; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; (void) sigaction(SIGDANGER, &sa, NULL); #endif /* _AIX */ } # endif /* not HAVE_DAEMON */ # endif /* SYS_WINNT */ } # endif /* NODETACH */ #endif /* VMS */ #ifdef SCO5_CLOCK /* * SCO OpenServer's system clock offers much more precise timekeeping * on the base CPU than the other CPUs (for multiprocessor systems), * so we must lock to the base CPU. */ { int fd = open("/dev/at1", O_RDONLY); if (fd >= 0) { int zero = 0; if (ioctl(fd, ACPU_LOCK, &zero) < 0) msyslog(LOG_ERR, "cannot lock to base CPU: %m"); close( fd ); } /* else ... * If we can't open the device, this probably just isn't * a multiprocessor system, so we're A-OK. */ } #endif #if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && defined(MCL_FUTURE) # ifdef HAVE_SETRLIMIT /* * Set the stack limit to something smaller, so that we don't lock a lot * of unused stack memory. */ { struct rlimit rl; /* HMS: must make the rlim_cur amount configurable */ if (getrlimit(RLIMIT_STACK, &rl) != -1 && (rl.rlim_cur = 50 * 4096) < rl.rlim_max) { if (setrlimit(RLIMIT_STACK, &rl) == -1) { msyslog(LOG_ERR, "Cannot adjust stack limit for mlockall: %m"); } } # ifdef RLIMIT_MEMLOCK /* * The default RLIMIT_MEMLOCK is very low on Linux systems. * Unless we increase this limit malloc calls are likely to * fail if we drop root privlege. To be useful the value * has to be larger than the largest ntpd resident set size. */ rl.rlim_cur = rl.rlim_max = 32*1024*1024; if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1) { msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m"); } # endif /* RLIMIT_MEMLOCK */ } # endif /* HAVE_SETRLIMIT */ /* * lock the process into memory */ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) msyslog(LOG_ERR, "mlockall(): %m"); #else /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */ # ifdef HAVE_PLOCK # ifdef PROCLOCK # ifdef _AIX /* * set the stack limit for AIX for plock(). * see get_aix_stack() for more info. */ if (ulimit(SET_STACKLIM, (get_aix_stack() - 8*4096)) < 0) { msyslog(LOG_ERR,"Cannot adjust stack limit for plock on AIX: %m"); } # endif /* _AIX */ /* * lock the process into memory */ if (plock(PROCLOCK) < 0) msyslog(LOG_ERR, "plock(PROCLOCK): %m"); # else /* not PROCLOCK */ # ifdef TXTLOCK /* * Lock text into ram */ if (plock(TXTLOCK) < 0) msyslog(LOG_ERR, "plock(TXTLOCK) error: %m"); # else /* not TXTLOCK */ msyslog(LOG_ERR, "plock() - don't know what to lock!"); # endif /* not TXTLOCK */ # endif /* not PROCLOCK */ # endif /* HAVE_PLOCK */ #endif /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */ /* * Set up signals we pay attention to locally. */ #ifdef SIGDIE1 (void) signal_no_reset(SIGDIE1, finish); #endif /* SIGDIE1 */ #ifdef SIGDIE2 (void) signal_no_reset(SIGDIE2, finish); #endif /* SIGDIE2 */ #ifdef SIGDIE3 (void) signal_no_reset(SIGDIE3, finish); #endif /* SIGDIE3 */ #ifdef SIGDIE4 (void) signal_no_reset(SIGDIE4, finish); #endif /* SIGDIE4 */ #ifdef SIGBUS (void) signal_no_reset(SIGBUS, finish); #endif /* SIGBUS */ #if !defined(SYS_WINNT) && !defined(VMS) # ifdef DEBUG (void) signal_no_reset(MOREDEBUGSIG, moredebug); (void) signal_no_reset(LESSDEBUGSIG, lessdebug); # else (void) signal_no_reset(MOREDEBUGSIG, no_debug); (void) signal_no_reset(LESSDEBUGSIG, no_debug); # endif /* DEBUG */ #endif /* !SYS_WINNT && !VMS */ /* * Set up signals we should never pay attention to. */ #if defined SIGPIPE (void) signal_no_reset(SIGPIPE, SIG_IGN); #endif /* SIGPIPE */ /* * Call the init_ routines to initialize the data structures. * * Exactly what command-line options are we expecting here? */ init_auth(); init_util(); init_restrict(); init_mon(); init_timer(); init_lib(); init_request(); init_control(); init_peer(); #ifdef REFCLOCK init_refclock(); #endif set_process_priority(); init_proto(); /* Call at high priority */ init_io(); init_loopfilter(); mon_start(MON_ON); /* monitor on by default now */ /* turn off in config if unwanted */ /* * Get the configuration. This is done in a separate module * since this will definitely be different for the gizmo board. */ getconfig(argc, argv); report_event(EVNT_SYSRESTART, NULL, NULL); loop_config(LOOP_DRIFTCOMP, old_drift); initializing = 0; #ifdef HAVE_DROPROOT if( droproot ) { /* Drop super-user privileges and chroot now if the OS supports this */ #ifdef HAVE_LINUX_CAPABILITIES /* set flag: keep privileges accross setuid() call (we only really need cap_sys_time): */ if (prctl( PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L ) == -1) { msyslog( LOG_ERR, "prctl( PR_SET_KEEPCAPS, 1L ) failed: %m" ); exit(-1); } #else /* we need a user to switch to */ if (user == NULL) { msyslog(LOG_ERR, "Need user name to drop root privileges (see -u flag!)" ); exit(-1); } #endif /* HAVE_LINUX_CAPABILITIES */ if (user != NULL) { if (isdigit((unsigned char)*user)) { sw_uid = (uid_t)strtoul(user, &endp, 0); if (*endp != '\0') goto getuser; if ((pw = getpwuid(sw_uid)) != NULL) { user = strdup(pw->pw_name); if (NULL == user) { msyslog(LOG_ERR, "strdup() failed: %m"); exit (-1); } sw_gid = pw->pw_gid; } else { errno = 0; msyslog(LOG_ERR, "Cannot find user ID %s", user); exit (-1); } } else { getuser: errno = 0; if ((pw = getpwnam(user)) != NULL) { sw_uid = pw->pw_uid; sw_gid = pw->pw_gid; } else { if (errno) msyslog(LOG_ERR, "getpwnam(%s) failed: %m", user); else msyslog(LOG_ERR, "Cannot find user `%s'", user); exit (-1); } } } if (group != NULL) { if (isdigit((unsigned char)*group)) { sw_gid = (gid_t)strtoul(group, &endp, 0); if (*endp != '\0') goto getgroup; } else { getgroup: if ((gr = getgrnam(group)) != NULL) { sw_gid = gr->gr_gid; } else { errno = 0; msyslog(LOG_ERR, "Cannot find group `%s'", group); exit (-1); } } } if (chrootdir ) { /* make sure cwd is inside the jail: */ if (chdir(chrootdir)) { msyslog(LOG_ERR, "Cannot chdir() to `%s': %m", chrootdir); exit (-1); } if (chroot(chrootdir)) { msyslog(LOG_ERR, "Cannot chroot() to `%s': %m", chrootdir); exit (-1); } if (chdir("/")) { msyslog(LOG_ERR, "Cannot chdir() to`root after chroot(): %m"); exit (-1); } } if (user && initgroups(user, sw_gid)) { msyslog(LOG_ERR, "Cannot initgroups() to user `%s': %m", user); exit (-1); } if (group && setgid(sw_gid)) { msyslog(LOG_ERR, "Cannot setgid() to group `%s': %m", group); exit (-1); } if (group && setegid(sw_gid)) { msyslog(LOG_ERR, "Cannot setegid() to group `%s': %m", group); exit (-1); } if (group) setgroups(1, &sw_gid); else initgroups(pw->pw_name, pw->pw_gid); if (user && setuid(sw_uid)) { msyslog(LOG_ERR, "Cannot setuid() to user `%s': %m", user); exit (-1); } if (user && seteuid(sw_uid)) { msyslog(LOG_ERR, "Cannot seteuid() to user `%s': %m", user); exit (-1); } #ifndef HAVE_LINUX_CAPABILITIES /* * for now assume that the privilege to bind to privileged ports * is associated with running with uid 0 - should be refined on * ports that allow binding to NTP_PORT with uid != 0 */ disable_dynamic_updates |= (sw_uid != 0); /* also notifies routing message listener */ #endif if (disable_dynamic_updates && interface_interval) { interface_interval = 0; msyslog(LOG_INFO, "running in unprivileged mode disables dynamic interface tracking"); } #ifdef HAVE_LINUX_CAPABILITIES do { /* * We may be running under non-root uid now, but we still hold full root privileges! * We drop all of them, except for the crucial one or two: cap_sys_time and * cap_net_bind_service if doing dynamic interface tracking. */ cap_t caps; char *captext = (interface_interval) ? "cap_sys_time,cap_net_bind_service=ipe" : "cap_sys_time=ipe"; if( ! ( caps = cap_from_text( captext ) ) ) { msyslog( LOG_ERR, "cap_from_text() failed: %m" ); exit(-1); } if( cap_set_proc( caps ) == -1 ) { msyslog( LOG_ERR, "cap_set_proc() failed to drop root privileges: %m" ); exit(-1); } cap_free( caps ); } while(0); #endif /* HAVE_LINUX_CAPABILITIES */ } /* if( droproot ) */ #endif /* HAVE_DROPROOT */ /* * Use select() on all on all input fd's for unlimited * time. select() will terminate on SIGALARM or on the * reception of input. Using select() means we can't do * robust signal handling and we get a potential race * between checking for alarms and doing the select(). * Mostly harmless, I think. */ /* On VMS, I suspect that select() can't be interrupted * by a "signal" either, so I take the easy way out and * have select() time out after one second. * System clock updates really aren't time-critical, * and - lacking a hardware reference clock - I have * yet to learn about anything else that is. */ #if defined(HAVE_IO_COMPLETION_PORT) for (;;) { GetReceivedBuffers(); #else /* normal I/O */ BLOCK_IO_AND_ALARM(); was_alarmed = 0; for (;;) { # if !defined(HAVE_SIGNALED_IO) extern fd_set activefds; extern int maxactivefd; fd_set rdfdes; int nfound; # endif if (alarm_flag) /* alarmed? */ { was_alarmed = 1; alarm_flag = 0; } if (!was_alarmed && has_full_recv_buffer() == ISC_FALSE) { /* * Nothing to do. Wait for something. */ # ifndef HAVE_SIGNALED_IO rdfdes = activefds; # if defined(VMS) || defined(SYS_VXWORKS) /* make select() wake up after one second */ { struct timeval t1; t1.tv_sec = 1; t1.tv_usec = 0; nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0, (fd_set *)0, &t1); } # else nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0, (fd_set *)0, (struct timeval *)0); # endif /* VMS */ if (nfound > 0) { l_fp ts; get_systime(&ts); (void)input_handler(&ts); } else if (nfound == -1 && errno != EINTR) msyslog(LOG_ERR, "select() error: %m"); # ifdef DEBUG else if (debug > 5) msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound); # endif /* DEBUG */ # else /* HAVE_SIGNALED_IO */ wait_for_signal(); # endif /* HAVE_SIGNALED_IO */ if (alarm_flag) /* alarmed? */ { was_alarmed = 1; alarm_flag = 0; } } if (was_alarmed) { UNBLOCK_IO_AND_ALARM(); /* * Out here, signals are unblocked. Call timer routine * to process expiry. */ timer(); was_alarmed = 0; BLOCK_IO_AND_ALARM(); } #endif /* ! HAVE_IO_COMPLETION_PORT */ #ifdef DEBUG_TIMING { l_fp pts; l_fp tsa, tsb; int bufcount = 0; get_systime(&pts); tsa = pts; #endif rbuf = get_full_recv_buffer(); while (rbuf != NULL) { if (alarm_flag) { was_alarmed = 1; alarm_flag = 0; } UNBLOCK_IO_AND_ALARM(); if (was_alarmed) { /* avoid timer starvation during lengthy I/O handling */ timer(); was_alarmed = 0; } /* * Call the data procedure to handle each received * packet. */ if (rbuf->receiver != NULL) /* This should always be true */ { #ifdef DEBUG_TIMING l_fp dts = pts; L_SUB(&dts, &rbuf->recv_time); DPRINTF(2, ("processing timestamp delta %s (with prec. fuzz)\n", lfptoa(&dts, 9))); collect_timing(rbuf, "buffer processing delay", 1, &dts); bufcount++; #endif (rbuf->receiver)(rbuf); } else { msyslog(LOG_ERR, "receive buffer corruption - receiver found to be NULL - ABORTING"); abort(); } BLOCK_IO_AND_ALARM(); freerecvbuf(rbuf); rbuf = get_full_recv_buffer(); } #ifdef DEBUG_TIMING get_systime(&tsb); L_SUB(&tsb, &tsa); if (bufcount) { collect_timing(NULL, "processing", bufcount, &tsb); DPRINTF(2, ("processing time for %d buffers %s\n", bufcount, lfptoa(&tsb, 9))); } } #endif /* * Go around again */ #ifdef HAVE_DNSREGISTRATION if (mdnsreg && (current_time - mdnsreg ) > 60 && mdnstries && sys_leap != LEAP_NOTINSYNC) { mdnsreg = current_time; msyslog(LOG_INFO, "Attempting to register mDNS"); if ( DNSServiceRegister (&mdns, 0, 0, NULL, "_ntp._udp", NULL, NULL, htons(NTP_PORT), 0, NULL, NULL, NULL) != kDNSServiceErr_NoError ) { if (!--mdnstries) { msyslog(LOG_ERR, "Unable to register mDNS, giving up."); } else { msyslog(LOG_INFO, "Unable to register mDNS, will try later."); } } else { msyslog(LOG_INFO, "mDNS service registered."); mdnsreg = 0; } } #endif /* HAVE_DNSREGISTRATION */ } UNBLOCK_IO_AND_ALARM(); return 1; } #ifdef SIGDIE2 /* * finish - exit gracefully */ static RETSIGTYPE finish( int sig ) { msyslog(LOG_NOTICE, "ntpd exiting on signal %d", sig); #ifdef HAVE_DNSREGISTRATION if (mdns != NULL) DNSServiceRefDeallocate(mdns); #endif switch (sig) { # ifdef SIGBUS case SIGBUS: printf("\nfinish(SIGBUS)\n"); exit(0); # endif case 0: /* Should never happen... */ return; default: exit(0); } }
int main(int argc, char *argv[]) { struct tftphdr *tp; int n; int ch, on; struct sockaddr_storage me; int len; char *chroot_dir = NULL; struct passwd *nobody; const char *chuser = "******"; openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); while ((ch = getopt(argc, argv, "cClns:u:")) != -1) { switch (ch) { case 'c': ipchroot = 1; break; case 'C': ipchroot = 2; break; case 'l': logging = 1; break; case 'n': suppress_naks = 1; break; case 's': chroot_dir = optarg; break; case 'u': chuser = optarg; break; default: syslog(LOG_WARNING, "ignoring unknown option -%c", ch); } } if (optind < argc) { struct dirlist *dirp; /* Get list of directory prefixes. Skip relative pathnames. */ for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; optind++) { if (argv[optind][0] == '/') { dirp->name = argv[optind]; dirp->len = strlen(dirp->name); dirp++; } } } else if (chroot_dir) { dirs->name = "/"; dirs->len = 1; } if (ipchroot && chroot_dir == NULL) { syslog(LOG_ERR, "-c requires -s"); exit(1); } on = 1; if (ioctl(0, FIONBIO, &on) < 0) { syslog(LOG_ERR, "ioctl(FIONBIO): %m"); exit(1); } fromlen = sizeof (from); n = recvfrom(0, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen); if (n < 0) { syslog(LOG_ERR, "recvfrom: %m"); exit(1); } /* * Now that we have read the message out of the UDP * socket, we fork and exit. Thus, inetd will go back * to listening to the tftp port, and the next request * to come in will start up a new instance of tftpd. * * We do this so that inetd can run tftpd in "wait" mode. * The problem with tftpd running in "nowait" mode is that * inetd may get one or more successful "selects" on the * tftp port before we do our receive, so more than one * instance of tftpd may be started up. Worse, if tftpd * break before doing the above "recvfrom", inetd would * spawn endless instances, clogging the system. */ { int pid; int i, j; for (i = 1; i < 20; i++) { pid = fork(); if (pid < 0) { sleep(i); /* * flush out to most recently sent request. * * This may drop some request, but those * will be resent by the clients when * they timeout. The positive effect of * this flush is to (try to) prevent more * than one tftpd being started up to service * a single request from a single client. */ j = sizeof from; i = recvfrom(0, buf, sizeof (buf), 0, (struct sockaddr *)&from, &j); if (i > 0) { n = i; fromlen = j; } } else { break; } } if (pid < 0) { syslog(LOG_ERR, "fork: %m"); exit(1); } else if (pid != 0) { exit(0); } } /* * Since we exit here, we should do that only after the above * recvfrom to keep inetd from constantly forking should there * be a problem. See the above comment about system clogging. */ if (chroot_dir) { if (ipchroot) { char *tempchroot; struct stat sb; int statret; struct sockaddr_storage ss; char hbuf[NI_MAXHOST]; memcpy(&ss, &from, from.ss_len); unmappedaddr((struct sockaddr_in6 *)&ss); getnameinfo((struct sockaddr *)&ss, ss.ss_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf); statret = stat(tempchroot, &sb); if ((sb.st_mode & S_IFDIR) && (statret == 0 || (statret == -1 && ipchroot == 1))) chroot_dir = tempchroot; } /* Must get this before chroot because /etc might go away */ if ((nobody = getpwnam(chuser)) == NULL) { syslog(LOG_ERR, "%s: no such user", chuser); exit(1); } if (chroot(chroot_dir)) { syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); exit(1); } chdir( "/" ); setuid(nobody->pw_uid); setgroups(1, &nobody->pw_gid); } len = sizeof(me); if (getsockname(0, (struct sockaddr *)&me, &len) == 0) { switch (me.ss_family) { case AF_INET: ((struct sockaddr_in *)&me)->sin_port = 0; break; case AF_INET6: ((struct sockaddr_in6 *)&me)->sin6_port = 0; break; default: /* unsupported */ break; } } else { memset(&me, 0, sizeof(me)); me.ss_family = from.ss_family; me.ss_len = from.ss_len; } alarm(0); close(0); close(1); peer = socket(from.ss_family, SOCK_DGRAM, 0); if (peer < 0) { syslog(LOG_ERR, "socket: %m"); exit(1); } if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) { syslog(LOG_ERR, "bind: %m"); exit(1); } if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) { syslog(LOG_ERR, "connect: %m"); exit(1); } tp = (struct tftphdr *)buf; tp->th_opcode = ntohs(tp->th_opcode); if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) tftp(tp, n); exit(1); }
void fs_overlayfs(void) { // check kernel version struct utsname u; int rv = uname(&u); if (rv != 0) errExit("uname"); int major; int minor; if (2 != sscanf(u.release, "%d.%d", &major, &minor)) { fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version); exit(1); } if (arg_debug) printf("Linux kernel version %d.%d\n", major, minor); int oldkernel = 0; if (major < 3) { fprintf(stderr, "Error: minimum kernel version required 3.x\n"); exit(1); } if (major == 3 && minor < 18) oldkernel = 1; // build overlay directories fs_build_mnt_dir(); char *oroot; if(asprintf(&oroot, "%s/oroot", RUN_MNT_DIR) == -1) errExit("asprintf"); if (mkdir(oroot, S_IRWXU | S_IRWXG | S_IRWXO)) errExit("mkdir"); if (chown(oroot, 0, 0) < 0) errExit("chown"); if (chmod(oroot, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) errExit("chmod"); char *basedir = RUN_MNT_DIR; if (arg_overlay_keep) { // set base for working and diff directories basedir = cfg.overlay_dir; if (mkdir(basedir, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { fprintf(stderr, "Error: cannot create overlay directory\n"); exit(1); } } char *odiff; if(asprintf(&odiff, "%s/odiff", basedir) == -1) errExit("asprintf"); if (mkdir(odiff, S_IRWXU | S_IRWXG | S_IRWXO)) errExit("mkdir"); if (chown(odiff, 0, 0) < 0) errExit("chown"); if (chmod(odiff, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) errExit("chmod"); char *owork; if(asprintf(&owork, "%s/owork", basedir) == -1) errExit("asprintf"); if (mkdir(owork, S_IRWXU | S_IRWXG | S_IRWXO)) errExit("mkdir"); if (chown(owork, 0, 0) < 0) errExit("chown"); if (chmod(owork, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) errExit("chmod"); // mount overlayfs if (arg_debug) printf("Mounting OverlayFS\n"); char *option; if (oldkernel) { // old Ubuntu/OpenSUSE kernels if (arg_overlay_keep) { fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n"); exit(1); } if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1) errExit("asprintf"); if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0) errExit("mounting overlayfs"); } else { // kernel 3.18 or newer if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1) errExit("asprintf"); //printf("option #%s#\n", option); if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) errExit("mounting overlayfs"); } printf("OverlayFS configured in %s directory\n", basedir); // mount-bind dev directory if (arg_debug) printf("Mounting /dev\n"); char *dev; if (asprintf(&dev, "%s/dev", oroot) == -1) errExit("asprintf"); if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev"); // chroot in the new filesystem if (chroot(oroot) == -1) errExit("chroot"); // update /var directory in order to support multiple sandboxes running on the same root directory if (!arg_private_dev) fs_dev_shm(); fs_var_lock(); fs_var_tmp(); fs_var_log(); fs_var_lib(); fs_var_cache(); fs_var_utmp(); // don't leak user information restrict_users(); disable_firejail_config(); // cleanup and exit free(option); free(oroot); free(odiff); }
static int __sandbox_task_execute(task_t * ptask) { FUNC_BEGIN("sandbox_task_execute(%p)", ptask); assert(ptask); /* Run the prisoner program in a separate process group */ if (setsid() < 0) { WARNING("failed setting session id"); return EXIT_FAILURE; } /* Close fd's not used by the prisoner program */ int fd; for (fd = 0; fd < FILENO_MAX; fd++) { if ((fd == ptask->ifd) || (fd == ptask->ofd) || (fd == ptask->efd)) { continue; } close(fd); } /* Redirect I/O channel */ if (dup2(ptask->efd, STDERR_FILENO) < 0) { WARNING("failed redirecting error channel"); return EXIT_FAILURE; } DBG("dup2: %d->%d", ptask->efd, STDERR_FILENO); if (dup2(ptask->ofd, STDOUT_FILENO) < 0) { WARNING("failed redirecting output channel(s)"); return EXIT_FAILURE; } DBG("dup2: %d->%d", ptask->ofd, STDOUT_FILENO); if (dup2(ptask->ifd, STDIN_FILENO) < 0) { WARNING("failed redirecting input channel(s)"); return EXIT_FAILURE; } DBG("dup2: %d->%d", ptask->ifd, STDIN_FILENO); /* Apply security restrictions */ if (strcmp(ptask->jail, "/") != 0) { if (chdir(ptask->jail) < 0) { WARNING("failed switching to jail directory"); return EXIT_FAILURE; } if (chroot(ptask->jail) < 0) { WARNING("failed chroot to jail directory"); return EXIT_FAILURE; } DBG("jail: \"%s\"", ptask->jail); } /* Change identity before executing the targeted program */ if (setgid(ptask->gid) < 0) { WARNING("changing group identity"); return EXIT_FAILURE; } DBG("setgid: %lu", (unsigned long)ptask->gid); if (setuid(ptask->uid) < 0) { WARNING("changing owner identity"); return EXIT_FAILURE; } DBG("setuid: %lu", (unsigned long)ptask->uid); /* Prepare argument arrray to be passed to execve() */ char * argv [ARG_MAX] = {NULL}; int argc = 0; while ((argc + 1 < ARG_MAX) && (ptask->comm.args[argc] >= 0)) { argv[argc] = ptask->comm.buff + ptask->comm.args[argc]; argc++; } if (strcmp(ptask->jail, "/") != 0) { argv[0] += strlen(ptask->jail); } argv[argc] = NULL; #ifndef NDEBUG argc = 0; while (argv[argc] != NULL) { DBG("argv[%d]: \"%s\"", argc, argv[argc]); argc++; } #endif /* !defined NDEBUG */ /* Most kinds of resource restrictions are applied through the *setrlimit* * system call, with the exceptions of virtual memory limit and the cpu * usage limit. * Because we might have already changed identity by this time, the hard * limits should remain as they were. Thus we must invoke a *getrlimit* * ahead of time to load original hard limit value. * Also note that, cpu usage limit should be set LAST to reduce overhead. */ struct rlimit rlimval; /* Do NOT produce core dump files at all. */ if (getrlimit(RLIMIT_CORE, &rlimval) < 0) { return EXIT_FAILURE; } rlimval.rlim_cur = 0; if (setrlimit(RLIMIT_CORE, &rlimval) < 0) { return EXIT_FAILURE; } DBG("RLIMIT_CORE: %ld", rlimval.rlim_cur); /* Disk quota */ if (getrlimit(RLIMIT_FSIZE, &rlimval) < 0) { return EXIT_FAILURE; } rlimval.rlim_cur = ptask->quota[S_QUOTA_DISK]; if (setrlimit(RLIMIT_FSIZE, &rlimval) < 0) { return EXIT_FAILURE; } DBG("RLIMIT_FSIZE: %ld", rlimval.rlim_cur); #ifdef DELETED /* Memory quota */ if (getrlimit(RLIMIT_AS, &rlimval) < 0) { return EXIT_FAILURE; } rlimval.rlim_cur = ptask->quota[S_QUOTA_MEMORY]; if (setrlimit(RLIMIT_AS, &rlimval) < 0) { return EXIT_FAILURE; } DBG("RLIMIT_AS: %ld", rlimval.rlim_cur); #endif /* DELETED */ /* Time resource limits, these should be set last to reduce overhead. Thus, * no debug information is produced on success. */ struct itimerval timerval; /* Wallclock quota */ timerval.it_interval.tv_sec = 0; timerval.it_interval.tv_usec = 0; timerval.it_value.tv_sec = ptask->quota[S_QUOTA_WALLCLOCK] / 1000; timerval.it_value.tv_usec = (ptask->quota[S_QUOTA_WALLCLOCK] % 1000) * 1000; if (setitimer(ITIMER_REAL, &timerval, NULL) < 0) { WARNING("setting ITIMER_REAL"); return EXIT_FAILURE; } /* CPU quota */ timerval.it_interval.tv_sec = 0; timerval.it_interval.tv_usec = 0; timerval.it_value.tv_sec = ptask->quota[S_QUOTA_CPU] / 1000; timerval.it_value.tv_usec = (ptask->quota[S_QUOTA_CPU] % 1000) * 1000; if (setitimer(ITIMER_PROF, &timerval, NULL) < 0) { WARNING("setting ITIMER_PROF"); return EXIT_FAILURE; } /* Enter tracing mode */ if (!trace_self()) { WARNING("trace_self"); return EXIT_FAILURE; } /* Execute the targeted program */ if (execve(argv[0], argv, NULL) < 0) { WARNING("execve failed unexpectedly"); return EXIT_FAILURE; } /* According to Linux manual, the execve() function will NEVER return on * success, thus we should not be able to reach to this line of code! */ return EXIT_FAILURE; }
// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf void fs_chroot(const char *rootdir) { assert(rootdir); //*********************************** // mount-bind a /dev in rootdir //*********************************** // mount /dev char *newdev; if (asprintf(&newdev, "%s/dev", rootdir) == -1) errExit("asprintf"); if (arg_debug) printf("Mounting /dev on %s\n", newdev); if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev"); // some older distros don't have a /run directory // create one by default // no exit on error, let the user deal with any problems char *rundir; if (asprintf(&rundir, "%s/run", rootdir) == -1) errExit("asprintf"); if (!is_dir(rundir)) { int rv = mkdir(rundir, S_IRWXU | S_IRWXG | S_IRWXO); (void) rv; rv = chown(rundir, 0, 0); (void) rv; } // copy /etc/resolv.conf in chroot directory // if resolv.conf in chroot is a symbolic link, this will fail // no exit on error, let the user deal with the problem char *fname; if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1) errExit("asprintf"); if (arg_debug) printf("Updating /etc/resolv.conf in %s\n", fname); if (copy_file("/etc/resolv.conf", fname) == -1) fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n"); // chroot into the new directory if (arg_debug) printf("Chrooting into %s\n", rootdir); if (chroot(rootdir) < 0) errExit("chroot"); // mount a new tmpfs in /run/firejail/mnt - the old one was lost in chroot fs_build_remount_mnt_dir(); // update /var directory in order to support multiple sandboxes running on the same root directory if (!arg_private_dev) fs_dev_shm(); fs_var_lock(); fs_var_tmp(); fs_var_log(); fs_var_lib(); fs_var_cache(); fs_var_utmp(); // don't leak user information restrict_users(); disable_firejail_config(); }
int ve_enter(const id_t veid) { int vzfd; int errcode; int retry = 0; struct vzctl_env_create env_create; char *root; if ((vzfd = open(VZCTLDEV, O_RDWR)) < 0) { log_e("open(%s): %m", VZCTLDEV); return 0; } if (asprintf(&root, "%sroot/%u/", VZ_DIR, veid) == -1) { log_e("malloc: %m"); close(vzfd); return 0; } if (chroot(root) < 0) { log_e("chroot(%s): %m", root); free(root); close(vzfd); return 0; } free(root); if (chdir("/") < 0) { log_e("chdir(/): %m"); close(vzfd); return 0; } /* associate the process with its beancounter */ if (setluid(veid) < 0) { log_e("setluid(%d): %m", veid); close(vzfd); return 0; } memset(&env_create, 0, sizeof(env_create)); env_create.veid = veid; env_create.flags = VE_ENTER; /* enter the VE */ do { if (retry) sleep(1); errcode = ioctl(vzfd, VZCTL_ENV_CREATE, &env_create); } while (errcode < 0 && errno == EBUSY && retry++ < 3); close(vzfd); if (errcode < 0) { log_e("ioctl(VZCTL_ENV_CREATE): %m"); return 0; } return 1; }
int main(int argc, char *argv[]) { struct tftphdr *tp; int peer; socklen_t peerlen, len; ssize_t n; int ch; char *chroot_dir = NULL; struct passwd *nobody; const char *chuser = "******"; char recvbuffer[MAXPKTSIZE]; int allow_ro = 1, allow_wo = 1; tzset(); /* syslog in localtime */ acting_as_client = 0; tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); while ((ch = getopt(argc, argv, "cCd:F:lnoOp:s:u:U:wW")) != -1) { switch (ch) { case 'c': ipchroot = 1; break; case 'C': ipchroot = 2; break; case 'd': if (atoi(optarg) != 0) debug += atoi(optarg); else debug |= debug_finds(optarg); break; case 'F': newfile_format = optarg; break; case 'l': logging = 1; break; case 'n': suppress_naks = 1; break; case 'o': options_rfc_enabled = 0; break; case 'O': options_extra_enabled = 0; break; case 'p': packetdroppercentage = atoi(optarg); tftp_log(LOG_INFO, "Randomly dropping %d out of 100 packets", packetdroppercentage); break; case 's': chroot_dir = optarg; break; case 'u': chuser = optarg; break; case 'U': mask = strtol(optarg, NULL, 0); break; case 'w': create_new = 1; break; case 'W': create_new = 1; increase_name = 1; break; default: tftp_log(LOG_WARNING, "ignoring unknown option -%c", ch); } } if (optind < argc) { struct dirlist *dirp; /* Get list of directory prefixes. Skip relative pathnames. */ for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; optind++) { if (argv[optind][0] == '/') { dirp->name = argv[optind]; dirp->len = strlen(dirp->name); dirp++; } } } else if (chroot_dir) { dirs->name = "/"; dirs->len = 1; } if (ipchroot > 0 && chroot_dir == NULL) { tftp_log(LOG_ERR, "-c requires -s"); exit(1); } umask(mask); { int on = 1; if (ioctl(0, FIONBIO, &on) < 0) { tftp_log(LOG_ERR, "ioctl(FIONBIO): %s", strerror(errno)); exit(1); } } /* Find out who we are talking to and what we are going to do */ peerlen = sizeof(peer_sock); n = recvfrom(0, recvbuffer, MAXPKTSIZE, 0, (struct sockaddr *)&peer_sock, &peerlen); if (n < 0) { tftp_log(LOG_ERR, "recvfrom: %s", strerror(errno)); exit(1); } getnameinfo((struct sockaddr *)&peer_sock, peer_sock.ss_len, peername, sizeof(peername), NULL, 0, NI_NUMERICHOST); /* * Now that we have read the message out of the UDP * socket, we fork and exit. Thus, inetd will go back * to listening to the tftp port, and the next request * to come in will start up a new instance of tftpd. * * We do this so that inetd can run tftpd in "wait" mode. * The problem with tftpd running in "nowait" mode is that * inetd may get one or more successful "selects" on the * tftp port before we do our receive, so more than one * instance of tftpd may be started up. Worse, if tftpd * break before doing the above "recvfrom", inetd would * spawn endless instances, clogging the system. */ { int i, pid; for (i = 1; i < 20; i++) { pid = fork(); if (pid < 0) { sleep(i); /* * flush out to most recently sent request. * * This may drop some request, but those * will be resent by the clients when * they timeout. The positive effect of * this flush is to (try to) prevent more * than one tftpd being started up to service * a single request from a single client. */ peerlen = sizeof peer_sock; i = recvfrom(0, recvbuffer, MAXPKTSIZE, 0, (struct sockaddr *)&peer_sock, &peerlen); if (i > 0) { n = i; } } else { break; } } if (pid < 0) { tftp_log(LOG_ERR, "fork: %s", strerror(errno)); exit(1); } else if (pid != 0) { exit(0); } } /* * See if the client is allowed to talk to me. * (This needs to be done before the chroot()) */ { struct request_info req; request_init(&req, RQ_CLIENT_ADDR, peername, 0); request_set(&req, RQ_DAEMON, "tftpd", 0); if (hosts_access(&req) == 0) { if (debug&DEBUG_ACCESS) tftp_log(LOG_WARNING, "Access denied by 'tftpd' entry " "in /etc/hosts.allow"); /* * Full access might be disabled, but maybe the * client is allowed to do read-only access. */ request_set(&req, RQ_DAEMON, "tftpd-ro", 0); allow_ro = hosts_access(&req); request_set(&req, RQ_DAEMON, "tftpd-wo", 0); allow_wo = hosts_access(&req); if (allow_ro == 0 && allow_wo == 0) { tftp_log(LOG_WARNING, "Unauthorized access from %s", peername); exit(1); } if (debug&DEBUG_ACCESS) { if (allow_ro) tftp_log(LOG_WARNING, "But allowed readonly access " "via 'tftpd-ro' entry"); if (allow_wo) tftp_log(LOG_WARNING, "But allowed writeonly access " "via 'tftpd-wo' entry"); } } else if (debug&DEBUG_ACCESS) tftp_log(LOG_WARNING, "Full access allowed" "in /etc/hosts.allow"); } /* * Since we exit here, we should do that only after the above * recvfrom to keep inetd from constantly forking should there * be a problem. See the above comment about system clogging. */ if (chroot_dir) { if (ipchroot > 0) { char *tempchroot; struct stat sb; int statret; struct sockaddr_storage ss; char hbuf[NI_MAXHOST]; statret = -1; memcpy(&ss, &peer_sock, peer_sock.ss_len); unmappedaddr((struct sockaddr_in6 *)&ss); getnameinfo((struct sockaddr *)&ss, ss.ss_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf); if (ipchroot == 2) statret = stat(tempchroot, &sb); if (ipchroot == 1 || (statret == 0 && (sb.st_mode & S_IFDIR))) chroot_dir = tempchroot; } /* Must get this before chroot because /etc might go away */ if ((nobody = getpwnam(chuser)) == NULL) { tftp_log(LOG_ERR, "%s: no such user", chuser); exit(1); } if (chroot(chroot_dir)) { tftp_log(LOG_ERR, "chroot: %s: %s", chroot_dir, strerror(errno)); exit(1); } chdir("/"); setgroups(1, &nobody->pw_gid); if (setuid(nobody->pw_uid) != 0) { tftp_log(LOG_ERR, "setuid failed"); exit(1); } } len = sizeof(me_sock); if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) { switch (me_sock.ss_family) { case AF_INET: ((struct sockaddr_in *)&me_sock)->sin_port = 0; break; case AF_INET6: ((struct sockaddr_in6 *)&me_sock)->sin6_port = 0; break; default: /* unsupported */ break; } } else { memset(&me_sock, 0, sizeof(me_sock)); me_sock.ss_family = peer_sock.ss_family; me_sock.ss_len = peer_sock.ss_len; } close(0); close(1); peer = socket(peer_sock.ss_family, SOCK_DGRAM, 0); if (peer < 0) { tftp_log(LOG_ERR, "socket: %s", strerror(errno)); exit(1); } if (bind(peer, (struct sockaddr *)&me_sock, me_sock.ss_len) < 0) { tftp_log(LOG_ERR, "bind: %s", strerror(errno)); exit(1); } tp = (struct tftphdr *)recvbuffer; tp->th_opcode = ntohs(tp->th_opcode); if (tp->th_opcode == RRQ) { if (allow_ro) tftp_rrq(peer, tp->th_stuff, n - 1); else { tftp_log(LOG_WARNING, "%s read access denied", peername); exit(1); } } if (tp->th_opcode == WRQ) { if (allow_wo) tftp_wrq(peer, tp->th_stuff, n - 1); else { tftp_log(LOG_WARNING, "%s write access denied", peername); exit(1); } } exit(1); }
(normally it shouldn't be bigger than 3) */ /* daemon init, return 0 on success, -1 on error */ int daemonize(char* name) { FILE *pid_stream; pid_t pid; int r, p; p=-1; /* flush std file descriptors to avoid flushes after fork * (same message appearing multiple times) * and switch to unbuffered */ setbuf(stdout, 0); setbuf(stderr, 0); if (chroot_dir&&(chroot(chroot_dir)<0)){ LOG(L_CRIT, "Cannot chroot to %s: %s\n", chroot_dir, strerror(errno)); goto error; } if (chdir(working_dir)<0){ LOG(L_CRIT,"cannot chdir to %s: %s\n", working_dir, strerror(errno)); goto error; } /* fork to become!= group leader*/ if ((pid=fork())<0){ LOG(L_CRIT, "Cannot fork:%s\n", strerror(errno)); goto error; }else if (pid!=0){ /* parent process => exit*/ exit(0); } /* become session leader to drop the ctrl. terminal */ if (setsid()<0){ LOG(L_WARN, "setsid failed: %s\n",strerror(errno)); }else{ own_pgid=1;/* we have our own process group */ } /* fork again to drop group leadership */ if ((pid=fork())<0){ LOG(L_CRIT, "Cannot fork:%s\n", strerror(errno)); goto error; }else if (pid!=0){ /*parent process => exit */ exit(0); } /* added by noh: create a pid file for the main process */ if (pid_file!=0){ if ((pid_stream=fopen(pid_file, "r"))!=NULL){ fscanf(pid_stream, "%d", &p); fclose(pid_stream); if (p==-1){ LOG(L_CRIT, "pid file %s exists, but doesn't contain a valid" " pid number\n", pid_file); goto error; } if (kill((pid_t)p, 0)==0 || errno==EPERM){ LOG(L_CRIT, "running process found in the pid file %s\n", pid_file); goto error; }else{ LOG(L_WARN, "pid file contains old pid, replacing pid\n"); } } pid=getpid(); if ((pid_stream=fopen(pid_file, "w"))==NULL){ LOG(L_WARN, "unable to create pid file %s: %s\n", pid_file, strerror(errno)); goto error; }else{ fprintf(pid_stream, "%i\n", (int)pid); fclose(pid_stream); } } if (pgid_file!=0){ if ((pid_stream=fopen(pgid_file, "r"))!=NULL){ fscanf(pid_stream, "%d", &p); fclose(pid_stream); if (p==-1){ LOG(L_CRIT, "pgid file %s exists, but doesn't contain a valid" " pgid number\n", pgid_file); goto error; } } if (own_pgid){ pid=getpgid(0); if ((pid_stream=fopen(pgid_file, "w"))==NULL){ LOG(L_WARN, "unable to create pgid file %s: %s\n", pgid_file, strerror(errno)); goto error; }else{ fprintf(pid_stream, "%i\n", (int)pid); fclose(pid_stream); } }else{ LOG(L_WARN, "we don't have our own process so we won't save" " our pgid\n"); unlink(pgid_file); /* just to be sure nobody will miss-use the old value*/ } } /* try to replace stdin, stdout & stderr with /dev/null */ if (freopen("/dev/null", "r", stdin)==0){ LOG(L_ERR, "unable to replace stdin with /dev/null: %s\n", strerror(errno)); /* continue, leave it open */ }; if (freopen("/dev/null", "w", stdout)==0){ LOG(L_ERR, "unable to replace stdout with /dev/null: %s\n", strerror(errno)); /* continue, leave it open */ }; /* close stderr only if log_stderr=0 */ if ((!log_stderr) &&(freopen("/dev/null", "w", stderr)==0)){ LOG(L_ERR, "unable to replace stderr with /dev/null: %s\n", strerror(errno)); /* continue, leave it open */ }; /* close any open file descriptors */ closelog(); for (r=3;r<MAX_FD; r++){ close(r); } if (log_stderr==0) openlog(name, LOG_PID|LOG_CONS, log_facility); /* LOG_CONS, LOG_PERRROR ? */ return 0; error: return -1; }
/*++ * 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); }