+static void
+create_and_populate_homedir(struct passwd *pwd)
+{
+ struct userconf *cnf = conf.userconf;
+ const char *skeldir;
+ int skelfd = -1;
+
+ skeldir = cnf->dotdir;
+
+ if (skeldir != NULL && *skeldir != '\0') {
+ if (*skeldir == '/')
+ skeldir++;
+ skelfd = openat(conf.rootfd, skeldir, O_DIRECTORY|O_CLOEXEC);
+ }
+
+ copymkdir(conf.rootfd, pwd->pw_dir, skelfd, cnf->homemode, pwd->pw_uid,
+ pwd->pw_gid, 0);
+ pw_log(cnf, M_ADD, W_USER, "%s(%u) home %s made", pwd->pw_name,
+ pwd->pw_uid, pwd->pw_dir);
+}
+
+static int
+set_passwd(struct passwd *pwd, bool update)
+{
+ int b, istty;
+ struct termios t, n;
+ login_cap_t *lc;
+ char line[_PASSWORD_LEN+1];
+ char *p;
+
+ if (conf.fd == '-') {
+ if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
+ pwd->pw_passwd = "*"; /* No access */
+ return (1);
+ }
+ return (0);
+ }
+
+ if ((istty = isatty(conf.fd))) {
+ if (tcgetattr(conf.fd, &t) == -1)
+ istty = 0;
+ else {
+ n = t;
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(conf.fd, TCSANOW, &n);
+ printf("%s%spassword for user %s:",
+ update ? "new " : "",
+ conf.precrypted ? "encrypted " : "",
+ pwd->pw_name);
+ fflush(stdout);
+ }
+ }
+ b = read(conf.fd, line, sizeof(line) - 1);
+ if (istty) { /* Restore state */
+ tcsetattr(conf.fd, TCSANOW, &t);
+ fputc('\n', stdout);
+ fflush(stdout);
+ }
+
+ if (b < 0)
+ err(EX_IOERR, "-%c file descriptor",
+ conf.precrypted ? 'H' : 'h');
+ line[b] = '\0';
+ if ((p = strpbrk(line, "\r\n")) != NULL)
+ *p = '\0';
+ if (!*line)
+ errx(EX_DATAERR, "empty password read on file descriptor %d",
+ conf.fd);
+ if (conf.precrypted) {
+ if (strchr(line, ':') != NULL)
+ errx(EX_DATAERR, "bad encrypted password");
+ pwd->pw_passwd = line;
+ } else {
+ lc = login_getpwclass(pwd);
+ if (lc == NULL ||
+ login_setcryptfmt(lc, "sha512", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_pwcrypt(line);
+ }
+ return (1);
+}
+
+int
+pw_usernext(struct userconf *cnf, bool quiet)
+{
+ uid_t next = pw_uidpolicy(cnf, -1);
+
+ if (quiet)
+ return (next);
+
+ printf("%u:", next);
+ pw_groupnext(cnf, quiet);
+
+ return (EXIT_SUCCESS);
+}
+
+static int
+pw_usershow(char *name, long id, struct passwd *fakeuser)
+{
+ struct passwd *pwd = NULL;
+
+ if (id < 0 && name == NULL && !conf.all)
+ errx(EX_DATAERR, "username or id or '-a' required");
+
+ if (conf.all) {
+ SETPWENT();
+ while ((pwd = GETPWENT()) != NULL)
+ print_user(pwd);
+ ENDPWENT();
+ return (EXIT_SUCCESS);
+ }
+
+ pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
+ if (pwd == NULL) {
+ if (conf.force) {
+ pwd = fakeuser;
+ } else {
+ if (name == NULL)
+ errx(EX_NOUSER, "no such uid `%ld'", id);
+ errx(EX_NOUSER, "no such user `%s'", name);
+ }
+ }
+
+ return (print_user(pwd));
+}
+
+static void
+perform_chgpwent(const char *name, struct passwd *pwd)
+{
+ int rc;
+
+ rc = chgpwent(name, pwd);
+ if (rc == -1)
+ errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name);
+ else if (rc != 0)
+ err(EX_IOERR, "passwd file update");
+
+ if (conf.userconf->nispasswd && *conf.userconf->nispasswd == '/') {
+ rc = chgnispwent(conf.userconf->nispasswd, name, 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 */
+ }
+}
+
+/*
+ * 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.
+ */
+static int
+pw_userlock(char *name, long id, int mode)
+{
+ struct passwd *pwd = NULL;
+ char *passtmp = NULL;
+ bool locked = false;
+
+ if (id < 0 && name == NULL)
+ errx(EX_DATAERR, "username or id required");
+
+ pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
+ if (pwd == NULL) {
+ if (name == NULL)
+ errx(EX_NOUSER, "no such uid `%ld'", id);
+ errx(EX_NOUSER, "no such user `%s'", name);
+ }
+
+ if (name == NULL)
+ name = pwd->pw_name;
+
+ if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0)
+ locked = true;
+ if (mode == M_LOCK && locked)
+ errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
+ if (mode == M_UNLOCK && !locked)
+ errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
+
+ if (mode == M_LOCK) {
+ asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd);
+ if (passtmp == NULL) /* disaster */
+ errx(EX_UNAVAILABLE, "out of memory");
+ pwd->pw_passwd = passtmp;
+ } else {
+ pwd->pw_passwd += sizeof(locked_str)-1;
+ }
+
+ perform_chgpwent(name, pwd);
+ free(passtmp);
+
+ return (EXIT_SUCCESS);
+}
+