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