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
main(argc,argv)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 			/* FALLTHROUGH */
221 
222 		  default:
223 			continue;
224 		}
225 	}
226 	(void) sm_io_close(cfp, SM_TIME_DEFAULT);
227 	exit(EX_OK);
228 	/* NOTREACHED */
229 	return EX_OK;
230 }
231 
232 static void
praliases(filename,argc,argv)233 praliases(filename, argc, argv)
234 	char *filename;
235 	int argc;
236 	char **argv;
237 {
238 	int result;
239 	char *colon;
240 	char *db_name;
241 	char *db_type;
242 	SMDB_DATABASE *database = NULL;
243 	SMDB_CURSOR *cursor = NULL;
244 	SMDB_DBENT db_key, db_value;
245 	SMDB_DBPARAMS params;
246 	SMDB_USER_INFO user_info;
247 
248 	colon = strchr(filename, PATH_SEPARATOR);
249 	if (colon == NULL)
250 	{
251 		db_name = filename;
252 		db_type = SMDB_TYPE_DEFAULT;
253 	}
254 	else
255 	{
256 		*colon = '\0';
257 		db_name = colon + 1;
258 		db_type = filename;
259 	}
260 
261 	/* clean off arguments */
262 	for (;;)
263 	{
264 		while (isascii(*db_name) && isspace(*db_name))
265 			db_name++;
266 
267 		if (*db_name != '-')
268 			break;
269 		while (*db_name != '\0' &&
270 		       !(isascii(*db_name) && isspace(*db_name)))
271 			db_name++;
272 	}
273 
274 	/* Skip non-file based DB types */
275 	if (db_type != NULL && *db_type != '\0')
276 	{
277 		if (db_type != SMDB_TYPE_DEFAULT &&
278 		    strcmp(db_type, "hash") != 0 &&
279 		    strcmp(db_type, "btree") != 0 &&
280 		    strcmp(db_type, "dbm") != 0)
281 		{
282 			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
283 				      "praliases: Skipping non-file based alias type %s\n",
284 				db_type);
285 			return;
286 		}
287 	}
288 
289 	if (*db_name == '\0' || (db_type != NULL && *db_type == '\0'))
290 	{
291 		if (colon != NULL)
292 			*colon = ':';
293 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
294 		    "praliases: illegal alias specification: %s\n", filename);
295 		goto fatal;
296 	}
297 
298 	memset(&params, '\0', sizeof params);
299 	params.smdbp_cache_size = 1024 * 1024;
300 
301 	user_info.smdbu_id = RunAsUid;
302 	user_info.smdbu_group_id = RunAsGid;
303 	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
304 			  SMDB_MAX_USER_NAME_LEN);
305 
306 	result = smdb_open_database(&database, db_name, O_RDONLY, 0,
307 				    SFF_ROOTOK, db_type, &user_info, &params);
308 	if (result != SMDBE_OK)
309 	{
310 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
311 			      "praliases: %s: open: %s\n",
312 			      db_name, sm_errstring(result));
313 		goto fatal;
314 	}
315 
316 	if (argc == 0)
317 	{
318 		memset(&db_key, '\0', sizeof db_key);
319 		memset(&db_value, '\0', sizeof db_value);
320 
321 		result = database->smdb_cursor(database, &cursor, 0);
322 		if (result != SMDBE_OK)
323 		{
324 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
325 			    "praliases: %s: set cursor: %s\n", db_name,
326 			    sm_errstring(result));
327 			goto fatal;
328 		}
329 
330 		while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
331 						   SMDB_CURSOR_GET_NEXT)) ==
332 						   SMDBE_OK)
333 		{
334 #if 0
335 			/* skip magic @:@ entry */
336 			if (db_key.size == 2 &&
337 			    db_key.data[0] == '@' &&
338 			    db_key.data[1] == '\0' &&
339 			    db_value.size == 2 &&
340 			    db_value.data[0] == '@' &&
341 			    db_value.data[1] == '\0')
342 				continue;
343 #endif /* 0 */
344 
345 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
346 					     "%.*s:%.*s\n",
347 					     (int) db_key.size,
348 					     (char *) db_key.data,
349 					     (int) db_value.size,
350 					     (char *) db_value.data);
351 		}
352 
353 		if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
354 		{
355 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
356 				"praliases: %s: get value at cursor: %s\n",
357 				db_name, sm_errstring(result));
358 			goto fatal;
359 		}
360 	}
361 	else for (; *argv != NULL; ++argv)
362 	{
363 		int get_res;
364 
365 		memset(&db_key, '\0', sizeof db_key);
366 		memset(&db_value, '\0', sizeof db_value);
367 		db_key.data = *argv;
368 		db_key.size = strlen(*argv);
369 		get_res = database->smdb_get(database, &db_key, &db_value, 0);
370 		if (get_res == SMDBE_NOT_FOUND)
371 		{
372 			db_key.size++;
373 			get_res = database->smdb_get(database, &db_key,
374 						     &db_value, 0);
375 		}
376 		if (get_res == SMDBE_OK)
377 		{
378 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
379 					     "%.*s:%.*s\n",
380 					     (int) db_key.size,
381 					     (char *) db_key.data,
382 					     (int) db_value.size,
383 					     (char *) db_value.data);
384 		}
385 		else
386 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
387 					     "%s: No such key\n",
388 					     (char *)db_key.data);
389 	}
390 
391  fatal:
392 	if (cursor != NULL)
393 		(void) cursor->smdbc_close(cursor);
394 	if (database != NULL)
395 		(void) database->smdb_close(database);
396 	if (colon != NULL)
397 		*colon = ':';
398 	return;
399 }
400