#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*";
* -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
struct stat st;
char line[_PASSWORD_LEN+1];
FILE *fp;
+ char *dmode_c;
+ void *set = NULL;
static struct passwd fakeuser =
{
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
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))
}
}
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);
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);
*/
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;
}
/*
* 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);
* 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)
/* 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);
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;
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;
}
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);
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);
}
}
- 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 */
}
} else {
int fd = atoi(arg->val);
+ int precrypt = (arg->ch == 'H');
int b;
int istty = isatty(fd);
struct termios t;
/* 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);
}
}
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;
}
}
* 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);
* 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);
}
/*
* 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];
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);
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);
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;
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));
}
}
}
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)