1 /*
2 * Copyright (c) 2006 Thorsten Glaser
3 * Copyright (c) 1998-2008 Proofpoint, Inc. and its suppliers.
4 * All rights reserved.
5 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
6 * Copyright (c) 1992, 1993
7 * The Regents of the University of California. All rights reserved.
8 *
9 * By using this file, you agree to the terms and conditions set
10 * forth in the LICENSE file which can be found at the top level of
11 * the sendmail distribution.
12 *
13 */
14
15 #include <sendmail.h>
16
17 SM_RCSID("$MirOS: src/gnu/usr.sbin/sendmail/sendmail/map.c,v 1.11 2014/06/09 15:17:49 tg Exp $")
18 SM_RCSID("@(#)$Id: map.c,v 8.713 2013-11-22 20:51:55 ca Exp $")
19
20 #if LDAPMAP
21 # include <sm/ldap.h>
22 #endif /* LDAPMAP */
23
24 #if NDBM
25 # include <ndbm.h>
26 # ifdef R_FIRST
27 ERROR README: You are running the Berkeley DB version of ndbm.h. See
28 ERROR README: the README file about tweaking Berkeley DB so it can
29 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
30 ERROR README: and use -DNEWDB instead.
31 # endif /* R_FIRST */
32 #endif /* NDBM */
33 #if NEWDB
34 # include "sm/bdb.h"
35 #endif /* NEWDB */
36 #if NIS
37 struct dom_binding; /* forward reference needed on IRIX */
38 # include <rpcsvc/ypclnt.h>
39 # if NDBM
40 # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
41 # endif /* NDBM */
42 #endif /* NIS */
43
44 #include "map.h"
45
46 #if NEWDB
47 # if DB_VERSION_MAJOR < 2
48 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
49 # endif /* DB_VERSION_MAJOR < 2 */
50 # if DB_VERSION_MAJOR == 2
51 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
52 # endif /* DB_VERSION_MAJOR == 2 */
53 # if DB_VERSION_MAJOR > 2
54 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
55 # endif /* DB_VERSION_MAJOR > 2 */
56 #endif /* NEWDB */
57 static bool extract_canonname __P((char *, char *, char *, char[], int));
58 static void map_close __P((STAB *, int));
59 static void map_init __P((STAB *, int));
60 #ifdef LDAPMAP
61 static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
62 #endif /* LDAPMAP */
63 #if NISPLUS
64 static bool nisplus_getcanonname __P((char *, int, int *));
65 #endif /* NISPLUS */
66 #if NIS
67 static bool nis_getcanonname __P((char *, int, int *));
68 #endif /* NIS */
69 #if NETINFO
70 static bool ni_getcanonname __P((char *, int, int *));
71 #endif /* NETINFO */
72 static bool text_getcanonname __P((char *, int, int *));
73 #if SOCKETMAP
74 static STAB *socket_map_findconn __P((const char*));
75
76 /* XXX arbitrary limit for sanity */
77 # define SOCKETMAP_MAXL 1000000
78 #endif /* SOCKETMAP */
79
80 /* default error message for trying to open a map in write mode */
81 #ifdef ENOSYS
82 # define SM_EMAPCANTWRITE ENOSYS
83 #else /* ENOSYS */
84 # ifdef EFTYPE
85 # define SM_EMAPCANTWRITE EFTYPE
86 # else /* EFTYPE */
87 # define SM_EMAPCANTWRITE ENXIO
88 # endif /* EFTYPE */
89 #endif /* ENOSYS */
90
91 /*
92 ** MAP.C -- implementations for various map classes.
93 **
94 ** Each map class implements a series of functions:
95 **
96 ** bool map_parse(MAP *map, char *args)
97 ** Parse the arguments from the config file. Return true
98 ** if they were ok, false otherwise. Fill in map with the
99 ** values.
100 **
101 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
102 ** Look up the key in the given map. If found, do any
103 ** rewriting the map wants (including "args" if desired)
104 ** and return the value. Set *pstat to the appropriate status
105 ** on error and return NULL. Args will be NULL if called
106 ** from the alias routines, although this should probably
107 ** not be relied upon. It is suggested you call map_rewrite
108 ** to return the results -- it takes care of null termination
109 ** and uses a dynamically expanded buffer as needed.
110 **
111 ** void map_store(MAP *map, char *key, char *value)
112 ** Store the key:value pair in the map.
113 **
114 ** bool map_open(MAP *map, int mode)
115 ** Open the map for the indicated mode. Mode should
116 ** be either O_RDONLY or O_RDWR. Return true if it
117 ** was opened successfully, false otherwise. If the open
118 ** failed and the MF_OPTIONAL flag is not set, it should
119 ** also print an error. If the MF_ALIAS bit is set
120 ** and this map class understands the @:@ convention, it
121 ** should call aliaswait() before returning.
122 **
123 ** void map_close(MAP *map)
124 ** Close the map.
125 **
126 ** This file also includes the implementation for getcanonname.
127 ** It is currently implemented in a pretty ad-hoc manner; it ought
128 ** to be more properly integrated into the map structure.
129 */
130
131 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
132 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
133 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
134 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
135 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
136
137 /*
138 ** MAP_PARSEARGS -- parse config line arguments for database lookup
139 **
140 ** This is a generic version of the map_parse method.
141 **
142 ** Parameters:
143 ** map -- the map being initialized.
144 ** ap -- a pointer to the args on the config line.
145 **
146 ** Returns:
147 ** true -- if everything parsed OK.
148 ** false -- otherwise.
149 **
150 ** Side Effects:
151 ** null terminates the filename; stores it in map
152 */
153
154 bool
map_parseargs(map,ap)155 map_parseargs(map, ap)
156 MAP *map;
157 char *ap;
158 {
159 register char *p = ap;
160
161 /*
162 ** There is no check whether there is really an argument,
163 ** but that's not important enough to warrant extra code.
164 */
165
166 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
167 map->map_spacesub = SpaceSub; /* default value */
168 for (;;)
169 {
170 while (isascii(*p) && isspace(*p))
171 p++;
172 if (*p != '-')
173 break;
174 switch (*++p)
175 {
176 case 'N':
177 map->map_mflags |= MF_INCLNULL;
178 map->map_mflags &= ~MF_TRY0NULL;
179 break;
180
181 case 'O':
182 map->map_mflags &= ~MF_TRY1NULL;
183 break;
184
185 case 'o':
186 map->map_mflags |= MF_OPTIONAL;
187 break;
188
189 case 'f':
190 map->map_mflags |= MF_NOFOLDCASE;
191 break;
192
193 case 'm':
194 map->map_mflags |= MF_MATCHONLY;
195 break;
196
197 case 'A':
198 map->map_mflags |= MF_APPEND;
199 break;
200
201 case 'q':
202 map->map_mflags |= MF_KEEPQUOTES;
203 break;
204
205 case 'a':
206 map->map_app = ++p;
207 break;
208
209 case 'T':
210 map->map_tapp = ++p;
211 break;
212
213 case 'k':
214 while (isascii(*++p) && isspace(*p))
215 continue;
216 map->map_keycolnm = p;
217 break;
218
219 case 'v':
220 while (isascii(*++p) && isspace(*p))
221 continue;
222 map->map_valcolnm = p;
223 break;
224
225 case 'z':
226 if (*++p != '\\')
227 map->map_coldelim = *p;
228 else
229 {
230 switch (*++p)
231 {
232 case 'n':
233 map->map_coldelim = '\n';
234 break;
235
236 case 't':
237 map->map_coldelim = '\t';
238 break;
239
240 default:
241 map->map_coldelim = '\\';
242 }
243 }
244 break;
245
246 case 't':
247 map->map_mflags |= MF_NODEFER;
248 break;
249
250
251 case 'S':
252 map->map_spacesub = *++p;
253 break;
254
255 case 'D':
256 map->map_mflags |= MF_DEFER;
257 break;
258
259 default:
260 syserr("Illegal option %c map %s", *p, map->map_mname);
261 break;
262 }
263 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
264 p++;
265 if (*p != '\0')
266 *p++ = '\0';
267 }
268 if (map->map_app != NULL)
269 map->map_app = newstr(map->map_app);
270 if (map->map_tapp != NULL)
271 map->map_tapp = newstr(map->map_tapp);
272 if (map->map_keycolnm != NULL)
273 map->map_keycolnm = newstr(map->map_keycolnm);
274 if (map->map_valcolnm != NULL)
275 map->map_valcolnm = newstr(map->map_valcolnm);
276
277 if (*p != '\0')
278 {
279 map->map_file = p;
280 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
281 p++;
282 if (*p != '\0')
283 *p++ = '\0';
284 map->map_file = newstr(map->map_file);
285 }
286
287 while (*p != '\0' && isascii(*p) && isspace(*p))
288 p++;
289 if (*p != '\0')
290 map->map_rebuild = newstr(p);
291
292 if (map->map_file == NULL &&
293 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
294 {
295 syserr("No file name for %s map %s",
296 map->map_class->map_cname, map->map_mname);
297 return false;
298 }
299 return true;
300 }
301 /*
302 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
303 **
304 ** It also adds the map_app string. It can be used as a utility
305 ** in the map_lookup method.
306 **
307 ** Parameters:
308 ** map -- the map that causes this.
309 ** s -- the string to rewrite, NOT necessarily null terminated.
310 ** slen -- the length of s.
311 ** av -- arguments to interpolate into buf.
312 **
313 ** Returns:
314 ** Pointer to rewritten result. This is static data that
315 ** should be copied if it is to be saved!
316 */
317
318 char *
map_rewrite(map,s,slen,av)319 map_rewrite(map, s, slen, av)
320 register MAP *map;
321 register const char *s;
322 size_t slen;
323 char **av;
324 {
325 register char *bp;
326 register char c;
327 char **avp;
328 register char *ap;
329 size_t l;
330 size_t len;
331 static size_t buflen = 0;
332 static char *buf = NULL;
333
334 if (tTd(39, 1))
335 {
336 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
337 if (av == NULL)
338 sm_dprintf(" (nullv)");
339 else
340 {
341 for (avp = av; *avp != NULL; avp++)
342 sm_dprintf("\n\t%s", *avp);
343 }
344 sm_dprintf("\n");
345 }
346
347 /* count expected size of output (can safely overestimate) */
348 l = len = slen;
349 if (av != NULL)
350 {
351 const char *sp = s;
352
353 while (l-- > 0 && (c = *sp++) != '\0')
354 {
355 if (c != '%')
356 continue;
357 if (l-- <= 0)
358 break;
359 c = *sp++;
360 if (!(isascii(c) && isdigit(c)))
361 continue;
362 for (avp = av; --c >= '0' && *avp != NULL; avp++)
363 continue;
364 if (*avp == NULL)
365 continue;
366 len += strlen(*avp);
367 }
368 }
369 if (map->map_app != NULL)
370 len += strlen(map->map_app);
371 if (buflen < ++len)
372 {
373 /* need to malloc additional space */
374 buflen = len;
375 if (buf != NULL)
376 sm_free(buf);
377 buf = sm_pmalloc_x(buflen);
378 }
379
380 bp = buf;
381 if (av == NULL)
382 {
383 memmove(bp, s, slen);
384 bp += slen;
385
386 /* assert(len > slen); */
387 len -= slen;
388 }
389 else
390 {
391 while (slen-- > 0 && (c = *s++) != '\0')
392 {
393 if (c != '%')
394 {
395 pushc:
396 if (len-- <= 1)
397 break;
398 *bp++ = c;
399 continue;
400 }
401 if (slen-- <= 0 || (c = *s++) == '\0')
402 c = '%';
403 if (c == '%')
404 goto pushc;
405 if (!(isascii(c) && isdigit(c)))
406 {
407 if (len-- <= 1)
408 break;
409 *bp++ = '%';
410 goto pushc;
411 }
412 for (avp = av; --c >= '0' && *avp != NULL; avp++)
413 continue;
414 if (*avp == NULL)
415 continue;
416
417 /* transliterate argument into output string */
418 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
419 *bp++ = c;
420 }
421 }
422 if (map->map_app != NULL && len > 0)
423 (void) sm_strlcpy(bp, map->map_app, len);
424 else
425 *bp = '\0';
426 if (tTd(39, 1))
427 sm_dprintf("map_rewrite => %s\n", buf);
428 return buf;
429 }
430 /*
431 ** INITMAPS -- rebuild alias maps
432 **
433 ** Parameters:
434 ** none.
435 **
436 ** Returns:
437 ** none.
438 */
439
440 void
initmaps()441 initmaps()
442 {
443 #if XDEBUG
444 checkfd012("entering initmaps");
445 #endif /* XDEBUG */
446 stabapply(map_init, 0);
447 #if XDEBUG
448 checkfd012("exiting initmaps");
449 #endif /* XDEBUG */
450 }
451 /*
452 ** MAP_INIT -- rebuild a map
453 **
454 ** Parameters:
455 ** s -- STAB entry: if map: try to rebuild
456 ** unused -- unused variable
457 **
458 ** Returns:
459 ** none.
460 **
461 ** Side Effects:
462 ** will close already open rebuildable map.
463 */
464
465 /* ARGSUSED1 */
466 static void
map_init(s,unused)467 map_init(s, unused)
468 register STAB *s;
469 int unused;
470 {
471 register MAP *map;
472
473 /* has to be a map */
474 if (s->s_symtype != ST_MAP)
475 return;
476
477 map = &s->s_map;
478 if (!bitset(MF_VALID, map->map_mflags))
479 return;
480
481 if (tTd(38, 2))
482 sm_dprintf("map_init(%s:%s, %s)\n",
483 map->map_class->map_cname == NULL ? "NULL" :
484 map->map_class->map_cname,
485 map->map_mname == NULL ? "NULL" : map->map_mname,
486 map->map_file == NULL ? "NULL" : map->map_file);
487
488 if (!bitset(MF_ALIAS, map->map_mflags) ||
489 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
490 {
491 if (tTd(38, 3))
492 sm_dprintf("\tnot rebuildable\n");
493 return;
494 }
495
496 /* if already open, close it (for nested open) */
497 if (bitset(MF_OPEN, map->map_mflags))
498 {
499 map->map_mflags |= MF_CLOSING;
500 map->map_class->map_close(map);
501 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
502 }
503
504 (void) rebuildaliases(map, false);
505 return;
506 }
507 /*
508 ** OPENMAP -- open a map
509 **
510 ** Parameters:
511 ** map -- map to open (it must not be open).
512 **
513 ** Returns:
514 ** whether open succeeded.
515 */
516
517 bool
openmap(map)518 openmap(map)
519 MAP *map;
520 {
521 bool restore = false;
522 bool savehold = HoldErrs;
523 bool savequick = QuickAbort;
524 int saveerrors = Errors;
525
526 if (!bitset(MF_VALID, map->map_mflags))
527 return false;
528
529 /* better safe than sorry... */
530 if (bitset(MF_OPEN, map->map_mflags))
531 return true;
532
533 /* Don't send a map open error out via SMTP */
534 if ((OnlyOneError || QuickAbort) &&
535 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
536 {
537 restore = true;
538 HoldErrs = true;
539 QuickAbort = false;
540 }
541
542 errno = 0;
543 if (map->map_class->map_open(map, O_RDONLY))
544 {
545 if (tTd(38, 4))
546 sm_dprintf("openmap()\t%s:%s %s: valid\n",
547 map->map_class->map_cname == NULL ? "NULL" :
548 map->map_class->map_cname,
549 map->map_mname == NULL ? "NULL" :
550 map->map_mname,
551 map->map_file == NULL ? "NULL" :
552 map->map_file);
553 map->map_mflags |= MF_OPEN;
554 map->map_pid = CurrentPid;
555 }
556 else
557 {
558 if (tTd(38, 4))
559 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
560 map->map_class->map_cname == NULL ? "NULL" :
561 map->map_class->map_cname,
562 map->map_mname == NULL ? "NULL" :
563 map->map_mname,
564 map->map_file == NULL ? "NULL" :
565 map->map_file,
566 errno == 0 ? "" : ": ",
567 errno == 0 ? "" : sm_errstring(errno));
568 if (!bitset(MF_OPTIONAL, map->map_mflags))
569 {
570 extern MAPCLASS BogusMapClass;
571
572 map->map_orgclass = map->map_class;
573 map->map_class = &BogusMapClass;
574 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
575 map->map_pid = CurrentPid;
576 }
577 else
578 {
579 /* don't try again */
580 map->map_mflags &= ~MF_VALID;
581 }
582 }
583
584 if (restore)
585 {
586 Errors = saveerrors;
587 HoldErrs = savehold;
588 QuickAbort = savequick;
589 }
590
591 return bitset(MF_OPEN, map->map_mflags);
592 }
593 /*
594 ** CLOSEMAPS -- close all open maps opened by the current pid.
595 **
596 ** Parameters:
597 ** bogus -- only close bogus maps.
598 **
599 ** Returns:
600 ** none.
601 */
602
603 void
closemaps(bogus)604 closemaps(bogus)
605 bool bogus;
606 {
607 stabapply(map_close, bogus);
608 }
609 /*
610 ** MAP_CLOSE -- close a map opened by the current pid.
611 **
612 ** Parameters:
613 ** s -- STAB entry: if map: try to close
614 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
615 **
616 ** Returns:
617 ** none.
618 */
619
620 /* ARGSUSED1 */
621 static void
map_close(s,bogus)622 map_close(s, bogus)
623 register STAB *s;
624 int bogus; /* int because of stabapply(), used as bool */
625 {
626 MAP *map;
627 extern MAPCLASS BogusMapClass;
628
629 if (s->s_symtype != ST_MAP)
630 return;
631
632 map = &s->s_map;
633
634 /*
635 ** close the map iff:
636 ** it is valid and open and opened by this process
637 ** and (!bogus or it's a bogus map or it is not persistent)
638 ** negate this: return iff
639 ** it is not valid or it is not open or not opened by this process
640 ** or (bogus and it's not a bogus map and it's not not-persistent)
641 */
642
643 if (!bitset(MF_VALID, map->map_mflags) ||
644 !bitset(MF_OPEN, map->map_mflags) ||
645 bitset(MF_CLOSING, map->map_mflags) ||
646 map->map_pid != CurrentPid ||
647 (bogus && map->map_class != &BogusMapClass &&
648 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
649 return;
650
651 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
652 map->map_orgclass != &BogusMapClass)
653 map->map_class = map->map_orgclass;
654 if (tTd(38, 5))
655 sm_dprintf("closemaps: closing %s (%s)\n",
656 map->map_mname == NULL ? "NULL" : map->map_mname,
657 map->map_file == NULL ? "NULL" : map->map_file);
658
659 if (!bitset(MF_OPENBOGUS, map->map_mflags))
660 {
661 map->map_mflags |= MF_CLOSING;
662 map->map_class->map_close(map);
663 }
664 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
665 }
666
667 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
668 extern int getdomainname();
669
670 /* this is mainly for backward compatibility in Sun environment */
671 static char *
sun_init_domain()672 sun_init_domain()
673 {
674 /*
675 ** Get the domain name from the kernel.
676 ** If it does not start with a leading dot, then remove
677 ** the first component. Since leading dots are funny Unix
678 ** files, we treat a leading "+" the same as a leading dot.
679 ** Finally, force there to be at least one dot in the domain name
680 ** (i.e. top-level domains are not allowed, like "com", must be
681 ** something like "sun.com").
682 */
683
684 char buf[MAXNAME];
685 char *period, *autodomain;
686
687 if (getdomainname(buf, sizeof buf) < 0)
688 return NULL;
689
690 if (buf[0] == '\0')
691 return NULL;
692
693 if (tTd(0, 20))
694 printf("domainname = %s\n", buf);
695
696 if (buf[0] == '+')
697 buf[0] = '.';
698 period = strchr(buf, '.');
699 if (period == NULL)
700 autodomain = buf;
701 else
702 autodomain = period + 1;
703 if (strchr(autodomain, '.') == NULL)
704 return newstr(buf);
705 else
706 return newstr(autodomain);
707 }
708 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
709
710 /*
711 ** GETCANONNAME -- look up name using service switch
712 **
713 ** Parameters:
714 ** host -- the host name to look up.
715 ** hbsize -- the size of the host buffer.
716 ** trymx -- if set, try MX records.
717 ** pttl -- pointer to return TTL (can be NULL).
718 **
719 ** Returns:
720 ** true -- if the host was found.
721 ** false -- otherwise.
722 */
723
724 bool
getcanonname(host,hbsize,trymx,pttl)725 getcanonname(host, hbsize, trymx, pttl)
726 char *host;
727 int hbsize;
728 bool trymx;
729 int *pttl;
730 {
731 int nmaps;
732 int mapno;
733 bool found = false;
734 bool got_tempfail = false;
735 auto int status = EX_UNAVAILABLE;
736 char *maptype[MAXMAPSTACK];
737 short mapreturn[MAXMAPACTIONS];
738 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
739 bool should_try_nis_domain = false;
740 static char *nis_domain = NULL;
741 #endif
742
743 nmaps = switch_map_find("hosts", maptype, mapreturn);
744 if (pttl != 0)
745 *pttl = SM_DEFAULT_TTL;
746 for (mapno = 0; mapno < nmaps; mapno++)
747 {
748 int i;
749
750 if (tTd(38, 20))
751 sm_dprintf("getcanonname(%s), trying %s\n",
752 host, maptype[mapno]);
753 if (strcmp("files", maptype[mapno]) == 0)
754 {
755 found = text_getcanonname(host, hbsize, &status);
756 }
757 #if NIS
758 else if (strcmp("nis", maptype[mapno]) == 0)
759 {
760 found = nis_getcanonname(host, hbsize, &status);
761 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
762 if (nis_domain == NULL)
763 nis_domain = sun_init_domain();
764 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
765 }
766 #endif /* NIS */
767 #if NISPLUS
768 else if (strcmp("nisplus", maptype[mapno]) == 0)
769 {
770 found = nisplus_getcanonname(host, hbsize, &status);
771 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
772 if (nis_domain == NULL)
773 nis_domain = sun_init_domain();
774 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
775 }
776 #endif /* NISPLUS */
777 #if NAMED_BIND
778 else if (strcmp("dns", maptype[mapno]) == 0)
779 {
780 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
781 }
782 #endif /* NAMED_BIND */
783 #if NETINFO
784 else if (strcmp("netinfo", maptype[mapno]) == 0)
785 {
786 found = ni_getcanonname(host, hbsize, &status);
787 }
788 #endif /* NETINFO */
789 else
790 {
791 found = false;
792 status = EX_UNAVAILABLE;
793 }
794
795 /*
796 ** Heuristic: if $m is not set, we are running during system
797 ** startup. In this case, when a name is apparently found
798 ** but has no dot, treat is as not found. This avoids
799 ** problems if /etc/hosts has no FQDN but is listed first
800 ** in the service switch.
801 */
802
803 if (found &&
804 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
805 break;
806
807 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
808 if (found)
809 should_try_nis_domain = true;
810 /* but don't break, as we need to try all methods first */
811 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
812
813 /* see if we should continue */
814 if (status == EX_TEMPFAIL)
815 {
816 i = MA_TRYAGAIN;
817 got_tempfail = true;
818 }
819 else if (status == EX_NOTFOUND)
820 i = MA_NOTFOUND;
821 else
822 i = MA_UNAVAIL;
823 if (bitset(1 << mapno, mapreturn[i]))
824 break;
825 }
826
827 if (found)
828 {
829 char *d;
830
831 if (tTd(38, 20))
832 sm_dprintf("getcanonname(%s), found\n", host);
833
834 /*
835 ** If returned name is still single token, compensate
836 ** by tagging on $m. This is because some sites set
837 ** up their DNS or NIS databases wrong.
838 */
839
840 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
841 {
842 d = macvalue('m', CurEnv);
843 if (d != NULL &&
844 hbsize > (int) (strlen(host) + strlen(d) + 1))
845 {
846 if (host[strlen(host) - 1] != '.')
847 (void) sm_strlcat2(host, ".", d,
848 hbsize);
849 else
850 (void) sm_strlcat(host, d, hbsize);
851 }
852 else
853 {
854 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
855 if (VendorCode == VENDOR_SUN &&
856 should_try_nis_domain)
857 {
858 goto try_nis_domain;
859 }
860 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
861 return false;
862 }
863 }
864 return true;
865 }
866
867 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
868 if (VendorCode == VENDOR_SUN && should_try_nis_domain)
869 {
870 try_nis_domain:
871 if (nis_domain != NULL &&
872 strlen(nis_domain) + strlen(host) + 1 < hbsize)
873 {
874 (void) sm_strlcat2(host, ".", nis_domain, hbsize);
875 return true;
876 }
877 }
878 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
879
880 if (tTd(38, 20))
881 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
882 status);
883
884 if (got_tempfail)
885 SM_SET_H_ERRNO(TRY_AGAIN);
886 else
887 SM_SET_H_ERRNO(HOST_NOT_FOUND);
888
889 return false;
890 }
891 /*
892 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
893 **
894 ** Parameters:
895 ** name -- the name against which to match.
896 ** dot -- where to reinsert '.' to get FQDN
897 ** line -- the /etc/hosts line.
898 ** cbuf -- the location to store the result.
899 ** cbuflen -- the size of cbuf.
900 **
901 ** Returns:
902 ** true -- if the line matched the desired name.
903 ** false -- otherwise.
904 */
905
906 static bool
extract_canonname(name,dot,line,cbuf,cbuflen)907 extract_canonname(name, dot, line, cbuf, cbuflen)
908 char *name;
909 char *dot;
910 char *line;
911 char cbuf[];
912 int cbuflen;
913 {
914 int i;
915 char *p;
916 bool found = false;
917
918 cbuf[0] = '\0';
919 if (line[0] == '#')
920 return false;
921
922 for (i = 1; ; i++)
923 {
924 char nbuf[MAXNAME + 1];
925
926 p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
927 if (p == NULL)
928 break;
929 if (*p == '\0')
930 continue;
931 if (cbuf[0] == '\0' ||
932 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
933 {
934 (void) sm_strlcpy(cbuf, p, cbuflen);
935 }
936 if (sm_strcasecmp(name, p) == 0)
937 found = true;
938 else if (dot != NULL)
939 {
940 /* try looking for the FQDN as well */
941 *dot = '.';
942 if (sm_strcasecmp(name, p) == 0)
943 found = true;
944 *dot = '\0';
945 }
946 }
947 if (found && strchr(cbuf, '.') == NULL)
948 {
949 /* try to add a domain on the end of the name */
950 char *domain = macvalue('m', CurEnv);
951
952 if (domain != NULL &&
953 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
954 {
955 p = &cbuf[i];
956 *p++ = '.';
957 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
958 }
959 }
960 return found;
961 }
962
963 /*
964 ** DNS modules
965 */
966
967 #if NAMED_BIND
968 # if DNSMAP
969
970 # include "sm_resolve.h"
971 # if NETINET || NETINET6
972 # include <arpa/inet.h>
973 # endif /* NETINET || NETINET6 */
974
975 /*
976 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
977 */
978
979 bool
dns_map_open(map,mode)980 dns_map_open(map, mode)
981 MAP *map;
982 int mode;
983 {
984 if (tTd(38,2))
985 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
986
987 mode &= O_ACCMODE;
988 if (mode != O_RDONLY)
989 {
990 /* issue a pseudo-error message */
991 errno = SM_EMAPCANTWRITE;
992 return false;
993 }
994 return true;
995 }
996
997 /*
998 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
999 **
1000 ** Parameters:
1001 ** map -- pointer to MAP
1002 ** args -- pointer to the args on the config line.
1003 **
1004 ** Returns:
1005 ** true -- if everything parsed OK.
1006 ** false -- otherwise.
1007 */
1008
1009 #define map_sizelimit map_lockfd /* overload field */
1010
1011 struct dns_map
1012 {
1013 int dns_m_type;
1014 };
1015
1016 bool
dns_map_parseargs(map,args)1017 dns_map_parseargs(map,args)
1018 MAP *map;
1019 char *args;
1020 {
1021 register char *p = args;
1022 struct dns_map *map_p;
1023
1024 map_p = (struct dns_map *) xalloc(sizeof(*map_p));
1025 map_p->dns_m_type = -1;
1026 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1027
1028 for (;;)
1029 {
1030 while (isascii(*p) && isspace(*p))
1031 p++;
1032 if (*p != '-')
1033 break;
1034 switch (*++p)
1035 {
1036 case 'N':
1037 map->map_mflags |= MF_INCLNULL;
1038 map->map_mflags &= ~MF_TRY0NULL;
1039 break;
1040
1041 case 'O':
1042 map->map_mflags &= ~MF_TRY1NULL;
1043 break;
1044
1045 case 'o':
1046 map->map_mflags |= MF_OPTIONAL;
1047 break;
1048
1049 case 'f':
1050 map->map_mflags |= MF_NOFOLDCASE;
1051 break;
1052
1053 case 'm':
1054 map->map_mflags |= MF_MATCHONLY;
1055 break;
1056
1057 case 'A':
1058 map->map_mflags |= MF_APPEND;
1059 break;
1060
1061 case 'q':
1062 map->map_mflags |= MF_KEEPQUOTES;
1063 break;
1064
1065 case 't':
1066 map->map_mflags |= MF_NODEFER;
1067 break;
1068
1069 case 'a':
1070 map->map_app = ++p;
1071 break;
1072
1073 case 'T':
1074 map->map_tapp = ++p;
1075 break;
1076
1077 case 'd':
1078 {
1079 char *h;
1080
1081 ++p;
1082 h = strchr(p, ' ');
1083 if (h != NULL)
1084 *h = '\0';
1085 map->map_timeout = convtime(p, 's');
1086 if (h != NULL)
1087 *h = ' ';
1088 }
1089 break;
1090
1091 case 'r':
1092 while (isascii(*++p) && isspace(*p))
1093 continue;
1094 map->map_retry = atoi(p);
1095 break;
1096
1097 case 'z':
1098 if (*++p != '\\')
1099 map->map_coldelim = *p;
1100 else
1101 {
1102 switch (*++p)
1103 {
1104 case 'n':
1105 map->map_coldelim = '\n';
1106 break;
1107
1108 case 't':
1109 map->map_coldelim = '\t';
1110 break;
1111
1112 default:
1113 map->map_coldelim = '\\';
1114 }
1115 }
1116 break;
1117
1118 case 'Z':
1119 while (isascii(*++p) && isspace(*p))
1120 continue;
1121 map->map_sizelimit = atoi(p);
1122 break;
1123
1124 /* Start of dns_map specific args */
1125 case 'R': /* search field */
1126 {
1127 char *h;
1128
1129 while (isascii(*++p) && isspace(*p))
1130 continue;
1131 h = strchr(p, ' ');
1132 if (h != NULL)
1133 *h = '\0';
1134 map_p->dns_m_type = dns_string_to_type(p);
1135 if (h != NULL)
1136 *h = ' ';
1137 if (map_p->dns_m_type < 0)
1138 syserr("dns map %s: wrong type %s",
1139 map->map_mname, p);
1140 }
1141 break;
1142
1143 case 'B': /* base domain */
1144 {
1145 char *h;
1146
1147 while (isascii(*++p) && isspace(*p))
1148 continue;
1149 h = strchr(p, ' ');
1150 if (h != NULL)
1151 *h = '\0';
1152
1153 /*
1154 ** slight abuse of map->map_file; it isn't
1155 ** used otherwise in this map type.
1156 */
1157
1158 map->map_file = newstr(p);
1159 if (h != NULL)
1160 *h = ' ';
1161 }
1162 break;
1163 }
1164 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1165 p++;
1166 if (*p != '\0')
1167 *p++ = '\0';
1168 }
1169 if (map_p->dns_m_type < 0)
1170 syserr("dns map %s: missing -R type", map->map_mname);
1171 if (map->map_app != NULL)
1172 map->map_app = newstr(map->map_app);
1173 if (map->map_tapp != NULL)
1174 map->map_tapp = newstr(map->map_tapp);
1175
1176 /*
1177 ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1178 ** Even if this assumption is wrong, we use only one byte,
1179 ** so it doesn't really matter.
1180 */
1181
1182 map->map_db1 = (ARBPTR_T) map_p;
1183 return true;
1184 }
1185
1186 /*
1187 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1188 **
1189 ** Parameters:
1190 ** map -- pointer to MAP
1191 ** name -- name to lookup
1192 ** av -- arguments to interpolate into buf.
1193 ** statp -- pointer to status (EX_)
1194 **
1195 ** Returns:
1196 ** result of lookup if succeeded.
1197 ** NULL -- otherwise.
1198 */
1199
1200 char *
dns_map_lookup(map,name,av,statp)1201 dns_map_lookup(map, name, av, statp)
1202 MAP *map;
1203 char *name;
1204 char **av;
1205 int *statp;
1206 {
1207 int resnum = 0;
1208 char *vp = NULL, *result = NULL;
1209 size_t vsize;
1210 struct dns_map *map_p;
1211 RESOURCE_RECORD_T *rr = NULL;
1212 DNS_REPLY_T *r = NULL;
1213 # if NETINET6
1214 static char buf6[INET6_ADDRSTRLEN];
1215 # endif /* NETINET6 */
1216
1217 if (tTd(38, 20))
1218 sm_dprintf("dns_map_lookup(%s, %s)\n",
1219 map->map_mname, name);
1220
1221 map_p = (struct dns_map *)(map->map_db1);
1222 if (map->map_file != NULL && *map->map_file != '\0')
1223 {
1224 size_t len;
1225 char *appdomain;
1226
1227 len = strlen(map->map_file) + strlen(name) + 2;
1228 appdomain = (char *) sm_malloc(len);
1229 if (appdomain == NULL)
1230 {
1231 *statp = EX_UNAVAILABLE;
1232 return NULL;
1233 }
1234 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1235 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1236 map->map_timeout, map->map_retry);
1237 sm_free(appdomain);
1238 }
1239 else
1240 {
1241 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1242 map->map_timeout, map->map_retry);
1243 }
1244
1245 if (r == NULL)
1246 {
1247 result = NULL;
1248 if (h_errno == TRY_AGAIN || transienterror(errno))
1249 *statp = EX_TEMPFAIL;
1250 else
1251 *statp = EX_NOTFOUND;
1252 goto cleanup;
1253 }
1254 *statp = EX_OK;
1255 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1256 {
1257 char *type = NULL;
1258 char *value = NULL;
1259
1260 switch (rr->rr_type)
1261 {
1262 case T_NS:
1263 type = "T_NS";
1264 value = rr->rr_u.rr_txt;
1265 break;
1266 case T_CNAME:
1267 type = "T_CNAME";
1268 value = rr->rr_u.rr_txt;
1269 break;
1270 case T_AFSDB:
1271 type = "T_AFSDB";
1272 value = rr->rr_u.rr_mx->mx_r_domain;
1273 break;
1274 case T_SRV:
1275 type = "T_SRV";
1276 value = rr->rr_u.rr_srv->srv_r_target;
1277 break;
1278 case T_PTR:
1279 type = "T_PTR";
1280 value = rr->rr_u.rr_txt;
1281 break;
1282 case T_TXT:
1283 type = "T_TXT";
1284 value = rr->rr_u.rr_txt;
1285 break;
1286 case T_MX:
1287 type = "T_MX";
1288 value = rr->rr_u.rr_mx->mx_r_domain;
1289 break;
1290 # if NETINET
1291 case T_A:
1292 type = "T_A";
1293 value = inet_ntoa(*(rr->rr_u.rr_a));
1294 break;
1295 # endif /* NETINET */
1296 # if NETINET6
1297 case T_AAAA:
1298 type = "T_AAAA";
1299 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1300 sizeof(buf6));
1301 break;
1302 # endif /* NETINET6 */
1303 }
1304
1305 (void) strreplnonprt(value, 'X');
1306 if (map_p->dns_m_type != rr->rr_type)
1307 {
1308 if (tTd(38, 40))
1309 sm_dprintf("\tskipping type %s (%d) value %s\n",
1310 type != NULL ? type : "<UNKNOWN>",
1311 rr->rr_type,
1312 value != NULL ? value : "<NO VALUE>");
1313 continue;
1314 }
1315
1316 # if NETINET6
1317 if (rr->rr_type == T_AAAA && value == NULL)
1318 {
1319 result = NULL;
1320 *statp = EX_DATAERR;
1321 if (tTd(38, 40))
1322 sm_dprintf("\tbad T_AAAA conversion\n");
1323 goto cleanup;
1324 }
1325 # endif /* NETINET6 */
1326 if (tTd(38, 40))
1327 sm_dprintf("\tfound type %s (%d) value %s\n",
1328 type != NULL ? type : "<UNKNOWN>",
1329 rr->rr_type,
1330 value != NULL ? value : "<NO VALUE>");
1331 if (value != NULL &&
1332 (map->map_coldelim == '\0' ||
1333 map->map_sizelimit == 1 ||
1334 bitset(MF_MATCHONLY, map->map_mflags)))
1335 {
1336 /* Only care about the first match */
1337 vp = newstr(value);
1338 break;
1339 }
1340 else if (vp == NULL)
1341 {
1342 /* First result */
1343 vp = newstr(value);
1344 }
1345 else
1346 {
1347 /* concatenate the results */
1348 int sz;
1349 char *new;
1350
1351 sz = strlen(vp) + strlen(value) + 2;
1352 new = xalloc(sz);
1353 (void) sm_snprintf(new, sz, "%s%c%s",
1354 vp, map->map_coldelim, value);
1355 sm_free(vp);
1356 vp = new;
1357 if (map->map_sizelimit > 0 &&
1358 ++resnum >= map->map_sizelimit)
1359 break;
1360 }
1361 }
1362 if (vp == NULL)
1363 {
1364 result = NULL;
1365 *statp = EX_NOTFOUND;
1366 if (tTd(38, 40))
1367 sm_dprintf("\tno match found\n");
1368 goto cleanup;
1369 }
1370
1371 /* Cleanly truncate for rulesets */
1372 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1373
1374 vsize = strlen(vp);
1375
1376 if (LogLevel > 9)
1377 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1378 name, vp);
1379 if (bitset(MF_MATCHONLY, map->map_mflags))
1380 result = map_rewrite(map, name, strlen(name), NULL);
1381 else
1382 result = map_rewrite(map, vp, vsize, av);
1383
1384 cleanup:
1385 if (vp != NULL)
1386 sm_free(vp);
1387 if (r != NULL)
1388 dns_free_data(r);
1389 return result;
1390 }
1391 # endif /* DNSMAP */
1392 #endif /* NAMED_BIND */
1393
1394 /*
1395 ** NDBM modules
1396 */
1397
1398 #if NDBM
1399
1400 /*
1401 ** NDBM_MAP_OPEN -- DBM-style map open
1402 */
1403
1404 bool
ndbm_map_open(map,mode)1405 ndbm_map_open(map, mode)
1406 MAP *map;
1407 int mode;
1408 {
1409 register DBM *dbm;
1410 int save_errno;
1411 int dfd;
1412 int pfd;
1413 long sff;
1414 int ret;
1415 int smode = S_IREAD;
1416 char dirfile[MAXPATHLEN];
1417 char pagfile[MAXPATHLEN];
1418 struct stat st;
1419 struct stat std, stp;
1420
1421 if (tTd(38, 2))
1422 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1423 map->map_mname, map->map_file, mode);
1424 map->map_lockfd = -1;
1425 mode &= O_ACCMODE;
1426
1427 /* do initial file and directory checks */
1428 if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
1429 map->map_file, ".dir") >= sizeof(dirfile) ||
1430 sm_strlcpyn(pagfile, sizeof(pagfile), 2,
1431 map->map_file, ".pag") >= sizeof(pagfile))
1432 {
1433 errno = 0;
1434 if (!bitset(MF_OPTIONAL, map->map_mflags))
1435 syserr("dbm map \"%s\": map file %s name too long",
1436 map->map_mname, map->map_file);
1437 return false;
1438 }
1439 sff = SFF_ROOTOK|SFF_REGONLY;
1440 if (mode == O_RDWR)
1441 {
1442 sff |= SFF_CREAT;
1443 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1444 sff |= SFF_NOSLINK;
1445 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1446 sff |= SFF_NOHLINK;
1447 smode = S_IWRITE;
1448 }
1449 else
1450 {
1451 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1452 sff |= SFF_NOWLINK;
1453 }
1454 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1455 sff |= SFF_SAFEDIRPATH;
1456 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1457 sff, smode, &std);
1458 if (ret == 0)
1459 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1460 sff, smode, &stp);
1461
1462 if (ret != 0)
1463 {
1464 char *prob = "unsafe";
1465
1466 /* cannot open this map */
1467 if (ret == ENOENT)
1468 prob = "missing";
1469 if (tTd(38, 2))
1470 sm_dprintf("\t%s map file: %d\n", prob, ret);
1471 if (!bitset(MF_OPTIONAL, map->map_mflags))
1472 syserr("dbm map \"%s\": %s map file %s",
1473 map->map_mname, prob, map->map_file);
1474 return false;
1475 }
1476 if (std.st_mode == ST_MODE_NOFILE)
1477 mode |= O_CREAT|O_EXCL;
1478
1479 # if LOCK_ON_OPEN
1480 if (mode == O_RDONLY)
1481 mode |= O_SHLOCK;
1482 else
1483 mode |= O_TRUNC|O_EXLOCK;
1484 # else /* LOCK_ON_OPEN */
1485 if ((mode & O_ACCMODE) == O_RDWR)
1486 {
1487 # if NOFTRUNCATE
1488 /*
1489 ** Warning: race condition. Try to lock the file as
1490 ** quickly as possible after opening it.
1491 ** This may also have security problems on some systems,
1492 ** but there isn't anything we can do about it.
1493 */
1494
1495 mode |= O_TRUNC;
1496 # else /* NOFTRUNCATE */
1497 /*
1498 ** This ugly code opens the map without truncating it,
1499 ** locks the file, then truncates it. Necessary to
1500 ** avoid race conditions.
1501 */
1502
1503 int dirfd;
1504 int pagfd;
1505 long sff = SFF_CREAT|SFF_OPENASROOT;
1506
1507 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1508 sff |= SFF_NOSLINK;
1509 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1510 sff |= SFF_NOHLINK;
1511
1512 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1513 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1514
1515 if (dirfd < 0 || pagfd < 0)
1516 {
1517 save_errno = errno;
1518 if (dirfd >= 0)
1519 (void) close(dirfd);
1520 if (pagfd >= 0)
1521 (void) close(pagfd);
1522 errno = save_errno;
1523 syserr("ndbm_map_open: cannot create database %s",
1524 map->map_file);
1525 return false;
1526 }
1527 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1528 ftruncate(pagfd, (off_t) 0) < 0)
1529 {
1530 save_errno = errno;
1531 (void) close(dirfd);
1532 (void) close(pagfd);
1533 errno = save_errno;
1534 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1535 map->map_file);
1536 return false;
1537 }
1538
1539 /* if new file, get "before" bits for later filechanged check */
1540 if (std.st_mode == ST_MODE_NOFILE &&
1541 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1542 {
1543 save_errno = errno;
1544 (void) close(dirfd);
1545 (void) close(pagfd);
1546 errno = save_errno;
1547 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1548 map->map_file);
1549 return false;
1550 }
1551
1552 /* have to save the lock for the duration (bletch) */
1553 map->map_lockfd = dirfd;
1554 (void) close(pagfd);
1555
1556 /* twiddle bits for dbm_open */
1557 mode &= ~(O_CREAT|O_EXCL);
1558 # endif /* NOFTRUNCATE */
1559 }
1560 # endif /* LOCK_ON_OPEN */
1561
1562 /* open the database */
1563 dbm = dbm_open(map->map_file, mode, DBMMODE);
1564 if (dbm == NULL)
1565 {
1566 save_errno = errno;
1567 if (bitset(MF_ALIAS, map->map_mflags) &&
1568 aliaswait(map, ".pag", false))
1569 return true;
1570 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1571 if (map->map_lockfd >= 0)
1572 (void) close(map->map_lockfd);
1573 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1574 errno = save_errno;
1575 if (!bitset(MF_OPTIONAL, map->map_mflags))
1576 syserr("Cannot open DBM database %s", map->map_file);
1577 return false;
1578 }
1579 dfd = dbm_dirfno(dbm);
1580 pfd = dbm_pagfno(dbm);
1581 if (dfd == pfd)
1582 {
1583 /* heuristic: if files are linked, this is actually gdbm */
1584 dbm_close(dbm);
1585 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1586 if (map->map_lockfd >= 0)
1587 (void) close(map->map_lockfd);
1588 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1589 errno = 0;
1590 syserr("dbm map \"%s\": cannot support GDBM",
1591 map->map_mname);
1592 return false;
1593 }
1594
1595 if (filechanged(dirfile, dfd, &std) ||
1596 filechanged(pagfile, pfd, &stp))
1597 {
1598 save_errno = errno;
1599 dbm_close(dbm);
1600 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1601 if (map->map_lockfd >= 0)
1602 (void) close(map->map_lockfd);
1603 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1604 errno = save_errno;
1605 syserr("ndbm_map_open(%s): file changed after open",
1606 map->map_file);
1607 return false;
1608 }
1609
1610 map->map_db1 = (ARBPTR_T) dbm;
1611
1612 /*
1613 ** Need to set map_mtime before the call to aliaswait()
1614 ** as aliaswait() will call map_lookup() which requires
1615 ** map_mtime to be set
1616 */
1617
1618 if (fstat(pfd, &st) >= 0)
1619 map->map_mtime = st.st_mtime;
1620
1621 if (mode == O_RDONLY)
1622 {
1623 # if LOCK_ON_OPEN
1624 if (dfd >= 0)
1625 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1626 if (pfd >= 0)
1627 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1628 # endif /* LOCK_ON_OPEN */
1629 if (bitset(MF_ALIAS, map->map_mflags) &&
1630 !aliaswait(map, ".pag", true))
1631 return false;
1632 }
1633 else
1634 {
1635 map->map_mflags |= MF_LOCKED;
1636 if (geteuid() == 0 && TrustedUid != 0)
1637 {
1638 # if HASFCHOWN
1639 if (fchown(dfd, TrustedUid, -1) < 0 ||
1640 fchown(pfd, TrustedUid, -1) < 0)
1641 {
1642 int err = errno;
1643
1644 sm_syslog(LOG_ALERT, NOQID,
1645 "ownership change on %s failed: %s",
1646 map->map_file, sm_errstring(err));
1647 message("050 ownership change on %s failed: %s",
1648 map->map_file, sm_errstring(err));
1649 }
1650 # else /* HASFCHOWN */
1651 sm_syslog(LOG_ALERT, NOQID,
1652 "no fchown(): cannot change ownership on %s",
1653 map->map_file);
1654 message("050 no fchown(): cannot change ownership on %s",
1655 map->map_file);
1656 # endif /* HASFCHOWN */
1657 }
1658 }
1659 return true;
1660 }
1661
1662
1663 /*
1664 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1665 */
1666
1667 char *
ndbm_map_lookup(map,name,av,statp)1668 ndbm_map_lookup(map, name, av, statp)
1669 MAP *map;
1670 char *name;
1671 char **av;
1672 int *statp;
1673 {
1674 datum key, val;
1675 int dfd, pfd;
1676 char keybuf[MAXNAME + 1];
1677 struct stat stbuf;
1678
1679 if (tTd(38, 20))
1680 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1681 map->map_mname, name);
1682
1683 key.dptr = name;
1684 key.dsize = strlen(name);
1685 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1686 {
1687 if (key.dsize > sizeof(keybuf) - 1)
1688 key.dsize = sizeof(keybuf) - 1;
1689 memmove(keybuf, key.dptr, key.dsize);
1690 keybuf[key.dsize] = '\0';
1691 makelower(keybuf);
1692 key.dptr = keybuf;
1693 }
1694 lockdbm:
1695 dfd = dbm_dirfno((DBM *) map->map_db1);
1696 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1697 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1698 pfd = dbm_pagfno((DBM *) map->map_db1);
1699 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1700 stbuf.st_mtime > map->map_mtime)
1701 {
1702 /* Reopen the database to sync the cache */
1703 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1704 : O_RDONLY;
1705
1706 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1707 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1708 map->map_mflags |= MF_CLOSING;
1709 map->map_class->map_close(map);
1710 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1711 if (map->map_class->map_open(map, omode))
1712 {
1713 map->map_mflags |= MF_OPEN;
1714 map->map_pid = CurrentPid;
1715 if ((omode & O_ACCMODE) == O_RDWR)
1716 map->map_mflags |= MF_WRITABLE;
1717 goto lockdbm;
1718 }
1719 else
1720 {
1721 if (!bitset(MF_OPTIONAL, map->map_mflags))
1722 {
1723 extern MAPCLASS BogusMapClass;
1724
1725 *statp = EX_TEMPFAIL;
1726 map->map_orgclass = map->map_class;
1727 map->map_class = &BogusMapClass;
1728 map->map_mflags |= MF_OPEN;
1729 map->map_pid = CurrentPid;
1730 syserr("Cannot reopen NDBM database %s",
1731 map->map_file);
1732 }
1733 return NULL;
1734 }
1735 }
1736 val.dptr = NULL;
1737 if (bitset(MF_TRY0NULL, map->map_mflags))
1738 {
1739 val = dbm_fetch((DBM *) map->map_db1, key);
1740 if (val.dptr != NULL)
1741 map->map_mflags &= ~MF_TRY1NULL;
1742 }
1743 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1744 {
1745 key.dsize++;
1746 val = dbm_fetch((DBM *) map->map_db1, key);
1747 if (val.dptr != NULL)
1748 map->map_mflags &= ~MF_TRY0NULL;
1749 }
1750 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1751 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1752 if (val.dptr == NULL)
1753 return NULL;
1754 if (bitset(MF_MATCHONLY, map->map_mflags))
1755 return map_rewrite(map, name, strlen(name), NULL);
1756 else
1757 return map_rewrite(map, val.dptr, val.dsize, av);
1758 }
1759
1760
1761 /*
1762 ** NDBM_MAP_STORE -- store a datum in the database
1763 */
1764
1765 void
ndbm_map_store(map,lhs,rhs)1766 ndbm_map_store(map, lhs, rhs)
1767 register MAP *map;
1768 char *lhs;
1769 char *rhs;
1770 {
1771 datum key;
1772 datum data;
1773 int status;
1774 char keybuf[MAXNAME + 1];
1775
1776 if (tTd(38, 12))
1777 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1778 map->map_mname, lhs, rhs);
1779
1780 key.dsize = strlen(lhs);
1781 key.dptr = lhs;
1782 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1783 {
1784 if (key.dsize > sizeof(keybuf) - 1)
1785 key.dsize = sizeof(keybuf) - 1;
1786 memmove(keybuf, key.dptr, key.dsize);
1787 keybuf[key.dsize] = '\0';
1788 makelower(keybuf);
1789 key.dptr = keybuf;
1790 }
1791
1792 data.dsize = strlen(rhs);
1793 data.dptr = rhs;
1794
1795 if (bitset(MF_INCLNULL, map->map_mflags))
1796 {
1797 key.dsize++;
1798 data.dsize++;
1799 }
1800
1801 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1802 if (status > 0)
1803 {
1804 if (!bitset(MF_APPEND, map->map_mflags))
1805 message("050 Warning: duplicate alias name %s", lhs);
1806 else
1807 {
1808 static char *buf = NULL;
1809 static int bufsiz = 0;
1810 auto int xstat;
1811 datum old;
1812
1813 old.dptr = ndbm_map_lookup(map, key.dptr,
1814 (char **) NULL, &xstat);
1815 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1816 {
1817 old.dsize = strlen(old.dptr);
1818 if (data.dsize + old.dsize + 2 > bufsiz)
1819 {
1820 if (buf != NULL)
1821 (void) sm_free(buf);
1822 bufsiz = data.dsize + old.dsize + 2;
1823 buf = sm_pmalloc_x(bufsiz);
1824 }
1825 (void) sm_strlcpyn(buf, bufsiz, 3,
1826 data.dptr, ",", old.dptr);
1827 data.dsize = data.dsize + old.dsize + 1;
1828 data.dptr = buf;
1829 if (tTd(38, 9))
1830 sm_dprintf("ndbm_map_store append=%s\n",
1831 data.dptr);
1832 }
1833 }
1834 status = dbm_store((DBM *) map->map_db1,
1835 key, data, DBM_REPLACE);
1836 }
1837 if (status != 0)
1838 syserr("readaliases: dbm put (%s): %d", lhs, status);
1839 }
1840
1841
1842 /*
1843 ** NDBM_MAP_CLOSE -- close the database
1844 */
1845
1846 void
ndbm_map_close(map)1847 ndbm_map_close(map)
1848 register MAP *map;
1849 {
1850 if (tTd(38, 9))
1851 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1852 map->map_mname, map->map_file, map->map_mflags);
1853
1854 if (bitset(MF_WRITABLE, map->map_mflags))
1855 {
1856 # ifdef NDBM_YP_COMPAT
1857 bool inclnull;
1858 char buf[MAXHOSTNAMELEN];
1859
1860 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1861 map->map_mflags &= ~MF_INCLNULL;
1862
1863 if (strstr(map->map_file, "/yp/") != NULL)
1864 {
1865 long save_mflags = map->map_mflags;
1866
1867 map->map_mflags |= MF_NOFOLDCASE;
1868
1869 (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
1870 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1871
1872 (void) gethostname(buf, sizeof(buf));
1873 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1874
1875 map->map_mflags = save_mflags;
1876 }
1877
1878 if (inclnull)
1879 map->map_mflags |= MF_INCLNULL;
1880 # endif /* NDBM_YP_COMPAT */
1881
1882 /* write out the distinguished alias */
1883 ndbm_map_store(map, "@", "@");
1884 }
1885 dbm_close((DBM *) map->map_db1);
1886
1887 /* release lock (if needed) */
1888 # if !LOCK_ON_OPEN
1889 if (map->map_lockfd >= 0)
1890 (void) close(map->map_lockfd);
1891 # endif /* !LOCK_ON_OPEN */
1892 }
1893
1894 #endif /* NDBM */
1895 /*
1896 ** NEWDB (Hash and BTree) Modules
1897 */
1898
1899 #if NEWDB
1900
1901 /*
1902 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1903 **
1904 ** These do rather bizarre locking. If you can lock on open,
1905 ** do that to avoid the condition of opening a database that
1906 ** is being rebuilt. If you don't, we'll try to fake it, but
1907 ** there will be a race condition. If opening for read-only,
1908 ** we immediately release the lock to avoid freezing things up.
1909 ** We really ought to hold the lock, but guarantee that we won't
1910 ** be pokey about it. That's hard to do.
1911 */
1912
1913 /* these should be K line arguments */
1914 # if DB_VERSION_MAJOR < 2
1915 # define db_cachesize cachesize
1916 # define h_nelem nelem
1917 # ifndef DB_CACHE_SIZE
1918 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1919 # endif /* ! DB_CACHE_SIZE */
1920 # ifndef DB_HASH_NELEM
1921 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1922 # endif /* ! DB_HASH_NELEM */
1923 # ifndef DB_HASH_BSIZE
1924 # define DB_HASH_BSIZE 16384 /* bucket size (default 4096) */
1925 # endif /* ! DB_HASH_BSIZE */
1926 # endif /* DB_VERSION_MAJOR < 2 */
1927
1928 bool
bt_map_open(map,mode)1929 bt_map_open(map, mode)
1930 MAP *map;
1931 int mode;
1932 {
1933 # if DB_VERSION_MAJOR < 2
1934 BTREEINFO btinfo;
1935 # endif /* DB_VERSION_MAJOR < 2 */
1936 # if DB_VERSION_MAJOR == 2
1937 DB_INFO btinfo;
1938 # endif /* DB_VERSION_MAJOR == 2 */
1939 # if DB_VERSION_MAJOR > 2
1940 void *btinfo = NULL;
1941 # endif /* DB_VERSION_MAJOR > 2 */
1942
1943 if (tTd(38, 2))
1944 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1945 map->map_mname, map->map_file, mode);
1946
1947 # if DB_VERSION_MAJOR < 3
1948 memset(&btinfo, '\0', sizeof(btinfo));
1949 # ifdef DB_CACHE_SIZE
1950 btinfo.db_cachesize = DB_CACHE_SIZE;
1951 # endif /* DB_CACHE_SIZE */
1952 # endif /* DB_VERSION_MAJOR < 3 */
1953
1954 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1955 }
1956
1957 bool
hash_map_open(map,mode)1958 hash_map_open(map, mode)
1959 MAP *map;
1960 int mode;
1961 {
1962 # if DB_VERSION_MAJOR < 2
1963 HASHINFO hinfo;
1964 # endif /* DB_VERSION_MAJOR < 2 */
1965 # if DB_VERSION_MAJOR == 2
1966 DB_INFO hinfo;
1967 # endif /* DB_VERSION_MAJOR == 2 */
1968 # if DB_VERSION_MAJOR > 2
1969 void *hinfo = NULL;
1970 # endif /* DB_VERSION_MAJOR > 2 */
1971
1972 if (tTd(38, 2))
1973 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1974 map->map_mname, map->map_file, mode);
1975
1976 # if DB_VERSION_MAJOR < 3
1977 memset(&hinfo, '\0', sizeof(hinfo));
1978 # ifdef DB_HASH_NELEM
1979 hinfo.h_nelem = DB_HASH_NELEM;
1980 # endif /* DB_HASH_NELEM */
1981 # ifdef DB_CACHE_SIZE
1982 hinfo.db_cachesize = DB_CACHE_SIZE;
1983 # endif /* DB_CACHE_SIZE */
1984 # ifdef DB_HASH_BSIZE
1985 hinfo.bsize = DB_HASH_BSIZE;
1986 # endif
1987 # endif /* DB_VERSION_MAJOR < 3 */
1988
1989 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1990 }
1991
1992 static bool
1993 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1994 MAP *map;
1995 int mode;
1996 char *mapclassname;
1997 DBTYPE dbtype;
1998 # if DB_VERSION_MAJOR < 2
1999 const void *openinfo;
2000 # endif /* DB_VERSION_MAJOR < 2 */
2001 # if DB_VERSION_MAJOR == 2
2002 DB_INFO *openinfo;
2003 # endif /* DB_VERSION_MAJOR == 2 */
2004 # if DB_VERSION_MAJOR > 2
2005 void **openinfo;
2006 # endif /* DB_VERSION_MAJOR > 2 */
2007 {
2008 DB *db = NULL;
2009 int i;
2010 int omode;
2011 int smode = S_IREAD;
2012 int fd;
2013 long sff;
2014 int save_errno;
2015 struct stat st;
2016 char buf[MAXPATHLEN];
2017
2018 /* do initial file and directory checks */
2019 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2020 {
2021 errno = 0;
2022 if (!bitset(MF_OPTIONAL, map->map_mflags))
2023 syserr("map \"%s\": map file %s name too long",
2024 map->map_mname, map->map_file);
2025 return false;
2026 }
2027 i = strlen(buf);
2028 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
2029 {
2030 if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
2031 {
2032 errno = 0;
2033 if (!bitset(MF_OPTIONAL, map->map_mflags))
2034 syserr("map \"%s\": map file %s name too long",
2035 map->map_mname, map->map_file);
2036 return false;
2037 }
2038 }
2039
2040 mode &= O_ACCMODE;
2041 omode = mode;
2042
2043 sff = SFF_ROOTOK|SFF_REGONLY;
2044 if (mode == O_RDWR)
2045 {
2046 sff |= SFF_CREAT;
2047 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
2048 sff |= SFF_NOSLINK;
2049 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
2050 sff |= SFF_NOHLINK;
2051 smode = S_IWRITE;
2052 }
2053 else
2054 {
2055 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
2056 sff |= SFF_NOWLINK;
2057 }
2058 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2059 sff |= SFF_SAFEDIRPATH;
2060 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2061
2062 if (i != 0)
2063 {
2064 char *prob = "unsafe";
2065
2066 /* cannot open this map */
2067 if (i == ENOENT)
2068 prob = "missing";
2069 if (tTd(38, 2))
2070 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2071 errno = i;
2072 if (!bitset(MF_OPTIONAL, map->map_mflags))
2073 syserr("%s map \"%s\": %s map file %s",
2074 mapclassname, map->map_mname, prob, buf);
2075 return false;
2076 }
2077 if (st.st_mode == ST_MODE_NOFILE)
2078 omode |= O_CREAT|O_EXCL;
2079
2080 map->map_lockfd = -1;
2081
2082 # if LOCK_ON_OPEN
2083 if (mode == O_RDWR)
2084 omode |= O_TRUNC|O_EXLOCK;
2085 else
2086 omode |= O_SHLOCK;
2087 # else /* LOCK_ON_OPEN */
2088 /*
2089 ** Pre-lock the file to avoid race conditions. In particular,
2090 ** since dbopen returns NULL if the file is zero length, we
2091 ** must have a locked instance around the dbopen.
2092 */
2093
2094 fd = open(buf, omode, DBMMODE);
2095 if (fd < 0)
2096 {
2097 if (!bitset(MF_OPTIONAL, map->map_mflags))
2098 syserr("db_map_open: cannot pre-open database %s", buf);
2099 return false;
2100 }
2101
2102 /* make sure no baddies slipped in just before the open... */
2103 if (filechanged(buf, fd, &st))
2104 {
2105 save_errno = errno;
2106 (void) close(fd);
2107 errno = save_errno;
2108 syserr("db_map_open(%s): file changed after pre-open", buf);
2109 return false;
2110 }
2111
2112 /* if new file, get the "before" bits for later filechanged check */
2113 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2114 {
2115 save_errno = errno;
2116 (void) close(fd);
2117 errno = save_errno;
2118 syserr("db_map_open(%s): cannot fstat pre-opened file",
2119 buf);
2120 return false;
2121 }
2122
2123 /* actually lock the pre-opened file */
2124 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2125 syserr("db_map_open: cannot lock %s", buf);
2126
2127 /* set up mode bits for dbopen */
2128 if (mode == O_RDWR)
2129 omode |= O_TRUNC;
2130 omode &= ~(O_EXCL|O_CREAT);
2131 # endif /* LOCK_ON_OPEN */
2132
2133 # if DB_VERSION_MAJOR < 2
2134 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2135 # else /* DB_VERSION_MAJOR < 2 */
2136 {
2137 int flags = 0;
2138 # if DB_VERSION_MAJOR > 2
2139 int ret;
2140 # endif /* DB_VERSION_MAJOR > 2 */
2141
2142 if (mode == O_RDONLY)
2143 flags |= DB_RDONLY;
2144 if (bitset(O_CREAT, omode))
2145 flags |= DB_CREATE;
2146 if (bitset(O_TRUNC, omode))
2147 flags |= DB_TRUNCATE;
2148 SM_DB_FLAG_ADD(flags);
2149
2150 # if DB_VERSION_MAJOR > 2
2151 ret = db_create(&db, NULL, 0);
2152 # ifdef DB_CACHE_SIZE
2153 if (ret == 0 && db != NULL)
2154 {
2155 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2156 if (ret != 0)
2157 {
2158 (void) db->close(db, 0);
2159 db = NULL;
2160 }
2161 }
2162 # endif /* DB_CACHE_SIZE */
2163 # ifdef DB_HASH_NELEM
2164 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2165 {
2166 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2167 if (ret != 0)
2168 {
2169 (void) db->close(db, 0);
2170 db = NULL;
2171 }
2172 }
2173 # endif /* DB_HASH_NELEM */
2174 if (ret == 0 && db != NULL)
2175 {
2176 ret = db->open(db,
2177 DBTXN /* transaction for DB 4.1 */
2178 buf, NULL, dbtype, flags, DBMMODE);
2179 if (ret != 0)
2180 {
2181 #ifdef DB_OLD_VERSION
2182 if (ret == DB_OLD_VERSION)
2183 ret = EINVAL;
2184 #endif /* DB_OLD_VERSION */
2185 (void) db->close(db, 0);
2186 db = NULL;
2187 }
2188 }
2189 errno = ret;
2190 # else /* DB_VERSION_MAJOR > 2 */
2191 errno = db_open(buf, dbtype, flags, DBMMODE,
2192 NULL, openinfo, &db);
2193 # endif /* DB_VERSION_MAJOR > 2 */
2194 }
2195 # endif /* DB_VERSION_MAJOR < 2 */
2196 save_errno = errno;
2197
2198 # if !LOCK_ON_OPEN
2199 if (mode == O_RDWR)
2200 map->map_lockfd = fd;
2201 else
2202 (void) close(fd);
2203 # endif /* !LOCK_ON_OPEN */
2204
2205 if (db == NULL)
2206 {
2207 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2208 aliaswait(map, ".db", false))
2209 return true;
2210 # if !LOCK_ON_OPEN
2211 if (map->map_lockfd >= 0)
2212 (void) close(map->map_lockfd);
2213 # endif /* !LOCK_ON_OPEN */
2214 errno = save_errno;
2215 if (!bitset(MF_OPTIONAL, map->map_mflags))
2216 syserr("Cannot open %s database %s",
2217 mapclassname, buf);
2218 return false;
2219 }
2220
2221 # if DB_VERSION_MAJOR < 2
2222 fd = db->fd(db);
2223 # else /* DB_VERSION_MAJOR < 2 */
2224 fd = -1;
2225 errno = db->fd(db, &fd);
2226 # endif /* DB_VERSION_MAJOR < 2 */
2227 if (filechanged(buf, fd, &st))
2228 {
2229 save_errno = errno;
2230 # if DB_VERSION_MAJOR < 2
2231 (void) db->close(db);
2232 # else /* DB_VERSION_MAJOR < 2 */
2233 errno = db->close(db, 0);
2234 # endif /* DB_VERSION_MAJOR < 2 */
2235 # if !LOCK_ON_OPEN
2236 if (map->map_lockfd >= 0)
2237 (void) close(map->map_lockfd);
2238 # endif /* !LOCK_ON_OPEN */
2239 errno = save_errno;
2240 syserr("db_map_open(%s): file changed after open", buf);
2241 return false;
2242 }
2243
2244 if (mode == O_RDWR)
2245 map->map_mflags |= MF_LOCKED;
2246 # if LOCK_ON_OPEN
2247 if (fd >= 0 && mode == O_RDONLY)
2248 {
2249 (void) lockfile(fd, buf, NULL, LOCK_UN);
2250 }
2251 # endif /* LOCK_ON_OPEN */
2252
2253 /* try to make sure that at least the database header is on disk */
2254 if (mode == O_RDWR)
2255 {
2256 (void) db->sync(db, 0);
2257 if (geteuid() == 0 && TrustedUid != 0)
2258 {
2259 # if HASFCHOWN
2260 if (fchown(fd, TrustedUid, -1) < 0)
2261 {
2262 int err = errno;
2263
2264 sm_syslog(LOG_ALERT, NOQID,
2265 "ownership change on %s failed: %s",
2266 buf, sm_errstring(err));
2267 message("050 ownership change on %s failed: %s",
2268 buf, sm_errstring(err));
2269 }
2270 # else /* HASFCHOWN */
2271 sm_syslog(LOG_ALERT, NOQID,
2272 "no fchown(): cannot change ownership on %s",
2273 map->map_file);
2274 message("050 no fchown(): cannot change ownership on %s",
2275 map->map_file);
2276 # endif /* HASFCHOWN */
2277 }
2278 }
2279
2280 map->map_db2 = (ARBPTR_T) db;
2281
2282 /*
2283 ** Need to set map_mtime before the call to aliaswait()
2284 ** as aliaswait() will call map_lookup() which requires
2285 ** map_mtime to be set
2286 */
2287
2288 if (fd >= 0 && fstat(fd, &st) >= 0)
2289 map->map_mtime = st.st_mtime;
2290
2291 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2292 !aliaswait(map, ".db", true))
2293 return false;
2294 return true;
2295 }
2296
2297
2298 /*
2299 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2300 */
2301
2302 char *
db_map_lookup(map,name,av,statp)2303 db_map_lookup(map, name, av, statp)
2304 MAP *map;
2305 char *name;
2306 char **av;
2307 int *statp;
2308 {
2309 DBT key, val;
2310 register DB *db = (DB *) map->map_db2;
2311 int i;
2312 int st;
2313 int save_errno;
2314 int fd;
2315 struct stat stbuf;
2316 char keybuf[MAXNAME + 1];
2317 char buf[MAXPATHLEN];
2318
2319 memset(&key, '\0', sizeof(key));
2320 memset(&val, '\0', sizeof(val));
2321
2322 if (tTd(38, 20))
2323 sm_dprintf("db_map_lookup(%s, %s)\n",
2324 map->map_mname, name);
2325
2326 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2327 {
2328 errno = 0;
2329 if (!bitset(MF_OPTIONAL, map->map_mflags))
2330 syserr("map \"%s\": map file %s name too long",
2331 map->map_mname, map->map_file);
2332 return NULL;
2333 }
2334 i = strlen(buf);
2335 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2336 buf[i - 3] = '\0';
2337
2338 key.size = strlen(name);
2339 if (key.size > sizeof(keybuf) - 1)
2340 key.size = sizeof(keybuf) - 1;
2341 key.data = keybuf;
2342 memmove(keybuf, name, key.size);
2343 keybuf[key.size] = '\0';
2344 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2345 makelower(keybuf);
2346 lockdb:
2347 # if DB_VERSION_MAJOR < 2
2348 fd = db->fd(db);
2349 # else /* DB_VERSION_MAJOR < 2 */
2350 fd = -1;
2351 errno = db->fd(db, &fd);
2352 # endif /* DB_VERSION_MAJOR < 2 */
2353 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2354 (void) lockfile(fd, buf, ".db", LOCK_SH);
2355 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2356 {
2357 /* Reopen the database to sync the cache */
2358 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2359 : O_RDONLY;
2360
2361 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2362 (void) lockfile(fd, buf, ".db", LOCK_UN);
2363 map->map_mflags |= MF_CLOSING;
2364 map->map_class->map_close(map);
2365 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2366 if (map->map_class->map_open(map, omode))
2367 {
2368 map->map_mflags |= MF_OPEN;
2369 map->map_pid = CurrentPid;
2370 if ((omode & O_ACCMODE) == O_RDWR)
2371 map->map_mflags |= MF_WRITABLE;
2372 db = (DB *) map->map_db2;
2373 goto lockdb;
2374 }
2375 else
2376 {
2377 if (!bitset(MF_OPTIONAL, map->map_mflags))
2378 {
2379 extern MAPCLASS BogusMapClass;
2380
2381 *statp = EX_TEMPFAIL;
2382 map->map_orgclass = map->map_class;
2383 map->map_class = &BogusMapClass;
2384 map->map_mflags |= MF_OPEN;
2385 map->map_pid = CurrentPid;
2386 syserr("Cannot reopen DB database %s",
2387 map->map_file);
2388 }
2389 return NULL;
2390 }
2391 }
2392
2393 st = 1;
2394 if (bitset(MF_TRY0NULL, map->map_mflags))
2395 {
2396 # if DB_VERSION_MAJOR < 2
2397 st = db->get(db, &key, &val, 0);
2398 # else /* DB_VERSION_MAJOR < 2 */
2399 errno = db->get(db, NULL, &key, &val, 0);
2400 switch (errno)
2401 {
2402 case DB_NOTFOUND:
2403 case DB_KEYEMPTY:
2404 st = 1;
2405 break;
2406
2407 case 0:
2408 st = 0;
2409 break;
2410
2411 default:
2412 st = -1;
2413 break;
2414 }
2415 # endif /* DB_VERSION_MAJOR < 2 */
2416 if (st == 0)
2417 map->map_mflags &= ~MF_TRY1NULL;
2418 }
2419 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2420 {
2421 key.size++;
2422 # if DB_VERSION_MAJOR < 2
2423 st = db->get(db, &key, &val, 0);
2424 # else /* DB_VERSION_MAJOR < 2 */
2425 errno = db->get(db, NULL, &key, &val, 0);
2426 switch (errno)
2427 {
2428 case DB_NOTFOUND:
2429 case DB_KEYEMPTY:
2430 st = 1;
2431 break;
2432
2433 case 0:
2434 st = 0;
2435 break;
2436
2437 default:
2438 st = -1;
2439 break;
2440 }
2441 # endif /* DB_VERSION_MAJOR < 2 */
2442 if (st == 0)
2443 map->map_mflags &= ~MF_TRY0NULL;
2444 }
2445 save_errno = errno;
2446 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2447 (void) lockfile(fd, buf, ".db", LOCK_UN);
2448 if (st != 0)
2449 {
2450 errno = save_errno;
2451 if (st < 0)
2452 syserr("db_map_lookup: get (%s)", name);
2453 return NULL;
2454 }
2455 if (bitset(MF_MATCHONLY, map->map_mflags))
2456 return map_rewrite(map, name, strlen(name), NULL);
2457 else
2458 return map_rewrite(map, val.data, val.size, av);
2459 }
2460
2461
2462 /*
2463 ** DB_MAP_STORE -- store a datum in the NEWDB database
2464 */
2465
2466 void
db_map_store(map,lhs,rhs)2467 db_map_store(map, lhs, rhs)
2468 register MAP *map;
2469 char *lhs;
2470 char *rhs;
2471 {
2472 int status;
2473 DBT key;
2474 DBT data;
2475 register DB *db = map->map_db2;
2476 char keybuf[MAXNAME + 1];
2477
2478 memset(&key, '\0', sizeof(key));
2479 memset(&data, '\0', sizeof(data));
2480
2481 if (tTd(38, 12))
2482 sm_dprintf("db_map_store(%s, %s, %s)\n",
2483 map->map_mname, lhs, rhs);
2484
2485 key.size = strlen(lhs);
2486 key.data = lhs;
2487 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2488 {
2489 if (key.size > sizeof(keybuf) - 1)
2490 key.size = sizeof(keybuf) - 1;
2491 memmove(keybuf, key.data, key.size);
2492 keybuf[key.size] = '\0';
2493 makelower(keybuf);
2494 key.data = keybuf;
2495 }
2496
2497 data.size = strlen(rhs);
2498 data.data = rhs;
2499
2500 if (bitset(MF_INCLNULL, map->map_mflags))
2501 {
2502 key.size++;
2503 data.size++;
2504 }
2505
2506 # if DB_VERSION_MAJOR < 2
2507 status = db->put(db, &key, &data, R_NOOVERWRITE);
2508 # else /* DB_VERSION_MAJOR < 2 */
2509 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2510 switch (errno)
2511 {
2512 case DB_KEYEXIST:
2513 status = 1;
2514 break;
2515
2516 case 0:
2517 status = 0;
2518 break;
2519
2520 default:
2521 status = -1;
2522 break;
2523 }
2524 # endif /* DB_VERSION_MAJOR < 2 */
2525 if (status > 0)
2526 {
2527 if (!bitset(MF_APPEND, map->map_mflags))
2528 message("050 Warning: duplicate alias name %s", lhs);
2529 else
2530 {
2531 static char *buf = NULL;
2532 static int bufsiz = 0;
2533 DBT old;
2534
2535 memset(&old, '\0', sizeof(old));
2536
2537 old.data = db_map_lookup(map, key.data,
2538 (char **) NULL, &status);
2539 if (old.data != NULL)
2540 {
2541 old.size = strlen(old.data);
2542 if (data.size + old.size + 2 > (size_t) bufsiz)
2543 {
2544 if (buf != NULL)
2545 sm_free(buf);
2546 bufsiz = data.size + old.size + 2;
2547 buf = sm_pmalloc_x(bufsiz);
2548 }
2549 (void) sm_strlcpyn(buf, bufsiz, 3,
2550 (char *) data.data, ",",
2551 (char *) old.data);
2552 data.size = data.size + old.size + 1;
2553 data.data = buf;
2554 if (tTd(38, 9))
2555 sm_dprintf("db_map_store append=%s\n",
2556 (char *) data.data);
2557 }
2558 }
2559 # if DB_VERSION_MAJOR < 2
2560 status = db->put(db, &key, &data, 0);
2561 # else /* DB_VERSION_MAJOR < 2 */
2562 status = errno = db->put(db, NULL, &key, &data, 0);
2563 # endif /* DB_VERSION_MAJOR < 2 */
2564 }
2565 if (status != 0)
2566 syserr("readaliases: db put (%s)", lhs);
2567 }
2568
2569
2570 /*
2571 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2572 */
2573
2574 void
db_map_close(map)2575 db_map_close(map)
2576 MAP *map;
2577 {
2578 register DB *db = map->map_db2;
2579
2580 if (tTd(38, 9))
2581 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2582 map->map_mname, map->map_file, map->map_mflags);
2583
2584 if (bitset(MF_WRITABLE, map->map_mflags))
2585 {
2586 /* write out the distinguished alias */
2587 db_map_store(map, "@", "@");
2588 }
2589
2590 (void) db->sync(db, 0);
2591
2592 # if !LOCK_ON_OPEN
2593 if (map->map_lockfd >= 0)
2594 (void) close(map->map_lockfd);
2595 # endif /* !LOCK_ON_OPEN */
2596
2597 # if DB_VERSION_MAJOR < 2
2598 if (db->close(db) != 0)
2599 # else /* DB_VERSION_MAJOR < 2 */
2600 /*
2601 ** Berkeley DB can use internal shared memory
2602 ** locking for its memory pool. Closing a map
2603 ** opened by another process will interfere
2604 ** with the shared memory and locks of the parent
2605 ** process leaving things in a bad state.
2606 */
2607
2608 /*
2609 ** If this map was not opened by the current
2610 ** process, do not close the map but recover
2611 ** the file descriptor.
2612 */
2613
2614 if (map->map_pid != CurrentPid)
2615 {
2616 int fd = -1;
2617
2618 errno = db->fd(db, &fd);
2619 if (fd >= 0)
2620 (void) close(fd);
2621 return;
2622 }
2623
2624 if ((errno = db->close(db, 0)) != 0)
2625 # endif /* DB_VERSION_MAJOR < 2 */
2626 syserr("db_map_close(%s, %s, %lx): db close failure",
2627 map->map_mname, map->map_file, map->map_mflags);
2628 }
2629 #endif /* NEWDB */
2630 /*
2631 ** NIS Modules
2632 */
2633
2634 #if NIS
2635
2636 # ifndef YPERR_BUSY
2637 # define YPERR_BUSY 16
2638 # endif /* ! YPERR_BUSY */
2639
2640 /*
2641 ** NIS_MAP_OPEN -- open DBM map
2642 */
2643
2644 bool
nis_map_open(map,mode)2645 nis_map_open(map, mode)
2646 MAP *map;
2647 int mode;
2648 {
2649 int yperr;
2650 register char *p;
2651 auto char *vp;
2652 auto int vsize;
2653
2654 if (tTd(38, 2))
2655 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2656 map->map_mname, map->map_file, mode);
2657
2658 mode &= O_ACCMODE;
2659 if (mode != O_RDONLY)
2660 {
2661 /* issue a pseudo-error message */
2662 errno = SM_EMAPCANTWRITE;
2663 return false;
2664 }
2665
2666 p = strchr(map->map_file, '@');
2667 if (p != NULL)
2668 {
2669 *p++ = '\0';
2670 if (*p != '\0')
2671 map->map_domain = p;
2672 }
2673
2674 if (*map->map_file == '\0')
2675 map->map_file = "mail.aliases";
2676
2677 if (map->map_domain == NULL)
2678 {
2679 yperr = yp_get_default_domain(&map->map_domain);
2680 if (yperr != 0)
2681 {
2682 if (!bitset(MF_OPTIONAL, map->map_mflags))
2683 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2684 map->map_file);
2685 return false;
2686 }
2687 }
2688
2689 /* check to see if this map actually exists */
2690 vp = NULL;
2691 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2692 &vp, &vsize);
2693 if (tTd(38, 10))
2694 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2695 map->map_domain, map->map_file, yperr_string(yperr));
2696 if (vp != NULL)
2697 sm_free(vp);
2698
2699 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2700 {
2701 /*
2702 ** We ought to be calling aliaswait() here if this is an
2703 ** alias file, but powerful HP-UX NIS servers apparently
2704 ** don't insert the @:@ token into the alias map when it
2705 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2706 */
2707
2708 # if 0
2709 if (!bitset(MF_ALIAS, map->map_mflags) ||
2710 aliaswait(map, NULL, true))
2711 # endif /* 0 */
2712 return true;
2713 }
2714
2715 if (!bitset(MF_OPTIONAL, map->map_mflags))
2716 {
2717 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2718 map->map_file, map->map_domain, yperr_string(yperr));
2719 }
2720
2721 return false;
2722 }
2723
2724
2725 /*
2726 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2727 */
2728
2729 /* ARGSUSED3 */
2730 char *
nis_map_lookup(map,name,av,statp)2731 nis_map_lookup(map, name, av, statp)
2732 MAP *map;
2733 char *name;
2734 char **av;
2735 int *statp;
2736 {
2737 char *vp;
2738 auto int vsize;
2739 int buflen;
2740 int yperr;
2741 char keybuf[MAXNAME + 1];
2742 char *SM_NONVOLATILE result = NULL;
2743
2744 if (tTd(38, 20))
2745 sm_dprintf("nis_map_lookup(%s, %s)\n",
2746 map->map_mname, name);
2747
2748 buflen = strlen(name);
2749 if (buflen > sizeof(keybuf) - 1)
2750 buflen = sizeof(keybuf) - 1;
2751 memmove(keybuf, name, buflen);
2752 keybuf[buflen] = '\0';
2753 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2754 makelower(keybuf);
2755 yperr = YPERR_KEY;
2756 vp = NULL;
2757 if (bitset(MF_TRY0NULL, map->map_mflags))
2758 {
2759 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2760 &vp, &vsize);
2761 if (yperr == 0)
2762 map->map_mflags &= ~MF_TRY1NULL;
2763 }
2764 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2765 {
2766 SM_FREE_CLR(vp);
2767 buflen++;
2768 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2769 &vp, &vsize);
2770 if (yperr == 0)
2771 map->map_mflags &= ~MF_TRY0NULL;
2772 }
2773 if (yperr != 0)
2774 {
2775 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2776 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2777 if (vp != NULL)
2778 sm_free(vp);
2779 return NULL;
2780 }
2781 SM_TRY
2782 if (bitset(MF_MATCHONLY, map->map_mflags))
2783 result = map_rewrite(map, name, strlen(name), NULL);
2784 else
2785 result = map_rewrite(map, vp, vsize, av);
2786 SM_FINALLY
2787 if (vp != NULL)
2788 sm_free(vp);
2789 SM_END_TRY
2790 return result;
2791 }
2792
2793
2794 /*
2795 ** NIS_GETCANONNAME -- look up canonical name in NIS
2796 */
2797
2798 static bool
nis_getcanonname(name,hbsize,statp)2799 nis_getcanonname(name, hbsize, statp)
2800 char *name;
2801 int hbsize;
2802 int *statp;
2803 {
2804 char *vp;
2805 auto int vsize;
2806 int keylen;
2807 int yperr;
2808 static bool try0null = true;
2809 static bool try1null = true;
2810 static char *yp_domain = NULL;
2811 char host_record[MAXLINE];
2812 char cbuf[MAXNAME];
2813 char nbuf[MAXNAME + 1];
2814
2815 if (tTd(38, 20))
2816 sm_dprintf("nis_getcanonname(%s)\n", name);
2817
2818 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
2819 {
2820 *statp = EX_UNAVAILABLE;
2821 return false;
2822 }
2823 (void) shorten_hostname(nbuf);
2824 keylen = strlen(nbuf);
2825
2826 if (yp_domain == NULL)
2827 (void) yp_get_default_domain(&yp_domain);
2828 makelower(nbuf);
2829 yperr = YPERR_KEY;
2830 vp = NULL;
2831 if (try0null)
2832 {
2833 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2834 &vp, &vsize);
2835 if (yperr == 0)
2836 try1null = false;
2837 }
2838 if (yperr == YPERR_KEY && try1null)
2839 {
2840 SM_FREE_CLR(vp);
2841 keylen++;
2842 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2843 &vp, &vsize);
2844 if (yperr == 0)
2845 try0null = false;
2846 }
2847 if (yperr != 0)
2848 {
2849 if (yperr == YPERR_KEY)
2850 *statp = EX_NOHOST;
2851 else if (yperr == YPERR_BUSY)
2852 *statp = EX_TEMPFAIL;
2853 else
2854 *statp = EX_UNAVAILABLE;
2855 if (vp != NULL)
2856 sm_free(vp);
2857 return false;
2858 }
2859 (void) sm_strlcpy(host_record, vp, sizeof(host_record));
2860 sm_free(vp);
2861 if (tTd(38, 44))
2862 sm_dprintf("got record `%s'\n", host_record);
2863 vp = strpbrk(host_record, "#\n");
2864 if (vp != NULL)
2865 *vp = '\0';
2866 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
2867 {
2868 /* this should not happen, but.... */
2869 *statp = EX_NOHOST;
2870 return false;
2871 }
2872 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2873 {
2874 *statp = EX_UNAVAILABLE;
2875 return false;
2876 }
2877 *statp = EX_OK;
2878 return true;
2879 }
2880
2881 #endif /* NIS */
2882 /*
2883 ** NISPLUS Modules
2884 **
2885 ** This code donated by Sun Microsystems.
2886 */
2887
2888 #if NISPLUS
2889
2890 # undef NIS /* symbol conflict in nis.h */
2891 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2892 # include <rpcsvc/nis.h>
2893 # include <rpcsvc/nislib.h>
2894 # ifndef NIS_TABLE_OBJ
2895 # define NIS_TABLE_OBJ TABLE_OBJ
2896 # endif /* NIS_TABLE_OBJ */
2897
2898 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2899 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2900 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2901 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2902
2903 /*
2904 ** NISPLUS_MAP_OPEN -- open nisplus table
2905 */
2906
2907 bool
nisplus_map_open(map,mode)2908 nisplus_map_open(map, mode)
2909 MAP *map;
2910 int mode;
2911 {
2912 nis_result *res = NULL;
2913 int retry_cnt, max_col, i;
2914 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2915
2916 if (tTd(38, 2))
2917 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2918 map->map_mname, map->map_file, mode);
2919
2920 mode &= O_ACCMODE;
2921 if (mode != O_RDONLY)
2922 {
2923 errno = EPERM;
2924 return false;
2925 }
2926
2927 if (*map->map_file == '\0')
2928 map->map_file = "mail_aliases.org_dir";
2929
2930 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2931 {
2932 /* set default NISPLUS Domain to $m */
2933 map->map_domain = newstr(nisplus_default_domain());
2934 if (tTd(38, 2))
2935 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2936 map->map_file, map->map_domain);
2937 }
2938 if (!PARTIAL_NAME(map->map_file))
2939 {
2940 map->map_domain = newstr("");
2941 (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
2942 }
2943 else
2944 {
2945 /* check to see if this map actually exists */
2946 (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
2947 map->map_file, ".", map->map_domain);
2948 }
2949
2950 retry_cnt = 0;
2951 while (res == NULL || res->status != NIS_SUCCESS)
2952 {
2953 res = nis_lookup(qbuf, FOLLOW_LINKS);
2954 switch (res->status)
2955 {
2956 case NIS_SUCCESS:
2957 break;
2958
2959 case NIS_TRYAGAIN:
2960 case NIS_RPCERROR:
2961 case NIS_NAMEUNREACHABLE:
2962 if (retry_cnt++ > 4)
2963 {
2964 errno = EAGAIN;
2965 return false;
2966 }
2967 /* try not to overwhelm hosed server */
2968 sleep(2);
2969 break;
2970
2971 default: /* all other nisplus errors */
2972 # if 0
2973 if (!bitset(MF_OPTIONAL, map->map_mflags))
2974 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2975 map->map_file, map->map_domain,
2976 nis_sperrno(res->status));
2977 # endif /* 0 */
2978 errno = EAGAIN;
2979 return false;
2980 }
2981 }
2982
2983 if (NIS_RES_NUMOBJ(res) != 1 ||
2984 (NIS_RES_OBJECT(res)->zo_data.zo_type != NIS_TABLE_OBJ))
2985 {
2986 if (tTd(38, 10))
2987 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2988 # if 0
2989 if (!bitset(MF_OPTIONAL, map->map_mflags))
2990 syserr("451 4.3.5 %s.%s: %s is not a table",
2991 map->map_file, map->map_domain,
2992 nis_sperrno(res->status));
2993 # endif /* 0 */
2994 errno = EBADF;
2995 return false;
2996 }
2997 /* default key column is column 0 */
2998 if (map->map_keycolnm == NULL)
2999 map->map_keycolnm = newstr(COL_NAME(res,0));
3000
3001 max_col = COL_MAX(res);
3002
3003 /* verify the key column exist */
3004 for (i = 0; i < max_col; i++)
3005 {
3006 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
3007 break;
3008 }
3009 if (i == max_col)
3010 {
3011 if (tTd(38, 2))
3012 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
3013 map->map_file, map->map_keycolnm);
3014 errno = ENOENT;
3015 return false;
3016 }
3017
3018 /* default value column is the last column */
3019 if (map->map_valcolnm == NULL)
3020 {
3021 map->map_valcolno = max_col - 1;
3022 return true;
3023 }
3024
3025 for (i = 0; i< max_col; i++)
3026 {
3027 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
3028 {
3029 map->map_valcolno = i;
3030 return true;
3031 }
3032 }
3033
3034 if (tTd(38, 2))
3035 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
3036 map->map_file, map->map_keycolnm);
3037 errno = ENOENT;
3038 return false;
3039 }
3040
3041
3042 /*
3043 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
3044 */
3045
3046 char *
nisplus_map_lookup(map,name,av,statp)3047 nisplus_map_lookup(map, name, av, statp)
3048 MAP *map;
3049 char *name;
3050 char **av;
3051 int *statp;
3052 {
3053 char *p;
3054 auto int vsize;
3055 char *skp;
3056 int skleft;
3057 char search_key[MAXNAME + 4];
3058 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3059 nis_result *result;
3060
3061 if (tTd(38, 20))
3062 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3063 map->map_mname, name);
3064
3065 if (!bitset(MF_OPEN, map->map_mflags))
3066 {
3067 if (nisplus_map_open(map, O_RDONLY))
3068 {
3069 map->map_mflags |= MF_OPEN;
3070 map->map_pid = CurrentPid;
3071 }
3072 else
3073 {
3074 *statp = EX_UNAVAILABLE;
3075 return NULL;
3076 }
3077 }
3078
3079 /*
3080 ** Copy the name to the key buffer, escaping double quote characters
3081 ** by doubling them and quoting "]" and "," to avoid having the
3082 ** NIS+ parser choke on them.
3083 */
3084
3085 skleft = sizeof(search_key) - 4;
3086 skp = search_key;
3087 for (p = name; *p != '\0' && skleft > 0; p++)
3088 {
3089 switch (*p)
3090 {
3091 case ']':
3092 case ',':
3093 /* quote the character */
3094 *skp++ = '"';
3095 *skp++ = *p;
3096 *skp++ = '"';
3097 skleft -= 3;
3098 break;
3099
3100 case '"':
3101 /* double the quote */
3102 *skp++ = '"';
3103 skleft--;
3104 /* FALLTHROUGH */
3105
3106 default:
3107 *skp++ = *p;
3108 skleft--;
3109 break;
3110 }
3111 }
3112 *skp = '\0';
3113 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3114 makelower(search_key);
3115
3116 /* construct the query */
3117 if (PARTIAL_NAME(map->map_file))
3118 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
3119 map->map_keycolnm, search_key, map->map_file,
3120 map->map_domain);
3121 else
3122 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
3123 map->map_keycolnm, search_key, map->map_file);
3124
3125 if (tTd(38, 20))
3126 sm_dprintf("qbuf=%s\n", qbuf);
3127 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3128 if (result->status == NIS_SUCCESS)
3129 {
3130 int count;
3131 char *str;
3132
3133 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3134 {
3135 if (LogLevel > 10)
3136 sm_syslog(LOG_WARNING, CurEnv->e_id,
3137 "%s: lookup error, expected 1 entry, got %d",
3138 map->map_file, count);
3139
3140 /* ignore second entry */
3141 if (tTd(38, 20))
3142 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3143 name, count);
3144 }
3145
3146 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3147 /* set the length of the result */
3148 if (p == NULL)
3149 p = "";
3150 vsize = strlen(p);
3151 if (tTd(38, 20))
3152 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3153 name, p);
3154 if (bitset(MF_MATCHONLY, map->map_mflags))
3155 str = map_rewrite(map, name, strlen(name), NULL);
3156 else
3157 str = map_rewrite(map, p, vsize, av);
3158 nis_freeresult(result);
3159 *statp = EX_OK;
3160 return str;
3161 }
3162 else
3163 {
3164 if (result->status == NIS_NOTFOUND)
3165 *statp = EX_NOTFOUND;
3166 else if (result->status == NIS_TRYAGAIN)
3167 *statp = EX_TEMPFAIL;
3168 else
3169 {
3170 *statp = EX_UNAVAILABLE;
3171 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3172 }
3173 }
3174 if (tTd(38, 20))
3175 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3176 nis_freeresult(result);
3177 return NULL;
3178 }
3179
3180
3181
3182 /*
3183 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3184 */
3185
3186 static bool
nisplus_getcanonname(name,hbsize,statp)3187 nisplus_getcanonname(name, hbsize, statp)
3188 char *name;
3189 int hbsize;
3190 int *statp;
3191 {
3192 char *vp;
3193 auto int vsize;
3194 nis_result *result;
3195 char *p;
3196 char nbuf[MAXNAME + 1];
3197 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3198
3199 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
3200 {
3201 *statp = EX_UNAVAILABLE;
3202 return false;
3203 }
3204 (void) shorten_hostname(nbuf);
3205
3206 p = strchr(nbuf, '.');
3207 if (p == NULL)
3208 {
3209 /* single token */
3210 (void) sm_snprintf(qbuf, sizeof(qbuf),
3211 "[name=%s],hosts.org_dir", nbuf);
3212 }
3213 else if (p[1] != '\0')
3214 {
3215 /* multi token -- take only first token in nbuf */
3216 *p = '\0';
3217 (void) sm_snprintf(qbuf, sizeof(qbuf),
3218 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3219 }
3220 else
3221 {
3222 *statp = EX_NOHOST;
3223 return false;
3224 }
3225
3226 if (tTd(38, 20))
3227 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3228 name, qbuf);
3229
3230 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3231 NULL, NULL);
3232
3233 if (result->status == NIS_SUCCESS)
3234 {
3235 int count;
3236 char *domain;
3237
3238 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3239 {
3240 if (LogLevel > 10)
3241 sm_syslog(LOG_WARNING, CurEnv->e_id,
3242 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3243 count);
3244
3245 /* ignore second entry */
3246 if (tTd(38, 20))
3247 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3248 name, count);
3249 }
3250
3251 if (tTd(38, 20))
3252 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3253 name, (NIS_RES_OBJECT(result))->zo_domain);
3254
3255
3256 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3257 vsize = strlen(vp);
3258 if (tTd(38, 20))
3259 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3260 name, vp);
3261 if (strchr(vp, '.') != NULL)
3262 {
3263 domain = "";
3264 }
3265 else
3266 {
3267 domain = macvalue('m', CurEnv);
3268 if (domain == NULL)
3269 domain = "";
3270 }
3271 if (hbsize > vsize + (int) strlen(domain) + 1)
3272 {
3273 if (domain[0] == '\0')
3274 (void) sm_strlcpy(name, vp, hbsize);
3275 else
3276 (void) sm_snprintf(name, hbsize,
3277 "%s.%s", vp, domain);
3278 *statp = EX_OK;
3279 }
3280 else
3281 *statp = EX_NOHOST;
3282 nis_freeresult(result);
3283 return true;
3284 }
3285 else
3286 {
3287 if (result->status == NIS_NOTFOUND)
3288 *statp = EX_NOHOST;
3289 else if (result->status == NIS_TRYAGAIN)
3290 *statp = EX_TEMPFAIL;
3291 else
3292 *statp = EX_UNAVAILABLE;
3293 }
3294 if (tTd(38, 20))
3295 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3296 name, result->status, *statp);
3297 nis_freeresult(result);
3298 return false;
3299 }
3300
3301 char *
nisplus_default_domain()3302 nisplus_default_domain()
3303 {
3304 static char default_domain[MAXNAME + 1] = "";
3305 char *p;
3306
3307 if (default_domain[0] != '\0')
3308 return default_domain;
3309
3310 p = nis_local_directory();
3311 (void) sm_strlcpy(default_domain, p, sizeof(default_domain));
3312 return default_domain;
3313 }
3314
3315 #endif /* NISPLUS */
3316 /*
3317 ** LDAP Modules
3318 */
3319
3320 /*
3321 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3322 */
3323
3324 #if defined(LDAPMAP) || defined(PH_MAP)
3325
3326 # if PH_MAP
3327 # define ph_map_dequote ldapmap_dequote
3328 # endif /* PH_MAP */
3329
3330 static char *ldapmap_dequote __P((char *));
3331
3332 static char *
ldapmap_dequote(str)3333 ldapmap_dequote(str)
3334 char *str;
3335 {
3336 char *p;
3337 char *start;
3338
3339 if (str == NULL)
3340 return NULL;
3341
3342 p = str;
3343 if (*p == '"')
3344 {
3345 /* Should probably swallow initial whitespace here */
3346 start = ++p;
3347 }
3348 else
3349 return str;
3350 while (*p != '"' && *p != '\0')
3351 p++;
3352 if (*p != '\0')
3353 *p = '\0';
3354 return start;
3355 }
3356 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3357
3358 #if LDAPMAP
3359
3360 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3361
3362 /*
3363 ** LDAPMAP_OPEN -- open LDAP map
3364 **
3365 ** Connect to the LDAP server. Re-use existing connections since a
3366 ** single server connection to a host (with the same host, port,
3367 ** bind DN, and secret) can answer queries for multiple maps.
3368 */
3369
3370 bool
ldapmap_open(map,mode)3371 ldapmap_open(map, mode)
3372 MAP *map;
3373 int mode;
3374 {
3375 SM_LDAP_STRUCT *lmap;
3376 STAB *s;
3377 char *id;
3378
3379 if (tTd(38, 2))
3380 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3381
3382 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3383 HASLDAPGETALIASBYNAME
3384 if (VendorCode == VENDOR_SUN &&
3385 strcmp(map->map_mname, "aliases.ldap") == 0)
3386 {
3387 return true;
3388 }
3389 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3390
3391 mode &= O_ACCMODE;
3392
3393 /* sendmail doesn't have the ability to write to LDAP (yet) */
3394 if (mode != O_RDONLY)
3395 {
3396 /* issue a pseudo-error message */
3397 errno = SM_EMAPCANTWRITE;
3398 return false;
3399 }
3400
3401 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3402
3403 s = ldapmap_findconn(lmap);
3404 if (s->s_lmap != NULL)
3405 {
3406 /* Already have a connection open to this LDAP server */
3407 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3408 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3409
3410 /* Add this map as head of linked list */
3411 lmap->ldap_next = s->s_lmap;
3412 s->s_lmap = map;
3413
3414 if (tTd(38, 2))
3415 sm_dprintf("using cached connection\n");
3416 return true;
3417 }
3418
3419 if (tTd(38, 2))
3420 sm_dprintf("opening new connection\n");
3421
3422 if (lmap->ldap_host != NULL)
3423 id = lmap->ldap_host;
3424 else if (lmap->ldap_uri != NULL)
3425 id = lmap->ldap_uri;
3426 else
3427 id = "localhost";
3428
3429 if (tTd(74, 104))
3430 {
3431 extern MAPCLASS NullMapClass;
3432
3433 /* debug mode: don't actually open an LDAP connection */
3434 map->map_orgclass = map->map_class;
3435 map->map_class = &NullMapClass;
3436 map->map_mflags |= MF_OPEN;
3437 map->map_pid = CurrentPid;
3438 return true;
3439 }
3440
3441 /* No connection yet, connect */
3442 if (!sm_ldap_start(map->map_mname, lmap))
3443 {
3444 if (errno == ETIMEDOUT)
3445 {
3446 if (LogLevel > 1)
3447 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3448 "timeout connecting to LDAP server %.100s",
3449 id);
3450 }
3451
3452 if (!bitset(MF_OPTIONAL, map->map_mflags))
3453 {
3454 if (bitset(MF_NODEFER, map->map_mflags))
3455 {
3456 syserr("%s failed to %s in map %s",
3457 # if USE_LDAP_INIT
3458 "ldap_init/ldap_bind",
3459 # else /* USE_LDAP_INIT */
3460 "ldap_open",
3461 # endif /* USE_LDAP_INIT */
3462 id, map->map_mname);
3463 }
3464 else
3465 {
3466 syserr("451 4.3.5 %s failed to %s in map %s",
3467 # if USE_LDAP_INIT
3468 "ldap_init/ldap_bind",
3469 # else /* USE_LDAP_INIT */
3470 "ldap_open",
3471 # endif /* USE_LDAP_INIT */
3472 id, map->map_mname);
3473 }
3474 }
3475 return false;
3476 }
3477
3478 /* Save connection for reuse */
3479 s->s_lmap = map;
3480 return true;
3481 }
3482
3483 /*
3484 ** LDAPMAP_CLOSE -- close ldap map
3485 */
3486
3487 void
ldapmap_close(map)3488 ldapmap_close(map)
3489 MAP *map;
3490 {
3491 SM_LDAP_STRUCT *lmap;
3492 STAB *s;
3493
3494 if (tTd(38, 2))
3495 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3496
3497 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3498
3499 /* Check if already closed */
3500 if (lmap->ldap_ld == NULL)
3501 return;
3502
3503 /* Close the LDAP connection */
3504 sm_ldap_close(lmap);
3505
3506 /* Mark all the maps that share the connection as closed */
3507 s = ldapmap_findconn(lmap);
3508
3509 while (s->s_lmap != NULL)
3510 {
3511 MAP *smap = s->s_lmap;
3512
3513 if (tTd(38, 2) && smap != map)
3514 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3515 map->map_mname, smap->map_mname);
3516 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3517 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3518 lmap->ldap_ld = NULL;
3519 s->s_lmap = lmap->ldap_next;
3520 lmap->ldap_next = NULL;
3521 }
3522 }
3523
3524 # ifdef SUNET_ID
3525 /*
3526 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3527 ** This only makes sense at Stanford University.
3528 */
3529
3530 static char *
sunet_id_hash(str)3531 sunet_id_hash(str)
3532 char *str;
3533 {
3534 char *p, *p_last;
3535
3536 p = str;
3537 p_last = p;
3538 while (*p != '\0')
3539 {
3540 if (isascii(*p) && (islower(*p) || isdigit(*p)))
3541 {
3542 *p_last = *p;
3543 p_last++;
3544 }
3545 else if (isascii(*p) && isupper(*p))
3546 {
3547 *p_last = tolower(*p);
3548 p_last++;
3549 }
3550 ++p;
3551 }
3552 if (*p_last != '\0')
3553 *p_last = '\0';
3554 return str;
3555 }
3556 # define SM_CONVERT_ID(str) sunet_id_hash(str)
3557 # else /* SUNET_ID */
3558 # define SM_CONVERT_ID(str) makelower(str)
3559 # endif /* SUNET_ID */
3560
3561 /*
3562 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3563 */
3564
3565 char *
ldapmap_lookup(map,name,av,statp)3566 ldapmap_lookup(map, name, av, statp)
3567 MAP *map;
3568 char *name;
3569 char **av;
3570 int *statp;
3571 {
3572 int flags;
3573 int i;
3574 int plen = 0;
3575 int psize = 0;
3576 int msgid;
3577 int save_errno;
3578 char *vp, *p;
3579 char *result = NULL;
3580 SM_RPOOL_T *rpool;
3581 SM_LDAP_STRUCT *lmap = NULL;
3582 char *argv[SM_LDAP_ARGS];
3583 char keybuf[MAXKEY];
3584 #if SM_LDAP_ARGS != MAX_MAP_ARGS
3585 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
3586 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
3587
3588 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3589 HASLDAPGETALIASBYNAME
3590 if (VendorCode == VENDOR_SUN &&
3591 strcmp(map->map_mname, "aliases.ldap") == 0)
3592 {
3593 int rc;
3594 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3595 extern char *__getldapaliasbyname();
3596 char *answer;
3597
3598 answer = __getldapaliasbyname(name, &rc);
3599 #else
3600 char answer[MAXNAME + 1];
3601
3602 rc = __getldapaliasbyname(name, answer, sizeof(answer));
3603 #endif
3604 if (rc != 0)
3605 {
3606 if (tTd(38, 20))
3607 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
3608 name, errno);
3609 *statp = EX_NOTFOUND;
3610 return NULL;
3611 }
3612 *statp = EX_OK;
3613 if (tTd(38, 20))
3614 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
3615 answer);
3616 if (bitset(MF_MATCHONLY, map->map_mflags))
3617 result = map_rewrite(map, name, strlen(name), NULL);
3618 else
3619 result = map_rewrite(map, answer, strlen(answer), av);
3620 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3621 free(answer);
3622 #endif
3623 return result;
3624 }
3625 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3626
3627 /* Get ldap struct pointer from map */
3628 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3629 sm_ldap_setopts(lmap->ldap_ld, lmap);
3630
3631 if (lmap->ldap_multi_args)
3632 {
3633 SM_REQUIRE(av != NULL);
3634 memset(argv, '\0', sizeof(argv));
3635 for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
3636 {
3637 argv[i] = sm_strdup(av[i]);
3638 if (argv[i] == NULL)
3639 {
3640 int save_errno, j;
3641
3642 save_errno = errno;
3643 for (j = 0; j < i && argv[j] != NULL; j++)
3644 SM_FREE(argv[j]);
3645 *statp = EX_TEMPFAIL;
3646 errno = save_errno;
3647 return NULL;
3648 }
3649
3650 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3651 SM_CONVERT_ID(av[i]);
3652 }
3653 }
3654 else
3655 {
3656 (void) sm_strlcpy(keybuf, name, sizeof(keybuf));
3657
3658 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3659 SM_CONVERT_ID(keybuf);
3660 }
3661
3662 if (tTd(38, 20))
3663 {
3664 if (lmap->ldap_multi_args)
3665 {
3666 sm_dprintf("ldapmap_lookup(%s, argv)\n",
3667 map->map_mname);
3668 for (i = 0; i < SM_LDAP_ARGS; i++)
3669 {
3670 sm_dprintf(" argv[%d] = %s\n", i,
3671 argv[i] == NULL ? "NULL" : argv[i]);
3672 }
3673 }
3674 else
3675 {
3676 sm_dprintf("ldapmap_lookup(%s, %s)\n",
3677 map->map_mname, name);
3678 }
3679 }
3680
3681 if (lmap->ldap_multi_args)
3682 {
3683 msgid = sm_ldap_search_m(lmap, argv);
3684
3685 /* free the argv array and its content, no longer needed */
3686 for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
3687 SM_FREE(argv[i]);
3688 }
3689 else
3690 msgid = sm_ldap_search(lmap, keybuf);
3691 if (msgid == SM_LDAP_ERR)
3692 {
3693 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3694 save_errno = errno;
3695 if (!bitset(MF_OPTIONAL, map->map_mflags))
3696 {
3697 /*
3698 ** Do not include keybuf as this error may be shown
3699 ** to outsiders.
3700 */
3701
3702 if (bitset(MF_NODEFER, map->map_mflags))
3703 syserr("Error in ldap_search in map %s",
3704 map->map_mname);
3705 else
3706 syserr("451 4.3.5 Error in ldap_search in map %s",
3707 map->map_mname);
3708 }
3709 *statp = EX_TEMPFAIL;
3710 switch (save_errno - E_LDAPBASE)
3711 {
3712 # ifdef LDAP_SERVER_DOWN
3713 case LDAP_SERVER_DOWN:
3714 # endif /* LDAP_SERVER_DOWN */
3715 case LDAP_TIMEOUT:
3716 case LDAP_UNAVAILABLE:
3717 /* server disappeared, try reopen on next search */
3718 ldapmap_close(map);
3719 break;
3720 }
3721 errno = save_errno;
3722 return NULL;
3723 }
3724 #if SM_LDAP_ERROR_ON_MISSING_ARGS
3725 else if (msgid == SM_LDAP_ERR_ARG_MISS)
3726 {
3727 if (bitset(MF_NODEFER, map->map_mflags))
3728 syserr("Error in ldap_search in map %s, too few arguments",
3729 map->map_mname);
3730 else
3731 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
3732 map->map_mname);
3733 *statp = EX_CONFIG;
3734 return NULL;
3735 }
3736 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
3737
3738 *statp = EX_NOTFOUND;
3739 vp = NULL;
3740
3741 flags = 0;
3742 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3743 flags |= SM_LDAP_SINGLEMATCH;
3744 if (bitset(MF_MATCHONLY, map->map_mflags))
3745 flags |= SM_LDAP_MATCHONLY;
3746 # if _FFR_LDAP_SINGLEDN
3747 if (bitset(MF_SINGLEDN, map->map_mflags))
3748 flags |= SM_LDAP_SINGLEDN;
3749 # endif /* _FFR_LDAP_SINGLEDN */
3750
3751 /* Create an rpool for search related memory usage */
3752 rpool = sm_rpool_new_x(NULL);
3753
3754 p = NULL;
3755 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3756 rpool, &p, &plen, &psize, NULL);
3757 save_errno = errno;
3758
3759 /* Copy result so rpool can be freed */
3760 if (*statp == EX_OK && p != NULL)
3761 vp = newstr(p);
3762 sm_rpool_free(rpool);
3763
3764 /* need to restart LDAP connection? */
3765 if (*statp == EX_RESTART)
3766 {
3767 *statp = EX_TEMPFAIL;
3768 ldapmap_close(map);
3769 }
3770
3771 errno = save_errno;
3772 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3773 {
3774 if (!bitset(MF_OPTIONAL, map->map_mflags))
3775 {
3776 if (bitset(MF_NODEFER, map->map_mflags))
3777 syserr("Error getting LDAP results, map=%s, name=%s",
3778 map->map_mname, name);
3779 else
3780 syserr("451 4.3.5 Error getting LDAP results, map=%s, name=%s",
3781 map->map_mname, name);
3782 }
3783 errno = save_errno;
3784 return NULL;
3785 }
3786
3787 /* Did we match anything? */
3788 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3789 return NULL;
3790
3791 if (*statp == EX_OK)
3792 {
3793 if (LogLevel > 9)
3794 sm_syslog(LOG_INFO, CurEnv->e_id,
3795 "ldap=%s, %.100s=>%s", map->map_mname, name,
3796 vp == NULL ? "<NULL>" : vp);
3797 if (bitset(MF_MATCHONLY, map->map_mflags))
3798 result = map_rewrite(map, name, strlen(name), NULL);
3799 else
3800 {
3801 /* vp != NULL according to test above */
3802 result = map_rewrite(map, vp, strlen(vp), av);
3803 }
3804 if (vp != NULL)
3805 sm_free(vp); /* XXX */
3806 }
3807 return result;
3808 }
3809
3810 /*
3811 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3812 **
3813 ** Cache LDAP connections based on the host, port, bind DN,
3814 ** secret, and PID so we don't have multiple connections open to
3815 ** the same server for different maps. Need a separate connection
3816 ** per PID since a parent process may close the map before the
3817 ** child is done with it.
3818 **
3819 ** Parameters:
3820 ** lmap -- LDAP map information
3821 **
3822 ** Returns:
3823 ** Symbol table entry for the LDAP connection.
3824 */
3825
3826 static STAB *
ldapmap_findconn(lmap)3827 ldapmap_findconn(lmap)
3828 SM_LDAP_STRUCT *lmap;
3829 {
3830 char *format;
3831 char *nbuf;
3832 char *id;
3833 STAB *SM_NONVOLATILE s = NULL;
3834
3835 if (lmap->ldap_host != NULL)
3836 id = lmap->ldap_host;
3837 else if (lmap->ldap_uri != NULL)
3838 id = lmap->ldap_uri;
3839 else
3840 id = "localhost";
3841
3842 format = "%s%c%d%c%d%c%s%c%s%d";
3843 nbuf = sm_stringf_x(format,
3844 id,
3845 CONDELSE,
3846 lmap->ldap_port,
3847 CONDELSE,
3848 lmap->ldap_version,
3849 CONDELSE,
3850 (lmap->ldap_binddn == NULL ? ""
3851 : lmap->ldap_binddn),
3852 CONDELSE,
3853 (lmap->ldap_secret == NULL ? ""
3854 : lmap->ldap_secret),
3855 (int) CurrentPid);
3856 SM_TRY
3857 s = stab(nbuf, ST_LMAP, ST_ENTER);
3858 SM_FINALLY
3859 sm_free(nbuf);
3860 SM_END_TRY
3861 return s;
3862 }
3863 /*
3864 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3865 */
3866
3867 static struct lamvalues LDAPAuthMethods[] =
3868 {
3869 { "none", LDAP_AUTH_NONE },
3870 { "simple", LDAP_AUTH_SIMPLE },
3871 # ifdef LDAP_AUTH_KRBV4
3872 { "krbv4", LDAP_AUTH_KRBV4 },
3873 # endif /* LDAP_AUTH_KRBV4 */
3874 { NULL, 0 }
3875 };
3876
3877 static struct ladvalues LDAPAliasDereference[] =
3878 {
3879 { "never", LDAP_DEREF_NEVER },
3880 { "always", LDAP_DEREF_ALWAYS },
3881 { "search", LDAP_DEREF_SEARCHING },
3882 { "find", LDAP_DEREF_FINDING },
3883 { NULL, 0 }
3884 };
3885
3886 static struct lssvalues LDAPSearchScope[] =
3887 {
3888 { "base", LDAP_SCOPE_BASE },
3889 { "one", LDAP_SCOPE_ONELEVEL },
3890 { "sub", LDAP_SCOPE_SUBTREE },
3891 { NULL, 0 }
3892 };
3893
3894 bool
ldapmap_parseargs(map,args)3895 ldapmap_parseargs(map, args)
3896 MAP *map;
3897 char *args;
3898 {
3899 bool secretread = true;
3900 bool attrssetup = false;
3901 int i;
3902 register char *p = args;
3903 SM_LDAP_STRUCT *lmap;
3904 struct lamvalues *lam;
3905 struct ladvalues *lad;
3906 struct lssvalues *lss;
3907 char ldapfilt[MAXLINE];
3908 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3909
3910 /* Get ldap struct pointer from map */
3911 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3912
3913 /* Check if setting the initial LDAP defaults */
3914 if (lmap == NULL || lmap != LDAPDefaults)
3915 {
3916 /* We need to alloc an SM_LDAP_STRUCT struct */
3917 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
3918 if (LDAPDefaults == NULL)
3919 sm_ldap_clear(lmap);
3920 else
3921 STRUCTCOPY(*LDAPDefaults, *lmap);
3922 }
3923
3924 /* there is no check whether there is really an argument */
3925 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3926 map->map_spacesub = SpaceSub; /* default value */
3927
3928 /* Check if setting up an alias or file class LDAP map */
3929 if (bitset(MF_ALIAS, map->map_mflags))
3930 {
3931 /* Comma separate if used as an alias file */
3932 map->map_coldelim = ',';
3933 if (*args == '\0')
3934 {
3935 int n;
3936 char *lc;
3937 char jbuf[MAXHOSTNAMELEN];
3938 char lcbuf[MAXLINE];
3939
3940 /* Get $j */
3941 expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
3942 if (jbuf[0] == '\0')
3943 {
3944 (void) sm_strlcpy(jbuf, "localhost",
3945 sizeof(jbuf));
3946 }
3947
3948 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3949 if (lc == NULL)
3950 lc = "";
3951 else
3952 {
3953 expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
3954 lc = lcbuf;
3955 }
3956
3957 n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
3958 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3959 lc, jbuf);
3960 if (n >= sizeof(ldapfilt))
3961 {
3962 syserr("%s: Default LDAP string too long",
3963 map->map_mname);
3964 return false;
3965 }
3966
3967 /* default args for an alias LDAP entry */
3968 lmap->ldap_filter = ldapfilt;
3969 lmap->ldap_attr[0] = "objectClass";
3970 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3971 lmap->ldap_attr_needobjclass[0] = NULL;
3972 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3973 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3974 lmap->ldap_attr_needobjclass[1] = NULL;
3975 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3976 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3977 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3978 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3979 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3980 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3981 lmap->ldap_attr[4] = NULL;
3982 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3983 lmap->ldap_attr_needobjclass[4] = NULL;
3984 attrssetup = true;
3985 }
3986 }
3987 else if (bitset(MF_FILECLASS, map->map_mflags))
3988 {
3989 /* Space separate if used as a file class file */
3990 map->map_coldelim = ' ';
3991 }
3992
3993 # if _FFR_LDAP_NETWORK_TIMEOUT
3994 lmap->ldap_networktmo = 120;
3995 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
3996
3997 for (;;)
3998 {
3999 while (isascii(*p) && isspace(*p))
4000 p++;
4001 if (*p != '-')
4002 break;
4003 switch (*++p)
4004 {
4005 case 'A':
4006 map->map_mflags |= MF_APPEND;
4007 break;
4008
4009 case 'a':
4010 map->map_app = ++p;
4011 break;
4012
4013 case 'D':
4014 map->map_mflags |= MF_DEFER;
4015 break;
4016
4017 case 'f':
4018 map->map_mflags |= MF_NOFOLDCASE;
4019 break;
4020
4021 case 'm':
4022 map->map_mflags |= MF_MATCHONLY;
4023 break;
4024
4025 case 'N':
4026 map->map_mflags |= MF_INCLNULL;
4027 map->map_mflags &= ~MF_TRY0NULL;
4028 break;
4029
4030 case 'O':
4031 map->map_mflags &= ~MF_TRY1NULL;
4032 break;
4033
4034 case 'o':
4035 map->map_mflags |= MF_OPTIONAL;
4036 break;
4037
4038 case 'q':
4039 map->map_mflags |= MF_KEEPQUOTES;
4040 break;
4041
4042 case 'S':
4043 map->map_spacesub = *++p;
4044 break;
4045
4046 case 'T':
4047 map->map_tapp = ++p;
4048 break;
4049
4050 case 't':
4051 map->map_mflags |= MF_NODEFER;
4052 break;
4053
4054 case 'z':
4055 if (*++p != '\\')
4056 map->map_coldelim = *p;
4057 else
4058 {
4059 switch (*++p)
4060 {
4061 case 'n':
4062 map->map_coldelim = '\n';
4063 break;
4064
4065 case 't':
4066 map->map_coldelim = '\t';
4067 break;
4068
4069 default:
4070 map->map_coldelim = '\\';
4071 }
4072 }
4073 break;
4074
4075 /* Start of ldapmap specific args */
4076 case '1':
4077 map->map_mflags |= MF_SINGLEMATCH;
4078 break;
4079
4080 # if _FFR_LDAP_SINGLEDN
4081 case '2':
4082 map->map_mflags |= MF_SINGLEDN;
4083 break;
4084 # endif /* _FFR_LDAP_SINGLEDN */
4085
4086 case 'b': /* search base */
4087 while (isascii(*++p) && isspace(*p))
4088 continue;
4089 lmap->ldap_base = p;
4090 break;
4091
4092 # if _FFR_LDAP_NETWORK_TIMEOUT
4093 case 'c': /* network (connect) timeout */
4094 while (isascii(*++p) && isspace(*p))
4095 continue;
4096 lmap->ldap_networktmo = atoi(p);
4097 break;
4098 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
4099
4100 case 'd': /* Dn to bind to server as */
4101 while (isascii(*++p) && isspace(*p))
4102 continue;
4103 lmap->ldap_binddn = p;
4104 break;
4105
4106 case 'H': /* Use LDAP URI */
4107 # if !USE_LDAP_INIT
4108 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4109 map->map_mname);
4110 return false;
4111 # else /* !USE_LDAP_INIT */
4112 if (lmap->ldap_host != NULL)
4113 {
4114 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4115 map->map_mname);
4116 return false;
4117 }
4118 while (isascii(*++p) && isspace(*p))
4119 continue;
4120 lmap->ldap_uri = p;
4121 break;
4122 # endif /* !USE_LDAP_INIT */
4123
4124 case 'h': /* ldap host */
4125 while (isascii(*++p) && isspace(*p))
4126 continue;
4127 if (lmap->ldap_uri != NULL)
4128 {
4129 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4130 map->map_mname);
4131 return false;
4132 }
4133 lmap->ldap_host = p;
4134 break;
4135
4136 case 'K':
4137 lmap->ldap_multi_args = true;
4138 break;
4139
4140 case 'k': /* search field */
4141 while (isascii(*++p) && isspace(*p))
4142 continue;
4143 lmap->ldap_filter = p;
4144 break;
4145
4146 case 'l': /* time limit */
4147 while (isascii(*++p) && isspace(*p))
4148 continue;
4149 lmap->ldap_timelimit = atoi(p);
4150 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4151 break;
4152
4153 case 'M': /* Method for binding */
4154 while (isascii(*++p) && isspace(*p))
4155 continue;
4156
4157 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4158 p += 10;
4159
4160 for (lam = LDAPAuthMethods;
4161 lam != NULL && lam->lam_name != NULL; lam++)
4162 {
4163 if (sm_strncasecmp(p, lam->lam_name,
4164 strlen(lam->lam_name)) == 0)
4165 break;
4166 }
4167 if (lam->lam_name != NULL)
4168 lmap->ldap_method = lam->lam_code;
4169 else
4170 {
4171 /* bad config line */
4172 if (!bitset(MCF_OPTFILE,
4173 map->map_class->map_cflags))
4174 {
4175 char *ptr;
4176
4177 if ((ptr = strchr(p, ' ')) != NULL)
4178 *ptr = '\0';
4179 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4180 p, map->map_mname);
4181 if (ptr != NULL)
4182 *ptr = ' ';
4183 return false;
4184 }
4185 }
4186 break;
4187
4188 case 'n': /* retrieve attribute names only */
4189 lmap->ldap_attrsonly = LDAPMAP_TRUE;
4190 break;
4191
4192 /*
4193 ** This is a string that is dependent on the
4194 ** method used defined by 'M'.
4195 */
4196
4197 case 'P': /* Secret password for binding */
4198 while (isascii(*++p) && isspace(*p))
4199 continue;
4200 lmap->ldap_secret = p;
4201 secretread = false;
4202 break;
4203
4204 case 'p': /* ldap port */
4205 while (isascii(*++p) && isspace(*p))
4206 continue;
4207 lmap->ldap_port = atoi(p);
4208 break;
4209
4210 /* args stolen from ldapsearch.c */
4211 case 'R': /* don't auto chase referrals */
4212 # ifdef LDAP_REFERRALS
4213 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
4214 # else /* LDAP_REFERRALS */
4215 syserr("compile with -DLDAP_REFERRALS for referral support");
4216 # endif /* LDAP_REFERRALS */
4217 break;
4218
4219 case 'r': /* alias dereferencing */
4220 while (isascii(*++p) && isspace(*p))
4221 continue;
4222
4223 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
4224 p += 11;
4225
4226 for (lad = LDAPAliasDereference;
4227 lad != NULL && lad->lad_name != NULL; lad++)
4228 {
4229 if (sm_strncasecmp(p, lad->lad_name,
4230 strlen(lad->lad_name)) == 0)
4231 break;
4232 }
4233 if (lad->lad_name != NULL)
4234 lmap->ldap_deref = lad->lad_code;
4235 else
4236 {
4237 /* bad config line */
4238 if (!bitset(MCF_OPTFILE,
4239 map->map_class->map_cflags))
4240 {
4241 char *ptr;
4242
4243 if ((ptr = strchr(p, ' ')) != NULL)
4244 *ptr = '\0';
4245 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4246 p, map->map_mname);
4247 if (ptr != NULL)
4248 *ptr = ' ';
4249 return false;
4250 }
4251 }
4252 break;
4253
4254 case 's': /* search scope */
4255 while (isascii(*++p) && isspace(*p))
4256 continue;
4257
4258 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
4259 p += 11;
4260
4261 for (lss = LDAPSearchScope;
4262 lss != NULL && lss->lss_name != NULL; lss++)
4263 {
4264 if (sm_strncasecmp(p, lss->lss_name,
4265 strlen(lss->lss_name)) == 0)
4266 break;
4267 }
4268 if (lss->lss_name != NULL)
4269 lmap->ldap_scope = lss->lss_code;
4270 else
4271 {
4272 /* bad config line */
4273 if (!bitset(MCF_OPTFILE,
4274 map->map_class->map_cflags))
4275 {
4276 char *ptr;
4277
4278 if ((ptr = strchr(p, ' ')) != NULL)
4279 *ptr = '\0';
4280 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4281 p, map->map_mname);
4282 if (ptr != NULL)
4283 *ptr = ' ';
4284 return false;
4285 }
4286 }
4287 break;
4288
4289 case 'V':
4290 if (*++p != '\\')
4291 lmap->ldap_attrsep = *p;
4292 else
4293 {
4294 switch (*++p)
4295 {
4296 case 'n':
4297 lmap->ldap_attrsep = '\n';
4298 break;
4299
4300 case 't':
4301 lmap->ldap_attrsep = '\t';
4302 break;
4303
4304 default:
4305 lmap->ldap_attrsep = '\\';
4306 }
4307 }
4308 break;
4309
4310 case 'v': /* attr to return */
4311 while (isascii(*++p) && isspace(*p))
4312 continue;
4313 lmap->ldap_attr[0] = p;
4314 lmap->ldap_attr[1] = NULL;
4315 break;
4316
4317 case 'w':
4318 /* -w should be for passwd, -P should be for version */
4319 while (isascii(*++p) && isspace(*p))
4320 continue;
4321 lmap->ldap_version = atoi(p);
4322 # ifdef LDAP_VERSION_MAX
4323 if (lmap->ldap_version > LDAP_VERSION_MAX)
4324 {
4325 syserr("LDAP version %d exceeds max of %d in map %s",
4326 lmap->ldap_version, LDAP_VERSION_MAX,
4327 map->map_mname);
4328 return false;
4329 }
4330 # endif /* LDAP_VERSION_MAX */
4331 # ifdef LDAP_VERSION_MIN
4332 if (lmap->ldap_version < LDAP_VERSION_MIN)
4333 {
4334 syserr("LDAP version %d is lower than min of %d in map %s",
4335 lmap->ldap_version, LDAP_VERSION_MIN,
4336 map->map_mname);
4337 return false;
4338 }
4339 # endif /* LDAP_VERSION_MIN */
4340 break;
4341
4342 case 'Z':
4343 while (isascii(*++p) && isspace(*p))
4344 continue;
4345 lmap->ldap_sizelimit = atoi(p);
4346 break;
4347
4348 default:
4349 syserr("Illegal option %c map %s", *p, map->map_mname);
4350 break;
4351 }
4352
4353 /* need to account for quoted strings here */
4354 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4355 {
4356 if (*p == '"')
4357 {
4358 while (*++p != '"' && *p != '\0')
4359 continue;
4360 if (*p != '\0')
4361 p++;
4362 }
4363 else
4364 p++;
4365 }
4366
4367 if (*p != '\0')
4368 *p++ = '\0';
4369 }
4370
4371 if (map->map_app != NULL)
4372 map->map_app = newstr(ldapmap_dequote(map->map_app));
4373 if (map->map_tapp != NULL)
4374 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4375
4376 /*
4377 ** We need to swallow up all the stuff into a struct
4378 ** and dump it into map->map_dbptr1
4379 */
4380
4381 if (lmap->ldap_host != NULL &&
4382 (LDAPDefaults == NULL ||
4383 LDAPDefaults == lmap ||
4384 LDAPDefaults->ldap_host != lmap->ldap_host))
4385 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4386 map->map_domain = lmap->ldap_host;
4387
4388 if (lmap->ldap_uri != NULL &&
4389 (LDAPDefaults == NULL ||
4390 LDAPDefaults == lmap ||
4391 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4392 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4393 map->map_domain = lmap->ldap_uri;
4394
4395 if (lmap->ldap_binddn != NULL &&
4396 (LDAPDefaults == NULL ||
4397 LDAPDefaults == lmap ||
4398 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4399 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4400
4401 if (lmap->ldap_secret != NULL &&
4402 (LDAPDefaults == NULL ||
4403 LDAPDefaults == lmap ||
4404 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4405 {
4406 SM_FILE_T *sfd;
4407 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4408
4409 if (DontLockReadFiles)
4410 sff |= SFF_NOLOCK;
4411
4412 /* need to use method to map secret to passwd string */
4413 switch (lmap->ldap_method)
4414 {
4415 case LDAP_AUTH_NONE:
4416 /* Do nothing */
4417 break;
4418
4419 case LDAP_AUTH_SIMPLE:
4420
4421 /*
4422 ** Secret is the name of a file with
4423 ** the first line as the password.
4424 */
4425
4426 /* Already read in the secret? */
4427 if (secretread)
4428 break;
4429
4430 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4431 O_RDONLY, 0, sff);
4432 if (sfd == NULL)
4433 {
4434 syserr("LDAP map: cannot open secret %s",
4435 ldapmap_dequote(lmap->ldap_secret));
4436 return false;
4437 }
4438 lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
4439 sfd, TimeOuts.to_fileopen,
4440 "ldapmap_parseargs");
4441 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4442 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4443 {
4444 syserr("LDAP map: secret in %s too long",
4445 ldapmap_dequote(lmap->ldap_secret));
4446 return false;
4447 }
4448 if (lmap->ldap_secret != NULL &&
4449 strlen(m_tmp) > 0)
4450 {
4451 /* chomp newline */
4452 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4453 m_tmp[strlen(m_tmp) - 1] = '\0';
4454
4455 lmap->ldap_secret = m_tmp;
4456 }
4457 break;
4458
4459 # ifdef LDAP_AUTH_KRBV4
4460 case LDAP_AUTH_KRBV4:
4461
4462 /*
4463 ** Secret is where the ticket file is
4464 ** stashed
4465 */
4466
4467 (void) sm_snprintf(m_tmp, sizeof(m_tmp),
4468 "KRBTKFILE=%s",
4469 ldapmap_dequote(lmap->ldap_secret));
4470 lmap->ldap_secret = m_tmp;
4471 break;
4472 # endif /* LDAP_AUTH_KRBV4 */
4473
4474 default: /* Should NEVER get here */
4475 syserr("LDAP map: Illegal value in lmap method");
4476 return false;
4477 /* NOTREACHED */
4478 break;
4479 }
4480 }
4481
4482 if (lmap->ldap_secret != NULL &&
4483 (LDAPDefaults == NULL ||
4484 LDAPDefaults == lmap ||
4485 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4486 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4487
4488 if (lmap->ldap_base != NULL &&
4489 (LDAPDefaults == NULL ||
4490 LDAPDefaults == lmap ||
4491 LDAPDefaults->ldap_base != lmap->ldap_base))
4492 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4493
4494 /*
4495 ** Save the server from extra work. If request is for a single
4496 ** match, tell the server to only return enough records to
4497 ** determine if there is a single match or not. This can not
4498 ** be one since the server would only return one and we wouldn't
4499 ** know if there were others available.
4500 */
4501
4502 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4503 lmap->ldap_sizelimit = 2;
4504
4505 /* If setting defaults, don't process ldap_filter and ldap_attr */
4506 if (lmap == LDAPDefaults)
4507 return true;
4508
4509 if (lmap->ldap_filter != NULL)
4510 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4511 else
4512 {
4513 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4514 {
4515 syserr("No filter given in map %s", map->map_mname);
4516 return false;
4517 }
4518 }
4519
4520 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4521 {
4522 bool recurse = false;
4523 bool normalseen = false;
4524
4525 i = 0;
4526 p = ldapmap_dequote(lmap->ldap_attr[0]);
4527 lmap->ldap_attr[0] = NULL;
4528
4529 /* Prime the attr list with the objectClass attribute */
4530 lmap->ldap_attr[i] = "objectClass";
4531 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4532 lmap->ldap_attr_needobjclass[i] = NULL;
4533 i++;
4534
4535 while (p != NULL)
4536 {
4537 char *v;
4538
4539 while (isascii(*p) && isspace(*p))
4540 p++;
4541 if (*p == '\0')
4542 break;
4543 v = p;
4544 p = strchr(v, ',');
4545 if (p != NULL)
4546 *p++ = '\0';
4547
4548 if (i >= LDAPMAP_MAX_ATTR)
4549 {
4550 syserr("Too many return attributes in %s (max %d)",
4551 map->map_mname, LDAPMAP_MAX_ATTR);
4552 return false;
4553 }
4554 if (*v != '\0')
4555 {
4556 int j;
4557 int use;
4558 char *type;
4559 char *needobjclass;
4560
4561 type = strchr(v, ':');
4562 if (type != NULL)
4563 {
4564 *type++ = '\0';
4565 needobjclass = strchr(type, ':');
4566 if (needobjclass != NULL)
4567 *needobjclass++ = '\0';
4568 }
4569 else
4570 {
4571 needobjclass = NULL;
4572 }
4573
4574 use = i;
4575
4576 /* allow override on "objectClass" type */
4577 if (sm_strcasecmp(v, "objectClass") == 0 &&
4578 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4579 {
4580 use = 0;
4581 }
4582 else
4583 {
4584 /*
4585 ** Don't add something to attribute
4586 ** list twice.
4587 */
4588
4589 for (j = 1; j < i; j++)
4590 {
4591 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4592 {
4593 syserr("Duplicate attribute (%s) in %s",
4594 v, map->map_mname);
4595 return false;
4596 }
4597 }
4598
4599 lmap->ldap_attr[use] = newstr(v);
4600 if (needobjclass != NULL &&
4601 *needobjclass != '\0' &&
4602 *needobjclass != '*')
4603 {
4604 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4605 }
4606 else
4607 {
4608 lmap->ldap_attr_needobjclass[use] = NULL;
4609 }
4610
4611 }
4612
4613 if (type != NULL && *type != '\0')
4614 {
4615 if (sm_strcasecmp(type, "dn") == 0)
4616 {
4617 recurse = true;
4618 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4619 }
4620 else if (sm_strcasecmp(type, "filter") == 0)
4621 {
4622 recurse = true;
4623 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4624 }
4625 else if (sm_strcasecmp(type, "url") == 0)
4626 {
4627 recurse = true;
4628 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4629 }
4630 else if (sm_strcasecmp(type, "normal") == 0)
4631 {
4632 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4633 normalseen = true;
4634 }
4635 else
4636 {
4637 syserr("Unknown attribute type (%s) in %s",
4638 type, map->map_mname);
4639 return false;
4640 }
4641 }
4642 else
4643 {
4644 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4645 normalseen = true;
4646 }
4647 i++;
4648 }
4649 }
4650 lmap->ldap_attr[i] = NULL;
4651
4652 /* Set in case needed in future code */
4653 attrssetup = true;
4654
4655 if (recurse && !normalseen)
4656 {
4657 syserr("LDAP recursion requested in %s but no returnable attribute given",
4658 map->map_mname);
4659 return false;
4660 }
4661 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4662 {
4663 syserr("LDAP recursion requested in %s can not be used with -n",
4664 map->map_mname);
4665 return false;
4666 }
4667 }
4668 map->map_db1 = (ARBPTR_T) lmap;
4669 return true;
4670 }
4671
4672 /*
4673 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4674 **
4675 ** Parameters:
4676 ** spec -- map argument string from LDAPDefaults option
4677 **
4678 ** Returns:
4679 ** None.
4680 */
4681
4682 void
ldapmap_set_defaults(spec)4683 ldapmap_set_defaults(spec)
4684 char *spec;
4685 {
4686 STAB *class;
4687 MAP map;
4688
4689 /* Allocate and set the default values */
4690 if (LDAPDefaults == NULL)
4691 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
4692 sm_ldap_clear(LDAPDefaults);
4693
4694 memset(&map, '\0', sizeof(map));
4695
4696 /* look up the class */
4697 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4698 if (class == NULL)
4699 {
4700 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4701 return;
4702 }
4703 map.map_class = &class->s_mapclass;
4704 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4705 map.map_mname = "O LDAPDefaultSpec";
4706
4707 (void) ldapmap_parseargs(&map, spec);
4708
4709 /* These should never be set in LDAPDefaults */
4710 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4711 map.map_spacesub != SpaceSub ||
4712 map.map_app != NULL ||
4713 map.map_tapp != NULL)
4714 {
4715 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4716 SM_FREE_CLR(map.map_app);
4717 SM_FREE_CLR(map.map_tapp);
4718 }
4719
4720 if (LDAPDefaults->ldap_filter != NULL)
4721 {
4722 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4723
4724 /* don't free, it isn't malloc'ed in parseargs */
4725 LDAPDefaults->ldap_filter = NULL;
4726 }
4727
4728 if (LDAPDefaults->ldap_attr[0] != NULL)
4729 {
4730 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4731 /* don't free, they aren't malloc'ed in parseargs */
4732 LDAPDefaults->ldap_attr[0] = NULL;
4733 }
4734 }
4735 #endif /* LDAPMAP */
4736 /*
4737 ** PH map
4738 */
4739
4740 #if PH_MAP
4741
4742 /*
4743 ** Support for the CCSO Nameserver (ph/qi).
4744 ** This code is intended to replace the so-called "ph mailer".
4745 ** Contributed by Mark D. Roth. Contact him for support.
4746 */
4747
4748 /* what version of the ph map code we're running */
4749 static char phmap_id[128];
4750
4751 /* sendmail version for phmap id string */
4752 extern const char Version[];
4753
4754 /* assume we're using nph-1.2.x if not specified */
4755 # ifndef NPH_VERSION
4756 # define NPH_VERSION 10200
4757 # endif
4758
4759 /* compatibility for versions older than nph-1.2.0 */
4760 # if NPH_VERSION < 10200
4761 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4762 # define PH_OPEN_DONTID PH_DONTID
4763 # define PH_CLOSE_FAST PH_FASTCLOSE
4764 # define PH_ERR_DATAERR PH_DATAERR
4765 # define PH_ERR_NOMATCH PH_NOMATCH
4766 # endif /* NPH_VERSION < 10200 */
4767
4768 /*
4769 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4770 */
4771
4772 bool
ph_map_parseargs(map,args)4773 ph_map_parseargs(map, args)
4774 MAP *map;
4775 char *args;
4776 {
4777 register bool done;
4778 register char *p = args;
4779 PH_MAP_STRUCT *pmap = NULL;
4780
4781 /* initialize version string */
4782 (void) sm_snprintf(phmap_id, sizeof(phmap_id),
4783 "sendmail-%s phmap-20010529 libphclient-%s",
4784 Version, libphclient_version);
4785
4786 pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
4787
4788 /* defaults */
4789 pmap->ph_servers = NULL;
4790 pmap->ph_field_list = NULL;
4791 pmap->ph = NULL;
4792 pmap->ph_timeout = 0;
4793 pmap->ph_fastclose = 0;
4794
4795 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4796 for (;;)
4797 {
4798 while (isascii(*p) && isspace(*p))
4799 p++;
4800 if (*p != '-')
4801 break;
4802 switch (*++p)
4803 {
4804 case 'N':
4805 map->map_mflags |= MF_INCLNULL;
4806 map->map_mflags &= ~MF_TRY0NULL;
4807 break;
4808
4809 case 'O':
4810 map->map_mflags &= ~MF_TRY1NULL;
4811 break;
4812
4813 case 'o':
4814 map->map_mflags |= MF_OPTIONAL;
4815 break;
4816
4817 case 'f':
4818 map->map_mflags |= MF_NOFOLDCASE;
4819 break;
4820
4821 case 'm':
4822 map->map_mflags |= MF_MATCHONLY;
4823 break;
4824
4825 case 'A':
4826 map->map_mflags |= MF_APPEND;
4827 break;
4828
4829 case 'q':
4830 map->map_mflags |= MF_KEEPQUOTES;
4831 break;
4832
4833 case 't':
4834 map->map_mflags |= MF_NODEFER;
4835 break;
4836
4837 case 'a':
4838 map->map_app = ++p;
4839 break;
4840
4841 case 'T':
4842 map->map_tapp = ++p;
4843 break;
4844
4845 case 'l':
4846 while (isascii(*++p) && isspace(*p))
4847 continue;
4848 pmap->ph_timeout = atoi(p);
4849 break;
4850
4851 case 'S':
4852 map->map_spacesub = *++p;
4853 break;
4854
4855 case 'D':
4856 map->map_mflags |= MF_DEFER;
4857 break;
4858
4859 case 'h': /* PH server list */
4860 while (isascii(*++p) && isspace(*p))
4861 continue;
4862 pmap->ph_servers = p;
4863 break;
4864
4865 case 'k': /* fields to search for */
4866 while (isascii(*++p) && isspace(*p))
4867 continue;
4868 pmap->ph_field_list = p;
4869 break;
4870
4871 default:
4872 syserr("ph_map_parseargs: unknown option -%c", *p);
4873 }
4874
4875 /* try to account for quoted strings */
4876 done = isascii(*p) && isspace(*p);
4877 while (*p != '\0' && !done)
4878 {
4879 if (*p == '"')
4880 {
4881 while (*++p != '"' && *p != '\0')
4882 continue;
4883 if (*p != '\0')
4884 p++;
4885 }
4886 else
4887 p++;
4888 done = isascii(*p) && isspace(*p);
4889 }
4890
4891 if (*p != '\0')
4892 *p++ = '\0';
4893 }
4894
4895 if (map->map_app != NULL)
4896 map->map_app = newstr(ph_map_dequote(map->map_app));
4897 if (map->map_tapp != NULL)
4898 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4899
4900 if (pmap->ph_field_list != NULL)
4901 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4902
4903 if (pmap->ph_servers != NULL)
4904 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4905 else
4906 {
4907 syserr("ph_map_parseargs: -h flag is required");
4908 return false;
4909 }
4910
4911 map->map_db1 = (ARBPTR_T) pmap;
4912 return true;
4913 }
4914
4915 /*
4916 ** PH_MAP_CLOSE -- close the connection to the ph server
4917 */
4918
4919 void
ph_map_close(map)4920 ph_map_close(map)
4921 MAP *map;
4922 {
4923 PH_MAP_STRUCT *pmap;
4924
4925 pmap = (PH_MAP_STRUCT *)map->map_db1;
4926 if (tTd(38, 9))
4927 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4928 map->map_mname, pmap->ph_fastclose);
4929
4930
4931 if (pmap->ph != NULL)
4932 {
4933 ph_set_sendhook(pmap->ph, NULL);
4934 ph_set_recvhook(pmap->ph, NULL);
4935 ph_close(pmap->ph, pmap->ph_fastclose);
4936 }
4937
4938 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4939 }
4940
4941 static jmp_buf PHTimeout;
4942
4943 /* ARGSUSED */
4944 static void
ph_timeout(unused)4945 ph_timeout(unused)
4946 int unused;
4947 {
4948 /*
4949 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4950 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4951 ** DOING.
4952 */
4953
4954 errno = ETIMEDOUT;
4955 longjmp(PHTimeout, 1);
4956 }
4957
4958 static void
4959 #if NPH_VERSION >= 10200
ph_map_send_debug(appdata,text)4960 ph_map_send_debug(appdata, text)
4961 void *appdata;
4962 #else
4963 ph_map_send_debug(text)
4964 #endif
4965 char *text;
4966 {
4967 if (LogLevel > 9)
4968 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4969 "ph_map_send_debug: ==> %s", text);
4970 if (tTd(38, 20))
4971 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4972 }
4973
4974 static void
4975 #if NPH_VERSION >= 10200
ph_map_recv_debug(appdata,text)4976 ph_map_recv_debug(appdata, text)
4977 void *appdata;
4978 #else
4979 ph_map_recv_debug(text)
4980 #endif
4981 char *text;
4982 {
4983 if (LogLevel > 10)
4984 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4985 "ph_map_recv_debug: <== %s", text);
4986 if (tTd(38, 21))
4987 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4988 }
4989
4990 /*
4991 ** PH_MAP_OPEN -- sub for opening PH map
4992 */
4993 bool
ph_map_open(map,mode)4994 ph_map_open(map, mode)
4995 MAP *map;
4996 int mode;
4997 {
4998 PH_MAP_STRUCT *pmap;
4999 register SM_EVENT *ev = NULL;
5000 int save_errno = 0;
5001 char *hostlist, *host;
5002
5003 if (tTd(38, 2))
5004 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
5005
5006 mode &= O_ACCMODE;
5007 if (mode != O_RDONLY)
5008 {
5009 /* issue a pseudo-error message */
5010 errno = SM_EMAPCANTWRITE;
5011 return false;
5012 }
5013
5014 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
5015 bitset(MF_DEFER, map->map_mflags))
5016 {
5017 if (tTd(9, 1))
5018 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
5019 map->map_mname);
5020
5021 /*
5022 ** Unset MF_DEFER here so that map_lookup() returns
5023 ** a temporary failure using the bogus map and
5024 ** map->map_tapp instead of the default permanent error.
5025 */
5026
5027 map->map_mflags &= ~MF_DEFER;
5028 return false;
5029 }
5030
5031 pmap = (PH_MAP_STRUCT *)map->map_db1;
5032 pmap->ph_fastclose = 0; /* refresh field for reopen */
5033
5034 /* try each host in the list */
5035 hostlist = newstr(pmap->ph_servers);
5036 for (host = strtok(hostlist, " ");
5037 host != NULL;
5038 host = strtok(NULL, " "))
5039 {
5040 /* set timeout */
5041 if (pmap->ph_timeout != 0)
5042 {
5043 if (setjmp(PHTimeout) != 0)
5044 {
5045 ev = NULL;
5046 if (LogLevel > 1)
5047 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5048 "timeout connecting to PH server %.100s",
5049 host);
5050 errno = ETIMEDOUT;
5051 goto ph_map_open_abort;
5052 }
5053 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5054 }
5055
5056 /* open connection to server */
5057 if (ph_open(&(pmap->ph), host,
5058 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
5059 ph_map_send_debug, ph_map_recv_debug
5060 #if NPH_VERSION >= 10200
5061 , NULL
5062 #endif
5063 ) == 0
5064 && ph_id(pmap->ph, phmap_id) == 0)
5065 {
5066 if (ev != NULL)
5067 sm_clrevent(ev);
5068 sm_free(hostlist); /* XXX */
5069 return true;
5070 }
5071
5072 ph_map_open_abort:
5073 save_errno = errno;
5074 if (ev != NULL)
5075 sm_clrevent(ev);
5076 pmap->ph_fastclose = PH_CLOSE_FAST;
5077 ph_map_close(map);
5078 errno = save_errno;
5079 }
5080
5081 if (bitset(MF_NODEFER, map->map_mflags))
5082 {
5083 if (errno == 0)
5084 errno = EAGAIN;
5085 syserr("ph_map_open: %s: cannot connect to PH server",
5086 map->map_mname);
5087 }
5088 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
5089 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5090 "ph_map_open: %s: cannot connect to PH server",
5091 map->map_mname);
5092 sm_free(hostlist); /* XXX */
5093 return false;
5094 }
5095
5096 /*
5097 ** PH_MAP_LOOKUP -- look up key from ph server
5098 */
5099
5100 char *
ph_map_lookup(map,key,args,pstat)5101 ph_map_lookup(map, key, args, pstat)
5102 MAP *map;
5103 char *key;
5104 char **args;
5105 int *pstat;
5106 {
5107 int i, save_errno = 0;
5108 register SM_EVENT *ev = NULL;
5109 PH_MAP_STRUCT *pmap;
5110 char *value = NULL;
5111
5112 pmap = (PH_MAP_STRUCT *)map->map_db1;
5113
5114 *pstat = EX_OK;
5115
5116 /* set timeout */
5117 if (pmap->ph_timeout != 0)
5118 {
5119 if (setjmp(PHTimeout) != 0)
5120 {
5121 ev = NULL;
5122 if (LogLevel > 1)
5123 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5124 "timeout during PH lookup of %.100s",
5125 key);
5126 errno = ETIMEDOUT;
5127 *pstat = EX_TEMPFAIL;
5128 goto ph_map_lookup_abort;
5129 }
5130 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5131 }
5132
5133 /* perform lookup */
5134 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
5135 if (i == -1)
5136 *pstat = EX_TEMPFAIL;
5137 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
5138 *pstat = EX_UNAVAILABLE;
5139
5140 ph_map_lookup_abort:
5141 if (ev != NULL)
5142 sm_clrevent(ev);
5143
5144 /*
5145 ** Close the connection if the timer popped
5146 ** or we got a temporary PH error
5147 */
5148
5149 if (*pstat == EX_TEMPFAIL)
5150 {
5151 save_errno = errno;
5152 pmap->ph_fastclose = PH_CLOSE_FAST;
5153 ph_map_close(map);
5154 errno = save_errno;
5155 }
5156
5157 if (*pstat == EX_OK)
5158 {
5159 if (tTd(38,20))
5160 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
5161
5162 if (bitset(MF_MATCHONLY, map->map_mflags))
5163 return map_rewrite(map, key, strlen(key), NULL);
5164 else
5165 return map_rewrite(map, value, strlen(value), args);
5166 }
5167
5168 return NULL;
5169 }
5170 #endif /* PH_MAP */
5171
5172 /*
5173 ** syslog map
5174 */
5175
5176 #define map_prio map_lockfd /* overload field */
5177
5178 /*
5179 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5180 */
5181
5182 bool
syslog_map_parseargs(map,args)5183 syslog_map_parseargs(map, args)
5184 MAP *map;
5185 char *args;
5186 {
5187 char *p = args;
5188 char *priority = NULL;
5189
5190 /* there is no check whether there is really an argument */
5191 while (*p != '\0')
5192 {
5193 while (isascii(*p) && isspace(*p))
5194 p++;
5195 if (*p != '-')
5196 break;
5197 ++p;
5198 if (*p == 'D')
5199 {
5200 map->map_mflags |= MF_DEFER;
5201 ++p;
5202 }
5203 else if (*p == 'S')
5204 {
5205 map->map_spacesub = *++p;
5206 if (*p != '\0')
5207 p++;
5208 }
5209 else if (*p == 'L')
5210 {
5211 while (*++p != '\0' && isascii(*p) && isspace(*p))
5212 continue;
5213 if (*p == '\0')
5214 break;
5215 priority = p;
5216 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5217 p++;
5218 if (*p != '\0')
5219 *p++ = '\0';
5220 }
5221 else
5222 {
5223 syserr("Illegal option %c map syslog", *p);
5224 ++p;
5225 }
5226 }
5227
5228 if (priority == NULL)
5229 map->map_prio = LOG_INFO;
5230 else
5231 {
5232 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5233 priority += 4;
5234
5235 #ifdef LOG_EMERG
5236 if (sm_strcasecmp("EMERG", priority) == 0)
5237 map->map_prio = LOG_EMERG;
5238 else
5239 #endif /* LOG_EMERG */
5240 #ifdef LOG_ALERT
5241 if (sm_strcasecmp("ALERT", priority) == 0)
5242 map->map_prio = LOG_ALERT;
5243 else
5244 #endif /* LOG_ALERT */
5245 #ifdef LOG_CRIT
5246 if (sm_strcasecmp("CRIT", priority) == 0)
5247 map->map_prio = LOG_CRIT;
5248 else
5249 #endif /* LOG_CRIT */
5250 #ifdef LOG_ERR
5251 if (sm_strcasecmp("ERR", priority) == 0)
5252 map->map_prio = LOG_ERR;
5253 else
5254 #endif /* LOG_ERR */
5255 #ifdef LOG_WARNING
5256 if (sm_strcasecmp("WARNING", priority) == 0)
5257 map->map_prio = LOG_WARNING;
5258 else
5259 #endif /* LOG_WARNING */
5260 #ifdef LOG_NOTICE
5261 if (sm_strcasecmp("NOTICE", priority) == 0)
5262 map->map_prio = LOG_NOTICE;
5263 else
5264 #endif /* LOG_NOTICE */
5265 #ifdef LOG_INFO
5266 if (sm_strcasecmp("INFO", priority) == 0)
5267 map->map_prio = LOG_INFO;
5268 else
5269 #endif /* LOG_INFO */
5270 #ifdef LOG_DEBUG
5271 if (sm_strcasecmp("DEBUG", priority) == 0)
5272 map->map_prio = LOG_DEBUG;
5273 else
5274 #endif /* LOG_DEBUG */
5275 {
5276 syserr("syslog_map_parseargs: Unknown priority %s",
5277 priority);
5278 return false;
5279 }
5280 }
5281 return true;
5282 }
5283
5284 /*
5285 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5286 */
5287
5288 char *
syslog_map_lookup(map,string,args,statp)5289 syslog_map_lookup(map, string, args, statp)
5290 MAP *map;
5291 char *string;
5292 char **args;
5293 int *statp;
5294 {
5295 char *ptr = map_rewrite(map, string, strlen(string), args);
5296
5297 if (ptr != NULL)
5298 {
5299 if (tTd(38, 20))
5300 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5301 map->map_mname, map->map_prio, ptr);
5302
5303 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5304 }
5305
5306 *statp = EX_OK;
5307 return "";
5308 }
5309
5310 #if _FFR_DPRINTF_MAP
5311 /*
5312 ** dprintf map
5313 */
5314
5315 #define map_dbg_level map_lockfd /* overload field */
5316
5317 /*
5318 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
5319 */
5320
5321 bool
dprintf_map_parseargs(map,args)5322 dprintf_map_parseargs(map, args)
5323 MAP *map;
5324 char *args;
5325 {
5326 char *p = args;
5327 char *dbg_level = NULL;
5328
5329 /* there is no check whether there is really an argument */
5330 while (*p != '\0')
5331 {
5332 while (isascii(*p) && isspace(*p))
5333 p++;
5334 if (*p != '-')
5335 break;
5336 ++p;
5337 if (*p == 'D')
5338 {
5339 map->map_mflags |= MF_DEFER;
5340 ++p;
5341 }
5342 else if (*p == 'S')
5343 {
5344 map->map_spacesub = *++p;
5345 if (*p != '\0')
5346 p++;
5347 }
5348 else if (*p == 'd')
5349 {
5350 while (*++p != '\0' && isascii(*p) && isspace(*p))
5351 continue;
5352 if (*p == '\0')
5353 break;
5354 dbg_level = p;
5355 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5356 p++;
5357 if (*p != '\0')
5358 *p++ = '\0';
5359 }
5360 else
5361 {
5362 syserr("Illegal option %c map dprintf", *p);
5363 ++p;
5364 }
5365 }
5366
5367 if (dbg_level == NULL)
5368 map->map_dbg_level = 0;
5369 else
5370 {
5371 if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
5372 {
5373 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
5374 map->map_mname, map->map_file,
5375 dbg_level);
5376 return false;
5377 }
5378 map->map_dbg_level = atoi(dbg_level);
5379 }
5380 return true;
5381 }
5382
5383 /*
5384 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
5385 */
5386
5387 char *
dprintf_map_lookup(map,string,args,statp)5388 dprintf_map_lookup(map, string, args, statp)
5389 MAP *map;
5390 char *string;
5391 char **args;
5392 int *statp;
5393 {
5394 char *ptr = map_rewrite(map, string, strlen(string), args);
5395
5396 if (ptr != NULL && tTd(85, map->map_dbg_level))
5397 sm_dprintf("%s\n", ptr);
5398 *statp = EX_OK;
5399 return "";
5400 }
5401 #endif /* _FFR_DPRINTF_MAP */
5402
5403 /*
5404 ** HESIOD Modules
5405 */
5406
5407 #if HESIOD
5408
5409 bool
hes_map_open(map,mode)5410 hes_map_open(map, mode)
5411 MAP *map;
5412 int mode;
5413 {
5414 if (tTd(38, 2))
5415 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5416 map->map_mname, map->map_file, mode);
5417
5418 if (mode != O_RDONLY)
5419 {
5420 /* issue a pseudo-error message */
5421 errno = SM_EMAPCANTWRITE;
5422 return false;
5423 }
5424
5425 # ifdef HESIOD_INIT
5426 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5427 return true;
5428
5429 if (!bitset(MF_OPTIONAL, map->map_mflags))
5430 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5431 sm_errstring(errno));
5432 return false;
5433 # else /* HESIOD_INIT */
5434 if (hes_error() == HES_ER_UNINIT)
5435 hes_init();
5436 switch (hes_error())
5437 {
5438 case HES_ER_OK:
5439 case HES_ER_NOTFOUND:
5440 return true;
5441 }
5442
5443 if (!bitset(MF_OPTIONAL, map->map_mflags))
5444 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5445
5446 return false;
5447 # endif /* HESIOD_INIT */
5448 }
5449
5450 char *
hes_map_lookup(map,name,av,statp)5451 hes_map_lookup(map, name, av, statp)
5452 MAP *map;
5453 char *name;
5454 char **av;
5455 int *statp;
5456 {
5457 char **hp;
5458
5459 if (tTd(38, 20))
5460 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5461
5462 if (name[0] == '\\')
5463 {
5464 char *np;
5465 int nl;
5466 int save_errno;
5467 char nbuf[MAXNAME];
5468
5469 nl = strlen(name);
5470 if (nl < sizeof(nbuf) - 1)
5471 np = nbuf;
5472 else
5473 np = xalloc(strlen(name) + 2);
5474 np[0] = '\\';
5475 (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
5476 # ifdef HESIOD_INIT
5477 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5478 # else /* HESIOD_INIT */
5479 hp = hes_resolve(np, map->map_file);
5480 # endif /* HESIOD_INIT */
5481 save_errno = errno;
5482 if (np != nbuf)
5483 sm_free(np); /* XXX */
5484 errno = save_errno;
5485 }
5486 else
5487 {
5488 # ifdef HESIOD_INIT
5489 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5490 # else /* HESIOD_INIT */
5491 hp = hes_resolve(name, map->map_file);
5492 # endif /* HESIOD_INIT */
5493 }
5494 # ifdef HESIOD_INIT
5495 if (hp == NULL || *hp == NULL)
5496 {
5497 switch (errno)
5498 {
5499 case ENOENT:
5500 *statp = EX_NOTFOUND;
5501 break;
5502 case ECONNREFUSED:
5503 *statp = EX_TEMPFAIL;
5504 break;
5505 case EMSGSIZE:
5506 case ENOMEM:
5507 default:
5508 *statp = EX_UNAVAILABLE;
5509 break;
5510 }
5511 if (hp != NULL)
5512 hesiod_free_list(HesiodContext, hp);
5513 return NULL;
5514 }
5515 # else /* HESIOD_INIT */
5516 if (hp == NULL || hp[0] == NULL)
5517 {
5518 switch (hes_error())
5519 {
5520 case HES_ER_OK:
5521 *statp = EX_OK;
5522 break;
5523
5524 case HES_ER_NOTFOUND:
5525 *statp = EX_NOTFOUND;
5526 break;
5527
5528 case HES_ER_CONFIG:
5529 *statp = EX_UNAVAILABLE;
5530 break;
5531
5532 case HES_ER_NET:
5533 *statp = EX_TEMPFAIL;
5534 break;
5535 }
5536 return NULL;
5537 }
5538 # endif /* HESIOD_INIT */
5539
5540 if (bitset(MF_MATCHONLY, map->map_mflags))
5541 return map_rewrite(map, name, strlen(name), NULL);
5542 else
5543 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5544 }
5545
5546 /*
5547 ** HES_MAP_CLOSE -- free the Hesiod context
5548 */
5549
5550 void
hes_map_close(map)5551 hes_map_close(map)
5552 MAP *map;
5553 {
5554 if (tTd(38, 20))
5555 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5556
5557 # ifdef HESIOD_INIT
5558 /* Free the hesiod context */
5559 if (HesiodContext != NULL)
5560 {
5561 hesiod_end(HesiodContext);
5562 HesiodContext = NULL;
5563 }
5564 # endif /* HESIOD_INIT */
5565 }
5566
5567 #endif /* HESIOD */
5568 /*
5569 ** NeXT NETINFO Modules
5570 */
5571
5572 #if NETINFO
5573
5574 # define NETINFO_DEFAULT_DIR "/aliases"
5575 # define NETINFO_DEFAULT_PROPERTY "members"
5576
5577 /*
5578 ** NI_MAP_OPEN -- open NetInfo Aliases
5579 */
5580
5581 bool
ni_map_open(map,mode)5582 ni_map_open(map, mode)
5583 MAP *map;
5584 int mode;
5585 {
5586 if (tTd(38, 2))
5587 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5588 map->map_mname, map->map_file, mode);
5589 mode &= O_ACCMODE;
5590
5591 if (*map->map_file == '\0')
5592 map->map_file = NETINFO_DEFAULT_DIR;
5593
5594 if (map->map_valcolnm == NULL)
5595 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5596
5597 if (map->map_coldelim == '\0')
5598 {
5599 if (bitset(MF_ALIAS, map->map_mflags))
5600 map->map_coldelim = ',';
5601 else if (bitset(MF_FILECLASS, map->map_mflags))
5602 map->map_coldelim = ' ';
5603 }
5604 return true;
5605 }
5606
5607
5608 /*
5609 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5610 */
5611
5612 char *
ni_map_lookup(map,name,av,statp)5613 ni_map_lookup(map, name, av, statp)
5614 MAP *map;
5615 char *name;
5616 char **av;
5617 int *statp;
5618 {
5619 char *res;
5620 char *propval;
5621
5622 if (tTd(38, 20))
5623 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5624
5625 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5626 map->map_valcolnm, map->map_coldelim);
5627
5628 if (propval == NULL)
5629 return NULL;
5630
5631 SM_TRY
5632 if (bitset(MF_MATCHONLY, map->map_mflags))
5633 res = map_rewrite(map, name, strlen(name), NULL);
5634 else
5635 res = map_rewrite(map, propval, strlen(propval), av);
5636 SM_FINALLY
5637 sm_free(propval);
5638 SM_END_TRY
5639 return res;
5640 }
5641
5642
5643 static bool
ni_getcanonname(name,hbsize,statp)5644 ni_getcanonname(name, hbsize, statp)
5645 char *name;
5646 int hbsize;
5647 int *statp;
5648 {
5649 char *vptr;
5650 char *ptr;
5651 char nbuf[MAXNAME + 1];
5652
5653 if (tTd(38, 20))
5654 sm_dprintf("ni_getcanonname(%s)\n", name);
5655
5656 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5657 {
5658 *statp = EX_UNAVAILABLE;
5659 return false;
5660 }
5661 (void) shorten_hostname(nbuf);
5662
5663 /* we only accept single token search key */
5664 if (strchr(nbuf, '.'))
5665 {
5666 *statp = EX_NOHOST;
5667 return false;
5668 }
5669
5670 /* Do the search */
5671 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5672
5673 if (vptr == NULL)
5674 {
5675 *statp = EX_NOHOST;
5676 return false;
5677 }
5678
5679 /* Only want the first machine name */
5680 if ((ptr = strchr(vptr, '\n')) != NULL)
5681 *ptr = '\0';
5682
5683 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5684 {
5685 sm_free(vptr);
5686 *statp = EX_UNAVAILABLE;
5687 return true;
5688 }
5689 sm_free(vptr);
5690 *statp = EX_OK;
5691 return false;
5692 }
5693 #endif /* NETINFO */
5694 /*
5695 ** TEXT (unindexed text file) Modules
5696 **
5697 ** This code donated by Sun Microsystems.
5698 */
5699
5700 #define map_sff map_lockfd /* overload field */
5701
5702
5703 /*
5704 ** TEXT_MAP_OPEN -- open text table
5705 */
5706
5707 bool
text_map_open(map,mode)5708 text_map_open(map, mode)
5709 MAP *map;
5710 int mode;
5711 {
5712 long sff;
5713 int i;
5714
5715 if (tTd(38, 2))
5716 sm_dprintf("text_map_open(%s, %s, %d)\n",
5717 map->map_mname, map->map_file, mode);
5718
5719 mode &= O_ACCMODE;
5720 if (mode != O_RDONLY)
5721 {
5722 errno = EPERM;
5723 return false;
5724 }
5725
5726 if (*map->map_file == '\0')
5727 {
5728 syserr("text map \"%s\": file name required",
5729 map->map_mname);
5730 return false;
5731 }
5732
5733 if (map->map_file[0] != '/')
5734 {
5735 syserr("text map \"%s\": file name must be fully qualified",
5736 map->map_mname);
5737 return false;
5738 }
5739
5740 sff = SFF_ROOTOK|SFF_REGONLY;
5741 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5742 sff |= SFF_NOWLINK;
5743 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5744 sff |= SFF_SAFEDIRPATH;
5745 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5746 sff, S_IRUSR, NULL)) != 0)
5747 {
5748 int save_errno = errno;
5749
5750 /* cannot open this map */
5751 if (tTd(38, 2))
5752 sm_dprintf("\tunsafe map file: %d\n", i);
5753 errno = save_errno;
5754 if (!bitset(MF_OPTIONAL, map->map_mflags))
5755 syserr("text map \"%s\": unsafe map file %s",
5756 map->map_mname, map->map_file);
5757 return false;
5758 }
5759
5760 if (map->map_keycolnm == NULL)
5761 map->map_keycolno = 0;
5762 else
5763 {
5764 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5765 {
5766 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5767 map->map_mname, map->map_file,
5768 map->map_keycolnm);
5769 return false;
5770 }
5771 map->map_keycolno = atoi(map->map_keycolnm);
5772 }
5773
5774 if (map->map_valcolnm == NULL)
5775 map->map_valcolno = 0;
5776 else
5777 {
5778 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5779 {
5780 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5781 map->map_mname, map->map_file,
5782 map->map_valcolnm);
5783 return false;
5784 }
5785 map->map_valcolno = atoi(map->map_valcolnm);
5786 }
5787
5788 if (tTd(38, 2))
5789 {
5790 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5791 map->map_mname, map->map_file);
5792 if (map->map_coldelim == '\0')
5793 sm_dprintf("(white space)\n");
5794 else
5795 sm_dprintf("%c\n", map->map_coldelim);
5796 }
5797
5798 map->map_sff = sff;
5799 return true;
5800 }
5801
5802
5803 /*
5804 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5805 */
5806
5807 char *
text_map_lookup(map,name,av,statp)5808 text_map_lookup(map, name, av, statp)
5809 MAP *map;
5810 char *name;
5811 char **av;
5812 int *statp;
5813 {
5814 char *vp;
5815 auto int vsize;
5816 int buflen;
5817 SM_FILE_T *f;
5818 char delim;
5819 int key_idx;
5820 bool found_it;
5821 long sff = map->map_sff;
5822 char search_key[MAXNAME + 1];
5823 char linebuf[MAXLINE];
5824 char buf[MAXNAME + 1];
5825
5826 found_it = false;
5827 if (tTd(38, 20))
5828 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5829
5830 buflen = strlen(name);
5831 if (buflen > sizeof(search_key) - 1)
5832 buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
5833 memmove(search_key, name, buflen);
5834 search_key[buflen] = '\0';
5835 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5836 makelower(search_key);
5837
5838 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5839 if (f == NULL)
5840 {
5841 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5842 *statp = EX_UNAVAILABLE;
5843 return NULL;
5844 }
5845 key_idx = map->map_keycolno;
5846 delim = map->map_coldelim;
5847 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5848 linebuf, sizeof(linebuf)) >= 0)
5849 {
5850 char *p;
5851
5852 /* skip comment line */
5853 if (linebuf[0] == '#')
5854 continue;
5855 p = strchr(linebuf, '\n');
5856 if (p != NULL)
5857 *p = '\0';
5858 p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
5859 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5860 {
5861 found_it = true;
5862 break;
5863 }
5864 }
5865 (void) sm_io_close(f, SM_TIME_DEFAULT);
5866 if (!found_it)
5867 {
5868 *statp = EX_NOTFOUND;
5869 return NULL;
5870 }
5871 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
5872 if (vp == NULL)
5873 {
5874 *statp = EX_NOTFOUND;
5875 return NULL;
5876 }
5877 vsize = strlen(vp);
5878 *statp = EX_OK;
5879 if (bitset(MF_MATCHONLY, map->map_mflags))
5880 return map_rewrite(map, name, strlen(name), NULL);
5881 else
5882 return map_rewrite(map, vp, vsize, av);
5883 }
5884
5885 /*
5886 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5887 */
5888
5889 static bool
text_getcanonname(name,hbsize,statp)5890 text_getcanonname(name, hbsize, statp)
5891 char *name;
5892 int hbsize;
5893 int *statp;
5894 {
5895 bool found;
5896 char *dot;
5897 SM_FILE_T *f;
5898 char linebuf[MAXLINE];
5899 char cbuf[MAXNAME + 1];
5900 char nbuf[MAXNAME + 1];
5901
5902 if (tTd(38, 20))
5903 sm_dprintf("text_getcanonname(%s)\n", name);
5904
5905 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5906 {
5907 *statp = EX_UNAVAILABLE;
5908 return false;
5909 }
5910 dot = shorten_hostname(nbuf);
5911
5912 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5913 NULL);
5914 if (f == NULL)
5915 {
5916 *statp = EX_UNAVAILABLE;
5917 return false;
5918 }
5919 found = false;
5920 while (!found &&
5921 sm_io_fgets(f, SM_TIME_DEFAULT,
5922 linebuf, sizeof(linebuf)) >= 0)
5923 {
5924 char *p = strpbrk(linebuf, "#\n");
5925
5926 if (p != NULL)
5927 *p = '\0';
5928 if (linebuf[0] != '\0')
5929 found = extract_canonname(nbuf, dot, linebuf,
5930 cbuf, sizeof(cbuf));
5931 }
5932 (void) sm_io_close(f, SM_TIME_DEFAULT);
5933 if (!found)
5934 {
5935 *statp = EX_NOHOST;
5936 return false;
5937 }
5938
5939 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5940 {
5941 *statp = EX_UNAVAILABLE;
5942 return false;
5943 }
5944 *statp = EX_OK;
5945 return true;
5946 }
5947 /*
5948 ** STAB (Symbol Table) Modules
5949 */
5950
5951
5952 /*
5953 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5954 */
5955
5956 /* ARGSUSED2 */
5957 char *
stab_map_lookup(map,name,av,pstat)5958 stab_map_lookup(map, name, av, pstat)
5959 register MAP *map;
5960 char *name;
5961 char **av;
5962 int *pstat;
5963 {
5964 register STAB *s;
5965
5966 if (tTd(38, 20))
5967 sm_dprintf("stab_lookup(%s, %s)\n",
5968 map->map_mname, name);
5969
5970 s = stab(name, ST_ALIAS, ST_FIND);
5971 if (s == NULL)
5972 return NULL;
5973 if (bitset(MF_MATCHONLY, map->map_mflags))
5974 return map_rewrite(map, name, strlen(name), NULL);
5975 else
5976 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5977 }
5978
5979 /*
5980 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5981 */
5982
5983 void
stab_map_store(map,lhs,rhs)5984 stab_map_store(map, lhs, rhs)
5985 register MAP *map;
5986 char *lhs;
5987 char *rhs;
5988 {
5989 register STAB *s;
5990
5991 s = stab(lhs, ST_ALIAS, ST_ENTER);
5992 s->s_alias = newstr(rhs);
5993 }
5994
5995
5996 /*
5997 ** STAB_MAP_OPEN -- initialize (reads data file)
5998 **
5999 ** This is a weird case -- it is only intended as a fallback for
6000 ** aliases. For this reason, opens for write (only during a
6001 ** "newaliases") always fails, and opens for read open the
6002 ** actual underlying text file instead of the database.
6003 */
6004
6005 bool
stab_map_open(map,mode)6006 stab_map_open(map, mode)
6007 register MAP *map;
6008 int mode;
6009 {
6010 SM_FILE_T *af;
6011 long sff;
6012 struct stat st;
6013
6014 if (tTd(38, 2))
6015 sm_dprintf("stab_map_open(%s, %s, %d)\n",
6016 map->map_mname, map->map_file, mode);
6017
6018 mode &= O_ACCMODE;
6019 if (mode != O_RDONLY)
6020 {
6021 errno = EPERM;
6022 return false;
6023 }
6024
6025 sff = SFF_ROOTOK|SFF_REGONLY;
6026 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
6027 sff |= SFF_NOWLINK;
6028 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
6029 sff |= SFF_SAFEDIRPATH;
6030 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
6031 if (af == NULL)
6032 return false;
6033 readaliases(map, af, false, false);
6034
6035 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
6036 map->map_mtime = st.st_mtime;
6037 (void) sm_io_close(af, SM_TIME_DEFAULT);
6038
6039 return true;
6040 }
6041 /*
6042 ** Implicit Modules
6043 **
6044 ** Tries several types. For back compatibility of aliases.
6045 */
6046
6047
6048 /*
6049 ** IMPL_MAP_LOOKUP -- lookup in best open database
6050 */
6051
6052 char *
impl_map_lookup(map,name,av,pstat)6053 impl_map_lookup(map, name, av, pstat)
6054 MAP *map;
6055 char *name;
6056 char **av;
6057 int *pstat;
6058 {
6059 if (tTd(38, 20))
6060 sm_dprintf("impl_map_lookup(%s, %s)\n",
6061 map->map_mname, name);
6062
6063 #if NEWDB
6064 if (bitset(MF_IMPL_HASH, map->map_mflags))
6065 return db_map_lookup(map, name, av, pstat);
6066 #endif /* NEWDB */
6067 #if NDBM
6068 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6069 return ndbm_map_lookup(map, name, av, pstat);
6070 #endif /* NDBM */
6071 return stab_map_lookup(map, name, av, pstat);
6072 }
6073
6074 /*
6075 ** IMPL_MAP_STORE -- store in open databases
6076 */
6077
6078 void
impl_map_store(map,lhs,rhs)6079 impl_map_store(map, lhs, rhs)
6080 MAP *map;
6081 char *lhs;
6082 char *rhs;
6083 {
6084 if (tTd(38, 12))
6085 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6086 map->map_mname, lhs, rhs);
6087 #if NEWDB
6088 if (bitset(MF_IMPL_HASH, map->map_mflags))
6089 db_map_store(map, lhs, rhs);
6090 #endif /* NEWDB */
6091 #if NDBM
6092 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6093 ndbm_map_store(map, lhs, rhs);
6094 #endif /* NDBM */
6095 stab_map_store(map, lhs, rhs);
6096 }
6097
6098 /*
6099 ** IMPL_MAP_OPEN -- implicit database open
6100 */
6101
6102 bool
impl_map_open(map,mode)6103 impl_map_open(map, mode)
6104 MAP *map;
6105 int mode;
6106 {
6107 if (tTd(38, 2))
6108 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6109 map->map_mname, map->map_file, mode);
6110
6111 mode &= O_ACCMODE;
6112 #if NEWDB
6113 map->map_mflags |= MF_IMPL_HASH;
6114 if (hash_map_open(map, mode))
6115 {
6116 # ifdef NDBM_YP_COMPAT
6117 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6118 # endif /* NDBM_YP_COMPAT */
6119 return true;
6120 }
6121 else
6122 map->map_mflags &= ~MF_IMPL_HASH;
6123 #endif /* NEWDB */
6124 #if NDBM
6125 map->map_mflags |= MF_IMPL_NDBM;
6126 if (ndbm_map_open(map, mode))
6127 {
6128 return true;
6129 }
6130 else
6131 map->map_mflags &= ~MF_IMPL_NDBM;
6132 #endif /* NDBM */
6133
6134 #if defined(NEWDB) || defined(NDBM)
6135 if (Verbose)
6136 message("WARNING: cannot open alias database %s%s",
6137 map->map_file,
6138 mode == O_RDONLY ? "; reading text version" : "");
6139 #else /* defined(NEWDB) || defined(NDBM) */
6140 if (mode != O_RDONLY)
6141 usrerr("Cannot rebuild aliases: no database format defined");
6142 #endif /* defined(NEWDB) || defined(NDBM) */
6143
6144 if (mode == O_RDONLY)
6145 return stab_map_open(map, mode);
6146 else
6147 return false;
6148 }
6149
6150
6151 /*
6152 ** IMPL_MAP_CLOSE -- close any open database(s)
6153 */
6154
6155 void
impl_map_close(map)6156 impl_map_close(map)
6157 MAP *map;
6158 {
6159 if (tTd(38, 9))
6160 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6161 map->map_mname, map->map_file, map->map_mflags);
6162 #if NEWDB
6163 if (bitset(MF_IMPL_HASH, map->map_mflags))
6164 {
6165 db_map_close(map);
6166 map->map_mflags &= ~MF_IMPL_HASH;
6167 }
6168 #endif /* NEWDB */
6169
6170 #if NDBM
6171 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6172 {
6173 ndbm_map_close(map);
6174 map->map_mflags &= ~MF_IMPL_NDBM;
6175 }
6176 #endif /* NDBM */
6177 }
6178 /*
6179 ** User map class.
6180 **
6181 ** Provides access to the system password file.
6182 */
6183
6184 /*
6185 ** USER_MAP_OPEN -- open user map
6186 **
6187 ** Really just binds field names to field numbers.
6188 */
6189
6190 bool
user_map_open(map,mode)6191 user_map_open(map, mode)
6192 MAP *map;
6193 int mode;
6194 {
6195 if (tTd(38, 2))
6196 sm_dprintf("user_map_open(%s, %d)\n",
6197 map->map_mname, mode);
6198
6199 mode &= O_ACCMODE;
6200 if (mode != O_RDONLY)
6201 {
6202 /* issue a pseudo-error message */
6203 errno = SM_EMAPCANTWRITE;
6204 return false;
6205 }
6206 if (map->map_valcolnm == NULL)
6207 /* EMPTY */
6208 /* nothing */ ;
6209 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
6210 map->map_valcolno = 1;
6211 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
6212 map->map_valcolno = 2;
6213 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
6214 map->map_valcolno = 3;
6215 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
6216 map->map_valcolno = 4;
6217 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
6218 map->map_valcolno = 5;
6219 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
6220 map->map_valcolno = 6;
6221 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
6222 map->map_valcolno = 7;
6223 else
6224 {
6225 syserr("User map %s: unknown column name %s",
6226 map->map_mname, map->map_valcolnm);
6227 return false;
6228 }
6229 return true;
6230 }
6231
6232
6233 /*
6234 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6235 */
6236
6237 /* ARGSUSED3 */
6238 char *
user_map_lookup(map,key,av,statp)6239 user_map_lookup(map, key, av, statp)
6240 MAP *map;
6241 char *key;
6242 char **av;
6243 int *statp;
6244 {
6245 auto bool fuzzy;
6246 SM_MBDB_T user;
6247
6248 if (tTd(38, 20))
6249 sm_dprintf("user_map_lookup(%s, %s)\n",
6250 map->map_mname, key);
6251
6252 *statp = finduser(key, &fuzzy, &user);
6253 if (*statp != EX_OK)
6254 return NULL;
6255 if (bitset(MF_MATCHONLY, map->map_mflags))
6256 return map_rewrite(map, key, strlen(key), NULL);
6257 else
6258 {
6259 char *rwval = NULL;
6260 char buf[30];
6261
6262 switch (map->map_valcolno)
6263 {
6264 case 0:
6265 case 1:
6266 rwval = user.mbdb_name;
6267 break;
6268
6269 case 2:
6270 rwval = "x"; /* passwd no longer supported */
6271 break;
6272
6273 case 3:
6274 (void) sm_snprintf(buf, sizeof(buf), "%d",
6275 (int) user.mbdb_uid);
6276 rwval = buf;
6277 break;
6278
6279 case 4:
6280 (void) sm_snprintf(buf, sizeof(buf), "%d",
6281 (int) user.mbdb_gid);
6282 rwval = buf;
6283 break;
6284
6285 case 5:
6286 rwval = user.mbdb_fullname;
6287 break;
6288
6289 case 6:
6290 rwval = user.mbdb_homedir;
6291 break;
6292
6293 case 7:
6294 rwval = user.mbdb_shell;
6295 break;
6296 default:
6297 syserr("user_map %s: bogus field %d",
6298 map->map_mname, map->map_valcolno);
6299 return NULL;
6300 }
6301 return map_rewrite(map, rwval, strlen(rwval), av);
6302 }
6303 }
6304 /*
6305 ** Program map type.
6306 **
6307 ** This provides access to arbitrary programs. It should be used
6308 ** only very sparingly, since there is no way to bound the cost
6309 ** of invoking an arbitrary program.
6310 */
6311
6312 char *
prog_map_lookup(map,name,av,statp)6313 prog_map_lookup(map, name, av, statp)
6314 MAP *map;
6315 char *name;
6316 char **av;
6317 int *statp;
6318 {
6319 int i;
6320 int save_errno;
6321 int fd;
6322 int status;
6323 auto pid_t pid;
6324 register char *p;
6325 char *rval;
6326 char *argv[MAXPV + 1];
6327 char buf[MAXLINE];
6328
6329 if (tTd(38, 20))
6330 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6331 map->map_mname, name, map->map_file);
6332
6333 i = 0;
6334 argv[i++] = map->map_file;
6335 if (map->map_rebuild != NULL)
6336 {
6337 (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
6338 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6339 {
6340 if (i >= MAXPV - 1)
6341 break;
6342 argv[i++] = p;
6343 }
6344 }
6345 argv[i++] = name;
6346 argv[i] = NULL;
6347 if (tTd(38, 21))
6348 {
6349 sm_dprintf("prog_open:");
6350 for (i = 0; argv[i] != NULL; i++)
6351 sm_dprintf(" %s", argv[i]);
6352 sm_dprintf("\n");
6353 }
6354 (void) sm_blocksignal(SIGCHLD);
6355 pid = prog_open(argv, &fd, CurEnv);
6356 if (pid < 0)
6357 {
6358 if (!bitset(MF_OPTIONAL, map->map_mflags))
6359 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6360 map->map_mname, sm_errstring(errno));
6361 else if (tTd(38, 9))
6362 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6363 map->map_mname, sm_errstring(errno));
6364 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6365 *statp = EX_OSFILE;
6366 return NULL;
6367 }
6368 i = read(fd, buf, sizeof(buf) - 1);
6369 if (i < 0)
6370 {
6371 syserr("prog_map_lookup(%s): read error %s",
6372 map->map_mname, sm_errstring(errno));
6373 rval = NULL;
6374 }
6375 else if (i == 0)
6376 {
6377 if (tTd(38, 20))
6378 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6379 map->map_mname);
6380 rval = NULL;
6381 }
6382 else
6383 {
6384 buf[i] = '\0';
6385 p = strchr(buf, '\n');
6386 if (p != NULL)
6387 *p = '\0';
6388
6389 /* collect the return value */
6390 if (bitset(MF_MATCHONLY, map->map_mflags))
6391 rval = map_rewrite(map, name, strlen(name), NULL);
6392 else
6393 rval = map_rewrite(map, buf, strlen(buf), av);
6394
6395 /* now flush any additional output */
6396 while ((i = read(fd, buf, sizeof(buf))) > 0)
6397 continue;
6398 }
6399
6400 /* wait for the process to terminate */
6401 (void) close(fd);
6402 status = waitfor(pid);
6403 save_errno = errno;
6404 (void) sm_releasesignal(SIGCHLD);
6405 errno = save_errno;
6406
6407 if (status == -1)
6408 {
6409 syserr("prog_map_lookup(%s): wait error %s",
6410 map->map_mname, sm_errstring(errno));
6411 *statp = EX_SOFTWARE;
6412 rval = NULL;
6413 }
6414 else if (WIFEXITED(status))
6415 {
6416 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6417 rval = NULL;
6418 }
6419 else
6420 {
6421 syserr("prog_map_lookup(%s): child died on signal %d",
6422 map->map_mname, status);
6423 *statp = EX_UNAVAILABLE;
6424 rval = NULL;
6425 }
6426 return rval;
6427 }
6428 /*
6429 ** Sequenced map type.
6430 **
6431 ** Tries each map in order until something matches, much like
6432 ** implicit. Stores go to the first map in the list that can
6433 ** support storing.
6434 **
6435 ** This is slightly unusual in that there are two interfaces.
6436 ** The "sequence" interface lets you stack maps arbitrarily.
6437 ** The "switch" interface builds a sequence map by looking
6438 ** at a system-dependent configuration file such as
6439 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6440 **
6441 ** We don't need an explicit open, since all maps are
6442 ** opened on demand.
6443 */
6444
6445 /*
6446 ** SEQ_MAP_PARSE -- Sequenced map parsing
6447 */
6448
6449 bool
seq_map_parse(map,ap)6450 seq_map_parse(map, ap)
6451 MAP *map;
6452 char *ap;
6453 {
6454 int maxmap;
6455
6456 if (tTd(38, 2))
6457 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6458 maxmap = 0;
6459 while (*ap != '\0')
6460 {
6461 register char *p;
6462 STAB *s;
6463
6464 /* find beginning of map name */
6465 while (isascii(*ap) && isspace(*ap))
6466 ap++;
6467 for (p = ap;
6468 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6469 p++)
6470 continue;
6471 if (*p != '\0')
6472 *p++ = '\0';
6473 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6474 p++;
6475 if (*ap == '\0')
6476 {
6477 ap = p;
6478 continue;
6479 }
6480 s = stab(ap, ST_MAP, ST_FIND);
6481 if (s == NULL)
6482 {
6483 syserr("Sequence map %s: unknown member map %s",
6484 map->map_mname, ap);
6485 }
6486 else if (maxmap >= MAXMAPSTACK)
6487 {
6488 syserr("Sequence map %s: too many member maps (%d max)",
6489 map->map_mname, MAXMAPSTACK);
6490 maxmap++;
6491 }
6492 else if (maxmap < MAXMAPSTACK)
6493 {
6494 map->map_stack[maxmap++] = &s->s_map;
6495 }
6496 ap = p;
6497 }
6498 return true;
6499 }
6500
6501 /*
6502 ** SWITCH_MAP_OPEN -- open a switched map
6503 **
6504 ** This looks at the system-dependent configuration and builds
6505 ** a sequence map that does the same thing.
6506 **
6507 ** Every system must define a switch_map_find routine in conf.c
6508 ** that will return the list of service types associated with a
6509 ** given service class.
6510 */
6511
6512 bool
switch_map_open(map,mode)6513 switch_map_open(map, mode)
6514 MAP *map;
6515 int mode;
6516 {
6517 int mapno;
6518 int nmaps;
6519 char *maptype[MAXMAPSTACK];
6520
6521 if (tTd(38, 2))
6522 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6523 map->map_mname, map->map_file, mode);
6524
6525 mode &= O_ACCMODE;
6526 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6527 if (tTd(38, 19))
6528 {
6529 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6530 for (mapno = 0; mapno < nmaps; mapno++)
6531 sm_dprintf("\t\t%s\n", maptype[mapno]);
6532 }
6533 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6534 return false;
6535
6536 for (mapno = 0; mapno < nmaps; mapno++)
6537 {
6538 register STAB *s;
6539 char nbuf[MAXNAME + 1];
6540
6541 if (maptype[mapno] == NULL)
6542 continue;
6543 (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
6544 map->map_mname, ".", maptype[mapno]);
6545 s = stab(nbuf, ST_MAP, ST_FIND);
6546 if (s == NULL)
6547 {
6548 syserr("Switch map %s: unknown member map %s",
6549 map->map_mname, nbuf);
6550 }
6551 else
6552 {
6553 map->map_stack[mapno] = &s->s_map;
6554 if (tTd(38, 4))
6555 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6556 mapno,
6557 s->s_map.map_class->map_cname,
6558 nbuf);
6559 }
6560 }
6561 return true;
6562 }
6563
6564 #if 0
6565 /*
6566 ** SEQ_MAP_CLOSE -- close all underlying maps
6567 */
6568
6569 void
6570 seq_map_close(map)
6571 MAP *map;
6572 {
6573 int mapno;
6574
6575 if (tTd(38, 9))
6576 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6577
6578 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6579 {
6580 MAP *mm = map->map_stack[mapno];
6581
6582 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6583 continue;
6584 mm->map_mflags |= MF_CLOSING;
6585 mm->map_class->map_close(mm);
6586 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6587 }
6588 }
6589 #endif /* 0 */
6590
6591 /*
6592 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6593 */
6594
6595 char *
seq_map_lookup(map,key,args,pstat)6596 seq_map_lookup(map, key, args, pstat)
6597 MAP *map;
6598 char *key;
6599 char **args;
6600 int *pstat;
6601 {
6602 int mapno;
6603 int mapbit = 0x01;
6604 bool tempfail = false;
6605
6606 if (tTd(38, 20))
6607 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6608
6609 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6610 {
6611 MAP *mm = map->map_stack[mapno];
6612 char *rv;
6613
6614 if (mm == NULL)
6615 continue;
6616 if (!bitset(MF_OPEN, mm->map_mflags) &&
6617 !openmap(mm))
6618 {
6619 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6620 {
6621 *pstat = EX_UNAVAILABLE;
6622 return NULL;
6623 }
6624 continue;
6625 }
6626 *pstat = EX_OK;
6627 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6628 if (rv != NULL)
6629 return rv;
6630 if (*pstat == EX_TEMPFAIL)
6631 {
6632 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6633 return NULL;
6634 tempfail = true;
6635 }
6636 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6637 break;
6638 }
6639 if (tempfail)
6640 *pstat = EX_TEMPFAIL;
6641 else if (*pstat == EX_OK)
6642 *pstat = EX_NOTFOUND;
6643 return NULL;
6644 }
6645
6646 /*
6647 ** SEQ_MAP_STORE -- sequenced map store
6648 */
6649
6650 void
seq_map_store(map,key,val)6651 seq_map_store(map, key, val)
6652 MAP *map;
6653 char *key;
6654 char *val;
6655 {
6656 int mapno;
6657
6658 if (tTd(38, 12))
6659 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6660 map->map_mname, key, val);
6661
6662 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6663 {
6664 MAP *mm = map->map_stack[mapno];
6665
6666 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6667 continue;
6668
6669 mm->map_class->map_store(mm, key, val);
6670 return;
6671 }
6672 syserr("seq_map_store(%s, %s, %s): no writable map",
6673 map->map_mname, key, val);
6674 }
6675 /*
6676 ** NULL stubs
6677 */
6678
6679 /* ARGSUSED */
6680 bool
null_map_open(map,mode)6681 null_map_open(map, mode)
6682 MAP *map;
6683 int mode;
6684 {
6685 return true;
6686 }
6687
6688 /* ARGSUSED */
6689 void
null_map_close(map)6690 null_map_close(map)
6691 MAP *map;
6692 {
6693 return;
6694 }
6695
6696 char *
null_map_lookup(map,key,args,pstat)6697 null_map_lookup(map, key, args, pstat)
6698 MAP *map;
6699 char *key;
6700 char **args;
6701 int *pstat;
6702 {
6703 *pstat = EX_NOTFOUND;
6704 return NULL;
6705 }
6706
6707 /* ARGSUSED */
6708 void
null_map_store(map,key,val)6709 null_map_store(map, key, val)
6710 MAP *map;
6711 char *key;
6712 char *val;
6713 {
6714 return;
6715 }
6716
6717 MAPCLASS NullMapClass =
6718 {
6719 "null-map", NULL, 0,
6720 NULL, null_map_lookup, null_map_store,
6721 null_map_open, null_map_close,
6722 };
6723
6724 /*
6725 ** BOGUS stubs
6726 */
6727
6728 char *
bogus_map_lookup(map,key,args,pstat)6729 bogus_map_lookup(map, key, args, pstat)
6730 MAP *map;
6731 char *key;
6732 char **args;
6733 int *pstat;
6734 {
6735 *pstat = EX_TEMPFAIL;
6736 return NULL;
6737 }
6738
6739 MAPCLASS BogusMapClass =
6740 {
6741 "bogus-map", NULL, 0,
6742 NULL, bogus_map_lookup, null_map_store,
6743 null_map_open, null_map_close,
6744 };
6745 /*
6746 ** MACRO modules
6747 */
6748
6749 char *
macro_map_lookup(map,name,av,statp)6750 macro_map_lookup(map, name, av, statp)
6751 MAP *map;
6752 char *name;
6753 char **av;
6754 int *statp;
6755 {
6756 int mid;
6757
6758 if (tTd(38, 20))
6759 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6760 name == NULL ? "NULL" : name);
6761
6762 if (name == NULL ||
6763 *name == '\0' ||
6764 (mid = macid(name)) == 0)
6765 {
6766 *statp = EX_CONFIG;
6767 return NULL;
6768 }
6769
6770 if (av[1] == NULL)
6771 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6772 else
6773 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6774
6775 *statp = EX_OK;
6776 return "";
6777 }
6778 /*
6779 ** REGEX modules
6780 */
6781
6782 #if MAP_REGEX
6783
6784 # include <regex.h>
6785
6786 # define DEFAULT_DELIM CONDELSE
6787 # define END_OF_FIELDS -1
6788 # define ERRBUF_SIZE 80
6789 # define MAX_MATCH 32
6790
6791 # define xnalloc(s) memset(xalloc(s), '\0', s);
6792
6793 struct regex_map
6794 {
6795 regex_t *regex_pattern_buf; /* xalloc it */
6796 int *regex_subfields; /* move to type MAP */
6797 char *regex_delim; /* move to type MAP */
6798 };
6799
6800 static int parse_fields __P((char *, int *, int, int));
6801 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6802
6803 static int
parse_fields(s,ibuf,blen,nr_substrings)6804 parse_fields(s, ibuf, blen, nr_substrings)
6805 char *s;
6806 int *ibuf; /* array */
6807 int blen; /* number of elements in ibuf */
6808 int nr_substrings; /* number of substrings in the pattern */
6809 {
6810 register char *cp;
6811 int i = 0;
6812 bool lastone = false;
6813
6814 blen--; /* for terminating END_OF_FIELDS */
6815 cp = s;
6816 do
6817 {
6818 for (;; cp++)
6819 {
6820 if (*cp == ',')
6821 {
6822 *cp = '\0';
6823 break;
6824 }
6825 if (*cp == '\0')
6826 {
6827 lastone = true;
6828 break;
6829 }
6830 }
6831 if (i < blen)
6832 {
6833 int val = atoi(s);
6834
6835 if (val < 0 || val >= nr_substrings)
6836 {
6837 syserr("field (%d) out of range, only %d substrings in pattern",
6838 val, nr_substrings);
6839 return -1;
6840 }
6841 ibuf[i++] = val;
6842 }
6843 else
6844 {
6845 syserr("too many fields, %d max", blen);
6846 return -1;
6847 }
6848 s = ++cp;
6849 } while (!lastone);
6850 ibuf[i] = END_OF_FIELDS;
6851 return i;
6852 }
6853
6854 bool
regex_map_init(map,ap)6855 regex_map_init(map, ap)
6856 MAP *map;
6857 char *ap;
6858 {
6859 int regerr;
6860 struct regex_map *map_p;
6861 register char *p;
6862 char *sub_param = NULL;
6863 int pflags;
6864 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6865
6866 if (tTd(38, 2))
6867 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6868 map->map_mname, ap);
6869
6870 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6871 p = ap;
6872 map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
6873 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6874
6875 for (;;)
6876 {
6877 while (isascii(*p) && isspace(*p))
6878 p++;
6879 if (*p != '-')
6880 break;
6881 switch (*++p)
6882 {
6883 case 'n': /* not */
6884 map->map_mflags |= MF_REGEX_NOT;
6885 break;
6886
6887 case 'f': /* case sensitive */
6888 map->map_mflags |= MF_NOFOLDCASE;
6889 pflags &= ~REG_ICASE;
6890 break;
6891
6892 case 'b': /* basic regular expressions */
6893 pflags &= ~REG_EXTENDED;
6894 break;
6895
6896 case 's': /* substring match () syntax */
6897 sub_param = ++p;
6898 pflags &= ~REG_NOSUB;
6899 break;
6900
6901 case 'd': /* delimiter */
6902 map_p->regex_delim = ++p;
6903 break;
6904
6905 case 'a': /* map append */
6906 map->map_app = ++p;
6907 break;
6908
6909 case 'm': /* matchonly */
6910 map->map_mflags |= MF_MATCHONLY;
6911 break;
6912
6913 case 'q':
6914 map->map_mflags |= MF_KEEPQUOTES;
6915 break;
6916
6917 case 'S':
6918 map->map_spacesub = *++p;
6919 break;
6920
6921 case 'D':
6922 map->map_mflags |= MF_DEFER;
6923 break;
6924
6925 }
6926 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6927 p++;
6928 if (*p != '\0')
6929 *p++ = '\0';
6930 }
6931 if (tTd(38, 3))
6932 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6933
6934 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6935 {
6936 /* Errorhandling */
6937 char errbuf[ERRBUF_SIZE];
6938
6939 (void) regerror(regerr, map_p->regex_pattern_buf,
6940 errbuf, sizeof(errbuf));
6941 syserr("pattern-compile-error: %s", errbuf);
6942 sm_free(map_p->regex_pattern_buf); /* XXX */
6943 sm_free(map_p); /* XXX */
6944 return false;
6945 }
6946
6947 if (map->map_app != NULL)
6948 map->map_app = newstr(map->map_app);
6949 if (map_p->regex_delim != NULL)
6950 map_p->regex_delim = newstr(map_p->regex_delim);
6951 else
6952 map_p->regex_delim = defdstr;
6953
6954 if (!bitset(REG_NOSUB, pflags))
6955 {
6956 /* substring matching */
6957 int substrings;
6958 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6959
6960 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6961
6962 if (tTd(38, 3))
6963 sm_dprintf("regex_map_init: nr of substrings %d\n",
6964 substrings);
6965
6966 if (substrings >= MAX_MATCH)
6967 {
6968 syserr("too many substrings, %d max", MAX_MATCH);
6969 sm_free(map_p->regex_pattern_buf); /* XXX */
6970 sm_free(map_p); /* XXX */
6971 return false;
6972 }
6973 if (sub_param != NULL && sub_param[0] != '\0')
6974 {
6975 /* optional parameter -sfields */
6976 if (parse_fields(sub_param, fields,
6977 MAX_MATCH + 1, substrings) == -1)
6978 return false;
6979 }
6980 else
6981 {
6982 int i;
6983
6984 /* set default fields */
6985 for (i = 0; i < substrings; i++)
6986 fields[i] = i;
6987 fields[i] = END_OF_FIELDS;
6988 }
6989 map_p->regex_subfields = fields;
6990 if (tTd(38, 3))
6991 {
6992 int *ip;
6993
6994 sm_dprintf("regex_map_init: subfields");
6995 for (ip = fields; *ip != END_OF_FIELDS; ip++)
6996 sm_dprintf(" %d", *ip);
6997 sm_dprintf("\n");
6998 }
6999 }
7000 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
7001 return true;
7002 }
7003
7004 static char *
regex_map_rewrite(map,s,slen,av)7005 regex_map_rewrite(map, s, slen, av)
7006 MAP *map;
7007 const char *s;
7008 size_t slen;
7009 char **av;
7010 {
7011 if (bitset(MF_MATCHONLY, map->map_mflags))
7012 return map_rewrite(map, av[0], strlen(av[0]), NULL);
7013 else
7014 return map_rewrite(map, s, slen, av);
7015 }
7016
7017 char *
regex_map_lookup(map,name,av,statp)7018 regex_map_lookup(map, name, av, statp)
7019 MAP *map;
7020 char *name;
7021 char **av;
7022 int *statp;
7023 {
7024 int reg_res;
7025 struct regex_map *map_p;
7026 regmatch_t pmatch[MAX_MATCH];
7027
7028 if (tTd(38, 20))
7029 {
7030 char **cpp;
7031
7032 sm_dprintf("regex_map_lookup: key '%s'\n", name);
7033 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7034 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
7035 }
7036
7037 map_p = (struct regex_map *)(map->map_db1);
7038 reg_res = regexec(map_p->regex_pattern_buf,
7039 name, MAX_MATCH, pmatch, 0);
7040
7041 if (bitset(MF_REGEX_NOT, map->map_mflags))
7042 {
7043 /* option -n */
7044 if (reg_res == REG_NOMATCH)
7045 return regex_map_rewrite(map, "", (size_t) 0, av);
7046 else
7047 return NULL;
7048 }
7049 if (reg_res == REG_NOMATCH)
7050 return NULL;
7051
7052 if (map_p->regex_subfields != NULL)
7053 {
7054 /* option -s */
7055 static char retbuf[MAXNAME];
7056 int fields[MAX_MATCH + 1];
7057 bool first = true;
7058 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
7059 bool quotemode = false, bslashmode = false;
7060 register char *dp, *sp;
7061 char *endp, *ldp;
7062 int *ip;
7063
7064 dp = retbuf;
7065 ldp = retbuf + sizeof(retbuf) - 1;
7066
7067 if (av[1] != NULL)
7068 {
7069 if (parse_fields(av[1], fields, MAX_MATCH + 1,
7070 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
7071 {
7072 *statp = EX_CONFIG;
7073 return NULL;
7074 }
7075 ip = fields;
7076 }
7077 else
7078 ip = map_p->regex_subfields;
7079
7080 for ( ; *ip != END_OF_FIELDS; ip++)
7081 {
7082 if (!first)
7083 {
7084 for (sp = map_p->regex_delim; *sp; sp++)
7085 {
7086 if (dp < ldp)
7087 *dp++ = *sp;
7088 }
7089 }
7090 else
7091 first = false;
7092
7093 if (*ip >= MAX_MATCH ||
7094 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7095 continue;
7096
7097 sp = name + pmatch[*ip].rm_so;
7098 endp = name + pmatch[*ip].rm_eo;
7099 for (; endp > sp; sp++)
7100 {
7101 if (dp < ldp)
7102 {
7103 if (bslashmode)
7104 {
7105 *dp++ = *sp;
7106 bslashmode = false;
7107 }
7108 else if (quotemode && *sp != '"' &&
7109 *sp != '\\')
7110 {
7111 *dp++ = *sp;
7112 }
7113 else switch (*dp++ = *sp)
7114 {
7115 case '\\':
7116 bslashmode = true;
7117 break;
7118
7119 case '(':
7120 cmntcnt++;
7121 break;
7122
7123 case ')':
7124 cmntcnt--;
7125 break;
7126
7127 case '<':
7128 anglecnt++;
7129 break;
7130
7131 case '>':
7132 anglecnt--;
7133 break;
7134
7135 case ' ':
7136 spacecnt++;
7137 break;
7138
7139 case '"':
7140 quotemode = !quotemode;
7141 break;
7142 }
7143 }
7144 }
7145 }
7146 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7147 bslashmode || spacecnt != 0)
7148 {
7149 sm_syslog(LOG_WARNING, NOQID,
7150 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7151 map->map_mname, name);
7152 return NULL;
7153 }
7154
7155 *dp = '\0';
7156
7157 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7158 }
7159 return regex_map_rewrite(map, "", (size_t)0, av);
7160 }
7161 #endif /* MAP_REGEX */
7162 /*
7163 ** NSD modules
7164 */
7165 #if MAP_NSD
7166
7167 # include <ndbm.h>
7168 # define _DATUM_DEFINED
7169 # include <ns_api.h>
7170
7171 typedef struct ns_map_list
7172 {
7173 ns_map_t *map; /* XXX ns_ ? */
7174 char *mapname;
7175 struct ns_map_list *next;
7176 } ns_map_list_t;
7177
7178 static ns_map_t *
ns_map_t_find(mapname)7179 ns_map_t_find(mapname)
7180 char *mapname;
7181 {
7182 static ns_map_list_t *ns_maps = NULL;
7183 ns_map_list_t *ns_map;
7184
7185 /* walk the list of maps looking for the correctly named map */
7186 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7187 {
7188 if (strcmp(ns_map->mapname, mapname) == 0)
7189 break;
7190 }
7191
7192 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7193 if (ns_map == NULL)
7194 {
7195 ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
7196 ns_map->mapname = newstr(mapname);
7197 ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
7198 memset(ns_map->map, '\0', sizeof(*ns_map->map));
7199 ns_map->next = ns_maps;
7200 ns_maps = ns_map;
7201 }
7202 return ns_map->map;
7203 }
7204
7205 char *
nsd_map_lookup(map,name,av,statp)7206 nsd_map_lookup(map, name, av, statp)
7207 MAP *map;
7208 char *name;
7209 char **av;
7210 int *statp;
7211 {
7212 int buflen, r;
7213 char *p;
7214 ns_map_t *ns_map;
7215 char keybuf[MAXNAME + 1];
7216 char buf[MAXLINE];
7217
7218 if (tTd(38, 20))
7219 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7220
7221 buflen = strlen(name);
7222 if (buflen > sizeof(keybuf) - 1)
7223 buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
7224 memmove(keybuf, name, buflen);
7225 keybuf[buflen] = '\0';
7226 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7227 makelower(keybuf);
7228
7229 ns_map = ns_map_t_find(map->map_file);
7230 if (ns_map == NULL)
7231 {
7232 if (tTd(38, 20))
7233 sm_dprintf("nsd_map_t_find failed\n");
7234 *statp = EX_UNAVAILABLE;
7235 return NULL;
7236 }
7237 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
7238 buf, sizeof(buf));
7239 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7240 {
7241 *statp = EX_TEMPFAIL;
7242 return NULL;
7243 }
7244 if (r == NS_BADREQ
7245 # ifdef NS_NOPERM
7246 || r == NS_NOPERM
7247 # endif /* NS_NOPERM */
7248 )
7249 {
7250 *statp = EX_CONFIG;
7251 return NULL;
7252 }
7253 if (r != NS_SUCCESS)
7254 {
7255 *statp = EX_NOTFOUND;
7256 return NULL;
7257 }
7258
7259 *statp = EX_OK;
7260
7261 /* Null out trailing \n */
7262 if ((p = strchr(buf, '\n')) != NULL)
7263 *p = '\0';
7264
7265 return map_rewrite(map, buf, strlen(buf), av);
7266 }
7267 #endif /* MAP_NSD */
7268
7269 char *
arith_map_lookup(map,name,av,statp)7270 arith_map_lookup(map, name, av, statp)
7271 MAP *map;
7272 char *name;
7273 char **av;
7274 int *statp;
7275 {
7276 long r;
7277 long v[2];
7278 bool res = false;
7279 bool boolres;
7280 static char result[16];
7281 char **cpp;
7282
7283 if (tTd(38, 2))
7284 {
7285 sm_dprintf("arith_map_lookup: key '%s'\n", name);
7286 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7287 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7288 }
7289 r = 0;
7290 boolres = false;
7291 cpp = av;
7292 *statp = EX_OK;
7293
7294 /*
7295 ** read arguments for arith map
7296 ** - no check is made whether they are really numbers
7297 ** - just ignores args after the second
7298 */
7299
7300 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7301 v[r++] = strtol(*cpp, NULL, 0);
7302
7303 /* operator and (at least) two operands given? */
7304 if (name != NULL && r == 2)
7305 {
7306 switch (*name)
7307 {
7308 case '|':
7309 r = v[0] | v[1];
7310 break;
7311
7312 case '&':
7313 r = v[0] & v[1];
7314 break;
7315
7316 case '%':
7317 if (v[1] == 0)
7318 return NULL;
7319 r = v[0] % v[1];
7320 break;
7321 case '+':
7322 r = v[0] + v[1];
7323 break;
7324
7325 case '-':
7326 r = v[0] - v[1];
7327 break;
7328
7329 case '*':
7330 r = v[0] * v[1];
7331 break;
7332
7333 case '/':
7334 if (v[1] == 0)
7335 return NULL;
7336 r = v[0] / v[1];
7337 break;
7338
7339 case 'l':
7340 res = v[0] < v[1];
7341 boolres = true;
7342 break;
7343
7344 case '=':
7345 res = v[0] == v[1];
7346 boolres = true;
7347 break;
7348
7349 case 'r':
7350 r = v[1] - v[0] + 1;
7351 if (r <= 0)
7352 return NULL;
7353 r = get_random() % r + v[0];
7354 break;
7355
7356 default:
7357 /* XXX */
7358 *statp = EX_CONFIG;
7359 if (LogLevel > 10)
7360 sm_syslog(LOG_WARNING, NOQID,
7361 "arith_map: unknown operator %c",
7362 (isascii(*name) && isprint(*name)) ?
7363 *name : '?');
7364 return NULL;
7365 }
7366 if (boolres)
7367 (void) sm_snprintf(result, sizeof(result),
7368 res ? "TRUE" : "FALSE");
7369 else
7370 (void) sm_snprintf(result, sizeof(result), "%ld", r);
7371 return result;
7372 }
7373 *statp = EX_CONFIG;
7374 return NULL;
7375 }
7376
7377 #if _FFR_ARPA_MAP
7378 char *
arpa_map_lookup(map,name,av,statp)7379 arpa_map_lookup(map, name, av, statp)
7380 MAP *map;
7381 char *name;
7382 char **av;
7383 int *statp;
7384 {
7385 int r;
7386 char *rval;
7387 char result[128]; /* IPv6: 64 + 10 + 1 would be enough */
7388
7389 if (tTd(38, 2))
7390 sm_dprintf("arpa_map_lookup: key '%s'\n", name);
7391 *statp = EX_DATAERR;
7392 r = 1;
7393 memset(result, '\0', sizeof(result));
7394 rval = NULL;
7395
7396 # if NETINET6
7397 if (sm_strncasecmp(name, "IPv6:", 5) == 0)
7398 {
7399 struct in6_addr in6_addr;
7400
7401 r = anynet_pton(AF_INET6, name, &in6_addr);
7402 if (r == 1)
7403 {
7404 static char hex_digits[] =
7405 { '0', '1', '2', '3', '4', '5', '6', '7', '8',
7406 '9', 'a', 'b', 'c', 'd', 'e', 'f' };
7407
7408 unsigned char *src;
7409 char *dst;
7410 int i;
7411
7412 src = (unsigned char *) &in6_addr;
7413 dst = result;
7414 for (i = 15; i >= 0; i--) {
7415 *dst++ = hex_digits[src[i] & 0x0f];
7416 *dst++ = '.';
7417 *dst++ = hex_digits[(src[i] >> 4) & 0x0f];
7418 if (i > 0)
7419 *dst++ = '.';
7420 }
7421 *statp = EX_OK;
7422 }
7423 }
7424 else
7425 # endif /* NETINET6 */
7426 # if NETINET
7427 {
7428 struct in_addr in_addr;
7429
7430 r = anynet_pton(AF_INET, name, &in_addr);
7431 if (r == 1)
7432 {
7433 unsigned char *src;
7434
7435 src = (unsigned char *) &in_addr;
7436 (void) snprintf(result, sizeof(result),
7437 "%u.%u.%u.%u",
7438 src[3], src[2], src[1], src[0]);
7439 *statp = EX_OK;
7440 }
7441 }
7442 # endif /* NETINET */
7443 if (r < 0)
7444 *statp = EX_UNAVAILABLE;
7445 if (tTd(38, 2))
7446 sm_dprintf("arpa_map_lookup: r=%d, result='%s'\n", r, result);
7447 if (*statp == EX_OK)
7448 {
7449 if (bitset(MF_MATCHONLY, map->map_mflags))
7450 rval = map_rewrite(map, name, strlen(name), NULL);
7451 else
7452 rval = map_rewrite(map, result, strlen(result), av);
7453 }
7454 return rval;
7455 }
7456 #endif /* _FFR_ARPA_MAP */
7457
7458 #if SOCKETMAP
7459
7460 # if NETINET || NETINET6
7461 # include <arpa/inet.h>
7462 # endif /* NETINET || NETINET6 */
7463
7464 # define socket_map_next map_stack[0]
7465
7466 /*
7467 ** SOCKET_MAP_OPEN -- open socket table
7468 */
7469
7470 bool
socket_map_open(map,mode)7471 socket_map_open(map, mode)
7472 MAP *map;
7473 int mode;
7474 {
7475 STAB *s;
7476 int sock = 0;
7477 SOCKADDR_LEN_T addrlen = 0;
7478 int addrno = 0;
7479 int save_errno;
7480 char *p;
7481 char *colon;
7482 char *at;
7483 struct hostent *hp = NULL;
7484 SOCKADDR addr;
7485
7486 if (tTd(38, 2))
7487 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7488 map->map_mname, map->map_file, mode);
7489
7490 mode &= O_ACCMODE;
7491
7492 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7493 if (mode != O_RDONLY)
7494 {
7495 /* issue a pseudo-error message */
7496 errno = SM_EMAPCANTWRITE;
7497 return false;
7498 }
7499
7500 if (*map->map_file == '\0')
7501 {
7502 syserr("socket map \"%s\": empty or missing socket information",
7503 map->map_mname);
7504 return false;
7505 }
7506
7507 s = socket_map_findconn(map->map_file);
7508 if (s->s_socketmap != NULL)
7509 {
7510 /* Copy open connection */
7511 map->map_db1 = s->s_socketmap->map_db1;
7512
7513 /* Add this map as head of linked list */
7514 map->socket_map_next = s->s_socketmap;
7515 s->s_socketmap = map;
7516
7517 if (tTd(38, 2))
7518 sm_dprintf("using cached connection\n");
7519 return true;
7520 }
7521
7522 if (tTd(38, 2))
7523 sm_dprintf("opening new connection\n");
7524
7525 /* following code is ripped from milter.c */
7526 /* XXX It should be put in a library... */
7527
7528 /* protocol:filename or protocol:port@host */
7529 memset(&addr, '\0', sizeof(addr));
7530 p = map->map_file;
7531 colon = strchr(p, ':');
7532 if (colon != NULL)
7533 {
7534 *colon = '\0';
7535
7536 if (*p == '\0')
7537 {
7538 # if NETUNIX
7539 /* default to AF_UNIX */
7540 addr.sa.sa_family = AF_UNIX;
7541 # else /* NETUNIX */
7542 # if NETINET
7543 /* default to AF_INET */
7544 addr.sa.sa_family = AF_INET;
7545 # else /* NETINET */
7546 # if NETINET6
7547 /* default to AF_INET6 */
7548 addr.sa.sa_family = AF_INET6;
7549 # else /* NETINET6 */
7550 /* no protocols available */
7551 syserr("socket map \"%s\": no valid socket protocols available",
7552 map->map_mname);
7553 return false;
7554 # endif /* NETINET6 */
7555 # endif /* NETINET */
7556 # endif /* NETUNIX */
7557 }
7558 # if NETUNIX
7559 else if (sm_strcasecmp(p, "unix") == 0 ||
7560 sm_strcasecmp(p, "local") == 0)
7561 addr.sa.sa_family = AF_UNIX;
7562 # endif /* NETUNIX */
7563 # if NETINET
7564 else if (sm_strcasecmp(p, "inet") == 0)
7565 addr.sa.sa_family = AF_INET;
7566 # endif /* NETINET */
7567 # if NETINET6
7568 else if (sm_strcasecmp(p, "inet6") == 0)
7569 addr.sa.sa_family = AF_INET6;
7570 # endif /* NETINET6 */
7571 else
7572 {
7573 # ifdef EPROTONOSUPPORT
7574 errno = EPROTONOSUPPORT;
7575 # else /* EPROTONOSUPPORT */
7576 errno = EINVAL;
7577 # endif /* EPROTONOSUPPORT */
7578 syserr("socket map \"%s\": unknown socket type %s",
7579 map->map_mname, p);
7580 return false;
7581 }
7582 *colon++ = ':';
7583 }
7584 else
7585 {
7586 colon = p;
7587 #if NETUNIX
7588 /* default to AF_UNIX */
7589 addr.sa.sa_family = AF_UNIX;
7590 #else /* NETUNIX */
7591 # if NETINET
7592 /* default to AF_INET */
7593 addr.sa.sa_family = AF_INET;
7594 # else /* NETINET */
7595 # if NETINET6
7596 /* default to AF_INET6 */
7597 addr.sa.sa_family = AF_INET6;
7598 # else /* NETINET6 */
7599 syserr("socket map \"%s\": unknown socket type %s",
7600 map->map_mname, p);
7601 return false;
7602 # endif /* NETINET6 */
7603 # endif /* NETINET */
7604 #endif /* NETUNIX */
7605 }
7606
7607 # if NETUNIX
7608 if (addr.sa.sa_family == AF_UNIX)
7609 {
7610 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7611
7612 at = colon;
7613 if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7614 {
7615 syserr("socket map \"%s\": local socket name %s too long",
7616 map->map_mname, colon);
7617 return false;
7618 }
7619 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7620 S_IRUSR|S_IWUSR, NULL);
7621
7622 if (errno != 0)
7623 {
7624 /* if not safe, don't create */
7625 syserr("socket map \"%s\": local socket name %s unsafe",
7626 map->map_mname, colon);
7627 return false;
7628 }
7629
7630 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7631 sizeof(addr.sunix.sun_path));
7632 addrlen = sizeof(struct sockaddr_un);
7633 }
7634 else
7635 # endif /* NETUNIX */
7636 # if NETINET || NETINET6
7637 if (false
7638 # if NETINET
7639 || addr.sa.sa_family == AF_INET
7640 # endif /* NETINET */
7641 # if NETINET6
7642 || addr.sa.sa_family == AF_INET6
7643 # endif /* NETINET6 */
7644 )
7645 {
7646 unsigned short port;
7647
7648 /* Parse port@host */
7649 at = strchr(colon, '@');
7650 if (at == NULL)
7651 {
7652 syserr("socket map \"%s\": bad address %s (expected port@host)",
7653 map->map_mname, colon);
7654 return false;
7655 }
7656 *at = '\0';
7657 if (isascii(*colon) && isdigit(*colon))
7658 port = htons((unsigned short) atoi(colon));
7659 else
7660 {
7661 # ifdef NO_GETSERVBYNAME
7662 syserr("socket map \"%s\": invalid port number %s",
7663 map->map_mname, colon);
7664 return false;
7665 # else /* NO_GETSERVBYNAME */
7666 register struct servent *sp;
7667
7668 sp = getservbyname(colon, "tcp");
7669 if (sp == NULL)
7670 {
7671 syserr("socket map \"%s\": unknown port name %s",
7672 map->map_mname, colon);
7673 return false;
7674 }
7675 port = sp->s_port;
7676 # endif /* NO_GETSERVBYNAME */
7677 }
7678 *at++ = '@';
7679 if (*at == '[')
7680 {
7681 char *end;
7682
7683 end = strchr(at, ']');
7684 if (end != NULL)
7685 {
7686 bool found = false;
7687 # if NETINET
7688 unsigned long hid = INADDR_NONE;
7689 # endif /* NETINET */
7690 # if NETINET6
7691 struct sockaddr_in6 hid6;
7692 # endif /* NETINET6 */
7693
7694 *end = '\0';
7695 # if NETINET
7696 if (addr.sa.sa_family == AF_INET &&
7697 (hid = inet_addr(&at[1])) != INADDR_NONE)
7698 {
7699 addr.sin.sin_addr.s_addr = hid;
7700 addr.sin.sin_port = port;
7701 found = true;
7702 }
7703 # endif /* NETINET */
7704 # if NETINET6
7705 (void) memset(&hid6, '\0', sizeof(hid6));
7706 if (addr.sa.sa_family == AF_INET6 &&
7707 anynet_pton(AF_INET6, &at[1],
7708 &hid6.sin6_addr) == 1)
7709 {
7710 addr.sin6.sin6_addr = hid6.sin6_addr;
7711 addr.sin6.sin6_port = port;
7712 found = true;
7713 }
7714 # endif /* NETINET6 */
7715 *end = ']';
7716 if (!found)
7717 {
7718 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7719 map->map_mname, at);
7720 return false;
7721 }
7722 }
7723 else
7724 {
7725 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7726 map->map_mname, at);
7727 return false;
7728 }
7729 }
7730 else
7731 {
7732 hp = sm_gethostbyname(at, addr.sa.sa_family);
7733 if (hp == NULL)
7734 {
7735 syserr("socket map \"%s\": Unknown host name %s",
7736 map->map_mname, at);
7737 return false;
7738 }
7739 addr.sa.sa_family = hp->h_addrtype;
7740 switch (hp->h_addrtype)
7741 {
7742 # if NETINET
7743 case AF_INET:
7744 memmove(&addr.sin.sin_addr,
7745 hp->h_addr, INADDRSZ);
7746 addr.sin.sin_port = port;
7747 addrlen = sizeof(struct sockaddr_in);
7748 addrno = 1;
7749 break;
7750 # endif /* NETINET */
7751
7752 # if NETINET6
7753 case AF_INET6:
7754 memmove(&addr.sin6.sin6_addr,
7755 hp->h_addr, IN6ADDRSZ);
7756 addr.sin6.sin6_port = port;
7757 addrlen = sizeof(struct sockaddr_in6);
7758 addrno = 1;
7759 break;
7760 # endif /* NETINET6 */
7761
7762 default:
7763 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7764 map->map_mname, at, hp->h_addrtype);
7765 # if NETINET6
7766 freehostent(hp);
7767 # endif /* NETINET6 */
7768 return false;
7769 }
7770 }
7771 }
7772 else
7773 # endif /* NETINET || NETINET6 */
7774 {
7775 syserr("socket map \"%s\": unknown socket protocol",
7776 map->map_mname);
7777 return false;
7778 }
7779
7780 /* nope, actually connecting */
7781 for (;;)
7782 {
7783 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7784 if (sock < 0)
7785 {
7786 save_errno = errno;
7787 if (tTd(38, 5))
7788 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7789 map->map_mname,
7790 sm_errstring(save_errno));
7791 # if NETINET6
7792 if (hp != NULL)
7793 freehostent(hp);
7794 # endif /* NETINET6 */
7795 return false;
7796 }
7797
7798 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7799 break;
7800
7801 /* couldn't connect.... try next address */
7802 save_errno = errno;
7803 p = CurHostName;
7804 CurHostName = at;
7805 if (tTd(38, 5))
7806 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7807 map->map_mname, at, sm_errstring(save_errno));
7808 CurHostName = p;
7809 (void) close(sock);
7810
7811 /* try next address */
7812 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7813 {
7814 switch (addr.sa.sa_family)
7815 {
7816 # if NETINET
7817 case AF_INET:
7818 memmove(&addr.sin.sin_addr,
7819 hp->h_addr_list[addrno++],
7820 INADDRSZ);
7821 break;
7822 # endif /* NETINET */
7823
7824 # if NETINET6
7825 case AF_INET6:
7826 memmove(&addr.sin6.sin6_addr,
7827 hp->h_addr_list[addrno++],
7828 IN6ADDRSZ);
7829 break;
7830 # endif /* NETINET6 */
7831
7832 default:
7833 if (tTd(38, 5))
7834 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7835 map->map_mname, at,
7836 hp->h_addrtype);
7837 # if NETINET6
7838 freehostent(hp);
7839 # endif /* NETINET6 */
7840 return false;
7841 }
7842 continue;
7843 }
7844 p = CurHostName;
7845 CurHostName = at;
7846 if (tTd(38, 5))
7847 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7848 map->map_mname, sm_errstring(save_errno));
7849 CurHostName = p;
7850 # if NETINET6
7851 if (hp != NULL)
7852 freehostent(hp);
7853 # endif /* NETINET6 */
7854 return false;
7855 }
7856 # if NETINET6
7857 if (hp != NULL)
7858 {
7859 freehostent(hp);
7860 hp = NULL;
7861 }
7862 # endif /* NETINET6 */
7863 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7864 SM_TIME_DEFAULT,
7865 (void *) &sock,
7866 SM_IO_RDWR,
7867 NULL)) == NULL)
7868 {
7869 close(sock);
7870 if (tTd(38, 2))
7871 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7872 map->map_mname, sm_errstring(errno));
7873 return false;
7874 }
7875
7876 /* Save connection for reuse */
7877 s->s_socketmap = map;
7878 return true;
7879 }
7880
7881 /*
7882 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7883 **
7884 ** Cache SOCKET connections based on the connection specifier
7885 ** and PID so we don't have multiple connections open to
7886 ** the same server for different maps. Need a separate connection
7887 ** per PID since a parent process may close the map before the
7888 ** child is done with it.
7889 **
7890 ** Parameters:
7891 ** conn -- SOCKET map connection specifier
7892 **
7893 ** Returns:
7894 ** Symbol table entry for the SOCKET connection.
7895 */
7896
7897 static STAB *
socket_map_findconn(conn)7898 socket_map_findconn(conn)
7899 const char *conn;
7900 {
7901 char *nbuf;
7902 STAB *SM_NONVOLATILE s = NULL;
7903
7904 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7905 SM_TRY
7906 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7907 SM_FINALLY
7908 sm_free(nbuf);
7909 SM_END_TRY
7910 return s;
7911 }
7912
7913 /*
7914 ** SOCKET_MAP_CLOSE -- close the socket
7915 */
7916
7917 void
socket_map_close(map)7918 socket_map_close(map)
7919 MAP *map;
7920 {
7921 STAB *s;
7922 MAP *smap;
7923
7924 if (tTd(38, 20))
7925 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7926 (long) CurrentPid);
7927
7928 /* Check if already closed */
7929 if (map->map_db1 == NULL)
7930 {
7931 if (tTd(38, 20))
7932 sm_dprintf("socket_map_close(%s) already closed\n",
7933 map->map_file);
7934 return;
7935 }
7936 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7937
7938 /* Mark all the maps that share the connection as closed */
7939 s = socket_map_findconn(map->map_file);
7940 smap = s->s_socketmap;
7941 while (smap != NULL)
7942 {
7943 MAP *next;
7944
7945 if (tTd(38, 2) && smap != map)
7946 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7947 map->map_mname, smap->map_mname);
7948
7949 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7950 smap->map_db1 = NULL;
7951 next = smap->socket_map_next;
7952 smap->socket_map_next = NULL;
7953 smap = next;
7954 }
7955 s->s_socketmap = NULL;
7956 }
7957
7958 /*
7959 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7960 */
7961
7962 char *
socket_map_lookup(map,name,av,statp)7963 socket_map_lookup(map, name, av, statp)
7964 MAP *map;
7965 char *name;
7966 char **av;
7967 int *statp;
7968 {
7969 unsigned int nettolen, replylen, recvlen;
7970 char *replybuf, *rval, *value, *status, *key;
7971 SM_FILE_T *f;
7972 char keybuf[MAXNAME + 1];
7973
7974 replybuf = NULL;
7975 rval = NULL;
7976 f = (SM_FILE_T *)map->map_db1;
7977 if (tTd(38, 20))
7978 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7979 map->map_mname, name, map->map_file);
7980
7981 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7982 {
7983 nettolen = strlen(name);
7984 if (nettolen > sizeof(keybuf) - 1)
7985 nettolen = sizeof(keybuf) - 1;
7986 memmove(keybuf, name, nettolen);
7987 keybuf[nettolen] = '\0';
7988 makelower(keybuf);
7989 key = keybuf;
7990 }
7991 else
7992 key = name;
7993
7994 nettolen = strlen(map->map_mname) + 1 + strlen(key);
7995 SM_ASSERT(nettolen > strlen(map->map_mname));
7996 SM_ASSERT(nettolen > strlen(key));
7997 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7998 nettolen, map->map_mname, key) == SM_IO_EOF) ||
7999 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
8000 (sm_io_error(f)))
8001 {
8002 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
8003 map->map_mname);
8004 *statp = EX_TEMPFAIL;
8005 goto errcl;
8006 }
8007
8008 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
8009 {
8010 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
8011 map->map_mname);
8012 *statp = EX_TEMPFAIL;
8013 goto errcl;
8014 }
8015 if (replylen > SOCKETMAP_MAXL)
8016 {
8017 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
8018 map->map_mname, replylen);
8019 *statp = EX_TEMPFAIL;
8020 goto errcl;
8021 }
8022 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
8023 {
8024 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
8025 map->map_mname);
8026 *statp = EX_TEMPFAIL;
8027 goto error;
8028 }
8029
8030 replybuf = (char *) sm_malloc(replylen + 1);
8031 if (replybuf == NULL)
8032 {
8033 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
8034 map->map_mname, replylen + 1);
8035 *statp = EX_OSERR;
8036 goto error;
8037 }
8038
8039 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
8040 if (recvlen < replylen)
8041 {
8042 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
8043 map->map_mname, recvlen, replylen);
8044 *statp = EX_TEMPFAIL;
8045 goto errcl;
8046 }
8047 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
8048 {
8049 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
8050 map->map_mname);
8051 *statp = EX_TEMPFAIL;
8052 goto errcl;
8053 }
8054 status = replybuf;
8055 replybuf[recvlen] = '\0';
8056 value = strchr(replybuf, ' ');
8057 if (value != NULL)
8058 {
8059 *value = '\0';
8060 value++;
8061 }
8062 if (strcmp(status, "OK") == 0)
8063 {
8064 *statp = EX_OK;
8065
8066 /* collect the return value */
8067 if (bitset(MF_MATCHONLY, map->map_mflags))
8068 rval = map_rewrite(map, key, strlen(key), NULL);
8069 else
8070 rval = map_rewrite(map, value, strlen(value), av);
8071 }
8072 else if (strcmp(status, "NOTFOUND") == 0)
8073 {
8074 *statp = EX_NOTFOUND;
8075 if (tTd(38, 20))
8076 sm_dprintf("socket_map_lookup(%s): %s not found\n",
8077 map->map_mname, key);
8078 }
8079 else
8080 {
8081 if (tTd(38, 5))
8082 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
8083 map->map_mname, key, status,
8084 value ? value : "");
8085 if ((strcmp(status, "TEMP") == 0) ||
8086 (strcmp(status, "TIMEOUT") == 0))
8087 *statp = EX_TEMPFAIL;
8088 else if(strcmp(status, "PERM") == 0)
8089 *statp = EX_UNAVAILABLE;
8090 else
8091 *statp = EX_PROTOCOL;
8092 }
8093
8094 if (replybuf != NULL)
8095 sm_free(replybuf);
8096 return rval;
8097
8098 errcl:
8099 socket_map_close(map);
8100 error:
8101 if (replybuf != NULL)
8102 sm_free(replybuf);
8103 return rval;
8104 }
8105 #endif /* SOCKETMAP */
8106