X-Git-Url: https://git.cameronkatri.com/pw-darwin.git/blobdiff_plain/3ac6f2f73aa5de1c99b76016bedf656a78c400a4..a7ad3937e5ac46dd20036eb87518c32bad76e953:/pw/pw_user.c diff --git a/pw/pw_user.c b/pw/pw_user.c index 0eb9802..9d00bfa 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -27,7 +27,7 @@ #ifndef lint static const char rcsid[] = - "$Id: pw_user.c,v 1.27 1999/02/23 07:15:10 davidn Exp $"; + "$FreeBSD$"; #endif /* not lint */ #include @@ -42,6 +42,7 @@ static const char rcsid[] = #include #include #include +#include #if defined(USE_MD5RAND) #include #endif @@ -54,7 +55,7 @@ static const char rcsid[] = #define LOGNAMESIZE (MAXLOGNAME-1) #endif -static randinit; +static char locked_str[] = "*LOCKED*"; static int print_user(struct passwd * pwd, int pretty, int v7); static uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args); @@ -102,8 +103,9 @@ static void rmskey(char const * name); int pw_user(struct userconf * cnf, int mode, struct cargs * args) { - int r, r1; + int rc, edited = 0; char *p = NULL; + char *passtmp; struct carg *a_name; struct carg *a_uid; struct carg *arg; @@ -121,11 +123,15 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) 0, "", "User &", + "/nonexistent", "/bin/sh", - 0, 0 +#if defined(__FreeBSD__) + ,0 +#endif }; + /* * With M_NEXT, we only need to return the * next uid to stdout @@ -203,7 +209,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home); } - if ((arg = getarg(args, 'e')) != NULL) cnf->expire_days = atoi(arg->val); @@ -214,22 +219,25 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) cnf->password_days = atoi(arg->val); if ((arg = getarg(args, 'g')) != NULL) { - p = arg->val; - if ((grp = GETGRNAM(p)) == NULL) { - if (!isdigit(*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) - errx(EX_NOUSER, "group `%s' does not exist", p); + if (!*(p = arg->val)) /* Handle empty group list specially */ + cnf->default_group = ""; + else { + if ((grp = GETGRNAM(p)) == NULL) { + if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) + errx(EX_NOUSER, "group `%s' does not exist", p); + } + cnf->default_group = newstr(grp->gr_name); } - cnf->default_group = newstr(grp->gr_name); } if ((arg = getarg(args, 'L')) != NULL) cnf->default_class = pw_checkname((u_char *)arg->val, 0); if ((arg = getarg(args, 'G')) != NULL && arg->val) { - int i = 0; + int i = 0; for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { if ((grp = GETGRNAM(p)) == NULL) { - if (!isdigit(*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) + if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) errx(EX_NOUSER, "group `%s' does not exist", p); } if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1) @@ -238,13 +246,17 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) while (i < cnf->numgroups) cnf->groups[i++] = NULL; } + if ((arg = getarg(args, 'k')) != NULL) { if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode)) errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir); } + if ((arg = getarg(args, 's')) != NULL) cnf->shell_default = arg->val; + if ((arg = getarg(args, 'w')) != NULL) + cnf->default_password = boolean_val(arg->val, cnf->default_password); if (mode == M_ADD && getarg(args, 'D')) { if (getarg(args, 'n') != NULL) errx(EX_DATAERR, "can't combine `-D' with `-n name'"); @@ -260,8 +272,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid) cnf->max_gid = 32000; } - if ((arg = getarg(args, 'w')) != NULL) - cnf->default_password = boolean_val(arg->val, cnf->default_password); arg = getarg(args, 'C'); if (write_userconfig(arg ? arg->val : NULL)) @@ -269,6 +279,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) warn("config update"); return EX_IOERR; } + if (mode == M_PRINT && getarg(args, 'a')) { int pretty = getarg(args, 'P') != NULL; int v7 = getarg(args, '7') != NULL; @@ -279,6 +290,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) ENDPWENT(); return EXIT_SUCCESS; } + if ((a_name = getarg(args, 'n')) != NULL) pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0)); a_uid = getarg(args, 'u'); @@ -299,10 +311,13 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) a_name = NULL; } } + /* * Update, delete & print require that the user exists */ - if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) { + if (mode == M_UPDATE || mode == M_DELETE || + mode == M_PRINT || mode == M_LOCK || mode == M_UNLOCK) { + if (a_name == NULL && pwd == NULL) /* Try harder */ pwd = GETPWUID(atoi(a_uid->val)); @@ -318,13 +333,39 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) errx(EX_NOUSER, "no such uid `%s'", a_uid->val); errx(EX_NOUSER, "no such user `%s'", a_name->val); } + if (a_name == NULL) /* May be needed later */ a_name = addarg(args, 'n', newstr(pwd->pw_name)); /* - * Handle deletions now + * The M_LOCK and M_UNLOCK functions simply add or remove + * a "*LOCKED*" prefix from in front of the password to + * prevent it decoding correctly, and therefore prevents + * access. Of course, this only prevents access via + * password authentication (not ssh, kerberos or any + * other method that does not use the UNIX password) but + * that is a known limitation. */ - if (mode == M_DELETE) { + + if (mode == M_LOCK) { + if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0) + errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name); + passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str)); + if (passtmp == NULL) /* disaster */ + errx(EX_UNAVAILABLE, "out of memory"); + strcpy(passtmp, locked_str); + strcat(passtmp, pwd->pw_passwd); + pwd->pw_passwd = passtmp; + edited = 1; + } else if (mode == M_UNLOCK) { + if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) != 0) + errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name); + pwd->pw_passwd += sizeof(locked_str)-1; + edited = 1; + } else if (mode == M_DELETE) { + /* + * Handle deletions now + */ char file[MAXPATHLEN]; char home[MAXPATHLEN]; uid_t uid = pwd->pw_uid; @@ -356,12 +397,23 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) strncpy(home, pwd->pw_dir, sizeof home); home[sizeof home - 1] = '\0'; - if (!delpwent(pwd)) - err(EX_IOERR, "error updating passwd file"); + rc = delpwent(pwd); + if (rc == -1) + err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); + else if (rc != 0) { + warn("passwd update"); + return EX_IOERR; + } + + if (cnf->nispasswd && *cnf->nispasswd=='/') { + rc = delnispwent(cnf->nispasswd, a_name->val); + if (rc == -1) + warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name); + else if (rc != 0) + warn("WARNING: NIS passwd update"); + /* non-fatal */ + } - if (cnf->nispasswd && *cnf->nispasswd=='/' && !delnispwent(cnf->nispasswd, a_name->val)) - warn("WARNING: NIS passwd update"); - editgroups(a_name->val, NULL); pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid); @@ -403,48 +455,87 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't rename `root' account"); pwd->pw_name = pw_checkname((u_char *)arg->val, 0); + edited = 1; } - if ((arg = getarg(args, 'u')) != NULL && isdigit(*arg->val)) { + + if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) { pwd->pw_uid = (uid_t) atol(arg->val); + edited = 1; if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't change uid of `root' account"); if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name); } - if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) /* Already checked this */ - pwd->pw_gid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid; + + if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) { /* Already checked this */ + gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid; + if (newgid != pwd->pw_gid) { + edited = 1; + pwd->pw_gid = newgid; + } + } if ((arg = getarg(args, 'p')) != NULL) { - if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) - pwd->pw_change = 0; + if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) { + if (pwd->pw_change != 0) { + pwd->pw_change = 0; + edited = 1; + } + } else { 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); - pwd->pw_change = expire; + if (pwd->pw_change != expire) { + pwd->pw_change = expire; + edited = 1; + } } } + if ((arg = getarg(args, 'e')) != NULL) { - if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) - pwd->pw_expire = 0; + if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) { + if (pwd->pw_expire != 0) { + pwd->pw_expire = 0; + edited = 1; + } + } else { 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); - pwd->pw_expire = expire; + if (pwd->pw_expire != expire) { + pwd->pw_expire = expire; + edited = 1; + } + } + } + + if ((arg = getarg(args, 's')) != NULL) { + char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val); + if (shell == NULL) + shell = ""; + if (strcmp(shell, pwd->pw_shell) != 0) { + pwd->pw_shell = shell; + edited = 1; } } - if ((arg = getarg(args, 's')) != NULL) - pwd->pw_shell = shell_path(cnf->shelldir, cnf->shells, arg->val); - if (getarg(args, 'L')) - pwd->pw_class = cnf->default_class; + if (getarg(args, 'L')) { + if (cnf->default_class == NULL) + cnf->default_class = ""; + if (strcmp(pwd->pw_class, cnf->default_class) != 0) { + pwd->pw_class = cnf->default_class; + edited = 1; + } + } if ((arg = getarg(args, 'd')) != NULL) { + edited = strcmp(pwd->pw_dir, arg->val) != 0; 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); @@ -452,10 +543,25 @@ 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) { + login_cap_t *lc; + + 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_password(cnf, args, pwd->pw_name); + edited = 1; + } } else { + login_cap_t *lc; + + /* + * Add code + */ + if (a_name == NULL) /* Required */ errx(EX_DATAERR, "login name required"); else if ((pwd = GETPWNAM(a_name->val)) != NULL) /* Exists */ @@ -467,13 +573,18 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) pwd = &fakeuser; pwd->pw_name = a_name->val; pwd->pw_class = cnf->default_class ? cnf->default_class : ""; - pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); pwd->pw_uid = pw_uidpolicy(cnf, args); pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid); pwd->pw_change = pw_pwdpolicy(cnf, args); pwd->pw_expire = pw_exppolicy(cnf, args); pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name); pwd->pw_shell = pw_shellpolicy(cnf, args, NULL); + 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_password(cnf, args, pwd->pw_name); + edited = 1; if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name); @@ -482,17 +593,26 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) /* * Shared add/edit code */ - if ((arg = getarg(args, 'c')) != NULL) - pwd->pw_gecos = pw_checkname((u_char *)arg->val, 1); + if ((arg = getarg(args, 'c')) != NULL) { + char *gecos = pw_checkname((u_char *)arg->val, 1); + if (strcmp(pwd->pw_gecos, gecos) != 0) { + pwd->pw_gecos = gecos; + edited = 1; + } + } if ((arg = getarg(args, 'h')) != NULL) { - if (strcmp(arg->val, "-") == 0) - pwd->pw_passwd = "*"; /* No access */ - else { + if (strcmp(arg->val, "-") == 0) { + if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { + pwd->pw_passwd = "*"; /* No access */ + edited = 1; + } + } else { int fd = atoi(arg->val); int b; int istty = isatty(fd); struct termios t; + login_cap_t *lc; if (istty) { if (tcgetattr(fd, &t) == -1) @@ -522,7 +642,13 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) *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); + edited = 1; } } @@ -534,23 +660,43 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) getarg(args, 'P') != NULL, getarg(args, '7') != NULL); - r = r1 = 1; if (mode == M_ADD) { - r = addpwent(pwd); - if (r && cnf->nispasswd && *cnf->nispasswd=='/') - r1 = addnispwent(cnf->nispasswd, pwd); - } else if (mode == M_UPDATE) { - r = chgpwent(a_name->val, pwd); - if (r && cnf->nispasswd && *cnf->nispasswd=='/') - r1 = chgnispwent(cnf->nispasswd, a_name->val, pwd); - } - - if (!r) { - warn("password update"); - return EX_IOERR; - } else if (!r1) { - warn("WARNING: NIS password update"); - /* Keep on trucking */ + edited = 1; /* Always */ + rc = addpwent(pwd); + if (rc == -1) { + warnx("user '%s' already exists", pwd->pw_name); + return EX_IOERR; + } else if (rc != 0) { + warn("passwd file update"); + return EX_IOERR; + } + if (cnf->nispasswd && *cnf->nispasswd=='/') { + rc = addnispwent(cnf->nispasswd, pwd); + if (rc == -1) + warnx("User '%s' already exists in NIS passwd", pwd->pw_name); + else + warn("NIS passwd update"); + /* NOTE: we treat NIS-only update errors as non-fatal */ + } + } else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) { + if (edited) { /* Only updated this if required */ + rc = chgpwent(a_name->val, pwd); + if (rc == -1) { + warnx("user '%s' does not exist (NIS?)", pwd->pw_name); + return EX_IOERR; + } else if (rc != 0) { + warn("passwd file update"); + return EX_IOERR; + } + if ( cnf->nispasswd && *cnf->nispasswd=='/') { + rc = chgnispwent(cnf->nispasswd, a_name->val, pwd); + if (rc == -1) + warn("User '%s' not found in NIS passwd", pwd->pw_name); + else + warn("NIS passwd update"); + /* NOTE: NIS-only update errors are not fatal */ + } + } } /* @@ -560,8 +706,16 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if (mode == M_ADD || getarg(args, 'G') != NULL) editgroups(pwd->pw_name, cnf->groups); - /* pwd may have been invalidated */ - if ((pwd = GETPWNAM(a_name->val)) == NULL) + /* go get a current version of pwd */ + pwd = GETPWNAM(a_name->val); + if (pwd == NULL) { + /* This will fail when we rename, so special case that */ + if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) { + a_name->val = arg->val; /* update new name */ + pwd = GETPWNAM(a_name->val); /* refetch renamed rec */ + } + } + if (pwd == NULL) /* can't go on without this */ errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val); grp = GETGRGID(pwd->pw_gid); @@ -606,6 +760,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) } } } + /* * Finally, let's create and populate the user's home directory. Note * that this also `works' for editing users if -m is used, but @@ -616,6 +771,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) pw_log(cnf, mode, W_USER, "%s(%ld) home %s made", pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir); } + return EXIT_SUCCESS; } @@ -696,7 +852,7 @@ pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer if (a_gid != NULL) { if ((grp = GETGRNAM(a_gid->val)) == NULL) { gid = (gid_t) atol(a_gid->val); - if ((gid == 0 && !isdigit(*a_gid->val)) || (grp = GETGRGID(gid)) == NULL) + if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL) errx(EX_NOUSER, "group `%s' is not defined", a_gid->val); } gid = grp->gr_gid; @@ -734,9 +890,9 @@ pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer if ((grp = GETGRNAM(nam)) != NULL) gid = grp->gr_gid; } - a_gid = grpargs.lh_first; + a_gid = LIST_FIRST(&grpargs); while (a_gid != NULL) { - struct carg *t = a_gid->list.le_next; + struct carg *t = LIST_NEXT(a_gid, list); LIST_REMOVE(a_gid, list); a_gid = t; } @@ -856,16 +1012,8 @@ pw_pwcrypt(char *password) /* * Calculate a salt value */ - if (!randinit) { - randinit = 1; -#ifdef __FreeBSD__ - srandomdev(); -#else - srandom((unsigned long) (time(NULL) ^ getpid())); -#endif - } for (i = 0; i < 8; i++) - salt[i] = chars[random() % 63]; + salt[i] = chars[arc4random() % 63]; salt[i] = '\0'; return strcpy(buf, crypt(password, salt)); @@ -898,7 +1046,7 @@ pw_getrand(u_char *buf, int len) /* cryptographically secure rng */ 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-n)); + memcpy(buf+i, ubuf, MIN(16, len-i)); } return buf; } @@ -929,15 +1077,7 @@ pw_password(struct userconf * cnf, struct cargs * args, char const * user) switch (cnf->default_password) { case -1: /* Random password */ - if (!randinit) { - randinit = 1; -#ifdef __FreeBSD__ - srandomdev(); -#else - srandom((unsigned long) (time(NULL) ^ getpid())); -#endif - } - l = (random() % 8 + 8); /* 8 - 16 chars */ + 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)]; @@ -947,7 +1087,7 @@ pw_password(struct userconf * cnf, struct cargs * args, char const * user) * We give this information back to the user */ if (getarg(args, 'h') == NULL && getarg(args, 'N') == NULL) { - if (isatty(1)) + if (isatty(STDOUT_FILENO)) printf("Password for '%s' is: ", user); printf("%s\n", pwbuf); fflush(stdout); @@ -1012,12 +1152,12 @@ print_user(struct passwd * pwd, int pretty, int v7) memmove(p + l, p + 1, m); memmove(p, pwd->pw_name, l); - *p = (char) toupper(*p); + *p = (char) toupper((unsigned char)*p); } if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL) - strftime(acexpire, sizeof acexpire, "%c", tptr); - if (pwd->pw_change > (time_t)9 && (tptr = localtime(&pwd->pw_change)) != NULL) - strftime(pwexpire, sizeof pwexpire, "%c", tptr); + strftime(acexpire, sizeof acexpire, "%c", tptr); + if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) + strftime(pwexpire, sizeof pwexpire, "%c", tptr); printf("Login Name: %-15s #%-12ld Group: %-15s #%ld\n" " Full Name: %s\n" " Home: %-26.26s Class: %s\n" @@ -1045,7 +1185,7 @@ print_user(struct passwd * pwd, int pretty, int v7) } } ENDGRENT(); - printf("%s\n", j ? "\n" : ""); + printf("%s", j ? "\n" : ""); } return EXIT_SUCCESS; }