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