ubase

suckless linux base utils
git clone git://git.suckless.org/ubase
Log | Files | Refs | README | LICENSE

passwd.c (5780B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/ioctl.h>
      3 #include <sys/stat.h>
      4 #include <sys/types.h>
      5 #include <sys/syscall.h>
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <limits.h>
     10 #include <pwd.h>
     11 #include <shadow.h>
     12 #include <stdint.h>
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 #include <unistd.h>
     17 
     18 #include "config.h"
     19 #include "passwd.h"
     20 #include "text.h"
     21 #include "util.h"
     22 
     23 static FILE *
     24 spw_get_file(const char *user)
     25 {
     26 	FILE *fp = NULL;
     27 	char file[PATH_MAX];
     28 	int r;
     29 
     30 	r = snprintf(file, sizeof(file), "/etc/tcb/%s/shadow", user);
     31 	if (r < 0 || (size_t)r >= sizeof(file))
     32 		eprintf("snprintf:");
     33 	fp = fopen(file, "r+");
     34 	if (!fp)
     35 		fp = fopen("/etc/shadow", "r+");
     36 	return fp;
     37 }
     38 
     39 static int
     40 spw_write_file(FILE *fp, const struct spwd *spw, char *pwhash)
     41 {
     42 	struct spwd *spwent;
     43 	int r = -1, w = 0;
     44 	FILE *tfp = NULL;
     45 
     46 	/* write to temporary file. */
     47 	tfp = tmpfile();
     48 	if (!tfp) {
     49 		weprintf("tmpfile:");
     50 		goto cleanup;
     51 	}
     52 	while ((spwent = fgetspent(fp))) {
     53 		/* update entry on name match */
     54 		if (strcmp(spwent->sp_namp, spw->sp_namp) == 0) {
     55 			spwent->sp_pwdp = pwhash;
     56 			w++;
     57 		}
     58 		errno = 0;
     59 		if (putspent(spwent, tfp) == -1) {
     60 			weprintf("putspent:");
     61 			goto cleanup;
     62 		}
     63 	}
     64 	if (!w) {
     65 		weprintf("shadow: no matching entry to write to\n");
     66 		goto cleanup;
     67 	}
     68 	fflush(tfp);
     69 
     70 	if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
     71 		weprintf("fseek:");
     72 		goto cleanup;
     73 	}
     74 
     75 	/* write temporary file to (tcb) shadow file */
     76 	concat(tfp, "tmpfile", fp, "shadow");
     77 	ftruncate(fileno(fp), ftell(tfp));
     78 
     79 	r = 0; /* success */
     80 cleanup:
     81 	if (tfp)
     82 		fclose(tfp);
     83 	return r;
     84 }
     85 
     86 static int
     87 pw_write_file(FILE *fp, const struct passwd *pw, char *pwhash) {
     88 	struct passwd *pwent;
     89 	int r = -1, w = 0;
     90 	FILE *tfp = NULL;
     91 
     92 	/* write to temporary file. */
     93 	tfp = tmpfile();
     94 	if (!tfp) {
     95 		weprintf("tmpfile:");
     96 		goto cleanup;
     97 	}
     98 	while ((pwent = fgetpwent(fp))) {
     99 		/* update entry on name match */
    100 		if (strcmp(pwent->pw_name, pw->pw_name) == 0) {
    101 			pwent->pw_passwd = pwhash;
    102 			w++;
    103 		}
    104 		errno = 0;
    105 		if (putpwent(pwent, tfp) == -1) {
    106 			weprintf("putpwent:");
    107 			goto cleanup;
    108 		}
    109 	}
    110 	if (!w) {
    111 		weprintf("passwd: no matching entry to write to\n");
    112 		goto cleanup;
    113 	}
    114 	fflush(tfp);
    115 
    116 	if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
    117 		weprintf("fseek:");
    118 		goto cleanup;
    119 	}
    120 
    121 	/* write to passwd file. */
    122 	concat(tfp, "tmpfile", fp, "passwd");
    123 	ftruncate(fileno(fp), ftell(tfp));
    124 
    125 	r = 0; /* success */
    126 cleanup:
    127 	if (tfp)
    128 		fclose(tfp);
    129 	return r;
    130 }
    131 
    132 /* generates a random base64-encoded salt string of length 16 */
    133 static void
    134 gensalt(char *s)
    135 {
    136 	static const char b64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    137 	uint8_t buf[12];
    138 	uint32_t n;
    139 	int i;
    140 
    141 	if (syscall(SYS_getrandom, buf, sizeof(buf), 0) < 0)
    142 		eprintf("getrandom:");
    143 	for (i = 0; i < 12; i += 3) {
    144 		n = buf[i] << 16 | buf[i+1] << 8 | buf[i+2];
    145 		*s++ = b64[n%64]; n /= 64;
    146 		*s++ = b64[n%64]; n /= 64;
    147 		*s++ = b64[n%64]; n /= 64;
    148 		*s++ = b64[n];
    149 	}
    150 	*s++ = '\0';
    151 }
    152 
    153 static void
    154 usage(void)
    155 {
    156 	eprintf("usage: %s [username]\n", argv0);
    157 }
    158 
    159 int
    160 main(int argc, char *argv[])
    161 {
    162 	char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL;
    163 	char *inpass, *p, *prevhash = NULL, salt[sizeof(PW_CIPHER) + 16] = PW_CIPHER;
    164 	struct passwd *pw;
    165 	struct spwd *spw = NULL;
    166 	FILE *fp = NULL;
    167 	int r = -1, status = 1;
    168 
    169 	ARGBEGIN {
    170 	default:
    171 		usage();
    172 	} ARGEND;
    173 
    174 	pw_init();
    175 	umask(077);
    176 
    177 	errno = 0;
    178 	if (argc == 0)
    179 		pw = getpwuid(getuid());
    180 	else
    181 		pw = getpwnam(argv[0]);
    182 	if (!pw) {
    183 		if (errno)
    184 			eprintf("getpwnam: %s:", argv[0]);
    185 		else
    186 			eprintf("who are you?\n");
    187 	}
    188 
    189 	/* is using shadow entry ? */
    190 	if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') {
    191 		errno = 0;
    192 		spw = getspnam(pw->pw_name);
    193 		if (!spw) {
    194 			if (errno)
    195 				eprintf("getspnam: %s:", pw->pw_name);
    196 			else
    197 				eprintf("who are you?\n");
    198 		}
    199 	}
    200 
    201 	/* Flush pending input */
    202 	ioctl(0, TCFLSH, (void *)0);
    203 
    204 	if (getuid() == 0) {
    205 		goto newpass;
    206 	} else {
    207 		if (pw->pw_passwd[0] == '!' ||
    208 		    pw->pw_passwd[0] == '*')
    209 			eprintf("denied\n");
    210 		if (pw->pw_passwd[0] == '\0') {
    211 			goto newpass;
    212 		}
    213 		if (pw->pw_passwd[0] == 'x' &&
    214 		    pw->pw_passwd[1] == '\0')
    215 			prevhash = spw->sp_pwdp;
    216 		else
    217 			prevhash = pw->pw_passwd;
    218 	}
    219 
    220 	printf("Changing password for %s\n", pw->pw_name);
    221 	inpass = getpass("Old password: ");
    222 	if (!inpass)
    223 		eprintf("getpass:");
    224 	if (inpass[0] == '\0')
    225 		eprintf("no password supplied\n");
    226 	p = crypt(inpass, prevhash);
    227 	if (!p)
    228 		eprintf("crypt:");
    229 	cryptpass1 = estrdup(p);
    230 	if (strcmp(cryptpass1, prevhash) != 0)
    231 		eprintf("incorrect password\n");
    232 
    233 newpass:
    234 	inpass = getpass("Enter new password: ");
    235 	if (!inpass)
    236 		eprintf("getpass:");
    237 	if (inpass[0] == '\0')
    238 		eprintf("no password supplied\n");
    239 
    240 	if(prevhash) {
    241 		p = crypt(inpass, prevhash);
    242 		if (!p)
    243 			eprintf("crypt:");
    244 		if (cryptpass1 && strcmp(cryptpass1, p) == 0)
    245 			eprintf("password left unchanged\n");
    246 	}
    247 	gensalt(salt + strlen(salt));
    248 	p = crypt(inpass, salt);
    249 	if (!p)
    250 		eprintf("crypt:");
    251 	cryptpass2 = estrdup(p);
    252 
    253 	/* Flush pending input */
    254 	ioctl(0, TCFLSH, (void *)0);
    255 
    256 	inpass = getpass("Retype new password: ");
    257 	if (!inpass)
    258 		eprintf("getpass:");
    259 	if (inpass[0] == '\0')
    260 		eprintf("no password supplied\n");
    261 	p = crypt(inpass, salt);
    262 	if (!p)
    263 		eprintf("crypt:");
    264 	cryptpass3 = estrdup(p);
    265 	if (strcmp(cryptpass2, cryptpass3) != 0)
    266 		eprintf("passwords don't match\n");
    267 
    268 	fp = spw_get_file(pw->pw_name);
    269 	if (fp) {
    270 		r = spw_write_file(fp, spw, cryptpass3);
    271 	} else {
    272 		fp = fopen("/etc/passwd", "r+");
    273 		if (fp)
    274 			r = pw_write_file(fp, pw, cryptpass3);
    275 		else
    276 			weprintf("fopen:");
    277 	}
    278 	if (!r)
    279 		status = 0;
    280 
    281 	if (fp)
    282 		fclose(fp);
    283 	free(cryptpass3);
    284 	free(cryptpass2);
    285 	free(cryptpass1);
    286 
    287 	return status;
    288 }