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