$NetBSD: patch-bk,v 1.2 2000/03/20 02:25:57 itojun Exp $

--- sshconnect.c-	Wed May 12 20:19:29 1999
+++ sshconnect.c	Mon Mar 20 09:55:37 2000
@@ -215,7 +215,6 @@
 #include "userfile.h"
 #include "emulate.h"
 
-#ifdef KERBEROS
 #ifdef KRB5
 #include <krb5.h>
 
@@ -223,7 +222,19 @@
 krb5_context ssh_context = 0;
 krb5_auth_context auth_context = 0;
 #endif /* KRB5 */
-#endif /* KERBEROS */
+
+#ifdef KRB4
+#include <krb.h>
+#ifdef AFS
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 4
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#include <kafs.h>
+#endif /* AFS */
+#endif /* KRB4 */
 
 /* Session id for the current session. */
 unsigned char session_id[16];
@@ -932,10 +943,9 @@
   return 0;
 }
 
-#ifdef KERBEROS
+#ifdef KRB5
 int try_kerberos_authentication(void)
 {
-#ifdef KRB5
   char *remotehost;
   krb5_data auth;
   krb5_error_code r;
@@ -1084,15 +1094,118 @@
     krb5_free_ap_rep_enc_part(ssh_context, repl);
   
   return(ret_stat);
+}
 #endif /* KRB5 */
+
+#ifdef KRB4
+int try_kerberos_authentication()
+{
+  KTEXT_ST auth;                     /* Kerberos data */
+  char *reply;
+  char inst[INST_SZ];
+  char *realm;
+  char *service;
+  CREDENTIALS cred;
+  int r, type;
+  Key_schedule schedule;
+  u_long checksum, cksum;
+  MSG_DAT msg_data;
+  struct sockaddr_in local, foreign;
+  struct stat st;
+  
+  /* Don't do anything if we don't have any tickets. */
+  if (stat(tkt_string(), &st) < 0) return 0;
+  
+  strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);
+  
+  realm = (char *)krb_realmofhost(get_canonical_hostname());
+  if (!realm) {
+    debug("Kerberos V4: no realm for %s", get_canonical_hostname());
+    return 0;
+  }
+  /* This can really be anything. */
+  checksum = (u_long) getpid();
+  
+  if (r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum)) {
+    debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]);
+    return 0;
+  }
+  /* Get session key to decrypt the server's reply with. */
+  if (r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred)) {
+     debug("get_cred failed: %s", krb_err_txt[r]);
+     return 0;
+  }
+  des_key_sched((des_cblock *)cred.session, schedule);
+  
+  /* Send authentication info to server. */
+  packet_start(SSH_CMSG_AUTH_KERBEROS);
+  packet_put_string((char *)auth.dat, auth.length);
+  packet_send();
+  packet_write_wait();
+  
+  /* zero the buffer */
+  (void) memset(auth.dat, 0, MAX_KTXT_LEN);
+  
+  r = sizeof(local);
+  memset(&local, 0, sizeof(local));
+  if (getsockname(packet_get_connection_in(),
+ 		  (struct sockaddr *) &local, &r) < 0)
+    debug("getsockname failed: %.100s", strerror(errno));
+  
+  r = sizeof(foreign);
+  memset(&foreign, 0, sizeof(foreign));
+   if (getpeername(packet_get_connection_in(),
+		   (struct sockaddr *)&foreign, &r) < 0)
+     debug("getpeername failed: %.100s", strerror(errno));
+   
+   /* Get server reply. */
+   type = packet_read();
+   switch(type) {
+     
+   case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */
+     debug("Kerberos V4 authentication failed.");
+     return 0;
+     break;
+     
+   case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */
+     debug("Kerberos V4 authentication accepted.");
+     
+     /* Get server's response. */
+     reply = packet_get_string((unsigned int *)&auth.length);
+     memcpy(auth.dat, reply, auth.length);
+     xfree(reply);
+     
+     /* If his response isn't properly encrypted with the session key,
+        and the decrypted checksum fails to match, he's bogus. Bail out. */
+     if (r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session,
+			 &foreign, &local, &msg_data)) {
+       debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]);
+       packet_disconnect("Kerberos V4 challenge failed!");
+     }
+     /* fetch the (incremented) checksum that we supplied in the request */
+     (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum));
+     cksum = ntohl(cksum);
+     
+     /* If it matches, we're golden. */
+     if (cksum == checksum + 1) {
+       debug("Kerberos V4 challenge successful.");
+       return 1;
+     }
+     else
+       packet_disconnect("Kerberos V4 challenge failed!");
+     break;
+     
+   default:
+     packet_disconnect("Protocol error on Kerberos V4 response: %d", type);
+   }
 }
