Exemplo n.º 1
0
/*
 * Search the tree for an alias with the specified name and type.
 * Returns a pointer to the alias structure or NULL if not found.
 * Caller is responsible for calling alias_put() on the returned
 * alias to mark it as unused.
 */
struct alias *
alias_get(char *name, int type)
{
    struct alias key;
    struct rbnode *node;
    struct alias *a = NULL;
    debug_decl(alias_get, SUDOERS_DEBUG_ALIAS)

    key.name = name;
    key.type = type;
    if ((node = rbfind(aliases, &key)) != NULL) {
	/*
	 * Check whether this alias is already in use.
	 * If so, we've detected a loop.  If not, set the flag,
	 * which the caller should clear with a call to alias_put().
	 */
	a = node->data;
	if (a->used) {
	    errno = ELOOP;
	    debug_return_ptr(NULL);
	}
	a->used = true;
    } else {
	errno = ENOENT;
    }
    debug_return_ptr(a);
}
Exemplo n.º 2
0
/*
 * Create a red black tree struct using the specified compare routine.
 * Allocates and returns the initialized (empty) tree or NULL if
 * memory cannot be allocated.
 */
struct rbtree *
rbcreate(int (*compar)(const void *, const void*))
{
    struct rbtree *tree;
    debug_decl(rbcreate, SUDOERS_DEBUG_RBTREE)

    if ((tree = malloc(sizeof(*tree))) == NULL) {
	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
	    "unable to allocate memory");
	debug_return_ptr(NULL);
    }

    tree->compar = compar;

    /*
     * We use a self-referencing sentinel node called nil to simplify the
     * code by avoiding the need to check for NULL pointers.
     */
    tree->nil.left = tree->nil.right = tree->nil.parent = &tree->nil;
    tree->nil.color = black;
    tree->nil.data = NULL;

    /*
     * Similarly, the fake root node keeps us from having to worry
     * about splitting the root.
     */
    tree->root.left = tree->root.right = tree->root.parent = &tree->nil;
    tree->root.color = black;
    tree->root.data = NULL;

    debug_return_ptr(tree);
}
Exemplo n.º 3
0
/*
 * Look for a node matching key in tree.
 * Returns a pointer to the node if found, else NULL.
 */
struct rbnode *
rbfind(struct rbtree *tree, void *key)
{
    struct rbnode *node = rbfirst(tree);
    int res;
    debug_decl(rbfind, SUDO_DEBUG_RBTREE)

    while (node != rbnil(tree)) {
	if ((res = tree->compar(key, node->data)) == 0)
	    debug_return_ptr(node);
	node = res < 0 ? node->left : node->right;
    }
    debug_return_ptr(NULL);
}
Exemplo n.º 4
0
/*
 * Take a uid in string form "#123" and return a faked up passwd struct.
 */
struct passwd *
sudo_fakepwnam(const char *user, gid_t gid)
{
    const char *errstr;
    uid_t uid;
    debug_decl(sudo_fakepwnam, SUDOERS_DEBUG_NSS)

    uid = (uid_t) sudo_strtoid(user + 1, NULL, NULL, &errstr);
    if (errstr != NULL) {
	sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG,
	    "uid %s %s", user, errstr);
	debug_return_ptr(NULL);
    }
    debug_return_ptr(sudo_mkpwent(user, uid, gid, NULL, NULL));
}
/*
 * Get passwd entry for the user we are going to authenticate as.
 * By default, this is the user invoking sudo.  In the most common
 * case, this matches sudo_user.pw or runas_pw.
 */
