示例#1
0
文件: logging.c 项目: radosroka/sudo
/*
 * Log and audit that user was not allowed to run the command.
 */
bool
log_failure(int status, int flags)
{
    bool ret, inform_user = true;
    debug_decl(log_failure, SUDOERS_DEBUG_LOGGING)

    /* The user doesn't always get to see the log message (path info). */
    if (!ISSET(status, FLAG_NO_USER | FLAG_NO_HOST) && def_path_info &&
	(flags == NOT_FOUND_DOT || flags == NOT_FOUND))
	inform_user = false;
    ret = log_denial(status, inform_user);

    if (!inform_user) {
	/*
	 * We'd like to not leak path info at all here, but that can
	 * *really* confuse the users.  To really close the leak we'd
	 * have to say "not allowed to run foo" even when the problem
	 * is just "no foo in path" since the user can trivially set
	 * their path to just contain a single dir.
	 */
	if (flags == NOT_FOUND)
	    sudo_warnx(U_("%s: command not found"), user_cmnd);
	else if (flags == NOT_FOUND_DOT)
	    sudo_warnx(U_("ignoring \"%s\" found in '.'\nUse \"sudo ./%s\" if this is the \"%s\" you wish to run."), user_cmnd, user_cmnd, user_cmnd);
    }

    debug_return_bool(ret);
}
示例#2
0
文件: selinux.c 项目: radosroka/sudo
/* 
 * 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 ret = -1;
    debug_decl(selinux_setup, SUDO_DEBUG_SELINUX)

    /* Store the caller's SID in old_context. */
    if (getprevcon(&se_state.old_context)) {
	sudo_warn(U_("failed to get old_context"));
	goto done;
    }

    se_state.enforcing = security_getenforce();
    if (se_state.enforcing < 0) {
	sudo_warn(U_("unable to determine enforcing mode."));
	goto done;
    }

#ifdef DEBUG
    sudo_warnx("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) {
#ifdef HAVE_LINUX_AUDIT
	audit_role_change(se_state.old_context, "?",
	  se_state.ttyn, 0);
#endif
	goto done;
    }
    
    if (relabel_tty(ttyn, ptyfd) < 0) {
	sudo_warn(U_("unable to set tty context to %s"), se_state.new_context);
	goto done;
    }

#ifdef DEBUG
    if (se_state.ttyfd != -1) {
	sudo_warnx("your old tty context is %s", se_state.tty_context);
	sudo_warnx("your new tty context is %s", se_state.new_tty_context);
    }
#endif

#ifdef HAVE_LINUX_AUDIT
    audit_role_change(se_state.old_context, se_state.new_context,
	se_state.ttyn, 1);
#endif

    ret = 0;

done:
    debug_return_int(ret);
}
示例#3
0
文件: selinux.c 项目: radosroka/sudo
/*
 * 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;
    debug_decl(selinux_restore_tty, SUDO_DEBUG_SELINUX)

    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) {
	sudo_warn(U_("unable to fgetfilecon %s"), se_state.ttyn);
	goto skip_relabel;
    }

    if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) {
	sudo_warnx(U_("%s changed labels"), se_state.ttyn);
	goto skip_relabel;
    }

    if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0)
	sudo_warn(U_("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;
    }
    debug_return_int(retval);
}
示例#4
0
文件: toke_util.c 项目: millert/sudo
bool
fill_txt(const char *src, size_t len, size_t olen)
{
    char *dst;
    int h;
    debug_decl(fill_txt, SUDOERS_DEBUG_PARSER)

    dst = olen ? realloc(sudoerslval.string, olen + len + 1) : malloc(len + 1);
    if (dst == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	sudoerserror(NULL);
	debug_return_bool(false);
    }
    sudoerslval.string = dst;

    /* Copy the string and collapse any escaped characters. */
    dst += olen;
    while (len--) {
	if (*src == '\\' && len) {
	    if (src[1] == 'x' && len >= 3 && (h = hexchar(src + 2)) != -1) {
		*dst++ = h;
		src += 4;
		len -= 3;
	    } else {
		src++;
		len--;
		*dst++ = *src++;
	    }
	} else {
	    *dst++ = *src++;
	}
    }
    *dst = '\0';
    debug_return_bool(true);
}
示例#5
0
文件: toke_util.c 项目: millert/sudo
bool
fill_cmnd(const char *src, size_t len)
{
    char *dst;
    size_t i;
    debug_decl(fill_cmnd, SUDOERS_DEBUG_PARSER)

    arg_len = arg_size = 0;

    dst = sudoerslval.command.cmnd = malloc(len + 1);
    if (dst == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	sudoerserror(NULL);
	debug_return_bool(false);
    }
    sudoerslval.command.args = NULL;

    /* Copy the string and collapse any escaped sudo-specific characters. */
    for (i = 0; i < len; i++) {
	if (src[i] == '\\' && i != len - 1 && SPECIAL(src[i + 1]))
	    *dst++ = src[++i];
	else
	    *dst++ = src[i];
    }
    *dst = '\0';

    debug_return_bool(true);
}
示例#6
0
文件: gidlist.c 项目: aixoss/sudo
/*
 * Parse a comma-separated list of gids into an allocated array of GETGROUPS_T.
 * If a pointer to the base gid is specified, it is stored as the first element
 * in the array.
 * Returns the number of gids in the allocated array.
 */
