r4355: More work from the elves on Christmas eve:
authorAndrew Bartlett <abartlet@samba.org>
Fri, 24 Dec 2004 09:54:23 +0000 (09:54 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:07:37 +0000 (13:07 -0500)
 - Update Samba4's kerberos code to match the 'salting' changes in
   Samba3 (and many other cleanups by jra).

 - Move GENSEC into the modern era of talloc destructors.  This avoids
   many of the memory leaks in this code, as we now can't somehow
   'forget' to call the end routine.
  - This required fixing some of the talloc hierarchies.

 - The new krb5 seems more sensitive to getting the service name
   right, so start actually setting the service name on the krb5 context.

Andrew Bartlett
(This used to be commit 278bf1a61a6da6ef955a12c13d7b1a0357cebf1f)

16 files changed:
source4/include/secrets.h
source4/ldap_server/ldap_bind.c
source4/libads/config.m4
source4/libcli/auth/clikrb5.c
source4/libcli/auth/gensec.c
source4/libcli/auth/gensec.h
source4/libcli/auth/gensec_krb5.c
source4/libcli/auth/gensec_ntlmssp.c
source4/libcli/auth/kerberos.c
source4/libcli/auth/kerberos.h
source4/libcli/auth/kerberos_verify.c
source4/libcli/auth/ntlmssp.c
source4/libcli/auth/spnego.c
source4/librpc/rpc/dcerpc_schannel.c
source4/passdb/secrets.c
source4/smb_server/sesssetup.c

index a34e86bc6eb6ec9b21af694b5fecbb2a4a89c2ef..8b60c1afb373e5ab0f5bb58b743d990cf087faee 100644 (file)
@@ -30,6 +30,9 @@
 /* this one is for storing trusted domain account password */
 #define SECRETS_DOMTRUST_ACCT_PASS "SECRETS/$DOMTRUST.ACC"
 
+/* Store the principal name used for Kerberos DES key salt under this key name. */
+#define SECRETS_SALTING_PRINCIPAL "SECRETS/SALTING_PRINCIPAL"
+
 /* The domain sid and our sid are stored here even though they aren't
    really secret. */
 #define SECRETS_DOMAIN_SID    "SECRETS/SID"
index b87919628fac10e287f42ab2feb22b77fc446201..80d1f517485f638c9a0e266374c486cc9676981f 100644 (file)
@@ -66,6 +66,8 @@ static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call)
                        DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
                        return status;
                }
+               
+               gensec_set_target_service(call->conn->gensec, "ldap");
 
                /*gensec_want_feature(call->conn->gensec, GENSEC_WANT_SIGN|GENSEC_WANT_SEAL);*/
 
index a696c54e0206f632f978d131c6f4ed49954fa755..d91682da2d0f8d7f408ce5ad32d24e030c2aa493 100644 (file)
@@ -321,13 +321,28 @@ if test x"$with_krb5_support" != x"no"; then
        AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS)
        AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS)
        AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS)
+       AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
        AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS)
        AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS)
        AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS)
        AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
+       AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS)
+       AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS)
 
        LIBS="$LIBS $KRB5_LIBS"
   
+       AC_CACHE_CHECK([for krb5_encrypt_block type],
+                samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[
+       AC_TRY_COMPILE([#include <krb5.h>],
+               [krb5_encrypt_block block;],
+               samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes,
+               samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)])
+
+       if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then
+               AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1,
+               [Whether the type krb5_encrypt_block exists])
+       fi
+
        AC_CACHE_CHECK([for addrtype in krb5_address],
                samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
                AC_TRY_COMPILE([#include <krb5.h>],
@@ -361,6 +376,30 @@ if test x"$with_krb5_support" != x"no"; then
                [Whether the krb5_ticket struct has a enc_part2 property])
        fi
 
+       AC_CACHE_CHECK([for keyblock in krb5_creds],
+                 samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[
+       AC_TRY_COMPILE([#include <krb5.h>],
+               [krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;],
+               samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes,
+               samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)])
+
+       if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then
+               AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1,
+               [Whether the krb5_creds struct has a keyblock property])
+       fi
+
+       AC_CACHE_CHECK([for session in krb5_creds],
+                 samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[
+       AC_TRY_COMPILE([#include <krb5.h>],
+               [krb5_creds creds; krb5_keyblock kb; creds.session = kb;],
+               samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes,
+               samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)])
+
+       if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then
+               AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1,
+               [Whether the krb5_creds struct has a session property])
+       fi
+
        AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
                samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
                AC_TRY_COMPILE([#include <krb5.h>],
index 7ad8dd7b6c5074ce25d29680fdb87daaecfdb2cf..122f9510e78f6c169c2cea2601d1f398040906d0 100644 (file)
        pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
 }
 #else
- __ERROR__XX__UNKNOWN_ADDRTYPE
+#error UNKNOWN_ADDRTYPE
 #endif
 
-#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
- int create_kerberos_key_from_string(krb5_context context,
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
+ int create_kerberos_key_from_string_direct(krb5_context context,
                                        krb5_principal host_princ,
                                        krb5_data *password,
                                        krb5_keyblock *key,
@@ -96,7 +96,7 @@
        return ret;
 }
 #elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
- int create_kerberos_key_from_string(krb5_context context,
+ int create_kerberos_key_from_string_direct(krb5_context context,
                                        krb5_principal host_princ,
                                        krb5_data *password,
                                        krb5_keyblock *key,
                salt, key);
 }
 #else
- __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
+#error UNKNOWN_CREATE_KEY_FUNCTIONS
 #endif
 
+ int create_kerberos_key_from_string(krb5_context context,
+                                       krb5_principal host_princ,
+                                       krb5_data *password,
+                                       krb5_keyblock *key,
+                                       krb5_enctype enctype)
+{
+       krb5_principal salt_princ = NULL;
+       int ret;
+       /*
+        * Check if we've determined that the KDC is salting keys for this
+        * principal/enctype in a non-obvious way.  If it is, try to match
+        * its behavior.
+        */
+       salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
+       ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype);
+       if (salt_princ) {
+               krb5_free_principal(context, salt_princ);
+       }
+       return ret;
+}
+
 #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
  krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 
                                            krb5_enctype **enctypes)
        krb5_error_code rc;
        int num_kdcs, i;
        struct sockaddr *sa;
-       struct addrinfo **ai;
+       struct addrinfo *ai;
 
        *addr_pp = NULL;
        *naddrs = 0;
                return -1;
        }
 
-       *addr_pp = malloc_array_p(struct sockaddr, num_kdcs);
-       memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs );
+       memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs );
 
        for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
 
 #if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO)
-               rc = krb5_krbhst_get_addrinfo(ctx, hinfo, ai);
+               rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai);
                if (rc) {
                        DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc)));
-                       return rc;
+                       continue;
                }
 #endif
                if (hinfo->ai && hinfo->ai->ai_family == AF_INET) 
 }
 #endif
 
+ void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
+{
+#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
+       if (pdata->data) {
+               krb5_free_data_contents(context, pdata);
+       }
+#else
+       SAFE_FREE(pdata->data);
+#endif
+}
+
+ void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
+{
+#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
+       KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
+#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
+       KRB5_KEY_TYPE((&pcreds->session)) = enctype;
+#else
+#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
+#endif
+}
+
+ BOOL kerberos_compatible_enctypes(krb5_context context,
+                                 krb5_enctype enctype1,
+                                 krb5_enctype enctype2)
+{
+#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
+       krb5_boolean similar = 0;
+
+       krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
+       return similar ? True : False;
+#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
+       return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
+#endif
+}
+
 static BOOL ads_cleanup_expired_creds(krb5_context context, 
                                      krb5_ccache  ccache,
                                      krb5_creds  *credsp)
