int rlogind_auth (int fd, struct auth_data *ap) { struct hostent *hp; char *hostname; int authenticated = 0; #ifdef SHISHI int len, c; #endif confirmed = 0; /* Check the remote host name */ hp = gethostbyaddr ((char *) &ap->from.sin_addr, sizeof (struct in_addr), ap->from.sin_family); if (hp) hostname = hp->h_name; else if (reverse_required) { syslog (LOG_CRIT, "can't resolve remote IP address"); exit (1); } else hostname = inet_ntoa (ap->from.sin_addr); ap->hostname = strdup (hostname); if (verify_hostname || in_local_domain (ap->hostname)) { int match = 0; for (hp = gethostbyname (ap->hostname); hp && !match; hp->h_addr_list++) { if (hp->h_addr_list[0] == NULL) break; match = memcmp (hp->h_addr_list[0], &ap->from.sin_addr, sizeof (ap->from.sin_addr)) == 0; } if (!match) { syslog (LOG_ERR | LOG_AUTH, "cannot find matching IP for %s (%s)", ap->hostname, inet_ntoa (ap->from.sin_addr)); fatal (fd, "Permission denied", 0); } } #if defined(KERBEROS) || defined(SHISHI) if (kerberos) { const char *err_msg; int c = 0; if (do_krb_login (fd, ap, &err_msg) == 0) authenticated++; else fatal (fd, err_msg, 0); write (fd, &c, 1); confirmed = 1; /* we sent the null! */ } else #endif { int port = ntohs (ap->from.sin_port); if (ap->from.sin_family != AF_INET || port >= IPPORT_RESERVED || port < IPPORT_RESERVED / 2) { syslog (LOG_NOTICE, "Connection from %s on illegal port %d", inet_ntoa (ap->from.sin_addr), port); fatal (fd, "Permission denied", 0); } #ifdef IP_OPTIONS { u_char optbuf[BUFSIZ / 3], *cp; char lbuf[BUFSIZ], *lp; socklen_t optsize = sizeof (optbuf); int ipproto; struct protoent *ip; if ((ip = getprotobyname ("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; if (getsockopt (0, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0 && optsize != 0) { lp = lbuf; for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) sprintf (lp, " %2.2x", *cp); syslog (LOG_NOTICE, "Ignoring IP options: %s", lbuf); if (setsockopt (0, ipproto, IP_OPTIONS, (char *) NULL, optsize)) { syslog (LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); exit (1); } } } #endif /* IP_OPTIONS */ if (do_rlogin (fd, ap) == 0) authenticated++; } if (confirmed == 0) { write (fd, "", 1); confirmed = 1; /* we sent the null! */ } #ifdef SHISHI len = sizeof (SECURE_MESSAGE) - 1; IF_ENCRYPT (writeenc (ap->h, fd, SECURE_MESSAGE, len, &c, &ap->iv2, ap->enckey, ap->protocol)); #else IF_ENCRYPT (des_write (fd, SECURE_MESSAGE, sizeof (SECURE_MESSAGE) - 1)); #endif return authenticated; }
static void doit(int f, struct sockaddr_storage *fromp, krb5_context krb_context, int encr_flag, krb5_keytab keytab) { int p, t, on = 1; char c; char abuf[INET6_ADDRSTRLEN]; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; int fromplen; in_port_t port; struct termios tp; boolean_t bad_port; boolean_t no_name; char rhost_addra[INET6_ADDRSTRLEN]; if (!(rlbuf = malloc(BUFSIZ))) { syslog(LOG_ERR, "rlbuf malloc failed\n"); exit(EXIT_FAILURE); } (void) alarm(60); if (read(f, &c, 1) != 1 || c != 0) { syslog(LOG_ERR, "failed to receive protocol zero byte\n"); exit(EXIT_FAILURE); } (void) alarm(0); if (fromp->ss_family == AF_INET) { sin = (struct sockaddr_in *)fromp; port = sin->sin_port = ntohs((ushort_t)sin->sin_port); fromplen = sizeof (struct sockaddr_in); if (!inet_ntop(AF_INET, &sin->sin_addr, rhost_addra, sizeof (rhost_addra))) goto badconversion; } else if (fromp->ss_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)fromp; port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port); fromplen = sizeof (struct sockaddr_in6); if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { struct in_addr ipv4_addr; IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr); if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra, sizeof (rhost_addra))) goto badconversion; } else { if (!inet_ntop(AF_INET6, &sin6->sin6_addr, rhost_addra, sizeof (rhost_addra))) goto badconversion; } } else { syslog(LOG_ERR, "unknown address family %d\n", fromp->ss_family); fatal(f, "Permission denied"); } /* * Allow connections only from the "ephemeral" reserved * ports(ports 512 - 1023) by checking the remote port * because other utilities(e.g. in.ftpd) can be used to * allow a unprivileged user to originate a connection * from a privileged port and provide untrustworthy * authentication. */ bad_port = (use_auth != KRB5_RECVAUTH_V5 && (port >= (in_port_t)IPPORT_RESERVED) || (port < (in_port_t)(IPPORT_RESERVED/2))); no_name = getnameinfo((const struct sockaddr *) fromp, fromplen, hostname, sizeof (hostname), NULL, 0, 0) != 0; if (no_name || bad_port) { (void) strlcpy(abuf, rhost_addra, sizeof (abuf)); /* If no host name, use IP address for name later on. */ if (no_name) (void) strlcpy(hostname, abuf, sizeof (hostname)); } if (!no_name) { /* * Even if getnameinfo() succeeded, we still have to check * for spoofing. */ check_address("rlogind", fromp, sin, sin6, rhost_addra, hostname, sizeof (hostname)); } if (bad_port) { if (no_name) syslog(LOG_NOTICE, "connection from %s - bad port\n", abuf); else syslog(LOG_NOTICE, "connection from %s(%s) - bad port\n", hostname, abuf); fatal(f, "Permission denied"); } if (use_auth == KRB5_RECVAUTH_V5) { do_krb_login(f, rhost_addra, hostname, krb_context, encr_flag, keytab); if (krusername != NULL && strlen(krusername)) { /* * Kerberos Authentication succeeded, * so set the proper program name to use * with pam (important during 'cleanup' * routine later). */ pam_prog_name = KRB5_PROG_NAME; } } if (write(f, "", 1) != 1) { syslog(LOG_NOTICE, "send of the zero byte(to %s) failed:" " cannot start data transfer mode\n", (no_name ? abuf : hostname)); exit(EXIT_FAILURE); } if ((p = open("/dev/ptmx", O_RDWR)) == -1) fatalperror(f, "cannot open /dev/ptmx"); if (grantpt(p) == -1) fatal(f, "could not grant slave pty"); if (unlockpt(p) == -1) fatal(f, "could not unlock slave pty"); if ((line = ptsname(p)) == NULL) fatal(f, "could not enable slave pty"); if ((t = open(line, O_RDWR)) == -1) fatal(f, "could not open slave pty"); if (ioctl(t, I_PUSH, "ptem") == -1) fatalperror(f, "ioctl I_PUSH ptem"); if (ioctl(t, I_PUSH, "ldterm") == -1) fatalperror(f, "ioctl I_PUSH ldterm"); if (ioctl(t, I_PUSH, "ttcompat") == -1) fatalperror(f, "ioctl I_PUSH ttcompat"); /* * POP the sockmod and push the rlmod module. * * Note that sockmod has to be removed since readstream assumes * a "raw" TPI endpoint(e.g. it uses getmsg). */ if (removemod(f, "sockmod") < 0) fatalperror(f, "couldn't remove sockmod"); if (encr_flag) { if (ioctl(f, I_PUSH, "cryptmod") < 0) fatalperror(f, "ioctl I_PUSH rlmod"); } if (ioctl(f, I_PUSH, "rlmod") < 0) fatalperror(f, "ioctl I_PUSH rlmod"); if (encr_flag) { /* * Make sure rlmod will pass unrecognized IOCTLs to cryptmod */ uchar_t passthru = 1; struct strioctl rlmodctl; rlmodctl.ic_cmd = CRYPTPASSTHRU; rlmodctl.ic_timout = -1; rlmodctl.ic_len = sizeof (uchar_t); rlmodctl.ic_dp = (char *)&passthru; if (ioctl(f, I_STR, &rlmodctl) < 0) fatal(f, "ioctl CRYPTPASSTHRU failed\n"); } /* * readstream will do a getmsg till it receives * M_PROTO type T_DATA_REQ from rloginmodopen() * indicating all data on the stream prior to pushing rlmod has * been drained at the stream head. */ if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0) fatalperror(f, "readstream failed"); /* * Make sure the pty doesn't modify the strings passed * to login as part of the "rlogin protocol." The login * program should set these flags to apropriate values * after it has read the strings. */ if (ioctl(t, TCGETS, &tp) == -1) fatalperror(f, "ioctl TCGETS"); tp.c_lflag &= ~(ECHO|ICANON); tp.c_oflag &= ~(XTABS|OCRNL); tp.c_iflag &= ~(IGNPAR|ICRNL); if (ioctl(t, TCSETS, &tp) == -1) fatalperror(f, "ioctl TCSETS"); /* * System V ptys allow the TIOC{SG}WINSZ ioctl to be * issued on the master side of the pty. Luckily, that's * the only tty ioctl we need to do do, so we can close the * slave side in the parent process after the fork. */ (void) ioctl(p, TIOCSWINSZ, &win); pid = fork(); if (pid < 0) fatalperror(f, "fork"); if (pid == 0) { int tt; struct utmpx ut; /* System V login expects a utmp entry to already be there */ (void) memset(&ut, 0, sizeof (ut)); (void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user)); (void) strncpy(ut.ut_line, line, sizeof (ut.ut_line)); ut.ut_pid = getpid(); ut.ut_id[0] = 'r'; ut.ut_id[1] = (char)SC_WILDC; ut.ut_id[2] = (char)SC_WILDC; ut.ut_id[3] = (char)SC_WILDC; ut.ut_type = LOGIN_PROCESS; ut.ut_exit.e_termination = 0; ut.ut_exit.e_exit = 0; (void) time(&ut.ut_tv.tv_sec); if (makeutx(&ut) == NULL) syslog(LOG_INFO, "in.rlogind:\tmakeutx failed"); /* controlling tty */ if (setsid() == -1) fatalperror(f, "setsid"); if ((tt = open(line, O_RDWR)) == -1) fatalperror(f, "could not re-open slave pty"); if (close(p) == -1) fatalperror(f, "error closing pty master"); if (close(t) == -1) fatalperror(f, "error closing pty slave" " opened before session established"); /* * If this fails we may or may not be able to output an * error message. */ if (close(f) == -1) fatalperror(f, "error closing deamon stdout"); if (dup2(tt, STDIN_FILENO) == -1 || dup2(tt, STDOUT_FILENO) == -1 || dup2(tt, STDERR_FILENO) == -1) exit(EXIT_FAILURE); /* Disaster! No stderr! */ (void) close(tt); if (use_auth == KRB5_RECVAUTH_V5 && krusername != NULL && strlen(krusername)) { (void) execl(LOGIN_PROGRAM, "login", "-d", line, "-r", hostname, "-u", krusername, /* KRB5 principal name */ "-s", pam_prog_name, "-t", term, /* Remote Terminal */ "-U", rusername, /* Remote User */ "-R", KRB5_REPOSITORY_NAME, lusername, /* local user */ NULL); } else { (void) execl(LOGIN_PROGRAM, "login", "-d", line, "-r", hostname, NULL); } fatalperror(STDERR_FILENO, "/bin/login"); /*NOTREACHED*/ } (void) close(t); (void) ioctl(f, FIONBIO, &on); (void) ioctl(p, FIONBIO, &on); /* * Must ignore SIGTTOU, otherwise we'll stop * when we try and set slave pty's window shape * (our controlling tty is the master pty). * Likewise, we don't want any of the tty-generated * signals from chars passing through. */ (void) sigset(SIGTSTP, SIG_IGN); (void) sigset(SIGINT, SIG_IGN); (void) sigset(SIGQUIT, SIG_IGN); (void) sigset(SIGTTOU, SIG_IGN); (void) sigset(SIGTTIN, SIG_IGN); (void) sigset(SIGCHLD, cleanup); (void) setpgrp(); if (encr_flag) { krb5_data ivec, *ivptr; uint_t ivec_usage; stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT); /* * Configure the STREAMS crypto module. For now, * don't use any IV parameter. KCMDV0.2 support * will require the use of Initialization Vectors * for both encrypt and decrypt modes. */ if (kcmd_protocol == KCMD_OLD_PROTOCOL) { if (session_key->enctype == ENCTYPE_DES_CBC_CRC) { /* * This is gross but necessary for MIT compat. */ ivec.length = session_key->length; ivec.data = (char *)session_key->contents; ivec_usage = IVEC_REUSE; ivptr = &ivec; } else { ivptr = NULL; /* defaults to all 0's */ ivec_usage = IVEC_NEVER; } /* * configure both sides of stream together * since they share the same IV. * This is what makes the OLD KCMD protocol * less secure than the newer one - Bad ivecs. */ if (configure_stream(f, session_key, CRYPT_ENCRYPT|CRYPT_DECRYPT, ivptr, ivec_usage) != 0) fatal(f, "Cannot initialize encryption -" " exiting.\n"); } else { size_t blocksize; if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC || session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { if (configure_stream(f, session_key, CRYPT_ENCRYPT|CRYPT_DECRYPT, NULL, IVEC_NEVER) != 0) fatal(f, "Cannot initialize encryption -" " exiting.\n"); goto startcrypto; } if (krb5_c_block_size(krb_context, session_key->enctype, &blocksize)) { syslog(LOG_ERR, "Cannot determine blocksize " "for encryption type %d", session_key->enctype); fatal(f, "Cannot determine blocksize " "for encryption - exiting.\n"); } ivec.data = (char *)malloc(blocksize); ivec.length = blocksize; if (ivec.data == NULL) fatal(f, "memory error - exiting\n"); /* * Following MIT convention - * encrypt IV = 0x01 x blocksize * decrypt IV = 0x00 x blocksize * ivec_usage = IVEC_ONETIME * * configure_stream separately for encrypt and * decrypt because there are 2 different IVs. * * AES uses 0's for IV. */ if (session_key->enctype == ENCTYPE_AES128_CTS_HMAC_SHA1_96 || session_key->enctype == ENCTYPE_AES256_CTS_HMAC_SHA1_96) (void) memset(ivec.data, 0x00, blocksize); else (void) memset(ivec.data, 0x01, blocksize); if (configure_stream(f, session_key, CRYPT_ENCRYPT, &ivec, IVEC_ONETIME) != 0) fatal(f, "Cannot initialize encryption -" " exiting.\n"); (void) memset(ivec.data, 0x00, blocksize); if (configure_stream(f, session_key, CRYPT_DECRYPT, &ivec, IVEC_ONETIME) != 0) fatal(f, "Cannot initialize encryption -" " exiting.\n"); (void) free(ivec.data); } startcrypto: start_stream(f, CRYPT_ENCRYPT); start_stream(f, CRYPT_DECRYPT); } protocol(f, p, encr_flag); cleanup(0); /*NOTREACHED*/ badconversion: fatalperror(f, "address conversion"); /*NOTREACHED*/ }