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 #include <sm/gen.h> 15 16 SM_IDSTR(copyright, 17 "@(#) Copyright (c) 1998-2002, 2004 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.179 2008/04/14 02:06:16 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 /* Find TrustedUser value in sendmail.cf */ 238 if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, 239 NULL)) == NULL) 240 { 241 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s", 242 cfile, sm_errstring(errno)); 243 exit(EX_NOINPUT); 244 } 245 while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) 246 { 247 register char *b; 248 249 if ((b = strchr(buf, '\n')) != NULL) 250 *b = '\0'; 251 252 b = buf; 253 switch (*b++) 254 { 255 case 'O': /* option */ 256 if (strncasecmp(b, " TrustedUser", 12) == 0 && 257 !(isascii(b[12]) && isalnum(b[12]))) 258 { 259 b = strchr(b, '='); 260 if (b == NULL) 261 continue; 262 while (isascii(*++b) && isspace(*b)) 263 continue; 264 if (isascii(*b) && isdigit(*b)) 265 TrustedUid = atoi(b); 266 else 267 { 268 TrustedUid = 0; 269 pw = getpwnam(b); 270 if (pw == NULL) 271 (void) sm_io_fprintf(smioerr, 272 SM_TIME_DEFAULT, 273 "TrustedUser: unknown user %s\n", b); 274 else 275 TrustedUid = pw->pw_uid; 276 } 277 278 # ifdef UID_MAX 279 if (TrustedUid > UID_MAX) 280 { 281 (void) sm_io_fprintf(smioerr, 282 SM_TIME_DEFAULT, 283 "TrustedUser: uid value (%ld) > UID_MAX (%ld)", 284 (long) TrustedUid, 285 (long) UID_MAX); 286 TrustedUid = 0; 287 } 288 # endif /* UID_MAX */ 289 break; 290 } 291 292 293 default: 294 continue; 295 } 296 } 297 (void) sm_io_close(cfp, SM_TIME_DEFAULT); 298 #endif /* HASFCHOWN */ 299 300 if (!params.smdbp_allow_dup && !allowreplace) 301 putflags = SMDBF_NO_OVERWRITE; 302 303 if (unmake) 304 { 305 mode = O_RDONLY; 306 smode = S_IRUSR; 307 } 308 else 309 { 310 mode = O_RDWR; 311 if (!notrunc) 312 { 313 mode |= O_CREAT|O_TRUNC; 314 sff |= SFF_CREAT; 315 } 316 smode = S_IWUSR; 317 } 318 319 params.smdbp_num_elements = 4096; 320 321 errno = smdb_open_database(&database, mapname, mode, smode, sff, 322 typename, &user_info, ¶ms); 323 if (errno != SMDBE_OK) 324 { 325 char *hint; 326 327 if (errno == SMDBE_UNSUPPORTED_DB_TYPE && 328 (hint = smdb_db_definition(typename)) != NULL) 329 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 330 "%s: Need to recompile with -D%s for %s support\n", 331 progname, hint, typename); 332 else 333 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 334 "%s: error opening type %s map %s: %s\n", 335 progname, typename, mapname, 336 sm_errstring(errno)); 337 exit(EX_CANTCREAT); 338 } 339 340 (void) database->smdb_sync(database, 0); 341 342 if (!unmake && geteuid() == 0 && TrustedUid != 0) 343 { 344 errno = database->smdb_set_owner(database, TrustedUid, -1); 345 if (errno != SMDBE_OK) 346 { 347 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 348 "WARNING: ownership change on %s failed %s", 349 mapname, sm_errstring(errno)); 350 } 351 } 352 353 /* 354 ** Copy the data 355 */ 356 357 exitstat = EX_OK; 358 if (unmake) 359 { 360 errno = database->smdb_cursor(database, &cursor, 0); 361 if (errno != SMDBE_OK) 362 { 363 364 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 365 "%s: cannot make cursor for type %s map %s\n", 366 progname, typename, mapname); 367 exit(EX_SOFTWARE); 368 } 369 370 memset(&db_key, '\0', sizeof db_key); 371 memset(&db_val, '\0', sizeof db_val); 372 373 for (lineno = 0; ; lineno++) 374 { 375 errno = cursor->smdbc_get(cursor, &db_key, &db_val, 376 SMDB_CURSOR_GET_NEXT); 377 if (errno != SMDBE_OK) 378 break; 379 380 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 381 "%.*s%c%.*s\n", 382 (int) db_key.size, 383 (char *) db_key.data, 384 (sep != '\0') ? sep : '\t', 385 (int) db_val.size, 386 (char *)db_val.data); 387 388 } 389 (void) cursor->smdbc_close(cursor); 390 } 391 else 392 { 393 lineno = 0; 394 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf) 395 != NULL) 396 { 397 register char *p; 398 399 lineno++; 400 401 /* 402 ** Parse the line. 403 */ 404 405 p = strchr(ibuf, '\n'); 406 if (p != NULL) 407 *p = '\0'; 408 else if (!sm_io_eof(smioin)) 409 { 410 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 411 "%s: %s: line %u: line too long (%ld bytes max)\n", 412 progname, mapname, lineno, 413 (long) sizeof ibuf); 414 exitstat = EX_DATAERR; 415 continue; 416 } 417 418 if (ibuf[0] == '\0' || ibuf[0] == comment) 419 continue; 420 if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0])) 421 { 422 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 423 "%s: %s: line %u: syntax error (leading space)\n", 424 progname, mapname, lineno); 425 exitstat = EX_DATAERR; 426 continue; 427 } 428 429 memset(&db_key, '\0', sizeof db_key); 430 memset(&db_val, '\0', sizeof db_val); 431 db_key.data = ibuf; 432 433 for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++) 434 { 435 if (foldcase && isascii(*p) && isupper(*p)) 436 *p = tolower(*p); 437 } 438 db_key.size = p - ibuf; 439 if (inclnull) 440 db_key.size++; 441 442 if (*p != '\0') 443 *p++ = '\0'; 444 while (*p != '\0' && ISSEP(*p)) 445 p++; 446 if (!allowempty && *p == '\0') 447 { 448 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 449 "%s: %s: line %u: no RHS for LHS %s\n", 450 progname, mapname, lineno, 451 (char *) db_key.data); 452 exitstat = EX_DATAERR; 453 continue; 454 } 455 456 db_val.data = p; 457 db_val.size = strlen(p); 458 if (inclnull) 459 db_val.size++; 460 461 /* 462 ** Do the database insert. 463 */ 464 465 if (verbose) 466 { 467 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 468 "key=`%s', val=`%s'\n", 469 (char *) db_key.data, 470 (char *) db_val.data); 471 } 472 473 errno = database->smdb_put(database, &db_key, &db_val, 474 putflags); 475 switch (errno) 476 { 477 case SMDBE_KEY_EXIST: 478 st = 1; 479 break; 480 481 case 0: 482 st = 0; 483 break; 484 485 default: 486 st = -1; 487 break; 488 } 489 490 if (st < 0) 491 { 492 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 493 "%s: %s: line %u: key %s: put error: %s\n", 494 progname, mapname, lineno, 495 (char *) db_key.data, 496 sm_errstring(errno)); 497 exitstat = EX_IOERR; 498 } 499 else if (st > 0) 500 { 501 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 502 "%s: %s: line %u: key %s: duplicate key\n", 503 progname, mapname, 504 lineno, 505 (char *) db_key.data); 506 exitstat = EX_DATAERR; 507 } 508 } 509 } 510 511 /* 512 ** Now close the database. 513 */ 514 515 errno = database->smdb_close(database); 516 if (errno != SMDBE_OK) 517 { 518 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 519 "%s: close(%s): %s\n", 520 progname, mapname, sm_errstring(errno)); 521 exitstat = EX_IOERR; 522 } 523 smdb_free_database(database); 524 525 exit(exitstat); 526 527 /* NOTREACHED */ 528 return exitstat; 529 } 530