gensec: clarify memory ownership for gensec_session_info() and gensec_session_key()
[samba.git] / source4 / auth / gensec / gensec_gssapi.c
index 1334e799aea34705ba7b96950922b274b56218e6..55610f57425ee69308e80e493e47f2027b070c7f 100644 (file)
 #include "auth/kerberos/kerberos.h"
 #include "librpc/gen_ndr/krb5pac.h"
 #include "auth/auth.h"
-#include "lib/ldb/include/ldb.h"
+#include <ldb.h>
 #include "auth/auth_sam.h"
 #include "librpc/rpc/dcerpc.h"
 #include "auth/credentials/credentials.h"
 #include "auth/credentials/credentials_krb5.h"
 #include "auth/gensec/gensec.h"
 #include "auth/gensec/gensec_proto.h"
+#include "auth/gensec/gensec_toplevel_proto.h"
 #include "param/param.h"
 #include "auth/session_proto.h"
 #include <gssapi/gssapi.h>
 #include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_spnego.h>
 #include "auth/gensec/gensec_gssapi.h"
+#include "lib/util/util_net.h"
+
+_PUBLIC_ NTSTATUS gensec_gssapi_init(void);
 
 static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security);
 static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security);
 
-static char *gssapi_error_string(TALLOC_CTX *mem_ctx, 
-                                OM_uint32 maj_stat, OM_uint32 min_stat, 
-                                const gss_OID mech)
-{
-       OM_uint32 disp_min_stat, disp_maj_stat;
-       gss_buffer_desc maj_error_message;
-       gss_buffer_desc min_error_message;
-       char *maj_error_string, *min_error_string;
-       OM_uint32 msg_ctx = 0;
-
-       char *ret;
-
-       maj_error_message.value = NULL;
-       min_error_message.value = NULL;
-       maj_error_message.length = 0;
-       min_error_message.length = 0;
-       
-       disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE,
-                          mech, &msg_ctx, &maj_error_message);
-       disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE,
-                          mech, &msg_ctx, &min_error_message);
-       
-       maj_error_string = talloc_strndup(mem_ctx, (char *)maj_error_message.value, maj_error_message.length);
-
-       min_error_string = talloc_strndup(mem_ctx, (char *)min_error_message.value, min_error_message.length);
-
-       ret = talloc_asprintf(mem_ctx, "%s: %s", maj_error_string, min_error_string);
-
-       talloc_free(maj_error_string);
-       talloc_free(min_error_string);
-
-       gss_release_buffer(&disp_min_stat, &maj_error_message);
-       gss_release_buffer(&disp_min_stat, &min_error_message);
-
-       return ret;
-}
-
-
 static int gensec_gssapi_destructor(struct gensec_gssapi_state *gensec_gssapi_state)
 {
        OM_uint32 maj_stat, min_stat;
@@ -145,54 +112,41 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
 {
        struct gensec_gssapi_state *gensec_gssapi_state;
        krb5_error_code ret;
-       struct gsskrb5_send_to_kdc send_to_kdc;
+       const char *realm;
 
-       gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state);
+       gensec_gssapi_state = talloc_zero(gensec_security, struct gensec_gssapi_state);
        if (!gensec_gssapi_state) {
                return NT_STATUS_NO_MEMORY;
        }
-       
-       gensec_gssapi_state->gss_exchange_count = 0;
-       gensec_gssapi_state->max_wrap_buf_size
-               = lp_parm_int(gensec_security->lp_ctx, NULL, "gensec_gssapi", "max wrap buf size", 65536);
-               
-       gensec_gssapi_state->sasl = false;
-       gensec_gssapi_state->sasl_state = STAGE_GSS_NEG;
 
        gensec_security->private_data = gensec_gssapi_state;
 
        gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT;
-       gensec_gssapi_state->server_name = GSS_C_NO_NAME;
-       gensec_gssapi_state->client_name = GSS_C_NO_NAME;
-       gensec_gssapi_state->lucid = NULL;
 
        /* TODO: Fill in channel bindings */
        gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+
+       gensec_gssapi_state->server_name = GSS_C_NO_NAME;
+       gensec_gssapi_state->client_name = GSS_C_NO_NAME;
        
        gensec_gssapi_state->want_flags = 0;
-       if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "mutual", true)) {
+
+       if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "delegation_by_kdc_policy", true)) {
+               gensec_gssapi_state->want_flags |= GSS_C_DELEG_POLICY_FLAG;
+       }
+       if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "mutual", true)) {
                gensec_gssapi_state->want_flags |= GSS_C_MUTUAL_FLAG;
        }
