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