source: trunk/third/sendmail/sendmail/map.c @ 19204

Revision 19204, 152.8 KB checked in by zacheiss, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
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
16SM_RCSID("@(#)$Id: map.c,v 1.1.1.1 2003-04-08 15:08:32 zacheiss Exp $")
17
18#if LDAPMAP
19# include <sm/ldap.h>
20#endif /* LDAPMAP */
21
22#if NDBM
23# include <ndbm.h>
24# ifdef R_FIRST
25  ERROR README: You are running the Berkeley DB version of ndbm.h.  See
26  ERROR README: the README file about tweaking Berkeley DB so it can
27  ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
28  ERROR README: and use -DNEWDB instead.
29# endif /* R_FIRST */
30#endif /* NDBM */
31#if NEWDB
32# include "sm/bdb.h"
33#endif /* NEWDB */
34#if NIS
35  struct dom_binding;   /* forward reference needed on IRIX */
36# include <rpcsvc/ypclnt.h>
37# if NDBM
38#  define NDBM_YP_COMPAT        /* create YP-compatible NDBM files */
39# endif /* NDBM */
40#endif /* NIS */
41
42#if NEWDB
43# if DB_VERSION_MAJOR < 2
44static bool     db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
45# endif /* DB_VERSION_MAJOR < 2 */
46# if DB_VERSION_MAJOR == 2
47static bool     db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
48# endif /* DB_VERSION_MAJOR == 2 */
49# if DB_VERSION_MAJOR > 2
50static bool     db_map_open __P((MAP *, int, char *, DBTYPE, void **));
51# endif /* DB_VERSION_MAJOR > 2 */
52#endif /* NEWDB */
53static bool     extract_canonname __P((char *, char *, char *, char[], int));
54static void     map_close __P((STAB *, int));
55static void     map_init __P((STAB *, int));
56#ifdef LDAPMAP
57static STAB *   ldapmap_findconn __P((SM_LDAP_STRUCT *));
58#endif /* LDAPMAP */
59#if NISPLUS
60static bool     nisplus_getcanonname __P((char *, int, int *));
61#endif /* NISPLUS */
62#if NIS
63static bool     nis_getcanonname __P((char *, int, int *));
64#endif /* NIS */
65#if NETINFO
66static bool     ni_getcanonname __P((char *, int, int *));
67#endif /* NETINFO */
68static bool     text_getcanonname __P((char *, int, int *));
69
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
144bool
145map_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
308char *
309map_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
429void
430initmaps()
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 */
455static void
456map_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
506bool
507openmap(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
592void
593closemaps(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 */
610static void
611map_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
669bool
670getcanonname(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
811static bool
812extract_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
884bool
885dns_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
926struct dns_map
927{
928        int dns_m_type;
929};
930
931bool
932dns_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
1122char *
1123dns_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
1346bool
1347ndbm_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
1609char *
1610ndbm_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        }
1636lockdbm:
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
1707void
1708ndbm_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
1788void
1789ndbm_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
1867bool
1868bt_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
1896bool
1897hash_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
1928static bool
1929db_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
2238char *
2239db_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
2402void
2403db_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
2510void
2511db_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
2580bool
2581nis_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 */
2666char *
2667nis_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
2734static bool
2735nis_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
2840bool
2841nisplus_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
2979char *
2980nisplus_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
3119static bool
3120nisplus_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
3234char *
3235nisplus_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
3263static char *ldapmap_dequote __P((char *));
3264
3265static char *
3266ldapmap_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
3293static 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
3303bool
3304ldapmap_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
3391void
3392ldapmap_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
3434static char *
3435sunet_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
3466char *
3467ldapmap_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
3991static STAB *
3992ldapmap_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
4031static 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
4041static 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
4050static struct lssvalues LDAPSearchScope[] =
4051{
4052        {       "base",         LDAP_SCOPE_BASE         },
4053        {       "one",          LDAP_SCOPE_ONELEVEL     },
4054        {       "sub",          LDAP_SCOPE_SUBTREE      },
4055        {       NULL,           0                       }
4056};
4057
4058bool
4059ldapmap_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
4820void
4821ldapmap_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 */
4887static char phmap_id[128];
4888
4889/* sendmail version for phmap id string */
4890extern 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
4910bool
4911ph_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
5063void
5064ph_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
5085static jmp_buf  PHTimeout;
5086
5087/* ARGSUSED */
5088static void
5089ph_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
5102static void
5103#if NPH_VERSION >= 10200
5104ph_map_send_debug(appdata, text)
5105        void *appdata;
5106#else
5107ph_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
5118static void
5119#if NPH_VERSION >= 10200
5120ph_map_recv_debug(appdata, text)
5121        void *appdata;
5122#else
5123ph_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*/
5137bool
5138ph_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
5244char *
5245ph_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
5325bool
5326syslog_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
5431char *
5432syslog_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
5459bool
5460hes_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
5500char *
5501hes_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
5600void
5601hes_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
5631bool
5632ni_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
5662char *
5663ni_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
5693static bool
5694ni_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
5757bool
5758text_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
5857char *
5858text_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
5939static bool
5940text_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 */
6007char *
6008stab_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
6031void
6032stab_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
6053bool
6054stab_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
6100char *
6101impl_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
6126void
6127impl_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
6150bool
6151impl_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
6203void
6204impl_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
6238bool
6239user_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 */
6286char *
6287user_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
6356char *
6357prog_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
6493bool
6494seq_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
6556bool
6557switch_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
6613void
6614seq_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
6639char *
6640seq_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
6694void
6695seq_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 */
6724bool
6725null_map_open(map, mode)
6726        MAP *map;
6727        int mode;
6728{
6729        return true;
6730}
6731
6732/* ARGSUSED */
6733void
6734null_map_close(map)
6735        MAP *map;
6736{
6737        return;
6738}
6739
6740char *
6741null_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 */
6752void
6753null_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
6765char *
6766bogus_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
6776MAPCLASS        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
6786char *
6787macro_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
6830struct 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
6837static int
6838parse_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
6888bool
6889regex_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
7034static char *
7035regex_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
7047char *
7048regex_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
7201typedef 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
7208static ns_map_t *
7209ns_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
7235char *
7236nsd_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
7299char *
7300arith_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}
Note: See TracBrowser for help on using the repository browser.