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