static int do_check(char *dir_in, char *file_in, char *tdir_out, char *tfile_out) { char *path, *slash; char dir_out[4096], file_out[4096]; struct tm *timeptr; time_t now; int error = 0; /* * Expand any strftime(3) escapes * XXX - want to pass timeptr to expand_iolog_path */ time(&now); timeptr = localtime(&now); strftime(dir_out, sizeof(dir_out), tdir_out, timeptr); strftime(file_out, sizeof(file_out), tfile_out, timeptr); path = expand_iolog_path(NULL, dir_in, file_in, &slash); *slash = '\0'; if (strcmp(path, dir_out) != 0) { warningx("%s: expected %s, got %s", dir_in, dir_out, path); error = 1; } if (strcmp(slash + 1, file_out) != 0) { warningx("%s: expected %s, got %s", file_in, file_out, slash + 1); error = 1; } return error; }
int fwtk_init(struct passwd *pw, sudo_auth *auth) { static Cfg *confp; /* Configuration entry struct */ char resp[128]; /* Response from the server */ if ((confp = cfg_read("sudo")) == (Cfg *)-1) { warningx(_("unable to read fwtk config")); return AUTH_FATAL; } if (auth_open(confp)) { warningx(_("unable to connect to authentication server")); return AUTH_FATAL; } /* Get welcome message from auth server */ if (auth_recv(resp, sizeof(resp))) { warningx(_("lost connection to authentication server")); return AUTH_FATAL; } if (strncmp(resp, "Authsrv ready", 13) != 0) { warningx(_("authentication server error:\n%s"), resp); return AUTH_FATAL; } return AUTH_SUCCESS; }
/* * This function attempts to revert the relabeling done to the tty. * fd - referencing the opened ttyn * ttyn - name of tty to restore * * Returns zero on success, non-zero otherwise */ int selinux_restore_tty(void) { int retval = 0; security_context_t chk_tty_context = NULL; if (se_state.ttyfd == -1 || se_state.new_tty_context == NULL) goto skip_relabel; /* Verify that the tty still has the context set by sudo. */ if ((retval = fgetfilecon(se_state.ttyfd, &chk_tty_context)) < 0) { warning("unable to fgetfilecon %s", se_state.ttyn); goto skip_relabel; } if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) { warningx("%s changed labels.", se_state.ttyn); goto skip_relabel; } if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0) warning("unable to restore context for %s", se_state.ttyn); skip_relabel: if (se_state.ttyfd != -1) { close(se_state.ttyfd); se_state.ttyfd = -1; } if (chk_tty_context != NULL) { freecon(chk_tty_context); chk_tty_context = NULL; } return retval; }
/* * Set the exec and tty contexts in preparation for fork/exec. * Must run as root, before the uid change. * If ptyfd is not -1, it indicates we are running * in a pty and do not need to reset std{in,out,err}. * Returns 0 on success and -1 on failure. */ int selinux_setup(const char *role, const char *type, const char *ttyn, int ptyfd) { int rval = -1; /* Store the caller's SID in old_context. */ if (getprevcon(&se_state.old_context)) { warning("failed to get old_context"); goto done; } se_state.enforcing = security_getenforce(); if (se_state.enforcing < 0) { warning("unable to determine enforcing mode."); goto done; } #ifdef DEBUG warningx("your old context was %s", se_state.old_context); #endif se_state.new_context = get_exec_context(se_state.old_context, role, type); if (!se_state.new_context) goto done; if (relabel_tty(ttyn, ptyfd) < 0) { warning("unable to setup tty context for %s", se_state.new_context); goto done; } #ifdef DEBUG if (se_state.ttyfd != -1) { warningx("your old tty context is %s", se_state.tty_context); warningx("your new tty context is %s", se_state.new_tty_context); } #endif #ifdef HAVE_LINUX_AUDIT linux_audit_role_change(se_state.old_context, se_state.new_context, se_state.ttyn); #endif rval = 0; done: return rval; }
void log_error(int flags, const char *fmt, ...) { int serrno = errno; char *message; char *logline; va_list ap; /* Expand printf-style format + args. */ va_start(ap, fmt); evasprintf(&message, fmt, ap); va_end(ap); /* Become root if we are not already to avoid user interference */ set_perms(PERM_ROOT|PERM_NOEXIT); if (ISSET(flags, MSG_ONLY)) logline = message; else logline = new_logline(message, ISSET(flags, USE_ERRNO) ? serrno : 0); /* * Tell the user. */ if (!ISSET(flags, NO_STDERR)) { if (ISSET(flags, USE_ERRNO)) warning("%s", message); else warningx("%s", message); } if (logline != message) efree(message); /* * Send a copy of the error via mail. */ if (!ISSET(flags, NO_MAIL)) send_mail("%s", logline); /* * Log to syslog and/or a file. */ if (def_syslog) do_syslog(def_syslog_badpri, logline); if (def_logfile) do_logfile(logline); efree(logline); restore_perms(); if (!ISSET(flags, NO_EXIT)) { plugin_cleanup(0); siglongjmp(error_jmp, 1); } }
int securid_setup(struct passwd *pw, char **promptp, sudo_auth *auth) { struct SD_CLIENT *sd = (struct SD_CLIENT *) auth->data; /* Re-initialize SecurID every time. */ if (sd_init(sd) == 0) { /* The programmer's guide says username is 32 bytes */ strlcpy(sd->username, pw->pw_name, 32); return AUTH_SUCCESS; } else { warningx(_("unable to contact the SecurID server")); return AUTH_FATAL; } }
static void sudoers_policy_close(int exit_status, int error_code) { if (sigsetjmp(error_jmp, 1)) { /* called via error(), errorx() or log_error() */ return; } /* We do not currently log the exit status. */ if (error_code) warningx(_("unable to execute %s: %s"), safe_cmnd, strerror(error_code)); /* Close the session we opened in sudoers_policy_init_session(). */ if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT)) (void)sudo_auth_end_session(runas_pw); /* Free remaining references to password and group entries. */ pw_delref(sudo_user.pw); pw_delref(runas_pw); if (runas_gr != NULL) gr_delref(runas_gr); if (user_group_list != NULL) grlist_delref(user_group_list); }
/* * Extract a regular file. */ static void extract_file(struct archive *a, struct archive_entry *e, char **path) { int mode; time_t mtime; struct stat sb; struct timeval tv[2]; int cr, fd, text, warn, check; ssize_t len; unsigned char *p, *q, *end; mode = archive_entry_mode(e) & 0777; if (mode == 0) mode = 0644; mtime = archive_entry_mtime(e); /* look for existing file of same name */ recheck: if (lstat(*path, &sb) == 0) { if (u_opt || f_opt) { /* check if up-to-date */ if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime) return; (void)unlink(*path); } else if (o_opt) { /* overwrite */ (void)unlink(*path); } else if (n_opt) { /* do not overwrite */ return; } else { check = handle_existing_file(path); if (check == 0) goto recheck; if (check == -1) return; /* do not overwrite */ } } else { if (f_opt) return; } if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) error("open('%s')", *path); /* loop over file contents and write to disk */ info(" extracting: %s", *path); text = a_opt; warn = 0; cr = 0; for (int n = 0; ; n++) { if (tty && (n % 4) == 0) info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); len = archive_read_data(a, buffer, sizeof buffer); if (len < 0) ac(len); /* left over CR from previous buffer */ if (a_opt && cr) { if (len == 0 || buffer[0] != '\n') if (write(fd, "\r", 1) != 1) error("write('%s')", *path); cr = 0; } /* EOF */ if (len == 0) break; end = buffer + len; /* * Detect whether this is a text file. The correct way to * do this is to check the least significant bit of the * "internal file attributes" field of the corresponding * file header in the central directory, but libarchive * does not read the central directory, so we have to * guess by looking for non-ASCII characters in the * buffer. Hopefully we won't guess wrong. If we do * guess wrong, we print a warning message later. */ if (a_opt && n == 0) { for (p = buffer; p < end; ++p) { if (!isascii((unsigned char)*p)) { text = 0; break; } } } /* simple case */ if (!a_opt || !text) { if (write(fd, buffer, len) != len) error("write('%s')", *path); continue; } /* hard case: convert \r\n to \n (sigh...) */ for (p = buffer; p < end; p = q + 1) { for (q = p; q < end; q++) { if (!warn && !isascii(*q)) { warningx("%s may be corrupted due" " to weak text file detection" " heuristic", *path); warn = 1; } if (q[0] != '\r') continue; if (&q[1] == end) { cr = 1; break; } if (q[1] == '\n') break; } if (write(fd, p, q - p) != q - p) error("write('%s')", *path); } } if (tty) info(" \b\b"); if (text) info(" (text)"); info("\n"); /* set access and modification time */ tv[0].tv_sec = now; tv[0].tv_usec = 0; tv[1].tv_sec = mtime; tv[1].tv_usec = 0; if (futimes(fd, tv) != 0) error("utimes('%s')", *path); if (close(fd) != 0) error("close('%s')", *path); }
int fwtk_verify(struct passwd *pw, char *prompt, sudo_auth *auth) { char *pass; /* Password from the user */ char buf[SUDO_PASS_MAX + 12]; /* General prupose buffer */ char resp[128]; /* Response from the server */ int error; /* Send username to authentication server. */ (void) snprintf(buf, sizeof(buf), "authorize %s 'sudo'", pw->pw_name); restart: if (auth_send(buf) || auth_recv(resp, sizeof(resp))) { warningx(_("lost connection to authentication server")); return AUTH_FATAL; } /* Get the password/response from the user. */ if (strncmp(resp, "challenge ", 10) == 0) { (void) snprintf(buf, sizeof(buf), "%s\nResponse: ", &resp[10]); pass = auth_getpass(buf, def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_OFF); if (pass && *pass == '\0') { pass = auth_getpass("Response [echo on]: ", def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_ON); } } else if (strncmp(resp, "chalnecho ", 10) == 0) { pass = auth_getpass(&resp[10], def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_OFF); } else if (strncmp(resp, "password", 8) == 0) { pass = auth_getpass(prompt, def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_OFF); } else if (strncmp(resp, "display ", 8) == 0) { fprintf(stderr, "%s\n", &resp[8]); strlcpy(buf, "response dummy", sizeof(buf)); goto restart; } else { warningx("%s", resp); return AUTH_FATAL; } if (!pass) { /* ^C or error */ return AUTH_INTR; } /* Send the user's response to the server */ (void) snprintf(buf, sizeof(buf), "response '%s'", pass); if (auth_send(buf) || auth_recv(resp, sizeof(resp))) { warningx(_("lost connection to authentication server")); error = AUTH_FATAL; goto done; } if (strncmp(resp, "ok", 2) == 0) { error = AUTH_SUCCESS; goto done; } /* Main loop prints "Permission Denied" or insult. */ if (strcmp(resp, "Permission Denied.") != 0) warningx("%s", resp); error = AUTH_FAILURE; done: zero_bytes(pass, strlen(pass)); zero_bytes(buf, strlen(buf)); return error; }
/* * Allocate and fill in the interfaces global variable with the * machine's ip addresses and netmasks. */ int get_net_ifs(char **addrinfo) { struct ifconf *ifconf; struct ifreq *ifr, ifr_tmp; struct sockaddr_in *sin; int ailen, i, len, n, sock, num_interfaces = 0; size_t buflen = sizeof(struct ifconf) + BUFSIZ; char *cp, *previfname = "", *ifconf_buf = NULL; #ifdef _ISC struct strioctl strioctl; #endif /* _ISC */ debug_decl(get_net_ifs, SUDO_DEBUG_NETIF) sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) error(1, _("unable to open socket")); /* * Get interface configuration or return. */ for (;;) { ifconf_buf = emalloc(buflen); ifconf = (struct ifconf *) ifconf_buf; ifconf->ifc_len = buflen - sizeof(struct ifconf); ifconf->ifc_buf = (caddr_t) (ifconf_buf + sizeof(struct ifconf)); #ifdef _ISC STRSET(SIOCGIFCONF, (caddr_t) ifconf, buflen); if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0) #else /* Note that some kernels return EINVAL if the buffer is too small */ if (ioctl(sock, SIOCGIFCONF, (caddr_t) ifconf) < 0 && errno != EINVAL) #endif /* _ISC */ goto done; /* Break out of loop if we have a big enough buffer. */ if (ifconf->ifc_len + sizeof(struct ifreq) < buflen) break; buflen += BUFSIZ; efree(ifconf_buf); } /* Allocate space for the maximum number of interfaces that could exist. */ if ((n = ifconf->ifc_len / sizeof(struct ifreq)) == 0) debug_return_int(0); ailen = n * 2 * INET6_ADDRSTRLEN; *addrinfo = cp = emalloc(ailen); /* For each interface, store the ip address and netmask. */ for (i = 0; i < ifconf->ifc_len; ) { /* Get a pointer to the current interface. */ ifr = (struct ifreq *) &ifconf->ifc_buf[i]; /* Set i to the subscript of the next interface. */ i += sizeof(struct ifreq); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr)) i += ifr->ifr_addr.sa_len - sizeof(struct sockaddr); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ /* Skip duplicates and interfaces with NULL addresses. */ sin = (struct sockaddr_in *) &ifr->ifr_addr; if (sin->sin_addr.s_addr == 0 || strncmp(previfname, ifr->ifr_name, sizeof(ifr->ifr_name) - 1) == 0) continue; if (ifr->ifr_addr.sa_family != AF_INET) continue; #ifdef SIOCGIFFLAGS memset(&ifr_tmp, 0, sizeof(ifr_tmp)); strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1); if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr_tmp) < 0) #endif ifr_tmp = *ifr; /* Skip interfaces marked "down" and "loopback". */ if (!ISSET(ifr_tmp.ifr_flags, IFF_UP) || ISSET(ifr_tmp.ifr_flags, IFF_LOOPBACK)) continue; sin = (struct sockaddr_in *) &ifr->ifr_addr; len = snprintf(cp, ailen - (*addrinfo - cp), "%s%s/", cp == *addrinfo ? "" : " ", inet_ntoa(sin->sin_addr)); if (len <= 0 || len >= ailen - (*addrinfo - cp)) { warningx(_("load_interfaces: overflow detected")); goto done; } cp += len; /* Stash the name of the interface we saved. */ previfname = ifr->ifr_name; /* Get the netmask. */ memset(&ifr_tmp, 0, sizeof(ifr_tmp)); strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1); #ifdef _ISC STRSET(SIOCGIFNETMASK, (caddr_t) &ifr_tmp, sizeof(ifr_tmp)); if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0) { #else if (ioctl(sock, SIOCGIFNETMASK, (caddr_t) &ifr_tmp) < 0) { #endif /* _ISC */ sin = (struct sockaddr_in *) &ifr_tmp.ifr_addr; sin->sin_addr.s_addr = htonl(IN_CLASSC_NET); } sin = (struct sockaddr_in *) &ifr_tmp.ifr_addr; len = snprintf(cp, ailen - (*addrinfo - cp), "%s", inet_ntoa(sin->sin_addr)); if (len <= 0 || len >= ailen - (*addrinfo - cp)) { warningx(_("load_interfaces: overflow detected")); goto done; } cp += len; num_interfaces++; } done: efree(ifconf_buf); (void) close(sock); debug_return_int(num_interfaces); } #else /* !SIOCGIFCONF || STUB_LOAD_INTERFACES */ /* * Stub function for those without SIOCGIFCONF or getifaddrs() */ int get_net_ifs(char **addrinfo) { debug_decl(get_net_ifs, SUDO_DEBUG_NETIF) debug_return_int(0); }
/* * Command line argument parsing. * Sets nargc and nargv which corresponds to the argc/argv we'll use * for the command to be run (if we are running one). */ int parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp, char ***env_addp) { int mode = 0; /* what mode is sudo to be run in? */ int flags = 0; /* mode flags */ int valid_flags, ch; int i, j; char *cp, **env_add, **settings; int nenv = 0; int env_size = 32; env_add = emalloc2(env_size, sizeof(char *)); /* Pass progname to plugin so it can call setprogname() */ sudo_settings[ARG_PROGNAME].value = getprogname(); /* First, check to see if we were invoked as "sudoedit". */ if (strcmp(getprogname(), "sudoedit") == 0) { mode = MODE_EDIT; sudo_settings[ARG_SUDOEDIT].value = "true"; } /* Load local IP addresses and masks. */ if (get_net_ifs(&cp) > 0) sudo_settings[ARG_NET_ADDRS].value = cp; /* Returns true if the last option string was "--" */ #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \ argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0') /* Returns true if next option is an environment variable */ #define is_envar (optind < argc && argv[optind][0] != '/' && \ strchr(argv[optind], '=') != NULL) /* Flags allowed when running a command */ valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME| MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL; /* XXX - should fill in settings at the end to avoid dupes */ for (;;) { /* * We disable arg permutation for GNU getopt(). * Some trickiness is required to allow environment variables * to be interspersed with command line options. */ if ((ch = getopt(argc, argv, "+Aa:bC:c:D:Eeg:HhiKklnPp:r:Sst:U:u:Vv")) != -1) { switch (ch) { case 'A': SET(tgetpass_flags, TGP_ASKPASS); break; #ifdef HAVE_BSD_AUTH_H case 'a': sudo_settings[ARG_BSDAUTH_TYPE].value = optarg; break; #endif case 'b': SET(flags, MODE_BACKGROUND); break; case 'C': if (atoi(optarg) < 3) { warningx(_("the argument to -C must be a number greater than or equal to 3")); usage(1); } sudo_settings[ARG_CLOSEFROM].value = optarg; break; #ifdef HAVE_LOGIN_CAP_H case 'c': sudo_settings[ARG_LOGIN_CLASS].value = optarg; break; #endif case 'D': if ((debug_level = atoi(optarg)) < 1 || debug_level > 9) { warningx(_("the argument to -D must be between 1 and 9 inclusive")); usage(1); } sudo_settings[ARG_DEBUG_LEVEL].value = optarg; break; case 'E': sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true"; break; case 'e': if (mode && mode != MODE_EDIT) usage_excl(1); mode = MODE_EDIT; sudo_settings[ARG_SUDOEDIT].value = "true"; valid_flags = MODE_NONINTERACTIVE; break; case 'g': runas_group = optarg; sudo_settings[ARG_RUNAS_GROUP].value = optarg; break; case 'H': sudo_settings[ARG_SET_HOME].value = "true"; break; case 'h': if (mode && mode != MODE_HELP) { if (strcmp(getprogname(), "sudoedit") != 0) usage_excl(1); } mode = MODE_HELP; valid_flags = 0; break; case 'i': sudo_settings[ARG_LOGIN_SHELL].value = "true"; SET(flags, MODE_LOGIN_SHELL); break; case 'k': sudo_settings[ARG_IGNORE_TICKET].value = "true"; break; case 'K': sudo_settings[ARG_IGNORE_TICKET].value = "true"; if (mode && mode != MODE_KILL) usage_excl(1); mode = MODE_KILL; valid_flags = 0; break; case 'l': if (mode) { if (mode == MODE_LIST) SET(flags, MODE_LONG_LIST); else usage_excl(1); } mode = MODE_LIST; valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST; break; case 'n': SET(flags, MODE_NONINTERACTIVE); sudo_settings[ARG_NONINTERACTIVE].value = "true"; break; case 'P': sudo_settings[ARG_PRESERVE_GROUPS].value = "true"; break; case 'p': sudo_settings[ARG_PROMPT].value = optarg; break; #ifdef HAVE_SELINUX case 'r': sudo_settings[ARG_SELINUX_ROLE].value = optarg; break; case 't': sudo_settings[ARG_SELINUX_TYPE].value = optarg; break; #endif case 'S': SET(tgetpass_flags, TGP_STDIN); break; case 's': sudo_settings[ARG_USER_SHELL].value = "true"; SET(flags, MODE_SHELL); break; case 'U': if ((getpwnam(optarg)) == NULL) errorx(1, _("unknown user: %s"), optarg); list_user = optarg; break; case 'u': runas_user = optarg; sudo_settings[ARG_RUNAS_USER].value = optarg; break; case 'v': if (mode && mode != MODE_VALIDATE) usage_excl(1); mode = MODE_VALIDATE; valid_flags = MODE_NONINTERACTIVE; break; case 'V': if (mode && mode != MODE_VERSION) usage_excl(1); mode = MODE_VERSION; valid_flags = 0; break; default: usage(1); } } else if (!got_end_of_args && is_envar) { if (nenv == env_size - 2) { env_size *= 2; env_add = erealloc3(env_add, env_size, sizeof(char *)); } env_add[nenv++] = argv[optind]; /* Crank optind and resume getopt. */ optind++; } else { /* Not an option or an environment variable -- we're done. */ break; } } env_add[nenv] = NULL; argc -= optind; argv += optind; if (!mode) { /* Defer -k mode setting until we know whether it is a flag or not */ if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) { if (argc == 0) { mode = MODE_INVALIDATE; /* -k by itself */ sudo_settings[ARG_IGNORE_TICKET].value = NULL; valid_flags = 0; } } if (!mode) mode = MODE_RUN; /* running a command */ } if (argc > 0 && mode == MODE_LIST) mode = MODE_CHECK; if (ISSET(flags, MODE_LOGIN_SHELL)) { if (ISSET(flags, MODE_SHELL)) { warningx(_("you may not specify both the `-i' and `-s' options")); usage(1); } if (ISSET(flags, MODE_PRESERVE_ENV)) { warningx(_("you may not specify both the `-i' and `-E' options")); usage(1); } SET(flags, MODE_SHELL); } if ((flags & valid_flags) != flags) usage(1); if (mode == MODE_EDIT && (ISSET(flags, MODE_PRESERVE_ENV) || env_add[0] != NULL)) { if (ISSET(mode, MODE_PRESERVE_ENV)) warningx(_("the `-E' option is not valid in edit mode")); if (env_add[0] != NULL) warningx(_("you may not specify environment variables in edit mode")); usage(1); } if ((runas_user != NULL || runas_group != NULL) && !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) { usage(1); } if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) { warningx(_("the `-U' option may only be used with the `-l' option")); usage(1); } if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) { warningx(_("the `-A' and `-S' options may not be used together")); usage(1); } if ((argc == 0 && mode == MODE_EDIT) || (argc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK))) usage(1); if (argc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) { SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL)); sudo_settings[ARG_IMPLIED_SHELL].value = "true"; } if (mode == MODE_HELP) help(); /* * For shell mode we need to rewrite argv */ if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { char **av; int ac; if (argc == 0) { /* just the shell */ ac = argc + 1; av = emalloc2(ac + 1, sizeof(char *)); memcpy(av + 1, argv, argc * sizeof(char *)); } else { /* shell -c "command" */ char *cmnd, *src, *dst; size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) + strlen(argv[argc - 1]) + 1; cmnd = dst = emalloc2(cmnd_size, 2); for (av = argv; *av != NULL; av++) { for (src = *av; *src != '\0'; src++) { /* quote potential meta characters */ if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-') *dst++ = '\\'; *dst++ = *src; } *dst++ = ' '; } if (cmnd != dst) dst--; /* replace last space with a NUL */ *dst = '\0'; ac = 3; av = emalloc2(ac + 1, sizeof(char *)); av[1] = "-c"; av[2] = cmnd; } av[0] = (char *)user_details.shell; /* plugin may override shell */ av[ac] = NULL; argv = av; argc = ac; } /* * Format setting_pairs into settings array. */ settings = emalloc2(NUM_SETTINGS + 1, sizeof(char *)); for (i = 0, j = 0; i < NUM_SETTINGS; i++) { if (sudo_settings[i].value) { sudo_debug(9, "settings: %s=%s", sudo_settings[i].name, sudo_settings[i].value); settings[j] = fmt_string(sudo_settings[i].name, sudo_settings[i].value); if (settings[j] == NULL) errorx(1, _("unable to allocate memory")); j++; } } settings[j] = NULL; if (mode == MODE_EDIT) { #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) /* Must have the command in argv[0]. */ argc++; argv--; argv[0] = "sudoedit"; #else errorx(1, _("sudoedit is not supported on this platform")); #endif } *settingsp = settings; *env_addp = env_add; *nargc = argc; *nargv = argv; return mode | flags; }
int sudo_rfc1938_setup(struct passwd *pw, char **promptp, sudo_auth *auth) { char challenge[256]; static char *orig_prompt = NULL, *new_prompt = NULL; static int op_len, np_size; static struct RFC1938 rfc1938; debug_decl(sudo_rfc1938_setup, SUDO_DEBUG_AUTH) /* Stash a pointer to the rfc1938 struct if we have not initialized */ if (!auth->data) auth->data = &rfc1938; /* Save the original prompt */ if (orig_prompt == NULL) { orig_prompt = *promptp; op_len = strlen(orig_prompt); /* Ignore trailing colon (we will add our own) */ if (orig_prompt[op_len - 1] == ':') op_len--; else if (op_len >= 2 && orig_prompt[op_len - 1] == ' ' && orig_prompt[op_len - 2] == ':') op_len -= 2; } #ifdef HAVE_SKEY /* Close old stream */ if (rfc1938.keyfile) (void) fclose(rfc1938.keyfile); #endif /* * Look up the user and get the rfc1938 challenge. * If the user is not in the OTP db, only post a fatal error if * we are running alone (since they may just use a normal passwd). */ if (rfc1938challenge(&rfc1938, pw->pw_name, challenge, sizeof(challenge))) { if (IS_ONEANDONLY(auth)) { warningx(_("you do not exist in the %s database"), auth->name); debug_return_int(AUTH_FATAL); } else { debug_return_int(AUTH_FAILURE); } } /* Get space for new prompt with embedded challenge */ if (np_size < op_len + strlen(challenge) + 7) { np_size = op_len + strlen(challenge) + 7; new_prompt = (char *) erealloc(new_prompt, np_size); } if (def_long_otp_prompt) (void) snprintf(new_prompt, np_size, "%s\n%s", challenge, orig_prompt); else (void) snprintf(new_prompt, np_size, "%.*s [ %s ]:", op_len, orig_prompt, challenge); *promptp = new_prompt; debug_return_int(AUTH_SUCCESS); }
/* * Returns a new security context based on the old context and the * specified role and type. */ security_context_t get_exec_context(security_context_t old_context, const char *role, const char *type) { security_context_t new_context = NULL; context_t context = NULL; char *typebuf = NULL; /* We must have a role, the type is optional (we can use the default). */ if (!role) { warningx("you must specify a role for type %s", type); errno = EINVAL; return NULL; } if (!type) { if (get_default_type(role, &typebuf)) { warningx("unable to get default type for role %s", role); errno = EINVAL; return NULL; } type = typebuf; } /* * Expand old_context into a context_t so that we extract and modify * its components easily. */ context = context_new(old_context); /* * Replace the role and type in "context" with the role and * type we will be running the command as. */ if (context_role_set(context, role)) { warning("failed to set new role %s", role); goto bad; } if (context_type_set(context, type)) { warning("failed to set new type %s", type); goto bad; } /* * Convert "context" back into a string and verify it. */ new_context = estrdup(context_str(context)); if (security_check_context(new_context) < 0) { warningx("%s is not a valid context", new_context); errno = EINVAL; goto bad; } #ifdef DEBUG warningx("Your new context is %s", new_context); #endif context_free(context); return new_context; bad: free(typebuf); context_free(context); freecon(new_context); return NULL; }
/* * Extract a zipfile entry: first perform some sanity checks to ensure * that it is either a directory or a regular file and that the path is * not absolute and does not try to break out of the current directory; * then call either extract_dir() or extract_file() as appropriate. * * This is complicated a bit by the various ways in which we need to * manipulate the path name. Case conversion (if requested by the -L * option) happens first, but the include / exclude patterns are applied * to the full converted path name, before the directory part of the path * is removed in accordance with the -j option. Sanity checks are * intentionally done earlier than they need to be, so the user will get a * warning about insecure paths even for files or directories which * wouldn't be extracted anyway. */ static void extract(struct archive *a, struct archive_entry *e) { char *pathname, *realpathname; mode_t filetype; char *p, *q; pathname = pathdup(archive_entry_pathname(e)); filetype = archive_entry_filetype(e); /* sanity checks */ if (pathname[0] == '/' || strncmp(pathname, "../", 3) == 0 || strstr(pathname, "/../") != NULL) { warningx("skipping insecure entry '%s'", pathname); ac(archive_read_data_skip(a)); free(pathname); return; } /* I don't think this can happen in a zipfile.. */ if (!S_ISDIR(filetype) && !S_ISREG(filetype)) { warningx("skipping non-regular entry '%s'", pathname); ac(archive_read_data_skip(a)); free(pathname); return; } /* skip directories in -j case */ if (S_ISDIR(filetype) && j_opt) { ac(archive_read_data_skip(a)); free(pathname); return; } /* apply include / exclude patterns */ if (!accept_pathname(pathname)) { ac(archive_read_data_skip(a)); free(pathname); return; } /* apply -j and -d */ if (j_opt) { for (p = q = pathname; *p; ++p) if (*p == '/') q = p + 1; realpathname = pathcat(d_arg, q); } else { realpathname = pathcat(d_arg, pathname); } /* ensure that parent directory exists */ make_parent(realpathname); if (S_ISDIR(filetype)) extract_dir(a, e, realpathname); else extract_file(a, e, &realpathname); free(realpathname); free(pathname); }
/* * Tell which options are mutually exclusive and exit. */ static void usage_excl(int fatal) { warningx(_("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified")); usage(fatal); }
static void extract_stdout(struct archive *a, struct archive_entry *e) { char *pathname; mode_t filetype; int cr, text, warn; ssize_t len; unsigned char *p, *q, *end; pathname = pathdup(archive_entry_pathname(e)); filetype = archive_entry_filetype(e); /* I don't think this can happen in a zipfile.. */ if (!S_ISDIR(filetype) && !S_ISREG(filetype)) { warningx("skipping non-regular entry '%s'", pathname); ac(archive_read_data_skip(a)); free(pathname); return; } /* skip directories in -j case */ if (S_ISDIR(filetype)) { ac(archive_read_data_skip(a)); free(pathname); return; } /* apply include / exclude patterns */ if (!accept_pathname(pathname)) { ac(archive_read_data_skip(a)); free(pathname); return; } if (c_opt) info("x %s\n", pathname); text = a_opt; warn = 0; cr = 0; for (int n = 0; ; n++) { len = archive_read_data(a, buffer, sizeof buffer); if (len < 0) ac(len); /* left over CR from previous buffer */ if (a_opt && cr) { if (len == 0 || buffer[0] != '\n') { if (fwrite("\r", 1, 1, stderr) != 1) error("write('%s')", pathname); } cr = 0; } /* EOF */ if (len == 0) break; end = buffer + len; /* * Detect whether this is a text file. The correct way to * do this is to check the least significant bit of the * "internal file attributes" field of the corresponding * file header in the central directory, but libarchive * does not read the central directory, so we have to * guess by looking for non-ASCII characters in the * buffer. Hopefully we won't guess wrong. If we do * guess wrong, we print a warning message later. */ if (a_opt && n == 0) { for (p = buffer; p < end; ++p) { if (!isascii((unsigned char)*p)) { text = 0; break; } } } /* simple case */ if (!a_opt || !text) { if (fwrite(buffer, 1, len, stdout) != (size_t)len) error("write('%s')", pathname); continue; } /* hard case: convert \r\n to \n (sigh...) */ for (p = buffer; p < end; p = q + 1) { for (q = p; q < end; q++) { if (!warn && !isascii(*q)) { warningx("%s may be corrupted due" " to weak text file detection" " heuristic", pathname); warn = 1; } if (q[0] != '\r') continue; if (&q[1] == end) { cr = 1; break; } if (q[1] == '\n') break; } if (fwrite(p, 1, q - p, stdout) != (size_t)(q - p)) error("write('%s')", pathname); } } free(pathname); }
void set_project(struct passwd *pw) { struct project proj; char buf[PROJECT_BUFSZ]; int errval; debug_decl(set_project, SUDO_DEBUG_UTIL) /* * Collect the default project for the user and settaskid */ setprojent(); if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) { errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL); switch(errval) { case 0: break; case SETPROJ_ERR_TASK: switch (errno) { case EAGAIN: warningx(U_("resource control limit has been reached")); break; case ESRCH: warningx(U_("user \"%s\" is not a member of project \"%s\""), pw->pw_name, proj.pj_name); break; case EACCES: warningx(U_("the invoking task is final")); break; default: warningx(U_("could not join project \"%s\""), proj.pj_name); } case SETPROJ_ERR_POOL: switch (errno) { case EACCES: warningx(U_("no resource pool accepting default bindings " "exists for project \"%s\""), proj.pj_name); break; case ESRCH: warningx(U_("specified resource pool does not exist for " "project \"%s\""), proj.pj_name); break; default: warningx(U_("could not bind to default resource pool for " "project \"%s\""), proj.pj_name); } break; default: if (errval <= 0) { warningx(U_("setproject failed for project \"%s\""), proj.pj_name); } else { warningx(U_("warning, resource control assignment failed for " "project \"%s\""), proj.pj_name); } } } else { warning("getdefaultproj"); } endprojent(); debug_return; }
static int sudoers_policy_open(unsigned int version, sudo_conv_t conversation, sudo_printf_t plugin_printf, char * const settings[], char * const user_info[], char * const envp[]) { volatile int sources = 0; sigaction_t sa; struct sudo_nss *nss; if (!sudo_conv) sudo_conv = conversation; if (!sudo_printf) sudo_printf = plugin_printf; if (sigsetjmp(error_jmp, 1)) { /* called via error(), errorx() or log_error() */ rewind_perms(); return -1; } bindtextdomain("sudoers", LOCALEDIR); /* * Signal setup: * Ignore keyboard-generated signals so the user cannot interrupt * us at some point and avoid the logging. * Install handler to wait for children when they exit. */ zero_bytes(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; (void) sigaction(SIGINT, &sa, &saved_sa_int); (void) sigaction(SIGQUIT, &sa, &saved_sa_quit); (void) sigaction(SIGTSTP, &sa, &saved_sa_tstp); sudo_setpwent(); sudo_setgrent(); /* Initialize environment functions (including replacements). */ env_init(envp); /* Setup defaults data structures. */ init_defaults(); /* Parse settings and user_info */ sudo_mode = deserialize_info(settings, user_info); init_vars(envp); /* XXX - move this later? */ /* Parse nsswitch.conf for sudoers order. */ snl = sudo_read_nss(); /* LDAP or NSS may modify the euid so we need to be root for the open. */ set_perms(PERM_INITIAL); set_perms(PERM_ROOT); /* Open and parse sudoers, set global defaults */ tq_foreach_fwd(snl, nss) { if (nss->open(nss) == 0 && nss->parse(nss) == 0) { sources++; if (nss->setdefs(nss) != 0) log_error(NO_STDERR|NO_EXIT, _("problem with defaults entries")); } } if (sources == 0) { warningx(_("no valid sudoers sources found, quitting")); return -1; } /* XXX - collect post-sudoers parse settings into a function */ /* * Initialize external group plugin, if any. */ if (def_group_plugin) { if (group_plugin_load(def_group_plugin) != TRUE) def_group_plugin = NULL; } /* * Set runas passwd/group entries based on command line or sudoers. * Note that if runas_group was specified without runas_user we * defer setting runas_pw so the match routines know to ignore it. */ if (runas_group != NULL) { set_runasgr(runas_group); if (runas_user != NULL) set_runaspw(runas_user); } else set_runaspw(runas_user ? runas_user : def_runas_default); if (!update_defaults(SETDEF_RUNAS)) log_error(NO_STDERR|NO_EXIT, _("problem with defaults entries")); if (def_fqdn) set_fqdn(); /* deferred until after sudoers is parsed */ /* Set login class if applicable. */ set_loginclass(sudo_user.pw); restore_perms(); return TRUE; }
/* * Load the specified plugin and run its init function. * Returns -1 if unable to open the plugin, else it returns * the value from the plugin's init function. */ int group_plugin_load(char *plugin_info) { struct stat sb; char *args, path[PATH_MAX]; char **argv = NULL; int len, rc = -1; debug_decl(group_plugin_load, SUDO_DEBUG_UTIL) /* * Fill in .so path and split out args (if any). */ if ((args = strpbrk(plugin_info, " \t")) != NULL) { len = snprintf(path, sizeof(path), "%s%.*s", (*plugin_info != '/') ? _PATH_SUDO_PLUGIN_DIR : "", (int)(args - plugin_info), plugin_info); args++; } else { len = snprintf(path, sizeof(path), "%s%s", (*plugin_info != '/') ? _PATH_SUDO_PLUGIN_DIR : "", plugin_info); } if (len <= 0 || len >= sizeof(path)) { errno = ENAMETOOLONG; warning("%s%s", (*plugin_info != '/') ? _PATH_SUDO_PLUGIN_DIR : "", plugin_info); goto done; } /* Sanity check plugin path. */ if (stat(path, &sb) != 0) { warning("%s", path); goto done; } if (sb.st_uid != ROOT_UID) { warningx(_("%s must be owned by uid %d"), path, ROOT_UID); goto done; } if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { warningx(_("%s must only be writable by owner"), path); goto done; } /* Open plugin and map in symbol. */ group_handle = dlopen(path, RTLD_LAZY|RTLD_GLOBAL); if (!group_handle) { warningx(_("unable to dlopen %s: %s"), path, dlerror()); goto done; } group_plugin = dlsym(group_handle, "group_plugin"); if (group_plugin == NULL) { warningx(_("unable to find symbol \"group_plugin\" in %s"), path); goto done; } if (GROUP_API_VERSION_GET_MAJOR(group_plugin->version) != GROUP_API_VERSION_MAJOR) { warningx(_("%s: incompatible group plugin major version %d, expected %d"), path, GROUP_API_VERSION_GET_MAJOR(group_plugin->version), GROUP_API_VERSION_MAJOR); goto done; } /* * Split args into a vector if specified. */ if (args != NULL) { int ac = 0; bool wasblank = true; char *cp; for (cp = args; *cp != '\0'; cp++) { if (isblank((unsigned char)*cp)) { wasblank = true; } else if (wasblank) { wasblank = false; ac++; } } if (ac != 0) { argv = emalloc2(ac, sizeof(char *)); ac = 0; for ((cp = strtok(args, " \t")); cp; (cp = strtok(NULL, " \t"))) argv[ac++] = cp; } } rc = (group_plugin->init)(GROUP_API_VERSION, sudo_printf, argv); done: efree(argv); if (rc != true) { if (group_handle != NULL) { dlclose(group_handle); group_handle = NULL; group_plugin = NULL; } } debug_return_bool(rc); }
static int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], char **command_infop[], char **argv_out[], char **user_env_out[]) { static char *command_info[32]; /* XXX */ char **edit_argv = NULL; struct sudo_nss *nss; int cmnd_status = -1, validated; volatile int info_len = 0; volatile int rval = TRUE; if (sigsetjmp(error_jmp, 1)) { /* error recovery via error(), errorx() or log_error() */ rval = -1; goto done; } /* Is root even allowed to run sudo? */ if (user_uid == 0 && !def_root_sudo) { warningx(_("sudoers specifies that root is not allowed to sudo")); goto bad; } /* Check for -C overriding def_closefrom. */ if (user_closefrom >= 0 && user_closefrom != def_closefrom) { if (!def_closefrom_override) { warningx(_("you are not permitted to use the -C option")); goto bad; } def_closefrom = user_closefrom; } set_perms(PERM_INITIAL); /* Environment variables specified on the command line. */ if (env_add != NULL && env_add[0] != NULL) sudo_user.env_vars = env_add; /* * Make a local copy of argc/argv, with special handling * for pseudo-commands and the '-i' option. */ if (argc == 0) { NewArgc = 1; NewArgv = emalloc2(NewArgc + 1, sizeof(char *)); NewArgv[0] = user_cmnd; NewArgv[1] = NULL; } else { /* Must leave an extra slot before NewArgv for bash's --login */ NewArgc = argc; NewArgv = emalloc2(NewArgc + 2, sizeof(char *)); memcpy(++NewArgv, argv, argc * sizeof(char *)); NewArgv[NewArgc] = NULL; if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) NewArgv[0] = estrdup(runas_pw->pw_shell); } /* If given the -P option, set the "preserve_groups" flag. */ if (ISSET(sudo_mode, MODE_PRESERVE_GROUPS)) def_preserve_groups = TRUE; /* Find command in path */ cmnd_status = set_cmnd(); if (cmnd_status == -1) { rval = -1; goto done; } #ifdef HAVE_SETLOCALE if (!setlocale(LC_ALL, def_sudoers_locale)) { warningx(_("unable to set locale to \"%s\", using \"C\""), def_sudoers_locale); setlocale(LC_ALL, "C"); } #endif /* * Check sudoers sources. */ validated = FLAG_NO_USER | FLAG_NO_HOST; tq_foreach_fwd(snl, nss) { validated = nss->lookup(nss, validated, pwflag); if (ISSET(validated, VALIDATE_OK)) { /* Handle "= auth" in netsvc.conf */ if (nss->ret_if_found) break; } else { /* Handle [NOTFOUND=return] */ if (nss->ret_if_notfound) break; } }
/* * Fill in the interfaces string with the machine's ip addresses and netmasks * and return the number of interfaces found. */ int get_net_ifs(char **addrinfo) { struct ifaddrs *ifa, *ifaddrs; struct sockaddr_in *sin; #ifdef HAVE_STRUCT_IN6_ADDR struct sockaddr_in6 *sin6; char addrbuf[INET6_ADDRSTRLEN]; #endif int ailen, i, len, num_interfaces = 0; char *cp; debug_decl(get_net_ifs, SUDO_DEBUG_NETIF) if (getifaddrs(&ifaddrs)) debug_return_int(0); /* Allocate space for the interfaces info string. */ for (ifa = ifaddrs; ifa != NULL; ifa = ifa -> ifa_next) { /* Skip interfaces marked "down" and "loopback". */ if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL || !ISSET(ifa->ifa_flags, IFF_UP) || ISSET(ifa->ifa_flags, IFF_LOOPBACK)) continue; switch (ifa->ifa_addr->sa_family) { case AF_INET: #ifdef HAVE_STRUCT_IN6_ADDR case AF_INET6: #endif num_interfaces++; break; } } if (num_interfaces == 0) debug_return_int(0); ailen = num_interfaces * 2 * INET6_ADDRSTRLEN; *addrinfo = cp = emalloc(ailen); /* Store the IP addr/netmask pairs. */ for (ifa = ifaddrs, i = 0; ifa != NULL; ifa = ifa -> ifa_next) { /* Skip interfaces marked "down" and "loopback". */ if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL || !ISSET(ifa->ifa_flags, IFF_UP) || ISSET(ifa->ifa_flags, IFF_LOOPBACK)) continue; switch (ifa->ifa_addr->sa_family) { case AF_INET: sin = (struct sockaddr_in *)ifa->ifa_addr; len = snprintf(cp, ailen - (*addrinfo - cp), "%s%s/", cp == *addrinfo ? "" : " ", inet_ntoa(sin->sin_addr)); if (len <= 0 || len >= ailen - (*addrinfo - cp)) { warningx(_("load_interfaces: overflow detected")); goto done; } cp += len; sin = (struct sockaddr_in *)ifa->ifa_netmask; len = snprintf(cp, ailen - (*addrinfo - cp), "%s", inet_ntoa(sin->sin_addr)); if (len <= 0 || len >= ailen - (*addrinfo - cp)) { warningx(_("load_interfaces: overflow detected")); goto done; } cp += len; break; #ifdef HAVE_STRUCT_IN6_ADDR case AF_INET6: sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf)); len = snprintf(cp, ailen - (*addrinfo - cp), "%s%s/", cp == *addrinfo ? "" : " ", addrbuf); if (len <= 0 || len >= ailen - (*addrinfo - cp)) { warningx(_("load_interfaces: overflow detected")); goto done; } cp += len; sin6 = (struct sockaddr_in6 *)ifa->ifa_netmask; inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf)); len = snprintf(cp, ailen - (*addrinfo - cp), "%s", addrbuf); if (len <= 0 || len >= ailen - (*addrinfo - cp)) { warningx(_("load_interfaces: overflow detected")); goto done; } cp += len; break; #endif /* HAVE_STRUCT_IN6_ADDR */ } } done: #ifdef HAVE_FREEIFADDRS freeifaddrs(ifaddrs); #else efree(ifaddrs); #endif debug_return_int(num_interfaces); }