xref: /freebsd/contrib/sendmail/editmap/editmap.c (revision 61e21613)
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 <sm/sendmail.h>
42 #include <sendmail/sendmail.h>
43 #include <sendmail/pathnames.h>
44 #include <libsmdb/smdb.h>
45 
46 uid_t	RealUid;
47 gid_t	RealGid;
48 char	*RealUserName;
49 uid_t	RunAsUid;
50 gid_t	RunAsGid;
51 char	*RunAsUserName;
52 int	Verbose = 2;
53 bool	DontInitGroups = false;
54 uid_t	TrustedUid = 0;
55 BITMAP256 DontBlameSendmail;
56 
57 #define BUFSIZE		1024
58 #define ISSEP(c) (isascii(c) && isspace(c))
59 
60 
61 static void usage __P((char *));
62 
63 static void
64 usage(progname)
65 	char *progname;
66 {
67 	fprintf(stderr,
68 		"Usage: %s [-C cffile] [-N] [-f] [-q|-u|-x] maptype mapname key [ \"value ...\" ]\n",
69 		progname);
70 	exit(EX_USAGE);
71 }
72 
73 int
74 main(argc, argv)
75 	int argc;
76 	char **argv;
77 {
78 	char *progname;
79 	char *cfile;
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
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:fquxN"
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 'N':
163 			inclnull = true;
164 			break;
165 
166 		  default:
167 			usage(progname);
168 			assert(0);  /* NOTREACHED */
169 		}
170 	}
171 
172 	if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
173 		sff |= SFF_NOSLINK;
174 	if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
175 		sff |= SFF_NOHLINK;
176 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
177 		sff |= SFF_NOWLINK;
178 
179 	argc -= optind;
180 	argv += optind;
181 	if ((nops != 1) ||
182 	    (query && argc != 3) ||
183 	    (remove && argc != 3) ||
184 	    (update && argc <= 3))
185 	{
186 		usage(progname);
187 		assert(0);  /* NOTREACHED */
188 	}
189 
190 	typename = argv[0];
191 	mapname = argv[1];
192 	keyname = argv[2];
193 	if (update)
194 		value = argv[3];
195 
196 	if (foldcase)
197 	{
198 		char *lower;
199 
200 		lower = makelower(keyname);
201 
202 		/* if it is different then it is a static variable */
203 		if (keyname != lower)
204 			keyname = lower;
205 	}
206 
207 
208 #if HASFCHOWN
209 	/* Find TrustedUser value in sendmail.cf */
210 	if ((cfp = fopen(cfile, "r")) == NULL)
211 	{
212 		fprintf(stderr, "%s: %s: %s\n", progname,
213 			cfile, sm_errstring(errno));
214 		exit(EX_NOINPUT);
215 	}
216 	while (fgets(buf, sizeof(buf), cfp) != NULL)
217 	{
218 		register char *b;
219 
220 		if ((b = strchr(buf, '\n')) != NULL)
221 			*b = '\0';
222 
223 		b = buf;
224 		switch (*b++)
225 		{
226 		  case 'O':		/* option */
227 			if (strncasecmp(b, " TrustedUser", 12) == 0 &&
228 			    !(isascii(b[12]) && isalnum(b[12])))
229 			{
230 				b = strchr(b, '=');
231 				if (b == NULL)
232 					continue;
233 				while (isascii(*++b) && isspace(*b))
234 					continue;
235 				if (isascii(*b) && isdigit(*b))
236 					TrustedUid = atoi(b);
237 				else
238 				{
239 					TrustedUid = 0;
240 					pw = getpwnam(b);
241 					if (pw == NULL)
242 						fprintf(stderr,
243 							"TrustedUser: unknown user %s\n", b);
244 					else
245 						TrustedUid = pw->pw_uid;
246 				}
247 
248 # ifdef UID_MAX
249 				if (TrustedUid > UID_MAX)
250 				{
251 					fprintf(stderr,
252 						"TrustedUser: uid value (%ld) > UID_MAX (%ld)",
253 						(long) TrustedUid,
254 						(long) UID_MAX);
255 					TrustedUid = 0;
256 				}
257 # endif /* UID_MAX */
258 				break;
259 			}
260 
261 
262 		  default:
263 			continue;
264 		}
265 	}
266 	(void) fclose(cfp);
267 #endif /* HASFCHOWN */
268 
269 	if (query)
270 	{
271 		mode = O_RDONLY;
272 		smode = S_IRUSR;
273 	}
274 	else
275 	{
276 		mode = O_RDWR | O_CREAT;
277 		sff |= SFF_CREAT|SFF_NOTEXCL;
278 		smode = S_IWUSR;
279 	}
280 
281 	params.smdbp_num_elements = 4096;
282 
283 	errno = smdb_open_database(&database, mapname, mode, smode, sff,
284 				   typename, &user_info, &params);
285 	if (errno != SMDBE_OK)
286 	{
287 		char *hint;
288 
289 		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
290 		    (hint = smdb_db_definition(typename)) != NULL)
291 			fprintf(stderr,
292 				"%s: Need to recompile with -D%s for %s support\n",
293 				progname, hint, typename);
294 		else
295 			fprintf(stderr,
296 				"%s: error opening type %s map %s: %s\n",
297 				progname, typename, mapname,
298 				sm_errstring(errno));
299 		exit(EX_CANTCREAT);
300 	}
301 
302 	(void) database->smdb_sync(database, 0);
303 
304 	if (geteuid() == 0 && TrustedUid != 0)
305 	{
306 		errno = database->smdb_set_owner(database, TrustedUid, -1);
307 		if (errno != SMDBE_OK)
308 		{
309 			fprintf(stderr,
310 				"WARNING: ownership change on %s failed %s",
311 				mapname, sm_errstring(errno));
312 		}
313 	}
314 
315 	exitstat = EX_OK;
316 	if (query)
317 	{
318 		memset(&db_key, '\0', sizeof db_key);
319 		memset(&db_val, '\0', sizeof db_val);
320 
321 		db_key.data = keyname;
322 		db_key.size = strlen(keyname);
323 		if (inclnull)
324 			db_key.size++;
325 
326 		errno = database->smdb_get(database, &db_key, &db_val, 0);
327 		if (errno != SMDBE_OK)
328 		{
329 			/* XXX - Need to distinguish between not found */
330 			fprintf(stderr,
331 				"%s: couldn't find key %s in map %s\n",
332 				progname, keyname, mapname);
333 			exitstat = EX_UNAVAILABLE;
334 		}
335 		else
336 		{
337 			printf("%.*s\n", (int) db_val.size,
338 			       (char *) db_val.data);
339 		}
340 	}
341 	else if (update)
342 	{
343 		memset(&db_key, '\0', sizeof db_key);
344 		memset(&db_val, '\0', sizeof db_val);
345 
346 		db_key.data = keyname;
347 		db_key.size = strlen(keyname);
348 		if (inclnull)
349 			db_key.size++;
350 		db_val.data = value;
351 		db_val.size = strlen(value);
352 		if (inclnull)
353 			db_val.size++;
354 
355 		errno = database->smdb_put(database, &db_key, &db_val,
356 					   putflags);
357 		if (errno != SMDBE_OK)
358 		{
359 			fprintf(stderr,
360 				"%s: error updating (%s, %s) in map %s: %s\n",
361 				progname, keyname, value, mapname,
362 				sm_errstring(errno));
363 			exitstat = EX_IOERR;
364 		}
365 	}
366 	else if (remove)
367 	{
368 		memset(&db_key, '\0', sizeof db_key);
369 		memset(&db_val, '\0', sizeof db_val);
370 
371 		db_key.data = keyname;
372 		db_key.size = strlen(keyname);
373 		if (inclnull)
374 			db_key.size++;
375 
376 		errno = database->smdb_del(database, &db_key, 0);
377 
378 		switch (errno)
379 		{
380 		case SMDBE_NOT_FOUND:
381 			fprintf(stderr,
382 				"%s: key %s doesn't exist in map %s\n",
383 				progname, keyname, mapname);
384 			/* Don't set exitstat */
385 			break;
386 		case SMDBE_OK:
387 			/* All's well */
388 			break;
389 		default:
390 			fprintf(stderr,
391 				"%s: couldn't remove key %s in map %s (error)\n",
392 				progname, keyname, mapname);
393 			exitstat = EX_IOERR;
394 			break;
395 		}
396 	}
397 	else
398 	{
399 		assert(0);  /* NOT REACHED */
400 	}
401 
402 	/*
403 	**  Now close the database.
404 	*/
405 
406 	errno = database->smdb_close(database);
407 	if (errno != SMDBE_OK)
408 	{
409 		fprintf(stderr, "%s: close(%s): %s\n",
410 			progname, mapname, sm_errstring(errno));
411 		exitstat = EX_IOERR;
412 	}
413 	smdb_free_database(database);
414 
415 	exit(exitstat);
416 	/* NOTREACHED */
417 	return exitstat;
418 }
419