Deleted Added
full compact
map.c (141858) map.c (147078)
1/*
1/*
2 * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
2 * Copyright (c) 1998-2005 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#include <sendmail.h>
15
3 * All rights reserved.
4 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: map.c,v 8.666 2004/08/17 16:50:19 gshapiro Exp $")
16SM_RCSID("@(#)$Id: map.c,v 8.669 2005/02/09 01:46:35 ca Exp $")
17
18#if LDAPMAP
19# include <sm/ldap.h>
20#endif /* LDAPMAP */
21
22#if NDBM
23# include <ndbm.h>
24# ifdef R_FIRST
25 ERROR README: You are running the Berkeley DB version of ndbm.h. See
26 ERROR README: the README file about tweaking Berkeley DB so it can
27 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
28 ERROR README: and use -DNEWDB instead.
29# endif /* R_FIRST */
30#endif /* NDBM */
31#if NEWDB
32# include "sm/bdb.h"
33#endif /* NEWDB */
34#if NIS
35 struct dom_binding; /* forward reference needed on IRIX */
36# include <rpcsvc/ypclnt.h>
37# if NDBM
38# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
39# endif /* NDBM */
40#endif /* NIS */
41
42#if NEWDB
43# if DB_VERSION_MAJOR < 2
44static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
45# endif /* DB_VERSION_MAJOR < 2 */
46# if DB_VERSION_MAJOR == 2
47static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
48# endif /* DB_VERSION_MAJOR == 2 */
49# if DB_VERSION_MAJOR > 2
50static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
51# endif /* DB_VERSION_MAJOR > 2 */
52#endif /* NEWDB */
53static bool extract_canonname __P((char *, char *, char *, char[], int));
54static void map_close __P((STAB *, int));
55static void map_init __P((STAB *, int));
56#ifdef LDAPMAP
57static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
58#endif /* LDAPMAP */
59#if NISPLUS
60static bool nisplus_getcanonname __P((char *, int, int *));
61#endif /* NISPLUS */
62#if NIS
63static bool nis_getcanonname __P((char *, int, int *));
64#endif /* NIS */
65#if NETINFO
66static bool ni_getcanonname __P((char *, int, int *));
67#endif /* NETINFO */
68static bool text_getcanonname __P((char *, int, int *));
69#if SOCKETMAP
70static STAB *socket_map_findconn __P((const char*));
71
72/* XXX arbitrary limit for sanity */
73# define SOCKETMAP_MAXL 1000000
74#endif /* SOCKETMAP */
75
76/* default error message for trying to open a map in write mode */
77#ifdef ENOSYS
78# define SM_EMAPCANTWRITE ENOSYS
79#else /* ENOSYS */
80# ifdef EFTYPE
81# define SM_EMAPCANTWRITE EFTYPE
82# else /* EFTYPE */
83# define SM_EMAPCANTWRITE ENXIO
84# endif /* EFTYPE */
85#endif /* ENOSYS */
86
87/*
88** MAP.C -- implementations for various map classes.
89**
90** Each map class implements a series of functions:
91**
92** bool map_parse(MAP *map, char *args)
93** Parse the arguments from the config file. Return true
94** if they were ok, false otherwise. Fill in map with the
95** values.
96**
97** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
98** Look up the key in the given map. If found, do any
99** rewriting the map wants (including "args" if desired)
100** and return the value. Set *pstat to the appropriate status
101** on error and return NULL. Args will be NULL if called
102** from the alias routines, although this should probably
103** not be relied upon. It is suggested you call map_rewrite
104** to return the results -- it takes care of null termination
105** and uses a dynamically expanded buffer as needed.
106**
107** void map_store(MAP *map, char *key, char *value)
108** Store the key:value pair in the map.
109**
110** bool map_open(MAP *map, int mode)
111** Open the map for the indicated mode. Mode should
112** be either O_RDONLY or O_RDWR. Return true if it
113** was opened successfully, false otherwise. If the open
114** failed and the MF_OPTIONAL flag is not set, it should
115** also print an error. If the MF_ALIAS bit is set
116** and this map class understands the @:@ convention, it
117** should call aliaswait() before returning.
118**
119** void map_close(MAP *map)
120** Close the map.
121**
122** This file also includes the implementation for getcanonname.
123** It is currently implemented in a pretty ad-hoc manner; it ought
124** to be more properly integrated into the map structure.
125*/
126
127#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
128# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
129#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
130# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
131#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
132
133/*
134** MAP_PARSEARGS -- parse config line arguments for database lookup
135**
136** This is a generic version of the map_parse method.
137**
138** Parameters:
139** map -- the map being initialized.
140** ap -- a pointer to the args on the config line.
141**
142** Returns:
143** true -- if everything parsed OK.
144** false -- otherwise.
145**
146** Side Effects:
147** null terminates the filename; stores it in map
148*/
149
150bool
151map_parseargs(map, ap)
152 MAP *map;
153 char *ap;
154{
155 register char *p = ap;
156
157 /*
158 ** There is no check whether there is really an argument,
159 ** but that's not important enough to warrant extra code.
160 */
161
162 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
163 map->map_spacesub = SpaceSub; /* default value */
164 for (;;)
165 {
166 while (isascii(*p) && isspace(*p))
167 p++;
168 if (*p != '-')
169 break;
170 switch (*++p)
171 {
172 case 'N':
173 map->map_mflags |= MF_INCLNULL;
174 map->map_mflags &= ~MF_TRY0NULL;
175 break;
176
177 case 'O':
178 map->map_mflags &= ~MF_TRY1NULL;
179 break;
180
181 case 'o':
182 map->map_mflags |= MF_OPTIONAL;
183 break;
184
185 case 'f':
186 map->map_mflags |= MF_NOFOLDCASE;
187 break;
188
189 case 'm':
190 map->map_mflags |= MF_MATCHONLY;
191 break;
192
193 case 'A':
194 map->map_mflags |= MF_APPEND;
195 break;
196
197 case 'q':
198 map->map_mflags |= MF_KEEPQUOTES;
199 break;
200
201 case 'a':
202 map->map_app = ++p;
203 break;
204
205 case 'T':
206 map->map_tapp = ++p;
207 break;
208
209 case 'k':
210 while (isascii(*++p) && isspace(*p))
211 continue;
212 map->map_keycolnm = p;
213 break;
214
215 case 'v':
216 while (isascii(*++p) && isspace(*p))
217 continue;
218 map->map_valcolnm = p;
219 break;
220
221 case 'z':
222 if (*++p != '\\')
223 map->map_coldelim = *p;
224 else
225 {
226 switch (*++p)
227 {
228 case 'n':
229 map->map_coldelim = '\n';
230 break;
231
232 case 't':
233 map->map_coldelim = '\t';
234 break;
235
236 default:
237 map->map_coldelim = '\\';
238 }
239 }
240 break;
241
242 case 't':
243 map->map_mflags |= MF_NODEFER;
244 break;
245
246
247 case 'S':
248 map->map_spacesub = *++p;
249 break;
250
251 case 'D':
252 map->map_mflags |= MF_DEFER;
253 break;
254
255 default:
256 syserr("Illegal option %c map %s", *p, map->map_mname);
257 break;
258 }
259 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
260 p++;
261 if (*p != '\0')
262 *p++ = '\0';
263 }
264 if (map->map_app != NULL)
265 map->map_app = newstr(map->map_app);
266 if (map->map_tapp != NULL)
267 map->map_tapp = newstr(map->map_tapp);
268 if (map->map_keycolnm != NULL)
269 map->map_keycolnm = newstr(map->map_keycolnm);
270 if (map->map_valcolnm != NULL)
271 map->map_valcolnm = newstr(map->map_valcolnm);
272
273 if (*p != '\0')
274 {
275 map->map_file = p;
276 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
277 p++;
278 if (*p != '\0')
279 *p++ = '\0';
280 map->map_file = newstr(map->map_file);
281 }
282
283 while (*p != '\0' && isascii(*p) && isspace(*p))
284 p++;
285 if (*p != '\0')
286 map->map_rebuild = newstr(p);
287
288 if (map->map_file == NULL &&
289 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
290 {
291 syserr("No file name for %s map %s",
292 map->map_class->map_cname, map->map_mname);
293 return false;
294 }
295 return true;
296}
297/*
298** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
299**
300** It also adds the map_app string. It can be used as a utility
301** in the map_lookup method.
302**
303** Parameters:
304** map -- the map that causes this.
305** s -- the string to rewrite, NOT necessarily null terminated.
306** slen -- the length of s.
307** av -- arguments to interpolate into buf.
308**
309** Returns:
310** Pointer to rewritten result. This is static data that
311** should be copied if it is to be saved!
312*/
313
314char *
315map_rewrite(map, s, slen, av)
316 register MAP *map;
317 register const char *s;
318 size_t slen;
319 char **av;
320{
321 register char *bp;
322 register char c;
323 char **avp;
324 register char *ap;
325 size_t l;
326 size_t len;
327 static size_t buflen = 0;
328 static char *buf = NULL;
329
330 if (tTd(39, 1))
331 {
332 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
333 if (av == NULL)
334 sm_dprintf(" (nullv)");
335 else
336 {
337 for (avp = av; *avp != NULL; avp++)
338 sm_dprintf("\n\t%s", *avp);
339 }
340 sm_dprintf("\n");
341 }
342
343 /* count expected size of output (can safely overestimate) */
344 l = len = slen;
345 if (av != NULL)
346 {
347 const char *sp = s;
348
349 while (l-- > 0 && (c = *sp++) != '\0')
350 {
351 if (c != '%')
352 continue;
353 if (l-- <= 0)
354 break;
355 c = *sp++;
356 if (!(isascii(c) && isdigit(c)))
357 continue;
358 for (avp = av; --c >= '0' && *avp != NULL; avp++)
359 continue;
360 if (*avp == NULL)
361 continue;
362 len += strlen(*avp);
363 }
364 }
365 if (map->map_app != NULL)
366 len += strlen(map->map_app);
367 if (buflen < ++len)
368 {
369 /* need to malloc additional space */
370 buflen = len;
371 if (buf != NULL)
372 sm_free(buf);
373 buf = sm_pmalloc_x(buflen);
374 }
375
376 bp = buf;
377 if (av == NULL)
378 {
379 memmove(bp, s, slen);
380 bp += slen;
381
382 /* assert(len > slen); */
383 len -= slen;
384 }
385 else
386 {
387 while (slen-- > 0 && (c = *s++) != '\0')
388 {
389 if (c != '%')
390 {
391 pushc:
392 if (len-- <= 1)
393 break;
394 *bp++ = c;
395 continue;
396 }
397 if (slen-- <= 0 || (c = *s++) == '\0')
398 c = '%';
399 if (c == '%')
400 goto pushc;
401 if (!(isascii(c) && isdigit(c)))
402 {
403 if (len-- <= 1)
404 break;
405 *bp++ = '%';
406 goto pushc;
407 }
408 for (avp = av; --c >= '0' && *avp != NULL; avp++)
409 continue;
410 if (*avp == NULL)
411 continue;
412
413 /* transliterate argument into output string */
414 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
415 *bp++ = c;
416 }
417 }
418 if (map->map_app != NULL && len > 0)
419 (void) sm_strlcpy(bp, map->map_app, len);
420 else
421 *bp = '\0';
422 if (tTd(39, 1))
423 sm_dprintf("map_rewrite => %s\n", buf);
424 return buf;
425}
426/*
427** INITMAPS -- rebuild alias maps
428**
429** Parameters:
430** none.
431**
432** Returns:
433** none.
434*/
435
436void
437initmaps()
438{
439#if XDEBUG
440 checkfd012("entering initmaps");
441#endif /* XDEBUG */
442 stabapply(map_init, 0);
443#if XDEBUG
444 checkfd012("exiting initmaps");
445#endif /* XDEBUG */
446}
447/*
448** MAP_INIT -- rebuild a map
449**
450** Parameters:
451** s -- STAB entry: if map: try to rebuild
452** unused -- unused variable
453**
454** Returns:
455** none.
456**
457** Side Effects:
458** will close already open rebuildable map.
459*/
460
461/* ARGSUSED1 */
462static void
463map_init(s, unused)
464 register STAB *s;
465 int unused;
466{
467 register MAP *map;
468
469 /* has to be a map */
470 if (s->s_symtype != ST_MAP)
471 return;
472
473 map = &s->s_map;
474 if (!bitset(MF_VALID, map->map_mflags))
475 return;
476
477 if (tTd(38, 2))
478 sm_dprintf("map_init(%s:%s, %s)\n",
479 map->map_class->map_cname == NULL ? "NULL" :
480 map->map_class->map_cname,
481 map->map_mname == NULL ? "NULL" : map->map_mname,
482 map->map_file == NULL ? "NULL" : map->map_file);
483
484 if (!bitset(MF_ALIAS, map->map_mflags) ||
485 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
486 {
487 if (tTd(38, 3))
488 sm_dprintf("\tnot rebuildable\n");
489 return;
490 }
491
492 /* if already open, close it (for nested open) */
493 if (bitset(MF_OPEN, map->map_mflags))
494 {
495 map->map_mflags |= MF_CLOSING;
496 map->map_class->map_close(map);
497 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
498 }
499
500 (void) rebuildaliases(map, false);
501 return;
502}
503/*
504** OPENMAP -- open a map
505**
506** Parameters:
507** map -- map to open (it must not be open).
508**
509** Returns:
510** whether open succeeded.
511*/
512
513bool
514openmap(map)
515 MAP *map;
516{
517 bool restore = false;
518 bool savehold = HoldErrs;
519 bool savequick = QuickAbort;
520 int saveerrors = Errors;
521
522 if (!bitset(MF_VALID, map->map_mflags))
523 return false;
524
525 /* better safe than sorry... */
526 if (bitset(MF_OPEN, map->map_mflags))
527 return true;
528
529 /* Don't send a map open error out via SMTP */
530 if ((OnlyOneError || QuickAbort) &&
531 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
532 {
533 restore = true;
534 HoldErrs = true;
535 QuickAbort = false;
536 }
537
538 errno = 0;
539 if (map->map_class->map_open(map, O_RDONLY))
540 {
541 if (tTd(38, 4))
542 sm_dprintf("openmap()\t%s:%s %s: valid\n",
543 map->map_class->map_cname == NULL ? "NULL" :
544 map->map_class->map_cname,
545 map->map_mname == NULL ? "NULL" :
546 map->map_mname,
547 map->map_file == NULL ? "NULL" :
548 map->map_file);
549 map->map_mflags |= MF_OPEN;
550 map->map_pid = CurrentPid;
551 }
552 else
553 {
554 if (tTd(38, 4))
555 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
556 map->map_class->map_cname == NULL ? "NULL" :
557 map->map_class->map_cname,
558 map->map_mname == NULL ? "NULL" :
559 map->map_mname,
560 map->map_file == NULL ? "NULL" :
561 map->map_file,
562 errno == 0 ? "" : ": ",
563 errno == 0 ? "" : sm_errstring(errno));
564 if (!bitset(MF_OPTIONAL, map->map_mflags))
565 {
566 extern MAPCLASS BogusMapClass;
567
568 map->map_orgclass = map->map_class;
569 map->map_class = &BogusMapClass;
570 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
571 map->map_pid = CurrentPid;
572 }
573 else
574 {
575 /* don't try again */
576 map->map_mflags &= ~MF_VALID;
577 }
578 }
579
580 if (restore)
581 {
582 Errors = saveerrors;
583 HoldErrs = savehold;
584 QuickAbort = savequick;
585 }
586
587 return bitset(MF_OPEN, map->map_mflags);
588}
589/*
590** CLOSEMAPS -- close all open maps opened by the current pid.
591**
592** Parameters:
593** bogus -- only close bogus maps.
594**
595** Returns:
596** none.
597*/
598
599void
600closemaps(bogus)
601 bool bogus;
602{
603 stabapply(map_close, bogus);
604}
605/*
606** MAP_CLOSE -- close a map opened by the current pid.
607**
608** Parameters:
609** s -- STAB entry: if map: try to close
610** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
611**
612** Returns:
613** none.
614*/
615
616/* ARGSUSED1 */
617static void
618map_close(s, bogus)
619 register STAB *s;
620 int bogus; /* int because of stabapply(), used as bool */
621{
622 MAP *map;
623 extern MAPCLASS BogusMapClass;
624
625 if (s->s_symtype != ST_MAP)
626 return;
627
628 map = &s->s_map;
629
630 /*
631 ** close the map iff:
632 ** it is valid and open and opened by this process
633 ** and (!bogus or it's a bogus map or it is not persistent)
634 ** negate this: return iff
635 ** it is not valid or it is not open or not opened by this process
636 ** or (bogus and it's not a bogus map and it's not not-persistent)
637 */
638
639 if (!bitset(MF_VALID, map->map_mflags) ||
640 !bitset(MF_OPEN, map->map_mflags) ||
641 bitset(MF_CLOSING, map->map_mflags) ||
642 map->map_pid != CurrentPid ||
643 (bogus && map->map_class != &BogusMapClass &&
644 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
645 return;
646
647 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
648 map->map_orgclass != &BogusMapClass)
649 map->map_class = map->map_orgclass;
650 if (tTd(38, 5))
651 sm_dprintf("closemaps: closing %s (%s)\n",
652 map->map_mname == NULL ? "NULL" : map->map_mname,
653 map->map_file == NULL ? "NULL" : map->map_file);
654
655 if (!bitset(MF_OPENBOGUS, map->map_mflags))
656 {
657 map->map_mflags |= MF_CLOSING;
658 map->map_class->map_close(map);
659 }
660 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
661}
662/*
663** GETCANONNAME -- look up name using service switch
664**
665** Parameters:
666** host -- the host name to look up.
667** hbsize -- the size of the host buffer.
668** trymx -- if set, try MX records.
669** pttl -- pointer to return TTL (can be NULL).
670**
671** Returns:
672** true -- if the host was found.
673** false -- otherwise.
674*/
675
676bool
677getcanonname(host, hbsize, trymx, pttl)
678 char *host;
679 int hbsize;
680 bool trymx;
681 int *pttl;
682{
683 int nmaps;
684 int mapno;
685 bool found = false;
686 bool got_tempfail = false;
687 auto int status;
688 char *maptype[MAXMAPSTACK];
689 short mapreturn[MAXMAPACTIONS];
690
691 nmaps = switch_map_find("hosts", maptype, mapreturn);
692 if (pttl != 0)
693 *pttl = SM_DEFAULT_TTL;
694 for (mapno = 0; mapno < nmaps; mapno++)
695 {
696 int i;
697
698 if (tTd(38, 20))
699 sm_dprintf("getcanonname(%s), trying %s\n",
700 host, maptype[mapno]);
701 if (strcmp("files", maptype[mapno]) == 0)
702 {
703 found = text_getcanonname(host, hbsize, &status);
704 }
705#if NIS
706 else if (strcmp("nis", maptype[mapno]) == 0)
707 {
708 found = nis_getcanonname(host, hbsize, &status);
709 }
710#endif /* NIS */
711#if NISPLUS
712 else if (strcmp("nisplus", maptype[mapno]) == 0)
713 {
714 found = nisplus_getcanonname(host, hbsize, &status);
715 }
716#endif /* NISPLUS */
717#if NAMED_BIND
718 else if (strcmp("dns", maptype[mapno]) == 0)
719 {
720 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
721 }
722#endif /* NAMED_BIND */
723#if NETINFO
724 else if (strcmp("netinfo", maptype[mapno]) == 0)
725 {
726 found = ni_getcanonname(host, hbsize, &status);
727 }
728#endif /* NETINFO */
729 else
730 {
731 found = false;
732 status = EX_UNAVAILABLE;
733 }
734
735 /*
736 ** Heuristic: if $m is not set, we are running during system
737 ** startup. In this case, when a name is apparently found
738 ** but has no dot, treat is as not found. This avoids
739 ** problems if /etc/hosts has no FQDN but is listed first
740 ** in the service switch.
741 */
742
743 if (found &&
744 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
745 break;
746
747 /* see if we should continue */
748 if (status == EX_TEMPFAIL)
749 {
750 i = MA_TRYAGAIN;
751 got_tempfail = true;
752 }
753 else if (status == EX_NOTFOUND)
754 i = MA_NOTFOUND;
755 else
756 i = MA_UNAVAIL;
757 if (bitset(1 << mapno, mapreturn[i]))
758 break;
759 }
760
761 if (found)
762 {
763 char *d;
764
765 if (tTd(38, 20))
766 sm_dprintf("getcanonname(%s), found\n", host);
767
768 /*
769 ** If returned name is still single token, compensate
770 ** by tagging on $m. This is because some sites set
771 ** up their DNS or NIS databases wrong.
772 */
773
774 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
775 {
776 d = macvalue('m', CurEnv);
777 if (d != NULL &&
778 hbsize > (int) (strlen(host) + strlen(d) + 1))
779 {
780 if (host[strlen(host) - 1] != '.')
781 (void) sm_strlcat2(host, ".", d,
782 hbsize);
783 else
784 (void) sm_strlcat(host, d, hbsize);
785 }
786 else
787 return false;
788 }
789 return true;
790 }
791
792 if (tTd(38, 20))
793 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
794 status);
795
796 if (got_tempfail)
797 SM_SET_H_ERRNO(TRY_AGAIN);
798 else
799 SM_SET_H_ERRNO(HOST_NOT_FOUND);
800
801 return false;
802}
803/*
804** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
805**
806** Parameters:
807** name -- the name against which to match.
808** dot -- where to reinsert '.' to get FQDN
809** line -- the /etc/hosts line.
810** cbuf -- the location to store the result.
811** cbuflen -- the size of cbuf.
812**
813** Returns:
814** true -- if the line matched the desired name.
815** false -- otherwise.
816*/
817
818static bool
819extract_canonname(name, dot, line, cbuf, cbuflen)
820 char *name;
821 char *dot;
822 char *line;
823 char cbuf[];
824 int cbuflen;
825{
826 int i;
827 char *p;
828 bool found = false;
829
830 cbuf[0] = '\0';
831 if (line[0] == '#')
832 return false;
833
834 for (i = 1; ; i++)
835 {
836 char nbuf[MAXNAME + 1];
837
838 p = get_column(line, i, '\0', nbuf, sizeof nbuf);
839 if (p == NULL)
840 break;
841 if (*p == '\0')
842 continue;
843 if (cbuf[0] == '\0' ||
844 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
845 {
846 (void) sm_strlcpy(cbuf, p, cbuflen);
847 }
848 if (sm_strcasecmp(name, p) == 0)
849 found = true;
850 else if (dot != NULL)
851 {
852 /* try looking for the FQDN as well */
853 *dot = '.';
854 if (sm_strcasecmp(name, p) == 0)
855 found = true;
856 *dot = '\0';
857 }
858 }
859 if (found && strchr(cbuf, '.') == NULL)
860 {
861 /* try to add a domain on the end of the name */
862 char *domain = macvalue('m', CurEnv);
863
864 if (domain != NULL &&
865 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
866 {
867 p = &cbuf[i];
868 *p++ = '.';
869 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
870 }
871 }
872 return found;
873}
874
875/*
876** DNS modules
877*/
878
879#if NAMED_BIND
880# if DNSMAP
881
882# include "sm_resolve.h"
883# if NETINET || NETINET6
884# include <arpa/inet.h>
885# endif /* NETINET || NETINET6 */
886
887/*
888** DNS_MAP_OPEN -- stub to check proper value for dns map type
889*/
890
891bool
892dns_map_open(map, mode)
893 MAP *map;
894 int mode;
895{
896 if (tTd(38,2))
897 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
898
899 mode &= O_ACCMODE;
900 if (mode != O_RDONLY)
901 {
902 /* issue a pseudo-error message */
903 errno = SM_EMAPCANTWRITE;
904 return false;
905 }
906 return true;
907}
908
909/*
910** DNS_MAP_PARSEARGS -- parse dns map definition args.
911**
912** Parameters:
913** map -- pointer to MAP
914** args -- pointer to the args on the config line.
915**
916** Returns:
917** true -- if everything parsed OK.
918** false -- otherwise.
919*/
920
921# if _FFR_DNSMAP_MULTILIMIT
922# if !_FFR_DNSMAP_MULTI
923 ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
924# endif /* ! _FFR_DNSMAP_MULTI */
925# endif /* _FFR_DNSMAP_MULTILIMIT */
926
927# if _FFR_DNSMAP_MULTI
928# if _FFR_DNSMAP_MULTILIMIT
929# define map_sizelimit map_lockfd /* overload field */
930# endif /* _FFR_DNSMAP_MULTILIMIT */
931# endif /* _FFR_DNSMAP_MULTI */
932
933struct dns_map
934{
935 int dns_m_type;
936};
937
938bool
939dns_map_parseargs(map,args)
940 MAP *map;
941 char *args;
942{
943 register char *p = args;
944 struct dns_map *map_p;
945
946 map_p = (struct dns_map *) xalloc(sizeof *map_p);
947 map_p->dns_m_type = -1;
948 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
949
950 for (;;)
951 {
952 while (isascii(*p) && isspace(*p))
953 p++;
954 if (*p != '-')
955 break;
956 switch (*++p)
957 {
958 case 'N':
959 map->map_mflags |= MF_INCLNULL;
960 map->map_mflags &= ~MF_TRY0NULL;
961 break;
962
963 case 'O':
964 map->map_mflags &= ~MF_TRY1NULL;
965 break;
966
967 case 'o':
968 map->map_mflags |= MF_OPTIONAL;
969 break;
970
971 case 'f':
972 map->map_mflags |= MF_NOFOLDCASE;
973 break;
974
975 case 'm':
976 map->map_mflags |= MF_MATCHONLY;
977 break;
978
979 case 'A':
980 map->map_mflags |= MF_APPEND;
981 break;
982
983 case 'q':
984 map->map_mflags |= MF_KEEPQUOTES;
985 break;
986
987 case 't':
988 map->map_mflags |= MF_NODEFER;
989 break;
990
991 case 'a':
992 map->map_app = ++p;
993 break;
994
995 case 'T':
996 map->map_tapp = ++p;
997 break;
998
999 case 'd':
1000 {
1001 char *h;
1002
1003 ++p;
1004 h = strchr(p, ' ');
1005 if (h != NULL)
1006 *h = '\0';
1007 map->map_timeout = convtime(p, 's');
1008 if (h != NULL)
1009 *h = ' ';
1010 }
1011 break;
1012
1013 case 'r':
1014 while (isascii(*++p) && isspace(*p))
1015 continue;
1016 map->map_retry = atoi(p);
1017 break;
1018
1019# if _FFR_DNSMAP_MULTI
1020 case 'z':
1021 if (*++p != '\\')
1022 map->map_coldelim = *p;
1023 else
1024 {
1025 switch (*++p)
1026 {
1027 case 'n':
1028 map->map_coldelim = '\n';
1029 break;
1030
1031 case 't':
1032 map->map_coldelim = '\t';
1033 break;
1034
1035 default:
1036 map->map_coldelim = '\\';
1037 }
1038 }
1039 break;
1040
1041# if _FFR_DNSMAP_MULTILIMIT
1042 case 'Z':
1043 while (isascii(*++p) && isspace(*p))
1044 continue;
1045 map->map_sizelimit = atoi(p);
1046 break;
1047# endif /* _FFR_DNSMAP_MULTILIMIT */
1048# endif /* _FFR_DNSMAP_MULTI */
1049
1050 /* Start of dns_map specific args */
1051 case 'R': /* search field */
1052 {
1053 char *h;
1054
1055 while (isascii(*++p) && isspace(*p))
1056 continue;
1057 h = strchr(p, ' ');
1058 if (h != NULL)
1059 *h = '\0';
1060 map_p->dns_m_type = dns_string_to_type(p);
1061 if (h != NULL)
1062 *h = ' ';
1063 if (map_p->dns_m_type < 0)
1064 syserr("dns map %s: wrong type %s",
1065 map->map_mname, p);
1066 }
1067 break;
1068
1069# if _FFR_DNSMAP_BASE
1070 case 'B': /* base domain */
1071 {
1072 char *h;
1073
1074 while (isascii(*++p) && isspace(*p))
1075 continue;
1076 h = strchr(p, ' ');
1077 if (h != NULL)
1078 *h = '\0';
1079
1080 /*
1081 ** slight abuse of map->map_file; it isn't
1082 ** used otherwise in this map type.
1083 */
1084
1085 map->map_file = newstr(p);
1086 if (h != NULL)
1087 *h = ' ';
1088 }
1089 break;
1090# endif /* _FFR_DNSMAP_BASE */
1091
1092 }
1093 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1094 p++;
1095 if (*p != '\0')
1096 *p++ = '\0';
1097 }
1098 if (map_p->dns_m_type < 0)
1099 syserr("dns map %s: missing -R type", map->map_mname);
1100 if (map->map_app != NULL)
1101 map->map_app = newstr(map->map_app);
1102 if (map->map_tapp != NULL)
1103 map->map_tapp = newstr(map->map_tapp);
1104
1105 /*
1106 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1107 ** Even if this assumption is wrong, we use only one byte,
1108 ** so it doesn't really matter.
1109 */
1110
1111 map->map_db1 = (ARBPTR_T) map_p;
1112 return true;
1113}
1114
1115/*
1116** DNS_MAP_LOOKUP -- perform dns map lookup.
1117**
1118** Parameters:
1119** map -- pointer to MAP
1120** name -- name to lookup
1121** av -- arguments to interpolate into buf.
1122** statp -- pointer to status (EX_)
1123**
1124** Returns:
1125** result of lookup if succeeded.
1126** NULL -- otherwise.
1127*/
1128
1129char *
1130dns_map_lookup(map, name, av, statp)
1131 MAP *map;
1132 char *name;
1133 char **av;
1134 int *statp;
1135{
1136# if _FFR_DNSMAP_MULTI
1137# if _FFR_DNSMAP_MULTILIMIT
1138 int resnum = 0;
1139# endif /* _FFR_DNSMAP_MULTILIMIT */
1140# endif /* _FFR_DNSMAP_MULTI */
1141 char *vp = NULL, *result = NULL;
1142 size_t vsize;
1143 struct dns_map *map_p;
1144 RESOURCE_RECORD_T *rr = NULL;
1145 DNS_REPLY_T *r = NULL;
1146# if NETINET6
1147 static char buf6[INET6_ADDRSTRLEN];
1148# endif /* NETINET6 */
1149
1150 if (tTd(38, 20))
1151 sm_dprintf("dns_map_lookup(%s, %s)\n",
1152 map->map_mname, name);
1153
1154 map_p = (struct dns_map *)(map->map_db1);
1155# if _FFR_DNSMAP_BASE
1156 if (map->map_file != NULL && *map->map_file != '\0')
1157 {
1158 size_t len;
1159 char *appdomain;
1160
1161 len = strlen(map->map_file) + strlen(name) + 2;
1162 appdomain = (char *) sm_malloc(len);
1163 if (appdomain == NULL)
1164 {
1165 *statp = EX_UNAVAILABLE;
1166 return NULL;
1167 }
1168 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1169 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1170 map->map_timeout, map->map_retry);
1171 sm_free(appdomain);
1172 }
1173 else
1174# endif /* _FFR_DNSMAP_BASE */
1175 {
1176 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1177 map->map_timeout, map->map_retry);
1178 }
1179
1180 if (r == NULL)
1181 {
1182 result = NULL;
1183 if (h_errno == TRY_AGAIN || transienterror(errno))
1184 *statp = EX_TEMPFAIL;
1185 else
1186 *statp = EX_NOTFOUND;
1187 goto cleanup;
1188 }
1189 *statp = EX_OK;
1190 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1191 {
1192 char *type = NULL;
1193 char *value = NULL;
1194
1195 switch (rr->rr_type)
1196 {
1197 case T_NS:
1198 type = "T_NS";
1199 value = rr->rr_u.rr_txt;
1200 break;
1201 case T_CNAME:
1202 type = "T_CNAME";
1203 value = rr->rr_u.rr_txt;
1204 break;
1205 case T_AFSDB:
1206 type = "T_AFSDB";
1207 value = rr->rr_u.rr_mx->mx_r_domain;
1208 break;
1209 case T_SRV:
1210 type = "T_SRV";
1211 value = rr->rr_u.rr_srv->srv_r_target;
1212 break;
1213 case T_PTR:
1214 type = "T_PTR";
1215 value = rr->rr_u.rr_txt;
1216 break;
1217 case T_TXT:
1218 type = "T_TXT";
1219 value = rr->rr_u.rr_txt;
1220 break;
1221 case T_MX:
1222 type = "T_MX";
1223 value = rr->rr_u.rr_mx->mx_r_domain;
1224 break;
1225# if NETINET
1226 case T_A:
1227 type = "T_A";
1228 value = inet_ntoa(*(rr->rr_u.rr_a));
1229 break;
1230# endif /* NETINET */
1231# if NETINET6
1232 case T_AAAA:
1233 type = "T_AAAA";
1234 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1235 sizeof buf6);
1236 break;
1237# endif /* NETINET6 */
1238 }
1239
1240 (void) strreplnonprt(value, 'X');
1241 if (map_p->dns_m_type != rr->rr_type)
1242 {
1243 if (tTd(38, 40))
1244 sm_dprintf("\tskipping type %s (%d) value %s\n",
1245 type != NULL ? type : "<UNKNOWN>",
1246 rr->rr_type,
1247 value != NULL ? value : "<NO VALUE>");
1248 continue;
1249 }
1250
1251# if NETINET6
1252 if (rr->rr_type == T_AAAA && value == NULL)
1253 {
1254 result = NULL;
1255 *statp = EX_DATAERR;
1256 if (tTd(38, 40))
1257 sm_dprintf("\tbad T_AAAA conversion\n");
1258 goto cleanup;
1259 }
1260# endif /* NETINET6 */
1261 if (tTd(38, 40))
1262 sm_dprintf("\tfound type %s (%d) value %s\n",
1263 type != NULL ? type : "<UNKNOWN>",
1264 rr->rr_type,
1265 value != NULL ? value : "<NO VALUE>");
1266# if _FFR_DNSMAP_MULTI
1267 if (value != NULL &&
1268 (map->map_coldelim == '\0' ||
1269# if _FFR_DNSMAP_MULTILIMIT
1270 map->map_sizelimit == 1 ||
1271# endif /* _FFR_DNSMAP_MULTILIMIT */
1272 bitset(MF_MATCHONLY, map->map_mflags)))
1273 {
1274 /* Only care about the first match */
1275 vp = newstr(value);
1276 break;
1277 }
1278 else if (vp == NULL)
1279 {
1280 /* First result */
1281 vp = newstr(value);
1282 }
1283 else
1284 {
1285 /* concatenate the results */
1286 int sz;
1287 char *new;
1288
1289 sz = strlen(vp) + strlen(value) + 2;
1290 new = xalloc(sz);
1291 (void) sm_snprintf(new, sz, "%s%c%s",
1292 vp, map->map_coldelim, value);
1293 sm_free(vp);
1294 vp = new;
1295# if _FFR_DNSMAP_MULTILIMIT
1296 if (map->map_sizelimit > 0 &&
1297 ++resnum >= map->map_sizelimit)
1298 break;
1299# endif /* _FFR_DNSMAP_MULTILIMIT */
1300 }
1301# else /* _FFR_DNSMAP_MULTI */
1302 vp = value;
1303 break;
1304# endif /* _FFR_DNSMAP_MULTI */
1305 }
1306 if (vp == NULL)
1307 {
1308 result = NULL;
1309 *statp = EX_NOTFOUND;
1310 if (tTd(38, 40))
1311 sm_dprintf("\tno match found\n");
1312 goto cleanup;
1313 }
1314
1315# if _FFR_DNSMAP_MULTI
1316 /* Cleanly truncate for rulesets */
1317 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1318# endif /* _FFR_DNSMAP_MULTI */
1319
1320 vsize = strlen(vp);
1321
1322 if (LogLevel > 9)
1323 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1324 name, vp);
1325 if (bitset(MF_MATCHONLY, map->map_mflags))
1326 result = map_rewrite(map, name, strlen(name), NULL);
1327 else
1328 result = map_rewrite(map, vp, vsize, av);
1329
1330 cleanup:
1331# if _FFR_DNSMAP_MULTI
1332 if (vp != NULL)
1333 sm_free(vp);
1334# endif /* _FFR_DNSMAP_MULTI */
1335 if (r != NULL)
1336 dns_free_data(r);
1337 return result;
1338}
1339# endif /* DNSMAP */
1340#endif /* NAMED_BIND */
1341
1342/*
1343** NDBM modules
1344*/
1345
1346#if NDBM
1347
1348/*
1349** NDBM_MAP_OPEN -- DBM-style map open
1350*/
1351
1352bool
1353ndbm_map_open(map, mode)
1354 MAP *map;
1355 int mode;
1356{
1357 register DBM *dbm;
1358 int save_errno;
1359 int dfd;
1360 int pfd;
1361 long sff;
1362 int ret;
1363 int smode = S_IREAD;
1364 char dirfile[MAXPATHLEN];
1365 char pagfile[MAXPATHLEN];
1366 struct stat st;
1367 struct stat std, stp;
1368
1369 if (tTd(38, 2))
1370 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1371 map->map_mname, map->map_file, mode);
1372 map->map_lockfd = -1;
1373 mode &= O_ACCMODE;
1374
1375 /* do initial file and directory checks */
1376 if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
1377 map->map_file, ".dir") >= sizeof dirfile ||
1378 sm_strlcpyn(pagfile, sizeof pagfile, 2,
1379 map->map_file, ".pag") >= sizeof pagfile)
1380 {
1381 errno = 0;
1382 if (!bitset(MF_OPTIONAL, map->map_mflags))
1383 syserr("dbm map \"%s\": map file %s name too long",
1384 map->map_mname, map->map_file);
1385 return false;
1386 }
1387 sff = SFF_ROOTOK|SFF_REGONLY;
1388 if (mode == O_RDWR)
1389 {
1390 sff |= SFF_CREAT;
1391 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1392 sff |= SFF_NOSLINK;
1393 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1394 sff |= SFF_NOHLINK;
1395 smode = S_IWRITE;
1396 }
1397 else
1398 {
1399 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1400 sff |= SFF_NOWLINK;
1401 }
1402 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1403 sff |= SFF_SAFEDIRPATH;
1404 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1405 sff, smode, &std);
1406 if (ret == 0)
1407 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1408 sff, smode, &stp);
1409
1410 if (ret != 0)
1411 {
1412 char *prob = "unsafe";
1413
1414 /* cannot open this map */
1415 if (ret == ENOENT)
1416 prob = "missing";
1417 if (tTd(38, 2))
1418 sm_dprintf("\t%s map file: %d\n", prob, ret);
1419 if (!bitset(MF_OPTIONAL, map->map_mflags))
1420 syserr("dbm map \"%s\": %s map file %s",
1421 map->map_mname, prob, map->map_file);
1422 return false;
1423 }
1424 if (std.st_mode == ST_MODE_NOFILE)
1425 mode |= O_CREAT|O_EXCL;
1426
1427# if LOCK_ON_OPEN
1428 if (mode == O_RDONLY)
1429 mode |= O_SHLOCK;
1430 else
1431 mode |= O_TRUNC|O_EXLOCK;
1432# else /* LOCK_ON_OPEN */
1433 if ((mode & O_ACCMODE) == O_RDWR)
1434 {
1435# if NOFTRUNCATE
1436 /*
1437 ** Warning: race condition. Try to lock the file as
1438 ** quickly as possible after opening it.
1439 ** This may also have security problems on some systems,
1440 ** but there isn't anything we can do about it.
1441 */
1442
1443 mode |= O_TRUNC;
1444# else /* NOFTRUNCATE */
1445 /*
1446 ** This ugly code opens the map without truncating it,
1447 ** locks the file, then truncates it. Necessary to
1448 ** avoid race conditions.
1449 */
1450
1451 int dirfd;
1452 int pagfd;
1453 long sff = SFF_CREAT|SFF_OPENASROOT;
1454
1455 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1456 sff |= SFF_NOSLINK;
1457 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1458 sff |= SFF_NOHLINK;
1459
1460 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1461 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1462
1463 if (dirfd < 0 || pagfd < 0)
1464 {
1465 save_errno = errno;
1466 if (dirfd >= 0)
1467 (void) close(dirfd);
1468 if (pagfd >= 0)
1469 (void) close(pagfd);
1470 errno = save_errno;
1471 syserr("ndbm_map_open: cannot create database %s",
1472 map->map_file);
1473 return false;
1474 }
1475 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1476 ftruncate(pagfd, (off_t) 0) < 0)
1477 {
1478 save_errno = errno;
1479 (void) close(dirfd);
1480 (void) close(pagfd);
1481 errno = save_errno;
1482 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1483 map->map_file);
1484 return false;
1485 }
1486
1487 /* if new file, get "before" bits for later filechanged check */
1488 if (std.st_mode == ST_MODE_NOFILE &&
1489 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1490 {
1491 save_errno = errno;
1492 (void) close(dirfd);
1493 (void) close(pagfd);
1494 errno = save_errno;
1495 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1496 map->map_file);
1497 return false;
1498 }
1499
1500 /* have to save the lock for the duration (bletch) */
1501 map->map_lockfd = dirfd;
1502 (void) close(pagfd);
1503
1504 /* twiddle bits for dbm_open */
1505 mode &= ~(O_CREAT|O_EXCL);
1506# endif /* NOFTRUNCATE */
1507 }
1508# endif /* LOCK_ON_OPEN */
1509
1510 /* open the database */
1511 dbm = dbm_open(map->map_file, mode, DBMMODE);
1512 if (dbm == NULL)
1513 {
1514 save_errno = errno;
1515 if (bitset(MF_ALIAS, map->map_mflags) &&
1516 aliaswait(map, ".pag", false))
1517 return true;
1518# if !LOCK_ON_OPEN && !NOFTRUNCATE
1519 if (map->map_lockfd >= 0)
1520 (void) close(map->map_lockfd);
1521# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1522 errno = save_errno;
1523 if (!bitset(MF_OPTIONAL, map->map_mflags))
1524 syserr("Cannot open DBM database %s", map->map_file);
1525 return false;
1526 }
1527 dfd = dbm_dirfno(dbm);
1528 pfd = dbm_pagfno(dbm);
1529 if (dfd == pfd)
1530 {
1531 /* heuristic: if files are linked, this is actually gdbm */
1532 dbm_close(dbm);
1533# if !LOCK_ON_OPEN && !NOFTRUNCATE
1534 if (map->map_lockfd >= 0)
1535 (void) close(map->map_lockfd);
1536# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1537 errno = 0;
1538 syserr("dbm map \"%s\": cannot support GDBM",
1539 map->map_mname);
1540 return false;
1541 }
1542
1543 if (filechanged(dirfile, dfd, &std) ||
1544 filechanged(pagfile, pfd, &stp))
1545 {
1546 save_errno = errno;
1547 dbm_close(dbm);
1548# if !LOCK_ON_OPEN && !NOFTRUNCATE
1549 if (map->map_lockfd >= 0)
1550 (void) close(map->map_lockfd);
1551# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1552 errno = save_errno;
1553 syserr("ndbm_map_open(%s): file changed after open",
1554 map->map_file);
1555 return false;
1556 }
1557
1558 map->map_db1 = (ARBPTR_T) dbm;
1559
1560 /*
1561 ** Need to set map_mtime before the call to aliaswait()
1562 ** as aliaswait() will call map_lookup() which requires
1563 ** map_mtime to be set
1564 */
1565
1566 if (fstat(pfd, &st) >= 0)
1567 map->map_mtime = st.st_mtime;
1568
1569 if (mode == O_RDONLY)
1570 {
1571# if LOCK_ON_OPEN
1572 if (dfd >= 0)
1573 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1574 if (pfd >= 0)
1575 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1576# endif /* LOCK_ON_OPEN */
1577 if (bitset(MF_ALIAS, map->map_mflags) &&
1578 !aliaswait(map, ".pag", true))
1579 return false;
1580 }
1581 else
1582 {
1583 map->map_mflags |= MF_LOCKED;
1584 if (geteuid() == 0 && TrustedUid != 0)
1585 {
1586# if HASFCHOWN
1587 if (fchown(dfd, TrustedUid, -1) < 0 ||
1588 fchown(pfd, TrustedUid, -1) < 0)
1589 {
1590 int err = errno;
1591
1592 sm_syslog(LOG_ALERT, NOQID,
1593 "ownership change on %s failed: %s",
1594 map->map_file, sm_errstring(err));
1595 message("050 ownership change on %s failed: %s",
1596 map->map_file, sm_errstring(err));
1597 }
1598# else /* HASFCHOWN */
1599 sm_syslog(LOG_ALERT, NOQID,
1600 "no fchown(): cannot change ownership on %s",
1601 map->map_file);
1602 message("050 no fchown(): cannot change ownership on %s",
1603 map->map_file);
1604# endif /* HASFCHOWN */
1605 }
1606 }
1607 return true;
1608}
1609
1610
1611/*
1612** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1613*/
1614
1615char *
1616ndbm_map_lookup(map, name, av, statp)
1617 MAP *map;
1618 char *name;
1619 char **av;
1620 int *statp;
1621{
1622 datum key, val;
1623 int dfd, pfd;
1624 char keybuf[MAXNAME + 1];
1625 struct stat stbuf;
1626
1627 if (tTd(38, 20))
1628 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1629 map->map_mname, name);
1630
1631 key.dptr = name;
1632 key.dsize = strlen(name);
1633 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1634 {
1635 if (key.dsize > sizeof keybuf - 1)
1636 key.dsize = sizeof keybuf - 1;
1637 memmove(keybuf, key.dptr, key.dsize);
1638 keybuf[key.dsize] = '\0';
1639 makelower(keybuf);
1640 key.dptr = keybuf;
1641 }
1642lockdbm:
1643 dfd = dbm_dirfno((DBM *) map->map_db1);
1644 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1645 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1646 pfd = dbm_pagfno((DBM *) map->map_db1);
1647 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1648 stbuf.st_mtime > map->map_mtime)
1649 {
1650 /* Reopen the database to sync the cache */
1651 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1652 : O_RDONLY;
1653
1654 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1655 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1656 map->map_mflags |= MF_CLOSING;
1657 map->map_class->map_close(map);
1658 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1659 if (map->map_class->map_open(map, omode))
1660 {
1661 map->map_mflags |= MF_OPEN;
1662 map->map_pid = CurrentPid;
1663 if ((omode && O_ACCMODE) == O_RDWR)
1664 map->map_mflags |= MF_WRITABLE;
1665 goto lockdbm;
1666 }
1667 else
1668 {
1669 if (!bitset(MF_OPTIONAL, map->map_mflags))
1670 {
1671 extern MAPCLASS BogusMapClass;
1672
1673 *statp = EX_TEMPFAIL;
1674 map->map_orgclass = map->map_class;
1675 map->map_class = &BogusMapClass;
1676 map->map_mflags |= MF_OPEN;
1677 map->map_pid = CurrentPid;
1678 syserr("Cannot reopen NDBM database %s",
1679 map->map_file);
1680 }
1681 return NULL;
1682 }
1683 }
1684 val.dptr = NULL;
1685 if (bitset(MF_TRY0NULL, map->map_mflags))
1686 {
1687 val = dbm_fetch((DBM *) map->map_db1, key);
1688 if (val.dptr != NULL)
1689 map->map_mflags &= ~MF_TRY1NULL;
1690 }
1691 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1692 {
1693 key.dsize++;
1694 val = dbm_fetch((DBM *) map->map_db1, key);
1695 if (val.dptr != NULL)
1696 map->map_mflags &= ~MF_TRY0NULL;
1697 }
1698 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1699 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1700 if (val.dptr == NULL)
1701 return NULL;
1702 if (bitset(MF_MATCHONLY, map->map_mflags))
1703 return map_rewrite(map, name, strlen(name), NULL);
1704 else
1705 return map_rewrite(map, val.dptr, val.dsize, av);
1706}
1707
1708
1709/*
1710** NDBM_MAP_STORE -- store a datum in the database
1711*/
1712
1713void
1714ndbm_map_store(map, lhs, rhs)
1715 register MAP *map;
1716 char *lhs;
1717 char *rhs;
1718{
1719 datum key;
1720 datum data;
1721 int status;
1722 char keybuf[MAXNAME + 1];
1723
1724 if (tTd(38, 12))
1725 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1726 map->map_mname, lhs, rhs);
1727
1728 key.dsize = strlen(lhs);
1729 key.dptr = lhs;
1730 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1731 {
1732 if (key.dsize > sizeof keybuf - 1)
1733 key.dsize = sizeof keybuf - 1;
1734 memmove(keybuf, key.dptr, key.dsize);
1735 keybuf[key.dsize] = '\0';
1736 makelower(keybuf);
1737 key.dptr = keybuf;
1738 }
1739
1740 data.dsize = strlen(rhs);
1741 data.dptr = rhs;
1742
1743 if (bitset(MF_INCLNULL, map->map_mflags))
1744 {
1745 key.dsize++;
1746 data.dsize++;
1747 }
1748
1749 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1750 if (status > 0)
1751 {
1752 if (!bitset(MF_APPEND, map->map_mflags))
1753 message("050 Warning: duplicate alias name %s", lhs);
1754 else
1755 {
1756 static char *buf = NULL;
1757 static int bufsiz = 0;
1758 auto int xstat;
1759 datum old;
1760
1761 old.dptr = ndbm_map_lookup(map, key.dptr,
1762 (char **) NULL, &xstat);
1763 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1764 {
1765 old.dsize = strlen(old.dptr);
1766 if (data.dsize + old.dsize + 2 > bufsiz)
1767 {
1768 if (buf != NULL)
1769 (void) sm_free(buf);
1770 bufsiz = data.dsize + old.dsize + 2;
1771 buf = sm_pmalloc_x(bufsiz);
1772 }
1773 (void) sm_strlcpyn(buf, bufsiz, 3,
1774 data.dptr, ",", old.dptr);
1775 data.dsize = data.dsize + old.dsize + 1;
1776 data.dptr = buf;
1777 if (tTd(38, 9))
1778 sm_dprintf("ndbm_map_store append=%s\n",
1779 data.dptr);
1780 }
1781 }
1782 status = dbm_store((DBM *) map->map_db1,
1783 key, data, DBM_REPLACE);
1784 }
1785 if (status != 0)
1786 syserr("readaliases: dbm put (%s): %d", lhs, status);
1787}
1788
1789
1790/*
1791** NDBM_MAP_CLOSE -- close the database
1792*/
1793
1794void
1795ndbm_map_close(map)
1796 register MAP *map;
1797{
1798 if (tTd(38, 9))
1799 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1800 map->map_mname, map->map_file, map->map_mflags);
1801
1802 if (bitset(MF_WRITABLE, map->map_mflags))
1803 {
1804# ifdef NDBM_YP_COMPAT
1805 bool inclnull;
1806 char buf[MAXHOSTNAMELEN];
1807
1808 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1809 map->map_mflags &= ~MF_INCLNULL;
1810
1811 if (strstr(map->map_file, "/yp/") != NULL)
1812 {
1813 long save_mflags = map->map_mflags;
1814
1815 map->map_mflags |= MF_NOFOLDCASE;
1816
1817 (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
1818 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1819
1820 (void) gethostname(buf, sizeof buf);
1821 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1822
1823 map->map_mflags = save_mflags;
1824 }
1825
1826 if (inclnull)
1827 map->map_mflags |= MF_INCLNULL;
1828# endif /* NDBM_YP_COMPAT */
1829
1830 /* write out the distinguished alias */
1831 ndbm_map_store(map, "@", "@");
1832 }
1833 dbm_close((DBM *) map->map_db1);
1834
1835 /* release lock (if needed) */
1836# if !LOCK_ON_OPEN
1837 if (map->map_lockfd >= 0)
1838 (void) close(map->map_lockfd);
1839# endif /* !LOCK_ON_OPEN */
1840}
1841
1842#endif /* NDBM */
1843/*
1844** NEWDB (Hash and BTree) Modules
1845*/
1846
1847#if NEWDB
1848
1849/*
1850** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1851**
1852** These do rather bizarre locking. If you can lock on open,
1853** do that to avoid the condition of opening a database that
1854** is being rebuilt. If you don't, we'll try to fake it, but
1855** there will be a race condition. If opening for read-only,
1856** we immediately release the lock to avoid freezing things up.
1857** We really ought to hold the lock, but guarantee that we won't
1858** be pokey about it. That's hard to do.
1859*/
1860
1861/* these should be K line arguments */
1862# if DB_VERSION_MAJOR < 2
1863# define db_cachesize cachesize
1864# define h_nelem nelem
1865# ifndef DB_CACHE_SIZE
1866# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1867# endif /* ! DB_CACHE_SIZE */
1868# ifndef DB_HASH_NELEM
1869# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1870# endif /* ! DB_HASH_NELEM */
1871# endif /* DB_VERSION_MAJOR < 2 */
1872
1873bool
1874bt_map_open(map, mode)
1875 MAP *map;
1876 int mode;
1877{
1878# if DB_VERSION_MAJOR < 2
1879 BTREEINFO btinfo;
1880# endif /* DB_VERSION_MAJOR < 2 */
1881# if DB_VERSION_MAJOR == 2
1882 DB_INFO btinfo;
1883# endif /* DB_VERSION_MAJOR == 2 */
1884# if DB_VERSION_MAJOR > 2
1885 void *btinfo = NULL;
1886# endif /* DB_VERSION_MAJOR > 2 */
1887
1888 if (tTd(38, 2))
1889 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1890 map->map_mname, map->map_file, mode);
1891
1892# if DB_VERSION_MAJOR < 3
1893 memset(&btinfo, '\0', sizeof btinfo);
1894# ifdef DB_CACHE_SIZE
1895 btinfo.db_cachesize = DB_CACHE_SIZE;
1896# endif /* DB_CACHE_SIZE */
1897# endif /* DB_VERSION_MAJOR < 3 */
1898
1899 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1900}
1901
1902bool
1903hash_map_open(map, mode)
1904 MAP *map;
1905 int mode;
1906{
1907# if DB_VERSION_MAJOR < 2
1908 HASHINFO hinfo;
1909# endif /* DB_VERSION_MAJOR < 2 */
1910# if DB_VERSION_MAJOR == 2
1911 DB_INFO hinfo;
1912# endif /* DB_VERSION_MAJOR == 2 */
1913# if DB_VERSION_MAJOR > 2
1914 void *hinfo = NULL;
1915# endif /* DB_VERSION_MAJOR > 2 */
1916
1917 if (tTd(38, 2))
1918 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1919 map->map_mname, map->map_file, mode);
1920
1921# if DB_VERSION_MAJOR < 3
1922 memset(&hinfo, '\0', sizeof hinfo);
1923# ifdef DB_HASH_NELEM
1924 hinfo.h_nelem = DB_HASH_NELEM;
1925# endif /* DB_HASH_NELEM */
1926# ifdef DB_CACHE_SIZE
1927 hinfo.db_cachesize = DB_CACHE_SIZE;
1928# endif /* DB_CACHE_SIZE */
1929# endif /* DB_VERSION_MAJOR < 3 */
1930
1931 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1932}
1933
1934static bool
1935db_map_open(map, mode, mapclassname, dbtype, openinfo)
1936 MAP *map;
1937 int mode;
1938 char *mapclassname;
1939 DBTYPE dbtype;
1940# if DB_VERSION_MAJOR < 2
1941 const void *openinfo;
1942# endif /* DB_VERSION_MAJOR < 2 */
1943# if DB_VERSION_MAJOR == 2
1944 DB_INFO *openinfo;
1945# endif /* DB_VERSION_MAJOR == 2 */
1946# if DB_VERSION_MAJOR > 2
1947 void **openinfo;
1948# endif /* DB_VERSION_MAJOR > 2 */
1949{
1950 DB *db = NULL;
1951 int i;
1952 int omode;
1953 int smode = S_IREAD;
1954 int fd;
1955 long sff;
1956 int save_errno;
1957 struct stat st;
1958 char buf[MAXPATHLEN];
1959
1960 /* do initial file and directory checks */
1961 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
1962 {
1963 errno = 0;
1964 if (!bitset(MF_OPTIONAL, map->map_mflags))
1965 syserr("map \"%s\": map file %s name too long",
1966 map->map_mname, map->map_file);
1967 return false;
1968 }
1969 i = strlen(buf);
1970 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1971 {
1972 if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
1973 {
1974 errno = 0;
1975 if (!bitset(MF_OPTIONAL, map->map_mflags))
1976 syserr("map \"%s\": map file %s name too long",
1977 map->map_mname, map->map_file);
1978 return false;
1979 }
1980 }
1981
1982 mode &= O_ACCMODE;
1983 omode = mode;
1984
1985 sff = SFF_ROOTOK|SFF_REGONLY;
1986 if (mode == O_RDWR)
1987 {
1988 sff |= SFF_CREAT;
1989 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1990 sff |= SFF_NOSLINK;
1991 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1992 sff |= SFF_NOHLINK;
1993 smode = S_IWRITE;
1994 }
1995 else
1996 {
1997 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1998 sff |= SFF_NOWLINK;
1999 }
2000 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2001 sff |= SFF_SAFEDIRPATH;
2002 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2003
2004 if (i != 0)
2005 {
2006 char *prob = "unsafe";
2007
2008 /* cannot open this map */
2009 if (i == ENOENT)
2010 prob = "missing";
2011 if (tTd(38, 2))
2012 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2013 errno = i;
2014 if (!bitset(MF_OPTIONAL, map->map_mflags))
2015 syserr("%s map \"%s\": %s map file %s",
2016 mapclassname, map->map_mname, prob, buf);
2017 return false;
2018 }
2019 if (st.st_mode == ST_MODE_NOFILE)
2020 omode |= O_CREAT|O_EXCL;
2021
2022 map->map_lockfd = -1;
2023
2024# if LOCK_ON_OPEN
2025 if (mode == O_RDWR)
2026 omode |= O_TRUNC|O_EXLOCK;
2027 else
2028 omode |= O_SHLOCK;
2029# else /* LOCK_ON_OPEN */
2030 /*
2031 ** Pre-lock the file to avoid race conditions. In particular,
2032 ** since dbopen returns NULL if the file is zero length, we
2033 ** must have a locked instance around the dbopen.
2034 */
2035
2036 fd = open(buf, omode, DBMMODE);
2037 if (fd < 0)
2038 {
2039 if (!bitset(MF_OPTIONAL, map->map_mflags))
2040 syserr("db_map_open: cannot pre-open database %s", buf);
2041 return false;
2042 }
2043
2044 /* make sure no baddies slipped in just before the open... */
2045 if (filechanged(buf, fd, &st))
2046 {
2047 save_errno = errno;
2048 (void) close(fd);
2049 errno = save_errno;
2050 syserr("db_map_open(%s): file changed after pre-open", buf);
2051 return false;
2052 }
2053
2054 /* if new file, get the "before" bits for later filechanged check */
2055 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2056 {
2057 save_errno = errno;
2058 (void) close(fd);
2059 errno = save_errno;
2060 syserr("db_map_open(%s): cannot fstat pre-opened file",
2061 buf);
2062 return false;
2063 }
2064
2065 /* actually lock the pre-opened file */
2066 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2067 syserr("db_map_open: cannot lock %s", buf);
2068
2069 /* set up mode bits for dbopen */
2070 if (mode == O_RDWR)
2071 omode |= O_TRUNC;
2072 omode &= ~(O_EXCL|O_CREAT);
2073# endif /* LOCK_ON_OPEN */
2074
2075# if DB_VERSION_MAJOR < 2
2076 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2077# else /* DB_VERSION_MAJOR < 2 */
2078 {
2079 int flags = 0;
2080# if DB_VERSION_MAJOR > 2
2081 int ret;
2082# endif /* DB_VERSION_MAJOR > 2 */
2083
2084 if (mode == O_RDONLY)
2085 flags |= DB_RDONLY;
2086 if (bitset(O_CREAT, omode))
2087 flags |= DB_CREATE;
2088 if (bitset(O_TRUNC, omode))
2089 flags |= DB_TRUNCATE;
2090 SM_DB_FLAG_ADD(flags);
2091
2092# if DB_VERSION_MAJOR > 2
2093 ret = db_create(&db, NULL, 0);
2094# ifdef DB_CACHE_SIZE
2095 if (ret == 0 && db != NULL)
2096 {
2097 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2098 if (ret != 0)
2099 {
2100 (void) db->close(db, 0);
2101 db = NULL;
2102 }
2103 }
2104# endif /* DB_CACHE_SIZE */
2105# ifdef DB_HASH_NELEM
2106 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2107 {
2108 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2109 if (ret != 0)
2110 {
2111 (void) db->close(db, 0);
2112 db = NULL;
2113 }
2114 }
2115# endif /* DB_HASH_NELEM */
2116 if (ret == 0 && db != NULL)
2117 {
2118 ret = db->open(db,
2119 DBTXN /* transaction for DB 4.1 */
2120 buf, NULL, dbtype, flags, DBMMODE);
2121 if (ret != 0)
2122 {
2123#ifdef DB_OLD_VERSION
2124 if (ret == DB_OLD_VERSION)
2125 ret = EINVAL;
2126#endif /* DB_OLD_VERSION */
2127 (void) db->close(db, 0);
2128 db = NULL;
2129 }
2130 }
2131 errno = ret;
2132# else /* DB_VERSION_MAJOR > 2 */
2133 errno = db_open(buf, dbtype, flags, DBMMODE,
2134 NULL, openinfo, &db);
2135# endif /* DB_VERSION_MAJOR > 2 */
2136 }
2137# endif /* DB_VERSION_MAJOR < 2 */
2138 save_errno = errno;
2139
2140# if !LOCK_ON_OPEN
2141 if (mode == O_RDWR)
2142 map->map_lockfd = fd;
2143 else
2144 (void) close(fd);
2145# endif /* !LOCK_ON_OPEN */
2146
2147 if (db == NULL)
2148 {
2149 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2150 aliaswait(map, ".db", false))
2151 return true;
2152# if !LOCK_ON_OPEN
2153 if (map->map_lockfd >= 0)
2154 (void) close(map->map_lockfd);
2155# endif /* !LOCK_ON_OPEN */
2156 errno = save_errno;
2157 if (!bitset(MF_OPTIONAL, map->map_mflags))
2158 syserr("Cannot open %s database %s",
2159 mapclassname, buf);
2160 return false;
2161 }
2162
2163# if DB_VERSION_MAJOR < 2
2164 fd = db->fd(db);
2165# else /* DB_VERSION_MAJOR < 2 */
2166 fd = -1;
2167 errno = db->fd(db, &fd);
2168# endif /* DB_VERSION_MAJOR < 2 */
2169 if (filechanged(buf, fd, &st))
2170 {
2171 save_errno = errno;
2172# if DB_VERSION_MAJOR < 2
2173 (void) db->close(db);
2174# else /* DB_VERSION_MAJOR < 2 */
2175 errno = db->close(db, 0);
2176# endif /* DB_VERSION_MAJOR < 2 */
2177# if !LOCK_ON_OPEN
2178 if (map->map_lockfd >= 0)
2179 (void) close(map->map_lockfd);
2180# endif /* !LOCK_ON_OPEN */
2181 errno = save_errno;
2182 syserr("db_map_open(%s): file changed after open", buf);
2183 return false;
2184 }
2185
2186 if (mode == O_RDWR)
2187 map->map_mflags |= MF_LOCKED;
2188# if LOCK_ON_OPEN
2189 if (fd >= 0 && mode == O_RDONLY)
2190 {
2191 (void) lockfile(fd, buf, NULL, LOCK_UN);
2192 }
2193# endif /* LOCK_ON_OPEN */
2194
2195 /* try to make sure that at least the database header is on disk */
2196 if (mode == O_RDWR)
2197 {
2198 (void) db->sync(db, 0);
2199 if (geteuid() == 0 && TrustedUid != 0)
2200 {
2201# if HASFCHOWN
2202 if (fchown(fd, TrustedUid, -1) < 0)
2203 {
2204 int err = errno;
2205
2206 sm_syslog(LOG_ALERT, NOQID,
2207 "ownership change on %s failed: %s",
2208 buf, sm_errstring(err));
2209 message("050 ownership change on %s failed: %s",
2210 buf, sm_errstring(err));
2211 }
2212# else /* HASFCHOWN */
2213 sm_syslog(LOG_ALERT, NOQID,
2214 "no fchown(): cannot change ownership on %s",
2215 map->map_file);
2216 message("050 no fchown(): cannot change ownership on %s",
2217 map->map_file);
2218# endif /* HASFCHOWN */
2219 }
2220 }
2221
2222 map->map_db2 = (ARBPTR_T) db;
2223
2224 /*
2225 ** Need to set map_mtime before the call to aliaswait()
2226 ** as aliaswait() will call map_lookup() which requires
2227 ** map_mtime to be set
2228 */
2229
2230 if (fd >= 0 && fstat(fd, &st) >= 0)
2231 map->map_mtime = st.st_mtime;
2232
2233 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2234 !aliaswait(map, ".db", true))
2235 return false;
2236 return true;
2237}
2238
2239
2240/*
2241** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2242*/
2243
2244char *
2245db_map_lookup(map, name, av, statp)
2246 MAP *map;
2247 char *name;
2248 char **av;
2249 int *statp;
2250{
2251 DBT key, val;
2252 register DB *db = (DB *) map->map_db2;
2253 int i;
2254 int st;
2255 int save_errno;
2256 int fd;
2257 struct stat stbuf;
2258 char keybuf[MAXNAME + 1];
2259 char buf[MAXPATHLEN];
2260
2261 memset(&key, '\0', sizeof key);
2262 memset(&val, '\0', sizeof val);
2263
2264 if (tTd(38, 20))
2265 sm_dprintf("db_map_lookup(%s, %s)\n",
2266 map->map_mname, name);
2267
2268 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
2269 {
2270 errno = 0;
2271 if (!bitset(MF_OPTIONAL, map->map_mflags))
2272 syserr("map \"%s\": map file %s name too long",
2273 map->map_mname, map->map_file);
2274 return NULL;
2275 }
2276 i = strlen(buf);
2277 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2278 buf[i - 3] = '\0';
2279
2280 key.size = strlen(name);
2281 if (key.size > sizeof keybuf - 1)
2282 key.size = sizeof keybuf - 1;
2283 key.data = keybuf;
2284 memmove(keybuf, name, key.size);
2285 keybuf[key.size] = '\0';
2286 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2287 makelower(keybuf);
2288 lockdb:
2289# if DB_VERSION_MAJOR < 2
2290 fd = db->fd(db);
2291# else /* DB_VERSION_MAJOR < 2 */
2292 fd = -1;
2293 errno = db->fd(db, &fd);
2294# endif /* DB_VERSION_MAJOR < 2 */
2295 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2296 (void) lockfile(fd, buf, ".db", LOCK_SH);
2297 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2298 {
2299 /* Reopen the database to sync the cache */
2300 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2301 : O_RDONLY;
2302
2303 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2304 (void) lockfile(fd, buf, ".db", LOCK_UN);
2305 map->map_mflags |= MF_CLOSING;
2306 map->map_class->map_close(map);
2307 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2308 if (map->map_class->map_open(map, omode))
2309 {
2310 map->map_mflags |= MF_OPEN;
2311 map->map_pid = CurrentPid;
2312 if ((omode && O_ACCMODE) == O_RDWR)
2313 map->map_mflags |= MF_WRITABLE;
2314 db = (DB *) map->map_db2;
2315 goto lockdb;
2316 }
2317 else
2318 {
2319 if (!bitset(MF_OPTIONAL, map->map_mflags))
2320 {
2321 extern MAPCLASS BogusMapClass;
2322
2323 *statp = EX_TEMPFAIL;
2324 map->map_orgclass = map->map_class;
2325 map->map_class = &BogusMapClass;
2326 map->map_mflags |= MF_OPEN;
2327 map->map_pid = CurrentPid;
2328 syserr("Cannot reopen DB database %s",
2329 map->map_file);
2330 }
2331 return NULL;
2332 }
2333 }
2334
2335 st = 1;
2336 if (bitset(MF_TRY0NULL, map->map_mflags))
2337 {
2338# if DB_VERSION_MAJOR < 2
2339 st = db->get(db, &key, &val, 0);
2340# else /* DB_VERSION_MAJOR < 2 */
2341 errno = db->get(db, NULL, &key, &val, 0);
2342 switch (errno)
2343 {
2344 case DB_NOTFOUND:
2345 case DB_KEYEMPTY:
2346 st = 1;
2347 break;
2348
2349 case 0:
2350 st = 0;
2351 break;
2352
2353 default:
2354 st = -1;
2355 break;
2356 }
2357# endif /* DB_VERSION_MAJOR < 2 */
2358 if (st == 0)
2359 map->map_mflags &= ~MF_TRY1NULL;
2360 }
2361 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2362 {
2363 key.size++;
2364# if DB_VERSION_MAJOR < 2
2365 st = db->get(db, &key, &val, 0);
2366# else /* DB_VERSION_MAJOR < 2 */
2367 errno = db->get(db, NULL, &key, &val, 0);
2368 switch (errno)
2369 {
2370 case DB_NOTFOUND:
2371 case DB_KEYEMPTY:
2372 st = 1;
2373 break;
2374
2375 case 0:
2376 st = 0;
2377 break;
2378
2379 default:
2380 st = -1;
2381 break;
2382 }
2383# endif /* DB_VERSION_MAJOR < 2 */
2384 if (st == 0)
2385 map->map_mflags &= ~MF_TRY0NULL;
2386 }
2387 save_errno = errno;
2388 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2389 (void) lockfile(fd, buf, ".db", LOCK_UN);
2390 if (st != 0)
2391 {
2392 errno = save_errno;
2393 if (st < 0)
2394 syserr("db_map_lookup: get (%s)", name);
2395 return NULL;
2396 }
2397 if (bitset(MF_MATCHONLY, map->map_mflags))
2398 return map_rewrite(map, name, strlen(name), NULL);
2399 else
2400 return map_rewrite(map, val.data, val.size, av);
2401}
2402
2403
2404/*
2405** DB_MAP_STORE -- store a datum in the NEWDB database
2406*/
2407
2408void
2409db_map_store(map, lhs, rhs)
2410 register MAP *map;
2411 char *lhs;
2412 char *rhs;
2413{
2414 int status;
2415 DBT key;
2416 DBT data;
2417 register DB *db = map->map_db2;
2418 char keybuf[MAXNAME + 1];
2419
2420 memset(&key, '\0', sizeof key);
2421 memset(&data, '\0', sizeof data);
2422
2423 if (tTd(38, 12))
2424 sm_dprintf("db_map_store(%s, %s, %s)\n",
2425 map->map_mname, lhs, rhs);
2426
2427 key.size = strlen(lhs);
2428 key.data = lhs;
2429 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2430 {
2431 if (key.size > sizeof keybuf - 1)
2432 key.size = sizeof keybuf - 1;
2433 memmove(keybuf, key.data, key.size);
2434 keybuf[key.size] = '\0';
2435 makelower(keybuf);
2436 key.data = keybuf;
2437 }
2438
2439 data.size = strlen(rhs);
2440 data.data = rhs;
2441
2442 if (bitset(MF_INCLNULL, map->map_mflags))
2443 {
2444 key.size++;
2445 data.size++;
2446 }
2447
2448# if DB_VERSION_MAJOR < 2
2449 status = db->put(db, &key, &data, R_NOOVERWRITE);
2450# else /* DB_VERSION_MAJOR < 2 */
2451 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2452 switch (errno)
2453 {
2454 case DB_KEYEXIST:
2455 status = 1;
2456 break;
2457
2458 case 0:
2459 status = 0;
2460 break;
2461
2462 default:
2463 status = -1;
2464 break;
2465 }
2466# endif /* DB_VERSION_MAJOR < 2 */
2467 if (status > 0)
2468 {
2469 if (!bitset(MF_APPEND, map->map_mflags))
2470 message("050 Warning: duplicate alias name %s", lhs);
2471 else
2472 {
2473 static char *buf = NULL;
2474 static int bufsiz = 0;
2475 DBT old;
2476
2477 memset(&old, '\0', sizeof old);
2478
2479 old.data = db_map_lookup(map, key.data,
2480 (char **) NULL, &status);
2481 if (old.data != NULL)
2482 {
2483 old.size = strlen(old.data);
2484 if (data.size + old.size + 2 > (size_t) bufsiz)
2485 {
2486 if (buf != NULL)
2487 sm_free(buf);
2488 bufsiz = data.size + old.size + 2;
2489 buf = sm_pmalloc_x(bufsiz);
2490 }
2491 (void) sm_strlcpyn(buf, bufsiz, 3,
2492 (char *) data.data, ",",
2493 (char *) old.data);
2494 data.size = data.size + old.size + 1;
2495 data.data = buf;
2496 if (tTd(38, 9))
2497 sm_dprintf("db_map_store append=%s\n",
2498 (char *) data.data);
2499 }
2500 }
2501# if DB_VERSION_MAJOR < 2
2502 status = db->put(db, &key, &data, 0);
2503# else /* DB_VERSION_MAJOR < 2 */
2504 status = errno = db->put(db, NULL, &key, &data, 0);
2505# endif /* DB_VERSION_MAJOR < 2 */
2506 }
2507 if (status != 0)
2508 syserr("readaliases: db put (%s)", lhs);
2509}
2510
2511
2512/*
2513** DB_MAP_CLOSE -- add distinguished entries and close the database
2514*/
2515
2516void
2517db_map_close(map)
2518 MAP *map;
2519{
2520 register DB *db = map->map_db2;
2521
2522 if (tTd(38, 9))
2523 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2524 map->map_mname, map->map_file, map->map_mflags);
2525
2526 if (bitset(MF_WRITABLE, map->map_mflags))
2527 {
2528 /* write out the distinguished alias */
2529 db_map_store(map, "@", "@");
2530 }
2531
2532 (void) db->sync(db, 0);
2533
2534# if !LOCK_ON_OPEN
2535 if (map->map_lockfd >= 0)
2536 (void) close(map->map_lockfd);
2537# endif /* !LOCK_ON_OPEN */
2538
2539# if DB_VERSION_MAJOR < 2
2540 if (db->close(db) != 0)
2541# else /* DB_VERSION_MAJOR < 2 */
2542 /*
2543 ** Berkeley DB can use internal shared memory
2544 ** locking for its memory pool. Closing a map
2545 ** opened by another process will interfere
2546 ** with the shared memory and locks of the parent
2547 ** process leaving things in a bad state.
2548 */
2549
2550 /*
2551 ** If this map was not opened by the current
2552 ** process, do not close the map but recover
2553 ** the file descriptor.
2554 */
2555
2556 if (map->map_pid != CurrentPid)
2557 {
2558 int fd = -1;
2559
2560 errno = db->fd(db, &fd);
2561 if (fd >= 0)
2562 (void) close(fd);
2563 return;
2564 }
2565
2566 if ((errno = db->close(db, 0)) != 0)
2567# endif /* DB_VERSION_MAJOR < 2 */
2568 syserr("db_map_close(%s, %s, %lx): db close failure",
2569 map->map_mname, map->map_file, map->map_mflags);
2570}
2571#endif /* NEWDB */
2572/*
2573** NIS Modules
2574*/
2575
2576#if NIS
2577
2578# ifndef YPERR_BUSY
2579# define YPERR_BUSY 16
2580# endif /* ! YPERR_BUSY */
2581
2582/*
2583** NIS_MAP_OPEN -- open DBM map
2584*/
2585
2586bool
2587nis_map_open(map, mode)
2588 MAP *map;
2589 int mode;
2590{
2591 int yperr;
2592 register char *p;
2593 auto char *vp;
2594 auto int vsize;
2595
2596 if (tTd(38, 2))
2597 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2598 map->map_mname, map->map_file, mode);
2599
2600 mode &= O_ACCMODE;
2601 if (mode != O_RDONLY)
2602 {
2603 /* issue a pseudo-error message */
2604 errno = SM_EMAPCANTWRITE;
2605 return false;
2606 }
2607
2608 p = strchr(map->map_file, '@');
2609 if (p != NULL)
2610 {
2611 *p++ = '\0';
2612 if (*p != '\0')
2613 map->map_domain = p;
2614 }
2615
2616 if (*map->map_file == '\0')
2617 map->map_file = "mail.aliases";
2618
2619 if (map->map_domain == NULL)
2620 {
2621 yperr = yp_get_default_domain(&map->map_domain);
2622 if (yperr != 0)
2623 {
2624 if (!bitset(MF_OPTIONAL, map->map_mflags))
2625 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2626 map->map_file);
2627 return false;
2628 }
2629 }
2630
2631 /* check to see if this map actually exists */
2632 vp = NULL;
2633 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2634 &vp, &vsize);
2635 if (tTd(38, 10))
2636 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2637 map->map_domain, map->map_file, yperr_string(yperr));
2638 if (vp != NULL)
2639 sm_free(vp);
2640
2641 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2642 {
2643 /*
2644 ** We ought to be calling aliaswait() here if this is an
2645 ** alias file, but powerful HP-UX NIS servers apparently
2646 ** don't insert the @:@ token into the alias map when it
2647 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2648 */
2649
2650# if 0
2651 if (!bitset(MF_ALIAS, map->map_mflags) ||
2652 aliaswait(map, NULL, true))
2653# endif /* 0 */
2654 return true;
2655 }
2656
2657 if (!bitset(MF_OPTIONAL, map->map_mflags))
2658 {
2659 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2660 map->map_file, map->map_domain, yperr_string(yperr));
2661 }
2662
2663 return false;
2664}
2665
2666
2667/*
2668** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2669*/
2670
2671/* ARGSUSED3 */
2672char *
2673nis_map_lookup(map, name, av, statp)
2674 MAP *map;
2675 char *name;
2676 char **av;
2677 int *statp;
2678{
2679 char *vp;
2680 auto int vsize;
2681 int buflen;
2682 int yperr;
2683 char keybuf[MAXNAME + 1];
2684 char *SM_NONVOLATILE result = NULL;
2685
2686 if (tTd(38, 20))
2687 sm_dprintf("nis_map_lookup(%s, %s)\n",
2688 map->map_mname, name);
2689
2690 buflen = strlen(name);
2691 if (buflen > sizeof keybuf - 1)
2692 buflen = sizeof keybuf - 1;
2693 memmove(keybuf, name, buflen);
2694 keybuf[buflen] = '\0';
2695 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2696 makelower(keybuf);
2697 yperr = YPERR_KEY;
2698 vp = NULL;
2699 if (bitset(MF_TRY0NULL, map->map_mflags))
2700 {
2701 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2702 &vp, &vsize);
2703 if (yperr == 0)
2704 map->map_mflags &= ~MF_TRY1NULL;
2705 }
2706 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2707 {
2708 SM_FREE_CLR(vp);
2709 buflen++;
2710 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2711 &vp, &vsize);
2712 if (yperr == 0)
2713 map->map_mflags &= ~MF_TRY0NULL;
2714 }
2715 if (yperr != 0)
2716 {
2717 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2718 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2719 if (vp != NULL)
2720 sm_free(vp);
2721 return NULL;
2722 }
2723 SM_TRY
2724 if (bitset(MF_MATCHONLY, map->map_mflags))
2725 result = map_rewrite(map, name, strlen(name), NULL);
2726 else
2727 result = map_rewrite(map, vp, vsize, av);
2728 SM_FINALLY
2729 if (vp != NULL)
2730 sm_free(vp);
2731 SM_END_TRY
2732 return result;
2733}
2734
2735
2736/*
2737** NIS_GETCANONNAME -- look up canonical name in NIS
2738*/
2739
2740static bool
2741nis_getcanonname(name, hbsize, statp)
2742 char *name;
2743 int hbsize;
2744 int *statp;
2745{
2746 char *vp;
2747 auto int vsize;
2748 int keylen;
2749 int yperr;
2750 static bool try0null = true;
2751 static bool try1null = true;
2752 static char *yp_domain = NULL;
2753 char host_record[MAXLINE];
2754 char cbuf[MAXNAME];
2755 char nbuf[MAXNAME + 1];
2756
2757 if (tTd(38, 20))
2758 sm_dprintf("nis_getcanonname(%s)\n", name);
2759
2760 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
2761 {
2762 *statp = EX_UNAVAILABLE;
2763 return false;
2764 }
2765 (void) shorten_hostname(nbuf);
2766 keylen = strlen(nbuf);
2767
2768 if (yp_domain == NULL)
2769 (void) yp_get_default_domain(&yp_domain);
2770 makelower(nbuf);
2771 yperr = YPERR_KEY;
2772 vp = NULL;
2773 if (try0null)
2774 {
2775 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2776 &vp, &vsize);
2777 if (yperr == 0)
2778 try1null = false;
2779 }
2780 if (yperr == YPERR_KEY && try1null)
2781 {
2782 SM_FREE_CLR(vp);
2783 keylen++;
2784 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2785 &vp, &vsize);
2786 if (yperr == 0)
2787 try0null = false;
2788 }
2789 if (yperr != 0)
2790 {
2791 if (yperr == YPERR_KEY)
2792 *statp = EX_NOHOST;
2793 else if (yperr == YPERR_BUSY)
2794 *statp = EX_TEMPFAIL;
2795 else
2796 *statp = EX_UNAVAILABLE;
2797 if (vp != NULL)
2798 sm_free(vp);
2799 return false;
2800 }
2801 (void) sm_strlcpy(host_record, vp, sizeof host_record);
2802 sm_free(vp);
2803 if (tTd(38, 44))
2804 sm_dprintf("got record `%s'\n", host_record);
2805 vp = strpbrk(host_record, "#\n");
2806 if (vp != NULL)
2807 *vp = '\0';
2808 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
2809 {
2810 /* this should not happen, but.... */
2811 *statp = EX_NOHOST;
2812 return false;
2813 }
2814 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2815 {
2816 *statp = EX_UNAVAILABLE;
2817 return false;
2818 }
2819 *statp = EX_OK;
2820 return true;
2821}
2822
2823#endif /* NIS */
2824/*
2825** NISPLUS Modules
2826**
2827** This code donated by Sun Microsystems.
2828*/
2829
2830#if NISPLUS
2831
2832# undef NIS /* symbol conflict in nis.h */
2833# undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2834# include <rpcsvc/nis.h>
2835# include <rpcsvc/nislib.h>
2836
2837# define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2838# define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2839# define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2840# define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2841
2842/*
2843** NISPLUS_MAP_OPEN -- open nisplus table
2844*/
2845
2846bool
2847nisplus_map_open(map, mode)
2848 MAP *map;
2849 int mode;
2850{
2851 nis_result *res = NULL;
2852 int retry_cnt, max_col, i;
2853 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2854
2855 if (tTd(38, 2))
2856 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2857 map->map_mname, map->map_file, mode);
2858
2859 mode &= O_ACCMODE;
2860 if (mode != O_RDONLY)
2861 {
2862 errno = EPERM;
2863 return false;
2864 }
2865
2866 if (*map->map_file == '\0')
2867 map->map_file = "mail_aliases.org_dir";
2868
2869 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2870 {
2871 /* set default NISPLUS Domain to $m */
2872 map->map_domain = newstr(nisplus_default_domain());
2873 if (tTd(38, 2))
2874 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2875 map->map_file, map->map_domain);
2876 }
2877 if (!PARTIAL_NAME(map->map_file))
2878 {
2879 map->map_domain = newstr("");
2880 (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
2881 }
2882 else
2883 {
2884 /* check to see if this map actually exists */
2885 (void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
2886 map->map_file, ".", map->map_domain);
2887 }
2888
2889 retry_cnt = 0;
2890 while (res == NULL || res->status != NIS_SUCCESS)
2891 {
2892 res = nis_lookup(qbuf, FOLLOW_LINKS);
2893 switch (res->status)
2894 {
2895 case NIS_SUCCESS:
2896 break;
2897
2898 case NIS_TRYAGAIN:
2899 case NIS_RPCERROR:
2900 case NIS_NAMEUNREACHABLE:
2901 if (retry_cnt++ > 4)
2902 {
2903 errno = EAGAIN;
2904 return false;
2905 }
2906 /* try not to overwhelm hosed server */
2907 sleep(2);
2908 break;
2909
2910 default: /* all other nisplus errors */
2911# if 0
2912 if (!bitset(MF_OPTIONAL, map->map_mflags))
2913 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2914 map->map_file, map->map_domain,
2915 nis_sperrno(res->status));
2916# endif /* 0 */
2917 errno = EAGAIN;
2918 return false;
2919 }
2920 }
2921
2922 if (NIS_RES_NUMOBJ(res) != 1 ||
2923 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2924 {
2925 if (tTd(38, 10))
2926 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2927# if 0
2928 if (!bitset(MF_OPTIONAL, map->map_mflags))
2929 syserr("451 4.3.5 %s.%s: %s is not a table",
2930 map->map_file, map->map_domain,
2931 nis_sperrno(res->status));
2932# endif /* 0 */
2933 errno = EBADF;
2934 return false;
2935 }
2936 /* default key column is column 0 */
2937 if (map->map_keycolnm == NULL)
2938 map->map_keycolnm = newstr(COL_NAME(res,0));
2939
2940 max_col = COL_MAX(res);
2941
2942 /* verify the key column exist */
2943 for (i = 0; i < max_col; i++)
2944 {
2945 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2946 break;
2947 }
2948 if (i == max_col)
2949 {
2950 if (tTd(38, 2))
2951 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2952 map->map_file, map->map_keycolnm);
2953 errno = ENOENT;
2954 return false;
2955 }
2956
2957 /* default value column is the last column */
2958 if (map->map_valcolnm == NULL)
2959 {
2960 map->map_valcolno = max_col - 1;
2961 return true;
2962 }
2963
2964 for (i = 0; i< max_col; i++)
2965 {
2966 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2967 {
2968 map->map_valcolno = i;
2969 return true;
2970 }
2971 }
2972
2973 if (tTd(38, 2))
2974 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2975 map->map_file, map->map_keycolnm);
2976 errno = ENOENT;
2977 return false;
2978}
2979
2980
2981/*
2982** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2983*/
2984
2985char *
2986nisplus_map_lookup(map, name, av, statp)
2987 MAP *map;
2988 char *name;
2989 char **av;
2990 int *statp;
2991{
2992 char *p;
2993 auto int vsize;
2994 char *skp;
2995 int skleft;
2996 char search_key[MAXNAME + 4];
2997 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2998 nis_result *result;
2999
3000 if (tTd(38, 20))
3001 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3002 map->map_mname, name);
3003
3004 if (!bitset(MF_OPEN, map->map_mflags))
3005 {
3006 if (nisplus_map_open(map, O_RDONLY))
3007 {
3008 map->map_mflags |= MF_OPEN;
3009 map->map_pid = CurrentPid;
3010 }
3011 else
3012 {
3013 *statp = EX_UNAVAILABLE;
3014 return NULL;
3015 }
3016 }
3017
3018 /*
3019 ** Copy the name to the key buffer, escaping double quote characters
3020 ** by doubling them and quoting "]" and "," to avoid having the
3021 ** NIS+ parser choke on them.
3022 */
3023
3024 skleft = sizeof search_key - 4;
3025 skp = search_key;
3026 for (p = name; *p != '\0' && skleft > 0; p++)
3027 {
3028 switch (*p)
3029 {
3030 case ']':
3031 case ',':
3032 /* quote the character */
3033 *skp++ = '"';
3034 *skp++ = *p;
3035 *skp++ = '"';
3036 skleft -= 3;
3037 break;
3038
3039 case '"':
3040 /* double the quote */
3041 *skp++ = '"';
3042 skleft--;
3043 /* FALLTHROUGH */
3044
3045 default:
3046 *skp++ = *p;
3047 skleft--;
3048 break;
3049 }
3050 }
3051 *skp = '\0';
3052 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3053 makelower(search_key);
3054
3055 /* construct the query */
3056 if (PARTIAL_NAME(map->map_file))
3057 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
3058 map->map_keycolnm, search_key, map->map_file,
3059 map->map_domain);
3060 else
3061 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
3062 map->map_keycolnm, search_key, map->map_file);
3063
3064 if (tTd(38, 20))
3065 sm_dprintf("qbuf=%s\n", qbuf);
3066 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3067 if (result->status == NIS_SUCCESS)
3068 {
3069 int count;
3070 char *str;
3071
3072 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3073 {
3074 if (LogLevel > 10)
3075 sm_syslog(LOG_WARNING, CurEnv->e_id,
3076 "%s: lookup error, expected 1 entry, got %d",
3077 map->map_file, count);
3078
3079 /* ignore second entry */
3080 if (tTd(38, 20))
3081 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3082 name, count);
3083 }
3084
3085 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3086 /* set the length of the result */
3087 if (p == NULL)
3088 p = "";
3089 vsize = strlen(p);
3090 if (tTd(38, 20))
3091 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3092 name, p);
3093 if (bitset(MF_MATCHONLY, map->map_mflags))
3094 str = map_rewrite(map, name, strlen(name), NULL);
3095 else
3096 str = map_rewrite(map, p, vsize, av);
3097 nis_freeresult(result);
3098 *statp = EX_OK;
3099 return str;
3100 }
3101 else
3102 {
3103 if (result->status == NIS_NOTFOUND)
3104 *statp = EX_NOTFOUND;
3105 else if (result->status == NIS_TRYAGAIN)
3106 *statp = EX_TEMPFAIL;
3107 else
3108 {
3109 *statp = EX_UNAVAILABLE;
3110 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3111 }
3112 }
3113 if (tTd(38, 20))
3114 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3115 nis_freeresult(result);
3116 return NULL;
3117}
3118
3119
3120
3121/*
3122** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3123*/
3124
3125static bool
3126nisplus_getcanonname(name, hbsize, statp)
3127 char *name;
3128 int hbsize;
3129 int *statp;
3130{
3131 char *vp;
3132 auto int vsize;
3133 nis_result *result;
3134 char *p;
3135 char nbuf[MAXNAME + 1];
3136 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3137
3138 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
3139 {
3140 *statp = EX_UNAVAILABLE;
3141 return false;
3142 }
3143 (void) shorten_hostname(nbuf);
3144
3145 p = strchr(nbuf, '.');
3146 if (p == NULL)
3147 {
3148 /* single token */
3149 (void) sm_snprintf(qbuf, sizeof qbuf,
3150 "[name=%s],hosts.org_dir", nbuf);
3151 }
3152 else if (p[1] != '\0')
3153 {
3154 /* multi token -- take only first token in nbuf */
3155 *p = '\0';
3156 (void) sm_snprintf(qbuf, sizeof qbuf,
3157 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3158 }
3159 else
3160 {
3161 *statp = EX_NOHOST;
3162 return false;
3163 }
3164
3165 if (tTd(38, 20))
3166 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3167 name, qbuf);
3168
3169 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3170 NULL, NULL);
3171
3172 if (result->status == NIS_SUCCESS)
3173 {
3174 int count;
3175 char *domain;
3176
3177 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3178 {
3179 if (LogLevel > 10)
3180 sm_syslog(LOG_WARNING, CurEnv->e_id,
3181 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3182 count);
3183
3184 /* ignore second entry */
3185 if (tTd(38, 20))
3186 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3187 name, count);
3188 }
3189
3190 if (tTd(38, 20))
3191 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3192 name, (NIS_RES_OBJECT(result))->zo_domain);
3193
3194
3195 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3196 vsize = strlen(vp);
3197 if (tTd(38, 20))
3198 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3199 name, vp);
3200 if (strchr(vp, '.') != NULL)
3201 {
3202 domain = "";
3203 }
3204 else
3205 {
3206 domain = macvalue('m', CurEnv);
3207 if (domain == NULL)
3208 domain = "";
3209 }
3210 if (hbsize > vsize + (int) strlen(domain) + 1)
3211 {
3212 if (domain[0] == '\0')
3213 (void) sm_strlcpy(name, vp, hbsize);
3214 else
3215 (void) sm_snprintf(name, hbsize,
3216 "%s.%s", vp, domain);
3217 *statp = EX_OK;
3218 }
3219 else
3220 *statp = EX_NOHOST;
3221 nis_freeresult(result);
3222 return true;
3223 }
3224 else
3225 {
3226 if (result->status == NIS_NOTFOUND)
3227 *statp = EX_NOHOST;
3228 else if (result->status == NIS_TRYAGAIN)
3229 *statp = EX_TEMPFAIL;
3230 else
3231 *statp = EX_UNAVAILABLE;
3232 }
3233 if (tTd(38, 20))
3234 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3235 name, result->status, *statp);
3236 nis_freeresult(result);
3237 return false;
3238}
3239
3240char *
3241nisplus_default_domain()
3242{
3243 static char default_domain[MAXNAME + 1] = "";
3244 char *p;
3245
3246 if (default_domain[0] != '\0')
3247 return default_domain;
3248
3249 p = nis_local_directory();
3250 (void) sm_strlcpy(default_domain, p, sizeof default_domain);
3251 return default_domain;
3252}
3253
3254#endif /* NISPLUS */
3255/*
3256** LDAP Modules
3257*/
3258
3259/*
3260** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3261*/
3262
3263#if defined(LDAPMAP) || defined(PH_MAP)
3264
3265# if PH_MAP
3266# define ph_map_dequote ldapmap_dequote
3267# endif /* PH_MAP */
3268
3269static char *ldapmap_dequote __P((char *));
3270
3271static char *
3272ldapmap_dequote(str)
3273 char *str;
3274{
3275 char *p;
3276 char *start;
3277
3278 if (str == NULL)
3279 return NULL;
3280
3281 p = str;
3282 if (*p == '"')
3283 {
3284 /* Should probably swallow initial whitespace here */
3285 start = ++p;
3286 }
3287 else
3288 return str;
3289 while (*p != '"' && *p != '\0')
3290 p++;
3291 if (*p != '\0')
3292 *p = '\0';
3293 return start;
3294}
3295#endif /* defined(LDAPMAP) || defined(PH_MAP) */
3296
3297#if LDAPMAP
3298
3299static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3300
3301/*
3302** LDAPMAP_OPEN -- open LDAP map
3303**
3304** Connect to the LDAP server. Re-use existing connections since a
3305** single server connection to a host (with the same host, port,
3306** bind DN, and secret) can answer queries for multiple maps.
3307*/
3308
3309bool
3310ldapmap_open(map, mode)
3311 MAP *map;
3312 int mode;
3313{
3314 SM_LDAP_STRUCT *lmap;
3315 STAB *s;
3316 char *id;
3317
3318 if (tTd(38, 2))
3319 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3320
3321 mode &= O_ACCMODE;
3322
3323 /* sendmail doesn't have the ability to write to LDAP (yet) */
3324 if (mode != O_RDONLY)
3325 {
3326 /* issue a pseudo-error message */
3327 errno = SM_EMAPCANTWRITE;
3328 return false;
3329 }
3330
3331 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3332
3333 s = ldapmap_findconn(lmap);
3334 if (s->s_lmap != NULL)
3335 {
3336 /* Already have a connection open to this LDAP server */
3337 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3338 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3339
3340 /* Add this map as head of linked list */
3341 lmap->ldap_next = s->s_lmap;
3342 s->s_lmap = map;
3343
3344 if (tTd(38, 2))
3345 sm_dprintf("using cached connection\n");
3346 return true;
3347 }
3348
3349 if (tTd(38, 2))
3350 sm_dprintf("opening new connection\n");
3351
3352 if (lmap->ldap_host != NULL)
3353 id = lmap->ldap_host;
3354 else if (lmap->ldap_uri != NULL)
3355 id = lmap->ldap_uri;
3356 else
3357 id = "localhost";
3358
3359 /* No connection yet, connect */
3360 if (!sm_ldap_start(map->map_mname, lmap))
3361 {
3362 if (errno == ETIMEDOUT)
3363 {
3364 if (LogLevel > 1)
3365 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3366 "timeout conning to LDAP server %.100s",
3367 id);
3368 }
3369
3370 if (!bitset(MF_OPTIONAL, map->map_mflags))
3371 {
3372 if (bitset(MF_NODEFER, map->map_mflags))
3373 {
3374 syserr("%s failed to %s in map %s",
3375# if USE_LDAP_INIT
3376 "ldap_init/ldap_bind",
3377# else /* USE_LDAP_INIT */
3378 "ldap_open",
3379# endif /* USE_LDAP_INIT */
3380 id, map->map_mname);
3381 }
3382 else
3383 {
3384 syserr("451 4.3.5 %s failed to %s in map %s",
3385# if USE_LDAP_INIT
3386 "ldap_init/ldap_bind",
3387# else /* USE_LDAP_INIT */
3388 "ldap_open",
3389# endif /* USE_LDAP_INIT */
3390 id, map->map_mname);
3391 }
3392 }
3393 return false;
3394 }
3395
3396 /* Save connection for reuse */
3397 s->s_lmap = map;
3398 return true;
3399}
3400
3401/*
3402** LDAPMAP_CLOSE -- close ldap map
3403*/
3404
3405void
3406ldapmap_close(map)
3407 MAP *map;
3408{
3409 SM_LDAP_STRUCT *lmap;
3410 STAB *s;
3411
3412 if (tTd(38, 2))
3413 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3414
3415 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3416
3417 /* Check if already closed */
3418 if (lmap->ldap_ld == NULL)
3419 return;
3420
3421 /* Close the LDAP connection */
3422 sm_ldap_close(lmap);
3423
3424 /* Mark all the maps that share the connection as closed */
3425 s = ldapmap_findconn(lmap);
3426
3427 while (s->s_lmap != NULL)
3428 {
3429 MAP *smap = s->s_lmap;
3430
3431 if (tTd(38, 2) && smap != map)
3432 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3433 map->map_mname, smap->map_mname);
3434 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3435 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3436 lmap->ldap_ld = NULL;
3437 s->s_lmap = lmap->ldap_next;
3438 lmap->ldap_next = NULL;
3439 }
3440}
3441
3442# ifdef SUNET_ID
3443/*
3444** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3445** This only makes sense at Stanford University.
3446*/
3447
3448static char *
3449sunet_id_hash(str)
3450 char *str;
3451{
3452 char *p, *p_last;
3453
3454 p = str;
3455 p_last = p;
3456 while (*p != '\0')
3457 {
3458 if (islower(*p) || isdigit(*p))
3459 {
3460 *p_last = *p;
3461 p_last++;
3462 }
3463 else if (isupper(*p))
3464 {
3465 *p_last = tolower(*p);
3466 p_last++;
3467 }
3468 ++p;
3469 }
3470 if (*p_last != '\0')
3471 *p_last = '\0';
3472 return str;
3473}
3474# endif /* SUNET_ID */
3475
3476/*
3477** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3478*/
3479
3480char *
3481ldapmap_lookup(map, name, av, statp)
3482 MAP *map;
3483 char *name;
3484 char **av;
3485 int *statp;
3486{
3487 int flags;
3488 int plen = 0;
3489 int psize = 0;
3490 int msgid;
3491 int save_errno;
3492 char *vp, *p;
3493 char *result = NULL;
3494 SM_RPOOL_T *rpool;
3495 SM_LDAP_STRUCT *lmap = NULL;
3496 char keybuf[MAXNAME + 1];
3497
3498 if (tTd(38, 20))
3499 sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3500
3501 /* Get ldap struct pointer from map */
3502 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3503 sm_ldap_setopts(lmap->ldap_ld, lmap);
3504
3505 (void) sm_strlcpy(keybuf, name, sizeof keybuf);
3506
3507 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3508 {
3509# ifdef SUNET_ID
3510 sunet_id_hash(keybuf);
3511# else /* SUNET_ID */
3512 makelower(keybuf);
3513# endif /* SUNET_ID */
3514 }
3515
3516 msgid = sm_ldap_search(lmap, keybuf);
3517 if (msgid == -1)
3518 {
3519 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3520 save_errno = errno;
3521 if (!bitset(MF_OPTIONAL, map->map_mflags))
3522 {
3523 if (bitset(MF_NODEFER, map->map_mflags))
3524 syserr("Error in ldap_search using %s in map %s",
3525 keybuf, map->map_mname);
3526 else
3527 syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3528 keybuf, map->map_mname);
3529 }
3530 *statp = EX_TEMPFAIL;
3531 switch (save_errno - E_LDAPBASE)
3532 {
3533# ifdef LDAP_SERVER_DOWN
3534 case LDAP_SERVER_DOWN:
3535# endif /* LDAP_SERVER_DOWN */
3536 case LDAP_TIMEOUT:
3537 case LDAP_UNAVAILABLE:
3538 /* server disappeared, try reopen on next search */
3539 ldapmap_close(map);
3540 break;
3541 }
3542 errno = save_errno;
3543 return NULL;
3544 }
3545
3546 *statp = EX_NOTFOUND;
3547 vp = NULL;
3548
3549 flags = 0;
3550 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3551 flags |= SM_LDAP_SINGLEMATCH;
3552 if (bitset(MF_MATCHONLY, map->map_mflags))
3553 flags |= SM_LDAP_MATCHONLY;
3554
3555 /* Create an rpool for search related memory usage */
3556 rpool = sm_rpool_new_x(NULL);
3557
3558 p = NULL;
3559 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3560 rpool, &p, &plen, &psize, NULL);
3561 save_errno = errno;
3562
3563 /* Copy result so rpool can be freed */
3564 if (*statp == EX_OK && p != NULL)
3565 vp = newstr(p);
3566 sm_rpool_free(rpool);
3567
3568 /* need to restart LDAP connection? */
3569 if (*statp == EX_RESTART)
3570 {
3571 *statp = EX_TEMPFAIL;
3572 ldapmap_close(map);
3573 }
3574
3575 errno = save_errno;
3576 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3577 {
3578 if (!bitset(MF_OPTIONAL, map->map_mflags))
3579 {
3580 if (bitset(MF_NODEFER, map->map_mflags))
3581 syserr("Error getting LDAP results in map %s",
3582 map->map_mname);
3583 else
3584 syserr("451 4.3.5 Error getting LDAP results in map %s",
3585 map->map_mname);
3586 }
3587 errno = save_errno;
3588 return NULL;
3589 }
3590
3591 /* Did we match anything? */
3592 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3593 return NULL;
3594
3595 if (*statp == EX_OK)
3596 {
3597 if (LogLevel > 9)
3598 sm_syslog(LOG_INFO, CurEnv->e_id,
3599 "ldap %.100s => %s", name,
3600 vp == NULL ? "<NULL>" : vp);
3601 if (bitset(MF_MATCHONLY, map->map_mflags))
3602 result = map_rewrite(map, name, strlen(name), NULL);
3603 else
3604 {
3605 /* vp != NULL according to test above */
3606 result = map_rewrite(map, vp, strlen(vp), av);
3607 }
3608 if (vp != NULL)
3609 sm_free(vp); /* XXX */
3610 }
3611 return result;
3612}
3613
3614/*
3615** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3616**
3617** Cache LDAP connections based on the host, port, bind DN,
3618** secret, and PID so we don't have multiple connections open to
3619** the same server for different maps. Need a separate connection
3620** per PID since a parent process may close the map before the
3621** child is done with it.
3622**
3623** Parameters:
3624** lmap -- LDAP map information
3625**
3626** Returns:
3627** Symbol table entry for the LDAP connection.
3628*/
3629
3630static STAB *
3631ldapmap_findconn(lmap)
3632 SM_LDAP_STRUCT *lmap;
3633{
3634 char *format;
3635 char *nbuf;
3636 char *id;
3637 STAB *SM_NONVOLATILE s = NULL;
3638
3639 if (lmap->ldap_host != NULL)
3640 id = lmap->ldap_host;
3641 else if (lmap->ldap_uri != NULL)
3642 id = lmap->ldap_uri;
3643 else
3644 id = "localhost";
3645
3646 format = "%s%c%d%c%d%c%s%c%s%d";
3647 nbuf = sm_stringf_x(format,
3648 id,
3649 CONDELSE,
3650 lmap->ldap_port,
3651 CONDELSE,
3652 lmap->ldap_version,
3653 CONDELSE,
3654 (lmap->ldap_binddn == NULL ? ""
3655 : lmap->ldap_binddn),
3656 CONDELSE,
3657 (lmap->ldap_secret == NULL ? ""
3658 : lmap->ldap_secret),
3659 (int) CurrentPid);
3660 SM_TRY
3661 s = stab(nbuf, ST_LMAP, ST_ENTER);
3662 SM_FINALLY
3663 sm_free(nbuf);
3664 SM_END_TRY
3665 return s;
3666}
3667/*
3668** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3669*/
3670
3671static struct lamvalues LDAPAuthMethods[] =
3672{
3673 { "none", LDAP_AUTH_NONE },
3674 { "simple", LDAP_AUTH_SIMPLE },
3675# ifdef LDAP_AUTH_KRBV4
3676 { "krbv4", LDAP_AUTH_KRBV4 },
3677# endif /* LDAP_AUTH_KRBV4 */
3678 { NULL, 0 }
3679};
3680
3681static struct ladvalues LDAPAliasDereference[] =
3682{
3683 { "never", LDAP_DEREF_NEVER },
3684 { "always", LDAP_DEREF_ALWAYS },
3685 { "search", LDAP_DEREF_SEARCHING },
3686 { "find", LDAP_DEREF_FINDING },
3687 { NULL, 0 }
3688};
3689
3690static struct lssvalues LDAPSearchScope[] =
3691{
3692 { "base", LDAP_SCOPE_BASE },
3693 { "one", LDAP_SCOPE_ONELEVEL },
3694 { "sub", LDAP_SCOPE_SUBTREE },
3695 { NULL, 0 }
3696};
3697
3698bool
3699ldapmap_parseargs(map, args)
3700 MAP *map;
3701 char *args;
3702{
3703 bool secretread = true;
3704 bool attrssetup = false;
3705 int i;
3706 register char *p = args;
3707 SM_LDAP_STRUCT *lmap;
3708 struct lamvalues *lam;
3709 struct ladvalues *lad;
3710 struct lssvalues *lss;
3711 char ldapfilt[MAXLINE];
3712 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3713
3714 /* Get ldap struct pointer from map */
3715 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3716
3717 /* Check if setting the initial LDAP defaults */
3718 if (lmap == NULL || lmap != LDAPDefaults)
3719 {
3720 /* We need to alloc an SM_LDAP_STRUCT struct */
3721 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
3722 if (LDAPDefaults == NULL)
3723 sm_ldap_clear(lmap);
3724 else
3725 STRUCTCOPY(*LDAPDefaults, *lmap);
3726 }
3727
3728 /* there is no check whether there is really an argument */
3729 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3730 map->map_spacesub = SpaceSub; /* default value */
3731
3732 /* Check if setting up an alias or file class LDAP map */
3733 if (bitset(MF_ALIAS, map->map_mflags))
3734 {
3735 /* Comma separate if used as an alias file */
3736 map->map_coldelim = ',';
3737 if (*args == '\0')
3738 {
3739 int n;
3740 char *lc;
3741 char jbuf[MAXHOSTNAMELEN];
3742 char lcbuf[MAXLINE];
3743
3744 /* Get $j */
3745 expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
3746 if (jbuf[0] == '\0')
3747 {
3748 (void) sm_strlcpy(jbuf, "localhost",
3749 sizeof jbuf);
3750 }
3751
3752 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3753 if (lc == NULL)
3754 lc = "";
3755 else
3756 {
3757 expand(lc, lcbuf, sizeof lcbuf, CurEnv);
3758 lc = lcbuf;
3759 }
3760
3761 n = sm_snprintf(ldapfilt, sizeof ldapfilt,
3762 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3763 lc, jbuf);
3764 if (n >= sizeof ldapfilt)
3765 {
3766 syserr("%s: Default LDAP string too long",
3767 map->map_mname);
3768 return false;
3769 }
3770
3771 /* default args for an alias LDAP entry */
3772 lmap->ldap_filter = ldapfilt;
3773 lmap->ldap_attr[0] = "objectClass";
3774 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3775 lmap->ldap_attr_needobjclass[0] = NULL;
3776 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3777 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3778 lmap->ldap_attr_needobjclass[1] = NULL;
3779 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3780 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3781 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3782 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3783 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3784 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3785 lmap->ldap_attr[4] = NULL;
3786 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3787 lmap->ldap_attr_needobjclass[4] = NULL;
3788 attrssetup = true;
3789 }
3790 }
3791 else if (bitset(MF_FILECLASS, map->map_mflags))
3792 {
3793 /* Space separate if used as a file class file */
3794 map->map_coldelim = ' ';
3795 }
3796
3797 for (;;)
3798 {
3799 while (isascii(*p) && isspace(*p))
3800 p++;
3801 if (*p != '-')
3802 break;
3803 switch (*++p)
3804 {
3805 case 'N':
3806 map->map_mflags |= MF_INCLNULL;
3807 map->map_mflags &= ~MF_TRY0NULL;
3808 break;
3809
3810 case 'O':
3811 map->map_mflags &= ~MF_TRY1NULL;
3812 break;
3813
3814 case 'o':
3815 map->map_mflags |= MF_OPTIONAL;
3816 break;
3817
3818 case 'f':
3819 map->map_mflags |= MF_NOFOLDCASE;
3820 break;
3821
3822 case 'm':
3823 map->map_mflags |= MF_MATCHONLY;
3824 break;
3825
3826 case 'A':
3827 map->map_mflags |= MF_APPEND;
3828 break;
3829
3830 case 'q':
3831 map->map_mflags |= MF_KEEPQUOTES;
3832 break;
3833
3834 case 'a':
3835 map->map_app = ++p;
3836 break;
3837
3838 case 'T':
3839 map->map_tapp = ++p;
3840 break;
3841
3842 case 't':
3843 map->map_mflags |= MF_NODEFER;
3844 break;
3845
3846 case 'S':
3847 map->map_spacesub = *++p;
3848 break;
3849
3850 case 'D':
3851 map->map_mflags |= MF_DEFER;
3852 break;
3853
3854 case 'z':
3855 if (*++p != '\\')
3856 map->map_coldelim = *p;
3857 else
3858 {
3859 switch (*++p)
3860 {
3861 case 'n':
3862 map->map_coldelim = '\n';
3863 break;
3864
3865 case 't':
3866 map->map_coldelim = '\t';
3867 break;
3868
3869 default:
3870 map->map_coldelim = '\\';
3871 }
3872 }
3873 break;
3874
3875 /* Start of ldapmap specific args */
3876 case 'V':
3877 if (*++p != '\\')
3878 lmap->ldap_attrsep = *p;
3879 else
3880 {
3881 switch (*++p)
3882 {
3883 case 'n':
3884 lmap->ldap_attrsep = '\n';
3885 break;
3886
3887 case 't':
3888 lmap->ldap_attrsep = '\t';
3889 break;
3890
3891 default:
3892 lmap->ldap_attrsep = '\\';
3893 }
3894 }
3895 break;
3896
3897 case 'k': /* search field */
3898 while (isascii(*++p) && isspace(*p))
3899 continue;
3900 lmap->ldap_filter = p;
3901 break;
3902
3903 case 'v': /* attr to return */
3904 while (isascii(*++p) && isspace(*p))
3905 continue;
3906 lmap->ldap_attr[0] = p;
3907 lmap->ldap_attr[1] = NULL;
3908 break;
3909
3910 case '1':
3911 map->map_mflags |= MF_SINGLEMATCH;
3912 break;
3913
3914 /* args stolen from ldapsearch.c */
3915 case 'R': /* don't auto chase referrals */
3916# ifdef LDAP_REFERRALS
3917 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3918# else /* LDAP_REFERRALS */
3919 syserr("compile with -DLDAP_REFERRALS for referral support");
3920# endif /* LDAP_REFERRALS */
3921 break;
3922
3923 case 'n': /* retrieve attribute names only */
3924 lmap->ldap_attrsonly = LDAPMAP_TRUE;
3925 break;
3926
3927 case 'r': /* alias dereferencing */
3928 while (isascii(*++p) && isspace(*p))
3929 continue;
3930
3931 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
3932 p += 11;
3933
3934 for (lad = LDAPAliasDereference;
3935 lad != NULL && lad->lad_name != NULL; lad++)
3936 {
3937 if (sm_strncasecmp(p, lad->lad_name,
3938 strlen(lad->lad_name)) == 0)
3939 break;
3940 }
3941 if (lad->lad_name != NULL)
3942 lmap->ldap_deref = lad->lad_code;
3943 else
3944 {
3945 /* bad config line */
3946 if (!bitset(MCF_OPTFILE,
3947 map->map_class->map_cflags))
3948 {
3949 char *ptr;
3950
3951 if ((ptr = strchr(p, ' ')) != NULL)
3952 *ptr = '\0';
3953 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
3954 p, map->map_mname);
3955 if (ptr != NULL)
3956 *ptr = ' ';
3957 return false;
3958 }
3959 }
3960 break;
3961
3962 case 's': /* search scope */
3963 while (isascii(*++p) && isspace(*p))
3964 continue;
3965
3966 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
3967 p += 11;
3968
3969 for (lss = LDAPSearchScope;
3970 lss != NULL && lss->lss_name != NULL; lss++)
3971 {
3972 if (sm_strncasecmp(p, lss->lss_name,
3973 strlen(lss->lss_name)) == 0)
3974 break;
3975 }
3976 if (lss->lss_name != NULL)
3977 lmap->ldap_scope = lss->lss_code;
3978 else
3979 {
3980 /* bad config line */
3981 if (!bitset(MCF_OPTFILE,
3982 map->map_class->map_cflags))
3983 {
3984 char *ptr;
3985
3986 if ((ptr = strchr(p, ' ')) != NULL)
3987 *ptr = '\0';
3988 syserr("Scope must be [base|one|sub] (not %s) in map %s",
3989 p, map->map_mname);
3990 if (ptr != NULL)
3991 *ptr = ' ';
3992 return false;
3993 }
3994 }
3995 break;
3996
3997 case 'h': /* ldap host */
3998 while (isascii(*++p) && isspace(*p))
3999 continue;
4000 if (lmap->ldap_uri != NULL)
4001 {
4002 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4003 map->map_mname);
4004 return false;
4005 }
4006 lmap->ldap_host = p;
4007 break;
4008
4009 case 'b': /* search base */
4010 while (isascii(*++p) && isspace(*p))
4011 continue;
4012 lmap->ldap_base = p;
4013 break;
4014
4015 case 'p': /* ldap port */
4016 while (isascii(*++p) && isspace(*p))
4017 continue;
4018 lmap->ldap_port = atoi(p);
4019 break;
4020
4021 case 'l': /* time limit */
4022 while (isascii(*++p) && isspace(*p))
4023 continue;
4024 lmap->ldap_timelimit = atoi(p);
4025 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4026 break;
4027
4028 case 'Z':
4029 while (isascii(*++p) && isspace(*p))
4030 continue;
4031 lmap->ldap_sizelimit = atoi(p);
4032 break;
4033
4034 case 'd': /* Dn to bind to server as */
4035 while (isascii(*++p) && isspace(*p))
4036 continue;
4037 lmap->ldap_binddn = p;
4038 break;
4039
4040 case 'M': /* Method for binding */
4041 while (isascii(*++p) && isspace(*p))
4042 continue;
4043
4044 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4045 p += 10;
4046
4047 for (lam = LDAPAuthMethods;
4048 lam != NULL && lam->lam_name != NULL; lam++)
4049 {
4050 if (sm_strncasecmp(p, lam->lam_name,
4051 strlen(lam->lam_name)) == 0)
4052 break;
4053 }
4054 if (lam->lam_name != NULL)
4055 lmap->ldap_method = lam->lam_code;
4056 else
4057 {
4058 /* bad config line */
4059 if (!bitset(MCF_OPTFILE,
4060 map->map_class->map_cflags))
4061 {
4062 char *ptr;
4063
4064 if ((ptr = strchr(p, ' ')) != NULL)
4065 *ptr = '\0';
4066 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4067 p, map->map_mname);
4068 if (ptr != NULL)
4069 *ptr = ' ';
4070 return false;
4071 }
4072 }
4073
4074 break;
4075
4076 /*
4077 ** This is a string that is dependent on the
4078 ** method used defined above.
4079 */
4080
4081 case 'P': /* Secret password for binding */
4082 while (isascii(*++p) && isspace(*p))
4083 continue;
4084 lmap->ldap_secret = p;
4085 secretread = false;
4086 break;
4087
4088 case 'H': /* Use LDAP URI */
4089# if !USE_LDAP_INIT
4090 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4091 map->map_mname);
4092 return false;
4093# else /* !USE_LDAP_INIT */
4094 if (lmap->ldap_host != NULL)
4095 {
4096 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4097 map->map_mname);
4098 return false;
4099 }
4100 while (isascii(*++p) && isspace(*p))
4101 continue;
4102 lmap->ldap_uri = p;
4103 break;
4104# endif /* !USE_LDAP_INIT */
4105
4106 case 'w':
4107 /* -w should be for passwd, -P should be for version */
4108 while (isascii(*++p) && isspace(*p))
4109 continue;
4110 lmap->ldap_version = atoi(p);
4111# ifdef LDAP_VERSION_MAX
4112 if (lmap->ldap_version > LDAP_VERSION_MAX)
4113 {
4114 syserr("LDAP version %d exceeds max of %d in map %s",
4115 lmap->ldap_version, LDAP_VERSION_MAX,
4116 map->map_mname);
4117 return false;
4118 }
4119# endif /* LDAP_VERSION_MAX */
4120# ifdef LDAP_VERSION_MIN
4121 if (lmap->ldap_version < LDAP_VERSION_MIN)
4122 {
4123 syserr("LDAP version %d is lower than min of %d in map %s",
4124 lmap->ldap_version, LDAP_VERSION_MIN,
4125 map->map_mname);
4126 return false;
4127 }
4128# endif /* LDAP_VERSION_MIN */
4129 break;
4130
4131 default:
4132 syserr("Illegal option %c map %s", *p, map->map_mname);
4133 break;
4134 }
4135
4136 /* need to account for quoted strings here */
4137 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4138 {
4139 if (*p == '"')
4140 {
4141 while (*++p != '"' && *p != '\0')
4142 continue;
4143 if (*p != '\0')
4144 p++;
4145 }
4146 else
4147 p++;
4148 }
4149
4150 if (*p != '\0')
4151 *p++ = '\0';
4152 }
4153
4154 if (map->map_app != NULL)
4155 map->map_app = newstr(ldapmap_dequote(map->map_app));
4156 if (map->map_tapp != NULL)
4157 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4158
4159 /*
4160 ** We need to swallow up all the stuff into a struct
4161 ** and dump it into map->map_dbptr1
4162 */
4163
4164 if (lmap->ldap_host != NULL &&
4165 (LDAPDefaults == NULL ||
4166 LDAPDefaults == lmap ||
4167 LDAPDefaults->ldap_host != lmap->ldap_host))
4168 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4169 map->map_domain = lmap->ldap_host;
4170
4171 if (lmap->ldap_uri != NULL &&
4172 (LDAPDefaults == NULL ||
4173 LDAPDefaults == lmap ||
4174 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4175 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4176 map->map_domain = lmap->ldap_uri;
4177
4178 if (lmap->ldap_binddn != NULL &&
4179 (LDAPDefaults == NULL ||
4180 LDAPDefaults == lmap ||
4181 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4182 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4183
4184 if (lmap->ldap_secret != NULL &&
4185 (LDAPDefaults == NULL ||
4186 LDAPDefaults == lmap ||
4187 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4188 {
4189 SM_FILE_T *sfd;
4190 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4191
4192 if (DontLockReadFiles)
4193 sff |= SFF_NOLOCK;
4194
4195 /* need to use method to map secret to passwd string */
4196 switch (lmap->ldap_method)
4197 {
4198 case LDAP_AUTH_NONE:
4199 /* Do nothing */
4200 break;
4201
4202 case LDAP_AUTH_SIMPLE:
4203
4204 /*
4205 ** Secret is the name of a file with
4206 ** the first line as the password.
4207 */
4208
4209 /* Already read in the secret? */
4210 if (secretread)
4211 break;
4212
4213 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4214 O_RDONLY, 0, sff);
4215 if (sfd == NULL)
4216 {
4217 syserr("LDAP map: cannot open secret %s",
4218 ldapmap_dequote(lmap->ldap_secret));
4219 return false;
4220 }
4221 lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
4222 sfd, TimeOuts.to_fileopen,
4223 "ldapmap_parseargs");
4224 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4225 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4226 {
4227 syserr("LDAP map: secret in %s too long",
4228 ldapmap_dequote(lmap->ldap_secret));
4229 return false;
4230 }
4231 if (lmap->ldap_secret != NULL &&
4232 strlen(m_tmp) > 0)
4233 {
4234 /* chomp newline */
4235 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4236 m_tmp[strlen(m_tmp) - 1] = '\0';
4237
4238 lmap->ldap_secret = m_tmp;
4239 }
4240 break;
4241
4242# ifdef LDAP_AUTH_KRBV4
4243 case LDAP_AUTH_KRBV4:
4244
4245 /*
4246 ** Secret is where the ticket file is
4247 ** stashed
4248 */
4249
4250 (void) sm_snprintf(m_tmp, sizeof m_tmp,
4251 "KRBTKFILE=%s",
4252 ldapmap_dequote(lmap->ldap_secret));
4253 lmap->ldap_secret = m_tmp;
4254 break;
4255# endif /* LDAP_AUTH_KRBV4 */
4256
4257 default: /* Should NEVER get here */
4258 syserr("LDAP map: Illegal value in lmap method");
4259 return false;
4260 /* NOTREACHED */
4261 break;
4262 }
4263 }
4264
4265 if (lmap->ldap_secret != NULL &&
4266 (LDAPDefaults == NULL ||
4267 LDAPDefaults == lmap ||
4268 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4269 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4270
4271 if (lmap->ldap_base != NULL &&
4272 (LDAPDefaults == NULL ||
4273 LDAPDefaults == lmap ||
4274 LDAPDefaults->ldap_base != lmap->ldap_base))
4275 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4276
4277 /*
4278 ** Save the server from extra work. If request is for a single
4279 ** match, tell the server to only return enough records to
4280 ** determine if there is a single match or not. This can not
4281 ** be one since the server would only return one and we wouldn't
4282 ** know if there were others available.
4283 */
4284
4285 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4286 lmap->ldap_sizelimit = 2;
4287
4288 /* If setting defaults, don't process ldap_filter and ldap_attr */
4289 if (lmap == LDAPDefaults)
4290 return true;
4291
4292 if (lmap->ldap_filter != NULL)
4293 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4294 else
4295 {
4296 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4297 {
4298 syserr("No filter given in map %s", map->map_mname);
4299 return false;
4300 }
4301 }
4302
4303 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4304 {
4305 bool recurse = false;
4306 bool normalseen = false;
4307
4308 i = 0;
4309 p = ldapmap_dequote(lmap->ldap_attr[0]);
4310 lmap->ldap_attr[0] = NULL;
4311
4312 /* Prime the attr list with the objectClass attribute */
4313 lmap->ldap_attr[i] = "objectClass";
4314 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4315 lmap->ldap_attr_needobjclass[i] = NULL;
4316 i++;
4317
4318 while (p != NULL)
4319 {
4320 char *v;
4321
4322 while (isascii(*p) && isspace(*p))
4323 p++;
4324 if (*p == '\0')
4325 break;
4326 v = p;
4327 p = strchr(v, ',');
4328 if (p != NULL)
4329 *p++ = '\0';
4330
4331 if (i >= LDAPMAP_MAX_ATTR)
4332 {
4333 syserr("Too many return attributes in %s (max %d)",
4334 map->map_mname, LDAPMAP_MAX_ATTR);
4335 return false;
4336 }
4337 if (*v != '\0')
4338 {
4339 int j;
4340 int use;
4341 char *type;
4342 char *needobjclass;
4343
4344 type = strchr(v, ':');
4345 if (type != NULL)
4346 {
4347 *type++ = '\0';
4348 needobjclass = strchr(type, ':');
4349 if (needobjclass != NULL)
4350 *needobjclass++ = '\0';
4351 }
4352 else
4353 {
4354 needobjclass = NULL;
4355 }
4356
4357 use = i;
4358
4359 /* allow override on "objectClass" type */
4360 if (sm_strcasecmp(v, "objectClass") == 0 &&
4361 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4362 {
4363 use = 0;
4364 }
4365 else
4366 {
4367 /*
4368 ** Don't add something to attribute
4369 ** list twice.
4370 */
4371
4372 for (j = 1; j < i; j++)
4373 {
4374 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4375 {
4376 syserr("Duplicate attribute (%s) in %s",
4377 v, map->map_mname);
4378 return false;
4379 }
4380 }
4381
4382 lmap->ldap_attr[use] = newstr(v);
4383 if (needobjclass != NULL &&
4384 *needobjclass != '\0' &&
4385 *needobjclass != '*')
4386 {
4387 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4388 }
4389 else
4390 {
4391 lmap->ldap_attr_needobjclass[use] = NULL;
4392 }
4393
4394 }
4395
4396 if (type != NULL && *type != '\0')
4397 {
4398 if (sm_strcasecmp(type, "dn") == 0)
4399 {
4400 recurse = true;
4401 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4402 }
4403 else if (sm_strcasecmp(type, "filter") == 0)
4404 {
4405 recurse = true;
4406 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4407 }
4408 else if (sm_strcasecmp(type, "url") == 0)
4409 {
4410 recurse = true;
4411 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4412 }
4413 else if (sm_strcasecmp(type, "normal") == 0)
4414 {
4415 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4416 normalseen = true;
4417 }
4418 else
4419 {
4420 syserr("Unknown attribute type (%s) in %s",
4421 type, map->map_mname);
4422 return false;
4423 }
4424 }
4425 else
4426 {
4427 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4428 normalseen = true;
4429 }
4430 i++;
4431 }
4432 }
4433 lmap->ldap_attr[i] = NULL;
4434
4435 /* Set in case needed in future code */
4436 attrssetup = true;
4437
4438 if (recurse && !normalseen)
4439 {
4440 syserr("LDAP recursion requested in %s but no returnable attribute given",
4441 map->map_mname);
4442 return false;
4443 }
4444 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4445 {
4446 syserr("LDAP recursion requested in %s can not be used with -n",
4447 map->map_mname);
4448 return false;
4449 }
4450 }
4451 map->map_db1 = (ARBPTR_T) lmap;
4452 return true;
4453}
4454
4455/*
4456** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4457**
4458** Parameters:
4459** spec -- map argument string from LDAPDefaults option
4460**
4461** Returns:
4462** None.
4463*/
4464
4465void
4466ldapmap_set_defaults(spec)
4467 char *spec;
4468{
4469 STAB *class;
4470 MAP map;
4471
4472 /* Allocate and set the default values */
4473 if (LDAPDefaults == NULL)
4474 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4475 sm_ldap_clear(LDAPDefaults);
4476
4477 memset(&map, '\0', sizeof map);
4478
4479 /* look up the class */
4480 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4481 if (class == NULL)
4482 {
4483 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4484 return;
4485 }
4486 map.map_class = &class->s_mapclass;
4487 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4488 map.map_mname = "O LDAPDefaultSpec";
4489
4490 (void) ldapmap_parseargs(&map, spec);
4491
4492 /* These should never be set in LDAPDefaults */
4493 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4494 map.map_spacesub != SpaceSub ||
4495 map.map_app != NULL ||
4496 map.map_tapp != NULL)
4497 {
4498 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4499 SM_FREE_CLR(map.map_app);
4500 SM_FREE_CLR(map.map_tapp);
4501 }
4502
4503 if (LDAPDefaults->ldap_filter != NULL)
4504 {
4505 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4506
4507 /* don't free, it isn't malloc'ed in parseargs */
4508 LDAPDefaults->ldap_filter = NULL;
4509 }
4510
4511 if (LDAPDefaults->ldap_attr[0] != NULL)
4512 {
4513 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4514 /* don't free, they aren't malloc'ed in parseargs */
4515 LDAPDefaults->ldap_attr[0] = NULL;
4516 }
4517}
4518#endif /* LDAPMAP */
4519/*
4520** PH map
4521*/
4522
4523#if PH_MAP
4524
4525/*
4526** Support for the CCSO Nameserver (ph/qi).
4527** This code is intended to replace the so-called "ph mailer".
4528** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
4529*/
4530
4531/* what version of the ph map code we're running */
4532static char phmap_id[128];
4533
4534/* sendmail version for phmap id string */
4535extern const char Version[];
4536
4537/* assume we're using nph-1.2.x if not specified */
4538# ifndef NPH_VERSION
4539# define NPH_VERSION 10200
4540# endif
4541
4542/* compatibility for versions older than nph-1.2.0 */
4543# if NPH_VERSION < 10200
4544# define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4545# define PH_OPEN_DONTID PH_DONTID
4546# define PH_CLOSE_FAST PH_FASTCLOSE
4547# define PH_ERR_DATAERR PH_DATAERR
4548# define PH_ERR_NOMATCH PH_NOMATCH
4549# endif /* NPH_VERSION < 10200 */
4550
4551/*
4552** PH_MAP_PARSEARGS -- parse ph map definition args.
4553*/
4554
4555bool
4556ph_map_parseargs(map, args)
4557 MAP *map;
4558 char *args;
4559{
4560 register bool done;
4561 register char *p = args;
4562 PH_MAP_STRUCT *pmap = NULL;
4563
4564 /* initialize version string */
4565 (void) sm_snprintf(phmap_id, sizeof phmap_id,
4566 "sendmail-%s phmap-20010529 libphclient-%s",
4567 Version, libphclient_version);
4568
4569 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4570
4571 /* defaults */
4572 pmap->ph_servers = NULL;
4573 pmap->ph_field_list = NULL;
4574 pmap->ph = NULL;
4575 pmap->ph_timeout = 0;
4576 pmap->ph_fastclose = 0;
4577
4578 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4579 for (;;)
4580 {
4581 while (isascii(*p) && isspace(*p))
4582 p++;
4583 if (*p != '-')
4584 break;
4585 switch (*++p)
4586 {
4587 case 'N':
4588 map->map_mflags |= MF_INCLNULL;
4589 map->map_mflags &= ~MF_TRY0NULL;
4590 break;
4591
4592 case 'O':
4593 map->map_mflags &= ~MF_TRY1NULL;
4594 break;
4595
4596 case 'o':
4597 map->map_mflags |= MF_OPTIONAL;
4598 break;
4599
4600 case 'f':
4601 map->map_mflags |= MF_NOFOLDCASE;
4602 break;
4603
4604 case 'm':
4605 map->map_mflags |= MF_MATCHONLY;
4606 break;
4607
4608 case 'A':
4609 map->map_mflags |= MF_APPEND;
4610 break;
4611
4612 case 'q':
4613 map->map_mflags |= MF_KEEPQUOTES;
4614 break;
4615
4616 case 't':
4617 map->map_mflags |= MF_NODEFER;
4618 break;
4619
4620 case 'a':
4621 map->map_app = ++p;
4622 break;
4623
4624 case 'T':
4625 map->map_tapp = ++p;
4626 break;
4627
4628 case 'l':
4629 while (isascii(*++p) && isspace(*p))
4630 continue;
4631 pmap->ph_timeout = atoi(p);
4632 break;
4633
4634 case 'S':
4635 map->map_spacesub = *++p;
4636 break;
4637
4638 case 'D':
4639 map->map_mflags |= MF_DEFER;
4640 break;
4641
4642 case 'h': /* PH server list */
4643 while (isascii(*++p) && isspace(*p))
4644 continue;
4645 pmap->ph_servers = p;
4646 break;
4647
4648 case 'k': /* fields to search for */
4649 while (isascii(*++p) && isspace(*p))
4650 continue;
4651 pmap->ph_field_list = p;
4652 break;
4653
4654 default:
4655 syserr("ph_map_parseargs: unknown option -%c", *p);
4656 }
4657
4658 /* try to account for quoted strings */
4659 done = isascii(*p) && isspace(*p);
4660 while (*p != '\0' && !done)
4661 {
4662 if (*p == '"')
4663 {
4664 while (*++p != '"' && *p != '\0')
4665 continue;
4666 if (*p != '\0')
4667 p++;
4668 }
4669 else
4670 p++;
4671 done = isascii(*p) && isspace(*p);
4672 }
4673
4674 if (*p != '\0')
4675 *p++ = '\0';
4676 }
4677
4678 if (map->map_app != NULL)
4679 map->map_app = newstr(ph_map_dequote(map->map_app));
4680 if (map->map_tapp != NULL)
4681 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4682
4683 if (pmap->ph_field_list != NULL)
4684 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4685
4686 if (pmap->ph_servers != NULL)
4687 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4688 else
4689 {
4690 syserr("ph_map_parseargs: -h flag is required");
4691 return false;
4692 }
4693
4694 map->map_db1 = (ARBPTR_T) pmap;
4695 return true;
4696}
4697
4698/*
4699** PH_MAP_CLOSE -- close the connection to the ph server
4700*/
4701
4702void
4703ph_map_close(map)
4704 MAP *map;
4705{
4706 PH_MAP_STRUCT *pmap;
4707
4708 pmap = (PH_MAP_STRUCT *)map->map_db1;
4709 if (tTd(38, 9))
4710 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4711 map->map_mname, pmap->ph_fastclose);
4712
4713
4714 if (pmap->ph != NULL)
4715 {
4716 ph_set_sendhook(pmap->ph, NULL);
4717 ph_set_recvhook(pmap->ph, NULL);
4718 ph_close(pmap->ph, pmap->ph_fastclose);
4719 }
4720
4721 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4722}
4723
4724static jmp_buf PHTimeout;
4725
4726/* ARGSUSED */
4727static void
4728ph_timeout(unused)
4729 int unused;
4730{
4731 /*
4732 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4733 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4734 ** DOING.
4735 */
4736
4737 errno = ETIMEDOUT;
4738 longjmp(PHTimeout, 1);
4739}
4740
4741static void
4742#if NPH_VERSION >= 10200
4743ph_map_send_debug(appdata, text)
4744 void *appdata;
4745#else
4746ph_map_send_debug(text)
4747#endif
4748 char *text;
4749{
4750 if (LogLevel > 9)
4751 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4752 "ph_map_send_debug: ==> %s", text);
4753 if (tTd(38, 20))
4754 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4755}
4756
4757static void
4758#if NPH_VERSION >= 10200
4759ph_map_recv_debug(appdata, text)
4760 void *appdata;
4761#else
4762ph_map_recv_debug(text)
4763#endif
4764 char *text;
4765{
4766 if (LogLevel > 10)
4767 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4768 "ph_map_recv_debug: <== %s", text);
4769 if (tTd(38, 21))
4770 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4771}
4772
4773/*
4774** PH_MAP_OPEN -- sub for opening PH map
4775*/
4776bool
4777ph_map_open(map, mode)
4778 MAP *map;
4779 int mode;
4780{
4781 PH_MAP_STRUCT *pmap;
4782 register SM_EVENT *ev = NULL;
4783 int save_errno = 0;
4784 char *hostlist, *host;
4785
4786 if (tTd(38, 2))
4787 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
4788
4789 mode &= O_ACCMODE;
4790 if (mode != O_RDONLY)
4791 {
4792 /* issue a pseudo-error message */
4793 errno = SM_EMAPCANTWRITE;
4794 return false;
4795 }
4796
4797 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
4798 bitset(MF_DEFER, map->map_mflags))
4799 {
4800 if (tTd(9, 1))
4801 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4802 map->map_mname);
4803
4804 /*
4805 ** Unset MF_DEFER here so that map_lookup() returns
4806 ** a temporary failure using the bogus map and
4807 ** map->map_tapp instead of the default permanent error.
4808 */
4809
4810 map->map_mflags &= ~MF_DEFER;
4811 return false;
4812 }
4813
4814 pmap = (PH_MAP_STRUCT *)map->map_db1;
4815 pmap->ph_fastclose = 0; /* refresh field for reopen */
4816
4817 /* try each host in the list */
4818 hostlist = newstr(pmap->ph_servers);
4819 for (host = strtok(hostlist, " ");
4820 host != NULL;
4821 host = strtok(NULL, " "))
4822 {
4823 /* set timeout */
4824 if (pmap->ph_timeout != 0)
4825 {
4826 if (setjmp(PHTimeout) != 0)
4827 {
4828 ev = NULL;
4829 if (LogLevel > 1)
4830 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4831 "timeout connecting to PH server %.100s",
4832 host);
4833 errno = ETIMEDOUT;
4834 goto ph_map_open_abort;
4835 }
4836 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4837 }
4838
4839 /* open connection to server */
4840 if (ph_open(&(pmap->ph), host,
4841 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
4842 ph_map_send_debug, ph_map_recv_debug
4843#if NPH_VERSION >= 10200
4844 , NULL
4845#endif
4846 ) == 0
4847 && ph_id(pmap->ph, phmap_id) == 0)
4848 {
4849 if (ev != NULL)
4850 sm_clrevent(ev);
4851 sm_free(hostlist); /* XXX */
4852 return true;
4853 }
4854
4855 ph_map_open_abort:
4856 save_errno = errno;
4857 if (ev != NULL)
4858 sm_clrevent(ev);
4859 pmap->ph_fastclose = PH_CLOSE_FAST;
4860 ph_map_close(map);
4861 errno = save_errno;
4862 }
4863
4864 if (bitset(MF_NODEFER, map->map_mflags))
4865 {
4866 if (errno == 0)
4867 errno = EAGAIN;
4868 syserr("ph_map_open: %s: cannot connect to PH server",
4869 map->map_mname);
4870 }
4871 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
4872 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4873 "ph_map_open: %s: cannot connect to PH server",
4874 map->map_mname);
4875 sm_free(hostlist); /* XXX */
4876 return false;
4877}
4878
4879/*
4880** PH_MAP_LOOKUP -- look up key from ph server
4881*/
4882
4883char *
4884ph_map_lookup(map, key, args, pstat)
4885 MAP *map;
4886 char *key;
4887 char **args;
4888 int *pstat;
4889{
4890 int i, save_errno = 0;
4891 register SM_EVENT *ev = NULL;
4892 PH_MAP_STRUCT *pmap;
4893 char *value = NULL;
4894
4895 pmap = (PH_MAP_STRUCT *)map->map_db1;
4896
4897 *pstat = EX_OK;
4898
4899 /* set timeout */
4900 if (pmap->ph_timeout != 0)
4901 {
4902 if (setjmp(PHTimeout) != 0)
4903 {
4904 ev = NULL;
4905 if (LogLevel > 1)
4906 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4907 "timeout during PH lookup of %.100s",
4908 key);
4909 errno = ETIMEDOUT;
4910 *pstat = EX_TEMPFAIL;
4911 goto ph_map_lookup_abort;
4912 }
4913 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4914 }
4915
4916 /* perform lookup */
4917 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
4918 if (i == -1)
4919 *pstat = EX_TEMPFAIL;
4920 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
4921 *pstat = EX_UNAVAILABLE;
4922
4923 ph_map_lookup_abort:
4924 if (ev != NULL)
4925 sm_clrevent(ev);
4926
4927 /*
4928 ** Close the connection if the timer popped
4929 ** or we got a temporary PH error
4930 */
4931
4932 if (*pstat == EX_TEMPFAIL)
4933 {
4934 save_errno = errno;
4935 pmap->ph_fastclose = PH_CLOSE_FAST;
4936 ph_map_close(map);
4937 errno = save_errno;
4938 }
4939
4940 if (*pstat == EX_OK)
4941 {
4942 if (tTd(38,20))
4943 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
4944
4945 if (bitset(MF_MATCHONLY, map->map_mflags))
4946 return map_rewrite(map, key, strlen(key), NULL);
4947 else
4948 return map_rewrite(map, value, strlen(value), args);
4949 }
4950
4951 return NULL;
4952}
4953#endif /* PH_MAP */
4954/*
4955** syslog map
4956*/
4957
4958#define map_prio map_lockfd /* overload field */
4959
4960/*
4961** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
4962*/
4963
4964bool
4965syslog_map_parseargs(map, args)
4966 MAP *map;
4967 char *args;
4968{
4969 char *p = args;
4970 char *priority = NULL;
4971
4972 /* there is no check whether there is really an argument */
4973 while (*p != '\0')
4974 {
4975 while (isascii(*p) && isspace(*p))
4976 p++;
4977 if (*p != '-')
4978 break;
4979 ++p;
4980 if (*p == 'D')
4981 {
4982 map->map_mflags |= MF_DEFER;
4983 ++p;
4984 }
4985 else if (*p == 'S')
4986 {
4987 map->map_spacesub = *++p;
4988 if (*p != '\0')
4989 p++;
4990 }
4991 else if (*p == 'L')
4992 {
4993 while (*++p != '\0' && isascii(*p) && isspace(*p))
4994 continue;
4995 if (*p == '\0')
4996 break;
4997 priority = p;
4998 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4999 p++;
5000 if (*p != '\0')
5001 *p++ = '\0';
5002 }
5003 else
5004 {
5005 syserr("Illegal option %c map syslog", *p);
5006 ++p;
5007 }
5008 }
5009
5010 if (priority == NULL)
5011 map->map_prio = LOG_INFO;
5012 else
5013 {
5014 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5015 priority += 4;
5016
5017#ifdef LOG_EMERG
5018 if (sm_strcasecmp("EMERG", priority) == 0)
5019 map->map_prio = LOG_EMERG;
5020 else
5021#endif /* LOG_EMERG */
5022#ifdef LOG_ALERT
5023 if (sm_strcasecmp("ALERT", priority) == 0)
5024 map->map_prio = LOG_ALERT;
5025 else
5026#endif /* LOG_ALERT */
5027#ifdef LOG_CRIT
5028 if (sm_strcasecmp("CRIT", priority) == 0)
5029 map->map_prio = LOG_CRIT;
5030 else
5031#endif /* LOG_CRIT */
5032#ifdef LOG_ERR
5033 if (sm_strcasecmp("ERR", priority) == 0)
5034 map->map_prio = LOG_ERR;
5035 else
5036#endif /* LOG_ERR */
5037#ifdef LOG_WARNING
5038 if (sm_strcasecmp("WARNING", priority) == 0)
5039 map->map_prio = LOG_WARNING;
5040 else
5041#endif /* LOG_WARNING */
5042#ifdef LOG_NOTICE
5043 if (sm_strcasecmp("NOTICE", priority) == 0)
5044 map->map_prio = LOG_NOTICE;
5045 else
5046#endif /* LOG_NOTICE */
5047#ifdef LOG_INFO
5048 if (sm_strcasecmp("INFO", priority) == 0)
5049 map->map_prio = LOG_INFO;
5050 else
5051#endif /* LOG_INFO */
5052#ifdef LOG_DEBUG
5053 if (sm_strcasecmp("DEBUG", priority) == 0)
5054 map->map_prio = LOG_DEBUG;
5055 else
5056#endif /* LOG_DEBUG */
5057 {
5058 syserr("syslog_map_parseargs: Unknown priority %s",
5059 priority);
5060 return false;
5061 }
5062 }
5063 return true;
5064}
5065
5066/*
5067** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5068*/
5069
5070char *
5071syslog_map_lookup(map, string, args, statp)
5072 MAP *map;
5073 char *string;
5074 char **args;
5075 int *statp;
5076{
5077 char *ptr = map_rewrite(map, string, strlen(string), args);
5078
5079 if (ptr != NULL)
5080 {
5081 if (tTd(38, 20))
5082 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5083 map->map_mname, map->map_prio, ptr);
5084
5085 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5086 }
5087
5088 *statp = EX_OK;
5089 return "";
5090}
5091
5092/*
5093** HESIOD Modules
5094*/
5095
5096#if HESIOD
5097
5098bool
5099hes_map_open(map, mode)
5100 MAP *map;
5101 int mode;
5102{
5103 if (tTd(38, 2))
5104 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5105 map->map_mname, map->map_file, mode);
5106
5107 if (mode != O_RDONLY)
5108 {
5109 /* issue a pseudo-error message */
5110 errno = SM_EMAPCANTWRITE;
5111 return false;
5112 }
5113
5114# ifdef HESIOD_INIT
5115 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5116 return true;
5117
5118 if (!bitset(MF_OPTIONAL, map->map_mflags))
5119 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5120 sm_errstring(errno));
5121 return false;
5122# else /* HESIOD_INIT */
5123 if (hes_error() == HES_ER_UNINIT)
5124 hes_init();
5125 switch (hes_error())
5126 {
5127 case HES_ER_OK:
5128 case HES_ER_NOTFOUND:
5129 return true;
5130 }
5131
5132 if (!bitset(MF_OPTIONAL, map->map_mflags))
5133 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5134
5135 return false;
5136# endif /* HESIOD_INIT */
5137}
5138
5139char *
5140hes_map_lookup(map, name, av, statp)
5141 MAP *map;
5142 char *name;
5143 char **av;
5144 int *statp;
5145{
5146 char **hp;
5147
5148 if (tTd(38, 20))
5149 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5150
5151 if (name[0] == '\\')
5152 {
5153 char *np;
5154 int nl;
5155 int save_errno;
5156 char nbuf[MAXNAME];
5157
5158 nl = strlen(name);
5159 if (nl < sizeof nbuf - 1)
5160 np = nbuf;
5161 else
5162 np = xalloc(strlen(name) + 2);
5163 np[0] = '\\';
5164 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
5165# ifdef HESIOD_INIT
5166 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5167# else /* HESIOD_INIT */
5168 hp = hes_resolve(np, map->map_file);
5169# endif /* HESIOD_INIT */
5170 save_errno = errno;
5171 if (np != nbuf)
5172 sm_free(np); /* XXX */
5173 errno = save_errno;
5174 }
5175 else
5176 {
5177# ifdef HESIOD_INIT
5178 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5179# else /* HESIOD_INIT */
5180 hp = hes_resolve(name, map->map_file);
5181# endif /* HESIOD_INIT */
5182 }
5183# ifdef HESIOD_INIT
5184 if (hp == NULL || *hp == NULL)
5185 {
5186 switch (errno)
5187 {
5188 case ENOENT:
5189 *statp = EX_NOTFOUND;
5190 break;
5191 case ECONNREFUSED:
5192 *statp = EX_TEMPFAIL;
5193 break;
5194 case EMSGSIZE:
5195 case ENOMEM:
5196 default:
5197 *statp = EX_UNAVAILABLE;
5198 break;
5199 }
5200 if (hp != NULL)
5201 hesiod_free_list(HesiodContext, hp);
5202 return NULL;
5203 }
5204# else /* HESIOD_INIT */
5205 if (hp == NULL || hp[0] == NULL)
5206 {
5207 switch (hes_error())
5208 {
5209 case HES_ER_OK:
5210 *statp = EX_OK;
5211 break;
5212
5213 case HES_ER_NOTFOUND:
5214 *statp = EX_NOTFOUND;
5215 break;
5216
5217 case HES_ER_CONFIG:
5218 *statp = EX_UNAVAILABLE;
5219 break;
5220
5221 case HES_ER_NET:
5222 *statp = EX_TEMPFAIL;
5223 break;
5224 }
5225 return NULL;
5226 }
5227# endif /* HESIOD_INIT */
5228
5229 if (bitset(MF_MATCHONLY, map->map_mflags))
5230 return map_rewrite(map, name, strlen(name), NULL);
5231 else
5232 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5233}
5234
5235/*
5236** HES_MAP_CLOSE -- free the Hesiod context
5237*/
5238
5239void
5240hes_map_close(map)
5241 MAP *map;
5242{
5243 if (tTd(38, 20))
5244 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5245
5246# ifdef HESIOD_INIT
5247 /* Free the hesiod context */
5248 if (HesiodContext != NULL)
5249 {
5250 hesiod_end(HesiodContext);
5251 HesiodContext = NULL;
5252 }
5253# endif /* HESIOD_INIT */
5254}
5255
5256#endif /* HESIOD */
5257/*
5258** NeXT NETINFO Modules
5259*/
5260
5261#if NETINFO
5262
5263# define NETINFO_DEFAULT_DIR "/aliases"
5264# define NETINFO_DEFAULT_PROPERTY "members"
5265
5266/*
5267** NI_MAP_OPEN -- open NetInfo Aliases
5268*/
5269
5270bool
5271ni_map_open(map, mode)
5272 MAP *map;
5273 int mode;
5274{
5275 if (tTd(38, 2))
5276 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5277 map->map_mname, map->map_file, mode);
5278 mode &= O_ACCMODE;
5279
5280 if (*map->map_file == '\0')
5281 map->map_file = NETINFO_DEFAULT_DIR;
5282
5283 if (map->map_valcolnm == NULL)
5284 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5285
5286 if (map->map_coldelim == '\0')
5287 {
5288 if (bitset(MF_ALIAS, map->map_mflags))
5289 map->map_coldelim = ',';
5290 else if (bitset(MF_FILECLASS, map->map_mflags))
5291 map->map_coldelim = ' ';
5292 }
5293 return true;
5294}
5295
5296
5297/*
5298** NI_MAP_LOOKUP -- look up a datum in NetInfo
5299*/
5300
5301char *
5302ni_map_lookup(map, name, av, statp)
5303 MAP *map;
5304 char *name;
5305 char **av;
5306 int *statp;
5307{
5308 char *res;
5309 char *propval;
5310
5311 if (tTd(38, 20))
5312 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5313
5314 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5315 map->map_valcolnm, map->map_coldelim);
5316
5317 if (propval == NULL)
5318 return NULL;
5319
5320 SM_TRY
5321 if (bitset(MF_MATCHONLY, map->map_mflags))
5322 res = map_rewrite(map, name, strlen(name), NULL);
5323 else
5324 res = map_rewrite(map, propval, strlen(propval), av);
5325 SM_FINALLY
5326 sm_free(propval);
5327 SM_END_TRY
5328 return res;
5329}
5330
5331
5332static bool
5333ni_getcanonname(name, hbsize, statp)
5334 char *name;
5335 int hbsize;
5336 int *statp;
5337{
5338 char *vptr;
5339 char *ptr;
5340 char nbuf[MAXNAME + 1];
5341
5342 if (tTd(38, 20))
5343 sm_dprintf("ni_getcanonname(%s)\n", name);
5344
5345 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5346 {
5347 *statp = EX_UNAVAILABLE;
5348 return false;
5349 }
5350 (void) shorten_hostname(nbuf);
5351
5352 /* we only accept single token search key */
5353 if (strchr(nbuf, '.'))
5354 {
5355 *statp = EX_NOHOST;
5356 return false;
5357 }
5358
5359 /* Do the search */
5360 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5361
5362 if (vptr == NULL)
5363 {
5364 *statp = EX_NOHOST;
5365 return false;
5366 }
5367
5368 /* Only want the first machine name */
5369 if ((ptr = strchr(vptr, '\n')) != NULL)
5370 *ptr = '\0';
5371
5372 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5373 {
5374 sm_free(vptr);
5375 *statp = EX_UNAVAILABLE;
5376 return true;
5377 }
5378 sm_free(vptr);
5379 *statp = EX_OK;
5380 return false;
5381}
5382#endif /* NETINFO */
5383/*
5384** TEXT (unindexed text file) Modules
5385**
5386** This code donated by Sun Microsystems.
5387*/
5388
5389#define map_sff map_lockfd /* overload field */
5390
5391
5392/*
5393** TEXT_MAP_OPEN -- open text table
5394*/
5395
5396bool
5397text_map_open(map, mode)
5398 MAP *map;
5399 int mode;
5400{
5401 long sff;
5402 int i;
5403
5404 if (tTd(38, 2))
5405 sm_dprintf("text_map_open(%s, %s, %d)\n",
5406 map->map_mname, map->map_file, mode);
5407
5408 mode &= O_ACCMODE;
5409 if (mode != O_RDONLY)
5410 {
5411 errno = EPERM;
5412 return false;
5413 }
5414
5415 if (*map->map_file == '\0')
5416 {
5417 syserr("text map \"%s\": file name required",
5418 map->map_mname);
5419 return false;
5420 }
5421
5422 if (map->map_file[0] != '/')
5423 {
5424 syserr("text map \"%s\": file name must be fully qualified",
5425 map->map_mname);
5426 return false;
5427 }
5428
5429 sff = SFF_ROOTOK|SFF_REGONLY;
5430 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5431 sff |= SFF_NOWLINK;
5432 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5433 sff |= SFF_SAFEDIRPATH;
5434 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5435 sff, S_IRUSR, NULL)) != 0)
5436 {
5437 int save_errno = errno;
5438
5439 /* cannot open this map */
5440 if (tTd(38, 2))
5441 sm_dprintf("\tunsafe map file: %d\n", i);
5442 errno = save_errno;
5443 if (!bitset(MF_OPTIONAL, map->map_mflags))
5444 syserr("text map \"%s\": unsafe map file %s",
5445 map->map_mname, map->map_file);
5446 return false;
5447 }
5448
5449 if (map->map_keycolnm == NULL)
5450 map->map_keycolno = 0;
5451 else
5452 {
5453 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5454 {
5455 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5456 map->map_mname, map->map_file,
5457 map->map_keycolnm);
5458 return false;
5459 }
5460 map->map_keycolno = atoi(map->map_keycolnm);
5461 }
5462
5463 if (map->map_valcolnm == NULL)
5464 map->map_valcolno = 0;
5465 else
5466 {
5467 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5468 {
5469 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5470 map->map_mname, map->map_file,
5471 map->map_valcolnm);
5472 return false;
5473 }
5474 map->map_valcolno = atoi(map->map_valcolnm);
5475 }
5476
5477 if (tTd(38, 2))
5478 {
5479 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5480 map->map_mname, map->map_file);
5481 if (map->map_coldelim == '\0')
5482 sm_dprintf("(white space)\n");
5483 else
5484 sm_dprintf("%c\n", map->map_coldelim);
5485 }
5486
5487 map->map_sff = sff;
5488 return true;
5489}
5490
5491
5492/*
5493** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5494*/
5495
5496char *
5497text_map_lookup(map, name, av, statp)
5498 MAP *map;
5499 char *name;
5500 char **av;
5501 int *statp;
5502{
5503 char *vp;
5504 auto int vsize;
5505 int buflen;
5506 SM_FILE_T *f;
5507 char delim;
5508 int key_idx;
5509 bool found_it;
5510 long sff = map->map_sff;
5511 char search_key[MAXNAME + 1];
5512 char linebuf[MAXLINE];
5513 char buf[MAXNAME + 1];
5514
5515 found_it = false;
5516 if (tTd(38, 20))
5517 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5518
5519 buflen = strlen(name);
5520 if (buflen > sizeof search_key - 1)
5521 buflen = sizeof search_key - 1; /* XXX just cut if off? */
5522 memmove(search_key, name, buflen);
5523 search_key[buflen] = '\0';
5524 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5525 makelower(search_key);
5526
5527 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5528 if (f == NULL)
5529 {
5530 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5531 *statp = EX_UNAVAILABLE;
5532 return NULL;
5533 }
5534 key_idx = map->map_keycolno;
5535 delim = map->map_coldelim;
5536 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5537 linebuf, sizeof linebuf) != NULL)
5538 {
5539 char *p;
5540
5541 /* skip comment line */
5542 if (linebuf[0] == '#')
5543 continue;
5544 p = strchr(linebuf, '\n');
5545 if (p != NULL)
5546 *p = '\0';
5547 p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5548 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5549 {
5550 found_it = true;
5551 break;
5552 }
5553 }
5554 (void) sm_io_close(f, SM_TIME_DEFAULT);
5555 if (!found_it)
5556 {
5557 *statp = EX_NOTFOUND;
5558 return NULL;
5559 }
5560 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5561 if (vp == NULL)
5562 {
5563 *statp = EX_NOTFOUND;
5564 return NULL;
5565 }
5566 vsize = strlen(vp);
5567 *statp = EX_OK;
5568 if (bitset(MF_MATCHONLY, map->map_mflags))
5569 return map_rewrite(map, name, strlen(name), NULL);
5570 else
5571 return map_rewrite(map, vp, vsize, av);
5572}
5573
5574/*
5575** TEXT_GETCANONNAME -- look up canonical name in hosts file
5576*/
5577
5578static bool
5579text_getcanonname(name, hbsize, statp)
5580 char *name;
5581 int hbsize;
5582 int *statp;
5583{
5584 bool found;
5585 char *dot;
5586 SM_FILE_T *f;
5587 char linebuf[MAXLINE];
5588 char cbuf[MAXNAME + 1];
5589 char nbuf[MAXNAME + 1];
5590
5591 if (tTd(38, 20))
5592 sm_dprintf("text_getcanonname(%s)\n", name);
5593
5594 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5595 {
5596 *statp = EX_UNAVAILABLE;
5597 return false;
5598 }
5599 dot = shorten_hostname(nbuf);
5600
5601 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5602 NULL);
5603 if (f == NULL)
5604 {
5605 *statp = EX_UNAVAILABLE;
5606 return false;
5607 }
5608 found = false;
5609 while (!found &&
5610 sm_io_fgets(f, SM_TIME_DEFAULT,
5611 linebuf, sizeof linebuf) != NULL)
5612 {
5613 char *p = strpbrk(linebuf, "#\n");
5614
5615 if (p != NULL)
5616 *p = '\0';
5617 if (linebuf[0] != '\0')
5618 found = extract_canonname(nbuf, dot, linebuf,
5619 cbuf, sizeof cbuf);
5620 }
5621 (void) sm_io_close(f, SM_TIME_DEFAULT);
5622 if (!found)
5623 {
5624 *statp = EX_NOHOST;
5625 return false;
5626 }
5627
5628 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5629 {
5630 *statp = EX_UNAVAILABLE;
5631 return false;
5632 }
5633 *statp = EX_OK;
5634 return true;
5635}
5636/*
5637** STAB (Symbol Table) Modules
5638*/
5639
5640
5641/*
5642** STAB_MAP_LOOKUP -- look up alias in symbol table
5643*/
5644
5645/* ARGSUSED2 */
5646char *
5647stab_map_lookup(map, name, av, pstat)
5648 register MAP *map;
5649 char *name;
5650 char **av;
5651 int *pstat;
5652{
5653 register STAB *s;
5654
5655 if (tTd(38, 20))
5656 sm_dprintf("stab_lookup(%s, %s)\n",
5657 map->map_mname, name);
5658
5659 s = stab(name, ST_ALIAS, ST_FIND);
17
18#if LDAPMAP
19# include <sm/ldap.h>
20#endif /* LDAPMAP */
21
22#if NDBM
23# include <ndbm.h>
24# ifdef R_FIRST
25 ERROR README: You are running the Berkeley DB version of ndbm.h. See
26 ERROR README: the README file about tweaking Berkeley DB so it can
27 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
28 ERROR README: and use -DNEWDB instead.
29# endif /* R_FIRST */
30#endif /* NDBM */
31#if NEWDB
32# include "sm/bdb.h"
33#endif /* NEWDB */
34#if NIS
35 struct dom_binding; /* forward reference needed on IRIX */
36# include <rpcsvc/ypclnt.h>
37# if NDBM
38# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
39# endif /* NDBM */
40#endif /* NIS */
41
42#if NEWDB
43# if DB_VERSION_MAJOR < 2
44static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
45# endif /* DB_VERSION_MAJOR < 2 */
46# if DB_VERSION_MAJOR == 2
47static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
48# endif /* DB_VERSION_MAJOR == 2 */
49# if DB_VERSION_MAJOR > 2
50static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
51# endif /* DB_VERSION_MAJOR > 2 */
52#endif /* NEWDB */
53static bool extract_canonname __P((char *, char *, char *, char[], int));
54static void map_close __P((STAB *, int));
55static void map_init __P((STAB *, int));
56#ifdef LDAPMAP
57static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
58#endif /* LDAPMAP */
59#if NISPLUS
60static bool nisplus_getcanonname __P((char *, int, int *));
61#endif /* NISPLUS */
62#if NIS
63static bool nis_getcanonname __P((char *, int, int *));
64#endif /* NIS */
65#if NETINFO
66static bool ni_getcanonname __P((char *, int, int *));
67#endif /* NETINFO */
68static bool text_getcanonname __P((char *, int, int *));
69#if SOCKETMAP
70static STAB *socket_map_findconn __P((const char*));
71
72/* XXX arbitrary limit for sanity */
73# define SOCKETMAP_MAXL 1000000
74#endif /* SOCKETMAP */
75
76/* default error message for trying to open a map in write mode */
77#ifdef ENOSYS
78# define SM_EMAPCANTWRITE ENOSYS
79#else /* ENOSYS */
80# ifdef EFTYPE
81# define SM_EMAPCANTWRITE EFTYPE
82# else /* EFTYPE */
83# define SM_EMAPCANTWRITE ENXIO
84# endif /* EFTYPE */
85#endif /* ENOSYS */
86
87/*
88** MAP.C -- implementations for various map classes.
89**
90** Each map class implements a series of functions:
91**
92** bool map_parse(MAP *map, char *args)
93** Parse the arguments from the config file. Return true
94** if they were ok, false otherwise. Fill in map with the
95** values.
96**
97** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
98** Look up the key in the given map. If found, do any
99** rewriting the map wants (including "args" if desired)
100** and return the value. Set *pstat to the appropriate status
101** on error and return NULL. Args will be NULL if called
102** from the alias routines, although this should probably
103** not be relied upon. It is suggested you call map_rewrite
104** to return the results -- it takes care of null termination
105** and uses a dynamically expanded buffer as needed.
106**
107** void map_store(MAP *map, char *key, char *value)
108** Store the key:value pair in the map.
109**
110** bool map_open(MAP *map, int mode)
111** Open the map for the indicated mode. Mode should
112** be either O_RDONLY or O_RDWR. Return true if it
113** was opened successfully, false otherwise. If the open
114** failed and the MF_OPTIONAL flag is not set, it should
115** also print an error. If the MF_ALIAS bit is set
116** and this map class understands the @:@ convention, it
117** should call aliaswait() before returning.
118**
119** void map_close(MAP *map)
120** Close the map.
121**
122** This file also includes the implementation for getcanonname.
123** It is currently implemented in a pretty ad-hoc manner; it ought
124** to be more properly integrated into the map structure.
125*/
126
127#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
128# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
129#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
130# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
131#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
132
133/*
134** MAP_PARSEARGS -- parse config line arguments for database lookup
135**
136** This is a generic version of the map_parse method.
137**
138** Parameters:
139** map -- the map being initialized.
140** ap -- a pointer to the args on the config line.
141**
142** Returns:
143** true -- if everything parsed OK.
144** false -- otherwise.
145**
146** Side Effects:
147** null terminates the filename; stores it in map
148*/
149
150bool
151map_parseargs(map, ap)
152 MAP *map;
153 char *ap;
154{
155 register char *p = ap;
156
157 /*
158 ** There is no check whether there is really an argument,
159 ** but that's not important enough to warrant extra code.
160 */
161
162 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
163 map->map_spacesub = SpaceSub; /* default value */
164 for (;;)
165 {
166 while (isascii(*p) && isspace(*p))
167 p++;
168 if (*p != '-')
169 break;
170 switch (*++p)
171 {
172 case 'N':
173 map->map_mflags |= MF_INCLNULL;
174 map->map_mflags &= ~MF_TRY0NULL;
175 break;
176
177 case 'O':
178 map->map_mflags &= ~MF_TRY1NULL;
179 break;
180
181 case 'o':
182 map->map_mflags |= MF_OPTIONAL;
183 break;
184
185 case 'f':
186 map->map_mflags |= MF_NOFOLDCASE;
187 break;
188
189 case 'm':
190 map->map_mflags |= MF_MATCHONLY;
191 break;
192
193 case 'A':
194 map->map_mflags |= MF_APPEND;
195 break;
196
197 case 'q':
198 map->map_mflags |= MF_KEEPQUOTES;
199 break;
200
201 case 'a':
202 map->map_app = ++p;
203 break;
204
205 case 'T':
206 map->map_tapp = ++p;
207 break;
208
209 case 'k':
210 while (isascii(*++p) && isspace(*p))
211 continue;
212 map->map_keycolnm = p;
213 break;
214
215 case 'v':
216 while (isascii(*++p) && isspace(*p))
217 continue;
218 map->map_valcolnm = p;
219 break;
220
221 case 'z':
222 if (*++p != '\\')
223 map->map_coldelim = *p;
224 else
225 {
226 switch (*++p)
227 {
228 case 'n':
229 map->map_coldelim = '\n';
230 break;
231
232 case 't':
233 map->map_coldelim = '\t';
234 break;
235
236 default:
237 map->map_coldelim = '\\';
238 }
239 }
240 break;
241
242 case 't':
243 map->map_mflags |= MF_NODEFER;
244 break;
245
246
247 case 'S':
248 map->map_spacesub = *++p;
249 break;
250
251 case 'D':
252 map->map_mflags |= MF_DEFER;
253 break;
254
255 default:
256 syserr("Illegal option %c map %s", *p, map->map_mname);
257 break;
258 }
259 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
260 p++;
261 if (*p != '\0')
262 *p++ = '\0';
263 }
264 if (map->map_app != NULL)
265 map->map_app = newstr(map->map_app);
266 if (map->map_tapp != NULL)
267 map->map_tapp = newstr(map->map_tapp);
268 if (map->map_keycolnm != NULL)
269 map->map_keycolnm = newstr(map->map_keycolnm);
270 if (map->map_valcolnm != NULL)
271 map->map_valcolnm = newstr(map->map_valcolnm);
272
273 if (*p != '\0')
274 {
275 map->map_file = p;
276 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
277 p++;
278 if (*p != '\0')
279 *p++ = '\0';
280 map->map_file = newstr(map->map_file);
281 }
282
283 while (*p != '\0' && isascii(*p) && isspace(*p))
284 p++;
285 if (*p != '\0')
286 map->map_rebuild = newstr(p);
287
288 if (map->map_file == NULL &&
289 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
290 {
291 syserr("No file name for %s map %s",
292 map->map_class->map_cname, map->map_mname);
293 return false;
294 }
295 return true;
296}
297/*
298** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
299**
300** It also adds the map_app string. It can be used as a utility
301** in the map_lookup method.
302**
303** Parameters:
304** map -- the map that causes this.
305** s -- the string to rewrite, NOT necessarily null terminated.
306** slen -- the length of s.
307** av -- arguments to interpolate into buf.
308**
309** Returns:
310** Pointer to rewritten result. This is static data that
311** should be copied if it is to be saved!
312*/
313
314char *
315map_rewrite(map, s, slen, av)
316 register MAP *map;
317 register const char *s;
318 size_t slen;
319 char **av;
320{
321 register char *bp;
322 register char c;
323 char **avp;
324 register char *ap;
325 size_t l;
326 size_t len;
327 static size_t buflen = 0;
328 static char *buf = NULL;
329
330 if (tTd(39, 1))
331 {
332 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
333 if (av == NULL)
334 sm_dprintf(" (nullv)");
335 else
336 {
337 for (avp = av; *avp != NULL; avp++)
338 sm_dprintf("\n\t%s", *avp);
339 }
340 sm_dprintf("\n");
341 }
342
343 /* count expected size of output (can safely overestimate) */
344 l = len = slen;
345 if (av != NULL)
346 {
347 const char *sp = s;
348
349 while (l-- > 0 && (c = *sp++) != '\0')
350 {
351 if (c != '%')
352 continue;
353 if (l-- <= 0)
354 break;
355 c = *sp++;
356 if (!(isascii(c) && isdigit(c)))
357 continue;
358 for (avp = av; --c >= '0' && *avp != NULL; avp++)
359 continue;
360 if (*avp == NULL)
361 continue;
362 len += strlen(*avp);
363 }
364 }
365 if (map->map_app != NULL)
366 len += strlen(map->map_app);
367 if (buflen < ++len)
368 {
369 /* need to malloc additional space */
370 buflen = len;
371 if (buf != NULL)
372 sm_free(buf);
373 buf = sm_pmalloc_x(buflen);
374 }
375
376 bp = buf;
377 if (av == NULL)
378 {
379 memmove(bp, s, slen);
380 bp += slen;
381
382 /* assert(len > slen); */
383 len -= slen;
384 }
385 else
386 {
387 while (slen-- > 0 && (c = *s++) != '\0')
388 {
389 if (c != '%')
390 {
391 pushc:
392 if (len-- <= 1)
393 break;
394 *bp++ = c;
395 continue;
396 }
397 if (slen-- <= 0 || (c = *s++) == '\0')
398 c = '%';
399 if (c == '%')
400 goto pushc;
401 if (!(isascii(c) && isdigit(c)))
402 {
403 if (len-- <= 1)
404 break;
405 *bp++ = '%';
406 goto pushc;
407 }
408 for (avp = av; --c >= '0' && *avp != NULL; avp++)
409 continue;
410 if (*avp == NULL)
411 continue;
412
413 /* transliterate argument into output string */
414 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
415 *bp++ = c;
416 }
417 }
418 if (map->map_app != NULL && len > 0)
419 (void) sm_strlcpy(bp, map->map_app, len);
420 else
421 *bp = '\0';
422 if (tTd(39, 1))
423 sm_dprintf("map_rewrite => %s\n", buf);
424 return buf;
425}
426/*
427** INITMAPS -- rebuild alias maps
428**
429** Parameters:
430** none.
431**
432** Returns:
433** none.
434*/
435
436void
437initmaps()
438{
439#if XDEBUG
440 checkfd012("entering initmaps");
441#endif /* XDEBUG */
442 stabapply(map_init, 0);
443#if XDEBUG
444 checkfd012("exiting initmaps");
445#endif /* XDEBUG */
446}
447/*
448** MAP_INIT -- rebuild a map
449**
450** Parameters:
451** s -- STAB entry: if map: try to rebuild
452** unused -- unused variable
453**
454** Returns:
455** none.
456**
457** Side Effects:
458** will close already open rebuildable map.
459*/
460
461/* ARGSUSED1 */
462static void
463map_init(s, unused)
464 register STAB *s;
465 int unused;
466{
467 register MAP *map;
468
469 /* has to be a map */
470 if (s->s_symtype != ST_MAP)
471 return;
472
473 map = &s->s_map;
474 if (!bitset(MF_VALID, map->map_mflags))
475 return;
476
477 if (tTd(38, 2))
478 sm_dprintf("map_init(%s:%s, %s)\n",
479 map->map_class->map_cname == NULL ? "NULL" :
480 map->map_class->map_cname,
481 map->map_mname == NULL ? "NULL" : map->map_mname,
482 map->map_file == NULL ? "NULL" : map->map_file);
483
484 if (!bitset(MF_ALIAS, map->map_mflags) ||
485 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
486 {
487 if (tTd(38, 3))
488 sm_dprintf("\tnot rebuildable\n");
489 return;
490 }
491
492 /* if already open, close it (for nested open) */
493 if (bitset(MF_OPEN, map->map_mflags))
494 {
495 map->map_mflags |= MF_CLOSING;
496 map->map_class->map_close(map);
497 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
498 }
499
500 (void) rebuildaliases(map, false);
501 return;
502}
503/*
504** OPENMAP -- open a map
505**
506** Parameters:
507** map -- map to open (it must not be open).
508**
509** Returns:
510** whether open succeeded.
511*/
512
513bool
514openmap(map)
515 MAP *map;
516{
517 bool restore = false;
518 bool savehold = HoldErrs;
519 bool savequick = QuickAbort;
520 int saveerrors = Errors;
521
522 if (!bitset(MF_VALID, map->map_mflags))
523 return false;
524
525 /* better safe than sorry... */
526 if (bitset(MF_OPEN, map->map_mflags))
527 return true;
528
529 /* Don't send a map open error out via SMTP */
530 if ((OnlyOneError || QuickAbort) &&
531 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
532 {
533 restore = true;
534 HoldErrs = true;
535 QuickAbort = false;
536 }
537
538 errno = 0;
539 if (map->map_class->map_open(map, O_RDONLY))
540 {
541 if (tTd(38, 4))
542 sm_dprintf("openmap()\t%s:%s %s: valid\n",
543 map->map_class->map_cname == NULL ? "NULL" :
544 map->map_class->map_cname,
545 map->map_mname == NULL ? "NULL" :
546 map->map_mname,
547 map->map_file == NULL ? "NULL" :
548 map->map_file);
549 map->map_mflags |= MF_OPEN;
550 map->map_pid = CurrentPid;
551 }
552 else
553 {
554 if (tTd(38, 4))
555 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
556 map->map_class->map_cname == NULL ? "NULL" :
557 map->map_class->map_cname,
558 map->map_mname == NULL ? "NULL" :
559 map->map_mname,
560 map->map_file == NULL ? "NULL" :
561 map->map_file,
562 errno == 0 ? "" : ": ",
563 errno == 0 ? "" : sm_errstring(errno));
564 if (!bitset(MF_OPTIONAL, map->map_mflags))
565 {
566 extern MAPCLASS BogusMapClass;
567
568 map->map_orgclass = map->map_class;
569 map->map_class = &BogusMapClass;
570 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
571 map->map_pid = CurrentPid;
572 }
573 else
574 {
575 /* don't try again */
576 map->map_mflags &= ~MF_VALID;
577 }
578 }
579
580 if (restore)
581 {
582 Errors = saveerrors;
583 HoldErrs = savehold;
584 QuickAbort = savequick;
585 }
586
587 return bitset(MF_OPEN, map->map_mflags);
588}
589/*
590** CLOSEMAPS -- close all open maps opened by the current pid.
591**
592** Parameters:
593** bogus -- only close bogus maps.
594**
595** Returns:
596** none.
597*/
598
599void
600closemaps(bogus)
601 bool bogus;
602{
603 stabapply(map_close, bogus);
604}
605/*
606** MAP_CLOSE -- close a map opened by the current pid.
607**
608** Parameters:
609** s -- STAB entry: if map: try to close
610** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
611**
612** Returns:
613** none.
614*/
615
616/* ARGSUSED1 */
617static void
618map_close(s, bogus)
619 register STAB *s;
620 int bogus; /* int because of stabapply(), used as bool */
621{
622 MAP *map;
623 extern MAPCLASS BogusMapClass;
624
625 if (s->s_symtype != ST_MAP)
626 return;
627
628 map = &s->s_map;
629
630 /*
631 ** close the map iff:
632 ** it is valid and open and opened by this process
633 ** and (!bogus or it's a bogus map or it is not persistent)
634 ** negate this: return iff
635 ** it is not valid or it is not open or not opened by this process
636 ** or (bogus and it's not a bogus map and it's not not-persistent)
637 */
638
639 if (!bitset(MF_VALID, map->map_mflags) ||
640 !bitset(MF_OPEN, map->map_mflags) ||
641 bitset(MF_CLOSING, map->map_mflags) ||
642 map->map_pid != CurrentPid ||
643 (bogus && map->map_class != &BogusMapClass &&
644 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
645 return;
646
647 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
648 map->map_orgclass != &BogusMapClass)
649 map->map_class = map->map_orgclass;
650 if (tTd(38, 5))
651 sm_dprintf("closemaps: closing %s (%s)\n",
652 map->map_mname == NULL ? "NULL" : map->map_mname,
653 map->map_file == NULL ? "NULL" : map->map_file);
654
655 if (!bitset(MF_OPENBOGUS, map->map_mflags))
656 {
657 map->map_mflags |= MF_CLOSING;
658 map->map_class->map_close(map);
659 }
660 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
661}
662/*
663** GETCANONNAME -- look up name using service switch
664**
665** Parameters:
666** host -- the host name to look up.
667** hbsize -- the size of the host buffer.
668** trymx -- if set, try MX records.
669** pttl -- pointer to return TTL (can be NULL).
670**
671** Returns:
672** true -- if the host was found.
673** false -- otherwise.
674*/
675
676bool
677getcanonname(host, hbsize, trymx, pttl)
678 char *host;
679 int hbsize;
680 bool trymx;
681 int *pttl;
682{
683 int nmaps;
684 int mapno;
685 bool found = false;
686 bool got_tempfail = false;
687 auto int status;
688 char *maptype[MAXMAPSTACK];
689 short mapreturn[MAXMAPACTIONS];
690
691 nmaps = switch_map_find("hosts", maptype, mapreturn);
692 if (pttl != 0)
693 *pttl = SM_DEFAULT_TTL;
694 for (mapno = 0; mapno < nmaps; mapno++)
695 {
696 int i;
697
698 if (tTd(38, 20))
699 sm_dprintf("getcanonname(%s), trying %s\n",
700 host, maptype[mapno]);
701 if (strcmp("files", maptype[mapno]) == 0)
702 {
703 found = text_getcanonname(host, hbsize, &status);
704 }
705#if NIS
706 else if (strcmp("nis", maptype[mapno]) == 0)
707 {
708 found = nis_getcanonname(host, hbsize, &status);
709 }
710#endif /* NIS */
711#if NISPLUS
712 else if (strcmp("nisplus", maptype[mapno]) == 0)
713 {
714 found = nisplus_getcanonname(host, hbsize, &status);
715 }
716#endif /* NISPLUS */
717#if NAMED_BIND
718 else if (strcmp("dns", maptype[mapno]) == 0)
719 {
720 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
721 }
722#endif /* NAMED_BIND */
723#if NETINFO
724 else if (strcmp("netinfo", maptype[mapno]) == 0)
725 {
726 found = ni_getcanonname(host, hbsize, &status);
727 }
728#endif /* NETINFO */
729 else
730 {
731 found = false;
732 status = EX_UNAVAILABLE;
733 }
734
735 /*
736 ** Heuristic: if $m is not set, we are running during system
737 ** startup. In this case, when a name is apparently found
738 ** but has no dot, treat is as not found. This avoids
739 ** problems if /etc/hosts has no FQDN but is listed first
740 ** in the service switch.
741 */
742
743 if (found &&
744 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
745 break;
746
747 /* see if we should continue */
748 if (status == EX_TEMPFAIL)
749 {
750 i = MA_TRYAGAIN;
751 got_tempfail = true;
752 }
753 else if (status == EX_NOTFOUND)
754 i = MA_NOTFOUND;
755 else
756 i = MA_UNAVAIL;
757 if (bitset(1 << mapno, mapreturn[i]))
758 break;
759 }
760
761 if (found)
762 {
763 char *d;
764
765 if (tTd(38, 20))
766 sm_dprintf("getcanonname(%s), found\n", host);
767
768 /*
769 ** If returned name is still single token, compensate
770 ** by tagging on $m. This is because some sites set
771 ** up their DNS or NIS databases wrong.
772 */
773
774 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
775 {
776 d = macvalue('m', CurEnv);
777 if (d != NULL &&
778 hbsize > (int) (strlen(host) + strlen(d) + 1))
779 {
780 if (host[strlen(host) - 1] != '.')
781 (void) sm_strlcat2(host, ".", d,
782 hbsize);
783 else
784 (void) sm_strlcat(host, d, hbsize);
785 }
786 else
787 return false;
788 }
789 return true;
790 }
791
792 if (tTd(38, 20))
793 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
794 status);
795
796 if (got_tempfail)
797 SM_SET_H_ERRNO(TRY_AGAIN);
798 else
799 SM_SET_H_ERRNO(HOST_NOT_FOUND);
800
801 return false;
802}
803/*
804** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
805**
806** Parameters:
807** name -- the name against which to match.
808** dot -- where to reinsert '.' to get FQDN
809** line -- the /etc/hosts line.
810** cbuf -- the location to store the result.
811** cbuflen -- the size of cbuf.
812**
813** Returns:
814** true -- if the line matched the desired name.
815** false -- otherwise.
816*/
817
818static bool
819extract_canonname(name, dot, line, cbuf, cbuflen)
820 char *name;
821 char *dot;
822 char *line;
823 char cbuf[];
824 int cbuflen;
825{
826 int i;
827 char *p;
828 bool found = false;
829
830 cbuf[0] = '\0';
831 if (line[0] == '#')
832 return false;
833
834 for (i = 1; ; i++)
835 {
836 char nbuf[MAXNAME + 1];
837
838 p = get_column(line, i, '\0', nbuf, sizeof nbuf);
839 if (p == NULL)
840 break;
841 if (*p == '\0')
842 continue;
843 if (cbuf[0] == '\0' ||
844 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
845 {
846 (void) sm_strlcpy(cbuf, p, cbuflen);
847 }
848 if (sm_strcasecmp(name, p) == 0)
849 found = true;
850 else if (dot != NULL)
851 {
852 /* try looking for the FQDN as well */
853 *dot = '.';
854 if (sm_strcasecmp(name, p) == 0)
855 found = true;
856 *dot = '\0';
857 }
858 }
859 if (found && strchr(cbuf, '.') == NULL)
860 {
861 /* try to add a domain on the end of the name */
862 char *domain = macvalue('m', CurEnv);
863
864 if (domain != NULL &&
865 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
866 {
867 p = &cbuf[i];
868 *p++ = '.';
869 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
870 }
871 }
872 return found;
873}
874
875/*
876** DNS modules
877*/
878
879#if NAMED_BIND
880# if DNSMAP
881
882# include "sm_resolve.h"
883# if NETINET || NETINET6
884# include <arpa/inet.h>
885# endif /* NETINET || NETINET6 */
886
887/*
888** DNS_MAP_OPEN -- stub to check proper value for dns map type
889*/
890
891bool
892dns_map_open(map, mode)
893 MAP *map;
894 int mode;
895{
896 if (tTd(38,2))
897 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
898
899 mode &= O_ACCMODE;
900 if (mode != O_RDONLY)
901 {
902 /* issue a pseudo-error message */
903 errno = SM_EMAPCANTWRITE;
904 return false;
905 }
906 return true;
907}
908
909/*
910** DNS_MAP_PARSEARGS -- parse dns map definition args.
911**
912** Parameters:
913** map -- pointer to MAP
914** args -- pointer to the args on the config line.
915**
916** Returns:
917** true -- if everything parsed OK.
918** false -- otherwise.
919*/
920
921# if _FFR_DNSMAP_MULTILIMIT
922# if !_FFR_DNSMAP_MULTI
923 ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
924# endif /* ! _FFR_DNSMAP_MULTI */
925# endif /* _FFR_DNSMAP_MULTILIMIT */
926
927# if _FFR_DNSMAP_MULTI
928# if _FFR_DNSMAP_MULTILIMIT
929# define map_sizelimit map_lockfd /* overload field */
930# endif /* _FFR_DNSMAP_MULTILIMIT */
931# endif /* _FFR_DNSMAP_MULTI */
932
933struct dns_map
934{
935 int dns_m_type;
936};
937
938bool
939dns_map_parseargs(map,args)
940 MAP *map;
941 char *args;
942{
943 register char *p = args;
944 struct dns_map *map_p;
945
946 map_p = (struct dns_map *) xalloc(sizeof *map_p);
947 map_p->dns_m_type = -1;
948 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
949
950 for (;;)
951 {
952 while (isascii(*p) && isspace(*p))
953 p++;
954 if (*p != '-')
955 break;
956 switch (*++p)
957 {
958 case 'N':
959 map->map_mflags |= MF_INCLNULL;
960 map->map_mflags &= ~MF_TRY0NULL;
961 break;
962
963 case 'O':
964 map->map_mflags &= ~MF_TRY1NULL;
965 break;
966
967 case 'o':
968 map->map_mflags |= MF_OPTIONAL;
969 break;
970
971 case 'f':
972 map->map_mflags |= MF_NOFOLDCASE;
973 break;
974
975 case 'm':
976 map->map_mflags |= MF_MATCHONLY;
977 break;
978
979 case 'A':
980 map->map_mflags |= MF_APPEND;
981 break;
982
983 case 'q':
984 map->map_mflags |= MF_KEEPQUOTES;
985 break;
986
987 case 't':
988 map->map_mflags |= MF_NODEFER;
989 break;
990
991 case 'a':
992 map->map_app = ++p;
993 break;
994
995 case 'T':
996 map->map_tapp = ++p;
997 break;
998
999 case 'd':
1000 {
1001 char *h;
1002
1003 ++p;
1004 h = strchr(p, ' ');
1005 if (h != NULL)
1006 *h = '\0';
1007 map->map_timeout = convtime(p, 's');
1008 if (h != NULL)
1009 *h = ' ';
1010 }
1011 break;
1012
1013 case 'r':
1014 while (isascii(*++p) && isspace(*p))
1015 continue;
1016 map->map_retry = atoi(p);
1017 break;
1018
1019# if _FFR_DNSMAP_MULTI
1020 case 'z':
1021 if (*++p != '\\')
1022 map->map_coldelim = *p;
1023 else
1024 {
1025 switch (*++p)
1026 {
1027 case 'n':
1028 map->map_coldelim = '\n';
1029 break;
1030
1031 case 't':
1032 map->map_coldelim = '\t';
1033 break;
1034
1035 default:
1036 map->map_coldelim = '\\';
1037 }
1038 }
1039 break;
1040
1041# if _FFR_DNSMAP_MULTILIMIT
1042 case 'Z':
1043 while (isascii(*++p) && isspace(*p))
1044 continue;
1045 map->map_sizelimit = atoi(p);
1046 break;
1047# endif /* _FFR_DNSMAP_MULTILIMIT */
1048# endif /* _FFR_DNSMAP_MULTI */
1049
1050 /* Start of dns_map specific args */
1051 case 'R': /* search field */
1052 {
1053 char *h;
1054
1055 while (isascii(*++p) && isspace(*p))
1056 continue;
1057 h = strchr(p, ' ');
1058 if (h != NULL)
1059 *h = '\0';
1060 map_p->dns_m_type = dns_string_to_type(p);
1061 if (h != NULL)
1062 *h = ' ';
1063 if (map_p->dns_m_type < 0)
1064 syserr("dns map %s: wrong type %s",
1065 map->map_mname, p);
1066 }
1067 break;
1068
1069# if _FFR_DNSMAP_BASE
1070 case 'B': /* base domain */
1071 {
1072 char *h;
1073
1074 while (isascii(*++p) && isspace(*p))
1075 continue;
1076 h = strchr(p, ' ');
1077 if (h != NULL)
1078 *h = '\0';
1079
1080 /*
1081 ** slight abuse of map->map_file; it isn't
1082 ** used otherwise in this map type.
1083 */
1084
1085 map->map_file = newstr(p);
1086 if (h != NULL)
1087 *h = ' ';
1088 }
1089 break;
1090# endif /* _FFR_DNSMAP_BASE */
1091
1092 }
1093 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1094 p++;
1095 if (*p != '\0')
1096 *p++ = '\0';
1097 }
1098 if (map_p->dns_m_type < 0)
1099 syserr("dns map %s: missing -R type", map->map_mname);
1100 if (map->map_app != NULL)
1101 map->map_app = newstr(map->map_app);
1102 if (map->map_tapp != NULL)
1103 map->map_tapp = newstr(map->map_tapp);
1104
1105 /*
1106 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1107 ** Even if this assumption is wrong, we use only one byte,
1108 ** so it doesn't really matter.
1109 */
1110
1111 map->map_db1 = (ARBPTR_T) map_p;
1112 return true;
1113}
1114
1115/*
1116** DNS_MAP_LOOKUP -- perform dns map lookup.
1117**
1118** Parameters:
1119** map -- pointer to MAP
1120** name -- name to lookup
1121** av -- arguments to interpolate into buf.
1122** statp -- pointer to status (EX_)
1123**
1124** Returns:
1125** result of lookup if succeeded.
1126** NULL -- otherwise.
1127*/
1128
1129char *
1130dns_map_lookup(map, name, av, statp)
1131 MAP *map;
1132 char *name;
1133 char **av;
1134 int *statp;
1135{
1136# if _FFR_DNSMAP_MULTI
1137# if _FFR_DNSMAP_MULTILIMIT
1138 int resnum = 0;
1139# endif /* _FFR_DNSMAP_MULTILIMIT */
1140# endif /* _FFR_DNSMAP_MULTI */
1141 char *vp = NULL, *result = NULL;
1142 size_t vsize;
1143 struct dns_map *map_p;
1144 RESOURCE_RECORD_T *rr = NULL;
1145 DNS_REPLY_T *r = NULL;
1146# if NETINET6
1147 static char buf6[INET6_ADDRSTRLEN];
1148# endif /* NETINET6 */
1149
1150 if (tTd(38, 20))
1151 sm_dprintf("dns_map_lookup(%s, %s)\n",
1152 map->map_mname, name);
1153
1154 map_p = (struct dns_map *)(map->map_db1);
1155# if _FFR_DNSMAP_BASE
1156 if (map->map_file != NULL && *map->map_file != '\0')
1157 {
1158 size_t len;
1159 char *appdomain;
1160
1161 len = strlen(map->map_file) + strlen(name) + 2;
1162 appdomain = (char *) sm_malloc(len);
1163 if (appdomain == NULL)
1164 {
1165 *statp = EX_UNAVAILABLE;
1166 return NULL;
1167 }
1168 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1169 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1170 map->map_timeout, map->map_retry);
1171 sm_free(appdomain);
1172 }
1173 else
1174# endif /* _FFR_DNSMAP_BASE */
1175 {
1176 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1177 map->map_timeout, map->map_retry);
1178 }
1179
1180 if (r == NULL)
1181 {
1182 result = NULL;
1183 if (h_errno == TRY_AGAIN || transienterror(errno))
1184 *statp = EX_TEMPFAIL;
1185 else
1186 *statp = EX_NOTFOUND;
1187 goto cleanup;
1188 }
1189 *statp = EX_OK;
1190 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1191 {
1192 char *type = NULL;
1193 char *value = NULL;
1194
1195 switch (rr->rr_type)
1196 {
1197 case T_NS:
1198 type = "T_NS";
1199 value = rr->rr_u.rr_txt;
1200 break;
1201 case T_CNAME:
1202 type = "T_CNAME";
1203 value = rr->rr_u.rr_txt;
1204 break;
1205 case T_AFSDB:
1206 type = "T_AFSDB";
1207 value = rr->rr_u.rr_mx->mx_r_domain;
1208 break;
1209 case T_SRV:
1210 type = "T_SRV";
1211 value = rr->rr_u.rr_srv->srv_r_target;
1212 break;
1213 case T_PTR:
1214 type = "T_PTR";
1215 value = rr->rr_u.rr_txt;
1216 break;
1217 case T_TXT:
1218 type = "T_TXT";
1219 value = rr->rr_u.rr_txt;
1220 break;
1221 case T_MX:
1222 type = "T_MX";
1223 value = rr->rr_u.rr_mx->mx_r_domain;
1224 break;
1225# if NETINET
1226 case T_A:
1227 type = "T_A";
1228 value = inet_ntoa(*(rr->rr_u.rr_a));
1229 break;
1230# endif /* NETINET */
1231# if NETINET6
1232 case T_AAAA:
1233 type = "T_AAAA";
1234 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1235 sizeof buf6);
1236 break;
1237# endif /* NETINET6 */
1238 }
1239
1240 (void) strreplnonprt(value, 'X');
1241 if (map_p->dns_m_type != rr->rr_type)
1242 {
1243 if (tTd(38, 40))
1244 sm_dprintf("\tskipping type %s (%d) value %s\n",
1245 type != NULL ? type : "<UNKNOWN>",
1246 rr->rr_type,
1247 value != NULL ? value : "<NO VALUE>");
1248 continue;
1249 }
1250
1251# if NETINET6
1252 if (rr->rr_type == T_AAAA && value == NULL)
1253 {
1254 result = NULL;
1255 *statp = EX_DATAERR;
1256 if (tTd(38, 40))
1257 sm_dprintf("\tbad T_AAAA conversion\n");
1258 goto cleanup;
1259 }
1260# endif /* NETINET6 */
1261 if (tTd(38, 40))
1262 sm_dprintf("\tfound type %s (%d) value %s\n",
1263 type != NULL ? type : "<UNKNOWN>",
1264 rr->rr_type,
1265 value != NULL ? value : "<NO VALUE>");
1266# if _FFR_DNSMAP_MULTI
1267 if (value != NULL &&
1268 (map->map_coldelim == '\0' ||
1269# if _FFR_DNSMAP_MULTILIMIT
1270 map->map_sizelimit == 1 ||
1271# endif /* _FFR_DNSMAP_MULTILIMIT */
1272 bitset(MF_MATCHONLY, map->map_mflags)))
1273 {
1274 /* Only care about the first match */
1275 vp = newstr(value);
1276 break;
1277 }
1278 else if (vp == NULL)
1279 {
1280 /* First result */
1281 vp = newstr(value);
1282 }
1283 else
1284 {
1285 /* concatenate the results */
1286 int sz;
1287 char *new;
1288
1289 sz = strlen(vp) + strlen(value) + 2;
1290 new = xalloc(sz);
1291 (void) sm_snprintf(new, sz, "%s%c%s",
1292 vp, map->map_coldelim, value);
1293 sm_free(vp);
1294 vp = new;
1295# if _FFR_DNSMAP_MULTILIMIT
1296 if (map->map_sizelimit > 0 &&
1297 ++resnum >= map->map_sizelimit)
1298 break;
1299# endif /* _FFR_DNSMAP_MULTILIMIT */
1300 }
1301# else /* _FFR_DNSMAP_MULTI */
1302 vp = value;
1303 break;
1304# endif /* _FFR_DNSMAP_MULTI */
1305 }
1306 if (vp == NULL)
1307 {
1308 result = NULL;
1309 *statp = EX_NOTFOUND;
1310 if (tTd(38, 40))
1311 sm_dprintf("\tno match found\n");
1312 goto cleanup;
1313 }
1314
1315# if _FFR_DNSMAP_MULTI
1316 /* Cleanly truncate for rulesets */
1317 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1318# endif /* _FFR_DNSMAP_MULTI */
1319
1320 vsize = strlen(vp);
1321
1322 if (LogLevel > 9)
1323 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1324 name, vp);
1325 if (bitset(MF_MATCHONLY, map->map_mflags))
1326 result = map_rewrite(map, name, strlen(name), NULL);
1327 else
1328 result = map_rewrite(map, vp, vsize, av);
1329
1330 cleanup:
1331# if _FFR_DNSMAP_MULTI
1332 if (vp != NULL)
1333 sm_free(vp);
1334# endif /* _FFR_DNSMAP_MULTI */
1335 if (r != NULL)
1336 dns_free_data(r);
1337 return result;
1338}
1339# endif /* DNSMAP */
1340#endif /* NAMED_BIND */
1341
1342/*
1343** NDBM modules
1344*/
1345
1346#if NDBM
1347
1348/*
1349** NDBM_MAP_OPEN -- DBM-style map open
1350*/
1351
1352bool
1353ndbm_map_open(map, mode)
1354 MAP *map;
1355 int mode;
1356{
1357 register DBM *dbm;
1358 int save_errno;
1359 int dfd;
1360 int pfd;
1361 long sff;
1362 int ret;
1363 int smode = S_IREAD;
1364 char dirfile[MAXPATHLEN];
1365 char pagfile[MAXPATHLEN];
1366 struct stat st;
1367 struct stat std, stp;
1368
1369 if (tTd(38, 2))
1370 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1371 map->map_mname, map->map_file, mode);
1372 map->map_lockfd = -1;
1373 mode &= O_ACCMODE;
1374
1375 /* do initial file and directory checks */
1376 if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
1377 map->map_file, ".dir") >= sizeof dirfile ||
1378 sm_strlcpyn(pagfile, sizeof pagfile, 2,
1379 map->map_file, ".pag") >= sizeof pagfile)
1380 {
1381 errno = 0;
1382 if (!bitset(MF_OPTIONAL, map->map_mflags))
1383 syserr("dbm map \"%s\": map file %s name too long",
1384 map->map_mname, map->map_file);
1385 return false;
1386 }
1387 sff = SFF_ROOTOK|SFF_REGONLY;
1388 if (mode == O_RDWR)
1389 {
1390 sff |= SFF_CREAT;
1391 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1392 sff |= SFF_NOSLINK;
1393 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1394 sff |= SFF_NOHLINK;
1395 smode = S_IWRITE;
1396 }
1397 else
1398 {
1399 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1400 sff |= SFF_NOWLINK;
1401 }
1402 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1403 sff |= SFF_SAFEDIRPATH;
1404 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1405 sff, smode, &std);
1406 if (ret == 0)
1407 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1408 sff, smode, &stp);
1409
1410 if (ret != 0)
1411 {
1412 char *prob = "unsafe";
1413
1414 /* cannot open this map */
1415 if (ret == ENOENT)
1416 prob = "missing";
1417 if (tTd(38, 2))
1418 sm_dprintf("\t%s map file: %d\n", prob, ret);
1419 if (!bitset(MF_OPTIONAL, map->map_mflags))
1420 syserr("dbm map \"%s\": %s map file %s",
1421 map->map_mname, prob, map->map_file);
1422 return false;
1423 }
1424 if (std.st_mode == ST_MODE_NOFILE)
1425 mode |= O_CREAT|O_EXCL;
1426
1427# if LOCK_ON_OPEN
1428 if (mode == O_RDONLY)
1429 mode |= O_SHLOCK;
1430 else
1431 mode |= O_TRUNC|O_EXLOCK;
1432# else /* LOCK_ON_OPEN */
1433 if ((mode & O_ACCMODE) == O_RDWR)
1434 {
1435# if NOFTRUNCATE
1436 /*
1437 ** Warning: race condition. Try to lock the file as
1438 ** quickly as possible after opening it.
1439 ** This may also have security problems on some systems,
1440 ** but there isn't anything we can do about it.
1441 */
1442
1443 mode |= O_TRUNC;
1444# else /* NOFTRUNCATE */
1445 /*
1446 ** This ugly code opens the map without truncating it,
1447 ** locks the file, then truncates it. Necessary to
1448 ** avoid race conditions.
1449 */
1450
1451 int dirfd;
1452 int pagfd;
1453 long sff = SFF_CREAT|SFF_OPENASROOT;
1454
1455 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1456 sff |= SFF_NOSLINK;
1457 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1458 sff |= SFF_NOHLINK;
1459
1460 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1461 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1462
1463 if (dirfd < 0 || pagfd < 0)
1464 {
1465 save_errno = errno;
1466 if (dirfd >= 0)
1467 (void) close(dirfd);
1468 if (pagfd >= 0)
1469 (void) close(pagfd);
1470 errno = save_errno;
1471 syserr("ndbm_map_open: cannot create database %s",
1472 map->map_file);
1473 return false;
1474 }
1475 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1476 ftruncate(pagfd, (off_t) 0) < 0)
1477 {
1478 save_errno = errno;
1479 (void) close(dirfd);
1480 (void) close(pagfd);
1481 errno = save_errno;
1482 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1483 map->map_file);
1484 return false;
1485 }
1486
1487 /* if new file, get "before" bits for later filechanged check */
1488 if (std.st_mode == ST_MODE_NOFILE &&
1489 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1490 {
1491 save_errno = errno;
1492 (void) close(dirfd);
1493 (void) close(pagfd);
1494 errno = save_errno;
1495 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1496 map->map_file);
1497 return false;
1498 }
1499
1500 /* have to save the lock for the duration (bletch) */
1501 map->map_lockfd = dirfd;
1502 (void) close(pagfd);
1503
1504 /* twiddle bits for dbm_open */
1505 mode &= ~(O_CREAT|O_EXCL);
1506# endif /* NOFTRUNCATE */
1507 }
1508# endif /* LOCK_ON_OPEN */
1509
1510 /* open the database */
1511 dbm = dbm_open(map->map_file, mode, DBMMODE);
1512 if (dbm == NULL)
1513 {
1514 save_errno = errno;
1515 if (bitset(MF_ALIAS, map->map_mflags) &&
1516 aliaswait(map, ".pag", false))
1517 return true;
1518# if !LOCK_ON_OPEN && !NOFTRUNCATE
1519 if (map->map_lockfd >= 0)
1520 (void) close(map->map_lockfd);
1521# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1522 errno = save_errno;
1523 if (!bitset(MF_OPTIONAL, map->map_mflags))
1524 syserr("Cannot open DBM database %s", map->map_file);
1525 return false;
1526 }
1527 dfd = dbm_dirfno(dbm);
1528 pfd = dbm_pagfno(dbm);
1529 if (dfd == pfd)
1530 {
1531 /* heuristic: if files are linked, this is actually gdbm */
1532 dbm_close(dbm);
1533# if !LOCK_ON_OPEN && !NOFTRUNCATE
1534 if (map->map_lockfd >= 0)
1535 (void) close(map->map_lockfd);
1536# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1537 errno = 0;
1538 syserr("dbm map \"%s\": cannot support GDBM",
1539 map->map_mname);
1540 return false;
1541 }
1542
1543 if (filechanged(dirfile, dfd, &std) ||
1544 filechanged(pagfile, pfd, &stp))
1545 {
1546 save_errno = errno;
1547 dbm_close(dbm);
1548# if !LOCK_ON_OPEN && !NOFTRUNCATE
1549 if (map->map_lockfd >= 0)
1550 (void) close(map->map_lockfd);
1551# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1552 errno = save_errno;
1553 syserr("ndbm_map_open(%s): file changed after open",
1554 map->map_file);
1555 return false;
1556 }
1557
1558 map->map_db1 = (ARBPTR_T) dbm;
1559
1560 /*
1561 ** Need to set map_mtime before the call to aliaswait()
1562 ** as aliaswait() will call map_lookup() which requires
1563 ** map_mtime to be set
1564 */
1565
1566 if (fstat(pfd, &st) >= 0)
1567 map->map_mtime = st.st_mtime;
1568
1569 if (mode == O_RDONLY)
1570 {
1571# if LOCK_ON_OPEN
1572 if (dfd >= 0)
1573 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1574 if (pfd >= 0)
1575 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1576# endif /* LOCK_ON_OPEN */
1577 if (bitset(MF_ALIAS, map->map_mflags) &&
1578 !aliaswait(map, ".pag", true))
1579 return false;
1580 }
1581 else
1582 {
1583 map->map_mflags |= MF_LOCKED;
1584 if (geteuid() == 0 && TrustedUid != 0)
1585 {
1586# if HASFCHOWN
1587 if (fchown(dfd, TrustedUid, -1) < 0 ||
1588 fchown(pfd, TrustedUid, -1) < 0)
1589 {
1590 int err = errno;
1591
1592 sm_syslog(LOG_ALERT, NOQID,
1593 "ownership change on %s failed: %s",
1594 map->map_file, sm_errstring(err));
1595 message("050 ownership change on %s failed: %s",
1596 map->map_file, sm_errstring(err));
1597 }
1598# else /* HASFCHOWN */
1599 sm_syslog(LOG_ALERT, NOQID,
1600 "no fchown(): cannot change ownership on %s",
1601 map->map_file);
1602 message("050 no fchown(): cannot change ownership on %s",
1603 map->map_file);
1604# endif /* HASFCHOWN */
1605 }
1606 }
1607 return true;
1608}
1609
1610
1611/*
1612** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1613*/
1614
1615char *
1616ndbm_map_lookup(map, name, av, statp)
1617 MAP *map;
1618 char *name;
1619 char **av;
1620 int *statp;
1621{
1622 datum key, val;
1623 int dfd, pfd;
1624 char keybuf[MAXNAME + 1];
1625 struct stat stbuf;
1626
1627 if (tTd(38, 20))
1628 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1629 map->map_mname, name);
1630
1631 key.dptr = name;
1632 key.dsize = strlen(name);
1633 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1634 {
1635 if (key.dsize > sizeof keybuf - 1)
1636 key.dsize = sizeof keybuf - 1;
1637 memmove(keybuf, key.dptr, key.dsize);
1638 keybuf[key.dsize] = '\0';
1639 makelower(keybuf);
1640 key.dptr = keybuf;
1641 }
1642lockdbm:
1643 dfd = dbm_dirfno((DBM *) map->map_db1);
1644 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1645 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1646 pfd = dbm_pagfno((DBM *) map->map_db1);
1647 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1648 stbuf.st_mtime > map->map_mtime)
1649 {
1650 /* Reopen the database to sync the cache */
1651 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1652 : O_RDONLY;
1653
1654 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1655 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1656 map->map_mflags |= MF_CLOSING;
1657 map->map_class->map_close(map);
1658 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1659 if (map->map_class->map_open(map, omode))
1660 {
1661 map->map_mflags |= MF_OPEN;
1662 map->map_pid = CurrentPid;
1663 if ((omode && O_ACCMODE) == O_RDWR)
1664 map->map_mflags |= MF_WRITABLE;
1665 goto lockdbm;
1666 }
1667 else
1668 {
1669 if (!bitset(MF_OPTIONAL, map->map_mflags))
1670 {
1671 extern MAPCLASS BogusMapClass;
1672
1673 *statp = EX_TEMPFAIL;
1674 map->map_orgclass = map->map_class;
1675 map->map_class = &BogusMapClass;
1676 map->map_mflags |= MF_OPEN;
1677 map->map_pid = CurrentPid;
1678 syserr("Cannot reopen NDBM database %s",
1679 map->map_file);
1680 }
1681 return NULL;
1682 }
1683 }
1684 val.dptr = NULL;
1685 if (bitset(MF_TRY0NULL, map->map_mflags))
1686 {
1687 val = dbm_fetch((DBM *) map->map_db1, key);
1688 if (val.dptr != NULL)
1689 map->map_mflags &= ~MF_TRY1NULL;
1690 }
1691 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1692 {
1693 key.dsize++;
1694 val = dbm_fetch((DBM *) map->map_db1, key);
1695 if (val.dptr != NULL)
1696 map->map_mflags &= ~MF_TRY0NULL;
1697 }
1698 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1699 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1700 if (val.dptr == NULL)
1701 return NULL;
1702 if (bitset(MF_MATCHONLY, map->map_mflags))
1703 return map_rewrite(map, name, strlen(name), NULL);
1704 else
1705 return map_rewrite(map, val.dptr, val.dsize, av);
1706}
1707
1708
1709/*
1710** NDBM_MAP_STORE -- store a datum in the database
1711*/
1712
1713void
1714ndbm_map_store(map, lhs, rhs)
1715 register MAP *map;
1716 char *lhs;
1717 char *rhs;
1718{
1719 datum key;
1720 datum data;
1721 int status;
1722 char keybuf[MAXNAME + 1];
1723
1724 if (tTd(38, 12))
1725 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1726 map->map_mname, lhs, rhs);
1727
1728 key.dsize = strlen(lhs);
1729 key.dptr = lhs;
1730 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1731 {
1732 if (key.dsize > sizeof keybuf - 1)
1733 key.dsize = sizeof keybuf - 1;
1734 memmove(keybuf, key.dptr, key.dsize);
1735 keybuf[key.dsize] = '\0';
1736 makelower(keybuf);
1737 key.dptr = keybuf;
1738 }
1739
1740 data.dsize = strlen(rhs);
1741 data.dptr = rhs;
1742
1743 if (bitset(MF_INCLNULL, map->map_mflags))
1744 {
1745 key.dsize++;
1746 data.dsize++;
1747 }
1748
1749 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1750 if (status > 0)
1751 {
1752 if (!bitset(MF_APPEND, map->map_mflags))
1753 message("050 Warning: duplicate alias name %s", lhs);
1754 else
1755 {
1756 static char *buf = NULL;
1757 static int bufsiz = 0;
1758 auto int xstat;
1759 datum old;
1760
1761 old.dptr = ndbm_map_lookup(map, key.dptr,
1762 (char **) NULL, &xstat);
1763 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1764 {
1765 old.dsize = strlen(old.dptr);
1766 if (data.dsize + old.dsize + 2 > bufsiz)
1767 {
1768 if (buf != NULL)
1769 (void) sm_free(buf);
1770 bufsiz = data.dsize + old.dsize + 2;
1771 buf = sm_pmalloc_x(bufsiz);
1772 }
1773 (void) sm_strlcpyn(buf, bufsiz, 3,
1774 data.dptr, ",", old.dptr);
1775 data.dsize = data.dsize + old.dsize + 1;
1776 data.dptr = buf;
1777 if (tTd(38, 9))
1778 sm_dprintf("ndbm_map_store append=%s\n",
1779 data.dptr);
1780 }
1781 }
1782 status = dbm_store((DBM *) map->map_db1,
1783 key, data, DBM_REPLACE);
1784 }
1785 if (status != 0)
1786 syserr("readaliases: dbm put (%s): %d", lhs, status);
1787}
1788
1789
1790/*
1791** NDBM_MAP_CLOSE -- close the database
1792*/
1793
1794void
1795ndbm_map_close(map)
1796 register MAP *map;
1797{
1798 if (tTd(38, 9))
1799 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1800 map->map_mname, map->map_file, map->map_mflags);
1801
1802 if (bitset(MF_WRITABLE, map->map_mflags))
1803 {
1804# ifdef NDBM_YP_COMPAT
1805 bool inclnull;
1806 char buf[MAXHOSTNAMELEN];
1807
1808 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1809 map->map_mflags &= ~MF_INCLNULL;
1810
1811 if (strstr(map->map_file, "/yp/") != NULL)
1812 {
1813 long save_mflags = map->map_mflags;
1814
1815 map->map_mflags |= MF_NOFOLDCASE;
1816
1817 (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
1818 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1819
1820 (void) gethostname(buf, sizeof buf);
1821 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1822
1823 map->map_mflags = save_mflags;
1824 }
1825
1826 if (inclnull)
1827 map->map_mflags |= MF_INCLNULL;
1828# endif /* NDBM_YP_COMPAT */
1829
1830 /* write out the distinguished alias */
1831 ndbm_map_store(map, "@", "@");
1832 }
1833 dbm_close((DBM *) map->map_db1);
1834
1835 /* release lock (if needed) */
1836# if !LOCK_ON_OPEN
1837 if (map->map_lockfd >= 0)
1838 (void) close(map->map_lockfd);
1839# endif /* !LOCK_ON_OPEN */
1840}
1841
1842#endif /* NDBM */
1843/*
1844** NEWDB (Hash and BTree) Modules
1845*/
1846
1847#if NEWDB
1848
1849/*
1850** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1851**
1852** These do rather bizarre locking. If you can lock on open,
1853** do that to avoid the condition of opening a database that
1854** is being rebuilt. If you don't, we'll try to fake it, but
1855** there will be a race condition. If opening for read-only,
1856** we immediately release the lock to avoid freezing things up.
1857** We really ought to hold the lock, but guarantee that we won't
1858** be pokey about it. That's hard to do.
1859*/
1860
1861/* these should be K line arguments */
1862# if DB_VERSION_MAJOR < 2
1863# define db_cachesize cachesize
1864# define h_nelem nelem
1865# ifndef DB_CACHE_SIZE
1866# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1867# endif /* ! DB_CACHE_SIZE */
1868# ifndef DB_HASH_NELEM
1869# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1870# endif /* ! DB_HASH_NELEM */
1871# endif /* DB_VERSION_MAJOR < 2 */
1872
1873bool
1874bt_map_open(map, mode)
1875 MAP *map;
1876 int mode;
1877{
1878# if DB_VERSION_MAJOR < 2
1879 BTREEINFO btinfo;
1880# endif /* DB_VERSION_MAJOR < 2 */
1881# if DB_VERSION_MAJOR == 2
1882 DB_INFO btinfo;
1883# endif /* DB_VERSION_MAJOR == 2 */
1884# if DB_VERSION_MAJOR > 2
1885 void *btinfo = NULL;
1886# endif /* DB_VERSION_MAJOR > 2 */
1887
1888 if (tTd(38, 2))
1889 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1890 map->map_mname, map->map_file, mode);
1891
1892# if DB_VERSION_MAJOR < 3
1893 memset(&btinfo, '\0', sizeof btinfo);
1894# ifdef DB_CACHE_SIZE
1895 btinfo.db_cachesize = DB_CACHE_SIZE;
1896# endif /* DB_CACHE_SIZE */
1897# endif /* DB_VERSION_MAJOR < 3 */
1898
1899 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1900}
1901
1902bool
1903hash_map_open(map, mode)
1904 MAP *map;
1905 int mode;
1906{
1907# if DB_VERSION_MAJOR < 2
1908 HASHINFO hinfo;
1909# endif /* DB_VERSION_MAJOR < 2 */
1910# if DB_VERSION_MAJOR == 2
1911 DB_INFO hinfo;
1912# endif /* DB_VERSION_MAJOR == 2 */
1913# if DB_VERSION_MAJOR > 2
1914 void *hinfo = NULL;
1915# endif /* DB_VERSION_MAJOR > 2 */
1916
1917 if (tTd(38, 2))
1918 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1919 map->map_mname, map->map_file, mode);
1920
1921# if DB_VERSION_MAJOR < 3
1922 memset(&hinfo, '\0', sizeof hinfo);
1923# ifdef DB_HASH_NELEM
1924 hinfo.h_nelem = DB_HASH_NELEM;
1925# endif /* DB_HASH_NELEM */
1926# ifdef DB_CACHE_SIZE
1927 hinfo.db_cachesize = DB_CACHE_SIZE;
1928# endif /* DB_CACHE_SIZE */
1929# endif /* DB_VERSION_MAJOR < 3 */
1930
1931 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1932}
1933
1934static bool
1935db_map_open(map, mode, mapclassname, dbtype, openinfo)
1936 MAP *map;
1937 int mode;
1938 char *mapclassname;
1939 DBTYPE dbtype;
1940# if DB_VERSION_MAJOR < 2
1941 const void *openinfo;
1942# endif /* DB_VERSION_MAJOR < 2 */
1943# if DB_VERSION_MAJOR == 2
1944 DB_INFO *openinfo;
1945# endif /* DB_VERSION_MAJOR == 2 */
1946# if DB_VERSION_MAJOR > 2
1947 void **openinfo;
1948# endif /* DB_VERSION_MAJOR > 2 */
1949{
1950 DB *db = NULL;
1951 int i;
1952 int omode;
1953 int smode = S_IREAD;
1954 int fd;
1955 long sff;
1956 int save_errno;
1957 struct stat st;
1958 char buf[MAXPATHLEN];
1959
1960 /* do initial file and directory checks */
1961 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
1962 {
1963 errno = 0;
1964 if (!bitset(MF_OPTIONAL, map->map_mflags))
1965 syserr("map \"%s\": map file %s name too long",
1966 map->map_mname, map->map_file);
1967 return false;
1968 }
1969 i = strlen(buf);
1970 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1971 {
1972 if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
1973 {
1974 errno = 0;
1975 if (!bitset(MF_OPTIONAL, map->map_mflags))
1976 syserr("map \"%s\": map file %s name too long",
1977 map->map_mname, map->map_file);
1978 return false;
1979 }
1980 }
1981
1982 mode &= O_ACCMODE;
1983 omode = mode;
1984
1985 sff = SFF_ROOTOK|SFF_REGONLY;
1986 if (mode == O_RDWR)
1987 {
1988 sff |= SFF_CREAT;
1989 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1990 sff |= SFF_NOSLINK;
1991 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1992 sff |= SFF_NOHLINK;
1993 smode = S_IWRITE;
1994 }
1995 else
1996 {
1997 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1998 sff |= SFF_NOWLINK;
1999 }
2000 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2001 sff |= SFF_SAFEDIRPATH;
2002 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2003
2004 if (i != 0)
2005 {
2006 char *prob = "unsafe";
2007
2008 /* cannot open this map */
2009 if (i == ENOENT)
2010 prob = "missing";
2011 if (tTd(38, 2))
2012 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2013 errno = i;
2014 if (!bitset(MF_OPTIONAL, map->map_mflags))
2015 syserr("%s map \"%s\": %s map file %s",
2016 mapclassname, map->map_mname, prob, buf);
2017 return false;
2018 }
2019 if (st.st_mode == ST_MODE_NOFILE)
2020 omode |= O_CREAT|O_EXCL;
2021
2022 map->map_lockfd = -1;
2023
2024# if LOCK_ON_OPEN
2025 if (mode == O_RDWR)
2026 omode |= O_TRUNC|O_EXLOCK;
2027 else
2028 omode |= O_SHLOCK;
2029# else /* LOCK_ON_OPEN */
2030 /*
2031 ** Pre-lock the file to avoid race conditions. In particular,
2032 ** since dbopen returns NULL if the file is zero length, we
2033 ** must have a locked instance around the dbopen.
2034 */
2035
2036 fd = open(buf, omode, DBMMODE);
2037 if (fd < 0)
2038 {
2039 if (!bitset(MF_OPTIONAL, map->map_mflags))
2040 syserr("db_map_open: cannot pre-open database %s", buf);
2041 return false;
2042 }
2043
2044 /* make sure no baddies slipped in just before the open... */
2045 if (filechanged(buf, fd, &st))
2046 {
2047 save_errno = errno;
2048 (void) close(fd);
2049 errno = save_errno;
2050 syserr("db_map_open(%s): file changed after pre-open", buf);
2051 return false;
2052 }
2053
2054 /* if new file, get the "before" bits for later filechanged check */
2055 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2056 {
2057 save_errno = errno;
2058 (void) close(fd);
2059 errno = save_errno;
2060 syserr("db_map_open(%s): cannot fstat pre-opened file",
2061 buf);
2062 return false;
2063 }
2064
2065 /* actually lock the pre-opened file */
2066 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2067 syserr("db_map_open: cannot lock %s", buf);
2068
2069 /* set up mode bits for dbopen */
2070 if (mode == O_RDWR)
2071 omode |= O_TRUNC;
2072 omode &= ~(O_EXCL|O_CREAT);
2073# endif /* LOCK_ON_OPEN */
2074
2075# if DB_VERSION_MAJOR < 2
2076 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2077# else /* DB_VERSION_MAJOR < 2 */
2078 {
2079 int flags = 0;
2080# if DB_VERSION_MAJOR > 2
2081 int ret;
2082# endif /* DB_VERSION_MAJOR > 2 */
2083
2084 if (mode == O_RDONLY)
2085 flags |= DB_RDONLY;
2086 if (bitset(O_CREAT, omode))
2087 flags |= DB_CREATE;
2088 if (bitset(O_TRUNC, omode))
2089 flags |= DB_TRUNCATE;
2090 SM_DB_FLAG_ADD(flags);
2091
2092# if DB_VERSION_MAJOR > 2
2093 ret = db_create(&db, NULL, 0);
2094# ifdef DB_CACHE_SIZE
2095 if (ret == 0 && db != NULL)
2096 {
2097 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2098 if (ret != 0)
2099 {
2100 (void) db->close(db, 0);
2101 db = NULL;
2102 }
2103 }
2104# endif /* DB_CACHE_SIZE */
2105# ifdef DB_HASH_NELEM
2106 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2107 {
2108 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2109 if (ret != 0)
2110 {
2111 (void) db->close(db, 0);
2112 db = NULL;
2113 }
2114 }
2115# endif /* DB_HASH_NELEM */
2116 if (ret == 0 && db != NULL)
2117 {
2118 ret = db->open(db,
2119 DBTXN /* transaction for DB 4.1 */
2120 buf, NULL, dbtype, flags, DBMMODE);
2121 if (ret != 0)
2122 {
2123#ifdef DB_OLD_VERSION
2124 if (ret == DB_OLD_VERSION)
2125 ret = EINVAL;
2126#endif /* DB_OLD_VERSION */
2127 (void) db->close(db, 0);
2128 db = NULL;
2129 }
2130 }
2131 errno = ret;
2132# else /* DB_VERSION_MAJOR > 2 */
2133 errno = db_open(buf, dbtype, flags, DBMMODE,
2134 NULL, openinfo, &db);
2135# endif /* DB_VERSION_MAJOR > 2 */
2136 }
2137# endif /* DB_VERSION_MAJOR < 2 */
2138 save_errno = errno;
2139
2140# if !LOCK_ON_OPEN
2141 if (mode == O_RDWR)
2142 map->map_lockfd = fd;
2143 else
2144 (void) close(fd);
2145# endif /* !LOCK_ON_OPEN */
2146
2147 if (db == NULL)
2148 {
2149 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2150 aliaswait(map, ".db", false))
2151 return true;
2152# if !LOCK_ON_OPEN
2153 if (map->map_lockfd >= 0)
2154 (void) close(map->map_lockfd);
2155# endif /* !LOCK_ON_OPEN */
2156 errno = save_errno;
2157 if (!bitset(MF_OPTIONAL, map->map_mflags))
2158 syserr("Cannot open %s database %s",
2159 mapclassname, buf);
2160 return false;
2161 }
2162
2163# if DB_VERSION_MAJOR < 2
2164 fd = db->fd(db);
2165# else /* DB_VERSION_MAJOR < 2 */
2166 fd = -1;
2167 errno = db->fd(db, &fd);
2168# endif /* DB_VERSION_MAJOR < 2 */
2169 if (filechanged(buf, fd, &st))
2170 {
2171 save_errno = errno;
2172# if DB_VERSION_MAJOR < 2
2173 (void) db->close(db);
2174# else /* DB_VERSION_MAJOR < 2 */
2175 errno = db->close(db, 0);
2176# endif /* DB_VERSION_MAJOR < 2 */
2177# if !LOCK_ON_OPEN
2178 if (map->map_lockfd >= 0)
2179 (void) close(map->map_lockfd);
2180# endif /* !LOCK_ON_OPEN */
2181 errno = save_errno;
2182 syserr("db_map_open(%s): file changed after open", buf);
2183 return false;
2184 }
2185
2186 if (mode == O_RDWR)
2187 map->map_mflags |= MF_LOCKED;
2188# if LOCK_ON_OPEN
2189 if (fd >= 0 && mode == O_RDONLY)
2190 {
2191 (void) lockfile(fd, buf, NULL, LOCK_UN);
2192 }
2193# endif /* LOCK_ON_OPEN */
2194
2195 /* try to make sure that at least the database header is on disk */
2196 if (mode == O_RDWR)
2197 {
2198 (void) db->sync(db, 0);
2199 if (geteuid() == 0 && TrustedUid != 0)
2200 {
2201# if HASFCHOWN
2202 if (fchown(fd, TrustedUid, -1) < 0)
2203 {
2204 int err = errno;
2205
2206 sm_syslog(LOG_ALERT, NOQID,
2207 "ownership change on %s failed: %s",
2208 buf, sm_errstring(err));
2209 message("050 ownership change on %s failed: %s",
2210 buf, sm_errstring(err));
2211 }
2212# else /* HASFCHOWN */
2213 sm_syslog(LOG_ALERT, NOQID,
2214 "no fchown(): cannot change ownership on %s",
2215 map->map_file);
2216 message("050 no fchown(): cannot change ownership on %s",
2217 map->map_file);
2218# endif /* HASFCHOWN */
2219 }
2220 }
2221
2222 map->map_db2 = (ARBPTR_T) db;
2223
2224 /*
2225 ** Need to set map_mtime before the call to aliaswait()
2226 ** as aliaswait() will call map_lookup() which requires
2227 ** map_mtime to be set
2228 */
2229
2230 if (fd >= 0 && fstat(fd, &st) >= 0)
2231 map->map_mtime = st.st_mtime;
2232
2233 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2234 !aliaswait(map, ".db", true))
2235 return false;
2236 return true;
2237}
2238
2239
2240/*
2241** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2242*/
2243
2244char *
2245db_map_lookup(map, name, av, statp)
2246 MAP *map;
2247 char *name;
2248 char **av;
2249 int *statp;
2250{
2251 DBT key, val;
2252 register DB *db = (DB *) map->map_db2;
2253 int i;
2254 int st;
2255 int save_errno;
2256 int fd;
2257 struct stat stbuf;
2258 char keybuf[MAXNAME + 1];
2259 char buf[MAXPATHLEN];
2260
2261 memset(&key, '\0', sizeof key);
2262 memset(&val, '\0', sizeof val);
2263
2264 if (tTd(38, 20))
2265 sm_dprintf("db_map_lookup(%s, %s)\n",
2266 map->map_mname, name);
2267
2268 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
2269 {
2270 errno = 0;
2271 if (!bitset(MF_OPTIONAL, map->map_mflags))
2272 syserr("map \"%s\": map file %s name too long",
2273 map->map_mname, map->map_file);
2274 return NULL;
2275 }
2276 i = strlen(buf);
2277 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2278 buf[i - 3] = '\0';
2279
2280 key.size = strlen(name);
2281 if (key.size > sizeof keybuf - 1)
2282 key.size = sizeof keybuf - 1;
2283 key.data = keybuf;
2284 memmove(keybuf, name, key.size);
2285 keybuf[key.size] = '\0';
2286 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2287 makelower(keybuf);
2288 lockdb:
2289# if DB_VERSION_MAJOR < 2
2290 fd = db->fd(db);
2291# else /* DB_VERSION_MAJOR < 2 */
2292 fd = -1;
2293 errno = db->fd(db, &fd);
2294# endif /* DB_VERSION_MAJOR < 2 */
2295 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2296 (void) lockfile(fd, buf, ".db", LOCK_SH);
2297 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2298 {
2299 /* Reopen the database to sync the cache */
2300 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2301 : O_RDONLY;
2302
2303 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2304 (void) lockfile(fd, buf, ".db", LOCK_UN);
2305 map->map_mflags |= MF_CLOSING;
2306 map->map_class->map_close(map);
2307 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2308 if (map->map_class->map_open(map, omode))
2309 {
2310 map->map_mflags |= MF_OPEN;
2311 map->map_pid = CurrentPid;
2312 if ((omode && O_ACCMODE) == O_RDWR)
2313 map->map_mflags |= MF_WRITABLE;
2314 db = (DB *) map->map_db2;
2315 goto lockdb;
2316 }
2317 else
2318 {
2319 if (!bitset(MF_OPTIONAL, map->map_mflags))
2320 {
2321 extern MAPCLASS BogusMapClass;
2322
2323 *statp = EX_TEMPFAIL;
2324 map->map_orgclass = map->map_class;
2325 map->map_class = &BogusMapClass;
2326 map->map_mflags |= MF_OPEN;
2327 map->map_pid = CurrentPid;
2328 syserr("Cannot reopen DB database %s",
2329 map->map_file);
2330 }
2331 return NULL;
2332 }
2333 }
2334
2335 st = 1;
2336 if (bitset(MF_TRY0NULL, map->map_mflags))
2337 {
2338# if DB_VERSION_MAJOR < 2
2339 st = db->get(db, &key, &val, 0);
2340# else /* DB_VERSION_MAJOR < 2 */
2341 errno = db->get(db, NULL, &key, &val, 0);
2342 switch (errno)
2343 {
2344 case DB_NOTFOUND:
2345 case DB_KEYEMPTY:
2346 st = 1;
2347 break;
2348
2349 case 0:
2350 st = 0;
2351 break;
2352
2353 default:
2354 st = -1;
2355 break;
2356 }
2357# endif /* DB_VERSION_MAJOR < 2 */
2358 if (st == 0)
2359 map->map_mflags &= ~MF_TRY1NULL;
2360 }
2361 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2362 {
2363 key.size++;
2364# if DB_VERSION_MAJOR < 2
2365 st = db->get(db, &key, &val, 0);
2366# else /* DB_VERSION_MAJOR < 2 */
2367 errno = db->get(db, NULL, &key, &val, 0);
2368 switch (errno)
2369 {
2370 case DB_NOTFOUND:
2371 case DB_KEYEMPTY:
2372 st = 1;
2373 break;
2374
2375 case 0:
2376 st = 0;
2377 break;
2378
2379 default:
2380 st = -1;
2381 break;
2382 }
2383# endif /* DB_VERSION_MAJOR < 2 */
2384 if (st == 0)
2385 map->map_mflags &= ~MF_TRY0NULL;
2386 }
2387 save_errno = errno;
2388 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2389 (void) lockfile(fd, buf, ".db", LOCK_UN);
2390 if (st != 0)
2391 {
2392 errno = save_errno;
2393 if (st < 0)
2394 syserr("db_map_lookup: get (%s)", name);
2395 return NULL;
2396 }
2397 if (bitset(MF_MATCHONLY, map->map_mflags))
2398 return map_rewrite(map, name, strlen(name), NULL);
2399 else
2400 return map_rewrite(map, val.data, val.size, av);
2401}
2402
2403
2404/*
2405** DB_MAP_STORE -- store a datum in the NEWDB database
2406*/
2407
2408void
2409db_map_store(map, lhs, rhs)
2410 register MAP *map;
2411 char *lhs;
2412 char *rhs;
2413{
2414 int status;
2415 DBT key;
2416 DBT data;
2417 register DB *db = map->map_db2;
2418 char keybuf[MAXNAME + 1];
2419
2420 memset(&key, '\0', sizeof key);
2421 memset(&data, '\0', sizeof data);
2422
2423 if (tTd(38, 12))
2424 sm_dprintf("db_map_store(%s, %s, %s)\n",
2425 map->map_mname, lhs, rhs);
2426
2427 key.size = strlen(lhs);
2428 key.data = lhs;
2429 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2430 {
2431 if (key.size > sizeof keybuf - 1)
2432 key.size = sizeof keybuf - 1;
2433 memmove(keybuf, key.data, key.size);
2434 keybuf[key.size] = '\0';
2435 makelower(keybuf);
2436 key.data = keybuf;
2437 }
2438
2439 data.size = strlen(rhs);
2440 data.data = rhs;
2441
2442 if (bitset(MF_INCLNULL, map->map_mflags))
2443 {
2444 key.size++;
2445 data.size++;
2446 }
2447
2448# if DB_VERSION_MAJOR < 2
2449 status = db->put(db, &key, &data, R_NOOVERWRITE);
2450# else /* DB_VERSION_MAJOR < 2 */
2451 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2452 switch (errno)
2453 {
2454 case DB_KEYEXIST:
2455 status = 1;
2456 break;
2457
2458 case 0:
2459 status = 0;
2460 break;
2461
2462 default:
2463 status = -1;
2464 break;
2465 }
2466# endif /* DB_VERSION_MAJOR < 2 */
2467 if (status > 0)
2468 {
2469 if (!bitset(MF_APPEND, map->map_mflags))
2470 message("050 Warning: duplicate alias name %s", lhs);
2471 else
2472 {
2473 static char *buf = NULL;
2474 static int bufsiz = 0;
2475 DBT old;
2476
2477 memset(&old, '\0', sizeof old);
2478
2479 old.data = db_map_lookup(map, key.data,
2480 (char **) NULL, &status);
2481 if (old.data != NULL)
2482 {
2483 old.size = strlen(old.data);
2484 if (data.size + old.size + 2 > (size_t) bufsiz)
2485 {
2486 if (buf != NULL)
2487 sm_free(buf);
2488 bufsiz = data.size + old.size + 2;
2489 buf = sm_pmalloc_x(bufsiz);
2490 }
2491 (void) sm_strlcpyn(buf, bufsiz, 3,
2492 (char *) data.data, ",",
2493 (char *) old.data);
2494 data.size = data.size + old.size + 1;
2495 data.data = buf;
2496 if (tTd(38, 9))
2497 sm_dprintf("db_map_store append=%s\n",
2498 (char *) data.data);
2499 }
2500 }
2501# if DB_VERSION_MAJOR < 2
2502 status = db->put(db, &key, &data, 0);
2503# else /* DB_VERSION_MAJOR < 2 */
2504 status = errno = db->put(db, NULL, &key, &data, 0);
2505# endif /* DB_VERSION_MAJOR < 2 */
2506 }
2507 if (status != 0)
2508 syserr("readaliases: db put (%s)", lhs);
2509}
2510
2511
2512/*
2513** DB_MAP_CLOSE -- add distinguished entries and close the database
2514*/
2515
2516void
2517db_map_close(map)
2518 MAP *map;
2519{
2520 register DB *db = map->map_db2;
2521
2522 if (tTd(38, 9))
2523 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2524 map->map_mname, map->map_file, map->map_mflags);
2525
2526 if (bitset(MF_WRITABLE, map->map_mflags))
2527 {
2528 /* write out the distinguished alias */
2529 db_map_store(map, "@", "@");
2530 }
2531
2532 (void) db->sync(db, 0);
2533
2534# if !LOCK_ON_OPEN
2535 if (map->map_lockfd >= 0)
2536 (void) close(map->map_lockfd);
2537# endif /* !LOCK_ON_OPEN */
2538
2539# if DB_VERSION_MAJOR < 2
2540 if (db->close(db) != 0)
2541# else /* DB_VERSION_MAJOR < 2 */
2542 /*
2543 ** Berkeley DB can use internal shared memory
2544 ** locking for its memory pool. Closing a map
2545 ** opened by another process will interfere
2546 ** with the shared memory and locks of the parent
2547 ** process leaving things in a bad state.
2548 */
2549
2550 /*
2551 ** If this map was not opened by the current
2552 ** process, do not close the map but recover
2553 ** the file descriptor.
2554 */
2555
2556 if (map->map_pid != CurrentPid)
2557 {
2558 int fd = -1;
2559
2560 errno = db->fd(db, &fd);
2561 if (fd >= 0)
2562 (void) close(fd);
2563 return;
2564 }
2565
2566 if ((errno = db->close(db, 0)) != 0)
2567# endif /* DB_VERSION_MAJOR < 2 */
2568 syserr("db_map_close(%s, %s, %lx): db close failure",
2569 map->map_mname, map->map_file, map->map_mflags);
2570}
2571#endif /* NEWDB */
2572/*
2573** NIS Modules
2574*/
2575
2576#if NIS
2577
2578# ifndef YPERR_BUSY
2579# define YPERR_BUSY 16
2580# endif /* ! YPERR_BUSY */
2581
2582/*
2583** NIS_MAP_OPEN -- open DBM map
2584*/
2585
2586bool
2587nis_map_open(map, mode)
2588 MAP *map;
2589 int mode;
2590{
2591 int yperr;
2592 register char *p;
2593 auto char *vp;
2594 auto int vsize;
2595
2596 if (tTd(38, 2))
2597 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2598 map->map_mname, map->map_file, mode);
2599
2600 mode &= O_ACCMODE;
2601 if (mode != O_RDONLY)
2602 {
2603 /* issue a pseudo-error message */
2604 errno = SM_EMAPCANTWRITE;
2605 return false;
2606 }
2607
2608 p = strchr(map->map_file, '@');
2609 if (p != NULL)
2610 {
2611 *p++ = '\0';
2612 if (*p != '\0')
2613 map->map_domain = p;
2614 }
2615
2616 if (*map->map_file == '\0')
2617 map->map_file = "mail.aliases";
2618
2619 if (map->map_domain == NULL)
2620 {
2621 yperr = yp_get_default_domain(&map->map_domain);
2622 if (yperr != 0)
2623 {
2624 if (!bitset(MF_OPTIONAL, map->map_mflags))
2625 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2626 map->map_file);
2627 return false;
2628 }
2629 }
2630
2631 /* check to see if this map actually exists */
2632 vp = NULL;
2633 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2634 &vp, &vsize);
2635 if (tTd(38, 10))
2636 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2637 map->map_domain, map->map_file, yperr_string(yperr));
2638 if (vp != NULL)
2639 sm_free(vp);
2640
2641 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2642 {
2643 /*
2644 ** We ought to be calling aliaswait() here if this is an
2645 ** alias file, but powerful HP-UX NIS servers apparently
2646 ** don't insert the @:@ token into the alias map when it
2647 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2648 */
2649
2650# if 0
2651 if (!bitset(MF_ALIAS, map->map_mflags) ||
2652 aliaswait(map, NULL, true))
2653# endif /* 0 */
2654 return true;
2655 }
2656
2657 if (!bitset(MF_OPTIONAL, map->map_mflags))
2658 {
2659 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2660 map->map_file, map->map_domain, yperr_string(yperr));
2661 }
2662
2663 return false;
2664}
2665
2666
2667/*
2668** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2669*/
2670
2671/* ARGSUSED3 */
2672char *
2673nis_map_lookup(map, name, av, statp)
2674 MAP *map;
2675 char *name;
2676 char **av;
2677 int *statp;
2678{
2679 char *vp;
2680 auto int vsize;
2681 int buflen;
2682 int yperr;
2683 char keybuf[MAXNAME + 1];
2684 char *SM_NONVOLATILE result = NULL;
2685
2686 if (tTd(38, 20))
2687 sm_dprintf("nis_map_lookup(%s, %s)\n",
2688 map->map_mname, name);
2689
2690 buflen = strlen(name);
2691 if (buflen > sizeof keybuf - 1)
2692 buflen = sizeof keybuf - 1;
2693 memmove(keybuf, name, buflen);
2694 keybuf[buflen] = '\0';
2695 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2696 makelower(keybuf);
2697 yperr = YPERR_KEY;
2698 vp = NULL;
2699 if (bitset(MF_TRY0NULL, map->map_mflags))
2700 {
2701 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2702 &vp, &vsize);
2703 if (yperr == 0)
2704 map->map_mflags &= ~MF_TRY1NULL;
2705 }
2706 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2707 {
2708 SM_FREE_CLR(vp);
2709 buflen++;
2710 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2711 &vp, &vsize);
2712 if (yperr == 0)
2713 map->map_mflags &= ~MF_TRY0NULL;
2714 }
2715 if (yperr != 0)
2716 {
2717 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2718 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2719 if (vp != NULL)
2720 sm_free(vp);
2721 return NULL;
2722 }
2723 SM_TRY
2724 if (bitset(MF_MATCHONLY, map->map_mflags))
2725 result = map_rewrite(map, name, strlen(name), NULL);
2726 else
2727 result = map_rewrite(map, vp, vsize, av);
2728 SM_FINALLY
2729 if (vp != NULL)
2730 sm_free(vp);
2731 SM_END_TRY
2732 return result;
2733}
2734
2735
2736/*
2737** NIS_GETCANONNAME -- look up canonical name in NIS
2738*/
2739
2740static bool
2741nis_getcanonname(name, hbsize, statp)
2742 char *name;
2743 int hbsize;
2744 int *statp;
2745{
2746 char *vp;
2747 auto int vsize;
2748 int keylen;
2749 int yperr;
2750 static bool try0null = true;
2751 static bool try1null = true;
2752 static char *yp_domain = NULL;
2753 char host_record[MAXLINE];
2754 char cbuf[MAXNAME];
2755 char nbuf[MAXNAME + 1];
2756
2757 if (tTd(38, 20))
2758 sm_dprintf("nis_getcanonname(%s)\n", name);
2759
2760 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
2761 {
2762 *statp = EX_UNAVAILABLE;
2763 return false;
2764 }
2765 (void) shorten_hostname(nbuf);
2766 keylen = strlen(nbuf);
2767
2768 if (yp_domain == NULL)
2769 (void) yp_get_default_domain(&yp_domain);
2770 makelower(nbuf);
2771 yperr = YPERR_KEY;
2772 vp = NULL;
2773 if (try0null)
2774 {
2775 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2776 &vp, &vsize);
2777 if (yperr == 0)
2778 try1null = false;
2779 }
2780 if (yperr == YPERR_KEY && try1null)
2781 {
2782 SM_FREE_CLR(vp);
2783 keylen++;
2784 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2785 &vp, &vsize);
2786 if (yperr == 0)
2787 try0null = false;
2788 }
2789 if (yperr != 0)
2790 {
2791 if (yperr == YPERR_KEY)
2792 *statp = EX_NOHOST;
2793 else if (yperr == YPERR_BUSY)
2794 *statp = EX_TEMPFAIL;
2795 else
2796 *statp = EX_UNAVAILABLE;
2797 if (vp != NULL)
2798 sm_free(vp);
2799 return false;
2800 }
2801 (void) sm_strlcpy(host_record, vp, sizeof host_record);
2802 sm_free(vp);
2803 if (tTd(38, 44))
2804 sm_dprintf("got record `%s'\n", host_record);
2805 vp = strpbrk(host_record, "#\n");
2806 if (vp != NULL)
2807 *vp = '\0';
2808 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
2809 {
2810 /* this should not happen, but.... */
2811 *statp = EX_NOHOST;
2812 return false;
2813 }
2814 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2815 {
2816 *statp = EX_UNAVAILABLE;
2817 return false;
2818 }
2819 *statp = EX_OK;
2820 return true;
2821}
2822
2823#endif /* NIS */
2824/*
2825** NISPLUS Modules
2826**
2827** This code donated by Sun Microsystems.
2828*/
2829
2830#if NISPLUS
2831
2832# undef NIS /* symbol conflict in nis.h */
2833# undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2834# include <rpcsvc/nis.h>
2835# include <rpcsvc/nislib.h>
2836
2837# define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2838# define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2839# define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2840# define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2841
2842/*
2843** NISPLUS_MAP_OPEN -- open nisplus table
2844*/
2845
2846bool
2847nisplus_map_open(map, mode)
2848 MAP *map;
2849 int mode;
2850{
2851 nis_result *res = NULL;
2852 int retry_cnt, max_col, i;
2853 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2854
2855 if (tTd(38, 2))
2856 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2857 map->map_mname, map->map_file, mode);
2858
2859 mode &= O_ACCMODE;
2860 if (mode != O_RDONLY)
2861 {
2862 errno = EPERM;
2863 return false;
2864 }
2865
2866 if (*map->map_file == '\0')
2867 map->map_file = "mail_aliases.org_dir";
2868
2869 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2870 {
2871 /* set default NISPLUS Domain to $m */
2872 map->map_domain = newstr(nisplus_default_domain());
2873 if (tTd(38, 2))
2874 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2875 map->map_file, map->map_domain);
2876 }
2877 if (!PARTIAL_NAME(map->map_file))
2878 {
2879 map->map_domain = newstr("");
2880 (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
2881 }
2882 else
2883 {
2884 /* check to see if this map actually exists */
2885 (void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
2886 map->map_file, ".", map->map_domain);
2887 }
2888
2889 retry_cnt = 0;
2890 while (res == NULL || res->status != NIS_SUCCESS)
2891 {
2892 res = nis_lookup(qbuf, FOLLOW_LINKS);
2893 switch (res->status)
2894 {
2895 case NIS_SUCCESS:
2896 break;
2897
2898 case NIS_TRYAGAIN:
2899 case NIS_RPCERROR:
2900 case NIS_NAMEUNREACHABLE:
2901 if (retry_cnt++ > 4)
2902 {
2903 errno = EAGAIN;
2904 return false;
2905 }
2906 /* try not to overwhelm hosed server */
2907 sleep(2);
2908 break;
2909
2910 default: /* all other nisplus errors */
2911# if 0
2912 if (!bitset(MF_OPTIONAL, map->map_mflags))
2913 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2914 map->map_file, map->map_domain,
2915 nis_sperrno(res->status));
2916# endif /* 0 */
2917 errno = EAGAIN;
2918 return false;
2919 }
2920 }
2921
2922 if (NIS_RES_NUMOBJ(res) != 1 ||
2923 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2924 {
2925 if (tTd(38, 10))
2926 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2927# if 0
2928 if (!bitset(MF_OPTIONAL, map->map_mflags))
2929 syserr("451 4.3.5 %s.%s: %s is not a table",
2930 map->map_file, map->map_domain,
2931 nis_sperrno(res->status));
2932# endif /* 0 */
2933 errno = EBADF;
2934 return false;
2935 }
2936 /* default key column is column 0 */
2937 if (map->map_keycolnm == NULL)
2938 map->map_keycolnm = newstr(COL_NAME(res,0));
2939
2940 max_col = COL_MAX(res);
2941
2942 /* verify the key column exist */
2943 for (i = 0; i < max_col; i++)
2944 {
2945 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2946 break;
2947 }
2948 if (i == max_col)
2949 {
2950 if (tTd(38, 2))
2951 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2952 map->map_file, map->map_keycolnm);
2953 errno = ENOENT;
2954 return false;
2955 }
2956
2957 /* default value column is the last column */
2958 if (map->map_valcolnm == NULL)
2959 {
2960 map->map_valcolno = max_col - 1;
2961 return true;
2962 }
2963
2964 for (i = 0; i< max_col; i++)
2965 {
2966 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2967 {
2968 map->map_valcolno = i;
2969 return true;
2970 }
2971 }
2972
2973 if (tTd(38, 2))
2974 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2975 map->map_file, map->map_keycolnm);
2976 errno = ENOENT;
2977 return false;
2978}
2979
2980
2981/*
2982** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2983*/
2984
2985char *
2986nisplus_map_lookup(map, name, av, statp)
2987 MAP *map;
2988 char *name;
2989 char **av;
2990 int *statp;
2991{
2992 char *p;
2993 auto int vsize;
2994 char *skp;
2995 int skleft;
2996 char search_key[MAXNAME + 4];
2997 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2998 nis_result *result;
2999
3000 if (tTd(38, 20))
3001 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3002 map->map_mname, name);
3003
3004 if (!bitset(MF_OPEN, map->map_mflags))
3005 {
3006 if (nisplus_map_open(map, O_RDONLY))
3007 {
3008 map->map_mflags |= MF_OPEN;
3009 map->map_pid = CurrentPid;
3010 }
3011 else
3012 {
3013 *statp = EX_UNAVAILABLE;
3014 return NULL;
3015 }
3016 }
3017
3018 /*
3019 ** Copy the name to the key buffer, escaping double quote characters
3020 ** by doubling them and quoting "]" and "," to avoid having the
3021 ** NIS+ parser choke on them.
3022 */
3023
3024 skleft = sizeof search_key - 4;
3025 skp = search_key;
3026 for (p = name; *p != '\0' && skleft > 0; p++)
3027 {
3028 switch (*p)
3029 {
3030 case ']':
3031 case ',':
3032 /* quote the character */
3033 *skp++ = '"';
3034 *skp++ = *p;
3035 *skp++ = '"';
3036 skleft -= 3;
3037 break;
3038
3039 case '"':
3040 /* double the quote */
3041 *skp++ = '"';
3042 skleft--;
3043 /* FALLTHROUGH */
3044
3045 default:
3046 *skp++ = *p;
3047 skleft--;
3048 break;
3049 }
3050 }
3051 *skp = '\0';
3052 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3053 makelower(search_key);
3054
3055 /* construct the query */
3056 if (PARTIAL_NAME(map->map_file))
3057 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
3058 map->map_keycolnm, search_key, map->map_file,
3059 map->map_domain);
3060 else
3061 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
3062 map->map_keycolnm, search_key, map->map_file);
3063
3064 if (tTd(38, 20))
3065 sm_dprintf("qbuf=%s\n", qbuf);
3066 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3067 if (result->status == NIS_SUCCESS)
3068 {
3069 int count;
3070 char *str;
3071
3072 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3073 {
3074 if (LogLevel > 10)
3075 sm_syslog(LOG_WARNING, CurEnv->e_id,
3076 "%s: lookup error, expected 1 entry, got %d",
3077 map->map_file, count);
3078
3079 /* ignore second entry */
3080 if (tTd(38, 20))
3081 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3082 name, count);
3083 }
3084
3085 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3086 /* set the length of the result */
3087 if (p == NULL)
3088 p = "";
3089 vsize = strlen(p);
3090 if (tTd(38, 20))
3091 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3092 name, p);
3093 if (bitset(MF_MATCHONLY, map->map_mflags))
3094 str = map_rewrite(map, name, strlen(name), NULL);
3095 else
3096 str = map_rewrite(map, p, vsize, av);
3097 nis_freeresult(result);
3098 *statp = EX_OK;
3099 return str;
3100 }
3101 else
3102 {
3103 if (result->status == NIS_NOTFOUND)
3104 *statp = EX_NOTFOUND;
3105 else if (result->status == NIS_TRYAGAIN)
3106 *statp = EX_TEMPFAIL;
3107 else
3108 {
3109 *statp = EX_UNAVAILABLE;
3110 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3111 }
3112 }
3113 if (tTd(38, 20))
3114 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3115 nis_freeresult(result);
3116 return NULL;
3117}
3118
3119
3120
3121/*
3122** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3123*/
3124
3125static bool
3126nisplus_getcanonname(name, hbsize, statp)
3127 char *name;
3128 int hbsize;
3129 int *statp;
3130{
3131 char *vp;
3132 auto int vsize;
3133 nis_result *result;
3134 char *p;
3135 char nbuf[MAXNAME + 1];
3136 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3137
3138 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
3139 {
3140 *statp = EX_UNAVAILABLE;
3141 return false;
3142 }
3143 (void) shorten_hostname(nbuf);
3144
3145 p = strchr(nbuf, '.');
3146 if (p == NULL)
3147 {
3148 /* single token */
3149 (void) sm_snprintf(qbuf, sizeof qbuf,
3150 "[name=%s],hosts.org_dir", nbuf);
3151 }
3152 else if (p[1] != '\0')
3153 {
3154 /* multi token -- take only first token in nbuf */
3155 *p = '\0';
3156 (void) sm_snprintf(qbuf, sizeof qbuf,
3157 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3158 }
3159 else
3160 {
3161 *statp = EX_NOHOST;
3162 return false;
3163 }
3164
3165 if (tTd(38, 20))
3166 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3167 name, qbuf);
3168
3169 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3170 NULL, NULL);
3171
3172 if (result->status == NIS_SUCCESS)
3173 {
3174 int count;
3175 char *domain;
3176
3177 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3178 {
3179 if (LogLevel > 10)
3180 sm_syslog(LOG_WARNING, CurEnv->e_id,
3181 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3182 count);
3183
3184 /* ignore second entry */
3185 if (tTd(38, 20))
3186 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3187 name, count);
3188 }
3189
3190 if (tTd(38, 20))
3191 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3192 name, (NIS_RES_OBJECT(result))->zo_domain);
3193
3194
3195 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3196 vsize = strlen(vp);
3197 if (tTd(38, 20))
3198 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3199 name, vp);
3200 if (strchr(vp, '.') != NULL)
3201 {
3202 domain = "";
3203 }
3204 else
3205 {
3206 domain = macvalue('m', CurEnv);
3207 if (domain == NULL)
3208 domain = "";
3209 }
3210 if (hbsize > vsize + (int) strlen(domain) + 1)
3211 {
3212 if (domain[0] == '\0')
3213 (void) sm_strlcpy(name, vp, hbsize);
3214 else
3215 (void) sm_snprintf(name, hbsize,
3216 "%s.%s", vp, domain);
3217 *statp = EX_OK;
3218 }
3219 else
3220 *statp = EX_NOHOST;
3221 nis_freeresult(result);
3222 return true;
3223 }
3224 else
3225 {
3226 if (result->status == NIS_NOTFOUND)
3227 *statp = EX_NOHOST;
3228 else if (result->status == NIS_TRYAGAIN)
3229 *statp = EX_TEMPFAIL;
3230 else
3231 *statp = EX_UNAVAILABLE;
3232 }
3233 if (tTd(38, 20))
3234 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3235 name, result->status, *statp);
3236 nis_freeresult(result);
3237 return false;
3238}
3239
3240char *
3241nisplus_default_domain()
3242{
3243 static char default_domain[MAXNAME + 1] = "";
3244 char *p;
3245
3246 if (default_domain[0] != '\0')
3247 return default_domain;
3248
3249 p = nis_local_directory();
3250 (void) sm_strlcpy(default_domain, p, sizeof default_domain);
3251 return default_domain;
3252}
3253
3254#endif /* NISPLUS */
3255/*
3256** LDAP Modules
3257*/
3258
3259/*
3260** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3261*/
3262
3263#if defined(LDAPMAP) || defined(PH_MAP)
3264
3265# if PH_MAP
3266# define ph_map_dequote ldapmap_dequote
3267# endif /* PH_MAP */
3268
3269static char *ldapmap_dequote __P((char *));
3270
3271static char *
3272ldapmap_dequote(str)
3273 char *str;
3274{
3275 char *p;
3276 char *start;
3277
3278 if (str == NULL)
3279 return NULL;
3280
3281 p = str;
3282 if (*p == '"')
3283 {
3284 /* Should probably swallow initial whitespace here */
3285 start = ++p;
3286 }
3287 else
3288 return str;
3289 while (*p != '"' && *p != '\0')
3290 p++;
3291 if (*p != '\0')
3292 *p = '\0';
3293 return start;
3294}
3295#endif /* defined(LDAPMAP) || defined(PH_MAP) */
3296
3297#if LDAPMAP
3298
3299static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3300
3301/*
3302** LDAPMAP_OPEN -- open LDAP map
3303**
3304** Connect to the LDAP server. Re-use existing connections since a
3305** single server connection to a host (with the same host, port,
3306** bind DN, and secret) can answer queries for multiple maps.
3307*/
3308
3309bool
3310ldapmap_open(map, mode)
3311 MAP *map;
3312 int mode;
3313{
3314 SM_LDAP_STRUCT *lmap;
3315 STAB *s;
3316 char *id;
3317
3318 if (tTd(38, 2))
3319 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3320
3321 mode &= O_ACCMODE;
3322
3323 /* sendmail doesn't have the ability to write to LDAP (yet) */
3324 if (mode != O_RDONLY)
3325 {
3326 /* issue a pseudo-error message */
3327 errno = SM_EMAPCANTWRITE;
3328 return false;
3329 }
3330
3331 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3332
3333 s = ldapmap_findconn(lmap);
3334 if (s->s_lmap != NULL)
3335 {
3336 /* Already have a connection open to this LDAP server */
3337 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3338 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3339
3340 /* Add this map as head of linked list */
3341 lmap->ldap_next = s->s_lmap;
3342 s->s_lmap = map;
3343
3344 if (tTd(38, 2))
3345 sm_dprintf("using cached connection\n");
3346 return true;
3347 }
3348
3349 if (tTd(38, 2))
3350 sm_dprintf("opening new connection\n");
3351
3352 if (lmap->ldap_host != NULL)
3353 id = lmap->ldap_host;
3354 else if (lmap->ldap_uri != NULL)
3355 id = lmap->ldap_uri;
3356 else
3357 id = "localhost";
3358
3359 /* No connection yet, connect */
3360 if (!sm_ldap_start(map->map_mname, lmap))
3361 {
3362 if (errno == ETIMEDOUT)
3363 {
3364 if (LogLevel > 1)
3365 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3366 "timeout conning to LDAP server %.100s",
3367 id);
3368 }
3369
3370 if (!bitset(MF_OPTIONAL, map->map_mflags))
3371 {
3372 if (bitset(MF_NODEFER, map->map_mflags))
3373 {
3374 syserr("%s failed to %s in map %s",
3375# if USE_LDAP_INIT
3376 "ldap_init/ldap_bind",
3377# else /* USE_LDAP_INIT */
3378 "ldap_open",
3379# endif /* USE_LDAP_INIT */
3380 id, map->map_mname);
3381 }
3382 else
3383 {
3384 syserr("451 4.3.5 %s failed to %s in map %s",
3385# if USE_LDAP_INIT
3386 "ldap_init/ldap_bind",
3387# else /* USE_LDAP_INIT */
3388 "ldap_open",
3389# endif /* USE_LDAP_INIT */
3390 id, map->map_mname);
3391 }
3392 }
3393 return false;
3394 }
3395
3396 /* Save connection for reuse */
3397 s->s_lmap = map;
3398 return true;
3399}
3400
3401/*
3402** LDAPMAP_CLOSE -- close ldap map
3403*/
3404
3405void
3406ldapmap_close(map)
3407 MAP *map;
3408{
3409 SM_LDAP_STRUCT *lmap;
3410 STAB *s;
3411
3412 if (tTd(38, 2))
3413 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3414
3415 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3416
3417 /* Check if already closed */
3418 if (lmap->ldap_ld == NULL)
3419 return;
3420
3421 /* Close the LDAP connection */
3422 sm_ldap_close(lmap);
3423
3424 /* Mark all the maps that share the connection as closed */
3425 s = ldapmap_findconn(lmap);
3426
3427 while (s->s_lmap != NULL)
3428 {
3429 MAP *smap = s->s_lmap;
3430
3431 if (tTd(38, 2) && smap != map)
3432 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3433 map->map_mname, smap->map_mname);
3434 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3435 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3436 lmap->ldap_ld = NULL;
3437 s->s_lmap = lmap->ldap_next;
3438 lmap->ldap_next = NULL;
3439 }
3440}
3441
3442# ifdef SUNET_ID
3443/*
3444** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3445** This only makes sense at Stanford University.
3446*/
3447
3448static char *
3449sunet_id_hash(str)
3450 char *str;
3451{
3452 char *p, *p_last;
3453
3454 p = str;
3455 p_last = p;
3456 while (*p != '\0')
3457 {
3458 if (islower(*p) || isdigit(*p))
3459 {
3460 *p_last = *p;
3461 p_last++;
3462 }
3463 else if (isupper(*p))
3464 {
3465 *p_last = tolower(*p);
3466 p_last++;
3467 }
3468 ++p;
3469 }
3470 if (*p_last != '\0')
3471 *p_last = '\0';
3472 return str;
3473}
3474# endif /* SUNET_ID */
3475
3476/*
3477** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3478*/
3479
3480char *
3481ldapmap_lookup(map, name, av, statp)
3482 MAP *map;
3483 char *name;
3484 char **av;
3485 int *statp;
3486{
3487 int flags;
3488 int plen = 0;
3489 int psize = 0;
3490 int msgid;
3491 int save_errno;
3492 char *vp, *p;
3493 char *result = NULL;
3494 SM_RPOOL_T *rpool;
3495 SM_LDAP_STRUCT *lmap = NULL;
3496 char keybuf[MAXNAME + 1];
3497
3498 if (tTd(38, 20))
3499 sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3500
3501 /* Get ldap struct pointer from map */
3502 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3503 sm_ldap_setopts(lmap->ldap_ld, lmap);
3504
3505 (void) sm_strlcpy(keybuf, name, sizeof keybuf);
3506
3507 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3508 {
3509# ifdef SUNET_ID
3510 sunet_id_hash(keybuf);
3511# else /* SUNET_ID */
3512 makelower(keybuf);
3513# endif /* SUNET_ID */
3514 }
3515
3516 msgid = sm_ldap_search(lmap, keybuf);
3517 if (msgid == -1)
3518 {
3519 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3520 save_errno = errno;
3521 if (!bitset(MF_OPTIONAL, map->map_mflags))
3522 {
3523 if (bitset(MF_NODEFER, map->map_mflags))
3524 syserr("Error in ldap_search using %s in map %s",
3525 keybuf, map->map_mname);
3526 else
3527 syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3528 keybuf, map->map_mname);
3529 }
3530 *statp = EX_TEMPFAIL;
3531 switch (save_errno - E_LDAPBASE)
3532 {
3533# ifdef LDAP_SERVER_DOWN
3534 case LDAP_SERVER_DOWN:
3535# endif /* LDAP_SERVER_DOWN */
3536 case LDAP_TIMEOUT:
3537 case LDAP_UNAVAILABLE:
3538 /* server disappeared, try reopen on next search */
3539 ldapmap_close(map);
3540 break;
3541 }
3542 errno = save_errno;
3543 return NULL;
3544 }
3545
3546 *statp = EX_NOTFOUND;
3547 vp = NULL;
3548
3549 flags = 0;
3550 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3551 flags |= SM_LDAP_SINGLEMATCH;
3552 if (bitset(MF_MATCHONLY, map->map_mflags))
3553 flags |= SM_LDAP_MATCHONLY;
3554
3555 /* Create an rpool for search related memory usage */
3556 rpool = sm_rpool_new_x(NULL);
3557
3558 p = NULL;
3559 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3560 rpool, &p, &plen, &psize, NULL);
3561 save_errno = errno;
3562
3563 /* Copy result so rpool can be freed */
3564 if (*statp == EX_OK && p != NULL)
3565 vp = newstr(p);
3566 sm_rpool_free(rpool);
3567
3568 /* need to restart LDAP connection? */
3569 if (*statp == EX_RESTART)
3570 {
3571 *statp = EX_TEMPFAIL;
3572 ldapmap_close(map);
3573 }
3574
3575 errno = save_errno;
3576 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3577 {
3578 if (!bitset(MF_OPTIONAL, map->map_mflags))
3579 {
3580 if (bitset(MF_NODEFER, map->map_mflags))
3581 syserr("Error getting LDAP results in map %s",
3582 map->map_mname);
3583 else
3584 syserr("451 4.3.5 Error getting LDAP results in map %s",
3585 map->map_mname);
3586 }
3587 errno = save_errno;
3588 return NULL;
3589 }
3590
3591 /* Did we match anything? */
3592 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3593 return NULL;
3594
3595 if (*statp == EX_OK)
3596 {
3597 if (LogLevel > 9)
3598 sm_syslog(LOG_INFO, CurEnv->e_id,
3599 "ldap %.100s => %s", name,
3600 vp == NULL ? "<NULL>" : vp);
3601 if (bitset(MF_MATCHONLY, map->map_mflags))
3602 result = map_rewrite(map, name, strlen(name), NULL);
3603 else
3604 {
3605 /* vp != NULL according to test above */
3606 result = map_rewrite(map, vp, strlen(vp), av);
3607 }
3608 if (vp != NULL)
3609 sm_free(vp); /* XXX */
3610 }
3611 return result;
3612}
3613
3614/*
3615** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3616**
3617** Cache LDAP connections based on the host, port, bind DN,
3618** secret, and PID so we don't have multiple connections open to
3619** the same server for different maps. Need a separate connection
3620** per PID since a parent process may close the map before the
3621** child is done with it.
3622**
3623** Parameters:
3624** lmap -- LDAP map information
3625**
3626** Returns:
3627** Symbol table entry for the LDAP connection.
3628*/
3629
3630static STAB *
3631ldapmap_findconn(lmap)
3632 SM_LDAP_STRUCT *lmap;
3633{
3634 char *format;
3635 char *nbuf;
3636 char *id;
3637 STAB *SM_NONVOLATILE s = NULL;
3638
3639 if (lmap->ldap_host != NULL)
3640 id = lmap->ldap_host;
3641 else if (lmap->ldap_uri != NULL)
3642 id = lmap->ldap_uri;
3643 else
3644 id = "localhost";
3645
3646 format = "%s%c%d%c%d%c%s%c%s%d";
3647 nbuf = sm_stringf_x(format,
3648 id,
3649 CONDELSE,
3650 lmap->ldap_port,
3651 CONDELSE,
3652 lmap->ldap_version,
3653 CONDELSE,
3654 (lmap->ldap_binddn == NULL ? ""
3655 : lmap->ldap_binddn),
3656 CONDELSE,
3657 (lmap->ldap_secret == NULL ? ""
3658 : lmap->ldap_secret),
3659 (int) CurrentPid);
3660 SM_TRY
3661 s = stab(nbuf, ST_LMAP, ST_ENTER);
3662 SM_FINALLY
3663 sm_free(nbuf);
3664 SM_END_TRY
3665 return s;
3666}
3667/*
3668** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3669*/
3670
3671static struct lamvalues LDAPAuthMethods[] =
3672{
3673 { "none", LDAP_AUTH_NONE },
3674 { "simple", LDAP_AUTH_SIMPLE },
3675# ifdef LDAP_AUTH_KRBV4
3676 { "krbv4", LDAP_AUTH_KRBV4 },
3677# endif /* LDAP_AUTH_KRBV4 */
3678 { NULL, 0 }
3679};
3680
3681static struct ladvalues LDAPAliasDereference[] =
3682{
3683 { "never", LDAP_DEREF_NEVER },
3684 { "always", LDAP_DEREF_ALWAYS },
3685 { "search", LDAP_DEREF_SEARCHING },
3686 { "find", LDAP_DEREF_FINDING },
3687 { NULL, 0 }
3688};
3689
3690static struct lssvalues LDAPSearchScope[] =
3691{
3692 { "base", LDAP_SCOPE_BASE },
3693 { "one", LDAP_SCOPE_ONELEVEL },
3694 { "sub", LDAP_SCOPE_SUBTREE },
3695 { NULL, 0 }
3696};
3697
3698bool
3699ldapmap_parseargs(map, args)
3700 MAP *map;
3701 char *args;
3702{
3703 bool secretread = true;
3704 bool attrssetup = false;
3705 int i;
3706 register char *p = args;
3707 SM_LDAP_STRUCT *lmap;
3708 struct lamvalues *lam;
3709 struct ladvalues *lad;
3710 struct lssvalues *lss;
3711 char ldapfilt[MAXLINE];
3712 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3713
3714 /* Get ldap struct pointer from map */
3715 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3716
3717 /* Check if setting the initial LDAP defaults */
3718 if (lmap == NULL || lmap != LDAPDefaults)
3719 {
3720 /* We need to alloc an SM_LDAP_STRUCT struct */
3721 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
3722 if (LDAPDefaults == NULL)
3723 sm_ldap_clear(lmap);
3724 else
3725 STRUCTCOPY(*LDAPDefaults, *lmap);
3726 }
3727
3728 /* there is no check whether there is really an argument */
3729 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3730 map->map_spacesub = SpaceSub; /* default value */
3731
3732 /* Check if setting up an alias or file class LDAP map */
3733 if (bitset(MF_ALIAS, map->map_mflags))
3734 {
3735 /* Comma separate if used as an alias file */
3736 map->map_coldelim = ',';
3737 if (*args == '\0')
3738 {
3739 int n;
3740 char *lc;
3741 char jbuf[MAXHOSTNAMELEN];
3742 char lcbuf[MAXLINE];
3743
3744 /* Get $j */
3745 expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
3746 if (jbuf[0] == '\0')
3747 {
3748 (void) sm_strlcpy(jbuf, "localhost",
3749 sizeof jbuf);
3750 }
3751
3752 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3753 if (lc == NULL)
3754 lc = "";
3755 else
3756 {
3757 expand(lc, lcbuf, sizeof lcbuf, CurEnv);
3758 lc = lcbuf;
3759 }
3760
3761 n = sm_snprintf(ldapfilt, sizeof ldapfilt,
3762 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3763 lc, jbuf);
3764 if (n >= sizeof ldapfilt)
3765 {
3766 syserr("%s: Default LDAP string too long",
3767 map->map_mname);
3768 return false;
3769 }
3770
3771 /* default args for an alias LDAP entry */
3772 lmap->ldap_filter = ldapfilt;
3773 lmap->ldap_attr[0] = "objectClass";
3774 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3775 lmap->ldap_attr_needobjclass[0] = NULL;
3776 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3777 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3778 lmap->ldap_attr_needobjclass[1] = NULL;
3779 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3780 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3781 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3782 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3783 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3784 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3785 lmap->ldap_attr[4] = NULL;
3786 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3787 lmap->ldap_attr_needobjclass[4] = NULL;
3788 attrssetup = true;
3789 }
3790 }
3791 else if (bitset(MF_FILECLASS, map->map_mflags))
3792 {
3793 /* Space separate if used as a file class file */
3794 map->map_coldelim = ' ';
3795 }
3796
3797 for (;;)
3798 {
3799 while (isascii(*p) && isspace(*p))
3800 p++;
3801 if (*p != '-')
3802 break;
3803 switch (*++p)
3804 {
3805 case 'N':
3806 map->map_mflags |= MF_INCLNULL;
3807 map->map_mflags &= ~MF_TRY0NULL;
3808 break;
3809
3810 case 'O':
3811 map->map_mflags &= ~MF_TRY1NULL;
3812 break;
3813
3814 case 'o':
3815 map->map_mflags |= MF_OPTIONAL;
3816 break;
3817
3818 case 'f':
3819 map->map_mflags |= MF_NOFOLDCASE;
3820 break;
3821
3822 case 'm':
3823 map->map_mflags |= MF_MATCHONLY;
3824 break;
3825
3826 case 'A':
3827 map->map_mflags |= MF_APPEND;
3828 break;
3829
3830 case 'q':
3831 map->map_mflags |= MF_KEEPQUOTES;
3832 break;
3833
3834 case 'a':
3835 map->map_app = ++p;
3836 break;
3837
3838 case 'T':
3839 map->map_tapp = ++p;
3840 break;
3841
3842 case 't':
3843 map->map_mflags |= MF_NODEFER;
3844 break;
3845
3846 case 'S':
3847 map->map_spacesub = *++p;
3848 break;
3849
3850 case 'D':
3851 map->map_mflags |= MF_DEFER;
3852 break;
3853
3854 case 'z':
3855 if (*++p != '\\')
3856 map->map_coldelim = *p;
3857 else
3858 {
3859 switch (*++p)
3860 {
3861 case 'n':
3862 map->map_coldelim = '\n';
3863 break;
3864
3865 case 't':
3866 map->map_coldelim = '\t';
3867 break;
3868
3869 default:
3870 map->map_coldelim = '\\';
3871 }
3872 }
3873 break;
3874
3875 /* Start of ldapmap specific args */
3876 case 'V':
3877 if (*++p != '\\')
3878 lmap->ldap_attrsep = *p;
3879 else
3880 {
3881 switch (*++p)
3882 {
3883 case 'n':
3884 lmap->ldap_attrsep = '\n';
3885 break;
3886
3887 case 't':
3888 lmap->ldap_attrsep = '\t';
3889 break;
3890
3891 default:
3892 lmap->ldap_attrsep = '\\';
3893 }
3894 }
3895 break;
3896
3897 case 'k': /* search field */
3898 while (isascii(*++p) && isspace(*p))
3899 continue;
3900 lmap->ldap_filter = p;
3901 break;
3902
3903 case 'v': /* attr to return */
3904 while (isascii(*++p) && isspace(*p))
3905 continue;
3906 lmap->ldap_attr[0] = p;
3907 lmap->ldap_attr[1] = NULL;
3908 break;
3909
3910 case '1':
3911 map->map_mflags |= MF_SINGLEMATCH;
3912 break;
3913
3914 /* args stolen from ldapsearch.c */
3915 case 'R': /* don't auto chase referrals */
3916# ifdef LDAP_REFERRALS
3917 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3918# else /* LDAP_REFERRALS */
3919 syserr("compile with -DLDAP_REFERRALS for referral support");
3920# endif /* LDAP_REFERRALS */
3921 break;
3922
3923 case 'n': /* retrieve attribute names only */
3924 lmap->ldap_attrsonly = LDAPMAP_TRUE;
3925 break;
3926
3927 case 'r': /* alias dereferencing */
3928 while (isascii(*++p) && isspace(*p))
3929 continue;
3930
3931 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
3932 p += 11;
3933
3934 for (lad = LDAPAliasDereference;
3935 lad != NULL && lad->lad_name != NULL; lad++)
3936 {
3937 if (sm_strncasecmp(p, lad->lad_name,
3938 strlen(lad->lad_name)) == 0)
3939 break;
3940 }
3941 if (lad->lad_name != NULL)
3942 lmap->ldap_deref = lad->lad_code;
3943 else
3944 {
3945 /* bad config line */
3946 if (!bitset(MCF_OPTFILE,
3947 map->map_class->map_cflags))
3948 {
3949 char *ptr;
3950
3951 if ((ptr = strchr(p, ' ')) != NULL)
3952 *ptr = '\0';
3953 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
3954 p, map->map_mname);
3955 if (ptr != NULL)
3956 *ptr = ' ';
3957 return false;
3958 }
3959 }
3960 break;
3961
3962 case 's': /* search scope */
3963 while (isascii(*++p) && isspace(*p))
3964 continue;
3965
3966 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
3967 p += 11;
3968
3969 for (lss = LDAPSearchScope;
3970 lss != NULL && lss->lss_name != NULL; lss++)
3971 {
3972 if (sm_strncasecmp(p, lss->lss_name,
3973 strlen(lss->lss_name)) == 0)
3974 break;
3975 }
3976 if (lss->lss_name != NULL)
3977 lmap->ldap_scope = lss->lss_code;
3978 else
3979 {
3980 /* bad config line */
3981 if (!bitset(MCF_OPTFILE,
3982 map->map_class->map_cflags))
3983 {
3984 char *ptr;
3985
3986 if ((ptr = strchr(p, ' ')) != NULL)
3987 *ptr = '\0';
3988 syserr("Scope must be [base|one|sub] (not %s) in map %s",
3989 p, map->map_mname);
3990 if (ptr != NULL)
3991 *ptr = ' ';
3992 return false;
3993 }
3994 }
3995 break;
3996
3997 case 'h': /* ldap host */
3998 while (isascii(*++p) && isspace(*p))
3999 continue;
4000 if (lmap->ldap_uri != NULL)
4001 {
4002 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4003 map->map_mname);
4004 return false;
4005 }
4006 lmap->ldap_host = p;
4007 break;
4008
4009 case 'b': /* search base */
4010 while (isascii(*++p) && isspace(*p))
4011 continue;
4012 lmap->ldap_base = p;
4013 break;
4014
4015 case 'p': /* ldap port */
4016 while (isascii(*++p) && isspace(*p))
4017 continue;
4018 lmap->ldap_port = atoi(p);
4019 break;
4020
4021 case 'l': /* time limit */
4022 while (isascii(*++p) && isspace(*p))
4023 continue;
4024 lmap->ldap_timelimit = atoi(p);
4025 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4026 break;
4027
4028 case 'Z':
4029 while (isascii(*++p) && isspace(*p))
4030 continue;
4031 lmap->ldap_sizelimit = atoi(p);
4032 break;
4033
4034 case 'd': /* Dn to bind to server as */
4035 while (isascii(*++p) && isspace(*p))
4036 continue;
4037 lmap->ldap_binddn = p;
4038 break;
4039
4040 case 'M': /* Method for binding */
4041 while (isascii(*++p) && isspace(*p))
4042 continue;
4043
4044 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4045 p += 10;
4046
4047 for (lam = LDAPAuthMethods;
4048 lam != NULL && lam->lam_name != NULL; lam++)
4049 {
4050 if (sm_strncasecmp(p, lam->lam_name,
4051 strlen(lam->lam_name)) == 0)
4052 break;
4053 }
4054 if (lam->lam_name != NULL)
4055 lmap->ldap_method = lam->lam_code;
4056 else
4057 {
4058 /* bad config line */
4059 if (!bitset(MCF_OPTFILE,
4060 map->map_class->map_cflags))
4061 {
4062 char *ptr;
4063
4064 if ((ptr = strchr(p, ' ')) != NULL)
4065 *ptr = '\0';
4066 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4067 p, map->map_mname);
4068 if (ptr != NULL)
4069 *ptr = ' ';
4070 return false;
4071 }
4072 }
4073
4074 break;
4075
4076 /*
4077 ** This is a string that is dependent on the
4078 ** method used defined above.
4079 */
4080
4081 case 'P': /* Secret password for binding */
4082 while (isascii(*++p) && isspace(*p))
4083 continue;
4084 lmap->ldap_secret = p;
4085 secretread = false;
4086 break;
4087
4088 case 'H': /* Use LDAP URI */
4089# if !USE_LDAP_INIT
4090 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4091 map->map_mname);
4092 return false;
4093# else /* !USE_LDAP_INIT */
4094 if (lmap->ldap_host != NULL)
4095 {
4096 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4097 map->map_mname);
4098 return false;
4099 }
4100 while (isascii(*++p) && isspace(*p))
4101 continue;
4102 lmap->ldap_uri = p;
4103 break;
4104# endif /* !USE_LDAP_INIT */
4105
4106 case 'w':
4107 /* -w should be for passwd, -P should be for version */
4108 while (isascii(*++p) && isspace(*p))
4109 continue;
4110 lmap->ldap_version = atoi(p);
4111# ifdef LDAP_VERSION_MAX
4112 if (lmap->ldap_version > LDAP_VERSION_MAX)
4113 {
4114 syserr("LDAP version %d exceeds max of %d in map %s",
4115 lmap->ldap_version, LDAP_VERSION_MAX,
4116 map->map_mname);
4117 return false;
4118 }
4119# endif /* LDAP_VERSION_MAX */
4120# ifdef LDAP_VERSION_MIN
4121 if (lmap->ldap_version < LDAP_VERSION_MIN)
4122 {
4123 syserr("LDAP version %d is lower than min of %d in map %s",
4124 lmap->ldap_version, LDAP_VERSION_MIN,
4125 map->map_mname);
4126 return false;
4127 }
4128# endif /* LDAP_VERSION_MIN */
4129 break;
4130
4131 default:
4132 syserr("Illegal option %c map %s", *p, map->map_mname);
4133 break;
4134 }
4135
4136 /* need to account for quoted strings here */
4137 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4138 {
4139 if (*p == '"')
4140 {
4141 while (*++p != '"' && *p != '\0')
4142 continue;
4143 if (*p != '\0')
4144 p++;
4145 }
4146 else
4147 p++;
4148 }
4149
4150 if (*p != '\0')
4151 *p++ = '\0';
4152 }
4153
4154 if (map->map_app != NULL)
4155 map->map_app = newstr(ldapmap_dequote(map->map_app));
4156 if (map->map_tapp != NULL)
4157 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4158
4159 /*
4160 ** We need to swallow up all the stuff into a struct
4161 ** and dump it into map->map_dbptr1
4162 */
4163
4164 if (lmap->ldap_host != NULL &&
4165 (LDAPDefaults == NULL ||
4166 LDAPDefaults == lmap ||
4167 LDAPDefaults->ldap_host != lmap->ldap_host))
4168 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4169 map->map_domain = lmap->ldap_host;
4170
4171 if (lmap->ldap_uri != NULL &&
4172 (LDAPDefaults == NULL ||
4173 LDAPDefaults == lmap ||
4174 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4175 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4176 map->map_domain = lmap->ldap_uri;
4177
4178 if (lmap->ldap_binddn != NULL &&
4179 (LDAPDefaults == NULL ||
4180 LDAPDefaults == lmap ||
4181 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4182 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4183
4184 if (lmap->ldap_secret != NULL &&
4185 (LDAPDefaults == NULL ||
4186 LDAPDefaults == lmap ||
4187 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4188 {
4189 SM_FILE_T *sfd;
4190 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4191
4192 if (DontLockReadFiles)
4193 sff |= SFF_NOLOCK;
4194
4195 /* need to use method to map secret to passwd string */
4196 switch (lmap->ldap_method)
4197 {
4198 case LDAP_AUTH_NONE:
4199 /* Do nothing */
4200 break;
4201
4202 case LDAP_AUTH_SIMPLE:
4203
4204 /*
4205 ** Secret is the name of a file with
4206 ** the first line as the password.
4207 */
4208
4209 /* Already read in the secret? */
4210 if (secretread)
4211 break;
4212
4213 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4214 O_RDONLY, 0, sff);
4215 if (sfd == NULL)
4216 {
4217 syserr("LDAP map: cannot open secret %s",
4218 ldapmap_dequote(lmap->ldap_secret));
4219 return false;
4220 }
4221 lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
4222 sfd, TimeOuts.to_fileopen,
4223 "ldapmap_parseargs");
4224 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4225 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4226 {
4227 syserr("LDAP map: secret in %s too long",
4228 ldapmap_dequote(lmap->ldap_secret));
4229 return false;
4230 }
4231 if (lmap->ldap_secret != NULL &&
4232 strlen(m_tmp) > 0)
4233 {
4234 /* chomp newline */
4235 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4236 m_tmp[strlen(m_tmp) - 1] = '\0';
4237
4238 lmap->ldap_secret = m_tmp;
4239 }
4240 break;
4241
4242# ifdef LDAP_AUTH_KRBV4
4243 case LDAP_AUTH_KRBV4:
4244
4245 /*
4246 ** Secret is where the ticket file is
4247 ** stashed
4248 */
4249
4250 (void) sm_snprintf(m_tmp, sizeof m_tmp,
4251 "KRBTKFILE=%s",
4252 ldapmap_dequote(lmap->ldap_secret));
4253 lmap->ldap_secret = m_tmp;
4254 break;
4255# endif /* LDAP_AUTH_KRBV4 */
4256
4257 default: /* Should NEVER get here */
4258 syserr("LDAP map: Illegal value in lmap method");
4259 return false;
4260 /* NOTREACHED */
4261 break;
4262 }
4263 }
4264
4265 if (lmap->ldap_secret != NULL &&
4266 (LDAPDefaults == NULL ||
4267 LDAPDefaults == lmap ||
4268 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4269 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4270
4271 if (lmap->ldap_base != NULL &&
4272 (LDAPDefaults == NULL ||
4273 LDAPDefaults == lmap ||
4274 LDAPDefaults->ldap_base != lmap->ldap_base))
4275 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4276
4277 /*
4278 ** Save the server from extra work. If request is for a single
4279 ** match, tell the server to only return enough records to
4280 ** determine if there is a single match or not. This can not
4281 ** be one since the server would only return one and we wouldn't
4282 ** know if there were others available.
4283 */
4284
4285 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4286 lmap->ldap_sizelimit = 2;
4287
4288 /* If setting defaults, don't process ldap_filter and ldap_attr */
4289 if (lmap == LDAPDefaults)
4290 return true;
4291
4292 if (lmap->ldap_filter != NULL)
4293 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4294 else
4295 {
4296 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4297 {
4298 syserr("No filter given in map %s", map->map_mname);
4299 return false;
4300 }
4301 }
4302
4303 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4304 {
4305 bool recurse = false;
4306 bool normalseen = false;
4307
4308 i = 0;
4309 p = ldapmap_dequote(lmap->ldap_attr[0]);
4310 lmap->ldap_attr[0] = NULL;
4311
4312 /* Prime the attr list with the objectClass attribute */
4313 lmap->ldap_attr[i] = "objectClass";
4314 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4315 lmap->ldap_attr_needobjclass[i] = NULL;
4316 i++;
4317
4318 while (p != NULL)
4319 {
4320 char *v;
4321
4322 while (isascii(*p) && isspace(*p))
4323 p++;
4324 if (*p == '\0')
4325 break;
4326 v = p;
4327 p = strchr(v, ',');
4328 if (p != NULL)
4329 *p++ = '\0';
4330
4331 if (i >= LDAPMAP_MAX_ATTR)
4332 {
4333 syserr("Too many return attributes in %s (max %d)",
4334 map->map_mname, LDAPMAP_MAX_ATTR);
4335 return false;
4336 }
4337 if (*v != '\0')
4338 {
4339 int j;
4340 int use;
4341 char *type;
4342 char *needobjclass;
4343
4344 type = strchr(v, ':');
4345 if (type != NULL)
4346 {
4347 *type++ = '\0';
4348 needobjclass = strchr(type, ':');
4349 if (needobjclass != NULL)
4350 *needobjclass++ = '\0';
4351 }
4352 else
4353 {
4354 needobjclass = NULL;
4355 }
4356
4357 use = i;
4358
4359 /* allow override on "objectClass" type */
4360 if (sm_strcasecmp(v, "objectClass") == 0 &&
4361 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4362 {
4363 use = 0;
4364 }
4365 else
4366 {
4367 /*
4368 ** Don't add something to attribute
4369 ** list twice.
4370 */
4371
4372 for (j = 1; j < i; j++)
4373 {
4374 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4375 {
4376 syserr("Duplicate attribute (%s) in %s",
4377 v, map->map_mname);
4378 return false;
4379 }
4380 }
4381
4382 lmap->ldap_attr[use] = newstr(v);
4383 if (needobjclass != NULL &&
4384 *needobjclass != '\0' &&
4385 *needobjclass != '*')
4386 {
4387 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4388 }
4389 else
4390 {
4391 lmap->ldap_attr_needobjclass[use] = NULL;
4392 }
4393
4394 }
4395
4396 if (type != NULL && *type != '\0')
4397 {
4398 if (sm_strcasecmp(type, "dn") == 0)
4399 {
4400 recurse = true;
4401 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4402 }
4403 else if (sm_strcasecmp(type, "filter") == 0)
4404 {
4405 recurse = true;
4406 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4407 }
4408 else if (sm_strcasecmp(type, "url") == 0)
4409 {
4410 recurse = true;
4411 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4412 }
4413 else if (sm_strcasecmp(type, "normal") == 0)
4414 {
4415 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4416 normalseen = true;
4417 }
4418 else
4419 {
4420 syserr("Unknown attribute type (%s) in %s",
4421 type, map->map_mname);
4422 return false;
4423 }
4424 }
4425 else
4426 {
4427 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4428 normalseen = true;
4429 }
4430 i++;
4431 }
4432 }
4433 lmap->ldap_attr[i] = NULL;
4434
4435 /* Set in case needed in future code */
4436 attrssetup = true;
4437
4438 if (recurse && !normalseen)
4439 {
4440 syserr("LDAP recursion requested in %s but no returnable attribute given",
4441 map->map_mname);
4442 return false;
4443 }
4444 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4445 {
4446 syserr("LDAP recursion requested in %s can not be used with -n",
4447 map->map_mname);
4448 return false;
4449 }
4450 }
4451 map->map_db1 = (ARBPTR_T) lmap;
4452 return true;
4453}
4454
4455/*
4456** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4457**
4458** Parameters:
4459** spec -- map argument string from LDAPDefaults option
4460**
4461** Returns:
4462** None.
4463*/
4464
4465void
4466ldapmap_set_defaults(spec)
4467 char *spec;
4468{
4469 STAB *class;
4470 MAP map;
4471
4472 /* Allocate and set the default values */
4473 if (LDAPDefaults == NULL)
4474 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4475 sm_ldap_clear(LDAPDefaults);
4476
4477 memset(&map, '\0', sizeof map);
4478
4479 /* look up the class */
4480 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4481 if (class == NULL)
4482 {
4483 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4484 return;
4485 }
4486 map.map_class = &class->s_mapclass;
4487 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4488 map.map_mname = "O LDAPDefaultSpec";
4489
4490 (void) ldapmap_parseargs(&map, spec);
4491
4492 /* These should never be set in LDAPDefaults */
4493 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4494 map.map_spacesub != SpaceSub ||
4495 map.map_app != NULL ||
4496 map.map_tapp != NULL)
4497 {
4498 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4499 SM_FREE_CLR(map.map_app);
4500 SM_FREE_CLR(map.map_tapp);
4501 }
4502
4503 if (LDAPDefaults->ldap_filter != NULL)
4504 {
4505 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4506
4507 /* don't free, it isn't malloc'ed in parseargs */
4508 LDAPDefaults->ldap_filter = NULL;
4509 }
4510
4511 if (LDAPDefaults->ldap_attr[0] != NULL)
4512 {
4513 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4514 /* don't free, they aren't malloc'ed in parseargs */
4515 LDAPDefaults->ldap_attr[0] = NULL;
4516 }
4517}
4518#endif /* LDAPMAP */
4519/*
4520** PH map
4521*/
4522
4523#if PH_MAP
4524
4525/*
4526** Support for the CCSO Nameserver (ph/qi).
4527** This code is intended to replace the so-called "ph mailer".
4528** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
4529*/
4530
4531/* what version of the ph map code we're running */
4532static char phmap_id[128];
4533
4534/* sendmail version for phmap id string */
4535extern const char Version[];
4536
4537/* assume we're using nph-1.2.x if not specified */
4538# ifndef NPH_VERSION
4539# define NPH_VERSION 10200
4540# endif
4541
4542/* compatibility for versions older than nph-1.2.0 */
4543# if NPH_VERSION < 10200
4544# define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4545# define PH_OPEN_DONTID PH_DONTID
4546# define PH_CLOSE_FAST PH_FASTCLOSE
4547# define PH_ERR_DATAERR PH_DATAERR
4548# define PH_ERR_NOMATCH PH_NOMATCH
4549# endif /* NPH_VERSION < 10200 */
4550
4551/*
4552** PH_MAP_PARSEARGS -- parse ph map definition args.
4553*/
4554
4555bool
4556ph_map_parseargs(map, args)
4557 MAP *map;
4558 char *args;
4559{
4560 register bool done;
4561 register char *p = args;
4562 PH_MAP_STRUCT *pmap = NULL;
4563
4564 /* initialize version string */
4565 (void) sm_snprintf(phmap_id, sizeof phmap_id,
4566 "sendmail-%s phmap-20010529 libphclient-%s",
4567 Version, libphclient_version);
4568
4569 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4570
4571 /* defaults */
4572 pmap->ph_servers = NULL;
4573 pmap->ph_field_list = NULL;
4574 pmap->ph = NULL;
4575 pmap->ph_timeout = 0;
4576 pmap->ph_fastclose = 0;
4577
4578 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4579 for (;;)
4580 {
4581 while (isascii(*p) && isspace(*p))
4582 p++;
4583 if (*p != '-')
4584 break;
4585 switch (*++p)
4586 {
4587 case 'N':
4588 map->map_mflags |= MF_INCLNULL;
4589 map->map_mflags &= ~MF_TRY0NULL;
4590 break;
4591
4592 case 'O':
4593 map->map_mflags &= ~MF_TRY1NULL;
4594 break;
4595
4596 case 'o':
4597 map->map_mflags |= MF_OPTIONAL;
4598 break;
4599
4600 case 'f':
4601 map->map_mflags |= MF_NOFOLDCASE;
4602 break;
4603
4604 case 'm':
4605 map->map_mflags |= MF_MATCHONLY;
4606 break;
4607
4608 case 'A':
4609 map->map_mflags |= MF_APPEND;
4610 break;
4611
4612 case 'q':
4613 map->map_mflags |= MF_KEEPQUOTES;
4614 break;
4615
4616 case 't':
4617 map->map_mflags |= MF_NODEFER;
4618 break;
4619
4620 case 'a':
4621 map->map_app = ++p;
4622 break;
4623
4624 case 'T':
4625 map->map_tapp = ++p;
4626 break;
4627
4628 case 'l':
4629 while (isascii(*++p) && isspace(*p))
4630 continue;
4631 pmap->ph_timeout = atoi(p);
4632 break;
4633
4634 case 'S':
4635 map->map_spacesub = *++p;
4636 break;
4637
4638 case 'D':
4639 map->map_mflags |= MF_DEFER;
4640 break;
4641
4642 case 'h': /* PH server list */
4643 while (isascii(*++p) && isspace(*p))
4644 continue;
4645 pmap->ph_servers = p;
4646 break;
4647
4648 case 'k': /* fields to search for */
4649 while (isascii(*++p) && isspace(*p))
4650 continue;
4651 pmap->ph_field_list = p;
4652 break;
4653
4654 default:
4655 syserr("ph_map_parseargs: unknown option -%c", *p);
4656 }
4657
4658 /* try to account for quoted strings */
4659 done = isascii(*p) && isspace(*p);
4660 while (*p != '\0' && !done)
4661 {
4662 if (*p == '"')
4663 {
4664 while (*++p != '"' && *p != '\0')
4665 continue;
4666 if (*p != '\0')
4667 p++;
4668 }
4669 else
4670 p++;
4671 done = isascii(*p) && isspace(*p);
4672 }
4673
4674 if (*p != '\0')
4675 *p++ = '\0';
4676 }
4677
4678 if (map->map_app != NULL)
4679 map->map_app = newstr(ph_map_dequote(map->map_app));
4680 if (map->map_tapp != NULL)
4681 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4682
4683 if (pmap->ph_field_list != NULL)
4684 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4685
4686 if (pmap->ph_servers != NULL)
4687 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4688 else
4689 {
4690 syserr("ph_map_parseargs: -h flag is required");
4691 return false;
4692 }
4693
4694 map->map_db1 = (ARBPTR_T) pmap;
4695 return true;
4696}
4697
4698/*
4699** PH_MAP_CLOSE -- close the connection to the ph server
4700*/
4701
4702void
4703ph_map_close(map)
4704 MAP *map;
4705{
4706 PH_MAP_STRUCT *pmap;
4707
4708 pmap = (PH_MAP_STRUCT *)map->map_db1;
4709 if (tTd(38, 9))
4710 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4711 map->map_mname, pmap->ph_fastclose);
4712
4713
4714 if (pmap->ph != NULL)
4715 {
4716 ph_set_sendhook(pmap->ph, NULL);
4717 ph_set_recvhook(pmap->ph, NULL);
4718 ph_close(pmap->ph, pmap->ph_fastclose);
4719 }
4720
4721 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4722}
4723
4724static jmp_buf PHTimeout;
4725
4726/* ARGSUSED */
4727static void
4728ph_timeout(unused)
4729 int unused;
4730{
4731 /*
4732 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4733 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4734 ** DOING.
4735 */
4736
4737 errno = ETIMEDOUT;
4738 longjmp(PHTimeout, 1);
4739}
4740
4741static void
4742#if NPH_VERSION >= 10200
4743ph_map_send_debug(appdata, text)
4744 void *appdata;
4745#else
4746ph_map_send_debug(text)
4747#endif
4748 char *text;
4749{
4750 if (LogLevel > 9)
4751 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4752 "ph_map_send_debug: ==> %s", text);
4753 if (tTd(38, 20))
4754 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4755}
4756
4757static void
4758#if NPH_VERSION >= 10200
4759ph_map_recv_debug(appdata, text)
4760 void *appdata;
4761#else
4762ph_map_recv_debug(text)
4763#endif
4764 char *text;
4765{
4766 if (LogLevel > 10)
4767 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4768 "ph_map_recv_debug: <== %s", text);
4769 if (tTd(38, 21))
4770 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4771}
4772
4773/*
4774** PH_MAP_OPEN -- sub for opening PH map
4775*/
4776bool
4777ph_map_open(map, mode)
4778 MAP *map;
4779 int mode;
4780{
4781 PH_MAP_STRUCT *pmap;
4782 register SM_EVENT *ev = NULL;
4783 int save_errno = 0;
4784 char *hostlist, *host;
4785
4786 if (tTd(38, 2))
4787 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
4788
4789 mode &= O_ACCMODE;
4790 if (mode != O_RDONLY)
4791 {
4792 /* issue a pseudo-error message */
4793 errno = SM_EMAPCANTWRITE;
4794 return false;
4795 }
4796
4797 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
4798 bitset(MF_DEFER, map->map_mflags))
4799 {
4800 if (tTd(9, 1))
4801 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4802 map->map_mname);
4803
4804 /*
4805 ** Unset MF_DEFER here so that map_lookup() returns
4806 ** a temporary failure using the bogus map and
4807 ** map->map_tapp instead of the default permanent error.
4808 */
4809
4810 map->map_mflags &= ~MF_DEFER;
4811 return false;
4812 }
4813
4814 pmap = (PH_MAP_STRUCT *)map->map_db1;
4815 pmap->ph_fastclose = 0; /* refresh field for reopen */
4816
4817 /* try each host in the list */
4818 hostlist = newstr(pmap->ph_servers);
4819 for (host = strtok(hostlist, " ");
4820 host != NULL;
4821 host = strtok(NULL, " "))
4822 {
4823 /* set timeout */
4824 if (pmap->ph_timeout != 0)
4825 {
4826 if (setjmp(PHTimeout) != 0)
4827 {
4828 ev = NULL;
4829 if (LogLevel > 1)
4830 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4831 "timeout connecting to PH server %.100s",
4832 host);
4833 errno = ETIMEDOUT;
4834 goto ph_map_open_abort;
4835 }
4836 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4837 }
4838
4839 /* open connection to server */
4840 if (ph_open(&(pmap->ph), host,
4841 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
4842 ph_map_send_debug, ph_map_recv_debug
4843#if NPH_VERSION >= 10200
4844 , NULL
4845#endif
4846 ) == 0
4847 && ph_id(pmap->ph, phmap_id) == 0)
4848 {
4849 if (ev != NULL)
4850 sm_clrevent(ev);
4851 sm_free(hostlist); /* XXX */
4852 return true;
4853 }
4854
4855 ph_map_open_abort:
4856 save_errno = errno;
4857 if (ev != NULL)
4858 sm_clrevent(ev);
4859 pmap->ph_fastclose = PH_CLOSE_FAST;
4860 ph_map_close(map);
4861 errno = save_errno;
4862 }
4863
4864 if (bitset(MF_NODEFER, map->map_mflags))
4865 {
4866 if (errno == 0)
4867 errno = EAGAIN;
4868 syserr("ph_map_open: %s: cannot connect to PH server",
4869 map->map_mname);
4870 }
4871 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
4872 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4873 "ph_map_open: %s: cannot connect to PH server",
4874 map->map_mname);
4875 sm_free(hostlist); /* XXX */
4876 return false;
4877}
4878
4879/*
4880** PH_MAP_LOOKUP -- look up key from ph server
4881*/
4882
4883char *
4884ph_map_lookup(map, key, args, pstat)
4885 MAP *map;
4886 char *key;
4887 char **args;
4888 int *pstat;
4889{
4890 int i, save_errno = 0;
4891 register SM_EVENT *ev = NULL;
4892 PH_MAP_STRUCT *pmap;
4893 char *value = NULL;
4894
4895 pmap = (PH_MAP_STRUCT *)map->map_db1;
4896
4897 *pstat = EX_OK;
4898
4899 /* set timeout */
4900 if (pmap->ph_timeout != 0)
4901 {
4902 if (setjmp(PHTimeout) != 0)
4903 {
4904 ev = NULL;
4905 if (LogLevel > 1)
4906 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4907 "timeout during PH lookup of %.100s",
4908 key);
4909 errno = ETIMEDOUT;
4910 *pstat = EX_TEMPFAIL;
4911 goto ph_map_lookup_abort;
4912 }
4913 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4914 }
4915
4916 /* perform lookup */
4917 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
4918 if (i == -1)
4919 *pstat = EX_TEMPFAIL;
4920 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
4921 *pstat = EX_UNAVAILABLE;
4922
4923 ph_map_lookup_abort:
4924 if (ev != NULL)
4925 sm_clrevent(ev);
4926
4927 /*
4928 ** Close the connection if the timer popped
4929 ** or we got a temporary PH error
4930 */
4931
4932 if (*pstat == EX_TEMPFAIL)
4933 {
4934 save_errno = errno;
4935 pmap->ph_fastclose = PH_CLOSE_FAST;
4936 ph_map_close(map);
4937 errno = save_errno;
4938 }
4939
4940 if (*pstat == EX_OK)
4941 {
4942 if (tTd(38,20))
4943 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
4944
4945 if (bitset(MF_MATCHONLY, map->map_mflags))
4946 return map_rewrite(map, key, strlen(key), NULL);
4947 else
4948 return map_rewrite(map, value, strlen(value), args);
4949 }
4950
4951 return NULL;
4952}
4953#endif /* PH_MAP */
4954/*
4955** syslog map
4956*/
4957
4958#define map_prio map_lockfd /* overload field */
4959
4960/*
4961** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
4962*/
4963
4964bool
4965syslog_map_parseargs(map, args)
4966 MAP *map;
4967 char *args;
4968{
4969 char *p = args;
4970 char *priority = NULL;
4971
4972 /* there is no check whether there is really an argument */
4973 while (*p != '\0')
4974 {
4975 while (isascii(*p) && isspace(*p))
4976 p++;
4977 if (*p != '-')
4978 break;
4979 ++p;
4980 if (*p == 'D')
4981 {
4982 map->map_mflags |= MF_DEFER;
4983 ++p;
4984 }
4985 else if (*p == 'S')
4986 {
4987 map->map_spacesub = *++p;
4988 if (*p != '\0')
4989 p++;
4990 }
4991 else if (*p == 'L')
4992 {
4993 while (*++p != '\0' && isascii(*p) && isspace(*p))
4994 continue;
4995 if (*p == '\0')
4996 break;
4997 priority = p;
4998 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4999 p++;
5000 if (*p != '\0')
5001 *p++ = '\0';
5002 }
5003 else
5004 {
5005 syserr("Illegal option %c map syslog", *p);
5006 ++p;
5007 }
5008 }
5009
5010 if (priority == NULL)
5011 map->map_prio = LOG_INFO;
5012 else
5013 {
5014 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5015 priority += 4;
5016
5017#ifdef LOG_EMERG
5018 if (sm_strcasecmp("EMERG", priority) == 0)
5019 map->map_prio = LOG_EMERG;
5020 else
5021#endif /* LOG_EMERG */
5022#ifdef LOG_ALERT
5023 if (sm_strcasecmp("ALERT", priority) == 0)
5024 map->map_prio = LOG_ALERT;
5025 else
5026#endif /* LOG_ALERT */
5027#ifdef LOG_CRIT
5028 if (sm_strcasecmp("CRIT", priority) == 0)
5029 map->map_prio = LOG_CRIT;
5030 else
5031#endif /* LOG_CRIT */
5032#ifdef LOG_ERR
5033 if (sm_strcasecmp("ERR", priority) == 0)
5034 map->map_prio = LOG_ERR;
5035 else
5036#endif /* LOG_ERR */
5037#ifdef LOG_WARNING
5038 if (sm_strcasecmp("WARNING", priority) == 0)
5039 map->map_prio = LOG_WARNING;
5040 else
5041#endif /* LOG_WARNING */
5042#ifdef LOG_NOTICE
5043 if (sm_strcasecmp("NOTICE", priority) == 0)
5044 map->map_prio = LOG_NOTICE;
5045 else
5046#endif /* LOG_NOTICE */
5047#ifdef LOG_INFO
5048 if (sm_strcasecmp("INFO", priority) == 0)
5049 map->map_prio = LOG_INFO;
5050 else
5051#endif /* LOG_INFO */
5052#ifdef LOG_DEBUG
5053 if (sm_strcasecmp("DEBUG", priority) == 0)
5054 map->map_prio = LOG_DEBUG;
5055 else
5056#endif /* LOG_DEBUG */
5057 {
5058 syserr("syslog_map_parseargs: Unknown priority %s",
5059 priority);
5060 return false;
5061 }
5062 }
5063 return true;
5064}
5065
5066/*
5067** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5068*/
5069
5070char *
5071syslog_map_lookup(map, string, args, statp)
5072 MAP *map;
5073 char *string;
5074 char **args;
5075 int *statp;
5076{
5077 char *ptr = map_rewrite(map, string, strlen(string), args);
5078
5079 if (ptr != NULL)
5080 {
5081 if (tTd(38, 20))
5082 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5083 map->map_mname, map->map_prio, ptr);
5084
5085 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5086 }
5087
5088 *statp = EX_OK;
5089 return "";
5090}
5091
5092/*
5093** HESIOD Modules
5094*/
5095
5096#if HESIOD
5097
5098bool
5099hes_map_open(map, mode)
5100 MAP *map;
5101 int mode;
5102{
5103 if (tTd(38, 2))
5104 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5105 map->map_mname, map->map_file, mode);
5106
5107 if (mode != O_RDONLY)
5108 {
5109 /* issue a pseudo-error message */
5110 errno = SM_EMAPCANTWRITE;
5111 return false;
5112 }
5113
5114# ifdef HESIOD_INIT
5115 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5116 return true;
5117
5118 if (!bitset(MF_OPTIONAL, map->map_mflags))
5119 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5120 sm_errstring(errno));
5121 return false;
5122# else /* HESIOD_INIT */
5123 if (hes_error() == HES_ER_UNINIT)
5124 hes_init();
5125 switch (hes_error())
5126 {
5127 case HES_ER_OK:
5128 case HES_ER_NOTFOUND:
5129 return true;
5130 }
5131
5132 if (!bitset(MF_OPTIONAL, map->map_mflags))
5133 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5134
5135 return false;
5136# endif /* HESIOD_INIT */
5137}
5138
5139char *
5140hes_map_lookup(map, name, av, statp)
5141 MAP *map;
5142 char *name;
5143 char **av;
5144 int *statp;
5145{
5146 char **hp;
5147
5148 if (tTd(38, 20))
5149 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5150
5151 if (name[0] == '\\')
5152 {
5153 char *np;
5154 int nl;
5155 int save_errno;
5156 char nbuf[MAXNAME];
5157
5158 nl = strlen(name);
5159 if (nl < sizeof nbuf - 1)
5160 np = nbuf;
5161 else
5162 np = xalloc(strlen(name) + 2);
5163 np[0] = '\\';
5164 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
5165# ifdef HESIOD_INIT
5166 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5167# else /* HESIOD_INIT */
5168 hp = hes_resolve(np, map->map_file);
5169# endif /* HESIOD_INIT */
5170 save_errno = errno;
5171 if (np != nbuf)
5172 sm_free(np); /* XXX */
5173 errno = save_errno;
5174 }
5175 else
5176 {
5177# ifdef HESIOD_INIT
5178 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5179# else /* HESIOD_INIT */
5180 hp = hes_resolve(name, map->map_file);
5181# endif /* HESIOD_INIT */
5182 }
5183# ifdef HESIOD_INIT
5184 if (hp == NULL || *hp == NULL)
5185 {
5186 switch (errno)
5187 {
5188 case ENOENT:
5189 *statp = EX_NOTFOUND;
5190 break;
5191 case ECONNREFUSED:
5192 *statp = EX_TEMPFAIL;
5193 break;
5194 case EMSGSIZE:
5195 case ENOMEM:
5196 default:
5197 *statp = EX_UNAVAILABLE;
5198 break;
5199 }
5200 if (hp != NULL)
5201 hesiod_free_list(HesiodContext, hp);
5202 return NULL;
5203 }
5204# else /* HESIOD_INIT */
5205 if (hp == NULL || hp[0] == NULL)
5206 {
5207 switch (hes_error())
5208 {
5209 case HES_ER_OK:
5210 *statp = EX_OK;
5211 break;
5212
5213 case HES_ER_NOTFOUND:
5214 *statp = EX_NOTFOUND;
5215 break;
5216
5217 case HES_ER_CONFIG:
5218 *statp = EX_UNAVAILABLE;
5219 break;
5220
5221 case HES_ER_NET:
5222 *statp = EX_TEMPFAIL;
5223 break;
5224 }
5225 return NULL;
5226 }
5227# endif /* HESIOD_INIT */
5228
5229 if (bitset(MF_MATCHONLY, map->map_mflags))
5230 return map_rewrite(map, name, strlen(name), NULL);
5231 else
5232 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5233}
5234
5235/*
5236** HES_MAP_CLOSE -- free the Hesiod context
5237*/
5238
5239void
5240hes_map_close(map)
5241 MAP *map;
5242{
5243 if (tTd(38, 20))
5244 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5245
5246# ifdef HESIOD_INIT
5247 /* Free the hesiod context */
5248 if (HesiodContext != NULL)
5249 {
5250 hesiod_end(HesiodContext);
5251 HesiodContext = NULL;
5252 }
5253# endif /* HESIOD_INIT */
5254}
5255
5256#endif /* HESIOD */
5257/*
5258** NeXT NETINFO Modules
5259*/
5260
5261#if NETINFO
5262
5263# define NETINFO_DEFAULT_DIR "/aliases"
5264# define NETINFO_DEFAULT_PROPERTY "members"
5265
5266/*
5267** NI_MAP_OPEN -- open NetInfo Aliases
5268*/
5269
5270bool
5271ni_map_open(map, mode)
5272 MAP *map;
5273 int mode;
5274{
5275 if (tTd(38, 2))
5276 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5277 map->map_mname, map->map_file, mode);
5278 mode &= O_ACCMODE;
5279
5280 if (*map->map_file == '\0')
5281 map->map_file = NETINFO_DEFAULT_DIR;
5282
5283 if (map->map_valcolnm == NULL)
5284 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5285
5286 if (map->map_coldelim == '\0')
5287 {
5288 if (bitset(MF_ALIAS, map->map_mflags))
5289 map->map_coldelim = ',';
5290 else if (bitset(MF_FILECLASS, map->map_mflags))
5291 map->map_coldelim = ' ';
5292 }
5293 return true;
5294}
5295
5296
5297/*
5298** NI_MAP_LOOKUP -- look up a datum in NetInfo
5299*/
5300
5301char *
5302ni_map_lookup(map, name, av, statp)
5303 MAP *map;
5304 char *name;
5305 char **av;
5306 int *statp;
5307{
5308 char *res;
5309 char *propval;
5310
5311 if (tTd(38, 20))
5312 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5313
5314 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5315 map->map_valcolnm, map->map_coldelim);
5316
5317 if (propval == NULL)
5318 return NULL;
5319
5320 SM_TRY
5321 if (bitset(MF_MATCHONLY, map->map_mflags))
5322 res = map_rewrite(map, name, strlen(name), NULL);
5323 else
5324 res = map_rewrite(map, propval, strlen(propval), av);
5325 SM_FINALLY
5326 sm_free(propval);
5327 SM_END_TRY
5328 return res;
5329}
5330
5331
5332static bool
5333ni_getcanonname(name, hbsize, statp)
5334 char *name;
5335 int hbsize;
5336 int *statp;
5337{
5338 char *vptr;
5339 char *ptr;
5340 char nbuf[MAXNAME + 1];
5341
5342 if (tTd(38, 20))
5343 sm_dprintf("ni_getcanonname(%s)\n", name);
5344
5345 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5346 {
5347 *statp = EX_UNAVAILABLE;
5348 return false;
5349 }
5350 (void) shorten_hostname(nbuf);
5351
5352 /* we only accept single token search key */
5353 if (strchr(nbuf, '.'))
5354 {
5355 *statp = EX_NOHOST;
5356 return false;
5357 }
5358
5359 /* Do the search */
5360 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5361
5362 if (vptr == NULL)
5363 {
5364 *statp = EX_NOHOST;
5365 return false;
5366 }
5367
5368 /* Only want the first machine name */
5369 if ((ptr = strchr(vptr, '\n')) != NULL)
5370 *ptr = '\0';
5371
5372 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5373 {
5374 sm_free(vptr);
5375 *statp = EX_UNAVAILABLE;
5376 return true;
5377 }
5378 sm_free(vptr);
5379 *statp = EX_OK;
5380 return false;
5381}
5382#endif /* NETINFO */
5383/*
5384** TEXT (unindexed text file) Modules
5385**
5386** This code donated by Sun Microsystems.
5387*/
5388
5389#define map_sff map_lockfd /* overload field */
5390
5391
5392/*
5393** TEXT_MAP_OPEN -- open text table
5394*/
5395
5396bool
5397text_map_open(map, mode)
5398 MAP *map;
5399 int mode;
5400{
5401 long sff;
5402 int i;
5403
5404 if (tTd(38, 2))
5405 sm_dprintf("text_map_open(%s, %s, %d)\n",
5406 map->map_mname, map->map_file, mode);
5407
5408 mode &= O_ACCMODE;
5409 if (mode != O_RDONLY)
5410 {
5411 errno = EPERM;
5412 return false;
5413 }
5414
5415 if (*map->map_file == '\0')
5416 {
5417 syserr("text map \"%s\": file name required",
5418 map->map_mname);
5419 return false;
5420 }
5421
5422 if (map->map_file[0] != '/')
5423 {
5424 syserr("text map \"%s\": file name must be fully qualified",
5425 map->map_mname);
5426 return false;
5427 }
5428
5429 sff = SFF_ROOTOK|SFF_REGONLY;
5430 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5431 sff |= SFF_NOWLINK;
5432 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5433 sff |= SFF_SAFEDIRPATH;
5434 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5435 sff, S_IRUSR, NULL)) != 0)
5436 {
5437 int save_errno = errno;
5438
5439 /* cannot open this map */
5440 if (tTd(38, 2))
5441 sm_dprintf("\tunsafe map file: %d\n", i);
5442 errno = save_errno;
5443 if (!bitset(MF_OPTIONAL, map->map_mflags))
5444 syserr("text map \"%s\": unsafe map file %s",
5445 map->map_mname, map->map_file);
5446 return false;
5447 }
5448
5449 if (map->map_keycolnm == NULL)
5450 map->map_keycolno = 0;
5451 else
5452 {
5453 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5454 {
5455 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5456 map->map_mname, map->map_file,
5457 map->map_keycolnm);
5458 return false;
5459 }
5460 map->map_keycolno = atoi(map->map_keycolnm);
5461 }
5462
5463 if (map->map_valcolnm == NULL)
5464 map->map_valcolno = 0;
5465 else
5466 {
5467 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5468 {
5469 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5470 map->map_mname, map->map_file,
5471 map->map_valcolnm);
5472 return false;
5473 }
5474 map->map_valcolno = atoi(map->map_valcolnm);
5475 }
5476
5477 if (tTd(38, 2))
5478 {
5479 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5480 map->map_mname, map->map_file);
5481 if (map->map_coldelim == '\0')
5482 sm_dprintf("(white space)\n");
5483 else
5484 sm_dprintf("%c\n", map->map_coldelim);
5485 }
5486
5487 map->map_sff = sff;
5488 return true;
5489}
5490
5491
5492/*
5493** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5494*/
5495
5496char *
5497text_map_lookup(map, name, av, statp)
5498 MAP *map;
5499 char *name;
5500 char **av;
5501 int *statp;
5502{
5503 char *vp;
5504 auto int vsize;
5505 int buflen;
5506 SM_FILE_T *f;
5507 char delim;
5508 int key_idx;
5509 bool found_it;
5510 long sff = map->map_sff;
5511 char search_key[MAXNAME + 1];
5512 char linebuf[MAXLINE];
5513 char buf[MAXNAME + 1];
5514
5515 found_it = false;
5516 if (tTd(38, 20))
5517 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5518
5519 buflen = strlen(name);
5520 if (buflen > sizeof search_key - 1)
5521 buflen = sizeof search_key - 1; /* XXX just cut if off? */
5522 memmove(search_key, name, buflen);
5523 search_key[buflen] = '\0';
5524 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5525 makelower(search_key);
5526
5527 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5528 if (f == NULL)
5529 {
5530 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5531 *statp = EX_UNAVAILABLE;
5532 return NULL;
5533 }
5534 key_idx = map->map_keycolno;
5535 delim = map->map_coldelim;
5536 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5537 linebuf, sizeof linebuf) != NULL)
5538 {
5539 char *p;
5540
5541 /* skip comment line */
5542 if (linebuf[0] == '#')
5543 continue;
5544 p = strchr(linebuf, '\n');
5545 if (p != NULL)
5546 *p = '\0';
5547 p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5548 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5549 {
5550 found_it = true;
5551 break;
5552 }
5553 }
5554 (void) sm_io_close(f, SM_TIME_DEFAULT);
5555 if (!found_it)
5556 {
5557 *statp = EX_NOTFOUND;
5558 return NULL;
5559 }
5560 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5561 if (vp == NULL)
5562 {
5563 *statp = EX_NOTFOUND;
5564 return NULL;
5565 }
5566 vsize = strlen(vp);
5567 *statp = EX_OK;
5568 if (bitset(MF_MATCHONLY, map->map_mflags))
5569 return map_rewrite(map, name, strlen(name), NULL);
5570 else
5571 return map_rewrite(map, vp, vsize, av);
5572}
5573
5574/*
5575** TEXT_GETCANONNAME -- look up canonical name in hosts file
5576*/
5577
5578static bool
5579text_getcanonname(name, hbsize, statp)
5580 char *name;
5581 int hbsize;
5582 int *statp;
5583{
5584 bool found;
5585 char *dot;
5586 SM_FILE_T *f;
5587 char linebuf[MAXLINE];
5588 char cbuf[MAXNAME + 1];
5589 char nbuf[MAXNAME + 1];
5590
5591 if (tTd(38, 20))
5592 sm_dprintf("text_getcanonname(%s)\n", name);
5593
5594 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5595 {
5596 *statp = EX_UNAVAILABLE;
5597 return false;
5598 }
5599 dot = shorten_hostname(nbuf);
5600
5601 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5602 NULL);
5603 if (f == NULL)
5604 {
5605 *statp = EX_UNAVAILABLE;
5606 return false;
5607 }
5608 found = false;
5609 while (!found &&
5610 sm_io_fgets(f, SM_TIME_DEFAULT,
5611 linebuf, sizeof linebuf) != NULL)
5612 {
5613 char *p = strpbrk(linebuf, "#\n");
5614
5615 if (p != NULL)
5616 *p = '\0';
5617 if (linebuf[0] != '\0')
5618 found = extract_canonname(nbuf, dot, linebuf,
5619 cbuf, sizeof cbuf);
5620 }
5621 (void) sm_io_close(f, SM_TIME_DEFAULT);
5622 if (!found)
5623 {
5624 *statp = EX_NOHOST;
5625 return false;
5626 }
5627
5628 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5629 {
5630 *statp = EX_UNAVAILABLE;
5631 return false;
5632 }
5633 *statp = EX_OK;
5634 return true;
5635}
5636/*
5637** STAB (Symbol Table) Modules
5638*/
5639
5640
5641/*
5642** STAB_MAP_LOOKUP -- look up alias in symbol table
5643*/
5644
5645/* ARGSUSED2 */
5646char *
5647stab_map_lookup(map, name, av, pstat)
5648 register MAP *map;
5649 char *name;
5650 char **av;
5651 int *pstat;
5652{
5653 register STAB *s;
5654
5655 if (tTd(38, 20))
5656 sm_dprintf("stab_lookup(%s, %s)\n",
5657 map->map_mname, name);
5658
5659 s = stab(name, ST_ALIAS, ST_FIND);
5660 if (s != NULL)
5661 return s->s_alias;
5662 return NULL;
5660 if (s == NULL)
5661 return NULL;
5662 if (bitset(MF_MATCHONLY, map->map_mflags))
5663 return map_rewrite(map, name, strlen(name), NULL);
5664 else
5665 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5663}
5664
5666}
5667
5665
5666/*
5667** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5668*/
5669
5670void
5671stab_map_store(map, lhs, rhs)
5672 register MAP *map;
5673 char *lhs;
5674 char *rhs;
5675{
5676 register STAB *s;
5677
5678 s = stab(lhs, ST_ALIAS, ST_ENTER);
5679 s->s_alias = newstr(rhs);
5680}
5681
5682
5683/*
5684** STAB_MAP_OPEN -- initialize (reads data file)
5685**
5686** This is a wierd case -- it is only intended as a fallback for
5687** aliases. For this reason, opens for write (only during a
5688** "newaliases") always fails, and opens for read open the
5689** actual underlying text file instead of the database.
5690*/
5691
5692bool
5693stab_map_open(map, mode)
5694 register MAP *map;
5695 int mode;
5696{
5697 SM_FILE_T *af;
5698 long sff;
5699 struct stat st;
5700
5701 if (tTd(38, 2))
5702 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5703 map->map_mname, map->map_file, mode);
5704
5705 mode &= O_ACCMODE;
5706 if (mode != O_RDONLY)
5707 {
5708 errno = EPERM;
5709 return false;
5710 }
5711
5712 sff = SFF_ROOTOK|SFF_REGONLY;
5713 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5714 sff |= SFF_NOWLINK;
5715 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5716 sff |= SFF_SAFEDIRPATH;
5717 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
5718 if (af == NULL)
5719 return false;
5720 readaliases(map, af, false, false);
5721
5722 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
5723 map->map_mtime = st.st_mtime;
5724 (void) sm_io_close(af, SM_TIME_DEFAULT);
5725
5726 return true;
5727}
5728/*
5729** Implicit Modules
5730**
5731** Tries several types. For back compatibility of aliases.
5732*/
5733
5734
5735/*
5736** IMPL_MAP_LOOKUP -- lookup in best open database
5737*/
5738
5739char *
5740impl_map_lookup(map, name, av, pstat)
5741 MAP *map;
5742 char *name;
5743 char **av;
5744 int *pstat;
5745{
5746 if (tTd(38, 20))
5747 sm_dprintf("impl_map_lookup(%s, %s)\n",
5748 map->map_mname, name);
5749
5750#if NEWDB
5751 if (bitset(MF_IMPL_HASH, map->map_mflags))
5752 return db_map_lookup(map, name, av, pstat);
5753#endif /* NEWDB */
5754#if NDBM
5755 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5756 return ndbm_map_lookup(map, name, av, pstat);
5757#endif /* NDBM */
5758 return stab_map_lookup(map, name, av, pstat);
5759}
5760
5761/*
5762** IMPL_MAP_STORE -- store in open databases
5763*/
5764
5765void
5766impl_map_store(map, lhs, rhs)
5767 MAP *map;
5768 char *lhs;
5769 char *rhs;
5770{
5771 if (tTd(38, 12))
5772 sm_dprintf("impl_map_store(%s, %s, %s)\n",
5773 map->map_mname, lhs, rhs);
5774#if NEWDB
5775 if (bitset(MF_IMPL_HASH, map->map_mflags))
5776 db_map_store(map, lhs, rhs);
5777#endif /* NEWDB */
5778#if NDBM
5779 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5780 ndbm_map_store(map, lhs, rhs);
5781#endif /* NDBM */
5782 stab_map_store(map, lhs, rhs);
5783}
5784
5785/*
5786** IMPL_MAP_OPEN -- implicit database open
5787*/
5788
5789bool
5790impl_map_open(map, mode)
5791 MAP *map;
5792 int mode;
5793{
5794 if (tTd(38, 2))
5795 sm_dprintf("impl_map_open(%s, %s, %d)\n",
5796 map->map_mname, map->map_file, mode);
5797
5798 mode &= O_ACCMODE;
5799#if NEWDB
5800 map->map_mflags |= MF_IMPL_HASH;
5801 if (hash_map_open(map, mode))
5802 {
5803# ifdef NDBM_YP_COMPAT
5804 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
5805# endif /* NDBM_YP_COMPAT */
5806 return true;
5807 }
5808 else
5809 map->map_mflags &= ~MF_IMPL_HASH;
5810#endif /* NEWDB */
5811#if NDBM
5812 map->map_mflags |= MF_IMPL_NDBM;
5813 if (ndbm_map_open(map, mode))
5814 {
5815 return true;
5816 }
5817 else
5818 map->map_mflags &= ~MF_IMPL_NDBM;
5819#endif /* NDBM */
5820
5821#if defined(NEWDB) || defined(NDBM)
5822 if (Verbose)
5823 message("WARNING: cannot open alias database %s%s",
5824 map->map_file,
5825 mode == O_RDONLY ? "; reading text version" : "");
5826#else /* defined(NEWDB) || defined(NDBM) */
5827 if (mode != O_RDONLY)
5828 usrerr("Cannot rebuild aliases: no database format defined");
5829#endif /* defined(NEWDB) || defined(NDBM) */
5830
5831 if (mode == O_RDONLY)
5832 return stab_map_open(map, mode);
5833 else
5834 return false;
5835}
5836
5837
5838/*
5839** IMPL_MAP_CLOSE -- close any open database(s)
5840*/
5841
5842void
5843impl_map_close(map)
5844 MAP *map;
5845{
5846 if (tTd(38, 9))
5847 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
5848 map->map_mname, map->map_file, map->map_mflags);
5849#if NEWDB
5850 if (bitset(MF_IMPL_HASH, map->map_mflags))
5851 {
5852 db_map_close(map);
5853 map->map_mflags &= ~MF_IMPL_HASH;
5854 }
5855#endif /* NEWDB */
5856
5857#if NDBM
5858 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5859 {
5860 ndbm_map_close(map);
5861 map->map_mflags &= ~MF_IMPL_NDBM;
5862 }
5863#endif /* NDBM */
5864}
5865/*
5866** User map class.
5867**
5868** Provides access to the system password file.
5869*/
5870
5871/*
5872** USER_MAP_OPEN -- open user map
5873**
5874** Really just binds field names to field numbers.
5875*/
5876
5877bool
5878user_map_open(map, mode)
5879 MAP *map;
5880 int mode;
5881{
5882 if (tTd(38, 2))
5883 sm_dprintf("user_map_open(%s, %d)\n",
5884 map->map_mname, mode);
5885
5886 mode &= O_ACCMODE;
5887 if (mode != O_RDONLY)
5888 {
5889 /* issue a pseudo-error message */
5890 errno = SM_EMAPCANTWRITE;
5891 return false;
5892 }
5893 if (map->map_valcolnm == NULL)
5894 /* EMPTY */
5895 /* nothing */ ;
5896 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
5897 map->map_valcolno = 1;
5898 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
5899 map->map_valcolno = 2;
5900 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
5901 map->map_valcolno = 3;
5902 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
5903 map->map_valcolno = 4;
5904 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
5905 map->map_valcolno = 5;
5906 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
5907 map->map_valcolno = 6;
5908 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
5909 map->map_valcolno = 7;
5910 else
5911 {
5912 syserr("User map %s: unknown column name %s",
5913 map->map_mname, map->map_valcolnm);
5914 return false;
5915 }
5916 return true;
5917}
5918
5919
5920/*
5921** USER_MAP_LOOKUP -- look up a user in the passwd file.
5922*/
5923
5924/* ARGSUSED3 */
5925char *
5926user_map_lookup(map, key, av, statp)
5927 MAP *map;
5928 char *key;
5929 char **av;
5930 int *statp;
5931{
5932 auto bool fuzzy;
5933 SM_MBDB_T user;
5934
5935 if (tTd(38, 20))
5936 sm_dprintf("user_map_lookup(%s, %s)\n",
5937 map->map_mname, key);
5938
5939 *statp = finduser(key, &fuzzy, &user);
5940 if (*statp != EX_OK)
5941 return NULL;
5942 if (bitset(MF_MATCHONLY, map->map_mflags))
5943 return map_rewrite(map, key, strlen(key), NULL);
5944 else
5945 {
5946 char *rwval = NULL;
5947 char buf[30];
5948
5949 switch (map->map_valcolno)
5950 {
5951 case 0:
5952 case 1:
5953 rwval = user.mbdb_name;
5954 break;
5955
5956 case 2:
5957 rwval = "x"; /* passwd no longer supported */
5958 break;
5959
5960 case 3:
5961 (void) sm_snprintf(buf, sizeof buf, "%d",
5962 (int) user.mbdb_uid);
5963 rwval = buf;
5964 break;
5965
5966 case 4:
5967 (void) sm_snprintf(buf, sizeof buf, "%d",
5968 (int) user.mbdb_gid);
5969 rwval = buf;
5970 break;
5971
5972 case 5:
5973 rwval = user.mbdb_fullname;
5974 break;
5975
5976 case 6:
5977 rwval = user.mbdb_homedir;
5978 break;
5979
5980 case 7:
5981 rwval = user.mbdb_shell;
5982 break;
5983 }
5984 return map_rewrite(map, rwval, strlen(rwval), av);
5985 }
5986}
5987/*
5988** Program map type.
5989**
5990** This provides access to arbitrary programs. It should be used
5991** only very sparingly, since there is no way to bound the cost
5992** of invoking an arbitrary program.
5993*/
5994
5995char *
5996prog_map_lookup(map, name, av, statp)
5997 MAP *map;
5998 char *name;
5999 char **av;
6000 int *statp;
6001{
6002 int i;
6003 int save_errno;
6004 int fd;
6005 int status;
6006 auto pid_t pid;
6007 register char *p;
6008 char *rval;
6009 char *argv[MAXPV + 1];
6010 char buf[MAXLINE];
6011
6012 if (tTd(38, 20))
6013 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6014 map->map_mname, name, map->map_file);
6015
6016 i = 0;
6017 argv[i++] = map->map_file;
6018 if (map->map_rebuild != NULL)
6019 {
6020 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
6021 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6022 {
6023 if (i >= MAXPV - 1)
6024 break;
6025 argv[i++] = p;
6026 }
6027 }
6028 argv[i++] = name;
6029 argv[i] = NULL;
6030 if (tTd(38, 21))
6031 {
6032 sm_dprintf("prog_open:");
6033 for (i = 0; argv[i] != NULL; i++)
6034 sm_dprintf(" %s", argv[i]);
6035 sm_dprintf("\n");
6036 }
6037 (void) sm_blocksignal(SIGCHLD);
6038 pid = prog_open(argv, &fd, CurEnv);
6039 if (pid < 0)
6040 {
6041 if (!bitset(MF_OPTIONAL, map->map_mflags))
6042 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6043 map->map_mname, sm_errstring(errno));
6044 else if (tTd(38, 9))
6045 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6046 map->map_mname, sm_errstring(errno));
6047 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6048 *statp = EX_OSFILE;
6049 return NULL;
6050 }
6051 i = read(fd, buf, sizeof buf - 1);
6052 if (i < 0)
6053 {
6054 syserr("prog_map_lookup(%s): read error %s",
6055 map->map_mname, sm_errstring(errno));
6056 rval = NULL;
6057 }
6058 else if (i == 0)
6059 {
6060 if (tTd(38, 20))
6061 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6062 map->map_mname);
6063 rval = NULL;
6064 }
6065 else
6066 {
6067 buf[i] = '\0';
6068 p = strchr(buf, '\n');
6069 if (p != NULL)
6070 *p = '\0';
6071
6072 /* collect the return value */
6073 if (bitset(MF_MATCHONLY, map->map_mflags))
6074 rval = map_rewrite(map, name, strlen(name), NULL);
6075 else
6076 rval = map_rewrite(map, buf, strlen(buf), av);
6077
6078 /* now flush any additional output */
6079 while ((i = read(fd, buf, sizeof buf)) > 0)
6080 continue;
6081 }
6082
6083 /* wait for the process to terminate */
6084 (void) close(fd);
6085 status = waitfor(pid);
6086 save_errno = errno;
6087 (void) sm_releasesignal(SIGCHLD);
6088 errno = save_errno;
6089
6090 if (status == -1)
6091 {
6092 syserr("prog_map_lookup(%s): wait error %s",
6093 map->map_mname, sm_errstring(errno));
6094 *statp = EX_SOFTWARE;
6095 rval = NULL;
6096 }
6097 else if (WIFEXITED(status))
6098 {
6099 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6100 rval = NULL;
6101 }
6102 else
6103 {
6104 syserr("prog_map_lookup(%s): child died on signal %d",
6105 map->map_mname, status);
6106 *statp = EX_UNAVAILABLE;
6107 rval = NULL;
6108 }
6109 return rval;
6110}
6111/*
6112** Sequenced map type.
6113**
6114** Tries each map in order until something matches, much like
6115** implicit. Stores go to the first map in the list that can
6116** support storing.
6117**
6118** This is slightly unusual in that there are two interfaces.
6119** The "sequence" interface lets you stack maps arbitrarily.
6120** The "switch" interface builds a sequence map by looking
6121** at a system-dependent configuration file such as
6122** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6123**
6124** We don't need an explicit open, since all maps are
6125** opened on demand.
6126*/
6127
6128/*
6129** SEQ_MAP_PARSE -- Sequenced map parsing
6130*/
6131
6132bool
6133seq_map_parse(map, ap)
6134 MAP *map;
6135 char *ap;
6136{
6137 int maxmap;
6138
6139 if (tTd(38, 2))
6140 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6141 maxmap = 0;
6142 while (*ap != '\0')
6143 {
6144 register char *p;
6145 STAB *s;
6146
6147 /* find beginning of map name */
6148 while (isascii(*ap) && isspace(*ap))
6149 ap++;
6150 for (p = ap;
6151 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6152 p++)
6153 continue;
6154 if (*p != '\0')
6155 *p++ = '\0';
6156 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6157 p++;
6158 if (*ap == '\0')
6159 {
6160 ap = p;
6161 continue;
6162 }
6163 s = stab(ap, ST_MAP, ST_FIND);
6164 if (s == NULL)
6165 {
6166 syserr("Sequence map %s: unknown member map %s",
6167 map->map_mname, ap);
6168 }
6169 else if (maxmap >= MAXMAPSTACK)
6170 {
6171 syserr("Sequence map %s: too many member maps (%d max)",
6172 map->map_mname, MAXMAPSTACK);
6173 maxmap++;
6174 }
6175 else if (maxmap < MAXMAPSTACK)
6176 {
6177 map->map_stack[maxmap++] = &s->s_map;
6178 }
6179 ap = p;
6180 }
6181 return true;
6182}
6183
6184/*
6185** SWITCH_MAP_OPEN -- open a switched map
6186**
6187** This looks at the system-dependent configuration and builds
6188** a sequence map that does the same thing.
6189**
6190** Every system must define a switch_map_find routine in conf.c
6191** that will return the list of service types associated with a
6192** given service class.
6193*/
6194
6195bool
6196switch_map_open(map, mode)
6197 MAP *map;
6198 int mode;
6199{
6200 int mapno;
6201 int nmaps;
6202 char *maptype[MAXMAPSTACK];
6203
6204 if (tTd(38, 2))
6205 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6206 map->map_mname, map->map_file, mode);
6207
6208 mode &= O_ACCMODE;
6209 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6210 if (tTd(38, 19))
6211 {
6212 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6213 for (mapno = 0; mapno < nmaps; mapno++)
6214 sm_dprintf("\t\t%s\n", maptype[mapno]);
6215 }
6216 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6217 return false;
6218
6219 for (mapno = 0; mapno < nmaps; mapno++)
6220 {
6221 register STAB *s;
6222 char nbuf[MAXNAME + 1];
6223
6224 if (maptype[mapno] == NULL)
6225 continue;
6226 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
6227 map->map_mname, ".", maptype[mapno]);
6228 s = stab(nbuf, ST_MAP, ST_FIND);
6229 if (s == NULL)
6230 {
6231 syserr("Switch map %s: unknown member map %s",
6232 map->map_mname, nbuf);
6233 }
6234 else
6235 {
6236 map->map_stack[mapno] = &s->s_map;
6237 if (tTd(38, 4))
6238 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6239 mapno,
6240 s->s_map.map_class->map_cname,
6241 nbuf);
6242 }
6243 }
6244 return true;
6245}
6246
6247#if 0
6248/*
6249** SEQ_MAP_CLOSE -- close all underlying maps
6250*/
6251
6252void
6253seq_map_close(map)
6254 MAP *map;
6255{
6256 int mapno;
6257
6258 if (tTd(38, 9))
6259 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6260
6261 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6262 {
6263 MAP *mm = map->map_stack[mapno];
6264
6265 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6266 continue;
6267 mm->map_mflags |= MF_CLOSING;
6268 mm->map_class->map_close(mm);
6269 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6270 }
6271}
6272#endif /* 0 */
6273
6274/*
6275** SEQ_MAP_LOOKUP -- sequenced map lookup
6276*/
6277
6278char *
6279seq_map_lookup(map, key, args, pstat)
6280 MAP *map;
6281 char *key;
6282 char **args;
6283 int *pstat;
6284{
6285 int mapno;
6286 int mapbit = 0x01;
6287 bool tempfail = false;
6288
6289 if (tTd(38, 20))
6290 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6291
6292 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6293 {
6294 MAP *mm = map->map_stack[mapno];
6295 char *rv;
6296
6297 if (mm == NULL)
6298 continue;
6299 if (!bitset(MF_OPEN, mm->map_mflags) &&
6300 !openmap(mm))
6301 {
6302 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6303 {
6304 *pstat = EX_UNAVAILABLE;
6305 return NULL;
6306 }
6307 continue;
6308 }
6309 *pstat = EX_OK;
6310 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6311 if (rv != NULL)
6312 return rv;
6313 if (*pstat == EX_TEMPFAIL)
6314 {
6315 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6316 return NULL;
6317 tempfail = true;
6318 }
6319 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6320 break;
6321 }
6322 if (tempfail)
6323 *pstat = EX_TEMPFAIL;
6324 else if (*pstat == EX_OK)
6325 *pstat = EX_NOTFOUND;
6326 return NULL;
6327}
6328
6329/*
6330** SEQ_MAP_STORE -- sequenced map store
6331*/
6332
6333void
6334seq_map_store(map, key, val)
6335 MAP *map;
6336 char *key;
6337 char *val;
6338{
6339 int mapno;
6340
6341 if (tTd(38, 12))
6342 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6343 map->map_mname, key, val);
6344
6345 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6346 {
6347 MAP *mm = map->map_stack[mapno];
6348
6349 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6350 continue;
6351
6352 mm->map_class->map_store(mm, key, val);
6353 return;
6354 }
6355 syserr("seq_map_store(%s, %s, %s): no writable map",
6356 map->map_mname, key, val);
6357}
6358/*
6359** NULL stubs
6360*/
6361
6362/* ARGSUSED */
6363bool
6364null_map_open(map, mode)
6365 MAP *map;
6366 int mode;
6367{
6368 return true;
6369}
6370
6371/* ARGSUSED */
6372void
6373null_map_close(map)
6374 MAP *map;
6375{
6376 return;
6377}
6378
6379char *
6380null_map_lookup(map, key, args, pstat)
6381 MAP *map;
6382 char *key;
6383 char **args;
6384 int *pstat;
6385{
6386 *pstat = EX_NOTFOUND;
6387 return NULL;
6388}
6389
6390/* ARGSUSED */
6391void
6392null_map_store(map, key, val)
6393 MAP *map;
6394 char *key;
6395 char *val;
6396{
6397 return;
6398}
6399
6400/*
6401** BOGUS stubs
6402*/
6403
6404char *
6405bogus_map_lookup(map, key, args, pstat)
6406 MAP *map;
6407 char *key;
6408 char **args;
6409 int *pstat;
6410{
6411 *pstat = EX_TEMPFAIL;
6412 return NULL;
6413}
6414
6415MAPCLASS BogusMapClass =
6416{
6417 "bogus-map", NULL, 0,
6418 NULL, bogus_map_lookup, null_map_store,
6419 null_map_open, null_map_close,
6420};
6421/*
6422** MACRO modules
6423*/
6424
6425char *
6426macro_map_lookup(map, name, av, statp)
6427 MAP *map;
6428 char *name;
6429 char **av;
6430 int *statp;
6431{
6432 int mid;
6433
6434 if (tTd(38, 20))
6435 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6436 name == NULL ? "NULL" : name);
6437
6438 if (name == NULL ||
6439 *name == '\0' ||
6440 (mid = macid(name)) == 0)
6441 {
6442 *statp = EX_CONFIG;
6443 return NULL;
6444 }
6445
6446 if (av[1] == NULL)
6447 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6448 else
6449 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6450
6451 *statp = EX_OK;
6452 return "";
6453}
6454/*
6455** REGEX modules
6456*/
6457
6458#if MAP_REGEX
6459
6460# include <regex.h>
6461
6462# define DEFAULT_DELIM CONDELSE
6463# define END_OF_FIELDS -1
6464# define ERRBUF_SIZE 80
6465# define MAX_MATCH 32
6466
6467# define xnalloc(s) memset(xalloc(s), '\0', s);
6468
6469struct regex_map
6470{
6471 regex_t *regex_pattern_buf; /* xalloc it */
6472 int *regex_subfields; /* move to type MAP */
6473 char *regex_delim; /* move to type MAP */
6474};
6475
6476static int parse_fields __P((char *, int *, int, int));
6477static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6478
6479static int
6480parse_fields(s, ibuf, blen, nr_substrings)
6481 char *s;
6482 int *ibuf; /* array */
6483 int blen; /* number of elements in ibuf */
6484 int nr_substrings; /* number of substrings in the pattern */
6485{
6486 register char *cp;
6487 int i = 0;
6488 bool lastone = false;
6489
6490 blen--; /* for terminating END_OF_FIELDS */
6491 cp = s;
6492 do
6493 {
6494 for (;; cp++)
6495 {
6496 if (*cp == ',')
6497 {
6498 *cp = '\0';
6499 break;
6500 }
6501 if (*cp == '\0')
6502 {
6503 lastone = true;
6504 break;
6505 }
6506 }
6507 if (i < blen)
6508 {
6509 int val = atoi(s);
6510
6511 if (val < 0 || val >= nr_substrings)
6512 {
6513 syserr("field (%d) out of range, only %d substrings in pattern",
6514 val, nr_substrings);
6515 return -1;
6516 }
6517 ibuf[i++] = val;
6518 }
6519 else
6520 {
6521 syserr("too many fields, %d max", blen);
6522 return -1;
6523 }
6524 s = ++cp;
6525 } while (!lastone);
6526 ibuf[i] = END_OF_FIELDS;
6527 return i;
6528}
6529
6530bool
6531regex_map_init(map, ap)
6532 MAP *map;
6533 char *ap;
6534{
6535 int regerr;
6536 struct regex_map *map_p;
6537 register char *p;
6538 char *sub_param = NULL;
6539 int pflags;
6540 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6541
6542 if (tTd(38, 2))
6543 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6544 map->map_mname, ap);
6545
6546 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6547 p = ap;
6548 map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6549 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6550
6551 for (;;)
6552 {
6553 while (isascii(*p) && isspace(*p))
6554 p++;
6555 if (*p != '-')
6556 break;
6557 switch (*++p)
6558 {
6559 case 'n': /* not */
6560 map->map_mflags |= MF_REGEX_NOT;
6561 break;
6562
6563 case 'f': /* case sensitive */
6564 map->map_mflags |= MF_NOFOLDCASE;
6565 pflags &= ~REG_ICASE;
6566 break;
6567
6568 case 'b': /* basic regular expressions */
6569 pflags &= ~REG_EXTENDED;
6570 break;
6571
6572 case 's': /* substring match () syntax */
6573 sub_param = ++p;
6574 pflags &= ~REG_NOSUB;
6575 break;
6576
6577 case 'd': /* delimiter */
6578 map_p->regex_delim = ++p;
6579 break;
6580
6581 case 'a': /* map append */
6582 map->map_app = ++p;
6583 break;
6584
6585 case 'm': /* matchonly */
6586 map->map_mflags |= MF_MATCHONLY;
6587 break;
6588
6589 case 'q':
6590 map->map_mflags |= MF_KEEPQUOTES;
6591 break;
6592
6593 case 'S':
6594 map->map_spacesub = *++p;
6595 break;
6596
6597 case 'D':
6598 map->map_mflags |= MF_DEFER;
6599 break;
6600
6601 }
6602 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6603 p++;
6604 if (*p != '\0')
6605 *p++ = '\0';
6606 }
6607 if (tTd(38, 3))
6608 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6609
6610 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6611 {
6612 /* Errorhandling */
6613 char errbuf[ERRBUF_SIZE];
6614
6615 (void) regerror(regerr, map_p->regex_pattern_buf,
6616 errbuf, sizeof errbuf);
6617 syserr("pattern-compile-error: %s", errbuf);
6618 sm_free(map_p->regex_pattern_buf); /* XXX */
6619 sm_free(map_p); /* XXX */
6620 return false;
6621 }
6622
6623 if (map->map_app != NULL)
6624 map->map_app = newstr(map->map_app);
6625 if (map_p->regex_delim != NULL)
6626 map_p->regex_delim = newstr(map_p->regex_delim);
6627 else
6628 map_p->regex_delim = defdstr;
6629
6630 if (!bitset(REG_NOSUB, pflags))
6631 {
6632 /* substring matching */
6633 int substrings;
6634 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6635
6636 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6637
6638 if (tTd(38, 3))
6639 sm_dprintf("regex_map_init: nr of substrings %d\n",
6640 substrings);
6641
6642 if (substrings >= MAX_MATCH)
6643 {
6644 syserr("too many substrings, %d max", MAX_MATCH);
6645 sm_free(map_p->regex_pattern_buf); /* XXX */
6646 sm_free(map_p); /* XXX */
6647 return false;
6648 }
6649 if (sub_param != NULL && sub_param[0] != '\0')
6650 {
6651 /* optional parameter -sfields */
6652 if (parse_fields(sub_param, fields,
6653 MAX_MATCH + 1, substrings) == -1)
6654 return false;
6655 }
6656 else
6657 {
6658 int i;
6659
6660 /* set default fields */
6661 for (i = 0; i < substrings; i++)
6662 fields[i] = i;
6663 fields[i] = END_OF_FIELDS;
6664 }
6665 map_p->regex_subfields = fields;
6666 if (tTd(38, 3))
6667 {
6668 int *ip;
6669
6670 sm_dprintf("regex_map_init: subfields");
6671 for (ip = fields; *ip != END_OF_FIELDS; ip++)
6672 sm_dprintf(" %d", *ip);
6673 sm_dprintf("\n");
6674 }
6675 }
6676 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
6677 return true;
6678}
6679
6680static char *
6681regex_map_rewrite(map, s, slen, av)
6682 MAP *map;
6683 const char *s;
6684 size_t slen;
6685 char **av;
6686{
6687 if (bitset(MF_MATCHONLY, map->map_mflags))
6688 return map_rewrite(map, av[0], strlen(av[0]), NULL);
6689 else
6690 return map_rewrite(map, s, slen, av);
6691}
6692
6693char *
6694regex_map_lookup(map, name, av, statp)
6695 MAP *map;
6696 char *name;
6697 char **av;
6698 int *statp;
6699{
6700 int reg_res;
6701 struct regex_map *map_p;
6702 regmatch_t pmatch[MAX_MATCH];
6703
6704 if (tTd(38, 20))
6705 {
6706 char **cpp;
6707
6708 sm_dprintf("regex_map_lookup: key '%s'\n", name);
6709 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6710 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
6711 }
6712
6713 map_p = (struct regex_map *)(map->map_db1);
6714 reg_res = regexec(map_p->regex_pattern_buf,
6715 name, MAX_MATCH, pmatch, 0);
6716
6717 if (bitset(MF_REGEX_NOT, map->map_mflags))
6718 {
6719 /* option -n */
6720 if (reg_res == REG_NOMATCH)
6721 return regex_map_rewrite(map, "", (size_t) 0, av);
6722 else
6723 return NULL;
6724 }
6725 if (reg_res == REG_NOMATCH)
6726 return NULL;
6727
6728 if (map_p->regex_subfields != NULL)
6729 {
6730 /* option -s */
6731 static char retbuf[MAXNAME];
6732 int fields[MAX_MATCH + 1];
6733 bool first = true;
6734 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
6735 bool quotemode = false, bslashmode = false;
6736 register char *dp, *sp;
6737 char *endp, *ldp;
6738 int *ip;
6739
6740 dp = retbuf;
6741 ldp = retbuf + sizeof(retbuf) - 1;
6742
6743 if (av[1] != NULL)
6744 {
6745 if (parse_fields(av[1], fields, MAX_MATCH + 1,
6746 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
6747 {
6748 *statp = EX_CONFIG;
6749 return NULL;
6750 }
6751 ip = fields;
6752 }
6753 else
6754 ip = map_p->regex_subfields;
6755
6756 for ( ; *ip != END_OF_FIELDS; ip++)
6757 {
6758 if (!first)
6759 {
6760 for (sp = map_p->regex_delim; *sp; sp++)
6761 {
6762 if (dp < ldp)
6763 *dp++ = *sp;
6764 }
6765 }
6766 else
6767 first = false;
6768
6769 if (*ip >= MAX_MATCH ||
6770 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
6771 continue;
6772
6773 sp = name + pmatch[*ip].rm_so;
6774 endp = name + pmatch[*ip].rm_eo;
6775 for (; endp > sp; sp++)
6776 {
6777 if (dp < ldp)
6778 {
6779 if (bslashmode)
6780 {
6781 *dp++ = *sp;
6782 bslashmode = false;
6783 }
6784 else if (quotemode && *sp != '"' &&
6785 *sp != '\\')
6786 {
6787 *dp++ = *sp;
6788 }
6789 else switch (*dp++ = *sp)
6790 {
6791 case '\\':
6792 bslashmode = true;
6793 break;
6794
6795 case '(':
6796 cmntcnt++;
6797 break;
6798
6799 case ')':
6800 cmntcnt--;
6801 break;
6802
6803 case '<':
6804 anglecnt++;
6805 break;
6806
6807 case '>':
6808 anglecnt--;
6809 break;
6810
6811 case ' ':
6812 spacecnt++;
6813 break;
6814
6815 case '"':
6816 quotemode = !quotemode;
6817 break;
6818 }
6819 }
6820 }
6821 }
6822 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
6823 bslashmode || spacecnt != 0)
6824 {
6825 sm_syslog(LOG_WARNING, NOQID,
6826 "Warning: regex may cause prescan() failure map=%s lookup=%s",
6827 map->map_mname, name);
6828 return NULL;
6829 }
6830
6831 *dp = '\0';
6832
6833 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
6834 }
6835 return regex_map_rewrite(map, "", (size_t)0, av);
6836}
6837#endif /* MAP_REGEX */
6838/*
6839** NSD modules
6840*/
6841#if MAP_NSD
6842
6843# include <ndbm.h>
6844# define _DATUM_DEFINED
6845# include <ns_api.h>
6846
6847typedef struct ns_map_list
6848{
6849 ns_map_t *map; /* XXX ns_ ? */
6850 char *mapname;
6851 struct ns_map_list *next;
6852} ns_map_list_t;
6853
6854static ns_map_t *
6855ns_map_t_find(mapname)
6856 char *mapname;
6857{
6858 static ns_map_list_t *ns_maps = NULL;
6859 ns_map_list_t *ns_map;
6860
6861 /* walk the list of maps looking for the correctly named map */
6862 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
6863 {
6864 if (strcmp(ns_map->mapname, mapname) == 0)
6865 break;
6866 }
6867
6868 /* if we are looking at a NULL ns_map_list_t, then create a new one */
6869 if (ns_map == NULL)
6870 {
6871 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
6872 ns_map->mapname = newstr(mapname);
6873 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6874 memset(ns_map->map, '\0', sizeof *ns_map->map);
6875 ns_map->next = ns_maps;
6876 ns_maps = ns_map;
6877 }
6878 return ns_map->map;
6879}
6880
6881char *
6882nsd_map_lookup(map, name, av, statp)
6883 MAP *map;
6884 char *name;
6885 char **av;
6886 int *statp;
6887{
6888 int buflen, r;
6889 char *p;
6890 ns_map_t *ns_map;
6891 char keybuf[MAXNAME + 1];
6892 char buf[MAXLINE];
6893
6894 if (tTd(38, 20))
6895 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
6896
6897 buflen = strlen(name);
6898 if (buflen > sizeof keybuf - 1)
6899 buflen = sizeof keybuf - 1; /* XXX simply cut off? */
6900 memmove(keybuf, name, buflen);
6901 keybuf[buflen] = '\0';
6902 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
6903 makelower(keybuf);
6904
6905 ns_map = ns_map_t_find(map->map_file);
6906 if (ns_map == NULL)
6907 {
6908 if (tTd(38, 20))
6909 sm_dprintf("nsd_map_t_find failed\n");
6910 *statp = EX_UNAVAILABLE;
6911 return NULL;
6912 }
6913 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
6914 buf, sizeof buf);
6915 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
6916 {
6917 *statp = EX_TEMPFAIL;
6918 return NULL;
6919 }
6920 if (r == NS_BADREQ
6921# ifdef NS_NOPERM
6922 || r == NS_NOPERM
6923# endif /* NS_NOPERM */
6924 )
6925 {
6926 *statp = EX_CONFIG;
6927 return NULL;
6928 }
6929 if (r != NS_SUCCESS)
6930 {
6931 *statp = EX_NOTFOUND;
6932 return NULL;
6933 }
6934
6935 *statp = EX_OK;
6936
6937 /* Null out trailing \n */
6938 if ((p = strchr(buf, '\n')) != NULL)
6939 *p = '\0';
6940
6941 return map_rewrite(map, buf, strlen(buf), av);
6942}
6943#endif /* MAP_NSD */
6944
6945char *
6946arith_map_lookup(map, name, av, statp)
6947 MAP *map;
6948 char *name;
6949 char **av;
6950 int *statp;
6951{
6952 long r;
6953 long v[2];
6954 bool res = false;
6955 bool boolres;
6956 static char result[16];
6957 char **cpp;
6958
6959 if (tTd(38, 2))
6960 {
6961 sm_dprintf("arith_map_lookup: key '%s'\n", name);
6962 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6963 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
6964 }
6965 r = 0;
6966 boolres = false;
6967 cpp = av;
6968 *statp = EX_OK;
6969
6970 /*
6971 ** read arguments for arith map
6972 ** - no check is made whether they are really numbers
6973 ** - just ignores args after the second
6974 */
6975
6976 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
6977 v[r++] = strtol(*cpp, NULL, 0);
6978
6979 /* operator and (at least) two operands given? */
6980 if (name != NULL && r == 2)
6981 {
6982 switch (*name)
6983 {
6984 case '|':
6985 r = v[0] | v[1];
6986 break;
6987
6988 case '&':
6989 r = v[0] & v[1];
6990 break;
6991
6992 case '%':
6993 if (v[1] == 0)
6994 return NULL;
6995 r = v[0] % v[1];
6996 break;
6997 case '+':
6998 r = v[0] + v[1];
6999 break;
7000
7001 case '-':
7002 r = v[0] - v[1];
7003 break;
7004
7005 case '*':
7006 r = v[0] * v[1];
7007 break;
7008
7009 case '/':
7010 if (v[1] == 0)
7011 return NULL;
7012 r = v[0] / v[1];
7013 break;
7014
7015 case 'l':
7016 res = v[0] < v[1];
7017 boolres = true;
7018 break;
7019
7020 case '=':
7021 res = v[0] == v[1];
7022 boolres = true;
7023 break;
7024
7025 default:
7026 /* XXX */
7027 *statp = EX_CONFIG;
7028 if (LogLevel > 10)
7029 sm_syslog(LOG_WARNING, NOQID,
7030 "arith_map: unknown operator %c",
7031 isprint(*name) ? *name : '?');
7032 return NULL;
7033 }
7034 if (boolres)
7035 (void) sm_snprintf(result, sizeof result,
7036 res ? "TRUE" : "FALSE");
7037 else
7038 (void) sm_snprintf(result, sizeof result, "%ld", r);
7039 return result;
7040 }
7041 *statp = EX_CONFIG;
7042 return NULL;
7043}
7044
7045#if SOCKETMAP
7046
7047# if NETINET || NETINET6
7048# include <arpa/inet.h>
7049# endif /* NETINET || NETINET6 */
7050
7051# define socket_map_next map_stack[0]
7052
7053/*
7054** SOCKET_MAP_OPEN -- open socket table
7055*/
7056
7057bool
7058socket_map_open(map, mode)
7059 MAP *map;
7060 int mode;
7061{
7062 STAB *s;
7063 int sock = 0;
7064 SOCKADDR_LEN_T addrlen = 0;
7065 int addrno = 0;
7066 int save_errno;
7067 char *p;
7068 char *colon;
7069 char *at;
7070 struct hostent *hp = NULL;
7071 SOCKADDR addr;
7072
7073 if (tTd(38, 2))
7074 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7075 map->map_mname, map->map_file, mode);
7076
7077 mode &= O_ACCMODE;
7078
7079 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7080 if (mode != O_RDONLY)
7081 {
7082 /* issue a pseudo-error message */
7083 errno = SM_EMAPCANTWRITE;
7084 return false;
7085 }
7086
7087 if (*map->map_file == '\0')
7088 {
7089 syserr("socket map \"%s\": empty or missing socket information",
7090 map->map_mname);
7091 return false;
7092 }
7093
7094 s = socket_map_findconn(map->map_file);
7095 if (s->s_socketmap != NULL)
7096 {
7097 /* Copy open connection */
7098 map->map_db1 = s->s_socketmap->map_db1;
7099
7100 /* Add this map as head of linked list */
7101 map->socket_map_next = s->s_socketmap;
7102 s->s_socketmap = map;
7103
7104 if (tTd(38, 2))
7105 sm_dprintf("using cached connection\n");
7106 return true;
7107 }
7108
7109 if (tTd(38, 2))
7110 sm_dprintf("opening new connection\n");
7111
7112 /* following code is ripped from milter.c */
7113 /* XXX It should be put in a library... */
7114
7115 /* protocol:filename or protocol:port@host */
7116 memset(&addr, '\0', sizeof addr);
7117 p = map->map_file;
7118 colon = strchr(p, ':');
7119 if (colon != NULL)
7120 {
7121 *colon = '\0';
7122
7123 if (*p == '\0')
7124 {
7125# if NETUNIX
7126 /* default to AF_UNIX */
7127 addr.sa.sa_family = AF_UNIX;
7128# else /* NETUNIX */
7129# if NETINET
7130 /* default to AF_INET */
7131 addr.sa.sa_family = AF_INET;
7132# else /* NETINET */
7133# if NETINET6
7134 /* default to AF_INET6 */
7135 addr.sa.sa_family = AF_INET6;
7136# else /* NETINET6 */
7137 /* no protocols available */
7138 syserr("socket map \"%s\": no valid socket protocols available",
7139 map->map_mname);
7140 return false;
7141# endif /* NETINET6 */
7142# endif /* NETINET */
7143# endif /* NETUNIX */
7144 }
7145# if NETUNIX
7146 else if (sm_strcasecmp(p, "unix") == 0 ||
7147 sm_strcasecmp(p, "local") == 0)
7148 addr.sa.sa_family = AF_UNIX;
7149# endif /* NETUNIX */
7150# if NETINET
7151 else if (sm_strcasecmp(p, "inet") == 0)
7152 addr.sa.sa_family = AF_INET;
7153# endif /* NETINET */
7154# if NETINET6
7155 else if (sm_strcasecmp(p, "inet6") == 0)
7156 addr.sa.sa_family = AF_INET6;
7157# endif /* NETINET6 */
7158 else
7159 {
7160# ifdef EPROTONOSUPPORT
7161 errno = EPROTONOSUPPORT;
7162# else /* EPROTONOSUPPORT */
7163 errno = EINVAL;
7164# endif /* EPROTONOSUPPORT */
7165 syserr("socket map \"%s\": unknown socket type %s",
7166 map->map_mname, p);
7167 return false;
7168 }
7169 *colon++ = ':';
7170 }
7171 else
7172 {
7173 colon = p;
7174#if NETUNIX
7175 /* default to AF_UNIX */
7176 addr.sa.sa_family = AF_UNIX;
7177#else /* NETUNIX */
7178# if NETINET
7179 /* default to AF_INET */
7180 addr.sa.sa_family = AF_INET;
7181# else /* NETINET */
7182# if NETINET6
7183 /* default to AF_INET6 */
7184 addr.sa.sa_family = AF_INET6;
7185# else /* NETINET6 */
7186 syserr("socket map \"%s\": unknown socket type %s",
7187 map->map_mname, p);
7188 return false;
7189# endif /* NETINET6 */
7190# endif /* NETINET */
7191#endif /* NETUNIX */
7192 }
7193
7194# if NETUNIX
7195 if (addr.sa.sa_family == AF_UNIX)
7196 {
7197 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7198
7199 at = colon;
7200 if (strlen(colon) >= sizeof addr.sunix.sun_path)
7201 {
7202 syserr("socket map \"%s\": local socket name %s too long",
7203 map->map_mname, colon);
7204 return false;
7205 }
7206 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7207 S_IRUSR|S_IWUSR, NULL);
7208
7209 if (errno != 0)
7210 {
7211 /* if not safe, don't create */
7212 syserr("socket map \"%s\": local socket name %s unsafe",
7213 map->map_mname, colon);
7214 return false;
7215 }
7216
7217 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7218 sizeof addr.sunix.sun_path);
7219 addrlen = sizeof (struct sockaddr_un);
7220 }
7221 else
7222# endif /* NETUNIX */
7223# if NETINET || NETINET6
7224 if (false
7225# if NETINET
7226 || addr.sa.sa_family == AF_INET
7227# endif /* NETINET */
7228# if NETINET6
7229 || addr.sa.sa_family == AF_INET6
7230# endif /* NETINET6 */
7231 )
7232 {
7233 unsigned short port;
7234
7235 /* Parse port@host */
7236 at = strchr(colon, '@');
7237 if (at == NULL)
7238 {
7239 syserr("socket map \"%s\": bad address %s (expected port@host)",
7240 map->map_mname, colon);
7241 return false;
7242 }
7243 *at = '\0';
7244 if (isascii(*colon) && isdigit(*colon))
7245 port = htons((unsigned short) atoi(colon));
7246 else
7247 {
7248# ifdef NO_GETSERVBYNAME
7249 syserr("socket map \"%s\": invalid port number %s",
7250 map->map_mname, colon);
7251 return false;
7252# else /* NO_GETSERVBYNAME */
7253 register struct servent *sp;
7254
7255 sp = getservbyname(colon, "tcp");
7256 if (sp == NULL)
7257 {
7258 syserr("socket map \"%s\": unknown port name %s",
7259 map->map_mname, colon);
7260 return false;
7261 }
7262 port = sp->s_port;
7263# endif /* NO_GETSERVBYNAME */
7264 }
7265 *at++ = '@';
7266 if (*at == '[')
7267 {
7268 char *end;
7269
7270 end = strchr(at, ']');
7271 if (end != NULL)
7272 {
7273 bool found = false;
7274# if NETINET
7275 unsigned long hid = INADDR_NONE;
7276# endif /* NETINET */
7277# if NETINET6
7278 struct sockaddr_in6 hid6;
7279# endif /* NETINET6 */
7280
7281 *end = '\0';
7282# if NETINET
7283 if (addr.sa.sa_family == AF_INET &&
7284 (hid = inet_addr(&at[1])) != INADDR_NONE)
7285 {
7286 addr.sin.sin_addr.s_addr = hid;
7287 addr.sin.sin_port = port;
7288 found = true;
7289 }
7290# endif /* NETINET */
7291# if NETINET6
7292 (void) memset(&hid6, '\0', sizeof hid6);
7293 if (addr.sa.sa_family == AF_INET6 &&
7294 anynet_pton(AF_INET6, &at[1],
7295 &hid6.sin6_addr) == 1)
7296 {
7297 addr.sin6.sin6_addr = hid6.sin6_addr;
7298 addr.sin6.sin6_port = port;
7299 found = true;
7300 }
7301# endif /* NETINET6 */
7302 *end = ']';
7303 if (!found)
7304 {
7305 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7306 map->map_mname, at);
7307 return false;
7308 }
7309 }
7310 else
7311 {
7312 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7313 map->map_mname, at);
7314 return false;
7315 }
7316 }
7317 else
7318 {
7319 hp = sm_gethostbyname(at, addr.sa.sa_family);
7320 if (hp == NULL)
7321 {
7322 syserr("socket map \"%s\": Unknown host name %s",
7323 map->map_mname, at);
7324 return false;
7325 }
7326 addr.sa.sa_family = hp->h_addrtype;
7327 switch (hp->h_addrtype)
7328 {
7329# if NETINET
7330 case AF_INET:
7331 memmove(&addr.sin.sin_addr,
7332 hp->h_addr, INADDRSZ);
7333 addr.sin.sin_port = port;
7334 addrlen = sizeof (struct sockaddr_in);
7335 addrno = 1;
7336 break;
7337# endif /* NETINET */
7338
7339# if NETINET6
7340 case AF_INET6:
7341 memmove(&addr.sin6.sin6_addr,
7342 hp->h_addr, IN6ADDRSZ);
7343 addr.sin6.sin6_port = port;
7344 addrlen = sizeof (struct sockaddr_in6);
7345 addrno = 1;
7346 break;
7347# endif /* NETINET6 */
7348
7349 default:
7350 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7351 map->map_mname, at, hp->h_addrtype);
7352# if NETINET6
7353 freehostent(hp);
7354# endif /* NETINET6 */
7355 return false;
7356 }
7357 }
7358 }
7359 else
7360# endif /* NETINET || NETINET6 */
7361 {
7362 syserr("socket map \"%s\": unknown socket protocol",
7363 map->map_mname);
7364 return false;
7365 }
7366
7367 /* nope, actually connecting */
7368 for (;;)
7369 {
7370 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7371 if (sock < 0)
7372 {
7373 save_errno = errno;
7374 if (tTd(38, 5))
7375 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7376 map->map_mname,
7377 sm_errstring(save_errno));
7378# if NETINET6
7379 if (hp != NULL)
7380 freehostent(hp);
7381# endif /* NETINET6 */
7382 return false;
7383 }
7384
7385 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7386 break;
7387
7388 /* couldn't connect.... try next address */
7389 save_errno = errno;
7390 p = CurHostName;
7391 CurHostName = at;
7392 if (tTd(38, 5))
7393 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7394 map->map_mname, at, sm_errstring(save_errno));
7395 CurHostName = p;
7396 (void) close(sock);
7397
7398 /* try next address */
7399 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7400 {
7401 switch (addr.sa.sa_family)
7402 {
7403# if NETINET
7404 case AF_INET:
7405 memmove(&addr.sin.sin_addr,
7406 hp->h_addr_list[addrno++],
7407 INADDRSZ);
7408 break;
7409# endif /* NETINET */
7410
7411# if NETINET6
7412 case AF_INET6:
7413 memmove(&addr.sin6.sin6_addr,
7414 hp->h_addr_list[addrno++],
7415 IN6ADDRSZ);
7416 break;
7417# endif /* NETINET6 */
7418
7419 default:
7420 if (tTd(38, 5))
7421 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7422 map->map_mname, at,
7423 hp->h_addrtype);
7424# if NETINET6
7425 freehostent(hp);
7426# endif /* NETINET6 */
7427 return false;
7428 }
7429 continue;
7430 }
7431 p = CurHostName;
7432 CurHostName = at;
7433 if (tTd(38, 5))
7434 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7435 map->map_mname, sm_errstring(save_errno));
7436 CurHostName = p;
7437# if NETINET6
7438 if (hp != NULL)
7439 freehostent(hp);
7440# endif /* NETINET6 */
7441 return false;
7442 }
7443# if NETINET6
7444 if (hp != NULL)
7445 {
7446 freehostent(hp);
7447 hp = NULL;
7448 }
7449# endif /* NETINET6 */
7450 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7451 SM_TIME_DEFAULT,
7452 (void *) &sock,
7453 SM_IO_RDWR,
7454 NULL)) == NULL)
7455 {
7456 close(sock);
7457 if (tTd(38, 2))
7458 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7459 map->map_mname, sm_errstring(errno));
7460 return false;
7461 }
7462
7463 /* Save connection for reuse */
7464 s->s_socketmap = map;
7465 return true;
7466}
7467
7468/*
7469** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7470**
7471** Cache SOCKET connections based on the connection specifier
7472** and PID so we don't have multiple connections open to
7473** the same server for different maps. Need a separate connection
7474** per PID since a parent process may close the map before the
7475** child is done with it.
7476**
7477** Parameters:
7478** conn -- SOCKET map connection specifier
7479**
7480** Returns:
7481** Symbol table entry for the SOCKET connection.
7482*/
7483
7484static STAB *
7485socket_map_findconn(conn)
7486 const char *conn;
7487{
7488 char *nbuf;
7489 STAB *SM_NONVOLATILE s = NULL;
7490
7491 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7492 SM_TRY
7493 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7494 SM_FINALLY
7495 sm_free(nbuf);
7496 SM_END_TRY
7497 return s;
7498}
7499
7500/*
7501** SOCKET_MAP_CLOSE -- close the socket
7502*/
7503
7504void
7505socket_map_close(map)
7506 MAP *map;
7507{
7508 STAB *s;
7509 MAP *smap;
7510
7511 if (tTd(38, 20))
7512 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7513 (long) CurrentPid);
7514
7515 /* Check if already closed */
7516 if (map->map_db1 == NULL)
7517 {
7518 if (tTd(38, 20))
7519 sm_dprintf("socket_map_close(%s) already closed\n",
7520 map->map_file);
7521 return;
7522 }
7523 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7524
7525 /* Mark all the maps that share the connection as closed */
7526 s = socket_map_findconn(map->map_file);
7527 smap = s->s_socketmap;
7528 while (smap != NULL)
7529 {
7530 MAP *next;
7531
7532 if (tTd(38, 2) && smap != map)
7533 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7534 map->map_mname, smap->map_mname);
7535
7536 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7537 smap->map_db1 = NULL;
7538 next = smap->socket_map_next;
7539 smap->socket_map_next = NULL;
7540 smap = next;
7541 }
7542 s->s_socketmap = NULL;
7543}
7544
7545/*
7546** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7547*/
7548
7549char *
7550socket_map_lookup(map, name, av, statp)
7551 MAP *map;
7552 char *name;
7553 char **av;
7554 int *statp;
7555{
7556 unsigned int nettolen, replylen, recvlen;
5668/*
5669** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5670*/
5671
5672void
5673stab_map_store(map, lhs, rhs)
5674 register MAP *map;
5675 char *lhs;
5676 char *rhs;
5677{
5678 register STAB *s;
5679
5680 s = stab(lhs, ST_ALIAS, ST_ENTER);
5681 s->s_alias = newstr(rhs);
5682}
5683
5684
5685/*
5686** STAB_MAP_OPEN -- initialize (reads data file)
5687**
5688** This is a wierd case -- it is only intended as a fallback for
5689** aliases. For this reason, opens for write (only during a
5690** "newaliases") always fails, and opens for read open the
5691** actual underlying text file instead of the database.
5692*/
5693
5694bool
5695stab_map_open(map, mode)
5696 register MAP *map;
5697 int mode;
5698{
5699 SM_FILE_T *af;
5700 long sff;
5701 struct stat st;
5702
5703 if (tTd(38, 2))
5704 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5705 map->map_mname, map->map_file, mode);
5706
5707 mode &= O_ACCMODE;
5708 if (mode != O_RDONLY)
5709 {
5710 errno = EPERM;
5711 return false;
5712 }
5713
5714 sff = SFF_ROOTOK|SFF_REGONLY;
5715 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5716 sff |= SFF_NOWLINK;
5717 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5718 sff |= SFF_SAFEDIRPATH;
5719 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
5720 if (af == NULL)
5721 return false;
5722 readaliases(map, af, false, false);
5723
5724 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
5725 map->map_mtime = st.st_mtime;
5726 (void) sm_io_close(af, SM_TIME_DEFAULT);
5727
5728 return true;
5729}
5730/*
5731** Implicit Modules
5732**
5733** Tries several types. For back compatibility of aliases.
5734*/
5735
5736
5737/*
5738** IMPL_MAP_LOOKUP -- lookup in best open database
5739*/
5740
5741char *
5742impl_map_lookup(map, name, av, pstat)
5743 MAP *map;
5744 char *name;
5745 char **av;
5746 int *pstat;
5747{
5748 if (tTd(38, 20))
5749 sm_dprintf("impl_map_lookup(%s, %s)\n",
5750 map->map_mname, name);
5751
5752#if NEWDB
5753 if (bitset(MF_IMPL_HASH, map->map_mflags))
5754 return db_map_lookup(map, name, av, pstat);
5755#endif /* NEWDB */
5756#if NDBM
5757 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5758 return ndbm_map_lookup(map, name, av, pstat);
5759#endif /* NDBM */
5760 return stab_map_lookup(map, name, av, pstat);
5761}
5762
5763/*
5764** IMPL_MAP_STORE -- store in open databases
5765*/
5766
5767void
5768impl_map_store(map, lhs, rhs)
5769 MAP *map;
5770 char *lhs;
5771 char *rhs;
5772{
5773 if (tTd(38, 12))
5774 sm_dprintf("impl_map_store(%s, %s, %s)\n",
5775 map->map_mname, lhs, rhs);
5776#if NEWDB
5777 if (bitset(MF_IMPL_HASH, map->map_mflags))
5778 db_map_store(map, lhs, rhs);
5779#endif /* NEWDB */
5780#if NDBM
5781 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5782 ndbm_map_store(map, lhs, rhs);
5783#endif /* NDBM */
5784 stab_map_store(map, lhs, rhs);
5785}
5786
5787/*
5788** IMPL_MAP_OPEN -- implicit database open
5789*/
5790
5791bool
5792impl_map_open(map, mode)
5793 MAP *map;
5794 int mode;
5795{
5796 if (tTd(38, 2))
5797 sm_dprintf("impl_map_open(%s, %s, %d)\n",
5798 map->map_mname, map->map_file, mode);
5799
5800 mode &= O_ACCMODE;
5801#if NEWDB
5802 map->map_mflags |= MF_IMPL_HASH;
5803 if (hash_map_open(map, mode))
5804 {
5805# ifdef NDBM_YP_COMPAT
5806 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
5807# endif /* NDBM_YP_COMPAT */
5808 return true;
5809 }
5810 else
5811 map->map_mflags &= ~MF_IMPL_HASH;
5812#endif /* NEWDB */
5813#if NDBM
5814 map->map_mflags |= MF_IMPL_NDBM;
5815 if (ndbm_map_open(map, mode))
5816 {
5817 return true;
5818 }
5819 else
5820 map->map_mflags &= ~MF_IMPL_NDBM;
5821#endif /* NDBM */
5822
5823#if defined(NEWDB) || defined(NDBM)
5824 if (Verbose)
5825 message("WARNING: cannot open alias database %s%s",
5826 map->map_file,
5827 mode == O_RDONLY ? "; reading text version" : "");
5828#else /* defined(NEWDB) || defined(NDBM) */
5829 if (mode != O_RDONLY)
5830 usrerr("Cannot rebuild aliases: no database format defined");
5831#endif /* defined(NEWDB) || defined(NDBM) */
5832
5833 if (mode == O_RDONLY)
5834 return stab_map_open(map, mode);
5835 else
5836 return false;
5837}
5838
5839
5840/*
5841** IMPL_MAP_CLOSE -- close any open database(s)
5842*/
5843
5844void
5845impl_map_close(map)
5846 MAP *map;
5847{
5848 if (tTd(38, 9))
5849 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
5850 map->map_mname, map->map_file, map->map_mflags);
5851#if NEWDB
5852 if (bitset(MF_IMPL_HASH, map->map_mflags))
5853 {
5854 db_map_close(map);
5855 map->map_mflags &= ~MF_IMPL_HASH;
5856 }
5857#endif /* NEWDB */
5858
5859#if NDBM
5860 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5861 {
5862 ndbm_map_close(map);
5863 map->map_mflags &= ~MF_IMPL_NDBM;
5864 }
5865#endif /* NDBM */
5866}
5867/*
5868** User map class.
5869**
5870** Provides access to the system password file.
5871*/
5872
5873/*
5874** USER_MAP_OPEN -- open user map
5875**
5876** Really just binds field names to field numbers.
5877*/
5878
5879bool
5880user_map_open(map, mode)
5881 MAP *map;
5882 int mode;
5883{
5884 if (tTd(38, 2))
5885 sm_dprintf("user_map_open(%s, %d)\n",
5886 map->map_mname, mode);
5887
5888 mode &= O_ACCMODE;
5889 if (mode != O_RDONLY)
5890 {
5891 /* issue a pseudo-error message */
5892 errno = SM_EMAPCANTWRITE;
5893 return false;
5894 }
5895 if (map->map_valcolnm == NULL)
5896 /* EMPTY */
5897 /* nothing */ ;
5898 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
5899 map->map_valcolno = 1;
5900 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
5901 map->map_valcolno = 2;
5902 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
5903 map->map_valcolno = 3;
5904 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
5905 map->map_valcolno = 4;
5906 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
5907 map->map_valcolno = 5;
5908 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
5909 map->map_valcolno = 6;
5910 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
5911 map->map_valcolno = 7;
5912 else
5913 {
5914 syserr("User map %s: unknown column name %s",
5915 map->map_mname, map->map_valcolnm);
5916 return false;
5917 }
5918 return true;
5919}
5920
5921
5922/*
5923** USER_MAP_LOOKUP -- look up a user in the passwd file.
5924*/
5925
5926/* ARGSUSED3 */
5927char *
5928user_map_lookup(map, key, av, statp)
5929 MAP *map;
5930 char *key;
5931 char **av;
5932 int *statp;
5933{
5934 auto bool fuzzy;
5935 SM_MBDB_T user;
5936
5937 if (tTd(38, 20))
5938 sm_dprintf("user_map_lookup(%s, %s)\n",
5939 map->map_mname, key);
5940
5941 *statp = finduser(key, &fuzzy, &user);
5942 if (*statp != EX_OK)
5943 return NULL;
5944 if (bitset(MF_MATCHONLY, map->map_mflags))
5945 return map_rewrite(map, key, strlen(key), NULL);
5946 else
5947 {
5948 char *rwval = NULL;
5949 char buf[30];
5950
5951 switch (map->map_valcolno)
5952 {
5953 case 0:
5954 case 1:
5955 rwval = user.mbdb_name;
5956 break;
5957
5958 case 2:
5959 rwval = "x"; /* passwd no longer supported */
5960 break;
5961
5962 case 3:
5963 (void) sm_snprintf(buf, sizeof buf, "%d",
5964 (int) user.mbdb_uid);
5965 rwval = buf;
5966 break;
5967
5968 case 4:
5969 (void) sm_snprintf(buf, sizeof buf, "%d",
5970 (int) user.mbdb_gid);
5971 rwval = buf;
5972 break;
5973
5974 case 5:
5975 rwval = user.mbdb_fullname;
5976 break;
5977
5978 case 6:
5979 rwval = user.mbdb_homedir;
5980 break;
5981
5982 case 7:
5983 rwval = user.mbdb_shell;
5984 break;
5985 }
5986 return map_rewrite(map, rwval, strlen(rwval), av);
5987 }
5988}
5989/*
5990** Program map type.
5991**
5992** This provides access to arbitrary programs. It should be used
5993** only very sparingly, since there is no way to bound the cost
5994** of invoking an arbitrary program.
5995*/
5996
5997char *
5998prog_map_lookup(map, name, av, statp)
5999 MAP *map;
6000 char *name;
6001 char **av;
6002 int *statp;
6003{
6004 int i;
6005 int save_errno;
6006 int fd;
6007 int status;
6008 auto pid_t pid;
6009 register char *p;
6010 char *rval;
6011 char *argv[MAXPV + 1];
6012 char buf[MAXLINE];
6013
6014 if (tTd(38, 20))
6015 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6016 map->map_mname, name, map->map_file);
6017
6018 i = 0;
6019 argv[i++] = map->map_file;
6020 if (map->map_rebuild != NULL)
6021 {
6022 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
6023 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6024 {
6025 if (i >= MAXPV - 1)
6026 break;
6027 argv[i++] = p;
6028 }
6029 }
6030 argv[i++] = name;
6031 argv[i] = NULL;
6032 if (tTd(38, 21))
6033 {
6034 sm_dprintf("prog_open:");
6035 for (i = 0; argv[i] != NULL; i++)
6036 sm_dprintf(" %s", argv[i]);
6037 sm_dprintf("\n");
6038 }
6039 (void) sm_blocksignal(SIGCHLD);
6040 pid = prog_open(argv, &fd, CurEnv);
6041 if (pid < 0)
6042 {
6043 if (!bitset(MF_OPTIONAL, map->map_mflags))
6044 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6045 map->map_mname, sm_errstring(errno));
6046 else if (tTd(38, 9))
6047 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6048 map->map_mname, sm_errstring(errno));
6049 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6050 *statp = EX_OSFILE;
6051 return NULL;
6052 }
6053 i = read(fd, buf, sizeof buf - 1);
6054 if (i < 0)
6055 {
6056 syserr("prog_map_lookup(%s): read error %s",
6057 map->map_mname, sm_errstring(errno));
6058 rval = NULL;
6059 }
6060 else if (i == 0)
6061 {
6062 if (tTd(38, 20))
6063 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6064 map->map_mname);
6065 rval = NULL;
6066 }
6067 else
6068 {
6069 buf[i] = '\0';
6070 p = strchr(buf, '\n');
6071 if (p != NULL)
6072 *p = '\0';
6073
6074 /* collect the return value */
6075 if (bitset(MF_MATCHONLY, map->map_mflags))
6076 rval = map_rewrite(map, name, strlen(name), NULL);
6077 else
6078 rval = map_rewrite(map, buf, strlen(buf), av);
6079
6080 /* now flush any additional output */
6081 while ((i = read(fd, buf, sizeof buf)) > 0)
6082 continue;
6083 }
6084
6085 /* wait for the process to terminate */
6086 (void) close(fd);
6087 status = waitfor(pid);
6088 save_errno = errno;
6089 (void) sm_releasesignal(SIGCHLD);
6090 errno = save_errno;
6091
6092 if (status == -1)
6093 {
6094 syserr("prog_map_lookup(%s): wait error %s",
6095 map->map_mname, sm_errstring(errno));
6096 *statp = EX_SOFTWARE;
6097 rval = NULL;
6098 }
6099 else if (WIFEXITED(status))
6100 {
6101 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6102 rval = NULL;
6103 }
6104 else
6105 {
6106 syserr("prog_map_lookup(%s): child died on signal %d",
6107 map->map_mname, status);
6108 *statp = EX_UNAVAILABLE;
6109 rval = NULL;
6110 }
6111 return rval;
6112}
6113/*
6114** Sequenced map type.
6115**
6116** Tries each map in order until something matches, much like
6117** implicit. Stores go to the first map in the list that can
6118** support storing.
6119**
6120** This is slightly unusual in that there are two interfaces.
6121** The "sequence" interface lets you stack maps arbitrarily.
6122** The "switch" interface builds a sequence map by looking
6123** at a system-dependent configuration file such as
6124** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6125**
6126** We don't need an explicit open, since all maps are
6127** opened on demand.
6128*/
6129
6130/*
6131** SEQ_MAP_PARSE -- Sequenced map parsing
6132*/
6133
6134bool
6135seq_map_parse(map, ap)
6136 MAP *map;
6137 char *ap;
6138{
6139 int maxmap;
6140
6141 if (tTd(38, 2))
6142 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6143 maxmap = 0;
6144 while (*ap != '\0')
6145 {
6146 register char *p;
6147 STAB *s;
6148
6149 /* find beginning of map name */
6150 while (isascii(*ap) && isspace(*ap))
6151 ap++;
6152 for (p = ap;
6153 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6154 p++)
6155 continue;
6156 if (*p != '\0')
6157 *p++ = '\0';
6158 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6159 p++;
6160 if (*ap == '\0')
6161 {
6162 ap = p;
6163 continue;
6164 }
6165 s = stab(ap, ST_MAP, ST_FIND);
6166 if (s == NULL)
6167 {
6168 syserr("Sequence map %s: unknown member map %s",
6169 map->map_mname, ap);
6170 }
6171 else if (maxmap >= MAXMAPSTACK)
6172 {
6173 syserr("Sequence map %s: too many member maps (%d max)",
6174 map->map_mname, MAXMAPSTACK);
6175 maxmap++;
6176 }
6177 else if (maxmap < MAXMAPSTACK)
6178 {
6179 map->map_stack[maxmap++] = &s->s_map;
6180 }
6181 ap = p;
6182 }
6183 return true;
6184}
6185
6186/*
6187** SWITCH_MAP_OPEN -- open a switched map
6188**
6189** This looks at the system-dependent configuration and builds
6190** a sequence map that does the same thing.
6191**
6192** Every system must define a switch_map_find routine in conf.c
6193** that will return the list of service types associated with a
6194** given service class.
6195*/
6196
6197bool
6198switch_map_open(map, mode)
6199 MAP *map;
6200 int mode;
6201{
6202 int mapno;
6203 int nmaps;
6204 char *maptype[MAXMAPSTACK];
6205
6206 if (tTd(38, 2))
6207 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6208 map->map_mname, map->map_file, mode);
6209
6210 mode &= O_ACCMODE;
6211 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6212 if (tTd(38, 19))
6213 {
6214 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6215 for (mapno = 0; mapno < nmaps; mapno++)
6216 sm_dprintf("\t\t%s\n", maptype[mapno]);
6217 }
6218 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6219 return false;
6220
6221 for (mapno = 0; mapno < nmaps; mapno++)
6222 {
6223 register STAB *s;
6224 char nbuf[MAXNAME + 1];
6225
6226 if (maptype[mapno] == NULL)
6227 continue;
6228 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
6229 map->map_mname, ".", maptype[mapno]);
6230 s = stab(nbuf, ST_MAP, ST_FIND);
6231 if (s == NULL)
6232 {
6233 syserr("Switch map %s: unknown member map %s",
6234 map->map_mname, nbuf);
6235 }
6236 else
6237 {
6238 map->map_stack[mapno] = &s->s_map;
6239 if (tTd(38, 4))
6240 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6241 mapno,
6242 s->s_map.map_class->map_cname,
6243 nbuf);
6244 }
6245 }
6246 return true;
6247}
6248
6249#if 0
6250/*
6251** SEQ_MAP_CLOSE -- close all underlying maps
6252*/
6253
6254void
6255seq_map_close(map)
6256 MAP *map;
6257{
6258 int mapno;
6259
6260 if (tTd(38, 9))
6261 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6262
6263 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6264 {
6265 MAP *mm = map->map_stack[mapno];
6266
6267 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6268 continue;
6269 mm->map_mflags |= MF_CLOSING;
6270 mm->map_class->map_close(mm);
6271 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6272 }
6273}
6274#endif /* 0 */
6275
6276/*
6277** SEQ_MAP_LOOKUP -- sequenced map lookup
6278*/
6279
6280char *
6281seq_map_lookup(map, key, args, pstat)
6282 MAP *map;
6283 char *key;
6284 char **args;
6285 int *pstat;
6286{
6287 int mapno;
6288 int mapbit = 0x01;
6289 bool tempfail = false;
6290
6291 if (tTd(38, 20))
6292 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6293
6294 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6295 {
6296 MAP *mm = map->map_stack[mapno];
6297 char *rv;
6298
6299 if (mm == NULL)
6300 continue;
6301 if (!bitset(MF_OPEN, mm->map_mflags) &&
6302 !openmap(mm))
6303 {
6304 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6305 {
6306 *pstat = EX_UNAVAILABLE;
6307 return NULL;
6308 }
6309 continue;
6310 }
6311 *pstat = EX_OK;
6312 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6313 if (rv != NULL)
6314 return rv;
6315 if (*pstat == EX_TEMPFAIL)
6316 {
6317 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6318 return NULL;
6319 tempfail = true;
6320 }
6321 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6322 break;
6323 }
6324 if (tempfail)
6325 *pstat = EX_TEMPFAIL;
6326 else if (*pstat == EX_OK)
6327 *pstat = EX_NOTFOUND;
6328 return NULL;
6329}
6330
6331/*
6332** SEQ_MAP_STORE -- sequenced map store
6333*/
6334
6335void
6336seq_map_store(map, key, val)
6337 MAP *map;
6338 char *key;
6339 char *val;
6340{
6341 int mapno;
6342
6343 if (tTd(38, 12))
6344 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6345 map->map_mname, key, val);
6346
6347 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6348 {
6349 MAP *mm = map->map_stack[mapno];
6350
6351 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6352 continue;
6353
6354 mm->map_class->map_store(mm, key, val);
6355 return;
6356 }
6357 syserr("seq_map_store(%s, %s, %s): no writable map",
6358 map->map_mname, key, val);
6359}
6360/*
6361** NULL stubs
6362*/
6363
6364/* ARGSUSED */
6365bool
6366null_map_open(map, mode)
6367 MAP *map;
6368 int mode;
6369{
6370 return true;
6371}
6372
6373/* ARGSUSED */
6374void
6375null_map_close(map)
6376 MAP *map;
6377{
6378 return;
6379}
6380
6381char *
6382null_map_lookup(map, key, args, pstat)
6383 MAP *map;
6384 char *key;
6385 char **args;
6386 int *pstat;
6387{
6388 *pstat = EX_NOTFOUND;
6389 return NULL;
6390}
6391
6392/* ARGSUSED */
6393void
6394null_map_store(map, key, val)
6395 MAP *map;
6396 char *key;
6397 char *val;
6398{
6399 return;
6400}
6401
6402/*
6403** BOGUS stubs
6404*/
6405
6406char *
6407bogus_map_lookup(map, key, args, pstat)
6408 MAP *map;
6409 char *key;
6410 char **args;
6411 int *pstat;
6412{
6413 *pstat = EX_TEMPFAIL;
6414 return NULL;
6415}
6416
6417MAPCLASS BogusMapClass =
6418{
6419 "bogus-map", NULL, 0,
6420 NULL, bogus_map_lookup, null_map_store,
6421 null_map_open, null_map_close,
6422};
6423/*
6424** MACRO modules
6425*/
6426
6427char *
6428macro_map_lookup(map, name, av, statp)
6429 MAP *map;
6430 char *name;
6431 char **av;
6432 int *statp;
6433{
6434 int mid;
6435
6436 if (tTd(38, 20))
6437 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6438 name == NULL ? "NULL" : name);
6439
6440 if (name == NULL ||
6441 *name == '\0' ||
6442 (mid = macid(name)) == 0)
6443 {
6444 *statp = EX_CONFIG;
6445 return NULL;
6446 }
6447
6448 if (av[1] == NULL)
6449 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6450 else
6451 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6452
6453 *statp = EX_OK;
6454 return "";
6455}
6456/*
6457** REGEX modules
6458*/
6459
6460#if MAP_REGEX
6461
6462# include <regex.h>
6463
6464# define DEFAULT_DELIM CONDELSE
6465# define END_OF_FIELDS -1
6466# define ERRBUF_SIZE 80
6467# define MAX_MATCH 32
6468
6469# define xnalloc(s) memset(xalloc(s), '\0', s);
6470
6471struct regex_map
6472{
6473 regex_t *regex_pattern_buf; /* xalloc it */
6474 int *regex_subfields; /* move to type MAP */
6475 char *regex_delim; /* move to type MAP */
6476};
6477
6478static int parse_fields __P((char *, int *, int, int));
6479static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6480
6481static int
6482parse_fields(s, ibuf, blen, nr_substrings)
6483 char *s;
6484 int *ibuf; /* array */
6485 int blen; /* number of elements in ibuf */
6486 int nr_substrings; /* number of substrings in the pattern */
6487{
6488 register char *cp;
6489 int i = 0;
6490 bool lastone = false;
6491
6492 blen--; /* for terminating END_OF_FIELDS */
6493 cp = s;
6494 do
6495 {
6496 for (;; cp++)
6497 {
6498 if (*cp == ',')
6499 {
6500 *cp = '\0';
6501 break;
6502 }
6503 if (*cp == '\0')
6504 {
6505 lastone = true;
6506 break;
6507 }
6508 }
6509 if (i < blen)
6510 {
6511 int val = atoi(s);
6512
6513 if (val < 0 || val >= nr_substrings)
6514 {
6515 syserr("field (%d) out of range, only %d substrings in pattern",
6516 val, nr_substrings);
6517 return -1;
6518 }
6519 ibuf[i++] = val;
6520 }
6521 else
6522 {
6523 syserr("too many fields, %d max", blen);
6524 return -1;
6525 }
6526 s = ++cp;
6527 } while (!lastone);
6528 ibuf[i] = END_OF_FIELDS;
6529 return i;
6530}
6531
6532bool
6533regex_map_init(map, ap)
6534 MAP *map;
6535 char *ap;
6536{
6537 int regerr;
6538 struct regex_map *map_p;
6539 register char *p;
6540 char *sub_param = NULL;
6541 int pflags;
6542 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6543
6544 if (tTd(38, 2))
6545 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6546 map->map_mname, ap);
6547
6548 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6549 p = ap;
6550 map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6551 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6552
6553 for (;;)
6554 {
6555 while (isascii(*p) && isspace(*p))
6556 p++;
6557 if (*p != '-')
6558 break;
6559 switch (*++p)
6560 {
6561 case 'n': /* not */
6562 map->map_mflags |= MF_REGEX_NOT;
6563 break;
6564
6565 case 'f': /* case sensitive */
6566 map->map_mflags |= MF_NOFOLDCASE;
6567 pflags &= ~REG_ICASE;
6568 break;
6569
6570 case 'b': /* basic regular expressions */
6571 pflags &= ~REG_EXTENDED;
6572 break;
6573
6574 case 's': /* substring match () syntax */
6575 sub_param = ++p;
6576 pflags &= ~REG_NOSUB;
6577 break;
6578
6579 case 'd': /* delimiter */
6580 map_p->regex_delim = ++p;
6581 break;
6582
6583 case 'a': /* map append */
6584 map->map_app = ++p;
6585 break;
6586
6587 case 'm': /* matchonly */
6588 map->map_mflags |= MF_MATCHONLY;
6589 break;
6590
6591 case 'q':
6592 map->map_mflags |= MF_KEEPQUOTES;
6593 break;
6594
6595 case 'S':
6596 map->map_spacesub = *++p;
6597 break;
6598
6599 case 'D':
6600 map->map_mflags |= MF_DEFER;
6601 break;
6602
6603 }
6604 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6605 p++;
6606 if (*p != '\0')
6607 *p++ = '\0';
6608 }
6609 if (tTd(38, 3))
6610 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6611
6612 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6613 {
6614 /* Errorhandling */
6615 char errbuf[ERRBUF_SIZE];
6616
6617 (void) regerror(regerr, map_p->regex_pattern_buf,
6618 errbuf, sizeof errbuf);
6619 syserr("pattern-compile-error: %s", errbuf);
6620 sm_free(map_p->regex_pattern_buf); /* XXX */
6621 sm_free(map_p); /* XXX */
6622 return false;
6623 }
6624
6625 if (map->map_app != NULL)
6626 map->map_app = newstr(map->map_app);
6627 if (map_p->regex_delim != NULL)
6628 map_p->regex_delim = newstr(map_p->regex_delim);
6629 else
6630 map_p->regex_delim = defdstr;
6631
6632 if (!bitset(REG_NOSUB, pflags))
6633 {
6634 /* substring matching */
6635 int substrings;
6636 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6637
6638 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6639
6640 if (tTd(38, 3))
6641 sm_dprintf("regex_map_init: nr of substrings %d\n",
6642 substrings);
6643
6644 if (substrings >= MAX_MATCH)
6645 {
6646 syserr("too many substrings, %d max", MAX_MATCH);
6647 sm_free(map_p->regex_pattern_buf); /* XXX */
6648 sm_free(map_p); /* XXX */
6649 return false;
6650 }
6651 if (sub_param != NULL && sub_param[0] != '\0')
6652 {
6653 /* optional parameter -sfields */
6654 if (parse_fields(sub_param, fields,
6655 MAX_MATCH + 1, substrings) == -1)
6656 return false;
6657 }
6658 else
6659 {
6660 int i;
6661
6662 /* set default fields */
6663 for (i = 0; i < substrings; i++)
6664 fields[i] = i;
6665 fields[i] = END_OF_FIELDS;
6666 }
6667 map_p->regex_subfields = fields;
6668 if (tTd(38, 3))
6669 {
6670 int *ip;
6671
6672 sm_dprintf("regex_map_init: subfields");
6673 for (ip = fields; *ip != END_OF_FIELDS; ip++)
6674 sm_dprintf(" %d", *ip);
6675 sm_dprintf("\n");
6676 }
6677 }
6678 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
6679 return true;
6680}
6681
6682static char *
6683regex_map_rewrite(map, s, slen, av)
6684 MAP *map;
6685 const char *s;
6686 size_t slen;
6687 char **av;
6688{
6689 if (bitset(MF_MATCHONLY, map->map_mflags))
6690 return map_rewrite(map, av[0], strlen(av[0]), NULL);
6691 else
6692 return map_rewrite(map, s, slen, av);
6693}
6694
6695char *
6696regex_map_lookup(map, name, av, statp)
6697 MAP *map;
6698 char *name;
6699 char **av;
6700 int *statp;
6701{
6702 int reg_res;
6703 struct regex_map *map_p;
6704 regmatch_t pmatch[MAX_MATCH];
6705
6706 if (tTd(38, 20))
6707 {
6708 char **cpp;
6709
6710 sm_dprintf("regex_map_lookup: key '%s'\n", name);
6711 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6712 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
6713 }
6714
6715 map_p = (struct regex_map *)(map->map_db1);
6716 reg_res = regexec(map_p->regex_pattern_buf,
6717 name, MAX_MATCH, pmatch, 0);
6718
6719 if (bitset(MF_REGEX_NOT, map->map_mflags))
6720 {
6721 /* option -n */
6722 if (reg_res == REG_NOMATCH)
6723 return regex_map_rewrite(map, "", (size_t) 0, av);
6724 else
6725 return NULL;
6726 }
6727 if (reg_res == REG_NOMATCH)
6728 return NULL;
6729
6730 if (map_p->regex_subfields != NULL)
6731 {
6732 /* option -s */
6733 static char retbuf[MAXNAME];
6734 int fields[MAX_MATCH + 1];
6735 bool first = true;
6736 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
6737 bool quotemode = false, bslashmode = false;
6738 register char *dp, *sp;
6739 char *endp, *ldp;
6740 int *ip;
6741
6742 dp = retbuf;
6743 ldp = retbuf + sizeof(retbuf) - 1;
6744
6745 if (av[1] != NULL)
6746 {
6747 if (parse_fields(av[1], fields, MAX_MATCH + 1,
6748 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
6749 {
6750 *statp = EX_CONFIG;
6751 return NULL;
6752 }
6753 ip = fields;
6754 }
6755 else
6756 ip = map_p->regex_subfields;
6757
6758 for ( ; *ip != END_OF_FIELDS; ip++)
6759 {
6760 if (!first)
6761 {
6762 for (sp = map_p->regex_delim; *sp; sp++)
6763 {
6764 if (dp < ldp)
6765 *dp++ = *sp;
6766 }
6767 }
6768 else
6769 first = false;
6770
6771 if (*ip >= MAX_MATCH ||
6772 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
6773 continue;
6774
6775 sp = name + pmatch[*ip].rm_so;
6776 endp = name + pmatch[*ip].rm_eo;
6777 for (; endp > sp; sp++)
6778 {
6779 if (dp < ldp)
6780 {
6781 if (bslashmode)
6782 {
6783 *dp++ = *sp;
6784 bslashmode = false;
6785 }
6786 else if (quotemode && *sp != '"' &&
6787 *sp != '\\')
6788 {
6789 *dp++ = *sp;
6790 }
6791 else switch (*dp++ = *sp)
6792 {
6793 case '\\':
6794 bslashmode = true;
6795 break;
6796
6797 case '(':
6798 cmntcnt++;
6799 break;
6800
6801 case ')':
6802 cmntcnt--;
6803 break;
6804
6805 case '<':
6806 anglecnt++;
6807 break;
6808
6809 case '>':
6810 anglecnt--;
6811 break;
6812
6813 case ' ':
6814 spacecnt++;
6815 break;
6816
6817 case '"':
6818 quotemode = !quotemode;
6819 break;
6820 }
6821 }
6822 }
6823 }
6824 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
6825 bslashmode || spacecnt != 0)
6826 {
6827 sm_syslog(LOG_WARNING, NOQID,
6828 "Warning: regex may cause prescan() failure map=%s lookup=%s",
6829 map->map_mname, name);
6830 return NULL;
6831 }
6832
6833 *dp = '\0';
6834
6835 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
6836 }
6837 return regex_map_rewrite(map, "", (size_t)0, av);
6838}
6839#endif /* MAP_REGEX */
6840/*
6841** NSD modules
6842*/
6843#if MAP_NSD
6844
6845# include <ndbm.h>
6846# define _DATUM_DEFINED
6847# include <ns_api.h>
6848
6849typedef struct ns_map_list
6850{
6851 ns_map_t *map; /* XXX ns_ ? */
6852 char *mapname;
6853 struct ns_map_list *next;
6854} ns_map_list_t;
6855
6856static ns_map_t *
6857ns_map_t_find(mapname)
6858 char *mapname;
6859{
6860 static ns_map_list_t *ns_maps = NULL;
6861 ns_map_list_t *ns_map;
6862
6863 /* walk the list of maps looking for the correctly named map */
6864 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
6865 {
6866 if (strcmp(ns_map->mapname, mapname) == 0)
6867 break;
6868 }
6869
6870 /* if we are looking at a NULL ns_map_list_t, then create a new one */
6871 if (ns_map == NULL)
6872 {
6873 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
6874 ns_map->mapname = newstr(mapname);
6875 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6876 memset(ns_map->map, '\0', sizeof *ns_map->map);
6877 ns_map->next = ns_maps;
6878 ns_maps = ns_map;
6879 }
6880 return ns_map->map;
6881}
6882
6883char *
6884nsd_map_lookup(map, name, av, statp)
6885 MAP *map;
6886 char *name;
6887 char **av;
6888 int *statp;
6889{
6890 int buflen, r;
6891 char *p;
6892 ns_map_t *ns_map;
6893 char keybuf[MAXNAME + 1];
6894 char buf[MAXLINE];
6895
6896 if (tTd(38, 20))
6897 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
6898
6899 buflen = strlen(name);
6900 if (buflen > sizeof keybuf - 1)
6901 buflen = sizeof keybuf - 1; /* XXX simply cut off? */
6902 memmove(keybuf, name, buflen);
6903 keybuf[buflen] = '\0';
6904 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
6905 makelower(keybuf);
6906
6907 ns_map = ns_map_t_find(map->map_file);
6908 if (ns_map == NULL)
6909 {
6910 if (tTd(38, 20))
6911 sm_dprintf("nsd_map_t_find failed\n");
6912 *statp = EX_UNAVAILABLE;
6913 return NULL;
6914 }
6915 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
6916 buf, sizeof buf);
6917 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
6918 {
6919 *statp = EX_TEMPFAIL;
6920 return NULL;
6921 }
6922 if (r == NS_BADREQ
6923# ifdef NS_NOPERM
6924 || r == NS_NOPERM
6925# endif /* NS_NOPERM */
6926 )
6927 {
6928 *statp = EX_CONFIG;
6929 return NULL;
6930 }
6931 if (r != NS_SUCCESS)
6932 {
6933 *statp = EX_NOTFOUND;
6934 return NULL;
6935 }
6936
6937 *statp = EX_OK;
6938
6939 /* Null out trailing \n */
6940 if ((p = strchr(buf, '\n')) != NULL)
6941 *p = '\0';
6942
6943 return map_rewrite(map, buf, strlen(buf), av);
6944}
6945#endif /* MAP_NSD */
6946
6947char *
6948arith_map_lookup(map, name, av, statp)
6949 MAP *map;
6950 char *name;
6951 char **av;
6952 int *statp;
6953{
6954 long r;
6955 long v[2];
6956 bool res = false;
6957 bool boolres;
6958 static char result[16];
6959 char **cpp;
6960
6961 if (tTd(38, 2))
6962 {
6963 sm_dprintf("arith_map_lookup: key '%s'\n", name);
6964 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6965 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
6966 }
6967 r = 0;
6968 boolres = false;
6969 cpp = av;
6970 *statp = EX_OK;
6971
6972 /*
6973 ** read arguments for arith map
6974 ** - no check is made whether they are really numbers
6975 ** - just ignores args after the second
6976 */
6977
6978 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
6979 v[r++] = strtol(*cpp, NULL, 0);
6980
6981 /* operator and (at least) two operands given? */
6982 if (name != NULL && r == 2)
6983 {
6984 switch (*name)
6985 {
6986 case '|':
6987 r = v[0] | v[1];
6988 break;
6989
6990 case '&':
6991 r = v[0] & v[1];
6992 break;
6993
6994 case '%':
6995 if (v[1] == 0)
6996 return NULL;
6997 r = v[0] % v[1];
6998 break;
6999 case '+':
7000 r = v[0] + v[1];
7001 break;
7002
7003 case '-':
7004 r = v[0] - v[1];
7005 break;
7006
7007 case '*':
7008 r = v[0] * v[1];
7009 break;
7010
7011 case '/':
7012 if (v[1] == 0)
7013 return NULL;
7014 r = v[0] / v[1];
7015 break;
7016
7017 case 'l':
7018 res = v[0] < v[1];
7019 boolres = true;
7020 break;
7021
7022 case '=':
7023 res = v[0] == v[1];
7024 boolres = true;
7025 break;
7026
7027 default:
7028 /* XXX */
7029 *statp = EX_CONFIG;
7030 if (LogLevel > 10)
7031 sm_syslog(LOG_WARNING, NOQID,
7032 "arith_map: unknown operator %c",
7033 isprint(*name) ? *name : '?');
7034 return NULL;
7035 }
7036 if (boolres)
7037 (void) sm_snprintf(result, sizeof result,
7038 res ? "TRUE" : "FALSE");
7039 else
7040 (void) sm_snprintf(result, sizeof result, "%ld", r);
7041 return result;
7042 }
7043 *statp = EX_CONFIG;
7044 return NULL;
7045}
7046
7047#if SOCKETMAP
7048
7049# if NETINET || NETINET6
7050# include <arpa/inet.h>
7051# endif /* NETINET || NETINET6 */
7052
7053# define socket_map_next map_stack[0]
7054
7055/*
7056** SOCKET_MAP_OPEN -- open socket table
7057*/
7058
7059bool
7060socket_map_open(map, mode)
7061 MAP *map;
7062 int mode;
7063{
7064 STAB *s;
7065 int sock = 0;
7066 SOCKADDR_LEN_T addrlen = 0;
7067 int addrno = 0;
7068 int save_errno;
7069 char *p;
7070 char *colon;
7071 char *at;
7072 struct hostent *hp = NULL;
7073 SOCKADDR addr;
7074
7075 if (tTd(38, 2))
7076 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7077 map->map_mname, map->map_file, mode);
7078
7079 mode &= O_ACCMODE;
7080
7081 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7082 if (mode != O_RDONLY)
7083 {
7084 /* issue a pseudo-error message */
7085 errno = SM_EMAPCANTWRITE;
7086 return false;
7087 }
7088
7089 if (*map->map_file == '\0')
7090 {
7091 syserr("socket map \"%s\": empty or missing socket information",
7092 map->map_mname);
7093 return false;
7094 }
7095
7096 s = socket_map_findconn(map->map_file);
7097 if (s->s_socketmap != NULL)
7098 {
7099 /* Copy open connection */
7100 map->map_db1 = s->s_socketmap->map_db1;
7101
7102 /* Add this map as head of linked list */
7103 map->socket_map_next = s->s_socketmap;
7104 s->s_socketmap = map;
7105
7106 if (tTd(38, 2))
7107 sm_dprintf("using cached connection\n");
7108 return true;
7109 }
7110
7111 if (tTd(38, 2))
7112 sm_dprintf("opening new connection\n");
7113
7114 /* following code is ripped from milter.c */
7115 /* XXX It should be put in a library... */
7116
7117 /* protocol:filename or protocol:port@host */
7118 memset(&addr, '\0', sizeof addr);
7119 p = map->map_file;
7120 colon = strchr(p, ':');
7121 if (colon != NULL)
7122 {
7123 *colon = '\0';
7124
7125 if (*p == '\0')
7126 {
7127# if NETUNIX
7128 /* default to AF_UNIX */
7129 addr.sa.sa_family = AF_UNIX;
7130# else /* NETUNIX */
7131# if NETINET
7132 /* default to AF_INET */
7133 addr.sa.sa_family = AF_INET;
7134# else /* NETINET */
7135# if NETINET6
7136 /* default to AF_INET6 */
7137 addr.sa.sa_family = AF_INET6;
7138# else /* NETINET6 */
7139 /* no protocols available */
7140 syserr("socket map \"%s\": no valid socket protocols available",
7141 map->map_mname);
7142 return false;
7143# endif /* NETINET6 */
7144# endif /* NETINET */
7145# endif /* NETUNIX */
7146 }
7147# if NETUNIX
7148 else if (sm_strcasecmp(p, "unix") == 0 ||
7149 sm_strcasecmp(p, "local") == 0)
7150 addr.sa.sa_family = AF_UNIX;
7151# endif /* NETUNIX */
7152# if NETINET
7153 else if (sm_strcasecmp(p, "inet") == 0)
7154 addr.sa.sa_family = AF_INET;
7155# endif /* NETINET */
7156# if NETINET6
7157 else if (sm_strcasecmp(p, "inet6") == 0)
7158 addr.sa.sa_family = AF_INET6;
7159# endif /* NETINET6 */
7160 else
7161 {
7162# ifdef EPROTONOSUPPORT
7163 errno = EPROTONOSUPPORT;
7164# else /* EPROTONOSUPPORT */
7165 errno = EINVAL;
7166# endif /* EPROTONOSUPPORT */
7167 syserr("socket map \"%s\": unknown socket type %s",
7168 map->map_mname, p);
7169 return false;
7170 }
7171 *colon++ = ':';
7172 }
7173 else
7174 {
7175 colon = p;
7176#if NETUNIX
7177 /* default to AF_UNIX */
7178 addr.sa.sa_family = AF_UNIX;
7179#else /* NETUNIX */
7180# if NETINET
7181 /* default to AF_INET */
7182 addr.sa.sa_family = AF_INET;
7183# else /* NETINET */
7184# if NETINET6
7185 /* default to AF_INET6 */
7186 addr.sa.sa_family = AF_INET6;
7187# else /* NETINET6 */
7188 syserr("socket map \"%s\": unknown socket type %s",
7189 map->map_mname, p);
7190 return false;
7191# endif /* NETINET6 */
7192# endif /* NETINET */
7193#endif /* NETUNIX */
7194 }
7195
7196# if NETUNIX
7197 if (addr.sa.sa_family == AF_UNIX)
7198 {
7199 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7200
7201 at = colon;
7202 if (strlen(colon) >= sizeof addr.sunix.sun_path)
7203 {
7204 syserr("socket map \"%s\": local socket name %s too long",
7205 map->map_mname, colon);
7206 return false;
7207 }
7208 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7209 S_IRUSR|S_IWUSR, NULL);
7210
7211 if (errno != 0)
7212 {
7213 /* if not safe, don't create */
7214 syserr("socket map \"%s\": local socket name %s unsafe",
7215 map->map_mname, colon);
7216 return false;
7217 }
7218
7219 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7220 sizeof addr.sunix.sun_path);
7221 addrlen = sizeof (struct sockaddr_un);
7222 }
7223 else
7224# endif /* NETUNIX */
7225# if NETINET || NETINET6
7226 if (false
7227# if NETINET
7228 || addr.sa.sa_family == AF_INET
7229# endif /* NETINET */
7230# if NETINET6
7231 || addr.sa.sa_family == AF_INET6
7232# endif /* NETINET6 */
7233 )
7234 {
7235 unsigned short port;
7236
7237 /* Parse port@host */
7238 at = strchr(colon, '@');
7239 if (at == NULL)
7240 {
7241 syserr("socket map \"%s\": bad address %s (expected port@host)",
7242 map->map_mname, colon);
7243 return false;
7244 }
7245 *at = '\0';
7246 if (isascii(*colon) && isdigit(*colon))
7247 port = htons((unsigned short) atoi(colon));
7248 else
7249 {
7250# ifdef NO_GETSERVBYNAME
7251 syserr("socket map \"%s\": invalid port number %s",
7252 map->map_mname, colon);
7253 return false;
7254# else /* NO_GETSERVBYNAME */
7255 register struct servent *sp;
7256
7257 sp = getservbyname(colon, "tcp");
7258 if (sp == NULL)
7259 {
7260 syserr("socket map \"%s\": unknown port name %s",
7261 map->map_mname, colon);
7262 return false;
7263 }
7264 port = sp->s_port;
7265# endif /* NO_GETSERVBYNAME */
7266 }
7267 *at++ = '@';
7268 if (*at == '[')
7269 {
7270 char *end;
7271
7272 end = strchr(at, ']');
7273 if (end != NULL)
7274 {
7275 bool found = false;
7276# if NETINET
7277 unsigned long hid = INADDR_NONE;
7278# endif /* NETINET */
7279# if NETINET6
7280 struct sockaddr_in6 hid6;
7281# endif /* NETINET6 */
7282
7283 *end = '\0';
7284# if NETINET
7285 if (addr.sa.sa_family == AF_INET &&
7286 (hid = inet_addr(&at[1])) != INADDR_NONE)
7287 {
7288 addr.sin.sin_addr.s_addr = hid;
7289 addr.sin.sin_port = port;
7290 found = true;
7291 }
7292# endif /* NETINET */
7293# if NETINET6
7294 (void) memset(&hid6, '\0', sizeof hid6);
7295 if (addr.sa.sa_family == AF_INET6 &&
7296 anynet_pton(AF_INET6, &at[1],
7297 &hid6.sin6_addr) == 1)
7298 {
7299 addr.sin6.sin6_addr = hid6.sin6_addr;
7300 addr.sin6.sin6_port = port;
7301 found = true;
7302 }
7303# endif /* NETINET6 */
7304 *end = ']';
7305 if (!found)
7306 {
7307 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7308 map->map_mname, at);
7309 return false;
7310 }
7311 }
7312 else
7313 {
7314 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7315 map->map_mname, at);
7316 return false;
7317 }
7318 }
7319 else
7320 {
7321 hp = sm_gethostbyname(at, addr.sa.sa_family);
7322 if (hp == NULL)
7323 {
7324 syserr("socket map \"%s\": Unknown host name %s",
7325 map->map_mname, at);
7326 return false;
7327 }
7328 addr.sa.sa_family = hp->h_addrtype;
7329 switch (hp->h_addrtype)
7330 {
7331# if NETINET
7332 case AF_INET:
7333 memmove(&addr.sin.sin_addr,
7334 hp->h_addr, INADDRSZ);
7335 addr.sin.sin_port = port;
7336 addrlen = sizeof (struct sockaddr_in);
7337 addrno = 1;
7338 break;
7339# endif /* NETINET */
7340
7341# if NETINET6
7342 case AF_INET6:
7343 memmove(&addr.sin6.sin6_addr,
7344 hp->h_addr, IN6ADDRSZ);
7345 addr.sin6.sin6_port = port;
7346 addrlen = sizeof (struct sockaddr_in6);
7347 addrno = 1;
7348 break;
7349# endif /* NETINET6 */
7350
7351 default:
7352 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7353 map->map_mname, at, hp->h_addrtype);
7354# if NETINET6
7355 freehostent(hp);
7356# endif /* NETINET6 */
7357 return false;
7358 }
7359 }
7360 }
7361 else
7362# endif /* NETINET || NETINET6 */
7363 {
7364 syserr("socket map \"%s\": unknown socket protocol",
7365 map->map_mname);
7366 return false;
7367 }
7368
7369 /* nope, actually connecting */
7370 for (;;)
7371 {
7372 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7373 if (sock < 0)
7374 {
7375 save_errno = errno;
7376 if (tTd(38, 5))
7377 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7378 map->map_mname,
7379 sm_errstring(save_errno));
7380# if NETINET6
7381 if (hp != NULL)
7382 freehostent(hp);
7383# endif /* NETINET6 */
7384 return false;
7385 }
7386
7387 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7388 break;
7389
7390 /* couldn't connect.... try next address */
7391 save_errno = errno;
7392 p = CurHostName;
7393 CurHostName = at;
7394 if (tTd(38, 5))
7395 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7396 map->map_mname, at, sm_errstring(save_errno));
7397 CurHostName = p;
7398 (void) close(sock);
7399
7400 /* try next address */
7401 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7402 {
7403 switch (addr.sa.sa_family)
7404 {
7405# if NETINET
7406 case AF_INET:
7407 memmove(&addr.sin.sin_addr,
7408 hp->h_addr_list[addrno++],
7409 INADDRSZ);
7410 break;
7411# endif /* NETINET */
7412
7413# if NETINET6
7414 case AF_INET6:
7415 memmove(&addr.sin6.sin6_addr,
7416 hp->h_addr_list[addrno++],
7417 IN6ADDRSZ);
7418 break;
7419# endif /* NETINET6 */
7420
7421 default:
7422 if (tTd(38, 5))
7423 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7424 map->map_mname, at,
7425 hp->h_addrtype);
7426# if NETINET6
7427 freehostent(hp);
7428# endif /* NETINET6 */
7429 return false;
7430 }
7431 continue;
7432 }
7433 p = CurHostName;
7434 CurHostName = at;
7435 if (tTd(38, 5))
7436 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7437 map->map_mname, sm_errstring(save_errno));
7438 CurHostName = p;
7439# if NETINET6
7440 if (hp != NULL)
7441 freehostent(hp);
7442# endif /* NETINET6 */
7443 return false;
7444 }
7445# if NETINET6
7446 if (hp != NULL)
7447 {
7448 freehostent(hp);
7449 hp = NULL;
7450 }
7451# endif /* NETINET6 */
7452 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7453 SM_TIME_DEFAULT,
7454 (void *) &sock,
7455 SM_IO_RDWR,
7456 NULL)) == NULL)
7457 {
7458 close(sock);
7459 if (tTd(38, 2))
7460 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7461 map->map_mname, sm_errstring(errno));
7462 return false;
7463 }
7464
7465 /* Save connection for reuse */
7466 s->s_socketmap = map;
7467 return true;
7468}
7469
7470/*
7471** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7472**
7473** Cache SOCKET connections based on the connection specifier
7474** and PID so we don't have multiple connections open to
7475** the same server for different maps. Need a separate connection
7476** per PID since a parent process may close the map before the
7477** child is done with it.
7478**
7479** Parameters:
7480** conn -- SOCKET map connection specifier
7481**
7482** Returns:
7483** Symbol table entry for the SOCKET connection.
7484*/
7485
7486static STAB *
7487socket_map_findconn(conn)
7488 const char *conn;
7489{
7490 char *nbuf;
7491 STAB *SM_NONVOLATILE s = NULL;
7492
7493 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7494 SM_TRY
7495 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7496 SM_FINALLY
7497 sm_free(nbuf);
7498 SM_END_TRY
7499 return s;
7500}
7501
7502/*
7503** SOCKET_MAP_CLOSE -- close the socket
7504*/
7505
7506void
7507socket_map_close(map)
7508 MAP *map;
7509{
7510 STAB *s;
7511 MAP *smap;
7512
7513 if (tTd(38, 20))
7514 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7515 (long) CurrentPid);
7516
7517 /* Check if already closed */
7518 if (map->map_db1 == NULL)
7519 {
7520 if (tTd(38, 20))
7521 sm_dprintf("socket_map_close(%s) already closed\n",
7522 map->map_file);
7523 return;
7524 }
7525 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7526
7527 /* Mark all the maps that share the connection as closed */
7528 s = socket_map_findconn(map->map_file);
7529 smap = s->s_socketmap;
7530 while (smap != NULL)
7531 {
7532 MAP *next;
7533
7534 if (tTd(38, 2) && smap != map)
7535 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7536 map->map_mname, smap->map_mname);
7537
7538 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7539 smap->map_db1 = NULL;
7540 next = smap->socket_map_next;
7541 smap->socket_map_next = NULL;
7542 smap = next;
7543 }
7544 s->s_socketmap = NULL;
7545}
7546
7547/*
7548** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7549*/
7550
7551char *
7552socket_map_lookup(map, name, av, statp)
7553 MAP *map;
7554 char *name;
7555 char **av;
7556 int *statp;
7557{
7558 unsigned int nettolen, replylen, recvlen;
7557 char *replybuf, *rval, *value, *status;
7559 char *replybuf, *rval, *value, *status, *key;
7558 SM_FILE_T *f;
7560 SM_FILE_T *f;
7561 char keybuf[MAXNAME + 1];
7559
7560 replybuf = NULL;
7561 rval = NULL;
7562 f = (SM_FILE_T *)map->map_db1;
7563 if (tTd(38, 20))
7564 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7565 map->map_mname, name, map->map_file);
7566
7562
7563 replybuf = NULL;
7564 rval = NULL;
7565 f = (SM_FILE_T *)map->map_db1;
7566 if (tTd(38, 20))
7567 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7568 map->map_mname, name, map->map_file);
7569
7567 nettolen = strlen(map->map_mname) + 1 + strlen(name);
7570 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7571 {
7572 nettolen = strlen(name);
7573 if (nettolen > sizeof keybuf - 1)
7574 nettolen = sizeof keybuf - 1;
7575 memmove(keybuf, name, nettolen);
7576 keybuf[nettolen] = '\0';
7577 makelower(keybuf);
7578 key = keybuf;
7579 }
7580 else
7581 key = name;
7582
7583 nettolen = strlen(map->map_mname) + 1 + strlen(key);
7568 SM_ASSERT(nettolen > strlen(map->map_mname));
7584 SM_ASSERT(nettolen > strlen(map->map_mname));
7569 SM_ASSERT(nettolen > strlen(name));
7585 SM_ASSERT(nettolen > strlen(key));
7570 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7586 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7571 nettolen, map->map_mname, name) == SM_IO_EOF) ||
7587 nettolen, map->map_mname, key) == SM_IO_EOF) ||
7572 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7573 (sm_io_error(f)))
7574 {
7575 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7576 map->map_mname);
7577 *statp = EX_TEMPFAIL;
7578 goto errcl;
7579 }
7580
7581 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7582 {
7583 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7584 map->map_mname);
7585 *statp = EX_TEMPFAIL;
7586 goto errcl;
7587 }
7588 if (replylen > SOCKETMAP_MAXL)
7589 {
7590 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7591 map->map_mname, replylen);
7592 *statp = EX_TEMPFAIL;
7593 goto errcl;
7594 }
7595 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7596 {
7597 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7598 map->map_mname);
7599 *statp = EX_TEMPFAIL;
7600 goto error;
7601 }
7602
7603 replybuf = (char *) sm_malloc(replylen + 1);
7604 if (replybuf == NULL)
7605 {
7606 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7607 map->map_mname, replylen + 1);
7608 *statp = EX_OSERR;
7609 goto error;
7610 }
7611
7612 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7613 if (recvlen < replylen)
7614 {
7615 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7616 map->map_mname, recvlen, replylen);
7617 *statp = EX_TEMPFAIL;
7618 goto errcl;
7619 }
7620 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7621 {
7622 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7623 map->map_mname);
7624 *statp = EX_TEMPFAIL;
7625 goto errcl;
7626 }
7627 status = replybuf;
7628 replybuf[recvlen] = '\0';
7629 value = strchr(replybuf, ' ');
7630 if (value != NULL)
7631 {
7632 *value = '\0';
7633 value++;
7634 }
7635 if (strcmp(status, "OK") == 0)
7636 {
7637 *statp = EX_OK;
7638
7639 /* collect the return value */
7640 if (bitset(MF_MATCHONLY, map->map_mflags))
7588 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7589 (sm_io_error(f)))
7590 {
7591 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7592 map->map_mname);
7593 *statp = EX_TEMPFAIL;
7594 goto errcl;
7595 }
7596
7597 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7598 {
7599 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7600 map->map_mname);
7601 *statp = EX_TEMPFAIL;
7602 goto errcl;
7603 }
7604 if (replylen > SOCKETMAP_MAXL)
7605 {
7606 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7607 map->map_mname, replylen);
7608 *statp = EX_TEMPFAIL;
7609 goto errcl;
7610 }
7611 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7612 {
7613 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7614 map->map_mname);
7615 *statp = EX_TEMPFAIL;
7616 goto error;
7617 }
7618
7619 replybuf = (char *) sm_malloc(replylen + 1);
7620 if (replybuf == NULL)
7621 {
7622 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7623 map->map_mname, replylen + 1);
7624 *statp = EX_OSERR;
7625 goto error;
7626 }
7627
7628 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7629 if (recvlen < replylen)
7630 {
7631 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7632 map->map_mname, recvlen, replylen);
7633 *statp = EX_TEMPFAIL;
7634 goto errcl;
7635 }
7636 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7637 {
7638 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7639 map->map_mname);
7640 *statp = EX_TEMPFAIL;
7641 goto errcl;
7642 }
7643 status = replybuf;
7644 replybuf[recvlen] = '\0';
7645 value = strchr(replybuf, ' ');
7646 if (value != NULL)
7647 {
7648 *value = '\0';
7649 value++;
7650 }
7651 if (strcmp(status, "OK") == 0)
7652 {
7653 *statp = EX_OK;
7654
7655 /* collect the return value */
7656 if (bitset(MF_MATCHONLY, map->map_mflags))
7641 rval = map_rewrite(map, name, strlen(name), NULL);
7657 rval = map_rewrite(map, key, strlen(key), NULL);
7642 else
7643 rval = map_rewrite(map, value, strlen(value), av);
7644 }
7645 else if (strcmp(status, "NOTFOUND") == 0)
7646 {
7647 *statp = EX_NOTFOUND;
7648 if (tTd(38, 20))
7649 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7658 else
7659 rval = map_rewrite(map, value, strlen(value), av);
7660 }
7661 else if (strcmp(status, "NOTFOUND") == 0)
7662 {
7663 *statp = EX_NOTFOUND;
7664 if (tTd(38, 20))
7665 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7650 map->map_mname, name);
7666 map->map_mname, key);
7651 }
7652 else
7653 {
7654 if (tTd(38, 5))
7655 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7667 }
7668 else
7669 {
7670 if (tTd(38, 5))
7671 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7656 map->map_mname, name, status,
7672 map->map_mname, key, status,
7657 value ? value : "");
7658 if ((strcmp(status, "TEMP") == 0) ||
7659 (strcmp(status, "TIMEOUT") == 0))
7660 *statp = EX_TEMPFAIL;
7661 else if(strcmp(status, "PERM") == 0)
7662 *statp = EX_UNAVAILABLE;
7663 else
7664 *statp = EX_PROTOCOL;
7665 }
7666
7667 if (replybuf != NULL)
7668 sm_free(replybuf);
7669 return rval;
7670
7671 errcl:
7672 socket_map_close(map);
7673 error:
7674 if (replybuf != NULL)
7675 sm_free(replybuf);
7676 return rval;
7677}
7678#endif /* SOCKETMAP */
7673 value ? value : "");
7674 if ((strcmp(status, "TEMP") == 0) ||
7675 (strcmp(status, "TIMEOUT") == 0))
7676 *statp = EX_TEMPFAIL;
7677 else if(strcmp(status, "PERM") == 0)
7678 *statp = EX_UNAVAILABLE;
7679 else
7680 *statp = EX_PROTOCOL;
7681 }
7682
7683 if (replybuf != NULL)
7684 sm_free(replybuf);
7685 return rval;
7686
7687 errcl:
7688 socket_map_close(map);
7689 error:
7690 if (replybuf != NULL)
7691 sm_free(replybuf);
7692 return rval;
7693}
7694#endif /* SOCKETMAP */