passwd.c - ubase - suckless linux base utils
HTML git clone git://git.suckless.org/ubase
DIR Log
DIR Files
DIR Refs
DIR README
DIR 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 }