static struct passwd *
get_authpw(int mode)
{
    struct passwd *pw;
    debug_decl(get_authpw, SUDO_DEBUG_AUTH)

    if (ISSET(mode, (MODE_CHECK|MODE_LIST))) {
	/* In list mode we always prompt for the user's password. */
	sudo_pw_addref(sudo_user.pw);
	pw = sudo_user.pw;
    } else {
	if (def_rootpw) {
	    if ((pw = sudo_getpwuid(ROOT_UID)) == NULL)
		log_fatal(0, N_("unknown uid: %u"), ROOT_UID);
	} else if (def_runaspw) {
	    if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
		log_fatal(0, N_("unknown user: %s"), def_runas_default);
	} else if (def_targetpw) {
	    if (runas_pw->pw_name == NULL)
		log_fatal(NO_MAIL|MSG_ONLY, N_("unknown uid: %u"),
		    (unsigned int) runas_pw->pw_uid);
	    sudo_pw_addref(runas_pw);
	    pw = runas_pw;
	} else {
	    sudo_pw_addref(sudo_user.pw);
	    pw = sudo_user.pw;
	}
    }

    debug_return_ptr(pw);
}
Exemplo n.º 6
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, SUDO_DEBUG_NSS)

    key.k.uid = uid;
    if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
	item = (struct cache_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 = ecalloc(1, sizeof(*item));
	item->refcnt = 1;
	item->k.uid = uid;
	/* item->d.pw = NULL; */
    }
    if (rbinsert(pwcache_byuid, item) != NULL)
	fatalx(_("unable to cache uid %u, already exists"),
	    (unsigned int) uid);
#ifdef HAVE_SETAUTHDB
    aix_restoreauthdb();
#endif
done:
    item->refcnt++;
    debug_return_ptr(item->d.pw);
}
Exemplo n.º 7
0
/*
 * Create a red black tree struct using the specified compare routine.
 * Allocates and returns the initialized (empty) tree.
 */
struct rbtree *
rbcreate(int (*compar)(const void *, const void*))
{
    struct rbtree *tree;
    debug_decl(rbcreate, SUDO_DEBUG_RBTREE)

    tree = (struct rbtree *) emalloc(sizeof(*tree));
    tree->compar = compar;

    /*
     * We use a self-referencing sentinel node called nil to simplify the
     * code by avoiding the need to check for NULL pointers.
     */
    tree->nil.left = tree->nil.right = tree->nil.parent = &tree->nil;
    tree->nil.color = black;
    tree->nil.data = NULL;

    /*
     * Similarly, the fake root node keeps us from having to worry
     * about splitting the root.
     */
    tree->root.left = tree->root.right = tree->root.parent = &tree->nil;
    tree->root.color = black;
    tree->root.data = NULL;

    debug_return_ptr(tree);
}
Exemplo n.º 8
0
/*
 * Take a user, uid and gid and return a faked up passwd struct.
 */
struct passwd *
sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)
{
    struct cache_item_pw *pwitem;
    struct passwd *pw;
    struct rbnode *node;
    size_t len, namelen;
    int i;
    debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS)

    namelen = strlen(user);
    len = sizeof(*pwitem) + namelen + 1 /* pw_name */ +
	sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
	sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);

    for (i = 0; i < 2; i++) {
	pwitem = ecalloc(1, len);
	pw = &pwitem->pw;
	pw->pw_uid = uid;
	pw->pw_gid = gid;
	pw->pw_name = (char *)(pwitem + 1);
	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));

	pwitem->cache.refcnt = 1;
	pwitem->cache.d.pw = pw;
	if (i == 0) {
	    /* Store by uid, overwriting cached version. */
	    pwitem->cache.k.uid = pw->pw_uid;
	    if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
		sudo_pw_delref_item(node->data);
		node->data = &pwitem->cache;
	    }
	} else {
	    /* Store by name, overwriting cached version. */
	    pwitem->cache.k.name = pw->pw_name;
	    if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
		sudo_pw_delref_item(node->data);
		node->data = &pwitem->cache;
	    }
	}
    }
    pwitem->cache.refcnt++;
    debug_return_ptr(pw);
}
Exemplo n.º 9
0
/*
 * Find the named alias, remove it from the tree and return it.
 */
struct alias *
alias_remove(char *name, int type)
{
    struct rbnode *node;
    struct alias key;
    debug_decl(alias_remove, SUDOERS_DEBUG_ALIAS)

    key.name = name;
    key.type = type;
    if ((node = rbfind(aliases, &key)) == NULL) {
	errno = ENOENT;
	return NULL;
    }
    debug_return_ptr(rbdelete(aliases, node));
}
/*
 * Non-nsswitch.conf version with hard-coded order.
 */
