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] [%p]", find_name[type], data);
89 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);
104 case SILC_SKR_FIND_PKCS_TYPE:
105 case SILC_SKR_FIND_USAGE:
106 case SILC_SKR_FIND_CONTEXT:
109 case SILC_SKR_FIND_PUBLIC_KEY:
110 silc_pkcs_public_key_free(context);
118 /* Hash table destructor for key entries */
120 static void silc_skr_destructor(void *key, void *context, void *user_context)
122 SilcSKREntry type = key;
123 SilcSKRKeyInternal entry = context;
125 /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
126 shares same context with the key entry. */
127 if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
128 silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
133 if (entry->refcnt > 0)
136 silc_pkcs_public_key_free(entry->key.key);
140 /* Hash table hash function for key entries */
142 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
144 SilcSKREntry type = key;
146 switch (type->type) {
147 case SILC_SKR_FIND_PKCS_TYPE:
148 case SILC_SKR_FIND_CONTEXT:
149 return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
152 case SILC_SKR_FIND_PUBLIC_KEY:
153 return type->type + silc_hash_public_key(type->data, user_context);
160 return type->type + silc_hash_string(type->data, user_context);
163 /* Hash table comparison function for key entries */
165 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
167 SilcSKREntry type1 = key1;
168 SilcSKREntry type2 = key2;
170 if (type1->type != type2->type)
173 switch (type1->type) {
174 case SILC_SKR_FIND_PKCS_TYPE:
175 case SILC_SKR_FIND_CONTEXT:
176 return type1->data == type2->data;
179 case SILC_SKR_FIND_PUBLIC_KEY:
180 return silc_hash_public_key_compare(type1->data, type2->data,
188 return silc_utf8_strcasecmp((const char *)type1->data,
189 (const char *)type2->data);
192 /* Foreach function for finding entries in the repository */
194 static void silc_skr_find_foreach(void *key, void *context,
197 SilcSKRFindForeach *f = user_context;
198 SilcSKRKeyInternal k = context;
201 /* If key context is present, it must match the context in the key.
202 This is used only internally when adding keys, to check if the key
203 is added with same context. */
204 if (f->key_context && f->key_context != k->key.key_context)
207 /* Check for usage bits. At least one usage bit must be set. */
208 if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
211 silc_dlist_add(f->list, k);
215 /* Finds entry from repository by search constraint type and data */
217 static SilcBool silc_skr_find_entry(SilcSKR skr,
218 SilcSKRStatus *status,
219 SilcSKRFindType type,
223 SilcSKRKeyUsage usage)
225 SilcSKREntryStruct find;
226 SilcSKRFindForeach f;
228 f.list = silc_dlist_init();
230 *status |= SILC_SKR_NO_MEMORY;
233 f.key_context = key_context;
237 find.data = type_data;
239 silc_hash_table_find_foreach(skr->keys, (void *)&find,
240 silc_skr_find_foreach, &f);
242 if (!silc_dlist_count(f.list)) {
243 *status |= SILC_SKR_NOT_FOUND;
244 silc_dlist_uninit(f.list);
251 silc_dlist_uninit(f.list);
256 /* Add a key by search constraint type to repository */
258 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
259 void *type_data, SilcSKRKeyInternal key)
263 entry = silc_calloc(1, sizeof(*entry));
268 entry->data = type_data;
270 return silc_hash_table_add(skr->keys, entry, key);
273 /* Add SILC style public key to repository */
275 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
276 SilcPublicKey public_key,
277 SilcSKRKeyUsage usage,
280 SilcSKRKeyInternal key;
281 SilcSKRStatus status = SILC_SKR_ERROR;
282 SilcPublicKeyIdentifier ident;
283 SilcSILCPublicKey silc_pubkey;
285 /* Get the SILC public key */
286 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
287 ident = &silc_pubkey->identifier;
289 SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
291 silc_mutex_lock(skr->lock);
293 /* Check that this key hasn't been added already */
294 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
295 public_key, NULL, key_context, 0)) {
296 silc_mutex_unlock(skr->lock);
297 SILC_LOG_DEBUG(("Key already added"));
298 return status | SILC_SKR_ALREADY_EXIST;
301 /* Allocate key entry */
302 key = silc_calloc(1, sizeof(*key));
304 silc_mutex_unlock(skr->lock);
305 return status | SILC_SKR_NO_MEMORY;
308 key->key.usage = usage;
309 key->key.key = public_key;
310 key->key.key_context = key_context;
312 /* Add key specifics */
314 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
319 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
320 SILC_32_TO_PTR(SILC_PKCS_SILC), key))
324 if (ident->username) {
325 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
326 ident->username, key))
332 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
338 if (ident->realname) {
339 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
340 ident->realname, key))
346 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
353 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
359 if (ident->country) {
360 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
361 ident->country, key))
367 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
373 silc_mutex_unlock(skr->lock);
378 silc_mutex_unlock(skr->lock);
382 /* Add SILC style public key to repository, and only the public key, not
383 other details from the key. */
385 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
386 SilcPublicKey public_key,
387 SilcSKRKeyUsage usage,
390 SilcSKRKeyInternal key;
391 SilcSKRStatus status = SILC_SKR_ERROR;
393 SILC_LOG_DEBUG(("Adding SILC public key"));
395 silc_mutex_lock(skr->lock);
397 /* Check that this key hasn't been added already */
398 if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
399 public_key, NULL, key_context, 0)) {
400 silc_mutex_unlock(skr->lock);
401 SILC_LOG_DEBUG(("Key already added"));
402 return status | SILC_SKR_ALREADY_EXIST;
405 /* Allocate key entry */
406 key = silc_calloc(1, sizeof(*key));
408 silc_mutex_unlock(skr->lock);
409 return status | SILC_SKR_NO_MEMORY;
412 key->key.usage = usage;
413 key->key.key = public_key;
414 key->key.key_context = key_context;
416 /* Add key specifics */
418 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
424 if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
430 silc_mutex_unlock(skr->lock);
435 silc_mutex_unlock(skr->lock);
439 /* This performs AND operation. Any entry already in `results' that is not
440 in `list' will be removed from `results'. */
442 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
445 SilcSKRKeyInternal entry, r;
447 if (*results == NULL) {
448 *results = silc_dlist_init();
449 if (*results == NULL) {
450 *status |= SILC_SKR_NO_MEMORY;
455 /* If results is empty, just add all entries from list to results */
456 if (!silc_dlist_count(*results)) {
457 silc_dlist_start(list);
458 while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
459 silc_dlist_add(*results, entry);
464 silc_dlist_start(*results);
465 while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
467 /* Check if this entry is in list */
468 silc_dlist_start(list);
469 while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
473 if (r != SILC_LIST_END)
476 /* Remove from results */
477 silc_dlist_del(*results, entry);
480 /* If results became empty, we did not find any key */
481 if (!silc_dlist_count(*results)) {
482 SILC_LOG_DEBUG(("Not all search constraints found"));
483 *status |= SILC_SKR_NOT_FOUND;
491 /**************************** Key Repository API ****************************/
493 /* Allocate key repository */
495 SilcSKR silc_skr_alloc(SilcSchedule scheduler)
499 skr = silc_calloc(1, sizeof(*skr));
503 if (!silc_skr_init(skr, scheduler)) {
511 /* Free key repository */
513 void silc_skr_free(SilcSKR skr)
515 silc_skr_uninit(skr);
519 /* Initializes key repository */
521 SilcBool silc_skr_init(SilcSKR skr, SilcSchedule scheduler)
526 skr->scheduler = scheduler;
528 if (!silc_mutex_alloc(&skr->lock))
531 skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
532 silc_skr_compare, NULL,
533 silc_skr_destructor, NULL, TRUE);
540 /* Uninitializes key repository */
542 void silc_skr_uninit(SilcSKR skr)
545 silc_hash_table_free(skr->keys);
546 silc_mutex_free(skr->lock);
549 /* Adds public key to key repository */
551 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
552 SilcPublicKey public_key,
553 SilcSKRKeyUsage usage,
559 return SILC_SKR_ERROR;
561 type = silc_pkcs_get_type(public_key);
563 SILC_LOG_DEBUG(("Adding public key to repository"));
568 return silc_skr_add_silc(skr, public_key, usage, key_context);
575 return SILC_SKR_ERROR;
578 /* Adds public key to repository. */
580 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
581 SilcPublicKey public_key,
582 SilcSKRKeyUsage usage,
588 return SILC_SKR_ERROR;
590 type = silc_pkcs_get_type(public_key);
592 SILC_LOG_DEBUG(("Adding public key to repository"));
597 return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
604 return SILC_SKR_ERROR;
608 /************************** Search Constraints API **************************/
610 /* Allocate search constraints */
612 SilcSKRFind silc_skr_find_alloc(void)
616 find = silc_calloc(1, sizeof(*find));
620 find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
621 silc_skr_find_destructor, NULL, TRUE);
623 silc_skr_find_free(find);
630 /* Free search constraints */
632 void silc_skr_find_free(SilcSKRFind find)
635 silc_hash_table_free(find->constr);
639 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
641 return silc_hash_table_add(find->constr,
642 SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
643 SILC_32_TO_PTR(type));
646 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
648 void *c = silc_memdup(username, strlen(username));
651 return silc_hash_table_add(find->constr,
652 SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
655 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
657 void *c = silc_memdup(host, strlen(host));
660 return silc_hash_table_add(find->constr,
661 SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
664 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
666 void *c = silc_memdup(realname, strlen(realname));
669 return silc_hash_table_add(find->constr,
670 SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
673 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
675 void *c = silc_memdup(email, strlen(email));
678 return silc_hash_table_add(find->constr,
679 SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
682 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
684 void *c = silc_memdup(org, strlen(org));
687 return silc_hash_table_add(find->constr,
688 SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
691 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
693 void *c = silc_memdup(country, strlen(country));
696 return silc_hash_table_add(find->constr,
697 SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
700 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
701 SilcPublicKey public_key)
703 SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
706 return silc_hash_table_add(find->constr,
707 SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
710 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
712 return silc_hash_table_add(find->constr,
713 SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
716 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
720 return silc_hash_table_add(find->constr,
721 SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
722 SILC_32_TO_PTR(usage));
725 /******************************** Search API ********************************/
727 /* Finds key(s) by the set search constraints. The callback will be called
728 once keys has been found. */
729 /* This is now synchronous function but may later change async */
731 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
732 SilcSKRFindCallback callback,
733 void *callback_context)
735 SilcSKRStatus status = SILC_SKR_ERROR;
736 SilcHashTableList htl;
737 SilcDList list, results = NULL;
738 void *type, *ctx, *usage = NULL;
740 SILC_LOG_DEBUG(("Finding key from repository"));
742 if (!find || !callback)
745 silc_mutex_lock(skr->lock);
747 /* Get usage bits, if searching by them */
748 silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
751 silc_hash_table_list(find->constr, &htl);
752 while (silc_hash_table_get(&htl, &type, &ctx)) {
754 #if defined(SILC_DEBUG)
756 memset(tmp, 0, sizeof(tmp));
757 silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
758 ctx, tmp, sizeof(tmp) - 1);
759 SILC_LOG_DEBUG(("Finding key by %s", tmp));
760 #endif /* SILC_DEBUG */
762 /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
763 if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
766 /* Find entries by this search constraint */
767 if (!silc_skr_find_entry(skr, &status,
768 (SilcSKRFindType)SILC_32_TO_PTR(type),
769 ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
770 SILC_LOG_DEBUG(("Not found"));
772 silc_dlist_uninit(results);
778 /* For now, our logic rule is AND. All constraints must be found
779 to find the key. Later OR might be added also. */
780 if (!silc_skr_results_and(list, &status, &results)) {
781 SILC_LOG_DEBUG(("Not found"));
783 silc_dlist_uninit(results);
786 silc_dlist_uninit(list);
790 silc_dlist_uninit(list);
792 silc_hash_table_list_reset(&htl);
794 silc_mutex_unlock(skr->lock);
798 callback(skr, find, status, NULL, callback_context);
800 silc_dlist_start(results);
801 callback(skr, find, SILC_SKR_OK, results, callback_context);