190792Sgshapiro/*
2141858Sgshapiro * Copyright (c) 1998-2002, 2004 Sendmail, Inc. and its suppliers.
390792Sgshapiro *	All rights reserved.
490792Sgshapiro * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
590792Sgshapiro * Copyright (c) 1992, 1993
690792Sgshapiro *	The Regents of the University of California.  All rights reserved.
790792Sgshapiro *
890792Sgshapiro * By using this file, you agree to the terms and conditions set
990792Sgshapiro * forth in the LICENSE file which can be found at the top level of
1090792Sgshapiro * the sendmail distribution.
1190792Sgshapiro *
1290792Sgshapiro */
1390792Sgshapiro
1490792Sgshapiro#include <sm/gen.h>
1590792Sgshapiro#ifndef lint
1690792SgshapiroSM_UNUSED(static char copyright[]) =
1790792Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
1890792Sgshapiro	All rights reserved.\n\
1990792Sgshapiro     Copyright (c) 1992 Eric P. Allman.  All rights reserved.\n\
2090792Sgshapiro     Copyright (c) 1992, 1993\n\
2190792Sgshapiro	The Regents of the University of California.  All rights reserved.\n";
2290792Sgshapiro#endif /* ! lint */
2390792Sgshapiro
2490792Sgshapiro#ifndef lint
25173340SgshapiroSM_UNUSED(static char id[]) = "@(#)$Id: editmap.c,v 1.25 2007/05/11 18:50:35 ca Exp $";
2690792Sgshapiro#endif /* ! lint */
2790792Sgshapiro
2890792Sgshapiro
2990792Sgshapiro#include <sys/types.h>
3090792Sgshapiro#ifndef ISC_UNIX
3190792Sgshapiro# include <sys/file.h>
3290792Sgshapiro#endif /* ! ISC_UNIX */
3390792Sgshapiro#include <ctype.h>
3490792Sgshapiro#include <stdlib.h>
3590792Sgshapiro#include <unistd.h>
3690792Sgshapiro#ifdef EX_OK
3790792Sgshapiro# undef EX_OK		/* unistd.h may have another use for this */
3890792Sgshapiro#endif /* EX_OK */
3990792Sgshapiro#include <sysexits.h>
4090792Sgshapiro#include <assert.h>
4190792Sgshapiro#include <sendmail/sendmail.h>
4290792Sgshapiro#include <sendmail/pathnames.h>
4390792Sgshapiro#include <libsmdb/smdb.h>
4490792Sgshapiro
4590792Sgshapirouid_t	RealUid;
4690792Sgshapirogid_t	RealGid;
4790792Sgshapirochar	*RealUserName;
4890792Sgshapirouid_t	RunAsUid;
49173340Sgshapirogid_t	RunAsGid;
5090792Sgshapirochar	*RunAsUserName;
5190792Sgshapiroint	Verbose = 2;
5290792Sgshapirobool	DontInitGroups = false;
5390792Sgshapirouid_t	TrustedUid = 0;
5490792SgshapiroBITMAP256 DontBlameSendmail;
5590792Sgshapiro
5690792Sgshapiro#define BUFSIZE		1024
5790792Sgshapiro#define ISSEP(c) (isascii(c) && isspace(c))
5890792Sgshapiro
5990792Sgshapiro
60141858Sgshapirostatic void usage __P((char *));
61141858Sgshapiro
6290792Sgshapirostatic void
6390792Sgshapirousage(progname)
6490792Sgshapiro	char *progname;
6590792Sgshapiro{
6690792Sgshapiro	fprintf(stderr,
6790792Sgshapiro		"Usage: %s [-C cffile] [-N] [-f] [-q|-u|-x] maptype mapname key [ \"value ...\" ]\n",
6890792Sgshapiro		progname);
6990792Sgshapiro	exit(EX_USAGE);
7090792Sgshapiro}
7190792Sgshapiro
7290792Sgshapiroint
7390792Sgshapiromain(argc, argv)
7490792Sgshapiro	int argc;
7590792Sgshapiro	char **argv;
7690792Sgshapiro{
7790792Sgshapiro	char *progname;
7890792Sgshapiro	char *cfile;
7990792Sgshapiro	bool verbose = false;
8090792Sgshapiro	bool query = false;
8190792Sgshapiro	bool update = false;
8290792Sgshapiro	bool remove = false;
8390792Sgshapiro	bool inclnull = false;
8490792Sgshapiro	bool foldcase = true;
8590792Sgshapiro	unsigned int nops = 0;
8690792Sgshapiro	int exitstat;
8790792Sgshapiro	int opt;
8890792Sgshapiro	char *typename = NULL;
8990792Sgshapiro	char *mapname = NULL;
9090792Sgshapiro	char *keyname = NULL;
9190792Sgshapiro	char *value = NULL;
9290792Sgshapiro	int mode;
9390792Sgshapiro	int smode;
9490792Sgshapiro	int putflags = 0;
9590792Sgshapiro	long sff = SFF_ROOTOK|SFF_REGONLY;
9690792Sgshapiro	struct passwd *pw;
9790792Sgshapiro	SMDB_DATABASE *database;
9890792Sgshapiro	SMDB_DBENT db_key, db_val;
9990792Sgshapiro	SMDB_DBPARAMS params;
10090792Sgshapiro	SMDB_USER_INFO user_info;
10190792Sgshapiro#if HASFCHOWN
10290792Sgshapiro	FILE *cfp;
10390792Sgshapiro	char buf[MAXLINE];
10490792Sgshapiro#endif /* HASFCHOWN */
10590792Sgshapiro	static char rnamebuf[MAXNAME];	/* holds RealUserName */
10690792Sgshapiro	extern char *optarg;
10790792Sgshapiro	extern int optind;
10890792Sgshapiro
10990792Sgshapiro	memset(&params, '\0', sizeof params);
11090792Sgshapiro	params.smdbp_cache_size = 1024 * 1024;
11190792Sgshapiro
11290792Sgshapiro	progname = strrchr(argv[0], '/');
11390792Sgshapiro	if (progname != NULL)
11490792Sgshapiro		progname++;
11590792Sgshapiro	else
11690792Sgshapiro		progname = argv[0];
11790792Sgshapiro	cfile = _PATH_SENDMAILCF;
11890792Sgshapiro
11990792Sgshapiro	clrbitmap(DontBlameSendmail);
12090792Sgshapiro	RunAsUid = RealUid = getuid();
12190792Sgshapiro	RunAsGid = RealGid = getgid();
12290792Sgshapiro	pw = getpwuid(RealUid);
12390792Sgshapiro	if (pw != NULL)
12490792Sgshapiro		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
12590792Sgshapiro	else
12690792Sgshapiro		(void) sm_snprintf(rnamebuf, sizeof rnamebuf,
12790792Sgshapiro				   "Unknown UID %d", (int) RealUid);
12890792Sgshapiro	RunAsUserName = RealUserName = rnamebuf;
12990792Sgshapiro	user_info.smdbu_id = RunAsUid;
13090792Sgshapiro	user_info.smdbu_group_id = RunAsGid;
13190792Sgshapiro	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
13290792Sgshapiro			  SMDB_MAX_USER_NAME_LEN);
13390792Sgshapiro
13490792Sgshapiro#define OPTIONS		"C:fquxvN"
13590792Sgshapiro	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
13690792Sgshapiro	{
13790792Sgshapiro		switch (opt)
13890792Sgshapiro		{
13990792Sgshapiro		  case 'C':
14090792Sgshapiro			cfile = optarg;
14190792Sgshapiro			break;
14290792Sgshapiro
14390792Sgshapiro		  case 'f':
14490792Sgshapiro			foldcase = false;
14590792Sgshapiro			break;
14690792Sgshapiro
14790792Sgshapiro		  case 'q':
14890792Sgshapiro			query = true;
14990792Sgshapiro			nops++;
15090792Sgshapiro			break;
15190792Sgshapiro
15290792Sgshapiro		  case 'u':
15390792Sgshapiro			update = true;
15490792Sgshapiro			nops++;
15590792Sgshapiro			break;
15690792Sgshapiro
15790792Sgshapiro		  case 'x':
15890792Sgshapiro			remove = true;
15990792Sgshapiro			nops++;
16090792Sgshapiro			break;
16190792Sgshapiro
16290792Sgshapiro		  case 'v':
16390792Sgshapiro			verbose = true;
16490792Sgshapiro			break;
16590792Sgshapiro
16690792Sgshapiro		  case 'N':
16790792Sgshapiro			inclnull = true;
16890792Sgshapiro			break;
16990792Sgshapiro
17090792Sgshapiro		  default:
17190792Sgshapiro			usage(progname);
17290792Sgshapiro			assert(0);  /* NOTREACHED */
17390792Sgshapiro		}
17490792Sgshapiro	}
17590792Sgshapiro
17690792Sgshapiro	if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
17790792Sgshapiro		sff |= SFF_NOSLINK;
17890792Sgshapiro	if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
17990792Sgshapiro		sff |= SFF_NOHLINK;
18090792Sgshapiro	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
18190792Sgshapiro		sff |= SFF_NOWLINK;
18290792Sgshapiro
18390792Sgshapiro	argc -= optind;
18490792Sgshapiro	argv += optind;
18590792Sgshapiro	if ((nops != 1) ||
18690792Sgshapiro	    (query && argc != 3) ||
18790792Sgshapiro	    (remove && argc != 3) ||
18890792Sgshapiro	    (update && argc <= 3))
18990792Sgshapiro	{
19090792Sgshapiro		usage(progname);
19190792Sgshapiro		assert(0);  /* NOTREACHED */
19290792Sgshapiro	}
19390792Sgshapiro
19490792Sgshapiro	typename = argv[0];
19590792Sgshapiro	mapname = argv[1];
19690792Sgshapiro	keyname = argv[2];
19790792Sgshapiro	if (update)
19890792Sgshapiro		value = argv[3];
19990792Sgshapiro
20090792Sgshapiro	if (foldcase)
20190792Sgshapiro	{
20290792Sgshapiro		char *p;
20390792Sgshapiro
20490792Sgshapiro		for (p = keyname; *p != '\0'; p++)
20590792Sgshapiro		{
20690792Sgshapiro			if (isascii(*p) && isupper(*p))
20790792Sgshapiro				*p = tolower(*p);
20890792Sgshapiro		}
20990792Sgshapiro	}
21090792Sgshapiro
21190792Sgshapiro
21290792Sgshapiro#if HASFCHOWN
21390792Sgshapiro	/* Find TrustedUser value in sendmail.cf */
21490792Sgshapiro	if ((cfp = fopen(cfile, "r")) == NULL)
21590792Sgshapiro	{
21690792Sgshapiro		fprintf(stderr, "%s: %s: %s\n", progname,
21790792Sgshapiro			cfile, sm_errstring(errno));
21890792Sgshapiro		exit(EX_NOINPUT);
21990792Sgshapiro	}
22090792Sgshapiro	while (fgets(buf, sizeof(buf), cfp) != NULL)
22190792Sgshapiro	{
22290792Sgshapiro		register char *b;
22390792Sgshapiro
22490792Sgshapiro		if ((b = strchr(buf, '\n')) != NULL)
22590792Sgshapiro			*b = '\0';
22690792Sgshapiro
22790792Sgshapiro		b = buf;
22890792Sgshapiro		switch (*b++)
22990792Sgshapiro		{
23090792Sgshapiro		  case 'O':		/* option */
23190792Sgshapiro			if (strncasecmp(b, " TrustedUser", 12) == 0 &&
23290792Sgshapiro			    !(isascii(b[12]) && isalnum(b[12])))
23390792Sgshapiro			{
23490792Sgshapiro				b = strchr(b, '=');
23590792Sgshapiro				if (b == NULL)
23690792Sgshapiro					continue;
23790792Sgshapiro				while (isascii(*++b) && isspace(*b))
23890792Sgshapiro					continue;
23990792Sgshapiro				if (isascii(*b) && isdigit(*b))
24090792Sgshapiro					TrustedUid = atoi(b);
24190792Sgshapiro				else
24290792Sgshapiro				{
24390792Sgshapiro					TrustedUid = 0;
24490792Sgshapiro					pw = getpwnam(b);
24590792Sgshapiro					if (pw == NULL)
24690792Sgshapiro						fprintf(stderr,
24790792Sgshapiro							"TrustedUser: unknown user %s\n", b);
24890792Sgshapiro					else
24990792Sgshapiro						TrustedUid = pw->pw_uid;
25090792Sgshapiro				}
25190792Sgshapiro
25290792Sgshapiro# ifdef UID_MAX
25390792Sgshapiro				if (TrustedUid > UID_MAX)
25490792Sgshapiro				{
25590792Sgshapiro					fprintf(stderr,
25690792Sgshapiro						"TrustedUser: uid value (%ld) > UID_MAX (%ld)",
25790792Sgshapiro						(long) TrustedUid,
25890792Sgshapiro						(long) UID_MAX);
25990792Sgshapiro					TrustedUid = 0;
26090792Sgshapiro				}
26190792Sgshapiro# endif /* UID_MAX */
26290792Sgshapiro				break;
26390792Sgshapiro			}
26490792Sgshapiro
26590792Sgshapiro
26690792Sgshapiro		  default:
26790792Sgshapiro			continue;
26890792Sgshapiro		}
26990792Sgshapiro	}
27090792Sgshapiro	(void) fclose(cfp);
27190792Sgshapiro#endif /* HASFCHOWN */
27290792Sgshapiro
27390792Sgshapiro	if (query)
27490792Sgshapiro	{
27590792Sgshapiro		mode = O_RDONLY;
27690792Sgshapiro		smode = S_IRUSR;
27790792Sgshapiro	}
27890792Sgshapiro	else
27990792Sgshapiro	{
28090792Sgshapiro		mode = O_RDWR | O_CREAT;
28190792Sgshapiro		sff |= SFF_CREAT|SFF_NOTEXCL;
28290792Sgshapiro		smode = S_IWUSR;
28390792Sgshapiro	}
28490792Sgshapiro
28590792Sgshapiro	params.smdbp_num_elements = 4096;
28690792Sgshapiro
28790792Sgshapiro	errno = smdb_open_database(&database, mapname, mode, smode, sff,
28890792Sgshapiro				   typename, &user_info, &params);
28990792Sgshapiro	if (errno != SMDBE_OK)
29090792Sgshapiro	{
29190792Sgshapiro		char *hint;
29290792Sgshapiro
29390792Sgshapiro		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
29490792Sgshapiro		    (hint = smdb_db_definition(typename)) != NULL)
29590792Sgshapiro			fprintf(stderr,
29690792Sgshapiro				"%s: Need to recompile with -D%s for %s support\n",
29790792Sgshapiro				progname, hint, typename);
29890792Sgshapiro		else
29990792Sgshapiro			fprintf(stderr,
30090792Sgshapiro				"%s: error opening type %s map %s: %s\n",
30190792Sgshapiro				progname, typename, mapname,
30290792Sgshapiro				sm_errstring(errno));
30390792Sgshapiro		exit(EX_CANTCREAT);
30490792Sgshapiro	}
30590792Sgshapiro
30690792Sgshapiro	(void) database->smdb_sync(database, 0);
30790792Sgshapiro
30890792Sgshapiro	if (geteuid() == 0 && TrustedUid != 0)
30990792Sgshapiro	{
31090792Sgshapiro		errno = database->smdb_set_owner(database, TrustedUid, -1);
31190792Sgshapiro		if (errno != SMDBE_OK)
31290792Sgshapiro		{
31390792Sgshapiro			fprintf(stderr,
31490792Sgshapiro				"WARNING: ownership change on %s failed %s",
31590792Sgshapiro				mapname, sm_errstring(errno));
31690792Sgshapiro		}
31790792Sgshapiro	}
31890792Sgshapiro
31990792Sgshapiro	exitstat = EX_OK;
32090792Sgshapiro	if (query)
32190792Sgshapiro	{
32290792Sgshapiro		memset(&db_key, '\0', sizeof db_key);
32390792Sgshapiro		memset(&db_val, '\0', sizeof db_val);
32490792Sgshapiro
32590792Sgshapiro		db_key.data = keyname;
32690792Sgshapiro		db_key.size = strlen(keyname);
32790792Sgshapiro		if (inclnull)
32890792Sgshapiro			db_key.size++;
32990792Sgshapiro
33090792Sgshapiro		errno = database->smdb_get(database, &db_key, &db_val, 0);
33190792Sgshapiro		if (errno != SMDBE_OK)
33290792Sgshapiro		{
33390792Sgshapiro			/* XXX - Need to distinguish between not found */
33490792Sgshapiro			fprintf(stderr,
33590792Sgshapiro				"%s: couldn't find key %s in map %s\n",
33690792Sgshapiro				progname, keyname, mapname);
33790792Sgshapiro			exitstat = EX_UNAVAILABLE;
33890792Sgshapiro		}
33990792Sgshapiro		else
34090792Sgshapiro		{
34190792Sgshapiro			printf("%.*s\n", (int) db_val.size,
34290792Sgshapiro			       (char *) db_val.data);
34390792Sgshapiro		}
34490792Sgshapiro	}
34590792Sgshapiro	else if (update)
34690792Sgshapiro	{
34790792Sgshapiro		memset(&db_key, '\0', sizeof db_key);
34890792Sgshapiro		memset(&db_val, '\0', sizeof db_val);
34990792Sgshapiro
35090792Sgshapiro		db_key.data = keyname;
35190792Sgshapiro		db_key.size = strlen(keyname);
35290792Sgshapiro		if (inclnull)
35390792Sgshapiro			db_key.size++;
35490792Sgshapiro		db_val.data = value;
35590792Sgshapiro		db_val.size = strlen(value);
35690792Sgshapiro		if (inclnull)
35790792Sgshapiro			db_val.size++;
35890792Sgshapiro
35990792Sgshapiro		errno = database->smdb_put(database, &db_key, &db_val,
36090792Sgshapiro					   putflags);
36190792Sgshapiro		if (errno != SMDBE_OK)
36290792Sgshapiro		{
36390792Sgshapiro			fprintf(stderr,
36490792Sgshapiro				"%s: error updating (%s, %s) in map %s: %s\n",
36590792Sgshapiro				progname, keyname, value, mapname,
36690792Sgshapiro				sm_errstring(errno));
36790792Sgshapiro			exitstat = EX_IOERR;
36890792Sgshapiro		}
36990792Sgshapiro	}
37090792Sgshapiro	else if (remove)
37190792Sgshapiro	{
37290792Sgshapiro		memset(&db_key, '\0', sizeof db_key);
37390792Sgshapiro		memset(&db_val, '\0', sizeof db_val);
37490792Sgshapiro
37590792Sgshapiro		db_key.data = keyname;
37690792Sgshapiro		db_key.size = strlen(keyname);
37790792Sgshapiro		if (inclnull)
37890792Sgshapiro			db_key.size++;
37990792Sgshapiro
38090792Sgshapiro		errno = database->smdb_del(database, &db_key, 0);
38190792Sgshapiro
38290792Sgshapiro		switch (errno)
38390792Sgshapiro		{
38490792Sgshapiro		case SMDBE_NOT_FOUND:
38590792Sgshapiro			fprintf(stderr,
38690792Sgshapiro				"%s: key %s doesn't exist in map %s\n",
38790792Sgshapiro				progname, keyname, mapname);
38890792Sgshapiro			/* Don't set exitstat */
38990792Sgshapiro			break;
39090792Sgshapiro		case SMDBE_OK:
39190792Sgshapiro			/* All's well */
39290792Sgshapiro			break;
39390792Sgshapiro		default:
39490792Sgshapiro			fprintf(stderr,
39590792Sgshapiro				"%s: couldn't remove key %s in map %s (error)\n",
39690792Sgshapiro				progname, keyname, mapname);
39790792Sgshapiro			exitstat = EX_IOERR;
39890792Sgshapiro			break;
39990792Sgshapiro		}
40090792Sgshapiro	}
40190792Sgshapiro	else
40290792Sgshapiro	{
40390792Sgshapiro		assert(0);  /* NOT REACHED */
40490792Sgshapiro	}
40590792Sgshapiro
40690792Sgshapiro	/*
40790792Sgshapiro	**  Now close the database.
40890792Sgshapiro	*/
40990792Sgshapiro
41090792Sgshapiro	errno = database->smdb_close(database);
41190792Sgshapiro	if (errno != SMDBE_OK)
41290792Sgshapiro	{
41390792Sgshapiro		fprintf(stderr, "%s: close(%s): %s\n",
41490792Sgshapiro			progname, mapname, sm_errstring(errno));
41590792Sgshapiro		exitstat = EX_IOERR;
41690792Sgshapiro	}
41790792Sgshapiro	smdb_free_database(database);
41890792Sgshapiro
41990792Sgshapiro	exit(exitstat);
42090792Sgshapiro	/* NOTREACHED */
42190792Sgshapiro	return exitstat;
42290792Sgshapiro}
423