/* * Take a uid in string form "#123" and return a faked up passwd struct. */ struct passwd * sudo_fakepwnam(const char *user, gid_t gid) { struct cache_item *item; struct passwd *pw; struct rbnode *node; size_t len, namelen; int i; namelen = strlen(user); len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ + sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ + sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL); for (i = 0; i < 2; i++) { item = emalloc(len); zero_bytes(item, sizeof(*item) + sizeof(*pw)); pw = (struct passwd *) ((char *)item + sizeof(*item)); pw->pw_uid = (uid_t) atoi(user + 1); pw->pw_gid = gid; pw->pw_name = (char *)pw + sizeof(struct passwd); memcpy(pw->pw_name, user, namelen + 1); pw->pw_passwd = pw->pw_name + namelen + 1; memcpy(pw->pw_passwd, "*", 2); pw->pw_gecos = pw->pw_passwd + 2; pw->pw_gecos[0] = '\0'; pw->pw_dir = pw->pw_gecos + 1; memcpy(pw->pw_dir, "/", 2); pw->pw_shell = pw->pw_dir + 2; memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL)); item->refcnt = 1; item->d.pw = pw; if (i == 0) { /* Store by uid, overwriting cached version. */ item->k.uid = pw->pw_uid; if ((node = rbinsert(pwcache_byuid, item)) != NULL) { pw_delref_item(node->data); node->data = item; } } else { /* Store by name, overwriting cached version. */ item->k.name = pw->pw_name; if ((node = rbinsert(pwcache_byname, item)) != NULL) { pw_delref_item(node->data); node->data = item; } } } item->refcnt++; return pw; }
struct passwd * getpwent(void) { static struct passwd pw; static char pwbuf[LINE_MAX]; size_t len; char *cp, *colon; if ((colon = fgets(pwbuf, sizeof(pwbuf), pwf)) == NULL) return NULL; zero_bytes(&pw, sizeof(pw)); if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; pw.pw_name = cp; if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; pw.pw_passwd = cp; if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; pw.pw_uid = atoi(cp); if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; pw.pw_gid = atoi(cp); if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; pw.pw_gecos = cp; if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; pw.pw_dir = cp; pw.pw_shell = colon; len = strlen(colon); if (len > 0 && colon[len - 1] == '\n') colon[len - 1] = '\0'; return &pw; }
/* * Take a gid in string form "#123" and return a faked up group struct. */ struct group * sudo_fakegrnam(const char *group) { struct cache_item *item; struct group *gr; struct rbnode *node; size_t len, namelen; int i; namelen = strlen(group); len = sizeof(*item) + sizeof(*gr) + namelen + 1; for (i = 0; i < 2; i++) { item = emalloc(len); zero_bytes(item, sizeof(*item) + sizeof(*gr)); gr = (struct group *) ((char *)item + sizeof(*item)); gr->gr_gid = (gid_t) atoi(group + 1); gr->gr_name = (char *)gr + sizeof(struct group); memcpy(gr->gr_name, group, namelen + 1); item->refcnt = 1; item->d.gr = gr; if (i == 0) { /* Store by gid, overwriting cached version. */ item->k.gid = gr->gr_gid; if ((node = rbinsert(grcache_bygid, item)) != NULL) { gr_delref_item(node->data); node->data = item; } } else { /* Store by name, overwriting cached version. */ item->k.name = gr->gr_name; if ((node = rbinsert(grcache_byname, item)) != NULL) { gr_delref_item(node->data); node->data = item; } } } item->refcnt++; return gr; }
struct group * getgrent(void) { static struct group gr; static char grbuf[LINE_MAX], *gr_mem[GRMEM_MAX+1]; size_t len; char *cp, *colon; int n; if ((colon = fgets(grbuf, sizeof(grbuf), grf)) == NULL) return NULL; zero_bytes(&gr, sizeof(gr)); if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; gr.gr_name = cp; if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; gr.gr_passwd = cp; if ((colon = strchr(cp = colon, ':')) == NULL) return NULL; *colon++ = '\0'; gr.gr_gid = atoi(cp); len = strlen(colon); if (len > 0 && colon[len - 1] == '\n') colon[len - 1] = '\0'; if (*colon != '\0') { gr.gr_mem = gr_mem; cp = strtok(colon, ","); for (n = 0; cp != NULL && n < GRMEM_MAX; n++) { gr.gr_mem[n] = cp; cp = strtok(NULL, ","); } gr.gr_mem[n++] = NULL; } else gr.gr_mem = NULL; return &gr; }
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; }
/* * Send a message to MAILTO user */ static void send_mail(const char *fmt, ...) { FILE *mail; char *p; int fd, pfd[2], status; pid_t pid, rv; sigaction_t sa; va_list ap; #ifndef NO_ROOT_MAILER static char *root_envp[] = { "HOME=/", "PATH=/usr/bin:/bin:/usr/sbin:/sbin", "LOGNAME=root", "USERNAME=root", "USER=root", NULL }; #endif /* NO_ROOT_MAILER */ /* Just return if mailer is disabled. */ if (!def_mailerpath || !def_mailto) return; /* Fork and return, child will daemonize. */ switch (pid = fork()) { case -1: /* Error. */ error(1, _("unable to fork")); break; case 0: /* Child. */ switch (pid = fork()) { case -1: /* Error. */ mysyslog(LOG_ERR, _("unable to fork: %m")); _exit(1); case 0: /* Grandchild continues below. */ break; default: /* Parent will wait for us. */ _exit(0); } break; default: /* Parent. */ do { rv = waitpid(pid, &status, 0); } while (rv == -1 && errno == EINTR); return; } /* Daemonize - disassociate from session/tty. */ if (setsid() == -1) warning("setsid"); if (chdir("/") == -1) warning("chdir(/)"); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) { (void) dup2(fd, STDIN_FILENO); (void) dup2(fd, STDOUT_FILENO); (void) dup2(fd, STDERR_FILENO); } #ifdef HAVE_SETLOCALE if (!setlocale(LC_ALL, def_sudoers_locale)) { setlocale(LC_ALL, "C"); efree(def_sudoers_locale); def_sudoers_locale = estrdup("C"); } #endif /* HAVE_SETLOCALE */ /* Close password, group and other fds so we don't leak. */ sudo_endpwent(); sudo_endgrent(); closefrom(STDERR_FILENO + 1); /* Ignore SIGPIPE in case mailer exits prematurely (or is missing). */ zero_bytes(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_INTERRUPT; sa.sa_handler = SIG_IGN; (void) sigaction(SIGPIPE, &sa, NULL); if (pipe(pfd) == -1) { mysyslog(LOG_ERR, _("unable to open pipe: %m")); _exit(1); } switch (pid = fork()) { case -1: /* Error. */ mysyslog(LOG_ERR, _("unable to fork: %m")); _exit(1); break; case 0: { char *argv[MAX_MAILFLAGS + 1]; char *mpath, *mflags; int i; /* Child, set stdin to output side of the pipe */ if (pfd[0] != STDIN_FILENO) { if (dup2(pfd[0], STDIN_FILENO) == -1) { mysyslog(LOG_ERR, _("unable to dup stdin: %m")); _exit(127); } (void) close(pfd[0]); } (void) close(pfd[1]); /* Build up an argv based on the mailer path and flags */ mflags = estrdup(def_mailerflags); mpath = estrdup(def_mailerpath); if ((argv[0] = strrchr(mpath, ' '))) argv[0]++; else argv[0] = mpath; i = 1; if ((p = strtok(mflags, " \t"))) { do { argv[i] = p; } while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t"))); } argv[i] = NULL; /* * Depending on the config, either run the mailer as root * (so user cannot kill it) or as the user (for the paranoid). */ #ifndef NO_ROOT_MAILER set_perms(PERM_ROOT|PERM_NOEXIT); execve(mpath, argv, root_envp); #else set_perms(PERM_FULL_USER|PERM_NOEXIT); execv(mpath, argv); #endif /* NO_ROOT_MAILER */ mysyslog(LOG_ERR, _("unable to execute %s: %m"), mpath); _exit(127); } break; } (void) close(pfd[0]); mail = fdopen(pfd[1], "w"); /* Pipes are all setup, send message. */ (void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ", def_mailto, def_mailfrom ? def_mailfrom : user_name, "auto-generated"); for (p = def_mailsub; *p; p++) { /* Expand escapes in the subject */ if (*p == '%' && *(p+1) != '%') { switch (*(++p)) { case 'h': (void) fputs(user_host, mail); break; case 'u': (void) fputs(user_name, mail); break; default: p--; break; } } else (void) fputc(*p, mail); } #ifdef HAVE_NL_LANGINFO if (strcmp(def_sudoers_locale, "C") != 0) (void) fprintf(mail, "\nContent-Type: text/plain; charset=\"%s\"\nContent-Transfer-Encoding: 8bit", nl_langinfo(CODESET)); #endif /* HAVE_NL_LANGINFO */ (void) fprintf(mail, "\n\n%s : %s : %s : ", user_host, get_timestr(time(NULL), def_log_year), user_name); va_start(ap, fmt); (void) vfprintf(mail, fmt, ap); va_end(ap); fputs("\n\n", mail); fclose(mail); do { rv = waitpid(pid, &status, 0); } while (rv == -1 && errno == EINTR); _exit(0); }
int verify_user(struct passwd *pw, char *prompt) { int counter = def_passwd_tries + 1; int success = AUTH_FAILURE; int flags, status, standalone, rval; char *p; sudo_auth *auth; sigaction_t sa, osa; /* Enable suspend during password entry. */ sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_DFL; (void) sigaction(SIGTSTP, &sa, &osa); /* Make sure we have at least one auth method. */ if (auth_switch[0].name == NULL) { audit_failure(NewArgv, "no authentication methods"); log_error(0, _("There are no authentication methods compiled into sudo! " "If you want to turn off authentication, use the " "--disable-authentication configure option.")); return -1; } /* Make sure we haven't mixed standalone and shared auth methods. */ standalone = IS_STANDALONE(&auth_switch[0]); if (standalone && auth_switch[1].name != NULL) { audit_failure(NewArgv, "invalid authentication methods"); log_error(0, _("Invalid authentication methods compiled into sudo! " "You may mix standalone and non-standalone authentication.")); return -1; } /* Set FLAG_ONEANDONLY if there is only one auth method. */ if (auth_switch[1].name == NULL) SET(auth_switch[0].flags, FLAG_ONEANDONLY); /* Initialize auth methods and unconfigure the method if necessary. */ for (auth = auth_switch; auth->name; auth++) { if (auth->init && !IS_DISABLED(auth)) { if (NEEDS_USER(auth)) set_perms(PERM_USER); status = (auth->init)(pw, &prompt, auth); if (status == AUTH_FAILURE) SET(auth->flags, FLAG_DISABLED); else if (status == AUTH_FATAL) { /* XXX log */ audit_failure(NewArgv, "authentication failure"); return -1; /* assume error msg already printed */ } if (NEEDS_USER(auth)) restore_perms(); } } while (--counter) { /* Do any per-method setup and unconfigure the method if needed */ for (auth = auth_switch; auth->name; auth++) { if (auth->setup && !IS_DISABLED(auth)) { if (NEEDS_USER(auth)) set_perms(PERM_USER); status = (auth->setup)(pw, &prompt, auth); if (status == AUTH_FAILURE) SET(auth->flags, FLAG_DISABLED); else if (status == AUTH_FATAL) {/* XXX log */ audit_failure(NewArgv, "authentication failure"); return -1; /* assume error msg already printed */ } if (NEEDS_USER(auth)) restore_perms(); } } /* Get the password unless the auth function will do it for us */ if (standalone) { p = prompt; } else { p = auth_getpass(prompt, def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_OFF); if (p == NULL) break; } /* Call authentication functions. */ for (auth = auth_switch; auth->name; auth++) { if (IS_DISABLED(auth)) continue; if (NEEDS_USER(auth)) set_perms(PERM_USER); success = auth->status = (auth->verify)(pw, p, auth); if (NEEDS_USER(auth)) restore_perms(); if (auth->status != AUTH_FAILURE) goto cleanup; } if (!standalone) zero_bytes(p, strlen(p)); pass_warn(); } cleanup: /* Call cleanup routines. */ for (auth = auth_switch; auth->name; auth++) { if (auth->cleanup && !IS_DISABLED(auth)) { if (NEEDS_USER(auth)) set_perms(PERM_USER); status = (auth->cleanup)(pw, auth); if (status == AUTH_FATAL) { /* XXX log */ audit_failure(NewArgv, "authentication failure"); return -1; /* assume error msg already printed */ } if (NEEDS_USER(auth)) restore_perms(); } } switch (success) { case AUTH_SUCCESS: (void) sigaction(SIGTSTP, &osa, NULL); rval = TRUE; break; case AUTH_INTR: case AUTH_FAILURE: if (counter != def_passwd_tries) { if (def_mail_badpass || def_mail_always) flags = 0; else flags = NO_MAIL; log_error(flags, ngettext("%d incorrect password attempt", "%d incorrect password attempts", def_passwd_tries - counter), def_passwd_tries - counter); } audit_failure(NewArgv, "authentication failure"); rval = FALSE; break; case AUTH_FATAL: default: audit_failure(NewArgv, "authentication failure"); rval = -1; break; } return rval; }
/* * ``Conversation function'' for PAM. * XXX - does not handle PAM_BINARY_PROMPT */ static int converse(int num_msg, PAM_CONST struct pam_message **msg, struct pam_response **response, void *appdata_ptr) { struct pam_response *pr; PAM_CONST struct pam_message *pm; const char *prompt; char *pass; int n, type, std_prompt; if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) return PAM_SYSTEM_ERR; zero_bytes(*response, num_msg * sizeof(struct pam_response)); for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) { type = SUDO_CONV_PROMPT_ECHO_OFF; switch (pm->msg_style) { case PAM_PROMPT_ECHO_ON: type = SUDO_CONV_PROMPT_ECHO_ON; case PAM_PROMPT_ECHO_OFF: prompt = def_prompt; /* Error out if the last password read was interrupted. */ if (gotintr) goto err; /* Is the sudo prompt standard? (If so, we'l just use PAM's) */ std_prompt = strncmp(def_prompt, "Password:"******"Password: "******"Password:"******"Password:"******""; #else goto err; #endif } pr->resp = estrdup(pass); zero_bytes(pass, strlen(pass)); break; case PAM_TEXT_INFO: if (pm->msg) (void) puts(pm->msg); break; case PAM_ERROR_MSG: if (pm->msg) { (void) fputs(pm->msg, stderr); (void) fputc('\n', stderr); } break; default: goto err; } } return PAM_SUCCESS; err: /* Zero and free allocated memory and return an error. */ for (pr = *response, n = num_msg; n--; pr++) { if (pr->resp != NULL) { zero_bytes(pr->resp, strlen(pr->resp)); free(pr->resp); pr->resp = NULL; } } zero_bytes(*response, num_msg * sizeof(struct pam_response)); free(*response); *response = NULL; return gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR; }
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. */ 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 */ } } }
/* * ``Conversation function'' for PAM. * XXX - does not handle PAM_BINARY_PROMPT */ static int converse(int num_msg, PAM_CONST struct pam_message **msg, struct pam_response **response, void *appdata_ptr) { struct pam_response *pr; PAM_CONST struct pam_message *pm; const char *prompt; char *pass; int n, type, std_prompt; int ret = PAM_AUTH_ERR; debug_decl(converse, SUDO_DEBUG_AUTH) if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) debug_return_int(PAM_SYSTEM_ERR); zero_bytes(*response, num_msg * sizeof(struct pam_response)); for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) { type = SUDO_CONV_PROMPT_ECHO_OFF; switch (pm->msg_style) { case PAM_PROMPT_ECHO_ON: type = SUDO_CONV_PROMPT_ECHO_ON; /* FALLTHROUGH */ case PAM_PROMPT_ECHO_OFF: prompt = def_prompt; /* Error out if the last password read was interrupted. */ if (getpass_error) goto done; /* Is the sudo prompt standard? (If so, we'l just use PAM's) */ std_prompt = strncmp(def_prompt, "Password:"******"Password: "******"Password:"******"Password:"******""; #else goto done; #endif } pr->resp = estrdup(pass); zero_bytes(pass, strlen(pass)); break; case PAM_TEXT_INFO: if (pm->msg) (void) puts(pm->msg); break; case PAM_ERROR_MSG: if (pm->msg) { (void) fputs(pm->msg, stderr); (void) fputc('\n', stderr); } break; default: ret = PAM_CONV_ERR; goto done; } } ret = PAM_SUCCESS; done: if (ret != PAM_SUCCESS) { /* Zero and free allocated memory and return an error. */ for (pr = *response, n = num_msg; n--; pr++) { if (pr->resp != NULL) { zero_bytes(pr->resp, strlen(pr->resp)); free(pr->resp); pr->resp = NULL; } } zero_bytes(*response, num_msg * sizeof(struct pam_response)); free(*response); *response = NULL; } debug_return_int(ret); }
int main(int argc, char *argv[]) { int ch, idx, plen, nready, interactive = 0, listonly = 0; const char *id, *user = NULL, *pattern = NULL, *tty = NULL, *decimal = "."; char path[PATH_MAX], buf[LINE_MAX], *cp, *ep; double seconds, to_wait, speed = 1.0, max_wait = 0; FILE *lfile; fd_set *fdsw; sigaction_t sa; size_t len, nbytes, nread, off; ssize_t nwritten; #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME) setprogname(argc > 0 ? argv[0] : "sudoreplay"); #endif #ifdef HAVE_SETLOCALE setlocale(LC_ALL, ""); decimal = localeconv()->decimal_point; #endif while ((ch = getopt(argc, argv, "d:f:hlm:s:V")) != -1) { switch(ch) { case 'd': session_dir = optarg; break; case 'f': /* Set the replay filter. */ replay_filter = 0; for (cp = strtok(optarg, ","); cp; cp = strtok(NULL, ",")) { if (strcmp(cp, "stdout") == 0) SET(replay_filter, 1 << IOFD_STDOUT); else if (strcmp(cp, "stderr") == 0) SET(replay_filter, 1 << IOFD_STDERR); else if (strcmp(cp, "ttyout") == 0) SET(replay_filter, 1 << IOFD_TTYOUT); else errorx(1, "invalid filter option: %s", optarg); } break; case 'h': help(); /* NOTREACHED */ case 'l': listonly = 1; break; case 'm': errno = 0; max_wait = strtod(optarg, &ep); if (*ep != '\0' || errno != 0) errorx(1, "invalid max wait: %s", optarg); break; case 's': errno = 0; speed = strtod(optarg, &ep); if (*ep != '\0' || errno != 0) errorx(1, "invalid speed factor: %s", optarg); break; case 'V': (void) printf("%s version %s\n", getprogname(), PACKAGE_VERSION); exit(0); default: usage(1); /* NOTREACHED */ } } argc -= optind; argv += optind; if (listonly) exit(list_sessions(argc, argv, pattern, user, tty)); if (argc != 1) usage(1); /* 6 digit ID in base 36, e.g. 01G712AB */ id = argv[0]; if (!VALID_ID(id)) errorx(1, "invalid ID %s", id); plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing", session_dir, id, &id[2], &id[4]); if (plen <= 0 || plen >= sizeof(path)) errorx(1, "%s/%.2s/%.2s/%.2s/%.2s/timing: %s", session_dir, id, &id[2], &id[4], strerror(ENAMETOOLONG)); plen -= 7; /* Open files for replay, applying replay filter for the -f flag. */ for (idx = 0; idx < IOFD_MAX; idx++) { if (ISSET(replay_filter, 1 << idx) || idx == IOFD_TIMING) { io_fds[idx].v = open_io_fd(path, plen, io_fnames[idx]); if (io_fds[idx].v == NULL) error(1, "unable to open %s", path); } } /* Read log file. */ path[plen] = '\0'; strlcat(path, "/log", sizeof(path)); lfile = fopen(path, "r"); if (lfile == NULL) error(1, "unable to open %s", path); cp = NULL; len = 0; /* Pull out command (third line). */ if (getline(&cp, &len, lfile) == -1 || getline(&cp, &len, lfile) == -1 || getline(&cp, &len, lfile) == -1) { errorx(1, "invalid log file %s", path); } printf("Replaying sudo session: %s", cp); free(cp); fclose(lfile); fflush(stdout); zero_bytes(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESETHAND; sa.sa_handler = cleanup; (void) sigaction(SIGINT, &sa, NULL); (void) sigaction(SIGKILL, &sa, NULL); (void) sigaction(SIGTERM, &sa, NULL); (void) sigaction(SIGHUP, &sa, NULL); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; (void) sigaction(SIGTSTP, &sa, NULL); (void) sigaction(SIGQUIT, &sa, NULL); /* XXX - read user input from /dev/tty and set STDOUT to raw if not a pipe */ /* Set stdin to raw mode if it is a tty */ interactive = isatty(STDIN_FILENO); if (interactive) { ch = fcntl(STDIN_FILENO, F_GETFL, 0); if (ch != -1) (void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK); if (!term_raw(STDIN_FILENO, 1)) error(1, "cannot set tty to raw mode"); } fdsw = (fd_set *)emalloc2(howmany(STDOUT_FILENO + 1, NFDBITS), sizeof(fd_mask)); /* * Timing file consists of line of the format: "%f %d\n" */ #ifdef HAVE_ZLIB_H while (gzgets(io_fds[IOFD_TIMING].g, buf, sizeof(buf)) != NULL) { #else while (fgets(buf, sizeof(buf), io_fds[IOFD_TIMING].f) != NULL) { #endif if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) errorx(1, "invalid timing file line: %s", buf); if (interactive) check_input(STDIN_FILENO, &speed); /* Adjust delay using speed factor and clamp to max_wait */ to_wait = seconds / speed; if (max_wait && to_wait > max_wait) to_wait = max_wait; delay(to_wait); /* Even if we are not relaying, we still have to delay. */ if (io_fds[idx].v == NULL) continue; /* All output is sent to stdout. */ while (nbytes != 0) { if (nbytes > sizeof(buf)) len = sizeof(buf); else len = nbytes; #ifdef HAVE_ZLIB_H nread = gzread(io_fds[idx].g, buf, len); #else nread = fread(buf, 1, len, io_fds[idx].f); #endif nbytes -= nread; off = 0; do { /* no stdio, must be unbuffered */ nwritten = write(STDOUT_FILENO, buf + off, nread - off); if (nwritten == -1) { if (errno == EINTR) continue; if (errno == EAGAIN) { FD_SET(STDOUT_FILENO, fdsw); do { nready = select(STDOUT_FILENO + 1, NULL, fdsw, NULL, NULL); } while (nready == -1 && errno == EINTR); if (nready == 1) continue; } error(1, "writing to standard output"); } off += nwritten; } while (nread > off); } } term_restore(STDIN_FILENO, 1); exit(0); } static void delay(double secs) { struct timespec ts, rts; int rval; /* * Typical max resolution is 1/HZ but we can't portably check that. * If the interval is small enough, just ignore it. */ if (secs < 0.0001) return; rts.tv_sec = secs; rts.tv_nsec = (secs - (double) rts.tv_sec) * 1000000000.0; do { memcpy(&ts, &rts, sizeof(ts)); rval = nanosleep(&ts, &rts); } while (rval == -1 && errno == EINTR); if (rval == -1) error(1, "nanosleep: tv_sec %ld, tv_nsec %ld", ts.tv_sec, ts.tv_nsec); } static void * open_io_fd(char *path, int len, const char *suffix) { path[len] = '\0'; strlcat(path, suffix, PATH_MAX); #ifdef HAVE_ZLIB_H return gzopen(path, "r"); #else return fopen(path, "r"); #endif }
void *alloc_zero(unsigned long size) { void *ptr = allocfunc(size); if (ptr) zero_bytes(ptr, size); return ptr; }