rshd.c revision 321069
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 321069 2017-07-17 06:37:46Z delphij $"); 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 strlcpy(luser, cp, sizeof(luser)); 326 /* XXX truncation! */ 327 } 328 pam_err = pam_acct_mgmt(pamh, 0); 329 } 330 if (pam_err != PAM_SUCCESS) { 331 syslog(LOG_INFO|LOG_AUTH, 332 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 333 ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf); 334 rshd_errx(1, "Login incorrect."); 335 } 336 337 setpwent(); 338 pwd = getpwnam(luser); 339 if (pwd == NULL) { 340 syslog(LOG_INFO|LOG_AUTH, 341 "%s@%s as %s: unknown login. cmd='%.80s'", 342 ruser, rhost, luser, cmdbuf); 343 if (errorstr == NULL) 344 errorstr = "Login incorrect."; 345 rshd_errx(1, errorstr, rhost); 346 } 347 348 lc = login_getpwclass(pwd); 349 if (pwd->pw_uid) 350 auth_checknologin(lc); 351 352 if (chdir(pwd->pw_dir) < 0) { 353 if (chdir("/") < 0 || 354 login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { 355 syslog(LOG_INFO|LOG_AUTH, 356 "%s@%s as %s: no home directory. cmd='%.80s'", 357 ruser, rhost, luser, cmdbuf); 358 rshd_errx(0, "No remote home directory."); 359 } 360 pwd->pw_dir = slash; 361 } 362 363 if (lc != NULL && fromp->sa_family == AF_INET) { /*XXX*/ 364 char remote_ip[MAXHOSTNAMELEN]; 365 366 strlcpy(remote_ip, numericname, sizeof(remote_ip)); 367 /* XXX truncation! */ 368 if (!auth_hostok(lc, rhost, remote_ip)) { 369 syslog(LOG_INFO|LOG_AUTH, 370 "%s@%s as %s: permission denied (%s). cmd='%.80s'", 371 ruser, rhost, luser, __rcmd_errstr, 372 cmdbuf); 373 rshd_errx(1, "Login incorrect."); 374 } 375 if (!auth_timeok(lc, time(NULL))) 376 rshd_errx(1, "Logins not available right now"); 377 } 378 379 /* 380 * PAM modules might add supplementary groups in 381 * pam_setcred(), so initialize them first. 382 * But we need to open the session as root. 383 */ 384 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { 385 syslog(LOG_ERR, "setusercontext: %m"); 386 exit(1); 387 } 388 389 if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 390 syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err)); 391 } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 392 syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); 393 } 394 395 (void) write(STDERR_FILENO, "\0", 1); 396 sent_null = 1; 397 398 if (port) { 399 if (pipe(pv) < 0) 400 rshd_errx(1, "Can't make pipe."); 401 pid = fork(); 402 if (pid == -1) 403 rshd_errx(1, "Can't fork; try again."); 404 if (pid) { 405 (void) close(0); 406 (void) close(1); 407 (void) close(2); 408 (void) close(pv[1]); 409 410 FD_ZERO(&readfrom); 411 FD_SET(s, &readfrom); 412 FD_SET(pv[0], &readfrom); 413 if (pv[0] > s) 414 nfd = pv[0]; 415 else 416 nfd = s; 417 ioctl(pv[0], FIONBIO, (char *)&one); 418 419 /* should set s nbio! */ 420 nfd++; 421 do { 422 ready = readfrom; 423 if (select(nfd, &ready, (fd_set *)0, 424 (fd_set *)0, (struct timeval *)0) < 0) 425 break; 426 if (FD_ISSET(s, &ready)) { 427 int ret; 428 ret = read(s, &sig, 1); 429 if (ret <= 0) 430 FD_CLR(s, &readfrom); 431 else 432 killpg(pid, sig); 433 } 434 if (FD_ISSET(pv[0], &ready)) { 435 errno = 0; 436 cc = read(pv[0], buf, sizeof(buf)); 437 if (cc <= 0) { 438 shutdown(s, SHUT_RDWR); 439 FD_CLR(pv[0], &readfrom); 440 } else { 441 (void)write(s, buf, cc); 442 } 443 } 444 445 } while (FD_ISSET(s, &readfrom) || 446 FD_ISSET(pv[0], &readfrom)); 447 PAM_END; 448 exit(0); 449 } 450 (void) close(s); 451 (void) close(pv[0]); 452 dup2(pv[1], 2); 453 close(pv[1]); 454 } 455 else { 456 pid = fork(); 457 if (pid == -1) 458 rshd_errx(1, "Can't fork; try again."); 459 if (pid) { 460 /* Parent. */ 461 while (wait(NULL) > 0 || errno == EINTR) 462 /* nothing */ ; 463 PAM_END; 464 exit(0); 465 } 466 } 467 468 for (fd = getdtablesize(); fd > 2; fd--) 469 (void) close(fd); 470 if (setsid() == -1) 471 syslog(LOG_ERR, "setsid() failed: %m"); 472 if (setlogin(pwd->pw_name) < 0) 473 syslog(LOG_ERR, "setlogin() failed: %m"); 474 475 if (*pwd->pw_shell == '\0') 476 pwd->pw_shell = bshell; 477 (void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1); 478 (void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); 479 (void) pam_setenv(pamh, "USER", pwd->pw_name, 1); 480 (void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); 481 environ = pam_getenvlist(pamh); 482 (void) pam_end(pamh, pam_err); 483 cp = strrchr(pwd->pw_shell, '/'); 484 if (cp) 485 cp++; 486 else 487 cp = pwd->pw_shell; 488 489 if (setusercontext(lc, pwd, pwd->pw_uid, 490 LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { 491 syslog(LOG_ERR, "setusercontext(): %m"); 492 exit(1); 493 } 494 login_close(lc); 495 endpwent(); 496 if (log_success || pwd->pw_uid == 0) { 497 syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", 498 ruser, rhost, luser, cmdbuf); 499 } 500 execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL); 501 err(1, "%s", pwd->pw_shell); 502 exit(1); 503} 504 505/* 506 * Report error to client. Note: can't be used until second socket has 507 * connected to client, or older clients will hang waiting for that 508 * connection first. 509 */ 510 511static void 512rshd_errx(int errcode, const char *fmt, ...) 513{ 514 va_list ap; 515 516 va_start(ap, fmt); 517 518 if (sent_null == 0) 519 write(STDERR_FILENO, "\1", 1); 520 521 verrx(errcode, fmt, ap); 522 /* NOTREACHED */ 523} 524 525void 526getstr(char *buf, int cnt, const char *error) 527{ 528 char c; 529 530 do { 531 if (read(STDIN_FILENO, &c, 1) != 1) 532 exit(1); 533 *buf++ = c; 534 if (--cnt == 0) 535 rshd_errx(1, "%s too long", error); 536 } while (c != 0); 537} 538 539/* 540 * Check whether host h is in our local domain, 541 * defined as sharing the last two components of the domain part, 542 * or the entire domain part if the local domain has only one component. 543 * If either name is unqualified (contains no '.'), 544 * assume that the host is local, as it will be 545 * interpreted as such. 546 */ 547int 548local_domain(char *h) 549{ 550 char localhost[MAXHOSTNAMELEN]; 551 char *p1, *p2; 552 553 localhost[0] = 0; 554 (void) gethostname(localhost, sizeof(localhost) - 1); 555 localhost[sizeof(localhost) - 1] = '\0'; 556 /* XXX truncation! */ 557 p1 = topdomain(localhost); 558 p2 = topdomain(h); 559 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 560 return (1); 561 return (0); 562} 563 564char * 565topdomain(char *h) 566{ 567 char *p, *maybe = NULL; 568 int dots = 0; 569 570 for (p = h + strlen(h); p >= h; p--) { 571 if (*p == '.') { 572 if (++dots == 2) 573 return (p); 574 maybe = p; 575 } 576 } 577 return (maybe); 578} 579 580void 581usage(void) 582{ 583 584 syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); 585 exit(2); 586} 587