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