1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
|
/* do PSK operations for IKEv2
*
* Copyright (C) 2007 Michael Richardson <mcr@xelerance.com>
* Copyright (C) 2008 David McCullough <david_mccullough@securecomputing.com>
* Copyright (C) 2008-2009 Paul Wouters <paul@xelerance.com>
* Copyright (C) 2008 Antony Antony <antony@xelerance.com>
* Copyright (C) 2015 Antony Antony <antony@phenome.org>
* Copyright (C) 2012-2013 Paul Wouters <paul@libreswan.org>
* Copyright (C) 2013-2019 D. Hugh Redelmeier <hugh@mimosa.com>
* Copyright (C) 2015 Paul Wouters <pwouters@redhat.com>
* Copyright (C) 2015-2019 Andrew Cagney <cagney@gnu.org>
* Copyright (C) 2017 Vukasin Karadzic <vukasin.karadzic@gmail.com>
* Copyright (C) 2020 Yulia Kuzovkova <ukuzovkova@gmail.com>
* Copyright (C) 2020 Nupur Agrawal <nupur202000@gmail.com>
*
* 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 <https://www.gnu.org/licenses/gpl2.txt>.
*
* 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 <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pk11pub.h>
#include "sysdep.h"
#include "constants.h"
#include "defs.h"
#include "id.h"
#include "x509.h"
#include "certs.h"
#include "connections.h" /* needs id.h */
#include "state.h"
#include "packet.h"
#include "crypto.h"
#include "ike_alg.h"
#include "log.h"
#include "demux.h" /* needs packet.h */
#include "ikev2.h"
#include "server.h"
#include "keys.h"
#include "crypt_prf.h"
#include "crypt_symkey.h"
#include "fips_mode.h"
#include "ikev2_prf.h"
#include "ikev2_psk.h"
diag_t ikev2_calculate_psk_sighash(enum perspective perspective,
const struct hash_signature *auth_sig,
const struct ike_sa *ike,
enum keyword_auth authby,
const struct crypt_mac *idhash,
const chunk_t firstpacket,
struct crypt_mac *sighash)
{
struct logger *logger = ike->sa.logger;
const struct connection *c = ike->sa.st_connection;
*sighash = empty_mac;
passert(authby == AUTH_EAPONLY || authby == AUTH_PSK || authby == AUTH_NULL);
enum_buf pb;
enum_buf an;
ldbg(logger, "%s() called for %s to %s PSK with authby=%s resume=%s",
__func__, ike->sa.st_state->name,
str_enum_short(&perspective_names, perspective, &pb),
str_enum(&keyword_auth_names, authby, &an),
bool_str(ike->sa.st_v2_resume_session != NULL));
/* this is the IKE_AUTH exchange, so a given */
passert(ike->sa.hidden_variables.st_skeyid_calculated);
chunk_t intermediate_auth = empty_chunk;
if (ike->sa.st_v2_ike_intermediate.enabled) {
intermediate_auth = clone_hunk_hunk(ike->sa.st_v2_ike_intermediate.initiator,
ike->sa.st_v2_ike_intermediate.responder,
"IntAuth_*_I_A | IntAuth_*_R");
/* IKE AUTH's first Message ID */
uint8_t ike_auth_mid[sizeof(ike->sa.st_v2_ike_intermediate.id)];
hton_thing(ike->sa.st_v2_ike_intermediate.id + 1, ike_auth_mid);
append_chunk_thing("IKE_AUTH_MID", &intermediate_auth, ike_auth_mid);
}
/*
* Pick nullauth_pss, nonce, and nonce_name suitable for
* (state, verify).
*
* For pre-shared key:
*
* For the initiator:
*
* AUTH = prf( prf(Shared Secret, "Key Pad for IKEv2"),
* <InitiatorSignedOctets>)
* For the responder:
*
* AUTH = prf( prf(Shared Secret, "Key Pad for IKEv2"),
* <ResponderSignedOctets>)
*
* The NULL Authentication Method (RFC-7619):
*
* When using the NULL Authentication Method, the content of
* the AUTH payload is computed using the syntax of
* pre-shared secret authentication, described in Section
* 2.15 of [RFC7296]. The values SK_pi and SK_pr are used
* as shared secrets for the content of the AUTH payloads
* generated by the initiator and the responder
* respectively.
*
* I'm guessing this means:
*
* prf(prf(SK_px, "Key Pad for IKEv2"), signed octets)
*
* We have SK_pi/SK_pr as PK11SymKey in st_skey_pi_nss and
* st_skey_pr_nss.
* Session resume (RFC-5723):
*
* The value of the AUTH payload is derived in a manner
* similar to the usage of IKEv2 pre-shared secret
* authentication:
*
* AUTH = prf(SK_px, <message octets>)
*/
chunk_t nullauth_pss;
const chunk_t *nonce;
const char *nonce_name;
PK11SymKey *SK_px;
switch (ike->sa.st_sa_role) {
case SA_INITIATOR:
switch (perspective) {
case REMOTE_PERSPECTIVE:
/* we are initiator verifying PSK */
nonce_name = "Ni: initiator re-generating hash from responder";
nonce = &ike->sa.st_ni;
nullauth_pss = ike->sa.st_skey_chunk_SK_pr;
SK_px = ike->sa.st_skey_pr_nss;
break;
case LOCAL_PERSPECTIVE:
/* we are initiator sending PSK */
nonce_name = "Nr: initiator generating hash for responder";
nonce = &ike->sa.st_nr;
nullauth_pss = ike->sa.st_skey_chunk_SK_pi;
SK_px = ike->sa.st_skey_pi_nss;
break;
case NO_PERSPECTIVE:
default:
bad_case(perspective);
}
break;
case SA_RESPONDER:
switch (perspective) {
case REMOTE_PERSPECTIVE:
/* we are responder verifying PSK */
nonce_name = "Nr: responder re-generating hash from initiator";
nonce = &ike->sa.st_nr;
nullauth_pss = ike->sa.st_skey_chunk_SK_pi;
SK_px = ike->sa.st_skey_pi_nss;
break;
case LOCAL_PERSPECTIVE:
/* we are responder sending PSK */
nonce_name = "Ni: create: responder generating hash for initiator";
nonce = &ike->sa.st_ni;
nullauth_pss = ike->sa.st_skey_chunk_SK_pr;
SK_px = ike->sa.st_skey_pr_nss;
break;
case NO_PERSPECTIVE:
default:
bad_case(perspective);
}
break;
default:
bad_case(ike->sa.st_sa_role);
}
/*
* IKE_SESSION_RESUME has its own idea of how to do crypto.
*/
if (ike->sa.st_v2_resume_session != NULL) {
/*
* RFC 5723 6.2:
* AUTH = prf(SK_px, <message octets>)
*/
(*sighash) = ikev2_psk_resume(ike->sa.st_oakley.ta_prf, SK_px,
firstpacket, logger);
PASSERT(logger, sighash->len > 0);
return NULL;
}
/* pick pss */
shunk_t pss;
if (auth_sig != NULL) {
/*
* Use the auth sig passed in, presumably accumulated
* by repeated EAP-TLS exchanges?
*/
if (!pexpect(auth_sig->len > 0)) {
return diag(PEXPECT_PREFIX"missing auth_sig");
}
pss = HUNK_AS_SHUNK(*auth_sig);
} else if (authby != AUTH_NULL) {
/*
* XXX: same PSK used for both local and remote end,
* so peer doesn't apply?
*/
const struct secret_preshared_stuff *psk = get_connection_psk(c);
if (psk == NULL) {
id_buf idb;
return diag("authentication failed: no PSK found for '%s'",
str_id(&c->local->host.id, &idb));
}
/* XXX: this should happen during connection load */
const size_t key_size_min = crypt_prf_fips_key_size_min(ike->sa.st_oakley.ta_prf);
if (psk->len < key_size_min) {
if (is_fips_mode()) {
id_buf idb;
return diag("FIPS: authentication failed: '%s' PSK length of %zu bytes is too short for PRF %s in FIPS mode (%zu bytes required)",
str_id(&c->local->host.id, &idb),
psk->len,
ike->sa.st_oakley.ta_prf->common.fqn,
key_size_min);
}
id_buf idb;
llog_sa(RC_LOG, ike,
"WARNING: '%s' PSK length of %zu bytes is too short for PRF %s in FIPS mode (%zu bytes required)",
str_id(&c->local->host.id, &idb),
psk->len,
ike->sa.st_oakley.ta_prf->common.fqn,
key_size_min);
}
pss = HUNK_AS_SHUNK(*psk);
} else {
pss = HUNK_AS_SHUNK(nullauth_pss);
}
passert(pss.len != 0);
if (DBGP(DBG_CRYPT)) {
DBG_dump_hunk("inputs to hash1 (first packet)", firstpacket);
DBG_dump_hunk(nonce_name, *nonce);
DBG_dump_hunk("idhash", *idhash);
DBG_dump_hunk("IntAuth", intermediate_auth);
DBG_dump_hunk("PSK", pss);
}
/*
* RFC 4306 2.15:
* AUTH = prf(prf(Shared Secret, "Key Pad for IKEv2"), <msg octets>)
*/
passert(idhash->len == ike->sa.st_oakley.ta_prf->prf_output_size);
*sighash = ikev2_psk_auth(ike->sa.st_oakley.ta_prf, pss,
firstpacket, *nonce, idhash,
intermediate_auth, ike->sa.logger);
free_chunk_content(&intermediate_auth);
return NULL;
}
bool ikev2_create_psk_auth(enum keyword_auth authby,
const struct ike_sa *ike,
const struct crypt_mac *idhash,
chunk_t *additional_auth /* output */)
{
*additional_auth = empty_chunk;
struct crypt_mac signed_octets = empty_mac;
diag_t d = ikev2_calculate_psk_sighash(LOCAL_PERSPECTIVE, NULL,
ike, authby, idhash,
ike->sa.st_firstpacket_me,
&signed_octets);
if (d != NULL) {
llog(RC_LOG, ike->sa.logger, "%s", str_diag(d));
pfree_diag(&d);
return false;
}
const char *chunk_n = (authby == AUTH_PSK) ? "NO_PPK_AUTH chunk" : "NULL_AUTH chunk";
*additional_auth = clone_hunk(signed_octets, chunk_n);
if (DBGP(DBG_CRYPT)) {
DBG_dump_hunk(chunk_n, *additional_auth);
}
return true;
}
/*
* Check the signature using PSK authentication and log the outcome.
*
* The log message must mention both the peer's ID and kind.
*/
diag_t verify_v2AUTH_and_log_using_psk(enum keyword_auth authby,
const struct ike_sa *ike,
const struct crypt_mac *idhash,
struct pbs_in *sig_pbs,
const struct hash_signature *auth_sig)
{
shunk_t sig = pbs_in_left(sig_pbs);
passert(authby == AUTH_EAPONLY || authby == AUTH_PSK || authby == AUTH_NULL);
size_t hash_len = ike->sa.st_oakley.ta_prf->prf_output_size;
if (sig.len != hash_len) {
esb_buf kb;
id_buf idb;
return diag("authentication failed: %zu byte hash received from peer %s '%s' does not match %zu byte hash of negotiated PRF %s",
sig.len,
str_enum(&ike_id_type_names, ike->sa.st_connection->remote->host.id.kind, &kb),
str_id(&ike->sa.st_connection->remote->host.id, &idb),
hash_len, ike->sa.st_oakley.ta_prf->common.fqn);
}
struct crypt_mac calc_hash = empty_mac;
diag_t d = ikev2_calculate_psk_sighash(REMOTE_PERSPECTIVE, auth_sig,
ike, authby, idhash,
ike->sa.st_firstpacket_peer,
&calc_hash);
if (d != NULL) {
return d;
}
if (DBGP(DBG_CRYPT)) {
DBG_dump_hunk("Received PSK auth octets", sig);
DBG_dump_hunk("Calculated PSK auth octets", calc_hash);
}
if (!hunk_eq(sig, calc_hash)) {
id_buf idb;
esb_buf kb;
return diag("authentication failed: computed hash does not match hash received from peer %s '%s'",
str_enum(&ike_id_type_names, ike->sa.st_connection->remote->host.id.kind, &kb),
str_id(&ike->sa.st_connection->remote->host.id, &idb));
}
LLOG_JAMBUF(RC_LOG, ike->sa.logger, buf) {
jam(buf, "%s established IKE SA; ",
(ike->sa.st_sa_role == SA_INITIATOR ? "initiator" :
ike->sa.st_sa_role == SA_RESPONDER ? "responder" :
"?"));
/* all methods log this string */
jam_string(buf, "authenticated peer ");
if (ike->sa.st_v2_resume_session != NULL) {
jam_string(buf, "using authby=session-resume");
} else {
/* what was in the AUTH payload */
/* XXX: log prf(prf(hash based on null or secret)) how? */
/* now it was authenticated */
jam_string(buf, "using authby=");
jam_enum(buf, &keyword_auth_names, authby);
jam_string(buf, " and ");
jam_enum(buf, &ike_id_type_names, ike->sa.st_connection->remote->host.id.kind);
jam_string(buf, " '");
jam_id_bytes(buf, &ike->sa.st_connection->remote->host.id, jam_raw_bytes);
jam_string(buf, "'");
}
}
return NULL;
}
|