/* This is a temporary function to be used before the generate_* internal API is changed to use mbuffers. For now we don't avoid the extra alloc + memcpy. */ static inline int send_handshake (gnutls_session_t session, opaque * data, size_t size, gnutls_handshake_description_t type) { mbuffer_st *bufel; if (data == NULL && size == 0) return _gnutls_send_handshake (session, NULL, type); if (data == NULL && size > 0) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } bufel = _gnutls_handshake_alloc(session, size, size); if (bufel == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } _mbuffer_set_udata (bufel, data, size); return _gnutls_send_handshake (session, bufel, type); }
/* This is the function for the client to send the certificate * verify message */ int _gnutls_send_client_certificate_verify (gnutls_session_t session, int again) { uint8_t *data; int ret = 0; int data_size; /* This is a packet that is only sent by the client */ if (session->security_parameters.entity == GNUTLS_SERVER) return 0; /* if certificate verify is not needed just exit */ if (session->key->certificate_requested == 0) return 0; if (session->internals.auth_struct->gnutls_generate_client_cert_vrfy == NULL) { gnutls_assert (); return 0; /* this algorithm does not support cli_cert_vrfy */ } data = NULL; data_size = 0; if (again == 0) { data_size = session->internals. auth_struct->gnutls_generate_client_cert_vrfy (session, &data); if (data_size < 0) { gnutls_assert (); return data_size; } if (data_size == 0) return 0; } ret = _gnutls_send_handshake (session, data, data_size, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY); gnutls_free (data); return ret; }
/* This is called when we want to receive the key exchange message of the * server. It does nothing if this type of message is not required * by the selected ciphersuite. */ int _gnutls_send_server_kx_message (gnutls_session_t session, int again) { uint8_t *data = NULL; int data_size = 0; int ret = 0; if (session->internals.auth_struct->gnutls_generate_server_kx == NULL) return 0; data = NULL; data_size = 0; if (again == 0) { data_size = session->internals.auth_struct->gnutls_generate_server_kx (session, &data); if (data_size == GNUTLS_E_INT_RET_0) { gnutls_assert (); return 0; } if (data_size < 0) { gnutls_assert (); return data_size; } } ret = _gnutls_send_handshake (session, data, data_size, GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE); gnutls_free (data); if (ret < 0) { gnutls_assert (); return ret; } return data_size; }
/* This function sends a certificate request message to the * client. */ int _gnutls_send_server_certificate_request (gnutls_session_t session, int again) { uint8_t *data = NULL; int data_size = 0; int ret = 0; if (session->internals. auth_struct->gnutls_generate_server_certificate_request == NULL) return 0; if (session->internals.send_cert_req <= 0) return 0; data = NULL; data_size = 0; if (again == 0) { data_size = session->internals. auth_struct->gnutls_generate_server_certificate_request (session, &data); if (data_size < 0) { gnutls_assert (); return data_size; } } ret = _gnutls_send_handshake (session, data, data_size, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST); gnutls_free (data); if (ret < 0) { gnutls_assert (); return ret; } return data_size; }
int _gnutls_send_server_certificate_status(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data; int data_size = 0; int ret; status_request_ext_st *priv = NULL; extension_priv_data_t epriv; if (again == 0) { ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv); if (ret < 0) return 0; priv = epriv.ptr; if (!priv->response.size) return 0; data_size = priv->response.size + 4; bufel = _gnutls_handshake_alloc(session, data_size, data_size); if (!bufel) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); data = _mbuffer_get_udata_ptr(bufel); data[0] = 0x01; _gnutls_write_uint24(priv->response.size, &data[1]); memcpy(&data[4], priv->response.data, priv->response.size); _gnutls_free_datum(&priv->response); } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_CERTIFICATE_STATUS); }
int _gnutls13_send_key_update(gnutls_session_t session, unsigned again, unsigned flags /* GNUTLS_KU_* */) { int ret; mbuffer_st *bufel = NULL; uint8_t val; if (again == 0) { if (flags & GNUTLS_KU_PEER) { /* mark that we asked a key update to prevent an * infinite ping pong when receiving the reply */ session->internals.hsk_flags |= HSK_KEY_UPDATE_ASKED; val = 0x01; } else { val = 0x00; } _gnutls_handshake_log("HSK[%p]: sending key update (%u)\n", session, (unsigned)val); bufel = _gnutls_handshake_alloc(session, 1); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); _mbuffer_set_udata_size(bufel, 0); ret = _mbuffer_append_data(bufel, &val, 1); if (ret < 0) { gnutls_assert(); goto cleanup; } } return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_KEY_UPDATE); cleanup: _mbuffer_xfree(&bufel); return ret; }
/* This is called when we want send our certificate */ int _gnutls_send_client_certificate (gnutls_session_t session, int again) { uint8_t *data = NULL; int data_size = 0; int ret = 0; if (session->key->certificate_requested == 0) return 0; if (session->internals.auth_struct->gnutls_generate_client_certificate == NULL) return 0; data = NULL; data_size = 0; if (again == 0) { if (gnutls_protocol_get_version (session) != GNUTLS_SSL3 || session->internals.selected_cert_list_length > 0) { /* TLS 1.0 or SSL 3.0 with a valid certificate */ data_size = session->internals. auth_struct->gnutls_generate_client_certificate (session, &data); if (data_size < 0) { gnutls_assert (); return data_size; } } } /* In the SSL 3.0 protocol we need to send a * no certificate alert instead of an * empty certificate. */ if (gnutls_protocol_get_version (session) == GNUTLS_SSL3 && session->internals.selected_cert_list_length == 0) { ret = gnutls_alert_send (session, GNUTLS_AL_WARNING, GNUTLS_A_SSL3_NO_CERTIFICATE); } else { /* TLS 1.0 or SSL 3.0 with a valid certificate */ ret = _gnutls_send_handshake (session, data, data_size, GNUTLS_HANDSHAKE_CERTIFICATE_PKT); gnutls_free (data); } if (ret < 0) { gnutls_assert (); return ret; } return data_size; }
int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) { int ret; gnutls_pcert_st *apr_cert_list = NULL; gnutls_privkey_t apr_pkey = NULL; int apr_cert_list_length = 0; mbuffer_st *bufel = NULL; gnutls_buffer_st buf; unsigned pos_mark, ext_pos_mark; unsigned i; struct ocsp_req_ctx_st ctx; gnutls_certificate_credentials_t cred; if (again == 0) { if (!session->internals.initial_negotiation_completed && session->internals.hsk_flags & HSK_PSK_SELECTED) return 0; if (session->security_parameters.entity == GNUTLS_SERVER && session->internals.resumed) return 0; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (session->security_parameters.entity == GNUTLS_CLIENT && !(session->internals.hsk_flags & HSK_CRT_ASKED)) { return 0; } ret = _gnutls_get_selected_cert(session, &apr_cert_list, &apr_cert_list_length, &apr_pkey); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_buffer_init_handshake_mbuffer(&buf); if (ret < 0) return gnutls_assert_val(ret); if (session->security_parameters.entity == GNUTLS_CLIENT) { ret = _gnutls_buffer_append_data_prefix(&buf, 8, session->internals.post_handshake_cr_context.data, session->internals.post_handshake_cr_context.size); if (ret < 0) { gnutls_assert(); goto cleanup; } } else { ret = _gnutls_buffer_append_prefix(&buf, 8, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } /* mark total size */ pos_mark = buf.length; ret = _gnutls_buffer_append_prefix(&buf, 24, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } for (i=0;i<(unsigned)apr_cert_list_length;i++) { ret = _gnutls_buffer_append_data_prefix(&buf, 24, apr_cert_list[i].cert.data, apr_cert_list[i].cert.size); if (ret < 0) { gnutls_assert(); goto cleanup; } #ifdef ENABLE_OCSP if ((session->internals.selected_ocsp_length > 0 || session->internals.selected_ocsp_func) && _gnutls_hello_ext_is_present(session, GNUTLS_EXTENSION_STATUS_REQUEST)) { /* append status response if available */ ret = _gnutls_extv_append_init(&buf); if (ret < 0) { gnutls_assert(); goto cleanup; } ext_pos_mark = ret; ctx.pcert = &apr_cert_list[i]; ctx.cert_index = i; ctx.session = session; ctx.cred = cred; ret = _gnutls_extv_append(&buf, STATUS_REQUEST_TLS_ID, &ctx, append_status_request); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_extv_append_final(&buf, ext_pos_mark, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } else #endif { ret = _gnutls_buffer_append_prefix(&buf, 16, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } } _gnutls_write_uint24(buf.length-pos_mark-3, &buf.data[pos_mark]); bufel = _gnutls_buffer_to_mbuffer(&buf); } return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_PKT); cleanup: _gnutls_buffer_clear(&buf); return ret; }
/* * Return zero if session tickets haven't been enabled. */ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data = NULL, *p; int data_size = 0; int ret; gnutls_datum_t state = { NULL, 0 }; uint16_t epoch_saved = session->security_parameters.epoch_write; gnutls_datum_t ticket_data; if (again == 0) { if (session->internals.flags & GNUTLS_NO_TICKETS) return 0; if (!session->internals.session_ticket_renew) return 0; _gnutls_handshake_log ("HSK[%p]: sending session ticket\n", session); /* XXX: Temporarily set write algorithms to be used. _gnutls_write_connection_state_init() does this job, but it also triggers encryption, while NewSessionTicket should not be encrypted in the record layer. */ ret = _gnutls_epoch_set_keys(session, session->security_parameters. epoch_next, 0); if (ret < 0) { gnutls_assert(); return ret; } /* Under TLS1.2 with session tickets, the session ID is used for different * purposes than the TLS1.0 session ID. Ensure that there is an internally * set value which the server will see on the original and resumed sessions */ if (session->internals.resumed != RESUME_TRUE) { ret = _gnutls_generate_session_id(session->security_parameters. session_id, &session->security_parameters. session_id_size); if (ret < 0) { gnutls_assert(); return ret; } } session->security_parameters.epoch_write = session->security_parameters.epoch_next; /* Pack security parameters. */ ret = _gnutls_session_pack(session, &state); if (ret < 0) { gnutls_assert(); return ret; } /* Generate an encrypted ticket */ ret = _gnutls_encrypt_session_ticket(session, &state, &ticket_data); session->security_parameters.epoch_write = epoch_saved; _gnutls_free_datum(&state); if (ret < 0) { gnutls_assert(); return ret; } bufel = _gnutls_handshake_alloc(session, 4 + 2 + ticket_data.size); if (!bufel) { gnutls_assert(); _gnutls_free_datum(&ticket_data); return GNUTLS_E_MEMORY_ERROR; } data = _mbuffer_get_udata_ptr(bufel); p = data; _gnutls_write_uint32(session->internals.expire_time, p); p += 4; _gnutls_write_uint16(ticket_data.size, p); p += 2; memcpy(p, ticket_data.data, ticket_data.size); p += ticket_data.size; _gnutls_free_datum(&ticket_data); data_size = p - data; session->internals.hsk_flags |= HSK_TLS12_TICKET_SENT; } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); }
int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data = NULL, *p; int data_size = 0; int ret; struct ticket_st ticket; uint16_t ticket_len; session_ticket_ext_st *priv = NULL; extension_priv_data_t epriv; uint16_t epoch_saved = session->security_parameters.epoch_write; if (again == 0) { ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_SESSION_TICKET, &epriv); if (ret < 0) return 0; priv = epriv; if (!priv->session_ticket_renew) return 0; /* XXX: Temporarily set write algorithms to be used. _gnutls_write_connection_state_init() does this job, but it also triggers encryption, while NewSessionTicket should not be encrypted in the record layer. */ ret = _gnutls_epoch_set_keys(session, session->security_parameters. epoch_next); if (ret < 0) { gnutls_assert(); return ret; } session->security_parameters.epoch_write = session->security_parameters.epoch_next; ret = encrypt_ticket(session, priv, &ticket); session->security_parameters.epoch_write = epoch_saved; if (ret < 0) { gnutls_assert(); return ret; } ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len + MAC_SIZE; bufel = _gnutls_handshake_alloc(session, 4 + 2 + ticket_len); if (!bufel) { gnutls_assert(); gnutls_free(ticket.encrypted_state); return GNUTLS_E_MEMORY_ERROR; } data = _mbuffer_get_udata_ptr(bufel); p = data; _gnutls_write_uint32(session->internals.expire_time, p); p += 4; _gnutls_write_uint16(ticket_len, p); p += 2; memcpy(p, ticket.key_name, KEY_NAME_SIZE); p += KEY_NAME_SIZE; memcpy(p, ticket.IV, IV_SIZE); p += IV_SIZE; _gnutls_write_uint16(ticket.encrypted_state_len, p); p += 2; memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len); gnutls_free(ticket.encrypted_state); p += ticket.encrypted_state_len; memcpy(p, ticket.mac, MAC_SIZE); p += MAC_SIZE; data_size = p - data; session->internals.ticket_sent = 1; } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); }