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