5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 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 snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
81 (int)SILC_PTR_TO_32(data));
84 case SILC_SKR_FIND_PUBLIC_KEY:
85 snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
86 ((SilcPublicKey)data)->identifier);
90 snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
95 #endif /* SILC_DEBUG */
97 /* Hash table destructor for search constraints */
99 static void silc_skr_find_destructor(void *key, void *context,
102 SilcSKRFindType type = SILC_PTR_TO_32(key);
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);
119 /* Hash table destructor for key entries */
121 static void silc_skr_destructor(void *key, void *context, void *user_context)
123 SilcSKREntry type = key;
124 SilcSKRKeyInternal entry = context;
126 /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
127 shares same context with the key entry. */
128 if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
129 silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
134 if (entry->refcnt == 0) {
135 switch (entry->key.pk_type) {
137 silc_pkcs_public_key_free(entry->key.key);
148 /* Hash table hash function for key entries */
150 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
152 SilcSKREntry type = key;
154 switch (type->type) {
155 case SILC_SKR_FIND_PKCS_TYPE:
156 case SILC_SKR_FIND_CONTEXT:
157 return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
160 case SILC_SKR_FIND_PUBLIC_KEY:
161 return type->type + silc_hash_public_key(type->data, user_context);
168 return type->type + silc_hash_string(type->data, user_context);
171 /* Hash table comparison function for key entries */
173 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
175 SilcSKREntry type1 = key1;
176 SilcSKREntry type2 = key2;
178 if (type1->type != type2->type)
181 switch (type1->type) {
182 case SILC_SKR_FIND_PKCS_TYPE:
183 case SILC_SKR_FIND_CONTEXT:
184 return type1->data == type2->data;
187 case SILC_SKR_FIND_PUBLIC_KEY:
188 return silc_hash_public_key_compare(type1->data, type2->data,
196 return silc_utf8_strcasecmp((const char *)type1->data,
197 (const char *)type2->data);
200 /* Foreach function for finding entries in the repository */
202 static void silc_skr_find_foreach(void *key, void *context,
205 SilcSKRFindForeach *f = user_context;
206 SilcSKRKeyInternal k = context;
209 /* If key context is present, it must match the context in the key.
210 This is used only internally when adding keys, to check if the key
211 is added with same context. */
212 if (f->key_context && f->key_context != k->key.key_context)
215 /* Check for usage bits. At least one usage bit must be set. */
216 if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
219 silc_dlist_add(f->list, k);
223 /* Finds entry from repository by search constraint type and data */
225 static SilcBool silc_skr_find_entry(SilcSKR skr,
226 SilcSKRStatus *status,
227 SilcSKRFindType type,
231 SilcSKRKeyUsage usage)
233 SilcSKREntryStruct find;
234 SilcSKRFindForeach f;
236 f.list = silc_dlist_init();
238 *status |= SILC_SKR_NO_MEMORY;
241 f.key_context = key_context;
245 find.data = type_data;
247 silc_hash_table_find_foreach(skr->keys, (void *)&find,
248 silc_skr_find_foreach, &f);
250 if (!silc_dlist_count(f.list)) {
251 *status |= SILC_SKR_NOT_FOUND;
252 silc_dlist_uninit(f.list);
259 silc_dlist_uninit(f.list);
264 /* Add a key by search constraint type to repository */
266 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
267 void *type_data, SilcSKRKeyInternal key)
271 entry = silc_calloc(1, sizeof(*entry));
276 entry->data = type_data;
278 return silc_hash_table_add(skr->keys, entry, key);
281 /* Add SILC style public key to repository */
283 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
284 SilcPublicKey public_key,
285 SilcSKRKeyUsage usage,
288 SilcPublicKeyIdentifier ident = NULL;
289 SilcSKRKeyInternal key;
290 SilcSKRStatus status = SILC_SKR_ERROR;
292 SILC_LOG_DEBUG(("Adding SILC public key [%s]", public_key->identifier));
294 silc_mutex_lock(skr->lock);
296 /* Check that this key hasn't been added already */
297 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
298 public_key, NULL, key_context, 0)) {
299 silc_mutex_unlock(skr->lock);
300 SILC_LOG_DEBUG(("Key already added"));
304 /* Allocate key entry */
305 key = silc_calloc(1, sizeof(*key));
307 silc_mutex_unlock(skr->lock);
308 return status | SILC_SKR_NO_MEMORY;
311 key->key.usage = usage;
312 key->key.pk_type = public_key->pk_type;
313 key->key.key = public_key;
314 key->key.key_context = key_context;
316 ident = silc_pkcs_decode_identifier(public_key->identifier);
318 silc_mutex_unlock(skr->lock);
319 silc_pkcs_free_identifier(ident);
320 return status | SILC_SKR_NO_MEMORY;
323 /* Add key specifics */
325 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
330 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
331 SILC_32_TO_PTR(public_key->pk_type), key))
335 if (ident->username) {
336 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
337 ident->username, key))
343 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
349 if (ident->realname) {
350 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
351 ident->realname, key))
357 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
364 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
370 if (ident->country) {
371 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
372 ident->country, key))
378 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
384 silc_mutex_unlock(skr->lock);
390 silc_mutex_unlock(skr->lock);
395 /* Add SILC style public key to repository, and only the public key, not
396 other details from the key. */
398 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
399 SilcPublicKey public_key,
400 SilcSKRKeyUsage usage,
403 SilcSKRKeyInternal key;
404 SilcSKRStatus status = SILC_SKR_ERROR;
406 SILC_LOG_DEBUG(("Adding SILC public key [%s]", public_key->identifier));
408 silc_mutex_lock(skr->lock);
410 /* Check that this key hasn't been added already */
411 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
412 public_key, NULL, key_context, 0)) {
413 silc_mutex_unlock(skr->lock);
414 SILC_LOG_DEBUG(("Key already added"));
418 /* Allocate key entry */
419 key = silc_calloc(1, sizeof(*key));
421 silc_mutex_unlock(skr->lock);
422 return status | SILC_SKR_NO_MEMORY;
425 key->key.usage = usage;
426 key->key.pk_type = public_key->pk_type;
427 key->key.key = public_key;
428 key->key.key_context = key_context;
430 /* Add key specifics */
432 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
438 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
444 silc_mutex_unlock(skr->lock);
449 silc_mutex_unlock(skr->lock);
453 /* This performs AND operation. Any entry already in `results' that is not
454 in `list' will be removed from `results'. */
456 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
459 SilcSKRKeyInternal entry, r;
461 if (*results == NULL) {
462 *results = silc_dlist_init();
463 if (*results == NULL) {
464 *status |= SILC_SKR_NO_MEMORY;
469 /* If results is empty, just add all entries from list to results */
470 if (!silc_dlist_count(*results)) {
471 silc_dlist_start(list);
472 while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
473 silc_dlist_add(*results, entry);
478 silc_dlist_start(*results);
479 while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
481 /* Check if this entry is in list */
482 silc_dlist_start(list);
483 while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
487 if (r != SILC_LIST_END)
490 /* Remove from results */
491 silc_dlist_del(*results, entry);
494 /* If results became empty, we did not find any key */
495 if (!silc_dlist_count(*results)) {
496 SILC_LOG_DEBUG(("Not all search constraints found"));
497 *status |= SILC_SKR_NOT_FOUND;
505 /**************************** Key Repository API ****************************/
507 /* Allocate key repository */
509 SilcSKR silc_skr_alloc(SilcSchedule scheduler)
513 skr = silc_calloc(1, sizeof(*skr));
517 if (!silc_skr_init(skr, scheduler)) {
525 /* Free key repository */
527 void silc_skr_free(SilcSKR skr)
529 silc_skr_uninit(skr);
533 /* Initializes key repository */
535 SilcBool silc_skr_init(SilcSKR skr, SilcSchedule scheduler)
540 skr->scheduler = scheduler;
542 if (!silc_mutex_alloc(&skr->lock))
545 skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
546 silc_skr_compare, NULL,
547 silc_skr_destructor, NULL, TRUE);
554 /* Uninitializes key repository */
556 void silc_skr_uninit(SilcSKR skr)
559 silc_hash_table_free(skr->keys);
560 silc_mutex_free(skr->lock);
563 /* Adds public key to key repository */
565 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
566 SilcPublicKey public_key,
567 SilcSKRKeyUsage usage,
571 return SILC_SKR_ERROR;
573 SILC_LOG_DEBUG(("Adding public key to repository"));
575 switch (public_key->pk_type) {
578 return silc_skr_add_silc(skr, public_key, usage, key_context);
585 return SILC_SKR_ERROR;
588 /* Adds public key to repository. */
590 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
591 SilcPublicKey public_key,
592 SilcSKRKeyUsage usage,
596 return SILC_SKR_ERROR;
598 SILC_LOG_DEBUG(("Adding public key to repository"));
600 switch (public_key->pk_type) {
603 return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
610 return SILC_SKR_ERROR;
614 /************************** Search Constraints API **************************/
616 /* Allocate search constraints */
618 SilcSKRFind silc_skr_find_alloc(void)
622 find = silc_calloc(1, sizeof(*find));
626 find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
627 silc_skr_find_destructor, NULL, TRUE);
629 silc_skr_find_free(find);
636 /* Free search constraints */
638 void silc_skr_find_free(SilcSKRFind find)
641 silc_hash_table_free(find->constr);
645 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
647 return silc_hash_table_add(find->constr,
648 SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
649 SILC_32_TO_PTR(type));
652 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
654 void *c = silc_memdup(username, strlen(username));
657 return silc_hash_table_add(find->constr,
658 SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
661 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
663 void *c = silc_memdup(host, strlen(host));
666 return silc_hash_table_add(find->constr,
667 SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
670 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
672 void *c = silc_memdup(realname, strlen(realname));
675 return silc_hash_table_add(find->constr,
676 SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
679 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
681 void *c = silc_memdup(email, strlen(email));
684 return silc_hash_table_add(find->constr,
685 SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
688 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
690 void *c = silc_memdup(org, strlen(org));
693 return silc_hash_table_add(find->constr,
694 SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
697 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
699 void *c = silc_memdup(country, strlen(country));
702 return silc_hash_table_add(find->constr,
703 SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
706 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
707 SilcPublicKey public_key)
709 SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
712 return silc_hash_table_add(find->constr,
713 SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
716 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
718 return silc_hash_table_add(find->constr,
719 SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
722 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
726 return silc_hash_table_add(find->constr,
727 SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
728 SILC_32_TO_PTR(usage));
731 /******************************** Search API ********************************/
733 /* Finds key(s) by the set search constraints. The callback will be called
734 once keys has been found. */
735 /* This is now synchronous function but may later change async */
737 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
738 SilcSKRFindCallback callback,
739 void *callback_context)
741 SilcSKRStatus status = SILC_SKR_ERROR;
742 SilcHashTableList htl;
743 SilcDList list, results = NULL;
744 void *type, *ctx, *usage = NULL;
746 SILC_LOG_DEBUG(("Finding key from repository"));
748 if (!find || !callback)
751 silc_mutex_lock(skr->lock);
753 /* Get usage bits, if searching by them */
754 silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
757 silc_hash_table_list(find->constr, &htl);
758 while (silc_hash_table_get(&htl, &type, &ctx)) {
760 #if defined(SILC_DEBUG)
762 memset(tmp, 0, sizeof(tmp));
763 silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
764 ctx, tmp, sizeof(tmp) - 1);
765 SILC_LOG_DEBUG(("Finding key by %s", tmp));
766 #endif /* SILC_DEBUG */
768 /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
769 if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
772 /* Find entries by this search constraint */
773 if (!silc_skr_find_entry(skr, &status,
774 (SilcSKRFindType)SILC_32_TO_PTR(type),
775 ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
776 SILC_LOG_DEBUG(("Not found"));
778 silc_dlist_uninit(results);
784 /* For now, our logic rule is AND. All constraints must be found
785 to find the key. Later OR might be added also. */
786 if (!silc_skr_results_and(list, &status, &results)) {
787 SILC_LOG_DEBUG(("Not found"));
789 silc_dlist_uninit(results);
792 silc_dlist_uninit(list);
796 silc_dlist_uninit(list);
798 silc_hash_table_list_reset(&htl);
800 silc_mutex_unlock(skr->lock);
804 callback(skr, find, status, NULL, callback_context);
806 silc_dlist_start(results);
807 callback(skr, find, SILC_SKR_OK, results, callback_context);
813 /* Helper function to find specificly SILC style public keys */
815 SilcAsyncOperation silc_skr_find_silc(SilcSKR skr,
816 SilcPublicKey public_key,
817 SilcSKRFindCallback callback,
818 void *callback_context)
820 SilcSKRFind find = NULL;
821 SilcAsyncOperation op;
823 SILC_LOG_DEBUG(("Finding SILC public key"));
825 if (!public_key || public_key->pk_type != SILC_PKCS_SILC)
828 find = silc_skr_find_alloc();
832 if (!silc_skr_find_set_public_key(find, public_key))
835 op = silc_skr_find(skr, find, callback, callback_context);
841 silc_skr_find_free(find);
842 callback(skr, NULL, SILC_SKR_ERROR, NULL, callback_context);