/* * Copyright (C) 2008-2017 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program 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 General Public License * for more details. */ #include "pubkey_authenticator.h" #include #include #include #include #include #include typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t; /** * Private data of an pubkey_authenticator_t object. */ struct private_pubkey_authenticator_t { /** * Public authenticator_t interface. */ pubkey_authenticator_t public; /** * Assigned IKE_SA */ ike_sa_t *ike_sa; /** * nonce to include in AUTH calculation */ chunk_t nonce; /** * IKE_SA_INIT message data to include in AUTH calculation */ chunk_t ike_sa_init; /** * Reserved bytes of ID payload */ char reserved[3]; }; /** * Parse authentication data used for Signature Authentication as per RFC 7427 */ static bool parse_signature_auth_data(chunk_t *auth_data, key_type_t *key_type, signature_params_t *params) { uint8_t len; if (!auth_data->len) { return FALSE; } len = auth_data->ptr[0]; *auth_data = chunk_skip(*auth_data, 1); if (!signature_params_parse(*auth_data, 1, params)) { return FALSE; } *key_type = key_type_from_signature_scheme(params->scheme); *auth_data = chunk_skip(*auth_data, len); return TRUE; } /** * Build authentication data used for Signature Authentication as per RFC 7427 */ static bool build_signature_auth_data(chunk_t *auth_data, signature_params_t *params) { chunk_t data; uint8_t len; if (!signature_params_build(params, &data)) { chunk_free(auth_data); return FALSE; } len = data.len; *auth_data = chunk_cat("cmm", chunk_from_thing(len), data, *auth_data); return TRUE; } /** * Selects possible signature schemes based on our configuration, the other * peer's capabilities and the private key */ static array_t *select_signature_schemes(keymat_v2_t *keymat, auth_cfg_t *auth, private_key_t *private) { enumerator_t *enumerator; signature_scheme_t scheme; signature_params_t *config; auth_rule_t rule; key_type_t key_type; bool have_config = FALSE; array_t *selected; selected = array_create(0, 0); key_type = private->get_type(private); enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &rule, &config)) { if (rule != AUTH_RULE_IKE_SIGNATURE_SCHEME) { continue; } have_config = TRUE; if (key_type == key_type_from_signature_scheme(config->scheme) && keymat->hash_algorithm_supported(keymat, hasher_from_signature_scheme(config->scheme, config->params))) { array_insert(selected, ARRAY_TAIL, signature_params_clone(config)); } } enumerator->destroy(enumerator); if (!have_config) { /* if no specific configuration, find schemes appropriate for the key * and supported by the other peer */ enumerator = signature_schemes_for_key(key_type, private->get_keysize(private)); while (enumerator->enumerate(enumerator, &config)) { if (config->scheme == SIGN_RSA_EMSA_PSS && !lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE, lib->ns)) { continue; } if (keymat->hash_algorithm_supported(keymat, hasher_from_signature_scheme(config->scheme, config->params))) { array_insert(selected, ARRAY_TAIL, signature_params_clone(config)); } } enumerator->destroy(enumerator); /* for RSA we tried at least SHA-512, also try other schemes */ if (key_type == KEY_RSA) { signature_scheme_t schemes[] = { SIGN_RSA_EMSA_PKCS1_SHA2_384, SIGN_RSA_EMSA_PKCS1_SHA2_256, }, contained; bool found; int i, j; for (i = 0; i < countof(schemes); i++) { scheme = schemes[i]; found = FALSE; for (j = 0; j < array_count(selected); j++) { array_get(selected, j, &contained); if (scheme == contained) { found = TRUE; break; } } if (!found && keymat->hash_algorithm_supported(keymat, hasher_from_signature_scheme(scheme, NULL))) { INIT(config, .scheme = scheme, ) array_insert(selected, ARRAY_TAIL, config); } } } } return selected; } CALLBACK(destroy_scheme, void, signature_params_t *params, int idx, void *user) { signature_params_destroy(params); } /** * Create a signature using RFC 7427 signature authentication */ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, auth_cfg_t *auth, private_key_t *private, identification_t *id, chunk_t *auth_data) { enumerator_t *enumerator; keymat_v2_t *keymat; signature_params_t *params = NULL; array_t *schemes; chunk_t octets = chunk_empty; status_t status = FAILED; keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); schemes = select_signature_schemes(keymat, auth, private); if (!array_count(schemes)) { DBG1(DBG_IKE, "no common hash algorithm found to create signature " "with %N key", key_type_names, private->get_type(private)); array_destroy(schemes); return FAILED; } if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, this->nonce, id, this->reserved, &octets, schemes)) { enumerator = array_create_enumerator(schemes); while (enumerator->enumerate(enumerator, ¶ms)) { if (private->sign(private, params->scheme, params->params, octets, auth_data) && build_signature_auth_data(auth_data, params)) { status = SUCCESS; break; } else { DBG2(DBG_IKE, "unable to create %N signature for %N key", signature_scheme_names, params->scheme, key_type_names, private->get_type(private)); } } enumerator->destroy(enumerator); } if (params) { if (params->scheme == SIGN_RSA_EMSA_PSS) { rsa_pss_params_t *pss = params->params; DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N_%N %s", id, signature_scheme_names, params->scheme, hash_algorithm_short_names_upper, pss->hash, status == SUCCESS ? "successful" : "failed"); } else { DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id, signature_scheme_names, params->scheme, status == SUCCESS ? "successful" : "failed"); } } else { DBG1(DBG_IKE, "authentication of '%Y' (myself) failed", id); } array_destroy_function(schemes, destroy_scheme, NULL); chunk_free(&octets); return status; } /** * Get the auth octets and the signature scheme (in case it is changed by the * keymat). */ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, bool verify, identification_t *id, chunk_t *octets, signature_params_t **scheme) { keymat_v2_t *keymat; array_t *schemes; bool success = FALSE; schemes = array_create(0, 0); array_insert(schemes, ARRAY_TAIL, *scheme); keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); if (keymat->get_auth_octets(keymat, verify, this->ike_sa_init, this->nonce, id, this->reserved, octets, schemes) && array_remove(schemes, 0, scheme)) { success = TRUE; } else { *scheme = NULL; } array_destroy_function(schemes, destroy_scheme, NULL); return success; } /** * Create a classic IKEv2 signature */ static status_t sign_classic(private_pubkey_authenticator_t *this, auth_cfg_t *auth, private_key_t *private, identification_t *id, auth_method_t *auth_method, chunk_t *auth_data) { signature_scheme_t scheme; signature_params_t *params; chunk_t octets = chunk_empty; status_t status = FAILED; switch (private->get_type(private)) { case KEY_RSA: scheme = SIGN_RSA_EMSA_PKCS1_SHA1; *auth_method = AUTH_RSA; break; case KEY_ECDSA: /* deduct the signature scheme from the keysize */ switch (private->get_keysize(private)) { case 256: scheme = SIGN_ECDSA_256; *auth_method = AUTH_ECDSA_256; break; case 384: scheme = SIGN_ECDSA_384; *auth_method = AUTH_ECDSA_384; break; case 521: scheme = SIGN_ECDSA_521; *auth_method = AUTH_ECDSA_521; break; default: DBG1(DBG_IKE, "%d bit ECDSA private key size not supported", private->get_keysize(private)); return FAILED; } break; default: DBG1(DBG_IKE, "private key of type %N not supported", key_type_names, private->get_type(private)); return FAILED; } INIT(params, .scheme = scheme, ); if (get_auth_octets_scheme(this, FALSE, id, &octets, ¶ms) && private->sign(private, params->scheme, NULL, octets, auth_data)) { status = SUCCESS; } if (params) { signature_params_destroy(params); } DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id, auth_method_names, *auth_method, status == SUCCESS ? "successful" : "failed"); chunk_free(&octets); return status; } METHOD(authenticator_t, build, status_t, private_pubkey_authenticator_t *this, message_t *message) { private_key_t *private; identification_t *id; auth_cfg_t *auth; chunk_t auth_data; status_t status; auth_payload_t *auth_payload; auth_method_t auth_method = AUTH_NONE; id = this->ike_sa->get_my_id(this->ike_sa); auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth); if (!private) { DBG1(DBG_IKE, "no private key found for '%Y'", id); return NOT_FOUND; } if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH)) { auth_method = AUTH_DS; status = sign_signature_auth(this, auth, private, id, &auth_data); } else { status = sign_classic(this, auth, private, id, &auth_method, &auth_data); } private->destroy(private); if (status == SUCCESS) { auth_payload = auth_payload_create(); auth_payload->set_auth_method(auth_payload, auth_method); auth_payload->set_data(auth_payload, auth_data); chunk_free(&auth_data); message->add_payload(message, (payload_t*)auth_payload); } return status; } METHOD(authenticator_t, process, status_t, private_pubkey_authenticator_t *this, message_t *message) { public_key_t *public; auth_method_t auth_method; auth_payload_t *auth_payload; chunk_t auth_data, octets; identification_t *id; auth_cfg_t *auth, *current_auth; enumerator_t *enumerator; key_type_t key_type = KEY_ECDSA; signature_params_t *params; status_t status = NOT_FOUND; const char *reason = "unsupported"; bool online; auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); if (!auth_payload) { return FAILED; } INIT(params); auth_method = auth_payload->get_auth_method(auth_payload); auth_data = auth_payload->get_data(auth_payload); switch (auth_method) { case AUTH_RSA: key_type = KEY_RSA; params->scheme = SIGN_RSA_EMSA_PKCS1_SHA1; break; case AUTH_ECDSA_256: params->scheme = SIGN_ECDSA_256; break; case AUTH_ECDSA_384: params->scheme = SIGN_ECDSA_384; break; case AUTH_ECDSA_521: params->scheme = SIGN_ECDSA_521; break; case AUTH_DS: if (parse_signature_auth_data(&auth_data, &key_type, params)) { break; } reason = "payload invalid"; /* fall-through */ default: DBG1(DBG_IKE, "%N authentication %s", auth_method_names, auth_method, reason); signature_params_destroy(params); return INVALID_ARG; } id = this->ike_sa->get_other_id(this->ike_sa); if (!get_auth_octets_scheme(this, TRUE, id, &octets, ¶ms)) { return FAILED; } auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); online = !this->ike_sa->has_condition(this->ike_sa, COND_ONLINE_VALIDATION_SUSPENDED); enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, key_type, id, auth, online); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, params->scheme, params->params, octets, auth_data)) { if (auth_method != AUTH_DS) { DBG1(DBG_IKE, "authentication of '%Y' with %N successful", id, auth_method_names, auth_method); } else if (params->scheme == SIGN_RSA_EMSA_PSS) { rsa_pss_params_t *pss = params->params; DBG1(DBG_IKE, "authentication of '%Y' with %N_%N successful", id, signature_scheme_names, params->scheme, hash_algorithm_short_names_upper, pss->hash); } else { DBG1(DBG_IKE, "authentication of '%Y' with %N successful", id, signature_scheme_names, params->scheme); } status = SUCCESS; auth->merge(auth, current_auth, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); auth->add(auth, AUTH_RULE_IKE_SIGNATURE_SCHEME, signature_params_clone(params)); if (!online) { auth->add(auth, AUTH_RULE_CERT_VALIDATION_SUSPENDED, TRUE); } break; } else { status = FAILED; DBG1(DBG_IKE, "signature validation failed, looking for another key"); } } enumerator->destroy(enumerator); chunk_free(&octets); signature_params_destroy(params); if (status == NOT_FOUND) { DBG1(DBG_IKE, "no trusted %N public key found for '%Y'", key_type_names, key_type, id); } return status; } METHOD(authenticator_t, destroy, void, private_pubkey_authenticator_t *this) { free(this); } /* * Described in header. */ pubkey_authenticator_t *pubkey_authenticator_create_builder(ike_sa_t *ike_sa, chunk_t received_nonce, chunk_t sent_init, char reserved[3]) { private_pubkey_authenticator_t *this; INIT(this, .public = { .authenticator = { .build = _build, .process = (void*)return_failed, .is_mutual = (void*)return_false, .destroy = _destroy, }, }, .ike_sa = ike_sa, .ike_sa_init = sent_init, .nonce = received_nonce, ); memcpy(this->reserved, reserved, sizeof(this->reserved)); return &this->public; } /* * Described in header. */ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa, chunk_t sent_nonce, chunk_t received_init, char reserved[3]) { private_pubkey_authenticator_t *this; INIT(this, .public = { .authenticator = { .build = (void*)return_failed, .process = _process, .is_mutual = (void*)return_false, .destroy = _destroy, }, }, .ike_sa = ike_sa, .ike_sa_init = received_init, .nonce = sent_nonce, ); memcpy(this->reserved, reserved, sizeof(this->reserved)); return &this->public; }