-       if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "delegation", true)) {
+       if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "delegation", true)) {
                gensec_gssapi_state->want_flags |= GSS_C_DELEG_FLAG;
        }
-       if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "replay", true)) {
+       if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "replay", true)) {
                gensec_gssapi_state->want_flags |= GSS_C_REPLAY_FLAG;
        }
-       if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "sequence", true)) {
+       if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "sequence", true)) {
                gensec_gssapi_state->want_flags |= GSS_C_SEQUENCE_FLAG;
        }
 
-       gensec_gssapi_state->got_flags = 0;
-
-       gensec_gssapi_state->session_key = data_blob(NULL, 0);
-       gensec_gssapi_state->pac = data_blob(NULL, 0);
-
-       gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
-       gensec_gssapi_state->sig_size = 0;
-
-       talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destructor);
-
        if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
                gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG;
        }
@@ -203,26 +157,50 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
                gensec_gssapi_state->want_flags |= GSS_C_DCE_STYLE;
        }
 
-       gensec_gssapi_state->gss_oid = GSS_C_NULL_OID;
-       
-       send_to_kdc.func = smb_krb5_send_and_recv_func;
-       send_to_kdc.ptr = gensec_security->event_ctx;
+       gensec_gssapi_state->got_flags = 0;
 
-       ret = gsskrb5_set_send_to_kdc(&send_to_kdc);
+       switch (gensec_security->ops->auth_type) {
+       case DCERPC_AUTH_TYPE_SPNEGO:
+               gensec_gssapi_state->gss_oid = gss_mech_spnego;
+               break;
+       case DCERPC_AUTH_TYPE_KRB5:
+       default:
+               gensec_gssapi_state->gss_oid = gss_mech_krb5;
+               break;
+       }
+
+       ret = smb_krb5_init_context(gensec_gssapi_state,
+                                   NULL,
+                                   gensec_security->settings->lp_ctx,
+                                   &gensec_gssapi_state->smb_krb5_context);
        if (ret) {
-               DEBUG(1,("gensec_krb5_start: gsskrb5_set_send_to_kdc failed\n"));
+               DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n",
+                        error_message(ret)));
                talloc_free(gensec_gssapi_state);
                return NT_STATUS_INTERNAL_ERROR;
        }
-       if (lp_realm(gensec_security->lp_ctx) && *lp_realm(gensec_security->lp_ctx)) {
-               char *upper_realm = strupper_talloc(gensec_gssapi_state, lp_realm(gensec_security->lp_ctx));
-               if (!upper_realm) {
-                       DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm(gensec_security->lp_ctx)));
-                       talloc_free(gensec_gssapi_state);
-                       return NT_STATUS_NO_MEMORY;
-               }
-               ret = gsskrb5_set_default_realm(upper_realm);
-               talloc_free(upper_realm);
+
+       gensec_gssapi_state->client_cred = NULL;
+       gensec_gssapi_state->server_cred = NULL;
+
+       gensec_gssapi_state->lucid = NULL;
+
+       gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+
+       gensec_gssapi_state->sasl = false;
+       gensec_gssapi_state->sasl_state = STAGE_GSS_NEG;
+       gensec_gssapi_state->sasl_protection = 0;
+
+       gensec_gssapi_state->max_wrap_buf_size
+               = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "max wrap buf size", 65536);
+       gensec_gssapi_state->gss_exchange_count = 0;
+       gensec_gssapi_state->sig_size = 0;
+
+       talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destructor);
+
+       realm = lpcfg_realm(gensec_security->settings->lp_ctx);
+       if (realm != NULL) {
+               ret = gsskrb5_set_default_realm(realm);
                if (ret) {
                        DEBUG(1,("gensec_krb5_start: gsskrb5_set_default_realm failed\n"));
                        talloc_free(gensec_gssapi_state);
@@ -231,23 +209,13 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
        }
 
        /* don't do DNS lookups of any kind, it might/will fail for a netbios name */
-       ret = gsskrb5_set_dns_canonicalize(lp_parm_bool(gensec_security->lp_ctx, NULL, "krb5", "set_dns_canonicalize", false));
+       ret = gsskrb5_set_dns_canonicalize(gensec_setting_bool(gensec_security->settings, "krb5", "set_dns_canonicalize", false));
        if (ret) {
                DEBUG(1,("gensec_krb5_start: gsskrb5_set_dns_canonicalize failed\n"));
                talloc_free(gensec_gssapi_state);
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       ret = smb_krb5_init_context(gensec_gssapi_state, 
-                                   gensec_security->event_ctx,
-                                   gensec_security->lp_ctx,
-                                   &gensec_gssapi_state->smb_krb5_context);
-       if (ret) {
-               DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n",
-                        error_message(ret)));
-               talloc_free(gensec_gssapi_state);
-               return NT_STATUS_INTERNAL_ERROR;
-       }
        return NT_STATUS_OK;
 }
 
