From d1d94d7bdd5da640055b624ac4047c3c2621a23d Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Sun, 12 Nov 2023 18:59:30 +0100 Subject: pam_mkhomedir: improve error path handling Extract logic reponsible for copying single directory entries into its own function for easier error handling, i.e. using goto for clean up of variables used in "for loop". Signed-off-by: Tobias Stoeckmann --- modules/pam_mkhomedir/mkhomedir_helper.c | 382 ++++++++++++++----------------- 1 file changed, 178 insertions(+), 204 deletions(-) (limited to 'modules/pam_mkhomedir/mkhomedir_helper.c') diff --git a/modules/pam_mkhomedir/mkhomedir_helper.c b/modules/pam_mkhomedir/mkhomedir_helper.c index c6e10288..58195788 100644 --- a/modules/pam_mkhomedir/mkhomedir_helper.c +++ b/modules/pam_mkhomedir/mkhomedir_helper.c @@ -29,266 +29,240 @@ static unsigned long u_mask = 0022; static char skeldir[BUFSIZ] = "/etc/skel"; -/* Do the actual work of creating a home dir */ +static int create_homedir(const struct passwd *, mode_t, const char *, + const char *); + static int -create_homedir(const struct passwd *pwd, mode_t dir_mode, - const char *source, const char *dest) +copy_entry(const struct passwd *pwd, mode_t dir_mode, + const char *source, const char *dest, struct dirent *dent) { char remark[BUFSIZ]; - DIR *d; - struct dirent *dent; + int srcfd = -1, destfd = -1; + int res; int retval = PAM_SESSION_ERR; + struct stat st; + char *newsource = NULL, *newdest = NULL; - /* Create the new directory */ - if (mkdir(dest, 0700)) + /* Determine what kind of file it is. */ + if (asprintf(&newsource, "%s/%s", source, dent->d_name) < 0) { - if (errno == EEXIST) - return PAM_SUCCESS; - pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest); - return PAM_PERM_DENIED; + pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newsource'"); + retval = PAM_BUF_ERR; + goto go_out; } - /* See if we need to copy the skel dir over. */ - if ((source == NULL) || (strlen(source) == 0)) + if (lstat(newsource, &st) != 0) { retval = PAM_SUCCESS; goto go_out; } - /* Scan the directory */ - d = opendir(source); - if (d == NULL) + /* We'll need the new file's name. */ + if (asprintf(&newdest, "%s/%s", dest, dent->d_name) < 0) { - pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source); - retval = PAM_PERM_DENIED; + pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newdest'"); + retval = PAM_BUF_ERR; goto go_out; } - for (dent = readdir(d); dent != NULL; dent = readdir(d)) + /* If it's a directory, recurse. */ + if (S_ISDIR(st.st_mode)) { - int srcfd; - int destfd; - int res; - struct stat st; - char *newsource = NULL, *newdest = NULL; - - /* Skip some files.. */ - if (strcmp(dent->d_name,".") == 0 || - strcmp(dent->d_name,"..") == 0) - continue; - - /* Determine what kind of file it is. */ - if (asprintf(&newsource, "%s/%s", source, dent->d_name) < 0) - { - pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newsource'"); - retval = PAM_BUF_ERR; - goto go_out; - } - - if (lstat(newsource, &st) != 0) - { - free(newsource); - continue; - } - - - /* We'll need the new file's name. */ - if (asprintf(&newdest, "%s/%s", dest, dent->d_name) < 0) - { - pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newdest'"); - free(newsource); - retval = PAM_BUF_ERR; - goto go_out; - } - + retval = create_homedir(pwd, dir_mode & (~u_mask), newsource, newdest); + goto go_out; + } - /* If it's a directory, recurse. */ - if (S_ISDIR(st.st_mode)) + /* If it's a symlink, create a new link. */ + if (S_ISLNK(st.st_mode)) + { + int pointedlen = 0; +#ifndef PATH_MAX + char *pointed = NULL; { - retval = create_homedir(pwd, dir_mode & (~u_mask), newsource, newdest); - - free(newsource); - free(newdest); - - if (retval != PAM_SUCCESS) - { - closedir(d); - goto go_out; - } - continue; + int size = 100; + + while (1) + { + pointed = malloc(size); + if (pointed == NULL) + { + retval = PAM_BUF_ERR; + goto go_out; + } + pointedlen = readlink(newsource, pointed, size); + if (pointedlen < 0) break; + if (pointedlen < size) break; + free(pointed); + size *= 2; + } } - - /* 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); - retval = PAM_BUF_ERR; - goto go_out; - } - 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; + if (pointedlen < 0) + free(pointed); + else + pointed[pointedlen] = 0; #else - char pointed[PATH_MAX] = {}; + char pointed[PATH_MAX] = {}; - pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1); + pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1); #endif - if (pointedlen >= 0) { - if(symlink(pointed, newdest) != 0) - { - retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; + if (pointedlen >= 0) + { + if (symlink(pointed, newdest) != 0) + { + retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; - if (retval != PAM_SUCCESS) - pam_syslog(NULL, LOG_DEBUG, - "unable to create link %s: %m", newdest); - closedir(d); + if (retval != PAM_SUCCESS) + pam_syslog(NULL, LOG_DEBUG, + "unable to create link %s: %m", newdest); #ifndef PATH_MAX - free(pointed); + free(pointed); #endif - free(newsource); - free(newdest); - goto go_out; - } - - 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); + goto go_out; + } + + if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0) + { + pam_syslog(NULL, LOG_DEBUG, + "unable to change perms on link %s: %m", newdest); #ifndef PATH_MAX - free(pointed); + free(pointed); #endif - free(newsource); - free(newdest); - retval = PAM_PERM_DENIED; - goto go_out; - } + retval = PAM_PERM_DENIED; + goto go_out; + } #ifndef PATH_MAX - free(pointed); + free(pointed); #endif - } - free(newsource); - free(newdest); - continue; } + retval = PAM_SUCCESS; + goto go_out; + } - /* 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)) - { - free(newsource); - free(newdest); - 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)) + { + retval = PAM_SUCCESS; + goto go_out; + } - /* 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); + /* 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); + retval = PAM_PERM_DENIED; + goto go_out; + } - free(newsource); - free(newdest); + /* Open the dest file */ + if ((destfd = open(newdest, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) + { + retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; + if (retval != PAM_SUCCESS) + pam_syslog(NULL, LOG_DEBUG, + "unable to open dest file %s: %m", newdest); + goto go_out; + } - retval = PAM_PERM_DENIED; - goto go_out; - } + /* 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); + retval = PAM_PERM_DENIED; + goto go_out; + } - /* Open the dest file */ - if ((destfd = open(newdest, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) - { - retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; - if (retval != PAM_SUCCESS) - pam_syslog(NULL, LOG_DEBUG, - "unable to open dest file %s: %m", newdest); - close(srcfd); - closedir(d); + /* Copy the file */ + do + { + res = pam_modutil_read(srcfd, remark, sizeof(remark)); - free(newsource); - free(newdest); - goto go_out; - } + if (res == 0) + continue; - /* 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) + if (res > 0) { - pam_syslog(NULL, LOG_DEBUG, - "unable to change perms on copy %s: %m", newdest); - close(srcfd); - close(destfd); - closedir(d); + if (pam_modutil_write(destfd, remark, res) == res) + continue; + } - free(newsource); - free(newdest); + /* 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"); + retval = PAM_PERM_DENIED; + goto go_out; + } + while (res != 0); - retval = PAM_PERM_DENIED; - goto go_out; - } + go_out: + if (srcfd >= 0) + close(srcfd); + if (destfd >= 0) + close(destfd); - /* Copy the file */ - do - { - res = pam_modutil_read(srcfd, remark, sizeof(remark)); + free(newsource); + free(newdest); - if (res == 0) - continue; + return retval; +} - if (res > 0) { - if (pam_modutil_write(destfd, remark, res) == res) - continue; - } +/* Do the actual work of creating a home dir */ +static int +create_homedir(const struct passwd *pwd, mode_t dir_mode, + const char *source, const char *dest) +{ + DIR *d = NULL; + struct dirent *dent; + int retval = PAM_SESSION_ERR; - /* 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); + /* Create the new directory */ + if (mkdir(dest, 0700)) + { + if (errno == EEXIST) + return PAM_SUCCESS; + pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest); + return PAM_PERM_DENIED; + } - free(newsource); - free(newdest); + /* See if we need to copy the skel dir over. */ + if ((source == NULL) || (strlen(source) == 0)) + { + retval = PAM_SUCCESS; + goto go_out; + } - retval = PAM_PERM_DENIED; - goto go_out; - } - while (res != 0); - close(srcfd); - close(destfd); + /* 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; + } - free(newsource); - free(newdest); + for (dent = readdir(d); dent != NULL; dent = readdir(d)) + { + /* Skip some files.. */ + if (strcmp(dent->d_name,".") == 0 || + strcmp(dent->d_name,"..") == 0) + continue; + retval = copy_entry(pwd, dir_mode, source, dest, dent); + if (retval != PAM_SUCCESS) + goto go_out; } - closedir(d); retval = PAM_SUCCESS; go_out: + if (d != NULL) + closedir(d); if (chmod(dest, dir_mode) != 0 || chown(dest, pwd->pw_uid, pwd->pw_gid) != 0) -- cgit v1.2.3