@@ -279,13 +335,13 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
           we're using creds obtained outside of our exectuable
        */
        if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) {
-               DEBUG(5, ("We do not remove creds from a FILE ccache\n"));
+               DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n"));
                return False;
        }
        
        retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
        if (retval) {
-               DEBUG(1, ("krb5_cc_remove_cred failed, err %s\n",
+               DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
                          error_message(retval)));
                /* If we have an error in this, we want to display it,
                   but continue as though we deleted it */
@@ -296,7 +352,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
 /*
   we can't use krb5_mk_req because w2k wants the service to be in a particular format
 */
- krb5_error_code ads_krb5_mk_req(krb5_context context, 
+krb5_error_code ads_krb5_mk_req(krb5_context context, 
                                krb5_auth_context *auth_context, 
                                const krb5_flags ap_req_options,
                                const char *principal,
@@ -314,7 +370,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
 
        retval = krb5_parse_name(context, principal, &server);
        if (retval) {
-               DEBUG(1,("Failed to parse principal %s\n", principal));
+               DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
                return retval;
        }
        
@@ -327,7 +383,9 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
        }
        
        if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
-               DEBUG(10,("krb5_cc_get_principal failed (%s)\n", 
+               /* This can commonly fail on smbd startup with no ticket in the cache.
+                * Report at higher level than 1. */
+               DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n", 
                         error_message(retval)));
                goto cleanup_creds;
        }
@@ -335,7 +393,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
        while(!creds_ready) {
                if ((retval = krb5_get_credentials(context, 0, ccache, 
                                                   &creds, &credsp))) {
-                       DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
+                       DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
                                 principal, error_message(retval)));
                        goto cleanup_creds;
                }
@@ -344,7 +402,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
                if ((unsigned)credsp->times.starttime > time(NULL)) {
                        time_t t = time(NULL);
                        int time_offset =(unsigned)credsp->times.starttime-t;
-                       DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+                       DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
                        krb5_set_real_time(context, t + time_offset + 1, 0);
                }
 
@@ -366,7 +424,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
        retval = krb5_mk_req_extended(context, auth_context, ap_req_options, 
                                      &in_data, credsp, outbuf);
        if (retval) {
-               DEBUG(1,("krb5_mk_req_extended failed (%s)\n", 
+               DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", 
                         error_message(retval)));
        }
        
@@ -378,13 +436,9 @@ cleanup_creds:
 cleanup_princ:
        krb5_free_principal(context, server);
 
-       if (mem_ctx) {
-               talloc_destroy(mem_ctx);
-       }
        return retval;
 }
 
-
 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
  const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
 {
index 8009df4e4050baf70bb343a29cb4308d5180ab04..7e33a159f94624569426d89844be14464eef33c0 100644 (file)
@@ -147,10 +147,11 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gense
  * @note Used by SPENGO in particular, for the actual implementation mechanism
  */
 
-NTSTATUS gensec_subcontext_start(struct gensec_security *parent, 
+NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx, 
+                                struct gensec_security *parent, 
                                 struct gensec_security **gensec_security)
 {
-       (*gensec_security) = talloc_p(parent, struct gensec_security);
+       (*gensec_security) = talloc_p(mem_ctx, struct gensec_security);
        if (!(*gensec_security)) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -444,10 +445,6 @@ void gensec_end(struct gensec_security **gensec_security)
        if (!*gensec_security) {
                return;
        }
-       if ((*gensec_security)->ops) {
-               (*gensec_security)->ops->end(*gensec_security);
-       }
-       (*gensec_security)->private_data = NULL;
 
        talloc_free(*gensec_security);
        *gensec_security = NULL;
@@ -646,7 +643,7 @@ NTSTATUS gensec_set_password(struct gensec_security *gensec_security,
                             const char *password) 
 {
        gensec_security->user.password = talloc_strdup(gensec_security, password);
-       if (!gensec_security->user.password) {
+       if (password && !gensec_security->user.password) {
                return NT_STATUS_NO_MEMORY;
        }
        return NT_STATUS_OK;
@@ -713,6 +710,20 @@ const char *gensec_get_target_service(struct gensec_security *gensec_security)
        return "host";
 }
 
+const char *gensec_get_target_principal(struct gensec_security *gensec_security) 
+{
+       const char *mechListMIC;
+       
+       if (gensec_security->target.principal) {
+               return gensec_security->target.principal;
+       }
+
+       mechListMIC = talloc_asprintf(gensec_security,"%s$@%s",
+                                     lp_netbios_name(),
+                                     lp_realm());
+       return mechListMIC;
+}
+
 /** 
  * Set a password callback, if the gensec module we use demands a password
  */
@@ -741,7 +752,8 @@ NTSTATUS gensec_get_password(struct gensec_security *gensec_security,
                }
        }
        if (!gensec_security->password_callback) {
-               return NT_STATUS_INVALID_PARAMETER;
+               *password = NULL;
+               return NT_STATUS_OK;
        }
        return gensec_security->password_callback(gensec_security, mem_ctx, password);
 }
index 9162c935b283bd1212b69f33bf9a0a8a081bd0a1..2b44f0d902ce44a74aae82fde9ff202d28dc19f4 100644 (file)
@@ -84,7 +84,6 @@ struct gensec_security_ops {
        NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
        NTSTATUS (*session_info)(struct gensec_security *gensec_security, 
                                 struct auth_session_info **session_info); 
-       void (*end)(struct gensec_security *gensec_security);
 };
        
 #define GENSEC_INTERFACE_VERSION 0
index 9323580e92ff091cf655181dc4f9dba109cc48a2..9d4a2f6b0e14d23f1b6643ab68f9745834402bc0 100644 (file)
@@ -229,9 +229,9 @@ static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx,
        return status;
 }
 
-static void gensec_krb5_end(struct gensec_security *gensec_security)
+static int gensec_krb5_destory(void *ptr) 
 {
-       struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
+       struct gensec_krb5_state *gensec_krb5_state = ptr;
 
        if (gensec_krb5_state->ticket.length) { 
        /* Hmm, early heimdal dooesn't have this - correct call would be krb5_data_free */
@@ -255,12 +255,9 @@ static void gensec_krb5_end(struct gensec_security *gensec_security)
        if (gensec_krb5_state->krb5_context) {
                krb5_free_context(gensec_krb5_state->krb5_context);
        }
-
-       talloc_free(gensec_krb5_state);
-       gensec_security->private_data = NULL;
+       return 0;
 }
 
-
 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
 {
        struct gensec_krb5_state *gensec_krb5_state;
@@ -282,6 +279,8 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
        gensec_krb5_state->session_key = data_blob(NULL, 0);
        gensec_krb5_state->pac = data_blob(NULL, 0);
 
+       talloc_set_destructor(gensec_krb5_state, gensec_krb5_destory); 
+
        ret = krb5_init_context(&gensec_krb5_state->krb5_context);
        if (ret) {
                DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret)));
