/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2019 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if !defined(__LWS_SSH_H__) #define __LWS_SSH_H__ #if defined(LWS_WITH_MBEDTLS) #include "mbedtls/sha1.h" #include "mbedtls/sha256.h" #include "mbedtls/sha512.h" #include "mbedtls/rsa.h" #endif #include "lws-plugin-ssh.h" #define LWS_SIZE_EC25519 32 #define LWS_SIZE_EC25519_PUBKEY 32 #define LWS_SIZE_EC25519_PRIKEY 64 #define LWS_SIZE_SHA256 32 #define LWS_SIZE_SHA512 64 #define LWS_SIZE_AES256_KEY 32 #define LWS_SIZE_AES256_IV 12 #define LWS_SIZE_AES256_MAC 16 #define LWS_SIZE_AES256_BLOCK 16 #define LWS_SIZE_CHACHA256_KEY (2 * 32) #define POLY1305_TAGLEN 16 #define POLY1305_KEYLEN 32 #define crypto_hash_sha512_BYTES 64U #define PEEK_U64(p) \ (((uint64_t)(((const uint8_t *)(p))[0]) << 56) | \ ((uint64_t)(((const uint8_t *)(p))[1]) << 48) | \ ((uint64_t)(((const uint8_t *)(p))[2]) << 40) | \ ((uint64_t)(((const uint8_t *)(p))[3]) << 32) | \ ((uint64_t)(((const uint8_t *)(p))[4]) << 24) | \ ((uint64_t)(((const uint8_t *)(p))[5]) << 16) | \ ((uint64_t)(((const uint8_t *)(p))[6]) << 8) | \ (uint64_t)(((const uint8_t *)(p))[7])) #define PEEK_U32(p) \ (((uint32_t)(((const uint8_t *)(p))[0]) << 24) | \ ((uint32_t)(((const uint8_t *)(p))[1]) << 16) | \ ((uint32_t)(((const uint8_t *)(p))[2]) << 8) | \ (uint32_t)(((const uint8_t *)(p))[3])) #define PEEK_U16(p) \ (((uint16_t)(((const uint8_t *)(p))[0]) << 8) | \ (uint16_t)(((const uint8_t *)(p))[1])) #define POKE_U64(p, v) \ do { \ const uint64_t __v = (v); \ ((uint8_t *)(p))[0] = (__v >> 56) & 0xff; \ ((uint8_t *)(p))[1] = (__v >> 48) & 0xff; \ ((uint8_t *)(p))[2] = (__v >> 40) & 0xff; \ ((uint8_t *)(p))[3] = (__v >> 32) & 0xff; \ ((uint8_t *)(p))[4] = (__v >> 24) & 0xff; \ ((uint8_t *)(p))[5] = (__v >> 16) & 0xff; \ ((uint8_t *)(p))[6] = (__v >> 8) & 0xff; \ ((uint8_t *)(p))[7] = __v & 0xff; \ } while (0) #define POKE_U32(p, v) \ do { \ const uint32_t __v = (v); \ ((uint8_t *)(p))[0] = (__v >> 24) & 0xff; \ ((uint8_t *)(p))[1] = (__v >> 16) & 0xff; \ ((uint8_t *)(p))[2] = (__v >> 8) & 0xff; \ ((uint8_t *)(p))[3] = __v & 0xff; \ } while (0) #define POKE_U16(p, v) \ do { \ const uint16_t __v = (v); \ ((uint8_t *)(p))[0] = (__v >> 8) & 0xff; \ ((uint8_t *)(p))[1] = __v & 0xff; \ } while (0) enum { SSH_MSG_DISCONNECT = 1, SSH_MSG_IGNORE = 2, SSH_MSG_UNIMPLEMENTED = 3, SSH_MSG_DEBUG = 4, SSH_MSG_SERVICE_REQUEST = 5, SSH_MSG_SERVICE_ACCEPT = 6, SSH_MSG_KEXINIT = 20, SSH_MSG_NEWKEYS = 21, /* 30 .. 49: KEX messages specific to KEX protocol */ SSH_MSG_KEX_ECDH_INIT = 30, SSH_MSG_KEX_ECDH_REPLY = 31, /* 50... userauth */ SSH_MSG_USERAUTH_REQUEST = 50, SSH_MSG_USERAUTH_FAILURE = 51, SSH_MSG_USERAUTH_SUCCESS = 52, SSH_MSG_USERAUTH_BANNER = 53, /* 60... publickey */ SSH_MSG_USERAUTH_PK_OK = 60, /* 80... connection */ SSH_MSG_GLOBAL_REQUEST = 80, SSH_MSG_REQUEST_SUCCESS = 81, SSH_MSG_REQUEST_FAILURE = 82, SSH_MSG_CHANNEL_OPEN = 90, SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91, SSH_MSG_CHANNEL_OPEN_FAILURE = 92, SSH_MSG_CHANNEL_WINDOW_ADJUST = 93, SSH_MSG_CHANNEL_DATA = 94, SSH_MSG_CHANNEL_EXTENDED_DATA = 95, SSH_MSG_CHANNEL_EOF = 96, SSH_MSG_CHANNEL_CLOSE = 97, SSH_MSG_CHANNEL_REQUEST = 98, SSH_MSG_CHANNEL_SUCCESS = 99, SSH_MSG_CHANNEL_FAILURE = 100, SSH_EXTENDED_DATA_STDERR = 1, SSH_CH_TYPE_SESSION = 1, SSH_CH_TYPE_SCP = 2, SSH_CH_TYPE_SFTP = 3, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, SSH_DISCONNECT_PROTOCOL_ERROR = 2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3, SSH_DISCONNECT_RESERVED = 4, SSH_DISCONNECT_MAC_ERROR = 5, SSH_DISCONNECT_COMPRESSION_ERROR = 6, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7, SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8, SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9, SSH_DISCONNECT_CONNECTION_LOST = 10, SSH_DISCONNECT_BY_APPLICATION = 11, SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12, SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13, SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14, SSH_DISCONNECT_ILLEGAL_USER_NAME = 15, SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1, SSH_OPEN_CONNECT_FAILED = 2, SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3, SSH_OPEN_RESOURCE_SHORTAGE = 4, KEX_STATE_EXPECTING_CLIENT_OFFER = 0, KEX_STATE_REPLIED_TO_OFFER, KEX_STATE_CRYPTO_INITIALIZED, SSH_KEYIDX_IV = 0, SSH_KEYIDX_ENC, SSH_KEYIDX_INTEG, /* things we may write on the connection */ SSH_WT_NONE = 0, SSH_WT_VERSION, SSH_WT_OFFER, SSH_WT_OFFER_REPLY, SSH_WT_SEND_NEWKEYS, SSH_WT_UA_ACCEPT, SSH_WT_UA_FAILURE, SSH_WT_UA_BANNER, SSH_WT_UA_PK_OK, SSH_WT_UA_SUCCESS, SSH_WT_CH_OPEN_CONF, SSH_WT_CH_FAILURE, SSH_WT_CHRQ_SUCC, SSH_WT_CHRQ_FAILURE, SSH_WT_SCP_ACK_OKAY, SSH_WT_SCP_ACK_ERROR, SSH_WT_CH_CLOSE, SSH_WT_CH_EOF, SSH_WT_WINDOW_ADJUST, SSH_WT_EXIT_STATUS, /* RX parser states */ SSH_INITIALIZE_TRANSIENT = 0, SSHS_IDSTRING, SSHS_IDSTRING_CR, SSHS_MSG_LEN, SSHS_MSG_PADDING, SSHS_MSG_ID, SSH_KEX_STATE_COOKIE, SSH_KEX_NL_KEX_ALGS_LEN, SSH_KEX_NL_KEX_ALGS, SSH_KEX_NL_SHK_ALGS_LEN, SSH_KEX_NL_SHK_ALGS, SSH_KEX_NL_EACTS_ALGS_LEN, SSH_KEX_NL_EACTS_ALGS, SSH_KEX_NL_EASTC_ALGS_LEN, SSH_KEX_NL_EASTC_ALGS, SSH_KEX_NL_MACTS_ALGS_LEN, SSH_KEX_NL_MACTS_ALGS, SSH_KEX_NL_MASTC_ALGS_LEN, SSH_KEX_NL_MASTC_ALGS, SSH_KEX_NL_CACTS_ALGS_LEN, SSH_KEX_NL_CACTS_ALGS, SSH_KEX_NL_CASTC_ALGS_LEN, SSH_KEX_NL_CASTC_ALGS, SSH_KEX_NL_LCTS_ALGS_LEN, SSH_KEX_NL_LCTS_ALGS, SSH_KEX_NL_LSTC_ALGS_LEN, SSH_KEX_NL_LSTC_ALGS, SSH_KEX_FIRST_PKT, SSH_KEX_RESERVED, SSH_KEX_STATE_ECDH_KEYLEN, SSH_KEX_STATE_ECDH_Q_C, SSHS_MSG_EAT_PADDING, SSH_KEX_STATE_SKIP, SSHS_GET_STRING_LEN, SSHS_GET_STRING, SSHS_GET_STRING_LEN_ALLOC, SSHS_GET_STRING_ALLOC, SSHS_DO_SERVICE_REQUEST, SSHS_DO_UAR_SVC, SSHS_DO_UAR_PUBLICKEY, SSHS_NVC_DO_UAR_CHECK_PUBLICKEY, SSHS_DO_UAR_SIG_PRESENT, SSHS_NVC_DO_UAR_ALG, SSHS_NVC_DO_UAR_PUBKEY_BLOB, SSHS_NVC_DO_UAR_SIG, SSHS_GET_U32, SSHS_NVC_CHOPEN_TYPE, SSHS_NVC_CHOPEN_SENDER_CH, SSHS_NVC_CHOPEN_WINSIZE, SSHS_NVC_CHOPEN_PKTSIZE, SSHS_NVC_CHRQ_RECIP, SSHS_NVC_CHRQ_TYPE, SSHS_CHRQ_WANT_REPLY, SSHS_NVC_CHRQ_TERM, SSHS_NVC_CHRQ_TW, SSHS_NVC_CHRQ_TH, SSHS_NVC_CHRQ_TWP, SSHS_NVC_CHRQ_THP, SSHS_NVC_CHRQ_MODES, SSHS_NVC_CHRQ_ENV_NAME, SSHS_NVC_CHRQ_ENV_VALUE, SSHS_NVC_CHRQ_EXEC_CMD, SSHS_NVC_CHRQ_SUBSYSTEM, SSHS_NVC_CH_EOF, SSHS_NVC_CH_CLOSE, SSHS_NVC_CD_RECIP, SSHS_NVC_CD_DATA, SSHS_NVC_CD_DATA_ALLOC, SSHS_NVC_WA_RECIP, SSHS_NVC_WA_ADD, SSHS_NVC_DISCONNECT_REASON, SSHS_NVC_DISCONNECT_DESC, SSHS_NVC_DISCONNECT_LANG, SSHS_SCP_COLLECTSTR = 0, SSHS_SCP_PAYLOADIN = 1, /* from https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13 */ SECSH_FILEXFER_VERSION = 6, /* sftp packet types */ SSH_FXP_INIT = 1, SSH_FXP_VERSION = 2, SSH_FXP_OPEN = 3, SSH_FXP_CLOSE = 4, SSH_FXP_READ = 5, SSH_FXP_WRITE = 6, SSH_FXP_LSTAT = 7, SSH_FXP_FSTAT = 8, SSH_FXP_SETSTAT = 9, SSH_FXP_FSETSTAT = 10, SSH_FXP_OPENDIR = 11, SSH_FXP_READDIR = 12, SSH_FXP_REMOVE = 13, SSH_FXP_MKDIR = 14, SSH_FXP_RMDIR = 15, SSH_FXP_REALPATH = 16, SSH_FXP_STAT = 17, SSH_FXP_RENAME = 18, SSH_FXP_READLINK = 19, SSH_FXP_LINK = 21, SSH_FXP_BLOCK = 22, SSH_FXP_UNBLOCK = 23, SSH_FXP_STATUS = 101, SSH_FXP_HANDLE = 102, SSH_FXP_DATA = 103, SSH_FXP_NAME = 104, SSH_FXP_ATTRS = 105, SSH_FXP_EXTENDED = 200, SSH_FXP_EXTENDED_REPLY = 201, /* sftp return codes */ SSH_FX_OK = 0, SSH_FX_EOF = 1, SSH_FX_NO_SUCH_FILE = 2, SSH_FX_PERMISSION_DENIED = 3, SSH_FX_FAILURE = 4, SSH_FX_BAD_MESSAGE = 5, SSH_FX_NO_CONNECTION = 6, SSH_FX_CONNECTION_LOST = 7, SSH_FX_OP_UNSUPPORTED = 8, SSH_FX_INVALID_HANDLE = 9, SSH_FX_NO_SUCH_PATH = 10, SSH_FX_FILE_ALREADY_EXISTS = 11, SSH_FX_WRITE_PROTECT = 12, SSH_FX_NO_MEDIA = 13, SSH_FX_NO_SPACE_ON_FILESYSTEM = 14, SSH_FX_QUOTA_EXCEEDED = 15, SSH_FX_UNKNOWN_PRINCIPAL = 16, SSH_FX_LOCK_CONFLICT = 17, SSH_FX_DIR_NOT_EMPTY = 18, SSH_FX_NOT_A_DIRECTORY = 19, SSH_FX_INVALID_FILENAME = 20, SSH_FX_LINK_LOOP = 21, SSH_FX_CANNOT_DELETE = 22, SSH_FX_INVALID_PARAMETER = 23, SSH_FX_FILE_IS_A_DIRECTORY = 24, SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25, SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26, SSH_FX_DELETE_PENDING = 27, SSH_FX_FILE_CORRUPT = 28, SSH_FX_OWNER_INVALID = 29, SSH_FX_GROUP_INVALID = 30, SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31, SSH_PENDING_TIMEOUT_CONNECT_TO_SUCCESSFUL_AUTH = PENDING_TIMEOUT_USER_REASON_BASE + 0, SSH_AUTH_STATE_NO_AUTH = 0, SSH_AUTH_STATE_GAVE_AUTH_IGNORE_REQS = 1, }; #define LWS_SSH_INITIAL_WINDOW 16384 struct lws_ssh_userauth { struct lws_genhash_ctx hash_ctx; char *username; char *service; char *alg; uint8_t *pubkey; uint32_t pubkey_len; uint8_t *sig; uint32_t sig_len; char sig_present; }; struct lws_ssh_keys { /* 3 == SSH_KEYIDX_IV (len=4), SSH_KEYIDX_ENC, SSH_KEYIDX_INTEG */ uint8_t key[3][LWS_SIZE_CHACHA256_KEY]; /* opaque allocation made when cipher activated */ void *cipher; uint8_t MAC_length; uint8_t padding_alignment; /* block size */ uint8_t valid:1; uint8_t full_length:1; }; struct lws_kex { uint8_t kex_r[256]; uint8_t Q_C[LWS_SIZE_EC25519]; /* client eph public key aka 'e' */ uint8_t eph_pri_key[LWS_SIZE_EC25519]; /* server eph private key */ uint8_t Q_S[LWS_SIZE_EC25519]; /* server ephemeral public key */ uint8_t kex_cookie[16]; uint8_t *I_C; /* malloc'd copy of client KEXINIT payload */ uint8_t *I_S; /* malloc'd copy of server KEXINIT payload */ uint32_t I_C_payload_len; uint32_t I_C_alloc_len; uint32_t I_S_payload_len; uint32_t kex_r_len; uint8_t match_bitfield; uint8_t newkeys; /* which sides newkeys have been applied */ struct lws_ssh_keys keys_next_cts; struct lws_ssh_keys keys_next_stc; }; struct lws_subprotocol_scp { char fp[128]; uint64_t len; uint32_t attr; char cmd; char ips; }; typedef union { struct lws_subprotocol_scp scp; } lws_subprotocol; struct per_session_data__sshd; struct lws_ssh_channel { struct lws_ssh_channel *next; struct per_session_data__sshd *pss; lws_subprotocol *sub; /* NULL, or allocated subprotocol state */ void *priv; /* owned by user code */ int type; uint32_t server_ch; uint32_t sender_ch; int32_t window; int32_t peer_window_est; uint32_t max_pkt; uint32_t spawn_pid; int retcode; uint8_t scheduled_close:1; uint8_t sent_close:1; uint8_t received_close:1; }; struct per_vhost_data__sshd; struct per_session_data__sshd { struct per_session_data__sshd *next; struct per_vhost_data__sshd *vhd; struct lws *wsi; struct lws_kex *kex; char *disconnect_desc; uint8_t K[LWS_SIZE_EC25519]; /* shared secret */ uint8_t session_id[LWS_SIZE_SHA256]; /* H from first working KEX */ char name[64]; char last_auth_req_username[32]; char last_auth_req_service[32]; struct lws_ssh_keys active_keys_cts; struct lws_ssh_keys active_keys_stc; struct lws_ssh_userauth *ua; struct lws_ssh_channel *ch_list; struct lws_ssh_channel *ch_temp; uint8_t *last_alloc; union { struct lws_ssh_pty pty; char aux[64]; } args; uint32_t ssh_sequence_ctr_cts; uint32_t ssh_sequence_ctr_stc; uint64_t payload_bytes_cts; uint64_t payload_bytes_stc; uint32_t disconnect_reason; char V_C[64]; /* Client version String */ uint8_t packet_assembly[2048]; uint32_t pa_pos; uint32_t msg_len; uint32_t pos; uint32_t len; uint32_t ctr; uint32_t npos; uint32_t reason; uint32_t channel_doing_spawn; int next_ch_num; uint8_t K_S[LWS_SIZE_EC25519]; /* server public key */ uint32_t copy_to_I_C:1; uint32_t okayed_userauth:1; uint32_t sent_banner:1; uint32_t seen_auth_req_before:1; uint32_t serviced_stderr_last:1; uint32_t kex_state; uint32_t chrq_server_port; uint32_t ch_recip; uint32_t count_auth_attempts; char parser_state; char state_after_string; char first_coming; uint8_t rq_want_reply; uint8_t ssh_auth_state; uint8_t msg_id; uint8_t msg_padding; uint8_t write_task[8]; struct lws_ssh_channel *write_channel[8]; uint8_t wt_head, wt_tail; }; struct per_vhost_data__sshd { struct lws_context *context; struct lws_vhost *vhost; const struct lws_protocols *protocol; struct per_session_data__sshd *live_pss_list; const struct lws_ssh_ops *ops; }; struct host_keys { uint8_t *data; uint32_t len; }; extern struct host_keys host_keys[]; extern int crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n, const unsigned char *p); extern int ed25519_key_parse(uint8_t *p, size_t len, char *type, size_t type_len, uint8_t *pub, uint8_t *pri); extern int kex_ecdh(struct per_session_data__sshd *pss, uint8_t *result, uint32_t *plen); extern uint32_t lws_g32(uint8_t **p); extern uint32_t lws_p32(uint8_t *p, uint32_t v); extern int lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len); extern const char *lws_V_S; extern int lws_chacha_activate(struct lws_ssh_keys *keys); extern void lws_chacha_destroy(struct lws_ssh_keys *keys); extern uint32_t lws_chachapoly_get_length(struct lws_ssh_keys *keys, uint32_t seq, const uint8_t *in4); extern void poly1305_auth(u_char out[POLY1305_TAGLEN], const u_char *m, size_t inlen, const u_char key[POLY1305_KEYLEN]); extern int lws_chacha_decrypt(struct lws_ssh_keys *keys, uint32_t seq, const uint8_t *ct, uint32_t len, uint8_t *pt); extern int lws_chacha_encrypt(struct lws_ssh_keys *keys, uint32_t seq, const uint8_t *ct, uint32_t len, uint8_t *pt); extern void lws_pad_set_length(struct per_session_data__sshd *pss, void *start, uint8_t **p, struct lws_ssh_keys *keys); extern size_t get_gen_server_key_25519(struct per_session_data__sshd *pss, uint8_t *b, size_t len); extern int crypto_sign_ed25519(unsigned char *sm, unsigned long long *smlen, const unsigned char *m, size_t mlen, const unsigned char *sk); extern int crypto_sign_ed25519_keypair(struct lws_context *context, uint8_t *pk, uint8_t *sk); #endif