1 /*
2 * Copyright (c) 1998-2002, 2004, 2008 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
16 SM_IDSTR(copyright,
17 "@(#) Copyright (c) 1998-2002, 2004 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
23 SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013-11-22 20:51:52 ca Exp $")
24
25
26 #include <sys/types.h>
27 #ifndef ISC_UNIX
28 # include <sys/file.h>
29 #endif /* ! ISC_UNIX */
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #ifdef EX_OK
34 # undef EX_OK /* unistd.h may have another use for this */
35 #endif /* EX_OK */
36 #include <sysexits.h>
37 #include <sendmail/sendmail.h>
38 #include <sendmail/pathnames.h>
39 #include <libsmdb/smdb.h>
40
41 uid_t RealUid;
42 gid_t RealGid;
43 char *RealUserName;
44 uid_t RunAsUid;
45 gid_t RunAsGid;
46 char *RunAsUserName;
47 int Verbose = 2;
48 bool DontInitGroups = false;
49 uid_t TrustedUid = 0;
50 BITMAP256 DontBlameSendmail;
51
52 #define BUFSIZE 1024
53 #define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep)
54
55 static void usage __P((char *));
56
57 static void
usage(progname)58 usage(progname)
59 char *progname;
60 {
61 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
62 "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
63 progname);
64 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
65 " %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n",
66 (int) strlen(progname), "");
67 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
68 " %*s [-u] [-v] type mapname\n",
69 (int) strlen(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 inclnull = false;
81 bool notrunc = false;
82 bool allowreplace = false;
83 bool allowempty = false;
84 bool verbose = false;
85 bool foldcase = true;
86 bool unmake = false;
87 char sep = '\0';
88 char comment = '#';
89 int exitstat;
90 int opt;
91 char *typename = NULL;
92 char *mapname = NULL;
93 unsigned int lineno;
94 int st;
95 int mode;
96 int smode;
97 int putflags = 0;
98 long sff = SFF_ROOTOK|SFF_REGONLY;
99 struct passwd *pw;
100 SMDB_DATABASE *database;
101 SMDB_CURSOR *cursor;
102 SMDB_DBENT db_key, db_val;
103 SMDB_DBPARAMS params;
104 SMDB_USER_INFO user_info;
105 char ibuf[BUFSIZE];
106 #if HASFCHOWN
107 SM_FILE_T *cfp;
108 char buf[MAXLINE];
109 #endif /* HASFCHOWN */
110 static char rnamebuf[MAXNAME]; /* holds RealUserName */
111 extern char *optarg;
112 extern int optind;
113
114 memset(¶ms, '\0', sizeof params);
115 params.smdbp_cache_size = 1024 * 1024;
116
117 progname = strrchr(argv[0], '/');
118 if (progname != NULL)
119 progname++;
120 else
121 progname = argv[0];
122 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
123
124 clrbitmap(DontBlameSendmail);
125 RunAsUid = RealUid = getuid();
126 RunAsGid = RealGid = getgid();
127 pw = getpwuid(RealUid);
128 if (pw != NULL)
129 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
130 else
131 (void) sm_snprintf(rnamebuf, sizeof rnamebuf,
132 "Unknown UID %d", (int) RealUid);
133 RunAsUserName = RealUserName = rnamebuf;
134 user_info.smdbu_id = RunAsUid;
135 user_info.smdbu_group_id = RunAsGid;
136 (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
137 SMDB_MAX_USER_NAME_LEN);
138
139 #define OPTIONS "C:D:Nc:deflorst:uv"
140 while ((opt = getopt(argc, argv, OPTIONS)) != -1)
141 {
142 switch (opt)
143 {
144 case 'C':
145 cfile = optarg;
146 break;
147
148 case 'N':
149 inclnull = true;
150 break;
151
152 case 'c':
153 params.smdbp_cache_size = atol(optarg);
154 break;
155
156 case 'd':
157 params.smdbp_allow_dup = true;
158 break;
159
160 case 'e':
161 allowempty = true;
162 break;
163
164 case 'f':
165 foldcase = false;
166 break;
167
168 case 'D':
169 comment = *optarg;
170 break;
171
172 case 'l':
173 smdb_print_available_types();
174 exit(EX_OK);
175 break;
176
177 case 'o':
178 notrunc = true;
179 break;
180
181 case 'r':
182 allowreplace = true;
183 break;
184
185 case 's':
186 setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
187 setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
188 setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
189 setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
190 break;
191
192 case 't':
193 if (optarg == NULL || *optarg == '\0')
194 {
195 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
196 "Invalid separator\n");
197 break;
198 }
199 sep = *optarg;
200 break;
201
202 case 'u':
203 unmake = true;
204 break;
205
206 case 'v':
207 verbose = true;
208 break;
209
210 default:
211 usage(progname);
212 /* NOTREACHED */
213 }
214 }
215
216 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
217 sff |= SFF_NOSLINK;
218 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
219 sff |= SFF_NOHLINK;
220 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
221 sff |= SFF_NOWLINK;
222
223 argc -= optind;
224 argv += optind;
225 if (argc != 2)
226 {
227 usage(progname);
228 /* NOTREACHED */
229 }
230 else
231 {
232 typename = argv[0];
233 mapname = argv[1];
234 }
235
236 #if HASFCHOWN
237 if (!unmake && geteuid() == 0)
238 {
239 /* Find TrustedUser value in sendmail.cf */
240 if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile,
241 SM_IO_RDONLY, NULL)) == NULL)
242 {
243 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
244 "makemap: %s: %s\n",
245 cfile, sm_errstring(errno));
246 exit(EX_NOINPUT);
247 }
248 while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
249 {
250 register char *b;
251
252 if ((b = strchr(buf, '\n')) != NULL)
253 *b = '\0';
254
255 b = buf;
256 switch (*b++)
257 {
258 case 'O': /* option */
259 if (strncasecmp(b, " TrustedUser", 12) == 0 &&
260 !(isascii(b[12]) && isalnum(b[12])))
261 {
262 b = strchr(b, '=');
263 if (b == NULL)
264 continue;
265 while (isascii(*++b) && isspace(*b))
266 continue;
267 if (isascii(*b) && isdigit(*b))
268 TrustedUid = atoi(b);
269 else
270 {
271 TrustedUid = 0;
272 pw = getpwnam(b);
273 if (pw == NULL)
274 (void) sm_io_fprintf(smioerr,
275 SM_TIME_DEFAULT,
276 "TrustedUser: unknown user %s\n", b);
277 else
278 TrustedUid = pw->pw_uid;
279 }
280
281 # ifdef UID_MAX
282 if (TrustedUid > UID_MAX)
283 {
284 (void) sm_io_fprintf(smioerr,
285 SM_TIME_DEFAULT,
286 "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
287 (long) TrustedUid,
288 (long) UID_MAX);
289 TrustedUid = 0;
290 }
291 # endif /* UID_MAX */
292 break;
293 }
294
295
296 default:
297 continue;
298 }
299 }
300 (void) sm_io_close(cfp, SM_TIME_DEFAULT);
301 }
302 #endif /* HASFCHOWN */
303
304 if (!params.smdbp_allow_dup && !allowreplace)
305 putflags = SMDBF_NO_OVERWRITE;
306
307 if (unmake)
308 {
309 mode = O_RDONLY;
310 smode = S_IRUSR;
311 }
312 else
313 {
314 mode = O_RDWR;
315 if (!notrunc)
316 {
317 mode |= O_CREAT|O_TRUNC;
318 sff |= SFF_CREAT;
319 }
320 smode = S_IWUSR;
321 }
322
323 params.smdbp_num_elements = 4096;
324
325 errno = smdb_open_database(&database, mapname, mode, smode, sff,
326 typename, &user_info, ¶ms);
327 if (errno != SMDBE_OK)
328 {
329 char *hint;
330
331 if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
332 (hint = smdb_db_definition(typename)) != NULL)
333 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
334 "%s: Need to recompile with -D%s for %s support\n",
335 progname, hint, typename);
336 else
337 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
338 "%s: error opening type %s map %s: %s\n",
339 progname, typename, mapname,
340 sm_errstring(errno));
341 exit(EX_CANTCREAT);
342 }
343
344 (void) database->smdb_sync(database, 0);
345
346 if (!unmake && geteuid() == 0 && TrustedUid != 0)
347 {
348 errno = database->smdb_set_owner(database, TrustedUid, -1);
349 if (errno != SMDBE_OK)
350 {
351 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
352 "WARNING: ownership change on %s failed %s",
353 mapname, sm_errstring(errno));
354 }
355 }
356
357 /*
358 ** Copy the data
359 */
360
361 exitstat = EX_OK;
362 if (unmake)
363 {
364 errno = database->smdb_cursor(database, &cursor, 0);
365 if (errno != SMDBE_OK)
366 {
367
368 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
369 "%s: cannot make cursor for type %s map %s\n",
370 progname, typename, mapname);
371 exit(EX_SOFTWARE);
372 }
373
374 memset(&db_key, '\0', sizeof db_key);
375 memset(&db_val, '\0', sizeof db_val);
376
377 for (lineno = 0; ; lineno++)
378 {
379 errno = cursor->smdbc_get(cursor, &db_key, &db_val,
380 SMDB_CURSOR_GET_NEXT);
381 if (errno != SMDBE_OK)
382 break;
383
384 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
385 "%.*s%c%.*s\n",
386 (int) db_key.size,
387 (char *) db_key.data,
388 (sep != '\0') ? sep : '\t',
389 (int) db_val.size,
390 (char *)db_val.data);
391
392 }
393 (void) cursor->smdbc_close(cursor);
394 }
395 else
396 {
397 lineno = 0;
398 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
399 >= 0)
400 {
401 register char *p;
402
403 lineno++;
404
405 /*
406 ** Parse the line.
407 */
408
409 p = strchr(ibuf, '\n');
410 if (p != NULL)
411 *p = '\0';
412 else if (!sm_io_eof(smioin))
413 {
414 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
415 "%s: %s: line %u: line too long (%ld bytes max)\n",
416 progname, mapname, lineno,
417 (long) sizeof ibuf);
418 exitstat = EX_DATAERR;
419 continue;
420 }
421
422 if (ibuf[0] == '\0' || ibuf[0] == comment)
423 continue;
424 if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
425 {
426 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
427 "%s: %s: line %u: syntax error (leading space)\n",
428 progname, mapname, lineno);
429 exitstat = EX_DATAERR;
430 continue;
431 }
432
433 memset(&db_key, '\0', sizeof db_key);
434 memset(&db_val, '\0', sizeof db_val);
435 db_key.data = ibuf;
436
437 for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
438 {
439 if (foldcase && isascii(*p) && isupper(*p))
440 *p = tolower(*p);
441 }
442 db_key.size = p - ibuf;
443 if (inclnull)
444 db_key.size++;
445
446 if (*p != '\0')
447 *p++ = '\0';
448 while (*p != '\0' && ISSEP(*p))
449 p++;
450 if (!allowempty && *p == '\0')
451 {
452 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
453 "%s: %s: line %u: no RHS for LHS %s\n",
454 progname, mapname, lineno,
455 (char *) db_key.data);
456 exitstat = EX_DATAERR;
457 continue;
458 }
459
460 db_val.data = p;
461 db_val.size = strlen(p);
462 if (inclnull)
463 db_val.size++;
464
465 /*
466 ** Do the database insert.
467 */
468
469 if (verbose)
470 {
471 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
472 "key=`%s', val=`%s'\n",
473 (char *) db_key.data,
474 (char *) db_val.data);
475 }
476
477 errno = database->smdb_put(database, &db_key, &db_val,
478 putflags);
479 switch (errno)
480 {
481 case SMDBE_KEY_EXIST:
482 st = 1;
483 break;
484
485 case 0:
486 st = 0;
487 break;
488
489 default:
490 st = -1;
491 break;
492 }
493
494 if (st < 0)
495 {
496 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
497 "%s: %s: line %u: key %s: put error: %s\n",
498 progname, mapname, lineno,
499 (char *) db_key.data,
500 sm_errstring(errno));
501 exitstat = EX_IOERR;
502 }
503 else if (st > 0)
504 {
505 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
506 "%s: %s: line %u: key %s: duplicate key\n",
507 progname, mapname,
508 lineno,
509 (char *) db_key.data);
510 exitstat = EX_DATAERR;
511 }
512 }
513 }
514
515 /*
516 ** Now close the database.
517 */
518
519 errno = database->smdb_close(database);
520 if (errno != SMDBE_OK)
521 {
522 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
523 "%s: close(%s): %s\n",
524 progname, mapname, sm_errstring(errno));
525 exitstat = EX_IOERR;
526 }
527 smdb_free_database(database);
528
529 exit(exitstat);
530
531 /* NOTREACHED */
532 return exitstat;
533 }
534