struct sudo_nss_list *
sudo_read_nss(void)
{
    static struct sudo_nss_list snl = TAILQ_HEAD_INITIALIZER(snl);
    debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)

#  ifdef HAVE_SSSD
    TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries);
#  endif
#  ifdef HAVE_LDAP
    TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries);
#  endif
    TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);

    debug_return_ptr(&snl);
}
Exemplo n.º 11
0
/*
 * Returns the successor of node, or nil if there is none.
 */
static struct rbnode *
rbsuccessor(struct rbtree *tree, struct rbnode *node)
{
    struct rbnode *succ;
    debug_decl(rbsuccessor, SUDO_DEBUG_RBTREE)

    if ((succ = node->right) != rbnil(tree)) {
	while (succ->left != rbnil(tree))
	    succ = succ->left;
    } else {
	/* No right child, move up until we find it or hit the root */
	for (succ = node->parent; node == succ->right; succ = succ->parent)
	    node = succ;
	if (succ == rbroot(tree))
	    succ = rbnil(tree);
    }
    debug_return_ptr(succ);
}
Exemplo n.º 12
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, SUDO_DEBUG_NSS)

    key.k.name = (char *) name;
    if ((node = rbfind(pwcache_byname, &key)) != NULL) {
	item = (struct cache_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
    if ((key.d.pw = getpwnam(name)) != NULL) {
	item = make_pwitem(key.d.pw, name);
	if (rbinsert(pwcache_byname, item) != NULL)
	    errorx(1, _("unable to cache user %s, already exists"), name);
    } else {
	len = strlen(name) + 1;
	item = 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)
	    errorx(1, _("unable to cache user %s, already exists"), name);
    }
#ifdef HAVE_SETAUTHDB
    aix_restoreauthdb();
#endif
done:
    item->refcnt++;
    debug_return_ptr(item->d.pw);
}
Exemplo n.º 13
0
/*
 * Get passwd entry for the user we are going to authenticate as.
 * By default, this is the user invoking sudo.  In the most common
 * case, this matches sudo_user.pw or runas_pw.
 */
static struct passwd *
get_authpw(int mode)
{
    struct passwd *pw = NULL;
    debug_decl(get_authpw, SUDOERS_DEBUG_AUTH)

    if (ISSET(mode, (MODE_CHECK|MODE_LIST))) {
	/* In list mode we always prompt for the user's password. */
	sudo_pw_addref(sudo_user.pw);
	pw = sudo_user.pw;
    } else {
	if (def_rootpw) {
	    if ((pw = sudo_getpwuid(ROOT_UID)) == NULL) {
		log_warningx(SLOG_SEND_MAIL, N_("unknown uid: %u"), ROOT_UID);
	    }
	} else if (def_runaspw) {
	    if ((pw = sudo_getpwnam(def_runas_default)) == NULL) {
		log_warningx(SLOG_SEND_MAIL,
		    N_("unknown user: %s"), def_runas_default);
	    }
	} else if (def_targetpw) {
	    if (runas_pw->pw_name == NULL) {
		/* This should never be NULL as we fake up the passwd struct */
		log_warningx(SLOG_RAW_MSG, N_("unknown uid: %u"),
		    (unsigned int) runas_pw->pw_uid);
	    } else {
		sudo_pw_addref(runas_pw);
		pw = runas_pw;
	    }
	} else {
	    sudo_pw_addref(sudo_user.pw);
	    pw = sudo_user.pw;
	}
    }

    debug_return_ptr(pw);
}
Exemplo n.º 14
0
/*
 * Parse the delay as seconds and nanoseconds: %lld.%09ld
 * Sudo used to write this as a double, but since timing data is logged
 * in the C locale this may not match the current locale.
 */
