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