5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
23 /************************** Types and definitions ***************************/
25 /* Search constraints */
27 SILC_SKR_FIND_PKCS_TYPE,
28 SILC_SKR_FIND_USERNAME,
30 SILC_SKR_FIND_REALNAME,
33 SILC_SKR_FIND_COUNTRY,
34 SILC_SKR_FIND_PUBLIC_KEY,
35 SILC_SKR_FIND_CONTEXT,
36 SILC_SKR_FIND_USAGE, /* Never added as key specific */
39 /* Hash table key context */
41 SilcSKRFindType type; /* Type of key */
42 void *data; /* Hash table key */
43 } *SilcSKREntry, SilcSKREntryStruct;
45 /* Foreach user context when finding entries from hash table */
49 SilcSKRKeyUsage usage;
52 #if defined(SILC_DEBUG)
53 static const char *find_name[] = {
66 #endif /* SILC_DEBUG */
68 /************************ Static utility functions **************************/
70 #if defined(SILC_DEBUG)
72 /* Returns search constraint string */
74 static void silc_skr_type_string(SilcSKRFindType type, void *data,
75 char *retbuf, SilcUInt32 retbuf_size)
78 case SILC_SKR_FIND_PKCS_TYPE:
79 case SILC_SKR_FIND_USAGE:
80 silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
81 (int)SILC_PTR_TO_32(data));
84 case SILC_SKR_FIND_PUBLIC_KEY:
85 case SILC_SKR_FIND_CONTEXT:
86 silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
90 silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
94 #endif /* SILC_DEBUG */
96 /* Hash table destructor for search constraints */
98 static void silc_skr_find_destructor(void *key, void *context,
101 SilcSKRFindType type = SILC_PTR_TO_32(key);
102 SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
105 case SILC_SKR_FIND_PKCS_TYPE:
106 case SILC_SKR_FIND_USAGE:
107 case SILC_SKR_FIND_CONTEXT:
110 case SILC_SKR_FIND_PUBLIC_KEY:
111 silc_pkcs_public_key_free(context);
115 /* In SILC Public key all entries are referenced from the public key
116 so don't free them. This test is valid only when removing key
117 from the repository. */
118 if (pkcs_type == SILC_PKCS_SILC)
125 /* Hash table destructor for key entries */
127 static void silc_skr_destructor(void *key, void *context, void *user_context)
129 SilcSKREntry type = key;
130 SilcSKRKeyInternal entry = context;
131 SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
133 /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
134 shares same context with the key entry. */
135 if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
136 silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
137 SILC_32_TO_PTR(pkcs_type));
142 if (entry->refcnt > 0)
145 SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
147 silc_pkcs_public_key_free(entry->key.key);
151 /* Hash table hash function for key entries */
153 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
155 SilcSKREntry type = key;
157 switch (type->type) {
158 case SILC_SKR_FIND_PKCS_TYPE:
159 case SILC_SKR_FIND_CONTEXT:
160 return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
163 case SILC_SKR_FIND_PUBLIC_KEY:
164 return type->type + silc_hash_public_key(type->data, user_context);
171 return type->type + silc_hash_string(type->data, user_context);
174 /* Hash table comparison function for key entries */
176 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
178 SilcSKREntry type1 = key1;
179 SilcSKREntry type2 = key2;
181 if (type1->type != type2->type)
184 switch (type1->type) {
185 case SILC_SKR_FIND_PKCS_TYPE:
186 case SILC_SKR_FIND_CONTEXT:
187 return type1->data == type2->data;
190 case SILC_SKR_FIND_PUBLIC_KEY:
191 return silc_hash_public_key_compare(type1->data, type2->data,
199 return silc_utf8_strcasecmp((const char *)type1->data,
200 (const char *)type2->data);
203 /* Foreach function for finding entries in the repository */
205 static void silc_skr_find_foreach(void *key, void *context,
208 SilcSKRFindForeach *f = user_context;
209 SilcSKRKeyInternal k = context;
212 /* If key context is present, it must match the context in the key.
213 This is used only internally when adding keys, to check if the key
214 is added with same context. */
215 if (f->key_context && f->key_context != k->key.key_context)
218 /* Check for usage bits. At least one usage bit must be set. */
219 if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
222 silc_dlist_add(f->list, k);
226 /* Finds entry from repository by search constraint type and data */
228 static SilcBool silc_skr_find_entry(SilcSKR skr,
229 SilcSKRStatus *status,
230 SilcSKRFindType type,
234 SilcSKRKeyUsage usage)
236 SilcSKREntryStruct find;
237 SilcSKRFindForeach f;
239 f.list = silc_dlist_init();
241 *status |= SILC_SKR_NO_MEMORY;
244 f.key_context = key_context;
248 find.data = type_data;
250 silc_hash_table_find_foreach(skr->keys, (void *)&find,
251 silc_skr_find_foreach, &f);
253 if (!silc_dlist_count(f.list)) {
254 *status |= SILC_SKR_NOT_FOUND;
255 silc_dlist_uninit(f.list);
262 silc_dlist_uninit(f.list);
267 /* Add a key by search constraint type to repository */
269 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
270 void *type_data, SilcSKRKeyInternal key)
274 entry = silc_calloc(1, sizeof(*entry));
279 entry->data = type_data;
281 return silc_hash_table_add(skr->keys, entry, key);
284 /* Delete a key by search constraint type from repository */
286 static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
287 void *type_data, SilcSKRKeyInternal key)
289 SilcSKREntryStruct entry;
295 entry.data = type_data;
297 return silc_hash_table_del_by_context(skr->keys, &entry, key);
300 /* This performs AND operation. Any entry already in `results' that is not
301 in `list' will be removed from `results'. */
303 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
306 SilcSKRKeyInternal entry, r;
308 if (*results == NULL) {
309 *results = silc_dlist_init();
310 if (*results == NULL) {
311 *status |= SILC_SKR_NO_MEMORY;
316 /* If results is empty, just add all entries from list to results */
317 if (!silc_dlist_count(*results)) {
318 silc_dlist_start(list);
319 while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
320 silc_dlist_add(*results, entry);
325 silc_dlist_start(*results);
326 while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
328 /* Check if this entry is in list */
329 silc_dlist_start(list);
330 while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
334 if (r != SILC_LIST_END)
337 /* Remove from results */
338 silc_dlist_del(*results, entry);
341 /* If results became empty, we did not find any key */
342 if (!silc_dlist_count(*results)) {
343 SILC_LOG_DEBUG(("Not all search constraints found"));
344 *status |= SILC_SKR_NOT_FOUND;
352 /***************************** SILC Public Key ******************************/
354 /* Add SILC style public key to repository */
356 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
357 SilcPublicKey public_key,
358 SilcSKRKeyUsage usage,
360 SilcSKRKey *return_key)
362 SilcSKRKeyInternal key;
363 SilcSKRStatus status = SILC_SKR_ERROR;
364 SilcPublicKeyIdentifier ident;
365 SilcSILCPublicKey silc_pubkey;
367 /* Get the SILC public key */
368 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
369 ident = &silc_pubkey->identifier;
371 SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
373 silc_mutex_lock(skr->lock);
375 /* Check that this key hasn't been added already */
376 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
377 public_key, NULL, key_context, 0)) {
378 silc_mutex_unlock(skr->lock);
379 SILC_LOG_DEBUG(("Key already added"));
380 return status | SILC_SKR_ALREADY_EXIST;
383 /* Allocate key entry */
384 key = silc_calloc(1, sizeof(*key));
386 silc_mutex_unlock(skr->lock);
387 return status | SILC_SKR_NO_MEMORY;
390 key->key.usage = usage;
391 key->key.key = public_key;
392 key->key.key_context = key_context;
394 /* Add key specifics */
396 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
401 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
402 SILC_32_TO_PTR(SILC_PKCS_SILC), key))
406 if (ident->username) {
407 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
408 ident->username, key))
414 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
420 if (ident->realname) {
421 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
422 ident->realname, key))
428 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
435 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
441 if (ident->country) {
442 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
443 ident->country, key))
449 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
455 silc_mutex_unlock(skr->lock);
458 *return_key = (SilcSKRKey)key;
463 silc_mutex_unlock(skr->lock);
467 /* Add SILC style public key to repository, and only the public key, not
468 other details from the key. */
470 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
471 SilcPublicKey public_key,
472 SilcSKRKeyUsage usage,
474 SilcSKRKey *return_key)
476 SilcSKRKeyInternal key;
477 SilcSKRStatus status = SILC_SKR_ERROR;
479 SILC_LOG_DEBUG(("Adding SILC public key"));
481 silc_mutex_lock(skr->lock);
483 /* Check that this key hasn't been added already */
484 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
485 public_key, NULL, key_context, 0)) {
486 silc_mutex_unlock(skr->lock);
487 SILC_LOG_DEBUG(("Key already added"));
488 return status | SILC_SKR_ALREADY_EXIST;
491 /* Allocate key entry */
492 key = silc_calloc(1, sizeof(*key));
494 silc_mutex_unlock(skr->lock);
495 return status | SILC_SKR_NO_MEMORY;
498 key->key.usage = usage;
499 key->key.key = public_key;
500 key->key.key_context = key_context;
502 /* Add key specifics */
504 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
510 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
516 silc_mutex_unlock(skr->lock);
519 *return_key = (SilcSKRKey)key;
524 silc_mutex_unlock(skr->lock);
528 /* Deletes SILC public key from repository */
530 static SilcSKRStatus silc_skr_del_silc_public_key(SilcSKR skr,
531 SilcPublicKey public_key,
534 SilcSKRStatus status = SILC_SKR_ERROR;
535 SilcPublicKeyIdentifier ident;
536 SilcSILCPublicKey silc_pubkey;
537 SilcSKRKeyInternal key;
540 /* Get the SILC public key */
541 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
542 ident = &silc_pubkey->identifier;
544 SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
546 silc_mutex_lock(skr->lock);
548 /* Check that this key exists */
549 if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
550 public_key, &entry, key_context, 0)) {
551 silc_mutex_unlock(skr->lock);
552 SILC_LOG_DEBUG(("Key does not exist"));
553 return status | SILC_SKR_NOT_FOUND;
556 silc_dlist_start(entry);
557 key = silc_dlist_get(entry);
558 silc_dlist_uninit(entry);
560 silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
561 silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
562 SILC_32_TO_PTR(SILC_PKCS_SILC), key);
563 silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
564 silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
565 silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
566 silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
567 silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
568 silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
569 silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
571 silc_mutex_unlock(skr->lock);
577 /**************************** Key Repository API ****************************/
579 /* Allocate key repository */
581 SilcSKR silc_skr_alloc(void)
585 skr = silc_calloc(1, sizeof(*skr));
589 if (!silc_skr_init(skr)) {
597 /* Free key repository */
599 void silc_skr_free(SilcSKR skr)
601 silc_skr_uninit(skr);
605 /* Initializes key repository */
607 SilcBool silc_skr_init(SilcSKR skr)
609 if (!silc_mutex_alloc(&skr->lock))
612 skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
613 silc_skr_compare, NULL,
614 silc_skr_destructor, NULL, TRUE);
621 /* Uninitializes key repository */
623 void silc_skr_uninit(SilcSKR skr)
626 silc_hash_table_free(skr->keys);
627 silc_mutex_free(skr->lock);
630 /* Adds public key to key repository */
632 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
633 SilcPublicKey public_key,
634 SilcSKRKeyUsage usage,
636 SilcSKRKey *return_key)
641 return SILC_SKR_ERROR;
643 type = silc_pkcs_get_type(public_key);
645 SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
650 return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
657 return SILC_SKR_ERROR;
660 /* Adds public key to repository. */
662 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
663 SilcPublicKey public_key,
664 SilcSKRKeyUsage usage,
666 SilcSKRKey *return_key)
671 return SILC_SKR_ERROR;
673 type = silc_pkcs_get_type(public_key);
675 SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
680 return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
688 return SILC_SKR_ERROR;
691 /* Remove key from repository */
693 SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
694 SilcPublicKey public_key,
700 return SILC_SKR_ERROR;
702 type = silc_pkcs_get_type(public_key);
704 SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
709 return silc_skr_del_silc_public_key(skr, public_key, key_context);
716 return SILC_SKR_ERROR;
721 void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
723 SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
725 silc_mutex_lock(skr->lock);
726 SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
728 silc_mutex_unlock(skr->lock);
731 /* Release key reference. */
733 void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
735 SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
737 silc_mutex_lock(skr->lock);
739 SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
742 if (k->refcnt == 0) {
743 /* If reference is zero, the key has been removed from the repository
744 already. Just destroy the public key. */
745 silc_pkcs_public_key_free(key->key);
749 silc_mutex_unlock(skr->lock);
753 /************************** Search Constraints API **************************/
755 /* Allocate search constraints */
757 SilcSKRFind silc_skr_find_alloc(void)
761 find = silc_calloc(1, sizeof(*find));
765 find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
766 silc_skr_find_destructor, NULL, TRUE);
768 silc_skr_find_free(find);
775 /* Free search constraints */
777 void silc_skr_find_free(SilcSKRFind find)
780 silc_hash_table_free(find->constr);
784 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
786 return silc_hash_table_add(find->constr,
787 SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
788 SILC_32_TO_PTR(type));
791 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
793 void *c = silc_memdup(username, strlen(username));
796 return silc_hash_table_add(find->constr,
797 SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
800 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
802 void *c = silc_memdup(host, strlen(host));
805 return silc_hash_table_add(find->constr,
806 SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
809 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
811 void *c = silc_memdup(realname, strlen(realname));
814 return silc_hash_table_add(find->constr,
815 SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
818 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
820 void *c = silc_memdup(email, strlen(email));
823 return silc_hash_table_add(find->constr,
824 SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
827 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
829 void *c = silc_memdup(org, strlen(org));
832 return silc_hash_table_add(find->constr,
833 SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
836 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
838 void *c = silc_memdup(country, strlen(country));
841 return silc_hash_table_add(find->constr,
842 SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
845 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
846 SilcPublicKey public_key)
848 SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
851 return silc_hash_table_add(find->constr,
852 SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
855 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
857 return silc_hash_table_add(find->constr,
858 SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
861 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
865 return silc_hash_table_add(find->constr,
866 SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
867 SILC_32_TO_PTR(usage));
870 /******************************** Search API ********************************/
872 /* Finds key(s) by the set search constraints. The callback will be called
873 once keys has been found. */
874 /* This is now synchronous function but may later change async */
876 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
878 SilcSKRFindCallback callback,
879 void *callback_context)
881 SilcSKRStatus status = SILC_SKR_ERROR;
882 SilcHashTableList htl;
883 SilcDList list, results = NULL;
884 void *type, *ctx, *usage = NULL;
886 SILC_LOG_DEBUG(("Finding key from repository"));
888 if (!find || !callback)
891 silc_mutex_lock(skr->lock);
893 /* Get usage bits, if searching by them */
894 silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
897 silc_hash_table_list(find->constr, &htl);
898 while (silc_hash_table_get(&htl, &type, &ctx)) {
900 #if defined(SILC_DEBUG)
902 memset(tmp, 0, sizeof(tmp));
903 silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
904 ctx, tmp, sizeof(tmp) - 1);
905 SILC_LOG_DEBUG(("Finding key by %s", tmp));
906 #endif /* SILC_DEBUG */
908 /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
909 if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
912 /* Find entries by this search constraint */
913 if (!silc_skr_find_entry(skr, &status,
914 (SilcSKRFindType)SILC_32_TO_PTR(type),
915 ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
916 SILC_LOG_DEBUG(("Not found"));
918 silc_dlist_uninit(results);
924 /* For now, our logic rule is AND. All constraints must be found
925 to find the key. Later OR might be added also. */
926 if (!silc_skr_results_and(list, &status, &results)) {
927 SILC_LOG_DEBUG(("Not found"));
929 silc_dlist_uninit(results);
932 silc_dlist_uninit(list);
936 silc_dlist_uninit(list);
938 silc_hash_table_list_reset(&htl);
940 silc_mutex_unlock(skr->lock);
944 callback(skr, find, status, NULL, callback_context);
946 silc_dlist_start(results);
947 callback(skr, find, SILC_SKR_OK, results, callback_context);