Skip to content

Commit

Permalink
Redo lib/password.c to remove shadow.h.
Browse files Browse the repository at this point in the history
Implement internal common plumbing to modify colon separated files.
Doesn't handle "pam" but the design of pam is incompatible with static linking.
  • Loading branch information
landley committed Aug 26, 2023
1 parent f614f4b commit 6c30b35
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 182 deletions.
7 changes: 3 additions & 4 deletions lib/lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,9 @@ char *escape_url(char *str, char *and);
char *unescape_url(char *str, int do_cut);

// password.c
int get_salt(char *salt, char * algo);
int get_salt(char *salt, char *algo, int rand);
int read_password(char *buff, int buflen, char *mesg);
int update_password(char *filename, char *username, char *entry, int pos);

// commas.c
void comma_args(struct arg_list *al, void *data, char *err,
Expand Down Expand Up @@ -423,6 +425,3 @@ pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid);

#define minof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aa<bb ? aa : bb;})
#define maxof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aa>bb ? aa : bb;})

// Functions in need of further review/cleanup
#include "lib/pending.h"
149 changes: 82 additions & 67 deletions lib/password.c
Original file line number Diff line number Diff line change
@@ -1,45 +1,48 @@
/* password.c - password read/update helper functions.
*
* Copyright 2012 Ashwini Kumar <[email protected]>
*
* TODO: cleanup
*/

#include "toys.h"

