(root)/
Linux-PAM-1.5.3/
modules/
pam_userdb/
pam_userdb.c
       1  /*
       2   * pam_userdb module
       3   *
       4   * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
       5   * See the end of the file for Copyright Information
       6   */
       7  
       8  #include "config.h"
       9  
      10  #include <stdio.h>
      11  #include <stdlib.h>
      12  #include <unistd.h>
      13  #include <string.h>
      14  #include <syslog.h>
      15  #include <stdarg.h>
      16  #include <sys/types.h>
      17  #include <sys/stat.h>
      18  #include <fcntl.h>
      19  #include <errno.h>
      20  #ifdef HAVE_CRYPT_H
      21  #include <crypt.h>
      22  #endif
      23  
      24  #include "pam_userdb.h"
      25  
      26  #ifdef HAVE_NDBM_H
      27  # include <ndbm.h>
      28  #else
      29  # ifdef HAVE_DB_H
      30  #  define DB_DBM_HSEARCH    1 /* use the dbm interface */
      31  #  define HAVE_DBM	      /* for BerkDB 5.0 and later */
      32  #  include <db.h>
      33  # else
      34  #  error "failed to find a libdb or equivalent"
      35  # endif
      36  #endif
      37  
      38  #include <security/pam_modules.h>
      39  #include <security/pam_ext.h>
      40  #include <security/_pam_macros.h>
      41  #include "pam_inline.h"
      42  
      43  /*
      44   * Conversation function to obtain the user's password
      45   */
      46  static int
      47  obtain_authtok(pam_handle_t *pamh)
      48  {
      49      char *resp;
      50      const void *item;
      51      int retval;
      52  
      53      retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &resp, _("Password: "));
      54  
      55      if (retval != PAM_SUCCESS)
      56  	return retval;
      57  
      58      if (resp == NULL)
      59  	return PAM_CONV_ERR;
      60  
      61      /* set the auth token */
      62      retval = pam_set_item(pamh, PAM_AUTHTOK, resp);
      63  
      64      /* clean it up */
      65      pam_overwrite_string(resp);
      66      _pam_drop(resp);
      67  
      68      if ( (retval != PAM_SUCCESS) ||
      69  	 (retval = pam_get_item(pamh, PAM_AUTHTOK, &item))
      70  	 != PAM_SUCCESS ) {
      71  	return retval;
      72      }
      73  
      74      return retval;
      75  }
      76  
      77  static int
      78  _pam_parse (pam_handle_t *pamh, int argc, const char **argv,
      79  	    const char **database, const char **cryptmode)
      80  {
      81    int ctrl;
      82  
      83    *database = NULL;
      84    *cryptmode = NULL;
      85  
      86    /* step through arguments */
      87    for (ctrl = 0; argc-- > 0; ++argv)
      88      {
      89        const char *str;
      90  
      91        /* generic options */
      92  
      93        if (!strcmp(*argv,"debug"))
      94  	ctrl |= PAM_DEBUG_ARG;
      95        else if (!strcasecmp(*argv, "icase"))
      96  	ctrl |= PAM_ICASE_ARG;
      97        else if (!strcasecmp(*argv, "dump"))
      98  	ctrl |= PAM_DUMP_ARG;
      99        else if (!strcasecmp(*argv, "unknown_ok"))
     100  	ctrl |= PAM_UNKNOWN_OK_ARG;
     101        else if (!strcasecmp(*argv, "key_only"))
     102  	ctrl |= PAM_KEY_ONLY_ARG;
     103        else if (!strcasecmp(*argv, "use_first_pass"))
     104  	ctrl |= PAM_USE_FPASS_ARG;
     105        else if (!strcasecmp(*argv, "try_first_pass"))
     106  	ctrl |= PAM_TRY_FPASS_ARG;
     107        else if ((str = pam_str_skip_icase_prefix(*argv, "db=")) != NULL)
     108  	{
     109  	  *database = str;
     110  	  if (**database == '\0') {
     111  	    *database = NULL;
     112  	    pam_syslog(pamh, LOG_ERR,
     113  		       "db= specification missing argument - ignored");
     114  	  }
     115  	}
     116        else if ((str = pam_str_skip_icase_prefix(*argv, "crypt=")) != NULL)
     117  	{
     118  	  *cryptmode = str;
     119  	  if (**cryptmode == '\0')
     120  	    pam_syslog(pamh, LOG_ERR,
     121  		       "crypt= specification missing argument - ignored");
     122  	}
     123        else
     124  	{
     125  	  pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
     126  	}
     127      }
     128  
     129    return ctrl;
     130  }
     131  
     132  
     133  /*
     134   * Looks up a user name in a database and checks the password
     135   *
     136   * return values:
     137   *	 1  = User not found
     138   *	 0  = OK
     139   *	-1  = Password incorrect
     140   *	-2  = System error
     141   */
     142  static int
     143  user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode,
     144  	     const char *user, const char *pass, int ctrl)
     145  {
     146      DBM *dbm;
     147      datum key, data;
     148  
     149      /* Open the DB file. */
     150      dbm = dbm_open(database, O_RDONLY, 0644);
     151      if (dbm == NULL) {
     152  	pam_syslog(pamh, LOG_ERR,
     153  		   "user_lookup: could not open database `%s': %m", database);
     154  	return -2;
     155      }
     156  
     157      /* dump out the database contents for debugging */
     158      if (ctrl & PAM_DUMP_ARG) {
     159  	pam_syslog(pamh, LOG_INFO, "Database dump:");
     160  	for (key = dbm_firstkey(dbm);  key.dptr != NULL;
     161  	     key = dbm_nextkey(dbm)) {
     162  	    data = dbm_fetch(dbm, key);
     163  	    pam_syslog(pamh, LOG_INFO,
     164  		       "key[len=%d] = `%s', data[len=%d] = `%s'",
     165  		       key.dsize, key.dptr, data.dsize, data.dptr);
     166  	}
     167      }
     168  
     169      /* do some more init work */
     170      memset(&key, 0, sizeof(key));
     171      memset(&data, 0, sizeof(data));
     172      if (ctrl & PAM_KEY_ONLY_ARG) {
     173  	if (asprintf(&key.dptr, "%s-%s", user, pass) < 0)
     174  	    key.dptr = NULL;
     175  	else
     176  	    key.dsize = strlen(key.dptr);
     177      } else {
     178          key.dptr = strdup(user);
     179          key.dsize = strlen(user);
     180      }
     181  
     182      if (key.dptr) {
     183  	data = dbm_fetch(dbm, key);
     184  	pam_overwrite_n(key.dptr, key.dsize);
     185  	free(key.dptr);
     186      }
     187  
     188      if (ctrl & PAM_DEBUG_ARG) {
     189  	pam_syslog(pamh, LOG_INFO,
     190  		   "password in database is [%p]`%.*s', len is %d",
     191  		   data.dptr, data.dsize, (char *) data.dptr, data.dsize);
     192      }
     193  
     194      if (data.dptr != NULL) {
     195  	int compare = -2;
     196  
     197  	if (ctrl & PAM_KEY_ONLY_ARG)
     198  	  {
     199  	    dbm_close (dbm);
     200  	    return 0; /* found it, data contents don't matter */
     201  	}
     202  
     203  	if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "crypt") != NULL) {
     204  
     205  	  /* crypt(3) password storage */
     206  
     207  	  char *cryptpw = NULL;
     208  
     209  	  if (data.dsize < 13) {
     210  	    /* hash is too short */
     211  	    pam_syslog(pamh, LOG_INFO, "password hash in database is too short");
     212  	  } else if (ctrl & PAM_ICASE_ARG) {
     213  	    pam_syslog(pamh, LOG_INFO,
     214  	       "case-insensitive comparison only works with plaintext passwords");
     215  	  } else {
     216  	    /* libdb is not guaranteed to produce null terminated strings */
     217  	    char *pwhash = strndup(data.dptr, data.dsize);
     218  
     219  	    if (pwhash == NULL) {
     220  	      pam_syslog(pamh, LOG_CRIT, "strndup failed: data.dptr");
     221  	    } else {
     222  #ifdef HAVE_CRYPT_R
     223  	      struct crypt_data *cdata = NULL;
     224  	      cdata = malloc(sizeof(*cdata));
     225  	      if (cdata == NULL) {
     226  	        pam_syslog(pamh, LOG_CRIT, "malloc failed: struct crypt_data");
     227  	      } else {
     228  	        cdata->initialized = 0;
     229  	        cryptpw = crypt_r(pass, pwhash, cdata);
     230  	      }
     231  #else
     232  	      cryptpw = crypt (pass, pwhash);
     233  #endif
     234  	      if (cryptpw && strlen(cryptpw) == (size_t)data.dsize) {
     235  	        compare = memcmp(data.dptr, cryptpw, data.dsize);
     236  	      } else {
     237  	        if (ctrl & PAM_DEBUG_ARG) {
     238  	          if (cryptpw) {
     239  	            pam_syslog(pamh, LOG_INFO, "lengths of computed and stored hashes differ");
     240  	            pam_syslog(pamh, LOG_INFO, "computed hash: %s", cryptpw);
     241  	          } else {
     242  	            pam_syslog(pamh, LOG_ERR, "crypt() returned NULL");
     243  	          }
     244  	        }
     245  	      }
     246  #ifdef HAVE_CRYPT_R
     247  	      free(cdata);
     248  #endif
     249  	    }
     250  	    pam_overwrite_string(pwhash);
     251  	    free(pwhash);
     252  	  }
     253  
     254  	  pam_overwrite_string(cryptpw);
     255  	} else {
     256  
     257  	  /* Unknown password encryption method -
     258  	   * default to plaintext password storage
     259  	   */
     260  
     261  	  if (strlen(pass) != (size_t)data.dsize) {
     262  	    compare = 1; /* wrong password len -> wrong password */
     263  	  } else if (ctrl & PAM_ICASE_ARG) {
     264  	    compare = strncasecmp(data.dptr, pass, data.dsize);
     265  	  } else {
     266  	    compare = strncmp(data.dptr, pass, data.dsize);
     267  	  }
     268  
     269  	  if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "none") == NULL
     270  		&& (ctrl & PAM_DEBUG_ARG)) {
     271  	    pam_syslog(pamh, LOG_INFO, "invalid value for crypt parameter: %s",
     272  		       cryptmode);
     273  	    pam_syslog(pamh, LOG_INFO, "defaulting to plaintext password mode");
     274  	  }
     275  
     276  	}
     277  
     278  	dbm_close(dbm);
     279  	if (compare == 0)
     280  	    return 0; /* match */
     281  	else
     282  	    return -1; /* wrong */
     283      } else {
     284          int saw_user = 0;
     285  
     286  	if (ctrl & PAM_DEBUG_ARG) {
     287  	    pam_syslog(pamh, LOG_INFO, "error returned by dbm_fetch: %m");
     288  	}
     289  
     290  	/* probably we should check dbm_error() here */
     291  
     292          if ((ctrl & PAM_KEY_ONLY_ARG) == 0) {
     293  	    dbm_close(dbm);
     294              return 1; /* not key_only, so no entry => no entry for the user */
     295          }
     296  
     297          /* now handle the key_only case */
     298          for (key = dbm_firstkey(dbm);
     299               key.dptr != NULL;
     300               key = dbm_nextkey(dbm)) {
     301              int compare;
     302              /* first compare the user portion (case sensitive) */
     303              compare = strncmp(key.dptr, user, strlen(user));
     304              if (compare == 0) {
     305                  /* assume failure */
     306                  compare = -1;
     307                  /* if we have the divider where we expect it to be... */
     308                  if (key.dptr[strlen(user)] == '-') {
     309  		    saw_user = 1;
     310  		    if ((size_t)key.dsize == strlen(user) + 1 + strlen(pass)) {
     311  		        if (ctrl & PAM_ICASE_ARG) {
     312  			    /* compare the password portion (case insensitive)*/
     313                              compare = strncasecmp(key.dptr + strlen(user) + 1,
     314                                                    pass,
     315                                                    strlen(pass));
     316  		        } else {
     317                              /* compare the password portion (case sensitive) */
     318                              compare = strncmp(key.dptr + strlen(user) + 1,
     319                                                pass,
     320                                                strlen(pass));
     321  		        }
     322  		    }
     323                  }
     324                  if (compare == 0) {
     325                      dbm_close(dbm);
     326                      return 0; /* match */
     327                  }
     328              }
     329          }
     330          dbm_close(dbm);
     331  	if (saw_user)
     332  	    return -1; /* saw the user, but password mismatch */
     333  	else
     334  	    return 1; /* not found */
     335      }
     336  
     337      /* NOT REACHED */
     338      return -2;
     339  }
     340  
     341  /* --- authentication management functions (only) --- */
     342  
     343  int
     344  pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
     345  		    int argc, const char **argv)
     346  {
     347       const char *username;
     348       const void *password;
     349       const char *database = NULL;
     350       const char *cryptmode = NULL;
     351       int retval = PAM_AUTH_ERR, ctrl;
     352  
     353       /* parse arguments */
     354       ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode);
     355       if (database == NULL) {
     356          pam_syslog(pamh, LOG_ERR, "can not get the database name");
     357          return PAM_SERVICE_ERR;
     358       }
     359  
     360       /* Get the username */
     361       retval = pam_get_user(pamh, &username, NULL);
     362       if (retval != PAM_SUCCESS) {
     363          pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
     364                     pam_strerror(pamh, retval));
     365          return PAM_SERVICE_ERR;
     366       }
     367  
     368       if ((ctrl & PAM_USE_FPASS_ARG) == 0 && (ctrl & PAM_TRY_FPASS_ARG) == 0) {
     369          /* Converse to obtain a password */
     370          retval = obtain_authtok(pamh);
     371          if (retval != PAM_SUCCESS) {
     372  	    pam_syslog(pamh, LOG_ERR, "can not obtain password from user");
     373  	    return retval;
     374          }
     375       }
     376  
     377       /* Check if we got a password */
     378       retval = pam_get_item(pamh, PAM_AUTHTOK, &password);
     379       if (retval != PAM_SUCCESS || password == NULL) {
     380          if ((ctrl & PAM_TRY_FPASS_ARG) != 0) {
     381  	    /* Converse to obtain a password */
     382  	    retval = obtain_authtok(pamh);
     383  	    if (retval != PAM_SUCCESS) {
     384  	        pam_syslog(pamh, LOG_ERR, "can not obtain password from user");
     385  		return retval;
     386  	    }
     387  	    retval = pam_get_item(pamh, PAM_AUTHTOK, &password);
     388  	}
     389  	if (retval != PAM_SUCCESS || password == NULL) {
     390  	    pam_syslog(pamh, LOG_ERR, "can not recover user password");
     391  	    return PAM_AUTHTOK_RECOVERY_ERR;
     392  	}
     393       }
     394  
     395       if (ctrl & PAM_DEBUG_ARG)
     396  	 pam_syslog(pamh, LOG_INFO, "Verify user `%s' with a password",
     397  		    username);
     398  
     399       /* Now use the username to look up password in the database file */
     400       retval = user_lookup(pamh, database, cryptmode, username, password, ctrl);
     401       switch (retval) {
     402  	 case -2:
     403  	     /* some sort of system error. The log was already printed */
     404  	     return PAM_SERVICE_ERR;
     405  	 case -1:
     406  	     /* incorrect password */
     407  	     pam_syslog(pamh, LOG_NOTICE,
     408  			"user `%s' denied access (incorrect password)",
     409  			username);
     410  	     return PAM_AUTH_ERR;
     411  	 case 1:
     412  	     /* the user does not exist in the database */
     413  	     if (ctrl & PAM_DEBUG_ARG)
     414  		 pam_syslog(pamh, LOG_NOTICE,
     415  			    "user `%s' not found in the database", username);
     416  	     return PAM_USER_UNKNOWN;
     417  	 case 0:
     418  	     /* Otherwise, the authentication looked good */
     419  	     pam_syslog(pamh, LOG_NOTICE, "user '%s' granted access", username);
     420  	     return PAM_SUCCESS;
     421  	 default:
     422  	     /* we don't know anything about this return value */
     423  	     pam_syslog(pamh, LOG_ERR,
     424  		      "internal module error (retval = %d, user = `%s'",
     425  		      retval, username);
     426  	     return PAM_SERVICE_ERR;
     427       }
     428  
     429       /* should not be reached */
     430       return PAM_IGNORE;
     431  }
     432  
     433  int
     434  pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
     435  	       int argc UNUSED, const char **argv UNUSED)
     436  {
     437      return PAM_SUCCESS;
     438  }
     439  
     440  int
     441  pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
     442  		 int argc, const char **argv)
     443  {
     444      const char *username;
     445      const char *database = NULL;
     446      const char *cryptmode = NULL;
     447      int retval = PAM_AUTH_ERR, ctrl;
     448  
     449      /* parse arguments */
     450      ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode);
     451  
     452      /* Get the username */
     453      retval = pam_get_user(pamh, &username, NULL);
     454      if (retval != PAM_SUCCESS) {
     455          pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
     456                     pam_strerror(pamh, retval));
     457          return PAM_SERVICE_ERR;
     458      }
     459  
     460      /* Now use the username to look up password in the database file */
     461      retval = user_lookup(pamh, database, cryptmode, username, "", ctrl);
     462      switch (retval) {
     463          case -2:
     464  	    /* some sort of system error. The log was already printed */
     465  	    return PAM_SERVICE_ERR;
     466  	case -1:
     467  	    /* incorrect password, but we don't care */
     468  	    /* FALL THROUGH */
     469  	case 0:
     470  	    /* authentication succeeded. dumbest password ever. */
     471  	    return PAM_SUCCESS;
     472  	case 1:
     473  	    /* the user does not exist in the database */
     474  	    return PAM_USER_UNKNOWN;
     475          default:
     476  	    /* we don't know anything about this return value */
     477  	    pam_syslog(pamh, LOG_ERR,
     478  		       "internal module error (retval = %d, user = `%s'",
     479  		       retval, username);
     480              return PAM_SERVICE_ERR;
     481      }
     482  
     483      return PAM_SUCCESS;
     484  }
     485  
     486  /*
     487   * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1999
     488   *                                              All rights reserved
     489   *
     490   * Redistribution and use in source and binary forms, with or without
     491   * modification, are permitted provided that the following conditions
     492   * are met:
     493   * 1. Redistributions of source code must retain the above copyright
     494   *    notice, and the entire permission notice in its entirety,
     495   *    including the disclaimer of warranties.
     496   * 2. Redistributions in binary form must reproduce the above copyright
     497   *    notice, this list of conditions and the following disclaimer in the
     498   *    documentation and/or other materials provided with the distribution.
     499   * 3. The name of the author may not be used to endorse or promote
     500   *    products derived from this software without specific prior
     501   *    written permission.
     502   *
     503   * ALTERNATIVELY, this product may be distributed under the terms of
     504   * the GNU Public License, in which case the provisions of the GPL are
     505   * required INSTEAD OF the above restrictions.  (This clause is
     506   * necessary due to a potential bad interaction between the GPL and
     507   * the restrictions contained in a BSD-style copyright.)
     508   *
     509   * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
     510   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     511   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     512   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     513   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     514   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     515   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     516   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     517   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     518   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     519   * OF THE POSSIBILITY OF SUCH DAMAGE.
     520   */