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