1 /* 2 * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 */ 10 11 #include <sm/gen.h> 12 SM_RCSID("@(#)$Id: mpeix.c,v 1.6 2002/05/24 20:50:14 gshapiro Exp $") 13 14 #ifdef MPE 15 /* 16 ** MPE lacks many common functions required across all sendmail programs 17 ** so we define implementations for these functions here. 18 */ 19 20 # include <errno.h> 21 # include <fcntl.h> 22 # include <limits.h> 23 # include <mpe.h> 24 # include <netinet/in.h> 25 # include <pwd.h> 26 # include <sys/socket.h> 27 # include <sys/stat.h> 28 # include <unistd.h> 29 # include <sm/conf.h> 30 31 /* 32 ** CHROOT -- dummy chroot() function 33 ** 34 ** The MPE documentation for sendmail says that chroot-based 35 ** functionality is not implemented because MPE lacks chroot. But 36 ** rather than mucking around with all the sendmail calls to chroot, 37 ** we define this dummy function to return an ENOSYS failure just in 38 ** case a sendmail user attempts to enable chroot-based functionality. 39 ** 40 ** Parameters: 41 ** path -- pathname of new root (ignored). 42 ** 43 ** Returns: 44 ** -1 and errno == ENOSYS (function not implemented) 45 */ 46 47 int 48 chroot(path) 49 char *path; 50 { 51 errno = ENOSYS; 52 return -1; 53 } 54 55 /* 56 ** ENDPWENT -- dummy endpwent() function 57 ** 58 ** Parameters: 59 ** none 60 ** 61 ** Returns: 62 ** none 63 */ 64 65 void 66 endpwent() 67 { 68 return; 69 } 70 71 /* 72 ** In addition to missing functions, certain existing MPE functions have 73 ** slightly different semantics (or bugs) compared to normal Unix OSes. 74 ** 75 ** Here we define wrappers for these functions to make them behave in the 76 ** manner expected by sendmail. 77 */ 78 79 /* 80 ** SENDMAIL_MPE_BIND -- shadow function for the standard socket bind() 81 ** 82 ** MPE requires GETPRIVMODE() for AF_INET sockets less than port 1024. 83 ** 84 ** Parameters: 85 ** sd -- socket descriptor. 86 ** addr -- socket address. 87 ** addrlen -- length of socket address. 88 ** 89 ** Results: 90 ** 0 -- success 91 ** != 0 -- failure 92 */ 93 94 #undef bind 95 int 96 sendmail_mpe_bind(sd, addr, addrlen) 97 int sd; 98 void *addr; 99 int addrlen; 100 { 101 bool priv = false; 102 int result; 103 extern void GETPRIVMODE __P((void)); 104 extern void GETUSERMODE __P((void)); 105 106 if (addrlen == sizeof(struct sockaddr_in) && 107 ((struct sockaddr_in *)addr)->sin_family == AF_INET) 108 { 109 /* AF_INET */ 110 if (((struct sockaddr_in *)addr)->sin_port > 0 && 111 ((struct sockaddr_in *)addr)->sin_port < 1024) 112 { 113 priv = true; 114 GETPRIVMODE(); 115 } 116 ((struct sockaddr_in *)addr)->sin_addr.s_addr = 0; 117 result = bind(sd, addr, addrlen); 118 if (priv) 119 GETUSERMODE(); 120 return result; 121 } 122 123 /* AF_UNIX */ 124 return bind(sd, addr, addrlen); 125 } 126 127 /* 128 ** SENDMAIL_MPE__EXIT -- wait for children to terminate, then _exit() 129 ** 130 ** Child processes cannot survive the death of their parent on MPE, so 131 ** we must call wait() before _exit() in order to prevent this 132 ** infanticide. 133 ** 134 ** Parameters: 135 ** status -- _exit status value. 136 ** 137 ** Returns: 138 ** none. 139 */ 140 141 #undef _exit 142 void 143 sendmail_mpe__exit(status) 144 int status; 145 { 146 int result; 147 148 /* Wait for all children to terminate. */ 149 do 150 { 151 result = wait(NULL); 152 } while (result > 0 || errno == EINTR); 153 _exit(status); 154 } 155 156 /* 157 ** SENDMAIL_MPE_EXIT -- wait for children to terminate, then exit() 158 ** 159 ** Child processes cannot survive the death of their parent on MPE, so 160 ** we must call wait() before exit() in order to prevent this 161 ** infanticide. 162 ** 163 ** Parameters: 164 ** status -- exit status value. 165 ** 166 ** Returns: 167 ** none. 168 */ 169 170 #undef exit 171 void 172 sendmail_mpe_exit(status) 173 int status; 174 { 175 int result; 176 177 /* Wait for all children to terminate. */ 178 do 179 { 180 result = wait(NULL); 181 } while (result > 0 || errno == EINTR); 182 exit(status); 183 } 184 185 /* 186 ** SENDMAIL_MPE_FCNTL -- shadow function for fcntl() 187 ** 188 ** MPE requires sfcntl() for sockets, and fcntl() for everything 189 ** else. This shadow routine determines the descriptor type and 190 ** makes the appropriate call. 191 ** 192 ** Parameters: 193 ** same as fcntl(). 194 ** 195 ** Returns: 196 ** same as fcntl(). 197 */ 198 199 #undef fcntl 200 int 201 sendmail_mpe_fcntl(int fildes, int cmd, ...) 202 { 203 int len, result; 204 struct sockaddr sa; 205 206 void *arg; 207 va_list ap; 208 209 va_start(ap, cmd); 210 arg = va_arg(ap, void *); 211 va_end(ap); 212 213 len = sizeof sa; 214 if (getsockname(fildes, &sa, &len) == -1) 215 { 216 if (errno == EAFNOSUPPORT) 217 { 218 /* AF_UNIX socket */ 219 return sfcntl(fildes, cmd, arg); 220 } 221 else if (errno == ENOTSOCK) 222 { 223 /* file or pipe */ 224 return fcntl(fildes, cmd, arg); 225 } 226 227 /* unknown getsockname() failure */ 228 return (-1); 229 } 230 else 231 { 232 /* AF_INET socket */ 233 if ((result = sfcntl(fildes, cmd, arg)) != -1 && 234 cmd == F_GETFL) 235 result |= O_RDWR; /* fill in some missing flags */ 236 return result; 237 } 238 } 239 240 /* 241 ** SENDMAIL_MPE_GETPWNAM - shadow function for getpwnam() 242 ** 243 ** Several issues apply here: 244 ** 245 ** - MPE user names MUST have one '.' separator character 246 ** - MPE user names MUST be in upper case 247 ** - MPE does not initialize all fields in the passwd struct 248 ** 249 ** Parameters: 250 ** name -- username string. 251 ** 252 ** Returns: 253 ** pointer to struct passwd if found else NULL 254 */ 255 256 static char *sendmail_mpe_nullstr = ""; 257 258 #undef getpwnam 259 extern struct passwd *getpwnam(const char *); 260 261 struct passwd * 262 sendmail_mpe_getpwnam(name) 263 const char *name; 264 { 265 int dots = 0; 266 int err; 267 int i = strlen(name); 268 char *upper; 269 struct passwd *result = NULL; 270 271 if (i <= 0) 272 { 273 errno = EINVAL; 274 return result; 275 } 276 277 if ((upper = (char *)malloc(i + 1)) != NULL) 278 { 279 /* upshift the username parameter and count the dots */ 280 while (i >= 0) 281 { 282 if (name[i] == '.') 283 { 284 dots++; 285 upper[i] = '.'; 286 } 287 else 288 upper[i] = toupper(name[i]); 289 i--; 290 } 291 292 if (dots != 1) 293 { 294 /* prevent bug when dots == 0 */ 295 err = EINVAL; 296 } 297 else if ((result = getpwnam(upper)) != NULL) 298 { 299 /* init the uninitialized fields */ 300 result->pw_gecos = sendmail_mpe_nullstr; 301 result->pw_passwd = sendmail_mpe_nullstr; 302 result->pw_age = sendmail_mpe_nullstr; 303 result->pw_comment = sendmail_mpe_nullstr; 304 result->pw_audid = 0; 305 result->pw_audflg = 0; 306 } 307 err = errno; 308 free(upper); 309 } 310 errno = err; 311 return result; 312 } 313 314 /* 315 ** SENDMAIL_MPE_GETPWUID -- shadow function for getpwuid() 316 ** 317 ** Initializes the uninitalized fields in the passwd struct. 318 ** 319 ** Parameters: 320 ** uid -- uid to obtain passwd data for 321 ** 322 ** Returns: 323 ** pointer to struct passwd or NULL if not found 324 */ 325 326 #undef getpwuid 327 extern struct passwd *getpwuid __P((uid_t)); 328 329 struct passwd * 330 sendmail_mpe_getpwuid(uid) 331 uid_t uid; 332 { 333 struct passwd *result; 334 335 if ((result = getpwuid(uid)) != NULL) 336 { 337 /* initialize the uninitialized fields */ 338 result->pw_gecos = sendmail_mpe_nullstr; 339 result->pw_passwd = sendmail_mpe_nullstr; 340 result->pw_age = sendmail_mpe_nullstr; 341 result->pw_comment = sendmail_mpe_nullstr; 342 result->pw_audid = 0; 343 result->pw_audflg = 0; 344 } 345 return result; 346 } 347 348 /* 349 ** OK boys and girls, time for some serious voodoo! 350 ** 351 ** MPE does not have a complete implementation of POSIX users and groups: 352 ** 353 ** - there is no uid 0 superuser 354 ** - setuid/setgid file permission bits exist but have no-op functionality 355 ** - setgid() exists, but only supports new gid == current gid (boring!) 356 ** - setuid() forces a gid change to the new uid's primary (and only) gid 357 ** 358 ** ...all of which thoroughly annoys sendmail. 359 ** 360 ** So what to do? We can't go on an #ifdef MPE rampage throughout 361 ** sendmail, because there are only about a zillion references to uid 0 362 ** and so success (and security) would probably be rather dubious by the 363 ** time we finished. 364 ** 365 ** Instead we take the approach of defining wrapper functions for the 366 ** gid/uid management functions getegid(), geteuid(), setgid(), and 367 ** setuid() in order to implement the following model: 368 ** 369 ** - the sendmail program thinks it is a setuid-root (uid 0) program 370 ** - uid 0 is recognized as being valid, but does not grant extra powers 371 ** - MPE priv mode allows sendmail to call setuid(), not uid 0 372 ** - file access is still controlled by the real non-zero uid 373 ** - the other programs (vacation, etc) have standard MPE POSIX behavior 374 ** 375 ** This emulation model is activated by use of the program file setgid and 376 ** setuid mode bits which exist but are unused by MPE. If the setgid mode 377 ** bit is on, then gid emulation will be enabled. If the setuid mode bit is 378 ** on, then uid emulation will be enabled. So for the mail daemon, we need 379 ** to do chmod u+s,g+s /SENDMAIL/CURRENT/SENDMAIL. 380 ** 381 ** The following flags determine the current emulation state: 382 ** 383 ** true == emulation enabled 384 ** false == emulation disabled, use unmodified MPE semantics 385 */ 386 387 static bool sendmail_mpe_flaginit = false; 388 static bool sendmail_mpe_gidflag = false; 389 static bool sendmail_mpe_uidflag = false; 390 391 /* 392 ** SENDMAIL_MPE_GETMODE -- return the mode bits for the current process 393 ** 394 ** Parameters: 395 ** none. 396 ** 397 ** Returns: 398 ** file mode bits for the current process program file. 399 */ 400 401 mode_t 402 sendmail_mpe_getmode() 403 { 404 int status = 666; 405 int myprogram_length; 406 int myprogram_syntax = 2; 407 char formaldesig[28]; 408 char myprogram[PATH_MAX + 2]; 409 char path[PATH_MAX + 1]; 410 struct stat st; 411 extern HPMYPROGRAM __P((int parms, char *formaldesig, int *status, 412 int *length, char *myprogram, 413 int *myprogram_length, int *myprogram_syntax)); 414 415 myprogram_length = sizeof(myprogram); 416 HPMYPROGRAM(6, formaldesig, &status, NULL, myprogram, 417 &myprogram_length, &myprogram_syntax); 418 419 /* should not occur, do not attempt emulation */ 420 if (status != 0) 421 return 0; 422 423 memcpy(&path, &myprogram[1], myprogram_length - 2); 424 path[myprogram_length - 2] = '\0'; 425 426 /* should not occur, do not attempt emulation */ 427 if (stat(path, &st) < 0) 428 return 0; 429 430 return st.st_mode; 431 } 432 433 /* 434 ** SENDMAIL_MPE_EMULGID -- should we perform gid emulation? 435 ** 436 ** If !sendmail_mpe_flaginit then obtain the mode bits to determine 437 ** if the setgid bit is on, we want gid emulation and so set 438 ** sendmail_mpe_gidflag to true. Otherwise we do not want gid emulation 439 ** and so set sendmail_mpe_gidflag to false. 440 ** 441 ** Parameters: 442 ** none. 443 ** 444 ** Returns: 445 ** true -- perform gid emulation 446 ** false -- do not perform gid emulation 447 */ 448 449 bool 450 sendmail_mpe_emulgid() 451 { 452 if (!sendmail_mpe_flaginit) 453 { 454 mode_t mode; 455 456 mode = sendmail_mpe_getmode(); 457 sendmail_mpe_gidflag = ((mode & S_ISGID) == S_ISGID); 458 sendmail_mpe_uidflag = ((mode & S_ISUID) == S_ISUID); 459 sendmail_mpe_flaginit = true; 460 } 461 return sendmail_mpe_gidflag; 462 } 463 464 /* 465 ** SENDMAIL_MPE_EMULUID -- should we perform uid emulation? 466 ** 467 ** If sendmail_mpe_uidflag == -1 then obtain the mode bits to determine 468 ** if the setuid bit is on, we want uid emulation and so set 469 ** sendmail_mpe_uidflag to true. Otherwise we do not want uid emulation 470 ** and so set sendmail_mpe_uidflag to false. 471 ** 472 ** Parameters: 473 ** none. 474 ** 475 ** Returns: 476 ** true -- perform uid emulation 477 ** false -- do not perform uid emulation 478 */ 479 480 bool 481 sendmail_mpe_emuluid() 482 { 483 if (!sendmail_mpe_flaginit) 484 { 485 mode_t mode; 486 487 mode = sendmail_mpe_getmode(); 488 sendmail_mpe_gidflag = ((mode & S_ISGID) == S_ISGID); 489 sendmail_mpe_uidflag = ((mode & S_ISUID) == S_ISUID); 490 sendmail_mpe_flaginit = true; 491 } 492 return sendmail_mpe_uidflag; 493 } 494 495 /* 496 ** SENDMAIL_MPE_GETEGID -- shadow function for getegid() 497 ** 498 ** If emulation mode is in effect and the saved egid has been 499 ** initialized, return the saved egid; otherwise return the value of the 500 ** real getegid() function. 501 ** 502 ** Parameters: 503 ** none. 504 ** 505 ** Returns: 506 ** emulated egid if present, else true egid. 507 */ 508 509 static uid_t sendmail_mpe_egid = -1; 510 511 #undef getegid 512 gid_t 513 sendmail_mpe_getegid() 514 { 515 if (sendmail_mpe_emulgid() && sendmail_mpe_egid != -1) 516 return sendmail_mpe_egid; 517 return getegid(); 518 } 519 520 /* 521 ** SENDMAIL_MPE_GETEUID -- shadow function for geteuid() 522 ** 523 ** If emulation mode is in effect, return the saved euid; otherwise 524 ** return the value of the real geteuid() function. 525 ** 526 ** Note that the initial value of the saved euid is zero, to simulate 527 ** a setuid-root program. 528 ** 529 ** Parameters: 530 ** none 531 ** 532 ** Returns: 533 ** emulated euid if in emulation mode, else true euid. 534 */ 535 536 static uid_t sendmail_mpe_euid = 0; 537 538 #undef geteuid 539 uid_t 540 sendmail_mpe_geteuid() 541 { 542 if (sendmail_mpe_emuluid()) 543 return sendmail_mpe_euid; 544 return geteuid(); 545 } 546 547 /* 548 ** SENDMAIL_MPE_SETGID -- shadow function for setgid() 549 ** 550 ** Simulate a call to setgid() without actually calling the real 551 ** function. Implement the expected uid 0 semantics. 552 ** 553 ** Note that sendmail will also be calling setuid() which will force an 554 ** implicit real setgid() to the proper primary gid. So it doesn't matter 555 ** that we don't actually alter the real gid in this shadow function. 556 ** 557 ** Parameters: 558 ** gid -- desired gid. 559 ** 560 ** Returns: 561 ** 0 -- emulated success 562 ** -1 -- emulated failure 563 */ 564 565 #undef setgid 566 int 567 sendmail_mpe_setgid(gid) 568 gid_t gid; 569 { 570 if (sendmail_mpe_emulgid()) 571 { 572 if (gid == getgid() || sendmail_mpe_euid == 0) 573 { 574 sendmail_mpe_egid = gid; 575 return 0; 576 } 577 errno = EINVAL; 578 return -1; 579 } 580 return setgid(gid); 581 } 582 583 /* 584 ** SENDMAIL_MPE_SETUID -- shadow function for setuid() 585 ** 586 ** setuid() is broken as of MPE 7.0 in that it changes the current 587 ** working directory to be the home directory of the new uid. Thus 588 ** we must obtain the cwd and restore it after the setuid(). 589 ** 590 ** Note that expected uid 0 semantics have been added, as well as 591 ** remembering the new uid for later use by the other shadow functions. 592 ** 593 ** Parameters: 594 ** uid -- desired uid. 595 ** 596 ** Returns: 597 ** 0 -- success 598 ** -1 -- failure 599 ** 600 ** Globals: 601 ** sendmail_mpe_euid 602 */ 603 604 #undef setuid 605 int 606 sendmail_mpe_setuid(uid) 607 uid_t uid; 608 { 609 char *cwd; 610 char cwd_buf[PATH_MAX + 1]; 611 int result; 612 extern void GETPRIVMODE __P((void)); 613 extern void GETUSERMODE __P((void)); 614 615 if (sendmail_mpe_emuluid()) 616 { 617 if (uid == 0) 618 { 619 if (sendmail_mpe_euid != 0) 620 { 621 errno = EINVAL; 622 return -1; 623 } 624 sendmail_mpe_euid = 0; 625 return 0; 626 } 627 628 /* Preserve the current working directory */ 629 if ((cwd = getcwd(cwd_buf, PATH_MAX + 1)) == NULL) 630 return -1; 631 632 GETPRIVMODE(); 633 result = setuid(uid); 634 GETUSERMODE(); 635 636 /* Restore the current working directory */ 637 chdir(cwd_buf); 638 639 if (result == 0) 640 sendmail_mpe_euid = uid; 641 642 return result; 643 } 644 return setuid(uid); 645 } 646 #endif /* MPE */ 647