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
usage(progname)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
main(argc,argv)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(¶ms, '\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 /* FALLTHROUGH */
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, ¶ms);
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