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