xref: /illumos-gate/usr/src/cmd/sendmail/util/makemap.c (revision 63f91fbc3c024870d86dc3332a4a0080fb29bc40)
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.179 2008/04/14 02:06:16 ca Exp $")
24 
25 #include <sys/types.h>
26 #ifndef ISC_UNIX
27 # include <sys/file.h>
28 #endif /* ! ISC_UNIX */
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #ifdef EX_OK
33 # undef EX_OK		/* unistd.h may have another use for this */
34 #endif /* EX_OK */
35 #include <sysexits.h>
36 #include <sendmail/sendmail.h>
37 #include <sendmail/pathnames.h>
38 #include <libsmdb/smdb.h>
39 
40 uid_t	RealUid;
41 gid_t	RealGid;
42 char	*RealUserName;
43 uid_t	RunAsUid;
44 gid_t	RunAsGid;
45 char	*RunAsUserName;
46 int	Verbose = 2;
47 bool	DontInitGroups = false;
48 uid_t	TrustedUid = 0;
49 BITMAP256 DontBlameSendmail;
50 
51 #define BUFSIZE		1024
52 #define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep)
53 
54 static void usage __P((char *));
55 
56 static void
57 usage(progname)
58 	char *progname;
59 {
60 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
61 		      "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
62 		      progname);
63 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
64 		      "       %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n",
65 		      (int) strlen(progname), "");
66 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
67 		      "       %*s [-u] [-v] type mapname\n",
68 		      (int) strlen(progname), "");
69 	exit(EX_USAGE);
70 }
71 
72 int
73 main(argc, argv)
74 	int argc;
75 	char **argv;
76 {
77 	char *progname;
78 	char *cfile;
79 	bool inclnull = false;
80 	bool notrunc = false;
81 	bool allowreplace = false;
82 	bool allowempty = false;
83 	bool verbose = false;
84 	bool foldcase = true;
85 	bool unmake = false;
86 	char sep = '\0';
87 	char comment = '#';
88 	int exitstat;
89 	int opt;
90 	char *typename = NULL;
91 	char *mapname = NULL;
92 	unsigned int lineno;
93 	int st;
94 	int mode;
95 	int smode;
96 	int putflags = 0;
97 	long sff = SFF_ROOTOK|SFF_REGONLY;
98 	struct passwd *pw;
99 	SMDB_DATABASE *database;
100 	SMDB_CURSOR *cursor;
101 	SMDB_DBENT db_key, db_val;
102 	SMDB_DBPARAMS params;
103 	SMDB_USER_INFO user_info;
104 	char ibuf[BUFSIZE];
105 #if HASFCHOWN
106 	SM_FILE_T *cfp;
107 	char buf[MAXLINE];
108 #endif /* HASFCHOWN */
109 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
110 	extern char *optarg;
111 	extern int optind;
112 
113 	memset(&params, '\0', sizeof params);
114 	params.smdbp_cache_size = 1024 * 1024;
115 
116 	progname = strrchr(argv[0], '/');
117 	if (progname != NULL)
118 		progname++;
119 	else
120 		progname = argv[0];
121 	cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
122 
123 	clrbitmap(DontBlameSendmail);
124 	RunAsUid = RealUid = getuid();
125 	RunAsGid = RealGid = getgid();
126 	pw = getpwuid(RealUid);
127 	if (pw != NULL)
128 		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
129 	else
130 		(void) sm_snprintf(rnamebuf, sizeof rnamebuf,
131 		    "Unknown UID %d", (int) RealUid);
132 	RunAsUserName = RealUserName = rnamebuf;
133 	user_info.smdbu_id = RunAsUid;
134 	user_info.smdbu_group_id = RunAsGid;
135 	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
136 		       SMDB_MAX_USER_NAME_LEN);
137 
138 #define OPTIONS		"C:D:Nc:deflorst:uv"
139 	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
140 	{
141 		switch (opt)
142 		{
143 		  case 'C':
144 			cfile = optarg;
145 			break;
146 
147 		  case 'N':
148 			inclnull = true;
149 			break;
150 
151 		  case 'c':
152 			params.smdbp_cache_size = atol(optarg);
153 			break;
154 
155 		  case 'd':
156 			params.smdbp_allow_dup = true;
157 			break;
158 
159 		  case 'e':
160 			allowempty = true;
161 			break;
162 
163 		  case 'f':
164 			foldcase = false;
165 			break;
166 
167 		  case 'D':
168 			comment = *optarg;
169 			break;
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 			/* FALLTHROUGH */
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%c%.*s\n",
381 					     (int) db_key.size,
382 					     (char *) db_key.data,
383 					     (sep != '\0') ? sep : '\t',
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