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