xref: /freebsd/contrib/sendmail/makemap/makemap.c (revision e14ddd1f16e7e5788392c50de21ea7c927e0690c)
1 /*
2  * Copyright (c) 1998-2002, 2004, 2008 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-2002, 2004 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 8.181 2013/03/12 15:24:51 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 	/* Find TrustedUser value in sendmail.cf */
238 	if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY,
239 			      NULL)) == NULL)
240 	{
241 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s\n",
242 			      cfile, sm_errstring(errno));
243 		exit(EX_NOINPUT);
244 	}
245 	while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
246 	{
247 		register char *b;
248 
249 		if ((b = strchr(buf, '\n')) != NULL)
250 			*b = '\0';
251 
252 		b = buf;
253 		switch (*b++)
254 		{
255 		  case 'O':		/* option */
256 			if (strncasecmp(b, " TrustedUser", 12) == 0 &&
257 			    !(isascii(b[12]) && isalnum(b[12])))
258 			{
259 				b = strchr(b, '=');
260 				if (b == NULL)
261 					continue;
262 				while (isascii(*++b) && isspace(*b))
263 					continue;
264 				if (isascii(*b) && isdigit(*b))
265 					TrustedUid = atoi(b);
266 				else
267 				{
268 					TrustedUid = 0;
269 					pw = getpwnam(b);
270 					if (pw == NULL)
271 						(void) sm_io_fprintf(smioerr,
272 								     SM_TIME_DEFAULT,
273 								     "TrustedUser: unknown user %s\n", b);
274 					else
275 						TrustedUid = pw->pw_uid;
276 				}
277 
278 # ifdef UID_MAX
279 				if (TrustedUid > UID_MAX)
280 				{
281 					(void) sm_io_fprintf(smioerr,
282 							     SM_TIME_DEFAULT,
283 							     "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
284 						(long) TrustedUid,
285 						(long) UID_MAX);
286 					TrustedUid = 0;
287 				}
288 # endif /* UID_MAX */
289 				break;
290 			}
291 
292 
293 		  default:
294 			continue;
295 		}
296 	}
297 	(void) sm_io_close(cfp, SM_TIME_DEFAULT);
298 #endif /* HASFCHOWN */
299 
300 	if (!params.smdbp_allow_dup && !allowreplace)
301 		putflags = SMDBF_NO_OVERWRITE;
302 
303 	if (unmake)
304 	{
305 		mode = O_RDONLY;
306 		smode = S_IRUSR;
307 	}
308 	else
309 	{
310 		mode = O_RDWR;
311 		if (!notrunc)
312 		{
313 			mode |= O_CREAT|O_TRUNC;
314 			sff |= SFF_CREAT;
315 		}
316 		smode = S_IWUSR;
317 	}
318 
319 	params.smdbp_num_elements = 4096;
320 
321 	errno = smdb_open_database(&database, mapname, mode, smode, sff,
322 				   typename, &user_info, &params);
323 	if (errno != SMDBE_OK)
324 	{
325 		char *hint;
326 
327 		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
328 		    (hint = smdb_db_definition(typename)) != NULL)
329 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
330 					     "%s: Need to recompile with -D%s for %s support\n",
331 					     progname, hint, typename);
332 		else
333 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
334 					     "%s: error opening type %s map %s: %s\n",
335 					     progname, typename, mapname,
336 					     sm_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 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
348 					     "WARNING: ownership change on %s failed %s",
349 					     mapname, sm_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 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
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 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
381 					     "%.*s%c%.*s\n",
382 					     (int) db_key.size,
383 					     (char *) db_key.data,
384 					     (sep != '\0') ? sep : '\t',
385 					     (int) db_val.size,
386 					     (char *)db_val.data);
387 
388 		}
389 		(void) cursor->smdbc_close(cursor);
390 	}
391 	else
392 	{
393 		lineno = 0;
394 		while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
395 		       >= 0)
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 (!sm_io_eof(smioin))
409 			{
410 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
411 						     "%s: %s: line %u: line too long (%ld bytes max)\n",
412 						     progname, mapname, lineno,
413 						     (long) sizeof ibuf);
414 				exitstat = EX_DATAERR;
415 				continue;
416 			}
417 
418 			if (ibuf[0] == '\0' || ibuf[0] == comment)
419 				continue;
420 			if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
421 			{
422 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
423 						     "%s: %s: line %u: 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 (*p != '\0' && ISSEP(*p))
445 				p++;
446 			if (!allowempty && *p == '\0')
447 			{
448 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
449 						     "%s: %s: line %u: 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 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
468 						     "key=`%s', val=`%s'\n",
469 						     (char *) db_key.data,
470 						     (char *) db_val.data);
471 			}
472 
473 			errno = database->smdb_put(database, &db_key, &db_val,
474 						   putflags);
475 			switch (errno)
476 			{
477 			  case SMDBE_KEY_EXIST:
478 				st = 1;
479 				break;
480 
481 			  case 0:
482 				st = 0;
483 				break;
484 
485 			  default:
486 				st = -1;
487 				break;
488 			}
489 
490 			if (st < 0)
491 			{
492 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
493 						     "%s: %s: line %u: key %s: put error: %s\n",
494 						     progname, mapname, lineno,
495 						     (char *) db_key.data,
496 						     sm_errstring(errno));
497 				exitstat = EX_IOERR;
498 			}
499 			else if (st > 0)
500 			{
501 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
502 						     "%s: %s: line %u: key %s: duplicate key\n",
503 						     progname, mapname,
504 						     lineno,
505 						     (char *) db_key.data);
506 				exitstat = EX_DATAERR;
507 			}
508 		}
509 	}
510 
511 	/*
512 	**  Now close the database.
513 	*/
514 
515 	errno = database->smdb_close(database);
516 	if (errno != SMDBE_OK)
517 	{
518 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
519 				     "%s: close(%s): %s\n",
520 				     progname, mapname, sm_errstring(errno));
521 		exitstat = EX_IOERR;
522 	}
523 	smdb_free_database(database);
524 
525 	exit(exitstat);
526 
527 	/* NOTREACHED */
528 	return exitstat;
529 }
530