int
sudo_parse_gids_v1(const char *gidstr, const gid_t *basegid, GETGROUPS_T **gidsp)
{
    int ngids = 0;
    GETGROUPS_T *gids;
    const char *cp = gidstr;
    const char *errstr;
    char *ep;
    debug_decl(sudo_parse_gids, SUDO_DEBUG_UTIL)

    /* Count groups. */
    if (*cp != '\0') {
	ngids++;
	do {
	    if (*cp++ == ',')
		ngids++;
	} while (*cp != '\0');
    }
    /* Base gid is optional. */
    if (basegid != NULL)
	ngids++;
    /* Allocate and fill in array. */
    if (ngids != 0) {
	gids = reallocarray(NULL, ngids, sizeof(GETGROUPS_T));
	if (gids == NULL) {
	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	    debug_return_int(-1);
	}
	ngids = 0;
	if (basegid != NULL)
	    gids[ngids++] = *basegid;
	cp = gidstr;
	do {
	    gids[ngids] = (GETGROUPS_T) sudo_strtoid(cp, ",", &ep, &errstr);
	    if (errstr != NULL) {
		sudo_warnx(U_("%s: %s"), cp, U_(errstr));
		free(gids);
		debug_return_int(-1);
	    }
	    if (basegid == NULL || gids[ngids] != *basegid)
		ngids++;
	    cp = ep + 1;
	} while (*ep != '\0');
	*gidsp = gids;
    }
    debug_return_int(ngids);
}
示例#7
0
文件: toke_util.c 项目: millert/sudo
bool
fill_args(const char *s, size_t len, int addspace)
{
    unsigned int new_len;
    char *p;
    debug_decl(fill_args, SUDOERS_DEBUG_PARSER)

    if (arg_size == 0) {
	addspace = 0;
	new_len = len;
    } else
	new_len = arg_len + len + addspace;

    if (new_len >= arg_size) {
	/* Allocate in increments of 128 bytes to avoid excessive realloc(). */
	arg_size = (new_len + 1 + 127) & ~127;

	p = realloc(sudoerslval.command.args, arg_size);
	if (p == NULL) {
	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	    goto bad;
	} else
	    sudoerslval.command.args = p;
    }

    /* Efficiently append the arg (with a leading space if needed). */
    p = sudoerslval.command.args + arg_len;
    if (addspace)
	*p++ = ' ';
    len = arg_size - (p - sudoerslval.command.args);
    if (strlcpy(p, s, len) >= len) {
	sudo_warnx(U_("internal error, %s overflow"), __func__);
	goto bad;
    }
    arg_len = new_len;
    debug_return_bool(true);
bad:
    sudoerserror(NULL);
    free(sudoerslval.command.args);
    sudoerslval.command.args = NULL;
    arg_len = arg_size = 0;
    debug_return_bool(false);
}
示例#8
0
void
selinux_execve(const char *path, char *const argv[], char *const envp[],
    int noexec)
{
    char **nargv;
    const char *sesh;
    int argc, serrno;
    debug_decl(selinux_execve, SUDO_DEBUG_SELINUX)

    sesh = sudo_conf_sesh_path();
    if (sesh == NULL) {
	sudo_warnx("internal error: sesh path not set");
	errno = EINVAL;
	debug_return;
    }

    if (setexeccon(se_state.new_context)) {
	sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
	if (se_state.enforcing)
	    debug_return;
    }

#ifdef HAVE_SETKEYCREATECON
    if (setkeycreatecon(se_state.new_context)) {
	sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
	if (se_state.enforcing)
	    debug_return;
    }
#endif /* HAVE_SETKEYCREATECON */

    /*
     * Build new argv with sesh as argv[0].
     * If argv[0] ends in -noexec, sesh will disable execute
     * for the command it runs.
     */
    for (argc = 0; argv[argc] != NULL; argc++)
	continue;
    nargv = sudo_emallocarray(argc + 2, sizeof(char *));
    if (noexec)
	nargv[0] = *argv[0] == '-' ? "-sesh-noexec" : "sesh-noexec";
    else
	nargv[0] = *argv[0] == '-' ? "-sesh" : "sesh";
    nargv[1] = (char *)path;
    memcpy(&nargv[2], &argv[1], argc * sizeof(char *)); /* copies NULL */

    /* sesh will handle noexec for us. */
    sudo_execve(sesh, nargv, envp, false);
    serrno = errno;
    free(nargv);
    errno = serrno;
    debug_return;
}
示例#9
0
文件: securid5.c 项目: millert/sudo
/*
 * securid_init - Initialises communications with ACE server
 * Arguments in:
 *     pw - UNUSED
 *     auth - sudo authentication structure
 *
 * Results out:
 *     auth - auth->data contains pointer to new SecurID handle
 *     return code - Fatal if initialization unsuccessful, otherwise
 *                   success.
 */
int
sudo_securid_init(struct passwd *pw, sudo_auth *auth)
{
    static SDI_HANDLE sd_dat;			/* SecurID handle */
    debug_decl(sudo_securid_init, SUDOERS_DEBUG_AUTH)

    auth->data = (void *) &sd_dat;		/* For method-specific data */

    /* Start communications */
    if (AceInitialize() != SD_FALSE)
	debug_return_int(AUTH_SUCCESS);

    sudo_warnx(U_("failed to initialise the ACE API library"));
    debug_return_int(AUTH_FATAL);
}
示例#10
0
int
linux_audit_command(char *argv[], int result)
{
    int au_fd, rc = -1;
    char *command, *cp, **av;
    size_t size, n;
    debug_decl(linux_audit_command, SUDOERS_DEBUG_AUDIT)

    /* Don't return an error if auditing is not configured. */
    if ((au_fd = linux_audit_open()) < 0)
	debug_return_int(au_fd == AUDIT_NOT_CONFIGURED ? 0 : -1);

    /* Convert argv to a flat string. */
    for (size = 0, av = argv; *av != NULL; av++)
	size += strlen(*av) + 1;
    command = cp = sudo_emalloc(size);
    for (av = argv; *av != NULL; av++) {
	n = strlcpy(cp, *av, size - (cp - command));
	if (n >= size - (cp - command)) {
	    sudo_warnx(U_("internal error, %s overflow"), __func__);
	    goto done;
	}
	cp += n;
	*cp++ = ' ';
    }
    *--cp = '\0';

    /* Log command, ignoring ECONNREFUSED on error. */
    if (audit_log_user_command(au_fd, AUDIT_USER_CMD, command, NULL, result) <= 0) {
	if (errno != ECONNREFUSED) {
	    sudo_warn(U_("unable to send audit message"));
	    goto done;
	}
    }

    rc = 0;

done:
    sudo_efree(command);

    debug_return_int(rc);
}
示例#11
0
/*
 * Get a password entry by name and allocate space for it.
 */
