1/* 2 * Copyright (c) 2001-2002 Proofpoint, 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> 12SM_RCSID("@(#)$Id: mpeix.c,v 1.8 2013-11-22 20:51:43 ca 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 47int 48chroot(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 65void 66endpwent() 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 95int 96sendmail_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 142void 143sendmail_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 171void 172sendmail_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 200int 201sendmail_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 256static char *sendmail_mpe_nullstr = ""; 257 258#undef getpwnam 259extern struct passwd *getpwnam(const char *); 260 261struct passwd * 262sendmail_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 327extern struct passwd *getpwuid __P((uid_t)); 328 329struct passwd * 330sendmail_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 387static bool sendmail_mpe_flaginit = false; 388static bool sendmail_mpe_gidflag = false; 389static 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 401mode_t 402sendmail_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 449bool 450sendmail_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 480bool 481sendmail_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 509static gid_t sendmail_mpe_egid = -1; 510 511#undef getegid 512gid_t 513sendmail_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 536static uid_t sendmail_mpe_euid = 0; 537 538#undef geteuid 539uid_t 540sendmail_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 566int 567sendmail_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 605int 606sendmail_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