char *
parse_delay(const char *cp, struct timespec *delay, const char *decimal_point)
{
    char numbuf[(((sizeof(long long) * 8) + 2) / 3) + 2];
    const char *errstr, *ep;
    long long llval;
    size_t len;
    debug_decl(parse_delay, SUDO_DEBUG_UTIL)

    /* Parse seconds (whole number portion). */
    for (ep = cp; isdigit((unsigned char)*ep); ep++)
	continue;
    len = (size_t)(ep - cp);
    if (len >= sizeof(numbuf)) {
	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
	    "%s: number of seconds is too large", cp);
	debug_return_ptr(NULL);
    }
    memcpy(numbuf, cp, len);
    numbuf[len] = '\0';
    delay->tv_sec = strtonum(numbuf, 0, TIME_T_MAX, &errstr);
    if (errstr != NULL) {
	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
	    "%s: number of seconds is %s", numbuf, errstr);
	debug_return_ptr(NULL);
    }

    /* Radix may be in user's locale for sudo < 1.7.4 so accept that too. */
    if (*ep != '.' && *ep != *decimal_point) {
	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
	    "invalid characters after seconds: %s", ep);
	debug_return_ptr(NULL);
    }
    cp = ep + 1;

    /* Parse fractional part, we may read more precision than we can store. */
    for (ep = cp; isdigit((unsigned char)*ep); ep++)
	continue;
    len = (size_t)(ep - cp);
    if (len >= sizeof(numbuf)) {
	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
	    "%s: number of nanoseconds is too large", cp);
	debug_return_ptr(NULL);
    }
    memcpy(numbuf, cp, len);
    numbuf[len] = '\0';
    llval = strtonum(numbuf, 0, LLONG_MAX, &errstr);
    if (errstr != NULL) {
	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
	    "%s: number of nanoseconds is %s", numbuf, errstr);
	debug_return_ptr(NULL);
    }

    /* Adjust fractional part to nanosecond precision. */
    if (len < 9) {
	/* Convert to nanosecond precision. */
	do {
	    llval *= 10;
	} while (++len < 9);
    } else if (len > 9) {
	/* Clamp to nanoseconds. */
	do {
	    llval /= 10;
	} while (--len > 9);
    }
    delay->tv_nsec = (long)llval;

    /* Advance to the next field. */
    while (isspace((unsigned char)*ep))
	ep++;

    debug_return_str((char *)ep);
}
Exemplo n.º 15
0
/*
 * Take a user, uid, gid, home and shell and return a faked up passwd struct.
 * If home or shell are NULL default values will be used.
 */
struct passwd *
sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home,
    const char *shell)
{
    struct cache_item_pw *pwitem;
    struct passwd *pw;
    struct rbnode *node;
    size_t len, name_len, home_len, shell_len;
    int i;
    debug_decl(sudo_mkpwent, SUDO_DEBUG_NSS)

    /* Optional arguments. */
    if (home == NULL)
	home = "/";
    if (shell == NULL)
	shell = _PATH_BSHELL;

    name_len = strlen(user);
    home_len = strlen(home);
    shell_len = strlen(shell);
    len = sizeof(*pwitem) + name_len + 1 /* pw_name */ +
	sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
	home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */;

    for (i = 0; i < 2; i++) {
	pwitem = ecalloc(1, len);
	pw = &pwitem->pw;
	pw->pw_uid = uid;
	pw->pw_gid = gid;
	pw->pw_name = (char *)(pwitem + 1);
	memcpy(pw->pw_name, user, name_len + 1);
	pw->pw_passwd = pw->pw_name + name_len + 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, home, home_len + 1);
	pw->pw_shell = pw->pw_dir + home_len + 1;
	memcpy(pw->pw_shell, shell, shell_len + 1);

	pwitem->cache.refcnt = 1;
	pwitem->cache.d.pw = pw;
	if (i == 0) {
	    /* Store by uid if it doesn't already exist. */
	    pwitem->cache.k.uid = pw->pw_uid;
	    if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
		/* Already exists, free the item we created. */
		efree(pwitem);
		pwitem = (struct cache_item_pw *) node->data;
	    }
	} else {
	    /* Store by name if it doesn't already exist. */
	    pwitem->cache.k.name = pw->pw_name;
	    if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
		/* Already exists, free the item we created. */
		efree(pwitem);
		pwitem = (struct cache_item_pw *) node->data;
	    }
	}
    }
    pwitem->cache.refcnt++;
    debug_return_ptr(&pwitem->pw);
}
Exemplo n.º 16
0
/*
 * Insert data pointer into a redblack tree.
 * Returns a NULL pointer on success.  If a node matching "data"
 * already exists, a pointer to the existant node is returned.
 */
