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
usage(progname)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
main(argc,argv)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(¶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: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, ¶ms);
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