Imported Upstream version 1.8.6p8
[debian/sudo] / plugins / sudoers / sudoers.c
index 9002712bc158a95ea56908278a19b7c8bee6b0eb..0399acd072fdf5da40202c4bdb2647f4f31b9f5b 100644 (file)
@@ -145,6 +145,7 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
     volatile int sources = 0;
     sigaction_t sa;
     struct sudo_nss *nss;
+    struct sudo_nss *nss_next;
     debug_decl(sudoers_policy_open, SUDO_DEBUG_PLUGIN)
 
     sudo_version = version;
@@ -201,12 +202,15 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
     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, _("problem with defaults entries"));
-       }
+    for (nss = snl->first; nss != NULL; nss = nss_next) {
+        nss_next = nss->next;
+        if (nss->open(nss) == 0 && nss->parse(nss) == 0) {
+            sources++;
+            if (nss->setdefs(nss) != 0)
+                log_error(NO_STDERR, _("problem with defaults entries"));
+        } else {
+            tq_remove(snl, nss);
+        }
     }
     if (sources == 0) {
        warningx(_("no valid sudoers sources found, quitting"));
@@ -268,12 +272,20 @@ sudoers_policy_close(int exit_status, int error_code)
        (void)sudo_auth_end_session(runas_pw);
 
     /* Free remaining references to password and group entries. */
-    pw_delref(sudo_user.pw);
-    pw_delref(runas_pw);
-    if (runas_gr != NULL)
-       gr_delref(runas_gr);
-    if (user_group_list != NULL)
-       grlist_delref(user_group_list);
+    sudo_pw_delref(sudo_user.pw);
+    sudo_user.pw = NULL;
+    sudo_pw_delref(runas_pw);
+    runas_pw = NULL;
+    if (runas_gr != NULL) {
+       sudo_gr_delref(runas_gr);
+       runas_gr = NULL;
+    }
+    if (user_group_list != NULL) {
+       sudo_grlist_delref(user_group_list);
+       user_group_list = NULL;
+    }
+    efree(user_gids);
+    user_gids = NULL;
 
     debug_return;
 }
@@ -281,6 +293,7 @@ sudoers_policy_close(int exit_status, int error_code)
 /*
  * The init_session function is called before executing the command
  * and before uid/gid changes occur.
+ * Returns 1 on success, 0 on failure and -1 on error.
  */
 static int
 sudoers_policy_init_session(struct passwd *pwd, char **user_env[])
@@ -293,7 +306,7 @@ sudoers_policy_init_session(struct passwd *pwd, char **user_env[])
 
     if (sigsetjmp(error_jmp, 1)) {
        /* called via error(), errorx() or log_fatal() */
-       return -1;
+       debug_return_bool(-1);
     }
 
     debug_return_bool(sudo_auth_begin_session(pwd, user_env));
@@ -363,10 +376,6 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
 
     /* Find command in path */
     cmnd_status = set_cmnd();
