diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-04-15 18:39:31 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-04-15 18:39:31 +0000 |
commit | b1ac98abf9e700f03d5aa5f97c06dab605d6da06 (patch) | |
tree | c08a9624b4a539b85c5650bcf09528d13eb2d039 /sbin/isakmpd/ike_quick_mode.c | |
parent | aeca45bc7d867b71e9600c7108674fae5db8ff9c (diff) |
partial move to KNF. More to come. This has happened because there
are a raft of source code auditors who are willing to help improve this
code only if this is done, and hey, isakmpd does need our standard
auditing process. ok ho hshoexer
Diffstat (limited to 'sbin/isakmpd/ike_quick_mode.c')
-rw-r--r-- | sbin/isakmpd/ike_quick_mode.c | 3443 |
1 files changed, 1650 insertions, 1793 deletions
diff --git a/sbin/isakmpd/ike_quick_mode.c b/sbin/isakmpd/ike_quick_mode.c index 1b4f2c34e6a..bb92aaf2a0e 100644 --- a/sbin/isakmpd/ike_quick_mode.c +++ b/sbin/isakmpd/ike_quick_mode.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ike_quick_mode.c,v 1.76 2004/04/07 22:45:49 ho Exp $ */ -/* $EOM: ike_quick_mode.c,v 1.139 2001/01/26 10:43:17 niklas Exp $ */ +/* $OpenBSD: ike_quick_mode.c,v 1.77 2004/04/15 18:39:25 deraadt Exp $ */ +/* $EOM: ike_quick_mode.c,v 1.139 2001/01/26 10:43:17 niklas Exp $ */ /* * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. @@ -65,29 +65,29 @@ #include "x509.h" #endif -static void gen_g_xy (struct message *); -static int initiator_send_HASH_SA_NONCE (struct message *); -static int initiator_recv_HASH_SA_NONCE (struct message *); -static int initiator_send_HASH (struct message *); -static void post_quick_mode (struct message *); -static int responder_recv_HASH_SA_NONCE (struct message *); -static int responder_send_HASH_SA_NONCE (struct message *); -static int responder_recv_HASH (struct message *); +static void gen_g_xy(struct message *); +static int initiator_send_HASH_SA_NONCE(struct message *); +static int initiator_recv_HASH_SA_NONCE(struct message *); +static int initiator_send_HASH(struct message *); +static void post_quick_mode(struct message *); +static int responder_recv_HASH_SA_NONCE(struct message *); +static int responder_send_HASH_SA_NONCE(struct message *); +static int responder_recv_HASH(struct message *); #ifdef USE_POLICY -static int check_policy (struct exchange *, struct sa *, struct sa *); +static int check_policy(struct exchange *, struct sa *, struct sa *); #endif -int (*ike_quick_mode_initiator[]) (struct message *) = { - initiator_send_HASH_SA_NONCE, - initiator_recv_HASH_SA_NONCE, - initiator_send_HASH +int (*ike_quick_mode_initiator[]) (struct message *) = { + initiator_send_HASH_SA_NONCE, + initiator_recv_HASH_SA_NONCE, + initiator_send_HASH }; -int (*ike_quick_mode_responder[]) (struct message *) = { - responder_recv_HASH_SA_NONCE, - responder_send_HASH_SA_NONCE, - responder_recv_HASH +int (*ike_quick_mode_responder[]) (struct message *) = { + responder_recv_HASH_SA_NONCE, + responder_send_HASH_SA_NONCE, + responder_recv_HASH }; #ifdef USE_POLICY @@ -100,1369 +100,1262 @@ int (*ike_quick_mode_responder[]) (struct message *) = { * acceptable. */ static int -check_policy (struct exchange *exchange, struct sa *sa, struct sa *isakmp_sa) +check_policy(struct exchange *exchange, struct sa *sa, struct sa *isakmp_sa) { - char *return_values[RETVALUES_NUM]; - char **principal = 0; - int i, len, result = 0, nprinc = 0; - int *x509_ids = 0, *keynote_ids = 0; - unsigned char hashbuf[20]; /* Set to the largest digest result */ + char *return_values[RETVALUES_NUM]; + char **principal = 0; + int i, len, result = 0, nprinc = 0; + int *x509_ids = 0, *keynote_ids = 0; + unsigned char hashbuf[20]; /* Set to the largest digest result */ #ifdef USE_X509 - struct keynote_deckey dc; - X509_NAME *subject; + struct keynote_deckey dc; + X509_NAME *subject; #endif - /* Initialize if necessary -- e.g., if pre-shared key auth was used */ - if (isakmp_sa->policy_id < 0) - { - if ((isakmp_sa->policy_id = kn_init ()) == -1) - { - log_print ("check_policy: failed to initialize policy session"); - return 0; + /* Initialize if necessary -- e.g., if pre-shared key auth was used */ + if (isakmp_sa->policy_id < 0) { + if ((isakmp_sa->policy_id = kn_init()) == -1) { + log_print("check_policy: failed to initialize policy session"); + return 0; + } } - } - - /* Add the callback that will handle attributes. */ - if (kn_add_action (isakmp_sa->policy_id, ".*", (char *) policy_callback, - ENVIRONMENT_FLAG_FUNC | ENVIRONMENT_FLAG_REGEX) == -1) - { - log_print ("check_policy: " - "kn_add_action (%d, \".*\", %p, FUNC | REGEX) failed", - isakmp_sa->policy_id, policy_callback); - kn_close (isakmp_sa->policy_id); - isakmp_sa->policy_id = -1; - return 0; - } - - if (keynote_policy_asserts_num) - { - keynote_ids = calloc (keynote_policy_asserts_num, sizeof *keynote_ids); - if (!keynote_ids) - { - log_error ("check_policy: calloc (%d, %lu) failed", - keynote_policy_asserts_num, (unsigned long)sizeof *keynote_ids); - return 0; - } - } - - /* Add the policy assertions */ - for (i = 0; i < keynote_policy_asserts_num; i++) - keynote_ids[i] = kn_add_assertion (isakmp_sa->policy_id, - keynote_policy_asserts[i], - strlen (keynote_policy_asserts[i]), - ASSERT_FLAG_LOCAL); - - /* Initialize -- we'll let the callback do all the work. */ - policy_exchange = exchange; - policy_sa = sa; - policy_isakmp_sa = isakmp_sa; - - /* Set the return values; true/false for now at least. */ - return_values[0] = "false"; /* Order of values in array is important. */ - return_values[1] = "true"; - - /* Create a principal (authorizer) for the SA/ID request. */ - switch (isakmp_sa->recv_certtype) - { - case ISAKMP_CERTENC_NONE: - /* - * For shared keys, just duplicate the passphrase with the - * appropriate prefix tag. - */ - nprinc = 3; - principal = calloc (nprinc, sizeof *principal); - if (!principal) - { - log_error ("check_policy: calloc (%d, %lu) failed", nprinc, - (unsigned long)sizeof *principal); - goto policydone; + /* Add the callback that will handle attributes. */ + if (kn_add_action(isakmp_sa->policy_id, ".*", (char *) policy_callback, + ENVIRONMENT_FLAG_FUNC | ENVIRONMENT_FLAG_REGEX) == -1) { + log_print("check_policy: " + "kn_add_action (%d, \".*\", %p, FUNC | REGEX) failed", + isakmp_sa->policy_id, policy_callback); + kn_close(isakmp_sa->policy_id); + isakmp_sa->policy_id = -1; + return 0; } - - len = strlen (isakmp_sa->recv_key) + sizeof "passphrase:"; - principal[0] = calloc (len, sizeof (char)); - if (!principal[0]) - { - log_error ("check_policy: calloc (%d, %lu) failed", len, - (unsigned long)sizeof (char)); - goto policydone; + if (keynote_policy_asserts_num) { + keynote_ids = calloc(keynote_policy_asserts_num, sizeof *keynote_ids); + if (!keynote_ids) { + log_error("check_policy: calloc (%d, %lu) failed", + keynote_policy_asserts_num, + (unsigned long) sizeof *keynote_ids); + return 0; + } } + /* Add the policy assertions */ + for (i = 0; i < keynote_policy_asserts_num; i++) + keynote_ids[i] = kn_add_assertion(isakmp_sa->policy_id, + keynote_policy_asserts[i], + strlen(keynote_policy_asserts[i]), ASSERT_FLAG_LOCAL); + + /* Initialize -- we'll let the callback do all the work. */ + policy_exchange = exchange; + policy_sa = sa; + policy_isakmp_sa = isakmp_sa; + + /* Set the return values; true/false for now at least. */ + return_values[0] = "false"; /* Order of values in array is + * important. */ + return_values[1] = "true"; + + /* Create a principal (authorizer) for the SA/ID request. */ + switch (isakmp_sa->recv_certtype) { + case ISAKMP_CERTENC_NONE: + /* + * For shared keys, just duplicate the passphrase with the + * appropriate prefix tag. + */ + nprinc = 3; + principal = calloc(nprinc, sizeof *principal); + if (!principal) { + log_error("check_policy: calloc (%d, %lu) failed", nprinc, + (unsigned long) sizeof *principal); + goto policydone; + } + len = strlen(isakmp_sa->recv_key) + sizeof "passphrase:"; + principal[0] = calloc(len, sizeof(char)); + if (!principal[0]) { + log_error("check_policy: calloc (%d, %lu) failed", len, + (unsigned long) sizeof(char)); + goto policydone; + } + /* + * XXX Consider changing the magic hash lengths with + * constants. + */ + strlcpy(principal[0], "passphrase:", len); + memcpy(principal[0] + sizeof "passphrase:" - 1, isakmp_sa->recv_key, + strlen(isakmp_sa->recv_key)); + + len = sizeof "passphrase-md5-hex:" + 2 * 16; + principal[1] = calloc(len, sizeof(char)); + if (!principal[1]) { + log_error("check_policy: calloc (%d, %lu) failed", len, + (unsigned long) sizeof(char)); + goto policydone; + } + strlcpy(principal[1], "passphrase-md5-hex:", len); + MD5(isakmp_sa->recv_key, strlen(isakmp_sa->recv_key), hashbuf); + for (i = 0; i < 16; i++) + snprintf(principal[1] + 2 * i + sizeof "passphrase-md5-hex:" - 1, + 3, "%02x", hashbuf[i]); + + len = sizeof "passphrase-sha1-hex:" + 2 * 20; + principal[2] = calloc(len, sizeof(char)); + if (!principal[2]) { + log_error("check_policy: calloc (%d, %lu) failed", len, + (unsigned long) sizeof(char)); + goto policydone; + } + strlcpy(principal[2], "passphrase-sha1-hex:", len); + SHA1(isakmp_sa->recv_key, strlen(isakmp_sa->recv_key), hashbuf); + for (i = 0; i < 20; i++) + snprintf(principal[2] + 2 * i + sizeof "passphrase-sha1-hex:" - 1, + 3, "%02x", hashbuf[i]); + break; + + case ISAKMP_CERTENC_KEYNOTE: +#ifdef USE_KEYNOTE + nprinc = 1; - /* XXX Consider changing the magic hash lengths with constants. */ - strlcpy (principal[0], "passphrase:", len); - memcpy (principal[0] + sizeof "passphrase:" - 1, isakmp_sa->recv_key, - strlen (isakmp_sa->recv_key)); - - len = sizeof "passphrase-md5-hex:" + 2 * 16; - principal[1] = calloc (len, sizeof (char)); - if (!principal[1]) - { - log_error ("check_policy: calloc (%d, %lu) failed", len, - (unsigned long)sizeof (char)); - goto policydone; - } + principal = calloc(nprinc, sizeof *principal); + if (!principal) { + log_error("check_policy: calloc (%d, %lu) failed", nprinc, + (unsigned long) sizeof *principal); + goto policydone; + } + /* Dup the keys */ + principal[0] = strdup(isakmp_sa->keynote_key); + if (!principal[0]) { + log_error("check_policy: calloc (%lu, %lu) failed", + (unsigned long) strlen(isakmp_sa->keynote_key), + (unsigned long) sizeof(char)); + goto policydone; + } +#endif + break; - strlcpy (principal[1], "passphrase-md5-hex:", len); - MD5 (isakmp_sa->recv_key, strlen (isakmp_sa->recv_key), hashbuf); - for (i = 0; i < 16; i++) - snprintf (principal[1] + 2 * i + sizeof "passphrase-md5-hex:" - 1, - 3, "%02x", hashbuf[i]); - - len = sizeof "passphrase-sha1-hex:" + 2 * 20; - principal[2] = calloc (len, sizeof (char)); - if (!principal[2]) - { - log_error ("check_policy: calloc (%d, %lu) failed", len, - (unsigned long)sizeof (char)); - goto policydone; - } + case ISAKMP_CERTENC_X509_SIG: +#ifdef USE_X509 + principal = calloc(2, sizeof *principal); + if (!principal) { + log_error("check_policy: calloc (2, %lu) failed", + (unsigned long) sizeof *principal); + goto policydone; + } + if (isakmp_sa->recv_keytype == ISAKMP_KEY_RSA) + dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA; + else { + log_error("check_policy: unknown/unsupported public key algorithm " + "%d", isakmp_sa->recv_keytype); + goto policydone; + } - strlcpy (principal[2], "passphrase-sha1-hex:", len); - SHA1 (isakmp_sa->recv_key, strlen (isakmp_sa->recv_key), hashbuf); - for (i = 0; i < 20; i++) - snprintf (principal[2] + 2 * i + sizeof "passphrase-sha1-hex:" - 1, - 3, "%02x", hashbuf[i]); - break; + dc.dec_key = isakmp_sa->recv_key; + principal[0] = kn_encode_key(&dc, INTERNAL_ENC_PKCS1, ENCODING_HEX, + KEYNOTE_PUBLIC_KEY); + if (keynote_errno == ERROR_MEMORY) { + log_print("check_policy: failed to get memory for public key"); + goto policydone; + } + if (!principal[0]) { + log_print("check_policy: failed to allocate memory for principal"); + goto policydone; + } + len = strlen(principal[0]) + sizeof "rsa-hex:"; + principal[1] = calloc(len, sizeof(char)); + if (!principal[1]) { + log_error("check_policy: calloc (%d, %lu) failed", len, + (unsigned long) sizeof(char)); + goto policydone; + } + snprintf(principal[1], len, "rsa-hex:%s", principal[0]); + free(principal[0]); + principal[0] = principal[1]; + principal[1] = 0; + + /* Generate a "DN:" principal. */ + subject = X509_get_subject_name(isakmp_sa->recv_cert); + if (subject) { + principal[1] = calloc(259, sizeof(char)); + if (!principal[1]) { + log_error("check_policy: calloc (259, %lu) failed", + (unsigned long) sizeof(char)); + goto policydone; + } + strlcpy(principal[1], "DN:", 259); + X509_NAME_oneline(subject, principal[1] + 3, 256); + nprinc = 2; + } else { + nprinc = 1; + } + break; +#endif - case ISAKMP_CERTENC_KEYNOTE: -#ifdef USE_KEYNOTE - nprinc = 1; - - principal = calloc (nprinc, sizeof *principal); - if (!principal) - { - log_error ("check_policy: calloc (%d, %lu) failed", nprinc, - (unsigned long)sizeof *principal); - goto policydone; + /* XXX Eventually handle these. */ + case ISAKMP_CERTENC_PKCS: + case ISAKMP_CERTENC_PGP: + case ISAKMP_CERTENC_DNS: + case ISAKMP_CERTENC_X509_KE: + case ISAKMP_CERTENC_KERBEROS: + case ISAKMP_CERTENC_CRL: + case ISAKMP_CERTENC_ARL: + case ISAKMP_CERTENC_SPKI: + case ISAKMP_CERTENC_X509_ATTR: + default: + log_print("check_policy: " + "unknown/unsupported certificate/authentication method %d", + isakmp_sa->recv_certtype); + goto policydone; } - /* Dup the keys */ - principal[0] = strdup (isakmp_sa->keynote_key); - if (!principal[0]) - { - log_error ("check_policy: calloc (%lu, %lu) failed", - (unsigned long)strlen (isakmp_sa->keynote_key), - (unsigned long)sizeof (char)); - goto policydone; + /* + * Add the authorizer (who is requesting the SA/ID); + * this may be a public or a secret key, depending on + * what mode of authentication we used in Phase 1. + */ + for (i = 0; i < nprinc; i++) { + LOG_DBG((LOG_POLICY, 40, "check_policy: adding authorizer [%s]", + principal[i])); + + if (kn_add_authorizer(isakmp_sa->policy_id, principal[i]) == -1) { + int j; + + for (j = 0; j < i; j++) + kn_remove_authorizer(isakmp_sa->policy_id, principal[j]); + log_print("check_policy: kn_add_authorizer failed"); + goto policydone; + } } -#endif - break; - case ISAKMP_CERTENC_X509_SIG: -#ifdef USE_X509 - principal = calloc (2, sizeof *principal); - if (!principal) - { - log_error ("check_policy: calloc (2, %lu) failed", - (unsigned long)sizeof *principal); - goto policydone; - } + /* Ask policy */ + result = kn_do_query(isakmp_sa->policy_id, return_values, RETVALUES_NUM); + LOG_DBG((LOG_POLICY, 40, "check_policy: kn_do_query returned %d", result)); - if (isakmp_sa->recv_keytype == ISAKMP_KEY_RSA) - dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA; - else - { - log_error ("check_policy: unknown/unsupported public key algorithm " - "%d", isakmp_sa->recv_keytype); - goto policydone; - } + /* Cleanup environment */ + kn_cleanup_action_environment(isakmp_sa->policy_id); - dc.dec_key = isakmp_sa->recv_key; - principal[0] = kn_encode_key (&dc, INTERNAL_ENC_PKCS1, ENCODING_HEX, - KEYNOTE_PUBLIC_KEY); - if (keynote_errno == ERROR_MEMORY) - { - log_print ("check_policy: failed to get memory for public key"); - goto policydone; + /* Remove authorizers from the session */ + for (i = 0; i < nprinc; i++) { + kn_remove_authorizer(isakmp_sa->policy_id, principal[i]); + free(principal[i]); } - if (!principal[0]) - { - log_print ("check_policy: failed to allocate memory for principal"); - goto policydone; - } + free(principal); + principal = 0; + nprinc = 0; - len = strlen (principal[0]) + sizeof "rsa-hex:"; - principal[1] = calloc (len, sizeof (char)); - if (!principal[1]) - { - log_error ("check_policy: calloc (%d, %lu) failed", len, - (unsigned long)sizeof (char)); - goto policydone; + /* Check what policy said. */ + if (result < 0) { + LOG_DBG((LOG_POLICY, 40, "check_policy: proposal refused")); + result = 0; + goto policydone; + } +policydone: + for (i = 0; i < nprinc; i++) + if (principal && principal[i]) + free(principal[i]); + + if (principal) + free(principal); + + /* Remove the policies */ + for (i = 0; i < keynote_policy_asserts_num; i++) { + if (keynote_ids[i] != -1) + kn_remove_assertion(isakmp_sa->policy_id, keynote_ids[i]); } - snprintf (principal[1], len, "rsa-hex:%s", principal[0]); - free (principal[0]); - principal[0] = principal[1]; - principal[1] = 0; + if (keynote_ids) + free(keynote_ids); - /* Generate a "DN:" principal. */ - subject = X509_get_subject_name (isakmp_sa->recv_cert); - if (subject) - { - principal[1] = calloc (259, sizeof (char)); - if (!principal[1]) - { - log_error ("check_policy: calloc (259, %lu) failed", - (unsigned long)sizeof (char)); - goto policydone; - } - strlcpy (principal[1], "DN:", 259); - X509_NAME_oneline (subject, principal[1] + 3, 256); - nprinc = 2; - } else { - nprinc = 1; - } - break; -#endif + if (x509_ids) + free(x509_ids); - /* XXX Eventually handle these. */ - case ISAKMP_CERTENC_PKCS: - case ISAKMP_CERTENC_PGP: - case ISAKMP_CERTENC_DNS: - case ISAKMP_CERTENC_X509_KE: - case ISAKMP_CERTENC_KERBEROS: - case ISAKMP_CERTENC_CRL: - case ISAKMP_CERTENC_ARL: - case ISAKMP_CERTENC_SPKI: - case ISAKMP_CERTENC_X509_ATTR: - default: - log_print ("check_policy: " - "unknown/unsupported certificate/authentication method %d", - isakmp_sa->recv_certtype); - goto policydone; - } - - /* - * Add the authorizer (who is requesting the SA/ID); - * this may be a public or a secret key, depending on - * what mode of authentication we used in Phase 1. - */ - for (i = 0; i < nprinc; i++) - { - LOG_DBG ((LOG_POLICY, 40, "check_policy: adding authorizer [%s]", - principal[i])); - - if (kn_add_authorizer (isakmp_sa->policy_id, principal[i]) == -1) - { - int j; - - for (j = 0; j < i; j++) - kn_remove_authorizer (isakmp_sa->policy_id, principal[j]); - log_print ("check_policy: kn_add_authorizer failed"); - goto policydone; - } - } - - /* Ask policy */ - result = kn_do_query (isakmp_sa->policy_id, return_values, RETVALUES_NUM); - LOG_DBG ((LOG_POLICY, 40, "check_policy: kn_do_query returned %d", result)); - - /* Cleanup environment */ - kn_cleanup_action_environment (isakmp_sa->policy_id); - - /* Remove authorizers from the session */ - for (i = 0; i < nprinc; i++) - { - kn_remove_authorizer (isakmp_sa->policy_id, principal[i]); - free (principal[i]); - } - - free (principal); - principal = 0; - nprinc = 0; - - /* Check what policy said. */ - if (result < 0) - { - LOG_DBG ((LOG_POLICY, 40, "check_policy: proposal refused")); - result = 0; - goto policydone; - } - - policydone: - for (i = 0; i < nprinc; i++) - if (principal && principal[i]) - free (principal[i]); - - if (principal) - free (principal); - - /* Remove the policies */ - for (i = 0; i < keynote_policy_asserts_num; i++) - { - if (keynote_ids[i] != -1) - kn_remove_assertion (isakmp_sa->policy_id, keynote_ids[i]); - } - - if (keynote_ids) - free (keynote_ids); - - if (x509_ids) - free (x509_ids); - - /* - * XXX Currently, check_policy() is only called from message_negotiate_sa(), - * and so this log message reflects this. Change to something better? - */ - if (result == 0) - log_print ("check_policy: negotiated SA failed policy check"); - - /* - * Given that we have only 2 return values from policy (true/false) - * we can just return the query result directly (no pre-processing needed). - */ - return result; + /* + * XXX Currently, check_policy() is only called from message_negotiate_sa(), + * and so this log message reflects this. Change to something better? + */ + if (result == 0) + log_print("check_policy: negotiated SA failed policy check"); + + /* + * Given that we have only 2 return values from policy (true/false) + * we can just return the query result directly (no pre-processing needed). + */ + return result; } -#endif /* USE_POLICY */ +#endif /* USE_POLICY */ /* * Offer several sets of transforms to the responder. * XXX Split this huge function up and look for common code with main mode. */ static int -initiator_send_HASH_SA_NONCE (struct message *msg) +initiator_send_HASH_SA_NONCE(struct message * msg) { - struct exchange *exchange = msg->exchange; - struct doi *doi = exchange->doi; - struct ipsec_exch *ie = exchange->data; - u_int8_t ***transform = 0, ***new_transform; - u_int8_t **proposal = 0, **new_proposal; - u_int8_t *sa_buf = 0, *attr, *saved_nextp_sa, *saved_nextp_prop, *id, *spi; - size_t spi_sz, sz; - size_t proposal_len = 0, proposals_len = 0, sa_len; - size_t **transform_len = 0, **new_transform_len; - size_t *transforms_len = 0, *new_transforms_len; - u_int32_t *transform_cnt = 0, *new_transform_cnt; - u_int32_t suite_no, prop_no, prot_no, xf_no, prop_cnt = 0; - u_int32_t i; - int value, update_nextp, protocol_num, proto_id; - struct proto *proto; - struct conf_list *suite_conf, *prot_conf = 0, *xf_conf = 0, *life_conf; - struct conf_list_node *suite, *prot, *xf, *life; - struct constant_map *id_map; - char *protocol_id, *transform_id; - char *local_id, *remote_id; - int group_desc = -1, new_group_desc; - struct ipsec_sa *isa = msg->isakmp_sa->data; - struct hash *hash = hash_get (isa->hash); - struct sockaddr *src; - struct proto_attr *pa; - - if (!ipsec_add_hash_payload (msg, hash->hashsize)) - return -1; - - /* Get the list of protocol suites. */ - suite_conf = conf_get_list (exchange->policy, "Suites"); - if (!suite_conf) - return -1; - - for (suite = TAILQ_FIRST (&suite_conf->fields), suite_no = prop_no = 0; - suite_no < suite_conf->cnt; - suite_no++, suite = TAILQ_NEXT (suite, link)) - { - /* Now get each protocol in this specific protocol suite. */ - prot_conf = conf_get_list (suite->field, "Protocols"); - if (!prot_conf) - goto bail_out; - - for (prot = TAILQ_FIRST (&prot_conf->fields), prot_no = 0; - prot_no < prot_conf->cnt; - prot_no++, prot = TAILQ_NEXT (prot, link)) - { - /* Make sure we have a proposal/transform vectors. */ - if (prop_no >= prop_cnt) - { - /* This resize algorithm is completely arbitrary. */ - prop_cnt = 2 * prop_cnt + 10; - new_proposal = realloc (proposal, prop_cnt * sizeof *proposal); - if (!new_proposal) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "realloc (%p, %lu) failed", - proposal, - prop_cnt * (unsigned long)sizeof *proposal); - goto bail_out; - } - proposal = new_proposal; - - new_transforms_len = realloc (transforms_len, - prop_cnt * sizeof *transforms_len); - if (!new_transforms_len) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "realloc (%p, %lu) failed", - transforms_len, - prop_cnt * (unsigned long)sizeof *transforms_len); - goto bail_out; - } - transforms_len = new_transforms_len; - - new_transform = realloc (transform, - prop_cnt * sizeof *transform); - if (!new_transform) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "realloc (%p, %lu) failed", - transform, - prop_cnt * (unsigned long)sizeof *transform); - goto bail_out; - } - transform = new_transform; - - new_transform_cnt = realloc (transform_cnt, - prop_cnt * sizeof *transform_cnt); - if (!new_transform_cnt) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "realloc (%p, %lu) failed", - transform_cnt, - prop_cnt * (unsigned long)sizeof *transform_cnt); - goto bail_out; - } - transform_cnt = new_transform_cnt; - - new_transform_len = realloc (transform_len, - prop_cnt * sizeof *transform_len); - if (!new_transform_len) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "realloc (%p, %lu) failed", - transform_len, - prop_cnt * (unsigned long)sizeof *transform_len); - goto bail_out; - } - transform_len = new_transform_len; - } - - protocol_id = conf_get_str (prot->field, "PROTOCOL_ID"); - if (!protocol_id) - goto bail_out; - - proto_id = constant_value (ipsec_proto_cst, protocol_id); - switch (proto_id) - { - case IPSEC_PROTO_IPSEC_AH: - id_map = ipsec_ah_cst; - break; - - case IPSEC_PROTO_IPSEC_ESP: - id_map = ipsec_esp_cst; - break; - - case IPSEC_PROTO_IPCOMP: - id_map = ipsec_ipcomp_cst; - break; - - default: - { - log_print ("initiator_send_HASH_SA_NONCE: invalid PROTCOL_ID: " - "%s", protocol_id); - goto bail_out; - } - } - - /* Now get each transform we offer for this protocol. */ - xf_conf = conf_get_list (prot->field, "Transforms"); - if (!xf_conf) - goto bail_out; - transform_cnt[prop_no] = xf_conf->cnt; - - transform[prop_no] = calloc (transform_cnt[prop_no], - sizeof **transform); - if (!transform[prop_no]) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "calloc (%d, %lu) failed", - transform_cnt[prop_no], - (unsigned long)sizeof **transform); - goto bail_out; - } - - transform_len[prop_no] - = calloc (transform_cnt[prop_no], sizeof **transform_len); - if (!transform_len[prop_no]) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "calloc (%d, %lu) failed", - transform_cnt[prop_no], - (unsigned long)sizeof **transform_len); - goto bail_out; - } - - transforms_len[prop_no] = 0; - for (xf = TAILQ_FIRST (&xf_conf->fields), xf_no = 0; - xf_no < transform_cnt[prop_no]; - xf_no++, xf = TAILQ_NEXT (xf, link)) - { - - /* XXX The sizing needs to be dynamic. */ - transform[prop_no][xf_no] = calloc (ISAKMP_TRANSFORM_SA_ATTRS_OFF - + 9 * ISAKMP_ATTR_VALUE_OFF, - 1); - if (!transform[prop_no][xf_no]) - { - log_error ("initiator_send_HASH_SA_NONCE: " - "calloc (%d, 1) failed", - ISAKMP_TRANSFORM_SA_ATTRS_OFF - + 9 * ISAKMP_ATTR_VALUE_OFF); - goto bail_out; - } + struct exchange *exchange = msg->exchange; + struct doi *doi = exchange->doi; + struct ipsec_exch *ie = exchange->data; + u_int8_t ***transform = 0, ***new_transform; + u_int8_t **proposal = 0, **new_proposal; + u_int8_t *sa_buf = 0, *attr, *saved_nextp_sa, *saved_nextp_prop, + *id, *spi; + size_t spi_sz, sz; + size_t proposal_len = 0, proposals_len = 0, sa_len; + size_t **transform_len = 0, **new_transform_len; + size_t *transforms_len = 0, *new_transforms_len; + u_int32_t *transform_cnt = 0, *new_transform_cnt; + u_int32_t suite_no, prop_no, prot_no, xf_no, prop_cnt = 0; + u_int32_t i; + int value, update_nextp, protocol_num, proto_id; + struct proto *proto; + struct conf_list *suite_conf, *prot_conf = 0, *xf_conf = 0, *life_conf; + struct conf_list_node *suite, *prot, *xf, *life; + struct constant_map *id_map; + char *protocol_id, *transform_id; + char *local_id, *remote_id; + int group_desc = -1, new_group_desc; + struct ipsec_sa *isa = msg->isakmp_sa->data; + struct hash *hash = hash_get(isa->hash); + struct sockaddr *src; + struct proto_attr *pa; + + if (!ipsec_add_hash_payload(msg, hash->hashsize)) + return -1; + + /* Get the list of protocol suites. */ + suite_conf = conf_get_list(exchange->policy, "Suites"); + if (!suite_conf) + return -1; + + for (suite = TAILQ_FIRST(&suite_conf->fields), suite_no = prop_no = 0; + suite_no < suite_conf->cnt; + suite_no++, suite = TAILQ_NEXT(suite, link)) { + /* Now get each protocol in this specific protocol suite. */ + prot_conf = conf_get_list(suite->field, "Protocols"); + if (!prot_conf) + goto bail_out; + + for (prot = TAILQ_FIRST(&prot_conf->fields), prot_no = 0; + prot_no < prot_conf->cnt; + prot_no++, prot = TAILQ_NEXT(prot, link)) { + /* Make sure we have a proposal/transform vectors. */ + if (prop_no >= prop_cnt) { + /* + * This resize algorithm is completely + * arbitrary. + */ + prop_cnt = 2 * prop_cnt + 10; + new_proposal = realloc(proposal, + prop_cnt * sizeof *proposal); + if (!new_proposal) { + log_error("initiator_send_HASH_SA_NONCE: " + "realloc (%p, %lu) failed", + proposal, + prop_cnt * (unsigned long) sizeof *proposal); + goto bail_out; + } + proposal = new_proposal; + + new_transforms_len = realloc(transforms_len, + prop_cnt * sizeof *transforms_len); + if (!new_transforms_len) { + log_error("initiator_send_HASH_SA_NONCE: " + "realloc (%p, %lu) failed", + transforms_len, + prop_cnt * (unsigned long) sizeof *transforms_len); + goto bail_out; + } + transforms_len = new_transforms_len; + + new_transform = realloc(transform, + prop_cnt * sizeof *transform); + if (!new_transform) { + log_error("initiator_send_HASH_SA_NONCE: " + "realloc (%p, %lu) failed", + transform, + prop_cnt * (unsigned long) sizeof *transform); + goto bail_out; + } + transform = new_transform; + + new_transform_cnt = realloc(transform_cnt, + prop_cnt * sizeof *transform_cnt); + if (!new_transform_cnt) { + log_error("initiator_send_HASH_SA_NONCE: " + "realloc (%p, %lu) failed", + transform_cnt, + prop_cnt * (unsigned long) sizeof *transform_cnt); + goto bail_out; + } + transform_cnt = new_transform_cnt; + + new_transform_len = realloc(transform_len, + prop_cnt * sizeof *transform_len); + if (!new_transform_len) { + log_error("initiator_send_HASH_SA_NONCE: " + "realloc (%p, %lu) failed", + transform_len, + prop_cnt * (unsigned long) sizeof *transform_len); + goto bail_out; + } + transform_len = new_transform_len; + } + protocol_id = conf_get_str(prot->field, "PROTOCOL_ID"); + if (!protocol_id) + goto bail_out; + + proto_id = constant_value(ipsec_proto_cst, protocol_id); + switch (proto_id) { + case IPSEC_PROTO_IPSEC_AH: + id_map = ipsec_ah_cst; + break; + + case IPSEC_PROTO_IPSEC_ESP: + id_map = ipsec_esp_cst; + break; + + case IPSEC_PROTO_IPCOMP: + id_map = ipsec_ipcomp_cst; + break; + + default: + { + log_print("initiator_send_HASH_SA_NONCE: invalid PROTCOL_ID: " + "%s", protocol_id); + goto bail_out; + } + } + + /* Now get each transform we offer for this protocol. */ + xf_conf = conf_get_list(prot->field, "Transforms"); + if (!xf_conf) + goto bail_out; + transform_cnt[prop_no] = xf_conf->cnt; + + transform[prop_no] = calloc(transform_cnt[prop_no], + sizeof **transform); + if (!transform[prop_no]) { + log_error("initiator_send_HASH_SA_NONCE: " + "calloc (%d, %lu) failed", + transform_cnt[prop_no], + (unsigned long) sizeof **transform); + goto bail_out; + } + transform_len[prop_no] = calloc(transform_cnt[prop_no], + sizeof **transform_len); + if (!transform_len[prop_no]) { + log_error("initiator_send_HASH_SA_NONCE: " + "calloc (%d, %lu) failed", + transform_cnt[prop_no], + (unsigned long) sizeof **transform_len); + goto bail_out; + } + transforms_len[prop_no] = 0; + for (xf = TAILQ_FIRST(&xf_conf->fields), xf_no = 0; + xf_no < transform_cnt[prop_no]; + xf_no++, xf = TAILQ_NEXT(xf, link)) { + + /* XXX The sizing needs to be dynamic. */ + transform[prop_no][xf_no] = + calloc(ISAKMP_TRANSFORM_SA_ATTRS_OFF + + 9 * ISAKMP_ATTR_VALUE_OFF, 1); + if (!transform[prop_no][xf_no]) { + log_error("initiator_send_HASH_SA_NONCE: " + "calloc (%d, 1) failed", + ISAKMP_TRANSFORM_SA_ATTRS_OFF + + 9 * ISAKMP_ATTR_VALUE_OFF); + goto bail_out; + } + SET_ISAKMP_TRANSFORM_NO(transform[prop_no][xf_no], + xf_no + 1); + + transform_id = conf_get_str(xf->field, "TRANSFORM_ID"); + if (!transform_id) + goto bail_out; + SET_ISAKMP_TRANSFORM_ID(transform[prop_no][xf_no], + constant_value(id_map, transform_id)); + SET_ISAKMP_TRANSFORM_RESERVED(transform[prop_no][xf_no], 0); + + attr = transform[prop_no][xf_no] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; + + /* + * Life durations are special, we should be able to specify + * several, one per type. + */ + life_conf = conf_get_list(xf->field, "Life"); + if (life_conf) { + for (life = TAILQ_FIRST(&life_conf->fields); life; + life = TAILQ_NEXT(life, link)) { + attribute_set_constant(life->field, "LIFE_TYPE", + ipsec_duration_cst, + IPSEC_ATTR_SA_LIFE_TYPE, &attr); + + /* + * XXX Deals with 16 and 32 + * bit lifetimes only + */ + value = conf_get_num(life->field, "LIFE_DURATION", 0); + if (value) { + if (value <= 0xffff) + attr = + attribute_set_basic(attr, + IPSEC_ATTR_SA_LIFE_DURATION, + value); + else { + value = htonl(value); + attr = + attribute_set_var(attr, + IPSEC_ATTR_SA_LIFE_DURATION, + (u_int8_t *) & value, + sizeof value); + } + } + } + conf_free_list(life_conf); + } + attribute_set_constant(xf->field, "ENCAPSULATION_MODE", + ipsec_encap_cst, + IPSEC_ATTR_ENCAPSULATION_MODE, &attr); - SET_ISAKMP_TRANSFORM_NO (transform[prop_no][xf_no], xf_no + 1); + if (proto_id != IPSEC_PROTO_IPCOMP) { + attribute_set_constant(xf->field, + "AUTHENTICATION_ALGORITHM", + ipsec_auth_cst, + IPSEC_ATTR_AUTHENTICATION_ALGORITHM, + &attr); + + attribute_set_constant(xf->field, "GROUP_DESCRIPTION", + ike_group_desc_cst, + IPSEC_ATTR_GROUP_DESCRIPTION, &attr); + + value = conf_get_num(xf->field, "KEY_LENGTH", 0); + if (value) + attr = attribute_set_basic(attr, IPSEC_ATTR_KEY_LENGTH, + value); + + value = conf_get_num(xf->field, "KEY_ROUNDS", 0); + if (value) + attr = attribute_set_basic(attr, IPSEC_ATTR_KEY_ROUNDS, + value); + } else { + value = conf_get_num(xf->field, "COMPRESS_DICTIONARY_SIZE", + 0); + if (value) + attr = attribute_set_basic(attr, + IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE, + value); + + value = conf_get_num(xf->field, + "COMPRESS_PRIVATE_ALGORITHM", 0); + if (value) + attr = attribute_set_basic(attr, + IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM, + value); + } + + value = conf_get_num(xf->field, "ECN_TUNNEL", 0); + if (value) + attr = attribute_set_basic(attr, IPSEC_ATTR_ECN_TUNNEL, + value); + + /* Record the real transform size. */ + transforms_len[prop_no] += (transform_len[prop_no][xf_no] + = attr - transform[prop_no][xf_no]); + + if (proto_id != IPSEC_PROTO_IPCOMP) { + /* + * Make sure that if a group description is specified, it is + * specified for all transforms equally. + */ + attr = (u_int8_t *) conf_get_str(xf->field, + "GROUP_DESCRIPTION"); + new_group_desc + = attr ? constant_value(ike_group_desc_cst, + (char *) attr) : 0; + if (group_desc == -1) + group_desc = new_group_desc; + else if (group_desc != new_group_desc) { + log_print("initiator_send_HASH_SA_NONCE: " + "differing group descriptions in a proposal"); + goto bail_out; + } + } + } + conf_free_list(xf_conf); + xf_conf = 0; + + /* + * Get SPI from application. + * XXX Should we care about unknown constants? + */ + protocol_num = constant_value(ipsec_proto_cst, protocol_id); + spi = doi->get_spi(&spi_sz, protocol_num, msg); + if (spi_sz && !spi) { + log_print("initiator_send_HASH_SA_NONCE: doi->get_spi failed"); + goto bail_out; + } + proposal_len = ISAKMP_PROP_SPI_OFF + spi_sz; + proposals_len += proposal_len + transforms_len[prop_no]; + proposal[prop_no] = malloc(proposal_len); + if (!proposal[prop_no]) { + log_error("initiator_send_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) proposal_len); + goto bail_out; + } + SET_ISAKMP_PROP_NO(proposal[prop_no], suite_no + 1); + SET_ISAKMP_PROP_PROTO(proposal[prop_no], protocol_num); + + /* XXX I would like to see this factored out. */ + proto = calloc(1, sizeof *proto); + if (!proto) { + log_error("initiator_send_HASH_SA_NONCE: calloc (1, %lu) " + "failed", (unsigned long) sizeof *proto); + goto bail_out; + } + if (doi->proto_size) { + proto->data = calloc(1, doi->proto_size); + if (!proto->data) { + log_error("initiator_send_HASH_SA_NONCE: calloc (1, %lu) " + "failed", (unsigned long) doi->proto_size); + goto bail_out; + } + } + proto->no = suite_no + 1; + proto->proto = protocol_num; + proto->sa = TAILQ_FIRST(&exchange->sa_list); + proto->xf_cnt = transform_cnt[prop_no]; + TAILQ_INIT(&proto->xfs); + for (xf_no = 0; xf_no < proto->xf_cnt; xf_no++) { + pa = (struct proto_attr *) calloc(1, sizeof *pa); + if (!pa) + goto bail_out; + pa->len = transform_len[prop_no][xf_no]; + pa->attrs = (u_int8_t *) malloc(pa->len); + if (!pa->attrs) { + free(pa); + goto bail_out; + } + memcpy(pa->attrs, transform[prop_no][xf_no], pa->len); + TAILQ_INSERT_TAIL(&proto->xfs, pa, next); + } + TAILQ_INSERT_TAIL(&TAILQ_FIRST(&exchange->sa_list)->protos, proto, + link); + + /* Setup the incoming SPI. */ + SET_ISAKMP_PROP_SPI_SZ(proposal[prop_no], spi_sz); + memcpy(proposal[prop_no] + ISAKMP_PROP_SPI_OFF, spi, spi_sz); + proto->spi_sz[1] = spi_sz; + proto->spi[1] = spi; + + /* + * Let the DOI get at proto for initializing its own + * data. + */ + if (doi->proto_init) + doi->proto_init(proto, prot->field); + + SET_ISAKMP_PROP_NTRANSFORMS(proposal[prop_no], + transform_cnt[prop_no]); + prop_no++; + } + conf_free_list(prot_conf); + prot_conf = 0; + } - transform_id = conf_get_str (xf->field, "TRANSFORM_ID"); - if (!transform_id) + sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; + sa_buf = malloc(sa_len); + if (!sa_buf) { + log_error("initiator_send_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) sa_len); + goto bail_out; + } + SET_ISAKMP_SA_DOI(sa_buf, IPSEC_DOI_IPSEC); + SET_IPSEC_SIT_SIT(sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); + + /* + * Add the payloads. As this is a SA, we need to recompute the + * lengths of the payloads containing others. We also need to + * reset these payload's "next payload type" field. + */ + if (message_add_payload(msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) goto bail_out; - SET_ISAKMP_TRANSFORM_ID (transform[prop_no][xf_no], - constant_value (id_map, transform_id)); - SET_ISAKMP_TRANSFORM_RESERVED (transform[prop_no][xf_no], 0); - - attr = transform[prop_no][xf_no] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; - - /* - * Life durations are special, we should be able to specify - * several, one per type. - */ - life_conf = conf_get_list (xf->field, "Life"); - if (life_conf) - { - for (life = TAILQ_FIRST (&life_conf->fields); life; - life = TAILQ_NEXT (life, link)) - { - attribute_set_constant (life->field, "LIFE_TYPE", - ipsec_duration_cst, - IPSEC_ATTR_SA_LIFE_TYPE, &attr); - - /* XXX Deals with 16 and 32 bit lifetimes only */ - value = conf_get_num (life->field, "LIFE_DURATION", 0); - if (value) - { - if (value <= 0xffff) - attr = - attribute_set_basic (attr, - IPSEC_ATTR_SA_LIFE_DURATION, - value); - else - { - value = htonl (value); - attr = - attribute_set_var (attr, - IPSEC_ATTR_SA_LIFE_DURATION, - (u_int8_t *)&value, - sizeof value); - } - } - } - conf_free_list (life_conf); + SET_ISAKMP_GEN_LENGTH(sa_buf, sa_len + proposals_len); + sa_buf = 0; + + update_nextp = 0; + saved_nextp_sa = msg->nextp; + for (i = 0; i < prop_no; i++) { + if (message_add_payload(msg, ISAKMP_PAYLOAD_PROPOSAL, proposal[i], + proposal_len, update_nextp)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH(proposal[i], proposal_len + transforms_len[i]); + proposal[i] = 0; + + update_nextp = 0; + saved_nextp_prop = msg->nextp; + for (xf_no = 0; xf_no < transform_cnt[i]; xf_no++) { + if (message_add_payload(msg, ISAKMP_PAYLOAD_TRANSFORM, + transform[i][xf_no], + transform_len[i][xf_no], update_nextp)) + goto bail_out; + update_nextp = 1; + transform[i][xf_no] = 0; } + msg->nextp = saved_nextp_prop; + update_nextp = 1; + } + msg->nextp = saved_nextp_sa; - attribute_set_constant (xf->field, "ENCAPSULATION_MODE", - ipsec_encap_cst, - IPSEC_ATTR_ENCAPSULATION_MODE, &attr); - - if (proto_id != IPSEC_PROTO_IPCOMP) - { - attribute_set_constant (xf->field, - "AUTHENTICATION_ALGORITHM", - ipsec_auth_cst, - IPSEC_ATTR_AUTHENTICATION_ALGORITHM, - &attr); - - attribute_set_constant (xf->field, "GROUP_DESCRIPTION", - ike_group_desc_cst, - IPSEC_ATTR_GROUP_DESCRIPTION, &attr); - - value = conf_get_num (xf->field, "KEY_LENGTH", 0); - if (value) - attr = attribute_set_basic (attr, IPSEC_ATTR_KEY_LENGTH, - value); - - value = conf_get_num (xf->field, "KEY_ROUNDS", 0); - if (value) - attr = attribute_set_basic (attr, IPSEC_ATTR_KEY_ROUNDS, - value); - } - else - { - value = conf_get_num (xf->field, "COMPRESS_DICTIONARY_SIZE", - 0); - if (value) - attr = attribute_set_basic (attr, - IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE, - value); - - value = conf_get_num (xf->field, - "COMPRESS_PRIVATE_ALGORITHM", 0); - if (value) - attr = attribute_set_basic (attr, - IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM, - value); - } + /* + * Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. + */ + ie->sa_i_b = message_copy(msg, ISAKMP_GEN_SZ, &ie->sa_i_b_len); + if (!ie->sa_i_b) + goto bail_out; - value = conf_get_num (xf->field, "ECN_TUNNEL", 0); - if (value) - attr = attribute_set_basic (attr, IPSEC_ATTR_ECN_TUNNEL, - value); - - /* Record the real transform size. */ - transforms_len[prop_no] += (transform_len[prop_no][xf_no] - = attr - transform[prop_no][xf_no]); - - if (proto_id != IPSEC_PROTO_IPCOMP) - { - /* - * Make sure that if a group description is specified, it is - * specified for all transforms equally. - */ - attr = (u_int8_t *)conf_get_str (xf->field, - "GROUP_DESCRIPTION"); - new_group_desc - = attr ? constant_value (ike_group_desc_cst, - (char *)attr) : 0; - if (group_desc == -1) - group_desc = new_group_desc; - else if (group_desc != new_group_desc) - { - log_print ("initiator_send_HASH_SA_NONCE: " - "differing group descriptions in a proposal"); - goto bail_out; - } + /* + * Generate a nonce, and add it to the message. + * XXX I want a better way to specify the nonce's size. + */ + if (exchange_gen_nonce(msg, 16)) + return -1; + + /* Generate optional KEY_EXCH payload. */ + if (group_desc > 0) { + ie->group = group_get(group_desc); + ie->g_x_len = dh_getlen(ie->group); + + if (ipsec_gen_g_x(msg)) { + group_free(ie->group); + ie->group = 0; + return -1; } - } - conf_free_list (xf_conf); - xf_conf = 0; - - /* - * Get SPI from application. - * XXX Should we care about unknown constants? - */ - protocol_num = constant_value (ipsec_proto_cst, protocol_id); - spi = doi->get_spi (&spi_sz, protocol_num, msg); - if (spi_sz && !spi) - { - log_print ("initiator_send_HASH_SA_NONCE: doi->get_spi failed"); - goto bail_out; - } - - proposal_len = ISAKMP_PROP_SPI_OFF + spi_sz; - proposals_len += proposal_len + transforms_len[prop_no]; - proposal[prop_no] = malloc (proposal_len); - if (!proposal[prop_no]) - { - log_error ("initiator_send_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)proposal_len); - goto bail_out; - } - - SET_ISAKMP_PROP_NO (proposal[prop_no], suite_no + 1); - SET_ISAKMP_PROP_PROTO (proposal[prop_no], protocol_num); - - /* XXX I would like to see this factored out. */ - proto = calloc (1, sizeof *proto); - if (!proto) - { - log_error ("initiator_send_HASH_SA_NONCE: calloc (1, %lu) " - "failed", (unsigned long)sizeof *proto); - goto bail_out; - } - - if (doi->proto_size) - { - proto->data = calloc (1, doi->proto_size); - if (!proto->data) - { - log_error ("initiator_send_HASH_SA_NONCE: calloc (1, %lu) " - "failed", (unsigned long)doi->proto_size); - goto bail_out; + } + /* Generate optional client ID payloads. XXX Share with responder. */ + local_id = conf_get_str(exchange->name, "Local-ID"); + remote_id = conf_get_str(exchange->name, "Remote-ID"); + if (local_id && remote_id) { + id = ipsec_build_id(local_id, &sz); + if (!id) + return -1; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDic", + id, sz)); + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) { + free(id); + return -1; } - } - - proto->no = suite_no + 1; - proto->proto = protocol_num; - proto->sa = TAILQ_FIRST (&exchange->sa_list); - proto->xf_cnt = transform_cnt[prop_no]; - TAILQ_INIT (&proto->xfs); - for (xf_no = 0; xf_no < proto->xf_cnt; xf_no++) - { - pa = (struct proto_attr *)calloc (1, sizeof *pa); - if (!pa) - goto bail_out; - pa->len = transform_len[prop_no][xf_no]; - pa->attrs = (u_int8_t *)malloc (pa->len); - if (!pa->attrs) - { - free (pa); - goto bail_out; + id = ipsec_build_id(remote_id, &sz); + if (!id) + return -1; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDrc", + id, sz)); + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) { + free(id); + return -1; } - memcpy (pa->attrs, transform[prop_no][xf_no], pa->len); - TAILQ_INSERT_TAIL (&proto->xfs, pa, next); - } - TAILQ_INSERT_TAIL (&TAILQ_FIRST (&exchange->sa_list)->protos, proto, - link); - - /* Setup the incoming SPI. */ - SET_ISAKMP_PROP_SPI_SZ (proposal[prop_no], spi_sz); - memcpy (proposal[prop_no] + ISAKMP_PROP_SPI_OFF, spi, spi_sz); - proto->spi_sz[1] = spi_sz; - proto->spi[1] = spi; - - /* Let the DOI get at proto for initializing its own data. */ - if (doi->proto_init) - doi->proto_init (proto, prot->field); - - SET_ISAKMP_PROP_NTRANSFORMS (proposal[prop_no], - transform_cnt[prop_no]); - prop_no++; - } - conf_free_list (prot_conf); - prot_conf = 0; - } - - sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; - sa_buf = malloc (sa_len); - if (!sa_buf) - { - log_error ("initiator_send_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)sa_len); - goto bail_out; - } - SET_ISAKMP_SA_DOI (sa_buf, IPSEC_DOI_IPSEC); - SET_IPSEC_SIT_SIT (sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); - - /* - * Add the payloads. As this is a SA, we need to recompute the - * lengths of the payloads containing others. We also need to - * reset these payload's "next payload type" field. - */ - if (message_add_payload (msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) - goto bail_out; - SET_ISAKMP_GEN_LENGTH (sa_buf, sa_len + proposals_len); - sa_buf = 0; - - update_nextp = 0; - saved_nextp_sa = msg->nextp; - for (i = 0; i < prop_no; i++) - { - if (message_add_payload (msg, ISAKMP_PAYLOAD_PROPOSAL, proposal[i], - proposal_len, update_nextp)) - goto bail_out; - SET_ISAKMP_GEN_LENGTH (proposal[i], proposal_len + transforms_len[i]); - proposal[i] = 0; - - update_nextp = 0; - saved_nextp_prop = msg->nextp; - for (xf_no = 0; xf_no < transform_cnt[i]; xf_no++) - { - if (message_add_payload (msg, ISAKMP_PAYLOAD_TRANSFORM, - transform[i][xf_no], - transform_len[i][xf_no], update_nextp)) - goto bail_out; - update_nextp = 1; - transform[i][xf_no] = 0; - } - msg->nextp = saved_nextp_prop; - update_nextp = 1; - } - msg->nextp = saved_nextp_sa; - - /* - * Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. - */ - ie->sa_i_b = message_copy (msg, ISAKMP_GEN_SZ, &ie->sa_i_b_len); - if (!ie->sa_i_b) - goto bail_out; - - /* - * Generate a nonce, and add it to the message. - * XXX I want a better way to specify the nonce's size. - */ - if (exchange_gen_nonce (msg, 16)) - return -1; - - /* Generate optional KEY_EXCH payload. */ - if (group_desc > 0) - { - ie->group = group_get (group_desc); - ie->g_x_len = dh_getlen (ie->group); - - if (ipsec_gen_g_x (msg)) - { - group_free (ie->group); - ie->group = 0; - return -1; - } - } - - /* Generate optional client ID payloads. XXX Share with responder. */ - local_id = conf_get_str (exchange->name, "Local-ID"); - remote_id = conf_get_str (exchange->name, "Remote-ID"); - if (local_id && remote_id) - { - id = ipsec_build_id (local_id, &sz); - if (!id) - return -1; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDic", - id, sz)); - if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) - { - free (id); - return -1; - } - - id = ipsec_build_id (remote_id, &sz); - if (!id) - return -1; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDrc", - id, sz)); - if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) - { - free (id); - return -1; } - } - /* XXX I do not judge these as errors, are they? */ - else if (local_id) - log_print ("initiator_send_HASH_SA_NONCE: " - "Local-ID given without Remote-ID for \"%s\"", - exchange->name); - else if (remote_id) - /* This code supports the "road warrior" case, where the initiator doesn't - * have a fixed IP address, but wants to specify a particular remote - * network to talk to. - * -- Adrian Close <adrian@esec.com.au> - */ - { - log_print ("initiator_send_HASH_SA_NONCE: " - "Remote-ID given without Local-ID for \"%s\"", - exchange->name); - - /* If we're here, then we are the initiator, so use initiator - address for local ID */ - msg->transport->vtbl->get_src (msg->transport, &src); - sz = ISAKMP_ID_SZ + sockaddr_addrlen (src); - - id = calloc (sz, sizeof (char)); - if (!id) + /* XXX I do not judge these as errors, are they? */ + else if (local_id) + log_print("initiator_send_HASH_SA_NONCE: " + "Local-ID given without Remote-ID for \"%s\"", + exchange->name); + else if (remote_id) + /* + * This code supports the "road warrior" case, where the + * initiator doesn't have a fixed IP address, but wants to + * specify a particular remote network to talk to. -- Adrian + * Close <adrian@esec.com.au> + */ { - log_error ("initiator_send_HASH_SA_NONCE: calloc (%lu, %lu) failed", - (unsigned long)sz, (unsigned long)sizeof (char)); - return -1; + log_print("initiator_send_HASH_SA_NONCE: " + "Remote-ID given without Local-ID for \"%s\"", + exchange->name); + + /* + * If we're here, then we are the initiator, so use initiator + * address for local ID + */ + msg->transport->vtbl->get_src(msg->transport, &src); + sz = ISAKMP_ID_SZ + sockaddr_addrlen(src); + + id = calloc(sz, sizeof(char)); + if (!id) { + log_error("initiator_send_HASH_SA_NONCE: calloc (%lu, %lu) failed", + (unsigned long) sz, (unsigned long) sizeof(char)); + return -1; + } + switch (src->sa_family) { + case AF_INET6: + SET_ISAKMP_ID_TYPE(id, IPSEC_ID_IPV6_ADDR); + break; + case AF_INET: + SET_ISAKMP_ID_TYPE(id, IPSEC_ID_IPV4_ADDR); + break; + default: + log_error("initiator_send_HASH_SA_NONCE: unknown sa_family %d", + src->sa_family); + free(id); + return -1; + } + memcpy(id + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(src), + sockaddr_addrlen(src)); + + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDic", + id, sz)); + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) { + free(id); + return -1; + } + /* Send supplied remote_id */ + id = ipsec_build_id(remote_id, &sz); + if (!id) + return -1; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDrc", + id, sz)); + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) { + free(id); + return -1; + } } + if (ipsec_fill_in_hash(msg)) + goto bail_out; - switch (src->sa_family) - { - case AF_INET6: - SET_ISAKMP_ID_TYPE (id, IPSEC_ID_IPV6_ADDR); - break; - case AF_INET: - SET_ISAKMP_ID_TYPE (id, IPSEC_ID_IPV4_ADDR); - break; - default: - log_error ("initiator_send_HASH_SA_NONCE: unknown sa_family %d", - src->sa_family); - free (id); - return -1; + conf_free_list(suite_conf); + for (i = 0; i < prop_no; i++) { + free(transform[i]); + free(transform_len[i]); } - memcpy (id + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src), - sockaddr_addrlen (src)); - - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDic", - id, sz)); - if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) - { - free (id); - return -1; + free(proposal); + free(transform); + free(transforms_len); + free(transform_len); + free(transform_cnt); + return 0; + +bail_out: + if (sa_buf) + free(sa_buf); + if (proposal) { + for (i = 0; i < prop_no; i++) { + if (proposal[i]) + free(proposal[i]); + if (transform[i]) { + for (xf_no = 0; xf_no < transform_cnt[i]; xf_no++) + if (transform[i][xf_no]) + free(transform[i][xf_no]); + free(transform[i]); + } + if (transform_len[i]) + free(transform_len[i]); + } + free(proposal); + free(transforms_len); + free(transform); + free(transform_len); + free(transform_cnt); } - - /* Send supplied remote_id */ - id = ipsec_build_id (remote_id, &sz); - if (!id) + if (xf_conf) + conf_free_list(xf_conf); + if (prot_conf) + conf_free_list(prot_conf); + conf_free_list(suite_conf); return -1; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH_SA_NONCE: IDrc", - id, sz)); - if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) - { - free (id); - return -1; - } - } - - if (ipsec_fill_in_hash (msg)) - goto bail_out; - - conf_free_list (suite_conf); - for (i = 0; i < prop_no; i++) - { - free (transform[i]); - free (transform_len[i]); - } - free (proposal); - free (transform); - free (transforms_len); - free (transform_len); - free (transform_cnt); - return 0; - - bail_out: - if (sa_buf) - free (sa_buf); - if (proposal) - { - for (i = 0; i < prop_no; i++) - { - if (proposal[i]) - free (proposal[i]); - if (transform[i]) - { - for (xf_no = 0; xf_no < transform_cnt[i]; xf_no++) - if (transform[i][xf_no]) - free (transform[i][xf_no]); - free (transform[i]); - } - if (transform_len[i]) - free (transform_len[i]); - } - free (proposal); - free (transforms_len); - free (transform); - free (transform_len); - free (transform_cnt); - } - if (xf_conf) - conf_free_list (xf_conf); - if (prot_conf) - conf_free_list (prot_conf); - conf_free_list (suite_conf); - return -1; } /* Figure out what transform the responder chose. */ static int -initiator_recv_HASH_SA_NONCE (struct message *msg) +initiator_recv_HASH_SA_NONCE(struct message * msg) { - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct sa *sa; - struct proto *proto, *next_proto; - struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); - struct payload *xf, *idp; - struct payload *hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); - struct payload *kep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); - struct prf *prf; - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct hash *hash = hash_get (isa->hash); - size_t hashsize = hash->hashsize; - u_int8_t *rest; - size_t rest_len; - struct sockaddr *src, *dst; - - /* Allocate the prf and start calculating our HASH(1). XXX Share? */ - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_recv_HASH_SA_NONCE: SKEYID_a", - (u_int8_t *)isa->skeyid_a, isa->skeyid_len)); - prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); - if (!prf) - return -1; - - prf->Init (prf->prfctx); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "initiator_recv_HASH_SA_NONCE: message_id", - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); - prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_recv_HASH_SA_NONCE: NONCE_I_b", - exchange->nonce_i, exchange->nonce_i_len)); - prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); - rest = hashp->p + GET_ISAKMP_GEN_LENGTH (hashp->p); - rest_len = (GET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base) - - (rest - (u_int8_t*)msg->iov[0].iov_base)); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "initiator_recv_HASH_SA_NONCE: payloads after HASH(2)", rest, - rest_len)); - prf->Update (prf->prfctx, rest, rest_len); - prf->Final (hash->digest, prf->prfctx); - prf_free (prf); - LOG_DBG_BUF ((LOG_NEGOTIATION, 80, - "initiator_recv_HASH_SA_NONCE: computed HASH(2)", - hash->digest, hashsize)); - if (memcmp (hashp->p + ISAKMP_HASH_DATA_OFF, hash->digest, hashsize) != 0) - { - message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); - return -1; - } - /* Mark the HASH as handled. */ - hashp->flags |= PL_MARK; - - /* - * As we are getting an answer on our transform offer, only one transform - * should be given. - * - * XXX Currently we only support negotiating one SA per quick mode run. - */ - if (TAILQ_NEXT (sa_p, link)) - { - log_print ("initiator_recv_HASH_SA_NONCE: " - "multiple SA payloads in quick mode not supported yet"); - return -1; - } - - sa = TAILQ_FIRST (&exchange->sa_list); - - /* This is here for the policy check */ - if (kep) - ie->pfs = 1; - - /* Handle optional client ID payloads. */ - idp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_ID]); - if (idp) - { - /* If IDci is there, IDcr must be too. */ - if (!TAILQ_NEXT (idp, link)) - { - /* XXX Is this a good notify type? */ - message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); - return -1; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *sa; + struct proto *proto, *next_proto; + struct payload *sa_p = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_SA]); + struct payload *xf, *idp; + struct payload *hashp = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_HASH]); + struct payload *kep = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); + struct prf *prf; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct hash *hash = hash_get(isa->hash); + size_t hashsize = hash->hashsize; + u_int8_t *rest; + size_t rest_len; + struct sockaddr *src, *dst; + + /* Allocate the prf and start calculating our HASH(1). XXX Share? */ + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_recv_HASH_SA_NONCE: SKEYID_a", + (u_int8_t *) isa->skeyid_a, isa->skeyid_len)); + prf = prf_alloc(isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + return -1; + + prf->Init(prf->prfctx); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "initiator_recv_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update(prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_recv_HASH_SA_NONCE: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len)); + prf->Update(prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + rest = hashp->p + GET_ISAKMP_GEN_LENGTH(hashp->p); + rest_len = (GET_ISAKMP_HDR_LENGTH(msg->iov[0].iov_base) + - (rest - (u_int8_t *) msg->iov[0].iov_base)); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "initiator_recv_HASH_SA_NONCE: payloads after HASH(2)", rest, + rest_len)); + prf->Update(prf->prfctx, rest, rest_len); + prf->Final(hash->digest, prf->prfctx); + prf_free(prf); + LOG_DBG_BUF((LOG_NEGOTIATION, 80, + "initiator_recv_HASH_SA_NONCE: computed HASH(2)", + hash->digest, hashsize)); + if (memcmp(hashp->p + ISAKMP_HASH_DATA_OFF, hash->digest, hashsize) != 0) { + message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + return -1; } - - /* XXX We should really compare, not override. */ - ie->id_ci_sz = GET_ISAKMP_GEN_LENGTH (idp->p); - ie->id_ci = malloc (ie->id_ci_sz); - if (!ie->id_ci) - { - log_error ("initiator_recv_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)ie->id_ci_sz); - return -1; - } - memcpy (ie->id_ci, idp->p, ie->id_ci_sz); - idp->flags |= PL_MARK; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "initiator_recv_HASH_SA_NONCE: IDci", - ie->id_ci + ISAKMP_GEN_SZ, ie->id_ci_sz - - ISAKMP_GEN_SZ)); - - idp = TAILQ_NEXT (idp, link); - ie->id_cr_sz = GET_ISAKMP_GEN_LENGTH (idp->p); - ie->id_cr = malloc (ie->id_cr_sz); - if (!ie->id_cr) - { - log_error ("initiator_recv_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)ie->id_cr_sz); - return -1; - } - memcpy (ie->id_cr, idp->p, ie->id_cr_sz); - idp->flags |= PL_MARK; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "initiator_recv_HASH_SA_NONCE: IDcr", - ie->id_cr + ISAKMP_GEN_SZ, ie->id_cr_sz - - ISAKMP_GEN_SZ)); - } - else - { - /* - * If client identifiers are not present in the exchange, - * we fake them. RFC 2409 states: - * The identities of the SAs negotiated in Quick Mode are - * implicitly assumed to be the IP addresses of the ISAKMP - * peers, without any constraints on the protocol or port - * numbers allowed, unless client identifiers are specified - * in Quick Mode. - * - * -- Michael Paddon (mwp@aba.net.au) - */ - - ie->flags = IPSEC_EXCH_FLAG_NO_ID; - - /* Get initiator and responder addresses. */ - msg->transport->vtbl->get_src (msg->transport, &src); - msg->transport->vtbl->get_dst (msg->transport, &dst); - ie->id_ci_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen (src); - ie->id_cr_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen (dst); - ie->id_ci = calloc (ie->id_ci_sz, sizeof (char)); - ie->id_cr = calloc (ie->id_cr_sz, sizeof (char)); - - if (!ie->id_ci || !ie->id_cr) - { - log_error ("initiator_recv_HASH_SA_NONCE: calloc (%lu, %lu) failed", - (unsigned long)ie->id_cr_sz, (unsigned long)sizeof (char)); - if (ie->id_ci) - { - free (ie->id_ci); - ie->id_ci = 0; - } - if (ie->id_cr) - { - free (ie->id_cr); - ie->id_cr = 0; - } - return -1; + /* Mark the HASH as handled. */ + hashp->flags |= PL_MARK; + + /* + * As we are getting an answer on our transform offer, only one transform + * should be given. + * + * XXX Currently we only support negotiating one SA per quick mode run. + */ + if (TAILQ_NEXT(sa_p, link)) { + log_print("initiator_recv_HASH_SA_NONCE: " + "multiple SA payloads in quick mode not supported yet"); + return -1; } - - if (src->sa_family != dst->sa_family) - { - log_error ("initiator_recv_HASH_SA_NONCE: sa_family mismatch"); - free (ie->id_ci); - ie->id_ci = 0; - free (ie->id_cr); - ie->id_cr = 0; - return -1; + sa = TAILQ_FIRST(&exchange->sa_list); + + /* This is here for the policy check */ + if (kep) + ie->pfs = 1; + + /* Handle optional client ID payloads. */ + idp = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_ID]); + if (idp) { + /* If IDci is there, IDcr must be too. */ + if (!TAILQ_NEXT(idp, link)) { + /* XXX Is this a good notify type? */ + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + return -1; + } + /* XXX We should really compare, not override. */ + ie->id_ci_sz = GET_ISAKMP_GEN_LENGTH(idp->p); + ie->id_ci = malloc(ie->id_ci_sz); + if (!ie->id_ci) { + log_error("initiator_recv_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) ie->id_ci_sz); + return -1; + } + memcpy(ie->id_ci, idp->p, ie->id_ci_sz); + idp->flags |= PL_MARK; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "initiator_recv_HASH_SA_NONCE: IDci", + ie->id_ci + ISAKMP_GEN_SZ, ie->id_ci_sz + - ISAKMP_GEN_SZ)); + + idp = TAILQ_NEXT(idp, link); + ie->id_cr_sz = GET_ISAKMP_GEN_LENGTH(idp->p); + ie->id_cr = malloc(ie->id_cr_sz); + if (!ie->id_cr) { + log_error("initiator_recv_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) ie->id_cr_sz); + return -1; + } + memcpy(ie->id_cr, idp->p, ie->id_cr_sz); + idp->flags |= PL_MARK; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "initiator_recv_HASH_SA_NONCE: IDcr", + ie->id_cr + ISAKMP_GEN_SZ, ie->id_cr_sz + - ISAKMP_GEN_SZ)); + } else { + /* + * If client identifiers are not present in the exchange, + * we fake them. RFC 2409 states: + * The identities of the SAs negotiated in Quick Mode are + * implicitly assumed to be the IP addresses of the ISAKMP + * peers, without any constraints on the protocol or port + * numbers allowed, unless client identifiers are specified + * in Quick Mode. + * + * -- Michael Paddon (mwp@aba.net.au) + */ + + ie->flags = IPSEC_EXCH_FLAG_NO_ID; + + /* Get initiator and responder addresses. */ + msg->transport->vtbl->get_src(msg->transport, &src); + msg->transport->vtbl->get_dst(msg->transport, &dst); + ie->id_ci_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen(src); + ie->id_cr_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen(dst); + ie->id_ci = calloc(ie->id_ci_sz, sizeof(char)); + ie->id_cr = calloc(ie->id_cr_sz, sizeof(char)); + + if (!ie->id_ci || !ie->id_cr) { + log_error("initiator_recv_HASH_SA_NONCE: calloc (%lu, %lu) failed", + (unsigned long) ie->id_cr_sz, (unsigned long) sizeof(char)); + if (ie->id_ci) { + free(ie->id_ci); + ie->id_ci = 0; + } + if (ie->id_cr) { + free(ie->id_cr); + ie->id_cr = 0; + } + return -1; + } + if (src->sa_family != dst->sa_family) { + log_error("initiator_recv_HASH_SA_NONCE: sa_family mismatch"); + free(ie->id_ci); + ie->id_ci = 0; + free(ie->id_cr); + ie->id_cr = 0; + return -1; + } + switch (src->sa_family) { + case AF_INET: + SET_ISAKMP_ID_TYPE(ie->id_ci, IPSEC_ID_IPV4_ADDR); + SET_ISAKMP_ID_TYPE(ie->id_cr, IPSEC_ID_IPV4_ADDR); + break; + + case AF_INET6: + SET_ISAKMP_ID_TYPE(ie->id_ci, IPSEC_ID_IPV6_ADDR); + SET_ISAKMP_ID_TYPE(ie->id_cr, IPSEC_ID_IPV6_ADDR); + break; + + default: + log_error("initiator_recv_HASH_SA_NONCE: unknown sa_family %d", + src->sa_family); + free(ie->id_ci); + ie->id_ci = 0; + free(ie->id_cr); + ie->id_cr = 0; + return -1; + } + memcpy(ie->id_ci + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(src), + sockaddr_addrlen(src)); + memcpy(ie->id_cr + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(dst), + sockaddr_addrlen(dst)); } - switch (src->sa_family) - { - case AF_INET: - SET_ISAKMP_ID_TYPE (ie->id_ci, IPSEC_ID_IPV4_ADDR); - SET_ISAKMP_ID_TYPE (ie->id_cr, IPSEC_ID_IPV4_ADDR); - break; + /* Build the protection suite in our SA. */ + for (xf = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); xf; + xf = TAILQ_NEXT(xf, link)) { - case AF_INET6: - SET_ISAKMP_ID_TYPE (ie->id_ci, IPSEC_ID_IPV6_ADDR); - SET_ISAKMP_ID_TYPE (ie->id_cr, IPSEC_ID_IPV6_ADDR); - break; + /* + * XXX We could check that the proposal each transform belongs to + * is unique. + */ - default: - log_error ("initiator_recv_HASH_SA_NONCE: unknown sa_family %d", - src->sa_family); - free (ie->id_ci); - ie->id_ci = 0; - free (ie->id_cr); - ie->id_cr = 0; - return -1; - } - memcpy (ie->id_ci + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src), - sockaddr_addrlen (src)); - memcpy (ie->id_cr + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (dst), - sockaddr_addrlen (dst)); - } - - /* Build the protection suite in our SA. */ - for (xf = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); xf; - xf = TAILQ_NEXT (xf, link)) - { - - /* - * XXX We could check that the proposal each transform belongs to - * is unique. - */ - - if (sa_add_transform (sa, xf, exchange->initiator, &proto)) - return -1; + if (sa_add_transform(sa, xf, exchange->initiator, &proto)) + return -1; - /* XXX Check that the chosen transform matches an offer. */ + /* XXX Check that the chosen transform matches an offer. */ - ipsec_decode_transform (msg, sa, proto, xf->p); - } + ipsec_decode_transform(msg, sa, proto, xf->p); + } - /* Now remove offers that we don't need anymore. */ - for (proto = TAILQ_FIRST (&sa->protos); proto; proto = next_proto) - { - next_proto = TAILQ_NEXT (proto, link); - if (!proto->chosen) - proto_free (proto); - } + /* Now remove offers that we don't need anymore. */ + for (proto = TAILQ_FIRST(&sa->protos); proto; proto = next_proto) { + next_proto = TAILQ_NEXT(proto, link); + if (!proto->chosen) + proto_free(proto); + } #ifdef USE_POLICY - if (!check_policy (exchange, sa, msg->isakmp_sa)) - { - message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); - log_print ("initiator_recv_HASH_SA_NONCE: policy check failed"); - return -1; - } + if (!check_policy(exchange, sa, msg->isakmp_sa)) { + message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); + log_print("initiator_recv_HASH_SA_NONCE: policy check failed"); + return -1; + } #endif - /* Mark the SA as handled. */ - sa_p->flags |= PL_MARK; + /* Mark the SA as handled. */ + sa_p->flags |= PL_MARK; - isa = sa->data; - if ((isa->group_desc && (!ie->group || ie->group->id != isa->group_desc)) - || (!isa->group_desc && ie->group)) - { - log_print ("initiator_recv_HASH_SA_NONCE: disagreement on PFS"); - return -1; - } - - /* Copy out the initiator's nonce. */ - if (exchange_save_nonce (msg)) - return -1; + isa = sa->data; + if ((isa->group_desc && (!ie->group || ie->group->id != isa->group_desc)) + || (!isa->group_desc && ie->group)) { + log_print("initiator_recv_HASH_SA_NONCE: disagreement on PFS"); + return -1; + } + /* Copy out the initiator's nonce. */ + if (exchange_save_nonce(msg)) + return -1; - /* Handle the optional KEY_EXCH payload. */ - if (kep && ipsec_save_g_x (msg)) - return -1; + /* Handle the optional KEY_EXCH payload. */ + if (kep && ipsec_save_g_x(msg)) + return -1; - return 0; + return 0; } static int -initiator_send_HASH (struct message *msg) +initiator_send_HASH(struct message * msg) { - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct prf *prf; - u_int8_t *buf; - struct hash *hash = hash_get (isa->hash); - size_t hashsize = hash->hashsize; - - /* We want a HASH payload to start with. XXX Share with ike_main_mode.c? */ - buf = malloc (ISAKMP_HASH_SZ + hashsize); - if (!buf) - { - log_error ("initiator_send_HASH: malloc (%lu) failed", - ISAKMP_HASH_SZ + (unsigned long)hashsize); - return -1; - } - if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, buf, - ISAKMP_HASH_SZ + hashsize, 1)) - { - free (buf); - return -1; - } - - /* Allocate the prf and start calculating our HASH(3). XXX Share? */ - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH: SKEYID_a", - isa->skeyid_a, isa->skeyid_len)); - 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, (unsigned char *)"\0", 1); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH: message_id", - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); - prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH: NONCE_I_b", - exchange->nonce_i, exchange->nonce_i_len)); - prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH: NONCE_R_b", - exchange->nonce_r, exchange->nonce_r_len)); - prf->Update (prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); - prf->Final (buf + ISAKMP_GEN_SZ, prf->prfctx); - prf_free (prf); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "initiator_send_HASH: HASH(3)", - buf + ISAKMP_GEN_SZ, hashsize)); - - if (ie->group) - message_register_post_send (msg, gen_g_xy); - - message_register_post_send (msg, post_quick_mode); - - return 0; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + u_int8_t *buf; + struct hash *hash = hash_get(isa->hash); + size_t hashsize = hash->hashsize; + + /* + * We want a HASH payload to start with. XXX Share with + * ike_main_mode.c? + */ + buf = malloc(ISAKMP_HASH_SZ + hashsize); + if (!buf) { + log_error("initiator_send_HASH: malloc (%lu) failed", + ISAKMP_HASH_SZ + (unsigned long) hashsize); + return -1; + } + if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH, buf, + ISAKMP_HASH_SZ + hashsize, 1)) { + free(buf); + return -1; + } + /* Allocate the prf and start calculating our HASH(3). XXX Share? */ + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH: SKEYID_a", + isa->skeyid_a, isa->skeyid_len)); + 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, (unsigned char *) "\0", 1); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update(prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len)); + prf->Update(prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH: NONCE_R_b", + exchange->nonce_r, exchange->nonce_r_len)); + prf->Update(prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); + prf->Final(buf + ISAKMP_GEN_SZ, prf->prfctx); + prf_free(prf); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "initiator_send_HASH: HASH(3)", + buf + ISAKMP_GEN_SZ, hashsize)); + + if (ie->group) + message_register_post_send(msg, gen_g_xy); + + message_register_post_send(msg, post_quick_mode); + + return 0; } static void -post_quick_mode (struct message *msg) +post_quick_mode(struct message * msg) { - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct prf *prf; - struct sa *sa; - struct proto *proto; - struct ipsec_proto *iproto; - u_int8_t *keymat; - int i; - - /* - * Loop over all SA negotiations and do both an in- and an outgoing SA - * per protocol. - */ - for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = TAILQ_NEXT (sa, next)) - { - for (proto = TAILQ_FIRST (&sa->protos); proto; - proto = TAILQ_NEXT (proto, link)) - { - if (proto->proto == IPSEC_PROTO_IPCOMP) - continue; - - iproto = proto->data; - - /* - * There are two SAs for each SA negotiation, incoming and outcoing. - */ - for (i = 0; i < 2; i++) - { - prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_d, - isa->skeyid_len); - if (!prf) - { - /* XXX What to do? */ - continue; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + struct sa *sa; + struct proto *proto; + struct ipsec_proto *iproto; + u_int8_t *keymat; + int i; + + /* + * Loop over all SA negotiations and do both an in- and an outgoing SA + * per protocol. + */ + for (sa = TAILQ_FIRST(&exchange->sa_list); sa; sa = TAILQ_NEXT(sa, next)) { + for (proto = TAILQ_FIRST(&sa->protos); proto; + proto = TAILQ_NEXT(proto, link)) { + if (proto->proto == IPSEC_PROTO_IPCOMP) + continue; + + iproto = proto->data; + + /* + * There are two SAs for each SA negotiation, incoming and outcoing. + */ + for (i = 0; i < 2; i++) { + prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_d, + isa->skeyid_len); + if (!prf) { + /* XXX What to do? */ + continue; + } + ie->keymat_len = ipsec_keymat_length(proto); + + /* + * We need to roundup the length of the key material buffer + * to a multiple of the PRF's blocksize as it is generated + * in chunks of that blocksize. + */ + iproto->keymat[i] + = malloc(((ie->keymat_len + prf->blocksize - 1) + / prf->blocksize) * prf->blocksize); + if (!iproto->keymat[i]) { + log_error("post_quick_mode: malloc (%lu) failed", + (((unsigned long) ie->keymat_len + + prf->blocksize - 1) / prf->blocksize) * + prf->blocksize); + /* XXX What more to do? */ + free(prf); + continue; + } + for (keymat = iproto->keymat[i]; + keymat < iproto->keymat[i] + ie->keymat_len; + keymat += prf->blocksize) { + prf->Init(prf->prfctx); + + if (keymat != iproto->keymat[i]) { + /* + * Hash in last round's + * KEYMAT. + */ + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "post_quick_mode: last KEYMAT", + keymat - prf->blocksize, + prf->blocksize)); + prf->Update(prf->prfctx, keymat - prf->blocksize, + prf->blocksize); + } + /* If PFS is used hash in g^xy. */ + if (ie->g_xy) { + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "post_quick_mode: g^xy", ie->g_xy, + ie->g_x_len)); + prf->Update(prf->prfctx, ie->g_xy, ie->g_x_len); + } + LOG_DBG((LOG_NEGOTIATION, 90, + "post_quick_mode: suite %d proto %d", proto->no, + proto->proto)); + prf->Update(prf->prfctx, &proto->proto, 1); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "post_quick_mode: SPI", + proto->spi[i], proto->spi_sz[i])); + prf->Update(prf->prfctx, proto->spi[i], proto->spi_sz[i]); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "post_quick_mode: Ni_b", + exchange->nonce_i, exchange->nonce_i_len)); + prf->Update(prf->prfctx, exchange->nonce_i, + exchange->nonce_i_len); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "post_quick_mode: Nr_b", + exchange->nonce_r, exchange->nonce_r_len)); + prf->Update(prf->prfctx, exchange->nonce_r, + exchange->nonce_r_len); + prf->Final(keymat, prf->prfctx); + } + prf_free(prf); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "post_quick_mode: KEYMAT", + iproto->keymat[i], ie->keymat_len)); + } } - - ie->keymat_len = ipsec_keymat_length (proto); - - /* - * We need to roundup the length of the key material buffer - * to a multiple of the PRF's blocksize as it is generated - * in chunks of that blocksize. - */ - iproto->keymat[i] - = malloc (((ie->keymat_len + prf->blocksize - 1) - / prf->blocksize) * prf->blocksize); - if (!iproto->keymat[i]) - { - log_error ("post_quick_mode: malloc (%lu) failed", - (((unsigned long)ie->keymat_len + - prf->blocksize - 1) / prf->blocksize) * - prf->blocksize); - /* XXX What more to do? */ - free (prf); - continue; - } - - for (keymat = iproto->keymat[i]; - keymat < iproto->keymat[i] + ie->keymat_len; - keymat += prf->blocksize) - { - prf->Init (prf->prfctx); - - if (keymat != iproto->keymat[i]) - { - /* Hash in last round's KEYMAT. */ - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "post_quick_mode: last KEYMAT", - keymat - prf->blocksize, - prf->blocksize)); - prf->Update (prf->prfctx, keymat - prf->blocksize, - prf->blocksize); - } - - /* If PFS is used hash in g^xy. */ - if (ie->g_xy) - { - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "post_quick_mode: g^xy", ie->g_xy, - ie->g_x_len)); - prf->Update (prf->prfctx, ie->g_xy, ie->g_x_len); - } - LOG_DBG ((LOG_NEGOTIATION, 90, - "post_quick_mode: suite %d proto %d", proto->no, - proto->proto)); - prf->Update (prf->prfctx, &proto->proto, 1); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "post_quick_mode: SPI", - proto->spi[i], proto->spi_sz[i])); - prf->Update (prf->prfctx, proto->spi[i], proto->spi_sz[i]); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "post_quick_mode: Ni_b", - exchange->nonce_i, exchange->nonce_i_len)); - prf->Update (prf->prfctx, exchange->nonce_i, - exchange->nonce_i_len); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "post_quick_mode: Nr_b", - exchange->nonce_r, exchange->nonce_r_len)); - prf->Update (prf->prfctx, exchange->nonce_r, - exchange->nonce_r_len); - prf->Final (keymat, prf->prfctx); - } - prf_free (prf); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "post_quick_mode: KEYMAT", - iproto->keymat[i], ie->keymat_len)); - } } - } - log_verbose ("isakmpd: quick mode done: %s", - !msg->isakmp_sa || !msg->isakmp_sa->transport - ? "<no transport>" - : msg->isakmp_sa->transport->vtbl->decode_ids - (msg->isakmp_sa ->transport)); + log_verbose("isakmpd: quick mode done: %s", + !msg->isakmp_sa || !msg->isakmp_sa->transport + ? "<no transport>" + : msg->isakmp_sa->transport->vtbl->decode_ids + (msg->isakmp_sa->transport)); } /* @@ -1471,533 +1364,497 @@ post_quick_mode (struct message *msg) * XXX Describe in more detail. */ static int -responder_recv_HASH_SA_NONCE (struct message *msg) +responder_recv_HASH_SA_NONCE(struct message * msg) { - struct payload *hashp, *kep, *idp; - struct sa *sa; - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct prf *prf; - u_int8_t *hash, *my_hash = 0; - size_t hash_len; - u_int8_t *pkt = msg->iov[0].iov_base; - u_int8_t group_desc = 0; - int retval = -1; - struct proto *proto; - struct sockaddr *src, *dst; - char *name; - - hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); - hash = hashp->p; - hashp->flags |= PL_MARK; - - /* The HASH payload should be the first one. */ - if (hash != pkt + ISAKMP_HDR_SZ) - { - /* XXX Is there a better notification type? */ - message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); - goto cleanup; - } - hash_len = GET_ISAKMP_GEN_LENGTH (hash); - my_hash = malloc (hash_len - ISAKMP_GEN_SZ); - if (!my_hash) - { - log_error ("responder_recv_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)hash_len - ISAKMP_GEN_SZ); - goto cleanup; - } - - /* - * Check the payload's integrity. - * XXX Share with ipsec_fill_in_hash? - */ - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_recv_HASH_SA_NONCE: SKEYID_a", - isa->skeyid_a, isa->skeyid_len)); - prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); - if (!prf) - goto cleanup; - prf->Init (prf->prfctx); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_recv_HASH_SA_NONCE: message_id", - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); - prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_recv_HASH_SA_NONCE: message after HASH", - hash + hash_len, - msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len)); - prf->Update (prf->prfctx, hash + hash_len, - msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); - prf->Final (my_hash, prf->prfctx); - prf_free (prf); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_recv_HASH_SA_NONCE: computed HASH(1)", my_hash, - hash_len - ISAKMP_GEN_SZ)); - if (memcmp (hash + ISAKMP_GEN_SZ, my_hash, hash_len - ISAKMP_GEN_SZ) != 0) - { - message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); - goto cleanup; - } - free (my_hash); - my_hash = 0; - - kep = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); - if (kep) - ie->pfs = 1; - - /* Handle optional client ID payloads. */ - idp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_ID]); - if (idp) - { - /* If IDci is there, IDcr must be too. */ - if (!TAILQ_NEXT (idp, link)) - { - /* XXX Is this a good notify type? */ - message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); - goto cleanup; - } - - ie->id_ci_sz = GET_ISAKMP_GEN_LENGTH (idp->p); - ie->id_ci = malloc (ie->id_ci_sz); - if (!ie->id_ci) - { - log_error ("responder_recv_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)ie->id_ci_sz); - goto cleanup; + struct payload *hashp, *kep, *idp; + struct sa *sa; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + u_int8_t *hash, *my_hash = 0; + size_t hash_len; + u_int8_t *pkt = msg->iov[0].iov_base; + u_int8_t group_desc = 0; + int retval = -1; + struct proto *proto; + struct sockaddr *src, *dst; + char *name; + + hashp = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_HASH]); + hash = hashp->p; + hashp->flags |= PL_MARK; + + /* The HASH payload should be the first one. */ + if (hash != pkt + ISAKMP_HDR_SZ) { + /* XXX Is there a better notification type? */ + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + goto cleanup; } - memcpy (ie->id_ci, idp->p, ie->id_ci_sz); - idp->flags |= PL_MARK; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_recv_HASH_SA_NONCE: IDci", - ie->id_ci + ISAKMP_GEN_SZ, ie->id_ci_sz - - ISAKMP_GEN_SZ)); - - idp = TAILQ_NEXT (idp, link); - ie->id_cr_sz = GET_ISAKMP_GEN_LENGTH (idp->p); - ie->id_cr = malloc (ie->id_cr_sz); - if (!ie->id_cr) - { - log_error ("responder_recv_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)ie->id_cr_sz); - goto cleanup; + hash_len = GET_ISAKMP_GEN_LENGTH(hash); + my_hash = malloc(hash_len - ISAKMP_GEN_SZ); + if (!my_hash) { + log_error("responder_recv_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) hash_len - ISAKMP_GEN_SZ); + goto cleanup; } - memcpy (ie->id_cr, idp->p, ie->id_cr_sz); - idp->flags |= PL_MARK; - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_recv_HASH_SA_NONCE: IDcr", - ie->id_cr + ISAKMP_GEN_SZ, ie->id_cr_sz - - ISAKMP_GEN_SZ)); - } - else - { - /* - * If client identifiers are not present in the exchange, - * we fake them. RFC 2409 states: - * The identities of the SAs negotiated in Quick Mode are - * implicitly assumed to be the IP addresses of the ISAKMP - * peers, without any constraints on the protocol or port - * numbers allowed, unless client identifiers are specified - * in Quick Mode. - * - * -- Michael Paddon (mwp@aba.net.au) - */ - - ie->flags = IPSEC_EXCH_FLAG_NO_ID; - - /* Get initiator and responder addresses. */ - msg->transport->vtbl->get_src (msg->transport, &src); - msg->transport->vtbl->get_dst (msg->transport, &dst); - ie->id_ci_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen (src); - ie->id_cr_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen (dst); - ie->id_ci = calloc (ie->id_ci_sz, sizeof (char)); - ie->id_cr = calloc (ie->id_cr_sz, sizeof (char)); - - if (!ie->id_ci || !ie->id_cr) - { - log_error ("responder_recv_HASH_SA_NONCE: calloc (%lu, %lu) failed", - (unsigned long)ie->id_ci_sz, (unsigned long)sizeof (char)); - goto cleanup; + /* + * Check the payload's integrity. + * XXX Share with ipsec_fill_in_hash? + */ + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_recv_HASH_SA_NONCE: SKEYID_a", + isa->skeyid_a, isa->skeyid_len)); + prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); + if (!prf) + goto cleanup; + prf->Init(prf->prfctx); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_recv_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update(prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_recv_HASH_SA_NONCE: message after HASH", + hash + hash_len, + msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len)); + prf->Update(prf->prfctx, hash + hash_len, + msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); + prf->Final(my_hash, prf->prfctx); + prf_free(prf); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_recv_HASH_SA_NONCE: computed HASH(1)", my_hash, + hash_len - ISAKMP_GEN_SZ)); + if (memcmp(hash + ISAKMP_GEN_SZ, my_hash, hash_len - ISAKMP_GEN_SZ) != 0) { + message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + goto cleanup; } + free(my_hash); + my_hash = 0; + + kep = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_KEY_EXCH]); + if (kep) + ie->pfs = 1; + + /* Handle optional client ID payloads. */ + idp = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_ID]); + if (idp) { + /* If IDci is there, IDcr must be too. */ + if (!TAILQ_NEXT(idp, link)) { + /* XXX Is this a good notify type? */ + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + goto cleanup; + } + ie->id_ci_sz = GET_ISAKMP_GEN_LENGTH(idp->p); + ie->id_ci = malloc(ie->id_ci_sz); + if (!ie->id_ci) { + log_error("responder_recv_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) ie->id_ci_sz); + goto cleanup; + } + memcpy(ie->id_ci, idp->p, ie->id_ci_sz); + idp->flags |= PL_MARK; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_recv_HASH_SA_NONCE: IDci", + ie->id_ci + ISAKMP_GEN_SZ, ie->id_ci_sz + - ISAKMP_GEN_SZ)); + + idp = TAILQ_NEXT(idp, link); + ie->id_cr_sz = GET_ISAKMP_GEN_LENGTH(idp->p); + ie->id_cr = malloc(ie->id_cr_sz); + if (!ie->id_cr) { + log_error("responder_recv_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) ie->id_cr_sz); + goto cleanup; + } + memcpy(ie->id_cr, idp->p, ie->id_cr_sz); + idp->flags |= PL_MARK; + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_recv_HASH_SA_NONCE: IDcr", + ie->id_cr + ISAKMP_GEN_SZ, ie->id_cr_sz + - ISAKMP_GEN_SZ)); + } else { + /* + * If client identifiers are not present in the exchange, + * we fake them. RFC 2409 states: + * The identities of the SAs negotiated in Quick Mode are + * implicitly assumed to be the IP addresses of the ISAKMP + * peers, without any constraints on the protocol or port + * numbers allowed, unless client identifiers are specified + * in Quick Mode. + * + * -- Michael Paddon (mwp@aba.net.au) + */ + + ie->flags = IPSEC_EXCH_FLAG_NO_ID; + + /* Get initiator and responder addresses. */ + msg->transport->vtbl->get_src(msg->transport, &src); + msg->transport->vtbl->get_dst(msg->transport, &dst); + ie->id_ci_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen(src); + ie->id_cr_sz = ISAKMP_ID_DATA_OFF + sockaddr_addrlen(dst); + ie->id_ci = calloc(ie->id_ci_sz, sizeof(char)); + ie->id_cr = calloc(ie->id_cr_sz, sizeof(char)); + + if (!ie->id_ci || !ie->id_cr) { + log_error("responder_recv_HASH_SA_NONCE: calloc (%lu, %lu) failed", + (unsigned long) ie->id_ci_sz, (unsigned long) sizeof(char)); + goto cleanup; + } + if (src->sa_family != dst->sa_family) { + log_error("initiator_recv_HASH_SA_NONCE: sa_family mismatch"); + goto cleanup; + } + switch (src->sa_family) { + case AF_INET: + SET_ISAKMP_ID_TYPE(ie->id_ci, IPSEC_ID_IPV4_ADDR); + SET_ISAKMP_ID_TYPE(ie->id_cr, IPSEC_ID_IPV4_ADDR); + break; + + case AF_INET6: + SET_ISAKMP_ID_TYPE(ie->id_ci, IPSEC_ID_IPV6_ADDR); + SET_ISAKMP_ID_TYPE(ie->id_cr, IPSEC_ID_IPV6_ADDR); + break; + + default: + log_error("initiator_recv_HASH_SA_NONCE: unknown sa_family %d", + src->sa_family); + goto cleanup; + } - if (src->sa_family != dst->sa_family) - { - log_error ("initiator_recv_HASH_SA_NONCE: sa_family mismatch"); - goto cleanup; - } - - switch (src->sa_family) - { - case AF_INET: - SET_ISAKMP_ID_TYPE (ie->id_ci, IPSEC_ID_IPV4_ADDR); - SET_ISAKMP_ID_TYPE (ie->id_cr, IPSEC_ID_IPV4_ADDR); - break; - - case AF_INET6: - SET_ISAKMP_ID_TYPE (ie->id_ci, IPSEC_ID_IPV6_ADDR); - SET_ISAKMP_ID_TYPE (ie->id_cr, IPSEC_ID_IPV6_ADDR); - break; - - default: - log_error ("initiator_recv_HASH_SA_NONCE: unknown sa_family %d", - src->sa_family); - goto cleanup; + memcpy(ie->id_cr + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(src), + sockaddr_addrlen(src)); + memcpy(ie->id_ci + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(dst), + sockaddr_addrlen(dst)); } - memcpy (ie->id_cr + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src), - sockaddr_addrlen (src)); - memcpy (ie->id_ci + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (dst), - sockaddr_addrlen (dst)); - } - #ifdef USE_POLICY #ifdef USE_KEYNOTE - if (message_negotiate_sa (msg, check_policy)) - goto cleanup; + if (message_negotiate_sa(msg, check_policy)) + goto cleanup; #else - if (message_negotiate_sa (msg, 0)) - goto cleanup; + if (message_negotiate_sa(msg, 0)) + goto cleanup; #endif #else - if (message_negotiate_sa (msg, 0)) - goto cleanup; -#endif /* USE_POLICY */ - - for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = TAILQ_NEXT (sa, next)) - { - for (proto = TAILQ_FIRST (&sa->protos); proto; - proto = TAILQ_NEXT (proto, link)) - { - /* XXX we need to have some attributes per proto, not all per SA. */ - ipsec_decode_transform (msg, sa, proto, proto->chosen->p); - if (proto->proto == IPSEC_PROTO_IPSEC_AH - && !((struct ipsec_proto *)proto->data)->auth) - { - log_print ("responder_recv_HASH_SA_NONCE: " - "AH proposed without an algorithm attribute"); - message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); - goto next_sa; - } - } + if (message_negotiate_sa(msg, 0)) + goto cleanup; +#endif /* USE_POLICY */ + + for (sa = TAILQ_FIRST(&exchange->sa_list); sa; sa = TAILQ_NEXT(sa, next)) { + for (proto = TAILQ_FIRST(&sa->protos); proto; + proto = TAILQ_NEXT(proto, link)) { + /* + * XXX we need to have some attributes per proto, not + * all per SA. + */ + ipsec_decode_transform(msg, sa, proto, proto->chosen->p); + if (proto->proto == IPSEC_PROTO_IPSEC_AH + && !((struct ipsec_proto *) proto->data)->auth) { + log_print("responder_recv_HASH_SA_NONCE: " + "AH proposed without an algorithm attribute"); + message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); + goto next_sa; + } + } - isa = sa->data; + isa = sa->data; + + /* + * The group description is mandatory if we got a KEY_EXCH + * payload. + */ + if (kep) { + if (!isa->group_desc) { + log_print("responder_recv_HASH_SA_NONCE: " + "KEY_EXCH payload without a group desc. attribute"); + message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); + continue; + } + /* Also, all SAs must have equal groups. */ + if (!group_desc) + group_desc = isa->group_desc; + else if (group_desc != isa->group_desc) { + log_print("responder_recv_HASH_SA_NONCE: " + "differing group descriptions in one QM"); + message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); + continue; + } + } + /* At least one SA was accepted. */ + retval = 0; - /* The group description is mandatory if we got a KEY_EXCH payload. */ - if (kep) - { - if (!isa->group_desc) - { - log_print ("responder_recv_HASH_SA_NONCE: " - "KEY_EXCH payload without a group desc. attribute"); - message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); - continue; - } - - /* Also, all SAs must have equal groups. */ - if (!group_desc) - group_desc = isa->group_desc; - else if (group_desc != isa->group_desc) - { - log_print ("responder_recv_HASH_SA_NONCE: " - "differing group descriptions in one QM"); - message_drop (msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); - continue; - } +next_sa: + ; /* XXX gcc3 wants this. */ } - /* At least one SA was accepted. */ - retval = 0; - - next_sa: - ; /* XXX gcc3 wants this. */ - } - - if (kep) - { - ie->group = group_get (group_desc); - if (!ie->group) - { - /* - * XXX If the error was due to an out-of-range group description - * we should notify our peer, but this should probably be done - * by the attribute validation. Is it? - */ - goto cleanup; + if (kep) { + ie->group = group_get(group_desc); + if (!ie->group) { + /* + * XXX If the error was due to an out-of-range group description + * we should notify our peer, but this should probably be done + * by the attribute validation. Is it? + */ + goto cleanup; + } } - } - - /* Copy out the initiator's nonce. */ - if (exchange_save_nonce (msg)) - goto cleanup; - - /* Handle the optional KEY_EXCH payload. */ - if (kep && ipsec_save_g_x (msg)) - goto cleanup; - - /* - * Try to find and set the connection name on the exchange. - */ - - /* - * Check for accepted identities as well as lookup the connection - * name and set it on the exchange. - */ - name = connection_passive_lookup_by_ids (ie->id_ci, ie->id_cr); - if (name) - { - exchange->name = strdup (name); - if (!exchange->name) - { - log_error ("responder_recv_HASH_SA_NONCE: strdup (\"%s\") failed", - name); - goto cleanup; + /* Copy out the initiator's nonce. */ + if (exchange_save_nonce(msg)) + goto cleanup; + + /* Handle the optional KEY_EXCH payload. */ + if (kep && ipsec_save_g_x(msg)) + goto cleanup; + + /* + * Try to find and set the connection name on the exchange. + */ + + /* + * Check for accepted identities as well as lookup the connection + * name and set it on the exchange. + */ + name = connection_passive_lookup_by_ids(ie->id_ci, ie->id_cr); + if (name) { + exchange->name = strdup(name); + if (!exchange->name) { + log_error("responder_recv_HASH_SA_NONCE: strdup (\"%s\") failed", + name); + goto cleanup; + } } - } #if !defined (USE_POLICY) && !defined (USE_KEYNOTE) - else - { - /* - * This code is no longer necessary, as policy determines acceptance - * of IDs/SAs. (angelos@openbsd.org) - * - * XXX Keep it if not USE_POLICY for now, though. - */ - - /* XXX Notify peer and log. */ - goto cleanup; - } -#endif /* !USE_POLICY && !USE_KEYNOTE */ - - return retval; + else { + /* + * This code is no longer necessary, as policy determines acceptance + * of IDs/SAs. (angelos@openbsd.org) + * + * XXX Keep it if not USE_POLICY for now, though. + */ + + /* XXX Notify peer and log. */ + goto cleanup; + } +#endif /* !USE_POLICY && !USE_KEYNOTE */ + + return retval; cleanup: - /* Remove all potential protocols that have been added to the SAs. */ - for (sa = TAILQ_FIRST (&exchange->sa_list); sa; sa = TAILQ_NEXT (sa, next)) - while ((proto = TAILQ_FIRST (&sa->protos)) != 0) - proto_free (proto); - if (my_hash) - free (my_hash); - if (ie->id_ci) - { - free (ie->id_ci); - ie->id_ci = 0; - } - if (ie->id_cr) - { - free (ie->id_cr); - ie->id_cr = 0; - } - return -1; + /* Remove all potential protocols that have been added to the SAs. */ + for (sa = TAILQ_FIRST(&exchange->sa_list); sa; sa = TAILQ_NEXT(sa, next)) + while ((proto = TAILQ_FIRST(&sa->protos)) != 0) + proto_free(proto); + if (my_hash) + free(my_hash); + if (ie->id_ci) { + free(ie->id_ci); + ie->id_ci = 0; + } + if (ie->id_cr) { + free(ie->id_cr); + ie->id_cr = 0; + } + return -1; } /* Reply with the transform we chose. */ static int -responder_send_HASH_SA_NONCE (struct message *msg) +responder_send_HASH_SA_NONCE(struct message * msg) { - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct prf *prf; - struct hash *hash = hash_get (isa->hash); - size_t hashsize = hash->hashsize; - size_t nonce_sz = exchange->nonce_i_len; - u_int8_t *buf; - int initiator = exchange->initiator; - char header[80]; - u_int32_t i; - u_int8_t *id; - size_t sz; - - /* We want a HASH payload to start with. XXX Share with ike_main_mode.c? */ - buf = malloc (ISAKMP_HASH_SZ + hashsize); - if (!buf) - { - log_error ("responder_send_HASH_SA_NONCE: malloc (%lu) failed", - ISAKMP_HASH_SZ + (unsigned long)hashsize); - return -1; - } - if (message_add_payload (msg, ISAKMP_PAYLOAD_HASH, buf, - ISAKMP_HASH_SZ + hashsize, 1)) - { - free (buf); - return -1; - } - - /* Add the SA payload(s) with the transform(s) that was/were chosen. */ - if (message_add_sa_payload (msg)) - return -1; - - /* Generate a nonce, and add it to the message. */ - if (exchange_gen_nonce (msg, nonce_sz)) - return -1; - - /* Generate optional KEY_EXCH payload. This is known as PFS. */ - if (ie->group && ipsec_gen_g_x (msg)) - return -1; - - /* If the initiator client ID's were acceptable, just mirror them back. */ - if (!(ie->flags & IPSEC_EXCH_FLAG_NO_ID)) - { - sz = ie->id_ci_sz; - id = malloc (sz); - if (!id) - { - log_error ("responder_send_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)sz); - return -1; + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + struct hash *hash = hash_get(isa->hash); + size_t hashsize = hash->hashsize; + size_t nonce_sz = exchange->nonce_i_len; + u_int8_t *buf; + int initiator = exchange->initiator; + char header[80]; + u_int32_t i; + u_int8_t *id; + size_t sz; + + /* + * We want a HASH payload to start with. XXX Share with + * ike_main_mode.c? + */ + buf = malloc(ISAKMP_HASH_SZ + hashsize); + if (!buf) { + log_error("responder_send_HASH_SA_NONCE: malloc (%lu) failed", + ISAKMP_HASH_SZ + (unsigned long) hashsize); + return -1; } - memcpy (id, ie->id_ci, sz); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: IDic", - id, sz)); - if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) - { - free (id); - return -1; + if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH, buf, + ISAKMP_HASH_SZ + hashsize, 1)) { + free(buf); + return -1; } - - sz = ie->id_cr_sz; - id = malloc (sz); - if (!id) - { - log_error ("responder_send_HASH_SA_NONCE: malloc (%lu) failed", - (unsigned long)sz); - return -1; + /* Add the SA payload(s) with the transform(s) that was/were chosen. */ + if (message_add_sa_payload(msg)) + return -1; + + /* Generate a nonce, and add it to the message. */ + if (exchange_gen_nonce(msg, nonce_sz)) + return -1; + + /* Generate optional KEY_EXCH payload. This is known as PFS. */ + if (ie->group && ipsec_gen_g_x(msg)) + return -1; + + /* + * If the initiator client ID's were acceptable, just mirror them + * back. + */ + if (!(ie->flags & IPSEC_EXCH_FLAG_NO_ID)) { + sz = ie->id_ci_sz; + id = malloc(sz); + if (!id) { + log_error("responder_send_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) sz); + return -1; + } + memcpy(id, ie->id_ci, sz); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: IDic", + id, sz)); + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) { + free(id); + return -1; + } + sz = ie->id_cr_sz; + id = malloc(sz); + if (!id) { + log_error("responder_send_HASH_SA_NONCE: malloc (%lu) failed", + (unsigned long) sz); + return -1; + } + memcpy(id, ie->id_cr, sz); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: IDrc", + id, sz)); + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) { + free(id); + return -1; + } } - memcpy (id, ie->id_cr, sz); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: IDrc", - id, sz)); - if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, id, sz, 1)) - { - free (id); - return -1; + /* Allocate the prf and start calculating our HASH(2). XXX Share? */ + LOG_DBG((LOG_NEGOTIATION, 90, "responder_recv_HASH: isakmp_sa %p isa %p", + isakmp_sa, isa)); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: SKEYID_a", + isa->skeyid_a, isa->skeyid_len)); + prf = prf_alloc(isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + return -1; + prf->Init(prf->prfctx); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_send_HASH_SA_NONCE: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update(prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len)); + prf->Update(prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + + /* Loop over all payloads after HASH(2). */ + for (i = 2; i < msg->iovlen; i++) { + /* XXX Misleading payload type printouts. */ + snprintf(header, sizeof header, + "responder_send_HASH_SA_NONCE: payload %d after HASH(2)", + i - 1); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, header, msg->iov[i].iov_base, + msg->iov[i].iov_len)); + prf->Update(prf->prfctx, msg->iov[i].iov_base, msg->iov[i].iov_len); } - } - - /* Allocate the prf and start calculating our HASH(2). XXX Share? */ - LOG_DBG ((LOG_NEGOTIATION, 90, "responder_recv_HASH: isakmp_sa %p isa %p", - isakmp_sa, isa)); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: SKEYID_a", - isa->skeyid_a, isa->skeyid_len)); - prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); - if (!prf) - return -1; - prf->Init (prf->prfctx); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_send_HASH_SA_NONCE: message_id", - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); - prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_send_HASH_SA_NONCE: NONCE_I_b", - exchange->nonce_i, exchange->nonce_i_len)); - prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); - - /* Loop over all payloads after HASH(2). */ - for (i = 2; i < msg->iovlen; i++) - { - /* XXX Misleading payload type printouts. */ - snprintf (header, sizeof header, - "responder_send_HASH_SA_NONCE: payload %d after HASH(2)", - i - 1); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, header, msg->iov[i].iov_base, - msg->iov[i].iov_len)); - prf->Update (prf->prfctx, msg->iov[i].iov_base, msg->iov[i].iov_len); - } - prf->Final (buf + ISAKMP_HASH_DATA_OFF, prf->prfctx); - prf_free (prf); - snprintf (header, sizeof header, "responder_send_HASH_SA_NONCE: HASH_%c", - initiator ? 'I' : 'R'); - LOG_DBG_BUF ((LOG_NEGOTIATION, 80, header, buf + ISAKMP_HASH_DATA_OFF, - hashsize)); - - if (ie->group) - message_register_post_send (msg, gen_g_xy); - - return 0; + prf->Final(buf + ISAKMP_HASH_DATA_OFF, prf->prfctx); + prf_free(prf); + snprintf(header, sizeof header, "responder_send_HASH_SA_NONCE: HASH_%c", + initiator ? 'I' : 'R'); + LOG_DBG_BUF((LOG_NEGOTIATION, 80, header, buf + ISAKMP_HASH_DATA_OFF, + hashsize)); + + if (ie->group) + message_register_post_send(msg, gen_g_xy); + + return 0; } static void -gen_g_xy (struct message *msg) +gen_g_xy(struct message * msg) { - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - - /* Compute Diffie-Hellman shared value. */ - ie->g_xy = malloc (ie->g_x_len); - if (!ie->g_xy) - { - log_error ("gen_g_xy: malloc (%lu) failed", (unsigned long)ie->g_x_len); - return; - } - if (dh_create_shared (ie->group, ie->g_xy, - exchange->initiator ? ie->g_xr : ie->g_xi)) - { - log_print ("gen_g_xy: dh_create_shared failed"); - return; - } - LOG_DBG_BUF ((LOG_NEGOTIATION, 80, "gen_g_xy: g^xy", ie->g_xy, ie->g_x_len)); + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + + /* Compute Diffie-Hellman shared value. */ + ie->g_xy = malloc(ie->g_x_len); + if (!ie->g_xy) { + log_error("gen_g_xy: malloc (%lu) failed", (unsigned long) ie->g_x_len); + return; + } + if (dh_create_shared(ie->group, ie->g_xy, + exchange->initiator ? ie->g_xr : ie->g_xi)) { + log_print("gen_g_xy: dh_create_shared failed"); + return; + } + LOG_DBG_BUF((LOG_NEGOTIATION, 80, "gen_g_xy: g^xy", ie->g_xy, ie->g_x_len)); } static int -responder_recv_HASH (struct message *msg) +responder_recv_HASH(struct message * msg) { - struct exchange *exchange = msg->exchange; - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct prf *prf; - u_int8_t *hash, *my_hash = 0; - size_t hash_len; - struct payload *hashp; - - /* Find HASH(3) and create our own hash, just as big. */ - hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); - hash = hashp->p; - hashp->flags |= PL_MARK; - hash_len = GET_ISAKMP_GEN_LENGTH (hash); - my_hash = malloc (hash_len - ISAKMP_GEN_SZ); - if (!my_hash) - { - log_error ("responder_recv_HASH: malloc (%lu) failed", - (unsigned long)hash_len - ISAKMP_GEN_SZ); - goto cleanup; - } - - /* Allocate the prf and start calculating our HASH(3). XXX Share? */ - LOG_DBG ((LOG_NEGOTIATION, 90, "responder_recv_HASH: isakmp_sa %p isa %p", - isakmp_sa, isa)); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_recv_HASH: SKEYID_a", - isa->skeyid_a, isa->skeyid_len)); - prf = prf_alloc (isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); - if (!prf) - goto cleanup; - prf->Init (prf->prfctx); - prf->Update (prf->prfctx, (unsigned char *)"\0", 1); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_recv_HASH: message_id", - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); - prf->Update (prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_recv_HASH: NONCE_I_b", - exchange->nonce_i, exchange->nonce_i_len)); - prf->Update (prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, "responder_recv_HASH: NONCE_R_b", - exchange->nonce_r, exchange->nonce_r_len)); - prf->Update (prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); - prf->Final (my_hash, prf->prfctx); - prf_free (prf); - LOG_DBG_BUF ((LOG_NEGOTIATION, 90, - "responder_recv_HASH: computed HASH(3)", my_hash, - hash_len - ISAKMP_GEN_SZ)); - if (memcmp (hash + ISAKMP_GEN_SZ, my_hash, hash_len - ISAKMP_GEN_SZ) != 0) - { - message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); - goto cleanup; - } - free (my_hash); - - post_quick_mode (msg); - - return 0; - - cleanup: - if (my_hash) - free (my_hash); - return -1; + struct exchange *exchange = msg->exchange; + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa = isakmp_sa->data; + struct prf *prf; + u_int8_t *hash, *my_hash = 0; + size_t hash_len; + struct payload *hashp; + + /* Find HASH(3) and create our own hash, just as big. */ + hashp = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_HASH]); + hash = hashp->p; + hashp->flags |= PL_MARK; + hash_len = GET_ISAKMP_GEN_LENGTH(hash); + my_hash = malloc(hash_len - ISAKMP_GEN_SZ); + if (!my_hash) { + log_error("responder_recv_HASH: malloc (%lu) failed", + (unsigned long) hash_len - ISAKMP_GEN_SZ); + goto cleanup; + } + /* Allocate the prf and start calculating our HASH(3). XXX Share? */ + LOG_DBG((LOG_NEGOTIATION, 90, "responder_recv_HASH: isakmp_sa %p isa %p", + isakmp_sa, isa)); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_recv_HASH: SKEYID_a", + isa->skeyid_a, isa->skeyid_len)); + prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); + if (!prf) + goto cleanup; + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, (unsigned char *) "\0", 1); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_recv_HASH: message_id", + exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update(prf->prfctx, exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_recv_HASH: NONCE_I_b", + exchange->nonce_i, exchange->nonce_i_len)); + prf->Update(prf->prfctx, exchange->nonce_i, exchange->nonce_i_len); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, "responder_recv_HASH: NONCE_R_b", + exchange->nonce_r, exchange->nonce_r_len)); + prf->Update(prf->prfctx, exchange->nonce_r, exchange->nonce_r_len); + prf->Final(my_hash, prf->prfctx); + prf_free(prf); + LOG_DBG_BUF((LOG_NEGOTIATION, 90, + "responder_recv_HASH: computed HASH(3)", my_hash, + hash_len - ISAKMP_GEN_SZ)); + if (memcmp(hash + ISAKMP_GEN_SZ, my_hash, hash_len - ISAKMP_GEN_SZ) != 0) { + message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + goto cleanup; + } + free(my_hash); + + post_quick_mode(msg); + + return 0; + +cleanup: + if (my_hash) + free(my_hash); + return -1; } |