struct passwd *
sudo_getpwnam(const char *name)
{
    struct cache_item key, *item;
    struct rbnode *node;
    size_t len;
    debug_decl(sudo_getpwnam, SUDOERS_DEBUG_NSS)

    key.k.name = (char *) name;
    if ((node = rbfind(pwcache_byname, &key)) != NULL) {
	item = node->data;
	goto done;
    }
    /*
     * Cache passwd db entry if it exists or a negative response if not.
     */
#ifdef HAVE_SETAUTHDB
    aix_setauthdb((char *) name);
#endif
    item = sudo_make_pwitem((uid_t)-1, name);
    if (item == NULL) {
	len = strlen(name) + 1;
	item = sudo_ecalloc(1, sizeof(*item) + len);
	item->refcnt = 1;
	item->k.name = (char *) item + sizeof(*item);
	memcpy(item->k.name, name, len);
	/* item->d.pw = NULL; */
    }
    if (rbinsert(pwcache_byname, item) != NULL) {
	/* should not happen */
	sudo_warnx(U_("unable to cache user %s, already exists"), name);
	item->refcnt = 0;
    }
#ifdef HAVE_SETAUTHDB
    aix_restoreauthdb();
#endif
done:
    item->refcnt++;
    debug_return_ptr(item->d.pw);
}
示例#12
0
/*
 * Get a password entry by uid and allocate space for it.
 */
struct passwd *
sudo_getpwuid(uid_t uid)
{
    struct cache_item key, *item;
    struct rbnode *node;
    debug_decl(sudo_getpwuid, SUDOERS_DEBUG_NSS)

    key.k.uid = uid;
    if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
	item = node->data;
	goto done;
    }
    /*
     * Cache passwd db entry if it exists or a negative response if not.
     */
#ifdef HAVE_SETAUTHDB
    aix_setauthdb(IDtouser(uid));
#endif
    item = sudo_make_pwitem(uid, NULL);
    if (item == NULL) {
	item = sudo_ecalloc(1, sizeof(*item));
	item->refcnt = 1;
	item->k.uid = uid;
	/* item->d.pw = NULL; */
    }
    if (rbinsert(pwcache_byuid, item) != NULL) {
	/* should not happen */
	sudo_warnx(U_("unable to cache uid %u, already exists"),
	    (unsigned int) uid);
	item->refcnt = 0;
    }
#ifdef HAVE_SETAUTHDB
    aix_restoreauthdb();
#endif
done:
    item->refcnt++;
    debug_return_ptr(item->d.pw);
}
示例#13
0
文件: securid5.c 项目: millert/sudo
/*
 * securid_setup - Initialises a SecurID transaction and locks out other
 *     ACE servers
 *
 * Arguments in:
 *     pw - struct passwd for username
 *     promptp - UNUSED
 *     auth - sudo authentication structure for SecurID handle
 *
 * Results out:
 *     return code - Success if transaction started correctly, fatal
 *                   otherwise
 */
int
sudo_securid_setup(struct passwd *pw, char **promptp, sudo_auth *auth)
{
    SDI_HANDLE *sd = (SDI_HANDLE *) auth->data;
    int retval;
    debug_decl(sudo_securid_setup, SUDOERS_DEBUG_AUTH)

    /* Re-initialize SecurID every time. */
    if (SD_Init(sd) != ACM_OK) {
	sudo_warnx(U_("unable to contact the SecurID server"));
	debug_return_int(AUTH_FATAL);
    }

    /* Lock new PIN code */
    retval = SD_Lock(*sd, pw->pw_name);

    switch (retval) {
	case ACM_OK:
		sudo_warnx(U_("User ID locked for SecurID Authentication"));
		debug_return_int(AUTH_SUCCESS);

        case ACE_UNDEFINED_USERNAME:
		sudo_warnx(U_("invalid username length for SecurID"));
		debug_return_int(AUTH_FATAL);

	case ACE_ERR_INVALID_HANDLE:
		sudo_warnx(U_("invalid Authentication Handle for SecurID"));
		debug_return_int(AUTH_FATAL);

	case ACM_ACCESS_DENIED:
		sudo_warnx(U_("SecurID communication failed"));
		debug_return_int(AUTH_FATAL);

	default:
		sudo_warnx(U_("unknown SecurID error"));
		debug_return_int(AUTH_FATAL);
	}
}
示例#14
0
文件: logging.c 项目: radosroka/sudo
static bool
do_logfile(const char *msg)
{
    static bool warned = false;
    const char *timestr;
    int len, oldlocale;
    bool ret = false;
    char *full_line;
    mode_t oldmask;
    FILE *fp;
    debug_decl(do_logfile, SUDOERS_DEBUG_LOGGING)

    sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);

    oldmask = umask(S_IRWXG|S_IRWXO);
    fp = fopen(def_logfile, "a");
    (void) umask(oldmask);
    if (fp == NULL) {
	if (!warned) {
	    log_warning(SLOG_SEND_MAIL|SLOG_NO_LOG,
		N_("unable to open log file: %s"), def_logfile);
	    warned = true;
	}
	goto done;
    }
    if (!sudo_lock_file(fileno(fp), SUDO_LOCK)) {
	if (!warned) {
	    log_warning(SLOG_SEND_MAIL|SLOG_NO_LOG,
		N_("unable to lock log file: %s"), def_logfile);
	    warned = true;
	}
	goto done;
    }

    timestr = get_timestr(time(NULL), def_log_year);
    if (timestr == NULL)
	timestr = "invalid date";
    if (def_log_host) {
	len = asprintf(&full_line, "%s : %s : HOST=%s : %s",
	    timestr, user_name, user_srunhost, msg);
    } else {
	len = asprintf(&full_line, "%s : %s : %s",
	    timestr, user_name, msg);
    }
    if (len == -1) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	goto done;
    }
    if ((size_t)def_loglinelen < sizeof(LOG_INDENT)) {
	/* Don't pretty-print long log file lines (hard to grep). */
	(void) fputs(full_line, fp);
	(void) fputc('\n', fp);
    } else {
	/* Write line with word wrap around def_loglinelen chars. */
	writeln_wrap(fp, full_line, len, def_loglinelen);
    }
    free(full_line);
    (void) fflush(fp);
    if (ferror(fp)) {
	if (!warned) {
	    log_warning(SLOG_SEND_MAIL|SLOG_NO_LOG,
		N_("unable to write log file: %s"), def_logfile);
	    warned = true;
	}
	goto done;
    }
    ret = true;