@@ -401,8 +400,8 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
                        }
 
                        ret = kerberos_kinit_password_cc(gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_ccache, 
-                                                        gensec_get_client_principal(gensec_security, gensec_security), 
-                                                        password, NULL, &kdc_time);
+                                                     gensec_get_client_principal(gensec_security, gensec_security), 
+                                                     password, NULL, &kdc_time);
 
                        /* cope with ticket being in the future due to clock skew */
                        if ((unsigned)kdc_time > time(NULL)) {
@@ -439,8 +438,9 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
  *                or NT_STATUS_OK if the user is authenticated. 
  */
 
-static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
-                                     const DATA_BLOB in, DATA_BLOB *out) 
+static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, 
+                                  TALLOC_CTX *out_mem_ctx, 
+                                  const DATA_BLOB in, DATA_BLOB *out) 
 {
        struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
        krb5_error_code ret = 0;
@@ -524,7 +524,8 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
                        nt_status = ads_verify_ticket(out_mem_ctx, 
                                                      gensec_krb5_state->krb5_context, 
                                                      gensec_krb5_state->krb5_auth_context, 
-                                                     lp_realm(), &in, 
+                                                     lp_realm(), 
+                                                     gensec_get_target_service(gensec_security), &in, 
                                                      &principal, &pac, &unwrapped_out,
                                                      &gensec_krb5_state->krb5_keyblock);
                } else {
@@ -532,7 +533,9 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
                        nt_status = ads_verify_ticket(out_mem_ctx, 
                                                      gensec_krb5_state->krb5_context, 
                                                      gensec_krb5_state->krb5_auth_context, 
-                                                     lp_realm(), &unwrapped_in, 
+                                                     lp_realm(), 
+                                                     gensec_get_target_service(gensec_security), 
+                                                     &unwrapped_in, 
                                                      &principal, &pac, &unwrapped_out,
                                                      &gensec_krb5_state->krb5_keyblock);
                }
@@ -683,7 +686,6 @@ static const struct gensec_security_ops gensec_krb5_security_ops = {
        .update         = gensec_krb5_update,
        .session_key    = gensec_krb5_session_key,
        .session_info   = gensec_krb5_session_info,
-       .end            = gensec_krb5_end
 };
 
 static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
@@ -695,7 +697,6 @@ static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
        .update         = gensec_krb5_update,
        .session_key    = gensec_krb5_session_key,
        .session_info   = gensec_krb5_session_info,
-       .end            = gensec_krb5_end
 };
 
 
index 07dacfb5e06af7d7fd7cabafddcc5c197103730e..cf8019402c0dffc19778d95c2a1ac4b40b6499da 100644 (file)
@@ -142,6 +142,23 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state,
        return nt_status;
 }
 
+static int gensec_ntlmssp_destroy(void *ptr)
+{
+       struct gensec_ntlmssp_state *gensec_ntlmssp_state = ptr;
+
+       if (gensec_ntlmssp_state->ntlmssp_state) {
+               ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state);
+       }
+
+       if (gensec_ntlmssp_state->auth_context) {
+               free_auth_context(&gensec_ntlmssp_state->auth_context);
+       }
+       if (gensec_ntlmssp_state->server_info) {
+               free_server_info(&gensec_ntlmssp_state->server_info);
+       }
+       return 0;
+}
+
 static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
 {
        struct gensec_ntlmssp_state *gensec_ntlmssp_state;
@@ -155,6 +172,8 @@ static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
        gensec_ntlmssp_state->auth_context = NULL;
        gensec_ntlmssp_state->server_info = NULL;
 
+       talloc_set_destructor(gensec_ntlmssp_state, gensec_ntlmssp_destroy); 
+
        gensec_security->private_data = gensec_ntlmssp_state;
        return NT_STATUS_OK;
 }
@@ -173,7 +192,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur
 
        gensec_ntlmssp_state = gensec_security->private_data;
 
-       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(gensec_security,
+       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(gensec_ntlmssp_state,
                                                              &gensec_ntlmssp_state->ntlmssp_state))) {
                return nt_status;
        }
@@ -186,7 +205,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur
        }
 
        ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state;
-       nt_status = make_auth_context_subsystem(gensec_security, &gensec_ntlmssp_state->auth_context);
+       nt_status = make_auth_context_subsystem(gensec_ntlmssp_state, &gensec_ntlmssp_state->auth_context);
        if (!NT_STATUS_IS_OK(nt_status)) {
                return nt_status;
        }
@@ -213,7 +232,7 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur
        }
 
        gensec_ntlmssp_state = gensec_security->private_data;
-       status = ntlmssp_client_start(gensec_security, 
+       status = ntlmssp_client_start(gensec_ntlmssp_state,
                                      &gensec_ntlmssp_state->ntlmssp_state);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -250,9 +269,11 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur
                return status;
        }
 
-       status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       if (gensec_security->user.name) {
+               status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
 
        if (password) {
@@ -397,24 +418,6 @@ static NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_secur
        return NT_STATUS_OK;
 }
 
-static void gensec_ntlmssp_end(struct gensec_security *gensec_security)
-{
-       struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
-
-       if (gensec_ntlmssp_state->ntlmssp_state) {
-               ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state);
-       }
-
-       if (gensec_ntlmssp_state->auth_context) {
-               free_auth_context(&gensec_ntlmssp_state->auth_context);
-       }
-       if (gensec_ntlmssp_state->server_info) {
-               free_server_info(&gensec_ntlmssp_state->server_info);
-       }
-       talloc_free(gensec_ntlmssp_state);
-       gensec_security->private_data = NULL;
-}
-
 static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
        .name           = "ntlmssp",
        .sasl_name      = "NTLM",
@@ -430,7 +433,6 @@ static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
        .unseal_packet  = gensec_ntlmssp_unseal_packet,
        .session_key    = gensec_ntlmssp_session_key,
        .session_info   = gensec_ntlmssp_session_info,
-       .end            = gensec_ntlmssp_end
 };
 
 
index 9510aaa7fb413bbb4080a7ffdfaa5ba2ea2a782b..a8d9d75a1e61c38a1cd533f1df876123ac3bf2f7 100644 (file)
@@ -3,8 +3,9 @@
    kerberos utility library
    Copyright (C) Andrew Tridgell 2001
    Copyright (C) Remus Koos 2001
-   
-   
+   Copyright (C) Nalin Dahyabhai 2004.
+   Copyright (C) Jeremy Allison 2004.
+
    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
    the Free Software Foundation; either version 2 of the License, or
 #include "system/kerberos.h"
 #include "libcli/auth/kerberos.h"
 #include "system/time.h"
+#include "secrets.h"
 
 #ifdef HAVE_KRB5
 
+#define LIBADS_CCACHE_NAME "MEMORY:libads"
+
 /*
   we use a prompter to avoid a crash bug in the kerberos libs when 
   dealing with empty passwords
@@ -41,7 +45,7 @@ kerb_prompter(krb5_context ctx, void *data,
 {
        if (num_prompts == 0) return 0;
 
-       memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+       memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
        if (prompts[0].reply->length > 0) {
                if (data) {
                        strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
@@ -54,10 +58,12 @@ kerb_prompter(krb5_context ctx, void *data,
 }
 
 /*
-  simulate a kinit, putting the tgt in the default cache location
-  remus@snapserver.com
+  simulate a kinit, putting the tgt in the given credentials cache. 
+  Orignally by remus@snapserver.com
 */
