/* * Copyright (C) 2002,2003 Nikos Mavroyanopoulos * * This file is part of GNUTLS. * * The GNUTLS library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Functions to manipulate the session (gnutls_int.h), and some other stuff * are included here. The file's name is traditionaly gnutls_state even if the * state has been renamed to session. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CHECK_AUTH(auth, ret) if (gnutls_auth_get_type(session) != auth) { \ gnutls_assert(); \ return ret; \ } void _gnutls_session_cert_type_set( gnutls_session session, gnutls_certificate_type ct) { session->security_parameters.cert_type = ct; } /** * gnutls_cipher_get - Returns the currently used cipher. * @session: is a &gnutls_session structure. * * Returns the currently used cipher. **/ gnutls_cipher_algorithm gnutls_cipher_get( gnutls_session session) { return session->security_parameters.read_bulk_cipher_algorithm; } /** * gnutls_certificate_type_get - Returns the currently used certificate type. * @session: is a &gnutls_session structure. * * Returns the currently used certificate type. The certificate type * is by default X.509, unless it is negotiated as a TLS extension. * **/ gnutls_certificate_type gnutls_certificate_type_get( gnutls_session session) { return session->security_parameters.cert_type; } /** * gnutls_kx_get - Returns the key exchange algorithm. * @session: is a &gnutls_session structure. * * Returns the key exchange algorithm used in the last handshake. **/ gnutls_kx_algorithm gnutls_kx_get( gnutls_session session) { return session->security_parameters.kx_algorithm; } /** * gnutls_mac_get - Returns the currently used mac algorithm. * @session: is a &gnutls_session structure. * * Returns the currently used mac algorithm. **/ gnutls_mac_algorithm gnutls_mac_get( gnutls_session session) { return session->security_parameters.read_mac_algorithm; } /** * gnutls_compression_get - Returns the currently used compression algorithm. * @session: is a &gnutls_session structure. * * Returns the currently used compression method. **/ gnutls_compression_method gnutls_compression_get( gnutls_session session) { return session->security_parameters.read_compression_algorithm; } int _gnutls_session_cert_type_supported( gnutls_session session, gnutls_certificate_type cert_type) { uint i; if (session->internals.cert_type_priority.algorithms==0 && cert_type == DEFAULT_CERT_TYPE) return 0; for (i=0;iinternals.cert_type_priority.algorithms;i++) { if (session->internals.cert_type_priority.priority[i] == cert_type) { return 0; /* ok */ } } return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; } /* This function will clear all the variables in internals * structure within the session, which depend on the current handshake. * This is used to allow further handshakes. */ void _gnutls_handshake_internal_state_clear( gnutls_session session) { session->internals.extensions_sent_size = 0; /* by default no selected certificate */ session->internals.proposed_record_size = DEFAULT_MAX_RECORD_SIZE; session->internals.adv_version_major = 0; session->internals.adv_version_minor = 0; session->internals.v2_hello = 0; memset( &session->internals.handshake_header_buffer, 0, sizeof(HANDSHAKE_HEADER_BUFFER)); session->internals.adv_version_minor = 0; session->internals.adv_version_minor = 0; session->internals.direction = 0; /* use out of band data for the last * handshake messages received. */ session->internals.last_handshake_in = -1; session->internals.last_handshake_out = -1; session->internals.resumable = RESUME_TRUE; } #define MIN_DH_BITS 727 /** * gnutls_init - This function initializes the session to null (null encryption etc...). * @con_end: is used to indicate if this session is to be used for server or * client. Can be one of GNUTLS_CLIENT and GNUTLS_SERVER. * @session: is a pointer to a &gnutls_session structure. * * This function initializes the current session to null. Every session * must be initialized before use, so internal structures can be allocated. * This function allocates structures which can only be free'd * by calling gnutls_deinit(). Returns zero on success. **/ int gnutls_init(gnutls_session * session, gnutls_connection_end con_end) { *session = gnutls_calloc(1, sizeof(struct gnutls_session_int)); if (*session==NULL) return GNUTLS_E_MEMORY_ERROR; (*session)->security_parameters.entity = con_end; /* the default certificate type for TLS */ (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE; /* Set the defaults for initial handshake */ (*session)->security_parameters.read_bulk_cipher_algorithm = (*session)->security_parameters.write_bulk_cipher_algorithm = GNUTLS_CIPHER_NULL; (*session)->security_parameters.read_mac_algorithm = (*session)->security_parameters.write_mac_algorithm = GNUTLS_MAC_NULL; (*session)->security_parameters.read_compression_algorithm = GNUTLS_COMP_NULL; (*session)->security_parameters.write_compression_algorithm = GNUTLS_COMP_NULL; (*session)->internals.enable_private = 0; /* Initialize buffers */ _gnutls_buffer_init( &(*session)->internals.application_data_buffer); _gnutls_buffer_init( &(*session)->internals.handshake_data_buffer); _gnutls_buffer_init( &(*session)->internals.handshake_hash_buffer); _gnutls_buffer_init( &(*session)->internals.record_send_buffer); _gnutls_buffer_init( &(*session)->internals.record_recv_buffer); _gnutls_buffer_init( &(*session)->internals.handshake_send_buffer); _gnutls_buffer_init( &(*session)->internals.handshake_recv_buffer); (*session)->key = gnutls_calloc(1, sizeof(struct GNUTLS_KEY_INT)); if ( (*session)->key == NULL) { cleanup_session: gnutls_free( *session); *session = NULL; return GNUTLS_E_MEMORY_ERROR; } (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME; /* one hour default */ gnutls_dh_set_prime_bits( (*session), MIN_DH_BITS); gnutls_transport_set_lowat((*session), DEFAULT_LOWAT); /* the default for tcp */ gnutls_handshake_set_max_packet_length( (*session), MAX_HANDSHAKE_PACKET_SIZE); /* Allocate a minimum size for recv_data * This is allocated in order to avoid small messages, making * the receive procedure slow. */ (*session)->internals.record_recv_buffer.data = gnutls_malloc(INITIAL_RECV_BUFFER_SIZE); if ( (*session)->internals.record_recv_buffer.data == NULL) { gnutls_free((*session)->key); goto cleanup_session; } /* set the socket pointers to -1; */ (*session)->internals.transport_recv_ptr = (gnutls_transport_ptr)-1; (*session)->internals.transport_send_ptr = (gnutls_transport_ptr)-1; /* set the default maximum record size for TLS */ (*session)->security_parameters.max_record_recv_size = DEFAULT_MAX_RECORD_SIZE; (*session)->security_parameters.max_record_send_size = DEFAULT_MAX_RECORD_SIZE; /* everything else not initialized here is initialized * as NULL or 0. This is why calloc is used. */ _gnutls_handshake_internal_state_clear( *session); return 0; } /* returns RESUME_FALSE or RESUME_TRUE. */ int _gnutls_session_is_resumable( gnutls_session session) { return session->internals.resumable; } /** * _gnutls_deinit - This function clears all buffers associated with the &session * @session: is a &gnutls_session structure. * * This function clears all buffers associated with the &session. * The difference with gnutls_deinit() is that this function will not * interfere with the session database. * **/ void _gnutls_deinit(gnutls_session session) { if (session==NULL) return; /* remove auth info firstly */ _gnutls_free_auth_info(session ); _gnutls_handshake_io_buffer_clear( session); _gnutls_free_datum(&session->connection_state.read_mac_secret); _gnutls_free_datum(&session->connection_state.write_mac_secret); _gnutls_buffer_clear( &session->internals.handshake_hash_buffer); _gnutls_buffer_clear( &session->internals.handshake_data_buffer); _gnutls_buffer_clear( &session->internals.application_data_buffer); _gnutls_buffer_clear( &session->internals.record_recv_buffer); _gnutls_buffer_clear( &session->internals.record_send_buffer); gnutls_credentials_clear( session); if (session->connection_state.read_cipher_state != NULL) _gnutls_cipher_deinit(session->connection_state.read_cipher_state); if (session->connection_state.write_cipher_state != NULL) _gnutls_cipher_deinit(session->connection_state.write_cipher_state); if (session->connection_state.read_compression_state != NULL) _gnutls_comp_deinit(session->connection_state.read_compression_state, 1); if (session->connection_state.write_compression_state != NULL) _gnutls_comp_deinit(session->connection_state.write_compression_state, 0); _gnutls_free_datum( &session->cipher_specs.server_write_mac_secret); _gnutls_free_datum( &session->cipher_specs.client_write_mac_secret); _gnutls_free_datum( &session->cipher_specs.server_write_IV); _gnutls_free_datum( &session->cipher_specs.client_write_IV); _gnutls_free_datum( &session->cipher_specs.server_write_key); _gnutls_free_datum( &session->cipher_specs.client_write_key); if (session->key != NULL) { _gnutls_mpi_release(&session->key->KEY); _gnutls_mpi_release(&session->key->client_Y); _gnutls_mpi_release(&session->key->client_p); _gnutls_mpi_release(&session->key->client_g); _gnutls_mpi_release(&session->key->u); _gnutls_mpi_release(&session->key->a); _gnutls_mpi_release(&session->key->x); _gnutls_mpi_release(&session->key->A); _gnutls_mpi_release(&session->key->B); _gnutls_mpi_release(&session->key->b); /* RSA */ _gnutls_mpi_release(&session->key->rsa[0]); _gnutls_mpi_release(&session->key->rsa[1]); _gnutls_mpi_release(&session->key->dh_secret); gnutls_free(session->key); session->key = NULL; } memset( session, 0, sizeof(struct gnutls_session_int)); gnutls_free(session); } /** * gnutls_deinit - This function clears all buffers associated with the &session * @session: is a &gnutls_session structure. * * This function clears all buffers associated with the &session. * This function will also remove session data from the session database * if the session was terminated abnormally. * **/ void gnutls_deinit(gnutls_session session) { if (session==NULL) return; /* If the session was terminated abnormally then remove * the session data. */ if (_gnutls_session_is_resumable(session)==RESUME_FALSE) { gnutls_db_remove_session( session); } _gnutls_deinit( session); } int _gnutls_dh_get_prime_bits( gnutls_session session) { return session->internals.dh_prime_bits; } int _gnutls_dh_set_peer_public_bits( gnutls_session session, uint bits) { switch( gnutls_auth_get_type( session)) { case GNUTLS_CRD_ANON: { ANON_SERVER_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->dh_peer_public_bits = bits; break; } case GNUTLS_CRD_CERTIFICATE: { CERTIFICATE_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->dh_peer_public_bits = bits; break; } default: gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } return 0; } int _gnutls_dh_set_secret_bits( gnutls_session session, uint bits) { switch( gnutls_auth_get_type( session)) { case GNUTLS_CRD_ANON: { ANON_SERVER_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->dh_secret_bits = bits; break; } case GNUTLS_CRD_CERTIFICATE: { CERTIFICATE_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->dh_secret_bits = bits; break; default: gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } } return 0; } int _gnutls_rsa_export_set_modulus_bits( gnutls_session session, uint bits) { CERTIFICATE_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->rsa_export_modulus_bits = bits; return 0; } int _gnutls_dh_set_prime_bits( gnutls_session session, uint bits) { switch( gnutls_auth_get_type( session)) { case GNUTLS_CRD_ANON: { ANON_SERVER_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->dh_prime_bits = bits; break; } case GNUTLS_CRD_CERTIFICATE: { CERTIFICATE_AUTH_INFO info; info = _gnutls_get_auth_info(session); if (info == NULL) return GNUTLS_E_INTERNAL_ERROR; info->dh_prime_bits = bits; break; } default: gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } return 0; } /** * gnutls_openpgp_send_key - This function will order gnutls to send the openpgp fingerprint instead of the key * @session: is a pointer to a &gnutls_session structure. * @status: is one of OPENPGP_KEY, or OPENPGP_KEY_FINGERPRINT * * This function will order gnutls to send the key fingerprint instead * of the key in the initial handshake procedure. This should be used * with care and only when there is indication or knowledge that the * server can obtain the client's key. * **/ void gnutls_openpgp_send_key(gnutls_session session, gnutls_openpgp_key_status status) { session->internals.pgp_fingerprint = status; } /** * gnutls_certificate_send_x509_rdn_sequence - This function will order gnutls to or not to send the x.509 rdn sequence * @session: is a pointer to a &gnutls_session structure. * @status: is 0 or 1 * * If status is non zero, this function will order gnutls not to send the rdnSequence * in the certificate request message. That is the server will not advertize * it's trusted CAs to the peer. If status is zero then the default behaviour will * take effect, which is to advertize the server's trusted CAs. * * This function has no effect in clients, and in authentication methods other than * certificate with X.509 certificates. * **/ void gnutls_certificate_send_x509_rdn_sequence(gnutls_session session, int status) { session->internals.ignore_rdn_sequence = status; } int _gnutls_openpgp_send_fingerprint(gnutls_session session) { return session->internals.pgp_fingerprint; } /*- * _gnutls_record_set_default_version - Used to set the default version for the first record packet * @session: is a &gnutls_session structure. * @major: is a tls major version * @minor: is a tls minor version * * This function sets the default version that we will use in the first * record packet (client hello). This function is only useful to people * that know TLS internals and want to debug other implementations. * -*/ void _gnutls_record_set_default_version(gnutls_session session, unsigned char major, unsigned char minor) { session->internals.default_record_version[0] = major; session->internals.default_record_version[1] = minor; } /** * gnutls_record_set_cbc_protection - Used to disable the CBC protection * @session: is a &gnutls_session structure. * @prot: is an integer (0 or 1) * * A newly discovered attack against the record protocol requires some * counter-measures to be taken. GnuTLS will not enable them by default. * The protection is to send an empty record packet, before each actual record * packet, in order to assure that the IV is not known to potential attackers. * * This function will enable or disable the chosen plaintext protection * in the TLS record protocol (used with ciphers in CBC mode). * if prot == 0 then protection is disabled (default), otherwise it * is enabled. * * The protection used will slightly decrease performance, and add * 20 or more bytes per record packet. * **/ void gnutls_record_set_cbc_protection(gnutls_session session, int prot) { session->internals.cbc_protection_hack = prot; } /** * gnutls_handshake_set_private_extensions - Used to enable the private cipher suites * @session: is a &gnutls_session structure. * @allow: is an integer (0 or 1) * * This function will enable or disable the use of private * cipher suites (the ones that start with 0xFF). By default * or if 'allow' is 0 then these cipher suites will not be * advertized nor used. * * Unless this function is called with the option to allow (1), then * no compression algorithms, like ZLIB, and encryption algorithms, * like TWOFISH, will be available. This is because these algorithms * are not yet defined in any RFC or even internet draft. * * Enabling the private ciphersuites when talking to other than gnutls * servers and clients may cause interoperability problems. * **/ void gnutls_handshake_set_private_extensions(gnutls_session session, int allow) { session->internals.enable_private = allow; } inline static int _gnutls_cal_PRF_A( gnutls_mac_algorithm algorithm, const void *secret, int secret_size, const void *seed, int seed_size, void* result) { GNUTLS_MAC_HANDLE td1; td1 = _gnutls_hmac_init(algorithm, secret, secret_size); if (td1 == GNUTLS_MAC_FAILED) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } _gnutls_hmac(td1, seed, seed_size); _gnutls_hmac_deinit(td1, result); return 0; } #define MAX_SEED_SIZE 200 /* Produces "total_bytes" bytes using the hash algorithm specified. * (used in the PRF function) */ static int _gnutls_P_hash( gnutls_mac_algorithm algorithm, const opaque * secret, int secret_size, const opaque * seed, int seed_size, int total_bytes, opaque* ret) { GNUTLS_MAC_HANDLE td2; int i, times, how, blocksize, A_size; opaque final[20], Atmp[MAX_SEED_SIZE]; int output_bytes, result; if (seed_size > MAX_SEED_SIZE || total_bytes<=0) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } blocksize = _gnutls_hmac_get_algo_len(algorithm); output_bytes = 0; do { output_bytes += blocksize; } while (output_bytes < total_bytes); /* calculate A(0) */ memcpy( Atmp, seed, seed_size); A_size = seed_size; times = output_bytes / blocksize; for (i = 0; i < times; i++) { td2 = _gnutls_hmac_init(algorithm, secret, secret_size); if (td2 == GNUTLS_MAC_FAILED) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } /* here we calculate A(i+1) */ if ((result=_gnutls_cal_PRF_A( algorithm, secret, secret_size, Atmp, A_size, Atmp)) < 0) { gnutls_assert(); _gnutls_hmac_deinit(td2, final); return result; } A_size = blocksize; _gnutls_hmac(td2, Atmp, A_size); _gnutls_hmac(td2, seed, seed_size); _gnutls_hmac_deinit(td2, final); if ( (1+i) * blocksize < total_bytes) { how = blocksize; } else { how = total_bytes - (i) * blocksize; } if (how > 0) { memcpy(&ret[i * blocksize], final, how); } } return 0; } /* Xor's two buffers and puts the output in the first one. */ inline static void _gnutls_xor(opaque* o1, opaque* o2, int length) { int i; for (i = 0; i < length; i++) { o1[i] ^= o2[i]; } } #define MAX_PRF_BYTES 200 /* The PRF function expands a given secret * needed by the TLS specification. ret must have a least total_bytes * available. */ int _gnutls_PRF( const opaque * secret, int secret_size, const uint8 * label, int label_size, opaque * seed, int seed_size, int total_bytes, void* ret) { int l_s, s_seed_size; const char *s1, *s2; opaque s_seed[MAX_SEED_SIZE]; opaque o1[MAX_PRF_BYTES], o2[MAX_PRF_BYTES]; int result; if (total_bytes > MAX_PRF_BYTES) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } /* label+seed = s_seed */ s_seed_size = seed_size + label_size; if (s_seed_size > MAX_SEED_SIZE) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } memcpy(s_seed, label, label_size); memcpy(&s_seed[label_size], seed, seed_size); l_s = secret_size / 2; s1 = &secret[0]; s2 = &secret[l_s]; if (secret_size % 2 != 0) { l_s++; } result = _gnutls_P_hash( GNUTLS_MAC_MD5, s1, l_s, s_seed, s_seed_size, total_bytes, o1); if (result<0) { gnutls_assert(); return result; } result = _gnutls_P_hash( GNUTLS_MAC_SHA, s2, l_s, s_seed, s_seed_size, total_bytes, o2); if (result<0) { gnutls_assert(); return result; } _gnutls_xor(o1, o2, total_bytes); memcpy( ret, o1, total_bytes); return 0; /* ok */ } /** * gnutls_session_is_resumed - Used to check whether this session is a resumed one * @session: is a &gnutls_session structure. * * This function will return non zero if this session is a resumed one, * or a zero if this is a new session. * **/ int gnutls_session_is_resumed(gnutls_session session) { if (session->security_parameters.entity==GNUTLS_CLIENT) { if (memcmp( session->security_parameters.session_id, session->internals.resumed_security_parameters.session_id, session->security_parameters.session_id_size)==0) return 1; } else { if (session->internals.resumed==RESUME_TRUE) return 1; } return 0; } /*- * _gnutls_session_is_export - Used to check whether this session is of export grade * @session: is a &gnutls_session structure. * * This function will return non zero if this session is of export grade. * -*/ int _gnutls_session_is_export(gnutls_session session) { gnutls_cipher_algorithm cipher; cipher = _gnutls_cipher_suite_get_cipher_algo( session->security_parameters.current_cipher_suite); if (_gnutls_cipher_get_export_flag( cipher) != 0) return 1; return 0; } /** * gnutls_session_get_ptr - Used to get the user pointer from the session structure * @session: is a &gnutls_session structure. * * This function will return the user given pointer from the session structure. * This is the pointer set with gnutls_session_set_ptr(). * **/ void* gnutls_session_get_ptr(gnutls_session session) { return session->internals.user_ptr; } /** * gnutls_session_set_ptr - Used to set the user pointer to the session structure * @session: is a &gnutls_session structure. * @ptr: is the user pointer * * This function will set (assosiate) the user given pointer to the session structure. * This is pointer can be accessed with gnutls_session_get_ptr(). * **/ void gnutls_session_set_ptr(gnutls_session session, void* ptr) { session->internals.user_ptr = ptr; } /** * gnutls_record_get_direction - This function will return the direction of the last interrupted function call * @session: is a a &gnutls_session structure. * * This function provides information about the internals of the record * protocol and is only useful if a prior gnutls function call (e.g. * gnutls_handshake()) was interrupted for some reason, that is, if a function * returned GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN. In such a case, you might * want to call select() (or poll()) before calling the interrupted gnutls * function again. To tell you whether a file descriptor should be selected * for either reading or writing, gnutls_record_get_direction() returns 0 if * the interrupted function was trying to read data, and 1 if it was trying to * write data. * **/ int gnutls_record_get_direction(gnutls_session session) { return session->internals.direction; } /*- * _gnutls_rsa_pms_set_version - Sets a version to be used at the RSA PMS * @session: is a &gnutls_session structure. * @major: is the major version to use * @minor: is the minor version to use * * This function will set the given version number to be used at the * RSA PMS secret. This is only useful to clients, which want to * test server's capabilities. * -*/ void _gnutls_rsa_pms_set_version(gnutls_session session, unsigned char major, unsigned char minor) { session->internals.rsa_pms_version[0] = major; session->internals.rsa_pms_version[1] = minor; }