1 /*
2  * Copyright (c) 1998-2001 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.94 2007/05/11 18:50:36 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]\n");
103 			exit(EX_USAGE);
104 		}
105 	}
106 	argc -= optind;
107 	argv += optind;
108 
109 	if (filename != NULL)
110 	{
111 		praliases(filename, argc, argv);
112 		exit(EX_OK);
113 	}
114 
115 	if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY,
116 			      NULL)) == NULL)
117 	{
118 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
119 				     "praliases: %s: %s\n", cfile,
120 				     sm_errstring(errno));
121 		exit(EX_NOINPUT);
122 	}
123 
124 	while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
125 	{
126 		register char *b, *p;
127 
128 		b = strchr(buf, '\n');
129 		if (b != NULL)
130 			*b = '\0';
131 
132 		b = buf;
133 		switch (*b++)
134 		{
135 		  case 'O':		/* option -- see if alias file */
136 			if (sm_strncasecmp(b, " AliasFile", 10) == 0 &&
137 			    !(isascii(b[10]) && isalnum(b[10])))
138 			{
139 				/* new form -- find value */
140 				b = strchr(b, '=');
141 				if (b == NULL)
142 					continue;
143 				while (isascii(*++b) && isspace(*b))
144 					continue;
145 			}
146 			else if (*b++ != 'A')
147 			{
148 				/* something else boring */
149 				continue;
150 			}
151 
152 			/* this is the A or AliasFile option -- save it */
153 			if (sm_strlcpy(afilebuf, b, sizeof afilebuf) >=
154 			    sizeof afilebuf)
155 			{
156 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
157 				    "praliases: AliasFile filename too long: %.30s\n",
158 					b);
159 				(void) sm_io_close(cfp, SM_TIME_DEFAULT);
160 				exit(EX_CONFIG);
161 			}
162 			b = afilebuf;
163 
164 			for (p = b; p != NULL; )
165 			{
166 				while (isascii(*p) && isspace(*p))
167 					p++;
168 				if (*p == '\0')
169 					break;
170 				b = p;
171 
172 				p = strpbrk(p, DELIMITERS);
173 
174 				/* find end of spec */
175 				if (p != NULL)
176 				{
177 					bool quoted = false;
178 
179 					for (; *p != '\0'; p++)
180 					{
181 						/*
182 						**  Don't break into a quoted
183 						**  string.
184 						*/
185 
186 						if (*p == '"')
187 							quoted = !quoted;
188 						else if (*p == ',' && !quoted)
189 							break;
190 					}
191 
192 					/* No more alias specs follow */
193 					if (*p == '\0')
194 					{
195 						/* chop trailing whitespace */
196 						while (isascii(*p) &&
197 						       isspace(*p) &&
198 						       p > b)
199 							p--;
200 						*p = '\0';
201 						p = NULL;
202 					}
203 				}
204 
205 				if (p != NULL)
206 				{
207 					char *e = p - 1;
208 
209 					/* chop trailing whitespace */
210 					while (isascii(*e) &&
211 					       isspace(*e) &&
212 					       e > b)
213 						e--;
214 					*++e = '\0';
215 					*p++ = '\0';
216 				}
217 				praliases(b, argc, argv);
218 			}
219 
220 		  default:
221 			continue;
222 		}
223 	}
224 	(void) sm_io_close(cfp, SM_TIME_DEFAULT);
225 	exit(EX_OK);
226 	/* NOTREACHED */
227 	return EX_OK;
228 }
229 
230 static void
231 praliases(filename, argc, argv)
232 	char *filename;
233 	int argc;
234 	char **argv;
235 {
236 	int result;
237 	char *colon;
238 	char *db_name;
239 	char *db_type;
240 	SMDB_DATABASE *database = NULL;
241 	SMDB_CURSOR *cursor = NULL;
242 	SMDB_DBENT db_key, db_value;
243 	SMDB_DBPARAMS params;
244 	SMDB_USER_INFO user_info;
245 
246 	colon = strchr(filename, PATH_SEPARATOR);
247 	if (colon == NULL)
248 	{
249 		db_name = filename;
250 		db_type = SMDB_TYPE_DEFAULT;
251 	}
252 	else
253 	{
254 		*colon = '\0';
255 		db_name = colon + 1;
256 		db_type = filename;
257 	}
258 
259 	/* clean off arguments */
260 	for (;;)
261 	{
262 		while (isascii(*db_name) && isspace(*db_name))
263 			db_name++;
264 
265 		if (*db_name != '-')
266 			break;
267 		while (*db_name != '\0' &&
268 		       !(isascii(*db_name) && isspace(*db_name)))
269 			db_name++;
270 	}
271 
272 	/* Skip non-file based DB types */
273 	if (db_type != NULL && *db_type != '\0')
274 	{
275 		if (db_type != SMDB_TYPE_DEFAULT &&
276 		    strcmp(db_type, "hash") != 0 &&
277 		    strcmp(db_type, "btree") != 0 &&
278 		    strcmp(db_type, "dbm") != 0)
279 		{
280 			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
281 				      "praliases: Skipping non-file based alias type %s\n",
282 				db_type);
283 			return;
284 		}
285 	}
286 
287 	if (*db_name == '\0' || (db_type != NULL && *db_type == '\0'))
288 	{
289 		if (colon != NULL)
290 			*colon = ':';
291 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
292 		    "praliases: illegal alias specification: %s\n", filename);
293 		goto fatal;
294 	}
295 
296 	memset(&params, '\0', sizeof params);
297 	params.smdbp_cache_size = 1024 * 1024;
298 
299 	user_info.smdbu_id = RunAsUid;
300 	user_info.smdbu_group_id = RunAsGid;
301 	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
302 			  SMDB_MAX_USER_NAME_LEN);
303 
304 	result = smdb_open_database(&database, db_name, O_RDONLY, 0,
305 				    SFF_ROOTOK, db_type, &user_info, &params);
306 	if (result != SMDBE_OK)
307 	{
308 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
309 			      "praliases: %s: open: %s\n",
310 			      db_name, sm_errstring(result));
311 		goto fatal;
312 	}
313 
314 	if (argc == 0)
315 	{
316 		memset(&db_key, '\0', sizeof db_key);
317 		memset(&db_value, '\0', sizeof db_value);
318 
319 		result = database->smdb_cursor(database, &cursor, 0);
320 		if (result != SMDBE_OK)
321 		{
322 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
323 			    "praliases: %s: set cursor: %s\n", db_name,
324 			    sm_errstring(result));
325 			goto fatal;
326 		}
327 
328 		while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
329 						   SMDB_CURSOR_GET_NEXT)) ==
330 						   SMDBE_OK)
331 		{
332 #if 0
333 			/* skip magic @:@ entry */
334 			if (db_key.size == 2 &&
335 			    db_key.data[0] == '@' &&
336 			    db_key.data[1] == '\0' &&
337 			    db_value.size == 2 &&
338 			    db_value.data[0] == '@' &&
339 			    db_value.data[1] == '\0')
340 				continue;
341 #endif /* 0 */
342 
343 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
344 					     "%.*s:%.*s\n",
345 					     (int) db_key.size,
346 					     (char *) db_key.data,
347 					     (int) db_value.size,
348 					     (char *) db_value.data);
349 		}
350 
351 		if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
352 		{
353 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
354 				"praliases: %s: get value at cursor: %s\n",
355 				db_name, sm_errstring(result));
356 			goto fatal;
357 		}
358 	}
359 	else for (; *argv != NULL; ++argv)
360 	{
361 		int get_res;
362 
363 		memset(&db_key, '\0', sizeof db_key);
364 		memset(&db_value, '\0', sizeof db_value);
365 		db_key.data = *argv;
366 		db_key.size = strlen(*argv);
367 		get_res = database->smdb_get(database, &db_key, &db_value, 0);
368 		if (get_res == SMDBE_NOT_FOUND)
369 		{
370 			db_key.size++;
371 			get_res = database->smdb_get(database, &db_key,
372 						     &db_value, 0);
373 		}
374 		if (get_res == SMDBE_OK)
375 		{
376 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
377 					     "%.*s:%.*s\n",
378 					     (int) db_key.size,
379 					     (char *) db_key.data,
380 					     (int) db_value.size,
381 					     (char *) db_value.data);
382 		}
383 		else
384 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
385 					     "%s: No such key\n",
386 					     (char *)db_key.data);
387 	}
388 
389  fatal:
390 	if (cursor != NULL)
391 		(void) cursor->smdbc_close(cursor);
392 	if (database != NULL)
393 		(void) database->smdb_close(database);
394 	if (colon != NULL)
395 		*colon = ':';
396 	return;
397 }
398