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