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