aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-05-15 08:51:53 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-05-15 08:51:53 +0300
commit996f0205833e24cdaa8c76bf7b2888137f020e4d (patch)
treeb3a0eac1b9ebdfe53bd2708ed3282f365a24a933
parent930096120da43c8b0ace147788fc786a8da40590 (diff)
downloadpam-modules-996f0205833e24cdaa8c76bf7b2888137f020e4d.tar.gz
pam-modules-996f0205833e24cdaa8c76bf7b2888137f020e4d.tar.bz2
pam_ldaphome: implement populate_homedir.
-rw-r--r--pam_ldaphome/pam_ldaphome.c409
1 files changed, 389 insertions, 20 deletions
diff --git a/pam_ldaphome/pam_ldaphome.c b/pam_ldaphome/pam_ldaphome.c
index bee64a2..3ad3e87 100644
--- a/pam_ldaphome/pam_ldaphome.c
+++ b/pam_ldaphome/pam_ldaphome.c
@@ -23,6 +23,10 @@
#include <security/pam_modules.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ldap.h>
@@ -587,18 +591,148 @@ check_user_groups(pam_handle_t *pamh, struct gray_env *env,
}
return 0;
}
+
+static int
+copy(int src_fd, int dst_fd, char *buffer, size_t bufsize)
+{
+ ssize_t n;
+
+ while ((n = read(src_fd, buffer, bufsize)) > 0) {
+ n = write(dst_fd, buffer, n);
+ if (n < 0)
+ break;
+ }
+ return n;
+}
static int
-populate_homedir(struct passwd *pw, const char *skel)
+copy_file(pam_handle_t *pamh, const char *src, const char *dst,
+ char *buffer, size_t bufsize, struct stat *st)
{
- // FIXME!!!
- _pam_log(LOG_ERR, "populate_homedir is not yet implemented!");
- return 1;
+ int sfd, dfd, rc;
+
+ sfd = open(src, O_RDONLY);
+ if (sfd == -1) {
+ _pam_log(LOG_ERR, "cannot open %s: %s",
+ src, strerror(errno));
+ return 1;
+ }
+
+ dfd = open(dst, O_CREAT|O_TRUNC|O_RDWR, 0600);
+ if (dfd == -1) {
+ close(sfd);
+ _pam_log(LOG_ERR, "cannot create %s: %s",
+ dst, strerror(errno));
+ return 1;
+ }
+ if (fchown(dfd, st->st_uid, st->st_gid) ||
+ fchmod(dfd, st->st_mode & 07777)) {
+ _pam_log(LOG_ERR, "cannot set privileges of %s: %s",
+ dst, strerror(errno));
+ /* try to continue anyway */
+ }
+
+ rc = copy(sfd, dfd, buffer, bufsize);
+ if (rc)
+ _pam_log(LOG_ERR, "I/O error copying %s to %s: %s",
+ src, dst, strerror(errno));
+
+ close(sfd);
+ close(dfd);
+
+ return rc;
+}
+
+#define INITIAL_READLINK_SIZE 128
+
+int
+read_link_name(const char *name, char **pbuf, size_t *psize, size_t *plen)
+{
+ int rc = 0;
+ char *buf = *pbuf;
+ size_t size = *psize;
+ ssize_t linklen;
+
+ while (1) {
+ if (!buf) {
+ size = INITIAL_READLINK_SIZE;
+ buf = malloc(size);
+ } else {
+ char *p;
+ size_t newsize = size << 1;
+ if (newsize < size) {
+ rc = ENAMETOOLONG;
+ break;
+ }
+ size = newsize;
+ p = realloc(buf, size);
+ if (!p)
+ free(buf);
+ buf = p;
+ }
+ if (!buf) {
+ rc = 1;
+ break;
+ }
+
+ linklen = readlink(name, buf, size);
+ if (linklen < 0 && errno != ERANGE) {
+ rc = 1;
+ break;
+ }
+
+ if ((size_t) linklen < size) {
+ buf[linklen++] = '\0';
+ rc = 0;
+ break;
+ }
+ }
+
+ if (rc) {
+ if (buf) {
+ free (buf);
+ buf = NULL;
+ }
+ size = 0;
+ }
+ *pbuf = buf;
+ *psize = size;
+ if (plen)
+ *plen = linklen;
+ return rc;
}
+
+static int
+copy_link(pam_handle_t *pamh, const char *src, const char *dst,
+ char *buffer, size_t bufsize, struct stat *st)
+{
+ char *lnkname = NULL;
+ size_t lnklen = 0;
+ int rc;
+
+ if (read_link_name(src, &lnkname, &lnklen, NULL)) {
+ _pam_log(LOG_ERR, "error reading link %s: %s",
+ src, strerror(errno));
+ return 1;
+ }
+ rc = symlink(lnkname, dst);
+ if (rc)
+ _pam_log(LOG_ERR, "can't link %s to %s: %s",
+ src, dst, strerror(errno));
+ else if (lchown(dst, st->st_uid, st->st_gid)) {
+ _pam_log(LOG_ERR, "cannot set privileges of %s: %s",
+ dst, strerror(errno));
+ /* try to continue anyway */
+ }
+
+ free(lnkname);
+ return rc;
+}
+
/* Create the directory DIR, eventually creating all intermediate directories
starting from DIR + BASELEN. */
-int
+static int
create_hierarchy(char *dir, size_t baselen)
{
int rc;
@@ -641,7 +775,7 @@ create_hierarchy(char *dir, size_t baselen)
return rc;
}
-int
+static int
create_interdir(const char *path, struct passwd *pw)
{
char *dir, *p;
@@ -661,8 +795,256 @@ create_interdir(const char *path, struct passwd *pw)
free(dir);
return rc;
}
+
+struct namebuf {
+ char *name;
+ size_t size;
+ size_t prefix_len;
+};
static void
+namebuf_trimslash(struct namebuf *buf)
+{
+ size_t len = strlen(buf->name);
+ while (len > 0 && buf->name[len-1] == '/')
+ --len;
+ buf->name[len] = 0;
+}
+
+static int
+namebuf_init(struct namebuf *buf, const char *name)
+{
+ buf->name = strdup(name);
+ if (!buf->name)
+ return 1;
+ buf->prefix_len = strlen(name);
+ buf->size = buf->prefix_len + 1;
+ namebuf_trimslash(buf);
+ return 0;
+}
+
+static int
+namebuf_set(struct namebuf *buf, const char *name)
+{
+ size_t len;
+
+ if (!buf->name)
+ return namebuf_init(buf, name);
+ len = strlen(name);
+ if (buf->prefix_len + len + 1 > buf->size) {
+ size_t ns;
+ char *np;
+
+ for (ns = buf->size; buf->prefix_len + len + 1 > ns;
+ ns += ns)
+ ;
+
+ np = realloc(buf->name, ns);
+ if (!np)
+ return 1;
+ buf->size = ns;
+ buf->name = np;
+ }
+
+ strcpy(buf->name + buf->prefix_len, name);
+ //namebuf_trimslash(buf);
+
+ return 0;
+}
+
+static size_t
+namebuf_set_prefix(struct namebuf *buf)
+{
+ size_t ret;
+ ret = buf->prefix_len;
+ buf->prefix_len = strlen(buf->name);
+ if (namebuf_set(buf, "/"))
+ return 1;
+ ++buf->prefix_len;
+ return ret;
+}
+
+static void
+namebuf_set_prefix_len(struct namebuf *buf, size_t len)
+{
+ buf->prefix_len = len;
+ buf->name[len] = 0;
+}
+
+static int recursive_copy(pam_handle_t *pamh, DIR *dir,
+ struct namebuf *srcbuf, struct namebuf *dstbuf,
+ char *buffer, size_t bufsize, struct passwd *pw,
+ struct stat *st);
+
+static int
+dir_copy_loop(pam_handle_t *pamh, DIR *dir,
+ struct namebuf *srcbuf, struct namebuf *dstbuf,
+ char *buffer, size_t bufsize, struct passwd *pw)
+{
+ struct dirent *ent;
+ struct stat dir_st;
+
+ while ((ent = readdir(dir))) {
+ char const *ename = ent->d_name;
+ struct stat st;
+ int rc;
+
+ if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0)
+ continue;
+ if (namebuf_set(srcbuf, ename)) {
+ _pam_log(LOG_ERR, "copy error: %s", strerror(errno));
+ return 1;
+ }
+ if (namebuf_set(dstbuf, ename)) {
+ _pam_log(LOG_ERR, "copy error: %s", strerror(errno));
+ return 1;
+ }
+ if (lstat(srcbuf->name, &st)) {
+ _pam_log(LOG_ERR, "cannot stat %s: %s",
+ srcbuf->name, strerror(errno));
+ return 1;
+ }
+ st.st_uid = pw->pw_uid;
+ st.st_gid = pw->pw_gid;
+ if (S_ISREG(st.st_mode))
+ rc = copy_file(pamh, srcbuf->name, dstbuf->name,
+ buffer, bufsize, &st);
+ else if (S_ISDIR(st.st_mode)) {
+ DIR *nd = opendir(srcbuf->name);
+ if (!nd) {
+ _pam_log(LOG_ERR,
+ "cannot open directory %s: %s",
+ srcbuf->name, strerror(errno));
+ rc = 1;
+ } else {
+ size_t srclen = namebuf_set_prefix(srcbuf);
+ size_t dstlen = namebuf_set_prefix(dstbuf);
+ rc = recursive_copy(pamh, nd, srcbuf, dstbuf,
+ buffer, bufsize, pw,
+ &st);
+ closedir(nd);
+ namebuf_set_prefix_len(dstbuf, dstlen);
+ namebuf_set_prefix_len(srcbuf, srclen);
+ }
+ } else if (S_ISLNK(st.st_mode))
+ rc = copy_link(pamh, srcbuf->name, dstbuf->name,
+ buffer, bufsize, &st);
+ else {
+ _pam_log(LOG_NOTICE,
+ "ignoring file %s: unsupported file type",
+ srcbuf->name);
+ rc = 0;
+ }
+
+ if (rc)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+recursive_copy(pam_handle_t *pamh, DIR *dir,
+ struct namebuf *srcbuf, struct namebuf *dstbuf,
+ char *buffer, size_t bufsize, struct passwd *pw,
+ struct stat *st)
+{
+ int rc;
+ struct stat dst_st;
+
+ if (stat(dstbuf->name, &dst_st)) {
+ if (errno == ENOENT) {
+ if (mkdir(dstbuf->name, 0700)) {
+ _pam_log(LOG_ERR, "cannot create %s: %s",
+ dstbuf->name, strerror(errno));
+ return 1;
+ }
+ } else {
+ _pam_log(LOG_ERR, "cannot stat %s: %s",
+ dstbuf->name, strerror(errno));
+ return 1;
+ }
+ }
+
+ rc = dir_copy_loop(pamh, dir, srcbuf, dstbuf, buffer, bufsize, pw);
+ dstbuf->name[dstbuf->prefix_len-1] = 0;
+ if (chown(dstbuf->name, pw->pw_uid, pw->pw_gid) ||
+ (st && chmod(dstbuf->name, st->st_mode & 07777))) {
+ _pam_log(LOG_ERR,
+ "cannot set privileges of %s:"
+ "%s",
+ dstbuf->name,
+ strerror(errno));
+ }
+
+ return rc;
+}
+
+#define MIN_BUF_SIZE 2
+#define MAX_BUF_SIZE 16384
+
+static int
+populate_homedir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
+{
+ const char *skel;
+ char *buffer;
+ size_t bufsize;
+ struct stat st;
+ unsigned long n;
+ DIR *dir;
+ int rc;
+
+ skel = gray_env_get(env, "skel");
+ if (!skel)
+ return 0;
+
+ if (stat(skel, &st)) {
+ _pam_log(LOG_ERR, "cannot stat skeleton directory %s: %s",
+ pw->pw_dir, strerror(errno));
+ return 1;
+ } else if (!S_ISDIR(st.st_mode)) {
+ _pam_log(LOG_ERR, "%s exists, but is not a directory",
+ pw->pw_dir);
+ return 1;
+ }
+
+ if (get_intval(env, "copy-buf-size", &n) == 0)
+ bufsize = n;
+ else
+ bufsize = MAX_BUF_SIZE;
+
+ for (; (buffer = malloc(bufsize)) == NULL; bufsize >>= 1)
+ if (bufsize < MIN_BUF_SIZE)
+ return ENOMEM;
+
+ dir = opendir(skel);
+ if (!dir) {
+ _pam_log(LOG_ERR, "cannot open skeleton directory %s: %s",
+ skel, strerror(errno));
+ rc = 1;
+ } else {
+ struct namebuf srcbuf, dstbuf;
+
+ if (namebuf_init(&srcbuf, skel) == 0) {
+ namebuf_set_prefix(&srcbuf);
+ if (namebuf_init(&dstbuf, pw->pw_dir) == 0) {
+ namebuf_set_prefix(&dstbuf);
+ rc = recursive_copy(pamh, dir,
+ &srcbuf, &dstbuf,
+ buffer, bufsize, pw,
+ NULL);
+ free(dstbuf.name);
+ } else
+ rc = 1;
+ free(srcbuf.name);
+ } else
+ rc = 1;
+ closedir(dir);
+ }
+ free(buffer);
+ return rc;
+}
+
+static void
store_pubkey(const char *key, struct passwd *pw)
{
FILE *fp;
@@ -772,7 +1154,6 @@ static int
create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
{
struct stat st;
- char *s;
if (stat(pw->pw_dir, &st)) {
if (errno != ENOENT) {
@@ -786,6 +1167,7 @@ create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
pw->pw_dir, strerror(errno));
return 1;
}
+ populate_homedir(pamh, pw, env);
chown(pw->pw_dir, pw->pw_uid, pw->pw_gid);
} else if (!S_ISDIR(st.st_mode)) {
_pam_log(LOG_ERR, "%s exists, but is not a directory",
@@ -793,19 +1175,6 @@ create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
return 1;
}
- s = gray_env_get(env, "skel");
- if (s) {
- if (stat(s, &st)) {
- _pam_log(LOG_ERR, "cannot stat skeleton directory %s: %s",
- pw->pw_dir, strerror(errno));
- return 1;
- } else if (!S_ISDIR(st.st_mode)) {
- _pam_log(LOG_ERR, "%s exists, but is not a directory",
- pw->pw_dir);
- return 1;
- }
- populate_homedir(pw, s);
- }
return 0;
}

Return to:

Send suggestions and report system problems to the System administrator.