1 /*
2 
3   silcskr.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2005 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
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 
silc_skr_type_string(SilcSKRFindType type,void * data,char * retbuf,SilcUInt32 retbuf_size)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     silc_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   case SILC_SKR_FIND_CONTEXT:
86     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
87     break;
88 
89   default:
90     silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
91 		  (char *)data);
92   }
93 }
94 #endif /* SILC_DEBUG */
95 
96 /* Hash table destructor for search constraints */
97 
silc_skr_find_destructor(void * key,void * context,void * user_context)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   SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
103 
104   switch (type) {
105   case SILC_SKR_FIND_PKCS_TYPE:
106   case SILC_SKR_FIND_USAGE:
107   case SILC_SKR_FIND_CONTEXT:
108     break;
109 
110   case SILC_SKR_FIND_PUBLIC_KEY:
111     silc_pkcs_public_key_free(context);
112     break;
113 
114   default:
115     /* In SILC Public key all entries are referenced from the public key
116        so don't free them.  This test is valid only when removing key
117        from the repository. */
118     if (pkcs_type == SILC_PKCS_SILC)
119       break;
120 
121     silc_free(context);
122   }
123 }
124 
125 /* Hash table destructor for key entries */
126 
silc_skr_destructor(void * key,void * context,void * user_context)127 static void silc_skr_destructor(void *key, void *context, void *user_context)
128 {
129   SilcSKREntry type = key;
130   SilcSKRKeyInternal entry = context;
131   SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
132 
133   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
134      shares same context with the key entry. */
135   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
136     silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
137 			     SILC_32_TO_PTR(pkcs_type));
138   silc_free(type);
139 
140   /* Destroy key */
141   entry->refcnt--;
142   if (entry->refcnt > 0)
143     return;
144 
145   SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
146 
147   silc_pkcs_public_key_free(entry->key.key);
148   silc_free(entry);
149 }
150 
151 /* Hash table hash function for key entries */
152 
silc_skr_hash(void * key,void * user_context)153 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
154 {
155   SilcSKREntry type = key;
156 
157   switch (type->type) {
158   case SILC_SKR_FIND_PKCS_TYPE:
159   case SILC_SKR_FIND_CONTEXT:
160     return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
161     break;
162 
163   case SILC_SKR_FIND_PUBLIC_KEY:
164     return type->type + silc_hash_public_key(type->data, user_context);
165     break;
166 
167   default:
168     break;
169   }
170 
171   return type->type + silc_hash_string(type->data, user_context);
172 }
173 
174 /* Hash table comparison function for key entries */
175 
silc_skr_compare(void * key1,void * key2,void * user_context)176 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
177 {
178   SilcSKREntry type1 = key1;
179   SilcSKREntry type2 = key2;
180 
181   if (type1->type != type2->type)
182     return FALSE;
183 
184   switch (type1->type) {
185   case SILC_SKR_FIND_PKCS_TYPE:
186   case SILC_SKR_FIND_CONTEXT:
187     return type1->data == type2->data;
188     break;
189 
190   case SILC_SKR_FIND_PUBLIC_KEY:
191     return silc_hash_public_key_compare(type1->data, type2->data,
192 					user_context);
193     break;
194 
195   default:
196     break;
197   }
198 
199   return silc_utf8_strcasecmp((const char *)type1->data,
200 			      (const char *)type2->data);
201 }
202 
203 /* Foreach function for finding entries in the repository */
204 
silc_skr_find_foreach(void * key,void * context,void * user_context)205 static void silc_skr_find_foreach(void *key, void *context,
206 				  void *user_context)
207 {
208   SilcSKRFindForeach *f = user_context;
209   SilcSKRKeyInternal k = context;
210 
211   if (k) {
212     /* If key context is present, it must match the context in the key.
213        This is used only internally when adding keys, to check if the key
214        is added with same context. */
215     if (f->key_context && f->key_context != k->key.key_context)
216       return;
217 
218     /* Check for usage bits.  At least one usage bit must be set. */
219     if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
220       return;
221 
222     silc_dlist_add(f->list, k);
223   }
224 }
225 
226 /* Finds entry from repository by search constraint type and data */
227 
silc_skr_find_entry(SilcSKR skr,SilcSKRStatus * status,SilcSKRFindType type,void * type_data,SilcDList * results,void * key_context,SilcSKRKeyUsage usage)228 static SilcBool silc_skr_find_entry(SilcSKR skr,
229 				    SilcSKRStatus *status,
230 				    SilcSKRFindType type,
231 				    void *type_data,
232 				    SilcDList *results,
233 				    void *key_context,
234 				    SilcSKRKeyUsage usage)
235 {
236   SilcSKREntryStruct find;
237   SilcSKRFindForeach f;
238 
239   f.list = silc_dlist_init();
240   if (!f.list) {
241     *status |= SILC_SKR_NO_MEMORY;
242     return FALSE;
243   }
244   f.key_context = key_context;
245   f.usage = usage;
246 
247   find.type = type;
248   find.data = type_data;
249 
250   silc_hash_table_find_foreach(skr->keys, (void *)&find,
251 			       silc_skr_find_foreach, &f);
252 
253   if (!silc_dlist_count(f.list)) {
254     *status |= SILC_SKR_NOT_FOUND;
255     silc_dlist_uninit(f.list);
256     return FALSE;
257   }
258 
259   if (results)
260     *results = f.list;
261   else
262     silc_dlist_uninit(f.list);
263 
264   return TRUE;
265 }
266 
267 /* Add a key by search constraint type to repository */
268 
silc_skr_add_entry(SilcSKR skr,SilcSKRFindType type,void * type_data,SilcSKRKeyInternal key)269 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
270 				   void *type_data, SilcSKRKeyInternal key)
271 {
272   SilcSKREntry entry;
273 
274 #if defined(SILC_DEBUG)
275   char tmp[256];
276   memset(tmp, 0, sizeof(tmp));
277   silc_skr_type_string(type, type_data, tmp, sizeof(tmp) - 1);
278   SILC_LOG_DEBUG(("Search constraint %s", tmp));
279 #endif /* SILC_DEBUG */
280 
281   entry = silc_calloc(1, sizeof(*entry));
282   if (!entry)
283     return FALSE;
284 
285   entry->type = type;
286   entry->data = type_data;
287 
288   return silc_hash_table_add(skr->keys, entry, key);
289 }
290 
291 /* Delete a key by search constraint type from repository */
292 
silc_skr_del_entry(SilcSKR skr,SilcSKRFindType type,void * type_data,SilcSKRKeyInternal key)293 static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
294 				   void *type_data, SilcSKRKeyInternal key)
295 {
296   SilcSKREntryStruct entry;
297 
298   if (!type_data)
299     return FALSE;
300 
301   entry.type = type;
302   entry.data = type_data;
303 
304   return silc_hash_table_del_by_context(skr->keys, &entry, key);
305 }
306 
307 /* This performs AND operation.  Any entry already in `results' that is not
308    in `list' will be removed from `results'. */
309 
silc_skr_results_and(SilcDList list,SilcSKRStatus * status,SilcDList * results)310 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
311 				     SilcDList *results)
312 {
313   SilcSKRKeyInternal entry, r;
314 
315   if (*results == NULL) {
316     *results = silc_dlist_init();
317     if (*results == NULL) {
318       *status |= SILC_SKR_NO_MEMORY;
319       return FALSE;
320     }
321   }
322 
323   /* If results is empty, just add all entries from list to results */
324   if (!silc_dlist_count(*results)) {
325     silc_dlist_start(list);
326     while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
327       silc_dlist_add(*results, entry);
328 
329     return TRUE;
330   }
331 
332   silc_dlist_start(*results);
333   while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
334 
335     /* Check if this entry is in list  */
336     silc_dlist_start(list);
337     while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
338       if (r == entry)
339 	break;
340     }
341     if (r != SILC_LIST_END)
342       continue;
343 
344     /* Remove from results */
345     silc_dlist_del(*results, entry);
346   }
347 
348   /* If results became empty, we did not find any key */
349   if (!silc_dlist_count(*results)) {
350     SILC_LOG_DEBUG(("Not all search constraints found"));
351     *status |= SILC_SKR_NOT_FOUND;
352     return FALSE;
353   }
354 
355   return TRUE;
356 }
357 
358 
359 /***************************** SILC Public Key ******************************/
360 
361 /* Add SILC style public key to repository */
362 
silc_skr_add_silc(SilcSKR skr,SilcPublicKey public_key,SilcSKRKeyUsage usage,void * key_context,SilcSKRKey * return_key)363 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
364 				       SilcPublicKey public_key,
365 				       SilcSKRKeyUsage usage,
366 				       void *key_context,
367 				       SilcSKRKey *return_key)
368 {
369   SilcSKRKeyInternal key;
370   SilcSKRStatus status = SILC_SKR_ERROR;
371   SilcPublicKeyIdentifier ident;
372   SilcSILCPublicKey silc_pubkey;
373 #if defined(SILC_DEBUG)
374   char tmp[256];
375 #endif /* SILC_DEBUG */
376 
377   /* Get the SILC public key */
378   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
379   ident = &silc_pubkey->identifier;
380 
381   SILC_LOG_DEBUG(("Adding SILC public key %p [%s], context %p",
382 		  public_key, ident->username, key_context));
383 
384   silc_mutex_lock(skr->lock);
385 
386   /* Check that this key hasn't been added already */
387   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
388 			  public_key, NULL, key_context, 0)) {
389     silc_mutex_unlock(skr->lock);
390     SILC_LOG_DEBUG(("Key already added"));
391     return status | SILC_SKR_ALREADY_EXIST;
392   }
393 
394   /* Allocate key entry */
395   key = silc_calloc(1, sizeof(*key));
396   if (!key) {
397     silc_mutex_unlock(skr->lock);
398     return status | SILC_SKR_NO_MEMORY;
399   }
400 
401   key->key.usage = usage;
402   key->key.key = public_key;
403   key->key.key_context = key_context;
404 
405 #if defined(SILC_DEBUG)
406   silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
407 		       tmp, sizeof(tmp) - 1);
408   SILC_LOG_DEBUG((" Search constraint %s", tmp));
409 #endif /* SILC_DEBUG */
410 
411   /* Add key specifics */
412 
413   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
414 			  public_key, key))
415     goto err;
416   key->refcnt++;
417 
418   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
419 			  SILC_32_TO_PTR(SILC_PKCS_SILC), key))
420     goto err;
421   key->refcnt++;
422 
423   if (ident->username) {
424     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
425 			    ident->username, key))
426       goto err;
427     key->refcnt++;
428   }
429 
430   if (ident->host) {
431     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
432 			    ident->host, key))
433       goto err;
434     key->refcnt++;
435   }
436 
437   if (ident->realname) {
438     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
439 			    ident->realname, key))
440       goto err;
441     key->refcnt++;
442   }
443 
444   if (ident->email) {
445     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
446 			    ident->email, key))
447       goto err;
448     key->refcnt++;
449   }
450 
451   if (ident->org) {
452     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
453 			    ident->org, key))
454       goto err;
455     key->refcnt++;
456   }
457 
458   if (ident->country) {
459     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
460 			    ident->country, key))
461       goto err;
462     key->refcnt++;
463   }
464 
465   if (key_context) {
466     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
467 			    key_context, key))
468       goto err;
469     key->refcnt++;
470   }
471 
472   silc_mutex_unlock(skr->lock);
473 
474   if (return_key)
475     *return_key = (SilcSKRKey)key;
476 
477   return SILC_SKR_OK;
478 
479  err:
480   silc_mutex_unlock(skr->lock);
481   return status;
482 }
483 
484 /* Add SILC style public key to repository, and only the public key, not
485    other details from the key. */
486 
silc_skr_add_silc_simple(SilcSKR skr,SilcPublicKey public_key,SilcSKRKeyUsage usage,void * key_context,SilcSKRKey * return_key)487 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
488 					      SilcPublicKey public_key,
489 					      SilcSKRKeyUsage usage,
490 					      void *key_context,
491 					      SilcSKRKey *return_key)
492 {
493   SilcSKRKeyInternal key;
494   SilcSKRStatus status = SILC_SKR_ERROR;
495 #if defined(SILC_DEBUG)
496   char tmp[256];
497 #endif /* SILC_DEBUG */
498 
499   SILC_LOG_DEBUG(("Adding SILC public key"));
500 
501   silc_mutex_lock(skr->lock);
502 
503   /* Check that this key hasn't been added already */
504   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
505 			  public_key, NULL, key_context, 0)) {
506     silc_mutex_unlock(skr->lock);
507     SILC_LOG_DEBUG(("Key already added"));
508     return status | SILC_SKR_ALREADY_EXIST;
509   }
510 
511   /* Allocate key entry */
512   key = silc_calloc(1, sizeof(*key));
513   if (!key) {
514     silc_mutex_unlock(skr->lock);
515     return status | SILC_SKR_NO_MEMORY;
516   }
517 
518   key->key.usage = usage;
519   key->key.key = public_key;
520   key->key.key_context = key_context;
521 
522 #if defined(SILC_DEBUG)
523   silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
524 		       tmp, sizeof(tmp) - 1);
525   SILC_LOG_DEBUG(("Search cons %s", tmp));
526 #endif /* SILC_DEBUG */
527 
528   /* Add key specifics */
529 
530   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
531 			  public_key, key))
532     goto err;
533   key->refcnt++;
534 
535   if (key_context) {
536     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
537 			    key_context, key))
538       goto err;
539     key->refcnt++;
540   }
541 
542   silc_mutex_unlock(skr->lock);
543 
544   if (return_key)
545     *return_key = (SilcSKRKey)key;
546 
547   return SILC_SKR_OK;
548 
549  err:
550   silc_mutex_unlock(skr->lock);
551   return status;
552 }
553 
554 /* Deletes SILC public key from repository */
555 
silc_skr_del_silc_public_key(SilcSKR skr,SilcPublicKey public_key,void * key_context)556 static SilcSKRStatus silc_skr_del_silc_public_key(SilcSKR skr,
557 						  SilcPublicKey public_key,
558 						  void *key_context)
559 {
560   SilcSKRStatus status = SILC_SKR_ERROR;
561   SilcPublicKeyIdentifier ident;
562   SilcSILCPublicKey silc_pubkey;
563   SilcSKRKeyInternal key;
564   SilcDList entry;
565 
566   /* Get the SILC public key */
567   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
568   ident = &silc_pubkey->identifier;
569 
570   SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
571 
572   silc_mutex_lock(skr->lock);
573 
574   /* Check that this key exists */
575   if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
576 			   public_key, &entry, key_context, 0)) {
577     silc_mutex_unlock(skr->lock);
578     SILC_LOG_DEBUG(("Key does not exist"));
579     return status | SILC_SKR_NOT_FOUND;
580   }
581 
582   silc_dlist_start(entry);
583   key = silc_dlist_get(entry);
584   silc_dlist_uninit(entry);
585 
586   silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
587   silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
588 		     SILC_32_TO_PTR(SILC_PKCS_SILC), key);
589   silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
590   silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
591   silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
592   silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
593   silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
594   silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
595   silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
596 
597   silc_mutex_unlock(skr->lock);
598 
599   return SILC_SKR_OK;
600 }
601 
602 
603 /**************************** Key Repository API ****************************/
604 
605 /* Allocate key repository */
606 
silc_skr_alloc(void)607 SilcSKR silc_skr_alloc(void)
608 {
609   SilcSKR skr;
610 
611   skr = silc_calloc(1, sizeof(*skr));
612   if (!skr)
613     return NULL;
614 
615   if (!silc_skr_init(skr)) {
616     silc_skr_free(skr);
617     return NULL;
618   }
619 
620   return skr;
621 }
622 
623 /* Free key repository */
624 
silc_skr_free(SilcSKR skr)625 void silc_skr_free(SilcSKR skr)
626 {
627   silc_skr_uninit(skr);
628   silc_free(skr);
629 }
630 
631 /* Initializes key repository */
632 
silc_skr_init(SilcSKR skr)633 SilcBool silc_skr_init(SilcSKR skr)
634 {
635   silc_mutex_alloc(&skr->lock);
636 
637   skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
638 				    silc_skr_compare, NULL,
639 				    silc_skr_destructor, NULL, TRUE);
640   if (!skr->keys)
641     return FALSE;
642 
643   return TRUE;
644 }
645 
646 /* Uninitializes key repository */
647 
silc_skr_uninit(SilcSKR skr)648 void silc_skr_uninit(SilcSKR skr)
649 {
650   if (skr->keys)
651     silc_hash_table_free(skr->keys);
652   silc_mutex_free(skr->lock);
653 }
654 
655 /* Adds public key to key repository */
656 
silc_skr_add_public_key(SilcSKR skr,SilcPublicKey public_key,SilcSKRKeyUsage usage,void * key_context,SilcSKRKey * return_key)657 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
658 				      SilcPublicKey public_key,
659 				      SilcSKRKeyUsage usage,
660 				      void *key_context,
661 				      SilcSKRKey *return_key)
662 {
663   SilcPKCSType type;
664 
665   if (!public_key)
666     return SILC_SKR_ERROR;
667 
668   type = silc_pkcs_get_type(public_key);
669 
670   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
671 
672   switch (type) {
673 
674   case SILC_PKCS_SILC:
675     return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
676     break;
677 
678   default:
679     break;
680   }
681 
682   return SILC_SKR_ERROR;
683 }
684 
685 /* Adds public key to repository. */
686 
silc_skr_add_public_key_simple(SilcSKR skr,SilcPublicKey public_key,SilcSKRKeyUsage usage,void * key_context,SilcSKRKey * return_key)687 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
688 					     SilcPublicKey public_key,
689 					     SilcSKRKeyUsage usage,
690 					     void *key_context,
691 					     SilcSKRKey *return_key)
692 {
693   SilcPKCSType type;
694 
695   if (!public_key)
696     return SILC_SKR_ERROR;
697 
698   type = silc_pkcs_get_type(public_key);
699 
700   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
701 
702   switch (type) {
703 
704   case SILC_PKCS_SILC:
705     return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
706 				    return_key);
707     break;
708 
709   default:
710     break;
711   }
712 
713   return SILC_SKR_ERROR;
714 }
715 
716 /* Remove key from repository */
717 
silc_skr_del_public_key(SilcSKR skr,SilcPublicKey public_key,void * key_context)718 SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
719 				      SilcPublicKey public_key,
720 				      void *key_context)
721 {
722   SilcPKCSType type;
723 
724   if (!public_key)
725     return SILC_SKR_ERROR;
726 
727   type = silc_pkcs_get_type(public_key);
728 
729   SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
730 
731   switch (type) {
732 
733   case SILC_PKCS_SILC:
734     return silc_skr_del_silc_public_key(skr, public_key, key_context);
735     break;
736 
737   default:
738     break;
739   }
740 
741   return SILC_SKR_ERROR;
742 }
743 
744 /* Reference key */
745 
silc_skr_ref_public_key(SilcSKR skr,SilcSKRKey key)746 void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
747 {
748   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
749 
750   silc_mutex_lock(skr->lock);
751   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k, k->refcnt, k->refcnt + 1));
752   k->refcnt++;
753   silc_mutex_unlock(skr->lock);
754 }
755 
756 /* Release key reference. */
757 
silc_skr_unref_public_key(SilcSKR skr,SilcSKRKey key)758 void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
759 {
760   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
761 
762   silc_mutex_lock(skr->lock);
763 
764   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k, k->refcnt, k->refcnt - 1));
765   k->refcnt--;
766 
767   if (k->refcnt == 0) {
768     /* If reference is zero, the key has been removed from the repository
769        already.  Just destroy the public key. */
770     silc_pkcs_public_key_free(key->key);
771     silc_free(key);
772   }
773 
774   silc_mutex_unlock(skr->lock);
775 }
776 
777 
778 /************************** Search Constraints API **************************/
779 
780 /* Allocate search constraints */
781 
silc_skr_find_alloc(void)782 SilcSKRFind silc_skr_find_alloc(void)
783 {
784   SilcSKRFind find;
785 
786   find = silc_calloc(1, sizeof(*find));
787   if (!find)
788     return NULL;
789 
790   find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
791 				       silc_skr_find_destructor, NULL, TRUE);
792   if (!find->constr) {
793     silc_skr_find_free(find);
794     return NULL;
795   }
796 
797   return find;
798 }
799 
800 /* Free search constraints */
801 
silc_skr_find_free(SilcSKRFind find)802 void silc_skr_find_free(SilcSKRFind find)
803 {
804   if (find->constr)
805     silc_hash_table_free(find->constr);
806   silc_free(find);
807 }
808 
silc_skr_find_set_pkcs_type(SilcSKRFind find,SilcPKCSType type)809 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
810 {
811   return silc_hash_table_add(find->constr,
812 			     SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
813 			     SILC_32_TO_PTR(type));
814 }
815 
silc_skr_find_set_username(SilcSKRFind find,const char * username)816 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
817 {
818   void *c = silc_memdup(username, strlen(username));
819   if (!c)
820     return FALSE;
821   return silc_hash_table_add(find->constr,
822 			     SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
823 }
824 
silc_skr_find_set_host(SilcSKRFind find,const char * host)825 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
826 {
827   void *c = silc_memdup(host, strlen(host));
828   if (!c)
829     return FALSE;
830   return silc_hash_table_add(find->constr,
831 			     SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
832 }
833 
silc_skr_find_set_realname(SilcSKRFind find,const char * realname)834 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
835 {
836   void *c = silc_memdup(realname, strlen(realname));
837   if (!c)
838     return FALSE;
839   return silc_hash_table_add(find->constr,
840 			     SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
841 }
842 
silc_skr_find_set_email(SilcSKRFind find,const char * email)843 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
844 {
845   void *c = silc_memdup(email, strlen(email));
846   if (!c)
847     return FALSE;
848   return silc_hash_table_add(find->constr,
849 			     SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
850 }
851 
silc_skr_find_set_org(SilcSKRFind find,const char * org)852 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
853 {
854   void *c = silc_memdup(org, strlen(org));
855   if (!c)
856     return FALSE;
857   return silc_hash_table_add(find->constr,
858 			     SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
859 }
860 
silc_skr_find_set_country(SilcSKRFind find,const char * country)861 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
862 {
863   void *c = silc_memdup(country, strlen(country));
864   if (!c)
865     return FALSE;
866   return silc_hash_table_add(find->constr,
867 			     SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
868 }
869 
silc_skr_find_set_public_key(SilcSKRFind find,SilcPublicKey public_key)870 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
871 				      SilcPublicKey public_key)
872 {
873   SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
874   if (!pk)
875     return FALSE;
876   return silc_hash_table_add(find->constr,
877 			     SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
878 }
879 
silc_skr_find_set_context(SilcSKRFind find,void * context)880 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
881 {
882   if (!context)
883     return TRUE;
884   return silc_hash_table_add(find->constr,
885 			     SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
886 }
887 
silc_skr_find_set_usage(SilcSKRFind find,SilcSKRKeyUsage usage)888 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
889 {
890   if (!usage)
891     return TRUE;
892   return silc_hash_table_add(find->constr,
893 			     SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
894 			     SILC_32_TO_PTR(usage));
895 }
896 
897 /******************************** Search API ********************************/
898 
899 /* Finds key(s) by the set search constraints.  The callback will be called
900    once keys has been found. */
901 /* This is now synchronous function but may later change async */
902 
silc_skr_find(SilcSKR skr,SilcSchedule schedule,SilcSKRFind find,SilcSKRFindCallback callback,void * callback_context)903 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
904 				 SilcSKRFind find,
905 				 SilcSKRFindCallback callback,
906 				 void *callback_context)
907 {
908   SilcSKRStatus status = SILC_SKR_ERROR;
909   SilcHashTableList htl;
910   SilcDList list, results = NULL;
911   void *type, *ctx, *usage = NULL;
912 #if defined(SILC_DEBUG)
913   char tmp[256];
914 #endif /* SILC_DEBUG */
915 
916   SILC_LOG_DEBUG(("Finding key from repository"));
917 
918   if (!find || !callback)
919     return NULL;
920 
921   silc_mutex_lock(skr->lock);
922 
923   /* Get usage bits, if searching by them */
924   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
925 		       NULL, &usage);
926 
927 #if defined(SILC_DEBUG)
928   if (usage) {
929     memset(tmp, 0, sizeof(tmp));
930     silc_skr_type_string(SILC_SKR_FIND_USAGE, usage, tmp, sizeof(tmp) - 1);
931     SILC_LOG_DEBUG(("Finding key by %s", tmp));
932   }
933 #endif /* SILC_DEBUG */
934 
935   silc_hash_table_list(find->constr, &htl);
936   while (silc_hash_table_get(&htl, &type, &ctx)) {
937 
938     /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
939     if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
940       continue;
941 
942 #if defined(SILC_DEBUG)
943     memset(tmp, 0, sizeof(tmp));
944     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
945 			 ctx, tmp, sizeof(tmp) - 1);
946     SILC_LOG_DEBUG(("Finding key by %s", tmp));
947 #endif /* SILC_DEBUG */
948 
949     /* Find entries by this search constraint */
950     if (!silc_skr_find_entry(skr, &status,
951 			     (SilcSKRFindType)SILC_32_TO_PTR(type),
952 			     ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
953       SILC_LOG_DEBUG(("Not found"));
954       if (results) {
955 	silc_dlist_uninit(results);
956 	results = NULL;
957       }
958       break;
959     }
960 
961     /* For now, our logic rule is AND.  All constraints must be found
962        to find the key.  Later OR might be added also. */
963     if (!silc_skr_results_and(list, &status, &results)) {
964       SILC_LOG_DEBUG(("Not found"));
965       if (results) {
966 	silc_dlist_uninit(results);
967 	results = NULL;
968       }
969       silc_dlist_uninit(list);
970       break;
971     }
972 
973     silc_dlist_uninit(list);
974   }
975   silc_hash_table_list_reset(&htl);
976 
977   silc_mutex_unlock(skr->lock);
978 
979   /* Return results */
980   if (!results) {
981     callback(skr, find, status, NULL, callback_context);
982   } else {
983     silc_dlist_start(results);
984     callback(skr, find, SILC_SKR_OK, results, callback_context);
985   }
986 
987   return NULL;
988 }
989