Default kdump's data limit to 64 bytes and document how it can be disabled.
[dragonfly.git] / contrib / sendmail-8.13.7 / sendmail / map.c
1 /*
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
16 SM_RCSID("@(#)$Id: map.c,v 8.672 2006/04/18 01:26:41 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
44 static bool     db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
45 # endif /* DB_VERSION_MAJOR < 2 */
46 # if DB_VERSION_MAJOR == 2
47 static bool     db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
48 # endif /* DB_VERSION_MAJOR == 2 */
49 # if DB_VERSION_MAJOR > 2
50 static bool     db_map_open __P((MAP *, int, char *, DBTYPE, void **));
51 # endif /* DB_VERSION_MAJOR > 2 */
52 #endif /* NEWDB */
53 static bool     extract_canonname __P((char *, char *, char *, char[], int));
54 static void     map_close __P((STAB *, int));
55 static void     map_init __P((STAB *, int));
56 #ifdef LDAPMAP
57 static STAB *   ldapmap_findconn __P((SM_LDAP_STRUCT *));
58 #endif /* LDAPMAP */
59 #if NISPLUS
60 static bool     nisplus_getcanonname __P((char *, int, int *));
61 #endif /* NISPLUS */
62 #if NIS
63 static bool     nis_getcanonname __P((char *, int, int *));
64 #endif /* NIS */
65 #if NETINFO
66 static bool     ni_getcanonname __P((char *, int, int *));
67 #endif /* NETINFO */
68 static bool     text_getcanonname __P((char *, int, int *));
69 #if SOCKETMAP
70 static 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
150 bool
151 map_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
314 char *
315 map_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
436 void
437 initmaps()
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 */
462 static void
463 map_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
513 bool
514 openmap(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
599 void
600 closemaps(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 */
617 static void
618 map_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
676 bool
677 getcanonname(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
818 static bool
819 extract_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
891 bool
892 dns_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
933 struct dns_map
934 {
935         int dns_m_type;
936 };
937
938 bool
939 dns_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
1129 char *
1130 dns_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
1352 bool
1353 ndbm_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
1615 char *
1616 ndbm_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         }
1642 lockdbm:
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
1713 void
1714 ndbm_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
1794 void
1795 ndbm_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
1873 bool
1874 bt_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
1902 bool
1903 hash_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
1934 static bool
1935 db_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
2244 char *
2245 db_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
2408 void
2409 db_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
2516 void
2517 db_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
2586 bool
2587 nis_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 */
2672 char *
2673 nis_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
2740 static bool
2741 nis_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
2846 bool
2847 nisplus_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
2985 char *
2986 nisplus_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
3125 static bool
3126 nisplus_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
3240 char *
3241 nisplus_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
3269 static char *ldapmap_dequote __P((char *));
3270
3271 static char *
3272 ldapmap_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
3299 static 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
3309 bool
3310 ldapmap_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
3405 void
3406 ldapmap_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
3448 static char *
3449 sunet_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
3480 char *
3481 ldapmap_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[MAXKEY];
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 # if _FFR_LDAP_SINGLEDN
3555         if (bitset(MF_SINGLEDN, map->map_mflags))
3556                 flags |= SM_LDAP_SINGLEDN;
3557 # endif /* _FFR_LDAP_SINGLEDN */
3558
3559         /* Create an rpool for search related memory usage */
3560         rpool = sm_rpool_new_x(NULL);
3561
3562         p = NULL;
3563         *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3564                                  rpool, &p, &plen, &psize, NULL);
3565         save_errno = errno;
3566
3567         /* Copy result so rpool can be freed */
3568         if (*statp == EX_OK && p != NULL)
3569                 vp = newstr(p);
3570         sm_rpool_free(rpool);
3571
3572         /* need to restart LDAP connection? */
3573         if (*statp == EX_RESTART)
3574         {
3575                 *statp = EX_TEMPFAIL;
3576                 ldapmap_close(map);
3577         }
3578
3579         errno = save_errno;
3580         if (*statp != EX_OK && *statp != EX_NOTFOUND)
3581         {
3582                 if (!bitset(MF_OPTIONAL, map->map_mflags))
3583                 {
3584                         if (bitset(MF_NODEFER, map->map_mflags))
3585                                 syserr("Error getting LDAP results in map %s",
3586                                        map->map_mname);
3587                         else
3588                                 syserr("451 4.3.5 Error getting LDAP results in map %s",
3589                                        map->map_mname);
3590                 }
3591                 errno = save_errno;
3592                 return NULL;
3593         }
3594
3595         /* Did we match anything? */
3596         if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3597                 return NULL;
3598
3599         if (*statp == EX_OK)
3600         {
3601                 if (LogLevel > 9)
3602                         sm_syslog(LOG_INFO, CurEnv->e_id,
3603                                   "ldap %.100s => %s", name,
3604                                   vp == NULL ? "<NULL>" : vp);
3605                 if (bitset(MF_MATCHONLY, map->map_mflags))
3606                         result = map_rewrite(map, name, strlen(name), NULL);
3607                 else
3608                 {
3609                         /* vp != NULL according to test above */
3610                         result = map_rewrite(map, vp, strlen(vp), av);
3611                 }
3612                 if (vp != NULL)
3613                         sm_free(vp); /* XXX */
3614         }
3615         return result;
3616 }
3617
3618 /*
3619 **  LDAPMAP_FINDCONN -- find an LDAP connection to the server
3620 **
3621 **      Cache LDAP connections based on the host, port, bind DN,
3622 **      secret, and PID so we don't have multiple connections open to
3623 **      the same server for different maps.  Need a separate connection
3624 **      per PID since a parent process may close the map before the
3625 **      child is done with it.
3626 **
3627 **      Parameters:
3628 **              lmap -- LDAP map information
3629 **
3630 **      Returns:
3631 **              Symbol table entry for the LDAP connection.
3632 */
3633
3634 static STAB *
3635 ldapmap_findconn(lmap)
3636         SM_LDAP_STRUCT *lmap;
3637 {
3638         char *format;
3639         char *nbuf;
3640         char *id;
3641         STAB *SM_NONVOLATILE s = NULL;
3642
3643         if (lmap->ldap_host != NULL)
3644                 id = lmap->ldap_host;
3645         else if (lmap->ldap_uri != NULL)
3646                 id = lmap->ldap_uri;
3647         else
3648                 id = "localhost";
3649
3650         format = "%s%c%d%c%d%c%s%c%s%d";
3651         nbuf = sm_stringf_x(format,
3652                             id,
3653                             CONDELSE,
3654                             lmap->ldap_port,
3655                             CONDELSE,
3656                             lmap->ldap_version,
3657                             CONDELSE,
3658                             (lmap->ldap_binddn == NULL ? ""
3659                                                        : lmap->ldap_binddn),
3660                             CONDELSE,
3661                             (lmap->ldap_secret == NULL ? ""
3662                                                        : lmap->ldap_secret),
3663                             (int) CurrentPid);
3664         SM_TRY
3665                 s = stab(nbuf, ST_LMAP, ST_ENTER);
3666         SM_FINALLY
3667                 sm_free(nbuf);
3668         SM_END_TRY
3669         return s;
3670 }
3671 /*
3672 **  LDAPMAP_PARSEARGS -- parse ldap map definition args.
3673 */
3674
3675 static struct lamvalues LDAPAuthMethods[] =
3676 {
3677         {       "none",         LDAP_AUTH_NONE          },
3678         {       "simple",       LDAP_AUTH_SIMPLE        },
3679 # ifdef LDAP_AUTH_KRBV4
3680         {       "krbv4",        LDAP_AUTH_KRBV4         },
3681 # endif /* LDAP_AUTH_KRBV4 */
3682         {       NULL,           0                       }
3683 };
3684
3685 static struct ladvalues LDAPAliasDereference[] =
3686 {
3687         {       "never",        LDAP_DEREF_NEVER        },
3688         {       "always",       LDAP_DEREF_ALWAYS       },
3689         {       "search",       LDAP_DEREF_SEARCHING    },
3690         {       "find",         LDAP_DEREF_FINDING      },
3691         {       NULL,           0                       }
3692 };
3693
3694 static struct lssvalues LDAPSearchScope[] =
3695 {
3696         {       "base",         LDAP_SCOPE_BASE         },
3697         {       "one",          LDAP_SCOPE_ONELEVEL     },
3698         {       "sub",          LDAP_SCOPE_SUBTREE      },
3699         {       NULL,           0                       }
3700 };
3701
3702 bool
3703 ldapmap_parseargs(map, args)
3704         MAP *map;
3705         char *args;
3706 {
3707         bool secretread = true;
3708         bool attrssetup = false;
3709         int i;
3710         register char *p = args;
3711         SM_LDAP_STRUCT *lmap;
3712         struct lamvalues *lam;
3713         struct ladvalues *lad;
3714         struct lssvalues *lss;
3715         char ldapfilt[MAXLINE];
3716         char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3717
3718         /* Get ldap struct pointer from map */
3719         lmap = (SM_LDAP_STRUCT *) map->map_db1;
3720
3721         /* Check if setting the initial LDAP defaults */
3722         if (lmap == NULL || lmap != LDAPDefaults)
3723         {
3724                 /* We need to alloc an SM_LDAP_STRUCT struct */
3725                 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
3726                 if (LDAPDefaults == NULL)
3727                         sm_ldap_clear(lmap);
3728                 else
3729                         STRUCTCOPY(*LDAPDefaults, *lmap);
3730         }
3731
3732         /* there is no check whether there is really an argument */
3733         map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3734         map->map_spacesub = SpaceSub;   /* default value */
3735
3736         /* Check if setting up an alias or file class LDAP map */
3737         if (bitset(MF_ALIAS, map->map_mflags))
3738         {
3739                 /* Comma separate if used as an alias file */
3740                 map->map_coldelim = ',';
3741                 if (*args == '\0')
3742                 {
3743                         int n;
3744                         char *lc;
3745                         char jbuf[MAXHOSTNAMELEN];
3746                         char lcbuf[MAXLINE];
3747
3748                         /* Get $j */
3749                         expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
3750                         if (jbuf[0] == '\0')
3751                         {
3752                                 (void) sm_strlcpy(jbuf, "localhost",
3753                                                   sizeof jbuf);
3754                         }
3755
3756                         lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3757                         if (lc == NULL)
3758                                 lc = "";
3759                         else
3760                         {
3761                                 expand(lc, lcbuf, sizeof lcbuf, CurEnv);
3762                                 lc = lcbuf;
3763                         }
3764
3765                         n = sm_snprintf(ldapfilt, sizeof ldapfilt,
3766                                         "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3767                                         lc, jbuf);
3768                         if (n >= sizeof ldapfilt)
3769                         {
3770                                 syserr("%s: Default LDAP string too long",
3771                                        map->map_mname);
3772                                 return false;
3773                         }
3774
3775                         /* default args for an alias LDAP entry */
3776                         lmap->ldap_filter = ldapfilt;
3777                         lmap->ldap_attr[0] = "objectClass";
3778                         lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3779                         lmap->ldap_attr_needobjclass[0] = NULL;
3780                         lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3781                         lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3782                         lmap->ldap_attr_needobjclass[1] = NULL;
3783                         lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3784                         lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3785                         lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3786                         lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3787                         lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3788                         lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3789                         lmap->ldap_attr[4] = NULL;
3790                         lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3791                         lmap->ldap_attr_needobjclass[4] = NULL;
3792                         attrssetup = true;
3793                 }
3794         }
3795         else if (bitset(MF_FILECLASS, map->map_mflags))
3796         {
3797                 /* Space separate if used as a file class file */
3798                 map->map_coldelim = ' ';
3799         }
3800
3801         for (;;)
3802         {
3803                 while (isascii(*p) && isspace(*p))
3804                         p++;
3805                 if (*p != '-')
3806                         break;
3807                 switch (*++p)
3808                 {
3809                   case 'N':
3810                         map->map_mflags |= MF_INCLNULL;
3811                         map->map_mflags &= ~MF_TRY0NULL;
3812                         break;
3813
3814                   case 'O':
3815                         map->map_mflags &= ~MF_TRY1NULL;
3816                         break;
3817
3818                   case 'o':
3819                         map->map_mflags |= MF_OPTIONAL;
3820                         break;
3821
3822                   case 'f':
3823                         map->map_mflags |= MF_NOFOLDCASE;
3824                         break;
3825
3826                   case 'm':
3827                         map->map_mflags |= MF_MATCHONLY;
3828                         break;
3829
3830                   case 'A':
3831                         map->map_mflags |= MF_APPEND;
3832                         break;
3833
3834                   case 'q':
3835                         map->map_mflags |= MF_KEEPQUOTES;
3836                         break;
3837
3838                   case 'a':
3839                         map->map_app = ++p;
3840                         break;
3841
3842                   case 'T':
3843                         map->map_tapp = ++p;
3844                         break;
3845
3846                   case 't':
3847                         map->map_mflags |= MF_NODEFER;
3848                         break;
3849
3850                   case 'S':
3851                         map->map_spacesub = *++p;
3852                         break;
3853
3854                   case 'D':
3855                         map->map_mflags |= MF_DEFER;
3856                         break;
3857
3858                   case 'z':
3859                         if (*++p != '\\')
3860                                 map->map_coldelim = *p;
3861                         else
3862                         {
3863                                 switch (*++p)
3864                                 {
3865                                   case 'n':
3866                                         map->map_coldelim = '\n';
3867                                         break;
3868
3869                                   case 't':
3870                                         map->map_coldelim = '\t';
3871                                         break;
3872
3873                                   default:
3874                                         map->map_coldelim = '\\';
3875                                 }
3876                         }
3877                         break;
3878
3879                         /* Start of ldapmap specific args */
3880                   case 'V':
3881                         if (*++p != '\\')
3882                                 lmap->ldap_attrsep = *p;
3883                         else
3884                         {
3885                                 switch (*++p)
3886                                 {
3887                                   case 'n':
3888                                         lmap->ldap_attrsep = '\n';
3889                                         break;
3890
3891                                   case 't':
3892                                         lmap->ldap_attrsep = '\t';
3893                                         break;
3894
3895                                   default:
3896                                         lmap->ldap_attrsep = '\\';
3897                                 }
3898                         }
3899                         break;
3900
3901                   case 'k':             /* search field */
3902                         while (isascii(*++p) && isspace(*p))
3903                                 continue;
3904                         lmap->ldap_filter = p;
3905                         break;
3906
3907                   case 'v':             /* attr to return */
3908                         while (isascii(*++p) && isspace(*p))
3909                                 continue;
3910                         lmap->ldap_attr[0] = p;
3911                         lmap->ldap_attr[1] = NULL;
3912                         break;
3913
3914                   case '1':
3915                         map->map_mflags |= MF_SINGLEMATCH;
3916                         break;
3917
3918 # if _FFR_LDAP_SINGLEDN
3919                   case '2':
3920                         map->map_mflags |= MF_SINGLEDN;
3921                         break;
3922 # endif /* _FFR_LDAP_SINGLEDN */
3923
3924                         /* args stolen from ldapsearch.c */
3925                   case 'R':             /* don't auto chase referrals */
3926 # ifdef LDAP_REFERRALS
3927                         lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3928 # else /* LDAP_REFERRALS */
3929                         syserr("compile with -DLDAP_REFERRALS for referral support");
3930 # endif /* LDAP_REFERRALS */
3931                         break;
3932
3933                   case 'n':             /* retrieve attribute names only */
3934                         lmap->ldap_attrsonly = LDAPMAP_TRUE;
3935                         break;
3936
3937                   case 'r':             /* alias dereferencing */
3938                         while (isascii(*++p) && isspace(*p))
3939                                 continue;
3940
3941                         if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
3942                                 p += 11;
3943
3944                         for (lad = LDAPAliasDereference;
3945                              lad != NULL && lad->lad_name != NULL; lad++)
3946                         {
3947                                 if (sm_strncasecmp(p, lad->lad_name,
3948                                                    strlen(lad->lad_name)) == 0)
3949                                         break;
3950                         }
3951                         if (lad->lad_name != NULL)
3952                                 lmap->ldap_deref = lad->lad_code;
3953                         else
3954                         {
3955                                 /* bad config line */
3956                                 if (!bitset(MCF_OPTFILE,
3957                                             map->map_class->map_cflags))
3958                                 {
3959                                         char *ptr;
3960
3961                                         if ((ptr = strchr(p, ' ')) != NULL)
3962                                                 *ptr = '\0';
3963                                         syserr("Deref must be [never|always|search|find] (not %s) in map %s",
3964                                                 p, map->map_mname);
3965                                         if (ptr != NULL)
3966                                                 *ptr = ' ';
3967                                         return false;
3968                                 }
3969                         }
3970                         break;
3971
3972                   case 's':             /* search scope */
3973                         while (isascii(*++p) && isspace(*p))
3974                                 continue;
3975
3976                         if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
3977                                 p += 11;
3978
3979                         for (lss = LDAPSearchScope;
3980                              lss != NULL && lss->lss_name != NULL; lss++)
3981                         {
3982                                 if (sm_strncasecmp(p, lss->lss_name,
3983                                                    strlen(lss->lss_name)) == 0)
3984                                         break;
3985                         }
3986                         if (lss->lss_name != NULL)
3987                                 lmap->ldap_scope = lss->lss_code;
3988                         else
3989                         {
3990                                 /* bad config line */
3991                                 if (!bitset(MCF_OPTFILE,
3992                                             map->map_class->map_cflags))
3993                                 {
3994                                         char *ptr;
3995
3996                                         if ((ptr = strchr(p, ' ')) != NULL)
3997                                                 *ptr = '\0';
3998                                         syserr("Scope must be [base|one|sub] (not %s) in map %s",
3999                                                 p, map->map_mname);
4000                                         if (ptr != NULL)
4001                                                 *ptr = ' ';
4002                                         return false;
4003                                 }
4004                         }
4005                         break;
4006
4007                   case 'h':             /* ldap host */
4008                         while (isascii(*++p) && isspace(*p))
4009                                 continue;
4010                         if (lmap->ldap_uri != NULL)
4011                         {
4012                                 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4013                                        map->map_mname);
4014                                 return false;
4015                         }
4016                         lmap->ldap_host = p;
4017                         break;
4018
4019                   case 'b':             /* search base */
4020                         while (isascii(*++p) && isspace(*p))
4021                                 continue;
4022                         lmap->ldap_base = p;
4023                         break;
4024
4025                   case 'p':             /* ldap port */
4026                         while (isascii(*++p) && isspace(*p))
4027                                 continue;
4028                         lmap->ldap_port = atoi(p);
4029                         break;
4030
4031                   case 'l':             /* time limit */
4032                         while (isascii(*++p) && isspace(*p))
4033                                 continue;
4034                         lmap->ldap_timelimit = atoi(p);
4035                         lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4036                         break;
4037
4038                   case 'Z':
4039                         while (isascii(*++p) && isspace(*p))
4040                                 continue;
4041                         lmap->ldap_sizelimit = atoi(p);
4042                         break;
4043
4044                   case 'd':             /* Dn to bind to server as */
4045                         while (isascii(*++p) && isspace(*p))
4046                                 continue;
4047                         lmap->ldap_binddn = p;
4048                         break;
4049
4050                   case 'M':             /* Method for binding */
4051                         while (isascii(*++p) && isspace(*p))
4052                                 continue;
4053
4054                         if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4055                                 p += 10;
4056
4057                         for (lam = LDAPAuthMethods;
4058                              lam != NULL && lam->lam_name != NULL; lam++)
4059                         {
4060                                 if (sm_strncasecmp(p, lam->lam_name,
4061                                                    strlen(lam->lam_name)) == 0)
4062                                         break;
4063                         }
4064                         if (lam->lam_name != NULL)
4065                                 lmap->ldap_method = lam->lam_code;
4066                         else
4067                         {
4068                                 /* bad config line */
4069                                 if (!bitset(MCF_OPTFILE,
4070                                             map->map_class->map_cflags))
4071                                 {
4072                                         char *ptr;
4073
4074                                         if ((ptr = strchr(p, ' ')) != NULL)
4075                                                 *ptr = '\0';
4076                                         syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4077                                                 p, map->map_mname);
4078                                         if (ptr != NULL)
4079                                                 *ptr = ' ';
4080                                         return false;
4081                                 }
4082                         }
4083
4084                         break;
4085
4086                         /*
4087                         **  This is a string that is dependent on the
4088                         **  method used defined above.
4089                         */
4090
4091                   case 'P':             /* Secret password for binding */
4092                          while (isascii(*++p) && isspace(*p))
4093                                 continue;
4094                         lmap->ldap_secret = p;
4095                         secretread = false;
4096                         break;
4097
4098                   case 'H':             /* Use LDAP URI */
4099 #  if !USE_LDAP_INIT
4100                         syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4101                                map->map_mname);
4102                         return false;
4103 #   else /* !USE_LDAP_INIT */
4104                         if (lmap->ldap_host != NULL)
4105                         {
4106                                 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4107                                        map->map_mname);
4108                                 return false;
4109                         }
4110                         while (isascii(*++p) && isspace(*p))
4111                                 continue;
4112                         lmap->ldap_uri = p;
4113                         break;
4114 #  endif /* !USE_LDAP_INIT */
4115
4116                   case 'w':
4117                         /* -w should be for passwd, -P should be for version */
4118                         while (isascii(*++p) && isspace(*p))
4119                                 continue;
4120                         lmap->ldap_version = atoi(p);
4121 # ifdef LDAP_VERSION_MAX
4122                         if (lmap->ldap_version > LDAP_VERSION_MAX)
4123                         {
4124                                 syserr("LDAP version %d exceeds max of %d in map %s",
4125                                        lmap->ldap_version, LDAP_VERSION_MAX,
4126                                        map->map_mname);
4127                                 return false;
4128                         }
4129 # endif /* LDAP_VERSION_MAX */
4130 # ifdef LDAP_VERSION_MIN
4131                         if (lmap->ldap_version < LDAP_VERSION_MIN)
4132                         {
4133                                 syserr("LDAP version %d is lower than min of %d in map %s",
4134                                        lmap->ldap_version, LDAP_VERSION_MIN,
4135                                        map->map_mname);
4136                                 return false;
4137                         }
4138 # endif /* LDAP_VERSION_MIN */
4139                         break;
4140
4141                   default:
4142                         syserr("Illegal option %c map %s", *p, map->map_mname);
4143                         break;
4144                 }
4145
4146                 /* need to account for quoted strings here */
4147                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4148                 {
4149                         if (*p == '"')
4150                         {
4151                                 while (*++p != '"' && *p != '\0')
4152                                         continue;
4153                                 if (*p != '\0')
4154                                         p++;
4155                         }
4156                         else
4157                                 p++;
4158                 }
4159
4160                 if (*p != '\0')
4161                         *p++ = '\0';
4162         }
4163
4164         if (map->map_app != NULL)
4165                 map->map_app = newstr(ldapmap_dequote(map->map_app));
4166         if (map->map_tapp != NULL)
4167                 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4168
4169         /*
4170         **  We need to swallow up all the stuff into a struct
4171         **  and dump it into map->map_dbptr1
4172         */
4173
4174         if (lmap->ldap_host != NULL &&
4175             (LDAPDefaults == NULL ||
4176              LDAPDefaults == lmap ||
4177              LDAPDefaults->ldap_host != lmap->ldap_host))
4178                 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4179         map->map_domain = lmap->ldap_host;
4180
4181         if (lmap->ldap_uri != NULL &&
4182             (LDAPDefaults == NULL ||
4183              LDAPDefaults == lmap ||
4184              LDAPDefaults->ldap_uri != lmap->ldap_uri))
4185                 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4186         map->map_domain = lmap->ldap_uri;
4187
4188         if (lmap->ldap_binddn != NULL &&
4189             (LDAPDefaults == NULL ||
4190              LDAPDefaults == lmap ||
4191              LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4192                 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4193
4194         if (lmap->ldap_secret != NULL &&
4195             (LDAPDefaults == NULL ||
4196              LDAPDefaults == lmap ||
4197              LDAPDefaults->ldap_secret != lmap->ldap_secret))
4198         {
4199                 SM_FILE_T *sfd;
4200                 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4201
4202                 if (DontLockReadFiles)
4203                         sff |= SFF_NOLOCK;
4204
4205                 /* need to use method to map secret to passwd string */
4206                 switch (lmap->ldap_method)
4207                 {
4208                   case LDAP_AUTH_NONE:
4209                         /* Do nothing */
4210                         break;
4211
4212                   case LDAP_AUTH_SIMPLE:
4213
4214                         /*
4215                         **  Secret is the name of a file with
4216                         **  the first line as the password.
4217                         */
4218
4219                         /* Already read in the secret? */
4220                         if (secretread)
4221                                 break;
4222
4223                         sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4224                                         O_RDONLY, 0, sff);
4225                         if (sfd == NULL)
4226                         {
4227                                 syserr("LDAP map: cannot open secret %s",
4228                                        ldapmap_dequote(lmap->ldap_secret));
4229                                 return false;
4230                         }
4231                         lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
4232                                                    sfd, TimeOuts.to_fileopen,
4233                                                    "ldapmap_parseargs");
4234                         (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4235                         if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4236                         {
4237                                 syserr("LDAP map: secret in %s too long",
4238                                        ldapmap_dequote(lmap->ldap_secret));
4239                                 return false;
4240                         }
4241                         if (lmap->ldap_secret != NULL &&
4242                             strlen(m_tmp) > 0)
4243                         {
4244                                 /* chomp newline */
4245                                 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4246                                         m_tmp[strlen(m_tmp) - 1] = '\0';
4247
4248                                 lmap->ldap_secret = m_tmp;
4249                         }
4250                         break;
4251
4252 # ifdef LDAP_AUTH_KRBV4
4253                   case LDAP_AUTH_KRBV4:
4254
4255                         /*
4256                         **  Secret is where the ticket file is
4257                         **  stashed
4258                         */
4259
4260                         (void) sm_snprintf(m_tmp, sizeof m_tmp,
4261                                 "KRBTKFILE=%s",
4262                                 ldapmap_dequote(lmap->ldap_secret));
4263                         lmap->ldap_secret = m_tmp;
4264                         break;
4265 # endif /* LDAP_AUTH_KRBV4 */
4266
4267                   default:             /* Should NEVER get here */
4268                         syserr("LDAP map: Illegal value in lmap method");
4269                         return false;
4270                         /* NOTREACHED */
4271                         break;
4272                 }
4273         }
4274
4275         if (lmap->ldap_secret != NULL &&
4276             (LDAPDefaults == NULL ||
4277              LDAPDefaults == lmap ||
4278              LDAPDefaults->ldap_secret != lmap->ldap_secret))
4279                 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4280
4281         if (lmap->ldap_base != NULL &&
4282             (LDAPDefaults == NULL ||
4283              LDAPDefaults == lmap ||
4284              LDAPDefaults->ldap_base != lmap->ldap_base))
4285                 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4286
4287         /*
4288         **  Save the server from extra work.  If request is for a single
4289         **  match, tell the server to only return enough records to
4290         **  determine if there is a single match or not.  This can not
4291         **  be one since the server would only return one and we wouldn't
4292         **  know if there were others available.
4293         */
4294
4295         if (bitset(MF_SINGLEMATCH, map->map_mflags))
4296                 lmap->ldap_sizelimit = 2;
4297
4298         /* If setting defaults, don't process ldap_filter and ldap_attr */
4299         if (lmap == LDAPDefaults)
4300                 return true;
4301
4302         if (lmap->ldap_filter != NULL)
4303                 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4304         else
4305         {
4306                 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4307                 {
4308                         syserr("No filter given in map %s", map->map_mname);
4309                         return false;
4310                 }
4311         }
4312
4313         if (!attrssetup && lmap->ldap_attr[0] != NULL)
4314         {
4315                 bool recurse = false;
4316                 bool normalseen = false;
4317
4318                 i = 0;
4319                 p = ldapmap_dequote(lmap->ldap_attr[0]);
4320                 lmap->ldap_attr[0] = NULL;
4321
4322                 /* Prime the attr list with the objectClass attribute */
4323                 lmap->ldap_attr[i] = "objectClass";
4324                 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4325                 lmap->ldap_attr_needobjclass[i] = NULL;
4326                 i++;
4327
4328                 while (p != NULL)
4329                 {
4330                         char *v;
4331
4332                         while (isascii(*p) && isspace(*p))
4333                                 p++;
4334                         if (*p == '\0')
4335                                 break;
4336                         v = p;
4337                         p = strchr(v, ',');
4338                         if (p != NULL)
4339                                 *p++ = '\0';
4340
4341                         if (i >= LDAPMAP_MAX_ATTR)
4342                         {
4343                                 syserr("Too many return attributes in %s (max %d)",
4344                                        map->map_mname, LDAPMAP_MAX_ATTR);
4345                                 return false;
4346                         }
4347                         if (*v != '\0')
4348                         {
4349                                 int j;
4350                                 int use;
4351                                 char *type;
4352                                 char *needobjclass;
4353
4354                                 type = strchr(v, ':');
4355                                 if (type != NULL)
4356                                 {
4357                                         *type++ = '\0';
4358                                         needobjclass = strchr(type, ':');
4359                                         if (needobjclass != NULL)
4360                                                 *needobjclass++ = '\0';
4361                                 }
4362                                 else
4363                                 {
4364                                         needobjclass = NULL;
4365                                 }
4366
4367                                 use = i;
4368
4369                                 /* allow override on "objectClass" type */
4370                                 if (sm_strcasecmp(v, "objectClass") == 0 &&
4371                                     lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4372                                 {
4373                                         use = 0;
4374                                 }
4375                                 else
4376                                 {
4377                                         /*
4378                                         **  Don't add something to attribute
4379                                         **  list twice.
4380                                         */
4381
4382                                         for (j = 1; j < i; j++)
4383                                         {
4384                                                 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4385                                                 {
4386                                                         syserr("Duplicate attribute (%s) in %s",
4387                                                                v, map->map_mname);
4388                                                         return false;
4389                                                 }
4390                                         }
4391
4392                                         lmap->ldap_attr[use] = newstr(v);
4393                                         if (needobjclass != NULL &&
4394                                             *needobjclass != '\0' &&
4395                                             *needobjclass != '*')
4396                                         {
4397                                                 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4398                                         }
4399                                         else
4400                                         {
4401                                                 lmap->ldap_attr_needobjclass[use] = NULL;
4402                                         }
4403
4404                                 }
4405
4406                                 if (type != NULL && *type != '\0')
4407                                 {
4408                                         if (sm_strcasecmp(type, "dn") == 0)
4409                                         {
4410                                                 recurse = true;
4411                                                 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4412                                         }
4413                                         else if (sm_strcasecmp(type, "filter") == 0)
4414                                         {
4415                                                 recurse = true;
4416                                                 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4417                                         }
4418                                         else if (sm_strcasecmp(type, "url") == 0)
4419                                         {
4420                                                 recurse = true;
4421                                                 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4422                                         }
4423                                         else if (sm_strcasecmp(type, "normal") == 0)
4424                                         {
4425                                                 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4426                                                 normalseen = true;
4427                                         }
4428                                         else
4429                                         {
4430                                                 syserr("Unknown attribute type (%s) in %s",
4431                                                        type, map->map_mname);
4432                                                 return false;
4433                                         }
4434                                 }
4435                                 else
4436                                 {
4437                                         lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4438                                         normalseen = true;
4439                                 }
4440                                 i++;
4441                         }
4442                 }
4443                 lmap->ldap_attr[i] = NULL;
4444
4445                 /* Set in case needed in future code */
4446                 attrssetup = true;
4447
4448                 if (recurse && !normalseen)
4449                 {
4450                         syserr("LDAP recursion requested in %s but no returnable attribute given",
4451                                map->map_mname);
4452                         return false;
4453                 }
4454                 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4455                 {
4456                         syserr("LDAP recursion requested in %s can not be used with -n",
4457                                map->map_mname);
4458                         return false;
4459                 }
4460         }
4461         map->map_db1 = (ARBPTR_T) lmap;
4462         return true;
4463 }
4464
4465 /*
4466 **  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4467 **
4468 **      Parameters:
4469 **              spec -- map argument string from LDAPDefaults option
4470 **
4471 **      Returns:
4472 **              None.
4473 */
4474
4475 void
4476 ldapmap_set_defaults(spec)
4477         char *spec;
4478 {
4479         STAB *class;
4480         MAP map;
4481
4482         /* Allocate and set the default values */
4483         if (LDAPDefaults == NULL)
4484                 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4485         sm_ldap_clear(LDAPDefaults);
4486
4487         memset(&map, '\0', sizeof map);
4488
4489         /* look up the class */
4490         class = stab("ldap", ST_MAPCLASS, ST_FIND);
4491         if (class == NULL)
4492         {
4493                 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4494                 return;
4495         }
4496         map.map_class = &class->s_mapclass;
4497         map.map_db1 = (ARBPTR_T) LDAPDefaults;
4498         map.map_mname = "O LDAPDefaultSpec";
4499
4500         (void) ldapmap_parseargs(&map, spec);
4501
4502         /* These should never be set in LDAPDefaults */
4503         if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4504             map.map_spacesub != SpaceSub ||
4505             map.map_app != NULL ||
4506             map.map_tapp != NULL)
4507         {
4508                 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4509                 SM_FREE_CLR(map.map_app);
4510                 SM_FREE_CLR(map.map_tapp);
4511         }
4512
4513         if (LDAPDefaults->ldap_filter != NULL)
4514         {
4515                 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4516
4517                 /* don't free, it isn't malloc'ed in parseargs */
4518                 LDAPDefaults->ldap_filter = NULL;
4519         }
4520
4521         if (LDAPDefaults->ldap_attr[0] != NULL)
4522         {
4523                 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4524                 /* don't free, they aren't malloc'ed in parseargs */
4525                 LDAPDefaults->ldap_attr[0] = NULL;
4526         }
4527 }
4528 #endif /* LDAPMAP */
4529 /*
4530 **  PH map
4531 */
4532
4533 #if PH_MAP
4534
4535 /*
4536 **  Support for the CCSO Nameserver (ph/qi).
4537 **  This code is intended to replace the so-called "ph mailer".
4538 **  Contributed by Mark D. Roth <roth@uiuc.edu>.  Contact him for support.
4539 */
4540
4541 /* what version of the ph map code we're running */
4542 static char phmap_id[128];
4543
4544 /* sendmail version for phmap id string */
4545 extern const char Version[];
4546
4547 /* assume we're using nph-1.2.x if not specified */
4548 # ifndef NPH_VERSION
4549 #  define NPH_VERSION           10200
4550 # endif
4551
4552 /* compatibility for versions older than nph-1.2.0 */
4553 # if NPH_VERSION < 10200
4554 #  define PH_OPEN_ROUNDROBIN    PH_ROUNDROBIN
4555 #  define PH_OPEN_DONTID        PH_DONTID
4556 #  define PH_CLOSE_FAST         PH_FASTCLOSE
4557 #  define PH_ERR_DATAERR        PH_DATAERR
4558 #  define PH_ERR_NOMATCH        PH_NOMATCH
4559 # endif /* NPH_VERSION < 10200 */
4560
4561 /*
4562 **  PH_MAP_PARSEARGS -- parse ph map definition args.
4563 */
4564
4565 bool
4566 ph_map_parseargs(map, args)
4567         MAP *map;
4568         char *args;
4569 {
4570         register bool done;
4571         register char *p = args;
4572         PH_MAP_STRUCT *pmap = NULL;
4573
4574         /* initialize version string */
4575         (void) sm_snprintf(phmap_id, sizeof phmap_id,
4576                            "sendmail-%s phmap-20010529 libphclient-%s",
4577                            Version, libphclient_version);
4578
4579         pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4580
4581         /* defaults */
4582         pmap->ph_servers = NULL;
4583         pmap->ph_field_list = NULL;
4584         pmap->ph = NULL;
4585         pmap->ph_timeout = 0;
4586         pmap->ph_fastclose = 0;
4587
4588         map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4589         for (;;)
4590         {
4591                 while (isascii(*p) && isspace(*p))
4592                         p++;
4593                 if (*p != '-')
4594                         break;
4595                 switch (*++p)
4596                 {
4597                   case 'N':
4598                         map->map_mflags |= MF_INCLNULL;
4599                         map->map_mflags &= ~MF_TRY0NULL;
4600                         break;
4601
4602                   case 'O':
4603                         map->map_mflags &= ~MF_TRY1NULL;
4604                         break;
4605
4606                   case 'o':
4607                         map->map_mflags |= MF_OPTIONAL;
4608                         break;
4609
4610                   case 'f':
4611                         map->map_mflags |= MF_NOFOLDCASE;
4612                         break;
4613
4614                   case 'm':
4615                         map->map_mflags |= MF_MATCHONLY;
4616                         break;
4617
4618                   case 'A':
4619                         map->map_mflags |= MF_APPEND;
4620                         break;
4621
4622                   case 'q':
4623                         map->map_mflags |= MF_KEEPQUOTES;
4624                         break;
4625
4626                   case 't':
4627                         map->map_mflags |= MF_NODEFER;
4628                         break;
4629
4630                   case 'a':
4631                         map->map_app = ++p;
4632                         break;
4633
4634                   case 'T':
4635                         map->map_tapp = ++p;
4636                         break;
4637
4638                   case 'l':
4639                         while (isascii(*++p) && isspace(*p))
4640                                 continue;
4641                         pmap->ph_timeout = atoi(p);
4642                         break;
4643
4644                   case 'S':
4645                         map->map_spacesub = *++p;
4646                         break;
4647
4648                   case 'D':
4649                         map->map_mflags |= MF_DEFER;
4650                         break;
4651
4652                   case 'h':             /* PH server list */
4653                         while (isascii(*++p) && isspace(*p))
4654                                 continue;
4655                         pmap->ph_servers = p;
4656                         break;
4657
4658                   case 'k':             /* fields to search for */
4659                         while (isascii(*++p) && isspace(*p))
4660                                 continue;
4661                         pmap->ph_field_list = p;
4662                         break;
4663
4664                   default:
4665                         syserr("ph_map_parseargs: unknown option -%c", *p);
4666                 }
4667
4668                 /* try to account for quoted strings */
4669                 done = isascii(*p) && isspace(*p);
4670                 while (*p != '\0' && !done)
4671                 {
4672                         if (*p == '"')
4673                         {
4674                                 while (*++p != '"' && *p != '\0')
4675                                         continue;
4676                                 if (*p != '\0')
4677                                         p++;
4678                         }
4679                         else
4680                                 p++;
4681                         done = isascii(*p) && isspace(*p);
4682                 }
4683
4684                 if (*p != '\0')
4685                         *p++ = '\0';
4686         }
4687
4688         if (map->map_app != NULL)
4689                 map->map_app = newstr(ph_map_dequote(map->map_app));
4690         if (map->map_tapp != NULL)
4691                 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4692
4693         if (pmap->ph_field_list != NULL)
4694                 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4695
4696         if (pmap->ph_servers != NULL)
4697                 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4698         else
4699         {
4700                 syserr("ph_map_parseargs: -h flag is required");
4701                 return false;
4702         }
4703
4704         map->map_db1 = (ARBPTR_T) pmap;
4705         return true;
4706 }
4707
4708 /*
4709 **  PH_MAP_CLOSE -- close the connection to the ph server
4710 */
4711
4712 void
4713 ph_map_close(map)
4714         MAP *map;
4715 {
4716         PH_MAP_STRUCT *pmap;
4717
4718         pmap = (PH_MAP_STRUCT *)map->map_db1;
4719         if (tTd(38, 9))
4720                 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4721                            map->map_mname, pmap->ph_fastclose);
4722
4723
4724         if (pmap->ph != NULL)
4725         {
4726                 ph_set_sendhook(pmap->ph, NULL);
4727                 ph_set_recvhook(pmap->ph, NULL);
4728                 ph_close(pmap->ph, pmap->ph_fastclose);
4729         }
4730
4731         map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4732 }
4733
4734 static jmp_buf  PHTimeout;
4735
4736 /* ARGSUSED */
4737 static void
4738 ph_timeout(unused)
4739         int unused;
4740 {
4741         /*
4742         **  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
4743         **      ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4744         **      DOING.
4745         */
4746
4747         errno = ETIMEDOUT;
4748         longjmp(PHTimeout, 1);
4749 }
4750
4751 static void
4752 #if NPH_VERSION >= 10200
4753 ph_map_send_debug(appdata, text)
4754         void *appdata;
4755 #else
4756 ph_map_send_debug(text)
4757 #endif
4758         char *text;
4759 {
4760         if (LogLevel > 9)
4761                 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4762                           "ph_map_send_debug: ==> %s", text);
4763         if (tTd(38, 20))
4764                 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4765 }
4766
4767 static void
4768 #if NPH_VERSION >= 10200
4769 ph_map_recv_debug(appdata, text)
4770         void *appdata;
4771 #else
4772 ph_map_recv_debug(text)
4773 #endif
4774         char *text;
4775 {
4776         if (LogLevel > 10)
4777                 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4778                           "ph_map_recv_debug: <== %s", text);
4779         if (tTd(38, 21))
4780                 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4781 }
4782
4783 /*
4784 **  PH_MAP_OPEN -- sub for opening PH map
4785 */
4786 bool
4787 ph_map_open(map, mode)
4788         MAP *map;
4789         int mode;
4790 {
4791         PH_MAP_STRUCT *pmap;
4792         register SM_EVENT *ev = NULL;
4793         int save_errno = 0;
4794         char *hostlist, *host;
4795
4796         if (tTd(38, 2))
4797                 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
4798
4799         mode &= O_ACCMODE;
4800         if (mode != O_RDONLY)
4801         {
4802                 /* issue a pseudo-error message */
4803                 errno = SM_EMAPCANTWRITE;
4804                 return false;
4805         }
4806
4807         if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
4808             bitset(MF_DEFER, map->map_mflags))
4809         {
4810                 if (tTd(9, 1))
4811                         sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4812                                    map->map_mname);
4813
4814                 /*
4815                 **  Unset MF_DEFER here so that map_lookup() returns
4816                 **  a temporary failure using the bogus map and
4817                 **  map->map_tapp instead of the default permanent error.
4818                 */
4819
4820                 map->map_mflags &= ~MF_DEFER;
4821                 return false;
4822         }
4823
4824         pmap = (PH_MAP_STRUCT *)map->map_db1;
4825         pmap->ph_fastclose = 0;         /* refresh field for reopen */
4826
4827         /* try each host in the list */
4828         hostlist = newstr(pmap->ph_servers);
4829         for (host = strtok(hostlist, " ");
4830              host != NULL;
4831              host = strtok(NULL, " "))
4832         {
4833                 /* set timeout */
4834                 if (pmap->ph_timeout != 0)
4835                 {
4836                         if (setjmp(PHTimeout) != 0)
4837                         {
4838                                 ev = NULL;
4839                                 if (LogLevel > 1)
4840                                         sm_syslog(LOG_NOTICE, CurEnv->e_id,
4841                                                   "timeout connecting to PH server %.100s",
4842                                                   host);
4843                                 errno = ETIMEDOUT;
4844                                 goto ph_map_open_abort;
4845                         }
4846                         ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4847                 }
4848
4849                 /* open connection to server */
4850                 if (ph_open(&(pmap->ph), host,
4851                             PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
4852                             ph_map_send_debug, ph_map_recv_debug
4853 #if NPH_VERSION >= 10200
4854                             , NULL
4855 #endif
4856                             ) == 0
4857                     && ph_id(pmap->ph, phmap_id) == 0)
4858                 {
4859                         if (ev != NULL)
4860                                 sm_clrevent(ev);
4861                         sm_free(hostlist); /* XXX */
4862                         return true;
4863                 }
4864
4865   ph_map_open_abort:
4866                 save_errno = errno;
4867                 if (ev != NULL)
4868                         sm_clrevent(ev);
4869                 pmap->ph_fastclose = PH_CLOSE_FAST;
4870                 ph_map_close(map);
4871                 errno = save_errno;
4872         }
4873
4874         if (bitset(MF_NODEFER, map->map_mflags))
4875         {
4876                 if (errno == 0)
4877                         errno = EAGAIN;
4878                 syserr("ph_map_open: %s: cannot connect to PH server",
4879                        map->map_mname);
4880         }
4881         else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
4882                 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4883                           "ph_map_open: %s: cannot connect to PH server",
4884                           map->map_mname);
4885         sm_free(hostlist); /* XXX */
4886         return false;
4887 }
4888
4889 /*
4890 **  PH_MAP_LOOKUP -- look up key from ph server
4891 */
4892
4893 char *
4894 ph_map_lookup(map, key, args, pstat)
4895         MAP *map;
4896         char *key;
4897         char **args;
4898         int *pstat;
4899 {
4900         int i, save_errno = 0;
4901         register SM_EVENT *ev = NULL;
4902         PH_MAP_STRUCT *pmap;
4903         char *value = NULL;
4904
4905         pmap = (PH_MAP_STRUCT *)map->map_db1;
4906
4907         *pstat = EX_OK;
4908
4909         /* set timeout */
4910         if (pmap->ph_timeout != 0)
4911         {
4912                 if (setjmp(PHTimeout) != 0)
4913                 {
4914                         ev = NULL;
4915                         if (LogLevel > 1)
4916                                 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4917                                           "timeout during PH lookup of %.100s",
4918                                           key);
4919                         errno = ETIMEDOUT;
4920                         *pstat = EX_TEMPFAIL;
4921                         goto ph_map_lookup_abort;
4922                 }
4923                 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4924         }
4925
4926         /* perform lookup */
4927         i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
4928         if (i == -1)
4929                 *pstat = EX_TEMPFAIL;
4930         else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
4931                 *pstat = EX_UNAVAILABLE;
4932
4933   ph_map_lookup_abort:
4934         if (ev != NULL)
4935                 sm_clrevent(ev);
4936
4937         /*
4938         **  Close the connection if the timer popped
4939         **  or we got a temporary PH error
4940         */
4941
4942         if (*pstat == EX_TEMPFAIL)
4943         {
4944                 save_errno = errno;
4945                 pmap->ph_fastclose = PH_CLOSE_FAST;
4946                 ph_map_close(map);
4947                 errno = save_errno;
4948         }
4949
4950         if (*pstat == EX_OK)
4951         {
4952                 if (tTd(38,20))
4953                         sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
4954
4955                 if (bitset(MF_MATCHONLY, map->map_mflags))
4956                         return map_rewrite(map, key, strlen(key), NULL);
4957                 else
4958                         return map_rewrite(map, value, strlen(value), args);
4959         }
4960
4961         return NULL;
4962 }
4963 #endif /* PH_MAP */
4964 /*
4965 **  syslog map
4966 */
4967
4968 #define map_prio        map_lockfd      /* overload field */
4969
4970 /*
4971 **  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
4972 */
4973
4974 bool
4975 syslog_map_parseargs(map, args)
4976         MAP *map;
4977         char *args;
4978 {
4979         char *p = args;
4980         char *priority = NULL;
4981
4982         /* there is no check whether there is really an argument */
4983         while (*p != '\0')
4984         {
4985                 while (isascii(*p) && isspace(*p))
4986                         p++;
4987                 if (*p != '-')
4988                         break;
4989                 ++p;
4990                 if (*p == 'D')
4991                 {
4992                         map->map_mflags |= MF_DEFER;
4993                         ++p;
4994                 }
4995                 else if (*p == 'S')
4996                 {
4997                         map->map_spacesub = *++p;
4998                         if (*p != '\0')
4999                                 p++;
5000                 }
5001                 else if (*p == 'L')
5002                 {
5003                         while (*++p != '\0' && isascii(*p) && isspace(*p))
5004                                 continue;
5005                         if (*p == '\0')
5006                                 break;
5007                         priority = p;
5008                         while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5009                                 p++;
5010                         if (*p != '\0')
5011                                 *p++ = '\0';
5012                 }
5013                 else
5014                 {
5015                         syserr("Illegal option %c map syslog", *p);
5016                         ++p;
5017                 }
5018         }
5019
5020         if (priority == NULL)
5021                 map->map_prio = LOG_INFO;
5022         else
5023         {
5024                 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5025                         priority += 4;
5026
5027 #ifdef LOG_EMERG
5028                 if (sm_strcasecmp("EMERG", priority) == 0)
5029                         map->map_prio = LOG_EMERG;
5030                 else
5031 #endif /* LOG_EMERG */
5032 #ifdef LOG_ALERT
5033                 if (sm_strcasecmp("ALERT", priority) == 0)
5034                         map->map_prio = LOG_ALERT;
5035                 else
5036 #endif /* LOG_ALERT */
5037 #ifdef LOG_CRIT
5038                 if (sm_strcasecmp("CRIT", priority) == 0)
5039                         map->map_prio = LOG_CRIT;
5040                 else
5041 #endif /* LOG_CRIT */
5042 #ifdef LOG_ERR
5043                 if (sm_strcasecmp("ERR", priority) == 0)
5044                         map->map_prio = LOG_ERR;
5045                 else
5046 #endif /* LOG_ERR */
5047 #ifdef LOG_WARNING
5048                 if (sm_strcasecmp("WARNING", priority) == 0)
5049                         map->map_prio = LOG_WARNING;
5050                 else
5051 #endif /* LOG_WARNING */
5052 #ifdef LOG_NOTICE
5053                 if (sm_strcasecmp("NOTICE", priority) == 0)
5054                         map->map_prio = LOG_NOTICE;
5055                 else
5056 #endif /* LOG_NOTICE */
5057 #ifdef LOG_INFO
5058                 if (sm_strcasecmp("INFO", priority) == 0)
5059                         map->map_prio = LOG_INFO;
5060                 else
5061 #endif /* LOG_INFO */
5062 #ifdef LOG_DEBUG
5063                 if (sm_strcasecmp("DEBUG", priority) == 0)
5064                         map->map_prio = LOG_DEBUG;
5065                 else
5066 #endif /* LOG_DEBUG */
5067                 {
5068                         syserr("syslog_map_parseargs: Unknown priority %s",
5069                                priority);
5070                         return false;
5071                 }
5072         }
5073         return true;
5074 }
5075
5076 /*
5077 **  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
5078 */
5079
5080 char *
5081 syslog_map_lookup(map, string, args, statp)
5082         MAP *map;
5083         char *string;
5084         char **args;
5085         int *statp;
5086 {
5087         char *ptr = map_rewrite(map, string, strlen(string), args);
5088
5089         if (ptr != NULL)
5090         {
5091                 if (tTd(38, 20))
5092                         sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5093                                 map->map_mname, map->map_prio, ptr);
5094
5095                 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5096         }
5097
5098         *statp = EX_OK;
5099         return "";
5100 }
5101
5102 /*
5103 **  HESIOD Modules
5104 */
5105
5106 #if HESIOD
5107
5108 bool
5109 hes_map_open(map, mode)
5110         MAP *map;
5111         int mode;
5112 {
5113         if (tTd(38, 2))
5114                 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5115                         map->map_mname, map->map_file, mode);
5116
5117         if (mode != O_RDONLY)
5118         {
5119                 /* issue a pseudo-error message */
5120                 errno = SM_EMAPCANTWRITE;
5121                 return false;
5122         }
5123
5124 # ifdef HESIOD_INIT
5125         if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5126                 return true;
5127
5128         if (!bitset(MF_OPTIONAL, map->map_mflags))
5129                 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5130                         sm_errstring(errno));
5131         return false;
5132 # else /* HESIOD_INIT */
5133         if (hes_error() == HES_ER_UNINIT)
5134                 hes_init();
5135         switch (hes_error())
5136         {
5137           case HES_ER_OK:
5138           case HES_ER_NOTFOUND:
5139                 return true;
5140         }
5141
5142         if (!bitset(MF_OPTIONAL, map->map_mflags))
5143                 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5144
5145         return false;
5146 # endif /* HESIOD_INIT */
5147 }
5148
5149 char *
5150 hes_map_lookup(map, name, av, statp)
5151         MAP *map;
5152         char *name;
5153         char **av;
5154         int *statp;
5155 {
5156         char **hp;
5157
5158         if (tTd(38, 20))
5159                 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5160
5161         if (name[0] == '\\')
5162         {
5163                 char *np;
5164                 int nl;
5165                 int save_errno;
5166                 char nbuf[MAXNAME];
5167
5168                 nl = strlen(name);
5169                 if (nl < sizeof nbuf - 1)
5170                         np = nbuf;
5171                 else
5172                         np = xalloc(strlen(name) + 2);
5173                 np[0] = '\\';
5174                 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
5175 # ifdef HESIOD_INIT
5176                 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5177 # else /* HESIOD_INIT */
5178                 hp = hes_resolve(np, map->map_file);
5179 # endif /* HESIOD_INIT */
5180                 save_errno = errno;
5181                 if (np != nbuf)
5182                         sm_free(np); /* XXX */
5183                 errno = save_errno;
5184         }
5185         else
5186         {
5187 # ifdef HESIOD_INIT
5188                 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5189 # else /* HESIOD_INIT */
5190                 hp = hes_resolve(name, map->map_file);
5191 # endif /* HESIOD_INIT */
5192         }
5193 # ifdef HESIOD_INIT
5194         if (hp == NULL || *hp == NULL)
5195         {
5196                 switch (errno)
5197                 {
5198                   case ENOENT:
5199                           *statp = EX_NOTFOUND;
5200                           break;
5201                   case ECONNREFUSED:
5202                           *statp = EX_TEMPFAIL;
5203                           break;
5204                   case EMSGSIZE:
5205                   case ENOMEM:
5206                   default:
5207                           *statp = EX_UNAVAILABLE;
5208                           break;
5209                 }
5210                 if (hp != NULL)
5211                         hesiod_free_list(HesiodContext, hp);
5212                 return NULL;
5213         }
5214 # else /* HESIOD_INIT */
5215         if (hp == NULL || hp[0] == NULL)
5216         {
5217                 switch (hes_error())
5218                 {
5219                   case HES_ER_OK:
5220                         *statp = EX_OK;
5221                         break;
5222
5223                   case HES_ER_NOTFOUND:
5224                         *statp = EX_NOTFOUND;
5225                         break;
5226
5227                   case HES_ER_CONFIG:
5228                         *statp = EX_UNAVAILABLE;
5229                         break;
5230
5231                   case HES_ER_NET:
5232                         *statp = EX_TEMPFAIL;
5233                         break;
5234                 }
5235                 return NULL;
5236         }
5237 # endif /* HESIOD_INIT */
5238
5239         if (bitset(MF_MATCHONLY, map->map_mflags))
5240                 return map_rewrite(map, name, strlen(name), NULL);
5241         else
5242                 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5243 }
5244
5245 /*
5246 **  HES_MAP_CLOSE -- free the Hesiod context
5247 */
5248
5249 void
5250 hes_map_close(map)
5251         MAP *map;
5252 {
5253         if (tTd(38, 20))
5254                 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5255
5256 # ifdef HESIOD_INIT
5257         /* Free the hesiod context */
5258         if (HesiodContext != NULL)
5259         {
5260                 hesiod_end(HesiodContext);
5261                 HesiodContext = NULL;
5262         }
5263 # endif /* HESIOD_INIT */
5264 }
5265
5266 #endif /* HESIOD */
5267 /*
5268 **  NeXT NETINFO Modules
5269 */
5270
5271 #if NETINFO
5272
5273 # define NETINFO_DEFAULT_DIR            "/aliases"
5274 # define NETINFO_DEFAULT_PROPERTY       "members"
5275
5276 /*
5277 **  NI_MAP_OPEN -- open NetInfo Aliases
5278 */
5279
5280 bool
5281 ni_map_open(map, mode)
5282         MAP *map;
5283         int mode;
5284 {
5285         if (tTd(38, 2))
5286                 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5287                         map->map_mname, map->map_file, mode);
5288         mode &= O_ACCMODE;
5289
5290         if (*map->map_file == '\0')
5291                 map->map_file = NETINFO_DEFAULT_DIR;
5292
5293         if (map->map_valcolnm == NULL)
5294                 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5295
5296         if (map->map_coldelim == '\0')
5297         {
5298                 if (bitset(MF_ALIAS, map->map_mflags))
5299                         map->map_coldelim = ',';
5300                 else if (bitset(MF_FILECLASS, map->map_mflags))
5301                         map->map_coldelim = ' ';
5302         }
5303         return true;
5304 }
5305
5306
5307 /*
5308 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
5309 */
5310
5311 char *
5312 ni_map_lookup(map, name, av, statp)
5313         MAP *map;
5314         char *name;
5315         char **av;
5316         int *statp;
5317 {
5318         char *res;
5319         char *propval;
5320
5321         if (tTd(38, 20))
5322                 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5323
5324         propval = ni_propval(map->map_file, map->map_keycolnm, name,
5325                              map->map_valcolnm, map->map_coldelim);
5326
5327         if (propval == NULL)
5328                 return NULL;
5329
5330         SM_TRY
5331                 if (bitset(MF_MATCHONLY, map->map_mflags))
5332                         res = map_rewrite(map, name, strlen(name), NULL);
5333                 else
5334                         res = map_rewrite(map, propval, strlen(propval), av);
5335         SM_FINALLY
5336                 sm_free(propval);
5337         SM_END_TRY
5338         return res;
5339 }
5340
5341
5342 static bool
5343 ni_getcanonname(name, hbsize, statp)
5344         char *name;
5345         int hbsize;
5346         int *statp;
5347 {
5348         char *vptr;
5349         char *ptr;
5350         char nbuf[MAXNAME + 1];
5351
5352         if (tTd(38, 20))
5353                 sm_dprintf("ni_getcanonname(%s)\n", name);
5354
5355         if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5356         {
5357                 *statp = EX_UNAVAILABLE;
5358                 return false;
5359         }
5360         (void) shorten_hostname(nbuf);
5361
5362         /* we only accept single token search key */
5363         if (strchr(nbuf, '.'))
5364         {
5365                 *statp = EX_NOHOST;
5366                 return false;
5367         }
5368
5369         /* Do the search */
5370         vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5371
5372         if (vptr == NULL)
5373         {
5374                 *statp = EX_NOHOST;
5375                 return false;
5376         }
5377
5378         /* Only want the first machine name */
5379         if ((ptr = strchr(vptr, '\n')) != NULL)
5380                 *ptr = '\0';
5381
5382         if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5383         {
5384                 sm_free(vptr);
5385                 *statp = EX_UNAVAILABLE;
5386                 return true;
5387         }
5388         sm_free(vptr);
5389         *statp = EX_OK;
5390         return false;
5391 }
5392 #endif /* NETINFO */
5393 /*
5394 **  TEXT (unindexed text file) Modules
5395 **
5396 **      This code donated by Sun Microsystems.
5397 */
5398
5399 #define map_sff         map_lockfd      /* overload field */
5400
5401
5402 /*
5403 **  TEXT_MAP_OPEN -- open text table
5404 */
5405
5406 bool
5407 text_map_open(map, mode)
5408         MAP *map;
5409         int mode;
5410 {
5411         long sff;
5412         int i;
5413
5414         if (tTd(38, 2))
5415                 sm_dprintf("text_map_open(%s, %s, %d)\n",
5416                         map->map_mname, map->map_file, mode);
5417
5418         mode &= O_ACCMODE;
5419         if (mode != O_RDONLY)
5420         {
5421                 errno = EPERM;
5422                 return false;
5423         }
5424
5425         if (*map->map_file == '\0')
5426         {
5427                 syserr("text map \"%s\": file name required",
5428                         map->map_mname);
5429                 return false;
5430         }
5431
5432         if (map->map_file[0] != '/')
5433         {
5434                 syserr("text map \"%s\": file name must be fully qualified",
5435                         map->map_mname);
5436                 return false;
5437         }
5438
5439         sff = SFF_ROOTOK|SFF_REGONLY;
5440         if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5441                 sff |= SFF_NOWLINK;
5442         if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5443                 sff |= SFF_SAFEDIRPATH;
5444         if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5445                           sff, S_IRUSR, NULL)) != 0)
5446         {
5447                 int save_errno = errno;
5448
5449                 /* cannot open this map */
5450                 if (tTd(38, 2))
5451                         sm_dprintf("\tunsafe map file: %d\n", i);
5452                 errno = save_errno;
5453                 if (!bitset(MF_OPTIONAL, map->map_mflags))
5454                         syserr("text map \"%s\": unsafe map file %s",
5455                                 map->map_mname, map->map_file);
5456                 return false;
5457         }
5458
5459         if (map->map_keycolnm == NULL)
5460                 map->map_keycolno = 0;
5461         else
5462         {
5463                 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5464                 {
5465                         syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5466                                 map->map_mname, map->map_file,
5467                                 map->map_keycolnm);
5468                         return false;
5469                 }
5470                 map->map_keycolno = atoi(map->map_keycolnm);
5471         }
5472
5473         if (map->map_valcolnm == NULL)
5474                 map->map_valcolno = 0;
5475         else
5476         {
5477                 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5478                 {
5479                         syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5480                                         map->map_mname, map->map_file,
5481                                         map->map_valcolnm);
5482                         return false;
5483                 }
5484                 map->map_valcolno = atoi(map->map_valcolnm);
5485         }
5486
5487         if (tTd(38, 2))
5488         {
5489                 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5490                         map->map_mname, map->map_file);
5491                 if (map->map_coldelim == '\0')
5492                         sm_dprintf("(white space)\n");
5493                 else
5494                         sm_dprintf("%c\n", map->map_coldelim);
5495         }
5496
5497         map->map_sff = sff;
5498         return true;
5499 }
5500
5501
5502 /*
5503 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5504 */
5505
5506 char *
5507 text_map_lookup(map, name, av, statp)
5508         MAP *map;
5509         char *name;
5510         char **av;
5511         int *statp;
5512 {
5513         char *vp;
5514         auto int vsize;
5515         int buflen;
5516         SM_FILE_T *f;
5517         char delim;
5518         int key_idx;
5519         bool found_it;
5520         long sff = map->map_sff;
5521         char search_key[MAXNAME + 1];
5522         char linebuf[MAXLINE];
5523         char buf[MAXNAME + 1];
5524
5525         found_it = false;
5526         if (tTd(38, 20))
5527                 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
5528
5529         buflen = strlen(name);
5530         if (buflen > sizeof search_key - 1)
5531                 buflen = sizeof search_key - 1; /* XXX just cut if off? */
5532         memmove(search_key, name, buflen);
5533         search_key[buflen] = '\0';
5534         if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5535                 makelower(search_key);
5536
5537         f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5538         if (f == NULL)
5539         {
5540                 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5541                 *statp = EX_UNAVAILABLE;
5542                 return NULL;
5543         }
5544         key_idx = map->map_keycolno;
5545         delim = map->map_coldelim;
5546         while (sm_io_fgets(f, SM_TIME_DEFAULT,
5547                            linebuf, sizeof linebuf) != NULL)
5548         {
5549                 char *p;
5550
5551                 /* skip comment line */
5552                 if (linebuf[0] == '#')
5553                         continue;
5554                 p = strchr(linebuf, '\n');
5555                 if (p != NULL)
5556                         *p = '\0';
5557                 p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5558                 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5559                 {
5560                         found_it = true;
5561                         break;
5562                 }
5563         }
5564         (void) sm_io_close(f, SM_TIME_DEFAULT);
5565         if (!found_it)
5566         {
5567                 *statp = EX_NOTFOUND;
5568                 return NULL;
5569         }
5570         vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5571         if (vp == NULL)
5572         {
5573                 *statp = EX_NOTFOUND;
5574                 return NULL;
5575         }
5576         vsize = strlen(vp);
5577         *statp = EX_OK;
5578         if (bitset(MF_MATCHONLY, map->map_mflags))
5579                 return map_rewrite(map, name, strlen(name), NULL);
5580         else
5581                 return map_rewrite(map, vp, vsize, av);
5582 }
5583
5584 /*
5585 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
5586 */
5587
5588 static bool
5589 text_getcanonname(name, hbsize, statp)
5590         char *name;
5591         int hbsize;
5592         int *statp;
5593 {
5594         bool found;
5595         char *dot;
5596         SM_FILE_T *f;
5597         char linebuf[MAXLINE];
5598         char cbuf[MAXNAME + 1];
5599         char nbuf[MAXNAME + 1];
5600
5601         if (tTd(38, 20))
5602                 sm_dprintf("text_getcanonname(%s)\n", name);
5603
5604         if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5605         {
5606                 *statp = EX_UNAVAILABLE;
5607                 return false;
5608         }
5609         dot = shorten_hostname(nbuf);
5610
5611         f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5612                        NULL);
5613         if (f == NULL)
5614         {
5615                 *statp = EX_UNAVAILABLE;
5616                 return false;
5617         }
5618         found = false;
5619         while (!found &&
5620                 sm_io_fgets(f, SM_TIME_DEFAULT,
5621                             linebuf, sizeof linebuf) != NULL)
5622         {
5623                 char *p = strpbrk(linebuf, "#\n");
5624
5625                 if (p != NULL)
5626                         *p = '\0';
5627                 if (linebuf[0] != '\0')
5628                         found = extract_canonname(nbuf, dot, linebuf,
5629                                                   cbuf, sizeof cbuf);
5630         }
5631         (void) sm_io_close(f, SM_TIME_DEFAULT);
5632         if (!found)
5633         {
5634                 *statp = EX_NOHOST;
5635                 return false;
5636         }
5637
5638         if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5639         {
5640                 *statp = EX_UNAVAILABLE;
5641                 return false;
5642         }
5643         *statp = EX_OK;
5644         return true;
5645 }
5646 /*
5647 **  STAB (Symbol Table) Modules
5648 */
5649
5650
5651 /*
5652 **  STAB_MAP_LOOKUP -- look up alias in symbol table
5653 */
5654
5655 /* ARGSUSED2 */
5656 char *
5657 stab_map_lookup(map, name, av, pstat)
5658         register MAP *map;
5659         char *name;
5660         char **av;
5661         int *pstat;
5662 {
5663         register STAB *s;
5664
5665         if (tTd(38, 20))
5666                 sm_dprintf("stab_lookup(%s, %s)\n",
5667                         map->map_mname, name);
5668
5669         s = stab(name, ST_ALIAS, ST_FIND);
5670         if (s == NULL)
5671                 return NULL;
5672         if (bitset(MF_MATCHONLY, map->map_mflags))
5673                 return map_rewrite(map, name, strlen(name), NULL);
5674         else
5675                 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5676 }
5677
5678 /*
5679 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5680 */
5681
5682 void
5683 stab_map_store(map, lhs, rhs)
5684         register MAP *map;
5685         char *lhs;
5686         char *rhs;
5687 {
5688         register STAB *s;
5689
5690         s = stab(lhs, ST_ALIAS, ST_ENTER);
5691         s->s_alias = newstr(rhs);
5692 }
5693
5694
5695 /*
5696 **  STAB_MAP_OPEN -- initialize (reads data file)
5697 **
5698 **      This is a wierd case -- it is only intended as a fallback for
5699 **      aliases.  For this reason, opens for write (only during a
5700 **      "newaliases") always fails, and opens for read open the
5701 **      actual underlying text file instead of the database.
5702 */
5703
5704 bool
5705 stab_map_open(map, mode)
5706         register MAP *map;
5707         int mode;
5708 {
5709         SM_FILE_T *af;
5710         long sff;
5711         struct stat st;
5712
5713         if (tTd(38, 2))
5714                 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5715                         map->map_mname, map->map_file, mode);
5716
5717         mode &= O_ACCMODE;
5718         if (mode != O_RDONLY)
5719         {
5720                 errno = EPERM;
5721                 return false;
5722         }
5723
5724         sff = SFF_ROOTOK|SFF_REGONLY;
5725         if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5726                 sff |= SFF_NOWLINK;
5727         if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5728                 sff |= SFF_SAFEDIRPATH;
5729         af = safefopen(map->map_file, O_RDONLY, 0444, sff);
5730         if (af == NULL)
5731                 return false;
5732         readaliases(map, af, false, false);
5733
5734         if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
5735                 map->map_mtime = st.st_mtime;
5736         (void) sm_io_close(af, SM_TIME_DEFAULT);
5737
5738         return true;
5739 }
5740 /*
5741 **  Implicit Modules
5742 **
5743 **      Tries several types.  For back compatibility of aliases.
5744 */
5745
5746
5747 /*
5748 **  IMPL_MAP_LOOKUP -- lookup in best open database
5749 */
5750
5751 char *
5752 impl_map_lookup(map, name, av, pstat)
5753         MAP *map;
5754         char *name;
5755         char **av;
5756         int *pstat;
5757 {
5758         if (tTd(38, 20))
5759                 sm_dprintf("impl_map_lookup(%s, %s)\n",
5760                         map->map_mname, name);
5761
5762 #if NEWDB
5763         if (bitset(MF_IMPL_HASH, map->map_mflags))
5764                 return db_map_lookup(map, name, av, pstat);
5765 #endif /* NEWDB */
5766 #if NDBM
5767         if (bitset(MF_IMPL_NDBM, map->map_mflags))
5768                 return ndbm_map_lookup(map, name, av, pstat);
5769 #endif /* NDBM */
5770         return stab_map_lookup(map, name, av, pstat);
5771 }
5772
5773 /*
5774 **  IMPL_MAP_STORE -- store in open databases
5775 */
5776
5777 void
5778 impl_map_store(map, lhs, rhs)
5779         MAP *map;
5780         char *lhs;
5781         char *rhs;
5782 {
5783         if (tTd(38, 12))
5784                 sm_dprintf("impl_map_store(%s, %s, %s)\n",
5785                         map->map_mname, lhs, rhs);
5786 #if NEWDB
5787         if (bitset(MF_IMPL_HASH, map->map_mflags))
5788                 db_map_store(map, lhs, rhs);
5789 #endif /* NEWDB */
5790 #if NDBM
5791         if (bitset(MF_IMPL_NDBM, map->map_mflags))
5792                 ndbm_map_store(map, lhs, rhs);
5793 #endif /* NDBM */
5794         stab_map_store(map, lhs, rhs);
5795 }
5796
5797 /*
5798 **  IMPL_MAP_OPEN -- implicit database open
5799 */
5800
5801 bool
5802 impl_map_open(map, mode)
5803         MAP *map;
5804         int mode;
5805 {
5806         if (tTd(38, 2))
5807                 sm_dprintf("impl_map_open(%s, %s, %d)\n",
5808                         map->map_mname, map->map_file, mode);
5809
5810         mode &= O_ACCMODE;
5811 #if NEWDB
5812         map->map_mflags |= MF_IMPL_HASH;
5813         if (hash_map_open(map, mode))
5814         {
5815 # ifdef NDBM_YP_COMPAT
5816                 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
5817 # endif /* NDBM_YP_COMPAT */
5818                         return true;
5819         }
5820         else
5821                 map->map_mflags &= ~MF_IMPL_HASH;
5822 #endif /* NEWDB */
5823 #if NDBM
5824         map->map_mflags |= MF_IMPL_NDBM;
5825         if (ndbm_map_open(map, mode))
5826         {
5827                 return true;
5828         }
5829         else
5830                 map->map_mflags &= ~MF_IMPL_NDBM;
5831 #endif /* NDBM */
5832
5833 #if defined(NEWDB) || defined(NDBM)
5834         if (Verbose)
5835                 message("WARNING: cannot open alias database %s%s",
5836                         map->map_file,
5837                         mode == O_RDONLY ? "; reading text version" : "");
5838 #else /* defined(NEWDB) || defined(NDBM) */
5839         if (mode != O_RDONLY)
5840                 usrerr("Cannot rebuild aliases: no database format defined");
5841 #endif /* defined(NEWDB) || defined(NDBM) */
5842
5843         if (mode == O_RDONLY)
5844                 return stab_map_open(map, mode);
5845         else
5846                 return false;
5847 }
5848
5849
5850 /*
5851 **  IMPL_MAP_CLOSE -- close any open database(s)
5852 */
5853
5854 void
5855 impl_map_close(map)
5856         MAP *map;
5857 {
5858         if (tTd(38, 9))
5859                 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
5860                         map->map_mname, map->map_file, map->map_mflags);
5861 #if NEWDB
5862         if (bitset(MF_IMPL_HASH, map->map_mflags))
5863         {
5864                 db_map_close(map);
5865                 map->map_mflags &= ~MF_IMPL_HASH;
5866         }
5867 #endif /* NEWDB */
5868
5869 #if NDBM
5870         if (bitset(MF_IMPL_NDBM, map->map_mflags))
5871         {
5872                 ndbm_map_close(map);
5873                 map->map_mflags &= ~MF_IMPL_NDBM;
5874         }
5875 #endif /* NDBM */
5876 }
5877 /*
5878 **  User map class.
5879 **
5880 **      Provides access to the system password file.
5881 */
5882
5883 /*
5884 **  USER_MAP_OPEN -- open user map
5885 **
5886 **      Really just binds field names to field numbers.
5887 */
5888
5889 bool
5890 user_map_open(map, mode)
5891         MAP *map;
5892         int mode;
5893 {
5894         if (tTd(38, 2))
5895                 sm_dprintf("user_map_open(%s, %d)\n",
5896                         map->map_mname, mode);
5897
5898         mode &= O_ACCMODE;
5899         if (mode != O_RDONLY)
5900         {
5901                 /* issue a pseudo-error message */
5902                 errno = SM_EMAPCANTWRITE;
5903                 return false;
5904         }
5905         if (map->map_valcolnm == NULL)
5906                 /* EMPTY */
5907                 /* nothing */ ;
5908         else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
5909                 map->map_valcolno = 1;
5910         else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
5911                 map->map_valcolno = 2;
5912         else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
5913                 map->map_valcolno = 3;
5914         else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
5915                 map->map_valcolno = 4;
5916         else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
5917                 map->map_valcolno = 5;
5918         else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
5919                 map->map_valcolno = 6;
5920         else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
5921                 map->map_valcolno = 7;
5922         else
5923         {
5924                 syserr("User map %s: unknown column name %s",
5925                         map->map_mname, map->map_valcolnm);
5926                 return false;
5927         }
5928         return true;
5929 }
5930
5931
5932 /*
5933 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
5934 */
5935
5936 /* ARGSUSED3 */
5937 char *
5938 user_map_lookup(map, key, av, statp)
5939         MAP *map;
5940         char *key;
5941         char **av;
5942         int *statp;
5943 {
5944         auto bool fuzzy;
5945         SM_MBDB_T user;
5946
5947         if (tTd(38, 20))
5948                 sm_dprintf("user_map_lookup(%s, %s)\n",
5949                         map->map_mname, key);
5950
5951         *statp = finduser(key, &fuzzy, &user);
5952         if (*statp != EX_OK)
5953                 return NULL;
5954         if (bitset(MF_MATCHONLY, map->map_mflags))
5955                 return map_rewrite(map, key, strlen(key), NULL);
5956         else
5957         {
5958                 char *rwval = NULL;
5959                 char buf[30];
5960
5961                 switch (map->map_valcolno)
5962                 {
5963                   case 0:
5964                   case 1:
5965                         rwval = user.mbdb_name;
5966                         break;
5967
5968                   case 2:
5969                         rwval = "x";    /* passwd no longer supported */
5970                         break;
5971
5972                   case 3:
5973                         (void) sm_snprintf(buf, sizeof buf, "%d",
5974                                            (int) user.mbdb_uid);
5975                         rwval = buf;
5976                         break;
5977
5978                   case 4:
5979                         (void) sm_snprintf(buf, sizeof buf, "%d",
5980                                            (int) user.mbdb_gid);
5981                         rwval = buf;
5982                         break;
5983
5984                   case 5:
5985                         rwval = user.mbdb_fullname;
5986                         break;
5987
5988                   case 6:
5989                         rwval = user.mbdb_homedir;
5990                         break;
5991
5992                   case 7:
5993                         rwval = user.mbdb_shell;
5994                         break;
5995                   default:
5996                         syserr("user_map %s: bogus field %d",
5997                                 map->map_mname, map->map_valcolno);
5998                         return NULL;
5999                 }
6000                 return map_rewrite(map, rwval, strlen(rwval), av);
6001         }
6002 }
6003 /*
6004 **  Program map type.
6005 **
6006 **      This provides access to arbitrary programs.  It should be used
6007 **      only very sparingly, since there is no way to bound the cost
6008 **      of invoking an arbitrary program.
6009 */
6010
6011 char *
6012 prog_map_lookup(map, name, av, statp)
6013         MAP *map;
6014         char *name;
6015         char **av;
6016         int *statp;
6017 {
6018         int i;
6019         int save_errno;
6020         int fd;
6021         int status;
6022         auto pid_t pid;
6023         register char *p;
6024         char *rval;
6025         char *argv[MAXPV + 1];
6026         char buf[MAXLINE];
6027
6028         if (tTd(38, 20))
6029                 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6030                         map->map_mname, name, map->map_file);
6031
6032         i = 0;
6033         argv[i++] = map->map_file;
6034         if (map->map_rebuild != NULL)
6035         {
6036                 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
6037                 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6038                 {
6039                         if (i >= MAXPV - 1)
6040                                 break;
6041                         argv[i++] = p;
6042                 }
6043         }
6044         argv[i++] = name;
6045         argv[i] = NULL;
6046         if (tTd(38, 21))
6047         {
6048                 sm_dprintf("prog_open:");
6049                 for (i = 0; argv[i] != NULL; i++)
6050                         sm_dprintf(" %s", argv[i]);
6051                 sm_dprintf("\n");
6052         }
6053         (void) sm_blocksignal(SIGCHLD);
6054         pid = prog_open(argv, &fd, CurEnv);
6055         if (pid < 0)
6056         {
6057                 if (!bitset(MF_OPTIONAL, map->map_mflags))
6058                         syserr("prog_map_lookup(%s) failed (%s) -- closing",
6059                                map->map_mname, sm_errstring(errno));
6060                 else if (tTd(38, 9))
6061                         sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6062                                    map->map_mname, sm_errstring(errno));
6063                 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6064                 *statp = EX_OSFILE;
6065                 return NULL;
6066         }
6067         i = read(fd, buf, sizeof buf - 1);
6068         if (i < 0)
6069         {
6070                 syserr("prog_map_lookup(%s): read error %s",
6071                        map->map_mname, sm_errstring(errno));
6072                 rval = NULL;
6073         }
6074         else if (i == 0)
6075         {
6076                 if (tTd(38, 20))
6077                         sm_dprintf("prog_map_lookup(%s): empty answer\n",
6078                                    map->map_mname);
6079                 rval = NULL;
6080         }
6081         else
6082         {
6083                 buf[i] = '\0';
6084                 p = strchr(buf, '\n');
6085                 if (p != NULL)
6086                         *p = '\0';
6087
6088                 /* collect the return value */
6089                 if (bitset(MF_MATCHONLY, map->map_mflags))
6090                         rval = map_rewrite(map, name, strlen(name), NULL);
6091                 else
6092                         rval = map_rewrite(map, buf, strlen(buf), av);
6093
6094                 /* now flush any additional output */
6095                 while ((i = read(fd, buf, sizeof buf)) > 0)
6096                         continue;
6097         }
6098
6099         /* wait for the process to terminate */
6100         (void) close(fd);
6101         status = waitfor(pid);
6102         save_errno = errno;
6103         (void) sm_releasesignal(SIGCHLD);
6104         errno = save_errno;
6105
6106         if (status == -1)
6107         {
6108                 syserr("prog_map_lookup(%s): wait error %s",
6109                        map->map_mname, sm_errstring(errno));
6110                 *statp = EX_SOFTWARE;
6111                 rval = NULL;
6112         }
6113         else if (WIFEXITED(status))
6114         {
6115                 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6116                         rval = NULL;
6117         }
6118         else
6119         {
6120                 syserr("prog_map_lookup(%s): child died on signal %d",
6121                        map->map_mname, status);
6122                 *statp = EX_UNAVAILABLE;
6123                 rval = NULL;
6124         }
6125         return rval;
6126 }
6127 /*
6128 **  Sequenced map type.
6129 **
6130 **      Tries each map in order until something matches, much like
6131 **      implicit.  Stores go to the first map in the list that can
6132 **      support storing.
6133 **
6134 **      This is slightly unusual in that there are two interfaces.
6135 **      The "sequence" interface lets you stack maps arbitrarily.
6136 **      The "switch" interface builds a sequence map by looking
6137 **      at a system-dependent configuration file such as
6138 **      /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6139 **
6140 **      We don't need an explicit open, since all maps are
6141 **      opened on demand.
6142 */
6143
6144 /*
6145 **  SEQ_MAP_PARSE -- Sequenced map parsing
6146 */
6147
6148 bool
6149 seq_map_parse(map, ap)
6150         MAP *map;
6151         char *ap;
6152 {
6153         int maxmap;
6154
6155         if (tTd(38, 2))
6156                 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6157         maxmap = 0;
6158         while (*ap != '\0')
6159         {
6160                 register char *p;
6161                 STAB *s;
6162
6163                 /* find beginning of map name */
6164                 while (isascii(*ap) && isspace(*ap))
6165                         ap++;
6166                 for (p = ap;
6167                      (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6168                      p++)
6169                         continue;
6170                 if (*p != '\0')
6171                         *p++ = '\0';
6172                 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6173                         p++;
6174                 if (*ap == '\0')
6175                 {
6176                         ap = p;
6177                         continue;
6178                 }
6179                 s = stab(ap, ST_MAP, ST_FIND);
6180                 if (s == NULL)
6181                 {
6182                         syserr("Sequence map %s: unknown member map %s",
6183                                 map->map_mname, ap);
6184                 }
6185                 else if (maxmap >= MAXMAPSTACK)
6186                 {
6187                         syserr("Sequence map %s: too many member maps (%d max)",
6188                                 map->map_mname, MAXMAPSTACK);
6189                         maxmap++;
6190                 }
6191                 else if (maxmap < MAXMAPSTACK)
6192                 {
6193                         map->map_stack[maxmap++] = &s->s_map;
6194                 }
6195                 ap = p;
6196         }
6197         return true;
6198 }
6199
6200 /*
6201 **  SWITCH_MAP_OPEN -- open a switched map
6202 **
6203 **      This looks at the system-dependent configuration and builds
6204 **      a sequence map that does the same thing.
6205 **
6206 **      Every system must define a switch_map_find routine in conf.c
6207 **      that will return the list of service types associated with a
6208 **      given service class.
6209 */
6210
6211 bool
6212 switch_map_open(map, mode)
6213         MAP *map;
6214         int mode;
6215 {
6216         int mapno;
6217         int nmaps;
6218         char *maptype[MAXMAPSTACK];
6219
6220         if (tTd(38, 2))
6221                 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6222                         map->map_mname, map->map_file, mode);
6223
6224         mode &= O_ACCMODE;
6225         nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6226         if (tTd(38, 19))
6227         {
6228                 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6229                 for (mapno = 0; mapno < nmaps; mapno++)
6230                         sm_dprintf("\t\t%s\n", maptype[mapno]);
6231         }
6232         if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6233                 return false;
6234
6235         for (mapno = 0; mapno < nmaps; mapno++)
6236         {
6237                 register STAB *s;
6238                 char nbuf[MAXNAME + 1];
6239
6240                 if (maptype[mapno] == NULL)
6241                         continue;
6242                 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
6243                                    map->map_mname, ".", maptype[mapno]);
6244                 s = stab(nbuf, ST_MAP, ST_FIND);
6245                 if (s == NULL)
6246                 {
6247                         syserr("Switch map %s: unknown member map %s",
6248                                 map->map_mname, nbuf);
6249                 }
6250                 else
6251                 {
6252                         map->map_stack[mapno] = &s->s_map;
6253                         if (tTd(38, 4))
6254                                 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6255                                            mapno,
6256                                            s->s_map.map_class->map_cname,
6257                                            nbuf);
6258                 }
6259         }
6260         return true;
6261 }
6262
6263 #if 0
6264 /*
6265 **  SEQ_MAP_CLOSE -- close all underlying maps
6266 */
6267
6268 void
6269 seq_map_close(map)
6270         MAP *map;
6271 {
6272         int mapno;
6273
6274         if (tTd(38, 9))
6275                 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6276
6277         for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6278         {
6279                 MAP *mm = map->map_stack[mapno];
6280
6281                 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6282                         continue;
6283                 mm->map_mflags |= MF_CLOSING;
6284                 mm->map_class->map_close(mm);
6285                 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6286         }
6287 }
6288 #endif /* 0 */
6289
6290 /*
6291 **  SEQ_MAP_LOOKUP -- sequenced map lookup
6292 */
6293
6294 char *
6295 seq_map_lookup(map, key, args, pstat)
6296         MAP *map;
6297         char *key;
6298         char **args;
6299         int *pstat;
6300 {
6301         int mapno;
6302         int mapbit = 0x01;
6303         bool tempfail = false;
6304
6305         if (tTd(38, 20))
6306                 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6307
6308         for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6309         {
6310                 MAP *mm = map->map_stack[mapno];
6311                 char *rv;
6312
6313                 if (mm == NULL)
6314                         continue;
6315                 if (!bitset(MF_OPEN, mm->map_mflags) &&
6316                     !openmap(mm))
6317                 {
6318                         if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6319                         {
6320                                 *pstat = EX_UNAVAILABLE;
6321                                 return NULL;
6322                         }
6323                         continue;
6324                 }
6325                 *pstat = EX_OK;
6326                 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6327                 if (rv != NULL)
6328                         return rv;
6329                 if (*pstat == EX_TEMPFAIL)
6330                 {
6331                         if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6332                                 return NULL;
6333                         tempfail = true;
6334                 }
6335                 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6336                         break;
6337         }
6338         if (tempfail)
6339                 *pstat = EX_TEMPFAIL;
6340         else if (*pstat == EX_OK)
6341                 *pstat = EX_NOTFOUND;
6342         return NULL;
6343 }
6344
6345 /*
6346 **  SEQ_MAP_STORE -- sequenced map store
6347 */
6348
6349 void
6350 seq_map_store(map, key, val)
6351         MAP *map;
6352         char *key;
6353         char *val;
6354 {
6355         int mapno;
6356
6357         if (tTd(38, 12))
6358                 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6359                         map->map_mname, key, val);
6360
6361         for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6362         {
6363                 MAP *mm = map->map_stack[mapno];
6364
6365                 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6366                         continue;
6367
6368                 mm->map_class->map_store(mm, key, val);
6369                 return;
6370         }
6371         syserr("seq_map_store(%s, %s, %s): no writable map",
6372                 map->map_mname, key, val);
6373 }
6374 /*
6375 **  NULL stubs
6376 */
6377
6378 /* ARGSUSED */
6379 bool
6380 null_map_open(map, mode)
6381         MAP *map;
6382         int mode;
6383 {
6384         return true;
6385 }
6386
6387 /* ARGSUSED */
6388 void
6389 null_map_close(map)
6390         MAP *map;
6391 {
6392         return;
6393 }
6394
6395 char *
6396 null_map_lookup(map, key, args, pstat)
6397         MAP *map;
6398         char *key;
6399         char **args;
6400         int *pstat;
6401 {
6402         *pstat = EX_NOTFOUND;
6403         return NULL;
6404 }
6405
6406 /* ARGSUSED */
6407 void
6408 null_map_store(map, key, val)
6409         MAP *map;
6410         char *key;
6411         char *val;
6412 {
6413         return;
6414 }
6415
6416 /*
6417 **  BOGUS stubs
6418 */
6419
6420 char *
6421 bogus_map_lookup(map, key, args, pstat)
6422         MAP *map;
6423         char *key;
6424         char **args;
6425         int *pstat;
6426 {
6427         *pstat = EX_TEMPFAIL;
6428         return NULL;
6429 }
6430
6431 MAPCLASS        BogusMapClass =
6432 {
6433         "bogus-map",            NULL,                   0,
6434         NULL,                   bogus_map_lookup,       null_map_store,
6435         null_map_open,          null_map_close,
6436 };
6437 /*
6438 **  MACRO modules
6439 */
6440
6441 char *
6442 macro_map_lookup(map, name, av, statp)
6443         MAP *map;
6444         char *name;
6445         char **av;
6446         int *statp;
6447 {
6448         int mid;
6449
6450         if (tTd(38, 20))
6451                 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6452                         name == NULL ? "NULL" : name);
6453
6454         if (name == NULL ||
6455             *name == '\0' ||
6456             (mid = macid(name)) == 0)
6457         {
6458                 *statp = EX_CONFIG;
6459                 return NULL;
6460         }
6461
6462         if (av[1] == NULL)
6463                 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6464         else
6465                 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6466
6467         *statp = EX_OK;
6468         return "";
6469 }
6470 /*
6471 **  REGEX modules
6472 */
6473
6474 #if MAP_REGEX
6475
6476 # include <regex.h>
6477
6478 # define DEFAULT_DELIM  CONDELSE
6479 # define END_OF_FIELDS  -1
6480 # define ERRBUF_SIZE    80
6481 # define MAX_MATCH      32
6482
6483 # define xnalloc(s)     memset(xalloc(s), '\0', s);
6484
6485 struct regex_map
6486 {
6487         regex_t *regex_pattern_buf;     /* xalloc it */
6488         int     *regex_subfields;       /* move to type MAP */
6489         char    *regex_delim;           /* move to type MAP */
6490 };
6491
6492 static int      parse_fields __P((char *, int *, int, int));
6493 static char     *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6494
6495 static int
6496 parse_fields(s, ibuf, blen, nr_substrings)
6497         char *s;
6498         int *ibuf;              /* array */
6499         int blen;               /* number of elements in ibuf */
6500         int nr_substrings;      /* number of substrings in the pattern */
6501 {
6502         register char *cp;
6503         int i = 0;
6504         bool lastone = false;
6505
6506         blen--;         /* for terminating END_OF_FIELDS */
6507         cp = s;
6508         do
6509         {
6510                 for (;; cp++)
6511                 {
6512                         if (*cp == ',')
6513                         {
6514                                 *cp = '\0';
6515                                 break;
6516                         }
6517                         if (*cp == '\0')
6518                         {
6519                                 lastone = true;
6520                                 break;
6521                         }
6522                 }
6523                 if (i < blen)
6524                 {
6525                         int val = atoi(s);
6526
6527                         if (val < 0 || val >= nr_substrings)
6528                         {
6529                                 syserr("field (%d) out of range, only %d substrings in pattern",
6530                                        val, nr_substrings);
6531                                 return -1;
6532                         }
6533                         ibuf[i++] = val;
6534                 }
6535                 else
6536                 {
6537                         syserr("too many fields, %d max", blen);
6538                         return -1;
6539                 }
6540                 s = ++cp;
6541         } while (!lastone);
6542         ibuf[i] = END_OF_FIELDS;
6543         return i;
6544 }
6545
6546 bool
6547 regex_map_init(map, ap)
6548         MAP *map;
6549         char *ap;
6550 {
6551         int regerr;
6552         struct regex_map *map_p;
6553         register char *p;
6554         char *sub_param = NULL;
6555         int pflags;
6556         static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6557
6558         if (tTd(38, 2))
6559                 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6560                         map->map_mname, ap);
6561
6562         pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6563         p = ap;
6564         map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6565         map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6566
6567         for (;;)
6568         {
6569                 while (isascii(*p) && isspace(*p))
6570                         p++;
6571                 if (*p != '-')
6572                         break;
6573                 switch (*++p)
6574                 {
6575                   case 'n':     /* not */
6576                         map->map_mflags |= MF_REGEX_NOT;
6577                         break;
6578
6579                   case 'f':     /* case sensitive */
6580                         map->map_mflags |= MF_NOFOLDCASE;
6581                         pflags &= ~REG_ICASE;
6582                         break;
6583
6584                   case 'b':     /* basic regular expressions */
6585                         pflags &= ~REG_EXTENDED;
6586                         break;
6587
6588                   case 's':     /* substring match () syntax */
6589                         sub_param = ++p;
6590                         pflags &= ~REG_NOSUB;
6591                         break;
6592
6593                   case 'd':     /* delimiter */
6594                         map_p->regex_delim = ++p;
6595                         break;
6596
6597                   case 'a':     /* map append */
6598                         map->map_app = ++p;
6599                         break;
6600
6601                   case 'm':     /* matchonly */
6602                         map->map_mflags |= MF_MATCHONLY;
6603                         break;
6604
6605                   case 'q':
6606                         map->map_mflags |= MF_KEEPQUOTES;
6607                         break;
6608
6609                   case 'S':
6610                         map->map_spacesub = *++p;
6611                         break;
6612
6613                   case 'D':
6614                         map->map_mflags |= MF_DEFER;
6615                         break;
6616
6617                 }
6618                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6619                         p++;
6620                 if (*p != '\0')
6621                         *p++ = '\0';
6622         }
6623         if (tTd(38, 3))
6624                 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6625
6626         if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6627         {
6628                 /* Errorhandling */
6629                 char errbuf[ERRBUF_SIZE];
6630
6631                 (void) regerror(regerr, map_p->regex_pattern_buf,
6632                          errbuf, sizeof errbuf);
6633                 syserr("pattern-compile-error: %s", errbuf);
6634                 sm_free(map_p->regex_pattern_buf); /* XXX */
6635                 sm_free(map_p); /* XXX */
6636                 return false;
6637         }
6638
6639         if (map->map_app != NULL)
6640                 map->map_app = newstr(map->map_app);
6641         if (map_p->regex_delim != NULL)
6642                 map_p->regex_delim = newstr(map_p->regex_delim);
6643         else
6644                 map_p->regex_delim = defdstr;
6645
6646         if (!bitset(REG_NOSUB, pflags))
6647         {
6648                 /* substring matching */
6649                 int substrings;
6650                 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6651
6652                 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6653
6654                 if (tTd(38, 3))
6655                         sm_dprintf("regex_map_init: nr of substrings %d\n",
6656                                 substrings);
6657
6658                 if (substrings >= MAX_MATCH)
6659                 {
6660                         syserr("too many substrings, %d max", MAX_MATCH);
6661                         sm_free(map_p->regex_pattern_buf); /* XXX */
6662                         sm_free(map_p); /* XXX */
6663                         return false;
6664                 }
6665                 if (sub_param != NULL && sub_param[0] != '\0')
6666                 {
6667                         /* optional parameter -sfields */
6668                         if (parse_fields(sub_param, fields,
6669                                          MAX_MATCH + 1, substrings) == -1)
6670                                 return false;
6671                 }
6672                 else
6673                 {
6674                         int i;
6675
6676                         /* set default fields */
6677                         for (i = 0; i < substrings; i++)
6678                                 fields[i] = i;
6679                         fields[i] = END_OF_FIELDS;
6680                 }
6681                 map_p->regex_subfields = fields;
6682                 if (tTd(38, 3))
6683                 {
6684                         int *ip;
6685
6686                         sm_dprintf("regex_map_init: subfields");
6687                         for (ip = fields; *ip != END_OF_FIELDS; ip++)
6688                                 sm_dprintf(" %d", *ip);
6689                         sm_dprintf("\n");
6690                 }
6691         }
6692         map->map_db1 = (ARBPTR_T) map_p;        /* dirty hack */
6693         return true;
6694 }
6695
6696 static char *
6697 regex_map_rewrite(map, s, slen, av)
6698         MAP *map;
6699         const char *s;
6700         size_t slen;
6701         char **av;
6702 {
6703         if (bitset(MF_MATCHONLY, map->map_mflags))
6704                 return map_rewrite(map, av[0], strlen(av[0]), NULL);
6705         else
6706                 return map_rewrite(map, s, slen, av);
6707 }
6708
6709 char *
6710 regex_map_lookup(map, name, av, statp)
6711         MAP *map;
6712         char *name;
6713         char **av;
6714         int *statp;
6715 {
6716         int reg_res;
6717         struct regex_map *map_p;
6718         regmatch_t pmatch[MAX_MATCH];
6719
6720         if (tTd(38, 20))
6721         {
6722                 char **cpp;
6723
6724                 sm_dprintf("regex_map_lookup: key '%s'\n", name);
6725                 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6726                         sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
6727         }
6728
6729         map_p = (struct regex_map *)(map->map_db1);
6730         reg_res = regexec(map_p->regex_pattern_buf,
6731                           name, MAX_MATCH, pmatch, 0);
6732
6733         if (bitset(MF_REGEX_NOT, map->map_mflags))
6734         {
6735                 /* option -n */
6736                 if (reg_res == REG_NOMATCH)
6737                         return regex_map_rewrite(map, "", (size_t) 0, av);
6738                 else
6739                         return NULL;
6740         }
6741         if (reg_res == REG_NOMATCH)
6742                 return NULL;
6743
6744         if (map_p->regex_subfields != NULL)
6745         {
6746                 /* option -s */
6747                 static char retbuf[MAXNAME];
6748                 int fields[MAX_MATCH + 1];
6749                 bool first = true;
6750                 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
6751                 bool quotemode = false, bslashmode = false;
6752                 register char *dp, *sp;
6753                 char *endp, *ldp;
6754                 int *ip;
6755
6756                 dp = retbuf;
6757                 ldp = retbuf + sizeof(retbuf) - 1;
6758
6759                 if (av[1] != NULL)
6760                 {
6761                         if (parse_fields(av[1], fields, MAX_MATCH + 1,
6762                                          (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
6763                         {
6764                                 *statp = EX_CONFIG;
6765                                 return NULL;
6766                         }
6767                         ip = fields;
6768                 }
6769                 else
6770                         ip = map_p->regex_subfields;
6771
6772                 for ( ; *ip != END_OF_FIELDS; ip++)
6773                 {
6774                         if (!first)
6775                         {
6776                                 for (sp = map_p->regex_delim; *sp; sp++)
6777                                 {
6778                                         if (dp < ldp)
6779                                                 *dp++ = *sp;
6780                                 }
6781                         }
6782                         else
6783                                 first = false;
6784
6785                         if (*ip >= MAX_MATCH ||
6786                             pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
6787                                 continue;
6788
6789                         sp = name + pmatch[*ip].rm_so;
6790                         endp = name + pmatch[*ip].rm_eo;
6791                         for (; endp > sp; sp++)
6792                         {
6793                                 if (dp < ldp)
6794                                 {
6795                                         if (bslashmode)
6796                                         {
6797                                                 *dp++ = *sp;
6798                                                 bslashmode = false;
6799                                         }
6800                                         else if (quotemode && *sp != '"' &&
6801                                                 *sp != '\\')
6802                                         {
6803                                                 *dp++ = *sp;
6804                                         }
6805                                         else switch (*dp++ = *sp)
6806                                         {
6807                                           case '\\':
6808                                                 bslashmode = true;
6809                                                 break;
6810
6811                                           case '(':
6812                                                 cmntcnt++;
6813                                                 break;
6814
6815                                           case ')':
6816                                                 cmntcnt--;
6817                                                 break;
6818
6819                                           case '<':
6820                                                 anglecnt++;
6821                                                 break;
6822
6823                                           case '>':
6824                                                 anglecnt--;
6825                                                 break;
6826
6827                                           case ' ':
6828                                                 spacecnt++;
6829                                                 break;
6830
6831                                           case '"':
6832                                                 quotemode = !quotemode;
6833                                                 break;
6834                                         }
6835                                 }
6836                         }
6837                 }
6838                 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
6839                     bslashmode || spacecnt != 0)
6840                 {
6841                         sm_syslog(LOG_WARNING, NOQID,
6842                                   "Warning: regex may cause prescan() failure map=%s lookup=%s",
6843                                   map->map_mname, name);
6844                         return NULL;
6845                 }
6846
6847                 *dp = '\0';
6848
6849                 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
6850         }
6851         return regex_map_rewrite(map, "", (size_t)0, av);
6852 }
6853 #endif /* MAP_REGEX */
6854 /*
6855 **  NSD modules
6856 */
6857 #if MAP_NSD
6858
6859 # include <ndbm.h>
6860 # define _DATUM_DEFINED
6861 # include <ns_api.h>
6862
6863 typedef struct ns_map_list
6864 {
6865         ns_map_t                *map;           /* XXX ns_ ? */
6866         char                    *mapname;
6867         struct ns_map_list      *next;
6868 } ns_map_list_t;
6869
6870 static ns_map_t *
6871 ns_map_t_find(mapname)
6872         char *mapname;
6873 {
6874         static ns_map_list_t *ns_maps = NULL;
6875         ns_map_list_t *ns_map;
6876
6877         /* walk the list of maps looking for the correctly named map */
6878         for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
6879         {
6880                 if (strcmp(ns_map->mapname, mapname) == 0)
6881                         break;
6882         }
6883
6884         /* if we are looking at a NULL ns_map_list_t, then create a new one */
6885         if (ns_map == NULL)
6886         {
6887                 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
6888                 ns_map->mapname = newstr(mapname);
6889                 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6890                 memset(ns_map->map, '\0', sizeof *ns_map->map);
6891                 ns_map->next = ns_maps;
6892                 ns_maps = ns_map;
6893         }
6894         return ns_map->map;
6895 }
6896
6897 char *
6898 nsd_map_lookup(map, name, av, statp)
6899         MAP *map;
6900         char *name;
6901         char **av;
6902         int *statp;
6903 {
6904         int buflen, r;
6905         char *p;
6906         ns_map_t *ns_map;
6907         char keybuf[MAXNAME + 1];
6908         char buf[MAXLINE];
6909
6910         if (tTd(38, 20))
6911                 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
6912
6913         buflen = strlen(name);
6914         if (buflen > sizeof keybuf - 1)
6915                 buflen = sizeof keybuf - 1;     /* XXX simply cut off? */
6916         memmove(keybuf, name, buflen);
6917         keybuf[buflen] = '\0';
6918         if (!bitset(MF_NOFOLDCASE, map->map_mflags))
6919                 makelower(keybuf);
6920
6921         ns_map = ns_map_t_find(map->map_file);
6922         if (ns_map == NULL)
6923         {
6924                 if (tTd(38, 20))
6925                         sm_dprintf("nsd_map_t_find failed\n");
6926                 *statp = EX_UNAVAILABLE;
6927                 return NULL;
6928         }
6929         r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
6930                       buf, sizeof buf);
6931         if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
6932         {
6933                 *statp = EX_TEMPFAIL;
6934                 return NULL;
6935         }
6936         if (r == NS_BADREQ
6937 # ifdef NS_NOPERM
6938             || r == NS_NOPERM
6939 # endif /* NS_NOPERM */
6940             )
6941         {
6942                 *statp = EX_CONFIG;
6943                 return NULL;
6944         }
6945         if (r != NS_SUCCESS)
6946         {
6947                 *statp = EX_NOTFOUND;
6948                 return NULL;
6949         }
6950
6951         *statp = EX_OK;
6952
6953         /* Null out trailing \n */
6954         if ((p = strchr(buf, '\n')) != NULL)
6955                 *p = '\0';
6956
6957         return map_rewrite(map, buf, strlen(buf), av);
6958 }
6959 #endif /* MAP_NSD */
6960
6961 char *
6962 arith_map_lookup(map, name, av, statp)
6963         MAP *map;
6964         char *name;
6965         char **av;
6966         int *statp;
6967 {
6968         long r;
6969         long v[2];
6970         bool res = false;
6971         bool boolres;
6972         static char result[16];
6973         char **cpp;
6974
6975         if (tTd(38, 2))
6976         {
6977                 sm_dprintf("arith_map_lookup: key '%s'\n", name);
6978                 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6979                         sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
6980         }
6981         r = 0;
6982         boolres = false;
6983         cpp = av;
6984         *statp = EX_OK;
6985
6986         /*
6987         **  read arguments for arith map
6988         **  - no check is made whether they are really numbers
6989         **  - just ignores args after the second
6990         */
6991
6992         for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
6993                 v[r++] = strtol(*cpp, NULL, 0);
6994
6995         /* operator and (at least) two operands given? */
6996         if (name != NULL && r == 2)
6997         {
6998                 switch (*name)
6999                 {
7000                   case '|':
7001                         r = v[0] | v[1];
7002                         break;
7003
7004                   case '&':
7005                         r = v[0] & v[1];
7006                         break;
7007
7008                   case '%':
7009                         if (v[1] == 0)
7010                                 return NULL;
7011                         r = v[0] % v[1];
7012                         break;
7013                   case '+':
7014                         r = v[0] + v[1];
7015                         break;
7016
7017                   case '-':
7018                         r = v[0] - v[1];
7019                         break;
7020
7021                   case '*':
7022                         r = v[0] * v[1];
7023                         break;
7024
7025                   case '/':
7026                         if (v[1] == 0)
7027                                 return NULL;
7028                         r = v[0] / v[1];
7029                         break;
7030
7031                   case 'l':
7032                         res = v[0] < v[1];
7033                         boolres = true;
7034                         break;
7035
7036                   case '=':
7037                         res = v[0] == v[1];
7038                         boolres = true;
7039                         break;
7040
7041                   default:
7042                         /* XXX */
7043                         *statp = EX_CONFIG;
7044                         if (LogLevel > 10)
7045                                 sm_syslog(LOG_WARNING, NOQID,
7046                                           "arith_map: unknown operator %c",
7047                                           isprint(*name) ? *name : '?');
7048                         return NULL;
7049                 }
7050                 if (boolres)
7051                         (void) sm_snprintf(result, sizeof result,
7052                                 res ? "TRUE" : "FALSE");
7053                 else
7054                         (void) sm_snprintf(result, sizeof result, "%ld", r);
7055                 return result;
7056         }
7057         *statp = EX_CONFIG;
7058         return NULL;
7059 }
7060
7061 #if SOCKETMAP
7062
7063 # if NETINET || NETINET6
7064 #  include <arpa/inet.h>
7065 # endif /* NETINET || NETINET6 */
7066
7067 # define socket_map_next map_stack[0]
7068
7069 /*
7070 **  SOCKET_MAP_OPEN -- open socket table
7071 */
7072
7073 bool
7074 socket_map_open(map, mode)
7075         MAP *map;
7076         int mode;
7077 {
7078         STAB *s;
7079         int sock = 0;
7080         SOCKADDR_LEN_T addrlen = 0;
7081         int addrno = 0;
7082         int save_errno;
7083         char *p;
7084         char *colon;
7085         char *at;
7086         struct hostent *hp = NULL;
7087         SOCKADDR addr;
7088
7089         if (tTd(38, 2))
7090                 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7091                         map->map_mname, map->map_file, mode);
7092
7093         mode &= O_ACCMODE;
7094
7095         /* sendmail doesn't have the ability to write to SOCKET (yet) */
7096         if (mode != O_RDONLY)
7097         {
7098                 /* issue a pseudo-error message */
7099                 errno = SM_EMAPCANTWRITE;
7100                 return false;
7101         }
7102
7103         if (*map->map_file == '\0')
7104         {
7105                 syserr("socket map \"%s\": empty or missing socket information",
7106                         map->map_mname);
7107                 return false;
7108         }
7109
7110         s = socket_map_findconn(map->map_file);
7111         if (s->s_socketmap != NULL)
7112         {
7113                 /* Copy open connection */
7114                 map->map_db1 = s->s_socketmap->map_db1;
7115
7116                 /* Add this map as head of linked list */
7117                 map->socket_map_next = s->s_socketmap;
7118                 s->s_socketmap = map;
7119
7120                 if (tTd(38, 2))
7121                         sm_dprintf("using cached connection\n");
7122                 return true;
7123         }
7124
7125         if (tTd(38, 2))
7126                 sm_dprintf("opening new connection\n");
7127
7128         /* following code is ripped from milter.c */
7129         /* XXX It should be put in a library... */
7130
7131         /* protocol:filename or protocol:port@host */
7132         memset(&addr, '\0', sizeof addr);
7133         p = map->map_file;
7134         colon = strchr(p, ':');
7135         if (colon != NULL)
7136         {
7137                 *colon = '\0';
7138
7139                 if (*p == '\0')
7140                 {
7141 # if NETUNIX
7142                         /* default to AF_UNIX */
7143                         addr.sa.sa_family = AF_UNIX;
7144 # else /* NETUNIX */
7145 #  if NETINET
7146                         /* default to AF_INET */
7147                         addr.sa.sa_family = AF_INET;
7148 #  else /* NETINET */
7149 #   if NETINET6
7150                         /* default to AF_INET6 */
7151                         addr.sa.sa_family = AF_INET6;
7152 #   else /* NETINET6 */
7153                         /* no protocols available */
7154                         syserr("socket map \"%s\": no valid socket protocols available",
7155                         map->map_mname);
7156                         return false;
7157 #   endif /* NETINET6 */
7158 #  endif /* NETINET */
7159 # endif /* NETUNIX */
7160                 }
7161 # if NETUNIX
7162                 else if (sm_strcasecmp(p, "unix") == 0 ||
7163                          sm_strcasecmp(p, "local") == 0)
7164                         addr.sa.sa_family = AF_UNIX;
7165 # endif /* NETUNIX */
7166 # if NETINET
7167                 else if (sm_strcasecmp(p, "inet") == 0)
7168                         addr.sa.sa_family = AF_INET;
7169 # endif /* NETINET */
7170 # if NETINET6
7171                 else if (sm_strcasecmp(p, "inet6") == 0)
7172                         addr.sa.sa_family = AF_INET6;
7173 # endif /* NETINET6 */
7174                 else
7175                 {
7176 # ifdef EPROTONOSUPPORT
7177                         errno = EPROTONOSUPPORT;
7178 # else /* EPROTONOSUPPORT */
7179                         errno = EINVAL;
7180 # endif /* EPROTONOSUPPORT */
7181                         syserr("socket map \"%s\": unknown socket type %s",
7182                                map->map_mname, p);
7183                         return false;
7184                 }
7185                 *colon++ = ':';
7186         }
7187         else
7188         {
7189                 colon = p;
7190 #if NETUNIX
7191                 /* default to AF_UNIX */
7192                 addr.sa.sa_family = AF_UNIX;
7193 #else /* NETUNIX */
7194 # if NETINET
7195                 /* default to AF_INET */
7196                 addr.sa.sa_family = AF_INET;
7197 # else /* NETINET */
7198 #  if NETINET6
7199                 /* default to AF_INET6 */
7200                 addr.sa.sa_family = AF_INET6;
7201 #  else /* NETINET6 */
7202                 syserr("socket map \"%s\": unknown socket type %s",
7203                        map->map_mname, p);
7204                 return false;
7205 #  endif /* NETINET6 */
7206 # endif /* NETINET */
7207 #endif /* NETUNIX */
7208         }
7209
7210 # if NETUNIX
7211         if (addr.sa.sa_family == AF_UNIX)
7212         {
7213                 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7214
7215                 at = colon;
7216                 if (strlen(colon) >= sizeof addr.sunix.sun_path)
7217                 {
7218                         syserr("socket map \"%s\": local socket name %s too long",
7219                                map->map_mname, colon);
7220                         return false;
7221                 }
7222                 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7223                                  S_IRUSR|S_IWUSR, NULL);
7224
7225                 if (errno != 0)
7226                 {
7227                         /* if not safe, don't create */
7228                                 syserr("socket map \"%s\": local socket name %s unsafe",
7229                                map->map_mname, colon);
7230                         return false;
7231                 }
7232
7233                 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7234                                sizeof addr.sunix.sun_path);
7235                 addrlen = sizeof (struct sockaddr_un);
7236         }
7237         else
7238 # endif /* NETUNIX */
7239 # if NETINET || NETINET6
7240         if (false
7241 #  if NETINET
7242                  || addr.sa.sa_family == AF_INET
7243 #  endif /* NETINET */
7244 #  if NETINET6
7245                  || addr.sa.sa_family == AF_INET6
7246 #  endif /* NETINET6 */
7247                  )
7248         {
7249                 unsigned short port;
7250
7251                 /* Parse port@host */
7252                 at = strchr(colon, '@');
7253                 if (at == NULL)
7254                 {
7255                         syserr("socket map \"%s\": bad address %s (expected port@host)",
7256                                        map->map_mname, colon);
7257                         return false;
7258                 }
7259                 *at = '\0';
7260                 if (isascii(*colon) && isdigit(*colon))
7261                         port = htons((unsigned short) atoi(colon));
7262                 else
7263                 {
7264 #  ifdef NO_GETSERVBYNAME
7265                         syserr("socket map \"%s\": invalid port number %s",
7266                                        map->map_mname, colon);
7267                         return false;
7268 #  else /* NO_GETSERVBYNAME */
7269                         register struct servent *sp;
7270
7271                         sp = getservbyname(colon, "tcp");
7272                         if (sp == NULL)
7273                         {
7274                                 syserr("socket map \"%s\": unknown port name %s",
7275                                                map->map_mname, colon);
7276                                 return false;
7277                         }
7278                         port = sp->s_port;
7279 #  endif /* NO_GETSERVBYNAME */
7280                 }
7281                 *at++ = '@';
7282                 if (*at == '[')
7283                 {
7284                         char *end;
7285
7286                         end = strchr(at, ']');
7287                         if (end != NULL)
7288                         {
7289                                 bool found = false;
7290 #  if NETINET
7291                                 unsigned long hid = INADDR_NONE;
7292 #  endif /* NETINET */
7293 #  if NETINET6
7294                                 struct sockaddr_in6 hid6;
7295 #  endif /* NETINET6 */
7296
7297                                 *end = '\0';
7298 #  if NETINET
7299                                 if (addr.sa.sa_family == AF_INET &&
7300                                     (hid = inet_addr(&at[1])) != INADDR_NONE)
7301                                 {
7302                                         addr.sin.sin_addr.s_addr = hid;
7303                                         addr.sin.sin_port = port;
7304                                         found = true;
7305                                 }
7306 #  endif /* NETINET */
7307 #  if NETINET6
7308                                 (void) memset(&hid6, '\0', sizeof hid6);
7309                                 if (addr.sa.sa_family == AF_INET6 &&
7310                                     anynet_pton(AF_INET6, &at[1],
7311                                                 &hid6.sin6_addr) == 1)
7312                                 {
7313                                         addr.sin6.sin6_addr = hid6.sin6_addr;
7314                                         addr.sin6.sin6_port = port;
7315                                         found = true;
7316                                 }
7317 #  endif /* NETINET6 */
7318                                 *end = ']';
7319                                 if (!found)
7320                                 {
7321                                         syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7322                                                map->map_mname, at);
7323                                         return false;
7324                                 }
7325                         }
7326                         else
7327                         {
7328                                 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7329                                        map->map_mname, at);
7330                                 return false;
7331                         }
7332                 }
7333                 else
7334                 {
7335                         hp = sm_gethostbyname(at, addr.sa.sa_family);
7336                         if (hp == NULL)
7337                         {
7338                                 syserr("socket map \"%s\": Unknown host name %s",
7339                                         map->map_mname, at);
7340                                 return false;
7341                         }
7342                         addr.sa.sa_family = hp->h_addrtype;
7343                         switch (hp->h_addrtype)
7344                         {
7345 #  if NETINET
7346                           case AF_INET:
7347                                 memmove(&addr.sin.sin_addr,
7348                                         hp->h_addr, INADDRSZ);
7349                                 addr.sin.sin_port = port;
7350                                 addrlen = sizeof (struct sockaddr_in);
7351                                 addrno = 1;
7352                                 break;
7353 #  endif /* NETINET */
7354
7355 #  if NETINET6
7356                           case AF_INET6:
7357                                 memmove(&addr.sin6.sin6_addr,
7358                                         hp->h_addr, IN6ADDRSZ);
7359                                 addr.sin6.sin6_port = port;
7360                                 addrlen = sizeof (struct sockaddr_in6);
7361                                 addrno = 1;
7362                                 break;
7363 #  endif /* NETINET6 */
7364
7365                           default:
7366                                 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7367                                         map->map_mname, at, hp->h_addrtype);
7368 #  if NETINET6
7369                                 freehostent(hp);
7370 #  endif /* NETINET6 */
7371                                 return false;
7372                         }
7373                 }
7374         }
7375         else
7376 # endif /* NETINET || NETINET6 */
7377         {
7378                 syserr("socket map \"%s\": unknown socket protocol",
7379                         map->map_mname);
7380                 return false;
7381         }
7382
7383         /* nope, actually connecting */
7384         for (;;)
7385         {
7386                 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7387                 if (sock < 0)
7388                 {
7389                         save_errno = errno;
7390                         if (tTd(38, 5))
7391                                 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7392                                            map->map_mname,
7393                                            sm_errstring(save_errno));
7394 # if NETINET6
7395                         if (hp != NULL)
7396                                 freehostent(hp);
7397 # endif /* NETINET6 */
7398                         return false;
7399                 }
7400
7401                 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7402                         break;
7403
7404                 /* couldn't connect.... try next address */
7405                 save_errno = errno;
7406                 p = CurHostName;
7407                 CurHostName = at;
7408                 if (tTd(38, 5))
7409                         sm_dprintf("socket_open (%s): open %s failed: %s\n",
7410                                 map->map_mname, at, sm_errstring(save_errno));
7411                 CurHostName = p;
7412                 (void) close(sock);
7413
7414                 /* try next address */
7415                 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7416                 {
7417                         switch (addr.sa.sa_family)
7418                         {
7419 # if NETINET
7420                           case AF_INET:
7421                                 memmove(&addr.sin.sin_addr,
7422                                         hp->h_addr_list[addrno++],
7423                                         INADDRSZ);
7424                                 break;
7425 # endif /* NETINET */
7426
7427 # if NETINET6
7428                           case AF_INET6:
7429                                 memmove(&addr.sin6.sin6_addr,
7430                                         hp->h_addr_list[addrno++],
7431                                         IN6ADDRSZ);
7432                                 break;
7433 # endif /* NETINET6 */
7434
7435                           default:
7436                                 if (tTd(38, 5))
7437                                         sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7438                                                    map->map_mname, at,
7439                                                    hp->h_addrtype);
7440 # if NETINET6
7441                                 freehostent(hp);
7442 # endif /* NETINET6 */
7443                                 return false;
7444                         }
7445                         continue;
7446                 }
7447                 p = CurHostName;
7448                 CurHostName = at;
7449                 if (tTd(38, 5))
7450                         sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7451                                    map->map_mname, sm_errstring(save_errno));
7452                 CurHostName = p;
7453 # if NETINET6
7454                 if (hp != NULL)
7455                         freehostent(hp);
7456 # endif /* NETINET6 */
7457                 return false;
7458         }
7459 # if NETINET6
7460         if (hp != NULL)
7461         {
7462                 freehostent(hp);
7463                 hp = NULL;
7464         }
7465 # endif /* NETINET6 */
7466         if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7467                                                   SM_TIME_DEFAULT,
7468                                                   (void *) &sock,
7469                                                   SM_IO_RDWR,
7470                                                   NULL)) == NULL)
7471         {
7472                 close(sock);
7473                 if (tTd(38, 2))
7474                     sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7475                                map->map_mname, sm_errstring(errno));
7476                 return false;
7477         }
7478
7479         /* Save connection for reuse */
7480         s->s_socketmap = map;
7481         return true;
7482 }
7483
7484 /*
7485 **  SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7486 **
7487 **      Cache SOCKET connections based on the connection specifier
7488 **      and PID so we don't have multiple connections open to
7489 **      the same server for different maps.  Need a separate connection
7490 **      per PID since a parent process may close the map before the
7491 **      child is done with it.
7492 **
7493 **      Parameters:
7494 **              conn -- SOCKET map connection specifier
7495 **
7496 **      Returns:
7497 **              Symbol table entry for the SOCKET connection.
7498 */
7499
7500 static STAB *
7501 socket_map_findconn(conn)
7502         const char *conn;
7503 {
7504         char *nbuf;
7505         STAB *SM_NONVOLATILE s = NULL;
7506
7507         nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7508         SM_TRY
7509                 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7510         SM_FINALLY
7511                 sm_free(nbuf);
7512         SM_END_TRY
7513         return s;
7514 }
7515
7516 /*
7517 **  SOCKET_MAP_CLOSE -- close the socket
7518 */
7519
7520 void
7521 socket_map_close(map)
7522         MAP *map;
7523 {
7524         STAB *s;
7525         MAP *smap;
7526
7527         if (tTd(38, 20))
7528                 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7529                         (long) CurrentPid);
7530
7531         /* Check if already closed */
7532         if (map->map_db1 == NULL)
7533         {
7534                 if (tTd(38, 20))
7535                         sm_dprintf("socket_map_close(%s) already closed\n",
7536                                 map->map_file);
7537                 return;
7538         }
7539         sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7540
7541         /* Mark all the maps that share the connection as closed */
7542         s = socket_map_findconn(map->map_file);
7543         smap = s->s_socketmap;
7544         while (smap != NULL)
7545         {
7546                 MAP *next;
7547
7548                 if (tTd(38, 2) && smap != map)
7549                         sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7550                                 map->map_mname, smap->map_mname);
7551
7552                 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7553                 smap->map_db1 = NULL;
7554                 next = smap->socket_map_next;
7555                 smap->socket_map_next = NULL;
7556                 smap = next;
7557         }
7558         s->s_socketmap = NULL;
7559 }
7560
7561 /*
7562 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7563 */
7564
7565 char *
7566 socket_map_lookup(map, name, av, statp)
7567         MAP *map;
7568         char *name;
7569         char **av;
7570         int *statp;
7571 {
7572         unsigned int nettolen, replylen, recvlen;
7573         char *replybuf, *rval, *value, *status, *key;
7574         SM_FILE_T *f;
7575         char keybuf[MAXNAME + 1];
7576
7577         replybuf = NULL;
7578         rval = NULL;
7579         f = (SM_FILE_T *)map->map_db1;
7580         if (tTd(38, 20))
7581                 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7582                         map->map_mname, name, map->map_file);
7583
7584         if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7585         {
7586                 nettolen = strlen(name);
7587                 if (nettolen > sizeof keybuf - 1)
7588                         nettolen = sizeof keybuf - 1;
7589                 memmove(keybuf, name, nettolen);
7590                 keybuf[nettolen] = '\0';
7591                 makelower(keybuf);
7592                 key = keybuf;
7593         }
7594         else
7595                 key = name;
7596
7597         nettolen = strlen(map->map_mname) + 1 + strlen(key);
7598         SM_ASSERT(nettolen > strlen(map->map_mname));
7599         SM_ASSERT(nettolen > strlen(key));
7600         if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7601                            nettolen, map->map_mname, key) == SM_IO_EOF) ||
7602             (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7603             (sm_io_error(f)))
7604         {
7605                 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7606                         map->map_mname);
7607                 *statp = EX_TEMPFAIL;
7608                 goto errcl;
7609         }
7610
7611         if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7612         {
7613                 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7614                         map->map_mname);
7615                 *statp = EX_TEMPFAIL;
7616                 goto errcl;
7617         }
7618         if (replylen > SOCKETMAP_MAXL)
7619         {
7620                 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7621                            map->map_mname, replylen);
7622                 *statp = EX_TEMPFAIL;
7623                 goto errcl;
7624         }
7625         if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7626         {
7627                 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7628                         map->map_mname);
7629                 *statp = EX_TEMPFAIL;
7630                 goto error;
7631         }
7632
7633         replybuf = (char *) sm_malloc(replylen + 1);
7634         if (replybuf == NULL)
7635         {
7636                 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7637                         map->map_mname, replylen + 1);
7638                 *statp = EX_OSERR;
7639                 goto error;
7640         }
7641
7642         recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7643         if (recvlen < replylen)
7644         {
7645                 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7646                            map->map_mname, recvlen, replylen);
7647                 *statp = EX_TEMPFAIL;
7648                 goto errcl;
7649         }
7650         if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7651         {
7652                 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7653                         map->map_mname);
7654                 *statp = EX_TEMPFAIL;
7655                 goto errcl;
7656         }
7657         status = replybuf;
7658         replybuf[recvlen] = '\0';
7659         value = strchr(replybuf, ' ');
7660         if (value != NULL)
7661         {
7662                 *value = '\0';
7663                 value++;
7664         }
7665         if (strcmp(status, "OK") == 0)
7666         {
7667                 *statp = EX_OK;
7668
7669                 /* collect the return value */
7670                 if (bitset(MF_MATCHONLY, map->map_mflags))
7671                         rval = map_rewrite(map, key, strlen(key), NULL);
7672                 else
7673                         rval = map_rewrite(map, value, strlen(value), av);
7674         }
7675         else if (strcmp(status, "NOTFOUND") == 0)
7676         {
7677                 *statp = EX_NOTFOUND;
7678                 if (tTd(38, 20))
7679                         sm_dprintf("socket_map_lookup(%s): %s not found\n",
7680                                 map->map_mname, key);
7681         }
7682         else
7683         {
7684                 if (tTd(38, 5))
7685                         sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7686                                 map->map_mname, key, status,
7687                                 value ? value : "");
7688                 if ((strcmp(status, "TEMP") == 0) ||
7689                     (strcmp(status, "TIMEOUT") == 0))
7690                         *statp = EX_TEMPFAIL;
7691                 else if(strcmp(status, "PERM") == 0)
7692                         *statp = EX_UNAVAILABLE;
7693                 else
7694                         *statp = EX_PROTOCOL;
7695         }
7696
7697         if (replybuf != NULL)
7698                 sm_free(replybuf);
7699         return rval;
7700
7701   errcl:
7702         socket_map_close(map);
7703   error:
7704         if (replybuf != NULL)
7705                 sm_free(replybuf);
7706         return rval;
7707 }
7708 #endif /* SOCKETMAP */