]> granicus.if.org Git - linux-pam/blobdiff - modules/pam_mkhomedir/pam_mkhomedir.c
Relevant BUGIDs:
[linux-pam] / modules / pam_mkhomedir / pam_mkhomedir.c
index ec6bd46d6dfc71b5d5b0f956b01a75762415a7e7..6814056f007682792617a1d7a066cd057d9766a9 100644 (file)
@@ -4,10 +4,10 @@
    when the session begins. This allows users to be present in central
    database (such as nis, kerb or ldap) without using a distributed
    file system or pre-creating a large number of directories.
-   
+
    Here is a sample /etc/pam.d/login file for Debian GNU/Linux
    2.1:
-   
+
    auth       requisite  pam_securetty.so
    auth       sufficient pam_ldap.so
    auth       required   pam_pwdb.so
    session    required   pam_mkhomedir.so skel=/etc/skel/ umask=0022
    session    required   pam_pwdb.so
    session    optional   pam_lastlog.so
-   password   required   pam_pwdb.so   
-   
+   password   required   pam_pwdb.so
+
    Released under the GNU LGPL version 2 or later
    Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
-   Structure taken from pam_lastlogin by Andrew Morgan 
+   Structure taken from pam_lastlogin by Andrew Morgan
      <morgan@parc.power.net> 1996
  */
 
@@ -51,6 +51,8 @@
 
 #include <security/pam_modules.h>
 #include <security/_pam_macros.h>
+#include <security/_pam_modutil.h>
+
 
 /* argument parsing */
 #define MKHOMEDIR_DEBUG      020       /* keep quiet about things */
@@ -98,8 +100,8 @@ static int _pam_parse(int flags, int argc, const char **argv)
    return ctrl;
 }
 
