xref: /titanic_44/usr/src/cmd/sendmail/aux/makemap.c (revision ae115bc77f6fcde83175c75b4206dc2e50747966)
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 #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.177 2004/08/03 23:57:24 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 uid_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\t%.*s\n",
384 					     (int) db_key.size,
385 					     (char *) db_key.data,
386 					     (int) db_val.size,
387 					     (char *)db_val.data);
388 
389 		}
390 		(void) cursor->smdbc_close(cursor);
391 	}
392 	else
393 	{
394 		lineno = 0;
395 		while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
396 		       != NULL)
397 		{
398 			register char *p;
399 
400 			lineno++;
401 
402 			/*
403 			**  Parse the line.
404 			*/
405 
406 			p = strchr(ibuf, '\n');
407 			if (p != NULL)
408 				*p = '\0';
409 			else if (!sm_io_eof(smioin))
410 			{
411 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
412 						     "%s: %s: line %u: line too long (%ld bytes max)\n",
413 						     progname, mapname, lineno,
414 						     (long) sizeof ibuf);
415 				exitstat = EX_DATAERR;
416 				continue;
417 			}
418 
419 			if (ibuf[0] == '\0' || ibuf[0] == comment)
420 				continue;
421 			if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
422 			{
423 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
424 						     "%s: %s: line %u: syntax error (leading space)\n",
425 						     progname, mapname, lineno);
426 				exitstat = EX_DATAERR;
427 				continue;
428 			}
429 
430 			memset(&db_key, '\0', sizeof db_key);
431 			memset(&db_val, '\0', sizeof db_val);
432 			db_key.data = ibuf;
433 
434 			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
435 			{
436 				if (foldcase && isascii(*p) && isupper(*p))
437 					*p = tolower(*p);
438 			}
439 			db_key.size = p - ibuf;
440 			if (inclnull)
441 				db_key.size++;
442 
443 			if (*p != '\0')
444 				*p++ = '\0';
445 			while (*p != '\0' && ISSEP(*p))
446 				p++;
447 			if (!allowempty && *p == '\0')
448 			{
449 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
450 						     "%s: %s: line %u: no RHS for LHS %s\n",
451 						     progname, mapname, lineno,
452 						     (char *) db_key.data);
453 				exitstat = EX_DATAERR;
454 				continue;
455 			}
456 
457 			db_val.data = p;
458 			db_val.size = strlen(p);
459 			if (inclnull)
460 				db_val.size++;
461 
462 			/*
463 			**  Do the database insert.
464 			*/
465 
466 			if (verbose)
467 			{
468 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
469 						     "key=`%s', val=`%s'\n",
470 						     (char *) db_key.data,
471 						     (char *) db_val.data);
472 			}
473 
474 			errno = database->smdb_put(database, &db_key, &db_val,
475 						   putflags);
476 			switch (errno)
477 			{
478 			  case SMDBE_KEY_EXIST:
479 				st = 1;
480 				break;
481 
482 			  case 0:
483 				st = 0;
484 				break;
485 
486 			  default:
487 				st = -1;
488 				break;
489 			}
490 
491 			if (st < 0)
492 			{
493 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
494 						     "%s: %s: line %u: key %s: put error: %s\n",
495 						     progname, mapname, lineno,
496 						     (char *) db_key.data,
497 						     sm_errstring(errno));
498 				exitstat = EX_IOERR;
499 			}
500 			else if (st > 0)
501 			{
502 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
503 						     "%s: %s: line %u: key %s: duplicate key\n",
504 						     progname, mapname,
505 						     lineno,
506 						     (char *) db_key.data);
507 				exitstat = EX_DATAERR;
508 			}
509 		}
510 	}
511 
512 	/*
513 	**  Now close the database.
514 	*/
515 
516 	errno = database->smdb_close(database);
517 	if (errno != SMDBE_OK)
518 	{
519 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
520 				     "%s: close(%s): %s\n",
521 				     progname, mapname, sm_errstring(errno));
522 		exitstat = EX_IOERR;
523 	}
524 	smdb_free_database(database);
525 
526 	exit(exitstat);
527 
528 	/* NOTREACHED */
529 	return exitstat;
530 }
531