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