-    if (cmnd_status == -1) {
-       rval = -1;
-       goto done;
-    }
 
 #ifdef HAVE_SETLOCALE
     if (!setlocale(LC_ALL, def_sudoers_locale)) {
@@ -384,7 +393,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
        validated = nss->lookup(nss, validated, pwflag);
 
        if (ISSET(validated, VALIDATE_OK)) {
-           /* Handle "= auth" in netsvc.conf */
+           /* Handle [SUCCESS=return] */
            if (nss->ret_if_found)
                break;
        } else {
@@ -417,7 +426,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
            pw = sudo_getpwnam(def_timestampowner);
        if (pw != NULL) {
            timestamp_uid = pw->pw_uid;
-           pw_delref(pw);
+           sudo_pw_delref(pw);
        } else {
            log_error(0, _("timestamp owner (%s): No such user"),
                def_timestampowner);
@@ -455,8 +464,11 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
 
     /* Require a password if sudoers says so.  */
     rval = check_user(validated, sudo_mode);
-    if (rval != true)
+    if (rval != true) {
+       if (!ISSET(validated, VALIDATE_OK))
+           log_denial(validated, false);
        goto done;
+    }
 
     /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
     /* XXX - causes confusion when root is not listed in sudoers */
@@ -466,7 +478,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
 
            if ((pw = sudo_getpwnam(prev_user)) != NULL) {
                    if (sudo_user.pw != NULL)
-                       pw_delref(sudo_user.pw);
+                       sudo_pw_delref(sudo_user.pw);
                    sudo_user.pw = pw;
            }
        }
@@ -474,30 +486,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
 
     /* If the user was not allowed to run the command we are done. */
     if (!ISSET(validated, VALIDATE_OK)) {
-       if (ISSET(validated, FLAG_NO_USER | FLAG_NO_HOST)) {
-           audit_failure(NewArgv, _("No user or host"));
-           log_denial(validated, 1);
-       } else {
-           if (def_path_info) {
-               /*
-                * 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.
-                */
-               log_denial(validated,
-                   !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
-               if (cmnd_status == NOT_FOUND)
-                   warningx(_("%s: command not found"), user_cmnd);
-               else if (cmnd_status == NOT_FOUND_DOT)
-                   warningx(_("ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run."), user_cmnd, user_cmnd, user_cmnd);
-           } else {
-               /* Just tell the user they are not allowed to run foo. */
-               log_denial(validated, 1);
-           }
-           audit_failure(NewArgv, _("validation failure"));
-       }
+       log_failure(validated, cmnd_status);
        goto bad;
     }
 
@@ -664,7 +653,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
        gid_t egid;
        size_t glsize;
        char *cp, *gid_list;
-       struct group_list *grlist = get_group_list(runas_pw);
+       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 +
@@ -678,19 +667,19 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
            (unsigned int)runas_pw->pw_gid;
        len = snprintf(cp, glsize - (cp - gid_list), "%u", egid);
        if (len < 0 || len >= glsize - (cp - gid_list))
-           errorx(1, _("internal error, runas_groups overflow"));
+           errorx(1, _("internal error, %s overflow"), "runas_groups");
        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 || len >= glsize - (cp - gid_list))
-                   errorx(1, _("internal error, runas_groups overflow"));
+                   errorx(1, _("internal error, %s overflow"), "runas_groups");
                cp += len;
            }
        }
        command_info[info_len++] = gid_list;
-       grlist_delref(grlist);
+       sudo_grlist_delref(grlist);
     }
     if (def_closefrom >= 0)
        easprintf(&command_info[info_len++], "closefrom=%d", def_closefrom);
@@ -712,6 +701,12 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
     if (user_type != NULL)
        command_info[info_len++] = fmt_string("selinux_type", user_type);
 #endif /* HAVE_SELINUX */
+#ifdef HAVE_PRIV_SET
+    if (runas_privs != NULL)
+       command_info[info_len++] = fmt_string("runas_privs", runas_privs);
+    if (runas_limitprivs != NULL)
+       command_info[info_len++] = fmt_string("runas_limitprivs", runas_limitprivs);
+#endif /* HAVE_SELINUX */
 
     /* Must audit before uid change. */
     audit_success(NewArgv);
@@ -800,7 +795,7 @@ sudoers_policy_list(int argc, char * const argv[], int verbose,
     }
     rval = sudoers_policy_main(argc, argv, I_LISTPW, NULL, NULL, NULL, NULL);
     if (list_user) {
-       pw_delref(list_pw);
+       sudo_pw_delref(list_pw);
        list_pw = NULL;
     }
 
@@ -865,7 +860,7 @@ init_vars(char * const envp[])
      * Get group list.
      */
     if (user_group_list == NULL)
-       user_group_list = get_group_list(sudo_user.pw);
+       user_group_list = sudo_get_grlist(sudo_user.pw);
 
     /* Set runas callback. */
     sudo_defs_table[I_RUNAS_DEFAULT].callback = cb_runas_default;
@@ -938,7 +933,7 @@ set_cmnd(void)
                for (to = user_args, av = NewArgv + 1; *av; av++) {
                    n = strlcpy(to, *av, size - (to - user_args));
                    if (n >= size - (to - user_args))
-                       errorx(1, _("internal error, set_cmnd() overflow"));
+                       errorx(1, _("internal error, %s overflow"), "set_cmnd()");
                    to += n;
                    *to++ = ' ';
                }
