/* * Copyright (C) 2001-2012 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* Functions to parse the SSLv2.0 hello message. */ #include "gnutls_int.h" #include "errors.h" #include "dh.h" #include "debug.h" #include "algorithms.h" #include "compress.h" #include "cipher.h" #include "buffers.h" #include "kx.h" #include "handshake.h" #include "num.h" #include "hash_int.h" #include "db.h" #include "extensions.h" #include "auth.h" #include "sslv2_compat.h" #include "constate.h" /* This selects the best supported ciphersuite from the ones provided */ static int _gnutls_handshake_select_v2_suite(gnutls_session_t session, uint8_t * data, unsigned int datalen) { unsigned int i, j; int ret; uint8_t *_data; int _datalen; _gnutls_handshake_log ("HSK[%p]: Parsing a version 2.0 client hello.\n", session); if (datalen % 3 != 0) { gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } _data = gnutls_malloc(datalen); if (_data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } i = _datalen = 0; for (j = 0; j < datalen; j += 3) { if (data[j] == 0) { memcpy(&_data[i], &data[j + 1], 2); i += 2; _datalen += 2; } } ret = _gnutls_server_select_suite(session, _data, _datalen); gnutls_free(_data); return ret; } /* Read a v2 client hello. Some browsers still use that beast! * However they set their version to 3.0 or 3.1. */ int _gnutls_read_client_hello_v2(gnutls_session_t session, uint8_t * data, unsigned int datalen) { uint16_t session_id_len = 0; int pos = 0; int ret = 0, sret = 0; uint16_t sizeOfSuites; gnutls_protocol_t adv_version; uint8_t rnd[GNUTLS_RANDOM_SIZE], major, minor; int len = datalen; uint16_t challenge; uint8_t session_id[GNUTLS_MAX_SESSION_ID_SIZE]; DECR_LEN(len, 2); _gnutls_handshake_log ("HSK[%p]: SSL 2.0 Hello: Client's version: %d.%d\n", session, data[pos], data[pos + 1]); major = data[pos]; minor = data[pos + 1]; set_adv_version(session, major, minor); adv_version = _gnutls_version_get(major, minor); ret = _gnutls_negotiate_version(session, adv_version, major, minor); if (ret < 0) { gnutls_assert(); return ret; } pos += 2; /* Read uint16_t cipher_spec_length */ DECR_LEN(len, 2); sizeOfSuites = _gnutls_read_uint16(&data[pos]); pos += 2; /* read session id length */ DECR_LEN(len, 2); session_id_len = _gnutls_read_uint16(&data[pos]); pos += 2; if (session_id_len > GNUTLS_MAX_SESSION_ID_SIZE) { gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* read challenge length */ DECR_LEN(len, 2); challenge = _gnutls_read_uint16(&data[pos]); pos += 2; if (challenge < 16 || challenge > GNUTLS_RANDOM_SIZE) { gnutls_assert(); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } /* call the user hello callback */ ret = _gnutls_user_hello_func(session, adv_version, major, minor); if (ret < 0) { if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { sret = GNUTLS_E_INT_RET_0; } else { gnutls_assert(); return ret; } } /* find an appropriate cipher suite */ DECR_LEN(len, sizeOfSuites); ret = _gnutls_handshake_select_v2_suite(session, &data[pos], sizeOfSuites); pos += sizeOfSuites; if (ret < 0) { gnutls_assert(); return ret; } /* check if the credentials (username, public key etc.) are ok */ if (_gnutls_get_kx_cred (session, _gnutls_cipher_suite_get_kx_algo(session->security_parameters. cipher_suite)) == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } /* set the mod_auth_st to the appropriate struct * according to the KX algorithm. This is needed since all the * handshake functions are read from there; */ session->internals.auth_struct = _gnutls_kx_auth_struct(_gnutls_cipher_suite_get_kx_algo (session->security_parameters. cipher_suite)); if (session->internals.auth_struct == NULL) { _gnutls_handshake_log ("HSK[%p]: SSL 2.0 Hello: Cannot find the appropriate handler for the KX algorithm\n", session); gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } /* read random new values -skip session id for now */ DECR_LEN(len, session_id_len); /* skip session id for now */ memcpy(session_id, &data[pos], session_id_len); pos += session_id_len; DECR_LEN(len, challenge); memset(rnd, 0, GNUTLS_RANDOM_SIZE); memcpy(&rnd[GNUTLS_RANDOM_SIZE - challenge], &data[pos], challenge); ret = _gnutls_set_client_random(session, rnd); if (ret < 0) return gnutls_assert_val(ret); /* generate server random value */ ret = _gnutls_set_server_random(session, NULL); if (ret < 0) return gnutls_assert_val(ret); session->security_parameters.timestamp = gnutls_time(NULL); /* RESUME SESSION */ DECR_LEN(len, session_id_len); ret = _gnutls_server_restore_session(session, session_id, session_id_len); if (ret == 0) { /* resumed! */ /* get the new random values */ memcpy(session->internals.resumed_security_parameters. server_random, session->security_parameters.server_random, GNUTLS_RANDOM_SIZE); memcpy(session->internals.resumed_security_parameters. client_random, session->security_parameters.client_random, GNUTLS_RANDOM_SIZE); session->internals.resumed = RESUME_TRUE; return 0; } else { _gnutls_generate_session_id(session->security_parameters. session_id, &session->security_parameters. session_id_size); session->internals.resumed = RESUME_FALSE; } _gnutls_epoch_set_compression(session, EPOCH_NEXT, GNUTLS_COMP_NULL); session->security_parameters.compression_method = GNUTLS_COMP_NULL; return sret; }