struct db_sa *oakley_alg_mergedb(struct alg_info_ike *ai, enum ikev1_auth_method auth_method, bool single_dh) { passert(ai != NULL); struct db_sa *gsp = NULL; /* Next two are for multiple proposals in aggressive mode... */ unsigned last_modp = 0; bool warned_dropped_dhgr = FALSE; int transcnt = 0; /* * for each group, we will create a new proposal item, and then * append it to the list of transforms in the conjoint point. * * when creating each item, we will use the first transform * from the base item as the template. */ FOR_EACH_IKE_INFO(ai, ike_info) { struct db_sa *emp_sp; passert(ike_info->ike_encrypt); passert(ike_info->ike_prf); passert(ike_info->ike_dh_group); unsigned ealg = ike_info->ike_encrypt->common.ikev1_oakley_id; unsigned halg = ike_info->ike_prf->common.ikev1_oakley_id; unsigned modp = ike_info->ike_dh_group->group; unsigned eklen = ike_info->ike_eklen; DBG(DBG_CONTROL, DBG_log("oakley_alg_makedb() processing ealg=%s=%u halg=%s=%u modp=%s=%u eklen=%u", ike_info->ike_encrypt->common.name, ealg, ike_info->ike_prf->common.name, halg, ike_info->ike_dh_group->common.name, modp, eklen)); const struct encrypt_desc *enc_desc = ike_info->ike_encrypt; if (eklen != 0 && !encrypt_has_key_bit_length(enc_desc, eklen)) { PEXPECT_LOG("IKEv1 proposal with ENCRYPT%s (specified) keylen:%d, not valid, should have been dropped", enc_desc->common.name, eklen); continue; } /* * copy the template */ emp_sp = sa_copy_sa(&oakley_empty); passert(emp_sp->dynamic); passert(emp_sp->prop_conj_cnt == 1); passert(emp_sp->prop_conjs[0].prop_cnt == 1); passert(emp_sp->prop_conjs[0].props[0].trans_cnt == 1); struct db_trans *trans = &emp_sp->prop_conjs[0].props[0].trans[0]; passert(trans->attr_cnt == 5); /* * See "struct db_attr otempty" above, and spdb.c, for * where these magic values come from. */ struct db_attr *enc = &trans->attrs[0]; struct db_attr *hash = &trans->attrs[1]; struct db_attr *auth = &trans->attrs[2]; struct db_attr *grp = &trans->attrs[3]; /* * auth type for IKE must be set. */ passert(auth->type.oakley == OAKLEY_AUTHENTICATION_METHOD); auth->val = auth_method; if (eklen > 0) { struct db_attr *enc_keylen = &trans->attrs[4]; passert(trans->attr_cnt == 5); passert(enc_keylen->type.oakley == OAKLEY_KEY_LENGTH); enc_keylen->val = eklen; } else { /* truncate */ trans->attr_cnt = 4; } passert(enc->type.oakley == OAKLEY_ENCRYPTION_ALGORITHM); if (ealg > 0) enc->val = ealg; /* * Either pass a hash algorithm or a PRF. * * Since AEAD algorithms don't need the hash, * but do need a PRF, the hash field can be * re-purposed as a PRF field. * * [cagney] While I suspect that type will * never initially be OAKLEY_PRF (it is * initialized using "struct db_attr otempty") * it doesn't hurt to be safe. */ passert(hash->type.oakley == OAKLEY_HASH_ALGORITHM || hash->type.oakley == OAKLEY_PRF); if (halg > 0) { hash->val = halg; if (ike_alg_enc_requires_integ(enc_desc)) { hash->type.oakley = OAKLEY_HASH_ALGORITHM; } else { hash->type.oakley = OAKLEY_PRF; } } passert(grp->type.oakley == OAKLEY_GROUP_DESCRIPTION); if (modp > 0) grp->val = modp; /* * Aggressive mode really only works with a single DH group. * If this is for Aggressive Mode, and we've previously seen * a different DH group, we try to deal with this. */ if (single_dh && transcnt > 0 && ike_info->ike_dh_group->group != last_modp) { if (last_modp == OAKLEY_GROUP_MODP1024 || last_modp == OAKLEY_GROUP_MODP1536) { /* * The previous group will work on old Cisco gear, * so we can discard this one. */ if (!warned_dropped_dhgr) { /* complain only once */ loglog(RC_LOG_SERIOUS, "multiple DH groups were set in aggressive mode. Only first one used."); } loglog(RC_LOG_SERIOUS, "transform (%s,%s,%s keylen %ld) ignored.", enum_name(&oakley_enc_names, ike_info->ike_encrypt->common.ikev1_oakley_id), enum_name(&oakley_hash_names, ike_info->ike_prf->common.ikev1_oakley_id), ike_info->ike_dh_group->common.name, (long)ike_info->ike_eklen); free_sa(&emp_sp); } else { /* * The previous group won't work on old Cisco gear, * so we discard the previous ones. * Of course this modp might be just as bad; * we won't look until the next one comes along. * * Lemma: there will be only a single previous * one in gsp (any others were discarded). */ loglog(RC_LOG_SERIOUS, "multiple DH groups in aggressive mode can cause interop failure"); loglog(RC_LOG_SERIOUS, "Deleting previous proposal in the hopes of selecting DH 2 or DH 5"); free_sa(&gsp); } warned_dropped_dhgr = TRUE; } if (emp_sp != NULL) { /* * Exclude 3des et.al. which do not include * default key lengths in the proposal. */ if (ike_info->ike_eklen == 0 && !ike_info->ike_encrypt->keylen_omitted) { const struct encrypt_desc *enc_desc = ike_info->ike_encrypt; int def_ks = enc_desc->keydeflen; passert(def_ks); /* ike=null not supported */ int max_ks = encrypt_max_key_bit_length(enc_desc); int ks; passert(emp_sp->dynamic); passert(emp_sp->prop_conj_cnt == 1); passert(emp_sp->prop_conjs[0].prop_cnt == 1); passert(emp_sp->prop_conjs[0].props[0].trans_cnt == 1); if (emp_sp->prop_conjs[0].props[0].trans[0].attr_cnt == 4) { /* add a key length attribute of 0 */ struct db_trans *tr = &emp_sp->prop_conjs[0].props[0].trans[0]; const int n = tr->attr_cnt; /* 4, actually */ struct db_attr *old_attrs = tr->attrs; struct db_attr *new_attrs = alloc_bytes( (n + 1) * sizeof(old_attrs[0]), "extended trans"); passert(emp_sp->dynamic); passert(old_attrs[0].type.oakley != OAKLEY_KEY_LENGTH && old_attrs[1].type.oakley != OAKLEY_KEY_LENGTH && old_attrs[2].type.oakley != OAKLEY_KEY_LENGTH && old_attrs[3].type.oakley != OAKLEY_KEY_LENGTH); memcpy(new_attrs, old_attrs, n * sizeof(old_attrs[0])); new_attrs[n].type.oakley = OAKLEY_KEY_LENGTH; new_attrs[n].val = 0; pfree(old_attrs); tr->attrs = new_attrs; tr->attr_cnt++; } passert(emp_sp->prop_conjs[0].props[0].trans[0].attr_cnt == 5); passert(emp_sp->prop_conjs[0].props[0].trans[0].attrs[4].type.oakley == OAKLEY_KEY_LENGTH); /* * This odd FOR loop executes its body for * exactly two values of ks (max_ks and def_ks) * unless def_ks == max_ks, in which case it is * executed once. */ for (ks = max_ks; ; ks = def_ks) { emp_sp->prop_conjs[0].props[0].trans[0].attrs[4].val = ks; if (gsp == NULL) { gsp = sa_copy_sa(emp_sp); } else { struct db_sa *new = sa_merge_proposals(gsp, emp_sp); free_sa(&gsp); gsp = new; } if (ks == def_ks) break; } free_sa(&emp_sp); } else { if (gsp != NULL) { /* now merge emp_sa and gsp */ struct db_sa *new = sa_merge_proposals(gsp, emp_sp); free_sa(&gsp); free_sa(&emp_sp); gsp = new; } else { gsp = emp_sp; emp_sp = NULL; } } last_modp = ike_info->ike_dh_group->group; }
/* * Create an OAKLEY proposal based on alg_info and policy * * single_dh is for Aggressive Mode where we must have exactly * one DH group. */ struct db_sa *oakley_alg_makedb(struct alg_info_ike *ai, struct db_sa *base, bool single_dh) { struct db_sa *gsp = NULL; struct ike_info *ike_info; /* Next two are for multiple proposals in agressive mode... */ unsigned last_modp = 0; bool warned_dropped_dhgr = FALSE; int transcnt = 0; int i; /* * start by copying the proposal that would have been picked by * standard defaults. */ if (ai == NULL) { DBG(DBG_CONTROL, DBG_log( "no specific IKE algorithms specified - using defaults")); return NULL; } /* * for each group, we will create a new proposal item, and then * append it to the list of transforms in the conjoint point. * * when creating each item, we will use the first transform * from the base item as the template. */ ALG_INFO_IKE_FOREACH(ai, ike_info, i) { struct db_sa *emp_sp; unsigned ealg = ike_info->ike_ealg; unsigned halg = ike_info->ike_halg; unsigned modp = ike_info->ike_modp; unsigned eklen = ike_info->ike_eklen; DBG(DBG_CONTROL, DBG_log("oakley_alg_makedb() " "processing ealg=%u halg=%u modp=%u eklen=%u", ealg, halg, modp, eklen)); const struct encrypt_desc *enc_desc = ike_alg_get_encrypter(ealg); if (enc_desc == NULL) { DBG_log("oakley_alg_makedb() " "ike enc ealg=%d not present", ealg); continue; } passert(enc_desc != NULL); if (ike_alg_enc_requires_integ(enc_desc)) { if (!ike_alg_hash_present(halg)) { DBG_log("oakley_alg_makedb() " "ike hash halg=%d not present but required for integrity", halg); continue; } } else { if (!ike_alg_hash_present(halg)) { DBG_log("oakley_alg_makedb() " "ike PRF=%d not present but needed for AEAD", halg); continue; } } if (eklen != 0 && (eklen < enc_desc->keyminlen || eklen > enc_desc->keymaxlen)) { DBG_log("ike_alg_db_new() ealg=%d (specified) keylen:%d, not valid min=%d, max=%d", ealg, eklen, enc_desc->keyminlen, enc_desc->keymaxlen); continue; } /* * copy the basic item, and modify it. * * ??? what are these two cases and why does * eklen select between them? * * [cagney] I suspect that this is to * compensate for logic further down that, * when eklen==0, truncates the attrs array to * 4 elements and sa_copy_sa_first() when * applied to that structure won't allocate * space for the 5th (eklen) element - oops. * To be honest, the attrs should be a list OR * the eklen>0 path should always be taken OR * ... * * The convoluted assignment is copying the * auth field (see "struct db_attr otempty" * above), from base to the new proposal. */ if (eklen > 0) { /* duplicate, but change auth to match template */ emp_sp = sa_copy_sa(&oakley_empty); emp_sp->prop_conjs[0].props[0].trans[0].attrs[2] = base->prop_conjs[0].props[0].trans[0].attrs[2]; } else { emp_sp = sa_copy_sa_first(base); } passert(emp_sp->dynamic); passert(emp_sp->prop_conj_cnt == 1); passert(emp_sp->prop_conjs[0].prop_cnt == 1); passert(emp_sp->prop_conjs[0].props[0].trans_cnt == 1); struct db_trans *trans = &emp_sp->prop_conjs[0].props[0].trans[0]; /* * See "struct db_attr otempty" above for * where these magic values come from. */ passert(trans->attr_cnt == 4 || trans->attr_cnt == 5); struct db_attr *enc = &trans->attrs[0]; struct db_attr *hash = &trans->attrs[1]; struct db_attr *auth = &trans->attrs[2]; struct db_attr *grp = &trans->attrs[3]; if (eklen > 0) { struct db_attr *enc_keylen = &trans->attrs[4]; passert(trans->attr_cnt == 5); passert(enc_keylen->type.oakley == OAKLEY_KEY_LENGTH); enc_keylen->val = eklen; } else { /* truncate */ trans->attr_cnt = 4; } passert(enc->type.oakley == OAKLEY_ENCRYPTION_ALGORITHM); if (ealg > 0) enc->val = ealg; /* * Either pass a hash algorithm or a PRF. * * Since AEAD algorithms don't need the hash, * but do need a PRF, the hash field can be * re-purposed as a PRF field. * * [cagney] While I suspect that type will * never initially be OAKLEY_PRF (it is * initialized using "struct db_attr otempty") * it doesn't hurt to be safe. */ passert(hash->type.oakley == OAKLEY_HASH_ALGORITHM || hash->type.oakley == OAKLEY_PRF); if (halg > 0) { hash->val = halg; if (ike_alg_enc_requires_integ(enc_desc)) { hash->type.oakley = OAKLEY_HASH_ALGORITHM; } else { hash->type.oakley = OAKLEY_PRF; } } /* * auth type for IKE must be set. * * Logic above uses sa_copy_sa or brute force * to copy the field from BASE. * * ??? until we support AES-GCM in IKE * * [cagney] aes-gcm doesn't require HASH, just * the PRF, so auth is unrelated? */ passert(auth->type.oakley == OAKLEY_AUTHENTICATION_METHOD); passert(grp->type.oakley == OAKLEY_GROUP_DESCRIPTION); if (modp > 0) grp->val = modp; /* * Aggressive mode really only works with a single DH group. * If this is for Aggressive Mode, and we've previously seen * a different DH group, we try to deal with this. */ if (single_dh && transcnt > 0 && ike_info->ike_modp != last_modp) { if (last_modp == OAKLEY_GROUP_MODP1024 || last_modp == OAKLEY_GROUP_MODP1536) { /* * The previous group will work on old Cisco gear, * so we can discard this one. */ if (!warned_dropped_dhgr) { /* complain only once */ loglog(RC_LOG_SERIOUS, "multiple DH groups were set in aggressive mode. Only first one used."); } loglog(RC_LOG_SERIOUS, "transform (%s,%s,%s keylen %ld) ignored.", enum_name(&oakley_enc_names, ike_info->ike_ealg), enum_name(&oakley_hash_names, ike_info->ike_halg), enum_name(&oakley_group_names, ike_info->ike_modp), (long)ike_info->ike_eklen); free_sa(&emp_sp); } else { /* * The previous group won't work on old Cisco gear, * so we discard the previous ones. * Of course this modp might be just as bad; * we won't look until the next one comes along. * * Lemma: there will be only a single previous * one in gsp (any others were discarded). */ loglog(RC_LOG_SERIOUS, "multiple DH groups in aggressive mode can cause interop failure"); loglog(RC_LOG_SERIOUS, "Deleting previous proposal in the hopes of selecting DH 2 or DH 5"); free_sa(&gsp); } warned_dropped_dhgr = TRUE; } if (emp_sp != NULL) { int def_ks = 0; if (ike_info->ike_eklen == 0) def_ks = crypto_req_keysize(CRK_IKEv1, ike_info->ike_ealg); if (def_ks != 0) { const struct encrypt_desc *enc_desc = ike_alg_get_encrypter(ike_info->ike_ealg); int max_ks = enc_desc->keymaxlen; int ks; passert(emp_sp->dynamic); passert(emp_sp->prop_conj_cnt == 1); passert(emp_sp->prop_conjs[0].prop_cnt == 1); passert(emp_sp->prop_conjs[0].props[0].trans_cnt == 1); if (emp_sp->prop_conjs[0].props[0].trans[0].attr_cnt == 4) { /* add a key length attribute of 0 */ struct db_trans *tr = &emp_sp->prop_conjs[0].props[0].trans[0]; const int n = tr->attr_cnt; /* 4, actually */ struct db_attr *old_attrs = tr->attrs; struct db_attr *new_attrs = alloc_bytes( (n + 1) * sizeof(old_attrs[0]), "extended trans"); passert(emp_sp->dynamic); passert(old_attrs[0].type.oakley != OAKLEY_KEY_LENGTH && old_attrs[1].type.oakley != OAKLEY_KEY_LENGTH && old_attrs[2].type.oakley != OAKLEY_KEY_LENGTH && old_attrs[3].type.oakley != OAKLEY_KEY_LENGTH); memcpy(new_attrs, old_attrs, n * sizeof(old_attrs[0])); new_attrs[n].type.oakley = OAKLEY_KEY_LENGTH; new_attrs[n].val = 0; pfree(old_attrs); tr->attrs = new_attrs; tr->attr_cnt++; } passert(emp_sp->prop_conjs[0].props[0].trans[0].attr_cnt == 5); passert(emp_sp->prop_conjs[0].props[0].trans[0].attrs[4].type.oakley == OAKLEY_KEY_LENGTH); /* * This odd FOR loop executes its body for * exactly two values of ks (def_ks and max_ks) * unless def_ks == max_ks, in which case it is * executed once. */ for (ks = def_ks; ; ks = max_ks) { emp_sp->prop_conjs[0].props[0].trans[0].attrs[4].val = ks; if (gsp == NULL) { gsp = sa_copy_sa(emp_sp); } else { struct db_sa *new = sa_merge_proposals(gsp, emp_sp); free_sa(&gsp); gsp = new; } if (ks == max_ks) break; } free_sa(&emp_sp); } else { if (gsp != NULL) { /* now merge emp_sa and gsp */ struct db_sa *new = sa_merge_proposals(gsp, emp_sp); free_sa(&gsp); free_sa(&emp_sp); gsp = new; } else { gsp = emp_sp; emp_sp = NULL; } }