xref: /illumos-gate/usr/src/cmd/sendmail/src/udb.c (revision ea8dc4b6d2251b437950c0056bc626b311c73c27)
1 /*
2  * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
15 
16 #include <sendmail.h>
17 
18 #if USERDB
19 SM_RCSID("@(#)$Id: udb.c,v 8.161 2005/08/31 21:34:20 ca Exp $ (with USERDB)")
20 #else /* USERDB */
21 SM_RCSID("@(#)$Id: udb.c,v 8.161 2005/08/31 21:34:20 ca Exp $ (without USERDB)")
22 #endif /* USERDB */
23 
24 #if USERDB
25 
26 # if NEWDB
27 #  include "sm/bdb.h"
28 # else /* NEWDB */
29 #  define DBT	struct _data_base_thang_
30 DBT
31 {
32 	void	*data;		/* pointer to data */
33 	size_t	size;		/* length of data */
34 };
35 # endif /* NEWDB */
36 
37 /*
38 **  UDB.C -- interface between sendmail and Berkeley User Data Base.
39 **
40 **	This depends on the 4.4BSD db package.
41 */
42 
43 
44 struct udbent
45 {
46 	char	*udb_spec;		/* string version of spec */
47 	int	udb_type;		/* type of entry */
48 	pid_t	udb_pid;		/* PID of process which opened db */
49 	char	*udb_default;		/* default host for outgoing mail */
50 	union
51 	{
52 # if NETINET || NETINET6
53 		/* type UE_REMOTE -- do remote call for lookup */
54 		struct
55 		{
56 			SOCKADDR	_udb_addr;	/* address */
57 			int		_udb_timeout;	/* timeout */
58 		} udb_remote;
59 #  define udb_addr	udb_u.udb_remote._udb_addr
60 #  define udb_timeout	udb_u.udb_remote._udb_timeout
61 # endif /* NETINET || NETINET6 */
62 
63 		/* type UE_FORWARD -- forward message to remote */
64 		struct
65 		{
66 			char	*_udb_fwdhost;	/* name of forward host */
67 		} udb_forward;
68 # define udb_fwdhost	udb_u.udb_forward._udb_fwdhost
69 
70 # if NEWDB
71 		/* type UE_FETCH -- lookup in local database */
72 		struct
73 		{
74 			char	*_udb_dbname;	/* pathname of database */
75 			DB	*_udb_dbp;	/* open database ptr */
76 		} udb_lookup;
77 #  define udb_dbname	udb_u.udb_lookup._udb_dbname
78 #  define udb_dbp	udb_u.udb_lookup._udb_dbp
79 # endif /* NEWDB */
80 	} udb_u;
81 };
82 
83 # define UDB_EOLIST	0	/* end of list */
84 # define UDB_SKIP	1	/* skip this entry */
85 # define UDB_REMOTE	2	/* look up in remote database */
86 # define UDB_DBFETCH	3	/* look up in local database */
87 # define UDB_FORWARD	4	/* forward to remote host */
88 # define UDB_HESIOD	5	/* look up via hesiod */
89 
90 # define MAXUDBENT	10	/* maximum number of UDB entries */
91 
92 
93 struct udb_option
94 {
95 	char	*udbo_name;
96 	char	*udbo_val;
97 };
98 
99 # if HESIOD
100 static int	hes_udb_get __P((DBT *, DBT *));
101 # endif /* HESIOD */
102 static char	*udbmatch __P((char *, char *, SM_RPOOL_T *));
103 static int	_udbx_init __P((ENVELOPE *));
104 static int	_udb_parsespec __P((char *, struct udb_option [], int));
105 
106 /*
107 **  UDBEXPAND -- look up user in database and expand
108 **
109 **	Parameters:
110 **		a -- address to expand.
111 **		sendq -- pointer to head of sendq to put the expansions in.
112 **		aliaslevel -- the current alias nesting depth.
113 **		e -- the current envelope.
114 **
115 **	Returns:
116 **		EX_TEMPFAIL -- if something "odd" happened -- probably due
117 **			to accessing a file on an NFS server that is down.
118 **		EX_OK -- otherwise.
119 **
120 **	Side Effects:
121 **		Modifies sendq.
122 */
123 
124 static struct udbent	UdbEnts[MAXUDBENT + 1];
125 static bool		UdbInitialized = false;
126 
127 int
128 udbexpand(a, sendq, aliaslevel, e)
129 	register ADDRESS *a;
130 	ADDRESS **sendq;
131 	int aliaslevel;
132 	register ENVELOPE *e;
133 {
134 	int i;
135 	DBT key;
136 	DBT info;
137 	bool breakout;
138 	register struct udbent *up;
139 	int keylen;
140 	int naddrs;
141 	char *user;
142 	char keybuf[MAXUDBKEY];
143 
144 	memset(&key, '\0', sizeof key);
145 	memset(&info, '\0', sizeof info);
146 
147 	if (tTd(28, 1))
148 		sm_dprintf("udbexpand(%s)\n", a->q_paddr);
149 
150 	/* make certain we are supposed to send to this address */
151 	if (!QS_IS_SENDABLE(a->q_state))
152 		return EX_OK;
153 	e->e_to = a->q_paddr;
154 
155 	/* on first call, locate the database */
156 	if (!UdbInitialized)
157 	{
158 		if (_udbx_init(e) == EX_TEMPFAIL)
159 			return EX_TEMPFAIL;
160 	}
161 
162 	/* short circuit the process if no chance of a match */
163 	if (UdbSpec == NULL || UdbSpec[0] == '\0')
164 		return EX_OK;
165 
166 	/* extract user to do userdb matching on */
167 	user = a->q_user;
168 
169 	/* short circuit name begins with '\\' since it can't possibly match */
170 	/* (might want to treat this as unquoted instead) */
171 	if (user[0] == '\\')
172 		return EX_OK;
173 
174 	/* if name begins with a colon, it indicates our metadata */
175 	if (user[0] == ':')
176 		return EX_OK;
177 
178 	keylen = sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop");
179 
180 	/* if name is too long, assume it won't match */
181 	if (keylen >= sizeof keybuf)
182 		return EX_OK;
183 
184 	/* build actual database key */
185 
186 	breakout = false;
187 	for (up = UdbEnts; !breakout; up++)
188 	{
189 		int usersize;
190 		int userleft;
191 		char userbuf[MEMCHUNKSIZE];
192 # if HESIOD && HES_GETMAILHOST
193 		char pobuf[MAXNAME];
194 # endif /* HESIOD && HES_GETMAILHOST */
195 # if defined(NEWDB) && DB_VERSION_MAJOR > 1
196 		DBC *dbc = NULL;
197 # endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */
198 
199 		user = userbuf;
200 		userbuf[0] = '\0';
201 		usersize = sizeof userbuf;
202 		userleft = sizeof userbuf - 1;
203 
204 		/*
205 		**  Select action based on entry type.
206 		**
207 		**	On dropping out of this switch, "class" should
208 		**	explain the type of the data, and "user" should
209 		**	contain the user information.
210 		*/
211 
212 		switch (up->udb_type)
213 		{
214 # if NEWDB
215 		  case UDB_DBFETCH:
216 			key.data = keybuf;
217 			key.size = keylen;
218 			if (tTd(28, 80))
219 				sm_dprintf("udbexpand: trying %s (%d) via db\n",
220 					keybuf, keylen);
221 #  if DB_VERSION_MAJOR < 2
222 			i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
223 #  else /* DB_VERSION_MAJOR < 2 */
224 			i = 0;
225 			if (dbc == NULL &&
226 #   if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
227 			    (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
228 							    NULL, &dbc, 0)) != 0)
229 #   else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
230 			    (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
231 							    NULL, &dbc)) != 0)
232 #   endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
233 				i = -1;
234 			if (i != 0 || dbc == NULL ||
235 			    (errno = dbc->c_get(dbc, &key,
236 						&info, DB_SET)) != 0)
237 				i = 1;
238 #  endif /* DB_VERSION_MAJOR < 2 */
239 			if (i > 0 || info.size <= 0)
240 			{
241 				if (tTd(28, 2))
242 					sm_dprintf("udbexpand: no match on %s (%d)\n",
243 						keybuf, keylen);
244 #  if DB_VERSION_MAJOR > 1
245 				if (dbc != NULL)
246 				{
247 					(void) dbc->c_close(dbc);
248 					dbc = NULL;
249 				}
250 #  endif /* DB_VERSION_MAJOR > 1 */
251 				break;
252 			}
253 			if (tTd(28, 80))
254 				sm_dprintf("udbexpand: match %.*s: %.*s\n",
255 					(int) key.size, (char *) key.data,
256 					(int) info.size, (char *) info.data);
257 
258 			a->q_flags &= ~QSELFREF;
259 			while (i == 0 && key.size == keylen &&
260 			       memcmp(key.data, keybuf, keylen) == 0)
261 			{
262 				char *p;
263 
264 				if (bitset(EF_VRFYONLY, e->e_flags))
265 				{
266 					a->q_state = QS_VERIFIED;
267 #  if DB_VERSION_MAJOR > 1
268 					if (dbc != NULL)
269 					{
270 						(void) dbc->c_close(dbc);
271 						dbc = NULL;
272 					}
273 #  endif /* DB_VERSION_MAJOR > 1 */
274 					return EX_OK;
275 				}
276 
277 				breakout = true;
278 				if (info.size >= userleft - 1)
279 				{
280 					char *nuser;
281 					int size = MEMCHUNKSIZE;
282 
283 					if (info.size > MEMCHUNKSIZE)
284 						size = info.size;
285 					nuser = sm_malloc_x(usersize + size);
286 
287 					memmove(nuser, user, usersize);
288 					if (user != userbuf)
289 						sm_free(user); /* XXX */
290 					user = nuser;
291 					usersize += size;
292 					userleft += size;
293 				}
294 				p = &user[strlen(user)];
295 				if (p != user)
296 				{
297 					*p++ = ',';
298 					userleft--;
299 				}
300 				memmove(p, info.data, info.size);
301 				p[info.size] = '\0';
302 				userleft -= info.size;
303 
304 				/* get the next record */
305 #  if DB_VERSION_MAJOR < 2
306 				i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
307 #  else /* DB_VERSION_MAJOR < 2 */
308 				i = 0;
309 				if ((errno = dbc->c_get(dbc, &key,
310 							&info, DB_NEXT)) != 0)
311 					i = 1;
312 #  endif /* DB_VERSION_MAJOR < 2 */
313 			}
314 
315 #  if DB_VERSION_MAJOR > 1
316 			if (dbc != NULL)
317 			{
318 				(void) dbc->c_close(dbc);
319 				dbc = NULL;
320 			}
321 #  endif /* DB_VERSION_MAJOR > 1 */
322 
323 			/* if nothing ever matched, try next database */
324 			if (!breakout)
325 				break;
326 
327 			message("expanded to %s", user);
328 			if (LogLevel > 10)
329 				sm_syslog(LOG_INFO, e->e_id,
330 					  "expand %.100s => %s",
331 					  e->e_to,
332 					  shortenstring(user, MAXSHORTSTR));
333 			naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
334 			if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
335 			{
336 				if (tTd(28, 5))
337 				{
338 					sm_dprintf("udbexpand: QS_EXPANDED ");
339 					printaddr(sm_debug_file(), a, false);
340 				}
341 				a->q_state = QS_EXPANDED;
342 			}
343 			if (i < 0)
344 			{
345 				syserr("udbexpand: db-get %.*s stat %d",
346 					(int) key.size, (char *) key.data, i);
347 				return EX_TEMPFAIL;
348 			}
349 
350 			/*
351 			**  If this address has a -request address, reflect
352 			**  it into the envelope.
353 			*/
354 
355 			memset(&key, '\0', sizeof key);
356 			memset(&info, '\0', sizeof info);
357 			(void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user,
358 					   ":mailsender");
359 			keylen = strlen(keybuf);
360 			key.data = keybuf;
361 			key.size = keylen;
362 
363 #  if DB_VERSION_MAJOR < 2
364 			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
365 #  else /* DB_VERSION_MAJOR < 2 */
366 			i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
367 							&key, &info, 0);
368 #  endif /* DB_VERSION_MAJOR < 2 */
369 			if (i != 0 || info.size <= 0)
370 				break;
371 			a->q_owner = sm_rpool_malloc_x(e->e_rpool,
372 						       info.size + 1);
373 			memmove(a->q_owner, info.data, info.size);
374 			a->q_owner[info.size] = '\0';
375 
376 			/* announce delivery; NORECEIPT bit set later */
377 			if (e->e_xfp != NULL)
378 			{
379 				(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
380 						     "Message delivered to mailing list %s\n",
381 						     a->q_paddr);
382 			}
383 			e->e_flags |= EF_SENDRECEIPT;
384 			a->q_flags |= QDELIVERED|QEXPANDED;
385 			break;
386 # endif /* NEWDB */
387 
388 # if HESIOD
389 		  case UDB_HESIOD:
390 			key.data = keybuf;
391 			key.size = keylen;
392 			if (tTd(28, 80))
393 				sm_dprintf("udbexpand: trying %s (%d) via hesiod\n",
394 					keybuf, keylen);
395 			/* look up the key via hesiod */
396 			i = hes_udb_get(&key, &info);
397 			if (i < 0)
398 			{
399 				syserr("udbexpand: hesiod-get %.*s stat %d",
400 					(int) key.size, (char *) key.data, i);
401 				return EX_TEMPFAIL;
402 			}
403 			else if (i > 0 || info.size <= 0)
404 			{
405 #  if HES_GETMAILHOST
406 				struct hes_postoffice *hp;
407 #  endif /* HES_GETMAILHOST */
408 
409 				if (tTd(28, 2))
410 					sm_dprintf("udbexpand: no match on %s (%d)\n",
411 						(char *) keybuf, (int) keylen);
412 #  if HES_GETMAILHOST
413 				if (tTd(28, 8))
414 					sm_dprintf("  ... trying hes_getmailhost(%s)\n",
415 						a->q_user);
416 				hp = hes_getmailhost(a->q_user);
417 				if (hp == NULL)
418 				{
419 					if (hes_error() == HES_ER_NET)
420 					{
421 						syserr("udbexpand: hesiod-getmail %s stat %d",
422 							a->q_user, hes_error());
423 						return EX_TEMPFAIL;
424 					}
425 					if (tTd(28, 2))
426 						sm_dprintf("hes_getmailhost(%s): %d\n",
427 							a->q_user, hes_error());
428 					break;
429 				}
430 				if (strlen(hp->po_name) + strlen(hp->po_host) >
431 				    sizeof pobuf - 2)
432 				{
433 					if (tTd(28, 2))
434 						sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
435 							a->q_user,
436 							hp->po_name,
437 							hp->po_host);
438 					break;
439 				}
440 				info.data = pobuf;
441 				(void) sm_snprintf(pobuf, sizeof pobuf,
442 					"%s@%s", hp->po_name, hp->po_host);
443 				info.size = strlen(info.data);
444 #  else /* HES_GETMAILHOST */
445 				break;
446 #  endif /* HES_GETMAILHOST */
447 			}
448 			if (tTd(28, 80))
449 				sm_dprintf("udbexpand: match %.*s: %.*s\n",
450 					(int) key.size, (char *) key.data,
451 					(int) info.size, (char *) info.data);
452 			a->q_flags &= ~QSELFREF;
453 
454 			if (bitset(EF_VRFYONLY, e->e_flags))
455 			{
456 				a->q_state = QS_VERIFIED;
457 				return EX_OK;
458 			}
459 
460 			breakout = true;
461 			if (info.size >= usersize)
462 				user = sm_malloc_x(info.size + 1);
463 			memmove(user, info.data, info.size);
464 			user[info.size] = '\0';
465 
466 			message("hesioded to %s", user);
467 			if (LogLevel > 10)
468 				sm_syslog(LOG_INFO, e->e_id,
469 					  "hesiod %.100s => %s",
470 					  e->e_to,
471 					  shortenstring(user, MAXSHORTSTR));
472 			naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
473 
474 			if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
475 			{
476 				if (tTd(28, 5))
477 				{
478 					sm_dprintf("udbexpand: QS_EXPANDED ");
479 					printaddr(sm_debug_file(), a, false);
480 				}
481 				a->q_state = QS_EXPANDED;
482 			}
483 
484 			/*
485 			**  If this address has a -request address, reflect
486 			**  it into the envelope.
487 			*/
488 
489 			(void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user,
490 					   ":mailsender");
491 			keylen = strlen(keybuf);
492 			key.data = keybuf;
493 			key.size = keylen;
494 			i = hes_udb_get(&key, &info);
495 			if (i != 0 || info.size <= 0)
496 				break;
497 			a->q_owner = sm_rpool_malloc_x(e->e_rpool,
498 						       info.size + 1);
499 			memmove(a->q_owner, info.data, info.size);
500 			a->q_owner[info.size] = '\0';
501 			break;
502 # endif /* HESIOD */
503 
504 		  case UDB_REMOTE:
505 			/* not yet implemented */
506 			break;
507 
508 		  case UDB_FORWARD:
509 			if (bitset(EF_VRFYONLY, e->e_flags))
510 			{
511 				a->q_state = QS_VERIFIED;
512 				return EX_OK;
513 			}
514 			i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
515 			if (i >= usersize)
516 			{
517 				usersize = i + 1;
518 				user = sm_malloc_x(usersize);
519 			}
520 			(void) sm_strlcpyn(user, usersize, 3,
521 					a->q_user, "@", up->udb_fwdhost);
522 			message("expanded to %s", user);
523 			a->q_flags &= ~QSELFREF;
524 			naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
525 			if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
526 			{
527 				if (tTd(28, 5))
528 				{
529 					sm_dprintf("udbexpand: QS_EXPANDED ");
530 					printaddr(sm_debug_file(), a, false);
531 				}
532 				a->q_state = QS_EXPANDED;
533 			}
534 			breakout = true;
535 			break;
536 
537 		  case UDB_EOLIST:
538 			breakout = true;
539 			break;
540 
541 		  default:
542 			/* unknown entry type */
543 			break;
544 		}
545 		/* XXX if an exception occurs, there is a storage leak */
546 		if (user != userbuf)
547 			sm_free(user); /* XXX */
548 	}
549 	return EX_OK;
550 }
551 /*
552 **  UDBSENDER -- return canonical external name of sender, given local name
553 **
554 **	Parameters:
555 **		sender -- the name of the sender on the local machine.
556 **		rpool -- resource pool from which to allocate result
557 **
558 **	Returns:
559 **		The external name for this sender, if derivable from the
560 **			database.  Storage allocated from rpool.
561 **		NULL -- if nothing is changed from the database.
562 **
563 **	Side Effects:
564 **		none.
565 */
566 
567 char *
568 udbsender(sender, rpool)
569 	char *sender;
570 	SM_RPOOL_T *rpool;
571 {
572 	return udbmatch(sender, "mailname", rpool);
573 }
574 /*
575 **  UDBMATCH -- match user in field, return result of lookup.
576 **
577 **	Parameters:
578 **		user -- the name of the user.
579 **		field -- the field to lookup.
580 **		rpool -- resource pool from which to allocate result
581 **
582 **	Returns:
583 **		The external name for this sender, if derivable from the
584 **			database.  Storage allocated from rpool.
585 **		NULL -- if nothing is changed from the database.
586 **
587 **	Side Effects:
588 **		none.
589 */
590 
591 static char *
592 udbmatch(user, field, rpool)
593 	char *user;
594 	char *field;
595 	SM_RPOOL_T *rpool;
596 {
597 	register char *p;
598 	register struct udbent *up;
599 	int i;
600 	int keylen;
601 	DBT key, info;
602 	char keybuf[MAXUDBKEY];
603 
604 	if (tTd(28, 1))
605 		sm_dprintf("udbmatch(%s, %s)\n", user, field);
606 
607 	if (!UdbInitialized)
608 	{
609 		if (_udbx_init(CurEnv) == EX_TEMPFAIL)
610 			return NULL;
611 	}
612 
613 	/* short circuit if no spec */
614 	if (UdbSpec == NULL || UdbSpec[0] == '\0')
615 		return NULL;
616 
617 	/* short circuit name begins with '\\' since it can't possibly match */
618 	if (user[0] == '\\')
619 		return NULL;
620 
621 	/* long names can never match and are a pain to deal with */
622 	i = strlen(field);
623 	if (i < sizeof "maildrop")
624 		i = sizeof "maildrop";
625 	if ((strlen(user) + i) > sizeof keybuf - 4)
626 		return NULL;
627 
628 	/* names beginning with colons indicate metadata */
629 	if (user[0] == ':')
630 		return NULL;
631 
632 	/* build database key */
633 	(void) sm_strlcpyn(keybuf, sizeof keybuf, 3, user, ":", field);
634 	keylen = strlen(keybuf);
635 
636 	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
637 	{
638 		/*
639 		**  Select action based on entry type.
640 		*/
641 
642 		switch (up->udb_type)
643 		{
644 # if NEWDB
645 		  case UDB_DBFETCH:
646 			memset(&key, '\0', sizeof key);
647 			memset(&info, '\0', sizeof info);
648 			key.data = keybuf;
649 			key.size = keylen;
650 #  if DB_VERSION_MAJOR < 2
651 			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
652 #  else /* DB_VERSION_MAJOR < 2 */
653 			i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
654 							&key, &info, 0);
655 #  endif /* DB_VERSION_MAJOR < 2 */
656 			if (i != 0 || info.size <= 0)
657 			{
658 				if (tTd(28, 2))
659 					sm_dprintf("udbmatch: no match on %s (%d) via db\n",
660 						keybuf, keylen);
661 				continue;
662 			}
663 
664 			p = sm_rpool_malloc_x(rpool, info.size + 1);
665 			memmove(p, info.data, info.size);
666 			p[info.size] = '\0';
667 			if (tTd(28, 1))
668 				sm_dprintf("udbmatch ==> %s\n", p);
669 			return p;
670 # endif /* NEWDB */
671 
672 # if HESIOD
673 		  case UDB_HESIOD:
674 			key.data = keybuf;
675 			key.size = keylen;
676 			i = hes_udb_get(&key, &info);
677 			if (i != 0 || info.size <= 0)
678 			{
679 				if (tTd(28, 2))
680 					sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n",
681 						keybuf, keylen);
682 				continue;
683 			}
684 
685 			p = sm_rpool_malloc_x(rpool, info.size + 1);
686 			memmove(p, info.data, info.size);
687 			p[info.size] = '\0';
688 			if (tTd(28, 1))
689 				sm_dprintf("udbmatch ==> %s\n", p);
690 			return p;
691 # endif /* HESIOD */
692 		}
693 	}
694 
695 	if (strcmp(field, "mailname") != 0)
696 		return NULL;
697 
698 	/*
699 	**  Nothing yet.  Search again for a default case.  But only
700 	**  use it if we also have a forward (:maildrop) pointer already
701 	**  in the database.
702 	*/
703 
704 	/* build database key */
705 	(void) sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop");
706 	keylen = strlen(keybuf);
707 
708 	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
709 	{
710 		switch (up->udb_type)
711 		{
712 # if NEWDB
713 		  case UDB_DBFETCH:
714 			/* get the default case for this database */
715 			if (up->udb_default == NULL)
716 			{
717 				memset(&key, '\0', sizeof key);
718 				memset(&info, '\0', sizeof info);
719 				key.data = ":default:mailname";
720 				key.size = strlen(key.data);
721 #  if DB_VERSION_MAJOR < 2
722 				i = (*up->udb_dbp->get)(up->udb_dbp,
723 							&key, &info, 0);
724 #  else /* DB_VERSION_MAJOR < 2 */
725 				i = errno = (*up->udb_dbp->get)(up->udb_dbp,
726 								NULL, &key,
727 								&info, 0);
728 #  endif /* DB_VERSION_MAJOR < 2 */
729 				if (i != 0 || info.size <= 0)
730 				{
731 					/* no default case */
732 					up->udb_default = "";
733 					continue;
734 				}
735 
736 				/* save the default case */
737 				up->udb_default = sm_pmalloc_x(info.size + 1);
738 				memmove(up->udb_default, info.data, info.size);
739 				up->udb_default[info.size] = '\0';
740 			}
741 			else if (up->udb_default[0] == '\0')
742 				continue;
743 
744 			/* we have a default case -- verify user:maildrop */
745 			memset(&key, '\0', sizeof key);
746 			memset(&info, '\0', sizeof info);
747 			key.data = keybuf;
748 			key.size = keylen;
749 #  if DB_VERSION_MAJOR < 2
750 			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
751 #  else /* DB_VERSION_MAJOR < 2 */
752 			i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
753 							&key, &info, 0);
754 #  endif /* DB_VERSION_MAJOR < 2 */
755 			if (i != 0 || info.size <= 0)
756 			{
757 				/* nope -- no aliasing for this user */
758 				continue;
759 			}
760 
761 			/* they exist -- build the actual address */
762 			i = strlen(user) + strlen(up->udb_default) + 2;
763 			p = sm_rpool_malloc_x(rpool, i);
764 			(void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
765 			if (tTd(28, 1))
766 				sm_dprintf("udbmatch ==> %s\n", p);
767 			return p;
768 # endif /* NEWDB */
769 
770 # if HESIOD
771 		  case UDB_HESIOD:
772 			/* get the default case for this database */
773 			if (up->udb_default == NULL)
774 			{
775 				key.data = ":default:mailname";
776 				key.size = strlen(key.data);
777 				i = hes_udb_get(&key, &info);
778 
779 				if (i != 0 || info.size <= 0)
780 				{
781 					/* no default case */
782 					up->udb_default = "";
783 					continue;
784 				}
785 
786 				/* save the default case */
787 				up->udb_default = sm_pmalloc_x(info.size + 1);
788 				memmove(up->udb_default, info.data, info.size);
789 				up->udb_default[info.size] = '\0';
790 			}
791 			else if (up->udb_default[0] == '\0')
792 				continue;
793 
794 			/* we have a default case -- verify user:maildrop */
795 			key.data = keybuf;
796 			key.size = keylen;
797 			i = hes_udb_get(&key, &info);
798 			if (i != 0 || info.size <= 0)
799 			{
800 				/* nope -- no aliasing for this user */
801 				continue;
802 			}
803 
804 			/* they exist -- build the actual address */
805 			i = strlen(user) + strlen(up->udb_default) + 2;
806 			p = sm_rpool_malloc_x(rpool, i);
807 			(void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
808 			if (tTd(28, 1))
809 				sm_dprintf("udbmatch ==> %s\n", p);
810 			return p;
811 			break;
812 # endif /* HESIOD */
813 		}
814 	}
815 
816 	/* still nothing....  too bad */
817 	return NULL;
818 }
819 /*
820 **  UDB_MAP_LOOKUP -- look up arbitrary entry in user database map
821 **
822 **	Parameters:
823 **		map -- the map being queried.
824 **		name -- the name to look up.
825 **		av -- arguments to the map lookup.
826 **		statp -- to get any error status.
827 **
828 **	Returns:
829 **		NULL if name not found in map.
830 **		The rewritten name otherwise.
831 */
832 
833 /* ARGSUSED3 */
834 char *
835 udb_map_lookup(map, name, av, statp)
836 	MAP *map;
837 	char *name;
838 	char **av;
839 	int *statp;
840 {
841 	char *val;
842 	char *key;
843 	char *SM_NONVOLATILE result = NULL;
844 	char keybuf[MAXNAME + 1];
845 
846 	if (tTd(28, 20) || tTd(38, 20))
847 		sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
848 
849 	if (bitset(MF_NOFOLDCASE, map->map_mflags))
850 	{
851 		key = name;
852 	}
853 	else
854 	{
855 		int keysize = strlen(name);
856 
857 		if (keysize > sizeof keybuf - 1)
858 			keysize = sizeof keybuf - 1;
859 		memmove(keybuf, name, keysize);
860 		keybuf[keysize] = '\0';
861 		makelower(keybuf);
862 		key = keybuf;
863 	}
864 	val = udbmatch(key, map->map_file, NULL);
865 	if (val == NULL)
866 		return NULL;
867 	SM_TRY
868 		if (bitset(MF_MATCHONLY, map->map_mflags))
869 			result = map_rewrite(map, name, strlen(name), NULL);
870 		else
871 			result = map_rewrite(map, val, strlen(val), av);
872 	SM_FINALLY
873 		sm_free(val);
874 	SM_END_TRY
875 	return result;
876 }
877 /*
878 **  _UDBX_INIT -- parse the UDB specification, opening any valid entries.
879 **
880 **	Parameters:
881 **		e -- the current envelope.
882 **
883 **	Returns:
884 **		EX_TEMPFAIL -- if it appeared it couldn't get hold of a
885 **			database due to a host being down or some similar
886 **			(recoverable) situation.
887 **		EX_OK -- otherwise.
888 **
889 **	Side Effects:
890 **		Fills in the UdbEnts structure from UdbSpec.
891 */
892 
893 # define MAXUDBOPTS	27
894 
895 static int
896 _udbx_init(e)
897 	ENVELOPE *e;
898 {
899 	int ents = 0;
900 	register char *p;
901 	register struct udbent *up;
902 
903 	if (UdbInitialized)
904 		return EX_OK;
905 
906 # ifdef UDB_DEFAULT_SPEC
907 	if (UdbSpec == NULL)
908 		UdbSpec = UDB_DEFAULT_SPEC;
909 # endif /* UDB_DEFAULT_SPEC */
910 
911 	p = UdbSpec;
912 	up = UdbEnts;
913 	while (p != NULL)
914 	{
915 		char *spec;
916 		int l;
917 		struct udb_option opts[MAXUDBOPTS + 1];
918 
919 		while (*p == ' ' || *p == '\t' || *p == ',')
920 			p++;
921 		if (*p == '\0')
922 			break;
923 		spec = p;
924 		p = strchr(p, ',');
925 		if (p != NULL)
926 			*p++ = '\0';
927 
928 		if (ents >= MAXUDBENT)
929 		{
930 			syserr("Maximum number of UDB entries exceeded");
931 			break;
932 		}
933 
934 		/* extract options */
935 		(void) _udb_parsespec(spec, opts, MAXUDBOPTS);
936 
937 		/*
938 		**  Decode database specification.
939 		**
940 		**	In the sendmail tradition, the leading character
941 		**	defines the semantics of the rest of the entry.
942 		**
943 		**	@hostname --	forward email to the indicated host.
944 		**			This should be the last in the list,
945 		**			since it always matches the input.
946 		**	/dbname	 --	search the named database on the local
947 		**			host using the Berkeley db package.
948 		**	Hesiod --	search the named database with BIND
949 		**			using the MIT Hesiod package.
950 		*/
951 
952 		switch (*spec)
953 		{
954 		  case '@':	/* forward to remote host */
955 			up->udb_type = UDB_FORWARD;
956 			up->udb_pid = CurrentPid;
957 			up->udb_fwdhost = spec + 1;
958 			ents++;
959 			up++;
960 			break;
961 
962 # if HESIOD
963 		  case 'h':	/* use hesiod */
964 		  case 'H':
965 			if (sm_strcasecmp(spec, "hesiod") != 0)
966 				goto badspec;
967 			up->udb_type = UDB_HESIOD;
968 			up->udb_pid = CurrentPid;
969 			ents++;
970 			up++;
971 			break;
972 # endif /* HESIOD */
973 
974 # if NEWDB
975 		  case '/':	/* look up remote name */
976 			l = strlen(spec);
977 			if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
978 			{
979 				up->udb_dbname = spec;
980 			}
981 			else
982 			{
983 				up->udb_dbname = sm_pmalloc_x(l + 4);
984 				(void) sm_strlcpyn(up->udb_dbname, l + 4, 2,
985 						   spec, ".db");
986 			}
987 			errno = 0;
988 #  if DB_VERSION_MAJOR < 2
989 			up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY,
990 					     0644, DB_BTREE, NULL);
991 #  else /* DB_VERSION_MAJOR < 2 */
992 			{
993 				int flags = DB_RDONLY;
994 #  if DB_VERSION_MAJOR > 2
995 				int ret;
996 #  endif /* DB_VERSION_MAJOR > 2 */
997 
998 				SM_DB_FLAG_ADD(flags);
999 				up->udb_dbp = NULL;
1000 #  if DB_VERSION_MAJOR > 2
1001 				ret = db_create(&up->udb_dbp, NULL, 0);
1002 				if (ret != 0)
1003 				{
1004 					(void) up->udb_dbp->close(up->udb_dbp,
1005 								  0);
1006 					up->udb_dbp = NULL;
1007 				}
1008 				else
1009 				{
1010 					ret = up->udb_dbp->open(up->udb_dbp,
1011 								DBTXN
1012 								up->udb_dbname,
1013 								NULL,
1014 								DB_BTREE,
1015 								flags,
1016 								0644);
1017 					if (ret != 0)
1018 					{
1019 #ifdef DB_OLD_VERSION
1020 						if (ret == DB_OLD_VERSION)
1021 							ret = EINVAL;
1022 #endif /* DB_OLD_VERSION */
1023 						(void) up->udb_dbp->close(up->udb_dbp, 0);
1024 						up->udb_dbp = NULL;
1025 					}
1026 				}
1027 				errno = ret;
1028 #  else /* DB_VERSION_MAJOR > 2 */
1029 				errno = db_open(up->udb_dbname, DB_BTREE,
1030 						flags, 0644, NULL,
1031 						NULL, &up->udb_dbp);
1032 #  endif /* DB_VERSION_MAJOR > 2 */
1033 			}
1034 #  endif /* DB_VERSION_MAJOR < 2 */
1035 			if (up->udb_dbp == NULL)
1036 			{
1037 				if (tTd(28, 1))
1038 				{
1039 					int save_errno = errno;
1040 
1041 #  if DB_VERSION_MAJOR < 2
1042 					sm_dprintf("dbopen(%s): %s\n",
1043 #  else /* DB_VERSION_MAJOR < 2 */
1044 					sm_dprintf("db_open(%s): %s\n",
1045 #  endif /* DB_VERSION_MAJOR < 2 */
1046 						up->udb_dbname,
1047 						sm_errstring(errno));
1048 					errno = save_errno;
1049 				}
1050 				if (errno != ENOENT && errno != EACCES)
1051 				{
1052 					if (LogLevel > 2)
1053 						sm_syslog(LOG_ERR, e->e_id,
1054 #  if DB_VERSION_MAJOR < 2
1055 							  "dbopen(%s): %s",
1056 #  else /* DB_VERSION_MAJOR < 2 */
1057 							  "db_open(%s): %s",
1058 #  endif /* DB_VERSION_MAJOR < 2 */
1059 							  up->udb_dbname,
1060 							  sm_errstring(errno));
1061 					up->udb_type = UDB_EOLIST;
1062 					if (up->udb_dbname != spec)
1063 						sm_free(up->udb_dbname); /* XXX */
1064 					goto tempfail;
1065 				}
1066 				if (up->udb_dbname != spec)
1067 					sm_free(up->udb_dbname); /* XXX */
1068 				break;
1069 			}
1070 			if (tTd(28, 1))
1071 			{
1072 #  if DB_VERSION_MAJOR < 2
1073 				sm_dprintf("_udbx_init: dbopen(%s)\n",
1074 #  else /* DB_VERSION_MAJOR < 2 */
1075 				sm_dprintf("_udbx_init: db_open(%s)\n",
1076 #  endif /* DB_VERSION_MAJOR < 2 */
1077 					up->udb_dbname);
1078 			}
1079 			up->udb_type = UDB_DBFETCH;
1080 			up->udb_pid = CurrentPid;
1081 			ents++;
1082 			up++;
1083 			break;
1084 # endif /* NEWDB */
1085 
1086 		  default:
1087 # if HESIOD
1088 badspec:
1089 # endif /* HESIOD */
1090 			syserr("Unknown UDB spec %s", spec);
1091 			break;
1092 		}
1093 	}
1094 	up->udb_type = UDB_EOLIST;
1095 
1096 	if (tTd(28, 4))
1097 	{
1098 		for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
1099 		{
1100 			switch (up->udb_type)
1101 			{
1102 			  case UDB_REMOTE:
1103 				sm_dprintf("REMOTE: addr %s, timeo %d\n",
1104 					   anynet_ntoa((SOCKADDR *) &up->udb_addr),
1105 					   up->udb_timeout);
1106 				break;
1107 
1108 			  case UDB_DBFETCH:
1109 # if NEWDB
1110 				sm_dprintf("FETCH: file %s\n",
1111 					up->udb_dbname);
1112 # else /* NEWDB */
1113 				sm_dprintf("FETCH\n");
1114 # endif /* NEWDB */
1115 				break;
1116 
1117 			  case UDB_FORWARD:
1118 				sm_dprintf("FORWARD: host %s\n",
1119 					up->udb_fwdhost);
1120 				break;
1121 
1122 			  case UDB_HESIOD:
1123 				sm_dprintf("HESIOD\n");
1124 				break;
1125 
1126 			  default:
1127 				sm_dprintf("UNKNOWN\n");
1128 				break;
1129 			}
1130 		}
1131 	}
1132 
1133 	UdbInitialized = true;
1134 	errno = 0;
1135 	return EX_OK;
1136 
1137 	/*
1138 	**  On temporary failure, back out anything we've already done
1139 	*/
1140 
1141   tempfail:
1142 # if NEWDB
1143 	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
1144 	{
1145 		if (up->udb_type == UDB_DBFETCH)
1146 		{
1147 #  if DB_VERSION_MAJOR < 2
1148 			(*up->udb_dbp->close)(up->udb_dbp);
1149 #  else /* DB_VERSION_MAJOR < 2 */
1150 			errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
1151 #  endif /* DB_VERSION_MAJOR < 2 */
1152 			if (tTd(28, 1))
1153 				sm_dprintf("_udbx_init: db->close(%s)\n",
1154 					up->udb_dbname);
1155 		}
1156 	}
1157 # endif /* NEWDB */
1158 	return EX_TEMPFAIL;
1159 }
1160 
1161 static int
1162 _udb_parsespec(udbspec, opt, maxopts)
1163 	char *udbspec;
1164 	struct udb_option opt[];
1165 	int maxopts;
1166 {
1167 	register char *spec;
1168 	register char *spec_end;
1169 	register int optnum;
1170 
1171 	spec_end = strchr(udbspec, ':');
1172 	for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
1173 	{
1174 		register char *p;
1175 
1176 		while (isascii(*spec) && isspace(*spec))
1177 			spec++;
1178 		spec_end = strchr(spec, ':');
1179 		if (spec_end != NULL)
1180 			*spec_end++ = '\0';
1181 
1182 		opt[optnum].udbo_name = spec;
1183 		opt[optnum].udbo_val = NULL;
1184 		p = strchr(spec, '=');
1185 		if (p != NULL)
1186 			opt[optnum].udbo_val = ++p;
1187 	}
1188 	return optnum;
1189 }
1190 /*
1191 **  _UDBX_CLOSE -- close all file based UDB entries.
1192 **
1193 **	Parameters:
1194 **		none
1195 **
1196 **	Returns:
1197 **		none
1198 */
1199 void
1200 _udbx_close()
1201 {
1202 	struct udbent *up;
1203 
1204 	if (!UdbInitialized)
1205 		return;
1206 
1207 	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
1208 	{
1209 		if (up->udb_pid != CurrentPid)
1210 			continue;
1211 
1212 # if NEWDB
1213 		if (up->udb_type == UDB_DBFETCH)
1214 		{
1215 #  if DB_VERSION_MAJOR < 2
1216 			(*up->udb_dbp->close)(up->udb_dbp);
1217 #  else /* DB_VERSION_MAJOR < 2 */
1218 			errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
1219 #  endif /* DB_VERSION_MAJOR < 2 */
1220 		}
1221 		if (tTd(28, 1))
1222 			sm_dprintf("_udbx_init: db->close(%s)\n",
1223 				up->udb_dbname);
1224 # endif /* NEWDB */
1225 	}
1226 }
1227 
1228 # if HESIOD
1229 
1230 static int
1231 hes_udb_get(key, info)
1232 	DBT *key;
1233 	DBT *info;
1234 {
1235 	char *name, *type;
1236 	char **hp;
1237 	char kbuf[MAXUDBKEY + 1];
1238 
1239 	if (sm_strlcpy(kbuf, key->data, sizeof kbuf) >= sizeof kbuf)
1240 		return 0;
1241 	name = kbuf;
1242 	type = strrchr(name, ':');
1243 	if (type == NULL)
1244 		return 1;
1245 	*type++ = '\0';
1246 	if (strchr(name, '@') != NULL)
1247 		return 1;
1248 
1249 	if (tTd(28, 1))
1250 		sm_dprintf("hes_udb_get(%s, %s)\n", name, type);
1251 
1252 	/* make the hesiod query */
1253 #  ifdef HESIOD_INIT
1254 	if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
1255 		return -1;
1256 	hp = hesiod_resolve(HesiodContext, name, type);
1257 #  else /* HESIOD_INIT */
1258 	hp = hes_resolve(name, type);
1259 #  endif /* HESIOD_INIT */
1260 	*--type = ':';
1261 #  ifdef HESIOD_INIT
1262 	if (hp == NULL)
1263 		return 1;
1264 	if (*hp == NULL)
1265 	{
1266 		hesiod_free_list(HesiodContext, hp);
1267 		if (errno == ECONNREFUSED || errno == EMSGSIZE)
1268 			return -1;
1269 		return 1;
1270 	}
1271 #  else /* HESIOD_INIT */
1272 	if (hp == NULL || hp[0] == NULL)
1273 	{
1274 		/* network problem or timeout */
1275 		if (hes_error() == HES_ER_NET)
1276 			return -1;
1277 
1278 		return 1;
1279 	}
1280 #  endif /* HESIOD_INIT */
1281 	else
1282 	{
1283 		/*
1284 		**  If there are multiple matches, just return the
1285 		**  first one.
1286 		**
1287 		**  XXX These should really be returned; for example,
1288 		**  XXX it is legal for :maildrop to be multi-valued.
1289 		*/
1290 
1291 		info->data = hp[0];
1292 		info->size = (size_t) strlen(info->data);
1293 	}
1294 
1295 	if (tTd(28, 80))
1296 		sm_dprintf("hes_udb_get => %s\n", *hp);
1297 
1298 	return 0;
1299 }
1300 # endif /* HESIOD */
1301 
1302 #else /* USERDB */
1303 
1304 int
1305 udbexpand(a, sendq, aliaslevel, e)
1306 	ADDRESS *a;
1307 	ADDRESS **sendq;
1308 	int aliaslevel;
1309 	ENVELOPE *e;
1310 {
1311 	return EX_OK;
1312 }
1313 
1314 #endif /* USERDB */
1315