Merge tag 'debian/1.8.5p2-1' into squeeze
[debian/sudo] / plugins / sudoers / sudo_nss.c
diff --git a/plugins/sudoers/sudo_nss.c b/plugins/sudoers/sudo_nss.c
new file mode 100644 (file)
index 0000000..d8ed0f5
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2007-2011 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;
+    bool saw_files = false;
+    bool saw_ldap = false;
+    bool got_match = false;
+    static struct sudo_nss_list snl;
+    debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
+
+    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);
+
+    debug_return_ptr(&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;
+    bool saw_files = false;
+    bool saw_ldap = false;
+    bool got_match = false;
+    static struct sudo_nss_list snl;
+    debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
+
+    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);
+
+    debug_return_ptr(&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;
+    debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
+
+#  ifdef HAVE_LDAP
+    tq_append(&snl, &sudo_nss_ldap);
+#  endif
+    tq_append(&snl, &sudo_nss_file);
+
+    debug_return_ptr(&snl);
+}
+
+# endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */
+
+#endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
+
+static int
+output(const char *buf)
+{
+    struct sudo_conv_message msg;
+    struct sudo_conv_reply repl;
+    debug_decl(output, SUDO_DEBUG_NSS)
+
+    /* 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)
+       debug_return_int(0);
+    debug_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 defs, privs;
+    int count, olen;
+    debug_decl(display_privs, SUDO_DEBUG_NSS)
+
+    lbuf_init(&defs, output, 4, NULL, sudo_user.cols);
+    lbuf_init(&privs, output, 4, NULL, sudo_user.cols);
+
+    /* Display defaults from all sources. */
+    lbuf_append(&defs, _("Matching Defaults entries for %s on this host:\n"),
+       pw->pw_name);
+    count = 0;
+    tq_foreach_fwd(snl, nss) {
+       count += nss->display_defaults(nss, pw, &defs);
+    }
+    if (count)
+       lbuf_append(&defs, "\n\n");
+    else
+       defs.len = 0;
+
+    /* Display Runas and Cmnd-specific defaults from all sources. */
+    olen = defs.len;
+    lbuf_append(&defs, _("Runas and Command-specific defaults for %s:\n"),
+       pw->pw_name);
+    count = 0;
+    tq_foreach_fwd(snl, nss) {
+       count += nss->display_bound_defaults(nss, pw, &defs);
+    }
+    if (count)
+       lbuf_append(&defs, "\n\n");
+    else
+       defs.len = olen;
+
+    /* Display privileges from all sources. */
+    lbuf_append(&privs,
+       _("User %s may run the following commands on this host:\n"),
+       pw->pw_name);
+    count = 0;
+    tq_foreach_fwd(snl, nss) {
+       count += nss->display_privs(nss, pw, &privs);
+    }
+    if (count) {
+       lbuf_print(&defs);
+       lbuf_print(&privs);
+    } else {
+       printf(_("User %s is not allowed to run sudo on %s.\n"), pw->pw_name,
+           user_shost);
+    }
+
+    lbuf_destroy(&defs);
+    lbuf_destroy(&privs);
+
+    debug_return;
+}
+
+/*
+ * Check user_cmnd against sudoers and print the matching entry if the
+ * command is allowed.
+ * Returns true if the command is allowed, else false.
+ */
+bool
+display_cmnd(struct sudo_nss_list *snl, struct passwd *pw)
+{
+    struct sudo_nss *nss;
+    debug_decl(display_cmnd, SUDO_DEBUG_NSS)
+
+    tq_foreach_fwd(snl, nss) {
+       if (nss->display_cmnd(nss, pw) == 0)
+           debug_return_bool(true);
+    }
+    debug_return_bool(false);
+}