Merge branch 'debian'
[hcoop/debian/courier-authlib.git] / authuserdbpwd.c
CommitLineData
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
0fde1ce3 26static const char rcsid[]="$Id: authuserdbpwd.c,v 1.9 2008/07/10 02:43:55 mrsam Exp $";
d9898ee8 27
28
29static 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 41static 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
70static int dochangepwd1(const char *, const char *, const char *, const char *,
71 const char *);
72
73static 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);
78static int makeuserdb();
79
80int 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
168static 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
229static int dochangepwd2(const char *service, const char *uid,
230 char *u,
231 const struct userdbs *udb, const char *npwd);
232
233static 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
314static 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
396static 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}