Patch from Luke Howard to add mutual kerberos authentication, and SMB session
authorAndrew Bartlett <abartlet@samba.org>
Mon, 24 Feb 2003 11:09:21 +0000 (11:09 +0000)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 24 Feb 2003 11:09:21 +0000 (11:09 +0000)
keys for kerberos authentication.

Andrew Bartlett

source/configure.in
source/include/ads.h
source/include/asn_1.h
source/libads/kerberos_verify.c
source/libsmb/cliconnect.c
source/libsmb/clikrb5.c
source/libsmb/clispnego.c
source/smbd/sesssetup.c

index 66bdf5edf198e43a51579bed350fc820a513272a..e3b5f2fe1565d1e0388a2b14f00b55ba4d2fd791 100644 (file)
@@ -2250,6 +2250,22 @@ if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
     AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,[Whether the krb5_ticket struct has a enc_part2 property])
 fi
 
+AC_CACHE_CHECK([for keyvalue in krb5_keyblock],samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_keyblock key; key.keyvalue.data = NULL;],
+samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
+if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
+    AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,[Whether the krb5_keyblock struct has a keyvalue property])
+fi
+
+AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
+samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
+if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes"; then
+    AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
+fi
+
   ########################################################
   # now see if we can find the krb5 libs in standard paths
   # or as specified above
index 304a997b2ce7dca3f5ff2f9a9328ec877791588c..f90983e4052c61c068dc67d14bdea6fd2a99e9ec 100644 (file)
@@ -208,3 +208,8 @@ typedef void **ADS_MODLIST;
 
 /* Kerberos environment variable names */
 #define KRB5_ENV_CCNAME "KRB5CCNAME"
