1 /*
2  * Copyright (c) 1998-2001, 2008 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983 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 #include <sm/gen.h>
15 
16 SM_IDSTR(copyright,
17 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
18 	All rights reserved.\n\
19      Copyright (c) 1983 Eric P. Allman.  All rights reserved.\n\
20      Copyright (c) 1988, 1993\n\
21 	The Regents of the University of California.  All rights reserved.\n")
22 
23 SM_IDSTR(id, "@(#)$Id: praliases.c,v 8.96 2008/07/10 20:13:10 ca Exp $")
24 
25 #include <sys/types.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #ifdef EX_OK
30 # undef EX_OK		/* unistd.h may have another use for this */
31 #endif /* EX_OK */
32 #include <sysexits.h>
33 
34 
35 #ifndef NOT_SENDMAIL
36 # define NOT_SENDMAIL
37 #endif /* ! NOT_SENDMAIL */
38 #include <sendmail/sendmail.h>
39 #include <sendmail/pathnames.h>
40 #include <libsmdb/smdb.h>
41 
42 static void praliases __P((char *, int, char **));
43 
44 uid_t	RealUid;
45 gid_t	RealGid;
46 char	*RealUserName;
47 uid_t	RunAsUid;
48 gid_t	RunAsGid;
49 char	*RunAsUserName;
50 int	Verbose = 2;
51 bool	DontInitGroups = false;
52 uid_t	TrustedUid = 0;
53 BITMAP256 DontBlameSendmail;
54 
55 # define DELIMITERS		" ,/"
56 # define PATH_SEPARATOR		':'
57 
58 int
59 main(argc, argv)
60 	int argc;
61 	char **argv;
62 {
63 	char *cfile;
64 	char *filename = NULL;
65 	SM_FILE_T *cfp;
66 	int ch;
67 	char afilebuf[MAXLINE];
68 	char buf[MAXLINE];
69 	struct passwd *pw;
70 	static char rnamebuf[MAXNAME];
71 	extern char *optarg;
72 	extern int optind;
73 
74 	clrbitmap(DontBlameSendmail);
75 	RunAsUid = RealUid = getuid();
76 	RunAsGid = RealGid = getgid();
77 	pw = getpwuid(RealUid);
78 	if (pw != NULL)
79 	{
80 		if (strlen(pw->pw_name) > MAXNAME - 1)
81 			pw->pw_name[MAXNAME] = 0;
82 		sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
83 	}
84 	else
85 		(void) sm_snprintf(rnamebuf, sizeof rnamebuf,
86 		    "Unknown UID %d", (int) RealUid);
87 	RunAsUserName = RealUserName = rnamebuf;
88 
89 	cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
90 	while ((ch = getopt(argc, argv, "C:f:")) != -1)
91 	{
92 		switch ((char)ch) {
93 		case 'C':
94 			cfile = optarg;
95 			break;
96 		case 'f':
97 			filename = optarg;
98 			break;
99 		case '?':
100 		default:
101 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
102 			    "usage: praliases [-C cffile] [-f aliasfile]"
103 			    " [key ...]\n");
104 			exit(EX_USAGE);
105 		}
106 	}
107 	argc -= optind;
108 	argv += optind;
109 
110 	if (filename != NULL)
111 	{
112 		praliases(filename, argc, argv);
113 		exit(EX_OK);
114 	}
115 
116 	if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY,
117 			      NULL)) == NULL)
118 	{
119 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
120 				     "praliases: %s: %s\n", cfile,
121 				     sm_errstring(errno));
122 		exit(EX_NOINPUT);
123 	}
124 
125 	while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
126 	{
127 		register char *b, *p;
128 
129 		b = strchr(buf, '\n');
130 		if (b != NULL)
131 			*b = '\0';
132 
133 		b = buf;
134 		switch (*b++)
135 		{
136 		  case 'O':		/* option -- see if alias file */
137 			if (sm_strncasecmp(b, " AliasFile", 10) == 0 &&
138 			    !(isascii(b[10]) && isalnum(b[10])))
139 			{
140 				/* new form -- find value */
141 				b = strchr(b, '=');
142 				if (b == NULL)
143 					continue;
144 				while (isascii(*++b) && isspace(*b))
145 					continue;
146 			}
147 			else if (*b++ != 'A')
148 			{
149 				/* something else boring */
150 				continue;
151 			}
152 
153 			/* this is the A or AliasFile option -- save it */
154 			if (sm_strlcpy(afilebuf, b, sizeof afilebuf) >=
155 			    sizeof afilebuf)
156 			{
157 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
158 				    "praliases: AliasFile filename too long: %.30s\n",
159 					b);
160 				(void) sm_io_close(cfp, SM_TIME_DEFAULT);
161 				exit(EX_CONFIG);
162 			}
163 			b = afilebuf;
164 
165 			for (p = b; p != NULL; )
166 			{
167 				while (isascii(*p) && isspace(*p))
168 					p++;
169 				if (*p == '\0')
170 					break;
171 				b = p;
172 
173 				p = strpbrk(p, DELIMITERS);
174 
175 				/* find end of spec */
176 				if (p != NULL)
177 				{
178 					bool quoted = false;
179 
180 					for (; *p != '\0'; p++)
181 					{
182 						/*
183 						**  Don't break into a quoted
184 						**  string.
185 						*/
186 
187 						if (*p == '"')
188 							quoted = !quoted;
189 						else if (*p == ',' && !quoted)
190 							break;
191 					}
192 
193 					/* No more alias specs follow */
194 					if (*p == '\0')
195 					{
196 						/* chop trailing whitespace */
197 						while (isascii(*p) &&
198 						       isspace(*p) &&
199 						       p > b)
200 							p--;
201 						*p = '\0';
202 						p = NULL;
203 					}
204 				}
205 
206 				if (p != NULL)
207 				{
208 					char *e = p - 1;
209 
210 					/* chop trailing whitespace */
211 					while (isascii(*e) &&
212 					       isspace(*e) &&
213 					       e > b)
214 						e--;
215 					*++e = '\0';
216 					*p++ = '\0';
217 				}
218 				praliases(b, argc, argv);
219 			}
220 
221 		  default:
222 			continue;
223 		}
224 	}
225 	(void) sm_io_close(cfp, SM_TIME_DEFAULT);
226 	exit(EX_OK);
227 	/* NOTREACHED */
228 	return EX_OK;
229 }
230 
231 static void
232 praliases(filename, argc, argv)
233 	char *filename;
234 	int argc;
235 	char **argv;
236 {
237 	int result;
238 	char *colon;
239 	char *db_name;
240 	char *db_type;
241 	SMDB_DATABASE *database = NULL;
242 	SMDB_CURSOR *cursor = NULL;
243 	SMDB_DBENT db_key, db_value;
244 	SMDB_DBPARAMS params;
245 	SMDB_USER_INFO user_info;
246 
247 	colon = strchr(filename, PATH_SEPARATOR);
248 	if (colon == NULL)
249 	{
250 		db_name = filename;
251 		db_type = SMDB_TYPE_DEFAULT;
252 	}
253 	else
254 	{
255 		*colon = '\0';
256 		db_name = colon + 1;
257 		db_type = filename;
258 	}
259 
260 	/* clean off arguments */
261 	for (;;)
262 	{
263 		while (isascii(*db_name) && isspace(*db_name))
264 			db_name++;
265 
266 		if (*db_name != '-')
267 			break;
268 		while (*db_name != '\0' &&
269 		       !(isascii(*db_name) && isspace(*db_name)))
270 			db_name++;
271 	}
272 
273 	/* Skip non-file based DB types */
274 	if (db_type != NULL && *db_type != '\0')
275 	{
276 		if (db_type != SMDB_TYPE_DEFAULT &&
277 		    strcmp(db_type, "hash") != 0 &&
278 		    strcmp(db_type, "btree") != 0 &&
279 		    strcmp(db_type, "dbm") != 0)
280 		{
281 			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
282 				      "praliases: Skipping non-file based alias type %s\n",
283 				db_type);
284 			return;
285 		}
286 	}
287 
288 	if (*db_name == '\0' || (db_type != NULL && *db_type == '\0'))
289 	{
290 		if (colon != NULL)
291 			*colon = ':';
292 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
293 		    "praliases: illegal alias specification: %s\n", filename);
294 		goto fatal;
295 	}
296 
297 	memset(&params, '\0', sizeof params);
298 	params.smdbp_cache_size = 1024 * 1024;
299 
300 	user_info.smdbu_id = RunAsUid;
301 	user_info.smdbu_group_id = RunAsGid;
302 	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
303 			  SMDB_MAX_USER_NAME_LEN);
304 
305 	result = smdb_open_database(&database, db_name, O_RDONLY, 0,
306 				    SFF_ROOTOK, db_type, &user_info, &params);
307 	if (result != SMDBE_OK)
308 	{
309 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
310 			      "praliases: %s: open: %s\n",
311 			      db_name, sm_errstring(result));
312 		goto fatal;
313 	}
314 
315 	if (argc == 0)
316 	{
317 		memset(&db_key, '\0', sizeof db_key);
318 		memset(&db_value, '\0', sizeof db_value);
319 
320 		result = database->smdb_cursor(database, &cursor, 0);
321 		if (result != SMDBE_OK)
322 		{
323 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
324 			    "praliases: %s: set cursor: %s\n", db_name,
325 			    sm_errstring(result));
326 			goto fatal;
327 		}
328 
329 		while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
330 						   SMDB_CURSOR_GET_NEXT)) ==
331 						   SMDBE_OK)
332 		{
333 #if 0
334 			/* skip magic @:@ entry */
335 			if (db_key.size == 2 &&
336 			    db_key.data[0] == '@' &&
337 			    db_key.data[1] == '\0' &&
338 			    db_value.size == 2 &&
339 			    db_value.data[0] == '@' &&
340 			    db_value.data[1] == '\0')
341 				continue;
342 #endif /* 0 */
343 
344 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
345 					     "%.*s:%.*s\n",
346 					     (int) db_key.size,
347 					     (char *) db_key.data,
348 					     (int) db_value.size,
349 					     (char *) db_value.data);
350 		}
351 
352 		if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
353 		{
354 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
355 				"praliases: %s: get value at cursor: %s\n",
356 				db_name, sm_errstring(result));
357 			goto fatal;
358 		}
359 	}
360 	else for (; *argv != NULL; ++argv)
361 	{
362 		int get_res;
363 
364 		memset(&db_key, '\0', sizeof db_key);
365 		memset(&db_value, '\0', sizeof db_value);
366 		db_key.data = *argv;
367 		db_key.size = strlen(*argv);
368 		get_res = database->smdb_get(database, &db_key, &db_value, 0);
369 		if (get_res == SMDBE_NOT_FOUND)
370 		{
371 			db_key.size++;
372 			get_res = database->smdb_get(database, &db_key,
373 						     &db_value, 0);
374 		}
375 		if (get_res == SMDBE_OK)
376 		{
377 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
378 					     "%.*s:%.*s\n",
379 					     (int) db_key.size,
380 					     (char *) db_key.data,
381 					     (int) db_value.size,
382 					     (char *) db_value.data);
383 		}
384 		else
385 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
386 					     "%s: No such key\n",
387 					     (char *)db_key.data);
388 	}
389 
390  fatal:
391 	if (cursor != NULL)
392 		(void) cursor->smdbc_close(cursor);
393 	if (database != NULL)
394 		(void) database->smdb_close(database);
395 	if (colon != NULL)
396 		*colon = ':';
397 	return;
398 }
399