-#endif /* KERBEROS */
+#endif /* KRB4 */
+
 
-#ifdef KERBEROS_TGT_PASSING
 /* Forward our local Kerberos tgt to the server. */
+#ifdef KRB5
 int send_kerberos_tgt(void)
 {
-#ifdef KRB5
   char *remotehost;
   krb5_principal client;
   krb5_principal server;
@@ -1172,22 +1285,117 @@
     krb5_free_principal(ssh_context, client);
     krb5_free_principal(ssh_context, server);
     
-    type = packet_read();
-    if (type == SSH_SMSG_SUCCESS)
-      {
-        debug("Kerberos V5 TGT passing was successful.");
-        return 1;
-      }
-    else
-      if (type != SSH_SMSG_FAILURE)
-        packet_disconnect("Protocol error on Kerberos tgt response: %d", type);
-      else 
-        debug("Kerberos V5 TGT passing failed.");
-    
-    return 0;
+    return 1;
+}
 #endif /* KRB5 */
+
+#ifdef AFS
+int send_kerberos_tgt()
+{
+  CREDENTIALS *creds;
+  char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
+  int r, type;
+  unsigned char buffer[8192];
+  struct stat st;
+
+  /* Don't do anything if we don't have any tickets. */
+  if (stat(tkt_string(), &st) < 0) return 0;
+      
+  creds = xmalloc(sizeof(CREDENTIALS));
+  
+  if ((r=krb_get_tf_fullname(TKT_FILE,pname,pinst,prealm)) != KSUCCESS) {
+    debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]);
+    return 0;
+  }
+  if ((r=krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) {
+    debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]);
+    return 0;
+  }
+  if (time(0) >
+#ifdef HAVE_KRB_LIFE_TO_TIME
+      (unsigned long)krb_life_to_time(creds->issue_date, creds->lifetime)) {
+#else
+      (creds->issue_date + ((unsigned char)creds->lifetime * 5 * 60))) {
+#endif /* HAVE_KRB_LIFE_TO_TIME */
+    debug("Kerberos V4 ticket expired: %s", TKT_FILE);
+    return 0;
+  }
+
+  creds_to_radix(creds, buffer);
+  xfree(creds);
+    
+  packet_start(SSH_CMSG_HAVE_KERBEROS_TGT);
+  packet_put_string((char *)buffer, strlen(buffer));
+  packet_send();
+  packet_write_wait();
+
+  return 1;
 }
-#endif /* KERBEROS_TGT_PASSING */
+
+/* Forwards our AFS tokens to the server. */
+void send_afs_tokens(void)
+{
+  CREDENTIALS creds;
+  struct ViceIoctl parms;
+  struct ClearToken ct;
+  int i, type;
+  int len;
+  char buf[2048], *p, *server_cell;
+  unsigned char buffer[8192];
+
+  /* Move over ktc_GetToken, here's something leaner. */
+  for (i = 0; i < 100; i++) { /* just in case */
+    parms.in = (char *)&i;
+    parms.in_size = sizeof(i);
+    parms.out = buf;
+    parms.out_size = sizeof(buf);
+    if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break;
+    p = buf;
+    
+    /* Get secret token. */
+    memcpy(&creds.ticket_st.length, p, sizeof(unsigned int));
+    if (creds.ticket_st.length > MAX_KTXT_LEN) break;
+    p += sizeof(unsigned int);
+    memcpy(creds.ticket_st.dat, p, creds.ticket_st.length);
+    p += creds.ticket_st.length;
+        
+    /* Get clear token. */
+    memcpy(&len, p, sizeof(len));
+    if (len != sizeof(struct ClearToken)) break;
+    p += sizeof(len);
+    memcpy(&ct, p, len);
+    p += len;
+    p += sizeof(len); /* primary flag */
+    server_cell = p;
+
+    /* Flesh out our credentials. */
+    strcpy(creds.service, "afs");
+    creds.instance[0] = '\0';
+    strncpy(creds.realm, server_cell, REALM_SZ);
+    memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ);
+    creds.issue_date = ct.BeginTimestamp;
+    creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp);
+    creds.kvno = ct.AuthHandle;
+    snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId);
+    creds.pinst[0] = '\0';
+
+    /* Encode token, ship it off. */
+    if (!creds_to_radix(&creds, buffer)) break;
+    packet_start(SSH_CMSG_HAVE_AFS_TOKEN);
+    packet_put_string((char *)buffer, strlen(buffer));
+    packet_send();
+    packet_write_wait();
+
+    /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */
+    type = packet_read();
+
+    if (type == SSH_SMSG_FAILURE)
+      debug("AFS token for cell %s rejected.", server_cell);
+    else if (type != SSH_SMSG_SUCCESS)
+      packet_disconnect("Protocol error on AFS token response: %d", type);
+  }  
+}
+#endif /* AFS */
 
 /* Waits for the server identification string, and sends our own identification
    string. */
