(root)/
Linux-PAM-1.5.3/
modules/
pam_mkhomedir/
mkhomedir_helper.c
       1  /* mkhomedir_helper - helper for pam_mkhomedir module
       2  
       3     Released under the GNU LGPL version 2 or later
       4  
       5     Copyright (c) Red Hat, Inc., 2009
       6     Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
       7     Structure taken from pam_lastlogin by Andrew Morgan
       8       <morgan@parc.power.net> 1996
       9   */
      10  
      11  #include "config.h"
      12  
      13  #include <stdarg.h>
      14  #include <sys/types.h>
      15  #include <sys/stat.h>
      16  #include <fcntl.h>
      17  #include <unistd.h>
      18  #include <pwd.h>
      19  #include <errno.h>
      20  #include <stdlib.h>
      21  #include <stdio.h>
      22  #include <string.h>
      23  #include <dirent.h>
      24  #include <syslog.h>
      25  
      26  #include <security/pam_ext.h>
      27  #include <security/pam_modutil.h>
      28  
      29  static unsigned long u_mask = 0022;
      30  static unsigned long home_mode = 0;
      31  static char skeldir[BUFSIZ] = "/etc/skel";
      32  
      33  /* Do the actual work of creating a home dir */
      34  static int
      35  create_homedir(const struct passwd *pwd,
      36  	       const char *source, const char *dest)
      37  {
      38     char remark[BUFSIZ];
      39     DIR *d;
      40     struct dirent *dent;
      41     int retval = PAM_SESSION_ERR;
      42  
      43     /* Create the new directory */
      44     if (mkdir(dest, 0700) && errno != EEXIST)
      45     {
      46        pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest);
      47        return PAM_PERM_DENIED;
      48     }
      49  
      50     /* See if we need to copy the skel dir over. */
      51     if ((source == NULL) || (strlen(source) == 0))
      52     {
      53        retval = PAM_SUCCESS;
      54        goto go_out;
      55     }
      56  
      57     /* Scan the directory */
      58     d = opendir(source);
      59     if (d == NULL)
      60     {
      61        pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source);
      62        retval = PAM_PERM_DENIED;
      63        goto go_out;
      64     }
      65  
      66     for (dent = readdir(d); dent != NULL; dent = readdir(d))
      67     {
      68        int srcfd;
      69        int destfd;
      70        int res;
      71        struct stat st;
      72  #ifndef PATH_MAX
      73        char *newsource = NULL, *newdest = NULL;
      74        /* track length of buffers */
      75        int nslen = 0, ndlen = 0;
      76        int slen = strlen(source), dlen = strlen(dest);
      77  #else
      78        char newsource[PATH_MAX], newdest[PATH_MAX];
      79  #endif
      80  
      81        /* Skip some files.. */
      82        if (strcmp(dent->d_name,".") == 0 ||
      83  	  strcmp(dent->d_name,"..") == 0)
      84  	 continue;
      85  
      86        /* Determine what kind of file it is. */
      87  #ifndef PATH_MAX
      88        nslen = slen + strlen(dent->d_name) + 2;
      89  
      90        if (nslen <= 0)
      91  	{
      92  	  retval = PAM_BUF_ERR;
      93  	  goto go_out;
      94  	}
      95  
      96        if ((newsource = malloc(nslen)) == NULL)
      97  	{
      98  	  retval = PAM_BUF_ERR;
      99  	  goto go_out;
     100  	}
     101  
     102        sprintf(newsource, "%s/%s", source, dent->d_name);
     103  #else
     104        snprintf(newsource, sizeof(newsource), "%s/%s", source, dent->d_name);
     105  #endif
     106  
     107        if (lstat(newsource, &st) != 0)
     108  #ifndef PATH_MAX
     109        {
     110  	      free(newsource);
     111  	      newsource = NULL;
     112           continue;
     113        }
     114  #else
     115        continue;
     116  #endif
     117  
     118  
     119        /* We'll need the new file's name. */
     120  #ifndef PATH_MAX
     121        ndlen = dlen + strlen(dent->d_name)+2;
     122  
     123        if (ndlen <= 0)
     124  	{
     125  	  retval = PAM_BUF_ERR;
     126  	  goto go_out;
     127  	}
     128  
     129        if ((newdest = malloc(ndlen)) == NULL)
     130  	{
     131  	  free (newsource);
     132  	  retval = PAM_BUF_ERR;
     133  	  goto go_out;
     134  	}
     135  
     136        sprintf (newdest, "%s/%s", dest, dent->d_name);
     137  #else
     138        snprintf (newdest, sizeof (newdest), "%s/%s", dest, dent->d_name);
     139  #endif
     140  
     141        /* If it's a directory, recurse. */
     142        if (S_ISDIR(st.st_mode))
     143        {
     144           retval = create_homedir(pwd, newsource, newdest);
     145  
     146  #ifndef PATH_MAX
     147  	 free(newsource); newsource = NULL;
     148  	 free(newdest); newdest = NULL;
     149  #endif
     150  
     151           if (retval != PAM_SUCCESS)
     152  	   {
     153  	     closedir(d);
     154  	     goto go_out;
     155  	   }
     156           continue;
     157        }
     158  
     159        /* If it's a symlink, create a new link. */
     160        if (S_ISLNK(st.st_mode))
     161        {
     162  	 int pointedlen = 0;
     163  #ifndef PATH_MAX
     164  	 char *pointed = NULL;
     165             {
     166  		   int size = 100;
     167  
     168  		   while (1) {
     169  			   pointed = malloc(size);
     170  			   if (pointed == NULL) {
     171  				   free(newsource);
     172  				   free(newdest);
     173  				   return PAM_BUF_ERR;
     174  			   }
     175  			   pointedlen = readlink(newsource, pointed, size);
     176  			   if (pointedlen < 0) break;
     177  			   if (pointedlen < size) break;
     178  			   free(pointed);
     179  			   size *= 2;
     180  		   }
     181  	   }
     182  	   if (pointedlen < 0)
     183  		   free(pointed);
     184  	   else
     185  		   pointed[pointedlen] = 0;
     186  #else
     187           char pointed[PATH_MAX] = {};
     188  
     189           pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1);
     190  #endif
     191  
     192  	 if (pointedlen >= 0) {
     193              if(symlink(pointed, newdest) == 0)
     194              {
     195                 if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0)
     196                 {
     197                     pam_syslog(NULL, LOG_DEBUG,
     198  			      "unable to change perms on link %s: %m", newdest);
     199                     closedir(d);
     200  #ifndef PATH_MAX
     201  		   free(pointed);
     202  		   free(newsource);
     203  		   free(newdest);
     204  #endif
     205                     return PAM_PERM_DENIED;
     206                 }
     207              }
     208  #ifndef PATH_MAX
     209  	    free(pointed);
     210  #endif
     211           }
     212  #ifndef PATH_MAX
     213  	 free(newsource); newsource = NULL;
     214  	 free(newdest); newdest = NULL;
     215  #endif
     216           continue;
     217        }
     218  
     219        /* If it's not a regular file, it's probably not a good idea to create
     220         * the new device node, FIFO, or whatever it is. */
     221        if (!S_ISREG(st.st_mode))
     222        {
     223  #ifndef PATH_MAX
     224  	 free(newsource); newsource = NULL;
     225  	 free(newdest); newdest = NULL;
     226  #endif
     227           continue;
     228        }
     229  
     230        /* Open the source file */
     231        if ((srcfd = open(newsource, O_RDONLY)) < 0 || fstat(srcfd, &st) != 0)
     232        {
     233           pam_syslog(NULL, LOG_DEBUG,
     234  		    "unable to open or stat src file %s: %m", newsource);
     235           if (srcfd >= 0)
     236              close(srcfd);
     237           closedir(d);
     238  
     239  #ifndef PATH_MAX
     240  	 free(newsource); newsource = NULL;
     241  	 free(newdest); newdest = NULL;
     242  #endif
     243  
     244  	 return PAM_PERM_DENIED;
     245        }
     246  
     247        /* Open the dest file */
     248        if ((destfd = open(newdest, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0)
     249        {
     250           pam_syslog(NULL, LOG_DEBUG,
     251  		    "unable to open dest file %s: %m", newdest);
     252  	 close(srcfd);
     253  	 closedir(d);
     254  
     255  #ifndef PATH_MAX
     256  	 free(newsource); newsource = NULL;
     257  	 free(newdest); newdest = NULL;
     258  #endif
     259  	 return PAM_PERM_DENIED;
     260        }
     261  
     262        /* Set the proper ownership and permissions for the module. We make
     263           the file a+w and then mask it with the set mask. This preserves
     264  	 execute bits */
     265        if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 ||
     266  	  fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0)
     267        {
     268           pam_syslog(NULL, LOG_DEBUG,
     269  		    "unable to change perms on copy %s: %m", newdest);
     270           close(srcfd);
     271           close(destfd);
     272           closedir(d);
     273  
     274  #ifndef PATH_MAX
     275  	 free(newsource); newsource = NULL;
     276  	 free(newdest); newdest = NULL;
     277  #endif
     278  
     279  	 return PAM_PERM_DENIED;
     280        }
     281  
     282        /* Copy the file */
     283        do
     284        {
     285  	 res = pam_modutil_read(srcfd, remark, sizeof(remark));
     286  
     287  	 if (res == 0)
     288  	     continue;
     289  
     290  	 if (res > 0) {
     291  	     if (pam_modutil_write(destfd, remark, res) == res)
     292  		continue;
     293  	 }
     294  
     295  	 /* If we get here, pam_modutil_read returned a -1 or
     296  	    pam_modutil_write returned something unexpected. */
     297  	 pam_syslog(NULL, LOG_DEBUG, "unable to perform IO: %m");
     298  	 close(srcfd);
     299  	 close(destfd);
     300  	 closedir(d);
     301  
     302  #ifndef PATH_MAX
     303  	 free(newsource); newsource = NULL;
     304  	 free(newdest); newdest = NULL;
     305  #endif
     306  
     307  	 return PAM_PERM_DENIED;
     308        }
     309        while (res != 0);
     310        close(srcfd);
     311        close(destfd);
     312  
     313  #ifndef PATH_MAX
     314        free(newsource); newsource = NULL;
     315        free(newdest); newdest = NULL;
     316  #endif
     317  
     318     }
     319     closedir(d);
     320  
     321     retval = PAM_SUCCESS;
     322  
     323   go_out:
     324  
     325     if (chmod(dest, 0777 & (~u_mask)) != 0 ||
     326         chown(dest, pwd->pw_uid, pwd->pw_gid) != 0)
     327     {
     328        pam_syslog(NULL, LOG_DEBUG,
     329  		 "unable to change perms on directory %s: %m", dest);
     330        return PAM_PERM_DENIED;
     331     }
     332  
     333     return retval;
     334  }
     335  
     336  static int
     337  create_homedir_helper(const struct passwd *_pwd,
     338  		      const char *_skeldir, const char *_homedir)
     339  {
     340     int retval = PAM_SESSION_ERR;
     341  
     342     retval = create_homedir(_pwd, _skeldir, _homedir);
     343  
     344     if (chmod(_homedir, home_mode) != 0)
     345     {
     346        pam_syslog(NULL, LOG_DEBUG,
     347  		 "unable to change perms on home directory %s: %m", _homedir);
     348        return PAM_PERM_DENIED;
     349     }
     350  
     351     return retval;
     352  }
     353  
     354  static int
     355  make_parent_dirs(char *dir, int make)
     356  {
     357    int rc = PAM_SUCCESS;
     358    char *cp = strrchr(dir, '/');
     359    struct stat st;
     360  
     361    if (!cp)
     362      return rc;
     363  
     364    if (cp != dir) {
     365      *cp = '\0';
     366      if (stat(dir, &st) && errno == ENOENT)
     367        rc = make_parent_dirs(dir, 1);
     368      *cp = '/';
     369  
     370      if (rc != PAM_SUCCESS)
     371        return rc;
     372    }
     373  
     374    if (make && mkdir(dir, 0755) && errno != EEXIST) {
     375      pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dir);
     376      return PAM_PERM_DENIED;
     377    }
     378  
     379    return rc;
     380  }
     381  
     382  int
     383  main(int argc, char *argv[])
     384  {
     385     struct passwd *pwd;
     386     struct stat st;
     387     char *eptr;
     388  
     389     if (argc < 2) {
     390  	fprintf(stderr, "Usage: %s <username> [<umask> [<skeldir> [<home_mode>]]]\n", argv[0]);
     391  	return PAM_SESSION_ERR;
     392     }
     393  
     394     pwd = getpwnam(argv[1]);
     395     if (pwd == NULL) {
     396  	pam_syslog(NULL, LOG_ERR, "User unknown.");
     397  	return PAM_USER_UNKNOWN;
     398     }
     399  
     400     if (argc >= 3) {
     401  	errno = 0;
     402  	u_mask = strtoul(argv[2], &eptr, 0);
     403  	if (errno != 0 || *eptr != '\0') {
     404  		pam_syslog(NULL, LOG_ERR, "Bogus umask value %s", argv[2]);
     405  		return PAM_SESSION_ERR;
     406  	}
     407     }
     408  
     409     if (argc >= 4) {
     410  	if (strlen(argv[3]) >= sizeof(skeldir)) {
     411  		pam_syslog(NULL, LOG_ERR, "Too long skeldir path.");
     412  		return PAM_SESSION_ERR;
     413  	}
     414  	strcpy(skeldir, argv[3]);
     415     }
     416  
     417     if (argc >= 5) {
     418         errno = 0;
     419         home_mode = strtoul(argv[4], &eptr, 0);
     420         if (errno != 0 || *eptr != '\0') {
     421  		pam_syslog(NULL, LOG_ERR, "Bogus home_mode value %s", argv[4]);
     422  		return PAM_SESSION_ERR;
     423         }
     424     }
     425  
     426     if (home_mode == 0)
     427        home_mode = 0777 & ~u_mask;
     428  
     429     /* Stat the home directory, if something exists then we assume it is
     430        correct and return a success */
     431     if (stat(pwd->pw_dir, &st) == 0)
     432  	return PAM_SUCCESS;
     433  
     434     if (make_parent_dirs(pwd->pw_dir, 0) != PAM_SUCCESS)
     435  	return PAM_PERM_DENIED;
     436  
     437     return create_homedir_helper(pwd, skeldir, pwd->pw_dir);
     438  }