- int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time)
+ int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, 
+                              const char *principal, const char *password, 
+                              time_t *expire_time, time_t *kdc_time)
 {
        krb5_error_code code = 0;
        krb5_principal me;
@@ -103,15 +109,21 @@ kerb_prompter(krb5_context ctx, void *data,
        return 0;
 }
 
-
 /*
-  simulate a kinit, putting the tgt in the default cache location
-  remus@snapserver.com
+  simulate a kinit, putting the tgt in the given credentials cache. 
+  If cache_name == NULL place in default cache location.
+
+  Orignally by remus@snapserver.com
 */
-int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time, time_t *kdc_time)
+int kerberos_kinit_password(const char *principal,
+                           const char *password,
+                           int time_offset,
+                           time_t *expire_time,
+                           const char *cache_name,
+                           time_t *kdc_time)
 {
+       int code;
        krb5_context ctx = NULL;
-       krb5_error_code code = 0;
        krb5_ccache cc = NULL;
 
        if ((code = krb5_init_context(&ctx)))
@@ -121,21 +133,19 @@ int kerberos_kinit_password(const char *principal, const char *password, int tim
                krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
        }
        
-       if ((code = krb5_cc_default(ctx, &cc))) {
+       if ((code = krb5_cc_resolve(ctx, cache_name ?
+                                   cache_name : krb5_cc_default_name(ctx), &cc))) {
                krb5_free_context(ctx);
                return code;
        }
 
-       if ((code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time))) {
-               krb5_cc_close(ctx, cc);
-               krb5_free_context(ctx);
-               return code;
-       }
+       code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time);
        
-       return 0;
-}
-
+       krb5_cc_close(ctx, cc);
+       krb5_free_context(ctx);
 
+       return code;
+}
 
 /* run kinit to setup our ccache */
 int ads_kinit_password(ADS_STRUCT *ads)
@@ -151,7 +161,8 @@ int ads_kinit_password(ADS_STRUCT *ads)
                return KRB5_LIBOS_CANTREADPWD;
        }
        
-       ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire, NULL);
+       ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset,
+                       &ads->auth.expire, NULL, NULL);
 
        if (ret) {
                DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
@@ -168,7 +179,8 @@ int ads_kdestroy(const char *cc_name)
        krb5_ccache cc = NULL;
 
        if ((code = krb5_init_context (&ctx))) {
-               DEBUG(3, ("ads_kdestroy: kdb5_init_context rc=%d\n", code));
+               DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
+                       error_message(code)));
                return code;
        }
   
@@ -179,19 +191,600 @@ int ads_kdestroy(const char *cc_name)
                }
        } else {
                if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
-                       DEBUG(3, ("ads_kdestroy: krb5_cc_resolve rc=%d\n",
-                                 code));
+                       DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
+                                 error_message(code)));
                        krb5_free_context(ctx);
                        return code;
                }
        }
 
        if ((code = krb5_cc_destroy (ctx, cc))) {
-               DEBUG(3, ("ads_kdestroy: krb5_cc_destroy rc=%d\n", code));
+               DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
+                       error_message(code)));
        }
 
        krb5_free_context (ctx);
        return code;
 }
 