@@ -1285,14 +1493,12 @@
   unsigned char check_bytes[8];
   unsigned int supported_ciphers, supported_authentications, protocol_flags;
   HostStatus host_status;
-#ifdef KERBEROS 
 #ifdef KRB5
   char *kuser;
   krb5_ccache ccache;
   krb5_error_code problem;
   krb5_principal client;
-#endif
-#endif
+#endif /* KRB5 */
   
   /* Convert the user-supplied hostname into all lowercase. */
   host = xstrdup(orighost);
@@ -1595,7 +1801,6 @@
 
   debug("Received encrypted confirmation.");
 
-#ifdef KERBEROS 
 #ifdef KRB5
   if (!ssh_context)
     {
@@ -1629,7 +1834,6 @@
         debug("Kerberos V5: could not get default ccache.");
     }
 #endif /* KRB5 */
-#endif /* KERBEROS */
   
   /* Send the name of the user to log in as on the server. */
   packet_start(SSH_CMSG_USER);
@@ -1647,24 +1851,39 @@
     packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER",
                       type);
 
-#ifdef KERBEROS_TGT_PASSING
+#if defined(KRB5) || defined(AFS)
   /* Try Kerberos tgt passing if the server supports it. */
   if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) &&
       options->kerberos_tgt_passing)
     {
       if (options->cipher == SSH_CIPHER_NONE)
         log_msg("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!");
-      (void)send_kerberos_tgt();
+      if (send_kerberos_tgt())
+      {
+        type = packet_read();
+        if (type == SSH_SMSG_FAILURE)
+	  debug("Kerberos TGT passing failed.");
+        else if (type != SSH_SMSG_SUCCESS)
+	  packet_disconnect("Protocol error on Kerberos tgt response: %d", type);
+      }
     }
-#endif /* KERBEROS_TGT_PASSING */
+#endif /* KRB5 || AFS */
+
+#ifdef AFS
+  /* Try AFS token passing if the server supports it. */
+  if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) &&
+      options->afs_token_passing && k_hasafs())  {
+    if (options->cipher == SSH_CIPHER_NONE)
+      log_msg("WARNING: Encryption is disabled! Token will be transmitted in the clear!");
+    send_afs_tokens();
+  }
+#endif /* AFS */
   
-#ifdef KERBEROS
-#ifdef KRB5
+#if defined(KRB4) || defined(KRB5)
   if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&
       options->kerberos_authentication)
     {
-      debug("Trying Kerberos V5 authentication.");
-#endif
+      debug("Trying Kerberos authentication.");
       if (try_kerberos_authentication()) {
         /* The server should respond with success or failure. */
         type = packet_read();
@@ -1673,10 +1892,8 @@
         if (type != SSH_SMSG_FAILURE)
           packet_disconnect("Protocol error: got %d in response to Kerberos auth", type);
       }
-#ifdef KRB5
     }
-#endif
-#endif /* KERBEROS */
+#endif /* KRB4 || KRB5 */
 
   /* Use rhosts authentication if running in privileged socket and we do not
      wish to remain anonymous. */
