Merge remote-tracking branch 'origin/debian'
[hcoop/debian/courier-authlib.git] / authdaemon.c
index 927536c..7f24431 100644 (file)
@@ -1,5 +1,5 @@
 /*
-** Copyright 2000-2008 Double Precision, Inc.  See COPYING for
+** Copyright 2000-2015 Double Precision, Inc.  See COPYING for
 ** distribution information.
 */
 
 #include       <sys/time.h>
 #include       <sys/select.h>
 #include       "numlib/numlib.h"
-
+#include       <sys/stat.h>
+#include       <pwd.h>
+#include       <sys/types.h>
+#include       <dirent.h>
 
 extern int authdaemondo(const char *authreq,
        int (*func)(struct authinfo *, void *), void *arg);
@@ -111,3 +114,182 @@ int auth_callback_default(struct authinfo *ainfo)
 
        return 0;
 }
+
+static void do_copy(const char *from,
+                   const char *to,
+                   struct stat *stat_buf,
+                   uid_t uid,
+                   gid_t gid)
+{
+       FILE *fp=fopen(from, "r");
+       FILE *fp2;
+       int c;
+
+       if (!fp)
+       {
+               fprintf(stderr, "WARN: %s: %s!!\n", from, strerror(errno));
+               return;
+       }
+
+       fp2=fopen(to, "w");
+
+       if (!fp2)
+       {
+               fprintf(stderr, "WARN: %s: %s!!\n", to, strerror(errno));
+               fclose(fp);
+               return;
+       }
+
+       while ((c=getc(fp)) != EOF)
+               putc(c, fp2);
+       fclose(fp);
+       fclose(fp2);
+
+       if (chmod(to, stat_buf->st_mode & ~S_IFMT) ||
+           chown(to, uid, gid) < 0)
+       {
+               fprintf(stderr, "WARN: %s: %s!!\n", to, strerror(errno));
+       }
+}
+
+static void do_symlink(const char *from,
+                      const char *to,
+                      uid_t uid,
+                      gid_t gid)
+{
+       char buf[1024];
+       ssize_t l=readlink(from, buf, sizeof(buf)-1);
+
+       if (l < 0)
+       {
+               fprintf(stderr, "WARN: %s: %s!!\n", from, strerror(errno));
+               return;
+       }
+       buf[l]=0;
+
+       if (symlink(buf, to) < 0 || lchown(to, uid, gid) < 0)
+       {
+               fprintf(stderr, "WARN: %s: %s!!\n", from, strerror(errno));
+               return;
+       }
+}
+
+static int do_mkhomedir(const char *skeldir,
+                       const char *homedir,
+                       uid_t uid,
+                       gid_t gid)
+{
+       struct stat stat_buf;
+       DIR *d;
+       struct dirent *de;
+
+       mkdir(homedir, 0);
+
+       if (stat(skeldir, &stat_buf) < 0 ||
+           chmod(homedir, stat_buf.st_mode & ~S_IFMT) ||
+           chown(homedir, uid, gid) < 0)
+       {
+               fprintf(stderr, "WARN: %s: %s!!\n", skeldir, strerror(errno));
+               return 0;
+       }
+
+       if ((d=opendir(skeldir)) != NULL)
+       {
+               while ((de=readdir(d)) != NULL)
+               {
+                       char *fskeldir, *fhomedir;
+
+                       if (strcmp(de->d_name, ".") == 0)
+                               continue;
+                       if (strcmp(de->d_name, "..") == 0)
+                               continue;
+
+                       fskeldir=malloc(strlen(skeldir)+2+strlen(de->d_name));
+                       fhomedir=malloc(strlen(homedir)+2+strlen(de->d_name));
+
+                       if (fskeldir && fhomedir)
+                       {
+                               strcat(strcat(strcpy(fskeldir, skeldir), "/"),
+                                      de->d_name);
+                               strcat(strcat(strcpy(fhomedir, homedir), "/"),
+                                      de->d_name);
+
+                               if (lstat(fskeldir, &stat_buf) == 0)
+                               {
+                                       if (S_ISDIR(stat_buf.st_mode))
+                                       {
+                                               do_mkhomedir(fskeldir, fhomedir,
+                                                            uid, gid);
+                                       }
+                                       else if (S_ISLNK(stat_buf.st_mode))
+                                       {
+                                               do_symlink(fskeldir, fhomedir,
+                                                          uid, gid);
+                                       }
+                                       else if (S_ISREG(stat_buf.st_mode))
+                                       {
+                                               do_copy(fskeldir, fhomedir,
+                                                       &stat_buf,
+                                                       uid, gid);
+                                       }
+                               }
+                       }
+
+                       if (fskeldir)
+                               free(fskeldir);
+                       if (fhomedir)
+                               free(fhomedir);
+               }
+               closedir(d);
+       }
+       return 0;
+}
+
+static int mkhomedir(struct authinfo *ainfo,
+                    const char *skel)
+{
+       if (ainfo->sysusername)
+       {
+               struct passwd *pw=getpwnam(ainfo->sysusername);
+               return do_mkhomedir(skel, ainfo->homedir,
+                                   pw->pw_uid,
+                                   pw->pw_gid);
+       }
+
+       return do_mkhomedir(skel, ainfo->homedir,
+                           *ainfo->sysuserid, ainfo->sysgroupid);
+}
+
+int auth_mkhomedir(struct authinfo *ainfo)
+{
+       struct stat stat_buf;
+
+       const char *p=getenv("AUTH_MKHOMEDIR_SKEL");
+
+       if (p && *p && ainfo->address &&
+           (ainfo->sysusername || ainfo->sysuserid) &&
+           ainfo->homedir && stat(ainfo->homedir, &stat_buf) < 0 &&
+           errno == ENOENT && stat(p, &stat_buf) == 0)
+       {
+               int rc;
+               mode_t old_umask=umask(0777);
+
+               rc=mkhomedir(ainfo, p);
+
+               umask(old_umask);
+
+               if (rc)
+                       return rc;
+       }
+       return 0;
+}
+
+int auth_callback_default_autocreate(struct authinfo *ainfo)
+{
+       int rc=auth_mkhomedir(ainfo);
+
+       if (rc)
+               return rc;
+
+       return auth_callback_default(ainfo);
+}