done:
    if (fp != NULL)
	(void) fclose(fp);
    sudoers_setlocale(oldlocale, NULL);

    debug_return_bool(ret);
}
示例#15
0
/*
 * Setup the execution environment.
 * Builds up the command_info list and sets argv and envp.
 * Returns 1 on success and -1 on error.
 */
int
sudoers_policy_exec_setup(char *argv[], char *envp[], mode_t cmnd_umask,
    char *iolog_path, void *v)
{
    struct sudoers_exec_args *exec_args = v;
    char **command_info;
    int info_len = 0;
    int rval = -1;
    debug_decl(sudoers_policy_exec_setup, SUDOERS_DEBUG_PLUGIN)

    /* Increase the length of command_info as needed, it is *not* checked. */
    command_info = sudo_ecalloc(32, sizeof(char **));

    command_info[info_len++] = sudo_new_key_val("command", safe_cmnd);
    if (def_log_input || def_log_output) {
	if (iolog_path)
	    command_info[info_len++] = iolog_path;
	if (def_log_input) {
	    command_info[info_len++] = sudo_estrdup("iolog_stdin=true");
	    command_info[info_len++] = sudo_estrdup("iolog_ttyin=true");
	}
	if (def_log_output) {
	    command_info[info_len++] = sudo_estrdup("iolog_stdout=true");
	    command_info[info_len++] = sudo_estrdup("iolog_stderr=true");
	    command_info[info_len++] = sudo_estrdup("iolog_ttyout=true");
	}
	if (def_compress_io) {
	    command_info[info_len++] = sudo_estrdup("iolog_compress=true");
	}
	if (def_maxseq) {
	    sudo_easprintf(&command_info[info_len++], "maxseq=%u", def_maxseq);
	}
    }
    if (ISSET(sudo_mode, MODE_EDIT))
	command_info[info_len++] = sudo_estrdup("sudoedit=true");
    if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
	/* Set cwd to run user's homedir. */
	command_info[info_len++] = sudo_new_key_val("cwd", runas_pw->pw_dir);
    }
    if (def_stay_setuid) {
	sudo_easprintf(&command_info[info_len++], "runas_uid=%u",
	    (unsigned int)user_uid);
	sudo_easprintf(&command_info[info_len++], "runas_gid=%u",
	    (unsigned int)user_gid);
	sudo_easprintf(&command_info[info_len++], "runas_euid=%u",
	    (unsigned int)runas_pw->pw_uid);
	sudo_easprintf(&command_info[info_len++], "runas_egid=%u",
	    runas_gr ? (unsigned int)runas_gr->gr_gid :
	    (unsigned int)runas_pw->pw_gid);
    } else {
	sudo_easprintf(&command_info[info_len++], "runas_uid=%u",
	    (unsigned int)runas_pw->pw_uid);
	sudo_easprintf(&command_info[info_len++], "runas_gid=%u",
	    runas_gr ? (unsigned int)runas_gr->gr_gid :
	    (unsigned int)runas_pw->pw_gid);
    }
    if (def_preserve_groups) {
	command_info[info_len++] = "preserve_groups=true";
    } else {
	int i, len;
	gid_t egid;
	size_t glsize;
	char *cp, *gid_list;
	struct group_list *grlist = sudo_get_grlist(runas_pw);

	/* We reserve an extra spot in the list for the effective gid. */
	glsize = sizeof("runas_groups=") - 1 +
	    ((grlist->ngids + 1) * (MAX_UID_T_LEN + 1));
	gid_list = sudo_emalloc(glsize);
	memcpy(gid_list, "runas_groups=", sizeof("runas_groups=") - 1);
	cp = gid_list + sizeof("runas_groups=") - 1;

	/* On BSD systems the effective gid is the first group in the list. */
	egid = runas_gr ? (unsigned int)runas_gr->gr_gid :
	    (unsigned int)runas_pw->pw_gid;
	len = snprintf(cp, glsize - (cp - gid_list), "%u", egid);
	if (len < 0 || (size_t)len >= glsize - (cp - gid_list)) {
	    sudo_warnx(U_("internal error, %s overflow"), __func__);
	    goto done;
	}
	cp += len;
	for (i = 0; i < grlist->ngids; i++) {
	    if (grlist->gids[i] != egid) {
		len = snprintf(cp, glsize - (cp - gid_list), ",%u",
		     (unsigned int) grlist->gids[i]);
		if (len < 0 || (size_t)len >= glsize - (cp - gid_list)) {
		    sudo_warnx(U_("internal error, %s overflow"), __func__);
		    goto done;
		}
		cp += len;
	    }
	}
	command_info[info_len++] = gid_list;
	sudo_grlist_delref(grlist);
    }
    if (def_closefrom >= 0)
	sudo_easprintf(&command_info[info_len++], "closefrom=%d", def_closefrom);
    if (def_noexec)
	command_info[info_len++] = sudo_estrdup("noexec=true");
    if (def_exec_background)
	command_info[info_len++] = sudo_estrdup("exec_background=true");
    if (def_set_utmp)
	command_info[info_len++] = sudo_estrdup("set_utmp=true");
    if (def_use_pty)
	command_info[info_len++] = sudo_estrdup("use_pty=true");
    if (def_utmp_runas)
	command_info[info_len++] = sudo_new_key_val("utmp_user", runas_pw->pw_name);
    if (cmnd_umask != 0777)
	sudo_easprintf(&command_info[info_len++], "umask=0%o", (unsigned int)cmnd_umask);
