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 26 /* Copyright (c) 1983-1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 4.3 BSD 31 * under license from the Regents of the University of California. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/ioctl.h> 36 #include <sys/param.h> 37 #include <sys/socket.h> 38 #include <sys/time.h> 39 #include <sys/filio.h> 40 41 #include <netinet/in.h> 42 #include <arpa/inet.h> 43 44 #include <unistd.h> 45 #include <string.h> 46 #include <stdlib.h> 47 #include <stdio.h> 48 #include <stdarg.h> 49 #include <errno.h> 50 #include <pwd.h> 51 #include <grp.h> 52 #include <signal.h> 53 #include <netdb.h> 54 #include <syslog.h> 55 #include <nss_dbdefs.h> 56 #include <security/pam_appl.h> 57 #include <deflt.h> 58 59 #ifdef SYSV 60 #include <shadow.h> 61 #endif /* SYSV */ 62 63 #ifndef NCARGS 64 #define NCARGS 5120 65 #endif /* NCARGS */ 66 67 #ifdef SYSV 68 #define rindex strrchr 69 #define killpg(a, b) kill(-(a), (b)) 70 #else 71 char *sprintf(); 72 #endif /* SYSV */ 73 74 #define MAXFD(A, B) ((A) > (B) ? (A) : (B)) 75 #define _PATH_DEFAULT_LOGIN "/etc/default/login" 76 77 static void error(char *fmt, ...); 78 static void doit(int f, struct sockaddr_storage *fromp); 79 static void getstr(char *buf, int cnt, char *err); 80 81 static int legalenvvar(char *s); 82 83 /* Function decls. for functions not in any header file. (Grrrr.) */ 84 extern int audit_rexecd_setup(void); 85 extern int audit_rexecd_success(char *, char *, char *); 86 extern int audit_rexecd_fail(char *, char *, char *, char *); 87 extern int audit_settid(int); /* set termnal ID */ 88 89 /* PAM conversation function */ 90 static int rexec_conv(int, struct pam_message **, 91 struct pam_response **, void *); 92 93 static pam_handle_t *pamh; /* authentication handle */ 94 static struct pam_conv conv = { 95 rexec_conv, 96 NULL 97 }; 98 99 /* 100 * remote execute server: 101 * username\0 102 * password\0 103 * command\0 104 * data 105 * 106 * in.rexecd has been modified to run as the user invoking it. Hence there is no 107 * need to limit any privileges. 108 */ 109 /*ARGSUSED*/ 110 int 111 main(int argc, char **argv) 112 { 113 struct sockaddr_storage from; 114 socklen_t fromlen; 115 116 openlog("rexec", LOG_PID | LOG_ODELAY, LOG_DAEMON); 117 (void) audit_rexecd_setup(); /* BSM */ 118 fromlen = (socklen_t)sizeof (from); 119 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 120 (void) fprintf(stderr, "%s: ", argv[0]); 121 perror("getpeername"); 122 exit(1); 123 } 124 125 if (audit_settid(0) != 0) { 126 perror("settid"); 127 exit(1); 128 } 129 130 doit(0, &from); 131 return (0); 132 } 133 134 static char username[20] = "USER="; 135 static char homedir[64] = "HOME="; 136 static char shell[64] = "SHELL="; 137 138 static char *envinit[] = 139 #ifdef SYSV 140 {homedir, shell, (char *)0, username, 141 (char *)0, (char *)0, (char *)0, (char *)0, 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}; 146 #define ENVINIT_PATH 2 /* position of PATH in envinit[] */ 147 #define PAM_ENV_ELIM 16 /* max PAM environment variables */ 148 149 /* 150 * See PSARC opinion 1992/025 151 */ 152 static char userpath[] = "PATH=/usr/bin:"; 153 static char rootpath[] = "PATH=/usr/sbin:/usr/bin"; 154 #else 155 {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0}; 156 #endif /* SYSV */ 157 158 static struct sockaddr_storage asin; 159 static char pass[16]; 160 161 static void 162 doit(int f, struct sockaddr_storage *fromp) 163 { 164 char cmdbuf[NCARGS+1], *cp; 165 char user[16]; 166 char hostname [MAXHOSTNAMELEN + 1]; 167 struct passwd *pwd, pw_data; 168 char pwdbuf[NSS_BUFLEN_PASSWD]; 169 int s; 170 ushort_t port; 171 pid_t pid; 172 int pv[2], cc; 173 fd_set readfrom, ready; 174 char buf[BUFSIZ], sig; 175 int one = 1; 176 int idx = 0, end_env = 0; 177 char **pam_env; 178 int status = PAM_AUTH_ERR; 179 char abuf[INET6_ADDRSTRLEN]; 180 struct in_addr v4dst; 181 socklen_t fromplen; 182 struct sockaddr_in *sin; 183 struct sockaddr_in6 *sin6; 184 int pam_flags = 0; 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 (defopen(_PATH_DEFAULT_LOGIN) == 0) { 282 int flags; 283 char *p; 284 flags = defcntl(DC_GETFLAGS, 0); 285 TURNOFF(flags, DC_CASE); 286 (void) defcntl(DC_SETFLAGS, flags); 287 if ((p = defread("PASSREQ=")) != NULL && 288 strcasecmp(p, "YES") == 0) { 289 pam_flags |= PAM_DISALLOW_NULL_AUTHTOK; 290 } 291 defopen(NULL); 292 } 293 294 if (pam_start("rexec", user, &conv, &pamh) != PAM_SUCCESS) { 295 exit(1); 296 } 297 if (pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS) { 298 exit(1); 299 } 300 301 if ((status = pam_authenticate(pamh, pam_flags)) != PAM_SUCCESS) { 302 switch (status) { 303 case PAM_USER_UNKNOWN: 304 (void) audit_rexecd_fail("Login incorrect", hostname, 305 user, cmdbuf); /* BSM */ 306 error("Login incorrect.\n"); 307 break; 308 default: 309 (void) audit_rexecd_fail("Password incorrect", hostname, 310 user, cmdbuf); /* BSM */ 311 error("Password incorrect.\n"); 312 } 313 pam_end(pamh, status); 314 exit(1); 315 } 316 if ((status = pam_acct_mgmt(pamh, pam_flags)) != PAM_SUCCESS) { 317 (void) audit_rexecd_fail("Account or Password Expired", 318 hostname, user, cmdbuf); 319 switch (status) { 320 case PAM_NEW_AUTHTOK_REQD: 321 error("Password Expired.\n"); 322 break; 323 case PAM_PERM_DENIED: 324 error("Account Expired.\n"); 325 break; 326 case PAM_AUTHTOK_EXPIRED: 327 error("Password Expired.\n"); 328 break; 329 default: 330 error("Login incorrect.\n"); 331 break; 332 } 333 pam_end(pamh, status); 334 exit(1); 335 } 336 337 (void) write(2, "\0", 1); 338 339 if (setgid((gid_t)pwd->pw_gid) < 0) { 340 (void) audit_rexecd_fail("Can't setgid", hostname, 341 user, cmdbuf); /* BSM */ 342 error("setgid"); 343 pam_end(pamh, PAM_ABORT); 344 exit(1); 345 } 346 (void) initgroups(pwd->pw_name, pwd->pw_gid); 347 348 if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 349 (void) audit_rexecd_fail("Unable to establish credentials", 350 hostname, user, cmdbuf); /* BSM */ 351 error("Unable to establish credentials.\n"); 352 pam_end(pamh, PAM_SUCCESS); 353 } 354 355 (void) audit_rexecd_success(hostname, user, cmdbuf); /* BSM */ 356 357 if (setuid((uid_t)pwd->pw_uid) < 0) { 358 (void) audit_rexecd_fail("Can't setuid", hostname, 359 user, cmdbuf); /* BSM */ 360 error("setuid"); 361 pam_end(pamh, PAM_ABORT); 362 exit(1); 363 } 364 365 366 if (port) { 367 (void) pipe(pv); 368 pid = fork(); 369 if (pid == (pid_t)-1) { 370 error("Try again.\n"); 371 pam_end(pamh, PAM_ABORT); 372 exit(1); 373 } 374 if (pid) { 375 /* 376 * since the daemon is running as the user no need 377 * to prune privileges. 378 */ 379 (void) close(0); (void) close(1); (void) close(2); 380 (void) close(f); (void) close(pv[1]); 381 FD_ZERO(&readfrom); 382 FD_SET(s, &readfrom); 383 FD_SET(pv[0], &readfrom); 384 (void) ioctl(pv[0], FIONBIO, (char *)&one); 385 /* should set s nbio! */ 386 do { 387 ready = readfrom; 388 if (select(MAXFD(s, pv[0])+1, &ready, NULL, 389 NULL, NULL) < 0) { 390 perror("select:"); 391 exit(1); 392 } 393 if (FD_ISSET(s, &ready)) { 394 if (read(s, &sig, 1) <= 0) 395 FD_CLR(s, &readfrom); 396 else 397 (void) killpg(pid, sig); 398 } 399 if (FD_ISSET(pv[0], &ready)) { 400 cc = read(pv[0], buf, sizeof (buf)); 401 if (cc <= 0) { 402 (void) shutdown(s, 1+1); 403 FD_CLR(pv[0], &readfrom); 404 } else 405 (void) write(s, buf, cc); 406 } 407 } while (FD_ISSET(s, &readfrom) || 408 FD_ISSET(pv[0], &readfrom)); 409 exit(0); 410 } 411 /* setpgrp(0, getpid()); */ 412 (void) setsid(); /* Should be the same as above. */ 413 (void) close(s); (void)close(pv[0]); 414 (void) dup2(pv[1], 2); 415 } 416 417 if (*pwd->pw_shell == '\0') 418 pwd->pw_shell = "/bin/sh"; 419 if (f > 2) 420 (void) close(f); 421 /* Change directory only after becoming the appropriate user. */ 422 if (chdir(pwd->pw_dir) < 0) { 423 error("No remote directory.\n"); 424 pam_end(pamh, PAM_ABORT); 425 exit(1); 426 } 427 #ifdef SYSV 428 if (pwd->pw_uid) 429 envinit[ENVINIT_PATH] = userpath; 430 else 431 envinit[ENVINIT_PATH] = rootpath; 432 #endif /* SYSV */ 433 (void) strncat(homedir, pwd->pw_dir, sizeof (homedir) - 6); 434 (void) strncat(shell, pwd->pw_shell, sizeof (shell) - 7); 435 (void) strncat(username, pwd->pw_name, sizeof (username) - 6); 436 437 /* 438 * add PAM environment variables set by modules 439 * -- only allowed 16 (PAM_ENV_ELIM) 440 * -- check to see if the environment variable is legal 441 */ 442 for (end_env = 0; envinit[end_env] != 0; end_env++) 443 ; 444 if ((pam_env = pam_getenvlist(pamh)) != 0) { 445 while (pam_env[idx] != 0) { 446 if (idx < PAM_ENV_ELIM && 447 legalenvvar(pam_env[idx])) { 448 envinit[end_env + idx] = pam_env[idx]; 449 } 450 idx++; 451 } 452 } 453 454 pam_end(pamh, PAM_SUCCESS); 455 456 cp = rindex(pwd->pw_shell, '/'); 457 if (cp) 458 cp++; 459 else 460 cp = pwd->pw_shell; 461 (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, (char *)0, envinit); 462 perror(pwd->pw_shell); 463 exit(1); 464 } 465 466 static void 467 getstr(char *buf, int cnt, char *err) 468 { 469 char c; 470 471 do { 472 if (read(0, &c, 1) != 1) 473 exit(1); 474 *buf++ = c; 475 if (--cnt == 0) { 476 error("%s too long\n", err); 477 exit(1); 478 } 479 } while (c != 0); 480 } 481 482 static void 483 error(char *fmt, ...) 484 { 485 va_list ap; 486 char buf[BUFSIZ]; 487 488 buf[0] = 1; 489 va_start(ap, fmt); 490 (void) vsprintf(buf+1, fmt, ap); 491 va_end(ap); 492 (void) write(2, buf, strlen(buf)); 493 } 494 495 static char *illegal[] = { 496 "SHELL=", 497 "HOME=", 498 "LOGNAME=", 499 #ifndef NO_MAIL 500 "MAIL=", 501 #endif 502 "CDPATH=", 503 "IFS=", 504 "PATH=", 505 "USER=", 506 0 507 }; 508 509 /* 510 * legalenvvar - can PAM insert this environmental variable? 511 */ 512 513 static int 514 legalenvvar(char *s) 515 { 516 register char **p; 517 518 for (p = illegal; *p; p++) 519 if (strncmp(s, *p, strlen(*p)) == 0) 520 return (0); 521 522 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_') 523 return (0); 524 525 return (1); 526 } 527 528 /* 529 * rexec_conv - This is the conv (conversation) function called from 530 * a PAM authentication module to print error messages 531 * or garner information from the user. 532 */ 533 534 /* ARGSUSED3 */ 535 static int 536 rexec_conv(int num_msg, struct pam_message **msg, 537 struct pam_response **response, void *appdata_ptr) 538 { 539 struct pam_message *m; 540 struct pam_response *r; 541 int i; 542 543 if (num_msg <= 0) 544 return (PAM_CONV_ERR); 545 546 *response = calloc(num_msg, sizeof (struct pam_response)); 547 if (*response == NULL) 548 return (PAM_BUF_ERR); 549 550 m = *msg; 551 r = *response; 552 553 if (m->msg_style == PAM_PROMPT_ECHO_OFF) { 554 if (pass[0] != '\0') { 555 r->resp = strdup(pass); 556 if (r->resp == NULL) { 557 /* free responses */ 558 r = *response; 559 for (i = 0; i < num_msg; i++, r++) { 560 if (r->resp) 561 free(r->resp); 562 } 563 free(*response); 564 *response = NULL; 565 return (PAM_BUF_ERR); 566 } 567 } 568 } 569 570 return (PAM_SUCCESS); 571 } 572