]> git.cameronkatri.com Git - pw-darwin.git/blobdiff - pw/pw_user.c
There is no reason to disallow setting the password or account expiry
[pw-darwin.git] / pw / pw_user.c
index a001e3e89fb4b489f60df96991ff1c90f5dca5e0..4d7e7ce4d3a49ba06dd76e898b39b6a48eb2662a 100644 (file)
@@ -41,19 +41,14 @@ static const char rcsid[] =
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
-#include <utmp.h>
 #include <login_cap.h>
-#if defined(USE_MD5RAND)
-#include <md5.h>
-#endif
+#include <pwd.h>
+#include <grp.h>
+#include <libutil.h>
 #include "pw.h"
 #include "bitmap.h"
 
-#if (MAXLOGNAME-1) > UT_NAMESIZE
-#define LOGNAMESIZE UT_NAMESIZE
-#else
 #define LOGNAMESIZE (MAXLOGNAME-1)
-#endif
 
 static         char locked_str[] = "*LOCKED*";
 
@@ -86,6 +81,7 @@ static void     rmopie(char const * name);
  * -L class       user class
  * -l name        new login name
  * -h fd          password filehandle
+ * -H fd          encrypted password filehandle
  * -F             force print or add
  *   Setting defaults:
  * -D             set user defaults
@@ -114,6 +110,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
        struct stat     st;
        char            line[_PASSWORD_LEN+1];
        FILE           *fp;
+       char *dmode_c;
+       void *set = NULL;
 
        static struct passwd fakeuser =
        {
@@ -155,6 +153,15 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                cnf->home = arg->val;
        }
 
+       if ((arg = getarg(args, 'M')) != NULL) {
+               dmode_c = arg->val;
+               if ((set = setmode(dmode_c)) == NULL)
+                       errx(EX_DATAERR, "invalid directory creation mode '%s'",
+                           dmode_c);
+               cnf->homemode = getmode(set, _DEF_DIRMODE);
+               free(set);
+       }
+
        /*
         * If we'll need to use it or we're updating it,
         * then create the base home directory if necessary
@@ -180,19 +187,23 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                        if (strchr(cnf->home+1, '/') == NULL) {
                                strcpy(dbuf, "/usr");
                                strncat(dbuf, cnf->home, MAXPATHLEN-5);
-                               if (mkdir(dbuf, 0755) != -1 || errno == EEXIST) {
+                               if (mkdir(dbuf, _DEF_DIRMODE) != -1 || errno == EEXIST) {
                                        chown(dbuf, 0, 0);
-                                       symlink(dbuf, cnf->home);
+                                       /*
+                                        * Skip first "/" and create symlink:
+                                        * /home -> usr/home
+                                        */
+                                       symlink(dbuf+1, cnf->home);
                                }
                                /* If this falls, fall back to old method */
                        }
