makemap.c revision 261363
1/*
2 * Copyright (c) 1998-2002, 2004, 2008 Proofpoint, 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
16SM_IDSTR(copyright,
17"@(#) Copyright (c) 1998-2002, 2004 Proofpoint, 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
23SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013/11/22 20:51:52 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
41uid_t	RealUid;
42gid_t	RealGid;
43char	*RealUserName;
44uid_t	RunAsUid;
45gid_t	RunAsGid;
46char	*RunAsUserName;
47int	Verbose = 2;
48bool	DontInitGroups = false;
49uid_t	TrustedUid = 0;
50BITMAP256 DontBlameSendmail;
51
52#define BUFSIZE		1024
53#define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep)
54
55static void usage __P((char *));
56
57static void
58usage(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
73int
74main(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	if (!unmake && geteuid() == 0)
238	{
239		/* Find TrustedUser value in sendmail.cf */
240		if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile,
241				      SM_IO_RDONLY, NULL)) == NULL)
242		{
243			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
244				      "makemap: %s: %s\n",
245				      cfile, sm_errstring(errno));
246			exit(EX_NOINPUT);
247		}
248		while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
249		{
250			register char *b;
251
252			if ((b = strchr(buf, '\n')) != NULL)
253				*b = '\0';
254
255			b = buf;
256			switch (*b++)
257			{
258			  case 'O':		/* option */
259				if (strncasecmp(b, " TrustedUser", 12) == 0 &&
260				    !(isascii(b[12]) && isalnum(b[12])))
261				{
262					b = strchr(b, '=');
263					if (b == NULL)
264						continue;
265					while (isascii(*++b) && isspace(*b))
266						continue;
267					if (isascii(*b) && isdigit(*b))
268						TrustedUid = atoi(b);
269					else
270					{
271						TrustedUid = 0;
272						pw = getpwnam(b);
273						if (pw == NULL)
274							(void) sm_io_fprintf(smioerr,
275									     SM_TIME_DEFAULT,
276									     "TrustedUser: unknown user %s\n", b);
277						else
278							TrustedUid = pw->pw_uid;
279					}
280
281# ifdef UID_MAX
282					if (TrustedUid > UID_MAX)
283					{
284						(void) sm_io_fprintf(smioerr,
285								     SM_TIME_DEFAULT,
286								     "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
287							(long) TrustedUid,
288							(long) UID_MAX);
289						TrustedUid = 0;
290					}
291# endif /* UID_MAX */
292					break;
293				}
294
295
296			  default:
297				continue;
298			}
299		}
300		(void) sm_io_close(cfp, SM_TIME_DEFAULT);
301	}
302#endif /* HASFCHOWN */
303
304	if (!params.smdbp_allow_dup && !allowreplace)
305		putflags = SMDBF_NO_OVERWRITE;
306
307	if (unmake)
308	{
309		mode = O_RDONLY;
310		smode = S_IRUSR;
311	}
312	else
313	{
314		mode = O_RDWR;
315		if (!notrunc)
316		{
317			mode |= O_CREAT|O_TRUNC;
318			sff |= SFF_CREAT;
319		}
320		smode = S_IWUSR;
321	}
322
323	params.smdbp_num_elements = 4096;
324
325	errno = smdb_open_database(&database, mapname, mode, smode, sff,
326				   typename, &user_info, &params);
327	if (errno != SMDBE_OK)
328	{
329		char *hint;
330
331		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
332		    (hint = smdb_db_definition(typename)) != NULL)
333			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
334					     "%s: Need to recompile with -D%s for %s support\n",
335					     progname, hint, typename);
336		else
337			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
338					     "%s: error opening type %s map %s: %s\n",
339					     progname, typename, mapname,
340					     sm_errstring(errno));
341		exit(EX_CANTCREAT);
342	}
343
344	(void) database->smdb_sync(database, 0);
345
346	if (!unmake && geteuid() == 0 && TrustedUid != 0)
347	{
348		errno = database->smdb_set_owner(database, TrustedUid, -1);
349		if (errno != SMDBE_OK)
350		{
351			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
352					     "WARNING: ownership change on %s failed %s",
353					     mapname, sm_errstring(errno));
354		}
355	}
356
357	/*
358	**  Copy the data
359	*/
360
361	exitstat = EX_OK;
362	if (unmake)
363	{
364		errno = database->smdb_cursor(database, &cursor, 0);
365		if (errno != SMDBE_OK)
366		{
367
368			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
369					     "%s: cannot make cursor for type %s map %s\n",
370					     progname, typename, mapname);
371			exit(EX_SOFTWARE);
372		}
373
374		memset(&db_key, '\0', sizeof db_key);
375		memset(&db_val, '\0', sizeof db_val);
376
377		for (lineno = 0; ; lineno++)
378		{
379			errno = cursor->smdbc_get(cursor, &db_key, &db_val,
380						  SMDB_CURSOR_GET_NEXT);
381			if (errno != SMDBE_OK)
382				break;
383
384			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
385					     "%.*s%c%.*s\n",
386					     (int) db_key.size,
387					     (char *) db_key.data,
388					     (sep != '\0') ? sep : '\t',
389					     (int) db_val.size,
390					     (char *)db_val.data);
391
392		}
393		(void) cursor->smdbc_close(cursor);
394	}
395	else
396	{
397		lineno = 0;
398		while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
399		       >= 0)
400		{
401			register char *p;
402
403			lineno++;
404
405			/*
406			**  Parse the line.
407			*/
408
409			p = strchr(ibuf, '\n');
410			if (p != NULL)
411				*p = '\0';
412			else if (!sm_io_eof(smioin))
413			{
414				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
415						     "%s: %s: line %u: line too long (%ld bytes max)\n",
416						     progname, mapname, lineno,
417						     (long) sizeof ibuf);
418				exitstat = EX_DATAERR;
419				continue;
420			}
421
422			if (ibuf[0] == '\0' || ibuf[0] == comment)
423				continue;
424			if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
425			{
426				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
427						     "%s: %s: line %u: syntax error (leading space)\n",
428						     progname, mapname, lineno);
429				exitstat = EX_DATAERR;
430				continue;
431			}
432
433			memset(&db_key, '\0', sizeof db_key);
434			memset(&db_val, '\0', sizeof db_val);
435			db_key.data = ibuf;
436
437			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
438			{
439				if (foldcase && isascii(*p) && isupper(*p))
440					*p = tolower(*p);
441			}
442			db_key.size = p - ibuf;
443			if (inclnull)
444				db_key.size++;
445
446			if (*p != '\0')
447				*p++ = '\0';
448			while (*p != '\0' && ISSEP(*p))
449				p++;
450			if (!allowempty && *p == '\0')
451			{
452				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
453						     "%s: %s: line %u: no RHS for LHS %s\n",
454						     progname, mapname, lineno,
455						     (char *) db_key.data);
456				exitstat = EX_DATAERR;
457				continue;
458			}
459
460			db_val.data = p;
461			db_val.size = strlen(p);
462			if (inclnull)
463				db_val.size++;
464
465			/*
466			**  Do the database insert.
467			*/
468
469			if (verbose)
470			{
471				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
472						     "key=`%s', val=`%s'\n",
473						     (char *) db_key.data,
474						     (char *) db_val.data);
475			}
476
477			errno = database->smdb_put(database, &db_key, &db_val,
478						   putflags);
479			switch (errno)
480			{
481			  case SMDBE_KEY_EXIST:
482				st = 1;
483				break;
484
485			  case 0:
486				st = 0;
487				break;
488
489			  default:
490				st = -1;
491				break;
492			}
493
494			if (st < 0)
495			{
496				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
497						     "%s: %s: line %u: key %s: put error: %s\n",
498						     progname, mapname, lineno,
499						     (char *) db_key.data,
500						     sm_errstring(errno));
501				exitstat = EX_IOERR;
502			}
503			else if (st > 0)
504			{
505				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
506						     "%s: %s: line %u: key %s: duplicate key\n",
507						     progname, mapname,
508						     lineno,
509						     (char *) db_key.data);
510				exitstat = EX_DATAERR;
511			}
512		}
513	}
514
515	/*
516	**  Now close the database.
517	*/
518
519	errno = database->smdb_close(database);
520	if (errno != SMDBE_OK)
521	{
522		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
523				     "%s: close(%s): %s\n",
524				     progname, mapname, sm_errstring(errno));
525		exitstat = EX_IOERR;
526	}
527	smdb_free_database(database);
528
529	exit(exitstat);
530
531	/* NOTREACHED */
532	return exitstat;
533}
534