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