1N/A/*
1N/A * Copyright (c) 1998-2002, 2004, 2008 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A * Copyright (c) 1992 Eric P. Allman. All rights reserved.
1N/A * Copyright (c) 1992, 1993
1N/A * The Regents of the University of California. All rights reserved.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A *
1N/A */
1N/A
1N/A#include <sm/gen.h>
1N/A
1N/ASM_IDSTR(copyright,
1N/A"@(#) Copyright (c) 1998-2002, 2004 Sendmail, Inc. and its suppliers.\n\
1N/A All rights reserved.\n\
1N/A Copyright (c) 1992 Eric P. Allman. All rights reserved.\n\
1N/A Copyright (c) 1992, 1993\n\
1N/A The Regents of the University of California. All rights reserved.\n")
1N/A
1N/ASM_IDSTR(id, "@(#)$Id: makemap.c,v 8.180 2010/11/23 02:35:08 gshapiro Exp $")
1N/A
1N/A
1N/A#include <sys/types.h>
1N/A#ifndef ISC_UNIX
1N/A# include <sys/file.h>
1N/A#endif /* ! ISC_UNIX */
1N/A#include <ctype.h>
1N/A#include <stdlib.h>
1N/A#include <unistd.h>
1N/A#ifdef EX_OK
1N/A# undef EX_OK /* unistd.h may have another use for this */
1N/A#endif /* EX_OK */
1N/A#include <sysexits.h>
1N/A#include <sendmail/sendmail.h>
1N/A#include <sendmail/pathnames.h>
1N/A#include <libsmdb/smdb.h>
1N/A
1N/Auid_t RealUid;
1N/Agid_t RealGid;
1N/Achar *RealUserName;
1N/Auid_t RunAsUid;
1N/Agid_t RunAsGid;
1N/Achar *RunAsUserName;
1N/Aint Verbose = 2;
1N/Abool DontInitGroups = false;
1N/Auid_t TrustedUid = 0;
1N/ABITMAP256 DontBlameSendmail;
1N/A
1N/A#define BUFSIZE 1024
1N/A#define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep)
1N/A
1N/Astatic void usage __P((char *));
1N/A
1N/Astatic void
1N/Ausage(progname)
1N/A char *progname;
1N/A{
1N/A sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
1N/A progname);
1N/A sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A " %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n",
1N/A (int) strlen(progname), "");
1N/A sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A " %*s [-u] [-v] type mapname\n",
1N/A (int) strlen(progname), "");
1N/A exit(EX_USAGE);
1N/A}
1N/A
1N/Aint
1N/Amain(argc, argv)
1N/A int argc;
1N/A char **argv;
1N/A{
1N/A char *progname;
1N/A char *cfile;
1N/A bool inclnull = false;
1N/A bool notrunc = false;
1N/A bool allowreplace = false;
1N/A bool allowempty = false;
1N/A bool verbose = false;
1N/A bool foldcase = true;
1N/A bool unmake = false;
1N/A char sep = '\0';
1N/A char comment = '#';
1N/A int exitstat;
1N/A int opt;
1N/A char *typename = NULL;
1N/A char *mapname = NULL;
1N/A unsigned int lineno;
1N/A int st;
1N/A int mode;
1N/A int smode;
1N/A int putflags = 0;
1N/A long sff = SFF_ROOTOK|SFF_REGONLY;
1N/A struct passwd *pw;
1N/A SMDB_DATABASE *database;
1N/A SMDB_CURSOR *cursor;
1N/A SMDB_DBENT db_key, db_val;
1N/A SMDB_DBPARAMS params;
1N/A SMDB_USER_INFO user_info;
1N/A char ibuf[BUFSIZE];
1N/A#if HASFCHOWN
1N/A SM_FILE_T *cfp;
1N/A char buf[MAXLINE];
1N/A#endif /* HASFCHOWN */
1N/A static char rnamebuf[MAXNAME]; /* holds RealUserName */
1N/A extern char *optarg;
1N/A extern int optind;
1N/A
1N/A memset(&params, '\0', sizeof params);
1N/A params.smdbp_cache_size = 1024 * 1024;
1N/A
1N/A progname = strrchr(argv[0], '/');
1N/A if (progname != NULL)
1N/A progname++;
1N/A else
1N/A progname = argv[0];
1N/A cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
1N/A
1N/A clrbitmap(DontBlameSendmail);
1N/A RunAsUid = RealUid = getuid();
1N/A RunAsGid = RealGid = getgid();
1N/A pw = getpwuid(RealUid);
1N/A if (pw != NULL)
1N/A (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
1N/A else
1N/A (void) sm_snprintf(rnamebuf, sizeof rnamebuf,
1N/A "Unknown UID %d", (int) RealUid);
1N/A RunAsUserName = RealUserName = rnamebuf;
1N/A user_info.smdbu_id = RunAsUid;
1N/A user_info.smdbu_group_id = RunAsGid;
1N/A (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
1N/A SMDB_MAX_USER_NAME_LEN);
1N/A
1N/A#define OPTIONS "C:D:Nc:deflorst:uv"
1N/A while ((opt = getopt(argc, argv, OPTIONS)) != -1)
1N/A {
1N/A switch (opt)
1N/A {
1N/A case 'C':
1N/A cfile = optarg;
1N/A break;
1N/A
1N/A case 'N':
1N/A inclnull = true;
1N/A break;
1N/A
1N/A case 'c':
1N/A params.smdbp_cache_size = atol(optarg);
1N/A break;
1N/A
1N/A case 'd':
1N/A params.smdbp_allow_dup = true;
1N/A break;
1N/A
1N/A case 'e':
1N/A allowempty = true;
1N/A break;
1N/A
1N/A case 'f':
1N/A foldcase = false;
1N/A break;
1N/A
1N/A case 'D':
1N/A comment = *optarg;
1N/A break;
1N/A
1N/A case 'l':
1N/A smdb_print_available_types();
1N/A exit(EX_OK);
1N/A break;
1N/A
1N/A case 'o':
1N/A notrunc = true;
1N/A break;
1N/A
1N/A case 'r':
1N/A allowreplace = true;
1N/A break;
1N/A
1N/A case 's':
1N/A setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
1N/A setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
1N/A setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
1N/A setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
1N/A break;
1N/A
1N/A case 't':
1N/A if (optarg == NULL || *optarg == '\0')
1N/A {
1N/A sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "Invalid separator\n");
1N/A break;
1N/A }
1N/A sep = *optarg;
1N/A break;
1N/A
1N/A case 'u':
1N/A unmake = true;
1N/A break;
1N/A
1N/A case 'v':
1N/A verbose = true;
1N/A break;
1N/A
1N/A default:
1N/A usage(progname);
1N/A /* NOTREACHED */
1N/A }
1N/A }
1N/A
1N/A if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1N/A sff |= SFF_NOSLINK;
1N/A if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1N/A sff |= SFF_NOHLINK;
1N/A if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1N/A sff |= SFF_NOWLINK;
1N/A
1N/A argc -= optind;
1N/A argv += optind;
1N/A if (argc != 2)
1N/A {
1N/A usage(progname);
1N/A /* NOTREACHED */
1N/A }
1N/A else
1N/A {
1N/A typename = argv[0];
1N/A mapname = argv[1];
1N/A }
1N/A
1N/A#if HASFCHOWN
1N/A /* Find TrustedUser value in sendmail.cf */
1N/A if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY,
1N/A NULL)) == NULL)
1N/A {
1N/A sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s\n",
1N/A cfile, sm_errstring(errno));
1N/A exit(EX_NOINPUT);
1N/A }
1N/A while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
1N/A {
1N/A register char *b;
1N/A
1N/A if ((b = strchr(buf, '\n')) != NULL)
1N/A *b = '\0';
1N/A
1N/A b = buf;
1N/A switch (*b++)
1N/A {
1N/A case 'O': /* option */
1N/A if (strncasecmp(b, " TrustedUser", 12) == 0 &&
1N/A !(isascii(b[12]) && isalnum(b[12])))
1N/A {
1N/A b = strchr(b, '=');
1N/A if (b == NULL)
1N/A continue;
1N/A while (isascii(*++b) && isspace(*b))
1N/A continue;
1N/A if (isascii(*b) && isdigit(*b))
1N/A TrustedUid = atoi(b);
1N/A else
1N/A {
1N/A TrustedUid = 0;
1N/A pw = getpwnam(b);
1N/A if (pw == NULL)
1N/A (void) sm_io_fprintf(smioerr,
1N/A SM_TIME_DEFAULT,
1N/A "TrustedUser: unknown user %s\n", b);
1N/A else
1N/A TrustedUid = pw->pw_uid;
1N/A }
1N/A
1N/A# ifdef UID_MAX
1N/A if (TrustedUid > UID_MAX)
1N/A {
1N/A (void) sm_io_fprintf(smioerr,
1N/A SM_TIME_DEFAULT,
1N/A "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
1N/A (long) TrustedUid,
1N/A (long) UID_MAX);
1N/A TrustedUid = 0;
1N/A }
1N/A# endif /* UID_MAX */
1N/A break;
1N/A }
1N/A
1N/A
1N/A default:
1N/A continue;
1N/A }
1N/A }
1N/A (void) sm_io_close(cfp, SM_TIME_DEFAULT);
1N/A#endif /* HASFCHOWN */
1N/A
1N/A if (!params.smdbp_allow_dup && !allowreplace)
1N/A putflags = SMDBF_NO_OVERWRITE;
1N/A
1N/A if (unmake)
1N/A {
1N/A mode = O_RDONLY;
1N/A smode = S_IRUSR;
1N/A }
1N/A else
1N/A {
1N/A mode = O_RDWR;
1N/A if (!notrunc)
1N/A {
1N/A mode |= O_CREAT|O_TRUNC;
1N/A sff |= SFF_CREAT;
1N/A }
1N/A smode = S_IWUSR;
1N/A }
1N/A
1N/A params.smdbp_num_elements = 4096;
1N/A
1N/A errno = smdb_open_database(&database, mapname, mode, smode, sff,
1N/A typename, &user_info, &params);
1N/A if (errno != SMDBE_OK)
1N/A {
1N/A char *hint;
1N/A
1N/A if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
1N/A (hint = smdb_db_definition(typename)) != NULL)
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: Need to recompile with -D%s for %s support\n",
1N/A progname, hint, typename);
1N/A else
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: error opening type %s map %s: %s\n",
1N/A progname, typename, mapname,
1N/A sm_errstring(errno));
1N/A exit(EX_CANTCREAT);
1N/A }
1N/A
1N/A (void) database->smdb_sync(database, 0);
1N/A
1N/A if (!unmake && geteuid() == 0 && TrustedUid != 0)
1N/A {
1N/A errno = database->smdb_set_owner(database, TrustedUid, -1);
1N/A if (errno != SMDBE_OK)
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "WARNING: ownership change on %s failed %s",
1N/A mapname, sm_errstring(errno));
1N/A }
1N/A }
1N/A
1N/A /*
1N/A ** Copy the data
1N/A */
1N/A
1N/A exitstat = EX_OK;
1N/A if (unmake)
1N/A {
1N/A errno = database->smdb_cursor(database, &cursor, 0);
1N/A if (errno != SMDBE_OK)
1N/A {
1N/A
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: cannot make cursor for type %s map %s\n",
1N/A progname, typename, mapname);
1N/A exit(EX_SOFTWARE);
1N/A }
1N/A
1N/A memset(&db_key, '\0', sizeof db_key);
1N/A memset(&db_val, '\0', sizeof db_val);
1N/A
1N/A for (lineno = 0; ; lineno++)
1N/A {
1N/A errno = cursor->smdbc_get(cursor, &db_key, &db_val,
1N/A SMDB_CURSOR_GET_NEXT);
1N/A if (errno != SMDBE_OK)
1N/A break;
1N/A
1N/A (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1N/A "%.*s%c%.*s\n",
1N/A (int) db_key.size,
1N/A (char *) db_key.data,
1N/A (sep != '\0') ? sep : '\t',
1N/A (int) db_val.size,
1N/A (char *)db_val.data);
1N/A
1N/A }
1N/A (void) cursor->smdbc_close(cursor);
1N/A }
1N/A else
1N/A {
1N/A lineno = 0;
1N/A while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
1N/A != NULL)
1N/A {
1N/A register char *p;
1N/A
1N/A lineno++;
1N/A
1N/A /*
1N/A ** Parse the line.
1N/A */
1N/A
1N/A p = strchr(ibuf, '\n');
1N/A if (p != NULL)
1N/A *p = '\0';
1N/A else if (!sm_io_eof(smioin))
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: %s: line %u: line too long (%ld bytes max)\n",
1N/A progname, mapname, lineno,
1N/A (long) sizeof ibuf);
1N/A exitstat = EX_DATAERR;
1N/A continue;
1N/A }
1N/A
1N/A if (ibuf[0] == '\0' || ibuf[0] == comment)
1N/A continue;
1N/A if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: %s: line %u: syntax error (leading space)\n",
1N/A progname, mapname, lineno);
1N/A exitstat = EX_DATAERR;
1N/A continue;
1N/A }
1N/A
1N/A memset(&db_key, '\0', sizeof db_key);
1N/A memset(&db_val, '\0', sizeof db_val);
1N/A db_key.data = ibuf;
1N/A
1N/A for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
1N/A {
1N/A if (foldcase && isascii(*p) && isupper(*p))
1N/A *p = tolower(*p);
1N/A }
1N/A db_key.size = p - ibuf;
1N/A if (inclnull)
1N/A db_key.size++;
1N/A
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A while (*p != '\0' && ISSEP(*p))
1N/A p++;
1N/A if (!allowempty && *p == '\0')
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: %s: line %u: no RHS for LHS %s\n",
1N/A progname, mapname, lineno,
1N/A (char *) db_key.data);
1N/A exitstat = EX_DATAERR;
1N/A continue;
1N/A }
1N/A
1N/A db_val.data = p;
1N/A db_val.size = strlen(p);
1N/A if (inclnull)
1N/A db_val.size++;
1N/A
1N/A /*
1N/A ** Do the database insert.
1N/A */
1N/A
1N/A if (verbose)
1N/A {
1N/A (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1N/A "key=`%s', val=`%s'\n",
1N/A (char *) db_key.data,
1N/A (char *) db_val.data);
1N/A }
1N/A
1N/A errno = database->smdb_put(database, &db_key, &db_val,
1N/A putflags);
1N/A switch (errno)
1N/A {
1N/A case SMDBE_KEY_EXIST:
1N/A st = 1;
1N/A break;
1N/A
1N/A case 0:
1N/A st = 0;
1N/A break;
1N/A
1N/A default:
1N/A st = -1;
1N/A break;
1N/A }
1N/A
1N/A if (st < 0)
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: %s: line %u: key %s: put error: %s\n",
1N/A progname, mapname, lineno,
1N/A (char *) db_key.data,
1N/A sm_errstring(errno));
1N/A exitstat = EX_IOERR;
1N/A }
1N/A else if (st > 0)
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: %s: line %u: key %s: duplicate key\n",
1N/A progname, mapname,
1N/A lineno,
1N/A (char *) db_key.data);
1N/A exitstat = EX_DATAERR;
1N/A }
1N/A }
1N/A }
1N/A
1N/A /*
1N/A ** Now close the database.
1N/A */
1N/A
1N/A errno = database->smdb_close(database);
1N/A if (errno != SMDBE_OK)
1N/A {
1N/A (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1N/A "%s: close(%s): %s\n",
1N/A progname, mapname, sm_errstring(errno));
1N/A exitstat = EX_IOERR;
1N/A }
1N/A smdb_free_database(database);
1N/A
1N/A exit(exitstat);
1N/A
1N/A /* NOTREACHED */
1N/A return exitstat;
1N/A}