(root)/
Linux-PAM-1.5.3/
modules/
pam_usertype/
pam_usertype.c
       1  /******************************************************************************
       2   * Check user type based on login.defs.
       3   *
       4   * Copyright (c) 2020 Red Hat, Inc.
       5   * Written by Pavel Březina <pbrezina@redhat.com>
       6   *
       7   * Redistribution and use in source and binary forms, with or without
       8   * modification, are permitted provided that the following conditions
       9   * are met:
      10   * 1. Redistributions of source code must retain the above copyright
      11   *    notice, and the entire permission notice in its entirety,
      12   *    including the disclaimer of warranties.
      13   * 2. Redistributions in binary form must reproduce the above copyright
      14   *    notice, this list of conditions and the following disclaimer in the
      15   *    documentation and/or other materials provided with the distribution.
      16   * 3. The name of the author may not be used to endorse or promote
      17   *    products derived from this software without specific prior
      18   *    written permission.
      19   *
      20   * ALTERNATIVELY, this product may be distributed under the terms of
      21   * the GNU Public License, in which case the provisions of the GPL are
      22   * required INSTEAD OF the above restrictions.  (This clause is
      23   * necessary due to a potential bad interaction between the GPL and
      24   * the restrictions contained in a BSD-style copyright.)
      25   *
      26   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
      27   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      28   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      29   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
      30   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      31   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      32   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      33   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      34   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      35   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      36   * OF THE POSSIBILITY OF SUCH DAMAGE.
      37   *
      38   */
      39  
      40  #include "config.h"
      41  
      42  #include <sys/types.h>
      43  #include <stdlib.h>
      44  #include <string.h>
      45  #include <syslog.h>
      46  #include <unistd.h>
      47  #include <pwd.h>
      48  #include <ctype.h>
      49  #include <errno.h>
      50  
      51  #include <security/pam_modules.h>
      52  #include <security/pam_modutil.h>
      53  #include <security/pam_ext.h>
      54  
      55  #define LOGIN_DEFS "/etc/login.defs"
      56  
      57  enum pam_usertype_op {
      58      OP_IS_SYSTEM,
      59      OP_IS_REGULAR,
      60  
      61      OP_SENTINEL
      62  };
      63  
      64  struct pam_usertype_opts {
      65      enum pam_usertype_op op;
      66      int use_uid;
      67      int audit;
      68  };
      69  
      70  static int
      71  pam_usertype_parse_args(struct pam_usertype_opts *opts,
      72                          pam_handle_t *pamh,
      73                          int argc,
      74                          const char **argv)
      75  {
      76      int i;
      77  
      78      memset(opts, 0, sizeof(struct pam_usertype_opts));
      79      opts->op = OP_SENTINEL;
      80  
      81      for (i = 0; i < argc; i++) {
      82          if (strcmp(argv[i], "use_uid") == 0) {
      83              opts->use_uid = 1;
      84          } else if (strcmp(argv[i], "audit") == 0) {
      85              opts->audit = 1;
      86          } else if (strcmp(argv[i], "issystem") == 0) {
      87              opts->op = OP_IS_SYSTEM;
      88          } else if (strcmp(argv[i], "isregular") == 0) {
      89              opts->op = OP_IS_REGULAR;
      90          } else {
      91              pam_syslog(pamh, LOG_WARNING, "Unknown argument: %s", argv[i]);
      92              /* Just continue. */
      93          }
      94      }
      95  
      96      if (opts->op == OP_SENTINEL) {
      97          pam_syslog(pamh, LOG_ERR, "Operation not specified");
      98          return PAM_SERVICE_ERR;
      99      }
     100  
     101      return PAM_SUCCESS;
     102  }
     103  
     104  static int
     105  pam_usertype_get_uid(struct pam_usertype_opts *opts,
     106                       pam_handle_t *pamh,
     107                       uid_t *_uid)
     108  {
     109      struct passwd *pwd;
     110      const char *username;
     111      int ret;
     112  
     113      /* Get uid of user that runs the application. */
     114      if (opts->use_uid) {
     115          pwd = pam_modutil_getpwuid(pamh, getuid());
     116          if (pwd == NULL) {
     117              pam_syslog(pamh, LOG_ERR,
     118                         "error retrieving information about user %lu",
     119                         (unsigned long)getuid());
     120              return PAM_USER_UNKNOWN;
     121          }
     122  
     123          *_uid = pwd->pw_uid;
     124          return PAM_SUCCESS;
     125      }
     126  
     127      /* Get uid of user that is being authenticated. */
     128      ret = pam_get_user(pamh, &username, NULL);
     129      if (ret != PAM_SUCCESS) {
     130          pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
     131                     pam_strerror(pamh, ret));
     132          return ret == PAM_CONV_AGAIN ? PAM_INCOMPLETE : ret;
     133      }
     134  
     135      pwd = pam_modutil_getpwnam(pamh, username);
     136      if (pwd == NULL) {
     137          if (opts->audit) {
     138              pam_syslog(pamh, LOG_NOTICE,
     139                         "error retrieving information about user %s", username);
     140          }
     141  
     142          pam_modutil_getpwnam(pamh, "root");
     143  
     144          return PAM_USER_UNKNOWN;
     145      }
     146      pam_modutil_getpwnam(pamh, "pam_usertype_non_existent:");
     147  
     148      *_uid = pwd->pw_uid;
     149  
     150      return PAM_SUCCESS;
     151  }
     152  
     153  #define MAX_UID_VALUE 0xFFFFFFFFUL
     154  
     155  static uid_t
     156  pam_usertype_get_id(pam_handle_t *pamh,
     157                      const char *key,
     158                      uid_t default_value)
     159  {
     160      unsigned long ul;
     161      char *value;
     162      char *ep;
     163      uid_t uid;
     164  
     165      value = pam_modutil_search_key(pamh, LOGIN_DEFS, key);
     166      if (value == NULL) {
     167          return default_value;
     168      }
     169  
     170      /* taken from get_lastlog_uid_max() */
     171      ep = value + strlen(value);
     172      while (ep > value && isspace(*(--ep))) {
     173          *ep = '\0';
     174      }
     175  
     176      errno = 0;
     177      ul = strtoul(value, &ep, 10);
     178      if (!(ul >= MAX_UID_VALUE
     179          || (uid_t)ul >= MAX_UID_VALUE
     180          || (errno != 0 && ul == 0)
     181          || value == ep
     182          || *ep != '\0')) {
     183          uid = (uid_t)ul;
     184      } else {
     185          uid = default_value;
     186      }
     187  
     188      free(value);
     189  
     190      return uid;
     191  }
     192  
     193  static int
     194  pam_usertype_is_system(pam_handle_t *pamh, uid_t uid)
     195  {
     196      uid_t uid_min;
     197      uid_t sys_max;
     198  
     199      if (uid == (uid_t)-1) {
     200          pam_syslog(pamh, LOG_WARNING, "invalid uid");
     201          return PAM_USER_UNKNOWN;
     202      }
     203  
     204      if (uid == PAM_USERTYPE_OVERFLOW_UID) {
     205          /* nobody */
     206          return PAM_SUCCESS;
     207      }
     208  
     209      uid_min = pam_usertype_get_id(pamh, "UID_MIN", PAM_USERTYPE_UIDMIN);
     210      sys_max = pam_usertype_get_id(pamh, "SYS_UID_MAX", uid_min - 1);
     211  
     212      if (uid <= sys_max && uid < uid_min) {
     213          return PAM_SUCCESS;
     214      }
     215  
     216      return PAM_AUTH_ERR;
     217  }
     218  
     219  static int
     220  pam_usertype_is_regular(pam_handle_t *pamh, uid_t uid)
     221  {
     222      int ret;
     223  
     224      ret = pam_usertype_is_system(pamh, uid);
     225      switch (ret) {
     226      case PAM_SUCCESS:
     227          return PAM_AUTH_ERR;
     228      case PAM_USER_UNKNOWN:
     229          return PAM_USER_UNKNOWN;
     230      default:
     231          return PAM_SUCCESS;
     232      }
     233  }
     234  
     235  static int
     236  pam_usertype_evaluate(struct pam_usertype_opts *opts,
     237                        pam_handle_t *pamh,
     238                        uid_t uid)
     239  {
     240      switch (opts->op) {
     241      case OP_IS_SYSTEM:
     242          return pam_usertype_is_system(pamh, uid);
     243      case OP_IS_REGULAR:
     244          return pam_usertype_is_regular(pamh, uid);
     245      default:
     246          pam_syslog(pamh, LOG_ERR, "Unknown operation: %d", opts->op);
     247          return PAM_SERVICE_ERR;
     248      }
     249  }
     250  
     251  /**
     252   * Arguments:
     253   * - issystem: uid less than SYS_UID_MAX
     254   * - isregular: not issystem
     255   * - use_uid: use user that runs application not that is being authenticate (same as in pam_succeed_if)
     256   * - audit: log unknown users to syslog
     257   */
     258  int
     259  pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
     260                      int argc, const char **argv)
     261  {
     262      struct pam_usertype_opts opts;
     263      uid_t uid = -1;
     264      int ret;
     265  
     266      ret = pam_usertype_parse_args(&opts, pamh, argc, argv);
     267      if (ret != PAM_SUCCESS) {
     268          return ret;
     269      }
     270  
     271      ret = pam_usertype_get_uid(&opts, pamh, &uid);
     272      if (ret != PAM_SUCCESS) {
     273          return ret;
     274      }
     275  
     276      return pam_usertype_evaluate(&opts, pamh, uid);
     277  }
     278  
     279  int
     280  pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
     281                 int argc UNUSED, const char **argv UNUSED)
     282  {
     283  	return PAM_IGNORE;
     284  }
     285  
     286  int
     287  pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
     288  {
     289  	return pam_sm_authenticate(pamh, flags, argc, argv);
     290  }
     291  
     292  int
     293  pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
     294  {
     295  	return pam_sm_authenticate(pamh, flags, argc, argv);
     296  }
     297  
     298  int
     299  pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
     300  {
     301  	return pam_sm_authenticate(pamh, flags, argc, argv);
     302  }
     303  
     304  int
     305  pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
     306  {
     307  	return pam_sm_authenticate(pamh, flags, argc, argv);
     308  }