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 #ifndef lint 16 SM_UNUSED(static char 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 #endif /* ! lint */ 23 24 #ifndef lint 25 SM_UNUSED(static char id[]) = "@(#)$Id: editmap.c,v 1.22 2002/01/11 23:52:27 gshapiro Exp $"; 26 #endif /* ! lint */ 27 28 29 #include <sys/types.h> 30 #ifndef ISC_UNIX 31 # include <sys/file.h> 32 #endif /* ! ISC_UNIX */ 33 #include <ctype.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #ifdef EX_OK 37 # undef EX_OK /* unistd.h may have another use for this */ 38 #endif /* EX_OK */ 39 #include <sysexits.h> 40 #include <assert.h> 41 #include <sendmail/sendmail.h> 42 #include <sendmail/pathnames.h> 43 #include <libsmdb/smdb.h> 44 45 uid_t RealUid; 46 gid_t RealGid; 47 char *RealUserName; 48 uid_t RunAsUid; 49 uid_t RunAsGid; 50 char *RunAsUserName; 51 int Verbose = 2; 52 bool DontInitGroups = false; 53 uid_t TrustedUid = 0; 54 BITMAP256 DontBlameSendmail; 55 56 #define BUFSIZE 1024 57 #define ISSEP(c) (isascii(c) && isspace(c)) 58 59 60 static void 61 usage(progname) 62 char *progname; 63 { 64 fprintf(stderr, 65 "Usage: %s [-C cffile] [-N] [-f] [-q|-u|-x] maptype mapname key [ \"value ...\" ]\n", 66 progname); 67 exit(EX_USAGE); 68 } 69 70 int 71 main(argc, argv) 72 int argc; 73 char **argv; 74 { 75 char *progname; 76 char *cfile; 77 bool verbose = false; 78 bool query = false; 79 bool update = false; 80 bool remove = false; 81 bool inclnull = false; 82 bool foldcase = true; 83 unsigned int nops = 0; 84 int exitstat; 85 int opt; 86 char *typename = NULL; 87 char *mapname = NULL; 88 char *keyname = NULL; 89 char *value = NULL; 90 int mode; 91 int smode; 92 int putflags = 0; 93 long sff = SFF_ROOTOK|SFF_REGONLY; 94 struct passwd *pw; 95 SMDB_DATABASE *database; 96 SMDB_DBENT db_key, db_val; 97 SMDB_DBPARAMS params; 98 SMDB_USER_INFO user_info; 99 #if HASFCHOWN 100 FILE *cfp; 101 char buf[MAXLINE]; 102 #endif /* HASFCHOWN */ 103 static char rnamebuf[MAXNAME]; /* holds RealUserName */ 104 extern char *optarg; 105 extern int optind; 106 107 memset(¶ms, '\0', sizeof params); 108 params.smdbp_cache_size = 1024 * 1024; 109 110 progname = strrchr(argv[0], '/'); 111 if (progname != NULL) 112 progname++; 113 else 114 progname = argv[0]; 115 cfile = _PATH_SENDMAILCF; 116 117 clrbitmap(DontBlameSendmail); 118 RunAsUid = RealUid = getuid(); 119 RunAsGid = RealGid = getgid(); 120 pw = getpwuid(RealUid); 121 if (pw != NULL) 122 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); 123 else 124 (void) sm_snprintf(rnamebuf, sizeof rnamebuf, 125 "Unknown UID %d", (int) RealUid); 126 RunAsUserName = RealUserName = rnamebuf; 127 user_info.smdbu_id = RunAsUid; 128 user_info.smdbu_group_id = RunAsGid; 129 (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, 130 SMDB_MAX_USER_NAME_LEN); 131 132 #define OPTIONS "C:fquxvN" 133 while ((opt = getopt(argc, argv, OPTIONS)) != -1) 134 { 135 switch (opt) 136 { 137 case 'C': 138 cfile = optarg; 139 break; 140 141 case 'f': 142 foldcase = false; 143 break; 144 145 case 'q': 146 query = true; 147 nops++; 148 break; 149 150 case 'u': 151 update = true; 152 nops++; 153 break; 154 155 case 'x': 156 remove = true; 157 nops++; 158 break; 159 160 case 'v': 161 verbose = true; 162 break; 163 164 case 'N': 165 inclnull = true; 166 break; 167 168 default: 169 usage(progname); 170 assert(0); /* NOTREACHED */ 171 } 172 } 173 174 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 175 sff |= SFF_NOSLINK; 176 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 177 sff |= SFF_NOHLINK; 178 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 179 sff |= SFF_NOWLINK; 180 181 argc -= optind; 182 argv += optind; 183 if ((nops != 1) || 184 (query && argc != 3) || 185 (remove && argc != 3) || 186 (update && argc <= 3)) 187 { 188 usage(progname); 189 assert(0); /* NOTREACHED */ 190 } 191 192 typename = argv[0]; 193 mapname = argv[1]; 194 keyname = argv[2]; 195 if (update) 196 value = argv[3]; 197 198 if (foldcase) 199 { 200 char *p; 201 202 for (p = keyname; *p != '\0'; p++) 203 { 204 if (isascii(*p) && isupper(*p)) 205 *p = tolower(*p); 206 } 207 } 208 209 210 #if HASFCHOWN 211 /* Find TrustedUser value in sendmail.cf */ 212 if ((cfp = fopen(cfile, "r")) == NULL) 213 { 214 fprintf(stderr, "%s: %s: %s\n", progname, 215 cfile, sm_errstring(errno)); 216 exit(EX_NOINPUT); 217 } 218 while (fgets(buf, sizeof(buf), cfp) != NULL) 219 { 220 register char *b; 221 222 if ((b = strchr(buf, '\n')) != NULL) 223 *b = '\0'; 224 225 b = buf; 226 switch (*b++) 227 { 228 case 'O': /* option */ 229 if (strncasecmp(b, " TrustedUser", 12) == 0 && 230 !(isascii(b[12]) && isalnum(b[12]))) 231 { 232 b = strchr(b, '='); 233 if (b == NULL) 234 continue; 235 while (isascii(*++b) && isspace(*b)) 236 continue; 237 if (isascii(*b) && isdigit(*b)) 238 TrustedUid = atoi(b); 239 else 240 { 241 TrustedUid = 0; 242 pw = getpwnam(b); 243 if (pw == NULL) 244 fprintf(stderr, 245 "TrustedUser: unknown user %s\n", b); 246 else 247 TrustedUid = pw->pw_uid; 248 } 249 250 # ifdef UID_MAX 251 if (TrustedUid > UID_MAX) 252 { 253 fprintf(stderr, 254 "TrustedUser: uid value (%ld) > UID_MAX (%ld)", 255 (long) TrustedUid, 256 (long) UID_MAX); 257 TrustedUid = 0; 258 } 259 # endif /* UID_MAX */ 260 break; 261 } 262 263 264 default: 265 continue; 266 } 267 } 268 (void) fclose(cfp); 269 #endif /* HASFCHOWN */ 270 271 if (query) 272 { 273 mode = O_RDONLY; 274 smode = S_IRUSR; 275 } 276 else 277 { 278 mode = O_RDWR | O_CREAT; 279 sff |= SFF_CREAT|SFF_NOTEXCL; 280 smode = S_IWUSR; 281 } 282 283 params.smdbp_num_elements = 4096; 284 285 errno = smdb_open_database(&database, mapname, mode, smode, sff, 286 typename, &user_info, ¶ms); 287 if (errno != SMDBE_OK) 288 { 289 char *hint; 290 291 if (errno == SMDBE_UNSUPPORTED_DB_TYPE && 292 (hint = smdb_db_definition(typename)) != NULL) 293 fprintf(stderr, 294 "%s: Need to recompile with -D%s for %s support\n", 295 progname, hint, typename); 296 else 297 fprintf(stderr, 298 "%s: error opening type %s map %s: %s\n", 299 progname, typename, mapname, 300 sm_errstring(errno)); 301 exit(EX_CANTCREAT); 302 } 303 304 (void) database->smdb_sync(database, 0); 305 306 if (geteuid() == 0 && TrustedUid != 0) 307 { 308 errno = database->smdb_set_owner(database, TrustedUid, -1); 309 if (errno != SMDBE_OK) 310 { 311 fprintf(stderr, 312 "WARNING: ownership change on %s failed %s", 313 mapname, sm_errstring(errno)); 314 } 315 } 316 317 exitstat = EX_OK; 318 if (query) 319 { 320 memset(&db_key, '\0', sizeof db_key); 321 memset(&db_val, '\0', sizeof db_val); 322 323 db_key.data = keyname; 324 db_key.size = strlen(keyname); 325 if (inclnull) 326 db_key.size++; 327 328 errno = database->smdb_get(database, &db_key, &db_val, 0); 329 if (errno != SMDBE_OK) 330 { 331 /* XXX - Need to distinguish between not found */ 332 fprintf(stderr, 333 "%s: couldn't find key %s in map %s\n", 334 progname, keyname, mapname); 335 exitstat = EX_UNAVAILABLE; 336 } 337 else 338 { 339 printf("%.*s\n", (int) db_val.size, 340 (char *) db_val.data); 341 } 342 } 343 else if (update) 344 { 345 memset(&db_key, '\0', sizeof db_key); 346 memset(&db_val, '\0', sizeof db_val); 347 348 db_key.data = keyname; 349 db_key.size = strlen(keyname); 350 if (inclnull) 351 db_key.size++; 352 db_val.data = value; 353 db_val.size = strlen(value); 354 if (inclnull) 355 db_val.size++; 356 357 errno = database->smdb_put(database, &db_key, &db_val, 358 putflags); 359 if (errno != SMDBE_OK) 360 { 361 fprintf(stderr, 362 "%s: error updating (%s, %s) in map %s: %s\n", 363 progname, keyname, value, mapname, 364 sm_errstring(errno)); 365 exitstat = EX_IOERR; 366 } 367 } 368 else if (remove) 369 { 370 memset(&db_key, '\0', sizeof db_key); 371 memset(&db_val, '\0', sizeof db_val); 372 373 db_key.data = keyname; 374 db_key.size = strlen(keyname); 375 if (inclnull) 376 db_key.size++; 377 378 errno = database->smdb_del(database, &db_key, 0); 379 380 switch (errno) 381 { 382 case SMDBE_NOT_FOUND: 383 fprintf(stderr, 384 "%s: key %s doesn't exist in map %s\n", 385 progname, keyname, mapname); 386 /* Don't set exitstat */ 387 break; 388 case SMDBE_OK: 389 /* All's well */ 390 break; 391 default: 392 fprintf(stderr, 393 "%s: couldn't remove key %s in map %s (error)\n", 394 progname, keyname, mapname); 395 exitstat = EX_IOERR; 396 break; 397 } 398 } 399 else 400 { 401 assert(0); /* NOT REACHED */ 402 } 403 404 /* 405 ** Now close the database. 406 */ 407 408 errno = database->smdb_close(database); 409 if (errno != SMDBE_OK) 410 { 411 fprintf(stderr, "%s: close(%s): %s\n", 412 progname, mapname, sm_errstring(errno)); 413 exitstat = EX_IOERR; 414 } 415 smdb_free_database(database); 416 417 exit(exitstat); 418 /* NOTREACHED */ 419 return exitstat; 420 } 421