+
+/* Heimdal uses a slightly different name */
+#if defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
+#define ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC_MD5
+#endif
index ab7fa5d398e0dc4d800f28b3f0b0f3a7124110a7..7d4da0db0c88d6d8833b4022a1af5d329506e2f0 100644 (file)
@@ -59,4 +59,11 @@ typedef struct {
 #define SPNEGO_NEG_RESULT_INCOMPLETE 1
 #define SPNEGO_NEG_RESULT_REJECT 2
 
+/* not really ASN.1, but RFC 1964 */
+#define TOK_ID_KRB_AP_REQ      "\x01\x00"
+#define TOK_ID_KRB_AP_REP      "\x02\x00"
+#define TOK_ID_KRB_ERROR       "\x03\x00"
+#define TOK_ID_GSS_GETMIC      "\x01\x01"
+#define TOK_ID_GSS_WRAP                "\x02\x01"
+
 #endif /* _ASN_1_H */
index 17fecf60c81b4c4c55e9dc80dc6de8019fbce240..4d9a1bf7657301b1bc58c7db39fa7acf808ed7cc 100644 (file)
@@ -3,7 +3,7 @@
    kerberos utility library
    Copyright (C) Andrew Tridgell 2001
    Copyright (C) Remus Koos 2001
-   
+   Copyright (C) Luke Howard 2003   
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -29,7 +29,9 @@
   authorization_data if available 
 */
 NTSTATUS ads_verify_ticket(ADS_STRUCT *ads, const DATA_BLOB *ticket, 
-                          char **principal, DATA_BLOB *auth_data)
+                          char **principal, DATA_BLOB *auth_data,
+                          DATA_BLOB *ap_rep,
+                          uint8 session_key[16])
 {
        krb5_context context;
        krb5_auth_context auth_context = NULL;
@@ -122,10 +124,24 @@ NTSTATUS ads_verify_ticket(ADS_STRUCT *ads, const DATA_BLOB *ticket,
        if (!auth_ok) {
                DEBUG(3,("krb5_rd_req with auth failed (%s)\n", 
                         error_message(ret)));
-               SAFE_FREE(key);
                return NT_STATUS_LOGON_FAILURE;
        }
 
+       ret = krb5_mk_rep(context, auth_context, &packet);
+       if (ret) {
+               DEBUG(3,("Failed to generate mutual authentication reply (%s)\n",
+                       error_message(ret)));
+               krb5_auth_con_free(context, auth_context);
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       *ap_rep = data_blob(packet.data, packet.length);
+       free(packet.data);
+
+       krb5_get_smb_session_key(context, auth_context, session_key);
+       DEBUG(0,("SMB session key (from ticket) follows:\n"));
+       dump_data(0, session_key, 16);
+
 #if 0
        file_save("/tmp/ticket.dat", ticket->data, ticket->length);
 #endif
@@ -134,20 +150,24 @@ NTSTATUS ads_verify_ticket(ADS_STRUCT *ads, const DATA_BLOB *ticket,
 
 #if 0
        if (tkt->enc_part2) {
-               file_save("/tmp/authdata.dat", 
+               file_save("/tmp/authdata.dat",
                          tkt->enc_part2->authorization_data[0]->contents,
                          tkt->enc_part2->authorization_data[0]->length);
-       }
 #endif
 
        if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
                                     principal))) {
                DEBUG(3,("krb5_unparse_name failed (%s)\n", 
                         error_message(ret)));
+               data_blob_free(auth_data);
+               data_blob_free(ap_rep);
+               krb5_auth_con_free(context, auth_context);
                return NT_STATUS_LOGON_FAILURE;
        }
 
+       krb5_auth_con_free(context, auth_context);
+
        return NT_STATUS_OK;
 }
 
-#endif
+#endif /* HAVE_KRB5 */
index 901daf4b0940e565e8522faf6d479876887fdcae..4962ffa3c9ecad67c57617c53bfc9e048d681415 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/CIFS implementation.
    client connect/disconnect routines
    Copyright (C) Andrew Tridgell 1994-1998
-   Copyright (C) Andrew Barteltt 2001-2003
+   Copyright (C) Andrew Bartlett 2001-2003
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
index bef6998a493ebdcd35427eb2cf75697bb6282177..47dec1f171048c83eabf55e04ebe56724fd51c5f 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/CIFS implementation.
    simple kerberos5 routines for active directory
    Copyright (C) Andrew Tridgell 2001
-   Copyright (C) Luke Howard 2002
+   Copyright (C) Luke Howard 2002-2003
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #ifdef HAVE_KRB5
 
+#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+#define KRB5_KEY_TYPE(k)       ((k)->keytype)
+#define KRB5_KEY_LENGTH(k)     ((k)->keyvalue.length)
+#define KRB5_KEY_DATA(k)       ((k)->keyvalue.data)
+#else
+#define        KRB5_KEY_TYPE(k)        ((k)->enctype)
+#define KRB5_KEY_LENGTH(k)     ((k)->length)
+#define KRB5_KEY_DATA(k)       ((k)->contents)
+#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+
 #ifndef HAVE_KRB5_SET_REAL_TIME
 /*
  * This function is not in the Heimdal mainline.
@@ -124,7 +134,7 @@ krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
        return krb5_get_default_in_tkt_etypes(context, enctypes);
 }
 #else
- __ERROR_XX_UNKNOWN_GET_ENCTYPES_FUNCTIONS
+#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
 #endif
 
  void free_kerberos_etypes(krb5_context context, 
@@ -305,12 +315,12 @@ DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
        DATA_BLOB ret;
        krb5_enctype enc_types[] = {
 #ifdef ENCTYPE_ARCFOUR_HMAC
-               ENCTYPE_ARCFOUR_HMAC, 
-#endif
-                                   ENCTYPE_DES_CBC_MD5, 
-                                   ENCTYPE_DES_CBC_CRC, 
-                                   ENCTYPE_NULL};
-
+               ENCTYPE_ARCFOUR_HMAC,
+#endif 
+               ENCTYPE_DES_CBC_MD5, 
+               ENCTYPE_DES_CBC_CRC, 
+               ENCTYPE_NULL};
+       
        retval = krb5_init_context(&context);
        if (retval) {
                DEBUG(1,("krb5_init_context failed (%s)\n", 
@@ -355,11 +365,39 @@ failed:
        return data_blob(NULL, 0);
 }
 
+BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16])
+ {
+       krb5_keyblock *skey;
+       BOOL ret = False;
+
+       memset(session_key, 0, 16);
+
+#ifdef ENCTYPE_ARCFOUR_HMAC
+       if (krb5_auth_con_getremotesubkey(context, auth_context, &skey) == 0 && skey != NULL) {
+               if (KRB5_KEY_TYPE(skey) ==
+                   ENCTYPE_ARCFOUR_HMAC
+                   && KRB5_KEY_LENGTH(skey) == 16) {
+                       memcpy(session_key, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+                       ret = True;
+               }
+               krb5_free_keyblock(context, skey);
+       }
+#endif /* ENCTYPE_ARCFOUR_HMAC */
+
+       return ret;
+ }
 #else /* HAVE_KRB5 */
  /* this saves a few linking headaches */
- DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
+DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
  {
         DEBUG(0,("NO KERBEROS SUPPORT\n"));
         return data_blob(NULL, 0);
  }
+
+BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context ac, uint8 session_key[16])
+ {
+       DEBUG(0,("NO KERBEROS SUPPORT\n"));
+       memset(session_key, 0, 16);
+       return False;
+ }
 #endif
index e93f1855ddd9100af1528733ed0b3742ca968fd7..dfa8f801465fc52625f3b6c4761f548c752d9732 100644 (file)
@@ -3,6 +3,7 @@
    simple kerberos5/SPNEGO routines
    Copyright (C) Andrew Tridgell 2001
    Copyright (C) Jim McDonough   2002
+   Copyright (C) Luke Howard     2003
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -259,7 +260,7 @@ BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *se
 /*
   generate a krb5 GSS-API wrapper packet given a ticket
 */
-DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
+DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, uint8 tok_id[2])
 {
        ASN1_DATA data;
        DATA_BLOB ret;
@@ -268,7 +269,8 @@ DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
 
        asn1_push_tag(&data, ASN1_APPLICATION(0));
        asn1_write_OID(&data, OID_KERBEROS5);
-       asn1_write_BOOLEAN(&data, 0);
+
+       asn1_write(&data, tok_id, 2);
        asn1_write(&data, ticket.data, ticket.length);
        asn1_pop_tag(&data);
 
@@ -286,7 +288,7 @@ DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
 /*
   parse a krb5 GSS-API wrapper packet giving a ticket
 */
-BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
+BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
 {
        BOOL ret;
        ASN1_DATA data;
@@ -295,15 +297,15 @@ BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
        asn1_load(&data, blob);
        asn1_start_tag(&data, ASN1_APPLICATION(0));
        asn1_check_OID(&data, OID_KERBEROS5);
-       asn1_check_BOOLEAN(&data, 0);
 
        data_remaining = asn1_tag_remaining(&data);
 
-       if (data_remaining < 1) {
+       if (data_remaining < 3) {
                data.has_error = True;
        } else {
-               
-               *ticket = data_blob(data.data, data_remaining);
+               asn1_read(&data, tok_id, 2);
+               data_remaining -= 2;
+               *ticket = data_blob(NULL, data_remaining);
                asn1_read(&data, ticket->data, ticket->length);
        }
 
@@ -330,7 +332,7 @@ DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset)
        tkt = krb5_get_ticket(principal, time_offset);
 
        /* wrap that up in a nice GSS-API wrapping */
-       tkt_wrapped = spnego_gen_krb5_wrap(tkt);
+       tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
 
        /* and wrap that in a shiny SPNEGO wrapper */
        targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
@@ -438,9 +440,10 @@ BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
 }
 
 /*
-  generate a minimal SPNEGO NTLMSSP response packet.  Doesn't contain much.
+  generate a minimal SPNEGO response packet.  Doesn't contain much.
 */
-DATA_BLOB spnego_gen_auth_response(DATA_BLOB *ntlmssp_reply, NTSTATUS nt_status)
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
+                                  const char *mechOID)
 {
        ASN1_DATA data;
        DATA_BLOB ret;
@@ -462,13 +465,13 @@ DATA_BLOB spnego_gen_auth_response(DATA_BLOB *ntlmssp_reply, NTSTATUS nt_status)
        asn1_write_enumerated(&data, negResult);
        asn1_pop_tag(&data);
 
-       if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+       if (reply->data != NULL) {
                asn1_push_tag(&data,ASN1_CONTEXT(1));
-               asn1_write_OID(&data, OID_NTLMSSP);
+               asn1_write_OID(&data, mechOID);
                asn1_pop_tag(&data);
                
                asn1_push_tag(&data,ASN1_CONTEXT(2));
-               asn1_write_OctetString(&data, ntlmssp_reply->data, ntlmssp_reply->length);
+               asn1_write_OctetString(&data, reply->data, reply->length);
                asn1_pop_tag(&data);
        }
 
index eafe805aba9f53a3ff2f5add682b2f2f98e48658..7e5ec56a271394b9891b7f85ee06fdb2240d679c 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Tridgell 1998-2001
    Copyright (C) Andrew Bartlett      2001
    Copyright (C) Jim McDonough        2002
+   Copyright (C) Luke Howard          2003
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -146,11 +147,14 @@ static int reply_spnego_kerberos(connection_struct *conn,
        int sess_vuid;
        NTSTATUS ret;
        DATA_BLOB auth_data;
+       DATA_BLOB ap_rep, ap_rep_wrapped, response;
        auth_serversupplied_info *server_info = NULL;
        ADS_STRUCT *ads;
+       uint8 session_key[16];
+       uint8 tok_id[2];
        BOOL foreign = False;
 
-       if (!spnego_parse_krb5_wrap(*secblob, &ticket)) {
+       if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) {
                return ERROR_NT(NT_STATUS_LOGON_FAILURE);
        }
 
@@ -162,7 +166,7 @@ static int reply_spnego_kerberos(connection_struct *conn,
 
        ads->auth.realm = strdup(lp_realm());
 
-       ret = ads_verify_ticket(ads, &ticket, &client, &auth_data);
+       ret = ads_verify_ticket(ads, &ticket, &client, &auth_data, &ap_rep, session_key);
        if (!NT_STATUS_IS_OK(ret)) {
                DEBUG(1,("Failed to verify incoming ticket!\n"));       
                ads_destroy(&ads);
@@ -177,6 +181,7 @@ static int reply_spnego_kerberos(connection_struct *conn,
        if (!p) {
                DEBUG(3,("Doesn't look like a valid principal\n"));
                ads_destroy(&ads);
+               data_blob_free(&ap_rep);
                return ERROR_NT(NT_STATUS_LOGON_FAILURE);
        }
 
@@ -184,6 +189,7 @@ static int reply_spnego_kerberos(connection_struct *conn,
        if (strcasecmp(p+1, ads->auth.realm) != 0) {
                DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
                if (!lp_allow_trusted_domains()) {
+                       data_blob_free(&ap_rep);
                        return ERROR_NT(NT_STATUS_LOGON_FAILURE);
                }
                foreign = True;
@@ -209,31 +215,51 @@ static int reply_spnego_kerberos(connection_struct *conn,
 
        if (!pw) {
                DEBUG(1,("Username %s is invalid on this system\n",user));
+               data_blob_free(&ap_rep);
                return ERROR_NT(NT_STATUS_NO_SUCH_USER);
        }
 
        if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info,pw))) {
                DEBUG(1,("make_server_info_from_pw failed!\n"));
+               data_blob_free(&ap_rep);
                return ERROR_NT(ret);
        }
-       
+
+       /* Copy out the session key from the AP_REQ. */
+       memcpy(server_info->session_key, session_key, sizeof(session_key));
+
        /* register_vuid keeps the server info */
        sess_vuid = register_vuid(server_info, user);
 
        free(user);
 
        if (sess_vuid == -1) {
-               return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+               ret = NT_STATUS_LOGON_FAILURE;
+       } else {
+               set_message(outbuf,4,0,True);
+               SSVAL(outbuf, smb_vwv3, 0);
+                       
+               if (server_info->guest) {
+                       SSVAL(outbuf,smb_vwv2,1);
+               }
+               
+               SSVAL(outbuf, smb_uid, sess_vuid);
        }
 
-       set_message(outbuf,4,0,True);
-       SSVAL(outbuf, smb_vwv3, 0);
-       add_signature(outbuf);
-       SSVAL(outbuf,smb_uid,sess_vuid);
-       SSVAL(inbuf,smb_uid,sess_vuid);
-       
-       return chain_reply(inbuf,outbuf,length,bufsize);
+        /* wrap that up in a nice GSS-API wrapping */
+       if (NT_STATUS_IS_OK(ret)) {
+               ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP);
+       } else {
+               ap_rep_wrapped = data_blob(NULL, 0);
+       }
+       response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD);
+       reply_sesssetup_blob(conn, outbuf, response, ret);
+
+       data_blob_free(&ap_rep);
+       data_blob_free(&ap_rep_wrapped);
+       data_blob_free(&response);
+
+       return -1; /* already replied */
 }
 #endif
 
@@ -280,7 +306,7 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *outbuf,
                }
        }
 
-        response = spnego_gen_auth_response(ntlmssp_blob, nt_status);
+        response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP);
        ret = reply_sesssetup_blob(conn, outbuf, response, nt_status);
        data_blob_free(&response);