+/************************************************************************
+ Routine to fetch the salting principal for a service.  Active
+ Directory may use a non-obvious principal name to generate the salt
+ when it determines the key to use for encrypting tickets for a service,
+ and hopefully we detected that when we joined the domain.
+ ************************************************************************/
+
+static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
+{
+       char *ret = NULL;
+
+#if 0
+       asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
+       if (!key) {
+               return NULL;
+       }
+       ret = (char *)secrets_fetch(key, NULL);
+       SAFE_FREE(key);
+#endif
+       return ret;
+}
+
+/************************************************************************
+ Routine to get the salting principal for this service.  Active
+ Directory may use a non-obvious principal name to generate the salt
+ when it determines the key to use for encrypting tickets for a service,
+ and hopefully we detected that when we joined the domain.
+ Caller must free if return is not null.
+ ************************************************************************/
+
+krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
+                                                       krb5_principal host_princ,
+                                                       int enctype)
+{
+       char *unparsed_name = NULL, *salt_princ_s = NULL;
+       krb5_principal ret_princ = NULL;
+
+       if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
+               return (krb5_principal)NULL;
+       }
+
+       if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
+               krb5_free_unparsed_name(context, unparsed_name);
+               return (krb5_principal)NULL;
+       }
+
+       if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
+               krb5_free_unparsed_name(context, unparsed_name);
+               SAFE_FREE(salt_princ_s);
+               return (krb5_principal)NULL;
+       }
+       krb5_free_unparsed_name(context, unparsed_name);
+       SAFE_FREE(salt_princ_s);
+       return ret_princ;
+}
+
+/************************************************************************
+ Routine to set the salting principal for this service.  Active
+ Directory may use a non-obvious principal name to generate the salt
+ when it determines the key to use for encrypting tickets for a service,
+ and hopefully we detected that when we joined the domain.
+ Setting principal to NULL deletes this entry.
+ ************************************************************************/
+
+ BOOL kerberos_secrets_store_salting_principal(const char *service,
+                                             int enctype,
+                                             const char *principal)
+{
+       char *key = NULL;
+       BOOL ret = False;
+       krb5_context context = NULL;
+       krb5_principal princ = NULL;
+       char *princ_s = NULL;
+       char *unparsed_name = NULL;
+
+       krb5_init_context(&context);
+       if (!context) {
+               return False;
+       }
+       if (strchr_m(service, '@')) {
+               asprintf(&princ_s, "%s", service);
+       } else {
+               asprintf(&princ_s, "%s@%s", service, lp_realm());
+       }
+
+       if (krb5_parse_name(context, princ_s, &princ) != 0) {
+               goto out;
+               
+       }
+       if (krb5_unparse_name(context, princ, &unparsed_name) != 0) {
+               goto out;
+       }
+
+       asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
+       if (!key)  {
+               goto out;
+       }
+
+#if 0
+       if ((principal != NULL) && (strlen(principal) > 0)) {
+               ret = secrets_store(key, principal, strlen(principal) + 1);
+       } else {
+               ret = secrets_delete(key);
+       }
+#endif 
+
+ out:
+
+       SAFE_FREE(key);
+       SAFE_FREE(princ_s);
+
+       if (unparsed_name) {
+               krb5_free_unparsed_name(context, unparsed_name);
+       }
+       if (context) {
+               krb5_free_context(context);
+       }
+
+       return ret;
+}
+
+/************************************************************************
+ Routine to get initial credentials as a service ticket for the local machine.
+ Returns a buffer initialized with krb5_mk_req_extended.
+ ************************************************************************/
+
+static krb5_error_code get_service_ticket(krb5_context ctx,
+                                       krb5_ccache ccache,
+                                       const char *service_principal,
+                                       int enctype,
+                                       krb5_data *p_outbuf)
+{
+       krb5_creds creds, *new_creds = NULL;
+       char *service_s = NULL;
+       char *machine_account = NULL, *password = NULL;
+       krb5_data in_data;
+       krb5_auth_context auth_context = NULL;
+       krb5_error_code err = 0;
+
+       ZERO_STRUCT(creds);
+
+       asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm());
+       if (machine_account == NULL) {
+               goto out;
+       }
+       password = secrets_fetch_machine_password(lp_workgroup());
+       if (password == NULL) {
+               goto out;
+       }
+       if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME, NULL)) != 0) {
+               DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n", 
+                       machine_account,
+                       lp_realm(),
+                       error_message(err)));
+               goto out;
+       }
+
+       /* Ok - the above call has gotten a TGT. Now we need to get a service
+          ticket to ourselves. */
+
+       /* Set up the enctype and client and server principal fields for krb5_get_credentials. */
+       kerberos_set_creds_enctype(&creds, enctype);
+
+       if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) {
+               DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n", 
+                       error_message(err)));
+               goto out;
+       }
+
+       if (strchr_m(service_principal, '@')) {
+               asprintf(&service_s, "%s", service_principal);
+       } else {
+               asprintf(&service_s, "%s@%s", service_principal, lp_realm());
+       }
+
+       if ((err = krb5_parse_name(ctx, service_s, &creds.server))) {
+               DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n", 
+                       service_s, error_message(err)));
+               goto out;
+       }
+
+       if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) {
+               DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n", 
+                       service_s, enctype, error_message(err)));
+               goto out;
+       }
+
+       memset(&in_data, '\0', sizeof(in_data));
+       if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data,
+                       new_creds, p_outbuf)) != 0) {
+               DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n", 
+                       error_message(err)));
+               goto out;
+       }
+
+ out:
+
+       if (auth_context) {
+               krb5_auth_con_free(ctx, auth_context);
+       }
+       if (new_creds) {
+               krb5_free_creds(ctx, new_creds);
+       }
+       if (creds.server) {
+               krb5_free_principal(ctx, creds.server);
+       }
+       if (creds.client) {
+               krb5_free_principal(ctx, creds.client);
+       }
+
+       SAFE_FREE(service_s);
+       SAFE_FREE(password);
+       SAFE_FREE(machine_account);
+       return err;
+}
+
+/************************************************************************
+ Check if the machine password can be used in conjunction with the salting_principal
+ to generate a key which will successfully decrypt the AP_REQ already
+ gotten as a message to the local machine.
+ ************************************************************************/
+
+static BOOL verify_service_password(krb5_context ctx,
+                                   int enctype,
+                                   const char *salting_principal,
+                                   krb5_data *in_data)
+{
+       BOOL ret = False;
+       krb5_principal salting_kprinc = NULL;
+       krb5_ticket *ticket = NULL;
+       krb5_keyblock key;
+       krb5_data passdata;
+       char *salting_s = NULL;
+       char *machine_account = NULL, *password = NULL;
+       krb5_auth_context auth_context = NULL;
+       krb5_error_code err;
+
+       memset(&passdata, '\0', sizeof(passdata));
+       memset(&key, '\0', sizeof(key));
+
+       asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm());
+       if (machine_account == NULL) {
+               goto out;
+       }
+       password = secrets_fetch_machine_password(lp_workgroup());
+       if (password == NULL) {
+               goto out;
+       }
+
+       if (strchr_m(salting_principal, '@')) {
+               asprintf(&salting_s, "%s", salting_principal);
+       } else {
+               asprintf(&salting_s, "%s@%s", salting_principal, lp_realm());
+       }
+
+       if ((err = krb5_parse_name(ctx, salting_s, &salting_kprinc))) {
+               DEBUG(0,("verify_service_password: krb5_parse_name %s failed: %s\n", 
+                       salting_s, error_message(err)));
+               goto out;
+       }
+
+       passdata.length = strlen(password);
+       passdata.data = (char*)password;
+       if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) {
+               DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n",
+                       enctype, error_message(err)));
+               goto out;
+       }
+
+       if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) {
+               DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err)));
+               goto out;
+       }
+
+       if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) {
+               DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err)));
+               goto out;
+       }
+
+       if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) {
+               DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n",
+                               (unsigned int)enctype, salting_s));
+               ret = True;
+       }
+
+ out:
+
+       memset(&passdata, 0, sizeof(passdata));
+       krb5_free_keyblock_contents(ctx, &key);
+       if (ticket != NULL) {
+               krb5_free_ticket(ctx, ticket);
+       }
+       if (salting_kprinc) {
+               krb5_free_principal(ctx, salting_kprinc);
+       }
+       SAFE_FREE(salting_s);
+       SAFE_FREE(password);
+       SAFE_FREE(machine_account);
+       return ret;
+}
+
+/************************************************************************
+ *
+ * From the current draft of kerberos-clarifications:
+ *
+ *     It is not possible to reliably generate a user's key given a pass
+ *     phrase without contacting the KDC, since it will not be known
+ *     whether alternate salt or parameter values are required.
+ *
+ * And because our server has a password, we have this exact problem.  We
+ * make multiple guesses as to which principal name provides the salt which
+ * the KDC is using.
+ *
+ ************************************************************************/
+
+static void kerberos_derive_salting_principal_for_enctype(const char *service_principal,
+                                                         krb5_context ctx,
+                                                         krb5_ccache ccache,
+                                                         krb5_enctype enctype,
+                                                         krb5_enctype *enctypes)
+{
+       char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL;
+       krb5_error_code err = 0;
+       krb5_data outbuf;
+       int i, j;
+
+       memset(&outbuf, '\0', sizeof(outbuf));
+
+       /* Check that the service_principal is useful. */
+       if ((service_principal == NULL) || (strlen(service_principal) == 0)) {
+               return;
+       }
+
+       /* Generate our first guess -- the principal as-given. */
+       asprintf(&salting_principals[0], "%s", service_principal);
+       if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) {
+               return;
+       }
+
+       /* Generate our second guess -- the computer's principal, as Win2k3. */
+       asprintf(&second_principal, "host/%s.%s", lp_netbios_name(), lp_realm());
+       if (second_principal != NULL) {
+               strlower_m(second_principal);
+               asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm());
+               SAFE_FREE(second_principal);
+       }
+       if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) {
+               goto out;
+       }
+
+       /* Generate our third guess -- the computer's principal, as Win2k. */
+       asprintf(&second_principal, "HOST/%s", lp_netbios_name());
+       if (second_principal != NULL) {
+               strlower_m(second_principal + 5);
+               asprintf(&salting_principals[2], "%s@%s",
+                       second_principal, lp_realm());
+               SAFE_FREE(second_principal);
+       }
+       if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) {
+               goto out;
+       }
+
+       /* Get a service ticket for ourselves into our memory ccache. */
+       /* This will commonly fail if there is no principal by that name (and we're trying
+          many names). So don't print a debug 0 error. */
+
+       if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) {
+               DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n", 
+                       error_message(err)));
+               goto out;
+       }
+
+       /* At this point we have a message to ourselves, salted only the KDC knows how. We
+          have to work out what that salting is. */
+
+       /* Try and find the correct salting principal. */
+       for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) {
+               if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) {
+                       break;
+               }
+       }
+
+       /* If we failed to get a match, return. */
+       if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) {
+               goto out;
+       }
+
+       /* If we succeeded, store the principal for use for all enctypes which
+        * share the same cipher and string-to-key function.  Doing this here
+        * allows servers which just pass a keytab to krb5_rd_req() to work
+        * correctly. */
+       for (j = 0; enctypes[j] != 0; j++) {
+               if (enctype != enctypes[j]) {
+                       /* If this enctype isn't compatible with the one which
+                        * we used, skip it. */
+
+                       if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype))
+                               continue;
+               }
+               /* If the principal which gives us the proper salt is the one
+                * which we would normally guess, don't bother noting anything
+                * in the secrets tdb. */
+               if (strcmp(service_principal, salting_principals[i]) != 0) {
+                       kerberos_secrets_store_salting_principal(service_principal,
+                                                               enctypes[j],
+                                                               salting_principals[i]);
+               }
+       }
+
+ out :
+
+       kerberos_free_data_contents(ctx, &outbuf);
+       SAFE_FREE(salting_principals[0]);
+       SAFE_FREE(salting_principals[1]);
+       SAFE_FREE(salting_principals[2]);
+       SAFE_FREE(second_principal);
+}
+
+/************************************************************************
+ Go through all the possible enctypes for this principal.
+ ************************************************************************/
+
+static void kerberos_derive_salting_principal_direct(krb5_context context,
+                                       krb5_ccache ccache,
+                                       krb5_enctype *enctypes,
+                                       char *service_principal)
+{
+       int i;
+
+       /* Try for each enctype separately, because the rules are
+        * different for different enctypes. */
+       for (i = 0; enctypes[i] != 0; i++) {
+               /* Delete secrets entry first. */
+               kerberos_secrets_store_salting_principal(service_principal, 0, NULL);
+#ifdef ENCTYPE_ARCFOUR_HMAC
+               if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) {
+                       /* Of course this'll always work, so just save
+                        * ourselves the effort. */
+                       continue;
+               }
+#endif
+               /* Try to figure out what's going on with this
+                * principal. */
+               kerberos_derive_salting_principal_for_enctype(service_principal,
+                                                               context,
+                                                               ccache,
+                                                               enctypes[i],
+                                                               enctypes);
+       }
+}
+
+/************************************************************************
+ Wrapper function for the above.
+ ************************************************************************/
+
+BOOL kerberos_derive_salting_principal(char *service_principal)
+{
+       krb5_context context = NULL;
+       krb5_enctype *enctypes = NULL;
+       krb5_ccache ccache = NULL;
+       krb5_error_code ret = 0;
+
+       initialize_krb5_error_table();
+       if ((ret = krb5_init_context(&context)) != 0) {
+               DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
+                       error_message(ret)));
+               return False;
+       }
+       if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
+               DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
+                       error_message(ret)));
+               goto out;
+       }
+
+       if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
+               DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", 
+                       LIBADS_CCACHE_NAME, error_message(ret)));
+               goto out;
+       }
+
+       kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal);
+
+  out: 
+       if (enctypes) {
+               free_kerberos_etypes(context, enctypes);
+       }
+       if (ccache) {
+               krb5_cc_destroy(context, ccache);
+       }
+       if (context) {
+               krb5_free_context(context);
+       }
+
+       return ret ? False : True;
+}
+
+/************************************************************************
+ Core function to try and determine what salt is being used for any keytab
+ keys.
+ ************************************************************************/
+
+BOOL kerberos_derive_cifs_salting_principals(void)
+{
+       fstring my_fqdn;
+       char *service = NULL;
+       krb5_context context = NULL;
+       krb5_enctype *enctypes = NULL;
+       krb5_ccache ccache = NULL;
+       krb5_error_code ret = 0;
+       BOOL retval = False;
+
+       initialize_krb5_error_table();
+       if ((ret = krb5_init_context(&context)) != 0) {
+               DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
+                       error_message(ret)));
+               return False;
+       }
+       if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
+               DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
+                       error_message(ret)));
+               goto out;
+       }
+
+       if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
+               DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", 
+                       LIBADS_CCACHE_NAME, error_message(ret)));
+               goto out;
+       }
+
+       if (asprintf(&service, "%s$", lp_netbios_name()) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+       if (asprintf(&service, "cifs/%s", lp_netbios_name()) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+       if (asprintf(&service, "host/%s", lp_netbios_name()) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+       if (asprintf(&service, "cifs/%s.%s", lp_netbios_name(), lp_realm()) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+       if (asprintf(&service, "host/%s.%s", lp_netbios_name(), lp_realm()) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+       name_to_fqdn(my_fqdn, lp_netbios_name());
+       if (asprintf(&service, "cifs/%s", my_fqdn) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+       if (asprintf(&service, "host/%s", my_fqdn) != -1) {
+               strlower_m(service);
+               kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+               SAFE_FREE(service);
+       }
+
+       retval = True;
+
+  out: 
+       if (enctypes) {
+               free_kerberos_etypes(context, enctypes);
+       }
+       if (ccache) {
+               krb5_cc_destroy(context, ccache);
+       }
+       if (context) {
+               krb5_free_context(context);
+       }
+       return retval;
+}
 #endif
index 3ab71f78750a2980b61db020220011f1a391cca0..9bb6d22eb6b485fe9c566e2ef717bd0f865e311b 100644 (file)
@@ -61,6 +61,7 @@ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal princ
 /* Samba wrapper function for krb5 functionality. */
 void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
 int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
+int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
 krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
 krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
 krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
@@ -74,13 +75,23 @@ krb5_error_code ads_krb5_mk_req(krb5_context context,
                                krb5_data *outbuf);
 DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, 
                                 krb5_ticket *tkt);
+
 NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, 
                           krb5_context context,
                           krb5_auth_context auth_context,
-                          const char *realm, const DATA_BLOB *ticket, 
+                          const char *realm, const char *service, 
+                          const DATA_BLOB *ticket, 
                           char **principal, DATA_BLOB *auth_data,
                           DATA_BLOB *ap_rep,
                           krb5_keyblock *keyblock);
-int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time);
+int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, 
+                              const char *principal, const char *password, 
+                              time_t *expire_time, time_t *kdc_time);
+krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
+                                                       krb5_principal host_princ,
+                                                       int enctype);
+void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
+BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
+void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
 #endif /* HAVE_KRB5 */
 