struct rbnode *
rbinsert(struct rbtree *tree, void *data)
{
    struct rbnode *node = rbfirst(tree);
    struct rbnode *parent = rbroot(tree);
    int res;
    debug_decl(rbinsert, SUDO_DEBUG_RBTREE)

    /* Find correct insertion point. */
    while (node != rbnil(tree)) {
	parent = node;
	if ((res = tree->compar(data, node->data)) == 0)
	    debug_return_ptr(node);
	node = res < 0 ? node->left : node->right;
    }

    node = (struct rbnode *) emalloc(sizeof(*node));
    node->data = data;
    node->left = node->right = rbnil(tree);
    node->parent = parent;
    if (parent == rbroot(tree) || tree->compar(data, parent->data) < 0)
	parent->left = node;
    else
	parent->right = node;
    node->color = red;

    /*
     * If the parent node is black we are all set, if it is red we have
     * the following possible cases to deal with.  We iterate through
     * the rest of the tree to make sure none of the required properties
     * is violated.
     *
     *	1) The uncle is red.  We repaint both the parent and uncle black
     *     and repaint the grandparent node red.
     *
     *  2) The uncle is black and the new node is the right child of its
     *     parent, and the parent in turn is the left child of its parent.
     *     We do a left rotation to switch the roles of the parent and
     *     child, relying on further iterations to fixup the old parent.
     *
     *  3) The uncle is black and the new node is the left child of its
     *     parent, and the parent in turn is the left child of its parent.
     *     We switch the colors of the parent and grandparent and perform
     *     a right rotation around the grandparent.  This makes the former
     *     parent the parent of the new node and the former grandparent.
     *
     * Note that because we use a sentinel for the root node we never
     * need to worry about replacing the root.
     */
    while (node->parent->color == red) {
	struct rbnode *uncle;
	if (node->parent == node->parent->parent->left) {
	    uncle = node->parent->parent->right;
	    if (uncle->color == red) {
		node->parent->color = black;
		uncle->color = black;
		node->parent->parent->color = red;
		node = node->parent->parent;
	    } else /* if (uncle->color == black) */ {
		if (node == node->parent->right) {
		    node = node->parent;
		    rotate_left(tree, node);
		}
		node->parent->color = black;
		node->parent->parent->color = red;
		rotate_right(tree, node->parent->parent);
	    }
	} else { /* if (node->parent == node->parent->parent->right) */
	    uncle = node->parent->parent->left;
	    if (uncle->color == red) {
		node->parent->color = black;
		uncle->color = black;
		node->parent->parent->color = red;
		node = node->parent->parent;
	    } else /* if (uncle->color == black) */ {
		if (node == node->parent->left) {
		    node = node->parent;
		    rotate_right(tree, node);
		}
		node->parent->color = black;
		node->parent->parent->color = red;
		rotate_left(tree, node->parent->parent);
	    }
	}
    }
    rbfirst(tree)->color = black;	/* first node is always black */
    debug_return_ptr(NULL);
}
Exemplo n.º 17
0
/*
 * Dynamically allocate space for a struct item plus the key and data
 * elements.  If name is non-NULL it is used as the key, else the
 * uid is the key.  Fills in datum from struct password.
 */