-/* This common function is used to send a message to the applications 
-   conversion function. Our only use is to ask the application to print 
+/* This common function is used to send a message to the applications
+   conversion function. Our only use is to ask the application to print
    an informative message that we are creating a home directory */
 static int converse(pam_handle_t * pamh, int ctrl, int nargs
                    ,struct pam_message **message
@@ -111,7 +113,7 @@ static int converse(pam_handle_t * pamh, int ctrl, int nargs
    D(("begin to converse"));
 
    retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
-   if (retval == PAM_SUCCESS)
+   if (retval == PAM_SUCCESS && conv)
    {
 
       retval = conv->conv(nargs, (const struct pam_message **) message
@@ -130,6 +132,8 @@ static int converse(pam_handle_t * pamh, int ctrl, int nargs
    {
       _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
               ,pam_strerror(pamh, retval));
+     if (retval == PAM_SUCCESS)
+         retval = PAM_BAD_ITEM; /* conv was NULL */
    }
 
    D(("ready to return from module conversation"));
@@ -171,104 +175,121 @@ static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark)
 
 /* Do the actual work of creating a home dir */
 static int create_homedir(pam_handle_t * pamh, int ctrl,
-                        const struct passwd *pwd)
+                          const struct passwd *pwd,
+                          const char *source, const char *dest)
 {
-   char *remark;
+   char remark[BUFSIZ];
    DIR *D;
    struct dirent *Dir;
-   
-   /* Some scratch space */
-   remark = malloc(BUFSIZ);
-   if (remark == NULL)
-   {
-      D(("no memory for last login remark"));
-      return PAM_BUF_ERR;
-   }
 
    /* Mention what is happening, if the notification fails that is OK */
-   if (snprintf(remark,BUFSIZ,"Creating home directory '%s'.",
-           pwd->pw_dir) == -1)
+   if (snprintf(remark,sizeof(remark),"Creating directory '%s'.", dest) == -1)
       return PAM_PERM_DENIED;
-   
+
    make_remark(pamh, ctrl, remark);
 
-   /* Crete the home directory */
-   if (mkdir(pwd->pw_dir,0700) != 0)
+   /* Create the new directory */
+   if (mkdir(dest,0700) != 0)
    {
-      free(remark);
-      _log_err(LOG_DEBUG, "unable to create home directory %s",pwd->pw_dir);
+      _log_err(LOG_DEBUG, "unable to create directory %s",dest);
       return PAM_PERM_DENIED;
-   }   
-   if (chmod(pwd->pw_dir,0777 & (~UMask)) != 0 ||
-       chown(pwd->pw_dir,pwd->pw_uid,pwd->pw_gid) != 0)
+   }
+   if (chmod(dest,0777 & (~UMask)) != 0 ||
+       chown(dest,pwd->pw_uid,pwd->pw_gid) != 0)
    {
-      free(remark);
-      _log_err(LOG_DEBUG, "unable to chance perms on home directory %s",pwd->pw_dir);
+      _log_err(LOG_DEBUG, "unable to change perms on directory %s",dest);
       return PAM_PERM_DENIED;
-   }   
-   
+   }
+
    /* See if we need to copy the skel dir over. */
-   if (SkelDir[0] == 0)
+   if ((source == NULL) || (strlen(source) == 0))
    {
-      free(remark);
       return PAM_SUCCESS;
    }
 
    /* Scan the directory */
-   D = opendir(SkelDir);
+   D = opendir(source);
    if (D == 0)
    {
-      free(remark);
-      _log_err(LOG_DEBUG, "unable to read directory %s",SkelDir);
+      _log_err(LOG_DEBUG, "unable to read directory %s",source);
       return PAM_PERM_DENIED;
    }
-   
+
    for (Dir = readdir(D); Dir != 0; Dir = readdir(D))
-   {  
+   {
       int SrcFd;
       int DestFd;
       int Res;
       struct stat St;
-      
+      char newsource[PATH_MAX], newdest[PATH_MAX];
+
       /* Skip some files.. */
       if (strcmp(Dir->d_name,".") == 0 ||
          strcmp(Dir->d_name,"..") == 0)
         continue;
-      
-      /* Check if it is a directory */
-      snprintf(remark,BUFSIZ,"%s/%s",SkelDir,Dir->d_name);
-      if (stat(remark,&St) != 0)
-        continue;
+
+      /* Determine what kind of file it is. */
+      snprintf(newsource,sizeof(newsource),"%s/%s",source,Dir->d_name);
+      if (lstat(newsource,&St) != 0)
+         continue;
+
+      /* We'll need the new file's name. */
+      snprintf(newdest,sizeof(newdest),"%s/%s",dest,Dir->d_name);
+
+      /* If it's a directory, recurse. */
       if (S_ISDIR(St.st_mode))
       {
-       snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
-       if (mkdir(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
-           chmod(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
-           chown(remark,pwd->pw_uid,pwd->pw_gid) != 0)
-       {
-          free(remark);
-          _log_err(LOG_DEBUG, "unable to change perms on copy %s",remark);
-          return PAM_PERM_DENIED;
-       }
-       continue;
+         int retval = create_homedir(pamh, ctrl, pwd, newsource, newdest);
+         if (retval != PAM_SUCCESS) {
+            closedir(D);
+            return retval;
+         }
+         continue;
+      }
+
+      /* If it's a symlink, create a new link. */
+      if (S_ISLNK(St.st_mode))
+      {
+         char pointed[PATH_MAX];
+         memset(pointed, 0, sizeof(pointed));
+         if(readlink(newsource, pointed, sizeof(pointed) - 1) != -1)
+         {
+            if(symlink(pointed, newdest) == 0)
+            {
+               if (lchown(newdest,pwd->pw_uid,pwd->pw_gid) != 0)
+               {
+                   closedir(D);
+                   _log_err(LOG_DEBUG, "unable to chang perms on link %s",
+                            newdest);
+                   return PAM_PERM_DENIED;
+               }
+            }
+         }
+         continue;
+      }
+
+      /* If it's not a regular file, it's probably not a good idea to create
+       * the new device node, FIFO, or whatever it is. */
+      if (!S_ISREG(St.st_mode))
+      {
+         continue;
       }
 
       /* Open the source file */
-      if ((SrcFd = open(remark,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0)
+      if ((SrcFd = open(newsource,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0)
       {
-        free(remark);
-        _log_err(LOG_DEBUG, "unable to open src file %s",remark);
+         closedir(D);
+         _log_err(LOG_DEBUG, "unable to open src file %s",newsource);
         return PAM_PERM_DENIED;
       }
-      stat(remark,&St);
-      
+      stat(newsource,&St);
+
       /* Open the dest file */
-      snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
-      if ((DestFd = open(remark,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
+      if ((DestFd = open(newdest,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
       {
         close(SrcFd);
-        free(remark);
-        _log_err(LOG_DEBUG, "unable to open dest file %s",remark);
+        closedir(D);
+         _log_err(LOG_DEBUG, "unable to open dest file %s",newdest);
         return PAM_PERM_DENIED;
       }
 
@@ -278,30 +299,40 @@ static int create_homedir(pam_handle_t * pamh, int ctrl,
       if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 ||
          fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0)
       {
-        free(remark);
-        _log_err(LOG_DEBUG, "unable to chang perms on copy %s",remark);
+         close(SrcFd);
+         close(DestFd);
+         closedir(D);
+         _log_err(LOG_DEBUG, "unable to chang perms on copy %s",newdest);
         return PAM_PERM_DENIED;
-      }   
-      
+      }
+
       /* Copy the file */
       do
       {
-        Res = read(SrcFd,remark,BUFSIZ);
-        if (Res < 0 || write(DestFd,remark,Res) != Res)
-        {
-           close(SrcFd);
-           close(DestFd);
-           free(remark);
-           _log_err(LOG_DEBUG, "unable to perform IO");
-           return PAM_PERM_DENIED;
+        Res = _pammodutil_read(SrcFd,remark,sizeof(remark));
+
+        if (Res == 0)
+            continue;
+
+        if (Res > 0) {
+            if (_pammodutil_write(DestFd,remark,Res) == Res)
+               continue;
         }
+
+        /* If we get here, pammodutil_read returned a -1 or
+           _pammodutil_write returned something unexpected. */
+        close(SrcFd);
+        close(DestFd);
+        closedir(D);
+        _log_err(LOG_DEBUG, "unable to perform IO");
+        return PAM_PERM_DENIED;
       }
       while (Res != 0);
       close(SrcFd);
       close(DestFd);
    }
-   
-   free(remark);
+   closedir(D);
+
    return PAM_SUCCESS;
 }
 
@@ -315,7 +346,7 @@ int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
    const char *user;
    const struct passwd *pwd;
    struct stat St;
-      
+
    /* Parse the flag values */
    ctrl = _pam_parse(flags, argc, argv);
 
@@ -328,7 +359,7 @@ int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
    }
 
    /* Get the password entry */
-   pwd = getpwnam(user);
+   pwd = _pammodutil_getpwnam (pamh, user);
    if (pwd == NULL)
    {
       D(("couldn't identify user %s", user));
@@ -340,11 +371,11 @@ int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
    if (stat(pwd->pw_dir,&St) == 0)
       return PAM_SUCCESS;
 
-   return create_homedir(pamh,ctrl,pwd);
+   return create_homedir(pamh,ctrl,pwd,SkelDir,pwd->pw_dir);
 }
 
 /* Ignore */
-PAM_EXTERN 
+PAM_EXTERN
 int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc
                         ,const char **argv)
 {