Imported Upstream version 1.17
[platform/upstream/krb5.git] / src / lib / gssapi / krb5 / accept_sec_context.c
index 7dca130..5baa6ce 100644 (file)
@@ -251,7 +251,7 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
         cred->usage = GSS_C_INITIATE; /* we can't accept with this */
         /* cred->name already set */
         cred->keytab = NULL; /* no keytab associated with this... */
-        cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
+        cred->expire = creds[0]->times.endtime; /* store the end time */
         cred->ccache = ccache; /* the ccache containing the credential */
         cred->destroy_ccache = 1;
         ccache = NULL; /* cred takes ownership so don't destroy */
@@ -325,12 +325,6 @@ kg_accept_dce(minor_status, context_handle, verifier_cred_handle,
         goto fail;
     }
 
-    if (ctx->krb_times.endtime < now) {
-        code = 0;
-        major_status = GSS_S_CREDENTIALS_EXPIRED;
-        goto fail;
-    }
-
     ap_rep.data = input_token->value;
     ap_rep.length = input_token->length;
 
@@ -357,13 +351,15 @@ kg_accept_dce(minor_status, context_handle, verifier_cred_handle,
     if (mech_type)
         *mech_type = ctx->mech_used;
 
-    if (time_rec)
-        *time_rec = ctx->krb_times.endtime - now;
+    if (time_rec) {
+        *time_rec = ts_delta(ctx->krb_times.endtime, now) +
+            ctx->k5_context->clockskew;
+    }
 
+    /* Never return GSS_C_DELEG_FLAG since we don't support DCE credential
+     * delegation yet. */
     if (ret_flags)
-        *ret_flags = ctx->gss_flags;
-
-    /* XXX no support for delegated credentials yet */
+        *ret_flags = (ctx->gss_flags & ~GSS_C_DELEG_FLAG);
 
     *minor_status = 0;
 
@@ -441,7 +437,6 @@ kg_accept_krb5(minor_status, context_handle,
     char *sptr;
     OM_uint32 tmp;
     size_t md5len;
-    int bigend;
     krb5_gss_cred_id_t cred = 0;
     krb5_data ap_rep, ap_req;
     unsigned int i;
@@ -451,7 +446,6 @@ kg_accept_krb5(minor_status, context_handle,
     krb5_checksum reqcksum;
     krb5_gss_name_t name = NULL;
     krb5_ui_4 gss_flags = 0;
-    int decode_req_message = 0;
     krb5_gss_ctx_id_rec *ctx = NULL;
     krb5_timestamp now;
     gss_buffer_desc token;
@@ -464,15 +458,17 @@ kg_accept_krb5(minor_status, context_handle,
     OM_uint32 tmp_minor_status;
     krb5_error krb_error_data;
     krb5_data scratch;
-    gss_cred_id_t cred_handle = NULL;
+    gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
     krb5_gss_cred_id_t deleg_cred = NULL;
     krb5int_access kaccess;
     int cred_rcache = 0;
     int no_encap = 0;
+    int token_deleg_flag = 0;
     krb5_flags ap_req_options = 0;
     krb5_enctype negotiated_etype;
     krb5_authdata_context ad_context = NULL;
     krb5_principal accprinc = NULL;
+    krb5_ap_req *request = NULL;
 
     code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
     if (code) {
@@ -507,23 +503,23 @@ kg_accept_krb5(minor_status, context_handle,
     if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
         major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
                                              GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
-                                             GSS_C_ACCEPT, &cred_handle,
+                                             GSS_C_ACCEPT, &defcred,
                                              NULL, NULL);
         if (major_status != GSS_S_COMPLETE) {
             code = *minor_status;
             goto fail;
         }
-    } else {
-        major_status = krb5_gss_validate_cred(minor_status,
-                                              verifier_cred_handle);
-        if (GSS_ERROR(major_status)) {
-            code = *minor_status;
-            goto fail;
-        }
-        cred_handle = verifier_cred_handle;
+        verifier_cred_handle = defcred;
     }
 
-    cred = (krb5_gss_cred_id_t) cred_handle;
+    /* Resolve any initiator state in the verifier cred and lock it. */
+    major_status = kg_cred_resolve(minor_status, context, verifier_cred_handle,
+                                   GSS_C_NO_NAME);
+    if (GSS_ERROR(major_status)) {
+        code = *minor_status;
+        goto fail;
+    }
+    cred = (krb5_gss_cred_id_t)verifier_cred_handle;
 
     /* make sure the supplied credentials are valid for accept */
 
@@ -587,7 +583,6 @@ kg_accept_krb5(minor_status, context_handle,
 
     sptr = (char *) ptr;
     TREAD_STR(sptr, ap_req.data, ap_req.length);
-    decode_req_message = 1;
 
     /* construct the sender_addr */
 
@@ -604,6 +599,12 @@ kg_accept_krb5(minor_status, context_handle,
     }
 
     /* decode the AP_REQ message */
+    code = decode_krb5_ap_req(&ap_req, &request);
+    if (code) {
+        major_status = GSS_S_FAILURE;
+        goto done;
+    }
+    ticket = request->ticket;
 
     /* decode the message */
 
@@ -626,8 +627,8 @@ kg_accept_krb5(minor_status, context_handle,
 
     /* Limit the encryption types negotiated (if requested). */
     if (cred->req_enctypes) {
-        if ((code = krb5_set_default_tgs_enctypes(context,
-                                                  cred->req_enctypes))) {
+        if ((code = krb5_auth_con_setpermetypes(context, auth_context,
+                                                cred->req_enctypes))) {
             major_status = GSS_S_FAILURE;
             goto fail;
         }
@@ -640,8 +641,9 @@ kg_accept_krb5(minor_status, context_handle,
         }
     }
 
-    code = krb5_rd_req(context, &auth_context, &ap_req, accprinc,
-                       cred->keytab, &ap_req_options, &ticket);
+    code = krb5_rd_req_decoded(context, &auth_context, request, accprinc,
+                               cred->keytab, &ap_req_options, NULL);
+
     krb5_free_principal(context, accprinc);
     if (code) {
         major_status = GSS_S_FAILURE;
@@ -652,25 +654,16 @@ kg_accept_krb5(minor_status, context_handle,
 
     krb5_auth_con_getauthenticator(context, auth_context, &authdat);
 
-#if 0
-    /* make sure the necessary parts of the authdat are present */
-
-    if ((authdat->authenticator->subkey == NULL) ||
-        (authdat->ticket->enc_part2 == NULL)) {
-        code = KG_NO_SUBKEY;
-        major_status = GSS_S_FAILURE;
-        goto fail;
-    }
-#endif
-
     if (authdat->checksum == NULL) {
-        /* missing checksum counts as "inappropriate type" */
-        code = KRB5KRB_AP_ERR_INAPP_CKSUM;
-        major_status = GSS_S_FAILURE;
-        goto fail;
-    }
-
-    if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
+        /*
+         * Some SMB client implementations use handcrafted GSSAPI code that
+         * does not provide a checksum.  MS-KILE documents that the Microsoft
+         * implementation considers a missing checksum acceptable; the server
+         * assumes all flags are unset in this case, and does not check channel
+         * bindings.
+         */
+        gss_flags = 0;
+    } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
         /* Samba does not send 0x8003 GSS-API checksums */
         krb5_boolean valid;
         krb5_key subkey;
@@ -697,9 +690,10 @@ kg_accept_krb5(minor_status, context_handle,
             goto fail;
         }
 
-        gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
-        bigend = 0;
-        decode_req_message = 0;
+        /* Use ap_options from the request to guess the mutual flag. */
+        gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+        if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
+            gss_flags |= GSS_C_MUTUAL_FLAG;
     } else {
         /* gss krb5 v1 */
 
@@ -726,37 +720,16 @@ kg_accept_krb5(minor_status, context_handle,
             goto fail;
         }
 
-        /*
-          "Be liberal in what you accept, and
-          conservative in what you send"
-          -- rfc1123
-
-          This code will let this acceptor interoperate with an initiator
-          using little-endian or big-endian integer encoding.
-        */
-
         ptr = (unsigned char *) authdat->checksum->contents;
-        bigend = 0;
 
-        TREAD_INT(ptr, tmp, bigend);
+        TREAD_INT(ptr, tmp, 0);
 
         if (tmp != md5len) {
-            ptr = (unsigned char *) authdat->checksum->contents;
-            bigend = 1;
-
-            TREAD_INT(ptr, tmp, bigend);
-
-            if (tmp != md5len) {
-                code = KG_BAD_LENGTH;
-                major_status = GSS_S_FAILURE;
-                goto fail;
-            }
+            code = KG_BAD_LENGTH;
+            major_status = GSS_S_FAILURE;
+            goto fail;
         }
 
-        /* at this point, bigend is set according to the initiator's
-           byte order */
-
-
         /*
           The following section of code attempts to implement the
           optional channel binding facility as described in RFC2743.
@@ -772,7 +745,7 @@ kg_accept_krb5(minor_status, context_handle,
 
         if ((code = kg_checksum_channel_bindings(context,
                                                  input_chan_bindings,
-                                                 &reqcksum, bigend))) {
+                                                 &reqcksum))) {
             major_status = GSS_S_BAD_BINDINGS;
             goto fail;
         }
@@ -794,24 +767,22 @@ kg_accept_krb5(minor_status, context_handle,
         xfree(reqcksum.contents);
         reqcksum.contents = 0;
 
-        TREAD_INT(ptr, gss_flags, bigend);
-#if 0
-        gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if
-                                           there's a delegation, we'll set
-                                           it below */
-#endif
-        decode_req_message = 0;
+        /* Read the token flags.  Remember if GSS_C_DELEG_FLAG was set, but
+         * mask it out until we actually read a delegated credential. */
+        TREAD_INT(ptr, gss_flags, 0);
+        token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG);
+        gss_flags &= ~GSS_C_DELEG_FLAG;
 
         /* if the checksum length > 24, there are options to process */
 
         i = authdat->checksum->length - 24;
-        if (i && (gss_flags & GSS_C_DELEG_FLAG)) {
+        if (i && token_deleg_flag) {
             if (i >= 4) {
-                TREAD_INT16(ptr, option_id, bigend);
-                TREAD_INT16(ptr, option.length, bigend);
+                TREAD_INT16(ptr, option_id, 0);
+                TREAD_INT16(ptr, option.length, 0);
                 i -= 4;
 
-                if (i < option.length || option.length < 0) {
+                if (i < option.length) {
                     code = KG_BAD_LENGTH;
                     major_status = GSS_S_FAILURE;
                     goto fail;
@@ -840,6 +811,7 @@ kg_accept_krb5(minor_status, context_handle,
                     goto fail;
                 }
 
+                gss_flags |= GSS_C_DELEG_FLAG;
             } /* if i >= 4 */
             /* ignore any additional trailing data, for now */
         }
@@ -904,7 +876,6 @@ kg_accept_krb5(minor_status, context_handle,
                                       GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
                                       GSS_C_EXTENDED_ERROR_FLAG)));
     ctx->seed_init = 0;
-    ctx->big_endian = bigend;
     ctx->cred_rcache = cred_rcache;
 
     /* XXX move this into gss_name_t */
@@ -989,8 +960,6 @@ kg_accept_krb5(minor_status, context_handle,
         ctx->gss_flags |= GSS_C_DELEG_FLAG;
     }
 
-    krb5_free_ticket(context, ticket); /* Done with ticket */
-
     {
         krb5_int32 seq_temp;
         krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
@@ -1002,16 +971,15 @@ kg_accept_krb5(minor_status, context_handle,
         goto fail;
     }
 
-    if (ctx->krb_times.endtime < now) {
-        code = 0;
-        major_status = GSS_S_CREDENTIALS_EXPIRED;
+    code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
+                           (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
+                           (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
+                           ctx->proto);
+    if (code) {
+        major_status = GSS_S_FAILURE;
         goto fail;
     }
 
-    g_order_init(&(ctx->seqstate), ctx->seq_recv,
-                 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
-                 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
-
     /* DCE_STYLE implies mutual authentication */
     if (ctx->gss_flags & GSS_C_DCE_STYLE)
         ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
@@ -1166,8 +1134,10 @@ kg_accept_krb5(minor_status, context_handle,
     if (mech_type)
         *mech_type = (gss_OID) mech_used;
 
+    /* Add the maximum allowable clock skew as a grace period for context
+     * expiration, just as we do for the ticket. */
     if (time_rec)
-        *time_rec = ctx->krb_times.endtime - now;
+        *time_rec = ts_delta(ctx->krb_times.endtime, now) + context->clockskew;
 
     if (ret_flags)
         *ret_flags = ctx->gss_flags;
@@ -1227,26 +1197,12 @@ fail:
 
     *minor_status = code;
 
-    /*
-     * If decode_req_message is set, then we need to decode the ap_req
-     * message to determine whether or not to send a response token.
-     * We need to do this because for some errors we won't be able to
-     * decode the authenticator to read out the gss_flags field.
-     */
-    if (decode_req_message) {
-        krb5_ap_req      * request;
-
-        if (decode_krb5_ap_req(&ap_req, &request))
-            goto done;
-
-        if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
-            gss_flags |= GSS_C_MUTUAL_FLAG;
-        krb5_free_ap_req(context, request);
-    }
-
-    if (cred
-        && ((gss_flags & GSS_C_MUTUAL_FLAG)
-            || (major_status == GSS_S_CONTINUE_NEEDED))) {
+    /* We may have failed before being able to read the GSS flags from the
+     * authenticator, so also check the request AP options. */
+    if (cred != NULL && request != NULL &&
+        ((gss_flags & GSS_C_MUTUAL_FLAG) ||
+         (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) ||
+         major_status == GSS_S_CONTINUE_NEEDED)) {
         unsigned int tmsglen;
         int toktype;
 
@@ -1257,13 +1213,14 @@ fail:
         memset(&krb_error_data, 0, sizeof(krb_error_data));
 
         code -= ERROR_TABLE_BASE_krb5;
-        if (code < 0 || code > 128)
+        if (code < 0 || code > KRB_ERR_MAX)
             code = 60 /* KRB_ERR_GENERIC */;
 
         krb_error_data.error = code;
         (void) krb5_us_timeofday(context, &krb_error_data.stime,
                                  &krb_error_data.susec);
 
+        krb_error_data.server = ticket->server;
         code = krb5_mk_error(context, &krb_error_data, &scratch);
         if (code)
             goto done;
@@ -1272,7 +1229,7 @@ fail:
         toktype = KG_TOK_CTX_ERROR;
 
         token.length = g_token_size(mech_used, tmsglen);
-        token.value = (unsigned char *) xmalloc(token.length);
+        token.value = gssalloc_malloc(token.length);
         if (!token.value)
             goto done;
 
@@ -1286,9 +1243,11 @@ fail:
     }
 
 done:
-    if (!verifier_cred_handle && cred_handle) {
-        krb5_gss_release_cred(&tmp_minor_status, &cred_handle);
-    }
+    krb5_free_ap_req(context, request);
+    if (cred)
+        k5_mutex_unlock(&cred->lock);
+    if (defcred)
+        krb5_gss_release_cred(&tmp_minor_status, &defcred);
     if (context) {
         if (major_status && *minor_status)
             save_error_info(*minor_status, context);