static struct cache_item *
make_pwitem(const struct passwd *pw, const char *name)
{
    char *cp;
    const char *pw_shell;
    size_t nsize, psize, csize, gsize, dsize, ssize, total;
    struct cache_item_pw *pwitem;
    struct passwd *newpw;
    debug_decl(make_pwitem, SUDO_DEBUG_NSS)

    /* If shell field is empty, expand to _PATH_BSHELL. */
    pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
	? _PATH_BSHELL : pw->pw_shell;

    /* Allocate in one big chunk for easy freeing. */
    nsize = psize = csize = gsize = dsize = ssize = 0;
    total = sizeof(*pwitem);
    FIELD_SIZE(pw, pw_name, nsize);
    FIELD_SIZE(pw, pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
    FIELD_SIZE(pw, pw_class, csize);
#endif
    FIELD_SIZE(pw, pw_gecos, gsize);
    FIELD_SIZE(pw, pw_dir, dsize);
    /* Treat shell specially since we expand "" -> _PATH_BSHELL */
    ssize = strlen(pw_shell) + 1;
    total += ssize;
    if (name != NULL)
	total += strlen(name) + 1;

    /* Allocate space for struct item, struct passwd and the strings. */
    pwitem = ecalloc(1, total);
    newpw = &pwitem->pw;

    /*
     * Copy in passwd contents and make strings relative to space
     * at the end of the struct.
     */
    memcpy(newpw, pw, sizeof(*pw));
    cp = (char *)(pwitem + 1);
    FIELD_COPY(pw, newpw, pw_name, nsize);
    FIELD_COPY(pw, newpw, pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
    FIELD_COPY(pw, newpw, pw_class, csize);
#endif
    FIELD_COPY(pw, newpw, pw_gecos, gsize);
    FIELD_COPY(pw, newpw, pw_dir, dsize);
    /* Treat shell specially since we expand "" -> _PATH_BSHELL */
    memcpy(cp, pw_shell, ssize);
    newpw->pw_shell = cp;
    cp += ssize;

    /* Set key and datum. */
    if (name != NULL) {
	memcpy(cp, name, strlen(name) + 1);
	pwitem->cache.k.name = cp;
    } else {
	pwitem->cache.k.uid = pw->pw_uid;
    }
    pwitem->cache.d.pw = newpw;
    pwitem->cache.refcnt = 1;

    debug_return_ptr(&pwitem->cache);
}
Exemplo n.º 18
0
/*
 * Take a user, uid, gid, home and shell and return a faked up passwd struct.
 * If home or shell are NULL default values will be used.
 */
struct passwd *
sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home,
    const char *shell)
{
    struct cache_item_pw *pwitem;
    struct cache_item *item;
    struct passwd *pw;
    size_t len, name_len, home_len, shell_len;
    int i;
    debug_decl(sudo_mkpwent, SUDOERS_DEBUG_NSS)

    /* Optional arguments. */
    if (home == NULL)
	home = "/";
    if (shell == NULL)
	shell = _PATH_BSHELL;

    name_len = strlen(user);
    home_len = strlen(home);
    shell_len = strlen(shell);
    len = sizeof(*pwitem) + name_len + 1 /* pw_name */ +
	sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
	home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */;

    for (i = 0; i < 2; i++) {
	struct rbtree *pwcache;
	struct rbnode *node;

	pwitem = sudo_ecalloc(1, len);
	pw = &pwitem->pw;
	pw->pw_uid = uid;
	pw->pw_gid = gid;
	pw->pw_name = (char *)(pwitem + 1);
	memcpy(pw->pw_name, user, name_len + 1);
	pw->pw_passwd = pw->pw_name + name_len + 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, home, home_len + 1);
	pw->pw_shell = pw->pw_dir + home_len + 1;
	memcpy(pw->pw_shell, shell, shell_len + 1);

	item = &pwitem->cache;
	item->refcnt = 1;
	item->d.pw = pw;
	if (i == 0) {
	    /* Store by uid if it doesn't already exist. */
	    item->k.uid = pw->pw_uid;
	    pwcache = pwcache_byuid;
	} else {
	    /* Store by name if it doesn't already exist. */
	    item->k.name = pw->pw_name;
	    pwcache = pwcache_byname;
	}
	if ((node = rbinsert(pwcache, item)) != NULL) {
	    /* Already exists. */
	    item = node->data;
	    if (item->d.pw == NULL) {
		/* Negative cache entry, replace with ours. */
		sudo_pw_delref_item(item);
		item = node->data = &pwitem->cache;
	    } else {
		/* Good entry, discard our fake one. */
		sudo_efree(pwitem);
	    }
	}
    }
    item->refcnt++;
    debug_return_ptr(item->d.pw);
}
/*
 * Read in /etc/netsvc.conf (like nsswitch.conf on AIX)
 * Returns a tail queue of matches.
 */
