TODO downgrade on LOGON_FAILURE??? gensec: move event-using code to gensec_update...
[metze/samba/wip.git] / source4 / auth / gensec / gensec_gssapi.c
index 6ecd29bf340ce09ba8ecc6a8ac764f4963a3d9ef..564c20cb48223b1c5fcda4a68e21a5fe1693704e 100644 (file)
@@ -169,9 +169,6 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
                break;
        }
 
-       gensec_gssapi_state->session_key = data_blob(NULL, 0);
-       gensec_gssapi_state->pac = data_blob(NULL, 0);
-
        ret = smb_krb5_init_context(gensec_gssapi_state,
                                    NULL,
                                    gensec_security->settings->lp_ctx,
@@ -270,18 +267,63 @@ static NTSTATUS gensec_gssapi_sasl_server_start(struct gensec_security *gensec_s
        return nt_status;
 }
 
+static NTSTATUS gensec_gssapi_client_creds(struct gensec_security *gensec_security)
+{
+       struct gensec_gssapi_state *gensec_gssapi_state;
+       struct gssapi_creds_container *gcc;
+       struct cli_credentials *creds = gensec_get_credentials(gensec_security);
+       const char *error_string;
+       int ret;
+
+       gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+
+       /* Only run this the first time the update() call is made */
+       if (gensec_gssapi_state->client_cred) {
+               return NT_STATUS_OK;
+       }
+
+       ret = cli_credentials_get_client_gss_creds(creds,
+                                                      gensec_security->event_ctx,
+                                                      gensec_security->settings->lp_ctx, &gcc, &error_string);
+       switch (ret) {
+       case 0:
+               break;
+       case EINVAL:
+               DEBUG(3, ("Cannot obtain client GSS credentials we need to contact %s : %s\n", gensec_gssapi_state->target_principal, error_string));
+               return NT_STATUS_INVALID_PARAMETER;
+       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 : %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: %s\n", error_string));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       gensec_gssapi_state->client_cred = gcc;
+       if (!talloc_reference(gensec_gssapi_state, gcc)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
 static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security)
 {
        struct gensec_gssapi_state *gensec_gssapi_state;
        struct cli_credentials *creds = gensec_get_credentials(gensec_security);
-       krb5_error_code ret;
        NTSTATUS nt_status;
        gss_buffer_desc name_token;
        gss_OID name_type;
        OM_uint32 maj_stat, min_stat;
        const char *hostname = gensec_get_target_hostname(gensec_security);
-       struct gssapi_creds_container *gcc;
-       const char *error_string;
 
        if (!hostname) {
                DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
@@ -332,33 +374,6 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       ret = cli_credentials_get_client_gss_creds(creds, 
-                                                  gensec_security->event_ctx, 
-                                                  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 : %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: %s\n", error_string));
-               return NT_STATUS_UNSUCCESSFUL;
-       }
-
-       gensec_gssapi_state->client_cred = gcc;
-       if (!talloc_reference(gensec_gssapi_state, gcc)) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       
        return NT_STATUS_OK;
 }
 
@@ -429,6 +444,12 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
                {
                        struct gsskrb5_send_to_kdc send_to_kdc;
                        krb5_error_code ret;
+
+                       nt_status = gensec_gssapi_client_creds(gensec_security);
+                       if (!NT_STATUS_IS_OK(nt_status)) {
+                               return nt_status;
+                       }
+
                        send_to_kdc.func = smb_krb5_send_and_recv_func;
                        send_to_kdc.ptr = gensec_security->event_ctx;
 
@@ -1038,7 +1059,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)
@@ -1053,7 +1073,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);
@@ -1067,9 +1087,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;
        }
 
@@ -1128,7 +1151,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)
@@ -1159,8 +1181,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;
        }
 
@@ -1239,6 +1263,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
@@ -1250,11 +1275,6 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit
                return NT_STATUS_NO_USER_SESSION_KEY;
        }
 
-       if (gensec_gssapi_state->session_key.data) {
-               *session_key = gensec_gssapi_state->session_key;
-               return NT_STATUS_OK;
-       }
-
        maj_stat = gsskrb5_get_subkey(&min_stat,
                                      gensec_gssapi_state->gssapi_context,
                                      &subkey);
@@ -1266,10 +1286,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);
-       gensec_gssapi_state->session_key = *session_key;
        dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
 
        return NT_STATUS_OK;
@@ -1279,6 +1298,7 @@ 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;
@@ -1299,7 +1319,7 @@ 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);
 
        nt_status = gssapi_obtain_pac_blob(mem_ctx,  gensec_gssapi_state->gssapi_context,
@@ -1388,7 +1408,7 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
                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;
@@ -1433,9 +1453,8 @@ 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;
 }