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