1 /*
2  * Data structure managing host keys in sessions based on GSSAPI KEX.
3  *
4  * In a session we started with a GSSAPI key exchange, the concept of
5  * 'host key' has completely different lifetime and security semantics
6  * from the usual ones. Per RFC 4462 section 2.1, we assume that any
7  * host key delivered to us in the course of a GSSAPI key exchange is
8  * _solely_ there to use as a transient fallback within the same
9  * session, if at the time of a subsequent rekey the GSS credentials
10  * are temporarily invalid and so a non-GSS KEX method has to be used.
11  *
12  * In particular, in a GSS-based SSH deployment, host keys may not
13  * even _be_ persistent identities for the server; it would be
14  * legitimate for a server to generate a fresh one routinely if it
15  * wanted to, like SSH-1 server keys.
16  *
17  * So, in this mode, we never touch the persistent host key cache at
18  * all, either to check keys against it _or_ to store keys in it.
19  * Instead, we maintain an in-memory cache of host keys that have been
20  * mentioned in GSS key exchanges within this particular session, and
21  * we permit precisely those host keys in non-GSS rekeys.
22  */
23 
24 #include <assert.h>
25 
26 #include "putty.h"
27 #include "ssh.h"
28 
29 struct ssh_transient_hostkey_cache {
30     tree234 *cache;
31 };
32 
33 struct ssh_transient_hostkey_cache_entry {
34     const ssh_keyalg *alg;
35     strbuf *pub_blob;
36 };
37 
ssh_transient_hostkey_cache_cmp(void * av,void * bv)38 static int ssh_transient_hostkey_cache_cmp(void *av, void *bv)
39 {
40     const struct ssh_transient_hostkey_cache_entry
41         *a = (const struct ssh_transient_hostkey_cache_entry *)av,
42         *b = (const struct ssh_transient_hostkey_cache_entry *)bv;
43     return strcmp(a->alg->ssh_id, b->alg->ssh_id);
44 }
45 
ssh_transient_hostkey_cache_find(void * av,void * bv)46 static int ssh_transient_hostkey_cache_find(void *av, void *bv)
47 {
48     const ssh_keyalg *aalg = (const ssh_keyalg *)av;
49     const struct ssh_transient_hostkey_cache_entry
50         *b = (const struct ssh_transient_hostkey_cache_entry *)bv;
51     return strcmp(aalg->ssh_id, b->alg->ssh_id);
52 }
53 
ssh_transient_hostkey_cache_new(void)54 ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void)
55 {
56     ssh_transient_hostkey_cache *thc = snew(ssh_transient_hostkey_cache);
57     thc->cache = newtree234(ssh_transient_hostkey_cache_cmp);
58     return thc;
59 }
60 
ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache * thc)61 void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc)
62 {
63     struct ssh_transient_hostkey_cache_entry *ent;
64     while ((ent = delpos234(thc->cache, 0)) != NULL) {
65         strbuf_free(ent->pub_blob);
66         sfree(ent);
67     }
68     freetree234(thc->cache);
69     sfree(thc);
70 }
71 
ssh_transient_hostkey_cache_add(ssh_transient_hostkey_cache * thc,ssh_key * key)72 void ssh_transient_hostkey_cache_add(
73     ssh_transient_hostkey_cache *thc, ssh_key *key)
74 {
75     struct ssh_transient_hostkey_cache_entry *ent, *retd;
76 
77     if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
78                        ssh_transient_hostkey_cache_find)) != NULL) {
79         del234(thc->cache, ent);
80         strbuf_free(ent->pub_blob);
81         sfree(ent);
82     }
83 
84     ent = snew(struct ssh_transient_hostkey_cache_entry);
85     ent->alg = ssh_key_alg(key);
86     ent->pub_blob = strbuf_new();
87     ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob));
88     retd = add234(thc->cache, ent);
89     assert(retd == ent);
90 }
91 
ssh_transient_hostkey_cache_verify(ssh_transient_hostkey_cache * thc,ssh_key * key)92 bool ssh_transient_hostkey_cache_verify(
93     ssh_transient_hostkey_cache *thc, ssh_key *key)
94 {
95     struct ssh_transient_hostkey_cache_entry *ent;
96     bool toret = false;
97 
98     if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
99                        ssh_transient_hostkey_cache_find)) != NULL) {
100         strbuf *this_blob = strbuf_new();
101         ssh_key_public_blob(key, BinarySink_UPCAST(this_blob));
102 
103         if (this_blob->len == ent->pub_blob->len &&
104             !memcmp(this_blob->s, ent->pub_blob->s,
105                     this_blob->len))
106             toret = true;
107 
108         strbuf_free(this_blob);
109     }
110 
111     return toret;
112 }
113 
ssh_transient_hostkey_cache_has(ssh_transient_hostkey_cache * thc,const ssh_keyalg * alg)114 bool ssh_transient_hostkey_cache_has(
115     ssh_transient_hostkey_cache *thc, const ssh_keyalg *alg)
116 {
117     struct ssh_transient_hostkey_cache_entry *ent =
118         find234(thc->cache, (void *)alg,
119                 ssh_transient_hostkey_cache_find);
120     return ent != NULL;
121 }
122 
ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache * thc)123 bool ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache *thc)
124 {
125     return count234(thc->cache) > 0;
126 }
127