1 /* 2 * Copyright (c) 1998-2001 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-2001 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.175 2001/12/28 22:44:01 ca Exp $") 24 25 /* $FreeBSD$ */ 26 27 #include <sys/types.h> 28 #ifndef ISC_UNIX 29 # include <sys/file.h> 30 #endif /* ! ISC_UNIX */ 31 #include <ctype.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #ifdef EX_OK 35 # undef EX_OK /* unistd.h may have another use for this */ 36 #endif /* EX_OK */ 37 #include <sysexits.h> 38 #include <sendmail/sendmail.h> 39 #include <sendmail/pathnames.h> 40 #include <libsmdb/smdb.h> 41 42 uid_t RealUid; 43 gid_t RealGid; 44 char *RealUserName; 45 uid_t RunAsUid; 46 uid_t RunAsGid; 47 char *RunAsUserName; 48 int Verbose = 2; 49 bool DontInitGroups = false; 50 uid_t TrustedUid = 0; 51 BITMAP256 DontBlameSendmail; 52 53 #define BUFSIZE 1024 54 #define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep) 55 56 static void 57 usage(progname) 58 char *progname; 59 { 60 /* XXX break the usage output into multiple lines? it's too long */ 61 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 62 "Usage: %s [-C cffile] [-N] [-c cachesize] [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter] [-u] [-v] type mapname\n", 63 progname); 64 #if _FFR_COMMENT_CHAR 65 /* add -D comment-char */ 66 #endif /* _FFR_COMMENT_CHAR */ 67 exit(EX_USAGE); 68 } 69 70 int 71 main(argc, argv) 72 int argc; 73 char **argv; 74 { 75 char *progname; 76 char *cfile; 77 bool inclnull = false; 78 bool notrunc = false; 79 bool allowreplace = false; 80 bool allowempty = false; 81 bool verbose = false; 82 bool foldcase = true; 83 bool unmake = false; 84 char sep = '\0'; 85 char comment = '#'; 86 int exitstat; 87 int opt; 88 char *typename = NULL; 89 char *mapname = NULL; 90 unsigned int lineno; 91 int st; 92 int mode; 93 int smode; 94 int putflags = 0; 95 long sff = SFF_ROOTOK|SFF_REGONLY; 96 struct passwd *pw; 97 SMDB_DATABASE *database; 98 SMDB_CURSOR *cursor; 99 SMDB_DBENT db_key, db_val; 100 SMDB_DBPARAMS params; 101 SMDB_USER_INFO user_info; 102 char ibuf[BUFSIZE]; 103 #if HASFCHOWN 104 SM_FILE_T *cfp; 105 char buf[MAXLINE]; 106 #endif /* HASFCHOWN */ 107 static char rnamebuf[MAXNAME]; /* holds RealUserName */ 108 extern char *optarg; 109 extern int optind; 110 111 memset(¶ms, '\0', sizeof params); 112 params.smdbp_cache_size = 1024 * 1024; 113 114 progname = strrchr(argv[0], '/'); 115 if (progname != NULL) 116 progname++; 117 else 118 progname = argv[0]; 119 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); 120 121 clrbitmap(DontBlameSendmail); 122 RunAsUid = RealUid = getuid(); 123 RunAsGid = RealGid = getgid(); 124 pw = getpwuid(RealUid); 125 if (pw != NULL) 126 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); 127 else 128 (void) sm_snprintf(rnamebuf, sizeof rnamebuf, 129 "Unknown UID %d", (int) RealUid); 130 RunAsUserName = RealUserName = rnamebuf; 131 user_info.smdbu_id = RunAsUid; 132 user_info.smdbu_group_id = RunAsGid; 133 (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, 134 SMDB_MAX_USER_NAME_LEN); 135 136 #define OPTIONS "C:D:Nc:deflorst:uv" 137 while ((opt = getopt(argc, argv, OPTIONS)) != -1) 138 { 139 switch (opt) 140 { 141 case 'C': 142 cfile = optarg; 143 break; 144 145 case 'N': 146 inclnull = true; 147 break; 148 149 case 'c': 150 params.smdbp_cache_size = atol(optarg); 151 break; 152 153 case 'd': 154 params.smdbp_allow_dup = true; 155 break; 156 157 case 'e': 158 allowempty = true; 159 break; 160 161 case 'f': 162 foldcase = false; 163 break; 164 165 #if _FFR_COMMENT_CHAR 166 case 'D': 167 comment = *optarg; 168 break; 169 #endif /* _FFR_COMMENT_CHAR */ 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 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\t%.*s\n", 381 (int) db_key.size, 382 (char *) db_key.data, 383 (int) db_val.size, 384 (char *)db_val.data); 385 386 } 387 (void) cursor->smdbc_close(cursor); 388 } 389 else 390 { 391 lineno = 0; 392 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf) 393 != NULL) 394 { 395 register char *p; 396 397 lineno++; 398 399 /* 400 ** Parse the line. 401 */ 402 403 p = strchr(ibuf, '\n'); 404 if (p != NULL) 405 *p = '\0'; 406 else if (!sm_io_eof(smioin)) 407 { 408 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 409 "%s: %s: line %u: line too long (%ld bytes max)\n", 410 progname, mapname, lineno, 411 (long) sizeof ibuf); 412 exitstat = EX_DATAERR; 413 continue; 414 } 415 416 if (ibuf[0] == '\0' || ibuf[0] == comment) 417 continue; 418 if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0])) 419 { 420 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 421 "%s: %s: line %u: syntax error (leading space)\n", 422 progname, mapname, lineno); 423 exitstat = EX_DATAERR; 424 continue; 425 } 426 427 memset(&db_key, '\0', sizeof db_key); 428 memset(&db_val, '\0', sizeof db_val); 429 db_key.data = ibuf; 430 431 for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++) 432 { 433 if (foldcase && isascii(*p) && isupper(*p)) 434 *p = tolower(*p); 435 } 436 db_key.size = p - ibuf; 437 if (inclnull) 438 db_key.size++; 439 440 if (*p != '\0') 441 *p++ = '\0'; 442 while (*p != '\0' && ISSEP(*p)) 443 p++; 444 if (!allowempty && *p == '\0') 445 { 446 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 447 "%s: %s: line %u: no RHS for LHS %s\n", 448 progname, mapname, lineno, 449 (char *) db_key.data); 450 exitstat = EX_DATAERR; 451 continue; 452 } 453 454 db_val.data = p; 455 db_val.size = strlen(p); 456 if (inclnull) 457 db_val.size++; 458 459 /* 460 ** Do the database insert. 461 */ 462 463 if (verbose) 464 { 465 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 466 "key=`%s', val=`%s'\n", 467 (char *) db_key.data, 468 (char *) db_val.data); 469 } 470 471 errno = database->smdb_put(database, &db_key, &db_val, 472 putflags); 473 switch (errno) 474 { 475 case SMDBE_KEY_EXIST: 476 st = 1; 477 break; 478 479 case 0: 480 st = 0; 481 break; 482 483 default: 484 st = -1; 485 break; 486 } 487 488 if (st < 0) 489 { 490 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 491 "%s: %s: line %u: key %s: put error: %s\n", 492 progname, mapname, lineno, 493 (char *) db_key.data, 494 sm_errstring(errno)); 495 exitstat = EX_IOERR; 496 } 497 else if (st > 0) 498 { 499 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 500 "%s: %s: line %u: key %s: duplicate key\n", 501 progname, mapname, 502 lineno, 503 (char *) db_key.data); 504 exitstat = EX_DATAERR; 505 } 506 } 507 } 508 509 /* 510 ** Now close the database. 511 */ 512 513 errno = database->smdb_close(database); 514 if (errno != SMDBE_OK) 515 { 516 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 517 "%s: close(%s): %s\n", 518 progname, mapname, sm_errstring(errno)); 519 exitstat = EX_IOERR; 520 } 521 smdb_free_database(database); 522 523 exit(exitstat); 524 525 /* NOTREACHED */ 526 return exitstat; 527 } 528