1 /* 2 * Copyright (c) 1998-2002, 2004 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.177 2004/08/03 23:57:24 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 uid_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\t%.*s\n", 384 (int) db_key.size, 385 (char *) db_key.data, 386 (int) db_val.size, 387 (char *)db_val.data); 388 389 } 390 (void) cursor->smdbc_close(cursor); 391 } 392 else 393 { 394 lineno = 0; 395 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf) 396 != NULL) 397 { 398 register char *p; 399 400 lineno++; 401 402 /* 403 ** Parse the line. 404 */ 405 406 p = strchr(ibuf, '\n'); 407 if (p != NULL) 408 *p = '\0'; 409 else if (!sm_io_eof(smioin)) 410 { 411 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 412 "%s: %s: line %u: line too long (%ld bytes max)\n", 413 progname, mapname, lineno, 414 (long) sizeof ibuf); 415 exitstat = EX_DATAERR; 416 continue; 417 } 418 419 if (ibuf[0] == '\0' || ibuf[0] == comment) 420 continue; 421 if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0])) 422 { 423 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 424 "%s: %s: line %u: syntax error (leading space)\n", 425 progname, mapname, lineno); 426 exitstat = EX_DATAERR; 427 continue; 428 } 429 430 memset(&db_key, '\0', sizeof db_key); 431 memset(&db_val, '\0', sizeof db_val); 432 db_key.data = ibuf; 433 434 for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++) 435 { 436 if (foldcase && isascii(*p) && isupper(*p)) 437 *p = tolower(*p); 438 } 439 db_key.size = p - ibuf; 440 if (inclnull) 441 db_key.size++; 442 443 if (*p != '\0') 444 *p++ = '\0'; 445 while (*p != '\0' && ISSEP(*p)) 446 p++; 447 if (!allowempty && *p == '\0') 448 { 449 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 450 "%s: %s: line %u: no RHS for LHS %s\n", 451 progname, mapname, lineno, 452 (char *) db_key.data); 453 exitstat = EX_DATAERR; 454 continue; 455 } 456 457 db_val.data = p; 458 db_val.size = strlen(p); 459 if (inclnull) 460 db_val.size++; 461 462 /* 463 ** Do the database insert. 464 */ 465 466 if (verbose) 467 { 468 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 469 "key=`%s', val=`%s'\n", 470 (char *) db_key.data, 471 (char *) db_val.data); 472 } 473 474 errno = database->smdb_put(database, &db_key, &db_val, 475 putflags); 476 switch (errno) 477 { 478 case SMDBE_KEY_EXIST: 479 st = 1; 480 break; 481 482 case 0: 483 st = 0; 484 break; 485 486 default: 487 st = -1; 488 break; 489 } 490 491 if (st < 0) 492 { 493 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 494 "%s: %s: line %u: key %s: put error: %s\n", 495 progname, mapname, lineno, 496 (char *) db_key.data, 497 sm_errstring(errno)); 498 exitstat = EX_IOERR; 499 } 500 else if (st > 0) 501 { 502 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 503 "%s: %s: line %u: key %s: duplicate key\n", 504 progname, mapname, 505 lineno, 506 (char *) db_key.data); 507 exitstat = EX_DATAERR; 508 } 509 } 510 } 511 512 /* 513 ** Now close the database. 514 */ 515 516 errno = database->smdb_close(database); 517 if (errno != SMDBE_OK) 518 { 519 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 520 "%s: close(%s): %s\n", 521 progname, mapname, sm_errstring(errno)); 522 exitstat = EX_IOERR; 523 } 524 smdb_free_database(database); 525 526 exit(exitstat); 527 528 /* NOTREACHED */ 529 return exitstat; 530 } 531