rshd.c revision 262435
1/*- 2 * Copyright (c) 1988, 1989, 1992, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38static const char copyright[] = 39"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44#if 0 45static const char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; 46#endif 47#endif /* not lint */ 48 49#include <sys/cdefs.h> 50__FBSDID("$FreeBSD: stable/10/libexec/rshd/rshd.c 262435 2014-02-24 08:21:49Z brueffer $"); 51 52/* 53 * remote shell server: 54 * [port]\0 55 * ruser\0 56 * luser\0 57 * command\0 58 * data 59 */ 60#include <sys/param.h> 61#include <sys/ioctl.h> 62#include <sys/time.h> 63#include <sys/socket.h> 64 65#include <netinet/in_systm.h> 66#include <netinet/in.h> 67#include <netinet/ip.h> 68#include <netinet/tcp.h> 69#include <arpa/inet.h> 70#include <netdb.h> 71 72#include <err.h> 73#include <errno.h> 74#include <fcntl.h> 75#include <libutil.h> 76#include <paths.h> 77#include <pwd.h> 78#include <signal.h> 79#include <stdarg.h> 80#include <stdio.h> 81#include <stdlib.h> 82#include <string.h> 83#include <syslog.h> 84#include <unistd.h> 85#include <login_cap.h> 86 87#include <security/pam_appl.h> 88#include <security/openpam.h> 89#include <sys/wait.h> 90 91static struct pam_conv pamc = { openpam_nullconv, NULL }; 92static pam_handle_t *pamh; 93static int pam_err; 94 95#define PAM_END { \ 96 if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \ 97 syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", pam_strerror(pamh, pam_err)); \ 98 if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \ 99 syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", pam_strerror(pamh, pam_err)); \ 100 if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \ 101 syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", pam_strerror(pamh, pam_err)); \ 102} 103 104int keepalive = 1; 105int log_success; /* If TRUE, log all successful accesses */ 106int sent_null; 107int no_delay; 108 109void doit(struct sockaddr *); 110static void rshd_errx(int, const char *, ...) __printf0like(2, 3); 111void getstr(char *, int, const char *); 112int local_domain(char *); 113char *topdomain(char *); 114void usage(void); 115 116char slash[] = "/"; 117char bshell[] = _PATH_BSHELL; 118 119#define OPTIONS "aDLln" 120 121int 122main(int argc, char *argv[]) 123{ 124 extern int __check_rhosts_file; 125 struct linger linger; 126 socklen_t fromlen; 127 int ch, on = 1; 128 struct sockaddr_storage from; 129 130 openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 131 132 opterr = 0; 133 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 134 switch (ch) { 135 case 'a': 136 /* ignored for compatibility */ 137 break; 138 case 'l': 139 __check_rhosts_file = 0; 140 break; 141 case 'n': 142 keepalive = 0; 143 break; 144 case 'D': 145 no_delay = 1; 146 break; 147 case 'L': 148 log_success = 1; 149 break; 150 case '?': 151 default: 152 usage(); 153 break; 154 } 155 156 argc -= optind; 157 argv += optind; 158 159 fromlen = sizeof (from); 160 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 161 syslog(LOG_ERR, "getpeername: %m"); 162 exit(1); 163 } 164 if (keepalive && 165 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 166 sizeof(on)) < 0) 167 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 168 linger.l_onoff = 1; 169 linger.l_linger = 60; /* XXX */ 170 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 171 sizeof (linger)) < 0) 172 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 173 if (no_delay && 174 setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 175 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); 176 doit((struct sockaddr *)&from); 177 /* NOTREACHED */ 178 return(0); 179} 180 181extern char **environ; 182 183void 184doit(struct sockaddr *fromp) 185{ 186 extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ 187 struct passwd *pwd; 188 u_short port; 189 fd_set ready, readfrom; 190 int cc, fd, nfd, pv[2], pid, s; 191 int one = 1; 192 const char *cp, *errorstr; 193 char sig, buf[BUFSIZ]; 194 char *cmdbuf, luser[16], ruser[16]; 195 char rhost[2 * MAXHOSTNAMELEN + 1]; 196 char numericname[INET6_ADDRSTRLEN]; 197 int af, srcport; 198 int maxcmdlen; 199 login_cap_t *lc; 200 201 maxcmdlen = (int)sysconf(_SC_ARG_MAX); 202 if (maxcmdlen <= 0 || (cmdbuf = malloc(maxcmdlen)) == NULL) 203 exit(1); 204 205 (void) signal(SIGINT, SIG_DFL); 206 (void) signal(SIGQUIT, SIG_DFL); 207 (void) signal(SIGTERM, SIG_DFL); 208 af = fromp->sa_family; 209 srcport = ntohs(*((in_port_t *)&fromp->sa_data)); 210 if (af == AF_INET) { 211 inet_ntop(af, &((struct sockaddr_in *)fromp)->sin_addr, 212 numericname, sizeof numericname); 213 } else if (af == AF_INET6) { 214 inet_ntop(af, &((struct sockaddr_in6 *)fromp)->sin6_addr, 215 numericname, sizeof numericname); 216 } else { 217 syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); 218 exit(1); 219 } 220#ifdef IP_OPTIONS 221 if (af == AF_INET) { 222 u_char optbuf[BUFSIZ/3]; 223 socklen_t optsize = sizeof(optbuf), ipproto, i; 224 struct protoent *ip; 225 226 if ((ip = getprotobyname("ip")) != NULL) 227 ipproto = ip->p_proto; 228 else 229 ipproto = IPPROTO_IP; 230 if (!getsockopt(0, ipproto, IP_OPTIONS, optbuf, &optsize) && 231 optsize != 0) { 232 for (i = 0; i < optsize; ) { 233 u_char c = optbuf[i]; 234 if (c == IPOPT_LSRR || c == IPOPT_SSRR) { 235 syslog(LOG_NOTICE, 236 "connection refused from %s with IP option %s", 237 numericname, 238 c == IPOPT_LSRR ? "LSRR" : "SSRR"); 239 exit(1); 240 } 241 if (c == IPOPT_EOL) 242 break; 243 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; 244 } 245 } 246 } 247#endif 248 249 if (srcport >= IPPORT_RESERVED || 250 srcport < IPPORT_RESERVED/2) { 251 syslog(LOG_NOTICE|LOG_AUTH, 252 "connection from %s on illegal port %u", 253 numericname, 254 srcport); 255 exit(1); 256 } 257 258 (void) alarm(60); 259 port = 0; 260 s = 0; /* not set or used if port == 0 */ 261 for (;;) { 262 char c; 263 if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { 264 if (cc < 0) 265 syslog(LOG_NOTICE, "read: %m"); 266 shutdown(0, SHUT_RDWR); 267 exit(1); 268 } 269 if (c == 0) 270 break; 271 port = port * 10 + c - '0'; 272 } 273 274 (void) alarm(0); 275 if (port != 0) { 276 int lport = IPPORT_RESERVED - 1; 277 s = rresvport_af(&lport, af); 278 if (s < 0) { 279 syslog(LOG_ERR, "can't get stderr port: %m"); 280 exit(1); 281 } 282 if (port >= IPPORT_RESERVED || 283 port < IPPORT_RESERVED/2) { 284 syslog(LOG_NOTICE|LOG_AUTH, 285 "2nd socket from %s on unreserved port %u", 286 numericname, 287 port); 288 exit(1); 289 } 290 *((in_port_t *)&fromp->sa_data) = htons(port); 291 if (connect(s, fromp, fromp->sa_len) < 0) { 292 syslog(LOG_INFO, "connect second port %d: %m", port); 293 exit(1); 294 } 295 } 296 297 errorstr = NULL; 298 realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len); 299 rhost[sizeof(rhost) - 1] = '\0'; 300 /* XXX truncation! */ 301 302 (void) alarm(60); 303 getstr(ruser, sizeof(ruser), "ruser"); 304 getstr(luser, sizeof(luser), "luser"); 305 getstr(cmdbuf, maxcmdlen, "command"); 306 (void) alarm(0); 307 308 pam_err = pam_start("rsh", luser, &pamc, &pamh); 309 if (pam_err != PAM_SUCCESS) { 310 syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s", 311 pam_strerror(pamh, pam_err)); 312 rshd_errx(1, "Login incorrect."); 313 } 314 315 if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS || 316 (pam_err = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) { 317 syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s", 318 pam_strerror(pamh, pam_err)); 319 rshd_errx(1, "Login incorrect."); 320 } 321 322 pam_err = pam_authenticate(pamh, 0); 323 if (pam_err == PAM_SUCCESS) { 324 if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) { 325 strncpy(luser, cp, sizeof(luser)); 326 luser[sizeof(luser) - 1] = '\0'; 327 /* XXX truncation! */ 328 } 329 pam_err = pam_acct_mgmt(pamh, 0); 330 } 331 if (pam_err != PAM_SUCCESS) { 332 syslog(LOG_INFO|LOG_AUTH, 333 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 334 ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf); 335 rshd_errx(1, "Login incorrect."); 336 } 337 338 setpwent(); 339 pwd = getpwnam(luser); 340 if (pwd == NULL) { 341 syslog(LOG_INFO|LOG_AUTH, 342 "%s@%s as %s: unknown login. cmd='%.80s'", 343 ruser, rhost, luser, cmdbuf); 344 if (errorstr == NULL) 345 errorstr = "Login incorrect."; 346 rshd_errx(1, errorstr, rhost); 347 } 348 349 lc = login_getpwclass(pwd); 350 if (pwd->pw_uid) 351 auth_checknologin(lc); 352 353 if (chdir(pwd->pw_dir) < 0) { 354 if (chdir("/") < 0 || 355 login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { 356 syslog(LOG_INFO|LOG_AUTH, 357 "%s@%s as %s: no home directory. cmd='%.80s'", 358 ruser, rhost, luser, cmdbuf); 359 rshd_errx(0, "No remote home directory."); 360 } 361 pwd->pw_dir = slash; 362 } 363 364 if (lc != NULL && fromp->sa_family == AF_INET) { /*XXX*/ 365 char remote_ip[MAXHOSTNAMELEN]; 366 367 strncpy(remote_ip, numericname, 368 sizeof(remote_ip) - 1); 369 remote_ip[sizeof(remote_ip) - 1] = 0; 370 /* XXX truncation! */ 371 if (!auth_hostok(lc, rhost, remote_ip)) { 372 syslog(LOG_INFO|LOG_AUTH, 373 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 374 ruser, rhost, luser, __rcmd_errstr, 375 cmdbuf); 376 rshd_errx(1, "Login incorrect."); 377 } 378 if (!auth_timeok(lc, time(NULL))) 379 rshd_errx(1, "Logins not available right now"); 380 } 381 382 /* 383 * PAM modules might add supplementary groups in 384 * pam_setcred(), so initialize them first. 385 * But we need to open the session as root. 386 */ 387 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { 388 syslog(LOG_ERR, "setusercontext: %m"); 389 exit(1); 390 } 391 392 if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 393 syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err)); 394 } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 395 syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); 396 } 397 398 (void) write(STDERR_FILENO, "\0", 1); 399 sent_null = 1; 400 401 if (port) { 402 if (pipe(pv) < 0) 403 rshd_errx(1, "Can't make pipe."); 404 pid = fork(); 405 if (pid == -1) 406 rshd_errx(1, "Can't fork; try again."); 407 if (pid) { 408 (void) close(0); 409 (void) close(1); 410 (void) close(2); 411 (void) close(pv[1]); 412 413 FD_ZERO(&readfrom); 414 FD_SET(s, &readfrom); 415 FD_SET(pv[0], &readfrom); 416 if (pv[0] > s) 417 nfd = pv[0]; 418 else 419 nfd = s; 420 ioctl(pv[0], FIONBIO, (char *)&one); 421 422 /* should set s nbio! */ 423 nfd++; 424 do { 425 ready = readfrom; 426 if (select(nfd, &ready, (fd_set *)0, 427 (fd_set *)0, (struct timeval *)0) < 0) 428 break; 429 if (FD_ISSET(s, &ready)) { 430 int ret; 431 ret = read(s, &sig, 1); 432 if (ret <= 0) 433 FD_CLR(s, &readfrom); 434 else 435 killpg(pid, sig); 436 } 437 if (FD_ISSET(pv[0], &ready)) { 438 errno = 0; 439 cc = read(pv[0], buf, sizeof(buf)); 440 if (cc <= 0) { 441 shutdown(s, SHUT_RDWR); 442 FD_CLR(pv[0], &readfrom); 443 } else { 444 (void)write(s, buf, cc); 445 } 446 } 447 448 } while (FD_ISSET(s, &readfrom) || 449 FD_ISSET(pv[0], &readfrom)); 450 PAM_END; 451 exit(0); 452 } 453 (void) close(s); 454 (void) close(pv[0]); 455 dup2(pv[1], 2); 456 close(pv[1]); 457 } 458 else { 459 pid = fork(); 460 if (pid == -1) 461 rshd_errx(1, "Can't fork; try again."); 462 if (pid) { 463 /* Parent. */ 464 while (wait(NULL) > 0 || errno == EINTR) 465 /* nothing */ ; 466 PAM_END; 467 exit(0); 468 } 469 } 470 471 for (fd = getdtablesize(); fd > 2; fd--) 472 (void) close(fd); 473 if (setsid() == -1) 474 syslog(LOG_ERR, "setsid() failed: %m"); 475 if (setlogin(pwd->pw_name) < 0) 476 syslog(LOG_ERR, "setlogin() failed: %m"); 477 478 if (*pwd->pw_shell == '\0') 479 pwd->pw_shell = bshell; 480 (void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1); 481 (void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); 482 (void) pam_setenv(pamh, "USER", pwd->pw_name, 1); 483 (void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); 484 environ = pam_getenvlist(pamh); 485 (void) pam_end(pamh, pam_err); 486 cp = strrchr(pwd->pw_shell, '/'); 487 if (cp) 488 cp++; 489 else 490 cp = pwd->pw_shell; 491 492 if (setusercontext(lc, pwd, pwd->pw_uid, 493 LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { 494 syslog(LOG_ERR, "setusercontext(): %m"); 495 exit(1); 496 } 497 login_close(lc); 498 endpwent(); 499 if (log_success || pwd->pw_uid == 0) { 500 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", 501 ruser, rhost, luser, cmdbuf); 502 } 503 execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL); 504 err(1, "%s", pwd->pw_shell); 505 exit(1); 506} 507 508/* 509 * Report error to client. Note: can't be used until second socket has 510 * connected to client, or older clients will hang waiting for that 511 * connection first. 512 */ 513 514static void 515rshd_errx(int errcode, const char *fmt, ...) 516{ 517 va_list ap; 518 519 va_start(ap, fmt); 520 521 if (sent_null == 0) 522 write(STDERR_FILENO, "\1", 1); 523 524 verrx(errcode, fmt, ap); 525 /* NOTREACHED */ 526} 527 528void 529getstr(char *buf, int cnt, const char *error) 530{ 531 char c; 532 533 do { 534 if (read(STDIN_FILENO, &c, 1) != 1) 535 exit(1); 536 *buf++ = c; 537 if (--cnt == 0) 538 rshd_errx(1, "%s too long", error); 539 } while (c != 0); 540} 541 542/* 543 * Check whether host h is in our local domain, 544 * defined as sharing the last two components of the domain part, 545 * or the entire domain part if the local domain has only one component. 546 * If either name is unqualified (contains no '.'), 547 * assume that the host is local, as it will be 548 * interpreted as such. 549 */ 550int 551local_domain(char *h) 552{ 553 char localhost[MAXHOSTNAMELEN]; 554 char *p1, *p2; 555 556 localhost[0] = 0; 557 (void) gethostname(localhost, sizeof(localhost) - 1); 558 localhost[sizeof(localhost) - 1] = '\0'; 559 /* XXX truncation! */ 560 p1 = topdomain(localhost); 561 p2 = topdomain(h); 562 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 563 return (1); 564 return (0); 565} 566 567char * 568topdomain(char *h) 569{ 570 char *p, *maybe = NULL; 571 int dots = 0; 572 573 for (p = h + strlen(h); p >= h; p--) { 574 if (*p == '.') { 575 if (++dots == 2) 576 return (p); 577 maybe = p; 578 } 579 } 580 return (maybe); 581} 582 583void 584usage(void) 585{ 586 587 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); 588 exit(2); 589} 590