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