xref: /freebsd/contrib/sendmail/makemap/makemap.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
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.13 2000/10/05 23:00:50 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 		errno = database->smdb_cursor(database, &cursor, 0);
361 		if (errno != SMDBE_OK)
362 		{
363 
364 			fprintf(stderr,
365 				"%s: cannot make cursor for type %s map %s\n",
366 				progname, typename, mapname);
367 			exit(EX_SOFTWARE);
368 		}
369 
370 		memset(&db_key, '\0', sizeof db_key);
371 		memset(&db_val, '\0', sizeof db_val);
372 
373 		for (lineno = 0; ; lineno++)
374 		{
375 			errno = cursor->smdbc_get(cursor, &db_key, &db_val,
376 						  SMDB_CURSOR_GET_NEXT);
377 			if (errno != SMDBE_OK)
378 				break;
379 
380 			printf("%.*s\t%.*s\n",
381 			       (int) db_key.size,
382 			       (char *) db_key.data,
383 			       (int) db_val.size,
384 			       (char *)db_val.data);
385 
386 		}
387 		(void) cursor->smdbc_close(cursor);
388 	}
389 	else
390 	{
391 		lineno = 0;
392 		while (fgets(ibuf, sizeof ibuf, stdin) != NULL)
393 		{
394 			register char *p;
395 
396 			lineno++;
397 
398 			/*
399 			**  Parse the line.
400 			*/
401 
402 			p = strchr(ibuf, '\n');
403 			if (p != NULL)
404 				*p = '\0';
405 			else if (!feof(stdin))
406 			{
407 				fprintf(stderr,
408 					"%s: %s: line %d: line too long (%ld bytes max)\n",
409 					progname, mapname, lineno, (long) sizeof ibuf);
410 				exitstat = EX_DATAERR;
411 				continue;
412 			}
413 
414 			if (ibuf[0] == '\0' || ibuf[0] == '#')
415 				continue;
416 			if (
417 #if _FFR_DELIM
418 			    sep == '\0' &&
419 #endif /* _FFR_DELIM */
420 			    isascii(ibuf[0]) && isspace(ibuf[0]))
421 			{
422 				fprintf(stderr,
423 					"%s: %s: line %d: syntax error (leading space)\n",
424 					progname, mapname, lineno);
425 				exitstat = EX_DATAERR;
426 				continue;
427 			}
428 
429 			memset(&db_key, '\0', sizeof db_key);
430 			memset(&db_val, '\0', sizeof db_val);
431 			db_key.data = ibuf;
432 
433 			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
434 			{
435 				if (foldcase && isascii(*p) && isupper(*p))
436 					*p = tolower(*p);
437 			}
438 			db_key.size = p - ibuf;
439 			if (inclnull)
440 				db_key.size++;
441 
442 			if (*p != '\0')
443 				*p++ = '\0';
444 			while (ISSEP(*p))
445 				p++;
446 			if (!allowempty && *p == '\0')
447 			{
448 				fprintf(stderr,
449 					"%s: %s: line %d: no RHS for LHS %s\n",
450 					progname, mapname, lineno,
451 					(char *) db_key.data);
452 				exitstat = EX_DATAERR;
453 				continue;
454 			}
455 
456 			db_val.data = p;
457 			db_val.size = strlen(p);
458 			if (inclnull)
459 				db_val.size++;
460 
461 			/*
462 			**  Do the database insert.
463 			*/
464 
465 			if (verbose)
466 			{
467 				printf("key=`%s', val=`%s'\n",
468 				       (char *) db_key.data,
469 				       (char *) db_val.data);
470 			}
471 
472 			errno = database->smdb_put(database, &db_key, &db_val,
473 						   putflags);
474 			switch (errno)
475 			{
476 			  case SMDBE_KEY_EXIST:
477 				st = 1;
478 				break;
479 
480 			  case 0:
481 				st = 0;
482 				break;
483 
484 			  default:
485 				st = -1;
486 				break;
487 			}
488 
489 			if (st < 0)
490 			{
491 				fprintf(stderr,
492 					"%s: %s: line %d: key %s: put error: %s\n",
493 					progname, mapname, lineno,
494 					(char *) db_key.data,
495 					errstring(errno));
496 				exitstat = EX_IOERR;
497 			}
498 			else if (st > 0)
499 			{
500 				fprintf(stderr,
501 					"%s: %s: line %d: key %s: duplicate key\n",
502 					progname, mapname,
503 					lineno, (char *) db_key.data);
504 				exitstat = EX_DATAERR;
505 			}
506 		}
507 	}
508 
509 	/*
510 	**  Now close the database.
511 	*/
512 
513 	errno = database->smdb_close(database);
514 	if (errno != SMDBE_OK)
515 	{
516 		fprintf(stderr, "%s: close(%s): %s\n",
517 			progname, mapname, errstring(errno));
518 		exitstat = EX_IOERR;
519 	}
520 	smdb_free_database(database);
521 
522 	exit(exitstat);
523 	/* NOTREACHED */
524 	return exitstat;
525 }
526 
527 /*VARARGS1*/
528 void
529 #ifdef __STDC__
530 message(const char *msg, ...)
531 #else /* __STDC__ */
532 message(msg, va_alist)
533 	const char *msg;
534 	va_dcl
535 #endif /* __STDC__ */
536 {
537 	const char *m;
538 	VA_LOCAL_DECL
539 
540 	m = msg;
541 	if (isascii(m[0]) && isdigit(m[0]) &&
542 	    isascii(m[1]) && isdigit(m[1]) &&
543 	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
544 		m += 4;
545 	VA_START(msg);
546 	(void) vfprintf(stderr, m, ap);
547 	VA_END;
548 	(void) fprintf(stderr, "\n");
549 }
550 
551 /*VARARGS1*/
552 void
553 #ifdef __STDC__
554 syserr(const char *msg, ...)
555 #else /* __STDC__ */
556 syserr(msg, va_alist)
557 	const char *msg;
558 	va_dcl
559 #endif /* __STDC__ */
560 {
561 	const char *m;
562 	VA_LOCAL_DECL
563 
564 	m = msg;
565 	if (isascii(m[0]) && isdigit(m[0]) &&
566 	    isascii(m[1]) && isdigit(m[1]) &&
567 	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
568 		m += 4;
569 	VA_START(msg);
570 	(void) vfprintf(stderr, m, ap);
571 	VA_END;
572 	(void) fprintf(stderr, "\n");
573 }
574