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 /* 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(void)
501 skr = silc_calloc(1, sizeof(*skr));
505 if (!silc_skr_init(skr)) {
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)
525 if (!silc_mutex_alloc(&skr->lock))
528 skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
529 silc_skr_compare, NULL,
530 silc_skr_destructor, NULL, TRUE);
537 /* Uninitializes key repository */
539 void silc_skr_uninit(SilcSKR skr)
542 silc_hash_table_free(skr->keys);
543 silc_mutex_free(skr->lock);
546 /* Adds public key to key repository */
548 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
549 SilcPublicKey public_key,
550 SilcSKRKeyUsage usage,
556 return SILC_SKR_ERROR;
558 type = silc_pkcs_get_type(public_key);
560 SILC_LOG_DEBUG(("Adding public key to repository"));
565 return silc_skr_add_silc(skr, public_key, usage, key_context);
572 return SILC_SKR_ERROR;
575 /* Adds public key to repository. */
577 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
578 SilcPublicKey public_key,
579 SilcSKRKeyUsage usage,
585 return SILC_SKR_ERROR;
587 type = silc_pkcs_get_type(public_key);
589 SILC_LOG_DEBUG(("Adding public key to repository"));
594 return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
601 return SILC_SKR_ERROR;
605 /************************** Search Constraints API **************************/
607 /* Allocate search constraints */
609 SilcSKRFind silc_skr_find_alloc(void)
613 find = silc_calloc(1, sizeof(*find));
617 find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
618 silc_skr_find_destructor, NULL, TRUE);
620 silc_skr_find_free(find);
627 /* Free search constraints */
629 void silc_skr_find_free(SilcSKRFind find)
632 silc_hash_table_free(find->constr);
636 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
638 return silc_hash_table_add(find->constr,
639 SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
640 SILC_32_TO_PTR(type));
643 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
645 void *c = silc_memdup(username, strlen(username));
648 return silc_hash_table_add(find->constr,
649 SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
652 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
654 void *c = silc_memdup(host, strlen(host));
657 return silc_hash_table_add(find->constr,
658 SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
661 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
663 void *c = silc_memdup(realname, strlen(realname));
666 return silc_hash_table_add(find->constr,
667 SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
670 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
672 void *c = silc_memdup(email, strlen(email));
675 return silc_hash_table_add(find->constr,
676 SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
679 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
681 void *c = silc_memdup(org, strlen(org));
684 return silc_hash_table_add(find->constr,
685 SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
688 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
690 void *c = silc_memdup(country, strlen(country));
693 return silc_hash_table_add(find->constr,
694 SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
697 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
698 SilcPublicKey public_key)
700 SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
703 return silc_hash_table_add(find->constr,
704 SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
707 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
709 return silc_hash_table_add(find->constr,
710 SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
713 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
717 return silc_hash_table_add(find->constr,
718 SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
719 SILC_32_TO_PTR(usage));
722 /******************************** Search API ********************************/
724 /* Finds key(s) by the set search constraints. The callback will be called
725 once keys has been found. */
726 /* This is now synchronous function but may later change async */
728 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
730 SilcSKRFindCallback callback,
731 void *callback_context)
733 SilcSKRStatus status = SILC_SKR_ERROR;
734 SilcHashTableList htl;
735 SilcDList list, results = NULL;
736 void *type, *ctx, *usage = NULL;
738 SILC_LOG_DEBUG(("Finding key from repository"));
740 if (!find || !callback)
743 silc_mutex_lock(skr->lock);
745 /* Get usage bits, if searching by them */
746 silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
749 silc_hash_table_list(find->constr, &htl);
750 while (silc_hash_table_get(&htl, &type, &ctx)) {
752 #if defined(SILC_DEBUG)
754 memset(tmp, 0, sizeof(tmp));
755 silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
756 ctx, tmp, sizeof(tmp) - 1);
757 SILC_LOG_DEBUG(("Finding key by %s", tmp));
758 #endif /* SILC_DEBUG */
760 /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
761 if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
764 /* Find entries by this search constraint */
765 if (!silc_skr_find_entry(skr, &status,
766 (SilcSKRFindType)SILC_32_TO_PTR(type),
767 ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
768 SILC_LOG_DEBUG(("Not found"));
770 silc_dlist_uninit(results);
776 /* For now, our logic rule is AND. All constraints must be found
777 to find the key. Later OR might be added also. */
778 if (!silc_skr_results_and(list, &status, &results)) {
779 SILC_LOG_DEBUG(("Not found"));
781 silc_dlist_uninit(results);
784 silc_dlist_uninit(list);
788 silc_dlist_uninit(list);
790 silc_hash_table_list_reset(&htl);
792 silc_mutex_unlock(skr->lock);
796 callback(skr, find, status, NULL, callback_context);
798 silc_dlist_start(results);
799 callback(skr, find, SILC_SKR_OK, results, callback_context);