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