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