1 /*
2  * Copyright (c) 1998-2001, 2008 Proofpoint, 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 Proofpoint, 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.98 2013-11-22 20:51:53 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
32 #include <sysexits.h>
33 
34 
35 #ifndef NOT_SENDMAIL
36 # define NOT_SENDMAIL
37 #endif
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)) >= 0)
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 		    !smdb_is_db_type(db_type))
278 		{
279 			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
280 				      "praliases: Skipping non-file based alias type %s\n",
281 				db_type);
282 			return;
283 		}
284 	}
285 
286 	if (*db_name == '\0' || (db_type != NULL && *db_type == '\0'))
287 	{
288 		if (colon != NULL)
289 			*colon = ':';
290 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
291 		    "praliases: illegal alias specification: %s\n", filename);
292 		goto fatal;
293 	}
294 
295 	memset(&params, '\0', sizeof params);
296 	params.smdbp_cache_size = 1024 * 1024;
297 
298 	user_info.smdbu_id = RunAsUid;
299 	user_info.smdbu_group_id = RunAsGid;
300 	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
301 			  SMDB_MAX_USER_NAME_LEN);
302 
303 	result = smdb_open_database(&database, db_name, O_RDONLY, 0,
304 				    SFF_ROOTOK, db_type, &user_info, &params);
305 	if (result != SMDBE_OK)
306 	{
307 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
308 			      "praliases: %s: open: %s\n",
309 			      db_name, sm_errstring(result));
310 		goto fatal;
311 	}
312 
313 	if (argc == 0)
314 	{
315 		memset(&db_key, '\0', sizeof db_key);
316 		memset(&db_value, '\0', sizeof db_value);
317 
318 		result = database->smdb_cursor(database, &cursor, 0);
319 		if (result != SMDBE_OK)
320 		{
321 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
322 			    "praliases: %s: set cursor: %s\n", db_name,
323 			    sm_errstring(result));
324 			goto fatal;
325 		}
326 
327 		while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
328 						   SMDB_CURSOR_GET_NEXT)) ==
329 						   SMDBE_OK)
330 		{
331 #if 0
332 			/* skip magic @:@ entry */
333 			if (db_key.size == 2 &&
334 			    db_key.data[0] == '@' &&
335 			    db_key.data[1] == '\0' &&
336 			    db_value.size == 2 &&
337 			    db_value.data[0] == '@' &&
338 			    db_value.data[1] == '\0')
339 				continue;
340 #endif /* 0 */
341 
342 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
343 					     "%.*s:%.*s\n",
344 					     (int) db_key.size,
345 					     (char *) db_key.data,
346 					     (int) db_value.size,
347 					     (char *) db_value.data);
348 		}
349 
350 		if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
351 		{
352 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
353 				"praliases: %s: get value at cursor: %s\n",
354 				db_name, sm_errstring(result));
355 			goto fatal;
356 		}
357 	}
358 	else for (; *argv != NULL; ++argv)
359 	{
360 		int get_res;
361 
362 		memset(&db_key, '\0', sizeof db_key);
363 		memset(&db_value, '\0', sizeof db_value);
364 		db_key.data = *argv;
365 		db_key.size = strlen(*argv);
366 		get_res = database->smdb_get(database, &db_key, &db_value, 0);
367 		if (get_res == SMDBE_NOT_FOUND)
368 		{
369 			db_key.size++;
370 			get_res = database->smdb_get(database, &db_key,
371 						     &db_value, 0);
372 		}
373 		if (get_res == SMDBE_OK)
374 		{
375 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
376 					     "%.*s:%.*s\n",
377 					     (int) db_key.size,
378 					     (char *) db_key.data,
379 					     (int) db_value.size,
380 					     (char *) db_value.data);
381 		}
382 		else
383 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
384 					     "%s: No such key\n",
385 					     (char *)db_key.data);
386 	}
387 
388  fatal:
389 	if (cursor != NULL)
390 		(void) cursor->smdbc_close(cursor);
391 	if (database != NULL)
392 		(void) database->smdb_close(database);
393 	if (colon != NULL)
394 		*colon = ':';
395 	return;
396 }
397