-                       p = strncpy(dbuf, cnf->home, sizeof dbuf);
-                       dbuf[MAXPATHLEN-1] = '\0';
+                       strlcpy(dbuf, cnf->home, sizeof(dbuf));
+                       p = dbuf;
                        if (stat(dbuf, &st) == -1) {
-                               while ((p = strchr(++p, '/')) != NULL) {
+                               while ((p = strchr(p + 1, '/')) != NULL) {
                                        *p = '\0';
                                        if (stat(dbuf, &st) == -1) {
-                                               if (mkdir(dbuf, 0755) == -1)
+                                               if (mkdir(dbuf, _DEF_DIRMODE) == -1)
                                                        goto direrr;
                                                chown(dbuf, 0, 0);
                                        } else if (!S_ISDIR(st.st_mode))
@@ -201,7 +212,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                }
                        }
                        if (stat(dbuf, &st) == -1) {
-                               if (mkdir(dbuf, 0755) == -1) {
+                               if (mkdir(dbuf, _DEF_DIRMODE) == -1) {
                                direrr: err(EX_OSFILE, "mkdir '%s'", dbuf);
                                }
                                chown(dbuf, 0, 0);
@@ -284,7 +295,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
        if (mode == M_PRINT && getarg(args, 'a')) {
                int             pretty = getarg(args, 'P') != NULL;
                int             v7 = getarg(args, '7') != NULL;
-
                SETPWENT();
                while ((pwd = GETPWENT()) != NULL)
                        print_user(pwd, pretty, v7);
@@ -307,7 +317,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                 */
                if (mode != M_ADD && pwd == NULL
                    && strspn(a_name->val, "0123456789") == strlen(a_name->val)
-                   && atoi(a_name->val) > 0) { /* Assume uid */
+                   && *a_name->val) {
                        (a_uid = a_name)->ch = 'u';
                        a_name = NULL;
                }
@@ -384,7 +394,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                /*
                                 * Remove crontabs
                                 */
-                               sprintf(file, "/var/cron/tabs/%s", pwd->pw_name);
+                               snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
                                if (access(file, F_OK) == 0) {
                                        sprintf(file, "crontab -u %s -r", pwd->pw_name);
                                        system(file);
@@ -395,8 +405,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                         * invalidated by deletion
                         */
                        sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
-                       strncpy(home, pwd->pw_dir, sizeof home);
-                       home[sizeof home - 1] = '\0';
+                       strlcpy(home, pwd->pw_dir, sizeof(home));
 
                        rc = delpwent(pwd);
                        if (rc == -1)
@@ -415,7 +424,24 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                /* non-fatal */
                        }
 
-                       editgroups(a_name->val, NULL);
+                       grp = GETGRNAM(a_name->val);
+                       if (grp != NULL && *grp->gr_mem == NULL)
+                               delgrent(GETGRNAM(a_name->val));
+                       SETGRENT();
+                       while ((grp = GETGRENT()) != NULL) {
+                               int i;
+                               char group[MAXLOGNAME];
+                               for (i = 0; grp->gr_mem[i] != NULL; i++) {
+                                       if (!strcmp(grp->gr_mem[i], a_name->val)) {
+                                               while (grp->gr_mem[i] != NULL) {
+                                                       grp->gr_mem[i] = grp->gr_mem[i+1];
+                                               }       
+                                               strlcpy(group, grp->gr_name, MAXLOGNAME);
+                                               chggrent(group, grp);
+                                       }
+                               }
+                       }
+                       ENDGRENT();
 
                        pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
 
@@ -487,8 +513,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                time_t          now = time(NULL);
                                time_t          expire = parse_date(now, arg->val);
 
-                               if (now == expire)
-                                       errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
                                if (pwd->pw_change != expire) {
                                        pwd->pw_change = expire;
                                        edited = 1;
@@ -507,8 +531,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                time_t          now = time(NULL);
                                time_t          expire = parse_date(now, arg->val);
 
-                               if (now == expire)
-                                       errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
                                if (pwd->pw_expire != expire) {
                                        pwd->pw_expire = expire;
                                        edited = 1;
@@ -536,7 +558,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                }
 
                if ((arg  = getarg(args, 'd')) != NULL) {
-                       edited = strcmp(pwd->pw_dir, arg->val) != 0;
+                       if (strcmp(pwd->pw_dir, arg->val))
+                               edited = 1;
                        if (stat(pwd->pw_dir = arg->val, &st) == -1) {
                                if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
                                  warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
@@ -544,7 +567,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
                }
 
-               if ((arg = getarg(args, 'w')) != NULL && getarg(args, 'h') == NULL) {
+               if ((arg = getarg(args, 'w')) != NULL &&
+                   getarg(args, 'h') == NULL && getarg(args, 'H') == NULL) {
                        login_cap_t *lc;
 
                        lc = login_getpwclass(pwd);
@@ -602,7 +626,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                }
        }
 
-       if ((arg = getarg(args, 'h')) != NULL) {
+       if ((arg = getarg(args, 'h')) != NULL ||
+           (arg = getarg(args, 'H')) != NULL) {
                if (strcmp(arg->val, "-") == 0) {
                        if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
                                pwd->pw_passwd = "*";   /* No access */
@@ -610,6 +635,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                        }
                } else {
                        int             fd = atoi(arg->val);
+                       int             precrypt = (arg->ch == 'H');
                        int             b;
                        int             istty = isatty(fd);
                        struct termios  t;
@@ -624,7 +650,10 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                        /* Disable echo */
                                        n.c_lflag &= ~(ECHO);
                                        tcsetattr(fd, TCSANOW, &n);
-                                       printf("%sassword for user %s:", (mode == M_UPDATE) ? "New p" : "P", pwd->pw_name);
+                                       printf("%s%spassword for user %s:",
+                                            (mode == M_UPDATE) ? "new " : "",
+                                            precrypt ? "encrypted " : "",
+                                            pwd->pw_name);
                                        fflush(stdout);
                                }
                        }
@@ -635,20 +664,27 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
                                fflush(stdout);
                        }
                        if (b < 0) {
-                               warn("-h file descriptor");
+                               warn("-%c file descriptor", precrypt ? 'H' :
+                                   'h');
                                return EX_IOERR;
                        }
                        line[b] = '\0';
-                       if ((p = strpbrk(line, " \t\r\n")) != NULL)
+                       if ((p = strpbrk(line, "\r\n")) != NULL)
                                *p = '\0';
                        if (!*line)
                                errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
-                       lc = login_getpwclass(pwd);
-                       if (lc == NULL ||
-                           login_setcryptfmt(lc, "md5", NULL) == NULL)
-                               warn("setting crypt(3) format");
-                       login_close(lc);
-                       pwd->pw_passwd = pw_pwcrypt(line);
+                       if (precrypt) {
+                               if (strchr(line, ':') != NULL)
+                                       return EX_DATAERR;
+                               pwd->pw_passwd = line;
+                       } else {
+                               lc = login_getpwclass(pwd);
+                               if (lc == NULL ||
+                                   login_setcryptfmt(lc, "md5", NULL) == NULL)
+                                       warn("setting crypt(3) format");
+                               login_close(lc);
+                               pwd->pw_passwd = pw_pwcrypt(line);
+                       }
                        edited = 1;
                }
        }
@@ -704,8 +740,24 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
         * Ok, user is created or changed - now edit group file
         */
 
-       if (mode == M_ADD || getarg(args, 'G') != NULL)
-               editgroups(pwd->pw_name, cnf->groups);
+       if (mode == M_ADD || getarg(args, 'G') != NULL) {
+               int i;
+               for (i = 0; cnf->groups[i] != NULL; i++) {
+                       grp = GETGRNAM(cnf->groups[i]);
+                       grp = gr_add(grp, pwd->pw_name);
+                       /*
+                        * grp can only be NULL in 2 cases:
+                        * - the new member is already a member
+                        * - a problem with memory occurs
+                        * in both cases we want to skip now.
+                        */
+                       if (grp == NULL)
+                               continue;
+                       chggrent(cnf->groups[i], grp);
+                       free(grp);
+               }
+       }
+
 
        /* go get a current version of pwd */
        pwd = GETPWNAM(a_name->val);
@@ -745,7 +797,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
         * existing files will *not* be overwritten.
         */
        if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
-               copymkdir(pwd->pw_dir, cnf->dotdir, 0755, pwd->pw_uid, pwd->pw_gid);
+               copymkdir(pwd->pw_dir, cnf->dotdir, cnf->homemode, pwd->pw_uid, pwd->pw_gid);
                pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
                       pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
        }
@@ -963,8 +1015,7 @@ shell_path(char const * path, char *shells[], char *sh)
                /*
                 * We need to search paths
                 */
-               strncpy(paths, path, sizeof paths);
-               paths[sizeof paths - 1] = '\0';
+               strlcpy(paths, path, sizeof(paths));
                for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
                        int             i;
                        static char     shellpath[256];
@@ -999,94 +1050,51 @@ pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
        return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
 }
 
-static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
+#define        SALTSIZE        32
+
+static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
 
 char           *
 pw_pwcrypt(char *password)
 {
        int             i;
-       char            salt[12];
+       char            salt[SALTSIZE + 1];
+       char            *cryptpw;
 
        static char     buf[256];
 
        /*
         * Calculate a salt value
         */
-       for (i = 0; i < 8; i++)
-               salt[i] = chars[arc4random() % 63];
-       salt[i] = '\0';
-
-       return strcpy(buf, crypt(password, salt));
-}
-
-#if defined(USE_MD5RAND)
-u_char *
-pw_getrand(u_char *buf, int len)       /* cryptographically secure rng */
-{
-       int i;
-       for (i=0;i<len;i+=16) {
-               u_char ubuf[16];
-
-               MD5_CTX md5_ctx;
-               struct timeval tv, tvo;
-               struct rusage ru;
-               int n=0;
-               int t;
-
-               MD5Init (&md5_ctx);
-               t=getpid();
-               MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
-               t=getppid();
-               MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
-               gettimeofday (&tvo, NULL);
-               do {
-                       getrusage (RUSAGE_SELF, &ru);
-                       MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
-                       gettimeofday (&tv, NULL);
-                       MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
-               } while (n++<20 || tv.tv_usec-tvo.tv_usec<100*1000);
-               MD5Final (ubuf, &md5_ctx);
-               memcpy(buf+i, ubuf, MIN(16, len-i));
-       }
-       return buf;
+       for (i = 0; i < SALTSIZE; i++)
+               salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
+       salt[SALTSIZE] = '\0';
+
+       cryptpw = crypt(password, salt);
+       if (cryptpw == NULL)
+               errx(EX_CONFIG, "crypt(3) failure");
+       return strcpy(buf, cryptpw);
 }
 
-#else  /* Portable version */
-
-static u_char *
-pw_getrand(u_char *buf, int len)
-{
-       int i;
-
-       for (i = 0; i < len; i++) {
-               unsigned long val = arc4random();
-               /* Use all bits in the random value */
-               buf[i]=(u_char)((val >> 24) ^ (val >> 16) ^ (val >> 8) ^ val);
-       }
-       return buf;
-}
-
-#endif
 
 static char    *
 pw_password(struct userconf * cnf, struct cargs * args, char const * user)
 {
        int             i, l;
        char            pwbuf[32];
-       u_char          rndbuf[sizeof pwbuf];
 
        switch (cnf->default_password) {
        case -1:                /* Random password */
                l = (arc4random() % 8 + 8);     /* 8 - 16 chars */
-               pw_getrand(rndbuf, l);
                for (i = 0; i < l; i++)
-                       pwbuf[i] = chars[rndbuf[i] % (sizeof(chars)-1)];
+                       pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
                pwbuf[i] = '\0';
 
                /*
                 * We give this information back to the user
                 */
-               if (getarg(args, 'h') == NULL && getarg(args, 'N') == NULL) {
+               if (getarg(args, 'h') == NULL && getarg(args, 'H') == NULL &&
+                   getarg(args, 'N') == NULL) {
                        if (isatty(STDOUT_FILENO))
                                printf("Password for '%s' is: ", user);
                        printf("%s\n", pwbuf);
@@ -1102,8 +1110,7 @@ pw_password(struct userconf * cnf, struct cargs * args, char const * user)
                return "*";
 
        case 1:         /* user's name */
-               strncpy(pwbuf, user, sizeof pwbuf);
-               pwbuf[sizeof pwbuf - 1] = '\0';
+               strlcpy(pwbuf, user, sizeof(pwbuf));
                break;
        }
        return pw_pwcrypt(pwbuf);
@@ -1114,10 +1121,14 @@ static int
 print_user(struct passwd * pwd, int pretty, int v7)
 {
        if (!pretty) {
-               char            buf[_UC_MAXLINE];
+               char            *buf;
+
+               if (!v7)
+                       pwd->pw_passwd = (pwd->pw_passwd == NULL) ? "" : "*";
 
-               fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD);
-               fputs(buf, stdout);
+               buf = v7 ? pw_make_v7(pwd) : pw_make(pwd);
+               printf("%s\n", buf);
+               free(buf);
        } else {
                int             j;
                char           *p;
@@ -1128,17 +1139,14 @@ print_user(struct passwd * pwd, int pretty, int v7)
                struct tm *    tptr;
 
                if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
-                       strncpy(uname, p, sizeof uname);
-                       uname[sizeof uname - 1] = '\0';
+                       strlcpy(uname, p, sizeof(uname));
                        if ((p = strtok(NULL, ",")) != NULL) {
-                               strncpy(office, p, sizeof office);
-                               office[sizeof office - 1] = '\0';
+                               strlcpy(office, p, sizeof(office));
                                if ((p = strtok(NULL, ",")) != NULL) {
-                                       strncpy(wphone, p, sizeof wphone);
-                                       wphone[sizeof wphone - 1] = '\0';
+                                       strlcpy(wphone, p, sizeof(wphone));
                                        if ((p = strtok(NULL, "")) != NULL) {
-                                               strncpy(hphone, p, sizeof hphone);
-                                               hphone[sizeof hphone - 1] = '\0';
+                                               strlcpy(hphone, p,
+                                                   sizeof(hphone));
                                        }
                                }
                        }
@@ -1239,7 +1247,7 @@ pw_checkname(u_char *name, int gecos)
        if (reject) {
                snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
                    ? "`%c'" : "0x%02x", *ch);
-               errx(EX_DATAERR, "invalid character %s at position %d in %s",
+               errx(EX_DATAERR, "invalid character %s at position %td in %s",
                    showch, (ch - name), showtype);
        }
        if (!gecos && (ch - name) > LOGNAMESIZE)