@@ -273,8 +241,7 @@ static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_securi
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        } else {
                ret = cli_credentials_get_server_gss_creds(machine_account, 
-                                                          gensec_security->event_ctx, 
-                                                          gensec_security->lp_ctx, &gcc);
+                                                          gensec_security->settings->lp_ctx, &gcc);
                if (ret) {
                        DEBUG(1, ("Aquiring acceptor credentials failed: %s\n", 
                                  error_message(ret)));
@@ -310,8 +277,8 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
        gss_OID name_type;
        OM_uint32 maj_stat, min_stat;
        const char *hostname = gensec_get_target_hostname(gensec_security);
-       const char *principal;
        struct gssapi_creds_container *gcc;
+       const char *error_string;
 
        if (!hostname) {
                DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
@@ -333,20 +300,22 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
 
        gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
 
-       gensec_gssapi_state->gss_oid = gss_mech_krb5;
+       if (cli_credentials_get_impersonate_principal(creds)) {
+               gensec_gssapi_state->want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
+       }
 
-       principal = gensec_get_target_principal(gensec_security);
-       if (principal && lp_client_use_spnego_principal(gensec_security->lp_ctx)) {
+       gensec_gssapi_state->target_principal = gensec_get_target_principal(gensec_security);
+       if (gensec_gssapi_state->target_principal) {
                name_type = GSS_C_NULL_OID;
        } else {
-               principal = talloc_asprintf(gensec_gssapi_state, "%s@%s", 
+               gensec_gssapi_state->target_principal = talloc_asprintf(gensec_gssapi_state, "%s/%s@%s",
                                            gensec_get_target_service(gensec_security), 
-                                           hostname);
+                                           hostname, lpcfg_realm(gensec_security->settings->lp_ctx));
 
-               name_type = GSS_C_NT_HOSTBASED_SERVICE;
-       }               
-       name_token.value  = discard_const_p(uint8_t, principal);
-       name_token.length = strlen(principal);
+               name_type = GSS_C_NT_USER_NAME;
+       }
+       name_token.value  = discard_const_p(uint8_t, gensec_gssapi_state->target_principal);
+       name_token.length = strlen(gensec_gssapi_state->target_principal);
 
 
        maj_stat = gss_import_name (&min_stat,
@@ -362,17 +331,23 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
 
        ret = cli_credentials_get_client_gss_creds(creds, 
                                                   gensec_security->event_ctx, 
-                                                  gensec_security->lp_ctx, &gcc);
+                                                  gensec_security->settings->lp_ctx, &gcc, &error_string);
        switch (ret) {
        case 0:
                break;
        case KRB5KDC_ERR_PREAUTH_FAILED:
+       case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+               DEBUG(1, ("Wrong username or password: %s\n", error_string));
                return NT_STATUS_LOGON_FAILURE;
        case KRB5_KDC_UNREACH:
-               DEBUG(3, ("Cannot reach a KDC we require to contact %s\n", principal));
-               return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+               DEBUG(3, ("Cannot reach a KDC we require to contact %s : %s\n", gensec_gssapi_state->target_principal, error_string));
+               return NT_STATUS_NO_LOGON_SERVERS;
+       case KRB5_CC_NOTFOUND:
+       case KRB5_CC_END:
+               DEBUG(2, ("Error obtaining ticket we require to contact %s: (possibly due to clock skew between us and the KDC) %s\n", gensec_gssapi_state->target_principal, error_string));
+               return NT_STATUS_TIME_DIFFERENCE_AT_DC;
        default:
-               DEBUG(1, ("Aquiring initiator credentials failed\n"));
+               DEBUG(1, ("Aquiring initiator credentials failed: %s\n", error_string));
                return NT_STATUS_UNSUCCESSFUL;
        }
 
@@ -449,6 +424,17 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                switch (gensec_security->gensec_role) {
                case GENSEC_CLIENT:
                {
+                       struct gsskrb5_send_to_kdc send_to_kdc;
+                       krb5_error_code ret;
+                       send_to_kdc.func = smb_krb5_send_and_recv_func;
+                       send_to_kdc.ptr = gensec_security->event_ctx;
+
+                       min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc);
+                       if (min_stat) {
+                               DEBUG(1,("gensec_krb5_start: gsskrb5_set_send_to_kdc failed\n"));
+                               return NT_STATUS_INTERNAL_ERROR;
+                       }
+
                        maj_stat = gss_init_sec_context(&min_stat, 
                                                        gensec_gssapi_state->client_cred->creds,
                                                        &gensec_gssapi_state->gssapi_context, 
@@ -465,6 +451,16 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                        if (gss_oid_p) {
                                gensec_gssapi_state->gss_oid = gss_oid_p;
                        }
+
+                       send_to_kdc.func = smb_krb5_send_and_recv_func;
+                       send_to_kdc.ptr = NULL;
+
+                       ret = gsskrb5_set_send_to_kdc(&send_to_kdc);
+                       if (ret) {
+                               DEBUG(1,("gensec_krb5_start: gsskrb5_set_send_to_kdc failed\n"));
+                               return NT_STATUS_INTERNAL_ERROR;
+                       }
+
                        break;
                }
                case GENSEC_SERVER:
@@ -505,28 +501,15 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                        /* We may have been invoked as SASL, so there
                         * is more work to do */
                        if (gensec_gssapi_state->sasl) {
-                               /* Due to a very subtle interaction
-                                * with SASL and the LDAP libs, we
-                                * must ensure the data pointer is 
-                                * != NULL, but the length is 0.  
-                                *
-                                * This ensures we send a 'zero
-                                * length' (rather than NULL) response 
-                                */
-                               
-                               if (!out->data) {
-                                       out->data = (uint8_t *)talloc_strdup(out_mem_ctx, "\0");
-                               }
-
                                gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG;
                                return NT_STATUS_MORE_PROCESSING_REQUIRED;
                        } else {
                                gensec_gssapi_state->sasl_state = STAGE_DONE;
 
                                if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
-                                       DEBUG(5, ("GSSAPI Connection will be cryptographicly sealed\n"));
+                                       DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n"));
                                } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
-                                       DEBUG(5, ("GSSAPI Connection will be cryptographicly signed\n"));
+                                       DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n"));
                                } else {
                                        DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n"));
                                }
@@ -538,27 +521,100 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                        gss_release_buffer(&min_stat2, &output_token);
                        
                        return NT_STATUS_MORE_PROCESSING_REQUIRED;
+               } else if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
+                       gss_cred_id_t creds;
+                       gss_name_t name;
+                       gss_buffer_desc buffer;
+                       OM_uint32 lifetime = 0;
+                       gss_cred_usage_t usage;
+                       const char *role = NULL;
+                       DEBUG(0, ("GSS %s Update(krb5)(%d) Update failed, credentials expired during GSSAPI handshake!\n",
+                                 role,
+                                 gensec_gssapi_state->gss_exchange_count));
+
+                       
+                       switch (gensec_security->gensec_role) {
+                       case GENSEC_CLIENT:
+                               creds = gensec_gssapi_state->client_cred->creds;
+                               role = "client";
+                       case GENSEC_SERVER:
+                               creds = gensec_gssapi_state->server_cred->creds;
+                               role = "server";
+                       }
+
+                       maj_stat = gss_inquire_cred(&min_stat, 
+                                                   creds,
+                                                   &name, &lifetime, &usage, NULL);
+
+                       if (maj_stat == GSS_S_COMPLETE) {
+                               const char *usage_string;
+                               switch (usage) {
+                               case GSS_C_BOTH:
+                                       usage_string = "GSS_C_BOTH";
+                                       break;
+                               case GSS_C_ACCEPT:
+                                       usage_string = "GSS_C_ACCEPT";
+                                       break;
+                               case GSS_C_INITIATE:
+                                       usage_string = "GSS_C_INITIATE";
+                                       break;
+                               }
+                               maj_stat = gss_display_name(&min_stat, name, &buffer, NULL);
+                               if (maj_stat) {
+                                       buffer.value = NULL;
+                                       buffer.length = 0;
+                               }
+                               if (lifetime > 0) {
+                                       DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", 
+                                                 (int)buffer.length, (int)buffer.length, (char *)buffer.value, 
+                                                 lifetime, usage_string));
+                               } else {
+                                       DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", 
+                                                 (int)buffer.length, (int)buffer.length, (char *)buffer.value, 
+                                                 usage_string));
+                               }
+                               gss_release_buffer(&min_stat, &buffer);
+                               gss_release_name(&min_stat, &name);
+                       } else if (maj_stat != GSS_S_COMPLETE) {
+                               DEBUG(0, ("inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
+                                         gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+                       }
+                       return NT_STATUS_INVALID_PARAMETER;
                } else if (gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) {
                        switch (min_stat) {
-                       case KRB5_KDC_UNREACH:
-                               DEBUG(3, ("Cannot reach a KDC we require: %s\n",
-                                         gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+                       case KRB5KRB_AP_ERR_TKT_NYV:
+                               DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n",
+                                         gensec_gssapi_state->target_principal,
+                                         gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+                               return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */
+                       case KRB5KRB_AP_ERR_TKT_EXPIRED:
+                               DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n",
+                                         gensec_gssapi_state->target_principal,
+                                         gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
                                return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+                       case KRB5_KDC_UNREACH:
+                               DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticetk to %s: %s\n",
+                                         gensec_gssapi_state->target_principal,
+                                         gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+                               return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */
                        case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
-                               DEBUG(3, ("Server is not registered with our KDC: %s\n", 
-                                         gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+                               DEBUG(3, ("Server %s is not registered with our KDC: %s\n",
+                                         gensec_gssapi_state->target_principal,
+                                         gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
                                return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
                        case KRB5KRB_AP_ERR_MSG_TYPE:
                                /* garbage input, possibly from the auto-mech detection */
                                return NT_STATUS_INVALID_PARAMETER;
                        default:
-                               DEBUG(1, ("GSS Update(krb5)(%d) Update failed: %s\n", 
+                               DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n",
+                                         gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server",
                                          gensec_gssapi_state->gss_exchange_count,
                                          gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
                                return nt_status;
                        }
                } else {
-                       DEBUG(1, ("GSS Update(%d) failed: %s\n", 
+                       DEBUG(1, ("GSS %s Update(%d) failed: %s\n",
+                                 gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server",
                                  gensec_gssapi_state->gss_exchange_count,
                                  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
                        return nt_status;
@@ -612,18 +668,21 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                        gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), 
                                                                     gensec_gssapi_state->max_wrap_buf_size);
                        gensec_gssapi_state->sasl_protection = 0;
-                       if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
-                               if (security_supported & NEG_SEAL) {
+                       if (security_supported & NEG_SEAL) {
+                               if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
                                        gensec_gssapi_state->sasl_protection |= NEG_SEAL;
                                }
-                       } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
-                               if (security_supported & NEG_SIGN) {
+                       }
+                       if (security_supported & NEG_SIGN) {
+                               if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
                                        gensec_gssapi_state->sasl_protection |= NEG_SIGN;
                                }
-                       } else if (security_supported & NEG_NONE) {
+                       }
+                       if (security_supported & NEG_NONE) {
                                gensec_gssapi_state->sasl_protection |= NEG_NONE;
-                       } else {
-                               DEBUG(1, ("Remote server does not support unprotected connections"));
+                       }
+                       if (gensec_gssapi_state->sasl_protection == 0) {
+                               DEBUG(1, ("Remote server does not support unprotected connections\n"));
                                return NT_STATUS_ACCESS_DENIED;
                        }
 
@@ -656,11 +715,11 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                        gensec_gssapi_state->sasl_state = STAGE_DONE;
 
                        if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
-                               DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographicly sealed\n"));
+                               DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n"));
                        } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
-                               DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographicly signed\n"));
+                               DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n"));
                        } else {
-                               DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographicly protection\n"));
+                               DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographically protection\n"));
                        }
 
                        return NT_STATUS_OK;
@@ -757,33 +816,36 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                /* first byte is the proposed security */
                security_accepted = maxlength_accepted[0];
                maxlength_accepted[0] = '\0';
-               
+
                /* Rest is the proposed max wrap length */
                gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), 
                                                             gensec_gssapi_state->max_wrap_buf_size);
 
                gensec_gssapi_state->sasl_protection = 0;
-               if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
-                       if (security_accepted & NEG_SEAL) {
-                               gensec_gssapi_state->sasl_protection |= NEG_SEAL;
+               if (security_accepted & NEG_SEAL) {
+                       if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+                               DEBUG(1, ("Remote client wanted seal, but gensec refused\n"));
+                               return NT_STATUS_ACCESS_DENIED;
                        }
-               } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
-                       if (security_accepted & NEG_SIGN) {
-                               gensec_gssapi_state->sasl_protection |= NEG_SIGN;
+                       gensec_gssapi_state->sasl_protection |= NEG_SEAL;
+               }
+               if (security_accepted & NEG_SIGN) {
+                       if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+                               DEBUG(1, ("Remote client wanted sign, but gensec refused\n"));
+                               return NT_STATUS_ACCESS_DENIED;
                        }
-               } else if (security_accepted & NEG_NONE) {
+                       gensec_gssapi_state->sasl_protection |= NEG_SIGN;
+               }
+               if (security_accepted & NEG_NONE) {
                        gensec_gssapi_state->sasl_protection |= NEG_NONE;
-               } else {
-                       DEBUG(1, ("Remote client does not support unprotected connections, but we failed to negotiate anything better"));
-                       return NT_STATUS_ACCESS_DENIED;
                }
 
                /* quirk:  This changes the value that gensec_have_feature returns, to be that after SASL negotiation */
                gensec_gssapi_state->sasl_state = STAGE_DONE;
                if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
-                       DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographicly sealed\n"));
+                       DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n"));
                } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
-                       DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographicly signed\n"));
+                       DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n"));
                } else {
                        DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n"));
                }
@@ -973,7 +1035,6 @@ static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_securit
 }
 
 static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, 
-                                           TALLOC_CTX *mem_ctx, 
                                            uint8_t *data, size_t length, 
                                            const uint8_t *whole_pdu, size_t pdu_length,
                                            const DATA_BLOB *sig)
@@ -988,7 +1049,7 @@ static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_secur
 
        dump_data_pw("gensec_gssapi_unseal_packet: sig\n", sig->data, sig->length);
 
-       in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
+       in = data_blob_talloc(gensec_security, NULL, sig->length + length);
 
        memcpy(in.data, sig->data, sig->length);
        memcpy(in.data + sig->length, data, length);
@@ -1002,9 +1063,12 @@ static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_secur
                              &output_token, 
                              &conf_state,
                              &qop_state);
+       talloc_free(in.data);
        if (GSS_ERROR(maj_stat)) {
+               char *error_string = gssapi_error_string(NULL, maj_stat, min_stat, gensec_gssapi_state->gss_oid);
                DEBUG(1, ("gensec_gssapi_unseal_packet: GSS UnWrap failed: %s\n", 
-                         gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+                         error_string));
+               talloc_free(error_string);
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -1063,7 +1127,6 @@ static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_securit
 }
 
 static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
-                                          TALLOC_CTX *mem_ctx, 
                                           const uint8_t *data, size_t length, 
                                           const uint8_t *whole_pdu, size_t pdu_length, 
                                           const DATA_BLOB *sig)
