editmap.c revision 261363
1/*
2 * Copyright (c) 1998-2002, 2004 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#ifndef lint
16SM_UNUSED(static char copyright[]) =
17"@(#) Copyright (c) 1998-2001 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#endif /* ! lint */
23
24#ifndef lint
25SM_UNUSED(static char id[]) = "@(#)$Id: editmap.c,v 1.26 2013/11/22 20:51:26 ca Exp $";
26#endif /* ! lint */
27
28
29#include <sys/types.h>
30#ifndef ISC_UNIX
31# include <sys/file.h>
32#endif /* ! ISC_UNIX */
33#include <ctype.h>
34#include <stdlib.h>
35#include <unistd.h>
36#ifdef EX_OK
37# undef EX_OK		/* unistd.h may have another use for this */
38#endif /* EX_OK */
39#include <sysexits.h>
40#include <assert.h>
41#include <sendmail/sendmail.h>
42#include <sendmail/pathnames.h>
43#include <libsmdb/smdb.h>
44
45uid_t	RealUid;
46gid_t	RealGid;
47char	*RealUserName;
48uid_t	RunAsUid;
49gid_t	RunAsGid;
50char	*RunAsUserName;
51int	Verbose = 2;
52bool	DontInitGroups = false;
53uid_t	TrustedUid = 0;
54BITMAP256 DontBlameSendmail;
55
56#define BUFSIZE		1024
57#define ISSEP(c) (isascii(c) && isspace(c))
58
59
60static void usage __P((char *));
61
62static void
63usage(progname)
64	char *progname;
65{
66	fprintf(stderr,
67		"Usage: %s [-C cffile] [-N] [-f] [-q|-u|-x] maptype mapname key [ \"value ...\" ]\n",
68		progname);
69	exit(EX_USAGE);
70}
71
72int
73main(argc, argv)
74	int argc;
75	char **argv;
76{
77	char *progname;
78	char *cfile;
79	bool verbose = false;
80	bool query = false;
81	bool update = false;
82	bool remove = false;
83	bool inclnull = false;
84	bool foldcase = true;
85	unsigned int nops = 0;
86	int exitstat;
87	int opt;
88	char *typename = NULL;
89	char *mapname = NULL;
90	char *keyname = NULL;
91	char *value = NULL;
92	int mode;
93	int smode;
94	int putflags = 0;
95	long sff = SFF_ROOTOK|SFF_REGONLY;
96	struct passwd *pw;
97	SMDB_DATABASE *database;
98	SMDB_DBENT db_key, db_val;
99	SMDB_DBPARAMS params;
100	SMDB_USER_INFO user_info;
101#if HASFCHOWN
102	FILE *cfp;
103	char buf[MAXLINE];
104#endif /* HASFCHOWN */
105	static char rnamebuf[MAXNAME];	/* holds RealUserName */
106	extern char *optarg;
107	extern int optind;
108
109	memset(&params, '\0', sizeof params);
110	params.smdbp_cache_size = 1024 * 1024;
111
112	progname = strrchr(argv[0], '/');
113	if (progname != NULL)
114		progname++;
115	else
116		progname = argv[0];
117	cfile = _PATH_SENDMAILCF;
118
119	clrbitmap(DontBlameSendmail);
120	RunAsUid = RealUid = getuid();
121	RunAsGid = RealGid = getgid();
122	pw = getpwuid(RealUid);
123	if (pw != NULL)
124		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
125	else
126		(void) sm_snprintf(rnamebuf, sizeof rnamebuf,
127				   "Unknown UID %d", (int) RealUid);
128	RunAsUserName = RealUserName = rnamebuf;
129	user_info.smdbu_id = RunAsUid;
130	user_info.smdbu_group_id = RunAsGid;
131	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
132			  SMDB_MAX_USER_NAME_LEN);
133
134#define OPTIONS		"C:fquxvN"
135	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
136	{
137		switch (opt)
138		{
139		  case 'C':
140			cfile = optarg;
141			break;
142
143		  case 'f':
144			foldcase = false;
145			break;
146
147		  case 'q':
148			query = true;
149			nops++;
150			break;
151
152		  case 'u':
153			update = true;
154			nops++;
155			break;
156
157		  case 'x':
158			remove = true;
159			nops++;
160			break;
161
162		  case 'v':
163			verbose = true;
164			break;
165
166		  case 'N':
167			inclnull = true;
168			break;
169
170		  default:
171			usage(progname);
172			assert(0);  /* NOTREACHED */
173		}
174	}
175
176	if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
177		sff |= SFF_NOSLINK;
178	if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
179		sff |= SFF_NOHLINK;
180	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
181		sff |= SFF_NOWLINK;
182
183	argc -= optind;
184	argv += optind;
185	if ((nops != 1) ||
186	    (query && argc != 3) ||
187	    (remove && argc != 3) ||
188	    (update && argc <= 3))
189	{
190		usage(progname);
191		assert(0);  /* NOTREACHED */
192	}
193
194	typename = argv[0];
195	mapname = argv[1];
196	keyname = argv[2];
197	if (update)
198		value = argv[3];
199
200	if (foldcase)
201	{
202		char *p;
203
204		for (p = keyname; *p != '\0'; p++)
205		{
206			if (isascii(*p) && isupper(*p))
207				*p = tolower(*p);
208		}
209	}
210
211
212#if HASFCHOWN
213	/* Find TrustedUser value in sendmail.cf */
214	if ((cfp = fopen(cfile, "r")) == NULL)
215	{
216		fprintf(stderr, "%s: %s: %s\n", progname,
217			cfile, sm_errstring(errno));
218		exit(EX_NOINPUT);
219	}
220	while (fgets(buf, sizeof(buf), cfp) != NULL)
221	{
222		register char *b;
223
224		if ((b = strchr(buf, '\n')) != NULL)
225			*b = '\0';
226
227		b = buf;
228		switch (*b++)
229		{
230		  case 'O':		/* option */
231			if (strncasecmp(b, " TrustedUser", 12) == 0 &&
232			    !(isascii(b[12]) && isalnum(b[12])))
233			{
234				b = strchr(b, '=');
235				if (b == NULL)
236					continue;
237				while (isascii(*++b) && isspace(*b))
238					continue;
239				if (isascii(*b) && isdigit(*b))
240					TrustedUid = atoi(b);
241				else
242				{
243					TrustedUid = 0;
244					pw = getpwnam(b);
245					if (pw == NULL)
246						fprintf(stderr,
247							"TrustedUser: unknown user %s\n", b);
248					else
249						TrustedUid = pw->pw_uid;
250				}
251
252# ifdef UID_MAX
253				if (TrustedUid > UID_MAX)
254				{
255					fprintf(stderr,
256						"TrustedUser: uid value (%ld) > UID_MAX (%ld)",
257						(long) TrustedUid,
258						(long) UID_MAX);
259					TrustedUid = 0;
260				}
261# endif /* UID_MAX */
262				break;
263			}
264
265
266		  default:
267			continue;
268		}
269	}
270	(void) fclose(cfp);
271#endif /* HASFCHOWN */
272
273	if (query)
274	{
275		mode = O_RDONLY;
276		smode = S_IRUSR;
277	}
278	else
279	{
280		mode = O_RDWR | O_CREAT;
281		sff |= SFF_CREAT|SFF_NOTEXCL;
282		smode = S_IWUSR;
283	}
284
285	params.smdbp_num_elements = 4096;
286
287	errno = smdb_open_database(&database, mapname, mode, smode, sff,
288				   typename, &user_info, &params);
289	if (errno != SMDBE_OK)
290	{
291		char *hint;
292
293		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
294		    (hint = smdb_db_definition(typename)) != NULL)
295			fprintf(stderr,
296				"%s: Need to recompile with -D%s for %s support\n",
297				progname, hint, typename);
298		else
299			fprintf(stderr,
300				"%s: error opening type %s map %s: %s\n",
301				progname, typename, mapname,
302				sm_errstring(errno));
303		exit(EX_CANTCREAT);
304	}
305
306	(void) database->smdb_sync(database, 0);
307
308	if (geteuid() == 0 && TrustedUid != 0)
309	{
310		errno = database->smdb_set_owner(database, TrustedUid, -1);
311		if (errno != SMDBE_OK)
312		{
313			fprintf(stderr,
314				"WARNING: ownership change on %s failed %s",
315				mapname, sm_errstring(errno));
316		}
317	}
318
319	exitstat = EX_OK;
320	if (query)
321	{
322		memset(&db_key, '\0', sizeof db_key);
323		memset(&db_val, '\0', sizeof db_val);
324
325		db_key.data = keyname;
326		db_key.size = strlen(keyname);
327		if (inclnull)
328			db_key.size++;
329
330		errno = database->smdb_get(database, &db_key, &db_val, 0);
331		if (errno != SMDBE_OK)
332		{
333			/* XXX - Need to distinguish between not found */
334			fprintf(stderr,
335				"%s: couldn't find key %s in map %s\n",
336				progname, keyname, mapname);
337			exitstat = EX_UNAVAILABLE;
338		}
339		else
340		{
341			printf("%.*s\n", (int) db_val.size,
342			       (char *) db_val.data);
343		}
344	}
345	else if (update)
346	{
347		memset(&db_key, '\0', sizeof db_key);
348		memset(&db_val, '\0', sizeof db_val);
349
350		db_key.data = keyname;
351		db_key.size = strlen(keyname);
352		if (inclnull)
353			db_key.size++;
354		db_val.data = value;
355		db_val.size = strlen(value);
356		if (inclnull)
357			db_val.size++;
358
359		errno = database->smdb_put(database, &db_key, &db_val,
360					   putflags);
361		if (errno != SMDBE_OK)
362		{
363			fprintf(stderr,
364				"%s: error updating (%s, %s) in map %s: %s\n",
365				progname, keyname, value, mapname,
366				sm_errstring(errno));
367			exitstat = EX_IOERR;
368		}
369	}
370	else if (remove)
371	{
372		memset(&db_key, '\0', sizeof db_key);
373		memset(&db_val, '\0', sizeof db_val);
374
375		db_key.data = keyname;
376		db_key.size = strlen(keyname);
377		if (inclnull)
378			db_key.size++;
379
380		errno = database->smdb_del(database, &db_key, 0);
381
382		switch (errno)
383		{
384		case SMDBE_NOT_FOUND:
385			fprintf(stderr,
386				"%s: key %s doesn't exist in map %s\n",
387				progname, keyname, mapname);
388			/* Don't set exitstat */
389			break;
390		case SMDBE_OK:
391			/* All's well */
392			break;
393		default:
394			fprintf(stderr,
395				"%s: couldn't remove key %s in map %s (error)\n",
396				progname, keyname, mapname);
397			exitstat = EX_IOERR;
398			break;
399		}
400	}
401	else
402	{
403		assert(0);  /* NOT REACHED */
404	}
405
406	/*
407	**  Now close the database.
408	*/
409
410	errno = database->smdb_close(database);
411	if (errno != SMDBE_OK)
412	{
413		fprintf(stderr, "%s: close(%s): %s\n",
414			progname, mapname, sm_errstring(errno));
415		exitstat = EX_IOERR;
416	}
417	smdb_free_database(database);
418
419	exit(exitstat);
420	/* NOTREACHED */
421	return exitstat;
422}
423