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