index d00394fd790459d4ef43aa68671f1138ade64080..92980f112238c6959f626bcc86acb248045649ba 100644 (file)
@@ -29,6 +29,9 @@
 
 #ifdef HAVE_KRB5
 
+#if !defined(HAVE_KRB5_PRINC_COMPONENT)
+const krb5_data *krb5_princ_component(krb5_context, krb5_principal, int );
+#endif
 static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
 {
        DATA_BLOB out;
@@ -65,21 +68,23 @@ static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
  ads_keytab_add_entry function for details.
 ***********************************************************************************/
 
-static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context,
-                       const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
-                       krb5_keyblock *keyblock)
+static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, 
+                                               krb5_auth_context auth_context,
+                                               const char *service,
+                                               const DATA_BLOB *ticket, krb5_data *p_packet, 
+                                               krb5_ticket **pp_tkt,
+                                               krb5_keyblock *keyblock)
 {
        krb5_error_code ret = 0;
-
+       BOOL auth_ok = False;
        krb5_keytab keytab = NULL;
-       krb5_kt_cursor cursor;
-       krb5_keytab_entry kt_entry;
-       char *princ_name = NULL;
-
-       ZERO_STRUCT(kt_entry);
-       ZERO_STRUCT(cursor);
-
-       ZERO_STRUCTP(keyblock);
+       char *my_fqdn;
+       fstring my_name;
+       fstring my_Fqdn, my_NAME;
+       char *p_fqdn;
+       char *host_princ_s[18];
+       krb5_principal host_princ;
+       int i;
 
        ret = krb5_kt_default(context, &keytab);
        if (ret) {
@@ -87,73 +92,74 @@ static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_
                goto out;
        }
 
-       ret = krb5_kt_start_seq_get(context, keytab, &cursor);
-       if (ret) {
-               DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", error_message(ret)));
-               goto out;
+       /* Generate the list of principal names which we expect clients might
+        * want to use for authenticating to the file service. */
+
+       fstrcpy(my_name, lp_netbios_name());
+       strlower_m(my_name);
+
+       fstrcpy(my_NAME, lp_netbios_name());
+       strupper_m(my_NAME);
+
+       my_fqdn = name_to_fqdn(mem_ctx, lp_netbios_name());
+       strlower_m(my_fqdn);
+
+       p_fqdn = strchr_m(my_fqdn, '.');
+       fstrcpy(my_Fqdn, my_NAME);
+       if (p_fqdn) {
+               fstrcat(my_Fqdn, p_fqdn);
        }
 
-       while (!(ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor))) {
-               ret = krb5_unparse_name(context, kt_entry.principal, &princ_name);
+        asprintf(&host_princ_s[0], "%s$@%s", my_name, lp_realm());
+        asprintf(&host_princ_s[1], "%s$@%s", my_NAME, lp_realm());
+        asprintf(&host_princ_s[2], "host/%s@%s", my_name, lp_realm());
+        asprintf(&host_princ_s[3], "host/%s@%s", my_NAME, lp_realm());
+        asprintf(&host_princ_s[4], "host/%s@%s", my_fqdn, lp_realm());
+        asprintf(&host_princ_s[5], "host/%s@%s", my_Fqdn, lp_realm());
+        asprintf(&host_princ_s[6], "HOST/%s@%s", my_name, lp_realm());
+        asprintf(&host_princ_s[7], "HOST/%s@%s", my_NAME, lp_realm());
+        asprintf(&host_princ_s[8], "HOST/%s@%s", my_fqdn, lp_realm());
+        asprintf(&host_princ_s[9], "HOST/%s@%s", my_Fqdn, lp_realm());
+        asprintf(&host_princ_s[10], "%s/%s@%s", service, my_name, lp_realm());
+        asprintf(&host_princ_s[11], "%s/%s@%s", service, my_NAME, lp_realm());
+        asprintf(&host_princ_s[12], "%s/%s@%s", service, my_fqdn, lp_realm());
+        asprintf(&host_princ_s[13], "%s/%s@%s", service, my_Fqdn, lp_realm());
+        asprintf(&host_princ_s[14], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_name, lp_realm());
+        asprintf(&host_princ_s[15], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_NAME, lp_realm());
+        asprintf(&host_princ_s[16], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_fqdn, lp_realm());
+        asprintf(&host_princ_s[17], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_Fqdn, lp_realm());
+
+       /* Now try to verify the ticket using the key associated with each of
+        * the principals which we think clients will expect us to be
+        * participating as. */
+       for (i = 0; i < sizeof(host_princ_s) / sizeof(host_princ_s[0]); i++) {
+               host_princ = NULL;
+               ret = krb5_parse_name(context, host_princ_s[i], &host_princ);
                if (ret) {
-                       DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", error_message(ret)));
+                       DEBUG(1, ("ads_keytab_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
+                               host_princ_s[i], error_message(ret)));
                        goto out;
                }
-               DEBUG(10, ("Checking principal: %s\n", princ_name));
-               /* Look for a CIFS ticket */
-               if (!strncasecmp(princ_name, "cifs/", 5) || 
-                   !strncasecmp(princ_name, "host/", 5) ||
-                   !strncasecmp(princ_name, "ldap/", 5)) {
-#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
-                       krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.keyblock);
-#else
-                       krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.key);
-#endif
-
-                       p_packet->length = ticket->length;
-                       p_packet->data = (krb5_pointer)ticket->data;
-
-                       ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt);
-                       if (!ret) {
-                               unsigned int keytype;
-                               krb5_free_unparsed_name(context, princ_name);
-                               princ_name = NULL;
-#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
-                               keytype = (unsigned int) kt_entry.keyblock.keytype;
-                               copy_EncryptionKey(&kt_entry.keyblock, keyblock);
-#else
-                               keytype = (unsigned int) kt_entry.key.enctype;
-                               /* TODO: copy the keyblock on MIT krb5*/
-#endif
-                               DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n",
-                                         keytype));
-                               
-                               break;
-                       }
-               }
-               krb5_free_unparsed_name(context, princ_name);
-               princ_name = NULL;
+               p_packet->length = ticket->length;
+               p_packet->data = (krb5_pointer)ticket->data;
+               *pp_tkt = NULL;
+               ret = krb5_rd_req(context, &auth_context, p_packet, host_princ, keytab, NULL, pp_tkt);
+               krb5_free_principal(context, host_princ);
+               if (ret) {
+                       DEBUG(0, ("krb5_rd_req(%s) failed: %s\n", host_princ_s[i], error_message(ret)));
+               } else {
+                       DEBUG(10,("krb5_rd_req succeeded for principal %s\n", host_princ_s[i]));
+                       auth_ok = True;
+                       break;
+                }
        }