@@ -1077,8 +1072,13 @@ set_loginclass(struct passwd *pw)
 }
 #endif /* HAVE_LOGIN_CAP_H */
 
+#ifndef AI_FQDN
+# define AI_FQDN AI_CANONNAME
+#endif
+
 /*
  * Look up the fully qualified domain name and set user_host and user_shost.
+ * Use AI_FQDN if available since "canonical" is not always the same as fqdn.
  */
 void
 set_fqdn(void)
@@ -1089,7 +1089,7 @@ set_fqdn(void)
 
     zero_bytes(&hint, sizeof(hint));
     hint.ai_family = PF_UNSPEC;
-    hint.ai_flags = AI_CANONNAME;
+    hint.ai_flags = AI_FQDN;
     if (getaddrinfo(user_host, NULL, &hint, &res0) != 0) {
        log_error(MSG_ONLY, _("unable to resolve host %s"), user_host);
     } else {
@@ -1098,11 +1098,11 @@ set_fqdn(void)
        efree(user_host);
        user_host = estrdup(res0->ai_canonname);
        freeaddrinfo(res0);
+       if ((p = strchr(user_host, '.')) != NULL)
+           user_shost = estrndup(user_host, (size_t)(p - user_host));
+       else
+           user_shost = user_host;
     }
-    if ((p = strchr(user_host, '.')) != NULL)
-       user_shost = estrndup(user_host, (size_t)(p - user_host));
-    else
-       user_shost = user_host;
     debug_return;
 }
 
@@ -1116,7 +1116,7 @@ set_runaspw(const char *user)
     debug_decl(set_runaspw, SUDO_DEBUG_PLUGIN)
 
     if (runas_pw != NULL)
-       pw_delref(runas_pw);
+       sudo_pw_delref(runas_pw);
     if (*user == '#') {
        if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL)
            runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0);
@@ -1137,7 +1137,7 @@ set_runasgr(const char *group)
     debug_decl(set_runasgr, SUDO_DEBUG_PLUGIN)
 
     if (runas_gr != NULL)
-       gr_delref(runas_gr);
+       sudo_gr_delref(runas_gr);
     if (*group == '#') {
        if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL)
            runas_gr = sudo_fakegrnam(group);
@@ -1264,10 +1264,12 @@ deserialize_info(char * const args[], char * const settings[], char * const user
        }
        if (MATCHES(*cur, "runas_user=")) {
            runas_user = *cur + sizeof("runas_user=") - 1;
+           sudo_user.flags |= RUNAS_USER_SPECIFIED;
            continue;
        }
        if (MATCHES(*cur, "runas_group=")) {
            runas_group = *cur + sizeof("runas_group=") - 1;
+           sudo_user.flags |= RUNAS_GROUP_SPECIFIED;
            continue;
        }
        if (MATCHES(*cur, "prompt=")) {
@@ -1327,6 +1329,16 @@ deserialize_info(char * const args[], char * const settings[], char * const user
            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;
@@ -1398,6 +1410,10 @@ deserialize_info(char * const args[], char * const settings[], char * const user
            sudo_user.cols = atoi(*cur + sizeof("cols=") - 1);
            continue;
        }
+       if (MATCHES(*cur, "sid=")) {
+           sudo_user.sid = atoi(*cur + sizeof("sid=") - 1);
+           continue;
+       }
     }
     if (user_cwd == NULL)
        user_cwd = "unknown";
@@ -1430,8 +1446,8 @@ deserialize_info(char * const args[], char * const settings[], char * const user
                break;
            cp++; /* skip over comma */
        }
-       set_group_list(user_name, gids, ngids);
-       efree(gids);
+       user_gids = gids;
+       user_ngids = ngids;
     }
 
     /* Setup debugging if indicated. */
@@ -1596,7 +1612,7 @@ sudoers_policy_register_hooks(int version, int (*register_hook)(struct sudo_hook
     register_hook(&hook);
 }
 
-struct policy_plugin sudoers_policy = {
+__dso_public struct policy_plugin sudoers_policy = {
     SUDO_POLICY_PLUGIN,
     SUDO_API_VERSION,
     sudoers_policy_open,