xref: /freebsd/contrib/sendmail/editmap/editmap.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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
16 SM_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
25 SM_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 
45 uid_t	RealUid;
46 gid_t	RealGid;
47 char	*RealUserName;
48 uid_t	RunAsUid;
49 gid_t	RunAsGid;
50 char	*RunAsUserName;
51 int	Verbose = 2;
52 bool	DontInitGroups = false;
53 uid_t	TrustedUid = 0;
54 BITMAP256 DontBlameSendmail;
55 
56 #define BUFSIZE		1024
57 #define ISSEP(c) (isascii(c) && isspace(c))
58 
59 
60 static void usage __P((char *));
61 
62 static void
63 usage(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 
72 int
73 main(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