Added SSH public key support to SKR
[crypto.git] / lib / silcskr / silcskr.c
1 /*
2
3   silcskr.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2008 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 "silccrypto.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     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
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 and SSH2 keys 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 || pkcs_type == SILC_PKCS_SSH2)
119       break;
120
121     silc_free(context);
122   }
123 }
124
125 /* Hash table destructor for key entries */
126
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
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_case(type->data, user_context);
172 }
173
174 /* Hash table comparison function for key entries */
175
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
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
228 static SilcBool silc_skr_find_entry(SilcSKR skr,
229                                     SilcResult *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
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
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
310 static SilcBool silc_skr_results_and(SilcDList list, SilcResult *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
363 static SilcResult 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   SilcResult status = SILC_ERR;
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_public_key_get_pkcs(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 SILC_ERR_ALREADY_EXISTS;
392   }
393
394   /* Allocate key entry */
395   key = silc_calloc(1, sizeof(*key));
396   if (!key) {
397     silc_mutex_unlock(skr->lock);
398     return silc_errno;
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_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
487 static SilcResult 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   SilcResult status = SILC_ERR;
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 SILC_ERR_ALREADY_EXISTS;
509   }
510
511   /* Allocate key entry */
512   key = silc_calloc(1, sizeof(*key));
513   if (!key) {
514     silc_mutex_unlock(skr->lock);
515     return silc_errno;
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_OK;
548
549  err:
550   silc_mutex_unlock(skr->lock);
551   return status;
552 }
553
554 /* Deletes SILC public key from repository */
555
556 static SilcResult silc_skr_del_silc_public_key(SilcSKR skr,
557                                                SilcPublicKey public_key,
558                                                void *key_context)
559 {
560   SilcResult status = SILC_ERR;
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_public_key_get_pkcs(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 SILC_ERR_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_OK;
600 }
601
602
603 /***************************** SSH2 Public Key ******************************/
604
605 /* Add SSH2 style public key to repository */
606
607 static SilcResult silc_skr_add_ssh(SilcSKR skr,
608                                    SilcPublicKey public_key,
609                                    SilcSKRKeyUsage usage,
610                                    void *key_context,
611                                    SilcSKRKey *return_key)
612 {
613   SilcSKRKeyInternal key;
614   SilcResult status = SILC_ERR;
615   SilcSshPublicKey ssh_pubkey;
616   const char *subject;
617 #if defined(SILC_DEBUG)
618   char tmp[256];
619 #endif /* SILC_DEBUG */
620
621   /* Get the SSH public key */
622   ssh_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SSH2, public_key);
623
624   /* Get subject */
625   subject = silc_ssh_public_key_get_field(ssh_pubkey, "Subject");
626
627   SILC_LOG_DEBUG(("Adding SSH public key %p [%s], context %p", public_key,
628                   subject ? subject : "none", key_context));
629
630   silc_mutex_lock(skr->lock);
631
632   /* Check that this key hasn't been added already */
633   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
634                           public_key, NULL, key_context, 0)) {
635     silc_mutex_unlock(skr->lock);
636     SILC_LOG_DEBUG(("Key already added"));
637     return SILC_ERR_ALREADY_EXISTS;
638   }
639
640   /* Allocate key entry */
641   key = silc_calloc(1, sizeof(*key));
642   if (!key) {
643     silc_mutex_unlock(skr->lock);
644     return silc_errno;
645   }
646
647   key->key.usage = usage;
648   key->key.key = public_key;
649   key->key.key_context = key_context;
650
651 #if defined(SILC_DEBUG)
652   silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
653                        tmp, sizeof(tmp) - 1);
654   SILC_LOG_DEBUG((" Search constraint %s", tmp));
655 #endif /* SILC_DEBUG */
656
657   /* Add key specifics */
658
659   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
660                           public_key, key))
661     goto err;
662   key->refcnt++;
663
664   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
665                           SILC_32_TO_PTR(SILC_PKCS_SSH2), key))
666     goto err;
667   key->refcnt++;
668
669   if (subject) {
670     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
671                             (void *)subject, key))
672       goto err;
673     key->refcnt++;
674   }
675
676   if (key_context) {
677     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
678                             key_context, key))
679       goto err;
680     key->refcnt++;
681   }
682
683   silc_mutex_unlock(skr->lock);
684
685   if (return_key)
686     *return_key = (SilcSKRKey)key;
687
688   return SILC_OK;
689
690  err:
691   silc_mutex_unlock(skr->lock);
692   return status;
693 }
694
695 /* Add SSH2 style public key to repository.  Only the public key is added,
696    not other information from the key. */
697
698 static SilcResult silc_skr_add_ssh_simple(SilcSKR skr,
699                                           SilcPublicKey public_key,
700                                           SilcSKRKeyUsage usage,
701                                           void *key_context,
702                                           SilcSKRKey *return_key)
703 {
704   SilcSKRKeyInternal key;
705   SilcResult status = SILC_ERR;
706   SilcSshPublicKey ssh_pubkey;
707 #if defined(SILC_DEBUG)
708   char tmp[256];
709 #endif /* SILC_DEBUG */
710
711   /* Get the SSH public key */
712   ssh_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SSH2, public_key);
713
714   SILC_LOG_DEBUG(("Adding SSH public key %p, context %p", public_key,
715                   key_context));
716
717   silc_mutex_lock(skr->lock);
718
719   /* Check that this key hasn't been added already */
720   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
721                           public_key, NULL, key_context, 0)) {
722     silc_mutex_unlock(skr->lock);
723     SILC_LOG_DEBUG(("Key already added"));
724     return SILC_ERR_ALREADY_EXISTS;
725   }
726
727   /* Allocate key entry */
728   key = silc_calloc(1, sizeof(*key));
729   if (!key) {
730     silc_mutex_unlock(skr->lock);
731     return silc_errno;
732   }
733
734   key->key.usage = usage;
735   key->key.key = public_key;
736   key->key.key_context = key_context;
737
738 #if defined(SILC_DEBUG)
739   silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
740                        tmp, sizeof(tmp) - 1);
741   SILC_LOG_DEBUG((" Search constraint %s", tmp));
742 #endif /* SILC_DEBUG */
743
744   /* Add key specifics */
745
746   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
747                           public_key, key))
748     goto err;
749   key->refcnt++;
750
751   if (key_context) {
752     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
753                             key_context, key))
754       goto err;
755     key->refcnt++;
756   }
757
758   silc_mutex_unlock(skr->lock);
759
760   if (return_key)
761     *return_key = (SilcSKRKey)key;
762
763   return SILC_OK;
764
765  err:
766   silc_mutex_unlock(skr->lock);
767   return status;
768 }
769
770 /* Deletes SSH public key from repository */
771
772 static SilcResult silc_skr_del_ssh_public_key(SilcSKR skr,
773                                               SilcPublicKey public_key,
774                                               void *key_context)
775 {
776   SilcResult status = SILC_ERR;
777   SilcSshPublicKey ssh_pubkey;
778   SilcSKRKeyInternal key;
779   SilcDList entry;
780   const char *subject;
781
782   /* Get the SSH public key */
783   ssh_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SSH2, public_key);
784
785   /* Get subject */
786   subject = silc_ssh_public_key_get_field(ssh_pubkey, "Subject");
787
788   SILC_LOG_DEBUG(("Deleting SSH public key %p [%s]", public_key,
789                   subject ? subject : "none"));
790
791   silc_mutex_lock(skr->lock);
792
793   /* Check that this key exists */
794   if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
795                            public_key, &entry, key_context, 0)) {
796     silc_mutex_unlock(skr->lock);
797     SILC_LOG_DEBUG(("Key does not exist"));
798     return SILC_ERR_NOT_FOUND;
799   }
800
801   silc_dlist_start(entry);
802   key = silc_dlist_get(entry);
803   silc_dlist_uninit(entry);
804
805   silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
806   silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
807                      SILC_32_TO_PTR(SILC_PKCS_SSH2), key);
808   silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, (void *)subject, key);
809   silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
810
811   silc_mutex_unlock(skr->lock);
812
813   return SILC_OK;
814 }
815
816
817 /**************************** Key Repository API ****************************/
818
819 /* Allocate key repository */
820
821 SilcSKR silc_skr_alloc(void)
822 {
823   SilcSKR skr;
824
825   skr = silc_calloc(1, sizeof(*skr));
826   if (!skr)
827     return NULL;
828
829   if (!silc_skr_init(skr)) {
830     silc_skr_free(skr);
831     return NULL;
832   }
833
834   return skr;
835 }
836
837 /* Free key repository */
838
839 void silc_skr_free(SilcSKR skr)
840 {
841   silc_skr_uninit(skr);
842   silc_free(skr);
843 }
844
845 /* Initializes key repository */
846
847 SilcBool silc_skr_init(SilcSKR skr)
848 {
849   if (!silc_mutex_alloc(&skr->lock))
850     return FALSE;
851
852   skr->keys = silc_hash_table_alloc(NULL, 0, silc_skr_hash, NULL,
853                                     silc_skr_compare, NULL,
854                                     silc_skr_destructor, NULL, TRUE);
855   if (!skr->keys)
856     return FALSE;
857
858   return TRUE;
859 }
860
861 /* Uninitializes key repository */
862
863 void silc_skr_uninit(SilcSKR skr)
864 {
865   if (skr->keys)
866     silc_hash_table_free(skr->keys);
867   silc_mutex_free(skr->lock);
868 }
869
870 /* Adds public key to key repository */
871
872 SilcResult silc_skr_add_public_key(SilcSKR skr,
873                                    SilcPublicKey public_key,
874                                    SilcSKRKeyUsage usage,
875                                    void *key_context,
876                                    SilcSKRKey *return_key)
877 {
878   SilcPKCSType type;
879
880   if (!public_key)
881     return SILC_ERR_INVALID_ARGUMENT;
882
883   type = silc_pkcs_get_type(public_key);
884
885   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
886
887   switch (type) {
888
889   case SILC_PKCS_SILC:
890     return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
891     break;
892
893   case SILC_PKCS_SSH2:
894     return silc_skr_add_ssh(skr, public_key, usage, key_context, return_key);
895     break;
896
897   default:
898     break;
899   }
900
901   return SILC_ERR_NOT_SUPPORTED;
902 }
903
904 /* Adds public key to repository. */
905
906 SilcResult silc_skr_add_public_key_simple(SilcSKR skr,
907                                           SilcPublicKey public_key,
908                                           SilcSKRKeyUsage usage,
909                                           void *key_context,
910                                           SilcSKRKey *return_key)
911 {
912   SilcPKCSType type;
913
914   if (!public_key)
915     return SILC_ERR_INVALID_ARGUMENT;
916
917   type = silc_pkcs_get_type(public_key);
918
919   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
920
921   switch (type) {
922
923   case SILC_PKCS_SILC:
924     return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
925                                     return_key);
926     break;
927
928   case SILC_PKCS_SSH2:
929     return silc_skr_add_ssh_simple(skr, public_key, usage, key_context,
930                                    return_key);
931     break;
932
933   default:
934     break;
935   }
936
937   return SILC_ERR_NOT_SUPPORTED;
938 }
939
940 /* Remove key from repository */
941
942 SilcResult silc_skr_del_public_key(SilcSKR skr,
943                                       SilcPublicKey public_key,
944                                       void *key_context)
945 {
946   SilcPKCSType type;
947
948   if (!public_key)
949     return SILC_ERR_INVALID_ARGUMENT;
950
951   type = silc_pkcs_get_type(public_key);
952
953   SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
954
955   switch (type) {
956
957   case SILC_PKCS_SILC:
958     return silc_skr_del_silc_public_key(skr, public_key, key_context);
959     break;
960
961   case SILC_PKCS_SSH2:
962     return silc_skr_del_ssh_public_key(skr, public_key, key_context);
963     break;
964
965   default:
966     break;
967   }
968
969   return SILC_ERR_NOT_SUPPORTED;
970 }
971
972 /* Reference key */
973
974 void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
975 {
976   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
977
978   silc_mutex_lock(skr->lock);
979   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
980   k->refcnt++;
981   silc_mutex_unlock(skr->lock);
982 }
983
984 /* Release key reference. */
985
986 void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
987 {
988   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
989
990   silc_mutex_lock(skr->lock);
991
992   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
993   k->refcnt--;
994
995   if (k->refcnt == 0) {
996     /* If reference is zero, the key has been removed from the repository
997        already.  Just destroy the public key. */
998     silc_pkcs_public_key_free(key->key);
999     silc_free(key);
1000   }
1001
1002   silc_mutex_unlock(skr->lock);
1003 }
1004
1005
1006 /************************** Search Constraints API **************************/
1007
1008 /* Allocate search constraints */
1009
1010 SilcSKRFind silc_skr_find_alloc(void)
1011 {
1012   SilcSKRFind find;
1013
1014   find = silc_calloc(1, sizeof(*find));
1015   if (!find)
1016     return NULL;
1017
1018   find->constr = silc_hash_table_alloc(NULL, 0, silc_hash_uint,
1019                                        NULL, NULL, NULL,
1020                                        silc_skr_find_destructor, NULL, TRUE);
1021   if (!find->constr) {
1022     silc_skr_find_free(find);
1023     return NULL;
1024   }
1025
1026   return find;
1027 }
1028
1029 /* Free search constraints */
1030
1031 void silc_skr_find_free(SilcSKRFind find)
1032 {
1033   if (find->constr)
1034     silc_hash_table_free(find->constr);
1035   silc_free(find);
1036 }
1037
1038 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
1039 {
1040   return silc_hash_table_add(find->constr,
1041                              SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
1042                              SILC_32_TO_PTR(type));
1043 }
1044
1045 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
1046 {
1047   void *c = silc_memdup(username, strlen(username));
1048   if (!c)
1049     return FALSE;
1050   return silc_hash_table_add(find->constr,
1051                              SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
1052 }
1053
1054 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
1055 {
1056   void *c = silc_memdup(host, strlen(host));
1057   if (!c)
1058     return FALSE;
1059   return silc_hash_table_add(find->constr,
1060                              SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
1061 }
1062
1063 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
1064 {
1065   void *c = silc_memdup(realname, strlen(realname));
1066   if (!c)
1067     return FALSE;
1068   return silc_hash_table_add(find->constr,
1069                              SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
1070 }
1071
1072 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
1073 {
1074   void *c = silc_memdup(email, strlen(email));
1075   if (!c)
1076     return FALSE;
1077   return silc_hash_table_add(find->constr,
1078                              SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
1079 }
1080
1081 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
1082 {
1083   void *c = silc_memdup(org, strlen(org));
1084   if (!c)
1085     return FALSE;
1086   return silc_hash_table_add(find->constr,
1087                              SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
1088 }
1089
1090 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
1091 {
1092   void *c = silc_memdup(country, strlen(country));
1093   if (!c)
1094     return FALSE;
1095   return silc_hash_table_add(find->constr,
1096                              SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
1097 }
1098
1099 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
1100                                       SilcPublicKey public_key)
1101 {
1102   SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
1103   if (!pk)
1104     return FALSE;
1105   return silc_hash_table_add(find->constr,
1106                              SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
1107 }
1108
1109 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
1110 {
1111   if (!context)
1112     return TRUE;
1113   return silc_hash_table_add(find->constr,
1114                              SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
1115 }
1116
1117 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
1118 {
1119   if (!usage)
1120     return TRUE;
1121   return silc_hash_table_add(find->constr,
1122                              SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
1123                              SILC_32_TO_PTR(usage));
1124 }
1125
1126 /******************************** Search API ********************************/
1127
1128 /* Finds key(s) by the set search constraints.  The callback will be called
1129    once keys has been found. */
1130 /* This is now synchronous function but may later change async */
1131
1132 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
1133                                  SilcSKRFind find,
1134                                  SilcSKRFindCallback callback,
1135                                  void *callback_context)
1136 {
1137   SilcResult status = SILC_ERR;
1138   SilcHashTableList htl;
1139   SilcDList list, results = NULL;
1140   void *type, *ctx, *usage = NULL;
1141 #if defined(SILC_DEBUG)
1142   char tmp[256];
1143 #endif /* SILC_DEBUG */
1144
1145   SILC_LOG_DEBUG(("Finding key from repository"));
1146
1147   if (!find || !callback)
1148     return NULL;
1149
1150   silc_mutex_lock(skr->lock);
1151
1152   /* Get usage bits, if searching by them */
1153   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
1154                        NULL, &usage);
1155
1156 #if defined(SILC_DEBUG)
1157   if (usage) {
1158     memset(tmp, 0, sizeof(tmp));
1159     silc_skr_type_string(SILC_SKR_FIND_USAGE, usage, tmp, sizeof(tmp) - 1);
1160     SILC_LOG_DEBUG(("Finding key by %s", tmp));
1161   }
1162 #endif /* SILC_DEBUG */
1163
1164   silc_hash_table_list(find->constr, &htl);
1165   while (silc_hash_table_get(&htl, &type, &ctx)) {
1166
1167     /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
1168     if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
1169       continue;
1170
1171 #if defined(SILC_DEBUG)
1172     memset(tmp, 0, sizeof(tmp));
1173     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
1174                          ctx, tmp, sizeof(tmp) - 1);
1175     SILC_LOG_DEBUG(("Finding key by %s", tmp));
1176 #endif /* SILC_DEBUG */
1177
1178     /* Find entries by this search constraint */
1179     if (!silc_skr_find_entry(skr, &status,
1180                              (SilcSKRFindType)SILC_32_TO_PTR(type),
1181                              ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
1182       SILC_LOG_DEBUG(("Not found"));
1183       if (results) {
1184         silc_dlist_uninit(results);
1185         results = NULL;
1186       }
1187       break;
1188     }
1189
1190     /* For now, our logic rule is AND.  All constraints must be found
1191        to find the key.  Later OR might be added also. */
1192     if (!silc_skr_results_and(list, &status, &results)) {
1193       SILC_LOG_DEBUG(("Not found"));
1194       if (results) {
1195         silc_dlist_uninit(results);
1196         results = NULL;
1197       }
1198       silc_dlist_uninit(list);
1199       break;
1200     }
1201
1202     silc_dlist_uninit(list);
1203   }
1204   silc_hash_table_list_reset(&htl);
1205
1206   silc_mutex_unlock(skr->lock);
1207
1208   /* Return results */
1209   if (!results) {
1210     callback(skr, find, status, NULL, callback_context);
1211   } else {
1212     silc_dlist_start(results);
1213     callback(skr, find, SILC_OK, results, callback_context);
1214   }
1215
1216   return NULL;
1217 }