#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 <ctype.h>
#include <sys/resource.h>
#include <unistd.h>
#include <utmp.h>
+#include <login_cap.h>
#if defined(USE_MD5RAND)
#include <md5.h>
#endif
#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);
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;
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
errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
}
-
if ((arg = getarg(args, 'e')) != NULL)
cnf->expire_days = atoi(arg->val);
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)
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'");
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))
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;
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');
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));
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;
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);
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);
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 */
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);
/*
* 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)
*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;
}
}
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 */
+ }
+ }
}
/*
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);
}
}
}
+
/*
* Finally, let's create and populate the user's home directory. Note
* that this also `works' for editing users if -m is used, but
pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
}
+
return EXIT_SUCCESS;
}
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;
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;
}
/*
* 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));
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;
}
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)];
* 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);
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"
}
}
ENDGRENT();
- printf("%s\n", j ? "\n" : "");
+ printf("%s", j ? "\n" : "");
}
return EXIT_SUCCESS;
}