5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 - 2006 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 /* XXX Locking, when removing keys */
25 /************************** Types and definitions ***************************/
27 /* Search constraints */
29 SILC_SKR_FIND_PKCS_TYPE,
30 SILC_SKR_FIND_USERNAME,
32 SILC_SKR_FIND_REALNAME,
35 SILC_SKR_FIND_COUNTRY,
36 SILC_SKR_FIND_PUBLIC_KEY,
37 SILC_SKR_FIND_CONTEXT,
38 SILC_SKR_FIND_USAGE, /* Never added as key specific */
41 /* Hash table key context */
43 SilcSKRFindType type; /* Type of key */
44 void *data; /* Hash table key */
45 } *SilcSKREntry, SilcSKREntryStruct;
47 /* Foreach user context when finding entries from hash table */
51 SilcSKRKeyUsage usage;
54 #if defined(SILC_DEBUG)
55 static const char *find_name[] = {
68 #endif /* SILC_DEBUG */
70 /************************ Static utility functions **************************/
72 #if defined(SILC_DEBUG)
74 /* Returns search constraint string */
76 static void silc_skr_type_string(SilcSKRFindType type, void *data,
77 char *retbuf, SilcUInt32 retbuf_size)
80 case SILC_SKR_FIND_PKCS_TYPE:
81 case SILC_SKR_FIND_USAGE:
82 silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
83 (int)SILC_PTR_TO_32(data));
86 case SILC_SKR_FIND_PUBLIC_KEY:
87 silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
91 silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
96 #endif /* SILC_DEBUG */
98 /* Hash table destructor for search constraints */
100 static void silc_skr_find_destructor(void *key, void *context,
103 SilcSKRFindType type = SILC_PTR_TO_32(key);
106 case SILC_SKR_FIND_PKCS_TYPE:
107 case SILC_SKR_FIND_USAGE:
108 case SILC_SKR_FIND_CONTEXT:
111 case SILC_SKR_FIND_PUBLIC_KEY:
112 silc_pkcs_public_key_free(context);
120 /* Hash table destructor for key entries */
122 static void silc_skr_destructor(void *key, void *context, void *user_context)
124 SilcSKREntry type = key;
125 SilcSKRKeyInternal entry = context;
127 /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
128 shares same context with the key entry. */
129 if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
130 silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
135 if (entry->refcnt > 0)
138 silc_pkcs_public_key_free(entry->key.key);
142 /* Hash table hash function for key entries */
144 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
146 SilcSKREntry type = key;
148 switch (type->type) {
149 case SILC_SKR_FIND_PKCS_TYPE:
150 case SILC_SKR_FIND_CONTEXT:
151 return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
154 case SILC_SKR_FIND_PUBLIC_KEY:
155 return type->type + silc_hash_public_key(type->data, user_context);
162 return type->type + silc_hash_string(type->data, user_context);
165 /* Hash table comparison function for key entries */
167 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
169 SilcSKREntry type1 = key1;
170 SilcSKREntry type2 = key2;
172 if (type1->type != type2->type)
175 switch (type1->type) {
176 case SILC_SKR_FIND_PKCS_TYPE:
177 case SILC_SKR_FIND_CONTEXT:
178 return type1->data == type2->data;
181 case SILC_SKR_FIND_PUBLIC_KEY:
182 return silc_hash_public_key_compare(type1->data, type2->data,
190 return silc_utf8_strcasecmp((const char *)type1->data,
191 (const char *)type2->data);
194 /* Foreach function for finding entries in the repository */
196 static void silc_skr_find_foreach(void *key, void *context,
199 SilcSKRFindForeach *f = user_context;
200 SilcSKRKeyInternal k = context;
203 /* If key context is present, it must match the context in the key.
204 This is used only internally when adding keys, to check if the key
205 is added with same context. */
206 if (f->key_context && f->key_context != k->key.key_context)
209 /* Check for usage bits. At least one usage bit must be set. */
210 if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
213 silc_dlist_add(f->list, k);
217 /* Finds entry from repository by search constraint type and data */
219 static SilcBool silc_skr_find_entry(SilcSKR skr,
220 SilcSKRStatus *status,
221 SilcSKRFindType type,
225 SilcSKRKeyUsage usage)
227 SilcSKREntryStruct find;
228 SilcSKRFindForeach f;
230 f.list = silc_dlist_init();
232 *status |= SILC_SKR_NO_MEMORY;
235 f.key_context = key_context;
239 find.data = type_data;
241 silc_hash_table_find_foreach(skr->keys, (void *)&find,
242 silc_skr_find_foreach, &f);
244 if (!silc_dlist_count(f.list)) {
245 *status |= SILC_SKR_NOT_FOUND;
246 silc_dlist_uninit(f.list);
253 silc_dlist_uninit(f.list);
258 /* Add a key by search constraint type to repository */
260 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
261 void *type_data, SilcSKRKeyInternal key)
265 entry = silc_calloc(1, sizeof(*entry));
270 entry->data = type_data;
272 return silc_hash_table_add(skr->keys, entry, key);
275 /* Add SILC style public key to repository */
277 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
278 SilcPublicKey public_key,
279 SilcSKRKeyUsage usage,
282 SilcSKRKeyInternal key;
283 SilcSKRStatus status = SILC_SKR_ERROR;
284 SilcPublicKeyIdentifier ident;
285 SilcSILCPublicKey silc_pubkey;
287 /* Get the SILC public key */
288 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
289 ident = &silc_pubkey->identifier;
291 SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
293 silc_mutex_lock(skr->lock);
295 /* Check that this key hasn't been added already */
296 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
297 public_key, NULL, key_context, 0)) {
298 silc_mutex_unlock(skr->lock);
299 SILC_LOG_DEBUG(("Key already added"));
300 return status | SILC_SKR_ALREADY_EXIST;
303 /* Allocate key entry */
304 key = silc_calloc(1, sizeof(*key));
306 silc_mutex_unlock(skr->lock);
307 return status | SILC_SKR_NO_MEMORY;
310 key->key.usage = usage;
311 key->key.key = public_key;
312 key->key.key_context = key_context;
314 /* Add key specifics */
316 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
321 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
322 SILC_32_TO_PTR(SILC_PKCS_SILC), key))
326 if (ident->username) {
327 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
328 ident->username, key))
334 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
340 if (ident->realname) {
341 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
342 ident->realname, key))
348 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
355 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
361 if (ident->country) {
362 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
363 ident->country, key))
369 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
375 silc_mutex_unlock(skr->lock);
380 silc_mutex_unlock(skr->lock);
384 /* Add SILC style public key to repository, and only the public key, not
385 other details from the key. */
387 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
388 SilcPublicKey public_key,
389 SilcSKRKeyUsage usage,
392 SilcSKRKeyInternal key;
393 SilcSKRStatus status = SILC_SKR_ERROR;
395 SILC_LOG_DEBUG(("Adding SILC public key"));
397 silc_mutex_lock(skr->lock);
399 /* Check that this key hasn't been added already */
400 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
401 public_key, NULL, key_context, 0)) {
402 silc_mutex_unlock(skr->lock);
403 SILC_LOG_DEBUG(("Key already added"));
404 return status | SILC_SKR_ALREADY_EXIST;
407 /* Allocate key entry */
408 key = silc_calloc(1, sizeof(*key));
410 silc_mutex_unlock(skr->lock);
411 return status | SILC_SKR_NO_MEMORY;
414 key->key.usage = usage;
415 key->key.key = public_key;
416 key->key.key_context = key_context;
418 /* Add key specifics */
420 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
426 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
432 silc_mutex_unlock(skr->lock);
437 silc_mutex_unlock(skr->lock);
441 /* This performs AND operation. Any entry already in `results' that is not
442 in `list' will be removed from `results'. */
444 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
447 SilcSKRKeyInternal entry, r;
449 if (*results == NULL) {
450 *results = silc_dlist_init();
451 if (*results == NULL) {
452 *status |= SILC_SKR_NO_MEMORY;
457 /* If results is empty, just add all entries from list to results */
458 if (!silc_dlist_count(*results)) {
459 silc_dlist_start(list);
460 while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
461 silc_dlist_add(*results, entry);
466 silc_dlist_start(*results);
467 while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
469 /* Check if this entry is in list */
470 silc_dlist_start(list);
471 while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
475 if (r != SILC_LIST_END)
478 /* Remove from results */
479 silc_dlist_del(*results, entry);
482 /* If results became empty, we did not find any key */
483 if (!silc_dlist_count(*results)) {
484 SILC_LOG_DEBUG(("Not all search constraints found"));
485 *status |= SILC_SKR_NOT_FOUND;
493 /**************************** Key Repository API ****************************/
495 /* Allocate key repository */
497 SilcSKR silc_skr_alloc(SilcSchedule scheduler)
501 skr = silc_calloc(1, sizeof(*skr));
505 if (!silc_skr_init(skr, scheduler)) {
513 /* Free key repository */
515 void silc_skr_free(SilcSKR skr)
517 silc_skr_uninit(skr);
521 /* Initializes key repository */
523 SilcBool silc_skr_init(SilcSKR skr, SilcSchedule scheduler)
528 skr->scheduler = scheduler;
530 if (!silc_mutex_alloc(&skr->lock))
533 skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
534 silc_skr_compare, NULL,
535 silc_skr_destructor, NULL, TRUE);
542 /* Uninitializes key repository */
544 void silc_skr_uninit(SilcSKR skr)
547 silc_hash_table_free(skr->keys);
548 silc_mutex_free(skr->lock);
551 /* Adds public key to key repository */
553 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
554 SilcPublicKey public_key,
555 SilcSKRKeyUsage usage,
561 return SILC_SKR_ERROR;
563 type = silc_pkcs_get_type(public_key);
565 SILC_LOG_DEBUG(("Adding public key to repository"));
570 return silc_skr_add_silc(skr, public_key, usage, key_context);
577 return SILC_SKR_ERROR;
580 /* Adds public key to repository. */
582 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
583 SilcPublicKey public_key,
584 SilcSKRKeyUsage usage,
590 return SILC_SKR_ERROR;
592 type = silc_pkcs_get_type(public_key);
594 SILC_LOG_DEBUG(("Adding public key to repository"));
599 return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
606 return SILC_SKR_ERROR;
610 /************************** Search Constraints API **************************/
612 /* Allocate search constraints */
614 SilcSKRFind silc_skr_find_alloc(void)
618 find = silc_calloc(1, sizeof(*find));
622 find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
623 silc_skr_find_destructor, NULL, TRUE);
625 silc_skr_find_free(find);
632 /* Free search constraints */
634 void silc_skr_find_free(SilcSKRFind find)
637 silc_hash_table_free(find->constr);
641 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
643 return silc_hash_table_add(find->constr,
644 SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
645 SILC_32_TO_PTR(type));
648 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
650 void *c = silc_memdup(username, strlen(username));
653 return silc_hash_table_add(find->constr,
654 SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
657 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
659 void *c = silc_memdup(host, strlen(host));
662 return silc_hash_table_add(find->constr,
663 SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
666 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
668 void *c = silc_memdup(realname, strlen(realname));
671 return silc_hash_table_add(find->constr,
672 SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
675 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
677 void *c = silc_memdup(email, strlen(email));
680 return silc_hash_table_add(find->constr,
681 SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
684 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
686 void *c = silc_memdup(org, strlen(org));
689 return silc_hash_table_add(find->constr,
690 SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
693 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
695 void *c = silc_memdup(country, strlen(country));
698 return silc_hash_table_add(find->constr,
699 SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
702 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
703 SilcPublicKey public_key)
705 SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
708 return silc_hash_table_add(find->constr,
709 SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
712 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
714 return silc_hash_table_add(find->constr,
715 SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
718 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
722 return silc_hash_table_add(find->constr,
723 SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
724 SILC_32_TO_PTR(usage));
727 /******************************** Search API ********************************/
729 /* Finds key(s) by the set search constraints. The callback will be called
730 once keys has been found. */
731 /* This is now synchronous function but may later change async */
733 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
734 SilcSKRFindCallback callback,
735 void *callback_context)
737 SilcSKRStatus status = SILC_SKR_ERROR;
738 SilcHashTableList htl;
739 SilcDList list, results = NULL;
740 void *type, *ctx, *usage = NULL;
742 SILC_LOG_DEBUG(("Finding key from repository"));
744 if (!find || !callback)
747 silc_mutex_lock(skr->lock);
749 /* Get usage bits, if searching by them */
750 silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
753 silc_hash_table_list(find->constr, &htl);
754 while (silc_hash_table_get(&htl, &type, &ctx)) {
756 #if defined(SILC_DEBUG)
758 memset(tmp, 0, sizeof(tmp));
759 silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
760 ctx, tmp, sizeof(tmp) - 1);
761 SILC_LOG_DEBUG(("Finding key by %s", tmp));
762 #endif /* SILC_DEBUG */
764 /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
765 if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
768 /* Find entries by this search constraint */
769 if (!silc_skr_find_entry(skr, &status,
770 (SilcSKRFindType)SILC_32_TO_PTR(type),
771 ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
772 SILC_LOG_DEBUG(("Not found"));
774 silc_dlist_uninit(results);
780 /* For now, our logic rule is AND. All constraints must be found
781 to find the key. Later OR might be added also. */
782 if (!silc_skr_results_and(list, &status, &results)) {
783 SILC_LOG_DEBUG(("Not found"));
785 silc_dlist_uninit(results);
788 silc_dlist_uninit(list);
792 silc_dlist_uninit(list);
794 silc_hash_table_list_reset(&htl);
796 silc_mutex_unlock(skr->lock);
800 callback(skr, find, status, NULL, callback_context);
802 silc_dlist_start(results);
803 callback(skr, find, SILC_SKR_OK, results, callback_context);