diff options
Diffstat (limited to 'kex.c')
-rw-r--r-- | kex.c | 386 |
1 files changed, 192 insertions, 194 deletions
@@ -23,12 +23,9 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include "includes.h" | 25 | #include "includes.h" |
26 | RCSID("$OpenBSD: kex.c,v 1.25 2001/03/29 21:17:39 markus Exp $"); | 26 | RCSID("$OpenBSD: kex.c,v 1.26 2001/04/03 19:53:29 markus Exp $"); |
27 | 27 | ||
28 | #include <openssl/crypto.h> | 28 | #include <openssl/crypto.h> |
29 | #include <openssl/bio.h> | ||
30 | #include <openssl/bn.h> | ||
31 | #include <openssl/pem.h> | ||
32 | 29 | ||
33 | #include "ssh2.h" | 30 | #include "ssh2.h" |
34 | #include "xmalloc.h" | 31 | #include "xmalloc.h" |
@@ -42,233 +39,169 @@ RCSID("$OpenBSD: kex.c,v 1.25 2001/03/29 21:17:39 markus Exp $"); | |||
42 | #include "log.h" | 39 | #include "log.h" |
43 | #include "mac.h" | 40 | #include "mac.h" |
44 | #include "match.h" | 41 | #include "match.h" |
42 | #include "dispatch.h" | ||
45 | 43 | ||
46 | #define KEX_COOKIE_LEN 16 | 44 | #define KEX_COOKIE_LEN 16 |
47 | 45 | ||
48 | Buffer * | 46 | void kex_kexinit_finish(Kex *kex); |
49 | kex_init(char *myproposal[PROPOSAL_MAX]) | 47 | void kex_choose_conf(Kex *k); |
48 | |||
49 | /* put algorithm proposal into buffer */ | ||
50 | void | ||
51 | kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) | ||
50 | { | 52 | { |
51 | int first_kex_packet_follows = 0; | ||
52 | u_char cookie[KEX_COOKIE_LEN]; | ||
53 | u_int32_t rand = 0; | 53 | u_int32_t rand = 0; |
54 | int i; | 54 | int i; |
55 | Buffer *ki = xmalloc(sizeof(*ki)); | 55 | |
56 | buffer_clear(b); | ||
56 | for (i = 0; i < KEX_COOKIE_LEN; i++) { | 57 | for (i = 0; i < KEX_COOKIE_LEN; i++) { |
57 | if (i % 4 == 0) | 58 | if (i % 4 == 0) |
58 | rand = arc4random(); | 59 | rand = arc4random(); |
59 | cookie[i] = rand & 0xff; | 60 | buffer_put_char(b, rand & 0xff); |
60 | rand >>= 8; | 61 | rand >>= 8; |
61 | } | 62 | } |
62 | buffer_init(ki); | ||
63 | buffer_append(ki, (char *)cookie, sizeof cookie); | ||
64 | for (i = 0; i < PROPOSAL_MAX; i++) | 63 | for (i = 0; i < PROPOSAL_MAX; i++) |
65 | buffer_put_cstring(ki, myproposal[i]); | 64 | buffer_put_cstring(b, proposal[i]); |
66 | buffer_put_char(ki, first_kex_packet_follows); | 65 | buffer_put_char(b, 0); /* first_kex_packet_follows */ |
67 | buffer_put_int(ki, 0); /* uint32 reserved */ | 66 | buffer_put_int(b, 0); /* uint32 reserved */ |
68 | return ki; | ||
69 | } | 67 | } |
70 | 68 | ||
71 | /* send kexinit, parse and save reply */ | 69 | /* parse buffer and return algorithm proposal */ |
72 | void | 70 | char ** |
73 | kex_exchange_kexinit( | 71 | kex_buf2prop(Buffer *raw) |
74 | Buffer *my_kexinit, Buffer *peer_kexint, | ||
75 | char *peer_proposal[PROPOSAL_MAX]) | ||
76 | { | 72 | { |
73 | Buffer b; | ||
77 | int i; | 74 | int i; |
78 | char *ptr; | 75 | char **proposal; |
79 | int plen; | ||
80 | 76 | ||
81 | debug("send KEXINIT"); | 77 | proposal = xmalloc(PROPOSAL_MAX * sizeof(char *)); |
82 | packet_start(SSH2_MSG_KEXINIT); | 78 | |
83 | packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit)); | 79 | buffer_init(&b); |
84 | packet_send(); | 80 | buffer_append(&b, buffer_ptr(raw), buffer_len(raw)); |
85 | packet_write_wait(); | ||
86 | debug("done"); | ||
87 | |||
88 | /* | ||
89 | * read and save raw KEXINIT payload in buffer. this is used during | ||
90 | * computation of the session_id and the session keys. | ||
91 | */ | ||
92 | debug("wait KEXINIT"); | ||
93 | packet_read_expect(&plen, SSH2_MSG_KEXINIT); | ||
94 | ptr = packet_get_raw(&plen); | ||
95 | buffer_append(peer_kexint, ptr, plen); | ||
96 | |||
97 | /* parse packet and save algorithm proposal */ | ||
98 | /* skip cookie */ | 81 | /* skip cookie */ |
99 | for (i = 0; i < KEX_COOKIE_LEN; i++) | 82 | for (i = 0; i < KEX_COOKIE_LEN; i++) |
100 | packet_get_char(); | 83 | buffer_get_char(&b); |
101 | /* extract kex init proposal strings */ | 84 | /* extract kex init proposal strings */ |
102 | for (i = 0; i < PROPOSAL_MAX; i++) { | 85 | for (i = 0; i < PROPOSAL_MAX; i++) { |
103 | peer_proposal[i] = packet_get_string(NULL); | 86 | proposal[i] = buffer_get_string(&b,NULL); |
104 | debug("got kexinit: %s", peer_proposal[i]); | 87 | debug2("kex_parse_kexinit: %s", proposal[i]); |
105 | } | 88 | } |
106 | /* first kex follow / reserved */ | 89 | /* first kex follows / reserved */ |
107 | i = packet_get_char(); | 90 | i = buffer_get_char(&b); |
108 | debug("first kex follow: %d ", i); | 91 | debug2("kex_parse_kexinit: first_kex_follows %d ", i); |
109 | i = packet_get_int(); | 92 | i = buffer_get_int(&b); |
110 | debug("reserved: %d ", i); | 93 | debug2("kex_parse_kexinit: reserved %d ", i); |
111 | packet_done(); | 94 | buffer_free(&b); |
112 | debug("done"); | 95 | return proposal; |
113 | } | 96 | } |
114 | 97 | ||
115 | #ifdef DEBUG_KEX | ||
116 | void | 98 | void |
117 | dump_digest(u_char *digest, int len) | 99 | kex_prop_free(char **proposal) |
118 | { | 100 | { |
119 | int i; | 101 | int i; |
120 | for (i = 0; i< len; i++){ | 102 | |
121 | fprintf(stderr, "%02x", digest[i]); | 103 | for (i = 0; i < PROPOSAL_MAX; i++) |
122 | if(i%2!=0) | 104 | xfree(proposal[i]); |
123 | fprintf(stderr, " "); | 105 | xfree(proposal); |
124 | } | ||
125 | fprintf(stderr, "\n"); | ||
126 | } | 106 | } |
127 | #endif | ||
128 | 107 | ||
129 | u_char * | 108 | void |
130 | kex_hash( | 109 | kex_protocol_error(int type, int plen, void *ctxt) |
131 | char *client_version_string, | ||
132 | char *server_version_string, | ||
133 | char *ckexinit, int ckexinitlen, | ||
134 | char *skexinit, int skexinitlen, | ||
135 | char *serverhostkeyblob, int sbloblen, | ||
136 | BIGNUM *client_dh_pub, | ||
137 | BIGNUM *server_dh_pub, | ||
138 | BIGNUM *shared_secret) | ||
139 | { | 110 | { |
140 | Buffer b; | 111 | error("Hm, kex protocol error: type %d plen %d", type, plen); |
141 | static u_char digest[EVP_MAX_MD_SIZE]; | 112 | } |
142 | EVP_MD *evp_md = EVP_sha1(); | ||
143 | EVP_MD_CTX md; | ||
144 | |||
145 | buffer_init(&b); | ||
146 | buffer_put_string(&b, client_version_string, strlen(client_version_string)); | ||
147 | buffer_put_string(&b, server_version_string, strlen(server_version_string)); | ||
148 | |||
149 | /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ | ||
150 | buffer_put_int(&b, ckexinitlen+1); | ||
151 | buffer_put_char(&b, SSH2_MSG_KEXINIT); | ||
152 | buffer_append(&b, ckexinit, ckexinitlen); | ||
153 | buffer_put_int(&b, skexinitlen+1); | ||
154 | buffer_put_char(&b, SSH2_MSG_KEXINIT); | ||
155 | buffer_append(&b, skexinit, skexinitlen); | ||
156 | |||
157 | buffer_put_string(&b, serverhostkeyblob, sbloblen); | ||
158 | buffer_put_bignum2(&b, client_dh_pub); | ||
159 | buffer_put_bignum2(&b, server_dh_pub); | ||
160 | buffer_put_bignum2(&b, shared_secret); | ||
161 | |||
162 | #ifdef DEBUG_KEX | ||
163 | buffer_dump(&b); | ||
164 | #endif | ||
165 | 113 | ||
166 | EVP_DigestInit(&md, evp_md); | 114 | void |
167 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); | 115 | kex_send_newkeys(void) |
168 | EVP_DigestFinal(&md, digest, NULL); | 116 | { |
117 | packet_start(SSH2_MSG_NEWKEYS); | ||
118 | packet_send(); | ||
119 | /* packet_write_wait(); */ | ||
120 | debug("SSH2_MSG_NEWKEYS sent"); | ||
121 | } | ||
169 | 122 | ||
170 | buffer_free(&b); | 123 | void |
124 | kex_input_newkeys(int type, int plen, void *ctxt) | ||
125 | { | ||
126 | Kex *kex = ctxt; | ||
127 | int i; | ||
171 | 128 | ||
172 | #ifdef DEBUG_KEX | 129 | debug("SSH2_MSG_NEWKEYS received"); |
173 | dump_digest(digest, evp_md->md_size); | 130 | kex->newkeys = 1; |
174 | #endif | 131 | for (i = 30; i <= 49; i++) |
175 | return digest; | 132 | dispatch_set(i, &kex_protocol_error); |
133 | buffer_clear(&kex->peer); | ||
134 | buffer_clear(&kex->my); | ||
135 | kex->flags &= ~KEX_INIT_SENT; | ||
176 | } | 136 | } |
177 | 137 | ||
178 | u_char * | 138 | void |
179 | kex_hash_gex( | 139 | kex_send_kexinit(Kex *kex) |
180 | char *client_version_string, | ||
181 | char *server_version_string, | ||
182 | char *ckexinit, int ckexinitlen, | ||
183 | char *skexinit, int skexinitlen, | ||
184 | char *serverhostkeyblob, int sbloblen, | ||
185 | int min, int wantbits, int max, BIGNUM *prime, BIGNUM *gen, | ||
186 | BIGNUM *client_dh_pub, | ||
187 | BIGNUM *server_dh_pub, | ||
188 | BIGNUM *shared_secret) | ||
189 | { | 140 | { |
190 | Buffer b; | 141 | packet_start(SSH2_MSG_KEXINIT); |
191 | static u_char digest[EVP_MAX_MD_SIZE]; | 142 | packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my)); |
192 | EVP_MD *evp_md = EVP_sha1(); | 143 | packet_send(); |
193 | EVP_MD_CTX md; | 144 | debug("SSH2_MSG_KEXINIT sent"); |
194 | 145 | kex->flags |= KEX_INIT_SENT; | |
195 | buffer_init(&b); | 146 | } |
196 | buffer_put_string(&b, client_version_string, strlen(client_version_string)); | ||
197 | buffer_put_string(&b, server_version_string, strlen(server_version_string)); | ||
198 | |||
199 | /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ | ||
200 | buffer_put_int(&b, ckexinitlen+1); | ||
201 | buffer_put_char(&b, SSH2_MSG_KEXINIT); | ||
202 | buffer_append(&b, ckexinit, ckexinitlen); | ||
203 | buffer_put_int(&b, skexinitlen+1); | ||
204 | buffer_put_char(&b, SSH2_MSG_KEXINIT); | ||
205 | buffer_append(&b, skexinit, skexinitlen); | ||
206 | |||
207 | buffer_put_string(&b, serverhostkeyblob, sbloblen); | ||
208 | if (min == -1 || max == -1) | ||
209 | buffer_put_int(&b, wantbits); | ||
210 | else { | ||
211 | buffer_put_int(&b, min); | ||
212 | buffer_put_int(&b, wantbits); | ||
213 | buffer_put_int(&b, max); | ||
214 | } | ||
215 | buffer_put_bignum2(&b, prime); | ||
216 | buffer_put_bignum2(&b, gen); | ||
217 | buffer_put_bignum2(&b, client_dh_pub); | ||
218 | buffer_put_bignum2(&b, server_dh_pub); | ||
219 | buffer_put_bignum2(&b, shared_secret); | ||
220 | 147 | ||
221 | #ifdef DEBUG_KEX | 148 | void |
222 | buffer_dump(&b); | 149 | kex_input_kexinit(int type, int plen, void *ctxt) |
223 | #endif | 150 | { |
151 | char *ptr; | ||
152 | int dlen; | ||
153 | Kex *kex = (Kex *)ctxt; | ||
224 | 154 | ||
225 | EVP_DigestInit(&md, evp_md); | 155 | dispatch_set(SSH2_MSG_KEXINIT, &kex_protocol_error); |
226 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); | 156 | debug("SSH2_MSG_KEXINIT received"); |
227 | EVP_DigestFinal(&md, digest, NULL); | ||
228 | 157 | ||
229 | buffer_free(&b); | 158 | ptr = packet_get_raw(&dlen); |
159 | buffer_append(&kex->peer, ptr, dlen); | ||
230 | 160 | ||
231 | #ifdef DEBUG_KEX | 161 | kex_kexinit_finish(kex); |
232 | dump_digest(digest, evp_md->md_size); | ||
233 | #endif | ||
234 | return digest; | ||
235 | } | 162 | } |
236 | 163 | ||
237 | u_char * | 164 | Kex * |
238 | derive_key(int id, int need, u_char *hash, BIGNUM *shared_secret) | 165 | kex_start(char *proposal[PROPOSAL_MAX]) |
239 | { | 166 | { |
240 | Buffer b; | 167 | Kex *kex; |
241 | EVP_MD *evp_md = EVP_sha1(); | 168 | int i; |
242 | EVP_MD_CTX md; | ||
243 | char c = id; | ||
244 | int have; | ||
245 | int mdsz = evp_md->md_size; | ||
246 | u_char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz); | ||
247 | |||
248 | buffer_init(&b); | ||
249 | buffer_put_bignum2(&b, shared_secret); | ||
250 | 169 | ||
251 | EVP_DigestInit(&md, evp_md); | 170 | kex = xmalloc(sizeof(*kex)); |
252 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */ | 171 | memset(kex, 0, sizeof(*kex)); |
253 | EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */ | 172 | buffer_init(&kex->peer); |
254 | EVP_DigestUpdate(&md, &c, 1); /* key id */ | 173 | buffer_init(&kex->my); |
255 | EVP_DigestUpdate(&md, hash, mdsz); /* session id */ | 174 | kex_prop2buf(&kex->my, proposal); |
256 | EVP_DigestFinal(&md, digest, NULL); | 175 | kex->newkeys = 0; |
176 | |||
177 | kex_send_kexinit(kex); /* we start */ | ||
178 | /* Numbers 30-49 are used for kex packets */ | ||
179 | for (i = 30; i <= 49; i++) | ||
180 | dispatch_set(i, kex_protocol_error); | ||
181 | |||
182 | dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); | ||
183 | dispatch_set(SSH2_MSG_NEWKEYS, &kex_input_newkeys); | ||
184 | return kex; | ||
185 | } | ||
257 | 186 | ||
258 | /* expand */ | 187 | void |
259 | for (have = mdsz; need > have; have += mdsz) { | 188 | kex_kexinit_finish(Kex *kex) |
260 | EVP_DigestInit(&md, evp_md); | 189 | { |
261 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); | 190 | if (!(kex->flags & KEX_INIT_SENT)) |
262 | EVP_DigestUpdate(&md, hash, mdsz); | 191 | kex_send_kexinit(kex); |
263 | EVP_DigestUpdate(&md, digest, have); | 192 | |
264 | EVP_DigestFinal(&md, digest + have, NULL); | 193 | kex_choose_conf(kex); |
194 | |||
195 | switch(kex->kex_type) { | ||
196 | case DH_GRP1_SHA1: | ||
197 | kexdh(kex); | ||
198 | break; | ||
199 | case DH_GEX_SHA1: | ||
200 | kexgex(kex); | ||
201 | break; | ||
202 | default: | ||
203 | fatal("Unsupported key exchange %d", kex->kex_type); | ||
265 | } | 204 | } |
266 | buffer_free(&b); | ||
267 | #ifdef DEBUG_KEX | ||
268 | fprintf(stderr, "Digest '%c'== ", c); | ||
269 | dump_digest(digest, need); | ||
270 | #endif | ||
271 | return digest; | ||
272 | } | 205 | } |
273 | 206 | ||
274 | void | 207 | void |
@@ -340,17 +273,25 @@ choose_hostkeyalg(Kex *k, char *client, char *server) | |||
340 | xfree(hostkeyalg); | 273 | xfree(hostkeyalg); |
341 | } | 274 | } |
342 | 275 | ||
343 | Kex * | 276 | void |
344 | kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) | 277 | kex_choose_conf(Kex *k) |
345 | { | 278 | { |
279 | char **my, **peer; | ||
280 | char **cprop, **sprop; | ||
346 | int mode; | 281 | int mode; |
347 | int ctos; /* direction: if true client-to-server */ | 282 | int ctos; /* direction: if true client-to-server */ |
348 | int need; | 283 | int need; |
349 | Kex *k; | ||
350 | 284 | ||
351 | k = xmalloc(sizeof(*k)); | 285 | my = kex_buf2prop(&k->my); |
352 | memset(k, 0, sizeof(*k)); | 286 | peer = kex_buf2prop(&k->peer); |
353 | k->server = server; | 287 | |
288 | if (k->server) { | ||
289 | cprop=peer; | ||
290 | sprop=my; | ||
291 | } else { | ||
292 | cprop=my; | ||
293 | sprop=peer; | ||
294 | } | ||
354 | 295 | ||
355 | for (mode = 0; mode < MODE_MAX; mode++) { | 296 | for (mode = 0; mode < MODE_MAX; mode++) { |
356 | int nenc, nmac, ncomp; | 297 | int nenc, nmac, ncomp; |
@@ -381,11 +322,51 @@ kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server | |||
381 | } | 322 | } |
382 | /* XXX need runden? */ | 323 | /* XXX need runden? */ |
383 | k->we_need = need; | 324 | k->we_need = need; |
384 | return k; | 325 | |
326 | kex_prop_free(my); | ||
327 | kex_prop_free(peer); | ||
328 | |||
329 | } | ||
330 | |||
331 | u_char * | ||
332 | derive_key(int id, int need, u_char *hash, BIGNUM *shared_secret) | ||
333 | { | ||
334 | Buffer b; | ||
335 | EVP_MD *evp_md = EVP_sha1(); | ||
336 | EVP_MD_CTX md; | ||
337 | char c = id; | ||
338 | int have; | ||
339 | int mdsz = evp_md->md_size; | ||
340 | u_char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz); | ||
341 | |||
342 | buffer_init(&b); | ||
343 | buffer_put_bignum2(&b, shared_secret); | ||
344 | |||
345 | EVP_DigestInit(&md, evp_md); | ||
346 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */ | ||
347 | EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */ | ||
348 | EVP_DigestUpdate(&md, &c, 1); /* key id */ | ||
349 | EVP_DigestUpdate(&md, hash, mdsz); /* session id */ | ||
350 | EVP_DigestFinal(&md, digest, NULL); | ||
351 | |||
352 | /* expand */ | ||
353 | for (have = mdsz; need > have; have += mdsz) { | ||
354 | EVP_DigestInit(&md, evp_md); | ||
355 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); | ||
356 | EVP_DigestUpdate(&md, hash, mdsz); | ||
357 | EVP_DigestUpdate(&md, digest, have); | ||
358 | EVP_DigestFinal(&md, digest + have, NULL); | ||
359 | } | ||
360 | buffer_free(&b); | ||
361 | #ifdef DEBUG_KEX | ||
362 | fprintf(stderr, "key '%c'== ", c); | ||
363 | dump_digest("key", digest, need); | ||
364 | #endif | ||
365 | return digest; | ||
385 | } | 366 | } |
386 | 367 | ||
387 | #define NKEYS 6 | 368 | #define NKEYS 6 |
388 | int | 369 | void |
389 | kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret) | 370 | kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret) |
390 | { | 371 | { |
391 | int i; | 372 | int i; |
@@ -402,5 +383,22 @@ kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret) | |||
402 | k->enc[mode].key = keys[ctos ? 2 : 3]; | 383 | k->enc[mode].key = keys[ctos ? 2 : 3]; |
403 | k->mac[mode].key = keys[ctos ? 4 : 5]; | 384 | k->mac[mode].key = keys[ctos ? 4 : 5]; |
404 | } | 385 | } |
405 | return 0; | ||
406 | } | 386 | } |
387 | |||
388 | #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) | ||
389 | void | ||
390 | dump_digest(char *msg, u_char *digest, int len) | ||
391 | { | ||
392 | int i; | ||
393 | |||
394 | fprintf(stderr, "%s\n", msg); | ||
395 | for (i = 0; i< len; i++){ | ||
396 | fprintf(stderr, "%02x", digest[i]); | ||
397 | if (i%32 == 31) | ||
398 | fprintf(stderr, "\n"); | ||
399 | else if (i%8 == 7) | ||
400 | fprintf(stderr, " "); | ||
401 | } | ||
402 | fprintf(stderr, "\n"); | ||
403 | } | ||
404 | #endif | ||