From 7e47f7162cb6fe83e269e9ddc3602b2869a46c90 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Mon, 15 Nov 2004 21:25:41 +0000 Subject: [PATCH] Merge Athena changes for requiring encrypted connections ticket: 841 tags: pullup git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16866 dc483132-0cff-0310-8789-dd5450dbe970 --- src/appl/telnet/libtelnet/ChangeLog | 5 +++ src/appl/telnet/libtelnet/auth-proto.h | 3 +- src/appl/telnet/libtelnet/auth.c | 33 +++++++++++--- src/appl/telnet/telnetd/ChangeLog | 12 +++++ src/appl/telnet/telnetd/ext.h | 4 ++ src/appl/telnet/telnetd/telnetd.8 | 13 +++--- src/appl/telnet/telnetd/telnetd.c | 48 +++++++++++++------- src/appl/telnet/telnetd/utility.c | 61 ++++++++++++++++++++++++++ src/tests/dejagnu/krb-root/ChangeLog | 7 +++ src/tests/dejagnu/krb-root/telnet.exp | 53 +++++++++++++++++++--- 10 files changed, 203 insertions(+), 36 deletions(-) diff --git a/src/appl/telnet/libtelnet/ChangeLog b/src/appl/telnet/libtelnet/ChangeLog index 9e5c888d7..3467422dd 100644 --- a/src/appl/telnet/libtelnet/ChangeLog +++ b/src/appl/telnet/libtelnet/ChangeLog @@ -1,3 +1,8 @@ +2004-11-15 Tom Yu + + * auth-proto.h, auth.c: Merge Athena changes for requiring + encrypted connections. + 2004-06-04 Ken Raeburn * Makefile.in (LIBBASE): Renamed from LIB. diff --git a/src/appl/telnet/libtelnet/auth-proto.h b/src/appl/telnet/libtelnet/auth-proto.h index 68cae7efc..6b4957032 100644 --- a/src/appl/telnet/libtelnet/auth-proto.h +++ b/src/appl/telnet/libtelnet/auth-proto.h @@ -67,7 +67,8 @@ void auth_send_retry (void); void auth_is (unsigned char *, int); void auth_reply (unsigned char *, int); void auth_finished (Authenticator *, int); -int auth_wait (char *); +void auth_wait (char *); +int auth_check (char *); int auth_must_encrypt (void); void auth_disable_name (char *); void auth_gen_printsub (unsigned char *, int, unsigned char *, unsigned int); diff --git a/src/appl/telnet/libtelnet/auth.c b/src/appl/telnet/libtelnet/auth.c index 9c1b0b0fc..28b8ae8d1 100644 --- a/src/appl/telnet/libtelnet/auth.c +++ b/src/appl/telnet/libtelnet/auth.c @@ -85,6 +85,7 @@ int auth_debug_mode = 0; int auth_has_failed = 0; int auth_enable_encrypt = 0; +int auth_client_non_unix = 0; static char *Name = "Noname"; static int Server = 0; static Authenticator *authenticated = 0; @@ -337,15 +338,28 @@ auth_request() authenticating = 1; while (ap->type) { if (i_support & ~i_wont_support & typemask(ap->type)) { - if (auth_debug_mode) { - printf(">>>%s: Sending type %d %d\r\n", - Name, ap->type, ap->way); + if (ap->type == AUTHTYPE_KERBEROS_V4 || + !auth_client_non_unix) { + if (auth_debug_mode) { + printf(">>>%s: Sending type %d %d\r\n", + Name, ap->type, ap->way); + } + *e++ = ap->type; + *e++ = ap->way; } - *e++ = ap->type; - *e++ = ap->way; } ++ap; } + if (auth_client_non_unix) { + ap = authenticators; + while (ap->type) { + if (i_support & ~i_wont_support & typemask(ap->type)) { + *e++ = ap->type; + *e++ = ap->way; + } + ++ap; + } + } *e++ = IAC; *e++ = SE; net_write(str_request, e - str_request); @@ -562,7 +576,7 @@ auth_intr(sig) auth_finished(0, AUTH_REJECT); } - int + void auth_wait(name) char *name; { @@ -570,7 +584,7 @@ auth_wait(name) printf(">>>%s: in auth_wait.\r\n", Name); if (Server && !authenticating) - return(0); + return; (void) signal(SIGALRM, auth_intr); alarm(30); @@ -579,7 +593,12 @@ auth_wait(name) break; alarm(0); (void) signal(SIGALRM, SIG_DFL); +} + int +auth_check(name) + char *name; +{ /* * Now check to see if the user is valid or not */ diff --git a/src/appl/telnet/telnetd/ChangeLog b/src/appl/telnet/telnetd/ChangeLog index 1902a32f7..760cbb23f 100644 --- a/src/appl/telnet/telnetd/ChangeLog +++ b/src/appl/telnet/telnetd/ChangeLog @@ -1,3 +1,15 @@ +2004-11-15 Tom Yu + + * ext.h: New variable "must_encrypt". + + * telnetd.8: Update for changed command-line options. + + * telnetd.c (getterminaltype): Merge Athena changes to require + encrypted connections. + + * utility.c (ttsuck): Merge Athena changes to work around some + client timing bugs. + 2004-09-22 Tom Yu * Makefile.in (telnetd): Use UTIL_LIB. diff --git a/src/appl/telnet/telnetd/ext.h b/src/appl/telnet/telnetd/ext.h index 9fe38ef8b..7b77a44b0 100644 --- a/src/appl/telnet/telnetd/ext.h +++ b/src/appl/telnet/telnetd/ext.h @@ -88,6 +88,10 @@ extern char *unptyip; /* pointer to remaining characters in buffer */ extern int pty, net; extern int SYNCHing; /* we are in TELNET SYNCH mode */ +#ifdef ENCRYPTION +extern int must_encrypt; +#endif + extern void _termstat (void), add_slc (int, int, int), diff --git a/src/appl/telnet/telnetd/telnetd.8 b/src/appl/telnet/telnetd/telnetd.8 index 9426a0c3b..78700cbed 100644 --- a/src/appl/telnet/telnetd/telnetd.8 +++ b/src/appl/telnet/telnetd/telnetd.8 @@ -39,7 +39,7 @@ protocol server .SH SYNOPSIS .B /usr/libexec/telnetd [\fB\-a\fP \fIauthmode\fP] [\fB\-B\fP] [\fB\-D\fP] [\fIdebugmode\fP] -[\fB\-edebug\fP] [\fB\-h\fP] [\fB\-I\fP\fIinitid\fP] [\fB\-l\fP] +[\fB\-e\fP] [\fB\-h\fP] [\fB\-I\fP\fIinitid\fP] [\fB\-l\fP] [\fB\-k\fP] [\fB\-n\fP] [\fB\-r\fP\fIlowpty-highpty\fP] [\fB\-s\fP] [\fB\-S\fP \fItos\fP] [\fB\-U\fP] [\fB\-X\fP \fIauthtype\fP] [\fB\-w\fP [\fBip\fP|\fImaxhostlen\fP[\fB,\fP[\fBno\fP]\fBstriplocal\fP]]] @@ -163,6 +163,9 @@ Displays the data stream received by .B ptydata Displays data written to the pty. .TP +.B encrypt +Enables encryption debugging code. +.TP .B exercise Has not been implemented yet. .RE @@ -175,12 +178,10 @@ Enables debugging on each socket created by in .IR socket (2)). .TP -.B \-edebug -If +.B \-e +This option causes .B telnetd -has been compiled with support for data encryption, then the -.B edebug -option may be used to enable encryption debugging code. +to refuse unencrypted connections. .TP .B \-h Disables the printing of host-specific information before login has been diff --git a/src/appl/telnet/telnetd/telnetd.c b/src/appl/telnet/telnetd/telnetd.c index 5633d5625..a90fa5c5c 100644 --- a/src/appl/telnet/telnetd/telnetd.c +++ b/src/appl/telnet/telnetd/telnetd.c @@ -173,7 +173,7 @@ char valid_opts[] = { 'D', ':', #endif #ifdef ENCRYPTION - 'e', ':', + 'e', #endif #if defined(CRAY) && defined(NEWINIT) 'I', ':', @@ -308,6 +308,9 @@ main(argc, argv) diagnostic |= TD_PTYDATA; } else if (!strcmp(optarg, "options")) { diagnostic |= TD_OPTIONS; + } else if (!strcmp(optarg, "encrypt")) { + extern int encrypt_debug_mode; + encrypt_debug_mode = 1; } else { usage(); /* NOT REACHED */ @@ -317,13 +320,7 @@ main(argc, argv) #ifdef ENCRYPTION case 'e': - if (strcmp(optarg, "debug") == 0) { - extern int encrypt_debug_mode; - encrypt_debug_mode = 1; - break; - } - usage(); - /* NOTREACHED */ + must_encrypt = 1; break; #endif /* ENCRYPTION */ @@ -694,8 +691,12 @@ usage() static void encrypt_failure() { - char *lerror_message = - "Encryption was not successfully negotiated. Goodbye.\r\n\r\n"; + char *lerror_message; + + if (auth_must_encrypt()) + lerror_message = "Encryption was not successfully negotiated. Goodbye.\r\n\r\n"; + else + lerror_message = "Unencrypted connection refused. Goodbye.\r\n\r\n"; netputs(lerror_message); netflush(); @@ -720,6 +721,7 @@ getterminaltype(name) settimer(baseline); #if defined(AUTHENTICATION) + ttsuck(); /* * Handle the Authentication option before we do anything else. */ @@ -727,7 +729,7 @@ getterminaltype(name) while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) ttloop(); if (his_state_is_will(TELOPT_AUTHENTICATION)) { - retval = auth_wait(name); + auth_wait(name); } #endif @@ -760,15 +762,25 @@ getterminaltype(name) if (his_state_is_will(TELOPT_ENCRYPT)) { encrypt_wait(); } - if (auth_must_encrypt()) { + if (must_encrypt || auth_must_encrypt()) { time_t timeout = time(0) + 60; if (my_state_is_dont(TELOPT_ENCRYPT) || - my_state_is_wont(TELOPT_ENCRYPT)) + my_state_is_wont(TELOPT_ENCRYPT) || + his_state_is_wont(TELOPT_AUTHENTICATION)) encrypt_failure(); - if (!EncryptStartInput() || !EncryptStartOutput()) - encrypt_failure(); + while (!EncryptStartInput()) { + if (time (0) > timeout) + encrypt_failure(); + ttloop(); + } + + while (!EncryptStartOutput()) { + if (time (0) > timeout) + encrypt_failure(); + ttloop(); + } while (!encrypt_is_encrypting()) { if (time(0) > timeout) @@ -865,7 +877,11 @@ getterminaltype(name) } } } - return(retval); +#ifdef AUTHENTICATION + return(auth_check(name)); +#else + return(-1); +#endif } /* end of getterminaltype */ static void diff --git a/src/appl/telnet/telnetd/utility.c b/src/appl/telnet/telnetd/utility.c index fc8ff7601..f4568ec28 100644 --- a/src/appl/telnet/telnetd/utility.c +++ b/src/appl/telnet/telnetd/utility.c @@ -90,6 +90,67 @@ read_again: } } /* end of ttloop */ +/* + * ttsuck - This is a horrible kludge to deal with a bug in + * HostExplorer. HostExplorer thinks it knows how to do krb5 auth, but + * it doesn't really. So if you offer it krb5 as an auth choice before + * krb4, it will sabotage the connection. So we peek ahead into the + * input stream to see if the client is a UNIX client, and then + * (later) offer krb5 first only if it is. Since no Mac/PC telnet + * clients do auto switching between krb4 and krb5 like the UNIX + * client does, it doesn't matter what order they see the choices in + * (except for HostExplorer). + * + * It is actually not possible to do this without looking ahead into + * the input stream: the client and server both try to begin + * auth/encryption negotiation as soon as possible, so if we let the + * server process things normally, it will already have sent the list + * of supported auth types before seeing the NEW-ENVIRON option. If + * you change the code to hold off sending the list of supported auth + * types until after it knows whether or not the remote side supports + * NEW-ENVIRON, then the auth negotiation and encryption negotiation + * race conditions won't interact properly, and encryption negotiation + * will reliably fail. + */ + + void +ttsuck() +{ + extern int auth_client_non_unix; + int nread; + struct timeval tv; + fd_set fds; + char *p, match[] = {IAC, WILL, TELOPT_NEW_ENVIRON}; + + if (nfrontp-nbackp) { + netflush(); + } + tv.tv_sec = 1; + tv.tv_usec = 0; + FD_SET(net, &fds); + + while (select(net + 1, &fds, NULL, NULL, &tv) == 1) + { + nread = read(net, netibuf + ncc, sizeof(netibuf) - ncc); + if (nread <= 0) + break; + ncc += nread; + } + + auth_client_non_unix = 1; + for (p = netibuf; p < netibuf + ncc; p++) + { + if (!memcmp(p, match, sizeof(match))) + { + auth_client_non_unix = 0; + break; + } + } + + if (ncc > 0) + telrcv(); +} + /* * Check a descriptor to see if out of band data exists on it. */ diff --git a/src/tests/dejagnu/krb-root/ChangeLog b/src/tests/dejagnu/krb-root/ChangeLog index df5650558..328a26cfd 100644 --- a/src/tests/dejagnu/krb-root/ChangeLog +++ b/src/tests/dejagnu/krb-root/ChangeLog @@ -1,3 +1,10 @@ +2004-11-15 Tom Yu + + * telnet.exp (telnet_test): Work around possible race condition + with client's resetting of terminal mode when returning from + interactive command mode. Test whether requiring encryption + works. + 2004-03-14 Ken Raeburn * rlogin.exp (start_rlogin_daemon, rlogin_test): Use portbase to diff --git a/src/tests/dejagnu/krb-root/telnet.exp b/src/tests/dejagnu/krb-root/telnet.exp index 17ca35e3c..03bdb7910 100644 --- a/src/tests/dejagnu/krb-root/telnet.exp +++ b/src/tests/dejagnu/krb-root/telnet.exp @@ -50,7 +50,7 @@ if {![get_hostname] \ # A procedure to start up the telnet daemon. -proc start_telnet_daemon { } { +proc start_telnet_daemon { args } { global REALMNAME global TELNETD global LOGINKRB5 @@ -70,7 +70,7 @@ proc start_telnet_daemon { } { # we don't need to use inetd. The portbase+8 is the port to listen at. # Note that tmppwd here is a shell variable, which is set in # setup_root_shell, not a TCL variable. - send -i $rlogin_spawn_id "sh -c \"$TELNETD -debug -t \$tmppwd/srvtab -R $REALMNAME -L $tmppwd/login.wrap -X KERBEROS_V4 [expr 8 + $portbase]\" &\r" + send -i $rlogin_spawn_id "sh -c \"$TELNETD $args -debug -t \$tmppwd/srvtab -R $REALMNAME -L $tmppwd/login.wrap -X KERBEROS_V4 [expr 8 + $portbase]\" &\r" expect { -i $rlogin_spawn_id -re "$ROOT_PROMPT" { } @@ -174,7 +174,7 @@ proc telnet_test { } { set testname "simple telnet" expect { - "ogin:" { + "ogin: " { pass $testname } } @@ -184,7 +184,7 @@ proc telnet_test { } { set testname "telnet command mode" send "\035" expect { - "telnet>" { + "telnet> " { pass $testname } } @@ -200,9 +200,16 @@ proc telnet_test { } { } set testname "back to command mode" + + # For some reason, the telnet client doesn't necessarily reset the + # terminal mode back to raw after exiting command mode. + # Kick it somewhat by sending a CR. + send "\r" + expect "ogin: " + send "\035" expect { - "telnet>" { + "telnet> " { pass $testname } } @@ -299,6 +306,7 @@ proc telnet_test { } { } expect_after + catch "expect eof" # We can't use check_exit_status, because we expect an exit status # of 1. @@ -355,7 +363,7 @@ proc telnet_test { } { # Move back to telnet command mode and check the encryption status. set testname "encryption status" send "\035" - expect "telnet>" + expect "telnet> " send "status\r" expect { -re "Currently encrypting output with DES_CFB64.*Currently decrypting input with DES_CFB64" { @@ -368,6 +376,7 @@ proc telnet_test { } { expect "Connection closed by foreign host.\r" expect_after + catch "expect eof" # We can't use check_exit_status, because we expect an exit status # of 1. @@ -384,6 +393,38 @@ proc telnet_test { } { # The telnet daemon should have stopped, but we have no easy way # of checking whether it actually did. Kill it just in case. stop_telnet_daemon + + set testname "reject unencrypted telnet" + # Check rejection of unencrypted client when encryption is required + start_telnet_daemon -e + + # unencrypted, unauthenticated + spawn $TELNET -- $hostname -[expr 8 + $portbase] + expect_after { + timeout { + fail $testname + catch "expect_after" + return + } + eof { + fail $testname + catch "expect_after" + return + } + } + + expect { + -re "Unencrypted connection refused.*\n" { + pass $testname + } + } + catch "expect_after" + catch "expect eof" + catch wait + + # The telnet daemon should have stopped, but we have no easy way + # of checking whether it actually did. Kill it just in case. + stop_telnet_daemon } # Run the test. Logging in sometimes takes a while, so increase the -- 2.26.2