xref: /illumos-gate/usr/src/cmd/sendmail/src/map.c (revision e9af4bc0)
1 /*
2  * Copyright (c) 1998-2008 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 /*
15  * Copyright 1996-2007 Sun Microsystems, Inc.  All rights reserved.
16  * Use is subject to license terms.
17  */
18 
19 #include <sendmail.h>
20 
21 SM_RCSID("@(#)$Id: map.c,v 8.705 2009/08/11 22:22:40 ca Exp $")
22 
23 #if LDAPMAP
24 # include <sm/ldap.h>
25 #endif /* LDAPMAP */
26 
27 #if NDBM
28 # include <ndbm.h>
29 # ifdef R_FIRST
30   ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
31   ERROR README:	the README file about tweaking Berkeley DB so it can
32   ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
33   ERROR README: and use -DNEWDB instead.
34 # endif /* R_FIRST */
35 #endif /* NDBM */
36 #if NEWDB
37 # include "sm/bdb.h"
38 #endif /* NEWDB */
39 #if NIS
40   struct dom_binding;	/* forward reference needed on IRIX */
41 # include <rpcsvc/ypclnt.h>
42 # if NDBM
43 #  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
44 # endif /* NDBM */
45 #endif /* NIS */
46 
47 #include "map.h"
48 
49 #if NEWDB
50 # if DB_VERSION_MAJOR < 2
51 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
52 # endif /* DB_VERSION_MAJOR < 2 */
53 # if DB_VERSION_MAJOR == 2
54 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
55 # endif /* DB_VERSION_MAJOR == 2 */
56 # if DB_VERSION_MAJOR > 2
57 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, void **));
58 # endif /* DB_VERSION_MAJOR > 2 */
59 #endif /* NEWDB */
60 static bool	extract_canonname __P((char *, char *, char *, char[], int));
61 static void	map_close __P((STAB *, int));
62 static void	map_init __P((STAB *, int));
63 #ifdef LDAPMAP
64 static STAB *	ldapmap_findconn __P((SM_LDAP_STRUCT *));
65 #endif /* LDAPMAP */
66 #if NISPLUS
67 static bool	nisplus_getcanonname __P((char *, int, int *));
68 #endif /* NISPLUS */
69 #if NIS
70 static bool	nis_getcanonname __P((char *, int, int *));
71 #endif /* NIS */
72 #if NETINFO
73 static bool	ni_getcanonname __P((char *, int, int *));
74 #endif /* NETINFO */
75 static bool	text_getcanonname __P((char *, int, int *));
76 #if SOCKETMAP
77 static STAB	*socket_map_findconn __P((const char*));
78 
79 /* XXX arbitrary limit for sanity */
80 # define SOCKETMAP_MAXL 1000000
81 #endif /* SOCKETMAP */
82 
83 /* default error message for trying to open a map in write mode */
84 #ifdef ENOSYS
85 # define SM_EMAPCANTWRITE	ENOSYS
86 #else /* ENOSYS */
87 # ifdef EFTYPE
88 #  define SM_EMAPCANTWRITE	EFTYPE
89 # else /* EFTYPE */
90 #  define SM_EMAPCANTWRITE	ENXIO
91 # endif /* EFTYPE */
92 #endif /* ENOSYS */
93 
94 /*
95 **  MAP.C -- implementations for various map classes.
96 **
97 **	Each map class implements a series of functions:
98 **
99 **	bool map_parse(MAP *map, char *args)
100 **		Parse the arguments from the config file.  Return true
101 **		if they were ok, false otherwise.  Fill in map with the
102 **		values.
103 **
104 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
105 **		Look up the key in the given map.  If found, do any
106 **		rewriting the map wants (including "args" if desired)
107 **		and return the value.  Set *pstat to the appropriate status
108 **		on error and return NULL.  Args will be NULL if called
109 **		from the alias routines, although this should probably
110 **		not be relied upon.  It is suggested you call map_rewrite
111 **		to return the results -- it takes care of null termination
112 **		and uses a dynamically expanded buffer as needed.
113 **
114 **	void map_store(MAP *map, char *key, char *value)
115 **		Store the key:value pair in the map.
116 **
117 **	bool map_open(MAP *map, int mode)
118 **		Open the map for the indicated mode.  Mode should
119 **		be either O_RDONLY or O_RDWR.  Return true if it
120 **		was opened successfully, false otherwise.  If the open
121 **		failed and the MF_OPTIONAL flag is not set, it should
122 **		also print an error.  If the MF_ALIAS bit is set
123 **		and this map class understands the @:@ convention, it
124 **		should call aliaswait() before returning.
125 **
126 **	void map_close(MAP *map)
127 **		Close the map.
128 **
129 **	This file also includes the implementation for getcanonname.
130 **	It is currently implemented in a pretty ad-hoc manner; it ought
131 **	to be more properly integrated into the map structure.
132 */
133 
134 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
135 # define LOCK_ON_OPEN	1	/* we can open/create a locked file */
136 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
137 # define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
138 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
139 
140 /*
141 **  MAP_PARSEARGS -- parse config line arguments for database lookup
142 **
143 **	This is a generic version of the map_parse method.
144 **
145 **	Parameters:
146 **		map -- the map being initialized.
147 **		ap -- a pointer to the args on the config line.
148 **
149 **	Returns:
150 **		true -- if everything parsed OK.
151 **		false -- otherwise.
152 **
153 **	Side Effects:
154 **		null terminates the filename; stores it in map
155 */
156 
157 bool
map_parseargs(map,ap)158 map_parseargs(map, ap)
159 	MAP *map;
160 	char *ap;
161 {
162 	register char *p = ap;
163 
164 	/*
165 	**  There is no check whether there is really an argument,
166 	**  but that's not important enough to warrant extra code.
167 	*/
168 
169 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
170 	map->map_spacesub = SpaceSub;	/* default value */
171 	for (;;)
172 	{
173 		while (isascii(*p) && isspace(*p))
174 			p++;
175 		if (*p != '-')
176 			break;
177 		switch (*++p)
178 		{
179 		  case 'N':
180 			map->map_mflags |= MF_INCLNULL;
181 			map->map_mflags &= ~MF_TRY0NULL;
182 			break;
183 
184 		  case 'O':
185 			map->map_mflags &= ~MF_TRY1NULL;
186 			break;
187 
188 		  case 'o':
189 			map->map_mflags |= MF_OPTIONAL;
190 			break;
191 
192 		  case 'f':
193 			map->map_mflags |= MF_NOFOLDCASE;
194 			break;
195 
196 		  case 'm':
197 			map->map_mflags |= MF_MATCHONLY;
198 			break;
199 
200 		  case 'A':
201 			map->map_mflags |= MF_APPEND;
202 			break;
203 
204 		  case 'q':
205 			map->map_mflags |= MF_KEEPQUOTES;
206 			break;
207 
208 		  case 'a':
209 			map->map_app = ++p;
210 			break;
211 
212 		  case 'T':
213 			map->map_tapp = ++p;
214 			break;
215 
216 		  case 'k':
217 			while (isascii(*++p) && isspace(*p))
218 				continue;
219 			map->map_keycolnm = p;
220 			break;
221 
222 		  case 'v':
223 			while (isascii(*++p) && isspace(*p))
224 				continue;
225 			map->map_valcolnm = p;
226 			break;
227 
228 		  case 'z':
229 			if (*++p != '\\')
230 				map->map_coldelim = *p;
231 			else
232 			{
233 				switch (*++p)
234 				{
235 				  case 'n':
236 					map->map_coldelim = '\n';
237 					break;
238 
239 				  case 't':
240 					map->map_coldelim = '\t';
241 					break;
242 
243 				  default:
244 					map->map_coldelim = '\\';
245 				}
246 			}
247 			break;
248 
249 		  case 't':
250 			map->map_mflags |= MF_NODEFER;
251 			break;
252 
253 
254 		  case 'S':
255 			map->map_spacesub = *++p;
256 			break;
257 
258 		  case 'D':
259 			map->map_mflags |= MF_DEFER;
260 			break;
261 
262 		  default:
263 			syserr("Illegal option %c map %s", *p, map->map_mname);
264 			break;
265 		}
266 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
267 			p++;
268 		if (*p != '\0')
269 			*p++ = '\0';
270 	}
271 	if (map->map_app != NULL)
272 		map->map_app = newstr(map->map_app);
273 	if (map->map_tapp != NULL)
274 		map->map_tapp = newstr(map->map_tapp);
275 	if (map->map_keycolnm != NULL)
276 		map->map_keycolnm = newstr(map->map_keycolnm);
277 	if (map->map_valcolnm != NULL)
278 		map->map_valcolnm = newstr(map->map_valcolnm);
279 
280 	if (*p != '\0')
281 	{
282 		map->map_file = p;
283 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
284 			p++;
285 		if (*p != '\0')
286 			*p++ = '\0';
287 		map->map_file = newstr(map->map_file);
288 	}
289 
290 	while (*p != '\0' && isascii(*p) && isspace(*p))
291 		p++;
292 	if (*p != '\0')
293 		map->map_rebuild = newstr(p);
294 
295 	if (map->map_file == NULL &&
296 	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
297 	{
298 		syserr("No file name for %s map %s",
299 			map->map_class->map_cname, map->map_mname);
300 		return false;
301 	}
302 	return true;
303 }
304 /*
305 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
306 **
307 **	It also adds the map_app string.  It can be used as a utility
308 **	in the map_lookup method.
309 **
310 **	Parameters:
311 **		map -- the map that causes this.
312 **		s -- the string to rewrite, NOT necessarily null terminated.
313 **		slen -- the length of s.
314 **		av -- arguments to interpolate into buf.
315 **
316 **	Returns:
317 **		Pointer to rewritten result.  This is static data that
318 **		should be copied if it is to be saved!
319 */
320 
321 char *
map_rewrite(map,s,slen,av)322 map_rewrite(map, s, slen, av)
323 	register MAP *map;
324 	register const char *s;
325 	size_t slen;
326 	char **av;
327 {
328 	register char *bp;
329 	register char c;
330 	char **avp;
331 	register char *ap;
332 	size_t l;
333 	size_t len;
334 	static size_t buflen = 0;
335 	static char *buf = NULL;
336 
337 	if (tTd(39, 1))
338 	{
339 		sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
340 		if (av == NULL)
341 			sm_dprintf(" (nullv)");
342 		else
343 		{
344 			for (avp = av; *avp != NULL; avp++)
345 				sm_dprintf("\n\t%s", *avp);
346 		}
347 		sm_dprintf("\n");
348 	}
349 
350 	/* count expected size of output (can safely overestimate) */
351 	l = len = slen;
352 	if (av != NULL)
353 	{
354 		const char *sp = s;
355 
356 		while (l-- > 0 && (c = *sp++) != '\0')
357 		{
358 			if (c != '%')
359 				continue;
360 			if (l-- <= 0)
361 				break;
362 			c = *sp++;
363 			if (!(isascii(c) && isdigit(c)))
364 				continue;
365 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
366 				continue;
367 			if (*avp == NULL)
368 				continue;
369 			len += strlen(*avp);
370 		}
371 	}
372 	if (map->map_app != NULL)
373 		len += strlen(map->map_app);
374 	if (buflen < ++len)
375 	{
376 		/* need to malloc additional space */
377 		buflen = len;
378 		if (buf != NULL)
379 			sm_free(buf);
380 		buf = sm_pmalloc_x(buflen);
381 	}
382 
383 	bp = buf;
384 	if (av == NULL)
385 	{
386 		memmove(bp, s, slen);
387 		bp += slen;
388 
389 		/* assert(len > slen); */
390 		len -= slen;
391 	}
392 	else
393 	{
394 		while (slen-- > 0 && (c = *s++) != '\0')
395 		{
396 			if (c != '%')
397 			{
398   pushc:
399 				if (len-- <= 1)
400 				     break;
401 				*bp++ = c;
402 				continue;
403 			}
404 			if (slen-- <= 0 || (c = *s++) == '\0')
405 				c = '%';
406 			if (c == '%')
407 				goto pushc;
408 			if (!(isascii(c) && isdigit(c)))
409 			{
410 				if (len-- <= 1)
411 				     break;
412 				*bp++ = '%';
413 				goto pushc;
414 			}
415 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
416 				continue;
417 			if (*avp == NULL)
418 				continue;
419 
420 			/* transliterate argument into output string */
421 			for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
422 				*bp++ = c;
423 		}
424 	}
425 	if (map->map_app != NULL && len > 0)
426 		(void) sm_strlcpy(bp, map->map_app, len);
427 	else
428 		*bp = '\0';
429 	if (tTd(39, 1))
430 		sm_dprintf("map_rewrite => %s\n", buf);
431 	return buf;
432 }
433 /*
434 **  INITMAPS -- rebuild alias maps
435 **
436 **	Parameters:
437 **		none.
438 **
439 **	Returns:
440 **		none.
441 */
442 
443 void
initmaps()444 initmaps()
445 {
446 #if XDEBUG
447 	checkfd012("entering initmaps");
448 #endif /* XDEBUG */
449 	stabapply(map_init, 0);
450 #if XDEBUG
451 	checkfd012("exiting initmaps");
452 #endif /* XDEBUG */
453 }
454 /*
455 **  MAP_INIT -- rebuild a map
456 **
457 **	Parameters:
458 **		s -- STAB entry: if map: try to rebuild
459 **		unused -- unused variable
460 **
461 **	Returns:
462 **		none.
463 **
464 **	Side Effects:
465 **		will close already open rebuildable map.
466 */
467 
468 /* ARGSUSED1 */
469 static void
map_init(s,unused)470 map_init(s, unused)
471 	register STAB *s;
472 	int unused;
473 {
474 	register MAP *map;
475 
476 	/* has to be a map */
477 	if (s->s_symtype != ST_MAP)
478 		return;
479 
480 	map = &s->s_map;
481 	if (!bitset(MF_VALID, map->map_mflags))
482 		return;
483 
484 	if (tTd(38, 2))
485 		sm_dprintf("map_init(%s:%s, %s)\n",
486 			map->map_class->map_cname == NULL ? "NULL" :
487 				map->map_class->map_cname,
488 			map->map_mname == NULL ? "NULL" : map->map_mname,
489 			map->map_file == NULL ? "NULL" : map->map_file);
490 
491 	if (!bitset(MF_ALIAS, map->map_mflags) ||
492 	    !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
493 	{
494 		if (tTd(38, 3))
495 			sm_dprintf("\tnot rebuildable\n");
496 		return;
497 	}
498 
499 	/* if already open, close it (for nested open) */
500 	if (bitset(MF_OPEN, map->map_mflags))
501 	{
502 		map->map_mflags |= MF_CLOSING;
503 		map->map_class->map_close(map);
504 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
505 	}
506 
507 	(void) rebuildaliases(map, false);
508 	return;
509 }
510 /*
511 **  OPENMAP -- open a map
512 **
513 **	Parameters:
514 **		map -- map to open (it must not be open).
515 **
516 **	Returns:
517 **		whether open succeeded.
518 */
519 
520 bool
openmap(map)521 openmap(map)
522 	MAP *map;
523 {
524 	bool restore = false;
525 	bool savehold = HoldErrs;
526 	bool savequick = QuickAbort;
527 	int saveerrors = Errors;
528 
529 	if (!bitset(MF_VALID, map->map_mflags))
530 		return false;
531 
532 	/* better safe than sorry... */
533 	if (bitset(MF_OPEN, map->map_mflags))
534 		return true;
535 
536 	/* Don't send a map open error out via SMTP */
537 	if ((OnlyOneError || QuickAbort) &&
538 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
539 	{
540 		restore = true;
541 		HoldErrs = true;
542 		QuickAbort = false;
543 	}
544 
545 	errno = 0;
546 	if (map->map_class->map_open(map, O_RDONLY))
547 	{
548 		if (tTd(38, 4))
549 			sm_dprintf("openmap()\t%s:%s %s: valid\n",
550 				map->map_class->map_cname == NULL ? "NULL" :
551 					map->map_class->map_cname,
552 				map->map_mname == NULL ? "NULL" :
553 					map->map_mname,
554 				map->map_file == NULL ? "NULL" :
555 					map->map_file);
556 		map->map_mflags |= MF_OPEN;
557 		map->map_pid = CurrentPid;
558 	}
559 	else
560 	{
561 		if (tTd(38, 4))
562 			sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
563 				map->map_class->map_cname == NULL ? "NULL" :
564 					map->map_class->map_cname,
565 				map->map_mname == NULL ? "NULL" :
566 					map->map_mname,
567 				map->map_file == NULL ? "NULL" :
568 					map->map_file,
569 				errno == 0 ? "" : ": ",
570 				errno == 0 ? "" : sm_errstring(errno));
571 		if (!bitset(MF_OPTIONAL, map->map_mflags))
572 		{
573 			extern MAPCLASS BogusMapClass;
574 
575 			map->map_orgclass = map->map_class;
576 			map->map_class = &BogusMapClass;
577 			map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
578 			map->map_pid = CurrentPid;
579 		}
580 		else
581 		{
582 			/* don't try again */
583 			map->map_mflags &= ~MF_VALID;
584 		}
585 	}
586 
587 	if (restore)
588 	{
589 		Errors = saveerrors;
590 		HoldErrs = savehold;
591 		QuickAbort = savequick;
592 	}
593 
594 	return bitset(MF_OPEN, map->map_mflags);
595 }
596 /*
597 **  CLOSEMAPS -- close all open maps opened by the current pid.
598 **
599 **	Parameters:
600 **		bogus -- only close bogus maps.
601 **
602 **	Returns:
603 **		none.
604 */
605 
606 void
closemaps(bogus)607 closemaps(bogus)
608 	bool bogus;
609 {
610 	stabapply(map_close, bogus);
611 }
612 /*
613 **  MAP_CLOSE -- close a map opened by the current pid.
614 **
615 **	Parameters:
616 **		s -- STAB entry: if map: try to close
617 **		bogus -- only close bogus maps or MCF_NOTPERSIST maps.
618 **
619 **	Returns:
620 **		none.
621 */
622 
623 /* ARGSUSED1 */
624 static void
map_close(s,bogus)625 map_close(s, bogus)
626 	register STAB *s;
627 	int bogus;	/* int because of stabapply(), used as bool */
628 {
629 	MAP *map;
630 	extern MAPCLASS BogusMapClass;
631 
632 	if (s->s_symtype != ST_MAP)
633 		return;
634 
635 	map = &s->s_map;
636 
637 	/*
638 	**  close the map iff:
639 	**  it is valid and open and opened by this process
640 	**  and (!bogus or it's a bogus map or it is not persistent)
641 	**  negate this: return iff
642 	**  it is not valid or it is not open or not opened by this process
643 	**  or (bogus and it's not a bogus map and it's not not-persistent)
644 	*/
645 
646 	if (!bitset(MF_VALID, map->map_mflags) ||
647 	    !bitset(MF_OPEN, map->map_mflags) ||
648 	    bitset(MF_CLOSING, map->map_mflags) ||
649 	    map->map_pid != CurrentPid ||
650 	    (bogus && map->map_class != &BogusMapClass &&
651 	     !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
652 		return;
653 
654 	if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
655 	    map->map_orgclass != &BogusMapClass)
656 		map->map_class = map->map_orgclass;
657 	if (tTd(38, 5))
658 		sm_dprintf("closemaps: closing %s (%s)\n",
659 			map->map_mname == NULL ? "NULL" : map->map_mname,
660 			map->map_file == NULL ? "NULL" : map->map_file);
661 
662 	if (!bitset(MF_OPENBOGUS, map->map_mflags))
663 	{
664 		map->map_mflags |= MF_CLOSING;
665 		map->map_class->map_close(map);
666 	}
667 	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
668 }
669 
670 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
671 extern int getdomainname();
672 
673 /* this is mainly for backward compatibility in Sun environment */
674 static char *
sun_init_domain()675 sun_init_domain()
676 {
677 	/*
678 	**  Get the domain name from the kernel.
679 	**  If it does not start with a leading dot, then remove
680 	**  the first component.  Since leading dots are funny Unix
681 	**  files, we treat a leading "+" the same as a leading dot.
682 	**  Finally, force there to be at least one dot in the domain name
683 	**  (i.e. top-level domains are not allowed, like "com", must be
684 	**  something like "sun.com").
685 	*/
686 
687 	char buf[MAXNAME];
688 	char *period, *autodomain;
689 
690 	if (getdomainname(buf, sizeof buf) < 0)
691 		return NULL;
692 
693 	if (buf[0] == '\0')
694 		return NULL;
695 
696 	if (tTd(0, 20))
697 		printf("domainname = %s\n", buf);
698 
699 	if (buf[0] == '+')
700 		buf[0] = '.';
701 	period = strchr(buf, '.');
702 	if (period == NULL)
703 		autodomain = buf;
704 	else
705 		autodomain = period + 1;
706 	if (strchr(autodomain, '.') == NULL)
707 		return newstr(buf);
708 	else
709 		return newstr(autodomain);
710 }
711 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
712 
713 /*
714 **  GETCANONNAME -- look up name using service switch
715 **
716 **	Parameters:
717 **		host -- the host name to look up.
718 **		hbsize -- the size of the host buffer.
719 **		trymx -- if set, try MX records.
720 **		pttl -- pointer to return TTL (can be NULL).
721 **
722 **	Returns:
723 **		true -- if the host was found.
724 **		false -- otherwise.
725 */
726 
727 bool
getcanonname(host,hbsize,trymx,pttl)728 getcanonname(host, hbsize, trymx, pttl)
729 	char *host;
730 	int hbsize;
731 	bool trymx;
732 	int *pttl;
733 {
734 	int nmaps;
735 	int mapno;
736 	bool found = false;
737 	bool got_tempfail = false;
738 	auto int status = EX_UNAVAILABLE;
739 	char *maptype[MAXMAPSTACK];
740 	short mapreturn[MAXMAPACTIONS];
741 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
742 	bool should_try_nis_domain = false;
743 	static char *nis_domain = NULL;
744 #endif
745 
746 	nmaps = switch_map_find("hosts", maptype, mapreturn);
747 	if (pttl != 0)
748 		*pttl = SM_DEFAULT_TTL;
749 	for (mapno = 0; mapno < nmaps; mapno++)
750 	{
751 		int i;
752 
753 		if (tTd(38, 20))
754 			sm_dprintf("getcanonname(%s), trying %s\n",
755 				host, maptype[mapno]);
756 		if (strcmp("files", maptype[mapno]) == 0)
757 		{
758 			found = text_getcanonname(host, hbsize, &status);
759 		}
760 #if NIS
761 		else if (strcmp("nis", maptype[mapno]) == 0)
762 		{
763 			found = nis_getcanonname(host, hbsize, &status);
764 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
765 			if (nis_domain == NULL)
766 				nis_domain = sun_init_domain();
767 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
768 		}
769 #endif /* NIS */
770 #if NISPLUS
771 		else if (strcmp("nisplus", maptype[mapno]) == 0)
772 		{
773 			found = nisplus_getcanonname(host, hbsize, &status);
774 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
775 			if (nis_domain == NULL)
776 				nis_domain = sun_init_domain();
777 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
778 		}
779 #endif /* NISPLUS */
780 #if NAMED_BIND
781 		else if (strcmp("dns", maptype[mapno]) == 0)
782 		{
783 			found = dns_getcanonname(host, hbsize, trymx, &status,							 pttl);
784 		}
785 #endif /* NAMED_BIND */
786 #if NETINFO
787 		else if (strcmp("netinfo", maptype[mapno]) == 0)
788 		{
789 			found = ni_getcanonname(host, hbsize, &status);
790 		}
791 #endif /* NETINFO */
792 		else
793 		{
794 			found = false;
795 			status = EX_UNAVAILABLE;
796 		}
797 
798 		/*
799 		**  Heuristic: if $m is not set, we are running during system
800 		**  startup.  In this case, when a name is apparently found
801 		**  but has no dot, treat is as not found.  This avoids
802 		**  problems if /etc/hosts has no FQDN but is listed first
803 		**  in the service switch.
804 		*/
805 
806 		if (found &&
807 		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
808 			break;
809 
810 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
811 		if (found)
812 			should_try_nis_domain = true;
813 		/* but don't break, as we need to try all methods first */
814 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
815 
816 		/* see if we should continue */
817 		if (status == EX_TEMPFAIL)
818 		{
819 			i = MA_TRYAGAIN;
820 			got_tempfail = true;
821 		}
822 		else if (status == EX_NOTFOUND)
823 			i = MA_NOTFOUND;
824 		else
825 			i = MA_UNAVAIL;
826 		if (bitset(1 << mapno, mapreturn[i]))
827 			break;
828 	}
829 
830 	if (found)
831 	{
832 		char *d;
833 
834 		if (tTd(38, 20))
835 			sm_dprintf("getcanonname(%s), found\n", host);
836 
837 		/*
838 		**  If returned name is still single token, compensate
839 		**  by tagging on $m.  This is because some sites set
840 		**  up their DNS or NIS databases wrong.
841 		*/
842 
843 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
844 		{
845 			d = macvalue('m', CurEnv);
846 			if (d != NULL &&
847 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
848 			{
849 				if (host[strlen(host) - 1] != '.')
850 					(void) sm_strlcat2(host, ".", d,
851 							   hbsize);
852 				else
853 					(void) sm_strlcat(host, d, hbsize);
854 			}
855 			else
856 			{
857 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
858 				if (VendorCode == VENDOR_SUN &&
859 				    should_try_nis_domain)
860 				{
861 					goto try_nis_domain;
862 				}
863 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
864 				return false;
865 			}
866 		}
867 		return true;
868 	}
869 
870 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
871 	if (VendorCode == VENDOR_SUN && should_try_nis_domain)
872 	{
873   try_nis_domain:
874 		if (nis_domain != NULL &&
875 		    strlen(nis_domain) + strlen(host) + 1 < hbsize)
876 		{
877 			(void) sm_strlcat2(host, ".", nis_domain, hbsize);
878 			return true;
879 		}
880 	}
881 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
882 
883 	if (tTd(38, 20))
884 		sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
885 			status);
886 
887 	if (got_tempfail)
888 		SM_SET_H_ERRNO(TRY_AGAIN);
889 	else
890 		SM_SET_H_ERRNO(HOST_NOT_FOUND);
891 
892 	return false;
893 }
894 /*
895 **  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
896 **
897 **	Parameters:
898 **		name -- the name against which to match.
899 **		dot -- where to reinsert '.' to get FQDN
900 **		line -- the /etc/hosts line.
901 **		cbuf -- the location to store the result.
902 **		cbuflen -- the size of cbuf.
903 **
904 **	Returns:
905 **		true -- if the line matched the desired name.
906 **		false -- otherwise.
907 */
908 
909 static bool
extract_canonname(name,dot,line,cbuf,cbuflen)910 extract_canonname(name, dot, line, cbuf, cbuflen)
911 	char *name;
912 	char *dot;
913 	char *line;
914 	char cbuf[];
915 	int cbuflen;
916 {
917 	int i;
918 	char *p;
919 	bool found = false;
920 
921 	cbuf[0] = '\0';
922 	if (line[0] == '#')
923 		return false;
924 
925 	for (i = 1; ; i++)
926 	{
927 		char nbuf[MAXNAME + 1];
928 
929 		p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
930 		if (p == NULL)
931 			break;
932 		if (*p == '\0')
933 			continue;
934 		if (cbuf[0] == '\0' ||
935 		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
936 		{
937 			(void) sm_strlcpy(cbuf, p, cbuflen);
938 		}
939 		if (sm_strcasecmp(name, p) == 0)
940 			found = true;
941 		else if (dot != NULL)
942 		{
943 			/* try looking for the FQDN as well */
944 			*dot = '.';
945 			if (sm_strcasecmp(name, p) == 0)
946 				found = true;
947 			*dot = '\0';
948 		}
949 	}
950 	if (found && strchr(cbuf, '.') == NULL)
951 	{
952 		/* try to add a domain on the end of the name */
953 		char *domain = macvalue('m', CurEnv);
954 
955 		if (domain != NULL &&
956 		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
957 		{
958 			p = &cbuf[i];
959 			*p++ = '.';
960 			(void) sm_strlcpy(p, domain, cbuflen - i - 1);
961 		}
962 	}
963 	return found;
964 }
965 
966 /*
967 **  DNS modules
968 */
969 
970 #if NAMED_BIND
971 # if DNSMAP
972 
973 #  include "sm_resolve.h"
974 #  if NETINET || NETINET6
975 #   include <arpa/inet.h>
976 #  endif /* NETINET || NETINET6 */
977 
978 /*
979 **  DNS_MAP_OPEN -- stub to check proper value for dns map type
980 */
981 
982 bool
dns_map_open(map,mode)983 dns_map_open(map, mode)
984 	MAP *map;
985 	int mode;
986 {
987 	if (tTd(38,2))
988 		sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
989 
990 	mode &= O_ACCMODE;
991 	if (mode != O_RDONLY)
992 	{
993 		/* issue a pseudo-error message */
994 		errno = SM_EMAPCANTWRITE;
995 		return false;
996 	}
997 	return true;
998 }
999 
1000 /*
1001 **  DNS_MAP_PARSEARGS -- parse dns map definition args.
1002 **
1003 **	Parameters:
1004 **		map -- pointer to MAP
1005 **		args -- pointer to the args on the config line.
1006 **
1007 **	Returns:
1008 **		true -- if everything parsed OK.
1009 **		false -- otherwise.
1010 */
1011 
1012 #define map_sizelimit	map_lockfd	/* overload field */
1013 
1014 struct dns_map
1015 {
1016 	int dns_m_type;
1017 };
1018 
1019 bool
dns_map_parseargs(map,args)1020 dns_map_parseargs(map,args)
1021 	MAP *map;
1022 	char *args;
1023 {
1024 	register char *p = args;
1025 	struct dns_map *map_p;
1026 
1027 	map_p = (struct dns_map *) xalloc(sizeof(*map_p));
1028 	map_p->dns_m_type = -1;
1029 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1030 
1031 	for (;;)
1032 	{
1033 		while (isascii(*p) && isspace(*p))
1034 			p++;
1035 		if (*p != '-')
1036 			break;
1037 		switch (*++p)
1038 		{
1039 		  case 'N':
1040 			map->map_mflags |= MF_INCLNULL;
1041 			map->map_mflags &= ~MF_TRY0NULL;
1042 			break;
1043 
1044 		  case 'O':
1045 			map->map_mflags &= ~MF_TRY1NULL;
1046 			break;
1047 
1048 		  case 'o':
1049 			map->map_mflags |= MF_OPTIONAL;
1050 			break;
1051 
1052 		  case 'f':
1053 			map->map_mflags |= MF_NOFOLDCASE;
1054 			break;
1055 
1056 		  case 'm':
1057 			map->map_mflags |= MF_MATCHONLY;
1058 			break;
1059 
1060 		  case 'A':
1061 			map->map_mflags |= MF_APPEND;
1062 			break;
1063 
1064 		  case 'q':
1065 			map->map_mflags |= MF_KEEPQUOTES;
1066 			break;
1067 
1068 		  case 't':
1069 			map->map_mflags |= MF_NODEFER;
1070 			break;
1071 
1072 		  case 'a':
1073 			map->map_app = ++p;
1074 			break;
1075 
1076 		  case 'T':
1077 			map->map_tapp = ++p;
1078 			break;
1079 
1080 		  case 'd':
1081 			{
1082 				char *h;
1083 
1084 				++p;
1085 				h = strchr(p, ' ');
1086 				if (h != NULL)
1087 					*h = '\0';
1088 				map->map_timeout = convtime(p, 's');
1089 				if (h != NULL)
1090 					*h = ' ';
1091 			}
1092 			break;
1093 
1094 		  case 'r':
1095 			while (isascii(*++p) && isspace(*p))
1096 				continue;
1097 			map->map_retry = atoi(p);
1098 			break;
1099 
1100 		  case 'z':
1101 			if (*++p != '\\')
1102 				map->map_coldelim = *p;
1103 			else
1104 			{
1105 				switch (*++p)
1106 				{
1107 				  case 'n':
1108 					map->map_coldelim = '\n';
1109 					break;
1110 
1111 				  case 't':
1112 					map->map_coldelim = '\t';
1113 					break;
1114 
1115 				  default:
1116 					map->map_coldelim = '\\';
1117 				}
1118 			}
1119 			break;
1120 
1121 		  case 'Z':
1122 			while (isascii(*++p) && isspace(*p))
1123 				continue;
1124 			map->map_sizelimit = atoi(p);
1125 			break;
1126 
1127 			/* Start of dns_map specific args */
1128 		  case 'R':		/* search field */
1129 			{
1130 				char *h;
1131 
1132 				while (isascii(*++p) && isspace(*p))
1133 					continue;
1134 				h = strchr(p, ' ');
1135 				if (h != NULL)
1136 					*h = '\0';
1137 				map_p->dns_m_type = dns_string_to_type(p);
1138 				if (h != NULL)
1139 					*h = ' ';
1140 				if (map_p->dns_m_type < 0)
1141 					syserr("dns map %s: wrong type %s",
1142 						map->map_mname, p);
1143 			}
1144 			break;
1145 
1146 		  case 'B':		/* base domain */
1147 			{
1148 				char *h;
1149 
1150 				while (isascii(*++p) && isspace(*p))
1151 					continue;
1152 				h = strchr(p, ' ');
1153 				if (h != NULL)
1154 					*h = '\0';
1155 
1156 				/*
1157 				**  slight abuse of map->map_file; it isn't
1158 				**	used otherwise in this map type.
1159 				*/
1160 
1161 				map->map_file = newstr(p);
1162 				if (h != NULL)
1163 					*h = ' ';
1164 			}
1165 			break;
1166 		}
1167 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1168 			p++;
1169 		if (*p != '\0')
1170 			*p++ = '\0';
1171 	}
1172 	if (map_p->dns_m_type < 0)
1173 		syserr("dns map %s: missing -R type", map->map_mname);
1174 	if (map->map_app != NULL)
1175 		map->map_app = newstr(map->map_app);
1176 	if (map->map_tapp != NULL)
1177 		map->map_tapp = newstr(map->map_tapp);
1178 
1179 	/*
1180 	**  Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1181 	**  Even if this assumption is wrong, we use only one byte,
1182 	**  so it doesn't really matter.
1183 	*/
1184 
1185 	map->map_db1 = (ARBPTR_T) map_p;
1186 	return true;
1187 }
1188 
1189 /*
1190 **  DNS_MAP_LOOKUP -- perform dns map lookup.
1191 **
1192 **	Parameters:
1193 **		map -- pointer to MAP
1194 **		name -- name to lookup
1195 **		av -- arguments to interpolate into buf.
1196 **		statp -- pointer to status (EX_)
1197 **
1198 **	Returns:
1199 **		result of lookup if succeeded.
1200 **		NULL -- otherwise.
1201 */
1202 
1203 char *
dns_map_lookup(map,name,av,statp)1204 dns_map_lookup(map, name, av, statp)
1205 	MAP *map;
1206 	char *name;
1207 	char **av;
1208 	int *statp;
1209 {
1210 	int resnum = 0;
1211 	char *vp = NULL, *result = NULL;
1212 	size_t vsize;
1213 	struct dns_map *map_p;
1214 	RESOURCE_RECORD_T *rr = NULL;
1215 	DNS_REPLY_T *r = NULL;
1216 #  if NETINET6
1217 	static char buf6[INET6_ADDRSTRLEN];
1218 #  endif /* NETINET6 */
1219 
1220 	if (tTd(38, 20))
1221 		sm_dprintf("dns_map_lookup(%s, %s)\n",
1222 			   map->map_mname, name);
1223 
1224 	map_p = (struct dns_map *)(map->map_db1);
1225 	if (map->map_file != NULL && *map->map_file != '\0')
1226 	{
1227 		size_t len;
1228 		char *appdomain;
1229 
1230 		len = strlen(map->map_file) + strlen(name) + 2;
1231 		appdomain = (char *) sm_malloc(len);
1232 		if (appdomain == NULL)
1233 		{
1234 			*statp = EX_UNAVAILABLE;
1235 			return NULL;
1236 		}
1237 		(void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1238 		r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1239 				   map->map_timeout, map->map_retry);
1240 		sm_free(appdomain);
1241 	}
1242 	else
1243 	{
1244 		r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1245 				   map->map_timeout, map->map_retry);
1246 	}
1247 
1248 	if (r == NULL)
1249 	{
1250 		result = NULL;
1251 		if (h_errno == TRY_AGAIN || transienterror(errno))
1252 			*statp = EX_TEMPFAIL;
1253 		else
1254 			*statp = EX_NOTFOUND;
1255 		goto cleanup;
1256 	}
1257 	*statp = EX_OK;
1258 	for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1259 	{
1260 		char *type = NULL;
1261 		char *value = NULL;
1262 
1263 		switch (rr->rr_type)
1264 		{
1265 		  case T_NS:
1266 			type = "T_NS";
1267 			value = rr->rr_u.rr_txt;
1268 			break;
1269 		  case T_CNAME:
1270 			type = "T_CNAME";
1271 			value = rr->rr_u.rr_txt;
1272 			break;
1273 		  case T_AFSDB:
1274 			type = "T_AFSDB";
1275 			value = rr->rr_u.rr_mx->mx_r_domain;
1276 			break;
1277 		  case T_SRV:
1278 			type = "T_SRV";
1279 			value = rr->rr_u.rr_srv->srv_r_target;
1280 			break;
1281 		  case T_PTR:
1282 			type = "T_PTR";
1283 			value = rr->rr_u.rr_txt;
1284 			break;
1285 		  case T_TXT:
1286 			type = "T_TXT";
1287 			value = rr->rr_u.rr_txt;
1288 			break;
1289 		  case T_MX:
1290 			type = "T_MX";
1291 			value = rr->rr_u.rr_mx->mx_r_domain;
1292 			break;
1293 #  if NETINET
1294 		  case T_A:
1295 			type = "T_A";
1296 			value = inet_ntoa(*(rr->rr_u.rr_a));
1297 			break;
1298 #  endif /* NETINET */
1299 #  if NETINET6
1300 		  case T_AAAA:
1301 			type = "T_AAAA";
1302 			value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1303 					    sizeof(buf6));
1304 			break;
1305 #  endif /* NETINET6 */
1306 		}
1307 
1308 		(void) strreplnonprt(value, 'X');
1309 		if (map_p->dns_m_type != rr->rr_type)
1310 		{
1311 			if (tTd(38, 40))
1312 				sm_dprintf("\tskipping type %s (%d) value %s\n",
1313 					   type != NULL ? type : "<UNKNOWN>",
1314 					   rr->rr_type,
1315 					   value != NULL ? value : "<NO VALUE>");
1316 			continue;
1317 		}
1318 
1319 #  if NETINET6
1320 		if (rr->rr_type == T_AAAA && value == NULL)
1321 		{
1322 			result = NULL;
1323 			*statp = EX_DATAERR;
1324 			if (tTd(38, 40))
1325 				sm_dprintf("\tbad T_AAAA conversion\n");
1326 			goto cleanup;
1327 		}
1328 #  endif /* NETINET6 */
1329 		if (tTd(38, 40))
1330 			sm_dprintf("\tfound type %s (%d) value %s\n",
1331 				   type != NULL ? type : "<UNKNOWN>",
1332 				   rr->rr_type,
1333 				   value != NULL ? value : "<NO VALUE>");
1334 		if (value != NULL &&
1335 		    (map->map_coldelim == '\0' ||
1336 		     map->map_sizelimit == 1 ||
1337 		     bitset(MF_MATCHONLY, map->map_mflags)))
1338 		{
1339 			/* Only care about the first match */
1340 			vp = newstr(value);
1341 			break;
1342 		}
1343 		else if (vp == NULL)
1344 		{
1345 			/* First result */
1346 			vp = newstr(value);
1347 		}
1348 		else
1349 		{
1350 			/* concatenate the results */
1351 			int sz;
1352 			char *new;
1353 
1354 			sz = strlen(vp) + strlen(value) + 2;
1355 			new = xalloc(sz);
1356 			(void) sm_snprintf(new, sz, "%s%c%s",
1357 					   vp, map->map_coldelim, value);
1358 			sm_free(vp);
1359 			vp = new;
1360 			if (map->map_sizelimit > 0 &&
1361 			    ++resnum >= map->map_sizelimit)
1362 				break;
1363 		}
1364 	}
1365 	if (vp == NULL)
1366 	{
1367 		result = NULL;
1368 		*statp = EX_NOTFOUND;
1369 		if (tTd(38, 40))
1370 			sm_dprintf("\tno match found\n");
1371 		goto cleanup;
1372 	}
1373 
1374 	/* Cleanly truncate for rulesets */
1375 	truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1376 
1377 	vsize = strlen(vp);
1378 
1379 	if (LogLevel > 9)
1380 		sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1381 			  name, vp);
1382 	if (bitset(MF_MATCHONLY, map->map_mflags))
1383 		result = map_rewrite(map, name, strlen(name), NULL);
1384 	else
1385 		result = map_rewrite(map, vp, vsize, av);
1386 
1387   cleanup:
1388 	if (vp != NULL)
1389 		sm_free(vp);
1390 	if (r != NULL)
1391 		dns_free_data(r);
1392 	return result;
1393 }
1394 # endif /* DNSMAP */
1395 #endif /* NAMED_BIND */
1396 
1397 /*
1398 **  NDBM modules
1399 */
1400 
1401 #if NDBM
1402 
1403 /*
1404 **  NDBM_MAP_OPEN -- DBM-style map open
1405 */
1406 
1407 bool
ndbm_map_open(map,mode)1408 ndbm_map_open(map, mode)
1409 	MAP *map;
1410 	int mode;
1411 {
1412 	register DBM *dbm;
1413 	int save_errno;
1414 	int dfd;
1415 	int pfd;
1416 	long sff;
1417 	int ret;
1418 	int smode = S_IREAD;
1419 	char dirfile[MAXPATHLEN];
1420 	char pagfile[MAXPATHLEN];
1421 	struct stat st;
1422 	struct stat std, stp;
1423 
1424 	if (tTd(38, 2))
1425 		sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1426 			map->map_mname, map->map_file, mode);
1427 	map->map_lockfd = -1;
1428 	mode &= O_ACCMODE;
1429 
1430 	/* do initial file and directory checks */
1431 	if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
1432 			map->map_file, ".dir") >= sizeof(dirfile) ||
1433 	    sm_strlcpyn(pagfile, sizeof(pagfile), 2,
1434 			map->map_file, ".pag") >= sizeof(pagfile))
1435 	{
1436 		errno = 0;
1437 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1438 			syserr("dbm map \"%s\": map file %s name too long",
1439 				map->map_mname, map->map_file);
1440 		return false;
1441 	}
1442 	sff = SFF_ROOTOK|SFF_REGONLY;
1443 	if (mode == O_RDWR)
1444 	{
1445 		sff |= SFF_CREAT;
1446 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1447 			sff |= SFF_NOSLINK;
1448 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1449 			sff |= SFF_NOHLINK;
1450 		smode = S_IWRITE;
1451 	}
1452 	else
1453 	{
1454 		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1455 			sff |= SFF_NOWLINK;
1456 	}
1457 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1458 		sff |= SFF_SAFEDIRPATH;
1459 	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1460 		       sff, smode, &std);
1461 	if (ret == 0)
1462 		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1463 			       sff, smode, &stp);
1464 
1465 	if (ret != 0)
1466 	{
1467 		char *prob = "unsafe";
1468 
1469 		/* cannot open this map */
1470 		if (ret == ENOENT)
1471 			prob = "missing";
1472 		if (tTd(38, 2))
1473 			sm_dprintf("\t%s map file: %d\n", prob, ret);
1474 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1475 			syserr("dbm map \"%s\": %s map file %s",
1476 				map->map_mname, prob, map->map_file);
1477 		return false;
1478 	}
1479 	if (std.st_mode == ST_MODE_NOFILE)
1480 		mode |= O_CREAT|O_EXCL;
1481 
1482 # if LOCK_ON_OPEN
1483 	if (mode == O_RDONLY)
1484 		mode |= O_SHLOCK;
1485 	else
1486 		mode |= O_TRUNC|O_EXLOCK;
1487 # else /* LOCK_ON_OPEN */
1488 	if ((mode & O_ACCMODE) == O_RDWR)
1489 	{
1490 #  if NOFTRUNCATE
1491 		/*
1492 		**  Warning: race condition.  Try to lock the file as
1493 		**  quickly as possible after opening it.
1494 		**	This may also have security problems on some systems,
1495 		**	but there isn't anything we can do about it.
1496 		*/
1497 
1498 		mode |= O_TRUNC;
1499 #  else /* NOFTRUNCATE */
1500 		/*
1501 		**  This ugly code opens the map without truncating it,
1502 		**  locks the file, then truncates it.  Necessary to
1503 		**  avoid race conditions.
1504 		*/
1505 
1506 		int dirfd;
1507 		int pagfd;
1508 		long sff = SFF_CREAT|SFF_OPENASROOT;
1509 
1510 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1511 			sff |= SFF_NOSLINK;
1512 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1513 			sff |= SFF_NOHLINK;
1514 
1515 		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1516 		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1517 
1518 		if (dirfd < 0 || pagfd < 0)
1519 		{
1520 			save_errno = errno;
1521 			if (dirfd >= 0)
1522 				(void) close(dirfd);
1523 			if (pagfd >= 0)
1524 				(void) close(pagfd);
1525 			errno = save_errno;
1526 			syserr("ndbm_map_open: cannot create database %s",
1527 				map->map_file);
1528 			return false;
1529 		}
1530 		if (ftruncate(dirfd, (off_t) 0) < 0 ||
1531 		    ftruncate(pagfd, (off_t) 0) < 0)
1532 		{
1533 			save_errno = errno;
1534 			(void) close(dirfd);
1535 			(void) close(pagfd);
1536 			errno = save_errno;
1537 			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1538 				map->map_file);
1539 			return false;
1540 		}
1541 
1542 		/* if new file, get "before" bits for later filechanged check */
1543 		if (std.st_mode == ST_MODE_NOFILE &&
1544 		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1545 		{
1546 			save_errno = errno;
1547 			(void) close(dirfd);
1548 			(void) close(pagfd);
1549 			errno = save_errno;
1550 			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1551 				map->map_file);
1552 			return false;
1553 		}
1554 
1555 		/* have to save the lock for the duration (bletch) */
1556 		map->map_lockfd = dirfd;
1557 		(void) close(pagfd);
1558 
1559 		/* twiddle bits for dbm_open */
1560 		mode &= ~(O_CREAT|O_EXCL);
1561 #  endif /* NOFTRUNCATE */
1562 	}
1563 # endif /* LOCK_ON_OPEN */
1564 
1565 	/* open the database */
1566 	dbm = dbm_open(map->map_file, mode, DBMMODE);
1567 	if (dbm == NULL)
1568 	{
1569 		save_errno = errno;
1570 		if (bitset(MF_ALIAS, map->map_mflags) &&
1571 		    aliaswait(map, ".pag", false))
1572 			return true;
1573 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1574 		if (map->map_lockfd >= 0)
1575 			(void) close(map->map_lockfd);
1576 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1577 		errno = save_errno;
1578 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1579 			syserr("Cannot open DBM database %s", map->map_file);
1580 		return false;
1581 	}
1582 	dfd = dbm_dirfno(dbm);
1583 	pfd = dbm_pagfno(dbm);
1584 	if (dfd == pfd)
1585 	{
1586 		/* heuristic: if files are linked, this is actually gdbm */
1587 		dbm_close(dbm);
1588 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1589 		if (map->map_lockfd >= 0)
1590 			(void) close(map->map_lockfd);
1591 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1592 		errno = 0;
1593 		syserr("dbm map \"%s\": cannot support GDBM",
1594 			map->map_mname);
1595 		return false;
1596 	}
1597 
1598 	if (filechanged(dirfile, dfd, &std) ||
1599 	    filechanged(pagfile, pfd, &stp))
1600 	{
1601 		save_errno = errno;
1602 		dbm_close(dbm);
1603 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1604 		if (map->map_lockfd >= 0)
1605 			(void) close(map->map_lockfd);
1606 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1607 		errno = save_errno;
1608 		syserr("ndbm_map_open(%s): file changed after open",
1609 			map->map_file);
1610 		return false;
1611 	}
1612 
1613 	map->map_db1 = (ARBPTR_T) dbm;
1614 
1615 	/*
1616 	**  Need to set map_mtime before the call to aliaswait()
1617 	**  as aliaswait() will call map_lookup() which requires
1618 	**  map_mtime to be set
1619 	*/
1620 
1621 	if (fstat(pfd, &st) >= 0)
1622 		map->map_mtime = st.st_mtime;
1623 
1624 	if (mode == O_RDONLY)
1625 	{
1626 # if LOCK_ON_OPEN
1627 		if (dfd >= 0)
1628 			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1629 		if (pfd >= 0)
1630 			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1631 # endif /* LOCK_ON_OPEN */
1632 		if (bitset(MF_ALIAS, map->map_mflags) &&
1633 		    !aliaswait(map, ".pag", true))
1634 			return false;
1635 	}
1636 	else
1637 	{
1638 		map->map_mflags |= MF_LOCKED;
1639 		if (geteuid() == 0 && TrustedUid != 0)
1640 		{
1641 #  if HASFCHOWN
1642 			if (fchown(dfd, TrustedUid, -1) < 0 ||
1643 			    fchown(pfd, TrustedUid, -1) < 0)
1644 			{
1645 				int err = errno;
1646 
1647 				sm_syslog(LOG_ALERT, NOQID,
1648 					  "ownership change on %s failed: %s",
1649 					  map->map_file, sm_errstring(err));
1650 				message("050 ownership change on %s failed: %s",
1651 					map->map_file, sm_errstring(err));
1652 			}
1653 #  else /* HASFCHOWN */
1654 			sm_syslog(LOG_ALERT, NOQID,
1655 				  "no fchown(): cannot change ownership on %s",
1656 				  map->map_file);
1657 			message("050 no fchown(): cannot change ownership on %s",
1658 				map->map_file);
1659 #  endif /* HASFCHOWN */
1660 		}
1661 	}
1662 	return true;
1663 }
1664 
1665 
1666 /*
1667 **  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1668 */
1669 
1670 char *
ndbm_map_lookup(map,name,av,statp)1671 ndbm_map_lookup(map, name, av, statp)
1672 	MAP *map;
1673 	char *name;
1674 	char **av;
1675 	int *statp;
1676 {
1677 	datum key, val;
1678 	int dfd, pfd;
1679 	char keybuf[MAXNAME + 1];
1680 	struct stat stbuf;
1681 
1682 	if (tTd(38, 20))
1683 		sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1684 			map->map_mname, name);
1685 
1686 	key.dptr = name;
1687 	key.dsize = strlen(name);
1688 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1689 	{
1690 		if (key.dsize > sizeof(keybuf) - 1)
1691 			key.dsize = sizeof(keybuf) - 1;
1692 		memmove(keybuf, key.dptr, key.dsize);
1693 		keybuf[key.dsize] = '\0';
1694 		makelower(keybuf);
1695 		key.dptr = keybuf;
1696 	}
1697 lockdbm:
1698 	dfd = dbm_dirfno((DBM *) map->map_db1);
1699 	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1700 		(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1701 	pfd = dbm_pagfno((DBM *) map->map_db1);
1702 	if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1703 	    stbuf.st_mtime > map->map_mtime)
1704 	{
1705 		/* Reopen the database to sync the cache */
1706 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1707 								 : O_RDONLY;
1708 
1709 		if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1710 			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1711 		map->map_mflags |= MF_CLOSING;
1712 		map->map_class->map_close(map);
1713 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1714 		if (map->map_class->map_open(map, omode))
1715 		{
1716 			map->map_mflags |= MF_OPEN;
1717 			map->map_pid = CurrentPid;
1718 			if ((omode & O_ACCMODE) == O_RDWR)
1719 				map->map_mflags |= MF_WRITABLE;
1720 			goto lockdbm;
1721 		}
1722 		else
1723 		{
1724 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1725 			{
1726 				extern MAPCLASS BogusMapClass;
1727 
1728 				*statp = EX_TEMPFAIL;
1729 				map->map_orgclass = map->map_class;
1730 				map->map_class = &BogusMapClass;
1731 				map->map_mflags |= MF_OPEN;
1732 				map->map_pid = CurrentPid;
1733 				syserr("Cannot reopen NDBM database %s",
1734 					map->map_file);
1735 			}
1736 			return NULL;
1737 		}
1738 	}
1739 	val.dptr = NULL;
1740 	if (bitset(MF_TRY0NULL, map->map_mflags))
1741 	{
1742 		val = dbm_fetch((DBM *) map->map_db1, key);
1743 		if (val.dptr != NULL)
1744 			map->map_mflags &= ~MF_TRY1NULL;
1745 	}
1746 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1747 	{
1748 		key.dsize++;
1749 		val = dbm_fetch((DBM *) map->map_db1, key);
1750 		if (val.dptr != NULL)
1751 			map->map_mflags &= ~MF_TRY0NULL;
1752 	}
1753 	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1754 		(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1755 	if (val.dptr == NULL)
1756 		return NULL;
1757 	if (bitset(MF_MATCHONLY, map->map_mflags))
1758 		return map_rewrite(map, name, strlen(name), NULL);
1759 	else
1760 		return map_rewrite(map, val.dptr, val.dsize, av);
1761 }
1762 
1763 
1764 /*
1765 **  NDBM_MAP_STORE -- store a datum in the database
1766 */
1767 
1768 void
ndbm_map_store(map,lhs,rhs)1769 ndbm_map_store(map, lhs, rhs)
1770 	register MAP *map;
1771 	char *lhs;
1772 	char *rhs;
1773 {
1774 	datum key;
1775 	datum data;
1776 	int status;
1777 	char keybuf[MAXNAME + 1];
1778 
1779 	if (tTd(38, 12))
1780 		sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1781 			map->map_mname, lhs, rhs);
1782 
1783 	key.dsize = strlen(lhs);
1784 	key.dptr = lhs;
1785 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1786 	{
1787 		if (key.dsize > sizeof(keybuf) - 1)
1788 			key.dsize = sizeof(keybuf) - 1;
1789 		memmove(keybuf, key.dptr, key.dsize);
1790 		keybuf[key.dsize] = '\0';
1791 		makelower(keybuf);
1792 		key.dptr = keybuf;
1793 	}
1794 
1795 	data.dsize = strlen(rhs);
1796 	data.dptr = rhs;
1797 
1798 	if (bitset(MF_INCLNULL, map->map_mflags))
1799 	{
1800 		key.dsize++;
1801 		data.dsize++;
1802 	}
1803 
1804 	status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1805 	if (status > 0)
1806 	{
1807 		if (!bitset(MF_APPEND, map->map_mflags))
1808 			message("050 Warning: duplicate alias name %s", lhs);
1809 		else
1810 		{
1811 			static char *buf = NULL;
1812 			static int bufsiz = 0;
1813 			auto int xstat;
1814 			datum old;
1815 
1816 			old.dptr = ndbm_map_lookup(map, key.dptr,
1817 						   (char **) NULL, &xstat);
1818 			if (old.dptr != NULL && *(char *) old.dptr != '\0')
1819 			{
1820 				old.dsize = strlen(old.dptr);
1821 				if (data.dsize + old.dsize + 2 > bufsiz)
1822 				{
1823 					if (buf != NULL)
1824 						(void) sm_free(buf);
1825 					bufsiz = data.dsize + old.dsize + 2;
1826 					buf = sm_pmalloc_x(bufsiz);
1827 				}
1828 				(void) sm_strlcpyn(buf, bufsiz, 3,
1829 					data.dptr, ",", old.dptr);
1830 				data.dsize = data.dsize + old.dsize + 1;
1831 				data.dptr = buf;
1832 				if (tTd(38, 9))
1833 					sm_dprintf("ndbm_map_store append=%s\n",
1834 						data.dptr);
1835 			}
1836 		}
1837 		status = dbm_store((DBM *) map->map_db1,
1838 				   key, data, DBM_REPLACE);
1839 	}
1840 	if (status != 0)
1841 		syserr("readaliases: dbm put (%s): %d", lhs, status);
1842 }
1843 
1844 
1845 /*
1846 **  NDBM_MAP_CLOSE -- close the database
1847 */
1848 
1849 void
ndbm_map_close(map)1850 ndbm_map_close(map)
1851 	register MAP  *map;
1852 {
1853 	if (tTd(38, 9))
1854 		sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1855 			map->map_mname, map->map_file, map->map_mflags);
1856 
1857 	if (bitset(MF_WRITABLE, map->map_mflags))
1858 	{
1859 # ifdef NDBM_YP_COMPAT
1860 		bool inclnull;
1861 		char buf[MAXHOSTNAMELEN];
1862 
1863 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
1864 		map->map_mflags &= ~MF_INCLNULL;
1865 
1866 		if (strstr(map->map_file, "/yp/") != NULL)
1867 		{
1868 			long save_mflags = map->map_mflags;
1869 
1870 			map->map_mflags |= MF_NOFOLDCASE;
1871 
1872 			(void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
1873 			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1874 
1875 			(void) gethostname(buf, sizeof(buf));
1876 			ndbm_map_store(map, "YP_MASTER_NAME", buf);
1877 
1878 			map->map_mflags = save_mflags;
1879 		}
1880 
1881 		if (inclnull)
1882 			map->map_mflags |= MF_INCLNULL;
1883 # endif /* NDBM_YP_COMPAT */
1884 
1885 		/* write out the distinguished alias */
1886 		ndbm_map_store(map, "@", "@");
1887 	}
1888 	dbm_close((DBM *) map->map_db1);
1889 
1890 	/* release lock (if needed) */
1891 # if !LOCK_ON_OPEN
1892 	if (map->map_lockfd >= 0)
1893 		(void) close(map->map_lockfd);
1894 # endif /* !LOCK_ON_OPEN */
1895 }
1896 
1897 #endif /* NDBM */
1898 /*
1899 **  NEWDB (Hash and BTree) Modules
1900 */
1901 
1902 #if NEWDB
1903 
1904 /*
1905 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1906 **
1907 **	These do rather bizarre locking.  If you can lock on open,
1908 **	do that to avoid the condition of opening a database that
1909 **	is being rebuilt.  If you don't, we'll try to fake it, but
1910 **	there will be a race condition.  If opening for read-only,
1911 **	we immediately release the lock to avoid freezing things up.
1912 **	We really ought to hold the lock, but guarantee that we won't
1913 **	be pokey about it.  That's hard to do.
1914 */
1915 
1916 /* these should be K line arguments */
1917 # if DB_VERSION_MAJOR < 2
1918 #  define db_cachesize	cachesize
1919 #  define h_nelem	nelem
1920 #  ifndef DB_CACHE_SIZE
1921 #   define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
1922 #  endif /* ! DB_CACHE_SIZE */
1923 #  ifndef DB_HASH_NELEM
1924 #   define DB_HASH_NELEM	4096		/* (starting) size of hash table */
1925 #  endif /* ! DB_HASH_NELEM */
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 # endif /* DB_VERSION_MAJOR < 3 */
1985 
1986 	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1987 }
1988 
1989 static bool
1990 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1991 	MAP *map;
1992 	int mode;
1993 	char *mapclassname;
1994 	DBTYPE dbtype;
1995 # if DB_VERSION_MAJOR < 2
1996 	const void *openinfo;
1997 # endif /* DB_VERSION_MAJOR < 2 */
1998 # if DB_VERSION_MAJOR == 2
1999 	DB_INFO *openinfo;
2000 # endif /* DB_VERSION_MAJOR == 2 */
2001 # if DB_VERSION_MAJOR > 2
2002 	void **openinfo;
2003 # endif /* DB_VERSION_MAJOR > 2 */
2004 {
2005 	DB *db = NULL;
2006 	int i;
2007 	int omode;
2008 	int smode = S_IREAD;
2009 	int fd;
2010 	long sff;
2011 	int save_errno;
2012 	struct stat st;
2013 	char buf[MAXPATHLEN];
2014 
2015 	/* do initial file and directory checks */
2016 	if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2017 	{
2018 		errno = 0;
2019 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2020 			syserr("map \"%s\": map file %s name too long",
2021 				map->map_mname, map->map_file);
2022 		return false;
2023 	}
2024 	i = strlen(buf);
2025 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
2026 	{
2027 		if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
2028 		{
2029 			errno = 0;
2030 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2031 				syserr("map \"%s\": map file %s name too long",
2032 					map->map_mname, map->map_file);
2033 			return false;
2034 		}
2035 	}
2036 
2037 	mode &= O_ACCMODE;
2038 	omode = mode;
2039 
2040 	sff = SFF_ROOTOK|SFF_REGONLY;
2041 	if (mode == O_RDWR)
2042 	{
2043 		sff |= SFF_CREAT;
2044 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
2045 			sff |= SFF_NOSLINK;
2046 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
2047 			sff |= SFF_NOHLINK;
2048 		smode = S_IWRITE;
2049 	}
2050 	else
2051 	{
2052 		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
2053 			sff |= SFF_NOWLINK;
2054 	}
2055 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2056 		sff |= SFF_SAFEDIRPATH;
2057 	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2058 
2059 	if (i != 0)
2060 	{
2061 		char *prob = "unsafe";
2062 
2063 		/* cannot open this map */
2064 		if (i == ENOENT)
2065 			prob = "missing";
2066 		if (tTd(38, 2))
2067 			sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2068 		errno = i;
2069 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2070 			syserr("%s map \"%s\": %s map file %s",
2071 				mapclassname, map->map_mname, prob, buf);
2072 		return false;
2073 	}
2074 	if (st.st_mode == ST_MODE_NOFILE)
2075 		omode |= O_CREAT|O_EXCL;
2076 
2077 	map->map_lockfd = -1;
2078 
2079 # if LOCK_ON_OPEN
2080 	if (mode == O_RDWR)
2081 		omode |= O_TRUNC|O_EXLOCK;
2082 	else
2083 		omode |= O_SHLOCK;
2084 # else /* LOCK_ON_OPEN */
2085 	/*
2086 	**  Pre-lock the file to avoid race conditions.  In particular,
2087 	**  since dbopen returns NULL if the file is zero length, we
2088 	**  must have a locked instance around the dbopen.
2089 	*/
2090 
2091 	fd = open(buf, omode, DBMMODE);
2092 	if (fd < 0)
2093 	{
2094 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2095 			syserr("db_map_open: cannot pre-open database %s", buf);
2096 		return false;
2097 	}
2098 
2099 	/* make sure no baddies slipped in just before the open... */
2100 	if (filechanged(buf, fd, &st))
2101 	{
2102 		save_errno = errno;
2103 		(void) close(fd);
2104 		errno = save_errno;
2105 		syserr("db_map_open(%s): file changed after pre-open", buf);
2106 		return false;
2107 	}
2108 
2109 	/* if new file, get the "before" bits for later filechanged check */
2110 	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2111 	{
2112 		save_errno = errno;
2113 		(void) close(fd);
2114 		errno = save_errno;
2115 		syserr("db_map_open(%s): cannot fstat pre-opened file",
2116 			buf);
2117 		return false;
2118 	}
2119 
2120 	/* actually lock the pre-opened file */
2121 	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2122 		syserr("db_map_open: cannot lock %s", buf);
2123 
2124 	/* set up mode bits for dbopen */
2125 	if (mode == O_RDWR)
2126 		omode |= O_TRUNC;
2127 	omode &= ~(O_EXCL|O_CREAT);
2128 # endif /* LOCK_ON_OPEN */
2129 
2130 # if DB_VERSION_MAJOR < 2
2131 	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2132 # else /* DB_VERSION_MAJOR < 2 */
2133 	{
2134 		int flags = 0;
2135 #  if DB_VERSION_MAJOR > 2
2136 		int ret;
2137 #  endif /* DB_VERSION_MAJOR > 2 */
2138 
2139 		if (mode == O_RDONLY)
2140 			flags |= DB_RDONLY;
2141 		if (bitset(O_CREAT, omode))
2142 			flags |= DB_CREATE;
2143 		if (bitset(O_TRUNC, omode))
2144 			flags |= DB_TRUNCATE;
2145 		SM_DB_FLAG_ADD(flags);
2146 
2147 #  if DB_VERSION_MAJOR > 2
2148 		ret = db_create(&db, NULL, 0);
2149 #  ifdef DB_CACHE_SIZE
2150 		if (ret == 0 && db != NULL)
2151 		{
2152 			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2153 			if (ret != 0)
2154 			{
2155 				(void) db->close(db, 0);
2156 				db = NULL;
2157 			}
2158 		}
2159 #  endif /* DB_CACHE_SIZE */
2160 #  ifdef DB_HASH_NELEM
2161 		if (dbtype == DB_HASH && ret == 0 && db != NULL)
2162 		{
2163 			ret = db->set_h_nelem(db, DB_HASH_NELEM);
2164 			if (ret != 0)
2165 			{
2166 				(void) db->close(db, 0);
2167 				db = NULL;
2168 			}
2169 		}
2170 #  endif /* DB_HASH_NELEM */
2171 		if (ret == 0 && db != NULL)
2172 		{
2173 			ret = db->open(db,
2174 					DBTXN	/* transaction for DB 4.1 */
2175 					buf, NULL, dbtype, flags, DBMMODE);
2176 			if (ret != 0)
2177 			{
2178 #ifdef DB_OLD_VERSION
2179 				if (ret == DB_OLD_VERSION)
2180 					ret = EINVAL;
2181 #endif /* DB_OLD_VERSION */
2182 				(void) db->close(db, 0);
2183 				db = NULL;
2184 			}
2185 		}
2186 		errno = ret;
2187 #  else /* DB_VERSION_MAJOR > 2 */
2188 		errno = db_open(buf, dbtype, flags, DBMMODE,
2189 				NULL, openinfo, &db);
2190 #  endif /* DB_VERSION_MAJOR > 2 */
2191 	}
2192 # endif /* DB_VERSION_MAJOR < 2 */
2193 	save_errno = errno;
2194 
2195 # if !LOCK_ON_OPEN
2196 	if (mode == O_RDWR)
2197 		map->map_lockfd = fd;
2198 	else
2199 		(void) close(fd);
2200 # endif /* !LOCK_ON_OPEN */
2201 
2202 	if (db == NULL)
2203 	{
2204 		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2205 		    aliaswait(map, ".db", false))
2206 			return true;
2207 # if !LOCK_ON_OPEN
2208 		if (map->map_lockfd >= 0)
2209 			(void) close(map->map_lockfd);
2210 # endif /* !LOCK_ON_OPEN */
2211 		errno = save_errno;
2212 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2213 			syserr("Cannot open %s database %s",
2214 				mapclassname, buf);
2215 		return false;
2216 	}
2217 
2218 # if DB_VERSION_MAJOR < 2
2219 	fd = db->fd(db);
2220 # else /* DB_VERSION_MAJOR < 2 */
2221 	fd = -1;
2222 	errno = db->fd(db, &fd);
2223 # endif /* DB_VERSION_MAJOR < 2 */
2224 	if (filechanged(buf, fd, &st))
2225 	{
2226 		save_errno = errno;
2227 # if DB_VERSION_MAJOR < 2
2228 		(void) db->close(db);
2229 # else /* DB_VERSION_MAJOR < 2 */
2230 		errno = db->close(db, 0);
2231 # endif /* DB_VERSION_MAJOR < 2 */
2232 # if !LOCK_ON_OPEN
2233 		if (map->map_lockfd >= 0)
2234 			(void) close(map->map_lockfd);
2235 # endif /* !LOCK_ON_OPEN */
2236 		errno = save_errno;
2237 		syserr("db_map_open(%s): file changed after open", buf);
2238 		return false;
2239 	}
2240 
2241 	if (mode == O_RDWR)
2242 		map->map_mflags |= MF_LOCKED;
2243 # if LOCK_ON_OPEN
2244 	if (fd >= 0 && mode == O_RDONLY)
2245 	{
2246 		(void) lockfile(fd, buf, NULL, LOCK_UN);
2247 	}
2248 # endif /* LOCK_ON_OPEN */
2249 
2250 	/* try to make sure that at least the database header is on disk */
2251 	if (mode == O_RDWR)
2252 	{
2253 		(void) db->sync(db, 0);
2254 		if (geteuid() == 0 && TrustedUid != 0)
2255 		{
2256 #  if HASFCHOWN
2257 			if (fchown(fd, TrustedUid, -1) < 0)
2258 			{
2259 				int err = errno;
2260 
2261 				sm_syslog(LOG_ALERT, NOQID,
2262 					  "ownership change on %s failed: %s",
2263 					  buf, sm_errstring(err));
2264 				message("050 ownership change on %s failed: %s",
2265 					buf, sm_errstring(err));
2266 			}
2267 #  else /* HASFCHOWN */
2268 			sm_syslog(LOG_ALERT, NOQID,
2269 				  "no fchown(): cannot change ownership on %s",
2270 				  map->map_file);
2271 			message("050 no fchown(): cannot change ownership on %s",
2272 				map->map_file);
2273 #  endif /* HASFCHOWN */
2274 		}
2275 	}
2276 
2277 	map->map_db2 = (ARBPTR_T) db;
2278 
2279 	/*
2280 	**  Need to set map_mtime before the call to aliaswait()
2281 	**  as aliaswait() will call map_lookup() which requires
2282 	**  map_mtime to be set
2283 	*/
2284 
2285 	if (fd >= 0 && fstat(fd, &st) >= 0)
2286 		map->map_mtime = st.st_mtime;
2287 
2288 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2289 	    !aliaswait(map, ".db", true))
2290 		return false;
2291 	return true;
2292 }
2293 
2294 
2295 /*
2296 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2297 */
2298 
2299 char *
db_map_lookup(map,name,av,statp)2300 db_map_lookup(map, name, av, statp)
2301 	MAP *map;
2302 	char *name;
2303 	char **av;
2304 	int *statp;
2305 {
2306 	DBT key, val;
2307 	register DB *db = (DB *) map->map_db2;
2308 	int i;
2309 	int st;
2310 	int save_errno;
2311 	int fd;
2312 	struct stat stbuf;
2313 	char keybuf[MAXNAME + 1];
2314 	char buf[MAXPATHLEN];
2315 
2316 	memset(&key, '\0', sizeof(key));
2317 	memset(&val, '\0', sizeof(val));
2318 
2319 	if (tTd(38, 20))
2320 		sm_dprintf("db_map_lookup(%s, %s)\n",
2321 			map->map_mname, name);
2322 
2323 	if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2324 	{
2325 		errno = 0;
2326 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2327 			syserr("map \"%s\": map file %s name too long",
2328 				map->map_mname, map->map_file);
2329 		return NULL;
2330 	}
2331 	i = strlen(buf);
2332 	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2333 		buf[i - 3] = '\0';
2334 
2335 	key.size = strlen(name);
2336 	if (key.size > sizeof(keybuf) - 1)
2337 		key.size = sizeof(keybuf) - 1;
2338 	key.data = keybuf;
2339 	memmove(keybuf, name, key.size);
2340 	keybuf[key.size] = '\0';
2341 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2342 		makelower(keybuf);
2343   lockdb:
2344 # if DB_VERSION_MAJOR < 2
2345 	fd = db->fd(db);
2346 # else /* DB_VERSION_MAJOR < 2 */
2347 	fd = -1;
2348 	errno = db->fd(db, &fd);
2349 # endif /* DB_VERSION_MAJOR < 2 */
2350 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2351 		(void) lockfile(fd, buf, ".db", LOCK_SH);
2352 	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2353 	{
2354 		/* Reopen the database to sync the cache */
2355 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2356 								 : O_RDONLY;
2357 
2358 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2359 			(void) lockfile(fd, buf, ".db", LOCK_UN);
2360 		map->map_mflags |= MF_CLOSING;
2361 		map->map_class->map_close(map);
2362 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2363 		if (map->map_class->map_open(map, omode))
2364 		{
2365 			map->map_mflags |= MF_OPEN;
2366 			map->map_pid = CurrentPid;
2367 			if ((omode & O_ACCMODE) == O_RDWR)
2368 				map->map_mflags |= MF_WRITABLE;
2369 			db = (DB *) map->map_db2;
2370 			goto lockdb;
2371 		}
2372 		else
2373 		{
2374 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2375 			{
2376 				extern MAPCLASS BogusMapClass;
2377 
2378 				*statp = EX_TEMPFAIL;
2379 				map->map_orgclass = map->map_class;
2380 				map->map_class = &BogusMapClass;
2381 				map->map_mflags |= MF_OPEN;
2382 				map->map_pid = CurrentPid;
2383 				syserr("Cannot reopen DB database %s",
2384 					map->map_file);
2385 			}
2386 			return NULL;
2387 		}
2388 	}
2389 
2390 	st = 1;
2391 	if (bitset(MF_TRY0NULL, map->map_mflags))
2392 	{
2393 # if DB_VERSION_MAJOR < 2
2394 		st = db->get(db, &key, &val, 0);
2395 # else /* DB_VERSION_MAJOR < 2 */
2396 		errno = db->get(db, NULL, &key, &val, 0);
2397 		switch (errno)
2398 		{
2399 		  case DB_NOTFOUND:
2400 		  case DB_KEYEMPTY:
2401 			st = 1;
2402 			break;
2403 
2404 		  case 0:
2405 			st = 0;
2406 			break;
2407 
2408 		  default:
2409 			st = -1;
2410 			break;
2411 		}
2412 # endif /* DB_VERSION_MAJOR < 2 */
2413 		if (st == 0)
2414 			map->map_mflags &= ~MF_TRY1NULL;
2415 	}
2416 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2417 	{
2418 		key.size++;
2419 # if DB_VERSION_MAJOR < 2
2420 		st = db->get(db, &key, &val, 0);
2421 # else /* DB_VERSION_MAJOR < 2 */
2422 		errno = db->get(db, NULL, &key, &val, 0);
2423 		switch (errno)
2424 		{
2425 		  case DB_NOTFOUND:
2426 		  case DB_KEYEMPTY:
2427 			st = 1;
2428 			break;
2429 
2430 		  case 0:
2431 			st = 0;
2432 			break;
2433 
2434 		  default:
2435 			st = -1;
2436 			break;
2437 		}
2438 # endif /* DB_VERSION_MAJOR < 2 */
2439 		if (st == 0)
2440 			map->map_mflags &= ~MF_TRY0NULL;
2441 	}
2442 	save_errno = errno;
2443 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2444 		(void) lockfile(fd, buf, ".db", LOCK_UN);
2445 	if (st != 0)
2446 	{
2447 		errno = save_errno;
2448 		if (st < 0)
2449 			syserr("db_map_lookup: get (%s)", name);
2450 		return NULL;
2451 	}
2452 	if (bitset(MF_MATCHONLY, map->map_mflags))
2453 		return map_rewrite(map, name, strlen(name), NULL);
2454 	else
2455 		return map_rewrite(map, val.data, val.size, av);
2456 }
2457 
2458 
2459 /*
2460 **  DB_MAP_STORE -- store a datum in the NEWDB database
2461 */
2462 
2463 void
db_map_store(map,lhs,rhs)2464 db_map_store(map, lhs, rhs)
2465 	register MAP *map;
2466 	char *lhs;
2467 	char *rhs;
2468 {
2469 	int status;
2470 	DBT key;
2471 	DBT data;
2472 	register DB *db = map->map_db2;
2473 	char keybuf[MAXNAME + 1];
2474 
2475 	memset(&key, '\0', sizeof(key));
2476 	memset(&data, '\0', sizeof(data));
2477 
2478 	if (tTd(38, 12))
2479 		sm_dprintf("db_map_store(%s, %s, %s)\n",
2480 			map->map_mname, lhs, rhs);
2481 
2482 	key.size = strlen(lhs);
2483 	key.data = lhs;
2484 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2485 	{
2486 		if (key.size > sizeof(keybuf) - 1)
2487 			key.size = sizeof(keybuf) - 1;
2488 		memmove(keybuf, key.data, key.size);
2489 		keybuf[key.size] = '\0';
2490 		makelower(keybuf);
2491 		key.data = keybuf;
2492 	}
2493 
2494 	data.size = strlen(rhs);
2495 	data.data = rhs;
2496 
2497 	if (bitset(MF_INCLNULL, map->map_mflags))
2498 	{
2499 		key.size++;
2500 		data.size++;
2501 	}
2502 
2503 # if DB_VERSION_MAJOR < 2
2504 	status = db->put(db, &key, &data, R_NOOVERWRITE);
2505 # else /* DB_VERSION_MAJOR < 2 */
2506 	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2507 	switch (errno)
2508 	{
2509 	  case DB_KEYEXIST:
2510 		status = 1;
2511 		break;
2512 
2513 	  case 0:
2514 		status = 0;
2515 		break;
2516 
2517 	  default:
2518 		status = -1;
2519 		break;
2520 	}
2521 # endif /* DB_VERSION_MAJOR < 2 */
2522 	if (status > 0)
2523 	{
2524 		if (!bitset(MF_APPEND, map->map_mflags))
2525 			message("050 Warning: duplicate alias name %s", lhs);
2526 		else
2527 		{
2528 			static char *buf = NULL;
2529 			static int bufsiz = 0;
2530 			DBT old;
2531 
2532 			memset(&old, '\0', sizeof(old));
2533 
2534 			old.data = db_map_lookup(map, key.data,
2535 						 (char **) NULL, &status);
2536 			if (old.data != NULL)
2537 			{
2538 				old.size = strlen(old.data);
2539 				if (data.size + old.size + 2 > (size_t) bufsiz)
2540 				{
2541 					if (buf != NULL)
2542 						sm_free(buf);
2543 					bufsiz = data.size + old.size + 2;
2544 					buf = sm_pmalloc_x(bufsiz);
2545 				}
2546 				(void) sm_strlcpyn(buf, bufsiz, 3,
2547 					(char *) data.data, ",",
2548 					(char *) old.data);
2549 				data.size = data.size + old.size + 1;
2550 				data.data = buf;
2551 				if (tTd(38, 9))
2552 					sm_dprintf("db_map_store append=%s\n",
2553 						(char *) data.data);
2554 			}
2555 		}
2556 # if DB_VERSION_MAJOR < 2
2557 		status = db->put(db, &key, &data, 0);
2558 # else /* DB_VERSION_MAJOR < 2 */
2559 		status = errno = db->put(db, NULL, &key, &data, 0);
2560 # endif /* DB_VERSION_MAJOR < 2 */
2561 	}
2562 	if (status != 0)
2563 		syserr("readaliases: db put (%s)", lhs);
2564 }
2565 
2566 
2567 /*
2568 **  DB_MAP_CLOSE -- add distinguished entries and close the database
2569 */
2570 
2571 void
db_map_close(map)2572 db_map_close(map)
2573 	MAP *map;
2574 {
2575 	register DB *db = map->map_db2;
2576 
2577 	if (tTd(38, 9))
2578 		sm_dprintf("db_map_close(%s, %s, %lx)\n",
2579 			map->map_mname, map->map_file, map->map_mflags);
2580 
2581 	if (bitset(MF_WRITABLE, map->map_mflags))
2582 	{
2583 		/* write out the distinguished alias */
2584 		db_map_store(map, "@", "@");
2585 	}
2586 
2587 	(void) db->sync(db, 0);
2588 
2589 # if !LOCK_ON_OPEN
2590 	if (map->map_lockfd >= 0)
2591 		(void) close(map->map_lockfd);
2592 # endif /* !LOCK_ON_OPEN */
2593 
2594 # if DB_VERSION_MAJOR < 2
2595 	if (db->close(db) != 0)
2596 # else /* DB_VERSION_MAJOR < 2 */
2597 	/*
2598 	**  Berkeley DB can use internal shared memory
2599 	**  locking for its memory pool.  Closing a map
2600 	**  opened by another process will interfere
2601 	**  with the shared memory and locks of the parent
2602 	**  process leaving things in a bad state.
2603 	*/
2604 
2605 	/*
2606 	**  If this map was not opened by the current
2607 	**  process, do not close the map but recover
2608 	**  the file descriptor.
2609 	*/
2610 
2611 	if (map->map_pid != CurrentPid)
2612 	{
2613 		int fd = -1;
2614 
2615 		errno = db->fd(db, &fd);
2616 		if (fd >= 0)
2617 			(void) close(fd);
2618 		return;
2619 	}
2620 
2621 	if ((errno = db->close(db, 0)) != 0)
2622 # endif /* DB_VERSION_MAJOR < 2 */
2623 		syserr("db_map_close(%s, %s, %lx): db close failure",
2624 			map->map_mname, map->map_file, map->map_mflags);
2625 }
2626 #endif /* NEWDB */
2627 /*
2628 **  NIS Modules
2629 */
2630 
2631 #if NIS
2632 
2633 # ifndef YPERR_BUSY
2634 #  define YPERR_BUSY	16
2635 # endif /* ! YPERR_BUSY */
2636 
2637 /*
2638 **  NIS_MAP_OPEN -- open DBM map
2639 */
2640 
2641 bool
nis_map_open(map,mode)2642 nis_map_open(map, mode)
2643 	MAP *map;
2644 	int mode;
2645 {
2646 	int yperr;
2647 	register char *p;
2648 	auto char *vp;
2649 	auto int vsize;
2650 
2651 	if (tTd(38, 2))
2652 		sm_dprintf("nis_map_open(%s, %s, %d)\n",
2653 			map->map_mname, map->map_file, mode);
2654 
2655 	mode &= O_ACCMODE;
2656 	if (mode != O_RDONLY)
2657 	{
2658 		/* issue a pseudo-error message */
2659 		errno = SM_EMAPCANTWRITE;
2660 		return false;
2661 	}
2662 
2663 	p = strchr(map->map_file, '@');
2664 	if (p != NULL)
2665 	{
2666 		*p++ = '\0';
2667 		if (*p != '\0')
2668 			map->map_domain = p;
2669 	}
2670 
2671 	if (*map->map_file == '\0')
2672 		map->map_file = "mail.aliases";
2673 
2674 	if (map->map_domain == NULL)
2675 	{
2676 		yperr = yp_get_default_domain(&map->map_domain);
2677 		if (yperr != 0)
2678 		{
2679 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2680 				syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2681 				       map->map_file);
2682 			return false;
2683 		}
2684 	}
2685 
2686 	/* check to see if this map actually exists */
2687 	vp = NULL;
2688 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2689 			&vp, &vsize);
2690 	if (tTd(38, 10))
2691 		sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2692 			map->map_domain, map->map_file, yperr_string(yperr));
2693 	if (vp != NULL)
2694 		sm_free(vp);
2695 
2696 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2697 	{
2698 		/*
2699 		**  We ought to be calling aliaswait() here if this is an
2700 		**  alias file, but powerful HP-UX NIS servers  apparently
2701 		**  don't insert the @:@ token into the alias map when it
2702 		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
2703 		*/
2704 
2705 # if 0
2706 		if (!bitset(MF_ALIAS, map->map_mflags) ||
2707 		    aliaswait(map, NULL, true))
2708 # endif /* 0 */
2709 			return true;
2710 	}
2711 
2712 	if (!bitset(MF_OPTIONAL, map->map_mflags))
2713 	{
2714 		syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2715 			map->map_file, map->map_domain, yperr_string(yperr));
2716 	}
2717 
2718 	return false;
2719 }
2720 
2721 
2722 /*
2723 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
2724 */
2725 
2726 /* ARGSUSED3 */
2727 char *
nis_map_lookup(map,name,av,statp)2728 nis_map_lookup(map, name, av, statp)
2729 	MAP *map;
2730 	char *name;
2731 	char **av;
2732 	int *statp;
2733 {
2734 	char *vp;
2735 	auto int vsize;
2736 	int buflen;
2737 	int yperr;
2738 	char keybuf[MAXNAME + 1];
2739 	char *SM_NONVOLATILE result = NULL;
2740 
2741 	if (tTd(38, 20))
2742 		sm_dprintf("nis_map_lookup(%s, %s)\n",
2743 			map->map_mname, name);
2744 
2745 	buflen = strlen(name);
2746 	if (buflen > sizeof(keybuf) - 1)
2747 		buflen = sizeof(keybuf) - 1;
2748 	memmove(keybuf, name, buflen);
2749 	keybuf[buflen] = '\0';
2750 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2751 		makelower(keybuf);
2752 	yperr = YPERR_KEY;
2753 	vp = NULL;
2754 	if (bitset(MF_TRY0NULL, map->map_mflags))
2755 	{
2756 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2757 			     &vp, &vsize);
2758 		if (yperr == 0)
2759 			map->map_mflags &= ~MF_TRY1NULL;
2760 	}
2761 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2762 	{
2763 		SM_FREE_CLR(vp);
2764 		buflen++;
2765 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2766 			     &vp, &vsize);
2767 		if (yperr == 0)
2768 			map->map_mflags &= ~MF_TRY0NULL;
2769 	}
2770 	if (yperr != 0)
2771 	{
2772 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2773 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
2774 		if (vp != NULL)
2775 			sm_free(vp);
2776 		return NULL;
2777 	}
2778 	SM_TRY
2779 		if (bitset(MF_MATCHONLY, map->map_mflags))
2780 			result = map_rewrite(map, name, strlen(name), NULL);
2781 		else
2782 			result = map_rewrite(map, vp, vsize, av);
2783 	SM_FINALLY
2784 		if (vp != NULL)
2785 			sm_free(vp);
2786 	SM_END_TRY
2787 	return result;
2788 }
2789 
2790 
2791 /*
2792 **  NIS_GETCANONNAME -- look up canonical name in NIS
2793 */
2794 
2795 static bool
nis_getcanonname(name,hbsize,statp)2796 nis_getcanonname(name, hbsize, statp)
2797 	char *name;
2798 	int hbsize;
2799 	int *statp;
2800 {
2801 	char *vp;
2802 	auto int vsize;
2803 	int keylen;
2804 	int yperr;
2805 	static bool try0null = true;
2806 	static bool try1null = true;
2807 	static char *yp_domain = NULL;
2808 	char host_record[MAXLINE];
2809 	char cbuf[MAXNAME];
2810 	char nbuf[MAXNAME + 1];
2811 
2812 	if (tTd(38, 20))
2813 		sm_dprintf("nis_getcanonname(%s)\n", name);
2814 
2815 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
2816 	{
2817 		*statp = EX_UNAVAILABLE;
2818 		return false;
2819 	}
2820 	(void) shorten_hostname(nbuf);
2821 	keylen = strlen(nbuf);
2822 
2823 	if (yp_domain == NULL)
2824 		(void) yp_get_default_domain(&yp_domain);
2825 	makelower(nbuf);
2826 	yperr = YPERR_KEY;
2827 	vp = NULL;
2828 	if (try0null)
2829 	{
2830 		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2831 			     &vp, &vsize);
2832 		if (yperr == 0)
2833 			try1null = false;
2834 	}
2835 	if (yperr == YPERR_KEY && try1null)
2836 	{
2837 		SM_FREE_CLR(vp);
2838 		keylen++;
2839 		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2840 			     &vp, &vsize);
2841 		if (yperr == 0)
2842 			try0null = false;
2843 	}
2844 	if (yperr != 0)
2845 	{
2846 		if (yperr == YPERR_KEY)
2847 			*statp = EX_NOHOST;
2848 		else if (yperr == YPERR_BUSY)
2849 			*statp = EX_TEMPFAIL;
2850 		else
2851 			*statp = EX_UNAVAILABLE;
2852 		if (vp != NULL)
2853 			sm_free(vp);
2854 		return false;
2855 	}
2856 	(void) sm_strlcpy(host_record, vp, sizeof(host_record));
2857 	sm_free(vp);
2858 	if (tTd(38, 44))
2859 		sm_dprintf("got record `%s'\n", host_record);
2860 	vp = strpbrk(host_record, "#\n");
2861 	if (vp != NULL)
2862 		*vp = '\0';
2863 	if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
2864 	{
2865 		/* this should not happen, but.... */
2866 		*statp = EX_NOHOST;
2867 		return false;
2868 	}
2869 	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2870 	{
2871 		*statp = EX_UNAVAILABLE;
2872 		return false;
2873 	}
2874 	*statp = EX_OK;
2875 	return true;
2876 }
2877 
2878 #endif /* NIS */
2879 /*
2880 **  NISPLUS Modules
2881 **
2882 **	This code donated by Sun Microsystems.
2883 */
2884 
2885 #if NISPLUS
2886 
2887 # undef NIS		/* symbol conflict in nis.h */
2888 # undef T_UNSPEC	/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2889 # include <rpcsvc/nis.h>
2890 # include <rpcsvc/nislib.h>
2891 
2892 # define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2893 # define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2894 # define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2895 # define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
2896 
2897 /*
2898 **  NISPLUS_MAP_OPEN -- open nisplus table
2899 */
2900 
2901 bool
nisplus_map_open(map,mode)2902 nisplus_map_open(map, mode)
2903 	MAP *map;
2904 	int mode;
2905 {
2906 	nis_result *res = NULL;
2907 	int retry_cnt, max_col, i;
2908 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
2909 
2910 	if (tTd(38, 2))
2911 		sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2912 			map->map_mname, map->map_file, mode);
2913 
2914 	mode &= O_ACCMODE;
2915 	if (mode != O_RDONLY)
2916 	{
2917 		errno = EPERM;
2918 		return false;
2919 	}
2920 
2921 	if (*map->map_file == '\0')
2922 		map->map_file = "mail_aliases.org_dir";
2923 
2924 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2925 	{
2926 		/* set default NISPLUS Domain to $m */
2927 		map->map_domain = newstr(nisplus_default_domain());
2928 		if (tTd(38, 2))
2929 			sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2930 				map->map_file, map->map_domain);
2931 	}
2932 	if (!PARTIAL_NAME(map->map_file))
2933 	{
2934 		map->map_domain = newstr("");
2935 		(void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
2936 	}
2937 	else
2938 	{
2939 		/* check to see if this map actually exists */
2940 		(void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
2941 				   map->map_file, ".", map->map_domain);
2942 	}
2943 
2944 	retry_cnt = 0;
2945 	while (res == NULL || res->status != NIS_SUCCESS)
2946 	{
2947 		res = nis_lookup(qbuf, FOLLOW_LINKS);
2948 		switch (res->status)
2949 		{
2950 		  case NIS_SUCCESS:
2951 			break;
2952 
2953 		  case NIS_TRYAGAIN:
2954 		  case NIS_RPCERROR:
2955 		  case NIS_NAMEUNREACHABLE:
2956 			if (retry_cnt++ > 4)
2957 			{
2958 				errno = EAGAIN;
2959 				return false;
2960 			}
2961 			/* try not to overwhelm hosed server */
2962 			sleep(2);
2963 			break;
2964 
2965 		  default:		/* all other nisplus errors */
2966 # if 0
2967 			if (!bitset(MF_OPTIONAL, map->map_mflags))
2968 				syserr("451 4.3.5 Cannot find table %s.%s: %s",
2969 					map->map_file, map->map_domain,
2970 					nis_sperrno(res->status));
2971 # endif /* 0 */
2972 			errno = EAGAIN;
2973 			return false;
2974 		}
2975 	}
2976 
2977 	if (NIS_RES_NUMOBJ(res) != 1 ||
2978 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2979 	{
2980 		if (tTd(38, 10))
2981 			sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2982 # if 0
2983 		if (!bitset(MF_OPTIONAL, map->map_mflags))
2984 			syserr("451 4.3.5 %s.%s: %s is not a table",
2985 				map->map_file, map->map_domain,
2986 				nis_sperrno(res->status));
2987 # endif /* 0 */
2988 		errno = EBADF;
2989 		return false;
2990 	}
2991 	/* default key column is column 0 */
2992 	if (map->map_keycolnm == NULL)
2993 		map->map_keycolnm = newstr(COL_NAME(res,0));
2994 
2995 	max_col = COL_MAX(res);
2996 
2997 	/* verify the key column exist */
2998 	for (i = 0; i < max_col; i++)
2999 	{
3000 		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
3001 			break;
3002 	}
3003 	if (i == max_col)
3004 	{
3005 		if (tTd(38, 2))
3006 			sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
3007 				map->map_file, map->map_keycolnm);
3008 		errno = ENOENT;
3009 		return false;
3010 	}
3011 
3012 	/* default value column is the last column */
3013 	if (map->map_valcolnm == NULL)
3014 	{
3015 		map->map_valcolno = max_col - 1;
3016 		return true;
3017 	}
3018 
3019 	for (i = 0; i< max_col; i++)
3020 	{
3021 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
3022 		{
3023 			map->map_valcolno = i;
3024 			return true;
3025 		}
3026 	}
3027 
3028 	if (tTd(38, 2))
3029 		sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
3030 			map->map_file, map->map_keycolnm);
3031 	errno = ENOENT;
3032 	return false;
3033 }
3034 
3035 
3036 /*
3037 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
3038 */
3039 
3040 char *
nisplus_map_lookup(map,name,av,statp)3041 nisplus_map_lookup(map, name, av, statp)
3042 	MAP *map;
3043 	char *name;
3044 	char **av;
3045 	int *statp;
3046 {
3047 	char *p;
3048 	auto int vsize;
3049 	char *skp;
3050 	int skleft;
3051 	char search_key[MAXNAME + 4];
3052 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
3053 	nis_result *result;
3054 
3055 	if (tTd(38, 20))
3056 		sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3057 			map->map_mname, name);
3058 
3059 	if (!bitset(MF_OPEN, map->map_mflags))
3060 	{
3061 		if (nisplus_map_open(map, O_RDONLY))
3062 		{
3063 			map->map_mflags |= MF_OPEN;
3064 			map->map_pid = CurrentPid;
3065 		}
3066 		else
3067 		{
3068 			*statp = EX_UNAVAILABLE;
3069 			return NULL;
3070 		}
3071 	}
3072 
3073 	/*
3074 	**  Copy the name to the key buffer, escaping double quote characters
3075 	**  by doubling them and quoting "]" and "," to avoid having the
3076 	**  NIS+ parser choke on them.
3077 	*/
3078 
3079 	skleft = sizeof(search_key) - 4;
3080 	skp = search_key;
3081 	for (p = name; *p != '\0' && skleft > 0; p++)
3082 	{
3083 		switch (*p)
3084 		{
3085 		  case ']':
3086 		  case ',':
3087 			/* quote the character */
3088 			*skp++ = '"';
3089 			*skp++ = *p;
3090 			*skp++ = '"';
3091 			skleft -= 3;
3092 			break;
3093 
3094 		  case '"':
3095 			/* double the quote */
3096 			*skp++ = '"';
3097 			skleft--;
3098 			/* FALLTHROUGH */
3099 
3100 		  default:
3101 			*skp++ = *p;
3102 			skleft--;
3103 			break;
3104 		}
3105 	}
3106 	*skp = '\0';
3107 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3108 		makelower(search_key);
3109 
3110 	/* construct the query */
3111 	if (PARTIAL_NAME(map->map_file))
3112 		(void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
3113 			map->map_keycolnm, search_key, map->map_file,
3114 			map->map_domain);
3115 	else
3116 		(void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
3117 			map->map_keycolnm, search_key, map->map_file);
3118 
3119 	if (tTd(38, 20))
3120 		sm_dprintf("qbuf=%s\n", qbuf);
3121 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3122 	if (result->status == NIS_SUCCESS)
3123 	{
3124 		int count;
3125 		char *str;
3126 
3127 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
3128 		{
3129 			if (LogLevel > 10)
3130 				sm_syslog(LOG_WARNING, CurEnv->e_id,
3131 					  "%s: lookup error, expected 1 entry, got %d",
3132 					  map->map_file, count);
3133 
3134 			/* ignore second entry */
3135 			if (tTd(38, 20))
3136 				sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3137 					name, count);
3138 		}
3139 
3140 		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3141 		/* set the length of the result */
3142 		if (p == NULL)
3143 			p = "";
3144 		vsize = strlen(p);
3145 		if (tTd(38, 20))
3146 			sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3147 				name, p);
3148 		if (bitset(MF_MATCHONLY, map->map_mflags))
3149 			str = map_rewrite(map, name, strlen(name), NULL);
3150 		else
3151 			str = map_rewrite(map, p, vsize, av);
3152 		nis_freeresult(result);
3153 		*statp = EX_OK;
3154 		return str;
3155 	}
3156 	else
3157 	{
3158 		if (result->status == NIS_NOTFOUND)
3159 			*statp = EX_NOTFOUND;
3160 		else if (result->status == NIS_TRYAGAIN)
3161 			*statp = EX_TEMPFAIL;
3162 		else
3163 		{
3164 			*statp = EX_UNAVAILABLE;
3165 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
3166 		}
3167 	}
3168 	if (tTd(38, 20))
3169 		sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3170 	nis_freeresult(result);
3171 	return NULL;
3172 }
3173 
3174 
3175 
3176 /*
3177 **  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3178 */
3179 
3180 static bool
nisplus_getcanonname(name,hbsize,statp)3181 nisplus_getcanonname(name, hbsize, statp)
3182 	char *name;
3183 	int hbsize;
3184 	int *statp;
3185 {
3186 	char *vp;
3187 	auto int vsize;
3188 	nis_result *result;
3189 	char *p;
3190 	char nbuf[MAXNAME + 1];
3191 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
3192 
3193 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
3194 	{
3195 		*statp = EX_UNAVAILABLE;
3196 		return false;
3197 	}
3198 	(void) shorten_hostname(nbuf);
3199 
3200 	p = strchr(nbuf, '.');
3201 	if (p == NULL)
3202 	{
3203 		/* single token */
3204 		(void) sm_snprintf(qbuf, sizeof(qbuf),
3205 			"[name=%s],hosts.org_dir", nbuf);
3206 	}
3207 	else if (p[1] != '\0')
3208 	{
3209 		/* multi token -- take only first token in nbuf */
3210 		*p = '\0';
3211 		(void) sm_snprintf(qbuf, sizeof(qbuf),
3212 				   "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3213 	}
3214 	else
3215 	{
3216 		*statp = EX_NOHOST;
3217 		return false;
3218 	}
3219 
3220 	if (tTd(38, 20))
3221 		sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3222 			   name, qbuf);
3223 
3224 	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3225 			  NULL, NULL);
3226 
3227 	if (result->status == NIS_SUCCESS)
3228 	{
3229 		int count;
3230 		char *domain;
3231 
3232 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
3233 		{
3234 			if (LogLevel > 10)
3235 				sm_syslog(LOG_WARNING, CurEnv->e_id,
3236 					  "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3237 					  count);
3238 
3239 			/* ignore second entry */
3240 			if (tTd(38, 20))
3241 				sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3242 					   name, count);
3243 		}
3244 
3245 		if (tTd(38, 20))
3246 			sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3247 				   name, (NIS_RES_OBJECT(result))->zo_domain);
3248 
3249 
3250 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3251 		vsize = strlen(vp);
3252 		if (tTd(38, 20))
3253 			sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3254 				   name, vp);
3255 		if (strchr(vp, '.') != NULL)
3256 		{
3257 			domain = "";
3258 		}
3259 		else
3260 		{
3261 			domain = macvalue('m', CurEnv);
3262 			if (domain == NULL)
3263 				domain = "";
3264 		}
3265 		if (hbsize > vsize + (int) strlen(domain) + 1)
3266 		{
3267 			if (domain[0] == '\0')
3268 				(void) sm_strlcpy(name, vp, hbsize);
3269 			else
3270 				(void) sm_snprintf(name, hbsize,
3271 						   "%s.%s", vp, domain);
3272 			*statp = EX_OK;
3273 		}
3274 		else
3275 			*statp = EX_NOHOST;
3276 		nis_freeresult(result);
3277 		return true;
3278 	}
3279 	else
3280 	{
3281 		if (result->status == NIS_NOTFOUND)
3282 			*statp = EX_NOHOST;
3283 		else if (result->status == NIS_TRYAGAIN)
3284 			*statp = EX_TEMPFAIL;
3285 		else
3286 			*statp = EX_UNAVAILABLE;
3287 	}
3288 	if (tTd(38, 20))
3289 		sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3290 			   name, result->status, *statp);
3291 	nis_freeresult(result);
3292 	return false;
3293 }
3294 
3295 char *
nisplus_default_domain()3296 nisplus_default_domain()
3297 {
3298 	static char default_domain[MAXNAME + 1] = "";
3299 	char *p;
3300 
3301 	if (default_domain[0] != '\0')
3302 		return default_domain;
3303 
3304 	p = nis_local_directory();
3305 	(void) sm_strlcpy(default_domain, p, sizeof(default_domain));
3306 	return default_domain;
3307 }
3308 
3309 #endif /* NISPLUS */
3310 /*
3311 **  LDAP Modules
3312 */
3313 
3314 /*
3315 **  LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3316 */
3317 
3318 #if defined(LDAPMAP) || defined(PH_MAP)
3319 
3320 # if PH_MAP
3321 #  define ph_map_dequote ldapmap_dequote
3322 # endif /* PH_MAP */
3323 
3324 static char *ldapmap_dequote __P((char *));
3325 
3326 static char *
ldapmap_dequote(str)3327 ldapmap_dequote(str)
3328 	char *str;
3329 {
3330 	char *p;
3331 	char *start;
3332 
3333 	if (str == NULL)
3334 		return NULL;
3335 
3336 	p = str;
3337 	if (*p == '"')
3338 	{
3339 		/* Should probably swallow initial whitespace here */
3340 		start = ++p;
3341 	}
3342 	else
3343 		return str;
3344 	while (*p != '"' && *p != '\0')
3345 		p++;
3346 	if (*p != '\0')
3347 		*p = '\0';
3348 	return start;
3349 }
3350 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3351 
3352 #if LDAPMAP
3353 
3354 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3355 
3356 /*
3357 **  LDAPMAP_OPEN -- open LDAP map
3358 **
3359 **	Connect to the LDAP server.  Re-use existing connections since a
3360 **	single server connection to a host (with the same host, port,
3361 **	bind DN, and secret) can answer queries for multiple maps.
3362 */
3363 
3364 bool
ldapmap_open(map,mode)3365 ldapmap_open(map, mode)
3366 	MAP *map;
3367 	int mode;
3368 {
3369 	SM_LDAP_STRUCT *lmap;
3370 	STAB *s;
3371 	char *id;
3372 
3373 	if (tTd(38, 2))
3374 		sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3375 
3376 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3377     HASLDAPGETALIASBYNAME
3378 	if (VendorCode == VENDOR_SUN &&
3379 	    strcmp(map->map_mname, "aliases.ldap") == 0)
3380 	{
3381 		return true;
3382 	}
3383 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3384 
3385 	mode &= O_ACCMODE;
3386 
3387 	/* sendmail doesn't have the ability to write to LDAP (yet) */
3388 	if (mode != O_RDONLY)
3389 	{
3390 		/* issue a pseudo-error message */
3391 		errno = SM_EMAPCANTWRITE;
3392 		return false;
3393 	}
3394 
3395 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
3396 
3397 	s = ldapmap_findconn(lmap);
3398 	if (s->s_lmap != NULL)
3399 	{
3400 		/* Already have a connection open to this LDAP server */
3401 		lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3402 		lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3403 
3404 		/* Add this map as head of linked list */
3405 		lmap->ldap_next = s->s_lmap;
3406 		s->s_lmap = map;
3407 
3408 		if (tTd(38, 2))
3409 			sm_dprintf("using cached connection\n");
3410 		return true;
3411 	}
3412 
3413 	if (tTd(38, 2))
3414 		sm_dprintf("opening new connection\n");
3415 
3416 	if (lmap->ldap_host != NULL)
3417 		id = lmap->ldap_host;
3418 	else if (lmap->ldap_uri != NULL)
3419 		id = lmap->ldap_uri;
3420 	else
3421 		id = "localhost";
3422 
3423 	if (tTd(74, 104))
3424 	{
3425 		extern MAPCLASS NullMapClass;
3426 
3427 		/* debug mode: don't actually open an LDAP connection */
3428 		map->map_orgclass = map->map_class;
3429 		map->map_class = &NullMapClass;
3430 		map->map_mflags |= MF_OPEN;
3431 		map->map_pid = CurrentPid;
3432 		return true;
3433 	}
3434 
3435 	/* No connection yet, connect */
3436 	if (!sm_ldap_start(map->map_mname, lmap))
3437 	{
3438 		if (errno == ETIMEDOUT)
3439 		{
3440 			if (LogLevel > 1)
3441 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
3442 					  "timeout conning to LDAP server %.100s",
3443 					  id);
3444 		}
3445 
3446 		if (!bitset(MF_OPTIONAL, map->map_mflags))
3447 		{
3448 			if (bitset(MF_NODEFER, map->map_mflags))
3449 			{
3450 				syserr("%s failed to %s in map %s",
3451 # if USE_LDAP_INIT
3452 				       "ldap_init/ldap_bind",
3453 # else /* USE_LDAP_INIT */
3454 				       "ldap_open",
3455 # endif /* USE_LDAP_INIT */
3456 				       id, map->map_mname);
3457 			}
3458 			else
3459 			{
3460 				syserr("451 4.3.5 %s failed to %s in map %s",
3461 # if USE_LDAP_INIT
3462 				       "ldap_init/ldap_bind",
3463 # else /* USE_LDAP_INIT */
3464 				       "ldap_open",
3465 # endif /* USE_LDAP_INIT */
3466 				       id, map->map_mname);
3467 			}
3468 		}
3469 		return false;
3470 	}
3471 
3472 	/* Save connection for reuse */
3473 	s->s_lmap = map;
3474 	return true;
3475 }
3476 
3477 /*
3478 **  LDAPMAP_CLOSE -- close ldap map
3479 */
3480 
3481 void
ldapmap_close(map)3482 ldapmap_close(map)
3483 	MAP *map;
3484 {
3485 	SM_LDAP_STRUCT *lmap;
3486 	STAB *s;
3487 
3488 	if (tTd(38, 2))
3489 		sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3490 
3491 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
3492 
3493 	/* Check if already closed */
3494 	if (lmap->ldap_ld == NULL)
3495 		return;
3496 
3497 	/* Close the LDAP connection */
3498 	sm_ldap_close(lmap);
3499 
3500 	/* Mark all the maps that share the connection as closed */
3501 	s = ldapmap_findconn(lmap);
3502 
3503 	while (s->s_lmap != NULL)
3504 	{
3505 		MAP *smap = s->s_lmap;
3506 
3507 		if (tTd(38, 2) && smap != map)
3508 			sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3509 				   map->map_mname, smap->map_mname);
3510 		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3511 		lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3512 		lmap->ldap_ld = NULL;
3513 		s->s_lmap = lmap->ldap_next;
3514 		lmap->ldap_next = NULL;
3515 	}
3516 }
3517 
3518 # ifdef SUNET_ID
3519 /*
3520 **  SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3521 **  This only makes sense at Stanford University.
3522 */
3523 
3524 static char *
sunet_id_hash(str)3525 sunet_id_hash(str)
3526 	char *str;
3527 {
3528 	char *p, *p_last;
3529 
3530 	p = str;
3531 	p_last = p;
3532 	while (*p != '\0')
3533 	{
3534 		if (isascii(*p) && (islower(*p) || isdigit(*p)))
3535 		{
3536 			*p_last = *p;
3537 			p_last++;
3538 		}
3539 		else if (isascii(*p) && isupper(*p))
3540 		{
3541 			*p_last = tolower(*p);
3542 			p_last++;
3543 		}
3544 		++p;
3545 	}
3546 	if (*p_last != '\0')
3547 		*p_last = '\0';
3548 	return str;
3549 }
3550 #  define SM_CONVERT_ID(str)	sunet_id_hash(str)
3551 # else /* SUNET_ID */
3552 #  define SM_CONVERT_ID(str)	makelower(str)
3553 # endif /* SUNET_ID */
3554 
3555 /*
3556 **  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3557 */
3558 
3559 char *
ldapmap_lookup(map,name,av,statp)3560 ldapmap_lookup(map, name, av, statp)
3561 	MAP *map;
3562 	char *name;
3563 	char **av;
3564 	int *statp;
3565 {
3566 	int flags;
3567 	int i;
3568 	int plen = 0;
3569 	int psize = 0;
3570 	int msgid;
3571 	int save_errno;
3572 	char *vp, *p;
3573 	char *result = NULL;
3574 	SM_RPOOL_T *rpool;
3575 	SM_LDAP_STRUCT *lmap = NULL;
3576 	char *argv[SM_LDAP_ARGS];
3577 	char keybuf[MAXKEY];
3578 #if SM_LDAP_ARGS != MAX_MAP_ARGS
3579 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
3580 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
3581 
3582 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3583     HASLDAPGETALIASBYNAME
3584 	if (VendorCode == VENDOR_SUN &&
3585 	    strcmp(map->map_mname, "aliases.ldap") == 0)
3586 	{
3587 		int rc;
3588 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3589 		extern char *__getldapaliasbyname();
3590 		char *answer;
3591 
3592 		answer = __getldapaliasbyname(name, &rc);
3593 #else
3594 		char answer[MAXNAME + 1];
3595 
3596 		rc = __getldapaliasbyname(name, answer, sizeof(answer));
3597 #endif
3598 		if (rc != 0)
3599 		{
3600 			if (tTd(38, 20))