struct sudo_nss_list *
sudo_read_nss(void)
{
    FILE *fp;
    char *cp, *ep, *line = NULL;
    size_t linesize = 0;
#ifdef HAVE_SSSD
    bool saw_sss = false;
#endif
    bool saw_files = false;
    bool saw_ldap = false;
    bool got_match = false;
    static struct sudo_nss_list snl = TAILQ_HEAD_INITIALIZER(snl);
    debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)

    if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL)
	goto nomatch;

    while (sudo_parseln(&line, &linesize, NULL, fp) != -1) {
	/* Skip blank or comment lines */
	if (*(cp = line) == '\0')
	    continue;

	/* Look for a line starting with "sudoers = " */
	if (strncasecmp(cp, "sudoers", 7) != 0)
	    continue;
	cp += 7;
	while (isspace((unsigned char)*cp))
	    cp++;
	if (*cp++ != '=')
	    continue;

	/* Parse line */
	for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
	    /* Trim leading whitespace. */
	    while (isspace((unsigned char)*cp))
		cp++;

	    if (!saw_files && strncasecmp(cp, "files", 5) == 0 &&
		(isspace((unsigned char)cp[5]) || cp[5] == '\0')) {
		TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
		got_match = true;
		ep = &cp[5];
#ifdef HAVE_LDAP
	    } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 &&
		(isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
		TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries);
		got_match = true;
		ep = &cp[4];
#endif
#ifdef HAVE_SSSD
	    } else if (!saw_sss && strncasecmp(cp, "sss", 3) == 0 &&
		(isspace((unsigned char)cp[3]) || cp[3] == '\0')) {
		TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries);
		got_match = true;
		ep = &cp[3];
#endif
	    } else {
		got_match = false;
	    }

	    /* check for = auth qualifier */
	    if (got_match && *ep) {
		cp = ep;
		while (isspace((unsigned char)*cp) || *cp == '=')
		    cp++;
		if (strncasecmp(cp, "auth", 4) == 0 &&
		    (isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
		    TAILQ_LAST(&snl, sudo_nss_list)->ret_if_found = true;
		}
	    }
	}
	/* Only parse the first "sudoers" line */
	break;
    }
    fclose(fp);

