1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association. 26 */ 27 28 /* Copyright (c) 1983-1989 AT&T */ 29 /* All Rights Reserved */ 30 31 /* 32 * Portions of this source code were derived from Berkeley 4.3 BSD 33 * under license from the Regents of the University of California. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/ioctl.h> 38 #include <sys/param.h> 39 #include <sys/socket.h> 40 #include <sys/time.h> 41 #include <sys/filio.h> 42 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 46 #include <unistd.h> 47 #include <string.h> 48 #include <stdlib.h> 49 #include <stdio.h> 50 #include <stdarg.h> 51 #include <errno.h> 52 #include <pwd.h> 53 #include <grp.h> 54 #include <signal.h> 55 #include <netdb.h> 56 #include <syslog.h> 57 #include <nss_dbdefs.h> 58 #include <security/pam_appl.h> 59 #include <deflt.h> 60 61 #ifdef SYSV 62 #include <shadow.h> 63 #endif /* SYSV */ 64 65 #ifndef NCARGS 66 #define NCARGS 5120 67 #endif /* NCARGS */ 68 69 #ifdef SYSV 70 #define rindex strrchr 71 #define killpg(a, b) kill(-(a), (b)) 72 #else 73 char *sprintf(); 74 #endif /* SYSV */ 75 76 #define MAXFD(A, B) ((A) > (B) ? (A) : (B)) 77 #define _PATH_DEFAULT_LOGIN "/etc/default/login" 78 79 static void error(char *fmt, ...); 80 static void doit(int f, struct sockaddr_storage *fromp); 81 static void getstr(char *buf, int cnt, char *err); 82 83 static int legalenvvar(char *s); 84 85 /* Function decls. for functions not in any header file. (Grrrr.) */ 86 extern int audit_rexecd_setup(void); 87 extern int audit_rexecd_success(char *, char *, char *); 88 extern int audit_rexecd_fail(char *, char *, char *, char *); 89 extern int audit_settid(int); /* set termnal ID */ 90 91 /* PAM conversation function */ 92 static int rexec_conv(int, const struct pam_message **, 93 struct pam_response **, void *); 94 95 static pam_handle_t *pamh; /* authentication handle */ 96 static struct pam_conv conv = { 97 rexec_conv, 98 NULL 99 }; 100 101 /* 102 * remote execute server: 103 * username\0 104 * password\0 105 * command\0 106 * data 107 * 108 * in.rexecd has been modified to run as the user invoking it. Hence there is no 109 * need to limit any privileges. 110 */ 111 /*ARGSUSED*/ 112 int 113 main(int argc, char **argv) 114 { 115 struct sockaddr_storage from; 116 socklen_t fromlen; 117 118 openlog("rexec", LOG_PID | LOG_ODELAY, LOG_DAEMON); 119 (void) audit_rexecd_setup(); /* BSM */ 120 fromlen = (socklen_t)sizeof (from); 121 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 122 (void) fprintf(stderr, "%s: ", argv[0]); 123 perror("getpeername"); 124 exit(1); 125 } 126 127 if (audit_settid(0) != 0) { 128 perror("settid"); 129 exit(1); 130 } 131 132 doit(0, &from); 133 return (0); 134 } 135 136 static char username[20] = "USER="; 137 static char homedir[64] = "HOME="; 138 static char shell[64] = "SHELL="; 139 140 static char *envinit[] = 141 #ifdef SYSV 142 {homedir, shell, (char *)0, username, 143 (char *)0, (char *)0, (char *)0, (char *)0, 144 (char *)0, (char *)0, (char *)0, (char *)0, 145 (char *)0, (char *)0, (char *)0, (char *)0, 146 (char *)0, (char *)0, (char *)0, (char *)0, 147 (char *)0}; 148 #define ENVINIT_PATH 2 /* position of PATH in envinit[] */ 149 #define PAM_ENV_ELIM 16 /* max PAM environment variables */ 150 151 /* 152 * See PSARC opinion 1992/025 153 */ 154 static char userpath[] = "PATH=/usr/bin:"; 155 static char rootpath[] = "PATH=/usr/sbin:/usr/bin"; 156 #else 157 {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0}; 158 #endif /* SYSV */ 159 160 static struct sockaddr_storage asin; 161 static char pass[16]; 162 163 static void 164 doit(int f, struct sockaddr_storage *fromp) 165 { 166 char cmdbuf[NCARGS+1], *cp; 167 char user[16]; 168 char hostname [MAXHOSTNAMELEN + 1]; 169 struct passwd *pwd, pw_data; 170 char pwdbuf[NSS_BUFLEN_PASSWD]; 171 int s; 172 ushort_t port; 173 pid_t pid; 174 int pv[2], cc; 175 fd_set readfrom, ready; 176 char buf[BUFSIZ], sig; 177 int one = 1; 178 int idx = 0, end_env = 0; 179 char **pam_env; 180 int status = PAM_AUTH_ERR; 181 char abuf[INET6_ADDRSTRLEN]; 182 struct in_addr v4dst; 183 socklen_t fromplen; 184 struct sockaddr_in *sin; 185 struct sockaddr_in6 *sin6; 186 int pam_flags = 0; 187 188 (void) signal(SIGINT, SIG_DFL); 189 (void) signal(SIGQUIT, SIG_DFL); 190 (void) signal(SIGTERM, SIG_DFL); 191 #ifdef DEBUG 192 { 193 int t = open("/dev/tty", 2); 194 if (t >= 0) { 195 #ifdef SYSV 196 (void) setsid(); 197 #else 198 (void) ioctl(t, TIOCNOTTY, (char *)0); 199 #endif /* SYSV */ 200 (void) close(t); 201 } 202 } 203 #endif 204 if (fromp->ss_family == AF_INET) { 205 sin = (struct sockaddr_in *)fromp; 206 fromplen = sizeof (struct sockaddr_in); 207 asin.ss_family = AF_INET; /* used for bind */ 208 } else if (fromp->ss_family == AF_INET6) { 209 sin6 = (struct sockaddr_in6 *)fromp; 210 fromplen = sizeof (struct sockaddr_in6); 211 asin.ss_family = AF_INET6; /* used for bind */ 212 } else { 213 syslog(LOG_ERR, "unknown address family %d\n", 214 fromp->ss_family); 215 exit(1); 216 } 217 /* 218 * store common info. for audit record 219 */ 220 221 if (getnameinfo((const struct sockaddr *) fromp, fromplen, hostname, 222 sizeof (hostname), NULL, 0, 0) != 0) { 223 if (fromp->ss_family == AF_INET6) { 224 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 225 struct in_addr ipv4_addr; 226 227 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, 228 &ipv4_addr); 229 inet_ntop(AF_INET, &ipv4_addr, abuf, 230 sizeof (abuf)); 231 } else { 232 inet_ntop(AF_INET6, &sin6->sin6_addr, 233 abuf, sizeof (abuf)); 234 } 235 } else if (fromp->ss_family == AF_INET) { 236 inet_ntop(AF_INET, &sin->sin_addr, 237 abuf, sizeof (abuf)); 238 } 239 (void) strncpy(hostname, abuf, sizeof (hostname)); 240 } 241 (void) dup2(f, 0); 242 (void) dup2(f, 1); 243 (void) dup2(f, 2); 244 (void) alarm(60); 245 port = 0; 246 for (;;) { 247 char c; 248 if (read(f, &c, 1) != 1) 249 exit(1); 250 if (c == 0) 251 break; 252 port = port * 10 + c - '0'; 253 } 254 (void) alarm(0); 255 if (port != 0) { 256 s = socket(fromp->ss_family, SOCK_STREAM, 0); 257 if (s < 0) 258 exit(1); 259 if (bind(s, (struct sockaddr *)&asin, fromplen) < 0) 260 exit(1); 261 (void) alarm(60); 262 if (fromp->ss_family == AF_INET) { 263 sin->sin_port = htons((ushort_t)port); 264 } else if (fromp->ss_family == AF_INET6) { 265 sin6->sin6_port = htons((ushort_t)port); 266 } 267 if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) 268 exit(1); 269 (void) alarm(0); 270 } 271 getstr(user, sizeof (user), "username"); 272 getstr(pass, sizeof (pass), "password"); 273 getstr(cmdbuf, sizeof (cmdbuf), "command"); 274 275 pwd = getpwnam_r(user, &pw_data, pwdbuf, sizeof (pwdbuf)); 276 if (pwd == NULL) { 277 (void) audit_rexecd_fail("Login incorrect", hostname, user, 278 cmdbuf); /* BSM */ 279 error("Login incorrect.\n"); 280 exit(1); 281 } 282 283 if (defopen(_PATH_DEFAULT_LOGIN) == 0) { 284 int flags; 285 char *p; 286 flags = defcntl(DC_GETFLAGS, 0); 287 TURNOFF(flags, DC_CASE); 288 (void) defcntl(DC_SETFLAGS, flags); 289 if ((p = defread("PASSREQ=")) != NULL && 290 strcasecmp(p, "YES") == 0) { 291 pam_flags |= PAM_DISALLOW_NULL_AUTHTOK; 292 } 293 defopen(NULL); 294 } 295 296 if (pam_start("rexec", user, &conv, &pamh) != PAM_SUCCESS) { 297 exit(1); 298 } 299 if (pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS) { 300 exit(1); 301 } 302 303 if ((status = pam_authenticate(pamh, pam_flags)) != PAM_SUCCESS) { 304 switch (status) { 305 case PAM_USER_UNKNOWN: 306 (void) audit_rexecd_fail("Login incorrect", hostname, 307 user, cmdbuf); /* BSM */ 308 error("Login incorrect.\n"); 309 break; 310 default: 311 (void) audit_rexecd_fail("Password incorrect", hostname, 312 user, cmdbuf); /* BSM */ 313 error("Password incorrect.\n"); 314 } 315 pam_end(pamh, status); 316 exit(1); 317 } 318 if ((status = pam_acct_mgmt(pamh, pam_flags)) != PAM_SUCCESS) { 319 (void) audit_rexecd_fail("Account or Password Expired", 320 hostname, user, cmdbuf); 321 switch (status) { 322 case PAM_NEW_AUTHTOK_REQD: 323 error("Password Expired.\n"); 324 break; 325 case PAM_PERM_DENIED: 326 error("Account Expired.\n"); 327 break; 328 case PAM_AUTHTOK_EXPIRED: 329 error("Password Expired.\n"); 330 break; 331 default: 332 error("Login incorrect.\n"); 333 break; 334 } 335 pam_end(pamh, status); 336 exit(1); 337 } 338 339 (void) write(2, "\0", 1); 340 341 if (setgid((gid_t)pwd->pw_gid) < 0) { 342 (void) audit_rexecd_fail("Can't setgid", hostname, 343 user, cmdbuf); /* BSM */ 344 error("setgid"); 345 pam_end(pamh, PAM_ABORT); 346 exit(1); 347 } 348 (void) initgroups(pwd->pw_name, pwd->pw_gid); 349 350 if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 351 (void) audit_rexecd_fail("Unable to establish credentials", 352 hostname, user, cmdbuf); /* BSM */ 353 error("Unable to establish credentials.\n"); 354 pam_end(pamh, PAM_SUCCESS); 355 } 356 357 (void) audit_rexecd_success(hostname, user, cmdbuf); /* BSM */ 358 359 if (setuid((uid_t)pwd->pw_uid) < 0) { 360 (void) audit_rexecd_fail("Can't setuid", hostname, 361 user, cmdbuf); /* BSM */ 362 error("setuid"); 363 pam_end(pamh, PAM_ABORT); 364 exit(1); 365 } 366 367 368 if (port) { 369 (void) pipe(pv); 370 pid = fork(); 371 if (pid == (pid_t)-1) { 372 error("Try again.\n"); 373 pam_end(pamh, PAM_ABORT); 374 exit(1); 375 } 376 if (pid) { 377 /* 378 * since the daemon is running as the user no need 379 * to prune privileges. 380 */ 381 (void) close(0); (void) close(1); (void) close(2); 382 (void) close(f); (void) close(pv[1]); 383 FD_ZERO(&readfrom); 384 FD_SET(s, &readfrom); 385 FD_SET(pv[0], &readfrom); 386 (void) ioctl(pv[0], FIONBIO, (char *)&one); 387 /* should set s nbio! */ 388 do { 389 ready = readfrom; 390 if (select(MAXFD(s, pv[0])+1, &ready, NULL, 391 NULL, NULL) < 0) { 392 perror("select:"); 393 exit(1); 394 } 395 if (FD_ISSET(s, &ready)) { 396 if (read(s, &sig, 1) <= 0) 397 FD_CLR(s, &readfrom); 398 else 399 (void) killpg(pid, sig); 400 } 401 if (FD_ISSET(pv[0], &ready)) { 402 cc = read(pv[0], buf, sizeof (buf)); 403 if (cc <= 0) { 404 (void) shutdown(s, 1+1); 405 FD_CLR(pv[0], &readfrom); 406 } else 407 (void) write(s, buf, cc); 408 } 409 } while (FD_ISSET(s, &readfrom) || 410 FD_ISSET(pv[0], &readfrom)); 411 exit(0); 412 } 413 /* setpgrp(0, getpid()); */ 414 (void) setsid(); /* Should be the same as above. */ 415 (void) close(s); (void)close(pv[0]); 416 (void) dup2(pv[1], 2); 417 } 418 419 if (*pwd->pw_shell == '\0') 420 pwd->pw_shell = "/bin/sh"; 421 if (f > 2) 422 (void) close(f); 423 /* Change directory only after becoming the appropriate user. */ 424 if (chdir(pwd->pw_dir) < 0) { 425 error("No remote directory.\n"); 426 pam_end(pamh, PAM_ABORT); 427 exit(1); 428 } 429 #ifdef SYSV 430 if (pwd->pw_uid) 431 envinit[ENVINIT_PATH] = userpath; 432 else 433 envinit[ENVINIT_PATH] = rootpath; 434 #endif /* SYSV */ 435 (void) strncat(homedir, pwd->pw_dir, sizeof (homedir) - 6); 436 (void) strncat(shell, pwd->pw_shell, sizeof (shell) - 7); 437 (void) strncat(username, pwd->pw_name, sizeof (username) - 6); 438 439 /* 440 * add PAM environment variables set by modules 441 * -- only allowed 16 (PAM_ENV_ELIM) 442 * -- check to see if the environment variable is legal 443 */ 444 for (end_env = 0; envinit[end_env] != 0; end_env++) 445 ; 446 if ((pam_env = pam_getenvlist(pamh)) != 0) { 447 while (pam_env[idx] != 0) { 448 if (idx < PAM_ENV_ELIM && 449 legalenvvar(pam_env[idx])) { 450 envinit[end_env + idx] = pam_env[idx]; 451 } 452 idx++; 453 } 454 } 455 456 pam_end(pamh, PAM_SUCCESS); 457 458 cp = rindex(pwd->pw_shell, '/'); 459 if (cp) 460 cp++; 461 else 462 cp = pwd->pw_shell; 463 (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, (char *)0, envinit); 464 perror(pwd->pw_shell); 465 exit(1); 466 } 467 468 static void 469 getstr(char *buf, int cnt, char *err) 470 { 471 char c; 472 473 do { 474 if (read(0, &c, 1) != 1) 475 exit(1); 476 *buf++ = c; 477 if (--cnt == 0) { 478 error("%s too long\n", err); 479 exit(1); 480 } 481 } while (c != 0); 482 } 483 484 static void 485 error(char *fmt, ...) 486 { 487 va_list ap; 488 char buf[BUFSIZ]; 489 490 buf[0] = 1; 491 va_start(ap, fmt); 492 (void) vsprintf(buf+1, fmt, ap); 493 va_end(ap); 494 (void) write(2, buf, strlen(buf)); 495 } 496 497 static char *illegal[] = { 498 "SHELL=", 499 "HOME=", 500 "LOGNAME=", 501 #ifndef NO_MAIL 502 "MAIL=", 503 #endif 504 "CDPATH=", 505 "IFS=", 506 "PATH=", 507 "USER=", 508 0 509 }; 510 511 /* 512 * legalenvvar - can PAM insert this environmental variable? 513 */ 514 515 static int 516 legalenvvar(char *s) 517 { 518 register char **p; 519 520 for (p = illegal; *p; p++) 521 if (strncmp(s, *p, strlen(*p)) == 0) 522 return (0); 523 524 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_') 525 return (0); 526 527 return (1); 528 } 529 530 /* 531 * rexec_conv - This is the conv (conversation) function called from 532 * a PAM authentication module to print error messages 533 * or garner information from the user. 534 */ 535 536 static int 537 rexec_conv(int num_msg, const struct pam_message **msg, 538 struct pam_response **response, void *appdata_ptr) 539 { 540 const struct pam_message *m; 541 struct pam_response *r; 542 int i; 543 544 if (num_msg <= 0) 545 return (PAM_CONV_ERR); 546 547 *response = calloc(num_msg, sizeof (struct pam_response)); 548 if (*response == NULL) 549 return (PAM_BUF_ERR); 550 551 m = *msg; 552 r = *response; 553 554 if (m->msg_style == PAM_PROMPT_ECHO_OFF) { 555 if (pass[0] != '\0') { 556 r->resp = strdup(pass); 557 if (r->resp == NULL) { 558 /* free responses */ 559 r = *response; 560 for (i = 0; i < num_msg; i++, r++) { 561 if (r->resp) 562 free(r->resp); 563 } 564 free(*response); 565 *response = NULL; 566 return (PAM_BUF_ERR); 567 } 568 } 569 } 570 571 return (PAM_SUCCESS); 572 } 573