Thread-safety for file-based credentials caches
authorKen Raeburn <raeburn@mit.edu>
Wed, 30 Jun 2004 23:24:42 +0000 (23:24 +0000)
committerKen Raeburn <raeburn@mit.edu>
Wed, 30 Jun 2004 23:24:42 +0000 (23:24 +0000)
* cc_file.c (krb5_fcc_data): Added a mutex.
(krb5_fcc_read*, krb5_fcc_write, krb5_fcc_store_*, krb5_fcc_open_file,
krb5_fcc_skip_header, krb5_fcc_skip_principal): Verify that the mutex is
locked.
(MAYBE_OPEN): Verify that the mutex is locked; unlock it if returning an error.
(krb5_fcc_initialize, krb5_fcc_start_seq_get, krb5_fcc_get_principal,
krb5_fcc_store, krb5_fcc_set_flags): Lock and unlock the mutex.
(krb5_fcc_close): Likewise.  Destroy the mutex when done.
(krb5_fcc_destroy): Merge stdio and non-stdio versions a little more.  Destroy
the mutex when done.
(krb5_fcc_resolve): Initialize and lock the mutex.
(krb5_fcc_next_cred): Lock and unlock the mutex.  Merge the stdio and non-stdio
branches a little more.

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

src/lib/krb5/ccache/ChangeLog
src/lib/krb5/ccache/cc_file.c

index ac653ace33f6ff47544b9cff038a549a32266167..19e9add2aa62a6a0072f6735a47f41b5a3578398 100644 (file)
@@ -1,3 +1,21 @@
+2004-06-30  Ken Raeburn  <raeburn@mit.edu>
+
+       * cc_file.c (krb5_fcc_data): Added a mutex.
+       (krb5_fcc_read*, krb5_fcc_write, krb5_fcc_store_*,
+       krb5_fcc_open_file, krb5_fcc_skip_header,
+       krb5_fcc_skip_principal): Verify that the mutex is locked.
+       (MAYBE_OPEN): Verify that the mutex is locked; unlock it if
+       returning an error.
+       (krb5_fcc_initialize, krb5_fcc_start_seq_get,
+       krb5_fcc_get_principal, krb5_fcc_store, krb5_fcc_set_flags): Lock
+       and unlock the mutex.
+       (krb5_fcc_close): Likewise.  Destroy the mutex when done.
+       (krb5_fcc_destroy): Merge stdio and non-stdio versions a little
+       more.  Destroy the mutex when done.
+       (krb5_fcc_resolve): Initialize and lock the mutex.
+       (krb5_fcc_next_cred): Lock and unlock the mutex.  Merge the stdio
+       and non-stdio branches a little more.
+
 2004-06-29  Jeffrey Altman <jaltman@mit.edu>
 
     * cc_mslsa.c: 
index 1b495edca1ebffc49441e4a364f24b5295673ef0..8ed18f673788f0465fd5fc2c5d26718402310b87 100644 (file)
@@ -82,22 +82,23 @@ format-vno ::= <int16>
 etc.
  */
 /* todo:
-Make sure that each time a function returns KRB5_NOMEM, everything
-allocated earlier in the function and stack tree is freed.
+   Make sure that each time a function returns KRB5_NOMEM, everything
+   allocated earlier in the function and stack tree is freed.
 
 #ifndef USE_STDIO
-File locking
+   File locking
 
-fcc_nseq.c and fcc_read don't check return values a lot.
+   Use pread/pwrite if available, so multiple threads can read
+   simultaneously.  (That may require reader/writer locks.)
+
+   fcc_nseq.c and fcc_read don't check return values a lot.
 #else
-Overwrite cache file with nulls before removing it.
-#endif
+   Overwrite cache file with nulls before removing it.
 
-#ifdef USE_STDIO
-Check return values and sanity-check parameters more thoroughly.  This
-code was derived from UNIX file I/O code, and the conversion of
-error-trapping may be incomplete.  Probably lots of bugs dealing with
-end-of-file versus other errors.
+   Check return values and sanity-check parameters more thoroughly.  This
+   code was derived from UNIX file I/O code, and the conversion of
+   error-trapping may be incomplete.  Probably lots of bugs dealing with
+   end-of-file versus other errors.
 #endif
  */
 #include "k5-int.h"
@@ -299,6 +300,7 @@ static krb5_error_code krb5_fcc_open_file
 
 typedef struct _krb5_fcc_data {
      char *filename;
+     k5_mutex_t lock;
 #ifndef USE_STDIO
      int file;
 #else
@@ -319,11 +321,18 @@ typedef struct _krb5_fcc_cursor {
 #endif
 } krb5_fcc_cursor;
 
-#define MAYBE_OPEN(CONTEXT, ID, MODE) \
+#define MAYBE_OPEN(CONTEXT, ID, MODE)                                  \
 {                                                                      \
+    k5_assert_locked(&((krb5_fcc_data *)(ID)->data)->lock);            \
     if (OPENCLOSE (ID)) {                                              \
-       krb5_error_code maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE);  \
-       if (maybe_open_ret) return maybe_open_ret; } }
+       krb5_error_code maybe_open_ret;                                 \
+       maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE);          \
+       if (maybe_open_ret) {                                           \
+           k5_mutex_unlock(&((krb5_fcc_data *)(ID)->data)->lock);      \
+           return maybe_open_ret;                                      \
+       }                                                               \
+    }                                                                  \
+}
 
 #define MAYBE_CLOSE(CONTEXT, ID, RET) \
 {                                                                      \
@@ -339,7 +348,7 @@ typedef struct _krb5_fcc_cursor {
 
 #endif
 #define CHECK(ret) if (ret != KRB5_OK) goto errout;
-     
+
 #ifndef USE_STDIO
 #define NO_FILE -1
 #else
@@ -350,6 +359,9 @@ typedef struct _krb5_fcc_cursor {
  * Effects:
  * Reads len bytes from the cache id, storing them in buf.
  *
+ * Requires:
+ * Must be called with mutex locked.
+ *
  * Errors:
  * KRB5_CC_END - there were not len bytes available
  * system errors (read)
@@ -359,6 +371,8 @@ krb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned i
 {
      int ret;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
 #ifndef USE_STDIO
      ret = read(((krb5_fcc_data *) id->data)->file, (char *) buf, len);
      if (ret == -1)
@@ -381,6 +395,8 @@ krb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned i
  * Requires:
  * id is open and set to read at the appropriate place in the file
  *
+ * mutex is locked
+ *
  * Effects:
  * Fills in the second argument with data of the appropriate type from
  * the file.  In some cases, the functions have to allocate space for
@@ -406,6 +422,8 @@ krb5_fcc_read_principal(krb5_context context, krb5_ccache id, krb5_principal *pr
     krb5_int32 length, type;
     int i;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if (data->version == KRB5_FCC_FVNO_1) {
        type = KRB5_NT_UNKNOWN;
     } else {
@@ -477,6 +495,8 @@ krb5_fcc_read_addrs(krb5_context context, krb5_ccache id, krb5_address ***addrs)
      size_t msize;
      int i;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      *addrs = 0;
 
      /* Read the number of components */
@@ -519,6 +539,8 @@ krb5_fcc_read_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyb
      krb5_ui_2 ui2;
      krb5_int32 int32;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      keyblock->magic = KV5M_KEYBLOCK;
      keyblock->contents = 0;
 
@@ -563,6 +585,8 @@ krb5_fcc_read_data(krb5_context context, krb5_ccache id, krb5_data *data)
      krb5_error_code kret;
      krb5_int32 len;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      data->magic = KV5M_DATA;
      data->data = 0;
 
@@ -601,6 +625,8 @@ krb5_fcc_read_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
      krb5_ui_2 ui2;
      krb5_int32 int32;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      addr->magic = KV5M_ADDRESS;
      addr->contents = 0;
 
@@ -643,6 +669,8 @@ krb5_fcc_read_int32(krb5_context context, krb5_ccache id, krb5_int32 *i)
     unsigned char buf[4];
     krb5_int32 val;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2)) 
        return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_int32));
@@ -666,6 +694,8 @@ krb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i)
     krb5_error_code retval;
     unsigned char buf[2];
     
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2))
        return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_ui_2));
@@ -681,6 +711,7 @@ krb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i)
 static krb5_error_code
 krb5_fcc_read_octet(krb5_context context, krb5_ccache id, krb5_octet *i)
 {
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
     return krb5_fcc_read(context, id, (krb5_pointer) i, 1);
 }    
 
@@ -692,6 +723,8 @@ krb5_fcc_read_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
     krb5_error_code retval;
     krb5_int32 i;
     
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2))
        return krb5_fcc_read(context, id, (krb5_pointer) t, sizeof(krb5_ticket_times));
@@ -725,6 +758,8 @@ krb5_fcc_read_authdata(krb5_context context, krb5_ccache id, krb5_authdata ***a)
      size_t msize;
      int i;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      *a = 0;
 
      /* Read the number of components */
@@ -769,6 +804,8 @@ krb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a)
     krb5_int32 int32;
     krb5_ui_2 ui2;
     
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     a->magic = KV5M_AUTHDATA;
     a->contents = NULL;
 
@@ -820,6 +857,9 @@ static krb5_error_code
 krb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
 {
      int ret;
+
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
 #ifndef USE_STDIO
      ret = write(((krb5_fcc_data *)id->data)->file, (char *) buf, len);
      if (ret < 0)
@@ -842,6 +882,8 @@ krb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned
  * 
  * Requires:
  * ((krb5_fcc_data *) id->data)->file is open and at the right position.
+ *
+ * mutex is locked
  * 
  * Effects:
  * Stores an encoded version of the second argument in the
@@ -858,6 +900,8 @@ krb5_fcc_store_principal(krb5_context context, krb5_ccache id, krb5_principal pr
     krb5_error_code ret;
     krb5_int32 i, length, tmp, type;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     type = krb5_princ_type(context, princ);
     tmp = length = krb5_princ_size(context, princ);
 
@@ -894,6 +938,8 @@ krb5_fcc_store_addrs(krb5_context context, krb5_ccache id, krb5_address **addrs)
      krb5_address **temp;
      krb5_int32 i, length = 0;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      /* Count the number of components */
      if (addrs) {
             temp = addrs;
@@ -917,6 +963,8 @@ krb5_fcc_store_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *key
      krb5_fcc_data *data = (krb5_fcc_data *)id->data;
      krb5_error_code ret;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
      CHECK(ret);
      if (data->version == KRB5_FCC_FVNO_3) {
@@ -933,6 +981,8 @@ krb5_fcc_store_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
 {
      krb5_error_code ret;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      ret = krb5_fcc_store_ui_2(context, id, addr->addrtype);
      CHECK(ret);
      ret = krb5_fcc_store_ui_4(context, id, addr->length);
@@ -946,6 +996,8 @@ krb5_fcc_store_data(krb5_context context, krb5_ccache id, krb5_data *data)
 {
      krb5_error_code ret;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      ret = krb5_fcc_store_ui_4(context, id, data->length);
      CHECK(ret);
      return krb5_fcc_write(context, id, data->data, data->length);
@@ -957,6 +1009,8 @@ krb5_fcc_store_int32(krb5_context context, krb5_ccache id, krb5_int32 i)
     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
     unsigned char buf[4];
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2)) 
        return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
@@ -978,6 +1032,8 @@ krb5_fcc_store_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i)
     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
     unsigned char buf[4];
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2)) 
        return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
@@ -1000,6 +1056,8 @@ krb5_fcc_store_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i)
     krb5_ui_2 ibuf;
     unsigned char buf[2];
     
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2)) {
         ibuf = (krb5_ui_2) i;
@@ -1017,6 +1075,8 @@ krb5_fcc_store_octet(krb5_context context, krb5_ccache id, krb5_int32 i)
 {
     krb5_octet ibuf;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     ibuf = (krb5_octet) i;
     return krb5_fcc_write(context, id, (char *) &ibuf, 1);
 }
@@ -1027,6 +1087,8 @@ krb5_fcc_store_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
     krb5_fcc_data *data = (krb5_fcc_data *)id->data;
     krb5_error_code retval;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if ((data->version == KRB5_FCC_FVNO_1) ||
        (data->version == KRB5_FCC_FVNO_2))
        return krb5_fcc_write(context, id, (char *) t, sizeof(krb5_ticket_times));
@@ -1050,6 +1112,8 @@ krb5_fcc_store_authdata(krb5_context context, krb5_ccache id, krb5_authdata **a)
     krb5_authdata **temp;
     krb5_int32 i, length=0;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if (a != NULL) {
        for (temp=a; *temp; temp++)
            length++;
@@ -1068,6 +1132,9 @@ static krb5_error_code
 krb5_fcc_store_authdatum (krb5_context context, krb5_ccache id, krb5_authdata *a)
 {
     krb5_error_code ret;
+
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     ret = krb5_fcc_store_ui_2(context, id, a->ad_type);
     CHECK(ret);
     ret = krb5_fcc_store_ui_4(context, id, a->length);
@@ -1083,6 +1150,8 @@ krb5_fcc_close_file (krb5_context context, krb5_ccache id)
      krb5_fcc_data *data = (krb5_fcc_data *)id->data;
      krb5_error_code retval;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      if (data->file == NO_FILE)
         return KRB5_FCC_INTERNAL;
 
@@ -1147,6 +1216,8 @@ krb5_fcc_open_file (krb5_context context, krb5_ccache id, int mode)
     int lock_flag;
     krb5_error_code retval = 0;
 
+    k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
     if (data->file != NO_FILE) {
        /* Don't know what state it's in; shut down and start anew.  */
 #ifndef USE_STDIO
@@ -1369,6 +1440,8 @@ krb5_fcc_skip_header(krb5_context context, krb5_ccache id)
      krb5_error_code kret;
      krb5_ui_2 fcc_flen;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
 #ifndef USE_STDIO
      lseek(data->file, (off_t) sizeof(krb5_ui_2), SEEK_SET);
 #else
@@ -1395,6 +1468,8 @@ krb5_fcc_skip_principal(krb5_context context, krb5_ccache id)
      krb5_error_code kret;
      krb5_principal princ;
 
+     k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
+
      kret = krb5_fcc_read_principal(context, id, &princ);
      if (kret != KRB5_OK)
          return kret;
@@ -1424,12 +1499,19 @@ krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
      int reti = 0;
 #endif
 
+     kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
+     if (kret)
+        return kret;
+
 #ifndef USE_STDIO
      MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE);
 #else
      kret = krb5_fcc_open_file (context, id, FCC_OPEN_AND_ERASE);
-     if (kret < 0)
-          return krb5_fcc_interpret(context, errno);
+     if (kret < 0) {
+        int e = errno;
+        k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
+        return krb5_fcc_interpret(context, e);
+     }
 #endif
 
 #ifndef USE_STDIO
@@ -1443,6 +1525,7 @@ krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
          if (reti == -1) {
              kret = krb5_fcc_interpret(context, errno);
              MAYBE_CLOSE(context, id, kret);
+            k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
              return kret;
          }
      }
@@ -1451,6 +1534,7 @@ krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
      kret = krb5_fcc_store_principal(context, id, princ);
 
      MAYBE_CLOSE(context, id, kret);
+     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
      krb5_change_cache ();
      return kret;
 }
@@ -1470,6 +1554,9 @@ krb5_fcc_close(krb5_context context, krb5_ccache id)
      register int closeval = KRB5_OK;
      register krb5_fcc_data *data = (krb5_fcc_data *) id->data;
 
+     /* If locking fails, ignore it, since we're destroying the thing
+        anyways.  */
+     k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
 #ifndef USE_STDIO
      if (data->file >= 0)
              krb5_fcc_close_file(context, id);
@@ -1485,6 +1572,8 @@ krb5_fcc_close(krb5_context context, krb5_ccache id)
      }
 #endif
      krb5_xfree(data->filename);
