passwd.c - 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) == -… | |
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) == -… | |
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[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXY… | |
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]… | |
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 } |