@@ -1094,8 +1157,10 @@ static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_securi
                              &input_token,
                              &qop_state);
        if (GSS_ERROR(maj_stat)) {
-               DEBUG(1, ("GSS VerifyMic failed: %s\n",
-                         gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+               char *error_string = gssapi_error_string(NULL, maj_stat, min_stat, gensec_gssapi_state->gss_oid);
+               DEBUG(1, ("GSS VerifyMic failed: %s\n", error_string));
+               talloc_free(error_string);
+
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -1142,10 +1207,10 @@ static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security,
                        return false;
                }
 
-               if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "force_new_spnego", false)) {
+               if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "force_new_spnego", false)) {
                        return true;
                }
-               if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "disable_new_spnego", false)) {
+               if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "disable_new_spnego", false)) {
                        return false;
                }
 
@@ -1174,6 +1239,7 @@ static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security,
  * This breaks all the abstractions, but what do you expect...
  */
 static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
+                                         TALLOC_CTX *mem_ctx,
                                          DATA_BLOB *session_key) 
 {
        struct gensec_gssapi_state *gensec_gssapi_state
@@ -1181,9 +1247,8 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit
        OM_uint32 maj_stat, min_stat;
        krb5_keyblock *subkey;
 
-       if (gensec_gssapi_state->session_key.data) {
-               *session_key = gensec_gssapi_state->session_key;
-               return NT_STATUS_OK;
+       if (gensec_gssapi_state->sasl_state != STAGE_DONE) {
+               return NT_STATUS_NO_USER_SESSION_KEY;
        }
 
        maj_stat = gsskrb5_get_subkey(&min_stat,
@@ -1197,13 +1262,9 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit
        DEBUG(10, ("Got KRB5 session key of length %d%s\n",
                   (int)KRB5_KEY_LENGTH(subkey),
                   (gensec_gssapi_state->sasl_state == STAGE_DONE)?" (done)":""));
-       *session_key = data_blob_talloc(gensec_gssapi_state,
+       *session_key = data_blob_talloc(mem_ctx,
                                        KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
        krb5_free_keyblock(gensec_gssapi_state->smb_krb5_context->krb5_context, subkey);
-       if (gensec_gssapi_state->sasl_state == STAGE_DONE) {
-               /* only cache in the done stage */
-               gensec_gssapi_state->session_key = *session_key;
-       }
        dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
 
        return NT_STATUS_OK;
@@ -1213,17 +1274,19 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit
  * this session.  This uses either the PAC (if present) or a local
  * database lookup */
 static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
+                                          TALLOC_CTX *mem_ctx_out,
                                           struct auth_session_info **_session_info) 
 {
        NTSTATUS nt_status;
        TALLOC_CTX *mem_ctx;
        struct gensec_gssapi_state *gensec_gssapi_state
                = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
-       struct auth_serversupplied_info *server_info = NULL;
+       struct auth_user_info_dc *user_info_dc = NULL;
        struct auth_session_info *session_info = NULL;
        OM_uint32 maj_stat, min_stat;
-       gss_buffer_desc pac;
        DATA_BLOB pac_blob;
+       struct PAC_SIGNATURE_DATA *pac_srv_sig = NULL;
+       struct PAC_SIGNATURE_DATA *pac_kdc_sig = NULL;
        
        if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
            || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
@@ -1232,33 +1295,35 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
                return NT_STATUS_INVALID_PARAMETER;
        }
                
-       mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context"); 
+       mem_ctx = talloc_named(mem_ctx_out, 0, "gensec_gssapi_session_info context");
        NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
 
-       maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, 
-                                                              gensec_gssapi_state->gssapi_context, 
-                                                              KRB5_AUTHDATA_WIN2K_PAC,
-                                                              &pac);
-       
-       
-       if (maj_stat == 0) {
-               pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length);
-               gss_release_buffer(&min_stat, &pac);
-
-       } else {
-               pac_blob = data_blob(NULL, 0);
-       }
+       nt_status = gssapi_obtain_pac_blob(mem_ctx,  gensec_gssapi_state->gssapi_context,
+                                          gensec_gssapi_state->client_name,
+                                          &pac_blob);
        
        /* IF we have the PAC - otherwise we need to get this
         * data from elsewere - local ldb, or (TODO) lookup of some
         * kind... 
         */
-       if (pac_blob.length) {
-               nt_status = kerberos_pac_blob_to_server_info(mem_ctx, 
-                                                            lp_iconv_convenience(gensec_security->lp_ctx),
-                                                            pac_blob, 
-                                                            gensec_gssapi_state->smb_krb5_context->krb5_context,
-                                                            &server_info);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               pac_srv_sig = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
+               if (!pac_srv_sig) {
+                       talloc_free(mem_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               pac_kdc_sig = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
+               if (!pac_kdc_sig) {
+                       talloc_free(mem_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               nt_status = kerberos_pac_blob_to_user_info_dc(mem_ctx,
+                                                             pac_blob,
+                                                             gensec_gssapi_state->smb_krb5_context->krb5_context,
+                                                             &user_info_dc,
+                                                             pac_srv_sig,
+                                                             pac_kdc_sig);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        talloc_free(mem_ctx);
                        return nt_status;
@@ -1289,12 +1354,15 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
                        return NT_STATUS_NO_MEMORY;
                }
 
-               if (!lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec", "require_pac", false)) {
+               if (gensec_security->auth_context && 
+                   !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
                        DEBUG(1, ("Unable to find PAC, resorting to local user lookup: %s\n",
                                  gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
-                       nt_status = sam_get_server_info_principal(mem_ctx, gensec_security->event_ctx, 
-                                                                 gensec_security->lp_ctx, principal_string,
-                                                                 &server_info);
+                       nt_status = gensec_security->auth_context->get_user_info_dc_principal(mem_ctx,
+                                                                                            gensec_security->auth_context, 
+                                                                                            principal_string,
+                                                                                            NULL,
+                                                                                            &user_info_dc);
                        
                        if (!NT_STATUS_IS_OK(nt_status)) {
                                talloc_free(mem_ctx);
@@ -1308,24 +1376,32 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
                }
        }
 
-       /* references the server_info into the session_info */
-       nt_status = auth_generate_session_info(mem_ctx, gensec_security->event_ctx, 
-                                              gensec_security->lp_ctx, server_info, &session_info);
+       /* references the user_info_dc into the session_info */
+       nt_status = gensec_generate_session_info(mem_ctx, gensec_security,
+                                                user_info_dc, &session_info);
        if (!NT_STATUS_IS_OK(nt_status)) {
                talloc_free(mem_ctx);
                return nt_status;
        }
 
-       nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
+       nt_status = gensec_gssapi_session_key(gensec_security, session_info, &session_info->session_key);
        if (!NT_STATUS_IS_OK(nt_status)) {
                talloc_free(mem_ctx);
                return nt_status;
        }
 
+       /* Allow torture tests to check the PAC signatures */
+       if (session_info->torture) {
+               session_info->torture->pac_srv_sig = talloc_steal(session_info->torture, pac_srv_sig);
+               session_info->torture->pac_kdc_sig = talloc_steal(session_info->torture, pac_kdc_sig);
+       }
+
        if (!(gensec_gssapi_state->got_flags & GSS_C_DELEG_FLAG)) {
                DEBUG(10, ("gensec_gssapi: NO delegated credentials supplied by client\n"));
        } else {
                krb5_error_code ret;
+               const char *error_string;
+
                DEBUG(10, ("gensec_gssapi: delegated credentials supplied by client\n"));
                session_info->credentials = cli_credentials_init(session_info);
                if (!session_info->credentials) {
@@ -1333,17 +1409,17 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
                        return NT_STATUS_NO_MEMORY;
                }
 
-               cli_credentials_set_conf(session_info->credentials, gensec_security->lp_ctx);
+               cli_credentials_set_conf(session_info->credentials, gensec_security->settings->lp_ctx);
                /* Just so we don't segfault trying to get at a username */
                cli_credentials_set_anonymous(session_info->credentials);
                
                ret = cli_credentials_set_client_gss_creds(session_info->credentials, 
-                                                          gensec_security->event_ctx,
-                                                          gensec_security->lp_ctx, 
+                                                          gensec_security->settings->lp_ctx,
                                                           gensec_gssapi_state->delegated_cred_handle,
-                                                          CRED_SPECIFIED);
+                                                          CRED_SPECIFIED, &error_string);
                if (ret) {
                        talloc_free(mem_ctx);
+                       DEBUG(2,("Failed to get gss creds: %s\n", error_string));
                        return NT_STATUS_NO_MEMORY;
                }
                
@@ -1353,14 +1429,13 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
                /* It has been taken from this place... */
                gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
        }
-       talloc_steal(gensec_gssapi_state, session_info);
+       *_session_info = talloc_steal(mem_ctx_out, session_info);
        talloc_free(mem_ctx);
-       *_session_info = session_info;
 
        return NT_STATUS_OK;
 }
 
-size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size)
+static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size)
 {
        struct gensec_gssapi_state *gensec_gssapi_state
                = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);