1 | /* |
---|
2 | * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers. |
---|
3 | * All rights reserved. |
---|
4 | * |
---|
5 | * By using this file, you agree to the terms and conditions set |
---|
6 | * forth in the LICENSE file which can be found at the top level of |
---|
7 | * the sendmail distribution. |
---|
8 | */ |
---|
9 | |
---|
10 | #include <sm/gen.h> |
---|
11 | SM_RCSID("@(#)$Id: mbdb.c,v 1.1.1.1 2003-04-08 15:08:30 zacheiss Exp $") |
---|
12 | |
---|
13 | #include <sys/param.h> |
---|
14 | |
---|
15 | #include <ctype.h> |
---|
16 | #include <errno.h> |
---|
17 | #include <pwd.h> |
---|
18 | #include <stdlib.h> |
---|
19 | #include <setjmp.h> |
---|
20 | #include <unistd.h> |
---|
21 | |
---|
22 | #include <sm/limits.h> |
---|
23 | #include <sm/conf.h> |
---|
24 | #include <sm/assert.h> |
---|
25 | #include <sm/bitops.h> |
---|
26 | #include <sm/errstring.h> |
---|
27 | #include <sm/heap.h> |
---|
28 | #include <sm/mbdb.h> |
---|
29 | #include <sm/string.h> |
---|
30 | # ifdef EX_OK |
---|
31 | # undef EX_OK /* for SVr4.2 SMP */ |
---|
32 | # endif /* EX_OK */ |
---|
33 | #include <sm/sysexits.h> |
---|
34 | |
---|
35 | #if LDAPMAP |
---|
36 | # if _LDAP_EXAMPLE_ |
---|
37 | # include <sm/ldap.h> |
---|
38 | # endif /* _LDAP_EXAMPLE_ */ |
---|
39 | #endif /* LDAPMAP */ |
---|
40 | |
---|
41 | typedef struct |
---|
42 | { |
---|
43 | char *mbdb_typename; |
---|
44 | int (*mbdb_initialize) __P((char *)); |
---|
45 | int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user)); |
---|
46 | void (*mbdb_terminate) __P((void)); |
---|
47 | } SM_MBDB_TYPE_T; |
---|
48 | |
---|
49 | static int mbdb_pw_initialize __P((char *)); |
---|
50 | static int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user)); |
---|
51 | static void mbdb_pw_terminate __P((void)); |
---|
52 | |
---|
53 | #if LDAPMAP |
---|
54 | # if _LDAP_EXAMPLE_ |
---|
55 | static struct sm_ldap_struct LDAPLMAP; |
---|
56 | static int mbdb_ldap_initialize __P((char *)); |
---|
57 | static int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user)); |
---|
58 | static void mbdb_ldap_terminate __P((void)); |
---|
59 | # endif /* _LDAP_EXAMPLE_ */ |
---|
60 | #endif /* LDAPMAP */ |
---|
61 | |
---|
62 | static SM_MBDB_TYPE_T SmMbdbTypes[] = |
---|
63 | { |
---|
64 | { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate }, |
---|
65 | #if LDAPMAP |
---|
66 | # if _LDAP_EXAMPLE_ |
---|
67 | { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate }, |
---|
68 | # endif /* _LDAP_EXAMPLE_ */ |
---|
69 | #endif /* LDAPMAP */ |
---|
70 | { NULL, NULL, NULL, NULL } |
---|
71 | }; |
---|
72 | |
---|
73 | static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0]; |
---|
74 | |
---|
75 | /* |
---|
76 | ** SM_MBDB_INITIALIZE -- specify which mailbox database to use |
---|
77 | ** |
---|
78 | ** If this function is not called, then the "pw" implementation |
---|
79 | ** is used by default; this implementation uses getpwnam(). |
---|
80 | ** |
---|
81 | ** Parameters: |
---|
82 | ** mbdb -- Which mailbox database to use. |
---|
83 | ** The argument has the form "name" or "name.arg". |
---|
84 | ** "pw" means use getpwnam(). |
---|
85 | ** |
---|
86 | ** Results: |
---|
87 | ** EX_OK on success, or an EX_* code on failure. |
---|
88 | */ |
---|
89 | |
---|
90 | int |
---|
91 | sm_mbdb_initialize(mbdb) |
---|
92 | char *mbdb; |
---|
93 | { |
---|
94 | size_t namelen; |
---|
95 | int err; |
---|
96 | char *name; |
---|
97 | char *arg; |
---|
98 | SM_MBDB_TYPE_T *t; |
---|
99 | |
---|
100 | SM_REQUIRE(mbdb != NULL); |
---|
101 | |
---|
102 | name = mbdb; |
---|
103 | arg = strchr(mbdb, '.'); |
---|
104 | if (arg == NULL) |
---|
105 | namelen = strlen(name); |
---|
106 | else |
---|
107 | { |
---|
108 | namelen = arg - name; |
---|
109 | ++arg; |
---|
110 | } |
---|
111 | |
---|
112 | for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t) |
---|
113 | { |
---|
114 | if (strlen(t->mbdb_typename) == namelen && |
---|
115 | strncmp(name, t->mbdb_typename, namelen) == 0) |
---|
116 | { |
---|
117 | err = EX_OK; |
---|
118 | if (t->mbdb_initialize != NULL) |
---|
119 | err = t->mbdb_initialize(arg); |
---|
120 | if (err == EX_OK) |
---|
121 | SmMbdbType = t; |
---|
122 | return err; |
---|
123 | } |
---|
124 | } |
---|
125 | return EX_UNAVAILABLE; |
---|
126 | } |
---|
127 | |
---|
128 | /* |
---|
129 | ** SM_MBDB_TERMINATE -- terminate connection to the mailbox database |
---|
130 | ** |
---|
131 | ** Because this function closes any cached file descriptors that |
---|
132 | ** are being held open for the connection to the mailbox database, |
---|
133 | ** it should be called for security reasons prior to dropping privileges |
---|
134 | ** and execing another process. |
---|
135 | ** |
---|
136 | ** Parameters: |
---|
137 | ** none. |
---|
138 | ** |
---|
139 | ** Results: |
---|
140 | ** none. |
---|
141 | */ |
---|
142 | |
---|
143 | void |
---|
144 | sm_mbdb_terminate() |
---|
145 | { |
---|
146 | if (SmMbdbType->mbdb_terminate != NULL) |
---|
147 | SmMbdbType->mbdb_terminate(); |
---|
148 | } |
---|
149 | |
---|
150 | /* |
---|
151 | ** SM_MBDB_LOOKUP -- look up a local mail recipient, given name |
---|
152 | ** |
---|
153 | ** Parameters: |
---|
154 | ** name -- name of local mail recipient |
---|
155 | ** user -- pointer to structure to fill in on success |
---|
156 | ** |
---|
157 | ** Results: |
---|
158 | ** On success, fill in *user and return EX_OK. |
---|
159 | ** If the user does not exist, return EX_NOUSER. |
---|
160 | ** If a temporary failure (eg, a network failure) occurred, |
---|
161 | ** return EX_TEMPFAIL. Otherwise return EX_OSERR. |
---|
162 | */ |
---|
163 | |
---|
164 | int |
---|
165 | sm_mbdb_lookup(name, user) |
---|
166 | char *name; |
---|
167 | SM_MBDB_T *user; |
---|
168 | { |
---|
169 | int ret = EX_NOUSER; |
---|
170 | |
---|
171 | if (SmMbdbType->mbdb_lookup != NULL) |
---|
172 | ret = SmMbdbType->mbdb_lookup(name, user); |
---|
173 | return ret; |
---|
174 | } |
---|
175 | |
---|
176 | /* |
---|
177 | ** SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T |
---|
178 | ** |
---|
179 | ** Parameters: |
---|
180 | ** user -- destination user information structure |
---|
181 | ** pw -- source passwd structure |
---|
182 | ** |
---|
183 | ** Results: |
---|
184 | ** none. |
---|
185 | */ |
---|
186 | |
---|
187 | void |
---|
188 | sm_mbdb_frompw(user, pw) |
---|
189 | SM_MBDB_T *user; |
---|
190 | struct passwd *pw; |
---|
191 | { |
---|
192 | SM_REQUIRE(user != NULL); |
---|
193 | (void) sm_strlcpy(user->mbdb_name, pw->pw_name, |
---|
194 | sizeof(user->mbdb_name)); |
---|
195 | user->mbdb_uid = pw->pw_uid; |
---|
196 | user->mbdb_gid = pw->pw_gid; |
---|
197 | sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname, |
---|
198 | sizeof(user->mbdb_fullname)); |
---|
199 | (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir, |
---|
200 | sizeof(user->mbdb_homedir)); |
---|
201 | (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell, |
---|
202 | sizeof(user->mbdb_shell)); |
---|
203 | } |
---|
204 | |
---|
205 | /* |
---|
206 | ** SM_PWFULLNAME -- build full name of user from pw_gecos field. |
---|
207 | ** |
---|
208 | ** This routine interprets the strange entry that would appear |
---|
209 | ** in the GECOS field of the password file. |
---|
210 | ** |
---|
211 | ** Parameters: |
---|
212 | ** gecos -- name to build. |
---|
213 | ** user -- the login name of this user (for &). |
---|
214 | ** buf -- place to put the result. |
---|
215 | ** buflen -- length of buf. |
---|
216 | ** |
---|
217 | ** Returns: |
---|
218 | ** none. |
---|
219 | */ |
---|
220 | |
---|
221 | #if _FFR_HANDLE_ISO8859_GECOS |
---|
222 | static char Latin1ToASCII[128] = |
---|
223 | { |
---|
224 | 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, |
---|
225 | 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, |
---|
226 | 99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42, |
---|
227 | 50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65, |
---|
228 | 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79, |
---|
229 | 79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97, |
---|
230 | 97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110, |
---|
231 | 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121 |
---|
232 | }; |
---|
233 | #endif /* _FFR_HANDLE_ISO8859_GECOS */ |
---|
234 | |
---|
235 | void |
---|
236 | sm_pwfullname(gecos, user, buf, buflen) |
---|
237 | register char *gecos; |
---|
238 | char *user; |
---|
239 | char *buf; |
---|
240 | size_t buflen; |
---|
241 | { |
---|
242 | register char *p; |
---|
243 | register char *bp = buf; |
---|
244 | |
---|
245 | if (*gecos == '*') |
---|
246 | gecos++; |
---|
247 | |
---|
248 | /* copy gecos, interpolating & to be full name */ |
---|
249 | for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) |
---|
250 | { |
---|
251 | if (bp >= &buf[buflen - 1]) |
---|
252 | { |
---|
253 | /* buffer overflow -- just use login name */ |
---|
254 | (void) sm_strlcpy(buf, user, buflen); |
---|
255 | return; |
---|
256 | } |
---|
257 | if (*p == '&') |
---|
258 | { |
---|
259 | /* interpolate full name */ |
---|
260 | (void) sm_strlcpy(bp, user, buflen - (bp - buf)); |
---|
261 | *bp = toupper(*bp); |
---|
262 | bp += strlen(bp); |
---|
263 | } |
---|
264 | else |
---|
265 | { |
---|
266 | #if _FFR_HANDLE_ISO8859_GECOS |
---|
267 | if ((unsigned char) *p >= 128) |
---|
268 | *bp++ = Latin1ToASCII[(unsigned char) *p - 128]; |
---|
269 | else |
---|
270 | #endif /* _FFR_HANDLE_ISO8859_GECOS */ |
---|
271 | *bp++ = *p; |
---|
272 | } |
---|
273 | } |
---|
274 | *bp = '\0'; |
---|
275 | } |
---|
276 | |
---|
277 | /* |
---|
278 | ** /etc/passwd implementation. |
---|
279 | */ |
---|
280 | |
---|
281 | /* |
---|
282 | ** MBDB_PW_INITIALIZE -- initialize getpwnam() version |
---|
283 | ** |
---|
284 | ** Parameters: |
---|
285 | ** arg -- unused. |
---|
286 | ** |
---|
287 | ** Results: |
---|
288 | ** EX_OK. |
---|
289 | */ |
---|
290 | |
---|
291 | /* ARGSUSED0 */ |
---|
292 | static int |
---|
293 | mbdb_pw_initialize(arg) |
---|
294 | char *arg; |
---|
295 | { |
---|
296 | return EX_OK; |
---|
297 | } |
---|
298 | |
---|
299 | /* |
---|
300 | ** MBDB_PW_LOOKUP -- look up a local mail recipient, given name |
---|
301 | ** |
---|
302 | ** Parameters: |
---|
303 | ** name -- name of local mail recipient |
---|
304 | ** user -- pointer to structure to fill in on success |
---|
305 | ** |
---|
306 | ** Results: |
---|
307 | ** On success, fill in *user and return EX_OK. |
---|
308 | ** Failure: EX_NOUSER. |
---|
309 | */ |
---|
310 | |
---|
311 | static int |
---|
312 | mbdb_pw_lookup(name, user) |
---|
313 | char *name; |
---|
314 | SM_MBDB_T *user; |
---|
315 | { |
---|
316 | struct passwd *pw; |
---|
317 | |
---|
318 | #ifdef HESIOD |
---|
319 | /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ |
---|
320 | { |
---|
321 | char *p; |
---|
322 | |
---|
323 | for (p = name; *p != '\0'; p++) |
---|
324 | if (!isascii(*p) || !isdigit(*p)) |
---|
325 | break; |
---|
326 | if (*p == '\0') |
---|
327 | return EX_NOUSER; |
---|
328 | } |
---|
329 | #endif /* HESIOD */ |
---|
330 | |
---|
331 | errno = 0; |
---|
332 | pw = getpwnam(name); |
---|
333 | if (pw == NULL) |
---|
334 | { |
---|
335 | #if 0 |
---|
336 | /* |
---|
337 | ** getpwnam() isn't advertised as setting errno. |
---|
338 | ** In fact, under FreeBSD, non-root getpwnam() on |
---|
339 | ** non-existant users returns NULL with errno = EPERM. |
---|
340 | ** This test won't work. |
---|
341 | */ |
---|
342 | switch (errno) |
---|
343 | { |
---|
344 | case 0: |
---|
345 | return EX_NOUSER; |
---|
346 | case EIO: |
---|
347 | return EX_OSERR; |
---|
348 | default: |
---|
349 | return EX_TEMPFAIL; |
---|
350 | } |
---|
351 | #endif /* 0 */ |
---|
352 | return EX_NOUSER; |
---|
353 | } |
---|
354 | |
---|
355 | sm_mbdb_frompw(user, pw); |
---|
356 | return EX_OK; |
---|
357 | } |
---|
358 | |
---|
359 | /* |
---|
360 | ** MBDB_PW_TERMINATE -- terminate connection to the mailbox database |
---|
361 | ** |
---|
362 | ** Parameters: |
---|
363 | ** none. |
---|
364 | ** |
---|
365 | ** Results: |
---|
366 | ** none. |
---|
367 | */ |
---|
368 | |
---|
369 | static void |
---|
370 | mbdb_pw_terminate() |
---|
371 | { |
---|
372 | endpwent(); |
---|
373 | } |
---|
374 | |
---|
375 | #if LDAPMAP |
---|
376 | # if _LDAP_EXAMPLE_ |
---|
377 | /* |
---|
378 | ** LDAP example implementation based on RFC 2307, "An Approach for Using |
---|
379 | ** LDAP as a Network Information Service": |
---|
380 | ** |
---|
381 | ** ( nisSchema.1.0 NAME 'uidNumber' |
---|
382 | ** DESC 'An integer uniquely identifying a user in an |
---|
383 | ** administrative domain' |
---|
384 | ** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) |
---|
385 | ** |
---|
386 | ** ( nisSchema.1.1 NAME 'gidNumber' |
---|
387 | ** DESC 'An integer uniquely identifying a group in an |
---|
388 | ** administrative domain' |
---|
389 | ** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) |
---|
390 | ** |
---|
391 | ** ( nisSchema.1.2 NAME 'gecos' |
---|
392 | ** DESC 'The GECOS field; the common name' |
---|
393 | ** EQUALITY caseIgnoreIA5Match |
---|
394 | ** SUBSTRINGS caseIgnoreIA5SubstringsMatch |
---|
395 | ** SYNTAX 'IA5String' SINGLE-VALUE ) |
---|
396 | ** |
---|
397 | ** ( nisSchema.1.3 NAME 'homeDirectory' |
---|
398 | ** DESC 'The absolute path to the home directory' |
---|
399 | ** EQUALITY caseExactIA5Match |
---|
400 | ** SYNTAX 'IA5String' SINGLE-VALUE ) |
---|
401 | ** |
---|
402 | ** ( nisSchema.1.4 NAME 'loginShell' |
---|
403 | ** DESC 'The path to the login shell' |
---|
404 | ** EQUALITY caseExactIA5Match |
---|
405 | ** SYNTAX 'IA5String' SINGLE-VALUE ) |
---|
406 | ** |
---|
407 | ** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY |
---|
408 | ** DESC 'Abstraction of an account with POSIX attributes' |
---|
409 | ** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) |
---|
410 | ** MAY ( userPassword $ loginShell $ gecos $ description ) ) |
---|
411 | ** |
---|
412 | */ |
---|
413 | |
---|
414 | # define MBDB_LDAP_LABEL "MailboxDatabase" |
---|
415 | |
---|
416 | # ifndef MBDB_LDAP_FILTER |
---|
417 | # define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))" |
---|
418 | # endif /* MBDB_LDAP_FILTER */ |
---|
419 | |
---|
420 | # ifndef MBDB_DEFAULT_LDAP_BASEDN |
---|
421 | # define MBDB_DEFAULT_LDAP_BASEDN NULL |
---|
422 | # endif /* MBDB_DEFAULT_LDAP_BASEDN */ |
---|
423 | |
---|
424 | # ifndef MBDB_DEFAULT_LDAP_SERVER |
---|
425 | # define MBDB_DEFAULT_LDAP_SERVER NULL |
---|
426 | # endif /* MBDB_DEFAULT_LDAP_SERVER */ |
---|
427 | |
---|
428 | /* |
---|
429 | ** MBDB_LDAP_INITIALIZE -- initialize LDAP version |
---|
430 | ** |
---|
431 | ** Parameters: |
---|
432 | ** arg -- LDAP specification |
---|
433 | ** |
---|
434 | ** Results: |
---|
435 | ** EX_OK on success, or an EX_* code on failure. |
---|
436 | */ |
---|
437 | |
---|
438 | static int |
---|
439 | mbdb_ldap_initialize(arg) |
---|
440 | char *arg; |
---|
441 | { |
---|
442 | sm_ldap_clear(&LDAPLMAP); |
---|
443 | LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN; |
---|
444 | LDAPLMAP.ldap_target = MBDB_DEFAULT_LDAP_SERVER; |
---|
445 | LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER; |
---|
446 | |
---|
447 | /* Only want one match */ |
---|
448 | LDAPLMAP.ldap_sizelimit = 1; |
---|
449 | |
---|
450 | /* interpolate new ldap_base and ldap_target from arg if given */ |
---|
451 | if (arg != NULL && *arg != '\0') |
---|
452 | { |
---|
453 | char *new; |
---|
454 | char *sep; |
---|
455 | size_t len; |
---|
456 | |
---|
457 | len = strlen(arg) + 1; |
---|
458 | new = sm_malloc(len); |
---|
459 | if (new == NULL) |
---|
460 | return EX_TEMPFAIL; |
---|
461 | (void) sm_strlcpy(new, arg, len); |
---|
462 | sep = strrchr(new, '@'); |
---|
463 | if (sep != NULL) |
---|
464 | { |
---|
465 | *sep++ = '\0'; |
---|
466 | LDAPLMAP.ldap_target = sep; |
---|
467 | } |
---|
468 | LDAPLMAP.ldap_base = new; |
---|
469 | } |
---|
470 | return EX_OK; |
---|
471 | } |
---|
472 | |
---|
473 | |
---|
474 | /* |
---|
475 | ** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name |
---|
476 | ** |
---|
477 | ** Parameters: |
---|
478 | ** name -- name of local mail recipient |
---|
479 | ** user -- pointer to structure to fill in on success |
---|
480 | ** |
---|
481 | ** Results: |
---|
482 | ** On success, fill in *user and return EX_OK. |
---|
483 | ** Failure: EX_NOUSER. |
---|
484 | */ |
---|
485 | |
---|
486 | #define NEED_FULLNAME 0x01 |
---|
487 | #define NEED_HOMEDIR 0x02 |
---|
488 | #define NEED_SHELL 0x04 |
---|
489 | #define NEED_UID 0x08 |
---|
490 | #define NEED_GID 0x10 |
---|
491 | |
---|
492 | static int |
---|
493 | mbdb_ldap_lookup(name, user) |
---|
494 | char *name; |
---|
495 | SM_MBDB_T *user; |
---|
496 | { |
---|
497 | int msgid; |
---|
498 | int need; |
---|
499 | int ret; |
---|
500 | int save_errno; |
---|
501 | LDAPMessage *entry; |
---|
502 | BerElement *ber; |
---|
503 | char *attr = NULL; |
---|
504 | |
---|
505 | if (strlen(name) >= sizeof(user->mbdb_name)) |
---|
506 | { |
---|
507 | errno = EINVAL; |
---|
508 | return EX_NOUSER; |
---|
509 | } |
---|
510 | |
---|
511 | if (LDAPLMAP.ldap_filter == NULL) |
---|
512 | { |
---|
513 | /* map not initialized, but don't have arg here */ |
---|
514 | errno = EFAULT; |
---|
515 | return EX_TEMPFAIL; |
---|
516 | } |
---|
517 | |
---|
518 | if (LDAPLMAP.ldap_pid != getpid()) |
---|
519 | { |
---|
520 | /* re-open map in this child process */ |
---|
521 | LDAPLMAP.ldap_ld = NULL; |
---|
522 | } |
---|
523 | |
---|
524 | if (LDAPLMAP.ldap_ld == NULL) |
---|
525 | { |
---|
526 | /* map not open, try to open now */ |
---|
527 | if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP)) |
---|
528 | return EX_TEMPFAIL; |
---|
529 | } |
---|
530 | |
---|
531 | sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP); |
---|
532 | msgid = sm_ldap_search(&LDAPLMAP, name); |
---|
533 | if (msgid == -1) |
---|
534 | { |
---|
535 | save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE; |
---|
536 | # ifdef LDAP_SERVER_DOWN |
---|
537 | if (errno == LDAP_SERVER_DOWN) |
---|
538 | { |
---|
539 | /* server disappeared, try reopen on next search */ |
---|
540 | sm_ldap_close(&LDAPLMAP); |
---|
541 | } |
---|
542 | # endif /* LDAP_SERVER_DOWN */ |
---|
543 | errno = save_errno; |
---|
544 | return EX_TEMPFAIL; |
---|
545 | } |
---|
546 | |
---|
547 | /* Get results */ |
---|
548 | ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1, |
---|
549 | (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL : |
---|
550 | &(LDAPLMAP.ldap_timeout)), |
---|
551 | &(LDAPLMAP.ldap_res)); |
---|
552 | |
---|
553 | if (ret != LDAP_RES_SEARCH_RESULT && |
---|
554 | ret != LDAP_RES_SEARCH_ENTRY) |
---|
555 | { |
---|
556 | if (ret == 0) |
---|
557 | errno = ETIMEDOUT; |
---|
558 | else |
---|
559 | errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); |
---|
560 | ret = EX_TEMPFAIL; |
---|
561 | goto abort; |
---|
562 | } |
---|
563 | |
---|
564 | entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res); |
---|
565 | if (entry == NULL) |
---|
566 | { |
---|
567 | save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); |
---|
568 | if (save_errno == LDAP_SUCCESS) |
---|
569 | { |
---|
570 | errno = ENOENT; |
---|
571 | ret = EX_NOUSER; |
---|
572 | } |
---|
573 | else |
---|
574 | { |
---|
575 | errno = save_errno; |
---|
576 | ret = EX_TEMPFAIL; |
---|
577 | } |
---|
578 | goto abort; |
---|
579 | } |
---|
580 | |
---|
581 | # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) |
---|
582 | /* |
---|
583 | ** Reset value to prevent lingering |
---|
584 | ** LDAP_DECODING_ERROR due to |
---|
585 | ** OpenLDAP 1.X's hack (see below) |
---|
586 | */ |
---|
587 | |
---|
588 | LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; |
---|
589 | # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ |
---|
590 | |
---|
591 | ret = EX_OK; |
---|
592 | need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID; |
---|
593 | for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber); |
---|
594 | attr != NULL; |
---|
595 | attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber)) |
---|
596 | { |
---|
597 | char **vals; |
---|
598 | |
---|
599 | vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr); |
---|
600 | if (vals == NULL) |
---|
601 | { |
---|
602 | errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); |
---|
603 | if (errno == LDAP_SUCCESS) |
---|
604 | { |
---|
605 | ldap_memfree(attr); |
---|
606 | continue; |
---|
607 | } |
---|
608 | |
---|
609 | /* Must be an error */ |
---|
610 | errno += E_LDAPBASE; |
---|
611 | ret = EX_TEMPFAIL; |
---|
612 | goto abort; |
---|
613 | } |
---|
614 | |
---|
615 | # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) |
---|
616 | /* |
---|
617 | ** Reset value to prevent lingering |
---|
618 | ** LDAP_DECODING_ERROR due to |
---|
619 | ** OpenLDAP 1.X's hack (see below) |
---|
620 | */ |
---|
621 | |
---|
622 | LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; |
---|
623 | # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ |
---|
624 | |
---|
625 | if (vals[0] == NULL || vals[0][0] == '\0') |
---|
626 | goto skip; |
---|
627 | |
---|
628 | if (strcasecmp(attr, "gecos") == 0) |
---|
629 | { |
---|
630 | if (!bitset(NEED_FULLNAME, need) || |
---|
631 | strlen(vals[0]) >= sizeof(user->mbdb_fullname)) |
---|
632 | goto skip; |
---|
633 | |
---|
634 | sm_pwfullname(vals[0], name, user->mbdb_fullname, |
---|
635 | sizeof(user->mbdb_fullname)); |
---|
636 | need &= ~NEED_FULLNAME; |
---|
637 | } |
---|
638 | else if (strcasecmp(attr, "homeDirectory") == 0) |
---|
639 | { |
---|
640 | if (!bitset(NEED_HOMEDIR, need) || |
---|
641 | strlen(vals[0]) >= sizeof(user->mbdb_homedir)) |
---|
642 | goto skip; |
---|
643 | |
---|
644 | (void) sm_strlcpy(user->mbdb_homedir, vals[0], |
---|
645 | sizeof(user->mbdb_homedir)); |
---|
646 | need &= ~NEED_HOMEDIR; |
---|
647 | } |
---|
648 | else if (strcasecmp(attr, "loginShell") == 0) |
---|
649 | { |
---|
650 | if (!bitset(NEED_SHELL, need) || |
---|
651 | strlen(vals[0]) >= sizeof(user->mbdb_shell)) |
---|
652 | goto skip; |
---|
653 | |
---|
654 | (void) sm_strlcpy(user->mbdb_shell, vals[0], |
---|
655 | sizeof(user->mbdb_shell)); |
---|
656 | need &= ~NEED_SHELL; |
---|
657 | } |
---|
658 | else if (strcasecmp(attr, "uidNumber") == 0) |
---|
659 | { |
---|
660 | char *p; |
---|
661 | |
---|
662 | if (!bitset(NEED_UID, need)) |
---|
663 | goto skip; |
---|
664 | |
---|
665 | for (p = vals[0]; *p != '\0'; p++) |
---|
666 | { |
---|
667 | /* allow negative numbers */ |
---|
668 | if (p == vals[0] && *p == '-') |
---|
669 | { |
---|
670 | /* but not simply '-' */ |
---|
671 | if (*(p + 1) == '\0') |
---|
672 | goto skip; |
---|
673 | } |
---|
674 | else if (!isascii(*p) || !isdigit(*p)) |
---|
675 | goto skip; |
---|
676 | } |
---|
677 | user->mbdb_uid = atoi(vals[0]); |
---|
678 | need &= ~NEED_UID; |
---|
679 | } |
---|
680 | else if (strcasecmp(attr, "gidNumber") == 0) |
---|
681 | { |
---|
682 | char *p; |
---|
683 | |
---|
684 | if (!bitset(NEED_GID, need)) |
---|
685 | goto skip; |
---|
686 | |
---|
687 | for (p = vals[0]; *p != '\0'; p++) |
---|
688 | { |
---|
689 | /* allow negative numbers */ |
---|
690 | if (p == vals[0] && *p == '-') |
---|
691 | { |
---|
692 | /* but not simply '-' */ |
---|
693 | if (*(p + 1) == '\0') |
---|
694 | goto skip; |
---|
695 | } |
---|
696 | else if (!isascii(*p) || !isdigit(*p)) |
---|
697 | goto skip; |
---|
698 | } |
---|
699 | user->mbdb_gid = atoi(vals[0]); |
---|
700 | need &= ~NEED_GID; |
---|
701 | } |
---|
702 | |
---|
703 | skip: |
---|
704 | ldap_value_free(vals); |
---|
705 | ldap_memfree(attr); |
---|
706 | } |
---|
707 | |
---|
708 | errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); |
---|
709 | |
---|
710 | /* |
---|
711 | ** We check errno != LDAP_DECODING_ERROR since |
---|
712 | ** OpenLDAP 1.X has a very ugly *undocumented* |
---|
713 | ** hack of returning this error code from |
---|
714 | ** ldap_next_attribute() if the library freed the |
---|
715 | ** ber attribute. See: |
---|
716 | ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html |
---|
717 | */ |
---|
718 | |
---|
719 | if (errno != LDAP_SUCCESS && |
---|
720 | errno != LDAP_DECODING_ERROR) |
---|
721 | { |
---|
722 | /* Must be an error */ |
---|
723 | errno += E_LDAPBASE; |
---|
724 | ret = EX_TEMPFAIL; |
---|
725 | goto abort; |
---|
726 | } |
---|
727 | |
---|
728 | abort: |
---|
729 | save_errno = errno; |
---|
730 | if (attr != NULL) |
---|
731 | { |
---|
732 | ldap_memfree(attr); |
---|
733 | attr = NULL; |
---|
734 | } |
---|
735 | if (LDAPLMAP.ldap_res != NULL) |
---|
736 | { |
---|
737 | ldap_msgfree(LDAPLMAP.ldap_res); |
---|
738 | LDAPLMAP.ldap_res = NULL; |
---|
739 | } |
---|
740 | if (ret == EX_OK) |
---|
741 | { |
---|
742 | if (need == 0) |
---|
743 | { |
---|
744 | (void) sm_strlcpy(user->mbdb_name, name, |
---|
745 | sizeof(user->mbdb_name)); |
---|
746 | save_errno = 0; |
---|
747 | } |
---|
748 | else |
---|
749 | { |
---|
750 | ret = EX_NOUSER; |
---|
751 | save_errno = EINVAL; |
---|
752 | } |
---|
753 | } |
---|
754 | errno = save_errno; |
---|
755 | return ret; |
---|
756 | } |
---|
757 | |
---|
758 | /* |
---|
759 | ** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database |
---|
760 | ** |
---|
761 | ** Parameters: |
---|
762 | ** none. |
---|
763 | ** |
---|
764 | ** Results: |
---|
765 | ** none. |
---|
766 | */ |
---|
767 | |
---|
768 | static void |
---|
769 | mbdb_ldap_terminate() |
---|
770 | { |
---|
771 | sm_ldap_close(&LDAPLMAP); |
---|
772 | } |
---|
773 | # endif /* _LDAP_EXAMPLE_ */ |
---|
774 | #endif /* LDAPMAP */ |
---|