123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /* mkhomedir_helper - helper for pam_mkhomedir module
- Released under the GNU LGPL version 2 or later
- Copyright (c) Red Hat, Inc., 2009
- Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
- Structure taken from pam_lastlogin by Andrew Morgan
- <morgan@parc.power.net> 1996
- */
- #include "config.h"
- #include <stdarg.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <pwd.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <dirent.h>
- #include <syslog.h>
- #include <security/pam_ext.h>
- #include <security/pam_modutil.h>
- static unsigned long u_mask = 0022;
- static unsigned long home_mode = 0;
- static char skeldir[BUFSIZ] = "/etc/skel";
- /* Do the actual work of creating a home dir */
- static int
- create_homedir(const struct passwd *pwd,
- const char *source, const char *dest)
- {
- char remark[BUFSIZ];
- DIR *d;
- struct dirent *dent;
- int retval = PAM_SESSION_ERR;
- /* Create the new directory */
- if (mkdir(dest, 0700) && errno != EEXIST)
- {
- pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest);
- return PAM_PERM_DENIED;
- }
- /* See if we need to copy the skel dir over. */
- if ((source == NULL) || (strlen(source) == 0))
- {
- retval = PAM_SUCCESS;
- goto go_out;
- }
- /* Scan the directory */
- d = opendir(source);
- if (d == NULL)
- {
- pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source);
- retval = PAM_PERM_DENIED;
- goto go_out;
- }
- for (dent = readdir(d); dent != NULL; dent = readdir(d))
- {
- int srcfd;
- int destfd;
- int res;
- struct stat st;
- #ifndef PATH_MAX
- char *newsource = NULL, *newdest = NULL;
- /* track length of buffers */
- int nslen = 0, ndlen = 0;
- int slen = strlen(source), dlen = strlen(dest);
- #else
- char newsource[PATH_MAX], newdest[PATH_MAX];
- #endif
- /* Skip some files.. */
- if (strcmp(dent->d_name,".") == 0 ||
- strcmp(dent->d_name,"..") == 0)
- continue;
- /* Determine what kind of file it is. */
- #ifndef PATH_MAX
- nslen = slen + strlen(dent->d_name) + 2;
- if (nslen <= 0)
- {
- retval = PAM_BUF_ERR;
- goto go_out;
- }
- if ((newsource = malloc(nslen)) == NULL)
- {
- retval = PAM_BUF_ERR;
- goto go_out;
- }
- sprintf(newsource, "%s/%s", source, dent->d_name);
- #else
- snprintf(newsource, sizeof(newsource), "%s/%s", source, dent->d_name);
- #endif
- if (lstat(newsource, &st) != 0)
- #ifndef PATH_MAX
- {
- free(newsource);
- newsource = NULL;
- continue;
- }
- #else
- continue;
- #endif
- /* We'll need the new file's name. */
- #ifndef PATH_MAX
- ndlen = dlen + strlen(dent->d_name)+2;
- if (ndlen <= 0)
- {
- retval = PAM_BUF_ERR;
- goto go_out;
- }
- if ((newdest = malloc(ndlen)) == NULL)
- {
- free (newsource);
- retval = PAM_BUF_ERR;
- goto go_out;
- }
- sprintf (newdest, "%s/%s", dest, dent->d_name);
- #else
- snprintf (newdest, sizeof (newdest), "%s/%s", dest, dent->d_name);
- #endif
- /* If it's a directory, recurse. */
- if (S_ISDIR(st.st_mode))
- {
- retval = create_homedir(pwd, newsource, newdest);
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- if (retval != PAM_SUCCESS)
- {
- closedir(d);
- goto go_out;
- }
- continue;
- }
- /* If it's a symlink, create a new link. */
- if (S_ISLNK(st.st_mode))
- {
- int pointedlen = 0;
- #ifndef PATH_MAX
- char *pointed = NULL;
- {
- int size = 100;
- while (1) {
- pointed = malloc(size);
- if (pointed == NULL) {
- free(newsource);
- free(newdest);
- return PAM_BUF_ERR;
- }
- pointedlen = readlink(newsource, pointed, size);
- if (pointedlen < 0) break;
- if (pointedlen < size) break;
- free(pointed);
- size *= 2;
- }
- }
- if (pointedlen < 0)
- free(pointed);
- else
- pointed[pointedlen] = 0;
- #else
- char pointed[PATH_MAX];
- memset(pointed, 0, sizeof(pointed));
- pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1);
- #endif
- if (pointedlen >= 0) {
- if(symlink(pointed, newdest) == 0)
- {
- if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0)
- {
- pam_syslog(NULL, LOG_DEBUG,
- "unable to change perms on link %s: %m", newdest);
- closedir(d);
- #ifndef PATH_MAX
- free(pointed);
- free(newsource);
- free(newdest);
- #endif
- return PAM_PERM_DENIED;
- }
- }
- #ifndef PATH_MAX
- free(pointed);
- #endif
- }
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- 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))
- {
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- continue;
- }
- /* Open the source file */
- if ((srcfd = open(newsource, O_RDONLY)) < 0 || fstat(srcfd, &st) != 0)
- {
- pam_syslog(NULL, LOG_DEBUG,
- "unable to open or stat src file %s: %m", newsource);
- if (srcfd >= 0)
- close(srcfd);
- closedir(d);
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- return PAM_PERM_DENIED;
- }
- /* Open the dest file */
- if ((destfd = open(newdest, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0)
- {
- pam_syslog(NULL, LOG_DEBUG,
- "unable to open dest file %s: %m", newdest);
- close(srcfd);
- closedir(d);
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- return PAM_PERM_DENIED;
- }
- /* Set the proper ownership and permissions for the module. We make
- the file a+w and then mask it with the set mask. This preserves
- execute bits */
- if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 ||
- fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0)
- {
- pam_syslog(NULL, LOG_DEBUG,
- "unable to change perms on copy %s: %m", newdest);
- close(srcfd);
- close(destfd);
- closedir(d);
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- return PAM_PERM_DENIED;
- }
- /* Copy the file */
- do
- {
- res = pam_modutil_read(srcfd, remark, sizeof(remark));
- if (res == 0)
- continue;
- if (res > 0) {
- if (pam_modutil_write(destfd, remark, res) == res)
- continue;
- }
- /* If we get here, pam_modutil_read returned a -1 or
- pam_modutil_write returned something unexpected. */
- pam_syslog(NULL, LOG_DEBUG, "unable to perform IO: %m");
- close(srcfd);
- close(destfd);
- closedir(d);
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- return PAM_PERM_DENIED;
- }
- while (res != 0);
- close(srcfd);
- close(destfd);
- #ifndef PATH_MAX
- free(newsource); newsource = NULL;
- free(newdest); newdest = NULL;
- #endif
- }
- closedir(d);
- retval = PAM_SUCCESS;
- go_out:
- if (chmod(dest, 0777 & (~u_mask)) != 0 ||
- chown(dest, pwd->pw_uid, pwd->pw_gid) != 0)
- {
- pam_syslog(NULL, LOG_DEBUG,
- "unable to change perms on directory %s: %m", dest);
- return PAM_PERM_DENIED;
- }
- return retval;
- }
- static int
- create_homedir_helper(const struct passwd *_pwd,
- const char *_skeldir, const char *_homedir)
- {
- int retval = PAM_SESSION_ERR;
- retval = create_homedir(_pwd, _skeldir, _homedir);
- if (chmod(_homedir, home_mode) != 0)
- {
- pam_syslog(NULL, LOG_DEBUG,
- "unable to change perms on home directory %s: %m", _homedir);
- return PAM_PERM_DENIED;
- }
- return retval;
- }
- static int
- make_parent_dirs(char *dir, int make)
- {
- int rc = PAM_SUCCESS;
- char *cp = strrchr(dir, '/');
- struct stat st;
- if (!cp)
- return rc;
- if (cp != dir) {
- *cp = '\0';
- if (stat(dir, &st) && errno == ENOENT)
- rc = make_parent_dirs(dir, 1);
- *cp = '/';
- if (rc != PAM_SUCCESS)
- return rc;
- }
- if (make && mkdir(dir, 0755) && errno != EEXIST) {
- pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dir);
- return PAM_PERM_DENIED;
- }
- return rc;
- }
- int
- main(int argc, char *argv[])
- {
- struct passwd *pwd;
- struct stat st;
- char *eptr;
- if (argc < 2) {
- fprintf(stderr, "Usage: %s <username> [<umask> [<skeldir> [<home_mode>]]]\n", argv[0]);
- return PAM_SESSION_ERR;
- }
- pwd = getpwnam(argv[1]);
- if (pwd == NULL) {
- pam_syslog(NULL, LOG_ERR, "User unknown.");
- return PAM_USER_UNKNOWN;
- }
- if (argc >= 3) {
- errno = 0;
- u_mask = strtoul(argv[2], &eptr, 0);
- if (errno != 0 || *eptr != '\0') {
- pam_syslog(NULL, LOG_ERR, "Bogus umask value %s", argv[2]);
- return PAM_SESSION_ERR;
- }
- }
- if (argc >= 4) {
- if (strlen(argv[3]) >= sizeof(skeldir)) {
- pam_syslog(NULL, LOG_ERR, "Too long skeldir path.");
- return PAM_SESSION_ERR;
- }
- strcpy(skeldir, argv[3]);
- }
- if (argc >= 5) {
- errno = 0;
- home_mode = strtoul(argv[4], &eptr, 0);
- if (errno != 0 || *eptr != '\0') {
- pam_syslog(NULL, LOG_ERR, "Bogus home_mode value %s", argv[4]);
- return PAM_SESSION_ERR;
- }
- }
- if (home_mode == 0)
- home_mode = 0777 & ~u_mask;
- /* Stat the home directory, if something exists then we assume it is
- correct and return a success */
- if (stat(pwd->pw_dir, &st) == 0)
- return PAM_SUCCESS;
- if (make_parent_dirs(pwd->pw_dir, 0) != PAM_SUCCESS)
- return PAM_PERM_DENIED;
- return create_homedir_helper(pwd, skeldir, pwd->pw_dir);
- }
|