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