+     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
+     k5_mutex_destroy(&((krb5_fcc_data *) id->data)->lock);
      krb5_xfree(data);
      krb5_xfree(id);
 
@@ -1507,15 +1596,16 @@ krb5_fcc_close(krb5_context context, krb5_ccache id)
 static krb5_error_code KRB5_CALLCONV
 krb5_fcc_destroy(krb5_context context, krb5_ccache id)
 {
+     krb5_error_code kret = 0;
+     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
+     register int ret;
+
 #ifndef USE_STDIO
      struct stat buf;
      unsigned long i, size;
      unsigned int wlen;
      char zeros[BUFSIZ];
-     register int ret;
-     krb5_error_code kret = 0;
-      
-     
+
      if (OPENCLOSE(id)) {
          ret = THREEPARAMOPEN(((krb5_fcc_data *) id->data)->filename, O_RDWR | O_BINARY, 0);
          if (ret < 0) {
@@ -1618,16 +1708,7 @@ krb5_fcc_destroy(krb5_context context, krb5_ccache id)
 
 #endif /* MSDOS_FILESYSTEM */
 
-  cleanup:
-     krb5_xfree(((krb5_fcc_data *) id->data)->filename);
-     krb5_xfree(id->data);
-     krb5_xfree(id);
-
-     krb5_change_cache ();
-     return kret;
 #else
-     krb5_fcc_data *data = (krb5_fcc_data *) id->data;
-     register int ret;
      
      if (!OPENCLOSE(id)) {
         (void) fclose(data->file);
@@ -1636,7 +1717,7 @@ krb5_fcc_destroy(krb5_context context, krb5_ccache id)
 
      ret = remove (data->filename);
      if (ret < 0) {
-        ret = krb5_fcc_interpret(context, errno);
+        kret = krb5_fcc_interpret(context, errno);
         if (OPENCLOSE(id)) {
             (void) fclose(data->file);
             data->file = 0;
@@ -1651,16 +1732,17 @@ krb5_fcc_destroy(krb5_context context, krb5_ccache id)
       */
 
      if (ret)
-        ret = krb5_fcc_interpret(context, errno);
+        kret = krb5_fcc_interpret(context, errno);
+#endif
 
   cleanup:
      krb5_xfree(data->filename);
+     k5_mutex_destroy(&data->lock);
      krb5_xfree(data);
      krb5_xfree(id);
 
      krb5_change_cache ();
-     return ret;
-#endif
+     return kret;
 }
 
 extern const krb5_cc_ops krb5_fcc_ops;
@@ -1688,7 +1770,9 @@ static krb5_error_code KRB5_CALLCONV
 krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
 {
      krb5_ccache lid;
-     
+     krb5_error_code kret;
+     krb5_fcc_data *data;
+
      lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
      if (lid == NULL)
          return KRB5_CC_NOMEM;
@@ -1700,26 +1784,33 @@ krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
          krb5_xfree(lid);
          return KRB5_CC_NOMEM;
      }
+     data = (krb5_fcc_data *) lid->data;
+     data->filename = (char *) malloc(strlen(residual) + 1);
 
-     ((krb5_fcc_data *) lid->data)->filename = (char *)
-         malloc(strlen(residual) + 1);
-
-     if (((krb5_fcc_data *) lid->data)->filename == NULL) {
-         krb5_xfree(((krb5_fcc_data *) lid->data));
+     if (data->filename == NULL) {
+         krb5_xfree(data);
          krb5_xfree(lid);
          return KRB5_CC_NOMEM;
      }
 
+     kret = k5_mutex_init(&data->lock);
+     if (kret) {
+        krb5_xfree(data->filename);
+        krb5_xfree(data);
+        krb5_xfree(lid);
+        return kret;
+     }
+
      /* default to open/close on every trn */
-     ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE;
+     data->flags = KRB5_TC_OPENCLOSE;
 #ifndef USE_STDIO
-     ((krb5_fcc_data *) lid->data)->file = -1;
+     data->file = -1;
 #else
-     ((krb5_fcc_data *) lid->data)->file = 0;
+     data->file = 0;
 #endif
      
      /* Set up the filename */
-     strcpy(((krb5_fcc_data *) lid->data)->filename, residual);
+     strcpy(data->filename, residual);
 
      lid->magic = KV5M_CCACHE;
 
@@ -1749,14 +1840,21 @@ krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cur
      krb5_error_code kret = KRB5_OK;
      krb5_fcc_data *data = (krb5_fcc_data *)id->data;
 
+     kret = k5_mutex_lock(&data->lock);
+     if (kret)
+        return kret;
+
      fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor));
-     if (fcursor == NULL)
-         return KRB5_CC_NOMEM;
+     if (fcursor == NULL) {
+        k5_mutex_unlock(&data->lock);
+        return KRB5_CC_NOMEM;
+     }
 #ifndef USE_STDIO
      if (OPENCLOSE(id)) {
           kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY);
           if (kret) {
               krb5_xfree(fcursor);
+             k5_mutex_unlock(&data->lock);
               return kret;
           }
      }
@@ -1765,8 +1863,8 @@ krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cur
      /* Make sure we start reading right after the primary principal */
 #ifdef USE_STDIO
      MAYBE_OPEN (context, id, FCC_OPEN_RDONLY);
-
 #endif
+
      kret = krb5_fcc_skip_header(context, id);
      if (kret) goto done;
      kret = krb5_fcc_skip_principal(context, id);
@@ -1781,6 +1879,7 @@ krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cur
 
 done:
      MAYBE_CLOSE(context, id, kret);
+     k5_mutex_unlock(&data->lock);
      return kret;
 }
 
@@ -1813,33 +1912,25 @@ krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
      krb5_fcc_cursor *fcursor;
      krb5_int32 int32;
      krb5_octet octet;
+     krb5_fcc_data *d = (krb5_fcc_data *) id->data;
 
-#ifndef USE_STDIO
-     memset((char *)creds, 0, sizeof(*creds));
-#else
-#define Z(field)        creds->field = 0
-     Z (client);
-     Z (server);
-     Z (keyblock.contents);
-     Z (authdata);
-     Z (ticket.data);
-     Z (second_ticket.data);
-     Z (addresses);
-#undef Z
-#endif
+     kret = k5_mutex_lock(&d->lock);
+     if (kret)
+        return kret;
 
+     memset((char *)creds, 0, sizeof(*creds));
      MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
-
      fcursor = (krb5_fcc_cursor *) *cursor;
 
 #ifndef USE_STDIO
-     kret = (lseek(((krb5_fcc_data *) id->data)->file, fcursor->pos, SEEK_SET) == (off_t) -1);
+     kret = (lseek(d->file, fcursor->pos, SEEK_SET) == (off_t) -1);
 #else
-     kret = (fseek(((krb5_fcc_data *) id->data)->file, fcursor->pos, 0) < 0);
+     kret = (fseek(d->file, fcursor->pos, 0) < 0);
 #endif
      if (kret) {
         kret = krb5_fcc_interpret(context, errno);
         MAYBE_CLOSE(context, id, kret);
+        k5_mutex_unlock(&d->lock);
         return kret;
      }
 
@@ -1867,23 +1958,17 @@ krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
      TCHECK(kret);
      
 #ifndef USE_STDIO
-     fcursor->pos = lseek(((krb5_fcc_data *) id->data)->file, (off_t) 0, 
-                          SEEK_CUR);
+     fcursor->pos = lseek(d->file, (off_t) 0, SEEK_CUR);
 #else
-     fcursor->pos = ftell(((krb5_fcc_data *) id->data)->file);
+     fcursor->pos = ftell(d->file);
 #endif
      cursor = (krb5_cc_cursor *) fcursor;
 
 lose:
-#ifndef USE_STDIO
-     MAYBE_CLOSE(context, id, kret);            /* won't overwrite kret
-                                           if already set */
-#endif
+     MAYBE_CLOSE (context, id, kret);
+     k5_mutex_unlock(&d->lock);
      if (kret != KRB5_OK)
         krb5_free_cred_contents(context, creds);
-#ifdef USE_STDIO
-     MAYBE_CLOSE (context, id, kret);
-#endif
      return kret;
 }
 
@@ -1903,12 +1988,14 @@ lose:
 static krb5_error_code KRB5_CALLCONV
 krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
 {
+     /* We don't do anything with the file cache itself, so
+       no need to lock anything.  */
+
      /* don't close; it may be left open by the caller,
         and if not, fcc_start_seq_get and/or fcc_next_cred will do the
         MAYBE_CLOSE.
      MAYBE_CLOSE(context, id, kret); */
      krb5_xfree((krb5_fcc_cursor *) *cursor);
-
      return 0;
 }
 
@@ -2118,6 +2205,10 @@ krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *pri
 {
      krb5_error_code kret = KRB5_OK;
 
+     kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
+     if (kret)
+        return kret;
+
      MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
      
      /* make sure we're beyond the header */
@@ -2127,6 +2218,7 @@ krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *pri
 
 done:
      MAYBE_CLOSE(context, id, kret);
+     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
      return kret;
 }
 
@@ -2156,6 +2248,10 @@ krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
 #define TCHECK(ret) if (ret != KRB5_OK) goto lose;
      krb5_error_code ret;
 
+     ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
+     if (ret)
+        return ret;
+
      /* Make sure we are writing to the end of the file */
      MAYBE_OPEN(context, id, FCC_OPEN_RDWR);
 
@@ -2169,6 +2265,7 @@ krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
 #ifndef USE_STDIO
           MAYBE_CLOSE_IGNORE(context, id);
 #endif
+         k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
          return krb5_fcc_interpret(context, errno);
      }
 
@@ -2195,6 +2292,7 @@ krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
 
 lose:
      MAYBE_CLOSE(context, id, ret);
+     k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
      krb5_change_cache ();
      return ret;
 #undef TCHECK
@@ -2229,6 +2327,10 @@ krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
 {
     krb5_error_code ret = KRB5_OK;
 
+    ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
+    if (ret)
+       return ret;
+
     /* XXX This should check for illegal combinations, if any.. */
     if (flags & KRB5_TC_OPENCLOSE) {
        /* asking to turn on OPENCLOSE mode */
@@ -2251,6 +2353,7 @@ krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
     }
 
     ((krb5_fcc_data *) id->data)->flags = flags;
+    k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
     return ret;
 }