Added silc_skr_del_public_key and silc_skr_[ref|unref]_public_key.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 28 Apr 2007 10:50:49 +0000 (10:50 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 28 Apr 2007 10:50:49 +0000 (10:50 +0000)
lib/silcskr/silcskr.c
lib/silcskr/silcskr.h

index 2a495f4362945475dda58cff5fa4d0ff32c5c83d..56d0024b4dc780777d8e5e6b8f0d2757b5d6600e 100644 (file)
@@ -20,8 +20,6 @@
 #include "silc.h"
 #include "silcskr.h"
 
-/* XXX Locking, when removing keys */
-
 /************************** Types and definitions ***************************/
 
 /* Search constraints */
@@ -84,6 +82,7 @@ static void silc_skr_type_string(SilcSKRFindType type, void *data,
     break;
 
   case SILC_SKR_FIND_PUBLIC_KEY:
+  case SILC_SKR_FIND_CONTEXT:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
     break;
 
@@ -92,7 +91,6 @@ static void silc_skr_type_string(SilcSKRFindType type, void *data,
             (char *)data);
   }
 }
-
 #endif /* SILC_DEBUG */
 
 /* Hash table destructor for search constraints */
@@ -101,6 +99,7 @@ static void silc_skr_find_destructor(void *key, void *context,
                                     void *user_context)
 {
   SilcSKRFindType type = SILC_PTR_TO_32(key);
+  SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
 
   switch (type) {
   case SILC_SKR_FIND_PKCS_TYPE:
@@ -113,6 +112,12 @@ static void silc_skr_find_destructor(void *key, void *context,
     break;
 
   default:
+    /* In SILC Public key all entries are referenced from the public key
+       so don't free them.  This test is valid only when removing key
+       from the repository. */
+    if (pkcs_type == SILC_PKCS_SILC)
+      break;
+
     silc_free(context);
   }
 }
@@ -123,11 +128,13 @@ static void silc_skr_destructor(void *key, void *context, void *user_context)
 {
   SilcSKREntry type = key;
   SilcSKRKeyInternal entry = context;
+  SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
 
   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
      shares same context with the key entry. */
   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
-    silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
+    silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
+                            SILC_32_TO_PTR(pkcs_type));
   silc_free(type);
 
   /* Destroy key */
@@ -135,6 +142,8 @@ static void silc_skr_destructor(void *key, void *context, void *user_context)
   if (entry->refcnt > 0)
     return;
 
+  SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
+
   silc_pkcs_public_key_free(entry->key.key);
   silc_free(entry);
 }
@@ -272,12 +281,83 @@ static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
   return silc_hash_table_add(skr->keys, entry, key);
 }
 
