2 * Copyright (c) 1998-2002, 2004, 2008, 2020 Proofpoint, Inc. and its suppliers.
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.
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.
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")
23 SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013-11-22 20:51:52 ca Exp $")
26 #include <sys/types.h>
28 # include <sys/file.h>
34 # undef EX_OK /* unistd.h may have another use for this */
37 #include <sendmail/sendmail.h>
39 #include <sendmail/pathnames.h>
40 #include <libsmdb/smdb.h>
42 # include <sm/ixlen.h>
52 bool DontInitGroups = false;
54 BITMAP256 DontBlameSendmail;
56 static bool verbose = false;
60 #define ISASCII(c) isascii((unsigned char)(c))
61 #define ISSPACE(c) (ISASCII(c) && isspace(c))
62 #define ISSEP(c) (sep == '\0' ? ISASCII(c) && isspace(c) : (c) == sep)
64 static void usage __P((const char *));
65 static char *readcf __P((const char *, char *, bool));
66 static void db_put __P((SMDB_DATABASE *, SMDB_DBENT, SMDB_DBENT, int, const char *, int, const char *));
72 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
73 "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
75 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
76 " %*s [-d] [-e] [-f] [-i type] [-l] [-o] [-r] [-s] [-t delimiter]\n",
77 (int) strlen(progname), "");
79 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
81 (int) strlen(progname), "");
83 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
84 " %*s [-u] [-v] type mapname\n",
85 (int) strlen(progname), "");
90 ** DB_PUT -- do the DB insert
93 ** database -- DB to use
96 ** putflags -- flags for smdb_put()
97 ** mapname -- name of map (for error reporting)
98 ** lineno -- line number (for error reporting)
99 ** progname -- name of program (for error reporting)
105 ** Sets exitstat so makemap exits with error if put fails
109 db_put(database, db_key, db_val, putflags, mapname, lineno, progname)
110 SMDB_DATABASE *database;
111 SMDB_DBENT db_key, db_val;
115 const char *progname;
121 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
122 "key=`%s', val=`%s'\n",
123 (char *) db_key.data,
124 (char *) db_val.data);
127 errcode = database->smdb_put(database, &db_key, &db_val, putflags);
131 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: ",
134 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "line %u: ",
136 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "key %s: ",
137 (char *) db_key.data);
138 if (SMDBE_KEY_EXIST == errcode)
140 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
142 exitstat = EX_DATAERR;
146 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
147 "put error: %s\n", sm_errstring(errcode));
153 ** READCF -- read some settings from configuration file.
156 ** cfile -- configuration file name.
157 ** mapfile -- file name of map to look up (if not NULL/empty)
158 ** Note: this finds the first match, so in case someone
159 ** uses the same map file for different maps, they are
160 ** hopefully using the same map type.
161 ** fullpath -- compare the full paths or just the "basename"s?
162 ** (even excluding any .ext !)
165 ** pointer to map class name (static!)
169 readcf(cfile, mapfile, fullpath)
176 static char classbuf[MAXLINE];
177 char *classname, *mapname;
180 if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile,
181 SM_IO_RDONLY, NULL)) == NULL)
183 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
185 cfile, sm_errstring(errno));
192 if (!fullpath && mapfile != NULL)
194 p = strrchr(mapfile, '/');
197 mapname = strdup(mapfile);
200 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
201 "makemap: strdup(%s) failed: %s\n",
202 mapfile, sm_errstring(errno));
205 if ((p = strchr(mapname, '.')) != NULL)
209 while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
213 if ((b = strchr(buf, '\n')) != NULL)
219 case 'O': /* option */
221 if (strncasecmp(b, " TrustedUser", 12) == 0 &&
222 !(ISASCII(b[12]) && isalnum(b[12])))
227 while (ISASCII(*++b) && isspace(*b))
229 if (ISASCII(*b) && isdigit(*b))
230 TrustedUid = atoi(b);
238 (void) sm_io_fprintf(smioerr,
240 "TrustedUser: unknown user %s\n", b);
242 TrustedUid = pw->pw_uid;
246 if (TrustedUid > UID_MAX)
248 (void) sm_io_fprintf(smioerr,
250 "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
255 # endif /* UID_MAX */
257 #endif /* HASFCHOWN */
260 case 'K': /* Keyfile (map) */
261 if (classname != NULL) /* found it already */
263 if (mapname == NULL || *mapname == '\0')
266 /* cut off trailing spaces */
267 for (p = buf + strlen(buf) - 1;
268 ISASCII(*p) && isspace(*p) && p > buf; p--)
271 /* find the last argument */
272 p = strrchr(buf, ' ');
275 b = strstr(p, mapname);
287 /* allow trailing white space? */
288 if (strcmp(mapname, b) != 0)
290 /* SM_ASSERT(b > buf); */
294 if (!isspace(*b) && fullpath)
296 if (!fullpath && !(SM_IS_DIR_DELIM(*b) || isspace(*b)))
299 /* basically from readcf.c */
300 for (b = buf + 1; ISASCII(*b) && isspace(*b); b++)
302 if (!(ISASCII(*b) && isalnum(*b)))
304 /* syserr("readcf: config K line: no map name"); */
308 while ((ISASCII(*++b) && isalnum(*b)) || *b == '_' || *b == '.')
312 while (ISASCII(*b) && isspace(*b))
314 if (!(ISASCII(*b) && isalnum(*b)))
316 /* syserr("readcf: config K line, map %s: no map class", b); */
320 while (ISASCII(*++b) && isalnum(*b))
324 (void) sm_strlcpy(classbuf, classname, sizeof classbuf);
331 (void) sm_io_close(cfp, SM_TIME_DEFAULT);
333 /* not really needed because it is just a "one time leak" */
334 if (mapname != mapfile && mapname != NULL)
349 bool inclnull = false;
350 bool notrunc = false;
351 bool allowreplace = false;
352 bool allowempty = false;
353 bool foldcase = true;
357 ** NOTE: this does not work properly:
358 ** sendmail does address rewriting which is not done here.
361 bool aliases = false;
363 bool didreadcf = false;
367 char *typename = NULL;
368 char *fallback = NULL;
369 char *mapname = NULL;
374 long sff = SFF_ROOTOK|SFF_REGONLY;
376 SMDB_DATABASE *database;
378 SMDB_DBENT db_key, db_val;
379 SMDB_DBPARAMS params;
380 SMDB_USER_INFO user_info;
382 static char rnamebuf[MAXNAME]; /* holds RealUserName */
392 memset(¶ms, '\0', sizeof params);
393 params.smdbp_cache_size = 1024 * 1024;
395 progname = strrchr(argv[0], '/');
396 if (progname != NULL)
400 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
402 clrbitmap(DontBlameSendmail);
403 RunAsUid = RealUid = getuid();
404 RunAsGid = RealGid = getgid();
405 pw = getpwuid(RealUid);
407 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
409 (void) sm_snprintf(rnamebuf, sizeof rnamebuf,
410 "Unknown UID %d", (int) RealUid);
411 RunAsUserName = RealUserName = rnamebuf;
412 user_info.smdbu_id = RunAsUid;
413 user_info.smdbu_group_id = RunAsGid;
414 (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
415 SMDB_MAX_USER_NAME_LEN);
417 #define OPTIONS "C:D:Nc:defi:Llorst:uvx"
419 # define A_OPTIONS "a"
424 # define X_OPTIONS "S:"
428 while ((opt = getopt(argc, argv, A_OPTIONS OPTIONS X_OPTIONS)) != -1)
442 /* Note: this doesn't verify e-mail addresses */
449 params.smdbp_cache_size = atol(optarg);
453 params.smdbp_allow_dup = true;
473 smdb_print_available_types(false);
474 sm_io_fprintf(smioout, SM_TIME_DEFAULT,
480 smdb_print_available_types(false);
499 setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
500 setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
501 setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
502 setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
506 if (optarg == NULL || *optarg == '\0')
508 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
509 "Invalid separator\n");
524 smdb_print_available_types(true);
534 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
536 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
538 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
554 #define TYPEFROMCF (strcasecmp(typename, "cf") == 0)
555 #define FULLPATHFROMCF (strcmp(typename, "cf") == 0)
561 typename = readcf(cfile, mapname, FULLPATHFROMCF);
563 (void) readcf(cfile, NULL, false);
566 #endif /* HASFCHOWN */
568 if (!params.smdbp_allow_dup && !allowreplace)
569 putflags = SMDBF_NO_OVERWRITE;
581 mode |= O_CREAT|O_TRUNC;
587 params.smdbp_num_elements = 4096;
589 if (!didreadcf && TYPEFROMCF)
591 typename = readcf(cfile, mapname, FULLPATHFROMCF);
594 if (didreadcf && (typename == NULL || *typename == '\0'))
596 if (fallback != NULL && *fallback != '\0')
600 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
601 "%s: mapfile %s: not found in %s, using fallback %s\n",
602 progname, mapname, cfile, fallback);
606 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
607 "%s: mapfile %s: not found in %s\n",
608 progname, mapname, cfile);
614 ** Note: if "implicit" is selected it does not work like
615 ** sendmail: it will just use the first available DB type,
616 ** it won't try several (for -u) to find one that "works".
619 errno = smdb_open_database(&database, mapname, mode, smode, sff,
620 typename, &user_info, ¶ms);
621 if (errno != SMDBE_OK)
625 if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
626 (hint = smdb_db_definition(typename)) != NULL)
627 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
628 "%s: Need to recompile with -D%s for %s support\n",
629 progname, hint, typename);
631 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
632 "%s: error opening type %s map %s: %s\n",
633 progname, typename, mapname,
634 sm_errstring(errno));
638 (void) database->smdb_sync(database, 0);
640 if (!unmake && geteuid() == 0 && TrustedUid != 0)
642 errno = database->smdb_set_owner(database, TrustedUid, -1);
643 if (errno != SMDBE_OK)
645 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
646 "WARNING: ownership change on %s failed %s",
647 mapname, sm_errstring(errno));
658 errno = database->smdb_cursor(database, &cursor, 0);
659 if (errno != SMDBE_OK)
662 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
663 "%s: cannot make cursor for type %s map %s\n",
664 progname, typename, mapname);
668 memset(&db_key, '\0', sizeof db_key);
669 memset(&db_val, '\0', sizeof db_val);
671 for (lineno = 0; ; lineno++)
673 errno = cursor->smdbc_get(cursor, &db_key, &db_val,
674 SMDB_CURSOR_GET_NEXT);
675 if (errno != SMDBE_OK)
678 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
681 (char *) db_key.data,
682 (sep != '\0') ? sep : '\t',
684 (char *)db_val.data);
687 (void) cursor->smdbc_close(cursor);
692 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
703 p = strchr(ibuf, '\n');
706 else if (!sm_io_eof(smioin))
708 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
709 "%s: %s: line %u: line too long (%ld bytes max)\n",
710 progname, mapname, lineno,
712 exitstat = EX_DATAERR;
716 if (ibuf[0] == '\0' || ibuf[0] == comment)
718 if (sep == '\0' && ISASCII(ibuf[0]) && isspace(ibuf[0]))
720 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
721 "%s: %s: line %u: syntax error (leading space)\n",
722 progname, mapname, lineno);
723 exitstat = EX_DATAERR;
727 memset(&db_key, '\0', sizeof db_key);
728 memset(&db_val, '\0', sizeof db_val);
735 for (p = ibuf; *p != '\0' && !ISSEP(*p); p++)
748 lkey = sm_lowercase(ibuf);
750 db_key.size = strlen(lkey);
756 /* NOTE: see if () above! */
757 for (p = ibuf; *p != '\0' && !ISSEP(*p); p++)
759 if (foldcase && ISASCII(*p) && isupper(*p))
763 if (0 == db_key.size)
765 db_key.size = p - ibuf;
771 while (*p != '\0' && ISSEP(*p))
774 while (aliases && *p != '\0' && ISSPACE(*p))
777 if (!allowempty && *p == '\0')
779 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
780 "%s: %s: line %u: no RHS for LHS %s\n",
781 progname, mapname, lineno,
782 (char *) db_key.data);
783 exitstat = EX_DATAERR;
788 db_val.size = strlen(p);
793 ** Do the database insert.
796 db_put(database, db_key, db_val, putflags, mapname,
808 db_put(database, db_key, db_val, putflags, mapname, -1,
811 #endif /* _FFR_MM_ALIASES */
820 ** Now close the database.
823 errno = database->smdb_close(database);
824 if (errno != SMDBE_OK)
826 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
827 "%s: close(%s): %s\n",
828 progname, mapname, sm_errstring(errno));
831 smdb_free_database(database);