📄 ssh.c
字号:
/****************************************************************************
* *
* cryptlib SSHv1/v2 Session Management *
* Copyright Peter Gutmann 1998-2002 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "stream.h"
#include "session.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../keymgmt/stream.h"
#include "../session/session.h"
#else
#include "crypt.h"
#include "keymgmt/stream.h"
#include "session/session.h"
#endif /* Compiler-specific includes */
/* Default SSH port */
#define SSH_PORT 22
/* Various SSH constants */
#define ID_SIZE 1 /* ID byte */
#define LENGTH_SIZE 4 /* Size of packet length field */
#define UINT_SIZE 4 /* Size of integer value */
#define PADLENGTH_SIZE 1 /* Size of padding length field */
#define BOOLEAN_SIZE 1 /* Size of boolean value */
#define CRC_SIZE 4 /* Size of CRC value */
#define MPI_LENGTH_SIZE 2 /* Size of MPI length field */
#define SSH_COOKIE_SIZE 8 /* Size of anti-spoofing cookie */
#define SSH2_COOKIE_SIZE 16 /* Size of SSHv2 cookie */
#define SSH_SESSIONID_SIZE 16 /* Size of session ID */
#define SSH_HEADER_SIZE 5 /* Size of the SSHv1 packet header */
#define SSH2_HEADER_SIZE 5 /* Size of the SSHv2 packet header */
#define SSH2_PAYLOAD_HEADER_SIZE 9 /* Size of SSHv2 inner payload header */
#define SSH_SECRET_SIZE 32 /* Size of SSH shared secret */
#define SSH2_FIXED_KEY_SIZE 16 /* Size of SSHv2 fixed-size keys */
#define SSH_CHALLENGE_SIZE 32 /* Size of RSA auth.challenge */
#define SSH_RESPONSE_SIZE 16 /* Size of RSA auth.response */
/* Default and maximum SSH send/receive buffer sizes */
#define BUFFER_SIZE ( 16384 + 64 )
#define MAX_BUFFER_SIZE 262144L
/* Various data sizes used for read-ahead and buffering. The minimum SSH
packet size is used to determine how much data we can read when reading
a packet header, the remainder size is how much extra data we've got
available from the header once we've extracted the length, and the
maximum SSH header size is used to determine how much space we need to
reserve at the start of the buffer when encoding SSHv1's variable-length
data packets (SSHv2 has a fixed header size so this isn't a problem any
more) */
#define MIN_PACKET_SIZE 16
#define PACKET_REMAINDER_SIZE ( MIN_PACKET_SIZE - LENGTH_SIZE )
#define MAX_HEADER_SIZE ( LENGTH_SIZE + 8 + ID_SIZE + LENGTH_SIZE )
/* SSH ID information */
#define SSH_ID "SSH-" /* Start of SSH ID */
#define SSH_ID_SIZE 4 /* Size of SSH ID */
#define SSH_ID_MAX_SIZE 255 /* Max.size of SSHv2 ID string */
#define SSH_ID_STRING "SSH-1.5-cryptlib"
#define SSH2_ID_STRING "SSH-2.0-cryptlib" /* cryptlib SSH ID strings */
/* SSHv1 packet types */
#define SSH_MSG_DISCONNECT 1 /* Disconnect session */
#define SSH_SMSG_PUBLIC_KEY 2 /* Server public key */
#define SSH_CMSG_SESSION_KEY 3 /* Encrypted session key */
#define SSH_CMSG_USER 4 /* User name */
#define SSH_CMSG_AUTH_RSA 6 /* RSA public key */
#define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* RSA challenge from server */
#define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* RSA response from client */
#define SSH_CMSG_AUTH_PASSWORD 9 /* Password */
#define SSH_CMSG_REQUEST_PTY 10 /* Request a pty */
#define SSH_CMSG_EXEC_SHELL 12 /* Request a shell */
#define SSH_CMSG_EXEC_CMD 13 /* Request command execution */
#define SSH_SMSG_SUCCESS 14 /* Success status message */
#define SSH_SMSG_FAILURE 15 /* Failure status message */
#define SSH_CMSG_STDIN_DATA 16 /* Data from client stdin */
#define SSH_SMSG_STDOUT_DATA 17 /* Data from server stdout */
#define SSH_SMSG_EXITSTATUS 20 /* Exit status of command run on server */
#define SSH_MSG_IGNORE 32 /* No-op */
#define SSH_CMSG_EXIT_CONFIRMATION 33 /* Client response to server exitstatus */
#define SSH_MSG_DEBUG 36 /* Debugging/informational message */
#define SSH_CMSG_MAX_PACKET_SIZE 38 /* Maximum data packet size */
/* Further SSHv1 packet types which aren't used but which we need to
recognise */
#define SSH_CMSG_PORT_FORWARD_REQUEST 28
#define SSH_CMSG_AGENT_REQUEST_FORWARDING 30
#define SSH_CMSG_X11_REQUEST_FORWARDING 34
#define SSH_CMSG_REQUEST_COMPRESSION 37
/* SSHv2 packet types. There is some overlap with SSHv1, but an annoying
number of messages have the same name but different values */
#define SSH2_MSG_DISCONNECT 1 /* Disconnect session */
#define SSH2_MSG_IGNORE 2 /* No-op */
#define SSH2_MSG_DEBUG 4 /* No-op */
#define SSH2_MSG_SERVICE_REQUEST 5 /* Request authentiction */
#define SSH2_MSG_SERVICE_ACCEPT 6 /* Acknowledge request */
#define SSH2_MSG_KEXINIT 20 /* Hello */
#define SSH2_MSG_NEWKEYS 21 /* Change cipherspec */
#define SSH2_MSG_KEXDH_INIT 30 /* DH, phase 1 */
#define SSH2_MSG_KEXDH_REPLY 31 /* DH, phase 2 */
#define SSH2_MSG_USERAUTH_REQUEST 50 /* Request authentication */
#define SSH2_MSG_USERAUTH_FAILURE 51 /* Authentication failed */
#define SSH2_MSG_USERAUTH_SUCCESS 52 /* Authentication succeeded */
#define SSH2_MSG_USERAUTH_BANNER 53 /* No-op */
#define SSH2_MSG_CHANNEL_OPEN 90 /* Open a channel over SSH link */
#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* Channel open succeeded */
#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* No-op */
#define SSH2_MSG_CHANNEL_DATA 94 /* Data */
#define SSH2_MSG_CHANNEL_REQUEST 98 /* Open' a channel' over SSH link */
#define SSH2_MSG_CHANNEL_SUCCESS 99 /* Channel' open' succeeded */
/* Special-case expected-packet-type values which are passed to readPacket()
to handle situations where more than one return value is valid. CMSG_USER
can return failure meaning "no password" even if there's no actual
failure, CMSG_AUTH_PASSWORD can return SMSG_FAILURE which indicates a
wrong password used iff it's a response to the client sending a password,
and MSG_USERAUTH_REQUEST can similarly return a failure or success
response.
In addition to these types there's a "any" type which is used during the
setup negotiation which will accept any (non-error) packet type and return
the type as the return code */
#define SSH_MSG_SPECIAL_USEROPT 500 /* Value to handle SSHv1 user name */
#define SSH_MSG_SPECIAL_PWOPT 501 /* Value to handle SSHv1 password */
#define SSH_MSG_SPECIAL_USERAUTH 502 /* Value to handle SSHv2 combined auth.*/
#define SSH_MSG_SPECIAL_RSAOPT 503 /* Value to handle RSA challenge */
#define SSH_MSG_SPECIAL_ANY 504 /* Any packet type */
/* SSHv1 cipher types */
#define SSH_CIPHER_NONE 0 /* No encryption */
#define SSH_CIPHER_IDEA 1 /* IDEA/CFB */
#define SSH_CIPHER_DES 2 /* DES/CBC */
#define SSH_CIPHER_3DES 3 /* 3DES/inner-CBC */
#define SSH_CIPHER_TSS 4 /* Deprecated */
#define SSH_CIPHER_RC4 5 /* RC4 */
#define SSH_CIPHER_BLOWFISH 6 /* Blowfish */
#define SSH_CIPHER_CRIPPLED 7 /* Reserved, from ssh 1.2.x source */
/* SSHv1 authentication types */
#define SSH_AUTH_RHOSTS 1 /* .rhosts or /etc/hosts.equiv */
#define SSH_AUTH_RSA 2 /* RSA challenge-response */
#define SSH_AUTH_PASSWORD 3 /* Password */
#define SSH_AUTH_RHOSTS_RSA 4 /* .rhosts with RSA challenge-response */
#define SSH_AUTH_TIS 5 /* TIS authsrv */
#define SSH_AUTH_KERBEROS 6 /* Kerberos */
#define SSH_PASS_KERBEROS_TGT 7 /* Kerberos TGT-passing */
/* Macros to evaluate the effective length for a packet and the number of
padding bytes to add to a packet to make it a multiple of 8 bytes (SSHv1)
or the cipher block size (SSHv2) long.
SSHv2 uses a peculiar means of determining the padding length which
requires a minimum padding size of 4 bytes. This isn't easy to evaluate
in one go without using intermediate variables, which in turn is
difficult to do in a macro. Because of this we have to break the
operation into two parts, storing the output of the first part in a temp.
variable. The first part gets the remainder, the second part evaluates
the padding size, scaling it upwards by an extra block if the remainder
size is within 4 bytes of the block size, which would cause the pad length
to be less than 4 bytes */
#define getEffectiveLength1( length ) \
( ID_SIZE + ( length ) + CRC_SIZE )
#define getEffectiveLength2( length ) \
( PADLENGTH_SIZE + ( length ) )
#define getPadLength1( length ) \
( 8 - ( getEffectiveLength1( length ) & 7 ) )
#define getPrePadLength2( length, blockSize ) \
( ( LENGTH_SIZE + getEffectiveLength2( length ) ) & ( ( blockSize ) - 1 ) )
#define getFinalPadLength2( length, blockSize ) \
( ( ( length ) > ( blockSize ) - 4 ) ? \
( ( 2 * ( blockSize ) ) - ( length ) ) : ( ( blockSize ) - ( length ) ) )
/* SSH handshake state information. This is passed around various
subfunctions which handle individual parts of the handshake */
typedef struct SH {
/* SSHv1 session state information/SSHv2 exchange hash */
BYTE cookie[ SSH_COOKIE_SIZE ]; /* Anti-spoofing cookie */
BYTE sessionID[ CRYPT_MAX_HASHSIZE ]; /* Session ID/exchange hash */
int sessionIDlength;
CRYPT_CONTEXT iExchangeHashcontext; /* Hash of exchanged info */
/* Information needed to compute the session ID. SSHv1 requires the
host and server key modulus, SSHv2 requires the client DH value
(along with various other things, but these are hashed inline).
Since the data fields are rather large and also disjoint, we alias
one to the other */
BYTE hostModulus[ CRYPT_MAX_PKCSIZE ], serverModulus[ CRYPT_MAX_PKCSIZE ];
#define clientKeyexValue hostModulus
int hostModulusLength, serverModulusLength, clientKeyexValueLength;
/* Encryption algorithm and key information */
CRYPT_ALGO pubkeyAlgo; /* Host signature algo */
BYTE secretValue[ CRYPT_MAX_PKCSIZE ]; /* Shared secret value */
int secretValueLength;
/* Short-term server key (SSHv1) or DH key agreement context (SSHv2).
The long-term host key is stored as the session info
iKeyexCryptContext for the client and privateKey for the server */
CRYPT_CONTEXT iServerCryptContext;
/* Function pointers to SSHv1 or SSHv2 functions */
int ( *beginClientHandshake )( SESSION_INFO *sessionInfoPtr,
struct SH *handshakeInfo );
int ( *exchangeClientKeys )( SESSION_INFO *sessionInfoPtr,
struct SH *handshakeInfo );
int ( *completeClientHandshake )( SESSION_INFO *sessionInfoPtr,
struct SH *handshakeInfo );
} SSH_HANDSHAKE_INFO;
/* Mapping of SSHv2 algorithm names to cryptlib algorithm IDs, in
preferred algorithm order */
typedef struct {
const char *name; /* Algorithm name */
const CRYPT_ALGO algo; /* Algorithm ID */
} ALGO_STRING_INFO;
static const ALGO_STRING_INFO algoStringCoprTbl[] = {
{ "none", CRYPT_ALGO_NONE },
{ NULL, 0 }
};
static const ALGO_STRING_INFO algoStringPubkeyTbl[] = {
{ "ssh-rsa", CRYPT_ALGO_RSA },
{ "ssh-dsa", CRYPT_ALGO_DSA },
{ NULL, 0 }
};
static const ALGO_STRING_INFO algoStringEncrTbl[] = {
{ "3des-cbc", CRYPT_ALGO_3DES },
{ "blowfish-cbc", CRYPT_ALGO_BLOWFISH },
{ "aes128-cbc", CRYPT_ALGO_AES },
{ "cast128-cbc", CRYPT_ALGO_CAST },
{ "idea-cbc", CRYPT_ALGO_IDEA },
{ NULL, 0 }
};
static const ALGO_STRING_INFO algoStringKeyexTbl[] = {
{ "diffie-hellman-group1-sha1", CRYPT_ALGO_DH },
{ NULL, 0 }
};
static const ALGO_STRING_INFO algoStringMACTbl[] = {
{ "hmac-sha1", CRYPT_ALGO_HMAC_SHA },
{ "hmac-md5", CRYPT_ALGO_HMAC_MD5 },
{ NULL, 0 }
};
static const ALGO_STRING_INFO algoStringUserauthentTbl[] = {
{ "password", CRYPT_ALGO_DES },
{ "publickey", CRYPT_ALGO_RSA },
{ NULL, 0 }
};
static const ALGO_STRING_INFO algoStringMapTbl[] = {
{ "none", CRYPT_ALGO_NONE },
{ "ssh-rsa", CRYPT_ALGO_RSA },
{ "ssh-dsa", CRYPT_ALGO_DSA },
{ "3des-cbc", CRYPT_ALGO_3DES },
{ "blowfish-cbc", CRYPT_ALGO_BLOWFISH },
{ "aes128-cbc", CRYPT_ALGO_AES },
{ "idea-cbc", CRYPT_ALGO_IDEA },
{ "cast128-cbc", CRYPT_ALGO_CAST },
{ "diffie-hellman-group1-sha1", CRYPT_ALGO_DH },
{ "hmac-sha1", CRYPT_ALGO_HMAC_SHA },
{ "hmac-md5", CRYPT_ALGO_HMAC_MD5 },
{ NULL, CRYPT_ALGO_NONE }
};
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Initialise and destroy the handshake state information */
static int initHandshakeInfo( SSH_HANDSHAKE_INFO *handshakeInfo )
{
/* Initialise the handshake state info values */
memset( handshakeInfo, 0, sizeof( SSH_HANDSHAKE_INFO ) );
handshakeInfo->iExchangeHashcontext = \
handshakeInfo->iServerCryptContext = CRYPT_ERROR;
return( CRYPT_OK );
}
static void destroyHandshakeInfo( SSH_HANDSHAKE_INFO *handshakeInfo )
{
/* Destroy any active contexts */
if( handshakeInfo->iExchangeHashcontext != CRYPT_ERROR )
krnlSendNotifier( handshakeInfo->iExchangeHashcontext,
RESOURCE_IMESSAGE_DECREFCOUNT );
if( handshakeInfo->iServerCryptContext != CRYPT_ERROR )
krnlSendNotifier( handshakeInfo->iServerCryptContext,
RESOURCE_IMESSAGE_DECREFCOUNT );
zeroise( handshakeInfo, sizeof( SSH_HANDSHAKE_INFO ) );
}
/* Initialise and destroy the security contexts */
static void destroySecurityContexts( SESSION_INFO *sessionInfoPtr )
{
/* Destroy any active contexts */
if( sessionInfoPtr->iKeyexCryptContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iKeyexCryptContext,
RESOURCE_IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iKeyexCryptContext = CRYPT_ERROR;
}
if( sessionInfoPtr->iCryptInContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iCryptInContext,
RESOURCE_IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCryptInContext = CRYPT_ERROR;
}
if( sessionInfoPtr->iCryptOutContext != CRYPT_ERROR )
{
krnlSendNotifier( sessionInfoPtr->iCryptOutContext,
RESOURCE_IMESSAGE_DECREFCOUNT );
sessionInfoPtr->iCryptOutContext = CRYPT_ERROR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -