Add a mutex to the GSSAPI krb5 mechanism credential structure. Lock it while
authorKen Raeburn <raeburn@mit.edu>
Thu, 29 Jul 2004 02:26:43 +0000 (02:26 +0000)
committerKen Raeburn <raeburn@mit.edu>
Thu, 29 Jul 2004 02:26:43 +0000 (02:26 +0000)
frobbing the contents.

Also added krb5_gss_validate_cred_1, which is like krb5_gss_validate_cred but
for internal use.  It lets the caller supply the krb5_context instead of
creating yet another one locally, and leaves the new credential mutex locked on
a successful return so that the caller doesn't have to reacquire it.  More
functions should be changed to use this internally, but it's a performance
issue; I don't think it's a correctness or thread-safety issue.

* gssapiP_krb5.h (struct _krb5_gss_cred_id_rec): Add a mutex.
(krb5_gss_validate_cred_1): Declare.
* accept_sec_context.c (rd_and_store_for_creds): Initialize mutex.
* acquire_cred.c (krb5_gss_acquire_cred): Initialize mutex.
* add_cred.c (krb5_gss_add_cred): Create the krb5 context earlier.  Call
krb5_gss_validate_cred_1.  Make sure the mutex is locked.
* copy_ccache.c (gss_krb5_copy_ccache): Lock the mutex in the source
credential.
* init_sec_context.c (get_credentials, new_connection): Check that the mutex is
locked.
(mutual_auth): Delete unused credential argument.
(krb5_gss_init_sec_context): Lock the mutex.
* inq_cred.c (krb5_gss_inquire_cred): Lock the mutex.
* rel_cred.c (krb5_gss_release_cred): Destroy the mutex.
* set_allowable_enctypes.c (gss_krb5_set_allowable_enctypes): Lock the mutex.
* val_cred.c (krb5_gss_validate_cred_1): New function.
(krb5_gss_validate_cred): Use it.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16630 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/gssapi/krb5/ChangeLog
src/lib/gssapi/krb5/accept_sec_context.c
src/lib/gssapi/krb5/acquire_cred.c
src/lib/gssapi/krb5/add_cred.c
src/lib/gssapi/krb5/copy_ccache.c
src/lib/gssapi/krb5/gssapiP_krb5.h
src/lib/gssapi/krb5/init_sec_context.c
src/lib/gssapi/krb5/inq_cred.c
src/lib/gssapi/krb5/rel_cred.c
src/lib/gssapi/krb5/set_allowable_enctypes.c
src/lib/gssapi/krb5/val_cred.c

index 4bc52f823812c6a0f495ea701777e76a1759c943..a9fb83df40d11d27aac00e5b6e89fff1792ae790 100644 (file)
@@ -1,5 +1,28 @@
 2004-07-28  Ken Raeburn  <raeburn@mit.edu>
 
+       * gssapiP_krb5.h (struct _krb5_gss_cred_id_rec): Add a mutex.
+       (krb5_gss_validate_cred_1): Declare.
+       * accept_sec_context.c (rd_and_store_for_creds): Initialize mutex.
+       * acquire_cred.c (krb5_gss_acquire_cred): Initialize mutex.
+       * add_cred.c (krb5_gss_add_cred): Create the krb5 context
+       earlier.  Call krb5_gss_validate_cred_1.  Make sure the mutex is
+       locked.
+       * copy_ccache.c (gss_krb5_copy_ccache): Lock the mutex in the
+       source credential.
+       * init_sec_context.c (get_credentials, new_connection): Check that
+       the mutex is locked.
+       (mutual_auth): Delete unused credential argument.
+       (krb5_gss_init_sec_context): Lock the mutex.
+       * inq_cred.c (krb5_gss_inquire_cred): Lock the mutex.
+       * rel_cred.c (krb5_gss_release_cred): Destroy the mutex.
+       * set_allowable_enctypes.c (gss_krb5_set_allowable_enctypes): Lock
+       the mutex.
+       * val_cred.c (krb5_gss_validate_cred_1): New function, most of old
+       krb5_gss_validate_cred but requires that the krb5 context be
+       supplied, and returns with the credential mutex still locked if
+       successful, so the caller needn't re-lock it.
+       (krb5_gss_validate_cred): Use it.
+
        * set_ccache.c (gss_krb5_ccache_name): Don't make a copy of the
        string returned by kg_get_ccache_name.  Simplify some calls using
        a temporary error code variable.
index 2b7d8494c99fe46963097c2b72bffe641489bea6..219d9da060737867bf699717024a6890027c000b 100644 (file)
@@ -160,9 +160,17 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
        /* zero it out... */
        memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
 
+       retval = k5_mutex_init(&cred->lock);
+       if (retval) {
+           xfree(cred);
+           cred = NULL;
+           goto cleanup;
+       }
+
        /* copy the client principle into it... */
        if ((retval =
             krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) {
+           k5_mutex_destroy(&cred->lock);
            retval = ENOMEM; /* out of memory? */
            xfree(cred); /* clean up memory on failure */
            cred = NULL;
index 0b0b57a31ff238752e2356a2c8407b183a60c422..b65fc0962167a2d2f7769e377a9afc2fcccb6b9b 100644 (file)
@@ -425,6 +425,16 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
    cred->keytab = NULL;
    cred->ccache = NULL;
 
+   code = k5_mutex_init(&cred->lock);
+   if (code) {
+       *minor_status = ret;
+       krb5_free_context(context);
+       return GSS_S_FAILURE;
+   }
+   /* Note that we don't need to lock this GSSAPI credential record
+      here, because no other thread can gain access to it until we
+      return it.  */
+
    if ((cred_usage != GSS_C_INITIATE) &&
        (cred_usage != GSS_C_ACCEPT) &&
        (cred_usage != GSS_C_BOTH)) {
index 4ec230a1e2cf346757ca347bf4cc059484a3e087..6cb6605d9e56344561156e52cae2c719abc75723 100644 (file)
@@ -113,12 +113,21 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
        return(GSS_S_DUPLICATE_ELEMENT);
     }
 
-    /* verify the credential */
-    if (GSS_ERROR(major_status =
-                 krb5_gss_validate_cred(minor_status, input_cred_handle)))
-       return(major_status);
+    code = krb5_init_context(&context);
+    if (code) {
+       *minor_status = code;
+       return GSS_S_FAILURE;
+    }
+
+    major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle,
+                                           context);
+    if (GSS_ERROR(major_status)) {
+       krb5_free_context(context);
+       return major_status;
+    }
 
     cred = (krb5_gss_cred_id_t) input_cred_handle;
+    k5_mutex_assert_locked(&cred->lock);
 
     /* check if the cred_usage is equal or "less" than the passed-in cred
        if copying */
@@ -127,6 +136,7 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
          ((cred->usage == GSS_C_BOTH) &&
           (output_cred_handle != NULL)))) {
       *minor_status = (OM_uint32) G_BAD_USAGE;
+      krb5_free_context(context);
       return(GSS_S_FAILURE);
     }
 
@@ -135,16 +145,14 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
     if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) ||
        (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) {
        *minor_status = 0;
+       krb5_free_context(context);
        return(GSS_S_DUPLICATE_ELEMENT);
     }
 
-    code = krb5_init_context(&context);
-    if (code) {
-       *minor_status = code;
+    if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
+       krb5_free_context(context);
        return GSS_S_FAILURE;
     }
-    if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
-       return GSS_S_FAILURE;
 
     /* verify the desired_name */
 
index d20f72c77c1e1608f3d5061456efb75c73a56004..fd408d7fb60b0effc5e59ada76548b586f6fc321 100644 (file)
@@ -19,19 +19,27 @@ gss_krb5_copy_ccache(minor_status, cred_handle, out_ccache)
        return(stat);
    
    k5creds = (krb5_gss_cred_id_t) cred_handle;
+   code = k5_mutex_lock(&k5creds->lock);
+   if (code) {
+       *minor_status = code;
+       return GSS_S_FAILURE;
+   }
    if (k5creds->usage == GSS_C_ACCEPT) {
+       k5_mutex_unlock(&k5creds->lock);
        *minor_status = (OM_uint32) G_BAD_USAGE;
        return(GSS_S_FAILURE);
    }
 
    code = krb5_init_context(&context);
    if (code) {
+       k5_mutex_unlock(&k5creds->lock);
        *minor_status = code;
        return GSS_S_FAILURE;
    }
 
    code = krb5_cc_start_seq_get(context, k5creds->ccache, &cursor);
    if (code) {
+       k5_mutex_unlock(&k5creds->lock);
        *minor_status = code;
        krb5_free_context(context);
        return(GSS_S_FAILURE);
@@ -39,7 +47,7 @@ gss_krb5_copy_ccache(minor_status, cred_handle, out_ccache)
    while (!code && !krb5_cc_next_cred(context, k5creds->ccache, &cursor, &creds)) 
        code = krb5_cc_store_cred(context, out_ccache, &creds);
    krb5_cc_end_seq_get(context, k5creds->ccache, &cursor);
-
+   k5_mutex_unlock(&k5creds->lock);
    krb5_free_context(context);
    if (code) {
        *minor_status = code;
index 2e5e3292bacb0ea7e66f6cb36f8b5864e8f462ac..cf78b94b86691bd64670d3f38e1627f9d5b88bb8 100644 (file)
@@ -141,6 +141,9 @@ enum qop {
 typedef krb5_principal krb5_gss_name_t;
 
 typedef struct _krb5_gss_cred_id_rec {
+   /* protect against simultaneous accesses */
+   k5_mutex_t lock;
+
    /* name/type of credential */
    gss_cred_usage_t usage;
    krb5_principal princ;       /* this is not interned as a gss_name_t */
@@ -607,9 +610,12 @@ OM_uint32 krb5_gss_validate_cred
            gss_cred_id_t               /* cred */
          );
 
-gss_OID krb5_gss_convert_static_mech_oid
-(gss_OID oid
-        );
+OM_uint32
+krb5_gss_validate_cred_1(OM_uint32 * /* minor_status */,
+                        gss_cred_id_t /* cred_handle */,
+                        krb5_context /* context */);
+
+gss_OID krb5_gss_convert_static_mech_oid(gss_OID oid);
        
 krb5_error_code gss_krb5int_make_seal_token_v3(krb5_context,
                                               krb5_gss_ctx_id_rec *,
index 90c3e7d721a62c0978195d9c02e7bdf80f25fcca..adc5ad918127b824192da8e9604c9526a90dcea0 100644 (file)
@@ -102,6 +102,7 @@ static krb5_error_code get_credentials(context, cred, server, now,
     krb5_error_code    code;
     krb5_creds                 in_creds;
 
+    k5_mutex_assert_locked(&cred->lock);
     memset((char *) &in_creds, 0, sizeof(krb5_creds));
 
     if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
@@ -262,7 +263,7 @@ make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token)
     unsigned char *t;
     unsigned int tlen;
 
-
+    k5_mutex_assert_locked(&cred->lock);
     ap_req.data = 0;
 
     /* compute the hash of the channel bindings */
@@ -463,6 +464,7 @@ new_connection(
    krb5_timestamp now;
    gss_buffer_desc token;
 
+   k5_mutex_assert_locked(&cred->lock);
    major_status = GSS_S_FAILURE;
    token.length = 0;
    token.value = NULL;
@@ -647,7 +649,6 @@ fail:
 static OM_uint32
 mutual_auth(
    OM_uint32 *minor_status,
-   krb5_gss_cred_id_t cred,
    gss_ctx_id_t *context_handle,
    gss_name_t target_name,
    gss_OID mech_type,
@@ -890,6 +891,12 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
       }
       cred = (krb5_gss_cred_id_t) claimant_cred_handle;
    }
+   kerr = k5_mutex_lock(&cred->lock);
+   if (kerr) {
+       krb5_free_context(context);
+       *minor_status = kerr;
+       return GSS_S_FAILURE;
+   }
 
    /* verify the mech_type */
 
@@ -914,6 +921,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
    }
    
    if (err) {
+      k5_mutex_unlock(&cred->lock);
       if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
         krb5_gss_release_cred(minor_status, (gss_cred_id_t)cred);
       *minor_status = 0;
@@ -932,12 +940,15 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
                                    input_token, actual_mech_type,
                                    output_token, ret_flags, time_rec,
                                    context, default_mech);
+      k5_mutex_unlock(&cred->lock);
       if (*context_handle == GSS_C_NO_CONTEXT)
          krb5_free_context(context);
       else
          ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
    } else {
-      major_status = mutual_auth(minor_status, cred, context_handle,
+      /* mutual_auth doesn't care about the credentials */
+      k5_mutex_unlock(&cred->lock);
+      major_status = mutual_auth(minor_status, context_handle,
                                 target_name, mech_type, req_flags,
                                 time_req, input_chan_bindings,
                                 input_token, actual_mech_type,
index b0a426aa54076979bb38630fddfc14f5a2d26135..4125dd5e48b8f5bb7b2d16841dc8bd7c1435c14a 100644 (file)
@@ -129,6 +129,12 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
       goto fail;
    }
 
+   code = k5_mutex_lock(&cred->lock);
+   if (code != 0) {
+       *minor_status = code;
+       ret = GSS_S_FAILURE;
+       goto fail;
+   }
    if (cred->tgt_expire > 0) {
        if ((lifetime = cred->tgt_expire - now) < 0)
           lifetime = 0;
@@ -139,6 +145,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
    if (name) {
       if (cred->princ &&
          (code = krb5_copy_principal(context, cred->princ, &ret_name))) {
+        k5_mutex_unlock(&cred->lock);
         *minor_status = code;
         ret = GSS_S_FAILURE;
         goto fail;
@@ -156,6 +163,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
            GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
                                                           (gss_OID) gss_mech_krb5,
                                                           &mechs)))) {
+          k5_mutex_unlock(&cred->lock);
           krb5_free_principal(context, ret_name);
           /* *minor_status set above */
           goto fail;
@@ -164,6 +172,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
 
    if (name) {
       if (! kg_save_name((gss_name_t) ret_name)) {
+        k5_mutex_unlock(&cred->lock);
         (void) gss_release_oid_set(minor_status, &mechs);
         krb5_free_principal(context, ret_name);
         *minor_status = (OM_uint32) G_VALIDATE_FAILED;
@@ -178,6 +187,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
 
    if (cred_usage)
       *cred_usage = cred->usage;
+   k5_mutex_unlock(&cred->lock);
 
    if (mechanisms)
       *mechanisms = mechs;
index fd8bb89456406a2827ed2163688c8ce30de6998b..ed88abb433481473d061b5f6c418ed1162552370 100644 (file)
@@ -51,6 +51,9 @@ krb5_gss_release_cred(minor_status, cred_handle)
 
    cred = *cred_handle;
 
+   k5_mutex_destroy(&cred->lock);
+   /* ignore error destroying mutex */
+
    if (cred->ccache)
       code1 = krb5_cc_close(context, cred->ccache);
    else
index aa3636e13b7fc4364a6386c4d2d1e5fb08786e32..88cae714a3a883a918c353945b170cbd62b29559 100644 (file)
@@ -95,9 +95,13 @@ gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
            }
        }
     } else {
+       kerr = k5_mutex_lock(&cred->lock);
+       if (kerr)
+           goto error_out;
        if (cred->req_enctypes)
            free(cred->req_enctypes);
        cred->req_enctypes = NULL;
+       k5_mutex_unlock(&cred->lock);
        return GSS_S_COMPLETE;
     }
 
@@ -110,9 +114,13 @@ gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
        kerr = ENOMEM;
        goto error_out;
     }
+    kerr = k5_mutex_lock(&cred->lock);
+    if (kerr)
+       goto error_out;
     if (cred->req_enctypes)
        free(cred->req_enctypes);
     cred->req_enctypes = new_ktypes;
+    k5_mutex_unlock(&cred->lock);
 
     /* Success! */
     return GSS_S_COMPLETE;
index fef848044011b6da4cc5ef747ace7e12916692df..1fdb3c29861ba436551a1ae051422947b8199ea0 100644 (file)
  */
 
 OM_uint32
-krb5_gss_validate_cred(minor_status, cred_handle)
-     OM_uint32 *minor_status;
-     gss_cred_id_t cred_handle;
+krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
+                        krb5_context context)
 {
-    krb5_context context;
     krb5_gss_cred_id_t cred;
     krb5_error_code code;
     krb5_principal princ;
-       
-    code = krb5_init_context(&context);
-    if (code) {
-       *minor_status = code;
-       return GSS_S_FAILURE;
-    }
 
     if (!kg_validate_cred_id(cred_handle)) {
        *minor_status = (OM_uint32) G_VALIDATE_FAILED;
-       krb5_free_context(context);
        return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_DEFECTIVE_CREDENTIAL);
     }
 
     cred = (krb5_gss_cred_id_t) cred_handle;
 
+    code = k5_mutex_lock(&cred->lock);
+    if (code) {
+       *minor_status = code;
+       return GSS_S_FAILURE;
+    }
+
     if (cred->ccache) {
        if ((code = krb5_cc_get_principal(context, cred->ccache, &princ))) {
+           k5_mutex_unlock(&cred->lock);
            *minor_status = code;
-           krb5_free_context(context);
            return(GSS_S_DEFECTIVE_CREDENTIAL);
        }
        if (!krb5_principal_compare(context, princ, cred->princ)) {
+           k5_mutex_unlock(&cred->lock);
            *minor_status = KG_CCACHE_NOMATCH;
-           krb5_free_context(context);
            return(GSS_S_DEFECTIVE_CREDENTIAL);
        }
        (void)krb5_free_principal(context, princ);
     }
-    krb5_free_context(context);
     *minor_status = 0;
     return GSS_S_COMPLETE;
 }
 
+OM_uint32
+krb5_gss_validate_cred(minor_status, cred_handle)
+     OM_uint32 *minor_status;
+     gss_cred_id_t cred_handle;
+{
+    krb5_context context;
+    krb5_error_code code;
+    OM_uint32 maj;
+
+    code = krb5_init_context(&context);
+    if (code) {
+       *minor_status = code;
+       return GSS_S_FAILURE;
+    }
+
+    maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
+    if (maj == 0) {
+       krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t) cred_handle;
+       k5_mutex_assert_locked(&cred->lock);
+       k5_mutex_unlock(&cred->lock);
+    }
+    krb5_free_context(context);
+    return maj;
+}
+