xref: /freebsd/contrib/sendmail/makemap/makemap.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*
2  * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1992, 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 #ifndef lint
15 static char copyright[] =
16 "@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
17 	All rights reserved.\n\
18      Copyright (c) 1992 Eric P. Allman.  All rights reserved.\n\
19      Copyright (c) 1992, 1993\n\
20 	The Regents of the University of California.  All rights reserved.\n";
21 #endif /* ! lint */
22 
23 #ifndef lint
24 static char id[] = "@(#)$Id: makemap.c,v 8.135.4.11 2000/09/13 01:11:10 gshapiro Exp $";
25 #endif /* ! lint */
26 
27 /* $FreeBSD$ */
28 
29 #include <sys/types.h>
30 #ifndef ISC_UNIX
31 # include <sys/file.h>
32 #endif /* ! ISC_UNIX */
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #ifdef EX_OK
37 # undef EX_OK		/* unistd.h may have another use for this */
38 #endif /* EX_OK */
39 #include <sysexits.h>
40 #include <sendmail/sendmail.h>
41 #include <sendmail/pathnames.h>
42 #include <libsmdb/smdb.h>
43 
44 uid_t	RealUid;
45 gid_t	RealGid;
46 char	*RealUserName;
47 uid_t	RunAsUid;
48 uid_t	RunAsGid;
49 char	*RunAsUserName;
50 int	Verbose = 2;
51 bool	DontInitGroups = FALSE;
52 uid_t	TrustedUid = 0;
53 BITMAP256 DontBlameSendmail;
54 
55 #define BUFSIZE		1024
56 #if _FFR_DELIM
57 # define ISSEP(c) ((sep == '\0' && isascii(c) && isspace(c)) || (c) == sep)
58 #else /* _FFR_DELIM */
59 # define ISSEP(c) (isascii(c) && isspace(c))
60 #endif /* _FFR_DELIM */
61 
62 
63 static void
64 usage(progname)
65 	char *progname;
66 {
67 	fprintf(stderr,
68 		"Usage: %s [-C cffile] [-N] [-c cachesize] [-d] [-e] [-f] [-l] [-o] [-r] [-s] %s[-u] [-v] type mapname\n",
69 		progname,
70 #if _FFR_DELIM
71 		"[-t  delimiter] "
72 #else /* _FFR_DELIM */
73 		""
74 #endif /* _FFR_DELIM */
75 		);
76 	exit(EX_USAGE);
77 }
78 
79 int
80 main(argc, argv)
81 	int argc;
82 	char **argv;
83 {
84 	char *progname;
85 	char *cfile;
86 	bool inclnull = FALSE;
87 	bool notrunc = FALSE;
88 	bool allowreplace = FALSE;
89 	bool allowempty = FALSE;
90 	bool verbose = FALSE;
91 	bool foldcase = TRUE;
92 	bool unmake = FALSE;
93 #if _FFR_DELIM
94 	char sep = '\0';
95 #endif /* _FFR_DELIM */
96 	int exitstat;
97 	int opt;
98 	char *typename = NULL;
99 	char *mapname = NULL;
100 	int lineno;
101 	int st;
102 	int mode;
103 	int smode;
104 	int putflags = 0;
105 	long sff = SFF_ROOTOK|SFF_REGONLY;
106 	struct passwd *pw;
107 	SMDB_DATABASE *database;
108 	SMDB_CURSOR *cursor;
109 	SMDB_DBENT db_key, db_val;
110 	SMDB_DBPARAMS params;
111 	SMDB_USER_INFO user_info;
112 	char ibuf[BUFSIZE];
113 #if HASFCHOWN
114 	FILE *cfp;
115 	char buf[MAXLINE];
116 #endif /* HASFCHOWN */
117 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
118 	extern char *optarg;
119 	extern int optind;
120 
121 	memset(&params, '\0', sizeof params);
122 	params.smdbp_cache_size = 1024 * 1024;
123 
124 	progname = strrchr(argv[0], '/');
125 	if (progname != NULL)
126 		progname++;
127 	else
128 		progname = argv[0];
129 	cfile = _PATH_SENDMAILCF;
130 
131 	clrbitmap(DontBlameSendmail);
132 	RunAsUid = RealUid = getuid();
133 	RunAsGid = RealGid = getgid();
134 	pw = getpwuid(RealUid);
135 	if (pw != NULL)
136 		(void) strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
137 	else
138 		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
139 				(int) RealUid);
140 	RunAsUserName = RealUserName = rnamebuf;
141 	user_info.smdbu_id = RunAsUid;
142 	user_info.smdbu_group_id = RunAsGid;
143 	(void) strlcpy(user_info.smdbu_name, RunAsUserName,
144 		       SMDB_MAX_USER_NAME_LEN);
145 
146 
147 #define OPTIONS		"C:Nc:t:deflorsuv"
148 	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
149 	{
150 		switch (opt)
151 		{
152 		  case 'C':
153 			cfile = optarg;
154 			break;
155 
156 		  case 'N':
157 			inclnull = TRUE;
158 			break;
159 
160 		  case 'c':
161 			params.smdbp_cache_size = atol(optarg);
162 			break;
163 
164 		  case 'd':
165 			params.smdbp_allow_dup = TRUE;
166 			break;
167 
168 		  case 'e':
169 			allowempty = TRUE;
170 			break;
171 
172 		  case 'f':
173 			foldcase = FALSE;
174 			break;
175 
176 		  case 'l':
177 			smdb_print_available_types();
178 			exit(EX_OK);
179 			break;
180 
181 		  case 'o':
182 			notrunc = TRUE;
183 			break;
184 
185 		  case 'r':
186 			allowreplace = TRUE;
187 			break;
188 
189 		  case 's':
190 			setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
191 			setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
192 			setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
193 			setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
194 			break;
195 
196 #if _FFR_DELIM
197 		  case 't':
198 			if (optarg == NULL || *optarg == '\0')
199 			{
200 				fprintf(stderr, "Invalid separator\n");
201 				break;
202 			}
203 			sep = *optarg;
204 			break;
205 #endif /* _FFR_DELIM */
206 
207 		  case 'u':
208 			unmake = TRUE;
209 			break;
210 
211 		  case 'v':
212 			verbose = TRUE;
213 			break;
214 
215 		  default:
216 			usage(progname);
217 			/* NOTREACHED */
218 		}
219 	}
220 
221 	if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
222 		sff |= SFF_NOSLINK;
223 	if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
224 		sff |= SFF_NOHLINK;
225 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
226 		sff |= SFF_NOWLINK;
227 
228 	argc -= optind;
229 	argv += optind;
230 	if (argc != 2)
231 	{
232 		usage(progname);
233 		/* NOTREACHED */
234 	}
235 	else
236 	{
237 		typename = argv[0];
238 		mapname = argv[1];
239 	}
240 
241 #if HASFCHOWN
242 	/* Find TrustedUser value in sendmail.cf */
243 	if ((cfp = fopen(cfile, "r")) == NULL)
244 	{
245 		fprintf(stderr, "makemap: %s: %s", cfile, errstring(errno));
246 		exit(EX_NOINPUT);
247 	}
248 	while (fgets(buf, sizeof(buf), cfp) != NULL)
249 	{
250 		register char *b;
251 
252 		if ((b = strchr(buf, '\n')) != NULL)
253 			*b = '\0';
254 
255 		b = buf;
256 		switch (*b++)
257 		{
258 		  case 'O':		/* option */
259 			if (strncasecmp(b, " TrustedUser", 12) == 0 &&
260 			    !(isascii(b[12]) && isalnum(b[12])))
261 			{
262 				b = strchr(b, '=');
263 				if (b == NULL)
264 					continue;
265 				while (isascii(*++b) && isspace(*b))
266 					continue;
267 				if (isascii(*b) && isdigit(*b))
268 					TrustedUid = atoi(b);
269 				else
270 				{
271 					TrustedUid = 0;
272 					pw = getpwnam(b);
273 					if (pw == NULL)
274 						fprintf(stderr,
275 							"TrustedUser: unknown user %s\n", b);
276 					else
277 						TrustedUid = pw->pw_uid;
278 				}
279 
280 # ifdef UID_MAX
281 				if (TrustedUid > UID_MAX)
282 				{
283 					fprintf(stderr,
284 						"TrustedUser: uid value (%ld) > UID_MAX (%ld)",
285 						(long) TrustedUid,
286 						(long) UID_MAX);
287 					TrustedUid = 0;
288 				}
289 # endif /* UID_MAX */
290 				break;
291 			}
292 
293 
294 		  default:
295 			continue;
296 		}
297 	}
298 	(void) fclose(cfp);
299 #endif /* HASFCHOWN */
300 
301 	if (!params.smdbp_allow_dup && !allowreplace)
302 		putflags = SMDBF_NO_OVERWRITE;
303 
304 	if (unmake)
305 	{
306 		mode = O_RDONLY;
307 		smode = S_IRUSR;
308 	}
309 	else
310 	{
311 		mode = O_RDWR;
312 		if (!notrunc)
313 		{
314 			mode |= O_CREAT|O_TRUNC;
315 			sff |= SFF_CREAT;
316 		}
317 		smode = S_IWUSR;
318 	}
319 
320 	params.smdbp_num_elements = 4096;
321 
322 	errno = smdb_open_database(&database, mapname, mode, smode, sff,
323 				   typename, &user_info, &params);
324 	if (errno != SMDBE_OK)
325 	{
326 		char *hint;
327 
328 		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
329 		    (hint = smdb_db_definition(typename)) != NULL)
330 			fprintf(stderr,
331 				"%s: Need to recompile with -D%s for %s support\n",
332 				progname, hint, typename);
333 		else
334 			fprintf(stderr,
335 				"%s: error opening type %s map %s: %s\n",
336 				progname, typename, mapname, errstring(errno));
337 		exit(EX_CANTCREAT);
338 	}
339 
340 	(void) database->smdb_sync(database, 0);
341 
342 	if (!unmake && geteuid() == 0 && TrustedUid != 0)
343 	{
344 		errno = database->smdb_set_owner(database, TrustedUid, -1);
345 		if (errno != SMDBE_OK)
346 		{
347 			fprintf(stderr,
348 				"WARNING: ownership change on %s failed %s",
349 				mapname, errstring(errno));
350 		}
351 	}
352 
353 	/*
354 	**  Copy the data
355 	*/
356 
357 	exitstat = EX_OK;
358 	if (unmake)
359 	{
360 		bool stop;
361 		errno = database->smdb_cursor(database, &cursor, 0);
362 		if (errno != SMDBE_OK)
363 		{
364 
365 			fprintf(stderr,
366 				"%s: cannot make cursor for type %s map %s\n",
367 				progname, typename, mapname);
368 			exit(EX_SOFTWARE);
369 		}
370 
371 		memset(&db_key, '\0', sizeof db_key);
372 		memset(&db_val, '\0', sizeof db_val);
373 
374 		for (stop = FALSE, lineno = 0; !stop; lineno++)
375 		{
376 			errno = cursor->smdbc_get(cursor, &db_key, &db_val,
377 						  SMDB_CURSOR_GET_NEXT);
378 			if (errno != SMDBE_OK)
379 			{
380 				stop = TRUE;
381 			}
382 			if (!stop)
383 				printf("%.*s\t%.*s\n",
384 				       (int) db_key.data.size,
385 				       (char *) db_key.data.data,
386 				       (int) db_val.data.size,
387 				       (char *)db_val.data.data);
388 
389 		}
390 		(void) cursor->smdbc_close(cursor);
391 	}
392 	else
393 	{
394 		lineno = 0;
395 		while (fgets(ibuf, sizeof ibuf, stdin) != NULL)
396 		{
397 			register char *p;
398 
399 			lineno++;
400 
401 			/*
402 			**  Parse the line.
403 			*/
404 
405 			p = strchr(ibuf, '\n');
406 			if (p != NULL)
407 				*p = '\0';
408 			else if (!feof(stdin))
409 			{
410 				fprintf(stderr,
411 					"%s: %s: line %d: line too long (%ld bytes max)\n",
412 					progname, mapname, lineno, (long) sizeof ibuf);
413 				exitstat = EX_DATAERR;
414 				continue;
415 			}
416 
417 			if (ibuf[0] == '\0' || ibuf[0] == '#')
418 				continue;
419 			if (
420 #if _FFR_DELIM
421 			    sep == '\0' &&
422 #endif /* _FFR_DELIM */
423 			    isascii(ibuf[0]) && isspace(ibuf[0]))
424 			{
425 				fprintf(stderr,
426 					"%s: %s: line %d: syntax error (leading space)\n",
427 					progname, mapname, lineno);
428 				exitstat = EX_DATAERR;
429 				continue;
430 			}
431 
432 			memset(&db_key, '\0', sizeof db_key);
433 			memset(&db_val, '\0', sizeof db_val);
434 			db_key.data.data = ibuf;
435 
436 			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
437 			{
438 				if (foldcase && isascii(*p) && isupper(*p))
439 					*p = tolower(*p);
440 			}
441 			db_key.data.size = p - ibuf;
442 			if (inclnull)
443 				db_key.data.size++;
444 
445 			if (*p != '\0')
446 				*p++ = '\0';
447 			while (ISSEP(*p))
448 				p++;
449 			if (!allowempty && *p == '\0')
450 			{
451 				fprintf(stderr,
452 					"%s: %s: line %d: no RHS for LHS %s\n",
453 					progname, mapname, lineno,
454 					(char *) db_key.data.data);
455 				exitstat = EX_DATAERR;
456 				continue;
457 			}
458 
459 			db_val.data.data = p;
460 			db_val.data.size = strlen(p);
461 			if (inclnull)
462 				db_val.data.size++;
463 
464 			/*
465 			**  Do the database insert.
466 			*/
467 
468 			if (verbose)
469 			{
470 				printf("key=`%s', val=`%s'\n",
471 				       (char *) db_key.data.data,
472 				       (char *) db_val.data.data);
473 			}
474 
475 			errno = database->smdb_put(database, &db_key, &db_val,
476 						   putflags);
477 			switch (errno)
478 			{
479 			  case SMDBE_KEY_EXIST:
480 				st = 1;
481 				break;
482 
483 			  case 0:
484 				st = 0;
485 				break;
486 
487 			  default:
488 				st = -1;
489 				break;
490 			}
491 
492 			if (st < 0)
493 			{
494 				fprintf(stderr,
495 					"%s: %s: line %d: key %s: put error: %s\n",
496 					progname, mapname, lineno,
497 					(char *) db_key.data.data,
498 					errstring(errno));
499 				exitstat = EX_IOERR;
500 			}
501 			else if (st > 0)
502 			{
503 				fprintf(stderr,
504 					"%s: %s: line %d: key %s: duplicate key\n",
505 					progname, mapname,
506 					lineno, (char *) db_key.data.data);
507 				exitstat = EX_DATAERR;
508 			}
509 		}
510 	}
511 
512 	/*
513 	**  Now close the database.
514 	*/
515 
516 	errno = database->smdb_close(database);
517 	if (errno != SMDBE_OK)
518 	{
519 		fprintf(stderr, "%s: close(%s): %s\n",
520 			progname, mapname, errstring(errno));
521 		exitstat = EX_IOERR;
522 	}
523 	smdb_free_database(database);
524 
525 	exit(exitstat);
526 	/* NOTREACHED */
527 	return exitstat;
528 }
529 
530 /*VARARGS1*/
531 void
532 #ifdef __STDC__
533 message(const char *msg, ...)
534 #else /* __STDC__ */
535 message(msg, va_alist)
536 	const char *msg;
537 	va_dcl
538 #endif /* __STDC__ */
539 {
540 	const char *m;
541 	VA_LOCAL_DECL
542 
543 	m = msg;
544 	if (isascii(m[0]) && isdigit(m[0]) &&
545 	    isascii(m[1]) && isdigit(m[1]) &&
546 	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
547 		m += 4;
548 	VA_START(msg);
549 	(void) vfprintf(stderr, m, ap);
550 	VA_END;
551 	(void) fprintf(stderr, "\n");
552 }
553 
554 /*VARARGS1*/
555 void
556 #ifdef __STDC__
557 syserr(const char *msg, ...)
558 #else /* __STDC__ */
559 syserr(msg, va_alist)
560 	const char *msg;
561 	va_dcl
562 #endif /* __STDC__ */
563 {
564 	const char *m;
565 	VA_LOCAL_DECL
566 
567 	m = msg;
568 	if (isascii(m[0]) && isdigit(m[0]) &&
569 	    isascii(m[1]) && isdigit(m[1]) &&
570 	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
571 		m += 4;
572 	VA_START(msg);
573 	(void) vfprintf(stderr, m, ap);
574 	VA_END;
575 	(void) fprintf(stderr, "\n");
576 }
577