1066 lines
27 KiB
Diff
1066 lines
27 KiB
Diff
diff -up /dev/null Linux-PAM-1.0.90/modules/pam_mkhomedir/mkhomedir_helper.c
|
|
--- /dev/null 2009-01-09 08:55:49.164063715 +0100
|
|
+++ Linux-PAM-1.0.90/modules/pam_mkhomedir/mkhomedir_helper.c 2009-01-19 10:15:39.000000000 +0100
|
|
@@ -0,0 +1,422 @@
|
|
+/* 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 char skeldir[BUFSIZ] = "/etc/skel";
|
|
+
|
|
+static int
|
|
+rec_mkdir(const char *dir, mode_t mode)
|
|
+{
|
|
+ char *cp;
|
|
+ char *parent = strdup(dir);
|
|
+
|
|
+ if (parent == NULL)
|
|
+ return 1;
|
|
+
|
|
+ cp = strrchr(parent, '/');
|
|
+
|
|
+ if (cp != NULL && cp != parent)
|
|
+ {
|
|
+ struct stat st;
|
|
+
|
|
+ *cp++ = '\0';
|
|
+ if (stat(parent, &st) == -1 && errno == ENOENT)
|
|
+ if (rec_mkdir(parent, mode) != 0)
|
|
+ {
|
|
+ free(parent);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ free(parent);
|
|
+
|
|
+ if (mkdir(dir, mode) != 0 && errno != EEXIST)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* 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 (rec_mkdir(dest, 0755) != 0)
|
|
+ {
|
|
+ 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 src file %s: %m", newsource);
|
|
+ closedir(d);
|
|
+
|
|
+#ifndef PATH_MAX
|
|
+ free(newsource); newsource = NULL;
|
|
+ free(newdest); newdest = NULL;
|
|
+#endif
|
|
+
|
|
+ return PAM_PERM_DENIED;
|
|
+ }
|
|
+ if (stat(newsource, &st) != 0)
|
|
+ {
|
|
+ pam_syslog(NULL, LOG_DEBUG, "unable to stat src file %s: %m",
|
|
+ newsource);
|
|
+ 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 preseves
|
|
+ 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;
|
|
+}
|
|
+
|
|
+int
|
|
+main(int argc, char *argv[])
|
|
+{
|
|
+ const struct passwd *pwd;
|
|
+ struct stat st;
|
|
+
|
|
+ if (argc < 2) {
|
|
+ fprintf(stderr, "Usage: %s <username> [<umask> [<skeldir>]]\n", argv[0]);
|
|
+ return PAM_SESSION_ERR;
|
|
+ }
|
|
+
|
|
+ pwd = getpwnam(argv[1]);
|
|
+ if (pwd == NULL) {
|
|
+ pam_syslog(NULL, LOG_ERR, "User unknown.");
|
|
+ return PAM_CRED_INSUFFICIENT;
|
|
+ }
|
|
+
|
|
+ if (argc >= 3) {
|
|
+ char *eptr;
|
|
+ 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]);
|
|
+ }
|
|
+
|
|
+ /* 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;
|
|
+
|
|
+ return create_homedir(pwd, skeldir, pwd->pw_dir);
|
|
+}
|
|
+
|
|
diff -up Linux-PAM-1.0.90/modules/pam_mkhomedir/Makefile.am.mkhomedir-helper Linux-PAM-1.0.90/modules/pam_mkhomedir/Makefile.am
|
|
--- Linux-PAM-1.0.90/modules/pam_mkhomedir/Makefile.am.mkhomedir-helper 2007-09-03 09:57:30.000000000 +0200
|
|
+++ Linux-PAM-1.0.90/modules/pam_mkhomedir/Makefile.am 2009-01-19 10:15:39.000000000 +0100
|
|
@@ -1,21 +1,23 @@
|
|
#
|
|
# Copyright (c) 2005, 2006 Thorsten Kukuk <kukuk@suse.de>
|
|
+# Copyright (c) 2008 Red Hat, Inc.
|
|
#
|
|
|
|
CLEANFILES = *~
|
|
|
|
EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_mkhomedir
|
|
|
|
-man_MANS = pam_mkhomedir.8
|
|
+man_MANS = pam_mkhomedir.8 mkhomedir_helper.8
|
|
|
|
-XMLS = README.xml pam_mkhomedir.8.xml
|
|
+XMLS = README.xml pam_mkhomedir.8.xml mkhomedir_helper.8.xml
|
|
|
|
TESTS = tst-pam_mkhomedir
|
|
|
|
securelibdir = $(SECUREDIR)
|
|
secureconfdir = $(SCONFIGDIR)
|
|
|
|
-AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
|
|
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
|
+ -DMKHOMEDIR_HELPER=\"$(sbindir)/mkhomedir_helper\"
|
|
AM_LDFLAGS = -no-undefined -avoid-version -module
|
|
if HAVE_VERSIONING
|
|
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
|
@@ -25,6 +27,10 @@ securelib_LTLIBRARIES = pam_mkhomedir.la
|
|
pam_mkhomedir_la_SOURCES = pam_mkhomedir.c
|
|
pam_mkhomedir_la_LIBADD = -L$(top_builddir)/libpam -lpam
|
|
|
|
+sbin_PROGRAMS = mkhomedir_helper
|
|
+mkhomedir_helper_SOURCES = mkhomedir_helper.c
|
|
+mkhomedir_helper_LDADD = -L$(top_builddir)/libpam -lpam
|
|
+
|
|
if ENABLE_REGENERATE_MAN
|
|
noinst_DATA = README
|
|
README: pam_mkhomedir.8.xml
|
|
diff -up /dev/null Linux-PAM-1.0.90/modules/pam_mkhomedir/mkhomedir_helper.8.xml
|
|
--- /dev/null 2009-01-09 08:55:49.164063715 +0100
|
|
+++ Linux-PAM-1.0.90/modules/pam_mkhomedir/mkhomedir_helper.8.xml 2009-01-19 10:15:39.000000000 +0100
|
|
@@ -0,0 +1,78 @@
|
|
+<?xml version="1.0" encoding='UTF-8'?>
|
|
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
|
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
|
|
+
|
|
+<refentry id="mkhomedir_helper">
|
|
+
|
|
+ <refmeta>
|
|
+ <refentrytitle>mkhomedir_helper</refentrytitle>
|
|
+ <manvolnum>8</manvolnum>
|
|
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
|
|
+ </refmeta>
|
|
+
|
|
+ <refnamediv id="mkhomedir_helper-name">
|
|
+ <refname>mkhomedir_helper</refname>
|
|
+ <refpurpose>Helper binary that creates home directories</refpurpose>
|
|
+ </refnamediv>
|
|
+
|
|
+ <refsynopsisdiv>
|
|
+ <cmdsynopsis id="mkhomedir_helper-cmdsynopsis">
|
|
+ <command>mkhomedir_helper</command>
|
|
+ <arg choice="req">
|
|
+ <replaceable>user</replaceable>
|
|
+ </arg>
|
|
+ <arg choice="opt">
|
|
+ <replaceable>umask</replaceable>
|
|
+ <arg choice="opt">
|
|
+ <replaceable>path-to-skel</replaceable>
|
|
+ </arg>
|
|
+ </arg>
|
|
+ </cmdsynopsis>
|
|
+ </refsynopsisdiv>
|
|
+
|
|
+ <refsect1 id="mkhomedir_helper-description">
|
|
+
|
|
+ <title>DESCRIPTION</title>
|
|
+
|
|
+ <para>
|
|
+ <emphasis>mkhomedir_helper</emphasis> is a helper program for the
|
|
+ <emphasis>pam_mkhomedir</emphasis> module that creates home directories
|
|
+ and populates them with contents of the specified skel directory.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ The default value of <replaceable>umask</replaceable> is 0022 and the
|
|
+ default value of <replaceable>path-to-skel</replaceable> is
|
|
+ <emphasis>/etc/skel</emphasis>.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ The helper is separated from the module to not require direct access from
|
|
+ login SELinux domains to the contents of user home directories. The
|
|
+ SELinux domain transition happens when the module is executing the
|
|
+ <emphasis>mkhomedir_helper</emphasis>.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ The helper never touches home directories if they already exist.
|
|
+ </para>
|
|
+ </refsect1>
|
|
+
|
|
+ <refsect1 id='mkhomedir_helper-see_also'>
|
|
+ <title>SEE ALSO</title>
|
|
+ <para>
|
|
+ <citerefentry>
|
|
+ <refentrytitle>pam_mkhomedir</refentrytitle><manvolnum>8</manvolnum>
|
|
+ </citerefentry>
|
|
+ </para>
|
|
+ </refsect1>
|
|
+
|
|
+ <refsect1 id='mkhomedir_helper-author'>
|
|
+ <title>AUTHOR</title>
|
|
+ <para>
|
|
+ Written by Tomas Mraz based on the code originally in
|
|
+ <emphasis>pam_mkhomedir</emphasis> module.
|
|
+ </para>
|
|
+ </refsect1>
|
|
+
|
|
+</refentry>
|
|
diff -up Linux-PAM-1.0.90/modules/pam_mkhomedir/pam_mkhomedir.c.mkhomedir-helper Linux-PAM-1.0.90/modules/pam_mkhomedir/pam_mkhomedir.c
|
|
--- Linux-PAM-1.0.90/modules/pam_mkhomedir/pam_mkhomedir.c.mkhomedir-helper 2007-06-28 11:00:30.000000000 +0200
|
|
+++ Linux-PAM-1.0.90/modules/pam_mkhomedir/pam_mkhomedir.c 2009-01-19 10:15:39.000000000 +0100
|
|
@@ -22,6 +22,7 @@
|
|
password required pam_unix.so
|
|
|
|
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
|
|
@@ -29,18 +30,19 @@
|
|
|
|
#include "config.h"
|
|
|
|
-#include <stdarg.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
-#include <fcntl.h>
|
|
+#include <sys/time.h>
|
|
+#include <sys/resource.h>
|
|
+#include <sys/wait.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 <signal.h>
|
|
|
|
/*
|
|
* here, we make a definition for the externally accessible function
|
|
@@ -56,12 +58,13 @@
|
|
#include <security/pam_modutil.h>
|
|
#include <security/pam_ext.h>
|
|
|
|
+#define MAX_FD_NO 10000
|
|
|
|
/* argument parsing */
|
|
#define MKHOMEDIR_DEBUG 020 /* be verbose about things */
|
|
#define MKHOMEDIR_QUIET 040 /* keep quiet about things */
|
|
|
|
-static unsigned int UMask = 0022;
|
|
+static char UMask[16] = "0022";
|
|
static char SkelDir[BUFSIZ] = "/etc/skel"; /* THIS MODULE IS NOT THREAD SAFE */
|
|
|
|
static int
|
|
@@ -81,7 +84,8 @@ _pam_parse (const pam_handle_t *pamh, in
|
|
} else if (!strcmp(*argv, "debug")) {
|
|
ctrl |= MKHOMEDIR_DEBUG;
|
|
} else if (!strncmp(*argv,"umask=",6)) {
|
|
- UMask = strtol(*argv+6,0,0);
|
|
+ strncpy(SkelDir,*argv+6,sizeof(UMask));
|
|
+ UMask[sizeof(UMask)-1] = '\0';
|
|
} else if (!strncmp(*argv,"skel=",5)) {
|
|
strncpy(SkelDir,*argv+5,sizeof(SkelDir));
|
|
SkelDir[sizeof(SkelDir)-1] = '\0';
|
|
@@ -94,357 +98,88 @@ _pam_parse (const pam_handle_t *pamh, in
|
|
return ctrl;
|
|
}
|
|
|
|
-static int
|
|
-rec_mkdir (const char *dir, mode_t mode)
|
|
-{
|
|
- char *cp;
|
|
- char *parent = strdup (dir);
|
|
-
|
|
- if (parent == NULL)
|
|
- return 1;
|
|
-
|
|
- cp = strrchr (parent, '/');
|
|
-
|
|
- if (cp != NULL && cp != parent)
|
|
- {
|
|
- struct stat st;
|
|
-
|
|
- *cp++ = '\0';
|
|
- if (stat (parent, &st) == -1 && errno == ENOENT)
|
|
- if (rec_mkdir (parent, mode) != 0)
|
|
- {
|
|
- free (parent);
|
|
- return 1;
|
|
- }
|
|
- }
|
|
-
|
|
- free (parent);
|
|
-
|
|
- if (mkdir (dir, mode) != 0 && errno != EEXIST)
|
|
- return 1;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
/* Do the actual work of creating a home dir */
|
|
static int
|
|
-create_homedir (pam_handle_t * pamh, int ctrl,
|
|
- const struct passwd *pwd,
|
|
- const char *source, const char *dest)
|
|
+create_homedir (pam_handle_t *pamh, int ctrl,
|
|
+ const struct passwd *pwd)
|
|
{
|
|
- char remark[BUFSIZ];
|
|
- DIR *D;
|
|
- struct dirent *Dir;
|
|
- int retval = PAM_AUTH_ERR;
|
|
+ int retval, child;
|
|
+ void (*sighandler)(int) = NULL;
|
|
|
|
/* Mention what is happening, if the notification fails that is OK */
|
|
- if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET)
|
|
- pam_info(pamh, _("Creating directory '%s'."), dest);
|
|
+ if (!(ctrl & MKHOMEDIR_QUIET))
|
|
+ pam_info(pamh, _("Creating directory '%s'."), pwd->pw_dir);
|
|
|
|
- /* Create the new directory */
|
|
- if (rec_mkdir (dest,0755) != 0)
|
|
- {
|
|
- pam_error(pamh, _("Unable to create directory %s: %m"), dest);
|
|
- pam_syslog(pamh, 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;
|
|
- }
|
|
+ D(("called."));
|
|
|
|
- /* Scan the directory */
|
|
- D = opendir (source);
|
|
- if (D == 0)
|
|
- {
|
|
- pam_syslog(pamh, LOG_DEBUG, "unable to read directory %s: %m", source);
|
|
- retval = PAM_PERM_DENIED;
|
|
- goto go_out;
|
|
- }
|
|
-
|
|
- for (Dir = readdir(D); Dir != 0; Dir = 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(Dir->d_name,".") == 0 ||
|
|
- strcmp(Dir->d_name,"..") == 0)
|
|
- continue;
|
|
-
|
|
- /* Determine what kind of file it is. */
|
|
-#ifndef PATH_MAX
|
|
- nslen = slen + strlen(Dir->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, Dir->d_name);
|
|
-#else
|
|
- snprintf(newsource,sizeof(newsource),"%s/%s",source,Dir->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(Dir->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;
|
|
+ /*
|
|
+ * This code arranges that the demise of the child does not cause
|
|
+ * the application to receive a signal it is not expecting - which
|
|
+ * may kill the application or worse.
|
|
+ */
|
|
+ sighandler = signal(SIGCHLD, SIG_DFL);
|
|
+
|
|
+ if (ctrl & MKHOMEDIR_DEBUG) {
|
|
+ pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
|
|
+ }
|
|
+
|
|
+ /* fork */
|
|
+ child = fork();
|
|
+ if (child == 0) {
|
|
+ int i;
|
|
+ struct rlimit rlim;
|
|
+ static char *envp[] = { NULL };
|
|
+ char *args[] = { NULL, NULL, NULL, NULL, NULL };
|
|
+
|
|
+ if (getrlimit(RLIMIT_NOFILE, &rlim)==0) {
|
|
+ if (rlim.rlim_max >= MAX_FD_NO)
|
|
+ rlim.rlim_max = MAX_FD_NO;
|
|
+ for (i=0; i < (int)rlim.rlim_max; i++) {
|
|
+ close(i);
|
|
+ }
|
|
}
|
|
|
|
- sprintf (newdest, "%s/%s", dest, Dir->d_name);
|
|
-#else
|
|
- snprintf (newdest,sizeof (newdest),"%s/%s",dest,Dir->d_name);
|
|
-#endif
|
|
-
|
|
- /* If it's a directory, recurse. */
|
|
- if (S_ISDIR(St.st_mode))
|
|
- {
|
|
- retval = create_homedir (pamh, ctrl, 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 = (char *) malloc(size);
|
|
- if ( ! pointed ) {
|
|
- 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(pamh, 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(pamh, LOG_DEBUG,
|
|
- "unable to open src file %s: %m", newsource);
|
|
- closedir(D);
|
|
-
|
|
-#ifndef PATH_MAX
|
|
- free(newsource); newsource = NULL;
|
|
- free(newdest); newdest = NULL;
|
|
-#endif
|
|
-
|
|
- return PAM_PERM_DENIED;
|
|
- }
|
|
- if (stat(newsource,&St) != 0)
|
|
- {
|
|
- pam_syslog(pamh, LOG_DEBUG, "unable to stat src file %s: %m",
|
|
- newsource);
|
|
- close(SrcFd);
|
|
- closedir(D);
|
|
-
|
|
-#ifndef PATH_MAX
|
|
- free(newsource); newsource = NULL;
|
|
- free(newdest); newdest = NULL;
|
|
-#endif
|
|
-
|
|
- return PAM_PERM_DENIED;
|
|
+ /* exec the mkhomedir helper */
|
|
+ args[0] = x_strdup(MKHOMEDIR_HELPER);
|
|
+ args[1] = pwd->pw_name;
|
|
+ args[2] = UMask;
|
|
+ args[3] = SkelDir;
|
|
+
|
|
+ execve(MKHOMEDIR_HELPER, args, envp);
|
|
+
|
|
+ /* should not get here: exit with error */
|
|
+ D(("helper binary is not available"));
|
|
+ exit(PAM_SYSTEM_ERR);
|
|
+ } else if (child > 0) {
|
|
+ int rc;
|
|
+ while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
|
|
+ if (rc < 0) {
|
|
+ pam_syslog(pamh, LOG_ERR, "waitpid failed: %m");
|
|
+ retval = PAM_SYSTEM_ERR;
|
|
+ } else {
|
|
+ retval = WEXITSTATUS(retval);
|
|
}
|
|
-
|
|
- /* Open the dest file */
|
|
- if ((DestFd = open(newdest,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
|
|
- {
|
|
- pam_syslog(pamh, 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 preseves
|
|
- execute bits */
|
|
- if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 ||
|
|
- fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0)
|
|
- {
|
|
- pam_syslog(pamh, 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(pamh, 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
|
|
-
|
|
+ } else {
|
|
+ D(("fork failed"));
|
|
+ pam_syslog(pamh, LOG_ERR, "fork failed: %m");
|
|
+ retval = PAM_SYSTEM_ERR;
|
|
}
|
|
- closedir(D);
|
|
|
|
- retval = PAM_SUCCESS;
|
|
+ if (sighandler != SIG_ERR) {
|
|
+ (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
|
|
+ }
|
|
|
|
- go_out:
|
|
+ if (ctrl & MKHOMEDIR_DEBUG) {
|
|
+ pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
|
|
+ }
|
|
|
|
- if (chmod(dest,0777 & (~UMask)) != 0 ||
|
|
- chown(dest,pwd->pw_uid,pwd->pw_gid) != 0)
|
|
- {
|
|
- pam_syslog(pamh, LOG_DEBUG,
|
|
- "unable to change perms on directory %s: %m", dest);
|
|
- return PAM_PERM_DENIED;
|
|
+ if (retval != PAM_SUCCESS && !(ctrl & MKHOMEDIR_QUIET)) {
|
|
+ pam_error(pamh, _("Unable to create and initialize directory '%s'."),
|
|
+ pwd->pw_dir);
|
|
}
|
|
|
|
+ D(("returning %d", retval));
|
|
return retval;
|
|
}
|
|
|
|
@@ -466,7 +201,7 @@ pam_sm_open_session (pam_handle_t *pamh,
|
|
retval = pam_get_item(pamh, PAM_USER, &user);
|
|
if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0')
|
|
{
|
|
- pam_syslog(pamh, LOG_NOTICE, "user unknown");
|
|
+ pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
|
|
return PAM_USER_UNKNOWN;
|
|
}
|
|
|
|
@@ -474,16 +209,22 @@ pam_sm_open_session (pam_handle_t *pamh,
|
|
pwd = pam_modutil_getpwnam (pamh, user);
|
|
if (pwd == NULL)
|
|
{
|
|
+ pam_syslog(pamh, LOG_NOTICE, "User unknown.");
|
|
D(("couldn't identify user %s", user));
|
|
return PAM_CRED_INSUFFICIENT;
|
|
}
|
|
|
|
/* Stat the home directory, if something exists then we assume it is
|
|
correct and return a success*/
|
|
- if (stat(pwd->pw_dir,&St) == 0)
|
|
+ if (stat(pwd->pw_dir, &St) == 0) {
|
|
+ if (ctrl & MKHOMEDIR_DEBUG) {
|
|
+ pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.",
|
|
+ pwd->pw_dir);
|
|
+ }
|
|
return PAM_SUCCESS;
|
|
+ }
|
|
|
|
- return create_homedir(pamh,ctrl,pwd,SkelDir,pwd->pw_dir);
|
|
+ return create_homedir(pamh, ctrl, pwd);
|
|
}
|
|
|
|
/* Ignore */
|