Merge Athena changes for requiring encrypted connections
authorTom Yu <tlyu@mit.edu>
Mon, 15 Nov 2004 21:25:41 +0000 (21:25 +0000)
committerTom Yu <tlyu@mit.edu>
Mon, 15 Nov 2004 21:25:41 +0000 (21:25 +0000)
ticket: 841
tags: pullup

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16866 dc483132-0cff-0310-8789-dd5450dbe970

src/appl/telnet/libtelnet/ChangeLog
src/appl/telnet/libtelnet/auth-proto.h
src/appl/telnet/libtelnet/auth.c
src/appl/telnet/telnetd/ChangeLog
src/appl/telnet/telnetd/ext.h
src/appl/telnet/telnetd/telnetd.8
src/appl/telnet/telnetd/telnetd.c
src/appl/telnet/telnetd/utility.c
src/tests/dejagnu/krb-root/ChangeLog
src/tests/dejagnu/krb-root/telnet.exp

index 9e5c888d76676fc4d49365627b65da23a52e7a1e..3467422dd3268d466e006e43bab0218eb2e59a30 100644 (file)
@@ -1,3 +1,8 @@
+2004-11-15  Tom Yu  <tlyu@mit.edu>
+
+       * auth-proto.h, auth.c: Merge Athena changes for requiring
+       encrypted connections.
+
 2004-06-04  Ken Raeburn  <raeburn@mit.edu>
 
        * Makefile.in (LIBBASE): Renamed from LIB.
index 68cae7efcebecd7bc9c291185f1b1c71895763d8..6b49570329b39fdcf8e178349609752aad17ec8c 100644 (file)
@@ -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);
index 9c1b0b0fc404cfe92efe875511b3195e04a174fc..28b8ae8d1bcfcdcb08d78b1e2c1e1a3bb20f07d9 100644 (file)
@@ -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
         */
index 1902a32f7d16683e0fee772fae9efa8f1c07e458..760cbb23f3d18cef3fa7e4825b41f6ac1c50ee01 100644 (file)
@@ -1,3 +1,15 @@
+2004-11-15  Tom Yu  <tlyu@mit.edu>
+
+       * 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  <tlyu@mit.edu>
 
        * Makefile.in (telnetd): Use UTIL_LIB.
index 9fe38ef8b07d7b06b81d809040a5e0af99217e9e..7b77a44b0c6810b23ddf711ec5b2eb63eaddfec4 100644 (file)
@@ -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),
index 9426a0c3b49374cb9debf6c2bf2758e18ebaa089..78700cbedb77b177d2f37cc83300097c70a76380 100644 (file)
@@ -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
index 5633d562513d707f2ef59b72d7c3edde3de85b9f..a90fa5c5cfd96b18f180debb81bc3acb7ea29e56 100644 (file)
@@ -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
index fc8ff7601eb08b489494840d8e15a35b1756dfaf..f4568ec289c95c620f795c9f081a1741e970254f 100644 (file)
@@ -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.
  */
index df5650558eaaec749d478d9e33fe5349206d0eca..328a26cfdb9f9dbbbabfd28cf1cb3b3a2cfe5945 100644 (file)
@@ -1,3 +1,10 @@
+2004-11-15  Tom Yu  <tlyu@mit.edu>
+
+       * 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  <raeburn@mit.edu>
 
        * rlogin.exp (start_rlogin_daemon, rlogin_test): Use portbase to
index 17ca35e3cf9b3c7614b278b2d5916bfcd3d980f1..03bdb79103366fd9866baf1fe58e0d7a32791f15 100644 (file)
@@ -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