1 /* $NetBSD: rshd.c,v 1.35 2005/02/20 06:11:51 christos Exp $ */ 2 3 /* 4 * Copyright (C) 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by WIDE Project and 18 * its contributors. 19 * 4. Neither the name of the project nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /*- 37 * Copyright (c) 1988, 1989, 1992, 1993, 1994 38 * The Regents of the University of California. All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 #include <sys/cdefs.h> 66 #ifndef lint 67 __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ 68 The Regents of the University of California. All rights reserved.\n"); 69 #if 0 70 static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; 71 #else 72 __RCSID("$NetBSD: rshd.c,v 1.35 2005/02/20 06:11:51 christos Exp $"); 73 #endif 74 #endif /* not lint */ 75 76 /* 77 * remote shell server: 78 * [port]\0 79 * remuser\0 80 * locuser\0 81 * command\0 82 * data 83 */ 84 #include <sys/param.h> 85 #include <sys/ioctl.h> 86 #include <sys/time.h> 87 #include <sys/socket.h> 88 89 #include <netinet/in.h> 90 #include <netinet/tcp.h> 91 #include <arpa/inet.h> 92 #include <netdb.h> 93 94 #include <errno.h> 95 #include <fcntl.h> 96 #include <paths.h> 97 #include <pwd.h> 98 #include <signal.h> 99 #include <stdio.h> 100 #include <stdlib.h> 101 #include <string.h> 102 #include <syslog.h> 103 #include <unistd.h> 104 #include <poll.h> 105 #ifdef LOGIN_CAP 106 #include <login_cap.h> 107 #endif 108 109 #ifdef USE_PAM 110 #include <security/pam_appl.h> 111 #include <security/openpam.h> 112 #include <sys/wait.h> 113 114 static struct pam_conv pamc = { openpam_nullconv, NULL }; 115 static pam_handle_t *pamh; 116 static int pam_err; 117 118 #define PAM_END { \ 119 if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \ 120 syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \ 121 pam_strerror(pamh, pam_err)); \ 122 if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \ 123 syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \ 124 pam_strerror(pamh, pam_err)); \ 125 if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \ 126 syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \ 127 pam_strerror(pamh, pam_err)); \ 128 } 129 #else 130 #define PAM_END 131 #endif 132 133 int keepalive = 1; 134 int check_all; 135 int log_success; /* If TRUE, log all successful accesses */ 136 int sent_null; 137 138 void doit(struct sockaddr *); 139 void rshd_errx(int, const char *, ...) 140 __attribute__((__noreturn__, __format__(__printf__, 2, 3))); 141 void getstr(char *, int, char *); 142 int local_domain(char *); 143 char *topdomain(char *); 144 void usage(void); 145 int main(int, char *[]); 146 147 #define OPTIONS "alnL" 148 extern int __check_rhosts_file; 149 extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ 150 static const char incorrect[] = "Login incorrect."; 151 152 int 153 main(int argc, char *argv[]) 154 { 155 struct linger linger; 156 int ch, on = 1, fromlen; 157 struct sockaddr_storage from; 158 struct protoent *proto; 159 160 openlog("rshd", LOG_PID, LOG_DAEMON); 161 162 opterr = 0; 163 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 164 switch (ch) { 165 case 'a': 166 check_all = 1; 167 break; 168 case 'l': 169 __check_rhosts_file = 0; 170 break; 171 case 'n': 172 keepalive = 0; 173 break; 174 case 'L': 175 log_success = 1; 176 break; 177 case '?': 178 default: 179 usage(); 180 break; 181 } 182 183 argc -= optind; 184 argv += optind; 185 186 fromlen = sizeof (from); /* xxx */ 187 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 188 syslog(LOG_ERR, "getpeername: %m"); 189 exit(1); 190 } 191 #if 0 192 if (((struct sockaddr *)&from)->sa_family == AF_INET6 && 193 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) && 194 sizeof(struct sockaddr_in) <= sizeof(from)) { 195 struct sockaddr_in sin; 196 struct sockaddr_in6 *sin6; 197 const int off = sizeof(struct sockaddr_in6) - 198 sizeof(struct sockaddr_in); 199 200 sin6 = (struct sockaddr_in6 *)&from; 201 memset(&sin, 0, sizeof(sin)); 202 sin.sin_family = AF_INET; 203 sin.sin_len = sizeof(struct sockaddr_in); 204 memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off], 205 sizeof(sin.sin_addr)); 206 memcpy(&from, &sin, sizeof(sin)); 207 fromlen = sin.sin_len; 208 } 209 #else 210 if (((struct sockaddr *)&from)->sa_family == AF_INET6 && 211 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) { 212 char hbuf[NI_MAXHOST]; 213 if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf, 214 sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) { 215 strlcpy(hbuf, "invalid", sizeof(hbuf)); 216 } 217 syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)", 218 hbuf); 219 exit(1); 220 } 221 #endif 222 if (keepalive && 223 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 224 sizeof(on)) < 0) 225 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 226 linger.l_onoff = 1; 227 linger.l_linger = 60; /* XXX */ 228 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 229 sizeof (linger)) < 0) 230 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 231 proto = getprotobyname("tcp"); 232 setsockopt(0, proto->p_proto, TCP_NODELAY, &on, sizeof(on)); 233 doit((struct sockaddr *)&from); 234 /* NOTREACHED */ 235 #ifdef __GNUC__ 236 exit(0); 237 #endif 238 } 239 240 char username[20] = "USER="; 241 char homedir[64] = "HOME="; 242 char shell[64] = "SHELL="; 243 char path[100] = "PATH="; 244 char *envinit[] = 245 {homedir, shell, path, username, 0}; 246 char **environ; 247 248 void 249 doit(struct sockaddr *fromp) 250 { 251 struct passwd *pwd; 252 in_port_t port; 253 struct pollfd set[2]; 254 int cc, pv[2], pid, s = -1; /* XXX gcc */ 255 int one = 1; 256 char *hostname, *errorhost = NULL; /* XXX gcc */ 257 const char *cp; 258 char sig, buf[BUFSIZ]; 259 char cmdbuf[NCARGS+1], locuser[16], remuser[16]; 260 char remotehost[2 * MAXHOSTNAMELEN + 1]; 261 char hostnamebuf[2 * MAXHOSTNAMELEN + 1]; 262 #ifdef LOGIN_CAP 263 login_cap_t *lc; 264 char *sh; 265 #endif 266 char naddr[NI_MAXHOST]; 267 char saddr[NI_MAXHOST]; 268 char raddr[NI_MAXHOST]; 269 char pbuf[NI_MAXSERV]; 270 int af = fromp->sa_family; 271 u_int16_t *portp; 272 struct addrinfo hints, *res, *res0; 273 int gaierror; 274 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 275 const char *errormsg = NULL, *errorstr = NULL; 276 277 (void) signal(SIGINT, SIG_DFL); 278 (void) signal(SIGQUIT, SIG_DFL); 279 (void) signal(SIGTERM, SIG_DFL); 280 #ifdef DEBUG 281 { int t = open(_PATH_TTY, 2); 282 if (t >= 0) { 283 ioctl(t, TIOCNOTTY, (char *)0); 284 (void) close(t); 285 } 286 } 287 #endif 288 switch (af) { 289 case AF_INET: 290 portp = &((struct sockaddr_in *)fromp)->sin_port; 291 break; 292 #ifdef INET6 293 case AF_INET6: 294 portp = &((struct sockaddr_in6 *)fromp)->sin6_port; 295 break; 296 #endif 297 default: 298 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 299 exit(1); 300 } 301 if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr), 302 pbuf, sizeof(pbuf), niflags) != 0) { 303 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 304 exit(1); 305 } 306 #ifdef IP_OPTIONS 307 if (af == AF_INET) 308 { 309 u_char optbuf[BUFSIZ/3], *cp; 310 char lbuf[BUFSIZ], *lp, *ep; 311 int optsize = sizeof(optbuf), ipproto; 312 struct protoent *ip; 313 314 if ((ip = getprotobyname("ip")) != NULL) 315 ipproto = ip->p_proto; 316 else 317 ipproto = IPPROTO_IP; 318 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && 319 optsize != 0) { 320 lp = lbuf; 321 ep = lbuf + sizeof(lbuf); 322 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 323 snprintf(lp, ep - lp, " %2.2x", *cp); 324 syslog(LOG_NOTICE, 325 "Connection received from %s using IP options (ignored):%s", 326 naddr, lbuf); 327 if (setsockopt(0, ipproto, IP_OPTIONS, 328 (char *)NULL, optsize) != 0) { 329 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 330 exit(1); 331 } 332 } 333 } 334 #endif 335 336 if (ntohs(*portp) >= IPPORT_RESERVED 337 || ntohs(*portp) < IPPORT_RESERVED/2) { 338 syslog(LOG_NOTICE|LOG_AUTH, 339 "Connection from %s on illegal port %u", 340 naddr, ntohs(*portp)); 341 exit(1); 342 } 343 344 (void) alarm(60); 345 port = 0; 346 for (;;) { 347 char c; 348 349 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { 350 if (cc < 0) 351 syslog(LOG_ERR, "read: %m"); 352 shutdown(0, SHUT_RDWR); 353 exit(1); 354 } 355 if (c == 0) 356 break; 357 port = port * 10 + c - '0'; 358 } 359 360 (void) alarm(0); 361 if (port != 0) { 362 int lport = IPPORT_RESERVED - 1; 363 s = rresvport_af(&lport, af); 364 if (s < 0) { 365 syslog(LOG_ERR, "can't get stderr port: %m"); 366 exit(1); 367 } 368 if (port >= IPPORT_RESERVED) { 369 syslog(LOG_ERR, "2nd port not reserved"); 370 exit(1); 371 } 372 *portp = htons(port); 373 if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) { 374 syslog(LOG_ERR, "connect second port %d: %m", port); 375 exit(1); 376 } 377 } 378 379 380 #ifdef notdef 381 /* from inetd, socket is already on 0, 1, 2 */ 382 dup2(f, 0); 383 dup2(f, 1); 384 dup2(f, 2); 385 #endif 386 if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr), 387 NULL, 0, NI_NAMEREQD) == 0) { 388 /* 389 * If name returned by getnameinfo is in our domain, 390 * attempt to verify that we haven't been fooled by someone 391 * in a remote net; look up the name and check that this 392 * address corresponds to the name. 393 */ 394 hostname = saddr; 395 res0 = NULL; 396 if (check_all || local_domain(saddr)) { 397 strlcpy(remotehost, saddr, sizeof(remotehost)); 398 errorhost = remotehost; 399 memset(&hints, 0, sizeof(hints)); 400 hints.ai_family = fromp->sa_family; 401 hints.ai_socktype = SOCK_STREAM; 402 hints.ai_flags = AI_CANONNAME; 403 gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0); 404 if (gaierror) { 405 syslog(LOG_NOTICE, 406 "Couldn't look up address for %s: %s", 407 remotehost, gai_strerror(gaierror)); 408 errorstr = 409 "Couldn't look up address for your host (%s)\n"; 410 hostname = naddr; 411 } else { 412 for (res = res0; res; res = res->ai_next) { 413 if (res->ai_family != fromp->sa_family) 414 continue; 415 if (res->ai_addrlen != fromp->sa_len) 416 continue; 417 if (getnameinfo(res->ai_addr, 418 res->ai_addrlen, 419 raddr, sizeof(raddr), NULL, 0, 420 niflags) == 0 421 && strcmp(naddr, raddr) == 0) { 422 hostname = res->ai_canonname 423 ? res->ai_canonname 424 : saddr; 425 break; 426 } 427 } 428 if (res == NULL) { 429 syslog(LOG_NOTICE, 430 "Host addr %s not listed for host %s", 431 naddr, res0->ai_canonname 432 ? res0->ai_canonname 433 : saddr); 434 errorstr = 435 "Host address mismatch for %s\n"; 436 hostname = naddr; 437 } 438 } 439 } 440 strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf)); 441 hostname = hostnamebuf; 442 if (res0) 443 freeaddrinfo(res0); 444 } else { 445 strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf)); 446 errorhost = hostname = hostnamebuf; 447 } 448 449 (void)alarm(60); 450 getstr(remuser, sizeof(remuser), "remuser"); 451 getstr(locuser, sizeof(locuser), "locuser"); 452 getstr(cmdbuf, sizeof(cmdbuf), "command"); 453 (void)alarm(0); 454 455 #ifdef USE_PAM 456 pam_err = pam_start("rsh", locuser, &pamc, &pamh); 457 if (pam_err != PAM_SUCCESS) { 458 syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s", 459 pam_strerror(pamh, pam_err)); 460 rshd_errx(1, incorrect); 461 } 462 463 if ((pam_err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS || 464 (pam_err = pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS)){ 465 syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s", 466 pam_strerror(pamh, pam_err)); 467 rshd_errx(1, incorrect); 468 } 469 470 pam_err = pam_authenticate(pamh, 0); 471 if (pam_err == PAM_SUCCESS) { 472 if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) { 473 strlcpy(locuser, cp, sizeof(locuser)); 474 /* XXX truncation! */ 475 } 476 pam_err = pam_acct_mgmt(pamh, 0); 477 } 478 if (pam_err != PAM_SUCCESS) { 479 errorstr = incorrect; 480 errormsg = pam_strerror(pamh, pam_err); 481 goto badlogin; 482 } 483 #endif /* USE_PAM */ 484 setpwent(); 485 pwd = getpwnam(locuser); 486 if (pwd == NULL) { 487 syslog(LOG_INFO|LOG_AUTH, 488 "%s@%s as %s: unknown login. cmd='%.80s'", 489 remuser, hostname, locuser, cmdbuf); 490 if (errorstr == NULL) 491 errorstr = "Permission denied."; 492 rshd_errx(1, errorstr, errorhost); 493 } 494 #ifdef LOGIN_CAP 495 lc = login_getclass(pwd ? pwd->pw_class : NULL); 496 #endif 497 498 if (chdir(pwd->pw_dir) < 0) { 499 if (chdir("/") < 0 500 #ifdef LOGIN_CAP 501 || login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0) 502 #endif 503 ) { 504 syslog(LOG_INFO|LOG_AUTH, 505 "%s@%s as %s: no home directory. cmd='%.80s'", 506 remuser, hostname, locuser, cmdbuf); 507 rshd_errx(0, "No remote home directory."); 508 } 509 } 510 511 #ifndef USE_PAM 512 if (errorstr || 513 (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 514 iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser, 515 locuser) < 0)) { 516 errormsg = __rcmd_errstr ? __rcmd_errstr : "unknown error"; 517 if (errorstr == NULL) 518 errorstr = "Permission denied."; 519 goto badlogin; 520 } 521 #endif 522 523 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) 524 rshd_errx(1, "Logins currently disabled."); 525 526 (void) write(STDERR_FILENO, "\0", 1); 527 sent_null = 1; 528 529 if (port) { 530 if (pipe(pv) < 0) 531 rshd_errx(1, "Can't make pipe. (%s)", strerror(errno)); 532 pid = fork(); 533 if (pid == -1) 534 rshd_errx(1, "Can't fork. (%s)", strerror(errno)); 535 if (pid) { 536 { 537 (void) close(0); 538 (void) close(1); 539 } 540 (void) close(2); 541 (void) close(pv[1]); 542 543 set[0].fd = s; 544 set[0].events = POLLIN; 545 set[1].fd = pv[0]; 546 set[1].events = POLLIN; 547 ioctl(pv[0], FIONBIO, (char *)&one); 548 549 /* should set s nbio! */ 550 do { 551 if (poll(set, 2, INFTIM) < 0) 552 break; 553 if (set[0].revents & POLLIN) { 554 int ret; 555 556 ret = read(s, &sig, 1); 557 if (ret <= 0) 558 set[0].events = 0; 559 else 560 killpg(pid, sig); 561 } 562 if (set[1].revents & POLLIN) { 563 errno = 0; 564 cc = read(pv[0], buf, sizeof(buf)); 565 if (cc <= 0) { 566 shutdown(s, SHUT_RDWR); 567 set[1].events = 0; 568 } else { 569 (void) write(s, buf, cc); 570 } 571 } 572 573 } while ((set[0].revents | set[1].revents) & POLLIN); 574 PAM_END 575 exit(0); 576 } 577 (void) close(s); 578 (void) close(pv[0]); 579 dup2(pv[1], 2); 580 close(pv[1]); 581 } 582 #ifdef USE_PAM 583 else { 584 pid = fork(); 585 if (pid == -1) 586 rshd_errx(1, "Can't fork. (%s)", strerror(errno)); 587 if (pid) { 588 /* Parent. */ 589 while (wait(NULL) > 0 || errno == EINTR) 590 continue; 591 PAM_END 592 exit(0); 593 } 594 } 595 #endif 596 597 if (*pwd->pw_shell == '\0') 598 pwd->pw_shell = _PATH_BSHELL; 599 #ifdef F_CLOSEM 600 (void)fcntl(3, F_CLOSEM, 0); 601 #else 602 for (fd = getdtablesize(); fd > 2; fd--) 603 (void)close(fd); 604 #endif 605 if (setsid() == -1) 606 syslog(LOG_ERR, "setsid() failed: %m"); 607 #ifdef USE_PAM 608 if (setlogin(pwd->pw_name) < 0) 609 syslog(LOG_ERR, "setlogin() failed: %m"); 610 611 (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1); 612 (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); 613 (void)pam_setenv(pamh, "USER", pwd->pw_name, 1); 614 (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); 615 environ = pam_getenvlist(pamh); 616 (void)pam_end(pamh, pam_err); 617 #endif 618 #ifdef LOGIN_CAP 619 if ((sh = login_getcapstr(lc, "shell", NULL, NULL))) { 620 if(!(sh = strdup(sh))) { 621 syslog(LOG_ERR, "Cannot alloc mem"); 622 exit(1); 623 } 624 pwd->pw_shell = sh; 625 } 626 #endif 627 environ = envinit; 628 strlcat(homedir, pwd->pw_dir, sizeof(homedir)); 629 strlcat(path, _PATH_DEFPATH, sizeof(path)); 630 strlcat(shell, pwd->pw_shell, sizeof(shell)); 631 strlcat(username, pwd->pw_name, sizeof(username)); 632 #ifdef LOGIN_CAP 633 /* 634 * PAM modules might add supplementary groups in 635 * pam_setcred(), so initialize them first. 636 * But we need to open the session as root. 637 */ 638 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { 639 syslog(LOG_ERR, "setusercontext: %m"); 640 exit(1); 641 } 642 #else 643 initgroups(pwd->pw_name, pwd->pw_gid); 644 #endif 645 646 #ifdef USE_PAM 647 if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 648 syslog(LOG_ERR, "pam_open_session: %s", 649 pam_strerror(pamh, pam_err)); 650 } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) 651 != PAM_SUCCESS) { 652 syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); 653 } 654 #endif 655 656 cp = strrchr(pwd->pw_shell, '/'); 657 if (cp) 658 cp++; 659 else 660 cp = pwd->pw_shell; 661 662 #ifdef LOGIN_CAP 663 if (setusercontext(lc, pwd, pwd->pw_uid, 664 LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { 665 syslog(LOG_ERR, "setusercontext(): %m"); 666 exit(1); 667 } 668 login_close(lc); 669 #else 670 (void)setgid((gid_t)pwd->pw_gid); 671 (void)setuid((uid_t)pwd->pw_uid); 672 #endif 673 endpwent(); 674 if (log_success || pwd->pw_uid == 0) { 675 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", 676 remuser, hostname, locuser, cmdbuf); 677 } 678 execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL); 679 rshd_errx(1, "%s: %s", pwd->pw_shell, strerror(errno)); 680 badlogin: 681 syslog(LOG_INFO|LOG_AUTH, 682 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 683 remuser, hostname, locuser, errormsg, cmdbuf); 684 rshd_errx(1, errorstr, errorhost); 685 } 686 687 /* 688 * Report error to client. Note: can't be used until second socket has 689 * connected to client, or older clients will hang waiting for that 690 * connection first. 691 */ 692 693 #include <stdarg.h> 694 695 void 696 rshd_errx(int error, const char *fmt, ...) 697 { 698 va_list ap; 699 int len, rv; 700 char *bp, buf[BUFSIZ]; 701 va_start(ap, fmt); 702 bp = buf; 703 if (sent_null == 0) { 704 *bp++ = 1; 705 len = 1; 706 } else 707 len = 0; 708 rv = vsnprintf(bp, sizeof(buf) - 2, fmt, ap); 709 bp[rv++] = '\n'; 710 (void)write(STDERR_FILENO, buf, len + rv); 711 va_end(ap); 712 exit(error); 713 } 714 715 void 716 getstr(char *buf, int cnt, char *err) 717 { 718 char c; 719 720 do { 721 if (read(STDIN_FILENO, &c, 1) != 1) 722 exit(1); 723 *buf++ = c; 724 if (--cnt == 0) 725 rshd_errx(1, "%s too long", err); 726 } while (c != 0); 727 } 728 729 /* 730 * Check whether host h is in our local domain, 731 * defined as sharing the last two components of the domain part, 732 * or the entire domain part if the local domain has only one component. 733 * If either name is unqualified (contains no '.'), 734 * assume that the host is local, as it will be 735 * interpreted as such. 736 */ 737 int 738 local_domain(char *h) 739 { 740 char localhost[MAXHOSTNAMELEN + 1]; 741 char *p1, *p2; 742 743 localhost[0] = 0; 744 (void)gethostname(localhost, sizeof(localhost)); 745 localhost[sizeof(localhost) - 1] = '\0'; 746 p1 = topdomain(localhost); 747 p2 = topdomain(h); 748 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 749 return (1); 750 return (0); 751 } 752 753 char * 754 topdomain(char *h) 755 { 756 char *p, *maybe = NULL; 757 int dots = 0; 758 759 for (p = h + strlen(h); p >= h; p--) { 760 if (*p == '.') { 761 if (++dots == 2) 762 return (p); 763 maybe = p; 764 } 765 } 766 return (maybe); 767 } 768 769 void 770 usage(void) 771 { 772 773 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); 774 exit(2); 775 } 776