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