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