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}