Home | History | Annotate | Download | only in aux
      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
     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
     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(&params, '\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, &params);
    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