+/* Delete a key by search constraint type from repository */
+
+static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
+                                  void *type_data, SilcSKRKeyInternal key)
+{
+  SilcSKREntryStruct entry;
+
+  if (!type_data)
+    return FALSE;
+
+  entry.type = type;
+  entry.data = type_data;
+
+  return silc_hash_table_del_by_context(skr->keys, &entry, key);
+}
+
+/* This performs AND operation.  Any entry already in `results' that is not
+   in `list' will be removed from `results'. */
+
+static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
+                                    SilcDList *results)
+{
+  SilcSKRKeyInternal entry, r;
+
+  if (*results == NULL) {
+    *results = silc_dlist_init();
+    if (*results == NULL) {
+      *status |= SILC_SKR_NO_MEMORY;
+      return FALSE;
+    }
+  }
+
+  /* If results is empty, just add all entries from list to results */
+  if (!silc_dlist_count(*results)) {
+    silc_dlist_start(list);
+    while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
+      silc_dlist_add(*results, entry);
+
+    return TRUE;
+  }
+
+  silc_dlist_start(*results);
+  while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
+
+    /* Check if this entry is in list  */
+    silc_dlist_start(list);
+    while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
+      if (r == entry)
+       break;
+    }
+    if (r != SILC_LIST_END)
+      continue;
+
+    /* Remove from results */
+    silc_dlist_del(*results, entry);
+  }
+
+  /* If results became empty, we did not find any key */
+  if (!silc_dlist_count(*results)) {
+    SILC_LOG_DEBUG(("Not all search constraints found"));
+    *status |= SILC_SKR_NOT_FOUND;
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/***************************** SILC Public Key ******************************/
+
 /* Add SILC style public key to repository */
 
 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
                                       SilcPublicKey public_key,
                                       SilcSKRKeyUsage usage,
-                                      void *key_context)
+                                      void *key_context,
+                                      SilcSKRKey *return_key)
 {
   SilcSKRKeyInternal key;
   SilcSKRStatus status = SILC_SKR_ERROR;
@@ -374,6 +454,9 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
 
   silc_mutex_unlock(skr->lock);
 
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
   return SILC_SKR_OK;
 
  err:
@@ -387,7 +470,8 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
                                              SilcPublicKey public_key,
                                              SilcSKRKeyUsage usage,
-                                             void *key_context)
+                                             void *key_context,
+                                             SilcSKRKey *return_key)
 {
   SilcSKRKeyInternal key;
   SilcSKRStatus status = SILC_SKR_ERROR;
@@ -431,6 +515,9 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
 
   silc_mutex_unlock(skr->lock);
 
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
   return SILC_SKR_OK;
 
  err:
@@ -438,55 +525,52 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
   return status;
 }
 
-/* This performs AND operation.  Any entry already in `results' that is not
-   in `list' will be removed from `results'. */
+/* Deletes SILC public key from repository */
 
-static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
-                                    SilcDList *results)
+static SilcSKRStatus silc_skr_del_silc_public_key(SilcSKR skr,
+                                                 SilcPublicKey public_key,
+                                                 void *key_context)
 {
-  SilcSKRKeyInternal entry, r;
-
-  if (*results == NULL) {
-    *results = silc_dlist_init();
-    if (*results == NULL) {
-      *status |= SILC_SKR_NO_MEMORY;
-      return FALSE;
-    }
-  }
-
-  /* If results is empty, just add all entries from list to results */
-  if (!silc_dlist_count(*results)) {
-    silc_dlist_start(list);
-    while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
-      silc_dlist_add(*results, entry);
+  SilcSKRStatus status = SILC_SKR_ERROR;
+  SilcPublicKeyIdentifier ident;
+  SilcSILCPublicKey silc_pubkey;
+  SilcSKRKeyInternal key;
+  SilcDList entry;
 
-    return TRUE;
-  }
+  /* Get the SILC public key */
+  silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+  ident = &silc_pubkey->identifier;
 
-  silc_dlist_start(*results);
-  while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
+  SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
 
-    /* Check if this entry is in list  */
-    silc_dlist_start(list);
-    while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
-      if (r == entry)
-       break;
-    }
-    if (r != SILC_LIST_END)
-      continue;
+  silc_mutex_lock(skr->lock);
 
-    /* Remove from results */
-    silc_dlist_del(*results, entry);
+  /* Check that this key exists */
+  if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
+                          public_key, &entry, key_context, 0)) {
+    silc_mutex_unlock(skr->lock);
+    SILC_LOG_DEBUG(("Key does not exist"));
+    return status | SILC_SKR_NOT_FOUND;
   }
 
-  /* If results became empty, we did not find any key */
-  if (!silc_dlist_count(*results)) {
-    SILC_LOG_DEBUG(("Not all search constraints found"));
-    *status |= SILC_SKR_NOT_FOUND;
-    return FALSE;
-  }
+  silc_dlist_start(entry);
+  key = silc_dlist_get(entry);
+  silc_dlist_uninit(entry);
+
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
+                    SILC_32_TO_PTR(SILC_PKCS_SILC), key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
 
-  return TRUE;
+  silc_mutex_unlock(skr->lock);
+
+  return SILC_SKR_OK;
 }
 
 
@@ -548,7 +632,8 @@ void silc_skr_uninit(SilcSKR skr)
 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
                                      SilcPublicKey public_key,
                                      SilcSKRKeyUsage usage,
-                                     void *key_context)
+                                     void *key_context,
+                                     SilcSKRKey *return_key)
 {
   SilcPKCSType type;
 
@@ -557,12 +642,12 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
 
   type = silc_pkcs_get_type(public_key);
 
-  SILC_LOG_DEBUG(("Adding public key to repository"));
+  SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
 
   switch (type) {
 
   case SILC_PKCS_SILC:
-    return silc_skr_add_silc(skr, public_key, usage, key_context);
+    return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
     break;
 
   default:
@@ -577,7 +662,37 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
                                             SilcPublicKey public_key,
                                             SilcSKRKeyUsage usage,
-                                            void *key_context)
+                                            void *key_context,
+                                            SilcSKRKey *return_key)
+{
+  SilcPKCSType type;
+
+  if (!public_key)
+    return SILC_SKR_ERROR;
+
+  type = silc_pkcs_get_type(public_key);
+
+  SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
+
+  switch (type) {
+
+  case SILC_PKCS_SILC:
+    return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
+                                   return_key);
+    break;
+
+  default:
+    break;
+  }
+
+  return SILC_SKR_ERROR;
+}
+
+/* Remove key from repository */
+
+SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
+                                     SilcPublicKey public_key,
+                                     void *key_context)
 {
   SilcPKCSType type;
 
@@ -586,12 +701,12 @@ SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
 
   type = silc_pkcs_get_type(public_key);
 
-  SILC_LOG_DEBUG(("Adding public key to repository"));
+  SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
 
   switch (type) {
 
   case SILC_PKCS_SILC:
-    return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
+    return silc_skr_del_silc_public_key(skr, public_key, key_context);
     break;
 
   default:
@@ -601,6 +716,39 @@ SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
   return SILC_SKR_ERROR;
 }
 
+/* Reference key */
+
+void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
+{
+  SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
+
+  silc_mutex_lock(skr->lock);
+  SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
+  k->refcnt++;
+  silc_mutex_unlock(skr->lock);
+}
+
+/* Release key reference. */
+
+void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
+{
+  SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
+
+  silc_mutex_lock(skr->lock);
+
+  SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
+  k->refcnt--;
+
+  if (k->refcnt == 0) {
+    /* If reference is zero, the key has been removed from the repository
+       already.  Just destroy the public key. */
+    silc_pkcs_public_key_free(key->key);
+    silc_free(key);
+  }
+
+  silc_mutex_unlock(skr->lock);
+}
+
 
 /************************** Search Constraints API **************************/
 
index eb9a5d98caf861eafe56b95a2490aa923bfdea24..be1521bbc7529411523a81c354f0b13125205bd1 100644 (file)
@@ -21,7 +21,9 @@
  *
  * DESCRIPTION
  *
- * SILC Key Repository
+ * SILC Key repository is a generic public key and certificate repository
+ * which allows fast and versatile ways to retrieve public keys from the
+ * the repository.
  *
  * SILC Key Repository is thread safe.  Same key repository context can be
  * safely used in multi threaded environment.
@@ -133,6 +135,9 @@ typedef struct SilcSKRKeyStruct {
  *    then at least SILC_SKR_ERROR is set, and possibly other error
  *    status also.
  *
+ *    If the SILC_SKR_UNSUPPORTED_TYPE is returned the repository does not
+ *    support the public key type and it cannot be added to the repository.
+ *
  * SOURCE
  */
 typedef enum {
@@ -230,7 +235,8 @@ void silc_skr_uninit(SilcSKR skr);
  *    SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
  *                                          SilcPublicKey public_key,
  *                                          SilcSKRKeyUsage usage,
- *                                          void *key_context);
+ *                                          void *key_context,
+ *                                          SilcSKRKey *return_key);
  *
  * DESCRIPTION
  *
@@ -241,21 +247,28 @@ void silc_skr_uninit(SilcSKR skr);
  *    repository.  To add same key more than once to repository different
  *    `key_context' must be used each time.
  *
+ *    Returns an entry of the added public key in the repository to the
+ *    `return_key' pointer, if it is non-NULL.  The returned entry remains
+ *    valid as long as the public key is in the repository, however a
+ *    reference may be taken with silc_skr_ref_public_key to assure the
+ *    entry remains valid.
+ *
  *    Returns SILC_SKR_OK if the key was added successfully, and error
  *    status if key could not be added, or has been added already.
  *
  * EXAMPLE
  *
  *    // Add a key to repository
- *    if (silc_skr_add_public_key(repository, public_key,
- *                                SILC_SKR_USAGE_ANY, NULL) != SILC_SKR_OK)
+ *    if (silc_skr_add_public_key(repository, pubkey, SILC_SKR_USAGE_ANY,
+ *                                NULL, NULL) != SILC_SKR_OK)
  *      goto error;
  *
  ***/
 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
                                      SilcPublicKey public_key,
                                      SilcSKRKeyUsage usage,
-                                     void *key_context);
+                                     void *key_context,
+                                     SilcSKRKey *return_key);
 
 /****f* silcskr/SilcSKRAPI/silc_skr_add_public_key_simple
  *
@@ -264,7 +277,8 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
  *    SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
  *                                                 SilcPublicKey public_key,
  *                                                 SilcSKRKeyUsage usage,
- *                                                 void *key_context);
+ *                                                 void *key_context,
+ *                                                 SilcSKRKey *return_key);
  *
  * DESCRIPTION
  *
@@ -275,6 +289,12 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
  *    key with as little memory as possible to the repository, and makes
  *    it a good way to cheaply store large amounts of public keys.
  *
+ *    Returns an entry of the added public key in the repository to the
+ *    `return_key' pointer, if it is non-NULL.  The returned entry remains
+ *    valid as long as the public key is in the repository, however a
+ *    reference may be taken with silc_skr_ref_public_key to assure the
+ *    entry remains valid.
+ *
  *    Returns SILC_SKR_OK if the key was added successfully, and error
  *    status if key could not be added, or has been added already.
  *
@@ -282,7 +302,59 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
                                             SilcPublicKey public_key,
                                             SilcSKRKeyUsage usage,
-                                            void *key_context);
+                                            void *key_context,
+                                            SilcSKRKey *return_key);
+
+/****f* silcskr/SilcSKRAPI/silc_skr_del_public_key
+ *
+ * SYNOPSIS
+ *
+ *    SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
+ *                                          SilcPublicKey public_key,
+ *                                          void *key_context);
+ *
+ * DESCRIPTION
+ *
+ *    Removes and destroyes the public key from the repository.  The
+ *    public_key will become invalid after this call returns.
+ *
+ *    Returns SILC_SKR_OK if the key was added successfully, and error
+ *    status if key could not be added, or has been added already.
+ *
+ ***/
+SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
+                                     SilcPublicKey public_key,
+                                     void *key_context);
+
+/****f* silcskr/SilcSKRAPI/silc_skr_ref_public_key
+ *
+ * SYNOPSIS
+ *
+ *    void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key);
+ *
+ * DESCRIPTION
+ *
+ *    Takes a reference of the public key added to repository indicated
+ *    by `key'.  The reference must be released by calling the function
+ *    silc_skr_unref_public_key when it is not needed anymore.
+ *
+ ***/
+void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key);
+
+/****f* silcskr/SilcSKRAPI/silc_skr_unref_public_key
+ *
+ * SYNOPSIS
+ *
+ *    void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the reference of the public key added to the repository
+ *    indicated by `key'.  If the released reference is the last reference
+ *    to the key it will become invalid after this function returns.
+ *
+ ***/
+void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key);
 
 /****f* silcskr/SilcSKRAPI/silc_skr_find_alloc
  *