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