-       if (ret && ret != KRB5_KT_END) {
-               /* This failed because something went wrong, not because the keytab file was empty. */
-               DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_next_entry failed (%s)\n", error_message(ret)));
-       } else if (ret == KRB5_KT_END) {
-               DEBUG(10, ("ads_keytab_verify_ticket: no keytab entry found: %s\n", error_message(ret)));
-       } else {
-               DEBUG(10, ("ads_keytab_verify_ticket: keytab entry found: %s\n", princ_name));
+
+       for (i = 0; i < sizeof(host_princ_s) / sizeof(host_princ_s[0]); i++) {
+               SAFE_FREE(host_princ_s[i]);
        }
+
   out:
 
-       if (princ_name) {
-               krb5_free_unparsed_name(context, princ_name);
-       }
-       {
-               krb5_kt_cursor zero_csr;
-               ZERO_STRUCT(zero_csr);
-               if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
-                       krb5_kt_end_seq_get(context, keytab, &cursor);
-               }
-       }
        if (keytab) {
                krb5_kt_close(context, keytab);
        }
@@ -165,10 +171,12 @@ static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_
  Try to verify a ticket using the secrets.tdb.
 ***********************************************************************************/
 
-static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context,
-                       krb5_principal host_princ,
-                       const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
-                       krb5_keyblock *keyblock)
+static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, 
+                                                krb5_auth_context auth_context,
+                                                krb5_principal host_princ,
+                                                const DATA_BLOB *ticket, krb5_data *p_packet, 
+                                                krb5_ticket **pp_tkt,
+                                                krb5_keyblock *keyblock)
 {
        krb5_error_code ret = 0;
        char *password_s = NULL;
@@ -250,12 +258,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
 ***********************************************************************************/
 
  NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, 
-                          krb5_context context,
-                          krb5_auth_context auth_context,
-                          const char *realm, const DATA_BLOB *ticket, 
-                          char **principal, DATA_BLOB *auth_data,
-                          DATA_BLOB *ap_rep,
-                          krb5_keyblock *keyblock)
+                           krb5_context context,
+                           krb5_auth_context auth_context,
+                           const char *realm, const char *service, 
+                           const DATA_BLOB *ticket, 
+                           char **principal, DATA_BLOB *auth_data,
+                           DATA_BLOB *ap_rep,
+                           krb5_keyblock *keyblock)
 {
        NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
        krb5_data packet;
@@ -267,8 +276,6 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
        char *host_princ_s = NULL;
        BOOL got_replay_mutex = False;
 
-       char *myname;
-
        char *malloc_principal;
 
        ZERO_STRUCT(packet);
@@ -279,9 +286,8 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
            like. We have to go through all this to allow us to store
            the secret internally, instead of using /etc/krb5.keytab */
 
-       myname = name_to_fqdn(mem_ctx, lp_netbios_name());
-       strlower_m(myname);
-       asprintf(&host_princ_s, "host/%s@%s", myname, lp_realm());
+       asprintf(&host_princ_s, "%s$", lp_netbios_name());
+       strlower_m(host_princ_s);
        ret = krb5_parse_name(context, host_princ_s, &host_princ);
        if (ret) {
                DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
@@ -316,11 +322,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
                goto out;
        }
 
-       ret = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, keyblock);
+       ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context, 
+                                      service, ticket, &packet, &tkt, keyblock);
        if (ret) {
                DEBUG(10, ("ads_secrets_verify_ticket: using host principal: [%s]\n", host_princ_s));
-               ret = ads_secrets_verify_ticket(context, auth_context, host_princ,
-                                                       ticket, &packet, &tkt, keyblock);
+               ret = ads_secrets_verify_ticket(mem_ctx, context, auth_context,
+                                               host_princ, ticket, 
+                                               &packet, &tkt, keyblock);
        }
 
        release_server_mutex();
