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