f2317c9e13d34db64ed88f0d95be13b05763b8e9
[silc.git] / lib / silcskr / silcskr.c
1 /*
2
3   silcskr.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2006 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 #include "silc.h"
21 #include "silcskr.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Search constraints */
26 typedef enum {
27   SILC_SKR_FIND_PKCS_TYPE,
28   SILC_SKR_FIND_USERNAME,
29   SILC_SKR_FIND_HOST,
30   SILC_SKR_FIND_REALNAME,
31   SILC_SKR_FIND_EMAIL,
32   SILC_SKR_FIND_ORG,
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 */
37 } SilcSKRFindType;
38
39 /* Hash table key context */
40 typedef struct {
41   SilcSKRFindType type;         /* Type of key */
42   void *data;                   /* Hash table key */
43 } *SilcSKREntry, SilcSKREntryStruct;
44
45 /* Foreach user context when finding entries from hash table */
46 typedef struct {
47   SilcDList list;
48   void *key_context;
49   SilcSKRKeyUsage usage;
50 } SilcSKRFindForeach;
51
52 #if defined(SILC_DEBUG)
53 static const char *find_name[] = {
54   "PKCS TYPE",
55   "USERNAME",
56   "HOST",
57   "REALNAME",
58   "EMAIL",
59   "ORG",
60   "COUNTRY",
61   "PUBLIC KEY",
62   "CONTEXT",
63   "USAGE",
64   NULL
65 };
66 #endif /* SILC_DEBUG */
67
68 /************************ Static utility functions **************************/
69
70 #if defined(SILC_DEBUG)
71
72 /* Returns search constraint string */
73
74 static void silc_skr_type_string(SilcSKRFindType type, void *data,
75                                  char *retbuf, SilcUInt32 retbuf_size)
76 {
77   switch (type) {
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));
82     break;
83
84   case SILC_SKR_FIND_PUBLIC_KEY:
85     snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
86     break;
87
88   default:
89     snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
90              (char *)data);
91   }
92 }
93
94 #endif /* SILC_DEBUG */
95
96 /* Hash table destructor for search constraints */
97
98 static void silc_skr_find_destructor(void *key, void *context,
99                                      void *user_context)
100 {
101   SilcSKRFindType type = SILC_PTR_TO_32(key);
102
103   switch (type) {
104   case SILC_SKR_FIND_PKCS_TYPE:
105   case SILC_SKR_FIND_USAGE:
106   case SILC_SKR_FIND_CONTEXT:
107     break;
108
109   case SILC_SKR_FIND_PUBLIC_KEY:
110     silc_pkcs_public_key_free(context);
111     break;
112
113   default:
114     silc_free(context);
115   }
116 }
117
118 /* Hash table destructor for key entries */
119
120 static void silc_skr_destructor(void *key, void *context, void *user_context)
121 {
122   SilcSKREntry type = key;
123   SilcSKRKeyInternal entry = context;
124
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);
129   silc_free(type);
130
131   /* Destroy key */
132   entry->refcnt--;
133   if (entry->refcnt > 0)
134     return;
135
136   silc_pkcs_public_key_free(entry->key.key);
137   silc_free(entry);
138 }
139
140 /* Hash table hash function for key entries */
141
142 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
143 {
144   SilcSKREntry type = key;
145
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));
150     break;
151
152   case SILC_SKR_FIND_PUBLIC_KEY:
153     return type->type + silc_hash_public_key(type->data, user_context);
154     break;
155
156   default:
157     break;
158   }
159
160   return type->type + silc_hash_string(type->data, user_context);
161 }
162
163 /* Hash table comparison function for key entries */
164
165 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
166 {
167   SilcSKREntry type1 = key1;
168   SilcSKREntry type2 = key2;
169
170   if (type1->type != type2->type)
171     return FALSE;
172
173   switch (type1->type) {
174   case SILC_SKR_FIND_PKCS_TYPE:
175   case SILC_SKR_FIND_CONTEXT:
176     return type1->data == type2->data;
177     break;
178
179   case SILC_SKR_FIND_PUBLIC_KEY:
180     return silc_hash_public_key_compare(type1->data, type2->data,
181                                         user_context);
182     break;
183
184   default:
185     break;
186   }
187
188   return silc_utf8_strcasecmp((const char *)type1->data,
189                               (const char *)type2->data);
190 }
191
192 /* Foreach function for finding entries in the repository */
193
194 static void silc_skr_find_foreach(void *key, void *context,
195                                   void *user_context)
196 {
197   SilcSKRFindForeach *f = user_context;
198   SilcSKRKeyInternal k = context;
199
200   if (k) {
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)
205       return;
206
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)
209       return;
210
211     silc_dlist_add(f->list, k);
212   }
213 }
214
215 /* Finds entry from repository by search constraint type and data */
216
217 static SilcBool silc_skr_find_entry(SilcSKR skr,
218                                     SilcSKRStatus *status,
219                                     SilcSKRFindType type,
220                                     void *type_data,
221                                     SilcDList *results,
222                                     void *key_context,
223                                     SilcSKRKeyUsage usage)
224 {
225   SilcSKREntryStruct find;
226   SilcSKRFindForeach f;
227
228   f.list = silc_dlist_init();
229   if (!f.list) {
230     *status |= SILC_SKR_NO_MEMORY;
231     return FALSE;
232   }
233   f.key_context = key_context;
234   f.usage = usage;
235
236   find.type = type;
237   find.data = type_data;
238
239   silc_hash_table_find_foreach(skr->keys, (void *)&find,
240                                silc_skr_find_foreach, &f);
241
242   if (!silc_dlist_count(f.list)) {
243     *status |= SILC_SKR_NOT_FOUND;
244     silc_dlist_uninit(f.list);
245     return FALSE;
246   }
247
248   if (results)
249     *results = f.list;
250   else
251     silc_dlist_uninit(f.list);
252
253   return TRUE;
254 }
255
256 /* Add a key by search constraint type to repository */
257
258 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
259                                    void *type_data, SilcSKRKeyInternal key)
260 {
261   SilcSKREntry entry;
262
263   entry = silc_calloc(1, sizeof(*entry));
264   if (!entry)
265     return FALSE;
266
267   entry->type = type;
268   entry->data = type_data;
269
270   return silc_hash_table_add(skr->keys, entry, key);
271 }
272
273 /* Add SILC style public key to repository */
274
275 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
276                                        SilcPublicKey public_key,
277                                        SilcSKRKeyUsage usage,
278                                        void *key_context)
279 {
280   SilcSKRKeyInternal key;
281   SilcSKRStatus status = SILC_SKR_ERROR;
282   SilcPublicKeyIdentifier ident;
283   SilcSILCPublicKey silc_pubkey;
284
285   /* Get the SILC public key */
286   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
287   ident = &silc_pubkey->identifier;
288
289   SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
290
291   silc_mutex_lock(skr->lock);
292
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;
299   }
300
301   /* Allocate key entry */
302   key = silc_calloc(1, sizeof(*key));
303   if (!key) {
304     silc_mutex_unlock(skr->lock);
305     return status | SILC_SKR_NO_MEMORY;
306   }
307
308   key->key.usage = usage;
309   key->key.key = public_key;
310   key->key.key_context = key_context;
311
312   /* Add key specifics */
313
314   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
315                           public_key, key))
316     goto err;
317   key->refcnt++;
318
319   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
320                           SILC_32_TO_PTR(SILC_PKCS_SILC), key))
321     goto err;
322   key->refcnt++;
323
324   if (ident->username) {
325     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
326                             ident->username, key))
327       goto err;
328     key->refcnt++;
329   }
330
331   if (ident->host) {
332     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
333                             ident->host, key))
334       goto err;
335     key->refcnt++;
336   }
337
338   if (ident->realname) {
339     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
340                             ident->realname, key))
341       goto err;
342     key->refcnt++;
343   }
344
345   if (ident->email) {
346     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
347                             ident->email, key))
348       goto err;
349     key->refcnt++;
350   }
351
352   if (ident->org) {
353     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
354                             ident->org, key))
355       goto err;
356     key->refcnt++;
357   }
358
359   if (ident->country) {
360     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
361                             ident->country, key))
362       goto err;
363     key->refcnt++;
364   }
365
366   if (key_context) {
367     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
368                             key_context, key))
369       goto err;
370     key->refcnt++;
371   }
372
373   silc_mutex_unlock(skr->lock);
374
375   return SILC_SKR_OK;
376
377  err:
378   silc_mutex_unlock(skr->lock);
379   return status;
380 }
381
382 /* Add SILC style public key to repository, and only the public key, not
383    other details from the key. */
384
385 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
386                                               SilcPublicKey public_key,
387                                               SilcSKRKeyUsage usage,
388                                               void *key_context)
389 {
390   SilcSKRKeyInternal key;
391   SilcSKRStatus status = SILC_SKR_ERROR;
392
393   SILC_LOG_DEBUG(("Adding SILC public key"));
394
395   silc_mutex_lock(skr->lock);
396
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;
403   }
404
405   /* Allocate key entry */
406   key = silc_calloc(1, sizeof(*key));
407   if (!key) {
408     silc_mutex_unlock(skr->lock);
409     return status | SILC_SKR_NO_MEMORY;
410   }
411
412   key->key.usage = usage;
413   key->key.key = public_key;
414   key->key.key_context = key_context;
415
416   /* Add key specifics */
417
418   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
419                           public_key, key))
420     goto err;
421   key->refcnt++;
422
423   if (key_context) {
424     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
425                             key_context, key))
426       goto err;
427     key->refcnt++;
428   }
429
430   silc_mutex_unlock(skr->lock);
431
432   return SILC_SKR_OK;
433
434  err:
435   silc_mutex_unlock(skr->lock);
436   return status;
437 }
438
439 /* This performs AND operation.  Any entry already in `results' that is not
440    in `list' will be removed from `results'. */
441
442 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
443                                      SilcDList *results)
444 {
445   SilcSKRKeyInternal entry, r;
446
447   if (*results == NULL) {
448     *results = silc_dlist_init();
449     if (*results == NULL) {
450       *status |= SILC_SKR_NO_MEMORY;
451       return FALSE;
452     }
453   }
454
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);
460
461     return TRUE;
462   }
463
464   silc_dlist_start(*results);
465   while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
466
467     /* Check if this entry is in list  */
468     silc_dlist_start(list);
469     while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
470       if (r == entry)
471         break;
472     }
473     if (r != SILC_LIST_END)
474       continue;
475
476     /* Remove from results */
477     silc_dlist_del(*results, entry);
478   }
479
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;
484     return FALSE;
485   }
486
487   return TRUE;
488 }
489
490
491 /**************************** Key Repository API ****************************/
492
493 /* Allocate key repository */
494
495 SilcSKR silc_skr_alloc(SilcSchedule scheduler)
496 {
497   SilcSKR skr;
498
499   skr = silc_calloc(1, sizeof(*skr));
500   if (!skr)
501     return NULL;
502
503   if (!silc_skr_init(skr, scheduler)) {
504     silc_skr_free(skr);
505     return NULL;
506   }
507
508   return skr;
509 }
510
511 /* Free key repository */
512
513 void silc_skr_free(SilcSKR skr)
514 {
515   silc_skr_uninit(skr);
516   silc_free(skr);
517 }
518
519 /* Initializes key repository */
520
521 SilcBool silc_skr_init(SilcSKR skr, SilcSchedule scheduler)
522 {
523   if (!scheduler)
524     return FALSE;
525
526   skr->scheduler = scheduler;
527
528   if (!silc_mutex_alloc(&skr->lock))
529     return FALSE;
530
531   skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
532                                     silc_skr_compare, NULL,
533                                     silc_skr_destructor, NULL, TRUE);
534   if (!skr->keys)
535     return FALSE;
536
537   return TRUE;
538 }
539
540 /* Uninitializes key repository */
541
542 void silc_skr_uninit(SilcSKR skr)
543 {
544   if (skr->keys)
545     silc_hash_table_free(skr->keys);
546   silc_mutex_free(skr->lock);
547 }
548
549 /* Adds public key to key repository */
550
551 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
552                                       SilcPublicKey public_key,
553                                       SilcSKRKeyUsage usage,
554                                       void *key_context)
555 {
556   SilcPKCSType type;
557
558   if (!public_key)
559     return SILC_SKR_ERROR;
560
561   type = silc_pkcs_get_type(public_key);
562
563   SILC_LOG_DEBUG(("Adding public key to repository"));
564
565   switch (type) {
566
567   case SILC_PKCS_SILC:
568     return silc_skr_add_silc(skr, public_key, usage, key_context);
569     break;
570
571   default:
572     break;
573   }
574
575   return SILC_SKR_ERROR;
576 }
577
578 /* Adds public key to repository. */
579
580 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
581                                              SilcPublicKey public_key,
582                                              SilcSKRKeyUsage usage,
583                                              void *key_context)
584 {
585   SilcPKCSType type;
586
587   if (!public_key)
588     return SILC_SKR_ERROR;
589
590   type = silc_pkcs_get_type(public_key);
591
592   SILC_LOG_DEBUG(("Adding public key to repository"));
593
594   switch (type) {
595
596   case SILC_PKCS_SILC:
597     return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
598     break;
599
600   default:
601     break;
602   }
603
604   return SILC_SKR_ERROR;
605 }
606
607
608 /************************** Search Constraints API **************************/
609
610 /* Allocate search constraints */
611
612 SilcSKRFind silc_skr_find_alloc(void)
613 {
614   SilcSKRFind find;
615
616   find = silc_calloc(1, sizeof(*find));
617   if (!find)
618     return NULL;
619
620   find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
621                                        silc_skr_find_destructor, NULL, TRUE);
622   if (!find->constr) {
623     silc_skr_find_free(find);
624     return NULL;
625   }
626
627   return find;
628 }
629
630 /* Free search constraints */
631
632 void silc_skr_find_free(SilcSKRFind find)
633 {
634   if (find->constr)
635     silc_hash_table_free(find->constr);
636   silc_free(find);
637 }
638
639 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
640 {
641   return silc_hash_table_add(find->constr,
642                              SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
643                              SILC_32_TO_PTR(type));
644 }
645
646 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
647 {
648   void *c = silc_memdup(username, strlen(username));
649   if (!c)
650     return FALSE;
651   return silc_hash_table_add(find->constr,
652                              SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
653 }
654
655 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
656 {
657   void *c = silc_memdup(host, strlen(host));
658   if (!c)
659     return FALSE;
660   return silc_hash_table_add(find->constr,
661                              SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
662 }
663
664 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
665 {
666   void *c = silc_memdup(realname, strlen(realname));
667   if (!c)
668     return FALSE;
669   return silc_hash_table_add(find->constr,
670                              SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
671 }
672
673 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
674 {
675   void *c = silc_memdup(email, strlen(email));
676   if (!c)
677     return FALSE;
678   return silc_hash_table_add(find->constr,
679                              SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
680 }
681
682 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
683 {
684   void *c = silc_memdup(org, strlen(org));
685   if (!c)
686     return FALSE;
687   return silc_hash_table_add(find->constr,
688                              SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
689 }
690
691 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
692 {
693   void *c = silc_memdup(country, strlen(country));
694   if (!c)
695     return FALSE;
696   return silc_hash_table_add(find->constr,
697                              SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
698 }
699
700 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
701                                       SilcPublicKey public_key)
702 {
703   SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
704   if (!pk)
705     return FALSE;
706   return silc_hash_table_add(find->constr,
707                              SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
708 }
709
710 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
711 {
712   return silc_hash_table_add(find->constr,
713                              SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
714 }
715
716 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
717 {
718   if (!usage)
719     return TRUE;
720   return silc_hash_table_add(find->constr,
721                              SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
722                              SILC_32_TO_PTR(usage));
723 }
724
725 /******************************** Search API ********************************/
726
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 */
730
731 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
732                                  SilcSKRFindCallback callback,
733                                  void *callback_context)
734 {
735   SilcSKRStatus status = SILC_SKR_ERROR;
736   SilcHashTableList htl;
737   SilcDList list, results = NULL;
738   void *type, *ctx, *usage = NULL;
739
740   SILC_LOG_DEBUG(("Finding key from repository"));
741
742   if (!find || !callback)
743     return NULL;
744
745   silc_mutex_lock(skr->lock);
746
747   /* Get usage bits, if searching by them */
748   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
749                        NULL, &usage);
750
751   silc_hash_table_list(find->constr, &htl);
752   while (silc_hash_table_get(&htl, &type, &ctx)) {
753
754 #if defined(SILC_DEBUG)
755     char tmp[256];
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 */
761
762     /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
763     if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
764       continue;
765
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"));
771       if (results) {
772         silc_dlist_uninit(results);
773         results = NULL;
774       }
775       break;
776     }
777
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"));
782       if (results) {
783         silc_dlist_uninit(results);
784         results = NULL;
785       }
786       silc_dlist_uninit(list);
787       break;
788     }
789
790     silc_dlist_uninit(list);
791   }
792   silc_hash_table_list_reset(&htl);
793
794   silc_mutex_unlock(skr->lock);
795
796   /* Return results */
797   if (!results) {
798     callback(skr, find, status, NULL, callback_context);
799   } else {
800     silc_dlist_start(results);
801     callback(skr, find, SILC_SKR_OK, results, callback_context);
802   }
803
804   return NULL;
805 }