nomatch:
    /* Default to files only if no matches */
    if (TAILQ_EMPTY(&snl))
	TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);

    debug_return_ptr(&snl);
}
Exemplo n.º 20
0
struct log_info *
parse_logfile(const char *logfile)
{
    FILE *fp;
    char *buf = NULL, *cp, *ep;
    const char *errstr;
    size_t bufsize = 0, cwdsize = 0, cmdsize = 0;
    struct log_info *li = NULL;
    debug_decl(parse_logfile, SUDO_DEBUG_UTIL)

    fp = fopen(logfile, "r");
    if (fp == NULL) {
	sudo_warn(U_("unable to open %s"), logfile);
	goto bad;
    }

    /*
     * ID file has three lines:
     *  1) a log info line
     *  2) cwd
     *  3) command with args
     */
    if ((li = calloc(1, sizeof(*li))) == NULL)
	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    if (getdelim(&buf, &bufsize, '\n', fp) == -1 ||
	getdelim(&li->cwd, &cwdsize, '\n', fp) == -1 ||
	getdelim(&li->cmd, &cmdsize, '\n', fp) == -1) {
	sudo_warn(U_("%s: invalid log file"), logfile);
	goto bad;
    }

    /* Strip the newline from the cwd and command. */
    li->cwd[strcspn(li->cwd, "\n")] = '\0';
    li->cmd[strcspn(li->cmd, "\n")] = '\0';

    /*
     * Crack the log line (rows and cols not present in old versions).
     *	timestamp:user:runas_user:runas_group:tty:rows:cols
     * XXX - probably better to use strtok and switch on the state.
     */
    buf[strcspn(buf, "\n")] = '\0';
    cp = buf;

    /* timestamp */
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: time stamp field is missing"), logfile);
	goto bad;
    }
    *ep = '\0';
    li->tstamp = strtonum(cp, 0, TIME_T_MAX, &errstr);
    if (errstr != NULL) {
	sudo_warn(U_("%s: time stamp %s: %s"), logfile, cp, errstr);
	goto bad;
    }

    /* user */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: user field is missing"), logfile);
	goto bad;
    }
    if ((li->user = strndup(cp, (size_t)(ep - cp))) == NULL)
	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));

    /* runas user */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: runas user field is missing"), logfile);
	goto bad;
    }
    if ((li->runas_user = strndup(cp, (size_t)(ep - cp))) == NULL)
	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));

    /* runas group */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: runas group field is missing"), logfile);
	goto bad;
    }
    if (cp != ep) {
	if ((li->runas_group = strndup(cp, (size_t)(ep - cp))) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    }

    /* tty, followed by optional rows + columns */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	/* just the tty */
	if ((li->tty = strdup(cp)) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    } else {
	/* tty followed by rows + columns */
	if ((li->tty = strndup(cp, (size_t)(ep - cp))) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	cp = ep + 1;
	/* need to NULL out separator to use strtonum() */
	if ((ep = strchr(cp, ':')) != NULL) {
	    *ep = '\0';
	}
	li->rows = strtonum(cp, 1, INT_MAX, &errstr);
	if (errstr != NULL) {
	    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
		"%s: tty rows %s: %s", logfile, cp, errstr);
	}
	if (ep != NULL) {
	    cp = ep + 1;
	    li->cols = strtonum(cp, 1, INT_MAX, &errstr);
	    if (errstr != NULL) {
		sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
		    "%s: tty cols %s: %s", logfile, cp, errstr);
	    }
	}
    }
    fclose(fp);
    free(buf);
    debug_return_ptr(li);

bad:
    if (fp != NULL)
	fclose(fp);
    free(buf);
    free_log_info(li);
    debug_return_ptr(NULL);
}
/*
 * Read in /etc/nsswitch.conf
 * Returns a tail queue of matches.
 */
struct sudo_nss_list *
sudo_read_nss(void)
{
    FILE *fp;
    char *cp, *line = NULL;
    size_t linesize = 0;
#ifdef HAVE_SSSD
    bool saw_sss = false;
#endif
    bool saw_files = false;
    bool saw_ldap = false;
    bool got_match = false;
    static struct sudo_nss_list snl = TAILQ_HEAD_INITIALIZER(snl);
    debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)

    if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL)
	goto nomatch;

    while (sudo_parseln(&line, &linesize, NULL, fp) != -1) {
	/* Skip blank or comment lines */
	if (*line == '\0')
	    continue;

	/* Look for a line starting with "sudoers:" */
	if (strncasecmp(line, "sudoers:", 8) != 0)
	    continue;

	/* Parse line */
	for ((cp = strtok(line + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) {
	    if (strcasecmp(cp, "files") == 0 && !saw_files) {
		TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
		got_match = true;
#ifdef HAVE_LDAP
	    } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) {
		TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries);
		got_match = true;
#endif
#ifdef HAVE_SSSD
	    } else if (strcasecmp(cp, "sss") == 0 && !saw_sss) {
		TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries);
		got_match = true;
#endif
	    } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) {
		/* NOTFOUND affects the most recent entry */
		TAILQ_LAST(&snl, sudo_nss_list)->ret_if_notfound = true;
		got_match = false;
	    } else if (strcasecmp(cp, "[SUCCESS=return]") == 0 && got_match) {
		/* SUCCESS affects the most recent entry */
		TAILQ_LAST(&snl, sudo_nss_list)->ret_if_found = true;
		got_match = false;
	    } else
		got_match = false;
	}
	/* Only parse the first "sudoers:" line */
	break;
    }
    free(line);
    fclose(fp);

nomatch:
    /* Default to files only if no matches */
    if (TAILQ_EMPTY(&snl))
	TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);

    debug_return_ptr(&snl);
}
Exemplo n.º 22
0
/*
 * 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);
}