// generate $id$salt$hash given a password and algorithm. Generates salt if NULL
//char *toy_crypt(char *pass, char *salt, char *algo)
// generate ID prefix and random salt for given encryption algorithm.
int get_salt(char *salt, char *algo)
int get_salt(char *salt, char *algo, int rand)
{
struct {
char *type, id, len;
} al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
int i;
char *s;
int i, len;

for (i = 0; i < ARRAY_LEN(al); i++) {
if (!strcmp(algo, al[i].type)) {
int len = al[i].len;
char *s = salt;

if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
for (s = al[i].type, len = 0; algo[len]; len++) {
while (ispunct(algo[len])) len++;
if (tolower(algo[len]) != tolower(*s++)) break;
}
if (algo[len]) continue;

// Read appropriate number of random bytes for salt
xgetrandom(libbuf, ((len*6)+7)/8);
len = al[i].len;
s = salt + (al[i].id ? sprintf(salt, "$%c$", '0'+al[i].id) : 0);

// Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
for (i = 0; i<len; i++) {
int bitpos = i*6, bits = bitpos/8;
// Read appropriate number of random bytes for salt
if (rand) xgetrandom(libbuf, ((len*6)+7)/8);

bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
bits += 46;
if (bits > 57) bits += 7;
if (bits > 90) bits += 6;
// Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
for (i = 0; i<len; i++) {
int bitpos = i*6, bits = bitpos/8;

s[i] = bits;
}
salt[len] = 0;
bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
bits += 46;
if (bits > 57) bits += 7;
if (bits > 90) bits += 6;

return s-salt;
s[i] = bits;
}
s[len] = 0;

return s-salt;
}

return -1;
Expand Down Expand Up @@ -79,79 +82,91 @@ int read_password(char *buf, int buflen, char *mesg)
return ret;
}

/* update colon-separated text files ala /etc/{passwd,shadow,group,gshadow}
* username = string match for first entry in line
* entry = new entry (NULL deletes matching line from file)
* pos = which entry to replace with "entry" (0 is first)
*/
// filename+ = new copy being written, filename- = backup of old version
// update colon-separated text files ala /etc/{passwd,shadow,group,gshadow}
// returns 1 for success, 0 for failure

// username = string match for first entry in line
// entry = new entry (NULL deletes matching line, contains : adds/replaces line)
// pos = if no : in "entry", which field to replace (0 is first)
// filename+ = new copy being written, filename- = backup of old version

int update_password(char *filename, char *username, char *entry, int pos)
{
char *filenamesfx = xmprintf("%s-", filename), *line = 0, *start, *end;
FILE *ofp, *nfp;
int ret = 0, found = 0, len = strlen(username)*!strchr(username, ':'), ii;
char *ff = xmprintf("%s-", filename), *line = 0, *start, *end, *out, *oo;
FILE *ofp;
int len = strlen(username)*!strchr(username,':'), rc = 0, ret = 0, found = 0,
nfd;
struct flock lock = {.l_type = F_WRLCK};
struct stat st;
long long ll = 0;

// Open old filename ("r" won't let us lock), get blocking lock
if (!(ofp = fopen(filename, "w+")) || 0>fcntl(fileno(ofp), F_SETLK, &lock)) {
perror_msg("%s", filename);
// Open old filename ("r" won't let us lock) and get blocking lock
if (!(ofp = fopen(filename, "w+")) || 0>fcntl(fileno(ofp), F_SETLK, &lock)\
|| fstat(fileno(ofp), &st))
{
perror_msg_raw(filename);
goto free_storage;
}

// Delete old backup, link new backup. (Failure here isn't fatal.)
unlink(filenamesfx);
if (0>link(filename, filenamesfx)) perror_msg("%s", filenamesfx);
unlink(ff);
if (0>link(filename, ff)) perror_msg_raw(ff);

// Open new file to copy entries to
filenamesfx[strlen(filenamesfx)-1] = '+';
if (!(nfp = fopen(filenamesfx, "w+"))) {
perror_msg("%s", filenamesfx);
ff[strlen(ff)-1] = '+';
if (-1 == (nfd = xcreate(ff, O_CREAT|O_EXCL|O_WRONLY, st.st_mode))) {
perror_msg_raw(ff);
goto free_storage;
}

// Loop through lines
while (getline(&line, (void *)&ll, ofp)) {
for (; getline(&line, (void *)&ll, ofp)!=-1; memset(line, 0, strlen(line))) {
// find matching line
start = end = line;
if (strncmp(chomp(line), username, len) || line[len]!=':') {
oo = 0;
start = end = chomp(line);
if (strncmp(line, username, len) || !(line[len] && line[len]!=':'))
out = line;
else {
found++;
if (!entry) continue;

// Find start and end of span to replace
for (ii = pos;;) {
while (*end != ':') {
if (!*end) break;
end++;
// Delete or replace whole line?
if (!entry) out = 0;
else if (strchr(entry, ':')) out = entry;

// Replace entry at pos
else {
for (;; pos--, start = ++end) {
while (*end && *end != ':') end++;
if (!pos || !*end) break;
}
if (ii) {
start = ++end;
ii--;
} else break;
if (pos>=0) out = line;
else oo = out = xmprintf("%*s%s%s\n", (int)(start-line),line,entry,end);
}
if (ii) start = end = line;
}

// Write with replacement (if any)
fprintf(nfp, "%*s%s%s\n", (int)(start-line), line,
(start==line) ? "" : entry, end);
memset(line, 0, strlen(line));
if (out) {
rc = dprintf(nfd, "%s\n", out);
free(out);
if (rc<0) {
perror_msg_raw(ff);
goto free_storage;
}
free(oo);
}
}
free(line);
fflush(nfp);
fsync(fileno(nfp));
fclose(nfp); // automatically unlocks
if (!found && entry && strchr(entry, ':')) dprintf(nfd, "%s\n", entry);
fsync(nfd);
close(nfd); // automatically unlocks

if (!found || rename(filenamesfx, filename)) {
if (found) perror_msg("%s -> %s", filenamesfx, filename);
else if (entry) fprintf(nfp, "%s\n", entry);
unlink(filenamesfx);
} else ret = 1;
if (!found || rename(ff, filename)) {
if (found) perror_msg("%s -> %s", ff, filename);
else if (entry) error_msg("No %s in %s", username, filename);
} else ret = 1, *ff = 0;

free_storage:
if (ofp) fclose(ofp);
free(filenamesfx);
if (ff && *ff) unlink(ff);
free(ff);

return ret;
}
6 changes: 0 additions & 6 deletions lib/pending.h

This file was deleted.

14 changes: 7 additions & 7 deletions toys/lsb/passwd.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* passwd.c - Program to update user password.
/* passwd.c - update user password.
*
* Copyright 2012 Ashwini Kumar <[email protected]>
* Modified 2012 Jason Kyungwan Han <[email protected]>
Expand All @@ -13,9 +13,9 @@ config PASSWD
help
usage: passwd [-a ALGO] [-dlu] [USER]
Update user's authentication tokens. Defaults to current user.
Update user's login password. Defaults to current user.
-a ALGO Encryption method (des, md5, sha256, sha512) default: des
-a ALGO Encryption method (des, md5, sha256, sha512) default: md5
-d Set password to ''
-l Lock (disable) account
-u Unlock (enable) account
Expand Down Expand Up @@ -53,17 +53,17 @@ static void weak_check(char *new, char *old, char *user)

void passwd_main(void)
{
uid_t myuid;
uid_t myuid = getuid();
struct passwd *pw = 0;
struct spwd *sp;
char *pass, *name, *encrypted = 0, salt[32];

// If we're root or not -lud, load specified user. Exit if not allowed.
if (!(myuid = getuid()) || !(toys.optflags&(FLAG_l|FLAG_u|FLAG_d))) {
if (!myuid || !(toys.optflags&(FLAG_l|FLAG_u|FLAG_d))) {
if (*toys.optargs) pw = xgetpwnam(*toys.optargs);
else pw = xgetpwuid(myuid);
}
if (!pw || (myuid && (myuid != pw->pw_uid))) error_exit("Not root");
if (!pw || (myuid && myuid != pw->pw_uid)) error_exit("Not root");

// Get password from /etc/passwd or /etc/shadow
// TODO: why still support non-shadow passwords...?
Expand All @@ -83,7 +83,7 @@ void passwd_main(void)
*(encrypted = toybuf) = 0;
} else {
if (!TT.a) TT.a = "des";
if (get_salt(salt, TT.a)<0) error_exit("bad -a '%s'", TT.a);
if (get_salt(salt, TT.a, 1)<0) error_exit("bad -a '%s'", TT.a);

printf("Changing password for %s\n", name);
if (myuid) {
Expand Down
23 changes: 12 additions & 11 deletions toys/other/mkpasswd.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ config MKPASSWD
help
usage: mkpasswd [-P FD] [-m TYPE] [-S SALT] [PASSWORD] [SALT]
Crypt PASSWORD using crypt(3)
Encrypt PASSWORD using crypt(3), with either random or provided SALT.
-P FD Read password from file descriptor FD
-m TYPE Encryption method (des, md5, sha256, or sha512; default is des)
-S SALT
*/

#define FOR_mkpasswd
Expand All @@ -31,24 +30,26 @@ GLOBALS(

void mkpasswd_main(void)
{
char salt[MAX_SALT_LEN] = {0,};
char salt[32] = {0,};
int i;

if (!TT.m) TT.m = "des";
if (toys.optc == 2) {
if (TT.S) error_exit("duplicate salt");
TT.S = toys.optargs[1];
}

if (-1 == (i = get_salt(salt, TT.m))) error_exit("bad -m");
if (-1 == (i = get_salt(salt, TT.m ? : "des", !TT.S))) error_exit("bad -m");
if (TT.S) {
char *s = TT.S;
char *mirv = strrchr(salt, '$'), *s = TT.S;

// In C locale, isalnum() means [A-Za-Z0-0]
while (isalnum(*s) || *s == '.' || *s == '/') s++;
if (*s) error_exit("salt not in [./A-Za-z0-9]");
if (mirv) mirv++;
else mirv = salt;

snprintf(salt+i, sizeof(salt)-i, "%s", TT.S);
// In C locale, isalnum() means [a-zA-Z0-9]
while (isalnum(*s) || *s == '.' || *s == '/') s++;
if (*s || s-TT.S!=strlen(mirv))
error_exit("bad SALT (need [a-zA-Z0-9] len %d)", (int)strlen(mirv));
strcpy(mirv, TT.S);
}

// Because read_password() doesn't have an fd argument
Expand All @@ -73,5 +74,5 @@ void mkpasswd_main(void)
}

// encrypt & print the password
xprintf("%s\n",crypt(*toys.optargs ? *toys.optargs : toybuf, salt));
xprintf("%s\n", crypt(*toys.optargs ? *toys.optargs : toybuf, salt));
}
Loading

0 comments on commit 6c30b35

Please sign in to comment.