Files
sudo/plugins/sudoers/sudo_nss.c
Todd C. Miller 30fe4a067c Set usrinfo for AIX
Set adminstrative domain for the process when looking up user's
    password or group info and when preparing for execve().
Include strings.h even if string.h exists since they may define
    different things.  Fixes warnings on AIX and others.
2010-06-29 13:08:05 -04:00

321 lines
7.7 KiB
C

/*
* Copyright (c) 2007-2010 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include "sudoers.h"
#include "lbuf.h"
extern struct sudo_nss sudo_nss_file;
#ifdef HAVE_LDAP
extern struct sudo_nss sudo_nss_ldap;
#endif
#if defined(HAVE_LDAP) && defined(_PATH_NSSWITCH_CONF)
/*
* Read in /etc/nsswitch.conf
* Returns a tail queue of matches.
*/
struct sudo_nss_list *
sudo_read_nss(void)
{
FILE *fp;
char *cp;
int saw_files = FALSE;
int saw_ldap = FALSE;
int got_match = FALSE;
static struct sudo_nss_list snl;
if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL)
goto nomatch;
while ((cp = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
if (*cp == '\0')
continue;
/* Look for a line starting with "sudoers:" */
if (strncasecmp(cp, "sudoers:", 8) != 0)
continue;
/* Parse line */
for ((cp = strtok(cp + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) {
if (strcasecmp(cp, "files") == 0 && !saw_files) {
tq_append(&snl, &sudo_nss_file);
got_match = TRUE;
} else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) {
tq_append(&snl, &sudo_nss_ldap);
got_match = TRUE;
} else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) {
/* NOTFOUND affects the most recent entry */
tq_last(&snl)->ret_if_notfound = TRUE;
got_match = FALSE;
} else
got_match = FALSE;
}
/* Only parse the first "sudoers:" line */
break;
}
fclose(fp);
nomatch:
/* Default to files only if no matches */
if (tq_empty(&snl))
tq_append(&snl, &sudo_nss_file);
return(&snl);
}
#else /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
# if defined(HAVE_LDAP) && defined(_PATH_NETSVC_CONF)
/*
* 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;
int saw_files = FALSE;
int saw_ldap = FALSE;
int got_match = FALSE;
static struct sudo_nss_list snl;
if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL)
goto nomatch;
while ((cp = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
if (*cp == '\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')) {
tq_append(&snl, &sudo_nss_file);
got_match = TRUE;
ep = &cp[5];
} else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 &&
(isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
tq_append(&snl, &sudo_nss_ldap);
got_match = TRUE;
ep = &cp[4];
} 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')) {
tq_last(&snl)->ret_if_found = TRUE;
}
}
}
/* Only parse the first "sudoers" line */
break;
}
fclose(fp);
nomatch:
/* Default to files only if no matches */
if (tq_empty(&snl))
tq_append(&snl, &sudo_nss_file);
return(&snl);
}
# else /* !_PATH_NETSVC_CONF && !_PATH_NSSWITCH_CONF */
/*
* Non-nsswitch.conf version with hard-coded order.
*/
struct sudo_nss_list *
sudo_read_nss(void)
{
static struct sudo_nss_list snl;
# ifdef HAVE_LDAP
tq_append(&snl, &sudo_nss_ldap);
# endif
tq_append(&snl, &sudo_nss_file);
return(&snl);
}
# endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */
#endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
/* Reset user_groups based on passwd entry. */
static void
reset_groups(struct passwd *pw)
{
#if defined(HAVE_INITGROUPS) && defined(HAVE_GETGROUPS)
if (pw != sudo_user.pw) {
# ifdef HAVE_SETAUTHDB
aix_setauthdb(pw->pw_name);
# endif
(void) initgroups(pw->pw_name, pw->pw_gid);
efree(user_groups);
user_groups = NULL;
if ((user_ngroups = getgroups(0, NULL)) > 0) {
user_groups = emalloc2(user_ngroups, sizeof(GETGROUPS_T));
if (getgroups(user_ngroups, user_groups) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't get group vector");
}
# ifdef HAVE_SETAUTHDB
aix_restoreauthdb();
# endif
}
#endif
}
static int
output(const char *buf)
{
struct sudo_conv_message msg;
struct sudo_conv_reply repl;
/* Call conversation function */
memset(&msg, 0, sizeof(msg));
msg.msg_type = SUDO_CONV_INFO_MSG;
msg.msg = buf;
memset(&repl, 0, sizeof(repl));
if (sudo_conv(1, &msg, &repl) == -1)
return 0;
return (int)strlen(buf);
}
/*
* Print out privileges for the specified user.
* We only get here if the user is allowed to run something on this host.
*/
void
display_privs(struct sudo_nss_list *snl, struct passwd *pw)
{
struct sudo_nss *nss;
struct lbuf lbuf;
int count;
/* Reset group vector so group matching works correctly. */
reset_groups(pw);
lbuf_init(&lbuf, output, 4, NULL, sudo_user.cols);
/* Display defaults from all sources. */
lbuf_append(&lbuf, "Matching Defaults entries for ", pw->pw_name,
" on this host:\n", NULL);
count = 0;
tq_foreach_fwd(snl, nss) {
count += nss->display_defaults(nss, pw, &lbuf);
}
if (count) {
lbuf_append(&lbuf, "\n\n", NULL);
lbuf_print(&lbuf);
}
/* Display Runas and Cmnd-specific defaults from all sources. */
lbuf.len = 0;
lbuf_append(&lbuf, "Runas and Command-specific defaults for ", pw->pw_name,
":\n", NULL);
count = 0;
tq_foreach_fwd(snl, nss) {
count += nss->display_bound_defaults(nss, pw, &lbuf);
}
if (count) {
lbuf_append(&lbuf, "\n\n", NULL);
lbuf_print(&lbuf);
}
/* Display privileges from all sources. */
lbuf.len = 0;
lbuf_append(&lbuf, "User ", pw->pw_name,
" may run the following commands on this host:\n", NULL);
count = 0;
tq_foreach_fwd(snl, nss) {
count += nss->display_privs(nss, pw, &lbuf);
}
if (count) {
lbuf_print(&lbuf);
}
lbuf_destroy(&lbuf);
}
/*
* Check user_cmnd against sudoers and print the matching entry if the
* command is allowed.
*/
int
display_cmnd(struct sudo_nss_list *snl, struct passwd *pw)
{
struct sudo_nss *nss;
/* Reset group vector so group matching works correctly. */
reset_groups(pw);
tq_foreach_fwd(snl, nss) {
if (nss->display_cmnd(nss, pw) == 0)
return(0);
}
return(1);
}