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