/* Waits for the last flight or retransmits * the previous on timeout. Returns 0 on success. */ int _dtls_wait_and_retransmit(gnutls_session_t session) { int ret; if (session->internals.dtls.blocking != 0) ret = _gnutls_io_check_recv(session, TIMER_WINDOW); else ret = _gnutls_io_check_recv(session, 0); if (ret == GNUTLS_E_TIMEDOUT) { ret = _dtls_retransmit(session); if (ret == 0) { RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0); } else return gnutls_assert_val(ret); } RESET_TIMER; return 0; }
static int wrap_nettle_rnd_init(void **_ctx) { int ret; uint8_t new_key[PRNG_KEY_SIZE*2]; struct generators_ctx_st *ctx; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); /* initialize the nonce RNG */ ret = _rnd_get_system_entropy(new_key, sizeof(new_key)); if (ret < 0) { gnutls_assert(); goto fail; } ret = single_prng_init(&ctx->nonce, new_key, PRNG_KEY_SIZE, 1); if (ret < 0) { gnutls_assert(); goto fail; } /* initialize the random/key RNG */ ret = single_prng_init(&ctx->normal, new_key+PRNG_KEY_SIZE, PRNG_KEY_SIZE, 1); if (ret < 0) { gnutls_assert(); goto fail; } *_ctx = ctx; return 0; fail: gnutls_free(ctx); return ret; }
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; }
int _gnutls_proc_ecdh_common_client_kx(gnutls_session_t session, uint8_t * data, size_t _data_size, gnutls_ecc_curve_t curve, gnutls_datum_t * psk_key) { ssize_t data_size = _data_size; int ret, i = 0; int point_size; if (curve == GNUTLS_ECC_CURVE_INVALID) return gnutls_assert_val(GNUTLS_E_ECC_NO_SUPPORTED_CURVES); DECR_LEN(data_size, 1); point_size = data[i]; i += 1; DECR_LEN(data_size, point_size); ret = _gnutls_ecc_ansi_x963_import(&data[i], point_size, &session->key.ecdh_x, &session->key.ecdh_y); if (ret < 0) { gnutls_assert(); goto cleanup; } /* generate pre-shared key */ ret = calc_ecdh_key(session, psk_key, curve); if (ret < 0) { gnutls_assert(); goto cleanup; } cleanup: gnutls_pk_params_clear(&session->key.ecdh_params); return ret; }
/* @total: The sum of the data in giovec */ static ssize_t _gnutls_writev(gnutls_session_t session, const giovec_t * giovec, unsigned giovec_cnt, unsigned total) { int i; bool is_dtls = IS_DTLS(session); unsigned no_writev = 0; gnutls_transport_ptr_t fd = session->internals.transport_send_ptr; reset_errno(session); if (session->internals.vec_push_func != NULL) { if (is_dtls && giovec_cnt > 1) { if (total > session->internals.dtls.mtu) { no_writev = 1; } } if (no_writev == 0) { i = session->internals.vec_push_func(fd, giovec, giovec_cnt); } else { i = _gnutls_writev_emu(session, fd, giovec, giovec_cnt, 1); } } else if (session->internals.push_func != NULL) { i = _gnutls_writev_emu(session, fd, giovec, giovec_cnt, 0); } else return gnutls_assert_val(GNUTLS_E_PUSH_ERROR); if (i == -1) { int err = get_errno(session); _gnutls_debug_log("WRITE: %d returned from %p, errno: %d\n", i, fd, err); return errno_to_gerr(err, is_dtls); } return i; }
/* * Russian differs from PKCS#12 here. It described proprietary way * to obtain MAC key instead of using standard mechanism. * * See https://wwwold.tc26.ru/standard/rs/%D0%A0%2050.1.112-2016.pdf * section 5. */ static int _gnutls_pkcs12_gost_string_to_key(gnutls_mac_algorithm_t algo, const uint8_t * salt, unsigned int salt_size, unsigned int iter, const char *pass, unsigned int req_keylen, uint8_t * keybuf) { uint8_t temp[96]; size_t temp_len = sizeof(temp); unsigned int pass_len = 0; if (pass) pass_len = strlen(pass); if (algo == GNUTLS_MAC_GOSTR_94) pbkdf2_hmac_gosthash94cp(pass_len, (uint8_t *) pass, iter, salt_size, salt, temp_len, temp); else if (algo == GNUTLS_MAC_STREEBOG_256) pbkdf2_hmac_streebog256(pass_len, (uint8_t *) pass, iter, salt_size, salt, temp_len, temp); else if (algo == GNUTLS_MAC_STREEBOG_512) pbkdf2_hmac_streebog512(pass_len, (uint8_t *) pass, iter, salt_size, salt, temp_len, temp); else /* Should not reach here */ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); memcpy(keybuf, temp + temp_len - req_keylen, req_keylen); return 0; }
/** * gnutls_privkey_decrypt_data: * @key: Holds the key * @flags: zero for now * @ciphertext: holds the data to be decrypted * @plaintext: will contain the decrypted data, allocated with gnutls_malloc() * * This function will decrypt the given data using the algorithm * supported by the private key. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.12.0 **/ int gnutls_privkey_decrypt_data(gnutls_privkey_t key, unsigned int flags, const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext) { switch (key->type) { #ifdef ENABLE_OPENPGP case GNUTLS_PRIVKEY_OPENPGP: return _gnutls_openpgp_privkey_decrypt_data(key->key. openpgp, flags, ciphertext, plaintext); #endif case GNUTLS_PRIVKEY_X509: return _gnutls_pk_decrypt(key->pk_algorithm, plaintext, ciphertext, &key->key.x509->params); #ifdef ENABLE_PKCS11 case GNUTLS_PRIVKEY_PKCS11: return _gnutls_pkcs11_privkey_decrypt_data(key->key.pkcs11, flags, ciphertext, plaintext); #endif case GNUTLS_PRIVKEY_EXT: if (key->key.ext.decrypt_func == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); return key->key.ext.decrypt_func(key, key->key.ext.userdata, ciphertext, plaintext); default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } }
/** * gnutls_privkey_import_x509_raw: * @pkey: The private key * @data: The private key data to be imported * @format: The format of the private key * @password: A password (optional) * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t * * This function will import the given private key to the abstract * #gnutls_privkey_t structure. * * The supported formats are basic unencrypted key, PKCS8, PKCS12, * and the openssl format. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 3.1.0 **/ int gnutls_privkey_import_x509_raw(gnutls_privkey_t pkey, const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, const char *password, unsigned int flags) { gnutls_x509_privkey_t xpriv; int ret; ret = gnutls_x509_privkey_init(&xpriv); if (ret < 0) return gnutls_assert_val(ret); ret = gnutls_x509_privkey_import2(xpriv, data, format, password, flags); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_privkey_import_x509(pkey, xpriv, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE); if (ret < 0) { gnutls_assert(); goto cleanup; } return 0; cleanup: gnutls_x509_privkey_deinit(xpriv); return ret; }
static int randomize_uuid(TSS_UUID * uuid) { uint8_t raw_uuid[16]; int ret; ret = _gnutls_rnd(GNUTLS_RND_NONCE, raw_uuid, sizeof(raw_uuid)); if (ret < 0) return gnutls_assert_val(ret); /* mark it as random uuid */ raw_uuid[6] &= 0x0f; raw_uuid[6] |= 0x40; raw_uuid[8] &= 0x0f; raw_uuid[8] |= 0x80; memcpy(&uuid->ulTimeLow, raw_uuid, 4); memcpy(&uuid->usTimeMid, &raw_uuid[4], 2); memcpy(&uuid->usTimeHigh, &raw_uuid[6], 2); uuid->bClockSeqHigh = raw_uuid[8]; uuid->bClockSeqLow = raw_uuid[9]; memcpy(&uuid->rgbNode, &raw_uuid[10], 6); return 0; }
/** * gnutls_pkcs12_bag_set_privkey: * @bag: The bag * @privkey: the private key to be copied. * @password: the password to protect the key with (may be %NULL) * @flags: should be one of #gnutls_pkcs_encrypt_flags_t elements bitwise or'd * * This function will insert the given private key into the * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data(). * * Returns: the index of the added bag on success, or a negative * value on failure. **/ int gnutls_pkcs12_bag_set_privkey(gnutls_pkcs12_bag_t bag, gnutls_x509_privkey_t privkey, const char *password, unsigned flags) { int ret; gnutls_datum_t data = {NULL, 0}; if (bag == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = gnutls_x509_privkey_export2_pkcs8(privkey, GNUTLS_X509_FMT_DER, password, flags, &data); if (ret < 0) return gnutls_assert_val(ret); if (password == NULL) { ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_PKCS8_KEY, &data); if (ret < 0) { gnutls_assert(); goto cleanup; } } else { ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, &data); if (ret < 0) { gnutls_assert(); goto cleanup; } } cleanup: _gnutls_free_datum(&data); return ret; }
int _gnutls_encode_ber_rs(gnutls_datum_t * sig_value, bigint_t r, bigint_t s) { ASN1_TYPE sig; int result; if ((result = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.DSASignatureValue", &sig)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = _gnutls_x509_write_int(sig, "r", r, 1); if (result < 0) { gnutls_assert(); asn1_delete_structure(&sig); return result; } result = _gnutls_x509_write_int(sig, "s", s, 1); if (result < 0) { gnutls_assert(); asn1_delete_structure(&sig); return result; } result = _gnutls_x509_der_encode(sig, "", sig_value, 0); asn1_delete_structure(&sig); if (result < 0) return gnutls_assert_val(result); return 0; }
/* Checks if the extra_certs contain certificates that may form a chain * with the first certificate in chain (it is expected that chain_len==1) * and appends those in the chain. */ static int make_chain(gnutls_x509_crt_t **chain, unsigned int *chain_len, gnutls_x509_crt_t **extra_certs, unsigned int *extra_certs_len) { unsigned int i; if (*chain_len != 1) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); i = 0; while(i<*extra_certs_len) { /* if it is an issuer but not a self-signed one */ if (gnutls_x509_crt_check_issuer((*chain)[*chain_len - 1], (*extra_certs)[i]) != 0 && gnutls_x509_crt_check_issuer((*extra_certs)[i], (*extra_certs)[i]) == 0) { void *tmp = *chain; *chain = gnutls_realloc (*chain, sizeof((*chain)[0]) * ++(*chain_len)); if (*chain == NULL) { gnutls_assert(); gnutls_free(tmp); return GNUTLS_E_MEMORY_ERROR; } (*chain)[*chain_len - 1] = (*extra_certs)[i]; (*extra_certs)[i] = (*extra_certs)[*extra_certs_len-1]; (*extra_certs_len)--; i=0; continue; } i++; } return 0; }
static int gen_ecdhe_psk_server_kx(gnutls_session_t session, gnutls_buffer_st * data) { int ret; if ((ret = _gnutls_auth_info_set(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1)) < 0) { gnutls_assert(); return ret; } ret = _gnutls_buffer_append_prefix(data, 16, 0); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_ecdh_common_print_server_kx(session, data, _gnutls_session_ecc_curve_get (session)); if (ret < 0) gnutls_assert(); return ret; }
/** * gnutls_privkey_import_rsa_raw: * @key: The structure to store the parsed key * @m: holds the modulus * @e: holds the public exponent * @d: holds the private exponent * @p: holds the first prime (p) * @q: holds the second prime (q) * @u: holds the coefficient (optional) * @e1: holds e1 = d mod (p-1) (optional) * @e2: holds e2 = d mod (q-1) (optional) * * This function will convert the given RSA raw parameters to the * native #gnutls_privkey_t format. The output will be stored in * @key. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_privkey_import_rsa_raw(gnutls_privkey_t key, const gnutls_datum_t * m, const gnutls_datum_t * e, const gnutls_datum_t * d, const gnutls_datum_t * p, const gnutls_datum_t * q, const gnutls_datum_t * u, const gnutls_datum_t * e1, const gnutls_datum_t * e2) { int ret; gnutls_x509_privkey_t xkey; ret = gnutls_x509_privkey_init(&xkey); if (ret < 0) return gnutls_assert_val(ret); ret = gnutls_x509_privkey_import_rsa_raw2(xkey, m, e, d, p, q, u, e1, e1); if (ret < 0) { gnutls_assert(); goto error; } ret = gnutls_privkey_import_x509(key, xkey, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE); if (ret < 0) { gnutls_assert(); goto error; } return 0; error: gnutls_x509_privkey_deinit(xkey); return ret; }
/** * gnutls_session_ticket_key_generate: * @key: is a pointer to a #gnutls_datum_t which will contain a newly * created key. * * Generate a random key to encrypt security parameters within * SessionTicket. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an * error code. * * Since: 2.10.0 **/ int gnutls_session_ticket_key_generate(gnutls_datum_t * key) { if (_gnutls_fips_mode_enabled()) { int ret; /* in FIPS140-2 mode gnutls_key_generate imposes * some limits on allowed key size, thus it is not * used. These limits do not affect this function as * it does not generate a "key" but rather key material * that includes nonces and other stuff. */ key->data = gnutls_malloc(TICKET_MASTER_KEY_SIZE); if (key->data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); key->size = TICKET_MASTER_KEY_SIZE; ret = gnutls_rnd(GNUTLS_RND_RANDOM, key->data, key->size); if (ret < 0) { gnutls_free(key->data); return ret; } return 0; } else { return gnutls_key_generate(key, TICKET_MASTER_KEY_SIZE); } }
static int capi_decrypt(gnutls_privkey_t key, void *userdata, const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext) { priv_st *priv = (priv_st *) userdata; DWORD size = 0; int ret; plaintext->data = NULL; plaintext->size = 0; if (priv->pk != GNUTLS_PK_RSA) { return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } plaintext->size = size = ciphertext->size; plaintext->data = (unsigned char *)gnutls_malloc(plaintext->size); if (plaintext->data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } memcpy(plaintext->data, ciphertext->data, size); if (0 == CryptDecrypt(priv->hCryptProv, 0, true, 0, plaintext->data, &size)) { gnutls_assert(); ret = GNUTLS_E_PK_DECRYPTION_FAILED; goto fail; } return 0; fail: gnutls_free(plaintext->data); return ret; }
/* Check if the given signature algorithm is supported. * This means that it is enabled by the priority functions, * and in case of a server a matching certificate exists. */ int _gnutls_session_sign_algo_enabled(gnutls_session_t session, gnutls_sign_algorithm_t sig) { unsigned i; const version_entry_st *ver = get_version(session); if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (!_gnutls_version_has_selectable_sighash(ver)) { return 0; } for (i = 0; i < session->internals.priorities.sign_algo.algorithms; i++) { if (session->internals.priorities.sign_algo.priority[i] == sig) { return 0; /* ok */ } } return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM; }
/* returns data_size or a negative number on failure */ static int _gnutls_ext_master_secret_send_params(gnutls_session_t session, gnutls_buffer_st * extdata) { if ((session->internals.flags & GNUTLS_NO_EXTENSIONS) || session->internals.priorities->no_extensions != 0 || session->internals.no_ext_master_secret != 0) { session->security_parameters.ext_master_secret = 0; return 0; } /* this function sends the client extension data */ #ifdef ENABLE_SSL3 if (session->security_parameters.entity == GNUTLS_CLIENT) { if (have_only_ssl3_enabled(session)) return 0; /* this extension isn't available for SSL 3.0 */ return GNUTLS_E_INT_RET_0; } else { /* server side */ const version_entry_st *ver = get_version(session); if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (ver->id != GNUTLS_SSL3 && session->security_parameters.ext_master_secret != 0) return GNUTLS_E_INT_RET_0; } return 0; #else if (session->security_parameters.entity == GNUTLS_CLIENT || session->security_parameters.ext_master_secret != 0) return GNUTLS_E_INT_RET_0; return 0; #endif }
int _gnutls_epoch_set_cipher_suite(gnutls_session_t session, int epoch_rel, const uint8_t suite[2]) { const cipher_entry_st *cipher_algo; const mac_entry_st *mac_algo; record_parameters_st *params; const gnutls_cipher_suite_entry_st *cs; int ret; ret = _gnutls_epoch_get(session, epoch_rel, ¶ms); if (ret < 0) return gnutls_assert_val(ret); if (params->initialized || params->cipher != NULL || params->mac != NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); cs = ciphersuite_to_entry(suite); if (cs == NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); cipher_algo = cipher_to_entry(cs->block_algorithm); mac_algo = mac_to_entry(cs->mac_algorithm); if (_gnutls_cipher_is_ok(cipher_algo) == 0 || _gnutls_mac_is_ok(mac_algo) == 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_cipher_priority(session, cipher_algo->id) < 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_mac_priority(session, mac_algo->id) < 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); params->cipher = cipher_algo; params->mac = mac_algo; return 0; }
/* If the psk flag is set, then an empty psk_identity_hint will * be inserted */ int _gnutls_ecdh_common_print_server_kx (gnutls_session_t session, gnutls_buffer_st* data, gnutls_ecc_curve_t curve) { uint8_t p; int ret; gnutls_datum_t out; if (curve == GNUTLS_ECC_CURVE_INVALID) return gnutls_assert_val(GNUTLS_E_ECC_NO_SUPPORTED_CURVES); /* curve type */ p = 3; ret = _gnutls_buffer_append_data(data, &p, 1); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_buffer_append_prefix(data, 16, _gnutls_ecc_curve_get_tls_id(curve)); if (ret < 0) return gnutls_assert_val(ret); /* generate temporal key */ ret = _gnutls_pk_generate(GNUTLS_PK_EC, curve, &session->key.ecdh_params); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_ecc_ansi_x963_export(curve, session->key.ecdh_params.params[6] /* x */, session->key.ecdh_params.params[7] /* y */, &out); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_buffer_append_data_prefix(data, 8, out.data, out.size); _gnutls_free_datum(&out); if (ret < 0) return gnutls_assert_val(ret); return data->length; }
/* will merge the given handshake_buffer_st to the handshake_recv_buffer * list. The given hsk packet will be released in any case (success or failure). * Only used in DTLS. */ static int merge_handshake_packet(gnutls_session_t session, handshake_buffer_st * hsk) { int exists = 0, i, pos = 0; int ret; for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) { if (session->internals.handshake_recv_buffer[i].htype == hsk->htype) { exists = 1; pos = i; break; } } if (!exists) pos = session->internals.handshake_recv_buffer_size; if (pos >= MAX_HANDSHAKE_MSGS) return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); if (!exists) { if (hsk->length > 0 && hsk->end_offset > 0 && hsk->end_offset - hsk->start_offset + 1 != hsk->length) { ret = _gnutls_buffer_resize(&hsk->data, hsk->length); if (ret < 0) return gnutls_assert_val(ret); hsk->data.length = hsk->length; memmove(&hsk->data.data[hsk->start_offset], hsk->data.data, hsk->end_offset - hsk->start_offset + 1); } session->internals.handshake_recv_buffer_size++; /* rewrite headers to make them look as each packet came as a single fragment */ _gnutls_write_uint24(hsk->length, &hsk->header[1]); _gnutls_write_uint24(0, &hsk->header[6]); _gnutls_write_uint24(hsk->length, &hsk->header[9]); _gnutls_handshake_buffer_move(&session->internals. handshake_recv_buffer[pos], hsk); } else { if (hsk->start_offset < session->internals.handshake_recv_buffer[pos]. start_offset && hsk->end_offset + 1 >= session->internals.handshake_recv_buffer[pos]. start_offset) { memcpy(&session->internals. handshake_recv_buffer[pos].data.data[hsk-> start_offset], hsk->data.data, hsk->data.length); session->internals.handshake_recv_buffer[pos]. start_offset = hsk->start_offset; session->internals.handshake_recv_buffer[pos]. end_offset = MIN(hsk->end_offset, session->internals. handshake_recv_buffer[pos].end_offset); } else if (hsk->end_offset > session->internals.handshake_recv_buffer[pos]. end_offset && hsk->start_offset <= session->internals.handshake_recv_buffer[pos]. end_offset + 1) { memcpy(&session->internals. handshake_recv_buffer[pos].data.data[hsk-> start_offset], hsk->data.data, hsk->data.length); session->internals.handshake_recv_buffer[pos]. end_offset = hsk->end_offset; session->internals.handshake_recv_buffer[pos]. start_offset = MIN(hsk->start_offset, session->internals. handshake_recv_buffer[pos].start_offset); } _gnutls_handshake_buffer_clear(hsk); } return 0; }
static int parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel, handshake_buffer_st * hsk) { uint8_t *dataptr = NULL; /* for realloc */ size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session), data_size, frag_size; /* Note: SSL2_HEADERS == 1 */ if (_mbuffer_get_udata_size(bufel) < handshake_header_size) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); dataptr = _mbuffer_get_udata_ptr(bufel); /* if reading a client hello of SSLv2 */ #ifdef ENABLE_SSL2 if (unlikely (!IS_DTLS(session) && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) { handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ frag_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* we've read the first byte */ if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); hsk->rtype = hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; hsk->sequence = 0; hsk->start_offset = 0; hsk->length = frag_size; } else #endif { /* TLS or DTLS handshake headers */ hsk->rtype = hsk->htype = dataptr[0]; /* we do not use DECR_LEN because we know * that the packet has enough data. */ hsk->length = _gnutls_read_uint24(&dataptr[1]); if (IS_DTLS(session)) { hsk->sequence = _gnutls_read_uint16(&dataptr[4]); hsk->start_offset = _gnutls_read_uint24(&dataptr[6]); frag_size = _gnutls_read_uint24(&dataptr[9]); } else { hsk->sequence = 0; hsk->start_offset = 0; frag_size = MIN((_mbuffer_get_udata_size(bufel) - handshake_header_size), hsk->length); } /* TLS1.3: distinguish server hello versus hello retry request. * The epitome of slick protocol design. */ if (hsk->htype == GNUTLS_HANDSHAKE_SERVER_HELLO && hsk->start_offset == 0 && !IS_DTLS(session)) { if (_mbuffer_get_udata_size(bufel) > handshake_header_size+2+GNUTLS_RANDOM_SIZE && memcmp(dataptr+handshake_header_size+2, HRR_RANDOM, GNUTLS_RANDOM_SIZE) == 0) { hsk->htype = GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST; } } } data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; if (frag_size > 0) hsk->end_offset = hsk->start_offset + frag_size - 1; else hsk->end_offset = 0; _gnutls_handshake_log ("HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n", session, _gnutls_handshake2str(hsk->htype), (unsigned) hsk->htype, (int) hsk->length, (int) data_size, hsk->start_offset, (int) frag_size, (int) hsk->sequence); hsk->header_size = handshake_header_size; memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), handshake_header_size); if (hsk->length > 0 && (frag_size > data_size || (frag_size > 0 && hsk->end_offset >= hsk->length))) { return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); } else if (hsk->length == 0 && hsk->end_offset != 0 && hsk->start_offset != 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); return handshake_header_size; }
/* This function writes the data that are left in the * TLS write buffer (ie. because the previous write was * interrupted. */ ssize_t _gnutls_io_write_flush(gnutls_session_t session) { gnutls_datum_t msg; mbuffer_head_st *send_buffer = &session->internals.record_send_buffer; int ret; ssize_t sent = 0, tosend = 0; giovec_t iovec[MAX_QUEUE]; int i = 0; mbuffer_st *cur; session->internals.direction = 1; _gnutls_write_log("WRITE FLUSH: %d bytes in buffer.\n", (int) send_buffer->byte_length); for (cur = _mbuffer_head_get_first(send_buffer, &msg); cur != NULL; cur = _mbuffer_head_get_next(cur, &msg)) { iovec[i].iov_base = msg.data; iovec[i++].iov_len = msg.size; tosend += msg.size; /* we buffer up to MAX_QUEUE messages */ if (i >= MAX_QUEUE) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } } if (tosend == 0) { gnutls_assert(); return 0; } ret = _gnutls_writev(session, iovec, i, tosend); if (ret >= 0) { _mbuffer_head_remove_bytes(send_buffer, ret); _gnutls_write_log ("WRITE: wrote %d bytes, %d bytes left.\n", ret, (int) send_buffer->byte_length); sent += ret; } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { _gnutls_write_log("WRITE interrupted: %d bytes left.\n", (int) send_buffer->byte_length); return ret; } else if (ret == GNUTLS_E_LARGE_PACKET) { _mbuffer_head_remove_bytes(send_buffer, tosend); _gnutls_write_log ("WRITE cannot send large packet (%u bytes).\n", (unsigned int) tosend); return ret; } else { _gnutls_write_log("WRITE error: code %d, %d bytes left.\n", ret, (int) send_buffer->byte_length); gnutls_assert(); return ret; } if (sent < tosend) { return gnutls_assert_val(GNUTLS_E_AGAIN); } return sent; }
/* * @ms: a pointer to the number of milliseconds to wait for data. Use zero or NULL for indefinite. * * This function is like recv(with MSG_PEEK). But it does not return -1 on error. * It does return gnutls_errno instead. * This function reads data from the socket and keeps them in a buffer, of up to * max_record_recv_size. * * This is not a general purpose function. It returns EXACTLY the data requested, * which are stored in a local (in the session) buffer. * * If the @ms parameter is non zero then this function will return before * the given amount of milliseconds or return GNUTLS_E_TIMEDOUT. * */ ssize_t _gnutls_io_read_buffered(gnutls_session_t session, size_t total, content_type_t recv_type, unsigned int *ms) { ssize_t ret; size_t min; mbuffer_st *bufel = NULL; size_t recvdata, readsize; if (total > max_record_recv_size(session) || total == 0) { gnutls_assert(); return GNUTLS_E_RECORD_OVERFLOW; } /* calculate the actual size, ie. get the minimum of the * buffered data and the requested data. */ min = MIN(session->internals.record_recv_buffer.byte_length, total); if (min > 0) { /* if we have enough buffered data * then just return them. */ if (min == total) { return min; } } /* min is over zero. recvdata is the data we must * receive in order to return the requested data. */ recvdata = total - min; readsize = recvdata; /* Check if the previously read data plus the new data to * receive are longer than the maximum receive buffer size. */ if ((session->internals.record_recv_buffer.byte_length + recvdata) > max_record_recv_size(session)) { gnutls_assert(); /* internal error */ return GNUTLS_E_INVALID_REQUEST; } /* READ DATA */ if (readsize > 0) { ret = _gnutls_read(session, &bufel, readsize, session->internals.pull_func, ms); /* return immediately if we got an interrupt or eagain * error. */ if (ret < 0) { return gnutls_assert_val(ret); } if (ret == 0) /* EOF */ return gnutls_assert_val(0); /* copy fresh data to our buffer. */ _gnutls_read_log ("RB: Have %d bytes into buffer. Adding %d bytes.\n", (int) session->internals.record_recv_buffer. byte_length, (int) ret); _gnutls_read_log("RB: Requested %d bytes\n", (int) total); _mbuffer_enqueue(&session->internals.record_recv_buffer, bufel); if (IS_DTLS(session)) ret = MIN(total, session->internals.record_recv_buffer. byte_length); else ret = session->internals.record_recv_buffer. byte_length; if ((ret > 0) && ((size_t) ret < total)) /* Short Read */ return gnutls_assert_val(GNUTLS_E_AGAIN); else return ret; } else return gnutls_assert_val(0); }
static ssize_t _gnutls_stream_read(gnutls_session_t session, mbuffer_st ** bufel, size_t size, gnutls_pull_func pull_func, unsigned int *ms) { size_t left; ssize_t i = 0; size_t max_size = max_record_recv_size(session); uint8_t *ptr; gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr; int ret; struct timespec t1, t2; unsigned int diff; session->internals.direction = 0; *bufel = _mbuffer_alloc_align16(MAX(max_size, size), get_total_headers(session)); if (!*bufel) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } ptr = (*bufel)->msg.data; left = size; while (left > 0) { if (ms && *ms > 0) { ret = _gnutls_io_check_recv(session, *ms); if (ret < 0) { gnutls_assert(); goto cleanup; } gnutls_gettime(&t1); } reset_errno(session); i = pull_func(fd, &ptr[size - left], left); if (i < 0) { int err = get_errno(session); _gnutls_read_log ("READ: %d returned from %p, errno=%d gerrno=%d\n", (int) i, fd, errno, session->internals.errnum); if (err == EAGAIN || err == EINTR) { if (size - left > 0) { _gnutls_read_log ("READ: returning %d bytes from %p\n", (int) (size - left), fd); goto finish; } ret = errno_to_gerr(err, 0); goto cleanup; } else { gnutls_assert(); ret = GNUTLS_E_PULL_ERROR; goto cleanup; } } else { _gnutls_read_log("READ: Got %d bytes from %p\n", (int) i, fd); if (i == 0) break; /* EOF */ } left -= i; (*bufel)->msg.size += i; if (ms && *ms > 0 && *ms != GNUTLS_INDEFINITE_TIMEOUT) { gnutls_gettime(&t2); diff = timespec_sub_ms(&t2, &t1); if (diff < *ms) *ms -= diff; else { ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT); goto cleanup; } } } finish: _gnutls_read_log("READ: read %d bytes from %p\n", (int) (size - left), fd); if (size - left == 0) _mbuffer_xfree(bufel); return (size - left); cleanup: _mbuffer_xfree(bufel); return ret; }
static ssize_t _gnutls_dgram_read(gnutls_session_t session, mbuffer_st ** bufel, gnutls_pull_func pull_func, unsigned int *ms) { ssize_t i, ret; uint8_t *ptr; struct timespec t1, t2; size_t max_size, recv_size; gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr; unsigned int diff; max_size = max_record_recv_size(session); recv_size = max_size; session->internals.direction = 0; if (ms && *ms > 0) { ret = _gnutls_io_check_recv(session, *ms); if (ret < 0) return gnutls_assert_val(ret); gnutls_gettime(&t1); } *bufel = _mbuffer_alloc_align16(max_size, get_total_headers(session)); if (*bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ptr = (*bufel)->msg.data; reset_errno(session); i = pull_func(fd, ptr, recv_size); if (i < 0) { int err = get_errno(session); _gnutls_read_log("READ: %d returned from %p, errno=%d\n", (int) i, fd, err); ret = errno_to_gerr(err, 1); goto cleanup; } else { _gnutls_read_log("READ: Got %d bytes from %p\n", (int) i, fd); if (i == 0) { /* If we get here, we likely have a stream socket. * That assumption may not work on DCCP. */ gnutls_assert(); ret = 0; goto cleanup; } _mbuffer_set_udata_size(*bufel, i); } if (ms && *ms > 0) { gnutls_gettime(&t2); diff = timespec_sub_ms(&t2, &t1); if (diff < *ms) *ms -= diff; else { ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT); goto cleanup; } } _gnutls_read_log("READ: read %d bytes from %p\n", (int) i, fd); return i; cleanup: _mbuffer_xfree(bufel); return ret; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ ssize_t _gnutls_handshake_io_recv_int(gnutls_session_t session, gnutls_handshake_description_t htype, handshake_buffer_st * hsk, unsigned int optional) { int ret; unsigned int tleft = 0; int retries = 7; ret = get_last_packet(session, htype, hsk, optional); if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && ret != GNUTLS_E_INT_CHECK_AGAIN) { return gnutls_assert_val(ret); } /* try using the already existing records before * trying to receive. */ ret = _gnutls_parse_record_buffered_msgs(session); if (ret == 0) { ret = get_last_packet(session, htype, hsk, optional); } if (IS_DTLS(session)) { if (ret >= 0) return ret; } else { if ((ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && ret < 0) || ret >= 0) return gnutls_assert_val(ret); } if (htype != (gnutls_handshake_description_t) -1) { ret = handshake_remaining_time(session); if (ret < 0) return gnutls_assert_val(ret); tleft = ret; } do { /* if we don't have a complete message waiting for us, try * receiving more */ ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype, tleft); if (ret < 0) return gnutls_assert_val_fatal(ret); ret = _gnutls_parse_record_buffered_msgs(session); if (ret == 0) { ret = get_last_packet(session, htype, hsk, optional); } /* we put an upper limit (retries) to the number of partial handshake * messages in a record packet. */ } while(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN && retries-- > 0); if (unlikely(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN)) { ret = gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); } return ret; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. * * htype is the next handshake packet expected. */ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) { gnutls_datum_t msg; mbuffer_st *bufel = NULL, *prev = NULL; int ret; size_t data_size; handshake_buffer_st *recv_buf = session->internals.handshake_recv_buffer; bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; if (!IS_DTLS(session)) { ssize_t append, header_size; do { if (bufel->type != GNUTLS_HANDSHAKE) return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); if (unlikely (session->internals.handshake_recv_buffer_size == 0 && msg.size < HANDSHAKE_HEADER_SIZE(session) && session->internals.handshake_header_recv_buffer.byte_length < HANDSHAKE_HEADER_SIZE(session) - msg.size)) { bufel = _mbuffer_head_pop_first(&session->internals.record_buffer); _mbuffer_enqueue(&session->internals.handshake_header_recv_buffer, bufel); break; } else if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length > recv_buf[0].data.length) { /* this is the rest of a previous message */ append = MIN(msg.size, recv_buf[0].length - recv_buf[0].data.length); ret = _gnutls_buffer_append_data(&recv_buf [0].data, msg.data, append); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_remove_bytes(&session-> internals. record_buffer, append); } else { /* received new message */ if (unlikely (session->internals. handshake_header_recv_buffer.length > 0)) { bufel = _mbuffer_head_pop_first(&session->internals. record_buffer); _mbuffer_enqueue(&session->internals. handshake_header_recv_buffer, bufel); ret = _mbuffer_linearize_align16(&session->internals. handshake_header_recv_buffer, get_total_headers(session)); if (ret < 0) return gnutls_assert_val(ret); bufel = _mbuffer_head_pop_first(&session->internals. handshake_header_recv_buffer); _mbuffer_head_push_first(&session->internals. record_buffer, bufel); } ret = parse_handshake_header(session, bufel, &recv_buf[0]); if (ret < 0) return gnutls_assert_val(ret); header_size = ret; session->internals. handshake_recv_buffer_size = 1; _mbuffer_set_uhead_size(bufel, header_size); data_size = MIN(recv_buf[0].length, _mbuffer_get_udata_size(bufel)); ret = _gnutls_buffer_append_data(&recv_buf [0].data, _mbuffer_get_udata_ptr (bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_set_uhead_size(bufel, 0); _mbuffer_head_remove_bytes(&session-> internals. record_buffer, data_size + header_size); } /* if packet is complete then return it */ if (recv_buf[0].length == recv_buf[0].data.length) { return 0; } bufel = _mbuffer_head_get_first(&session->internals. record_buffer, &msg); } while (bufel != NULL); /* if we are here it means that the received packets were not * enough to complete the handshake packet. */ return gnutls_assert_val(GNUTLS_E_AGAIN); } else { /* DTLS */ handshake_buffer_st tmp; do { /* we now * 0. parse headers * 1. insert to handshake_recv_buffer * 2. sort handshake_recv_buffer on sequence numbers * 3. return first packet if completed or GNUTLS_E_AGAIN. */ do { if (bufel->type != GNUTLS_HANDSHAKE) { gnutls_assert(); goto next; /* ignore packet */ } _gnutls_handshake_buffer_init(&tmp); ret = parse_handshake_header(session, bufel, &tmp); if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, "Invalid handshake packet headers. Discarding.\n"); break; } _mbuffer_consume(&session->internals. record_buffer, bufel, ret); data_size = MIN(tmp.length, tmp.end_offset - tmp.start_offset + 1); ret = _gnutls_buffer_append_data(&tmp.data, _mbuffer_get_udata_ptr (bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_consume(&session->internals. record_buffer, bufel, data_size); ret = merge_handshake_packet(session, &tmp); if (ret < 0) return gnutls_assert_val(ret); } while (_mbuffer_get_udata_size(bufel) > 0); prev = bufel; bufel = _mbuffer_dequeue(&session->internals. record_buffer, bufel); _mbuffer_xfree(&prev); continue; next: bufel = _mbuffer_head_get_next(bufel, NULL); } while (bufel != NULL); /* sort in descending order */ if (session->internals.handshake_recv_buffer_size > 1) qsort(recv_buf, session->internals. handshake_recv_buffer_size, sizeof(recv_buf[0]), handshake_compare); while (session->internals.handshake_recv_buffer_size > 0 && recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq) { _gnutls_audit_log(session, "Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence); _gnutls_handshake_buffer_clear(&recv_buf [LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; } return 0; } }
/* returns the last stored handshake packet. */ static int get_last_packet(gnutls_session_t session, gnutls_handshake_description_t htype, handshake_buffer_st * hsk, unsigned int optional) { handshake_buffer_st *recv_buf = session->internals.handshake_recv_buffer; if (IS_DTLS(session)) { if (session->internals.handshake_recv_buffer_size == 0 || (session->internals.dtls.hsk_read_seq != recv_buf[LAST_ELEMENT].sequence)) goto timeout; if (htype != recv_buf[LAST_ELEMENT].htype) { if (optional == 0) _gnutls_audit_log(session, "Received unexpected handshake message '%s' (%d). Expected '%s' (%d)\n", _gnutls_handshake2str (recv_buf[0].htype), (int) recv_buf[0].htype, _gnutls_handshake2str (htype), (int) htype); return gnutls_assert_val (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } else if ((recv_buf[LAST_ELEMENT].start_offset == 0 && recv_buf[LAST_ELEMENT].end_offset == recv_buf[LAST_ELEMENT].length - 1) || recv_buf[LAST_ELEMENT].length == 0) { session->internals.dtls.hsk_read_seq++; _gnutls_handshake_buffer_move(hsk, &recv_buf [LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; return 0; } else { /* if we don't have a complete handshake message, but we * have queued data waiting, try again to reconstruct the * handshake packet, using the queued */ if (recv_buf[LAST_ELEMENT].end_offset != recv_buf[LAST_ELEMENT].length - 1 && record_check_unprocessed(session) > 0) return gnutls_assert_val(GNUTLS_E_INT_CHECK_AGAIN); else goto timeout; } } else { /* TLS */ if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length == recv_buf[0].data.length) { if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) { return gnutls_assert_val (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } _gnutls_handshake_buffer_move(hsk, &recv_buf[0]); session->internals.handshake_recv_buffer_size--; return 0; } else return gnutls_assert_val (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); } timeout: RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0); }
/** * gnutls_x509_trust_list_verify_crt: * @list: The structure of the list * @cert_list: is the certificate list to be verified * @cert_list_size: is the certificate list size * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. * @verify: will hold the certificate verification output. * @func: If non-null will be called on each chain element verification with the output. * * This function will try to verify the given certificate and return * its status. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 3.0.0 **/ int gnutls_x509_trust_list_verify_crt(gnutls_x509_trust_list_t list, gnutls_x509_crt_t * cert_list, unsigned int cert_list_size, unsigned int flags, unsigned int *verify, gnutls_verify_output_function func) { gnutls_datum_t dn; int ret, i; uint32_t hash; if (cert_list == NULL || cert_list_size < 1) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); cert_list_size = shorten_clist(list, cert_list, cert_list_size); if (cert_list_size <= 0) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); ret = gnutls_x509_crt_get_raw_issuer_dn(cert_list[cert_list_size - 1], &dn); if (ret < 0) { gnutls_assert(); return ret; } hash = _gnutls_bhash(dn.data, dn.size, INIT_HASH); hash %= list->size; _gnutls_free_datum(&dn); *verify = _gnutls_x509_verify_certificate(cert_list, cert_list_size, list->node[hash].trusted_cas, list->node[hash]. trusted_ca_size, flags, func); if (*verify != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) return 0; /* Check revocation of individual certificates. * start with the last one that we already have its hash */ ret = _gnutls_x509_crt_check_revocation(cert_list[cert_list_size - 1], list->node[hash].crls, list->node[hash].crl_size, func); if (ret == 1) { /* revoked */ *verify |= GNUTLS_CERT_REVOKED; *verify |= GNUTLS_CERT_INVALID; return 0; } for (i = 0; i < cert_list_size - 1; i++) { ret = gnutls_x509_crt_get_raw_issuer_dn(cert_list[i], &dn); if (ret < 0) { gnutls_assert(); return ret; } hash = _gnutls_bhash(dn.data, dn.size, INIT_HASH); hash %= list->size; _gnutls_free_datum(&dn); ret = _gnutls_x509_crt_check_revocation(cert_list[i], list->node[hash].crls, list->node[hash].crl_size, func); if (ret == 1) { /* revoked */ *verify |= GNUTLS_CERT_REVOKED; *verify |= GNUTLS_CERT_INVALID; return 0; } } return 0; }