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