#ifdef HAVE_LOGIN_CAP_H
    if (def_use_loginclass)
	command_info[info_len++] = sudo_new_key_val("login_class", login_class);
#endif /* HAVE_LOGIN_CAP_H */
#ifdef HAVE_SELINUX
    if (user_role != NULL)
	command_info[info_len++] = sudo_new_key_val("selinux_role", user_role);
    if (user_type != NULL)
	command_info[info_len++] = sudo_new_key_val("selinux_type", user_type);
#endif /* HAVE_SELINUX */
#ifdef HAVE_PRIV_SET
    if (runas_privs != NULL)
	command_info[info_len++] = sudo_new_key_val("runas_privs", runas_privs);
    if (runas_limitprivs != NULL)
	command_info[info_len++] = sudo_new_key_val("runas_limitprivs", runas_limitprivs);
#endif /* HAVE_SELINUX */

    /* Fill in exec environment info */
    *(exec_args->argv) = argv;
    *(exec_args->envp) = envp;
    *(exec_args->info) = command_info;

    rval = true;

done:
    debug_return_bool(rval);
}
示例#16
0
/*
 * Deserialize args, settings and user_info arrays.
 * Fills in struct sudo_user and other common sudoers state.
 */
int
sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group)
{
    struct sudoers_policy_open_info *info = v;
    char * const *cur;
    const char *p, *errstr, *groups = NULL;
    const char *remhost = NULL;
    int flags = 0;
    debug_decl(sudoers_policy_deserialize_info, SUDOERS_DEBUG_PLUGIN)

#define MATCHES(s, v) (strncmp(s, v, sizeof(v) - 1) == 0)

    /* Parse sudo.conf plugin args. */
    if (info->plugin_args != NULL) {
	for (cur = info->plugin_args; *cur != NULL; cur++) {
	    if (MATCHES(*cur, "sudoers_file=")) {
		sudoers_file = *cur + sizeof("sudoers_file=") - 1;
		continue;
	    }
	    if (MATCHES(*cur, "sudoers_uid=")) {
		p = *cur + sizeof("sudoers_uid=") - 1;
		sudoers_uid = (uid_t) sudo_strtoid(p, NULL, NULL, &errstr);
		if (errstr != NULL) {
		    sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		    goto bad;
		}
		continue;
	    }
	    if (MATCHES(*cur, "sudoers_gid=")) {
		p = *cur + sizeof("sudoers_gid=") - 1;
		sudoers_gid = (gid_t) sudo_strtoid(p, NULL, NULL, &errstr);
		if (errstr != NULL) {
		    sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		    goto bad;
		}
		continue;
	    }
	    if (MATCHES(*cur, "sudoers_mode=")) {
		p = *cur + sizeof("sudoers_mode=") - 1;
		sudoers_mode = sudo_strtomode(p, &errstr);
		if (errstr != NULL) {
		    sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		    goto bad;
		}
		continue;
	    }
	    if (MATCHES(*cur, "ldap_conf=")) {
		path_ldap_conf = *cur + sizeof("ldap_conf=") - 1;
		continue;
	    }
	    if (MATCHES(*cur, "ldap_secret=")) {
		path_ldap_secret = *cur + sizeof("ldap_secret=") - 1;
		continue;
	    }
	}
    }

    /* Parse command line settings. */
    user_closefrom = -1;
    for (cur = info->settings; *cur != NULL; cur++) {
	if (MATCHES(*cur, "closefrom=")) {
	    errno = 0;
	    p = *cur + sizeof("closefrom=") - 1;
	    user_closefrom = strtonum(p, 4, INT_MAX, &errstr);
	    if (user_closefrom == 0) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
	if (MATCHES(*cur, "runas_user="******"runas_user="******"runas_group=")) {
	    *runas_group = *cur + sizeof("runas_group=") - 1;
	    sudo_user.flags |= RUNAS_GROUP_SPECIFIED;
	    continue;
	}
	if (MATCHES(*cur, "prompt=")) {
	    user_prompt = *cur + sizeof("prompt=") - 1;
	    def_passprompt_override = true;
	    continue;
	}
	if (MATCHES(*cur, "set_home=")) {
	    if (sudo_strtobool(*cur + sizeof("set_home=") - 1) == true)
		SET(flags, MODE_RESET_HOME);
	    continue;
	}
	if (MATCHES(*cur, "preserve_environment=")) {
	    if (sudo_strtobool(*cur + sizeof("preserve_environment=") - 1) == true)
		SET(flags, MODE_PRESERVE_ENV);
	    continue;
	}
	if (MATCHES(*cur, "run_shell=")) {
	    if (sudo_strtobool(*cur + sizeof("run_shell=") - 1) == true)
		SET(flags, MODE_SHELL);
	    continue;
	}
	if (MATCHES(*cur, "login_shell=")) {
	    if (sudo_strtobool(*cur + sizeof("login_shell=") - 1) == true) {
		SET(flags, MODE_LOGIN_SHELL);
		def_env_reset = true;
	    }
	    continue;
	}
	if (MATCHES(*cur, "implied_shell=")) {
	    if (sudo_strtobool(*cur + sizeof("implied_shell=") - 1) == true)
		SET(flags, MODE_IMPLIED_SHELL);
	    continue;
	}
	if (MATCHES(*cur, "preserve_groups=")) {
	    if (sudo_strtobool(*cur + sizeof("preserve_groups=") - 1) == true)
		SET(flags, MODE_PRESERVE_GROUPS);
	    continue;
	}
	if (MATCHES(*cur, "ignore_ticket=")) {
	    if (sudo_strtobool(*cur + sizeof("ignore_ticket=") - 1) == true)
		SET(flags, MODE_IGNORE_TICKET);
	    continue;
	}
	if (MATCHES(*cur, "noninteractive=")) {
	    if (sudo_strtobool(*cur + sizeof("noninteractive=") - 1) == true)
		SET(flags, MODE_NONINTERACTIVE);
	    continue;
	}
	if (MATCHES(*cur, "sudoedit=")) {
	    if (sudo_strtobool(*cur + sizeof("sudoedit=") - 1) == true)
		SET(flags, MODE_EDIT);
	    continue;
	}
	if (MATCHES(*cur, "login_class=")) {
	    login_class = *cur + sizeof("login_class=") - 1;
	    def_use_loginclass = true;
	    continue;
	}
#ifdef HAVE_PRIV_SET
	if (MATCHES(*cur, "runas_privs=")) {
	    def_privs = *cur + sizeof("runas_privs=") - 1;
	    continue;
	}
	if (MATCHES(*cur, "runas_limitprivs=")) {
	    def_limitprivs = *cur + sizeof("runas_limitprivs=") - 1;
	    continue;
	}
#endif /* HAVE_PRIV_SET */
#ifdef HAVE_SELINUX
	if (MATCHES(*cur, "selinux_role=")) {
	    user_role = *cur + sizeof("selinux_role=") - 1;
	    continue;
	}
	if (MATCHES(*cur, "selinux_type=")) {
	    user_type = *cur + sizeof("selinux_type=") - 1;
	    continue;
	}
#endif /* HAVE_SELINUX */
#ifdef HAVE_BSD_AUTH_H
	if (MATCHES(*cur, "bsdauth_type=")) {
	    login_style = *cur + sizeof("bsdauth_type=") - 1;
	    continue;
	}
#endif /* HAVE_BSD_AUTH_H */
	if (MATCHES(*cur, "network_addrs=")) {
	    interfaces_string = *cur + sizeof("network_addrs=") - 1;
	    set_interfaces(interfaces_string);
	    continue;
	}
	if (MATCHES(*cur, "max_groups=")) {
	    errno = 0;
	    p = *cur + sizeof("max_groups=") - 1;
	    sudo_user.max_groups = strtonum(p, 1, INT_MAX, &errstr);
	    if (sudo_user.max_groups == 0) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
	if (MATCHES(*cur, "remote_host=")) {
	    remhost = *cur + sizeof("remote_host=") - 1;
	    continue;
	}
    }

    for (cur = info->user_info; *cur != NULL; cur++) {
	if (MATCHES(*cur, "user="******"user="******"uid=")) {
	    p = *cur + sizeof("uid=") - 1;
	    user_uid = (uid_t) sudo_strtoid(p, NULL, NULL, &errstr);
	    if (errstr != NULL) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
	if (MATCHES(*cur, "gid=")) {
	    p = *cur + sizeof("gid=") - 1;
	    user_gid = (gid_t) sudo_strtoid(p, NULL, NULL, &errstr);
	    if (errstr != NULL) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
	if (MATCHES(*cur, "groups=")) {
	    groups = *cur + sizeof("groups=") - 1;
	    continue;
	}
	if (MATCHES(*cur, "cwd=")) {
	    user_cwd = sudo_estrdup(*cur + sizeof("cwd=") - 1);
	    continue;
	}
	if (MATCHES(*cur, "tty=")) {
	    user_tty = user_ttypath = sudo_estrdup(*cur + sizeof("tty=") - 1);
	    if (strncmp(user_tty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
		user_tty += sizeof(_PATH_DEV) - 1;
	    continue;
	}
	if (MATCHES(*cur, "host=")) {
	    user_host = user_shost = sudo_estrdup(*cur + sizeof("host=") - 1);
	    if ((p = strchr(user_host, '.')))
		user_shost = sudo_estrndup(user_host, (size_t)(p - user_host));
	    continue;
	}
	if (MATCHES(*cur, "lines=")) {
	    errno = 0;
	    p = *cur + sizeof("lines=") - 1;
	    sudo_user.lines = strtonum(p, 1, INT_MAX, &errstr);
	    if (sudo_user.lines == 0) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
	if (MATCHES(*cur, "cols=")) {
	    errno = 0;
	    p = *cur + sizeof("cols=") - 1;
	    sudo_user.cols = strtonum(p, 1, INT_MAX, &errstr);
	    if (sudo_user.lines == 0) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
	if (MATCHES(*cur, "sid=")) {
	    p = *cur + sizeof("sid=") - 1;
	    sudo_user.sid = (pid_t) sudo_strtoid(p, NULL, NULL, &errstr);
	    if (errstr != NULL) {
		sudo_warnx(U_("%s: %s"), *cur, U_(errstr));
		goto bad;
	    }
	    continue;
	}
    }
    user_runhost = user_srunhost = sudo_estrdup(remhost ? remhost : user_host);
    if ((p = strchr(user_runhost, '.')))
	user_srunhost = sudo_estrndup(user_runhost, (size_t)(p - user_runhost));
    if (user_cwd == NULL)
	user_cwd = sudo_estrdup("unknown");
    if (user_tty == NULL)
	user_tty = sudo_estrdup("unknown"); /* user_ttypath remains NULL */

    if (groups != NULL && groups[0] != '\0') {
	/* sudo_parse_gids() will print a warning on error. */
	user_ngids = sudo_parse_gids(groups, &user_gid, &user_gids);
	if (user_ngids == -1)
	    goto bad;
    }

    /* Stash initial umask for later use. */
    user_umask = umask(SUDO_UMASK);
    umask(user_umask);

    /* Dump settings and user info (XXX - plugin args) */
    for (cur = info->settings; *cur != NULL; cur++)
	sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s", *cur);
    for (cur = info->user_info; *cur != NULL; cur++)
	sudo_debug_printf(SUDO_DEBUG_INFO, "user_info: %s", *cur);

#undef MATCHES
    debug_return_int(flags);

bad:
    debug_return_int(MODE_ERROR);
}
示例#17
0
文件: prompt.c 项目: aixoss/sudo
/*
 * Expand %h and %u escapes (if present) in the prompt and pass back
 * the dynamically allocated result.
 */
char *
expand_prompt(const char *old_prompt, const char *auth_user)
{
    size_t len, n;
    int subst;
    const char *p;
    char *np, *new_prompt, *endp;
    debug_decl(expand_prompt, SUDOERS_DEBUG_AUTH)

    /* How much space do we need to malloc for the prompt? */
    subst = 0;
    for (p = old_prompt, len = strlen(old_prompt); *p; p++) {
	if (p[0] =='%') {
	    switch (p[1]) {
		case 'h':
		    p++;
		    len += strlen(user_shost) - 2;
		    subst = 1;
		    break;
		case 'H':
		    p++;
		    len += strlen(user_host) - 2;
		    subst = 1;
		    break;
		case 'p':
		    p++;
		    len += strlen(auth_user) - 2;
		    subst = 1;
		    break;
		case 'u':
		    p++;
		    len += strlen(user_name) - 2;
		    subst = 1;
		    break;
		case 'U':
		    p++;
		    len += strlen(runas_pw->pw_name) - 2;
		    subst = 1;
		    break;
		case '%':
		    p++;
		    len--;
		    subst = 1;
		    break;
		default:
		    break;
	    }
	}
    }

    if ((new_prompt = malloc(++len)) == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	debug_return_str(NULL);
    }

    if (subst) {
	endp = new_prompt + len;
	for (p = old_prompt, np = new_prompt; *p; p++) {
	    if (p[0] =='%') {
		switch (p[1]) {
		    case 'h':
			p++;
			n = strlcpy(np, user_shost, np - endp);
			if (n >= (size_t)(np - endp))
			    goto oflow;
			np += n;
			continue;
		    case 'H':
			p++;
			n = strlcpy(np, user_host, np - endp);
			if (n >= (size_t)(np - endp))
			    goto oflow;
			np += n;
			continue;
		    case 'p':
			p++;
			n = strlcpy(np, auth_user, np - endp);
			if (n >= (size_t)(np - endp))
				goto oflow;
			np += n;
			continue;
		    case 'u':
			p++;
			n = strlcpy(np, user_name, np - endp);
			if (n >= (size_t)(np - endp))
			    goto oflow;
			np += n;
			continue;
		    case 'U':
			p++;
			n = strlcpy(np,  runas_pw->pw_name, np - endp);
			if (n >= (size_t)(np - endp))
			    goto oflow;
			np += n;
			continue;
		    case '%':
			/* convert %% -> % */
			p++;
			break;
		    default:
			/* no conversion */
			break;
		}
	    }
	    *np++ = *p;
	    if (np >= endp)
		goto oflow;
	}
	*np = '\0';
    } else {
	/* Nothing to expand. */
	memcpy(new_prompt, old_prompt, len);	/* len includes NUL */
    }

    debug_return_str(new_prompt);

oflow:
    /* We pre-allocate enough space, so this should never happen. */
    free(new_prompt);
    sudo_warnx(U_("internal error, %s overflow"), __func__);
    debug_return_str(NULL);
}
示例#18
0
文件: rfc1938.c 项目: radosroka/sudo
int
sudo_rfc1938_setup(struct passwd *pw, char **promptp, sudo_auth *auth)
{
    char challenge[256];
    size_t challenge_len;
    static char *orig_prompt = NULL, *new_prompt = NULL;
    static size_t op_len, np_size;
    static struct RFC1938 rfc1938;
    debug_decl(sudo_rfc1938_setup, SUDOERS_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)) {
	    sudo_warnx(U_("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 */
    challenge_len = strlen(challenge);
    if (np_size < op_len + challenge_len + 7) {
	char *p = realloc(new_prompt, op_len + challenge_len + 7);
	if (p == NULL) {
	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	    debug_return_int(AUTH_FATAL);
	}
	np_size = op_len + challenge_len + 7;
	new_prompt = p;
    }

    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 ]:", (int)op_len,
	    orig_prompt, challenge);

    *promptp = new_prompt;
    debug_return_int(AUTH_SUCCESS);
}
示例#19
0
文件: solaris.c 项目: aixoss/sudo
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:
		sudo_warnx(U_("resource control limit has been reached"));
		break;
	    case ESRCH:
		sudo_warnx(U_("user \"%s\" is not a member of project \"%s\""),
		    pw->pw_name, proj.pj_name);
		break;
	    case EACCES:
		sudo_warnx(U_("the invoking task is final"));
		break;
	    default:
		sudo_warnx(U_("could not join project \"%s\""), proj.pj_name);
	    }
	case SETPROJ_ERR_POOL:
	    switch (errno) {
	    case EACCES:
		sudo_warnx(U_("no resource pool accepting default bindings "
		    "exists for project \"%s\""), proj.pj_name);
		break;
	    case ESRCH:
		sudo_warnx(U_("specified resource pool does not exist for "
		    "project \"%s\""), proj.pj_name);
		break;
	    default:
		sudo_warnx(U_("could not bind to default resource pool for "
		    "project \"%s\""), proj.pj_name);
	    }
	    break;
	default:
	    if (errval <= 0) {
		sudo_warnx(U_("setproject failed for project \"%s\""), proj.pj_name);
	    } else {
		sudo_warnx(U_("warning, resource control assignment failed for "
		    "project \"%s\""), proj.pj_name);
	    }
	}
    } else {
	sudo_warn("getdefaultproj");
    }
    endprojent();
    debug_return;
}
示例#20
0
文件: selinux.c 项目: radosroka/sudo
/*
 * 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;
    debug_decl(get_exec_context, SUDO_DEBUG_SELINUX)
    
    /* We must have a role, the type is optional (we can use the default). */
    if (!role) {
	sudo_warnx(U_("you must specify a role for type %s"), type);
	errno = EINVAL;
	goto bad;
    }
    if (!type) {
	if (get_default_type(role, &typebuf)) {
	    sudo_warnx(U_("unable to get default type for role %s"), role);
	    errno = EINVAL;
	    goto bad;
	}
	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)) {
	sudo_warn(U_("failed to set new role %s"), role);
	goto bad;
    }
    if (context_type_set(context, type)) {
	sudo_warn(U_("failed to set new type %s"), type);
	goto bad;
    }
      
    /*
     * Convert "context" back into a string and verify it.
     */
    if ((new_context = strdup(context_str(context))) == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	goto bad;
    }
    if (security_check_context(new_context) < 0) {
	sudo_warnx(U_("%s is not a valid context"), new_context);
	errno = EINVAL;
	goto bad;
    }

#ifdef DEBUG
    sudo_warnx("Your new context is %s", new_context);
#endif

    context_free(context);
    debug_return_ptr(new_context);

bad:
    free(typebuf);
    context_free(context);
    freecon(new_context);
    debug_return_ptr(NULL);
}
示例#21
0
文件: securid5.c 项目: millert/sudo
/*
 * securid_verify - Authenticates user and handles ACE responses
 *
 * Arguments in:
 *     pw - struct passwd for username
 *     pass - UNUSED
 *     auth - sudo authentication structure for SecurID handle
 *
 * Results out:
 *     return code - Success on successful authentication, failure on
 *                   incorrect authentication, fatal on errors
 */
int
sudo_securid_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
{
    SDI_HANDLE *sd = (SDI_HANDLE *) auth->data;
    int ret;
    debug_decl(sudo_securid_verify, SUDOERS_DEBUG_AUTH)

    pass = auth_getpass("Enter your PASSCODE: ", SUDO_CONV_PROMPT_ECHO_OFF,
	callback);

    /* Have ACE verify password */
    switch (SD_Check(*sd, pass, pw->pw_name)) {
	case ACM_OK:
		ret = AUTH_SUCESS;
		break;

	case ACE_UNDEFINED_PASSCODE:
		sudo_warnx(U_("invalid passcode length for SecurID"));
		ret = AUTH_FATAL;
		break;

	case ACE_UNDEFINED_USERNAME:
		sudo_warnx(U_("invalid username length for SecurID"));
		ret = AUTH_FATAL;
		break;

	case ACE_ERR_INVALID_HANDLE:
		sudo_warnx(U_("invalid Authentication Handle for SecurID"));
		ret = AUTH_FATAL;
		break;

	case ACM_ACCESS_DENIED:
		ret = AUTH_FAILURE;
		break;

	case ACM_NEXT_CODE_REQUIRED:
                /* Sometimes (when current token close to expire?)
                   ACE challenges for the next token displayed
                   (entered without the PIN) */
		if (pass != NULL) {
		    memset_s(pass, SUDO_PASS_MAX, 0, strlen(pass));
		    free(pass);
		}
        	pass = auth_getpass("\
!!! ATTENTION !!!\n\
Wait for the token code to change, \n\
then enter the new token code.\n", \
		SUDO_CONV_PROMPT_ECHO_OFF, callback);

		if (SD_Next(*sd, pass) == ACM_OK) {
			ret = AUTH_SUCCESS;
			break;
		}

		ret = AUTH_FAILURE;
		break;

	case ACM_NEW_PIN_REQUIRED:
                /*
		 * This user's SecurID has not been activated yet,
                 * or the pin has been reset
		 */
		/* XXX - Is setting up a new PIN within sudo's scope? */
		SD_Pin(*sd, "");
		sudo_printf(SUDO_CONV_ERROR_MSG, 
		    "Your SecurID access has not yet been set up.\n");
		sudo_printf(SUDO_CONV_ERROR_MSG, 
		    "Please set up a PIN before you try to authenticate.\n");
		ret = AUTH_FATAL;
		break;

	default:
		sudo_warnx(U_("unknown SecurID error"));
		ret = AUTH_FATAL;
		break;
    }

    /* Free resources */
    SD_Close(*sd);

    if (pass != NULL) {
	memset_s(pass, SUDO_PASS_MAX, 0, strlen(pass));
	free(pass);
    }

    /* Return stored state to calling process */
    debug_return_int(ret);
}