d9898ee8 |
1 | /* |
2 | ** Copyright 2001-2005 Double Precision, Inc. See COPYING for |
3 | ** distribution information. |
4 | */ |
5 | |
6 | #if HAVE_CONFIG_H |
7 | #include "courier_auth_config.h" |
8 | #endif |
9 | #include <stdio.h> |
10 | #include <stdlib.h> |
11 | #include <string.h> |
12 | #include <errno.h> |
13 | #include <signal.h> |
14 | #include <pwd.h> |
15 | #if HAVE_UNISTD_H |
16 | #include <unistd.h> |
17 | #endif |
18 | #include "userdb/userdb.h" |
19 | #include "auth.h" |
20 | #include "authstaticlist.h" |
21 | #include "authwait.h" |
22 | #include "sbindir.h" |
23 | #include "courierauthdebug.h" |
d9898ee8 |
24 | #include "libhmac/hmac.h" |
d9898ee8 |
25 | |
ac40fd9e |
26 | static const char rcsid[]="$Id: authuserdbpwd.c,v 1.9 2008/07/10 02:43:55 mrsam Exp $"; |
d9898ee8 |
27 | |
28 | |
29 | static int bad(const char *q) |
30 | { |
31 | const char *p; |
32 | |
33 | for (p=q; *p; p++) |
34 | if ((int)(unsigned char)*p < ' ' || *p == '|' || *p == '=' |
35 | || *p == '\'' || *p == '"') |
36 | return (1); |
37 | return (0); |
38 | } |
39 | |
40 | |
d9898ee8 |
41 | static char *hmacpw(const char *pw, const char *hash) |
42 | { |
43 | int i; |
44 | |
45 | for (i=0; hmac_list[i] && |
46 | strcmp(hmac_list[i]->hh_name, hash); i++) |
47 | ; |
48 | if (hmac_list[i]) |
49 | { |
50 | struct hmac_hashinfo *hmac=hmac_list[i]; |
51 | unsigned char *p=malloc(hmac->hh_L*2); |
52 | char *q=malloc(hmac->hh_L*4+1); |
53 | unsigned i; |
54 | |
55 | if (!p || !q) |
56 | { |
57 | perror("malloc"); |
58 | exit(1); |
59 | } |
60 | |
61 | hmac_hashkey(hmac, pw, strlen(pw), p, p+hmac->hh_L); |
62 | for (i=0; i<hmac->hh_L*2; i++) |
63 | sprintf(q+i*2, "%02x", (int)p[i]); |
64 | free(p); |
65 | return (q); |
66 | } |
67 | return (NULL); |
68 | } |
d9898ee8 |
69 | |
70 | static int dochangepwd1(const char *, const char *, const char *, const char *, |
71 | const char *); |
72 | |
73 | static int try_auth_userdb_passwd(const char *hmac_flag, |
74 | const char *service, |
75 | const char *uid, |
76 | const char *opwd_buf, |
77 | const char *npwd_buf); |
78 | static int makeuserdb(); |
79 | |
80 | int auth_userdb_passwd(const char *service, |
81 | const char *uid, |
82 | const char *opwd_buf, |
83 | const char *npwd_buf) |
84 | { |
85 | int rc; |
86 | int rc2; |
87 | |
88 | if (bad(uid) || strchr(uid, '/')) |
89 | { |
90 | errno=EPERM; |
91 | DPRINTF("userdb: %s is not a valid userid.\n", |
92 | uid); |
93 | return -1; |
94 | } |
95 | |
96 | if (bad(service) || |
97 | bad(opwd_buf) || |
98 | bad(npwd_buf)) |
99 | { |
100 | errno=EPERM; |
101 | DPRINTF("userdb: Invalid service or password string for %s.\n", |
102 | uid); |
103 | return (1); |
104 | } |
105 | |
106 | rc=try_auth_userdb_passwd(NULL, service, uid, opwd_buf, npwd_buf); |
107 | |
108 | if (rc > 0) |
109 | return rc; |
110 | |
d9898ee8 |
111 | { |
112 | int i; |
113 | |
114 | |
115 | for (i=0; hmac_list[i]; i++) |
116 | { |
117 | const char *n=hmac_list[i]->hh_name; |
118 | |
119 | char *hmacservice=malloc(strlen(service)+strlen(n) |
120 | +sizeof("-hmac-")); |
121 | |
122 | if (hmacservice == NULL) |
123 | return (1); |
124 | |
125 | strcat(strcat(strcpy(hmacservice, service), |
126 | "-hmac-"), n); |
127 | |
128 | rc2=try_auth_userdb_passwd(n, hmacservice, uid, |
129 | opwd_buf, npwd_buf); |
130 | |
131 | if (rc2 > 0) |
132 | { |
133 | free(hmacservice); |
134 | return (1); |
135 | } |
136 | |
137 | if (rc2 == 0) |
138 | rc=0; |
139 | |
140 | strcat(strcpy(hmacservice, "hmac-"), n); |
141 | |
142 | rc2=try_auth_userdb_passwd(n, hmacservice, uid, |
143 | opwd_buf, npwd_buf); |
144 | free(hmacservice); |
145 | if (rc2 > 0) |
146 | return (1); |
147 | |
148 | if (rc2 == 0) |
149 | rc=0; |
150 | |
151 | } |
152 | } |
d9898ee8 |
153 | |
154 | if (rc == 0) |
155 | { |
156 | rc=makeuserdb(); |
157 | |
158 | if (rc) |
159 | { |
160 | DPRINTF("makeuserdb: error: %s", strerror(errno)); |
161 | } |
162 | } |
163 | |
164 | DPRINTF("authuserdb: return code %d", rc); |
165 | return rc; |
166 | } |
167 | |
168 | static int try_auth_userdb_passwd(const char *hmac_flag, |
169 | const char *service, |
170 | const char *uid, |
171 | const char *opwd_buf, |
172 | const char *npwd_buf) |
173 | { |
174 | char *opwd; |
175 | char *npwd; |
176 | int rc; |
177 | |
d9898ee8 |
178 | if (hmac_flag) |
179 | { |
180 | DPRINTF("Trying to change password for %s", |
181 | hmac_flag); |
182 | |
183 | DPWPRINTF("Old password=%s, new password=%s", |
184 | opwd_buf, npwd_buf); |
185 | |
186 | opwd=hmacpw(opwd_buf, hmac_flag); |
187 | if (!opwd) |
188 | return 1; |
189 | |
190 | npwd=hmacpw(npwd_buf, hmac_flag); |
191 | |
192 | if (!npwd) |
193 | { |
194 | free(opwd); |
195 | return (1); |
196 | } |
197 | } |
198 | else |
d9898ee8 |
199 | { |
200 | DPRINTF("Trying to change system password for %s", |
201 | service); |
202 | |
203 | DPWPRINTF("Old password=%s, new password=%s", |
204 | opwd_buf, npwd_buf); |
205 | |
206 | opwd=strdup(opwd_buf); |
207 | if (!opwd) |
208 | { |
209 | return (1); |
210 | } |
211 | |
212 | npwd=userdb_mkmd5pw(npwd_buf); |
213 | if (!npwd || !(npwd=strdup(npwd))) |
214 | { |
215 | free(opwd); |
216 | errno=EPERM; |
217 | return (1); |
218 | } |
219 | } |
220 | |
221 | |
222 | rc=dochangepwd1(service, uid, opwd, npwd, hmac_flag); |
223 | |
224 | free(opwd); |
225 | free(npwd); |
226 | return (rc); |
227 | } |
228 | |
229 | static int dochangepwd2(const char *service, const char *uid, |
230 | char *u, |
231 | const struct userdbs *udb, const char *npwd); |
232 | |
233 | static int dochangepwd1(const char *service, const char *uid, |
234 | const char *opwd, const char *npwd, |
235 | const char *hmac_flag) |
236 | { |
237 | char *udbs; |
238 | char *services; |
239 | char *passwords; |
240 | int rc; |
241 | |
242 | char *u; |
243 | struct userdbs *udb; |
244 | |
245 | |
246 | udbs=userdbshadow(USERDB "shadow.dat", uid); |
247 | |
248 | if (!udbs) |
249 | { |
250 | errno=ENOENT; |
251 | return (-1); |
252 | } |
253 | |
254 | if ((services=malloc(strlen(service)+sizeof("pw"))) == 0) |
255 | { |
256 | perror("malloc"); |
257 | free(udbs); |
258 | errno=EPERM; |
259 | return (1); |
260 | } |
261 | |
262 | strcat(strcpy(services, service), "pw"); |
263 | |
264 | DPRINTF("Checking for password called \"%s\"", services); |
265 | |
266 | passwords=userdb_gets(udbs, services); |
267 | free(services); |
268 | |
269 | if (passwords == 0 && hmac_flag == 0) |
270 | { |
271 | DPRINTF("Not found, checking for \"systempw\""); |
272 | passwords=userdb_gets(udbs, "systempw"); |
273 | service="system"; |
274 | } |
275 | |
276 | if (!passwords || (hmac_flag ? strcmp(opwd, passwords): |
277 | authcheckpassword(opwd, passwords))) |
278 | { |
279 | if (!passwords) |
280 | { |
281 | DPRINTF("Password not found."); |
282 | } |
283 | else |
284 | { |
285 | DPRINTF("Password didn't match."); |
286 | } |
287 | |
288 | if (passwords) |
289 | free(passwords); |
290 | free(udbs); |
291 | errno=EPERM; |
292 | return (-1); |
293 | } |
294 | free(passwords); |
295 | free(udbs); |
296 | |
297 | userdb_init(USERDB ".dat"); |
298 | if ( (u=userdb(uid)) == 0 || |
299 | (udb=userdb_creates(u)) == 0) |
300 | { |
301 | if (u) |
302 | free(u); |
303 | errno=EPERM; |
304 | return (1); |
305 | } |
306 | |
307 | rc=dochangepwd2(service, uid, u, udb, npwd); |
308 | |
309 | userdb_frees(udb); |
310 | free(u); |
311 | return (rc); |
312 | } |
313 | |
314 | static int dochangepwd2(const char *services, const char *uid, |
315 | char *u, |
316 | const struct userdbs *udb, const char *npwd) |
317 | { |
318 | char *argv[10]; |
319 | pid_t p, p2; |
320 | int waitstat; |
321 | |
322 | argv[0]=SBINDIR "/userdb"; |
323 | argv[1]=malloc(strlen(udb->udb_source ? udb->udb_source:"") |
324 | +strlen(uid)+1); |
325 | |
326 | if (!argv[1]) |
327 | { |
328 | errno=EPERM; |
329 | return (1); |
330 | } |
331 | strcpy(argv[1],udb->udb_source ? udb->udb_source:""); |
332 | strcat(argv[1],uid); |
333 | argv[2]="set"; |
334 | |
335 | argv[3]=malloc(strlen(services)+strlen(npwd)+10); |
336 | if (!argv[3]) |
337 | { |
338 | free(argv[1]); |
339 | errno=EPERM; |
340 | return (1); |
341 | } |
342 | |
343 | sprintf(argv[3], "%spw=%s", services, npwd); |
344 | signal(SIGCHLD, SIG_DFL); |
345 | argv[4]=0; |
346 | |
347 | DPRINTF("Executing %s %s %s %s%s", |
348 | argv[0], |
349 | argv[1], |
350 | argv[2], |
351 | courier_authdebug_login_level >= 2 ? argv[3]:services, |
352 | courier_authdebug_login_level >= 2 ? "":"pw=******"); |
353 | |
354 | p=fork(); |
355 | |
356 | if (p < 0) |
357 | { |
358 | free(argv[3]); |
359 | free(argv[1]); |
360 | errno=EPERM; |
361 | return (1); |
362 | } |
363 | |
364 | if (p == 0) |
365 | { |
366 | execv(argv[0], argv); |
367 | perror(argv[0]); |
368 | exit(1); |
369 | } |
370 | |
371 | free(argv[1]); |
372 | free(argv[3]); |
373 | |
374 | while ((p2=wait(&waitstat)) != p) |
375 | { |
376 | if (p2 < 0 && errno == ECHILD) |
377 | { |
378 | perror("wait"); |
379 | errno=EPERM; |
380 | return (1); |
381 | } |
382 | } |
383 | |
384 | if (!WIFEXITED(waitstat) || WEXITSTATUS(waitstat)) |
385 | { |
386 | DPRINTF("Command failed: with exit code %d", |
387 | (int)WEXITSTATUS(waitstat)); |
388 | errno=EPERM; |
389 | return (1); |
390 | } |
391 | DPRINTF("Command succeeded: with exit code %d", |
392 | (int)WEXITSTATUS(waitstat)); |
393 | return 0; |
394 | } |
395 | |
396 | static int makeuserdb() |
397 | { |
398 | char *argv[2]; |
399 | pid_t p, p2; |
400 | int waitstat; |
401 | |
402 | DPRINTF("Executing makeuserdb"); |
403 | p=fork(); |
404 | |
405 | if (p < 0) |
406 | { |
407 | perror("fork"); |
408 | return (1); |
409 | } |
410 | |
411 | if (p == 0) |
412 | { |
413 | argv[0]= SBINDIR "/makeuserdb"; |
414 | argv[1]=0; |
415 | |
416 | execv(argv[0], argv); |
417 | perror(argv[0]); |
418 | exit(1); |
419 | } |
420 | |
421 | while ((p2=wait(&waitstat)) != p) |
422 | { |
423 | if (p2 < 0 && errno == ECHILD) |
424 | { |
425 | errno=EPERM; |
426 | return (1); |
427 | } |
428 | } |
429 | |
430 | if (!WIFEXITED(waitstat) || WEXITSTATUS(waitstat)) |
431 | { |
432 | errno=EPERM; |
433 | return (1); |
434 | } |
435 | return (0); |
436 | } |