"Fossies" - the Fresh Open Source Software Archive

Member "sudo-1.9.11p3/plugins/sudoers/check.c" (12 Jun 2022, 10675 Bytes) of package /linux/misc/sudo-1.9.11p3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "check.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.9.10_vs_1.9.11rc1.

    1 /*
    2  * SPDX-License-Identifier: ISC
    3  *
    4  * Copyright (c) 1993-1996,1998-2005, 2007-2018
    5  *  Todd C. Miller <Todd.Miller@sudo.ws>
    6  *
    7  * Permission to use, copy, modify, and distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  *
   19  * Sponsored in part by the Defense Advanced Research Projects
   20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
   21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
   22  */
   23 
   24 /*
   25  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
   26  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
   27  */
   28 
   29 #include <config.h>
   30 
   31 #include <sys/types.h>          /* for ssize_t */
   32 #include <stdio.h>
   33 #include <stdlib.h>
   34 #include <string.h>
   35 #include <unistd.h>
   36 #include <fcntl.h>
   37 #include <time.h>
   38 #include <errno.h>
   39 #include <pwd.h>
   40 #include <grp.h>
   41 
   42 #include "sudoers.h"
   43 #include "check.h"
   44 
   45 struct getpass_closure {
   46     int tstat;
   47     int lectured;
   48     void *cookie;
   49     struct passwd *auth_pw;
   50 };
   51 
   52 static struct passwd *get_authpw(int);
   53 
   54 /*
   55  * Called when getpass is suspended so we can drop the lock.
   56  */
   57 static int
   58 getpass_suspend(int signo, void *vclosure)
   59 {
   60     struct getpass_closure *closure = vclosure;
   61 
   62     timestamp_close(closure->cookie);
   63     closure->cookie = NULL;
   64     return 0;
   65 }
   66 
   67 /*
   68  * Called when getpass is resumed so we can reacquire the lock.
   69  */
   70 static int
   71 getpass_resume(int signo, void *vclosure)
   72 {
   73     struct getpass_closure *closure = vclosure;
   74 
   75     closure->cookie = timestamp_open(user_name, user_sid);
   76     if (closure->cookie == NULL)
   77     return -1;
   78     if (!timestamp_lock(closure->cookie, closure->auth_pw))
   79     return -1;
   80     return 0;
   81 }
   82 
   83 /*
   84  * Returns true if the user successfully authenticates, false if not
   85  * or -1 on fatal error.
   86  */
   87 static int
   88 check_user_interactive(int validated, int mode, struct getpass_closure *closure)
   89 {
   90     struct sudo_conv_callback callback;
   91     int ret = -1;
   92     char *prompt;
   93     debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH);
   94 
   95     /* Construct callback for getpass function. */
   96     memset(&callback, 0, sizeof(callback));
   97     callback.version = SUDO_CONV_CALLBACK_VERSION;
   98     callback.closure = closure;
   99     callback.on_suspend = getpass_suspend;
  100     callback.on_resume = getpass_resume;
  101 
  102     /* Open, lock and read time stamp file if we are using it. */
  103     if (!ISSET(mode, MODE_IGNORE_TICKET)) {
  104     /* Open time stamp file and check its status. */
  105     closure->cookie = timestamp_open(user_name, user_sid);
  106     if (closure->cookie != NULL) {
  107         if (timestamp_lock(closure->cookie, closure->auth_pw)) {
  108         closure->tstat = timestamp_status(closure->cookie,
  109             closure->auth_pw);
  110         }
  111         callback.on_suspend = getpass_suspend;
  112         callback.on_resume = getpass_resume;
  113     }
  114     }
  115 
  116     switch (closure->tstat) {
  117     case TS_FATAL:
  118     /* Fatal error (usually setuid failure), unsafe to proceed. */
  119     goto done;
  120 
  121     case TS_CURRENT:
  122     /* Time stamp file is valid and current. */
  123     if (!ISSET(validated, FLAG_CHECK_USER)) {
  124         ret = true;
  125         break;
  126     }
  127     sudo_debug_printf(SUDO_DEBUG_INFO,
  128         "%s: check user flag overrides time stamp", __func__);
  129     FALLTHROUGH;
  130 
  131     default:
  132     if (ISSET(mode, MODE_NONINTERACTIVE) && !def_noninteractive_auth) {
  133         validated |= FLAG_NO_USER_INPUT;
  134         log_auth_failure(validated, 0);
  135         goto done;
  136     }
  137 
  138     /* Expand any escapes in the prompt. */
  139     prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
  140         closure->auth_pw->pw_name);
  141     if (prompt == NULL)
  142         goto done;
  143 
  144     ret = verify_user(closure->auth_pw, prompt, validated, &callback);
  145     if (ret == true && closure->lectured)
  146         (void)set_lectured();   /* lecture error not fatal */
  147     free(prompt);
  148     break;
  149     }
  150 
  151 done:
  152     debug_return_int(ret);
  153 }
  154 
  155 /*
  156  * Returns true if the user successfully authenticates, false if not
  157  * or -1 on error.
  158  */
  159 int
  160 check_user(int validated, int mode)
  161 {
  162     struct getpass_closure closure = { TS_ERROR };
  163     int ret = -1;
  164     bool exempt = false;
  165     debug_decl(check_user, SUDOERS_DEBUG_AUTH);
  166 
  167     /*
  168      * Init authentication system regardless of whether we need a password.
  169      * Required for proper PAM session support.
  170      */
  171     if ((closure.auth_pw = get_authpw(mode)) == NULL)
  172     goto done;
  173     if (sudo_auth_init(closure.auth_pw, mode) == -1)
  174     goto done;
  175 
  176     /*
  177      * Don't prompt for the root passwd or if the user is exempt.
  178      * If the user is not changing uid/gid, no need for a password.
  179      */
  180     if (!def_authenticate || user_is_exempt()) {
  181     sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %s", __func__,
  182         !def_authenticate ? "authentication disabled" :
  183         "user exempt from authentication");
  184     exempt = true;
  185     ret = true;
  186     goto done;
  187     }
  188     if (user_uid == 0 || (user_uid == runas_pw->pw_uid &&
  189     (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name)))) {
  190 #ifdef HAVE_SELINUX
  191     if (user_role == NULL && user_type == NULL)
  192 #endif
  193 #ifdef HAVE_APPARMOR
  194     if (user_apparmor_profile == NULL)
  195 #endif
  196 #ifdef HAVE_PRIV_SET
  197     if (runas_privs == NULL && runas_limitprivs == NULL)
  198 #endif
  199     {
  200         sudo_debug_printf(SUDO_DEBUG_INFO,
  201         "%s: user running command as self", __func__);
  202         ret = true;
  203         goto done;
  204     }
  205     }
  206 
  207     ret = check_user_interactive(validated, mode, &closure);
  208 
  209 done:
  210     if (ret == true) {
  211     /* The approval function may disallow a user post-authentication. */
  212     ret = sudo_auth_approval(closure.auth_pw, validated, exempt);
  213 
  214     /*
  215      * Only update time stamp if user validated and was approved.
  216      * Failure to update the time stamp is not a fatal error.
  217      */
  218     if (ret == true && closure.tstat != TS_ERROR) {
  219         if (ISSET(validated, VALIDATE_SUCCESS))
  220         (void)timestamp_update(closure.cookie, closure.auth_pw);
  221     }
  222     }
  223     timestamp_close(closure.cookie);
  224     sudo_auth_cleanup(closure.auth_pw, !ISSET(validated, VALIDATE_SUCCESS));
  225     if (closure.auth_pw != NULL)
  226     sudo_pw_delref(closure.auth_pw);
  227 
  228     debug_return_int(ret);
  229 }
  230 
  231 /*
  232  * Display sudo lecture (standard or custom).
  233  * Returns true if the user was lectured, else false.
  234  */
  235 void
  236 display_lecture(struct sudo_conv_callback *callback)
  237 {
  238     struct getpass_closure *closure;
  239     struct sudo_conv_message msg;
  240     struct sudo_conv_reply repl;
  241     char buf[BUFSIZ];
  242     struct stat sb;
  243     ssize_t nread;
  244     int fd;
  245     debug_decl(lecture, SUDOERS_DEBUG_AUTH);
  246 
  247     if (callback == NULL || (closure = callback->closure) == NULL)
  248     debug_return;
  249 
  250     if (closure->lectured)
  251     debug_return;
  252 
  253     if (def_lecture == never || (def_lecture == once && already_lectured()))
  254     debug_return;
  255 
  256     memset(&msg, 0, sizeof(msg));
  257     memset(&repl, 0, sizeof(repl));
  258 
  259     if (def_lecture_file) {
  260     fd = open(def_lecture_file, O_RDONLY|O_NONBLOCK);
  261     if (fd != -1 && fstat(fd, &sb) == 0) {
  262         if (S_ISREG(sb.st_mode)) {
  263         (void) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
  264         while ((nread = read(fd, buf, sizeof(buf) - 1)) > 0) {
  265             buf[nread] = '\0';
  266             msg.msg_type = SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY;
  267             msg.msg = buf;
  268             sudo_conv(1, &msg, &repl, NULL);
  269         }
  270         if (nread == 0) {
  271             close(fd);
  272             goto done;
  273         }
  274         log_warning(SLOG_RAW_MSG,
  275             N_("error reading lecture file %s"), def_lecture_file);
  276         } else {
  277         log_warningx(SLOG_RAW_MSG,
  278             N_("ignoring lecture file %s: not a regular file"),
  279             def_lecture_file);
  280         }
  281     } else {
  282         log_warning(SLOG_RAW_MSG|SLOG_NO_STDERR, N_("unable to open %s"),
  283         def_lecture_file);
  284     }
  285     if (fd != -1)
  286         close(fd);
  287     }
  288 
  289     /* Default sudo lecture. */
  290     msg.msg_type = SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY;
  291     msg.msg = _("\n"
  292     "We trust you have received the usual lecture from the local System\n"
  293     "Administrator. It usually boils down to these three things:\n\n"
  294     "    #1) Respect the privacy of others.\n"
  295     "    #2) Think before you type.\n"
  296     "    #3) With great power comes great responsibility.\n\n");
  297     sudo_conv(1, &msg, &repl, NULL);
  298 
  299 done:
  300     closure->lectured = true;
  301     debug_return;
  302 }
  303 
  304 /*
  305  * Checks if the user is exempt from supplying a password.
  306  */
  307 bool
  308 user_is_exempt(void)
  309 {
  310     bool ret = false;
  311     debug_decl(user_is_exempt, SUDOERS_DEBUG_AUTH);
  312 
  313     if (ISSET(sudo_mode, MODE_POLICY_INTERCEPTED)) {
  314     if (!def_intercept_authenticate)
  315         ret = true;
  316     }
  317     if (def_exempt_group) {
  318     if (user_in_group(sudo_user.pw, def_exempt_group))
  319         ret = true;
  320     }
  321     debug_return_bool(ret);
  322 }
  323 
  324 /*
  325  * Get passwd entry for the user we are going to authenticate as.
  326  * By default, this is the user invoking sudo.  In the most common
  327  * case, this matches sudo_user.pw or runas_pw.
  328  */
  329 static struct passwd *
  330 get_authpw(int mode)
  331 {
  332     struct passwd *pw = NULL;
  333     debug_decl(get_authpw, SUDOERS_DEBUG_AUTH);
  334 
  335     if (ISSET(mode, (MODE_CHECK|MODE_LIST))) {
  336     /* In list mode we always prompt for the user's password. */
  337     sudo_pw_addref(sudo_user.pw);
  338     pw = sudo_user.pw;
  339     } else {
  340     if (def_rootpw) {
  341         if ((pw = sudo_getpwuid(ROOT_UID)) == NULL) {
  342         log_warningx(SLOG_SEND_MAIL, N_("unknown uid %u"), ROOT_UID);
  343         }
  344     } else if (def_runaspw) {
  345         if ((pw = sudo_getpwnam(def_runas_default)) == NULL) {
  346         log_warningx(SLOG_SEND_MAIL,
  347             N_("unknown user %s"), def_runas_default);
  348         }
  349     } else if (def_targetpw) {
  350         if (runas_pw->pw_name == NULL) {
  351         /* This should never be NULL as we fake up the passwd struct */
  352         log_warningx(SLOG_RAW_MSG, N_("unknown uid %u"),
  353             (unsigned int) runas_pw->pw_uid);
  354         } else {
  355         sudo_pw_addref(runas_pw);
  356         pw = runas_pw;
  357         }
  358     } else {
  359         sudo_pw_addref(sudo_user.pw);
  360         pw = sudo_user.pw;
  361     }
  362     }
  363 
  364     debug_return_ptr(pw);
  365 }
  366 
  367 /*
  368  * Returns true if the specified shell is allowed by /etc/shells, else false.
  369  */
  370 bool
  371 check_user_shell(const struct passwd *pw)
  372 {
  373     const char *shell;
  374     debug_decl(check_user_shell, SUDOERS_DEBUG_AUTH);
  375 
  376     if (!def_runas_check_shell)
  377     debug_return_bool(true);
  378 
  379     sudo_debug_printf(SUDO_DEBUG_INFO,
  380     "%s: checking /etc/shells for %s", __func__, pw->pw_shell);
  381 
  382     setusershell();
  383     while ((shell = getusershell()) != NULL) {
  384     if (strcmp(shell, pw->pw_shell) == 0)
  385         debug_return_bool(true);
  386     }
  387     endusershell();
  388 
  389     debug_return_bool(false);
  390 }