index 5d61361d69f57e7c0c9f0b558a89087a29da71f3..bee93985acd7698c14a701bfa1679acddddf99a4 100644 (file)
@@ -146,6 +146,9 @@ static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *ch
 NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user) 
 {
        ntlmssp_state->user = talloc_strdup(ntlmssp_state, user);
+       if (!user) {
+               ntlmssp_state->user = NULL;
+       }
        if (!ntlmssp_state->user) {
                return NT_STATUS_NO_MEMORY;
        }
index 01757a04873cacf631f1623c8f01ab93e3537bd8..988d0b32a564c37267ca1a380b46cc0d076c0534 100644 (file)
@@ -44,6 +44,17 @@ struct spnego_state {
        struct gensec_security *sub_sec_security;
 };
 
+
+static int gensec_spnego_destroy(void *ptr)
+{
+       struct spnego_state *spnego_state = ptr;
+
+       if (spnego_state->sub_sec_security) {
+               gensec_end(&spnego_state->sub_sec_security);
+       }
+       return 0;
+}
+
 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
 {
        struct spnego_state *spnego_state;
@@ -57,6 +68,8 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
        spnego_state->state_position = SPNEGO_CLIENT_START;
        spnego_state->sub_sec_security = NULL;
 
+       talloc_set_destructor(spnego_state, gensec_spnego_destroy);
+
        gensec_security->private_data = spnego_state;
        return NT_STATUS_OK;
 }
@@ -74,6 +87,8 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
        spnego_state->state_position = SPNEGO_SERVER_START;
        spnego_state->sub_sec_security = NULL;
 
+       talloc_set_destructor(spnego_state, gensec_spnego_destroy);
+
        gensec_security->private_data = spnego_state;
        return NT_STATUS_OK;
 }
@@ -221,7 +236,8 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
                        continue;
                }
 
-               nt_status = gensec_subcontext_start(gensec_security, 
+               nt_status = gensec_subcontext_start(spnego_state, 
+                                                   gensec_security, 
                                                    &spnego_state->sub_sec_security);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        return nt_status;
@@ -257,7 +273,8 @@ static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_
        DATA_BLOB null_data_blob = data_blob(NULL,0);
 
        for (i=0; mechType && mechType[i]; i++) {
-               nt_status = gensec_subcontext_start(gensec_security,
+               nt_status = gensec_subcontext_start(spnego_state,
+                                                   gensec_security,
                                                    &spnego_state->sub_sec_security);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        break;
@@ -317,7 +334,8 @@ static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       nt_status = gensec_subcontext_start(gensec_security, 
+       nt_status = gensec_subcontext_start(spnego_state, 
+                                           gensec_security, 
                                            &spnego_state->sub_sec_security);
        if (!NT_STATUS_IS_OK(nt_status)) {
                return nt_status;
@@ -467,19 +485,12 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        return nt_status;
                } else {
                        const char **mechlist = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
-                       const char *mechListMIC;
-
-                       mechListMIC = talloc_asprintf(out_mem_ctx,"%s$@%s",
-                                                       lp_netbios_name(),
-                                                       lp_realm());
-                       if (!mechListMIC) {
-                               return NT_STATUS_NO_MEMORY;
-                       }
 
                        spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
                        spnego_out.negTokenInit.mechTypes = mechlist;
                        spnego_out.negTokenInit.reqFlags = 0;
-                       spnego_out.negTokenInit.mechListMIC = data_blob_string_const(mechListMIC);
+                       spnego_out.negTokenInit.mechListMIC
+                               = data_blob_string_const(gensec_get_target_principal(gensec_security));
                        spnego_out.negTokenInit.mechToken = unwrapped_out;
                        
                        if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
@@ -694,19 +705,6 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
        return NT_STATUS_INVALID_PARAMETER;
 }
 
-static void gensec_spnego_end(struct gensec_security *gensec_security)
-{
-       struct spnego_state *spnego_state = gensec_security->private_data;
-
-       if (spnego_state->sub_sec_security) {
-               gensec_end(&spnego_state->sub_sec_security);
-       }
-
-       talloc_free(spnego_state);
-
-       gensec_security->private_data = NULL;
-}
-
 static const struct gensec_security_ops gensec_spnego_security_ops = {
        .name           = "spnego",
        .sasl_name      = "GSS-SPNEGO",
@@ -722,7 +720,6 @@ static const struct gensec_security_ops gensec_spnego_security_ops = {
        .unseal_packet  = gensec_spnego_unseal_packet,
        .session_key    = gensec_spnego_session_key,
        .session_info   = gensec_spnego_session_info,
-       .end            = gensec_spnego_end
 };
 
 NTSTATUS gensec_spnego_init(void)
index ab32a8293429cec1169c1748ecc4582a6fd3bb46..635735a55d656ba0324f9aa3c92234deeafb8e3b 100644 (file)
@@ -266,6 +266,18 @@ NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
 }
                
 
+/*
+  end crypto state
+*/
+static int dcerpc_schannel_destroy(void *ptr)
+{
+       struct dcerpc_schannel_state *dce_schan_state = ptr;
+
+       schannel_end(&dce_schan_state->schannel_state);
+
+       return 0;
+}
+
 static NTSTATUS dcerpc_schannel_start(struct gensec_security *gensec_security)
 {
        struct dcerpc_schannel_state *dce_schan_state;
@@ -281,6 +293,8 @@ static NTSTATUS dcerpc_schannel_start(struct gensec_security *gensec_security)
                GENSEC_FEATURE_SESSION_KEY | 
                GENSEC_FEATURE_SIGN | 
                GENSEC_FEATURE_SEAL;
+
+       talloc_set_destructor(dce_schan_state, dcerpc_schannel_destroy);
        
        return NT_STATUS_OK;
 }
@@ -309,20 +323,6 @@ static NTSTATUS dcerpc_schannel_client_start(struct gensec_security *gensec_secu
        return NT_STATUS_OK;
 }
 
-/*
-  end crypto state
-*/
-static void dcerpc_schannel_end(struct gensec_security *gensec_security)
-{
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-
-       schannel_end(&dce_schan_state->schannel_state);
-
-       talloc_free(dce_schan_state);
-
-       gensec_security->private_data = NULL;
-}
-
 
 /*
   get a schannel key using a netlogon challenge on a secondary pipe
@@ -522,7 +522,6 @@ static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = {
        .session_key    = dcerpc_schannel_session_key,
        .session_info   = dcerpc_schannel_session_info,
        .sig_size       = dcerpc_schannel_sig_size,
-       .end            = dcerpc_schannel_end
 };
 
 NTSTATUS gensec_dcerpc_schannel_init(void)
index f7b9c02d8dfafb72c45a8b93bf4ae48a9cac522a..389db30f8ca18801e2afec1a2a165b404493c538 100644 (file)
@@ -88,7 +88,7 @@ BOOL secrets_init(void)
 /* read a entry from the secrets database - the caller must free the result
    if size is non-null then the size of the entry is put in there
  */
-static void *secrets_fetch(const char *key, size_t *size)
+void *secrets_fetch(const char *key, size_t *size)
 {
        TDB_DATA kbuf, dbuf;
        secrets_init();
index d8dde02c123d9ec693b46545032f24e00e44d469..85fac5680bb4c6b896dad2dba02d1f3ac25f70c6 100644 (file)
@@ -238,6 +238,8 @@ static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup
                        return status;
                }
 
+               gensec_set_target_service(gensec_ctx, "cifs");
+
                gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
 
                status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);