/* * Similar to putenv(3) but operates on sudo's private copy of the * environment (not environ) and it always overwrites. The dupcheck param * determines whether we need to verify that the variable is not already set. * Will only overwrite an existing variable if overwrite is set. */ static void sudo_putenv(char *str, int dupcheck, int overwrite) { char **ep; size_t len; int found = FALSE; /* Make sure there is room for the new entry plus a NULL. */ if (env.env_len + 2 > env.env_size) { env.env_size += 128; env.envp = erealloc3(env.envp, env.env_size, sizeof(char *)); #ifdef ENV_DEBUG memset(env.envp + env.env_len, 0, (env.env_size - env.env_len) * sizeof(char *)); #endif } #ifdef ENV_DEBUG if (env.envp[env.env_len] != NULL) errorx(1, _("sudo_putenv: corrupted envp, length mismatch")); #endif if (dupcheck) { len = (strchr(str, '=') - str) + 1; for (ep = env.envp; !found && *ep != NULL; ep++) { if (strncmp(str, *ep, len) == 0) { if (overwrite) *ep = str; found = TRUE; } } /* Prune out duplicate variables. */ if (found && overwrite) { while (*ep != NULL) { if (strncmp(str, *ep, len) == 0) { char **cur = ep; while ((*cur = *(cur + 1)) != NULL) cur++; } else { ep++; } } env.env_len = ep - env.envp; } } if (!found) { ep = env.envp + env.env_len; env.env_len++; *ep++ = str; *ep = NULL; } }
/* * Allocate and fill in the interfaces global variable with the * machine's ip addresses and netmasks. */ void load_interfaces() { struct ifconf *ifconf; struct ifreq *ifr, ifr_tmp; struct sockaddr_in *sin; int sock, n, i; size_t len = sizeof(struct ifconf) + BUFSIZ; char *previfname = "", *ifconf_buf = NULL; #ifdef _ISC struct strioctl strioctl; #endif /* _ISC */ sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) error(1, "cannot open socket"); /* * Get interface configuration or return (leaving num_interfaces == 0) */ for (;;) { ifconf_buf = erealloc(ifconf_buf, len); ifconf = (struct ifconf *) ifconf_buf; ifconf->ifc_len = len - sizeof(struct ifconf); ifconf->ifc_buf = (caddr_t) (ifconf_buf + sizeof(struct ifconf)); #ifdef _ISC STRSET(SIOCGIFCONF, (caddr_t) ifconf, len); 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 */ efree(ifconf_buf); (void) close(sock); return; } /* Break out of loop if we have a big enough buffer. */ if (ifconf->ifc_len + sizeof(struct ifreq) < len) break; len += BUFSIZ; } /* Allocate space for the maximum number of interfaces that could exist. */ if ((n = ifconf->ifc_len / sizeof(struct ifreq)) == 0) return; interfaces = (struct interface *) emalloc2(n, sizeof(struct interface)); /* 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_SA_LEN if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr)) i += ifr->ifr_addr.sa_len - sizeof(struct sockaddr); #endif /* HAVE_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 zero_bytes(&ifr_tmp, 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; interfaces[num_interfaces].addr.ip4.s_addr = sin->sin_addr.s_addr; /* Stash the name of the interface we saved. */ previfname = ifr->ifr_name; /* Get the netmask. */ zero_bytes(&ifr_tmp, sizeof(ifr_tmp)); strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1); #ifdef SIOCGIFNETMASK #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; interfaces[num_interfaces].netmask.ip4.s_addr = sin->sin_addr.s_addr; } else { #else { #endif /* SIOCGIFNETMASK */ if (IN_CLASSC(interfaces[num_interfaces].addr.ip4.s_addr)) interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSC_NET); else if (IN_CLASSB(interfaces[num_interfaces].addr.ip4.s_addr)) interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSB_NET); else interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSA_NET); } /* Only now can we be sure it was a good/interesting interface. */ interfaces[num_interfaces].family = AF_INET; num_interfaces++; } /* If the expected size < real size, realloc the array. */ if (n != num_interfaces) { if (num_interfaces != 0) interfaces = (struct interface *) erealloc3(interfaces, num_interfaces, sizeof(struct interface)); else efree(interfaces); } efree(ifconf_buf); (void) close(sock); } #else /* !SIOCGIFCONF || STUB_LOAD_INTERFACES */ /* * Stub function for those without SIOCGIFCONF */ void load_interfaces() { return; } #endif /* SIOCGIFCONF && !STUB_LOAD_INTERFACES */ void dump_interfaces() { int i; #ifdef HAVE_IN6_ADDR char addrbuf[INET6_ADDRSTRLEN], maskbuf[INET6_ADDRSTRLEN]; #endif puts("Local IP address and netmask pairs:"); for (i = 0; i < num_interfaces; i++) { switch(interfaces[i].family) { case AF_INET: printf("\t%s / ", inet_ntoa(interfaces[i].addr.ip4)); puts(inet_ntoa(interfaces[i].netmask.ip4)); break; #ifdef HAVE_IN6_ADDR case AF_INET6: inet_ntop(AF_INET6, &interfaces[i].addr.ip6, addrbuf, sizeof(addrbuf)); inet_ntop(AF_INET6, &interfaces[i].netmask.ip6, maskbuf, sizeof(maskbuf)); printf("\t%s / %s\n", addrbuf, maskbuf); break; #endif /* HAVE_IN6_ADDR */ } } }
/* * 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; }