(root)/
Linux-PAM-1.5.3/
modules/
pam_mkhomedir/
pam_mkhomedir.c
       1  /* PAM Make Home Dir module
       2  
       3     This module will create a users home directory if it does not exist
       4     when the session begins. This allows users to be present in central
       5     database (such as nis, kerb or ldap) without using a distributed
       6     file system or pre-creating a large number of directories.
       7  
       8     Here is a sample /etc/pam.d/login file for Debian GNU/Linux
       9     2.1:
      10  
      11     auth       requisite  pam_securetty.so
      12     auth       sufficient pam_ldap.so
      13     auth       required   pam_unix.so
      14     auth       optional   pam_group.so
      15     auth       optional   pam_mail.so
      16     account    requisite  pam_time.so
      17     account    sufficient pam_ldap.so
      18     account    required   pam_unix.so
      19     session    required   pam_mkhomedir.so skel=/etc/skel/ umask=0022
      20     session    required   pam_unix.so
      21     session    optional   pam_lastlog.so
      22     password   required   pam_unix.so
      23  
      24     Released under the GNU LGPL version 2 or later
      25     Copyright (c) Red Hat, Inc. 2009
      26     Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
      27     Structure taken from pam_lastlogin by Andrew Morgan
      28       <morgan@parc.power.net> 1996
      29   */
      30  
      31  #include "config.h"
      32  
      33  #include <sys/types.h>
      34  #include <sys/stat.h>
      35  #include <sys/time.h>
      36  #include <sys/resource.h>
      37  #include <sys/wait.h>
      38  #include <unistd.h>
      39  #include <pwd.h>
      40  #include <errno.h>
      41  #include <stdlib.h>
      42  #include <stdio.h>
      43  #include <string.h>
      44  #include <syslog.h>
      45  #include <signal.h>
      46  
      47  #include <security/pam_modules.h>
      48  #include <security/_pam_macros.h>
      49  #include <security/pam_modutil.h>
      50  #include <security/pam_ext.h>
      51  
      52  #include "pam_cc_compat.h"
      53  #include "pam_inline.h"
      54  
      55  /* argument parsing */
      56  #define MKHOMEDIR_DEBUG      020	/* be verbose about things */
      57  #define MKHOMEDIR_QUIET      040	/* keep quiet about things */
      58  
      59  #define LOGIN_DEFS           "/etc/login.defs"
      60  #define UMASK_DEFAULT        "0022"
      61  
      62  struct options_t {
      63    int ctrl;
      64    const char *umask;
      65    const char *skeldir;
      66  };
      67  typedef struct options_t options_t;
      68  
      69  static void
      70  _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv,
      71  	    options_t *opt)
      72  {
      73     opt->ctrl = 0;
      74     opt->umask = NULL;
      75     opt->skeldir = "/etc/skel";
      76  
      77     /* does the application require quiet? */
      78     if ((flags & PAM_SILENT) == PAM_SILENT)
      79        opt->ctrl |= MKHOMEDIR_QUIET;
      80  
      81     /* step through arguments */
      82     for (; argc-- > 0; ++argv)
      83     {
      84        const char *str;
      85  
      86        if (!strcmp(*argv, "silent")) {
      87  	 opt->ctrl |= MKHOMEDIR_QUIET;
      88        } else if (!strcmp(*argv, "debug")) {
      89           opt->ctrl |= MKHOMEDIR_DEBUG;
      90        } else if ((str = pam_str_skip_prefix(*argv, "umask=")) != NULL) {
      91  	 opt->umask = str;
      92        } else if ((str = pam_str_skip_prefix(*argv, "skel=")) != NULL) {
      93  	 opt->skeldir = str;
      94        } else {
      95  	 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
      96        }
      97     }
      98  }
      99  
     100  static char*
     101  _pam_conv_str_umask_to_homemode(const char *umask)
     102  {
     103     unsigned int m = 0;
     104     char tmp[5];
     105  
     106     m = 0777 & ~strtoul(umask, NULL, 8);
     107     (void) snprintf(tmp, sizeof(tmp), "0%o", m);
     108     return strdup(tmp);
     109  }
     110  
     111  /* Do the actual work of creating a home dir */
     112  static int
     113  create_homedir (pam_handle_t *pamh, options_t *opt,
     114  		const char *user, const char *dir)
     115  {
     116     int retval, child;
     117     struct sigaction newsa, oldsa;
     118     char *login_umask = NULL;
     119     char *login_homemode = NULL;
     120  
     121     /* Mention what is happening, if the notification fails that is OK */
     122     if (!(opt->ctrl & MKHOMEDIR_QUIET))
     123        pam_info(pamh, _("Creating directory '%s'."), dir);
     124  
     125  
     126     D(("called."));
     127  
     128     if (opt->ctrl & MKHOMEDIR_DEBUG) {
     129          pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
     130     }
     131  
     132     /* fetch UMASK from /etc/login.defs if not in argv */
     133     if (opt->umask == NULL) {
     134        login_umask = pam_modutil_search_key(pamh, LOGIN_DEFS, "UMASK");
     135        login_homemode = pam_modutil_search_key(pamh, LOGIN_DEFS, "HOME_MODE");
     136        if (login_homemode == NULL) {
     137           if (login_umask != NULL) {
     138              login_homemode = _pam_conv_str_umask_to_homemode(login_umask);
     139           } else {
     140              login_homemode = _pam_conv_str_umask_to_homemode(UMASK_DEFAULT);
     141           }
     142        }
     143     } else {
     144        login_homemode = _pam_conv_str_umask_to_homemode(opt->umask);
     145     }
     146  
     147     /*
     148      * This code arranges that the demise of the child does not cause
     149      * the application to receive a signal it is not expecting - which
     150      * may kill the application or worse.
     151      */
     152     memset(&newsa, '\0', sizeof(newsa));
     153     newsa.sa_handler = SIG_DFL;
     154     sigaction(SIGCHLD, &newsa, &oldsa);
     155  
     156     /* fork */
     157     child = fork();
     158     if (child == 0) {
     159  	static char *envp[] = { NULL };
     160  	const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
     161  
     162  	if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
     163  					    PAM_MODUTIL_PIPE_FD,
     164  					    PAM_MODUTIL_PIPE_FD) < 0)
     165  		_exit(PAM_SYSTEM_ERR);
     166  
     167  	/* exec the mkhomedir helper */
     168  	args[0] = MKHOMEDIR_HELPER;
     169  	args[1] = user;
     170  	args[2] = opt->umask ? opt->umask : UMASK_DEFAULT;
     171  	args[3] = opt->skeldir;
     172  	args[4] = login_homemode;
     173  
     174  	DIAG_PUSH_IGNORE_CAST_QUAL;
     175  	execve(MKHOMEDIR_HELPER, (char **)args, envp);
     176  	DIAG_POP_IGNORE_CAST_QUAL;
     177  
     178  	/* should not get here: exit with error */
     179  	D(("helper binary is not available"));
     180  	_exit(PAM_SYSTEM_ERR);
     181     } else if (child > 0) {
     182  	int rc;
     183  	while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
     184  	if (rc < 0) {
     185  	  pam_syslog(pamh, LOG_ERR, "waitpid failed: %m");
     186  	  retval = PAM_SYSTEM_ERR;
     187  	} else if (!WIFEXITED(retval)) {
     188            pam_syslog(pamh, LOG_ERR, "mkhomedir_helper abnormal exit: %d", retval);
     189            retval = PAM_SYSTEM_ERR;
     190          } else {
     191  	  retval = WEXITSTATUS(retval);
     192  	}
     193     } else {
     194  	D(("fork failed"));
     195  	pam_syslog(pamh, LOG_ERR, "fork failed: %m");
     196  	retval = PAM_SYSTEM_ERR;
     197     }
     198  
     199     sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
     200  
     201     if (opt->ctrl & MKHOMEDIR_DEBUG) {
     202          pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
     203     }
     204  
     205     if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) {
     206  	pam_error(pamh, _("Unable to create and initialize directory '%s'."),
     207  		  dir);
     208     }
     209  
     210     free(login_umask);
     211     free(login_homemode);
     212  
     213     D(("returning %d", retval));
     214     return retval;
     215  }
     216  
     217  /* --- authentication management functions (only) --- */
     218  
     219  int
     220  pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
     221  		     const char **argv)
     222  {
     223     int retval;
     224     options_t opt;
     225     const void *user;
     226     const struct passwd *pwd;
     227     struct stat St;
     228  
     229     /* Parse the flag values */
     230     _pam_parse(pamh, flags, argc, argv, &opt);
     231  
     232     /* Determine the user name so we can get the home directory */
     233     retval = pam_get_item(pamh, PAM_USER, &user);
     234     if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0')
     235     {
     236        pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
     237        return PAM_USER_UNKNOWN;
     238     }
     239  
     240     /* Get the password entry */
     241     pwd = pam_modutil_getpwnam (pamh, user);
     242     if (pwd == NULL)
     243     {
     244        pam_syslog(pamh, LOG_NOTICE, "User unknown.");
     245        D(("couldn't identify user %s", user));
     246        return PAM_USER_UNKNOWN;
     247     }
     248  
     249     /* Stat the home directory, if something exists then we assume it is
     250        correct and return a success*/
     251     if (stat(pwd->pw_dir, &St) == 0) {
     252        if (opt.ctrl & MKHOMEDIR_DEBUG) {
     253            pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.",
     254                pwd->pw_dir);
     255        }
     256        return PAM_SUCCESS;
     257     }
     258  
     259     return create_homedir(pamh, &opt, user, pwd->pw_dir);
     260  }
     261  
     262  /* Ignore */
     263  int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED,
     264  			  int argc UNUSED, const char **argv UNUSED)
     265  {
     266     return PAM_SUCCESS;
     267  }