/* Receive HASH and check that the exchange has been consistent. */ int ike_phase_1_recv_AUTH (struct message *msg) { struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; struct prf *prf; struct hash *hash = ie->hash; char header[80]; size_t hashsize = hash->hashsize; int initiator = exchange->initiator; u_int8_t **hash_p, *id; size_t id_len; /* Choose the right fields to fill in */ hash_p = initiator ? &ie->hash_r : &ie->hash_i; id = initiator ? exchange->id_r : exchange->id_i; id_len = initiator ? exchange->id_r_len : exchange->id_i_len; /* The decoded hash will be in ie->hash_r or ie->hash_i */ if (ie->ike_auth->decode_hash (msg)) { message_drop (msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1, 0); return -1; } /* Allocate the prf and start calculating his HASH. */ prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); if (!prf) { /* XXX Log? */ return -1; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); prf->Update (prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); prf->Update (prf->prfctx, exchange->cookies + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), ISAKMP_HDR_ICOOKIE_LEN); prf->Update (prf->prfctx, exchange->cookies + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), ISAKMP_HDR_ICOOKIE_LEN); prf->Update (prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); prf->Update (prf->prfctx, id, id_len); prf->Final (hash->digest, prf->prfctx); prf_free (prf); snprintf (header, sizeof header, "ike_phase_1_recv_AUTH: computed HASH_%c", initiator ? 'R' : 'I'); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, header, hash->digest, hashsize)); /* Check that the hash we got matches the one we computed. */ if (memcmp (*hash_p, hash->digest, hashsize) != 0) { /* XXX Log? */ return -1; } return 0; }
/* * Both standard and revised RSA encryption authentication use this SKEYID * computation. */ static u_int8_t * enc_gen_skeyid (struct exchange *exchange, size_t *sz) { struct prf *prf; struct ipsec_exch *ie = exchange->data; struct hash *hash = ie->hash; u_int8_t *skeyid; hash->Init (hash->ctx); hash->Update (hash->ctx, exchange->nonce_i, exchange->nonce_i_len); hash->Update (hash->ctx, exchange->nonce_r, exchange->nonce_r_len); hash->Final (hash->digest, hash->ctx); prf = prf_alloc (ie->prf_type, hash->type, hash->digest, *sz); if (!prf) return 0; *sz = prf->blocksize; skeyid = malloc (*sz); if (!skeyid) { log_error ("enc_gen_skeyid: malloc (%d) failed", *sz); prf_free (prf); return 0; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); prf->Final (skeyid, prf->prfctx); prf_free (prf); return skeyid; }
int cfg_verify_hash(struct message *msg) { struct payload *hashp = payload_first(msg, ISAKMP_PAYLOAD_HASH); struct ipsec_sa *isa = msg->isakmp_sa->data; struct prf *prf; u_int8_t *hash, *comp_hash; size_t hash_len; if (!hashp) { log_print("cfg_verify_hash: phase 2 message missing HASH"); message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); return -1; } hash = hashp->p; hash_len = GET_ISAKMP_GEN_LENGTH(hash); comp_hash = malloc(hash_len - ISAKMP_GEN_SZ); if (!comp_hash) { log_error("cfg_verify_hash: malloc (%lu) failed", (unsigned long)hash_len - ISAKMP_GEN_SZ); return -1; } /* Verify hash. */ prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); if (!prf) { free(comp_hash); return -1; } prf->Init(prf->prfctx); prf->Update(prf->prfctx, msg->exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); prf->Update(prf->prfctx, hash + hash_len, msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); prf->Final(comp_hash, prf->prfctx); prf_free(prf); if (memcmp(hash + ISAKMP_GEN_SZ, comp_hash, hash_len - ISAKMP_GEN_SZ) != 0) { message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); free(comp_hash); return -1; } free(comp_hash); /* Mark the HASH as handled. */ hashp->flags |= PL_MARK; /* Mark message authenticated. */ msg->flags |= MSG_AUTHENTICATED; return 0; }
/* Both DSS & RSA signature authentication use this algorithm. */ static u_int8_t * sig_gen_skeyid (struct exchange *exchange, size_t *sz) { struct prf *prf; struct ipsec_exch *ie = exchange->data; u_int8_t *skeyid; unsigned char *key; key = malloc (exchange->nonce_i_len + exchange->nonce_r_len); if (!key) return 0; memcpy (key, exchange->nonce_i, exchange->nonce_i_len); memcpy (key + exchange->nonce_i_len, exchange->nonce_r, exchange->nonce_r_len); LOG_DBG ((LOG_NEGOTIATION, 80, "sig_gen_skeyid: PRF type %d, hash %d", ie->prf_type, ie->hash->type)); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "sig_gen_skeyid: SKEYID initialized with", (u_int8_t *)key, exchange->nonce_i_len + exchange->nonce_r_len)); prf = prf_alloc (ie->prf_type, ie->hash->type, key, exchange->nonce_i_len + exchange->nonce_r_len); free (key); if (!prf) return 0; *sz = prf->blocksize; skeyid = malloc (*sz); if (!skeyid) { log_error ("sig_gen_skeyid: malloc (%lu) failed", (unsigned long)*sz); prf_free (prf); return 0; } LOG_DBG ((LOG_NEGOTIATION, 80, "sig_gen_skeyid: g^xy length %lu", (unsigned long)ie->g_x_len)); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "sig_gen_skeyid: SKEYID fed with g^xy", ie->g_xy, ie->g_x_len)); prf->Init (prf->prfctx); prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); prf->Final (skeyid, prf->prfctx); prf_free (prf); return skeyid; }
int cfg_finalize_hash(struct message *msg, u_int8_t *hashp, u_int8_t *data, u_int16_t length) { struct ipsec_sa *isa = msg->isakmp_sa->data; struct prf *prf; prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); if (!prf) return -1; prf->Init(prf->prfctx); prf->Update(prf->prfctx, msg->exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); prf->Update(prf->prfctx, data, length); prf->Final(hashp + ISAKMP_GEN_SZ, prf->prfctx); prf_free(prf); return 0; }
int ike_auth_hash (struct exchange *exchange, u_int8_t *buf) { struct ipsec_exch *ie = exchange->data; struct prf *prf; struct hash *hash = ie->hash; int initiator = exchange->initiator; u_int8_t *id; size_t id_len; /* Choose the right fields to fill-in. */ id = initiator ? exchange->id_i : exchange->id_r; id_len = initiator ? exchange->id_i_len : exchange->id_r_len; /* Allocate the prf and start calculating our HASH. */ prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); if (!prf) return -1; prf->Init (prf->prfctx); prf->Update (prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); prf->Update (prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); prf->Update (prf->prfctx, exchange->cookies + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), ISAKMP_HDR_ICOOKIE_LEN); prf->Update (prf->prfctx, exchange->cookies + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), ISAKMP_HDR_ICOOKIE_LEN); prf->Update (prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); prf->Update (prf->prfctx, id, id_len); prf->Final (buf, prf->prfctx); prf_free (prf); return 0; }
/* * Compute DH values and key material. This is done in a post-send function * as that means we can do parallel work in both the initiator and responder * thus speeding up exchanges. */ int ike_phase_1_post_exchange_KE_NONCE (struct message *msg) { struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; struct prf *prf; struct hash *hash = ie->hash; enum cryptoerr err; /* Compute Diffie-Hellman shared value. */ ie->g_xy = malloc (ie->g_x_len); if (!ie->g_xy) { /* XXX How to notify peer? */ log_error ("ike_phase_1_post_exchange_KE_NONCE: malloc (%lu) failed", (unsigned long)ie->g_x_len); return -1; } if (dh_create_shared (ie->group, ie->g_xy, exchange->initiator ? ie->g_xr : ie->g_xi)) { log_print ("ike_phase_1_post_exchange_KE_NONCE: " "dh_create_shared failed"); return -1; } LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "ike_phase_1_post_exchange_KE_NONCE: g^xy", ie->g_xy, ie->g_x_len)); /* Compute the SKEYID depending on the authentication method. */ ie->skeyid = ie->ike_auth->gen_skeyid (exchange, &ie->skeyid_len); if (!ie->skeyid) { /* XXX Log and teardown? */ return -1; } LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "ike_phase_1_post_exchange_KE_NONCE: SKEYID", ie->skeyid, ie->skeyid_len)); /* SKEYID_d. */ ie->skeyid_d = malloc (ie->skeyid_len); if (!ie->skeyid_d) { /* XXX How to notify peer? */ log_error ("ike_phase_1_post_exchange_KE_NONCE: malloc (%lu) failed", (unsigned long)ie->skeyid_len); return -1; } prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); if (!prf) { /* XXX Log and teardown? */ return -1; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); prf->Update (prf->prfctx, (unsigned char *)"\0", 1); prf->Final (ie->skeyid_d, prf->prfctx); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "ike_phase_1_post_exchange_KE_NONCE: SKEYID_d", ie->skeyid_d, ie->skeyid_len)); /* SKEYID_a. */ ie->skeyid_a = malloc (ie->skeyid_len); if (!ie->skeyid_a) { log_error ("ike_phase_1_post_exchange_KE_NONCE: malloc (%lu) failed", (unsigned long)ie->skeyid_len); prf_free (prf); return -1; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, ie->skeyid_d, ie->skeyid_len); prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); prf->Update (prf->prfctx, (unsigned char *)"\1", 1); prf->Final (ie->skeyid_a, prf->prfctx); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "ike_phase_1_post_exchange_KE_NONCE: SKEYID_a", ie->skeyid_a, ie->skeyid_len)); /* SKEYID_e. */ ie->skeyid_e = malloc (ie->skeyid_len); if (!ie->skeyid_e) { /* XXX How to notify peer? */ log_error ("ike_phase_1_post_exchange_KE_NONCE: malloc (%lu) failed", (unsigned long)ie->skeyid_len); prf_free (prf); return -1; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, ie->skeyid_a, ie->skeyid_len); prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); prf->Update (prf->prfctx, (unsigned char *)"\2", 1); prf->Final (ie->skeyid_e, prf->prfctx); prf_free (prf); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "ike_phase_1_post_exchange_KE_NONCE: SKEYID_e", ie->skeyid_e, ie->skeyid_len)); /* Key length determination. */ if (!exchange->key_length) exchange->key_length = exchange->crypto->keymax; /* Derive a longer key from skeyid_e */ if (ie->skeyid_len < exchange->key_length) { u_int16_t len, keylen; u_int8_t *key, *p; prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid_e, ie->skeyid_len); if (!prf) { /* XXX - notify peer */ return -1; } /* Make keylen a multiple of prf->blocksize */ keylen = exchange->key_length; if (keylen % prf->blocksize) keylen += prf->blocksize - (keylen % prf->blocksize); key = malloc (keylen); if (!key) { /* XXX - Notify peer. */ log_error ("ike_phase_1_post_exchange_KE_NONCE: malloc (%d) failed", keylen); return -1; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, (unsigned char *)"\0", 1); prf->Final (key, prf->prfctx); for (len = prf->blocksize, p = key; len < exchange->key_length; len += prf->blocksize, p += prf->blocksize) { prf->Init (prf->prfctx); prf->Update (prf->prfctx, p, prf->blocksize); prf->Final (p + prf->blocksize, prf->prfctx); } prf_free (prf); /* Setup our keystate using the derived encryption key. */ exchange->keystate = crypto_init (exchange->crypto, key, exchange->key_length, &err); free (key); } else /* Setup our keystate using the raw skeyid_e. */ exchange->keystate = crypto_init (exchange->crypto, ie->skeyid_e, exchange->key_length, &err); /* Special handling for DES weak keys. */ if (!exchange->keystate && err == EWEAKKEY && (exchange->key_length << 1) <= ie->skeyid_len) { log_print ("ike_phase_1_post_exchange_KE_NONCE: " "weak key, trying subseq. skeyid_e"); exchange->keystate = crypto_init (exchange->crypto, ie->skeyid_e + exchange->key_length, exchange->key_length, &err); } if (!exchange->keystate) { log_print ("ike_phase_1_post_exchange_KE_NONCE: " "exchange->crypto->init () failed: %d", err); /* * XXX We really need to know if problems are of transient nature * or fatal (like failed assertions etc.) */ return -1; } /* Setup IV. XXX Only for CBC transforms, no? */ hash->Init (hash->ctx); hash->Update (hash->ctx, ie->g_xi, ie->g_x_len); hash->Update (hash->ctx, ie->g_xr, ie->g_x_len); hash->Final (hash->digest, hash->ctx); crypto_init_iv (exchange->keystate, hash->digest, exchange->crypto->blocksize); return 0; }
static u_int8_t * pre_shared_gen_skeyid (struct exchange *exchange, size_t *sz) { struct prf *prf; struct ipsec_exch *ie = exchange->data; u_int8_t *skeyid; u_int8_t *buf = 0; unsigned char *key; size_t keylen; /* * If we're the responder and have the initiator's ID (which is the * case in Aggressive mode), try to find the preshared key in the * section of the initiator's Phase 1 ID. This allows us to do * mobile user support with preshared keys. */ if (!exchange->initiator && exchange->id_i) { switch (exchange->id_i[0]) { case IPSEC_ID_IPV4_ADDR: case IPSEC_ID_IPV6_ADDR: util_ntoa ((char **)&buf, exchange->id_i[0] == IPSEC_ID_IPV4_ADDR ? AF_INET : AF_INET6, exchange->id_i + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ); if (!buf) return 0; break; case IPSEC_ID_FQDN: case IPSEC_ID_USER_FQDN: buf = calloc (exchange->id_i_len - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1, sizeof (char)); if (!buf) { log_print ("pre_shared_gen_skeyid: malloc (%lu) failed", (unsigned long)exchange->id_i_len - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1); return 0; } memcpy (buf, exchange->id_i + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, exchange->id_i_len - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ); break; /* XXX Support more ID types ? */ default: break; } } /* * Get the pre-shared key for our peer. This will work even if the key * has been passed to us through a mechanism like PFKEYv2. */ key = ike_auth_get_key (IKE_AUTH_PRE_SHARED, exchange->name, (char *)buf, &keylen); if (buf) free (buf); /* Fail if no key could be found. */ if (!key) return 0; /* Store the secret key for later policy processing. */ exchange->recv_key = calloc (keylen + 1, sizeof (char)); exchange->recv_keytype = ISAKMP_KEY_PASSPHRASE; if (!exchange->recv_key) { log_error ("pre_shared_gen_skeyid: malloc (%lu) failed", (unsigned long)keylen); return 0; } memcpy (exchange->recv_key, key, keylen); exchange->recv_certtype = ISAKMP_CERTENC_NONE; prf = prf_alloc (ie->prf_type, ie->hash->type, key, keylen); if (!prf) return 0; *sz = prf->blocksize; skeyid = malloc (*sz); if (!skeyid) { log_error ("pre_shared_gen_skeyid: malloc (%lu) failed", (unsigned long)*sz); prf_free (prf); return 0; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); prf->Update (prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); prf->Final (skeyid, prf->prfctx); prf_free (prf); return skeyid; }