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