1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Server Management *
4 * Copyright Peter Gutmann 1998-2014 *
5 * *
6 ****************************************************************************/
7
8 #if defined( INC_ALL )
9 #include "crypt.h"
10 #include "misc_rw.h"
11 #include "session.h"
12 #include "ssh.h"
13 #else
14 #include "crypt.h"
15 #include "enc_dec/misc_rw.h"
16 #include "session/session.h"
17 #include "session/ssh.h"
18 #endif /* Compiler-specific includes */
19
20 #ifdef USE_SSH
21
22 /****************************************************************************
23 * *
24 * Utility Functions *
25 * *
26 ****************************************************************************/
27
28 /* Set up the public-key algorithm that we'll be advertising to the client
29 based on the server key */
30
31 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
initPubkeyAlgo(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo)32 static int initPubkeyAlgo( INOUT SESSION_INFO *sessionInfoPtr,
33 INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
34 {
35 static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyRSATbl[] = {
36 { "rsa-sha2-256", 12, CRYPT_ALGO_RSA, CRYPT_ALGO_SHA2 },
37 { "ssh-rsa", 7, CRYPT_ALGO_RSA, CRYPT_ALGO_SHA1 },
38 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
39 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
40 };
41 static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyDSATbl[] = {
42 { "ssh-dss", 7, CRYPT_ALGO_DSA, CRYPT_ALGO_SHA1 },
43 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
44 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
45 };
46 static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyECDSATbl[] = {
47 { "ecdsa-sha2-nistp256", 19, CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2 },
48 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
49 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
50 };
51 static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyECDSA384Tbl[] = {
52 { "ecdsa-sha2-nistp384", 19, CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2 },
53 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
54 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
55 };
56 static const ALGO_STRING_INFO FAR_BSS algoStringPubkeyECDSA521Tbl[] = {
57 { "ecdsa-sha2-nistp521", 19, CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2 },
58 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE },
59 { NULL, 0, CRYPT_ALGO_NONE, CRYPT_ALGO_NONE }
60 };
61 int pubKeyAlgo, keySize, status;
62
63 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
64 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
65
66 /* Find out which algorithm the server key is using */
67 status = krnlSendMessage( sessionInfoPtr->privateKey,
68 IMESSAGE_GETATTRIBUTE, &pubKeyAlgo,
69 CRYPT_CTXINFO_ALGO );
70 if( cryptStatusError( status ) )
71 return( status );
72 handshakeInfo->pubkeyAlgo = pubKeyAlgo; /* int vs.enum */
73
74 /* If it's a standard public-key algorithm, return the algorithm
75 information directly */
76 if( handshakeInfo->pubkeyAlgo == CRYPT_ALGO_RSA )
77 {
78 handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyRSATbl;
79 handshakeInfo->algoStringPubkeyTblNoEntries = \
80 FAILSAFE_ARRAYSIZE( algoStringPubkeyRSATbl, ALGO_STRING_INFO );
81 return( CRYPT_OK );
82 }
83 if( handshakeInfo->pubkeyAlgo == CRYPT_ALGO_DSA )
84 {
85 handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyDSATbl;
86 handshakeInfo->algoStringPubkeyTblNoEntries = \
87 FAILSAFE_ARRAYSIZE( algoStringPubkeyDSATbl, ALGO_STRING_INFO );
88 return( CRYPT_OK );
89 }
90 ENSURES( handshakeInfo->pubkeyAlgo == CRYPT_ALGO_ECDSA );
91
92 /* ECDSA gets more complicated because there are multiple fixed key
93 sizes possible so we have to vary the algrithm table based on our key
94 size */
95 status = krnlSendMessage( sessionInfoPtr->privateKey,
96 IMESSAGE_GETATTRIBUTE, &keySize,
97 CRYPT_CTXINFO_KEYSIZE );
98 if( cryptStatusError( status ) )
99 return( status );
100 switch( keySize )
101 {
102 case bitsToBytes( 256 ):
103 handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyECDSATbl;
104 handshakeInfo->algoStringPubkeyTblNoEntries = \
105 FAILSAFE_ARRAYSIZE( algoStringPubkeyECDSATbl, ALGO_STRING_INFO );
106 break;
107
108 case bitsToBytes( 384 ):
109 handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyECDSA384Tbl;
110 handshakeInfo->algoStringPubkeyTblNoEntries = \
111 FAILSAFE_ARRAYSIZE( algoStringPubkeyECDSA384Tbl, ALGO_STRING_INFO );
112 break;
113
114 case bitsToBytes( 521 ):
115 handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyECDSA521Tbl;
116 handshakeInfo->algoStringPubkeyTblNoEntries = \
117 FAILSAFE_ARRAYSIZE( algoStringPubkeyECDSA521Tbl, ALGO_STRING_INFO );
118 break;
119
120 default:
121 retIntError();
122 }
123
124 return( CRYPT_OK );
125 }
126
127 /* Handle an ephemeral DH key exchange */
128
129 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
processDHE(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo)130 static int processDHE( INOUT SESSION_INFO *sessionInfoPtr,
131 INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
132 {
133 MESSAGE_DATA msgData;
134 STREAM stream;
135 BYTE keyData[ ( CRYPT_MAX_PKCSIZE * 2 ) + 16 + 8 ];
136 void *keyexInfoPtr DUMMY_INIT_PTR;
137 int keyexInfoLength, keyDataStart, keyDataLength, length;
138 int keySize, status;
139
140 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
141 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
142
143 /* Get the keyex key request from the client:
144
145 byte type = SSH_MSG_KEXDH_GEX_REQUEST_OLD
146 uint32 n (bits)
147
148 or:
149
150 byte type = SSH_MSG_KEXDH_GEX_REQUEST_NEW
151 uint32 min (bits)
152 uint32 n (bits)
153 uint32 max (bits)
154
155 Portions of the the request information are hashed later as part of
156 the exchange hash so we have to save a copy for then. We save the
157 original encoded form because some clients send non-integral lengths
158 that don't survive the conversion from bits to bytes */
159 status = length = \
160 readHSPacketSSH2( sessionInfoPtr, SSH_MSG_KEXDH_GEX_REQUEST_OLD,
161 ID_SIZE + UINT32_SIZE );
162 if( cryptStatusError( status ) )
163 return( status );
164 sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
165 streamBookmarkSet( &stream, keyexInfoLength );
166 if( sessionInfoPtr->sessionSSH->packetType == SSH_MSG_KEXDH_GEX_REQUEST_NEW )
167 {
168 int minKeySize;
169
170 /* It's a { min_length, length, max_length } sequence, save a copy
171 and get the length value */
172 minKeySize = readUint32( &stream );
173 keySize = readUint32( &stream );
174 status = readUint32( &stream );
175
176 /* Some implementations (e.g. OpenSSH >= 6.7) request ridiculous key
177 sizes, to deal with this we change the effective key size to
178 CRYPT_MAX_PKCSIZE if the client has asked for a key size >
179 CRYPT_MAX_PKCSIZE but also specified that they'll accept a
180 min_length <= CRYPT_MAX_PKCSIZE */
181 if( cryptStatusOK( status ) && \
182 keySize > bytesToBits( CRYPT_MAX_PKCSIZE ) && \
183 minKeySize <= bytesToBits( CRYPT_MAX_PKCSIZE ) )
184 {
185 DEBUG_PRINT(( "Client requested key size %d...%d bits, using "
186 "%d bits.\n", minKeySize, keySize ));
187 keySize = bytesToBits( CRYPT_MAX_PKCSIZE );
188 }
189 }
190 else
191 {
192 /* It's a straight length, save a copy and get the length value */
193 status = keySize = readUint32( &stream );
194 }
195 if( !cryptStatusError( status ) )
196 status = streamBookmarkComplete( &stream, &keyexInfoPtr,
197 &keyexInfoLength, keyexInfoLength );
198 sMemDisconnect( &stream );
199 if( cryptStatusError( status ) )
200 {
201 retExt( status,
202 ( status, SESSION_ERRINFO,
203 "Invalid ephemeral DH key data request packet" ) );
204 }
205 ANALYSER_HINT( keyexInfoPtr != NULL );
206 if( keySize < bytesToBits( MIN_PKCSIZE ) || \
207 keySize > bytesToBits( CRYPT_MAX_PKCSIZE ) )
208 {
209 retExt( CRYPT_ERROR_BADDATA,
210 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
211 "Client requested invalid ephemeral DH key size %d bits, "
212 "should be %d...%d", keySize,
213 bytesToBits( MIN_PKCSIZE ),
214 bytesToBits( CRYPT_MAX_PKCSIZE ) ) );
215 }
216 ENSURES( rangeCheckZ( 0, keyexInfoLength, MAX_ENCODED_KEYEXSIZE ) );
217 memcpy( handshakeInfo->encodedReqKeySizes, keyexInfoPtr,
218 keyexInfoLength );
219 handshakeInfo->encodedReqKeySizesLength = keyexInfoLength;
220 handshakeInfo->requestedServerKeySize = bitsToBytes( keySize );
221
222 /* If the requested key size differs too much from the built-in default
223 one, destroy the existing default DH key and load a new one of the
224 appropriate size. Things get quite confusing here because the spec
225 is a schizophrenic mix of two different documents, one that specifies
226 the behaviour for the original message format which uses a single
227 length value and a second one that specifies the behaviour for the
228 { min, n, max } combination (multi sunt, qui ad id, quod non
229 proposuerant scribere, alicuius verbi placentis decore vocentur).
230
231 The range option was added as an attempted fix for implementations
232 that couldn't handle the single size option but the real problem is
233 that the server knows what key sizes are appropriate but the client
234 has to make the choice, without any knowledge of what the server can
235 actually handle. Because of this the spec (in its n-only mindset,
236 which also applies to the min/n/max version since it's the same
237 document) contains assorted weasel-words that allow the server to
238 choose any key size it feels like if the client sends a range
239 indication that's inappropriate. Although the spec ends up saying
240 that the server can do anything it feels like ("The server should
241 return the smallest group it knows that is larger than the size the
242 client requested. If the server does not know a group that is
243 larger than the client request, then it SHOULD return the largest
244 group it knows"), we use a least-upper-bound interpretation of the
245 above, mostly because we store a range of fixed keys of different
246 sizes and can always find something reasonably close to any
247 (sensible) requested length */
248 if( handshakeInfo->requestedServerKeySize < \
249 SSH2_DEFAULT_KEYSIZE - 16 || \
250 handshakeInfo->requestedServerKeySize > \
251 SSH2_DEFAULT_KEYSIZE + 16 )
252 {
253 krnlSendNotifier( handshakeInfo->iServerCryptContext,
254 IMESSAGE_DECREFCOUNT );
255 handshakeInfo->iServerCryptContext = CRYPT_ERROR;
256 status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
257 &handshakeInfo->serverKeySize, NULL, 0,
258 handshakeInfo->requestedServerKeySize );
259 if( cryptStatusError( status ) )
260 return( status );
261 }
262
263 /* Send the DH key values to the client:
264
265 byte type = SSH_MSG_KEXDH_GEX_GROUP
266 mpint p
267 mpint g
268
269 Since this phase of the key negotiation exchanges raw key components
270 rather than the standard SSH public-key format we have to rewrite
271 the public key before we can send it to the client. What this
272 involves is stripping the:
273
274 uint32 length
275 string "ssh-dh"
276
277 header from the start of the datab and then writing what's left to the
278 packet. First we export the key data and figure out the location of
279 the payload that we need to send */
280 setMessageData( &msgData, keyData, ( CRYPT_MAX_PKCSIZE * 2 ) + 16 );
281 status = krnlSendMessage( handshakeInfo->iServerCryptContext,
282 IMESSAGE_GETATTRIBUTE_S, &msgData,
283 CRYPT_IATTRIBUTE_KEY_SSH );
284 if( cryptStatusError( status ) )
285 return( status );
286 sMemConnect( &stream, keyData, msgData.length );
287 readUint32( &stream ); /* Length */
288 status = readUniversal32( &stream ); /* ID string */
289 ENSURES( cryptStatusOK( status ) );
290 keyDataStart = stell( &stream );
291 keyDataLength = sMemDataLeft( &stream );
292 sMemDisconnect( &stream );
293
294 /* Then we create and send the SSH packet using as the payload the key
295 data content of the SSH public key */
296 status = openPacketStreamSSH( &stream, sessionInfoPtr,
297 SSH_MSG_KEXDH_GEX_GROUP );
298 if( cryptStatusError( status ) )
299 return( status );
300 status = swrite( &stream, keyData + keyDataStart, keyDataLength );
301 if( cryptStatusOK( status ) )
302 status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
303 sMemDisconnect( &stream );
304 return( status );
305 }
306
307 /****************************************************************************
308 * *
309 * Server-side Connect Functions *
310 * *
311 ****************************************************************************/
312
313 /* Perform the initial part of the handshake with the client */
314
315 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
beginServerHandshake(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo)316 static int beginServerHandshake( INOUT SESSION_INFO *sessionInfoPtr,
317 INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
318 {
319 STREAM stream;
320 BOOLEAN skipGuessedKeyex = FALSE;
321 void *serverHelloPtr DUMMY_INIT_PTR;
322 int length, serverHelloLength, clientHelloLength, status;
323
324 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
325 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
326
327 /* Get the public-key algorithm that we'll be advertising to the client
328 and set the algorithm table used for processing the client hello to
329 match the one that we're offering */
330 status = initPubkeyAlgo( sessionInfoPtr, handshakeInfo );
331 if( cryptStatusError( status ) )
332 return( status );
333
334 /* SSH hashes the handshake ID strings for integrity-protection purposes,
335 first the client string that we read previously and then our server
336 string */
337 status = hashHandshakeStrings( handshakeInfo,
338 sessionInfoPtr->receiveBuffer,
339 sessionInfoPtr->receiveBufEnd,
340 SSH_ID_STRING, SSH_ID_STRING_SIZE );
341 if( cryptStatusError( status ) )
342 return( status );
343
344 /* Now that we've processed the out-of-band data in the receive buffer,
345 mark it as empty */
346 sessionInfoPtr->receiveBufEnd = 0;
347
348 /* Send the server hello packet:
349
350 byte type = SSH_MSG_KEXINIT
351 byte[16] cookie
352 string keyex algorithms
353 string pubkey algorithms
354 string client_crypto algorithms
355 string server_crypto algorithms
356 string client_mac algorithms
357 string server_mac algorithms
358 string client_compression algorithms = "none"
359 string server_compression algorithms = "none"
360 string client_language = ""
361 string server_language = ""
362 boolean first_keyex_packet_follows = FALSE
363 uint32 reserved = 0
364
365 The SSH spec leaves the order in which things happen ambiguous, in
366 order to save a while round trip it has provisions for both sides
367 shouting at each other and then a complex interlock process where
368 bits of the initial exchange can be discarded and retried if necessary.
369 This is ugly and error-prone. The client code solves this by waiting
370 for the server hello, choosing known-good parameters based on what the
371 server communicates in its hello message, and then sending the client
372 hello immediately followed by the client key exchange data. Since it
373 waits for the server to speak first it can choose parameters that are
374 accepted the first time.
375
376 Unfortunately this doesn't work if we're the server since we'd end up
377 waiting for the client to speak first while it waits for us to speak
378 first, so we have to send the server hello in order to prevent
379 deadlock. This works fine with most clients, which take the same
380 approach and wait for the server to speak first. The message flow is
381 then:
382
383 server hello;
384 client hello;
385 client keyex;
386 server keyex;
387
388 There are one or two exceptions to this, the worst of which is the
389 F-Secure client, which has the client speak first choosing as its
390 preference the incompletely specified "x509v3-sign-dss" format (see
391 the comment in exchangeServerKeys() below) that we can't use since no-
392 one's quite sure what the format is (this was fixed in mid-2004 when
393 the x509v3-* schemes were removed from the spec since no-one could
394 figure out what they were. F-Secure still specifies them, but has
395 moved them down so that they follow the standard ssh-* schemes). In
396 this case the message flow is:
397
398 server hello;
399 client hello;
400 client keyex1;
401 client keyex2;
402 server keyex;
403
404 This is handled by having the code that reads the client hello return
405 OK_SPECIAL to indicate that the next packet should be skipped. An
406 alternative (and simpler) strategy would be to always throw away the
407 client's first keyex sent by older versions of the F-Secure client
408 since they're using an algorithm choice that's impossible to use, but
409 that implementation-specific approach doesn't generalise well to
410 other versions or other clients */
411 status = openPacketStreamSSH( &stream, sessionInfoPtr, SSH_MSG_KEXINIT );
412 if( cryptStatusError( status ) )
413 return( status );
414 streamBookmarkSetFullPacket( &stream, serverHelloLength );
415 status = exportVarsizeAttributeToStream( &stream, SYSTEM_OBJECT_HANDLE,
416 CRYPT_IATTRIBUTE_RANDOM_NONCE,
417 SSH2_COOKIE_SIZE );
418 if( cryptStatusOK( status ) )
419 {
420 int pkcAlgo;
421
422 /* If the server key is a non-ECC key then it can't be used with an
423 ECC keyex so we have to explicitly disable it (technically it's
424 possible to mix ECDH with RSA but this is more likely an error
425 than anything deliberate) */
426 status = krnlSendMessage( sessionInfoPtr->privateKey,
427 IMESSAGE_GETATTRIBUTE, &pkcAlgo,
428 CRYPT_CTXINFO_ALGO );
429 if( cryptStatusOK( status ) )
430 {
431 status = writeAlgoClassList( &stream, isEccAlgo( pkcAlgo ) ? \
432 SSH_ALGOCLASS_KEYEX : \
433 SSH_ALGOCLASS_KEYEX_NOECC );
434 }
435 }
436 if( cryptStatusOK( status ) )
437 status = writeAlgoList( &stream, handshakeInfo->algoStringPubkeyTbl,
438 handshakeInfo->algoStringPubkeyTblNoEntries );
439 if( cryptStatusOK( status ) )
440 status = writeAlgoClassList( &stream, SSH_ALGOCLASS_ENCR );
441 if( cryptStatusOK( status ) )
442 status = writeAlgoClassList( &stream, SSH_ALGOCLASS_ENCR );
443 if( cryptStatusOK( status ) )
444 status = writeAlgoClassList( &stream, SSH_ALGOCLASS_MAC );
445 if( cryptStatusOK( status ) )
446 status = writeAlgoClassList( &stream, SSH_ALGOCLASS_MAC );
447 if( cryptStatusOK( status ) )
448 status = writeAlgoClassList( &stream, SSH_ALGOCLASS_COPR );
449 if( cryptStatusOK( status ) )
450 status = writeAlgoClassList( &stream, SSH_ALGOCLASS_COPR );
451 if( cryptStatusOK( status ) )
452 {
453 writeUint32( &stream, 0 ); /* No language tag */
454 writeUint32( &stream, 0 );
455 sputc( &stream, 0 ); /* Don't try and guess the keyex */
456 status = writeUint32( &stream, 0 ); /* Reserved */
457 }
458 if( cryptStatusOK( status ) )
459 {
460 status = streamBookmarkComplete( &stream, &serverHelloPtr,
461 &serverHelloLength,
462 serverHelloLength );
463 }
464 INJECT_FAULT( SESSION_CORRUPT_HANDSHAKE, SESSION_CORRUPT_HANDSHAKE_SSH_1 );
465 if( cryptStatusOK( status ) )
466 status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
467 sMemDisconnect( &stream );
468 if( cryptStatusError( status ) )
469 return( status );
470 ANALYSER_HINT( serverHelloPtr != NULL );
471 INJECT_FAULT( SESSION_CORRUPT_HANDSHAKE, SESSION_CORRUPT_HANDSHAKE_SSH_2 );
472
473 /* While we wait for the client to digest our hello and send back its
474 response, create the context with the DH key */
475 status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
476 &handshakeInfo->serverKeySize, NULL, 0,
477 CRYPT_USE_DEFAULT );
478 if( cryptStatusError( status ) )
479 return( status );
480
481 /* Process the client hello packet and hash the client and server
482 hello. Since the entire encoded packet (including the type value)
483 is hashed we have to reconstruct this at the start of the client
484 hello packet */
485 status = processHelloSSH( sessionInfoPtr, handshakeInfo,
486 &clientHelloLength, TRUE );
487 if( cryptStatusError( status ) )
488 {
489 if( status != OK_SPECIAL )
490 return( status );
491
492 /* There's a guessed keyex following the client hello that we have
493 to skip later (we can't process it at this point because we still
494 need to hash the data sitting in the receive buffer) */
495 skipGuessedKeyex = TRUE;
496 }
497 REQUIRES( rangeCheck( 1, clientHelloLength,
498 sessionInfoPtr->receiveBufSize ) );
499 memmove( sessionInfoPtr->receiveBuffer + 1,
500 sessionInfoPtr->receiveBuffer, clientHelloLength );
501 sessionInfoPtr->receiveBuffer[ 0 ] = SSH_MSG_KEXINIT;
502 status = hashAsString( handshakeInfo->iExchangeHashContext,
503 sessionInfoPtr->receiveBuffer,
504 clientHelloLength + 1 );
505 if( cryptStatusOK( status ) && skipGuessedKeyex )
506 {
507 /* There's an incorrectly-guessed keyex following the client hello,
508 skip it */
509 status = readHSPacketSSH2( sessionInfoPtr,
510 ( handshakeInfo->requestedServerKeySize > 0 ) ? \
511 SSH_MSG_KEXDH_GEX_INIT : SSH_MSG_KEXDH_INIT,
512 ID_SIZE + sizeofString32( MIN_PKCSIZE ) );
513 }
514 if( !cryptStatusError( status ) ) /* readHSPSSH2() returns a length */
515 status = hashAsString( handshakeInfo->iExchangeHashContext,
516 serverHelloPtr, serverHelloLength );
517 if( cryptStatusError( status ) )
518 return( status );
519
520 /* If we're fuzzing the input then we're reading static data for which
521 we can't go beyond this point */
522 FUZZ_EXIT();
523
524 /* If we're using a nonstandard DH key value, negotiate a new key with
525 the client */
526 if( handshakeInfo->requestedServerKeySize > 0 )
527 {
528 status = processDHE( sessionInfoPtr, handshakeInfo );
529 if( cryptStatusError( status ) )
530 return( status );
531 }
532
533 #ifdef USE_ECDH
534 /* If we're using ECDH rather than DH we have to switch from DH contexts
535 and a DH exchange to the equivalent ECDH contexts and values */
536 if( handshakeInfo->isECDH )
537 {
538 krnlSendNotifier( handshakeInfo->iServerCryptContext,
539 IMESSAGE_DECREFCOUNT );
540 handshakeInfo->iServerCryptContext = CRYPT_ERROR;
541 status = initECDHcontextSSH( &handshakeInfo->iServerCryptContext,
542 &handshakeInfo->serverKeySize,
543 handshakeInfo->keyexAlgo );
544 if( cryptStatusError( status ) )
545 {
546 sMemDisconnect( &stream );
547 return( status );
548 }
549 }
550 #endif /* USE_ECDH */
551
552 /* Process the client keyex:
553
554 DH:
555 byte type = SSH_MSG_KEXDH_INIT / SSH_MSG_KEXDH_GEX_INIT
556 mpint y
557 ECDH:
558 byte type = SSH_MSG_KEX_ECDH_INIT
559 string q_c */
560 status = length = \
561 readHSPacketSSH2( sessionInfoPtr,
562 ( handshakeInfo->requestedServerKeySize > 0 ) ? \
563 SSH_MSG_KEXDH_GEX_INIT : SSH_MSG_KEXDH_INIT,
564 ID_SIZE + ( handshakeInfo->isECDH ? \
565 sizeofString32( MIN_PKCSIZE_ECCPOINT ) : \
566 sizeofString32( MIN_PKCSIZE ) ) );
567 if( cryptStatusError( status ) )
568 return( status );
569 sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
570 status = readRawObject32( &stream, handshakeInfo->clientKeyexValue,
571 MAX_ENCODED_KEYEXSIZE,
572 &handshakeInfo->clientKeyexValueLength );
573 sMemDisconnect( &stream );
574 if( cryptStatusOK( status ) )
575 {
576 if( handshakeInfo->isECDH )
577 {
578 if( !isValidECDHsize( handshakeInfo->clientKeyexValueLength,
579 handshakeInfo->serverKeySize, LENGTH_SIZE ) )
580 status = CRYPT_ERROR_BADDATA;
581 }
582 else
583 {
584 if( !isValidDHsize( handshakeInfo->clientKeyexValueLength,
585 handshakeInfo->serverKeySize, LENGTH_SIZE ) )
586 status = CRYPT_ERROR_BADDATA;
587 }
588 }
589 if( cryptStatusError( status ) )
590 {
591 retExt( CRYPT_ERROR_BADDATA,
592 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
593 "Invalid %s phase 1 keyex value",
594 handshakeInfo->isECDH ? "ECDH" : "DH" ) );
595 }
596 return( CRYPT_OK );
597 }
598
599 /* Exchange keys with the client */
600
601 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
exchangeServerKeys(INOUT SESSION_INFO * sessionInfoPtr,INOUT SSH_HANDSHAKE_INFO * handshakeInfo)602 static int exchangeServerKeys( INOUT SESSION_INFO *sessionInfoPtr,
603 INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
604 {
605 KEYAGREE_PARAMS keyAgreeParams;
606 STREAM stream;
607 void *keyPtr DUMMY_INIT_PTR, *dataPtr;
608 int keyLength, dataLength, sigLength DUMMY_INIT, packetOffset, status;
609
610 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
611 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
612
613 /* Create the server DH/ECDH value */
614 memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
615 status = krnlSendMessage( handshakeInfo->iServerCryptContext,
616 IMESSAGE_CTX_ENCRYPT, &keyAgreeParams,
617 sizeof( KEYAGREE_PARAMS ) );
618 if( cryptStatusError( status ) )
619 return( status );
620 sMemOpen( &stream, handshakeInfo->serverKeyexValue,
621 MAX_ENCODED_KEYEXSIZE );
622 if( handshakeInfo->isECDH )
623 {
624 status = writeString32( &stream, keyAgreeParams.publicValue,
625 keyAgreeParams.publicValueLen );
626 }
627 else
628 {
629 status = writeInteger32( &stream, keyAgreeParams.publicValue,
630 keyAgreeParams.publicValueLen );
631 }
632 if( cryptStatusOK( status ) )
633 handshakeInfo->serverKeyexValueLength = stell( &stream );
634 sMemDisconnect( &stream );
635 if( cryptStatusError( status ) )
636 return( status );
637
638 /* Build the DH phase 2 keyex packet:
639
640 DH + RSA/DSA
641 byte type = SSH_MSG_KEXDH_REPLY / SSH_MSG_KEXDH_GEX_REPLY
642 string [ server key/certificate ]
643 string "ssh-rsa" "ssh-dss"
644 mpint e p
645 mpint n q
646 mpint g
647 mpint y
648 mpint y'
649 string [ signature of handshake data ]
650 string "ssh-rsa" "ssh-dss"
651 string signature signature
652 ...
653
654 ECDH + ECDSA
655 byte SSH_MSG_KEX_ECDH_REPLY
656 string [ server key/certificate ]
657 string "ecdsa-sha2-*"
658 string "*" -- The "*" portion from the above field
659 string Q
660 string q_s
661 string [ signature of handshake data ]
662 string "ecdsa-sha2-*"
663 string signature
664 ...
665
666 The specification also makes provision for using X.509 and PGP keys,
667 but only so far as to say that keys and signatures are in "X.509 DER"
668 and "PGP" formats, neither of which actually explain what it is
669 that's sent or signed (and no-one on the SSH list can agree on what
670 they're supposed to look like) so we can't use either of them */
671 status = openPacketStreamSSH( &stream, sessionInfoPtr,
672 handshakeInfo->requestedServerKeySize ? \
673 SSH_MSG_KEXDH_GEX_REPLY : \
674 SSH_MSG_KEXDH_REPLY );
675 if( cryptStatusError( status ) )
676 return( status );
677 streamBookmarkSet( &stream, keyLength );
678 INJECT_FAULT( SESSION_WRONGCERT, SESSION_WRONGCERT_SSH_1 );
679 status = exportAttributeToStream( &stream, sessionInfoPtr->privateKey,
680 CRYPT_IATTRIBUTE_KEY_SSH );
681 if( cryptStatusOK( status ) )
682 status = streamBookmarkComplete( &stream, &keyPtr, &keyLength,
683 keyLength );
684 if( cryptStatusOK( status ) )
685 status = krnlSendMessage( handshakeInfo->iExchangeHashContext,
686 IMESSAGE_CTX_HASH, keyPtr, keyLength );
687 if( cryptStatusError( status ) )
688 {
689 sMemDisconnect( &stream );
690 return( status );
691 }
692 INJECT_FAULT( SESSION_WRONGCERT, SESSION_WRONGCERT_SSH_2 );
693 INJECT_FAULT( SESSION_BADSIG_DATA, SESSION_BADSIG_DATA_SSH_1 );
694 swrite( &stream, handshakeInfo->serverKeyexValue,
695 handshakeInfo->serverKeyexValueLength );
696 INJECT_FAULT( SESSION_BADSIG_DATA, SESSION_BADSIG_DATA_SSH_2 );
697
698 /* Complete phase 2 of the DH key agreement process to obtain the shared
699 secret value */
700 status = completeKeyex( sessionInfoPtr, handshakeInfo, TRUE );
701 if( cryptStatusError( status ) )
702 return( status );
703
704 /* Sign the hash. The reason for the min() part of the expression is
705 that iCryptCreateSignature() gets suspicious of very large buffer
706 sizes, for example when the user has specified the use of a 1MB send
707 buffer */
708 status = sMemGetDataBlockRemaining( &stream, &dataPtr, &dataLength );
709 if( cryptStatusOK( status ) )
710 {
711 status = iCryptCreateSignature( dataPtr,
712 min( dataLength, MAX_INTLENGTH_SHORT - 1 ),
713 &sigLength, CRYPT_IFORMAT_SSH,
714 sessionInfoPtr->privateKey,
715 handshakeInfo->iExchangeHashContext, NULL );
716 }
717 krnlSendNotifier( handshakeInfo->iExchangeHashContext,
718 IMESSAGE_DECREFCOUNT );
719 handshakeInfo->iExchangeHashContext = CRYPT_ERROR;
720 if( handshakeInfo->iExchangeHashAltContext != CRYPT_ERROR )
721 {
722 krnlSendNotifier( handshakeInfo->iExchangeHashAltContext,
723 IMESSAGE_DECREFCOUNT );
724 handshakeInfo->iExchangeHashAltContext = CRYPT_ERROR;
725 }
726 if( cryptStatusOK( status ) )
727 status = sSkip( &stream, sigLength, MAX_INTLENGTH_SHORT );
728 if( cryptStatusOK( status ) )
729 status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
730 if( cryptStatusError( status ) )
731 {
732 sMemDisconnect( &stream );
733 return( status );
734 }
735
736 /* Set up the security information required for the session. We have to
737 do this before sending the change cipherspec (rather than during the
738 pause while we're waiting for the other side's response) because we
739 can only tell the other side to switch to secure mode if we're sure
740 that we're already in that state ourselves */
741 status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
742 if( cryptStatusError( status ) )
743 return( status );
744
745 /* Build our change cipherspec message and send the whole mess through
746 to the client:
747 ...
748 byte type = SSH_MSG_NEWKEYS
749
750 After this point the write channel is in the secure state */
751 status = continuePacketStreamSSH( &stream, SSH_MSG_NEWKEYS,
752 &packetOffset );
753 if( cryptStatusOK( status ) )
754 status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset,
755 FALSE, TRUE );
756 if( cryptStatusOK( status ) )
757 status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
758 sMemDisconnect( &stream );
759 if( cryptStatusError( status ) )
760 return( status );
761 sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;
762 return( CRYPT_OK );
763 }
764
765 /* Complete the handshake with the client */
766
767 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
completeServerHandshake(INOUT SESSION_INFO * sessionInfoPtr,STDC_UNUSED SSH_HANDSHAKE_INFO * handshakeInfo)768 static int completeServerHandshake( INOUT SESSION_INFO *sessionInfoPtr,
769 STDC_UNUSED \
770 SSH_HANDSHAKE_INFO *handshakeInfo )
771 {
772 STREAM stream;
773 BOOLEAN userInfoPresent = FALSE;
774 int length, status;
775
776 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
777 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
778
779 /* If this is the first time through, set up the security information
780 and wait for the client's pre-authentication */
781 if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) )
782 {
783 BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
784 int stringLength;
785
786 /* If the caller has supplied user information to match against then
787 we require a match against the fixed caller-supplied information
788 rather than accepting what the client sends us and passing it
789 back to the caller to check */
790 if( findSessionInfo( sessionInfoPtr->attributeList,
791 CRYPT_SESSINFO_USERNAME ) != NULL )
792 userInfoPresent = TRUE;
793
794 /* Wait for the client's change cipherspec message. From this point
795 on the read channel is in the secure state */
796 status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_NEWKEYS,
797 ID_SIZE );
798 if( cryptStatusError( status ) )
799 return( status );
800 sessionInfoPtr->flags |= SESSION_ISSECURE_READ;
801
802 /* Wait for the client's pre-authentication packets, which aren't
803 used for any authentication but which are required anyway by the
804 protocol. For some reason SSH requires the use of two messages
805 where one would do, first an "I'm about to authenticate" packet
806 and then an "I'm authenticating" packet after that. Since the
807 former isn't useful for anything we clear it to get it out of the
808 way so that we can perform the actual authentication:
809
810 byte type = SSH_MSG_SERVICE_REQUEST
811 string service_name = "ssh-userauth"
812
813 byte type = SSH_MSG_SERVICE_ACCEPT
814 string service_name = "ssh-userauth" */
815 status = length = \
816 readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_REQUEST,
817 ID_SIZE + sizeofString32( 8 ) );
818 if( cryptStatusError( status ) )
819 return( status );
820 sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
821 status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
822 &stringLength );
823 sMemDisconnect( &stream );
824 if( cryptStatusError( status ) )
825 {
826 retExt( CRYPT_ERROR_BADDATA,
827 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
828 "Invalid service request packet" ) );
829 }
830 if( stringLength != 12 || \
831 memcmp( stringBuffer, "ssh-userauth", 12 ) )
832 {
833 retExt( CRYPT_ERROR_BADDATA,
834 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
835 "Invalid service request packet '%s'",
836 sanitiseString( stringBuffer, CRYPT_MAX_TEXTSIZE,
837 stringLength ) ) );
838 }
839 status = openPacketStreamSSH( &stream, sessionInfoPtr,
840 SSH_MSG_SERVICE_ACCEPT );
841 if( cryptStatusError( status ) )
842 return( status );
843 status = writeString32( &stream, "ssh-userauth", 12 );
844 if( cryptStatusOK( status ) )
845 status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
846 sMemDisconnect( &stream );
847 if( cryptStatusError( status ) )
848 return( status );
849 }
850
851 /* Process the client's authentication */
852 status = processServerAuth( sessionInfoPtr, userInfoPresent );
853 if( cryptStatusError( status ) )
854 return( status );
855
856 /* Handle the channel open */
857 status = length = \
858 readHSPacketSSH2( sessionInfoPtr, SSH_MSG_CHANNEL_OPEN,
859 ID_SIZE + sizeofString32( 4 ) + \
860 UINT32_SIZE + UINT32_SIZE + UINT32_SIZE );
861 if( cryptStatusError( status ) )
862 return( status );
863 sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
864 status = processChannelOpen( sessionInfoPtr, &stream );
865 sMemDisconnect( &stream );
866
867 return( status );
868 }
869
870 /****************************************************************************
871 * *
872 * Session Access Routines *
873 * *
874 ****************************************************************************/
875
876 STDC_NONNULL_ARG( ( 1 ) ) \
initSSH2serverProcessing(INOUT SSH_HANDSHAKE_INFO * handshakeInfo)877 void initSSH2serverProcessing( INOUT SSH_HANDSHAKE_INFO *handshakeInfo )
878 {
879 assert( isWritePtr( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) ) );
880
881 FNPTR_SET( handshakeInfo->beginHandshake, beginServerHandshake );
882 FNPTR_SET( handshakeInfo->exchangeKeys, exchangeServerKeys );
883 FNPTR_SET( handshakeInfo->completeHandshake, completeServerHandshake );
884 }
885 #endif /* USE_SSH */
886