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