xref: /freebsd/contrib/sendmail/makemap/makemap.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
1 /*
2  * Copyright (c) 1998-2002, 2004 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.178 2007/05/11 18:45:39 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",
242 			      cfile, sm_errstring(errno));
243 		exit(EX_NOINPUT);
244 	}
245 	while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
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\t%.*s\n",
382 					     (int) db_key.size,
383 					     (char *) db_key.data,
384 					     (int) db_val.size,
385 					     (char *)db_val.data);
386 
387 		}
388 		(void) cursor->smdbc_close(cursor);
389 	}
390 	else
391 	{
392 		lineno = 0;
393 		while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
394 		       != NULL)
395 		{
396 			register char *p;
397 
398 			lineno++;
399 
400 			/*
401 			**  Parse the line.
402 			*/
403 
404 			p = strchr(ibuf, '\n');
405 			if (p != NULL)
406 				*p = '\0';
407 			else if (!sm_io_eof(smioin))
408 			{
409 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
410 						     "%s: %s: line %u: line too long (%ld bytes max)\n",
411 						     progname, mapname, lineno,
412 						     (long) sizeof ibuf);
413 				exitstat = EX_DATAERR;
414 				continue;
415 			}
416 
417 			if (ibuf[0] == '\0' || ibuf[0] == comment)
418 				continue;
419 			if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
420 			{
421 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
422 						     "%s: %s: line %u: syntax error (leading space)\n",
423 						     progname, mapname, lineno);
424 				exitstat = EX_DATAERR;
425 				continue;
426 			}
427 
428 			memset(&db_key, '\0', sizeof db_key);
429 			memset(&db_val, '\0', sizeof db_val);
430 			db_key.data = ibuf;
431 
432 			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
433 			{
434 				if (foldcase && isascii(*p) && isupper(*p))
435 					*p = tolower(*p);
436 			}
437 			db_key.size = p - ibuf;
438 			if (inclnull)
439 				db_key.size++;
440 
441 			if (*p != '\0')
442 				*p++ = '\0';
443 			while (*p != '\0' && ISSEP(*p))
444 				p++;
445 			if (!allowempty && *p == '\0')
446 			{
447 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
448 						     "%s: %s: line %u: no RHS for LHS %s\n",
449 						     progname, mapname, lineno,
450 						     (char *) db_key.data);
451 				exitstat = EX_DATAERR;
452 				continue;
453 			}
454 
455 			db_val.data = p;
456 			db_val.size = strlen(p);
457 			if (inclnull)
458 				db_val.size++;
459 
460 			/*
461 			**  Do the database insert.
462 			*/
463 
464 			if (verbose)
465 			{
466 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
467 						     "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 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
492 						     "%s: %s: line %u: key %s: put error: %s\n",
493 						     progname, mapname, lineno,
494 						     (char *) db_key.data,
495 						     sm_errstring(errno));
496 				exitstat = EX_IOERR;
497 			}
498 			else if (st > 0)
499 			{
500 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
501 						     "%s: %s: line %u: key %s: duplicate key\n",
502 						     progname, mapname,
503 						     lineno,
504 						     (char *) db_key.data);
505 				exitstat = EX_DATAERR;
506 			}
507 		}
508 	}
509 
510 	/*
511 	**  Now close the database.
512 	*/
513 
514 	errno = database->smdb_close(database);
515 	if (errno != SMDBE_OK)
516 	{
517 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
518 				     "%s: close(%s): %s\n",
519 				     progname, mapname, sm_errstring(errno));
520 		exitstat = EX_IOERR;
521 	}
522 	smdb_free_database(database);
523 
524 	exit(exitstat);
525 
526 	/* NOTREACHED */
527 	return exitstat;
528 }
529