/* accept_KE * * Check and accept DH public value (Gi or Gr) from peer's message. * According to RFC2409 "The Internet key exchange (IKE)" 5: * The Diffie-Hellman public value passed in a KE payload, in either * a phase 1 or phase 2 exchange, MUST be the length of the negotiated * Diffie-Hellman group enforced, if necessary, by pre-pending the * value with zeros. */ notification_t accept_KE(chunk_t *dest, const char *val_name, const struct oakley_group_desc *gr, pb_stream *pbs) { if (pbs_left(pbs) != gr->bytes) { loglog(RC_LOG_SERIOUS, "KE has %u byte DH public value; %u required", (unsigned) pbs_left(pbs), (unsigned) gr->bytes); /* XXX Could send notification back */ return INVALID_KEY_INFORMATION; } clonereplacechunk(*dest, pbs->cur, pbs_left(pbs), val_name); DBG_cond_dump_chunk(DBG_CRYPT, "DH public value received:\n", *dest); return NOTHING_WRONG; }
/* * replaces try_RSA_signature_v2() */ err_t try_RSA_signature_v2(const u_char hash_val[MAX_DIGEST_LEN] , size_t hash_len , const pb_stream *sig_pbs, struct pubkey *kr , struct state *st) { const u_char *sig_val = sig_pbs->cur; size_t sig_len = pbs_left(sig_pbs); const struct RSA_public_key *k = &kr->u.rsa; if (k == NULL) return "1""no key available"; /* failure: no key to use */ /* decrypt the signature -- reversing RSA_sign_hash */ if (sig_len != k->k) { return "1""SIG length does not match public key length"; } err_t ugh = RSA_signature_verify_nss (k,hash_val,hash_len,sig_val,sig_len); if(ugh!=NULL) { return ugh; } unreference_key(&st->st_peer_pubkey); st->st_peer_pubkey = reference_key(kr); return NULL; }
void ikev2_decode_cert(struct msg_digest *md) { struct payload_digest *p; for (p = md->chain[ISAKMP_NEXT_v2CERT]; p != NULL; p = p->next) { struct ikev2_cert *const v2cert = &p->payload.v2cert; chunk_t blob; time_t valid_until; blob.ptr = p->pbs.cur; blob.len = pbs_left(&p->pbs); if (v2cert->isac_enc == CERT_X509_SIGNATURE) { x509cert_t cert2 = empty_x509cert; if (parse_x509cert(blob, 0, &cert2)) { if (verify_x509cert(&cert2, strict_crl_policy, &valid_until)) { DBG(DBG_X509 | DBG_PARSING, DBG_log("Public key validated") ) add_x509_public_key(NULL, &cert2, valid_until, DAL_SIGNED); } else { plog("X.509 certificate rejected"); } free_generalNames(cert2.subjectAltName, FALSE); free_generalNames(cert2.crlDistributionPoints, FALSE); } else plog("Syntax error in X.509 certificate"); } else if (v2cert->isac_enc == CERT_PKCS7_WRAPPED_X509) { x509cert_t *cert2 = NULL; if (parse_pkcs7_cert(blob, &cert2)) store_x509certs(&cert2, strict_crl_policy); else plog("Syntax error in PKCS#7 wrapped X.509 certificates"); } else { loglog(RC_LOG_SERIOUS, "ignoring %s certificate payload", enum_show(&ikev2_cert_type_names, v2cert->isac_enc)); DBG_cond_dump_chunk(DBG_PARSING, "CERT:\n", blob); } } }
notification_t accept_nonce(struct msg_digest *md, chunk_t *dest , const char *name, enum next_payload_types paynum) { pb_stream *nonce_pbs = &md->chain[paynum]->pbs; size_t len = pbs_left(nonce_pbs); if (len < MINIMUM_NONCE_SIZE || MAXIMUM_NONCE_SIZE < len) { loglog(RC_LOG_SERIOUS, "%s length not between %d and %d" , name , MINIMUM_NONCE_SIZE, MAXIMUM_NONCE_SIZE); return PAYLOAD_MALFORMED; /* ??? */ } clonereplacechunk(*dest, nonce_pbs->cur, len, "nonce"); return NOTHING_WRONG; }
/* * Decode the CR payload of Phase 1. */ void decode_cr(struct msg_digest *md, generalName_t **requested_ca) { struct payload_digest *p; for (p = md->chain[ISAKMP_NEXT_CR]; p != NULL; p = p->next) { struct isakmp_cr *const cr = &p->payload.cr; chunk_t ca_name; ca_name.len = pbs_left(&p->pbs); ca_name.ptr = (ca_name.len > 0)? p->pbs.cur : NULL; DBG_cond_dump_chunk(DBG_PARSING, "CR", ca_name); if (cr->isacr_type == CERT_X509_SIGNATURE) { if (ca_name.len > 0) { generalName_t *gn; if (!is_asn1(ca_name)) continue; gn = alloc_thing(generalName_t, "generalName"); clonetochunk(ca_name, ca_name.ptr,ca_name.len, "ca name"); gn->kind = GN_DIRECTORY_NAME; gn->name = ca_name; gn->next = *requested_ca; *requested_ca = gn; } DBG(DBG_PARSING | DBG_CONTROL, char buf[IDTOA_BUF]; dntoa_or_null(buf, IDTOA_BUF, ca_name, "%any"); DBG_log("requested CA: '%s'", buf); ) } else loglog(RC_LOG_SERIOUS, "ignoring %s certificate request payload", enum_show(&cert_type_names, cr->isacr_type)); }
bool in_raw(void *bytes, size_t len, pb_stream *ins, const char *name) { if (pbs_left(ins) < len) { loglog(RC_LOG_SERIOUS, "not enough bytes left to get %s from %s", name, ins->name); return FALSE; } else { if (bytes == NULL) { DBG(DBG_PARSING , DBG_log("skipping %u raw bytes of %s (%s)" , (unsigned) len, ins->name, name); DBG_dump(name, ins->cur, len)); } else {
/* * used by responder, for extracting PPK_ID from IKEv2 Notify * PPK_ID Payload, we store PPK_ID and its type in payl */ bool extract_ppk_id(pb_stream *pbs, struct ppk_id_payload *payl) { size_t len = pbs_left(pbs); u_char dst[PPK_ID_MAXLEN]; int idtype; if (len > PPK_ID_MAXLEN) { loglog(RC_LOG_SERIOUS, "PPK ID length is too big"); return FALSE; } if (len <= 1) { loglog(RC_LOG_SERIOUS, "PPK ID data must be at least 1 byte (received %zd bytes including ppk type byte)", len); return FALSE; } if (!in_raw(dst, len, pbs, "Unified PPK_ID Payload")) { loglog(RC_LOG_SERIOUS, "PPK ID data could not be read"); return FALSE; } DBG(DBG_CONTROL, DBG_log("received PPK_ID type: %s", enum_name(&ikev2_ppk_id_type_names, dst[0]))); idtype = (int)dst[0]; switch (idtype) { case PPK_ID_FIXED: DBG(DBG_CONTROL, DBG_log("PPK_ID of type PPK_ID_FIXED.")); break; case PPK_ID_OPAQUE: default: loglog(RC_LOG_SERIOUS, "PPK_ID type %d (%s) not supported", idtype, enum_name(&ikev2_ppk_id_type_names, idtype)); return FALSE; } /* clone ppk id data without ppk id type byte */ clonetochunk(payl->ppk_id, dst + 1, len - 1, "PPK_ID data"); DBG(DBG_CONTROL, DBG_dump_chunk("Extracted PPK_ID", payl->ppk_id)); return TRUE; }
stf_status ikev2_verify_psk_auth(struct state *st, enum original_role role, unsigned char *idhash, pb_stream *sig_pbs) { unsigned int hash_len = st->st_oakley.prf_hasher->hash_digest_len; unsigned char calc_hash[hash_len]; size_t sig_len = pbs_left(sig_pbs); enum original_role invertrole; invertrole = (role == ORIGINAL_INITIATOR ? ORIGINAL_RESPONDER : ORIGINAL_INITIATOR); if (sig_len != hash_len) { libreswan_log("negotiated prf: %s ", st->st_oakley.prf_hasher->common.name); libreswan_log( "I2 hash length:%lu does not match with PRF hash len %lu", (long unsigned) sig_len, (long unsigned) hash_len); return STF_FAIL; } if (!ikev2_calculate_psk_sighash(st, invertrole, idhash, st->st_firstpacket_him, calc_hash)) return STF_FAIL; DBG(DBG_PRIVATE, DBG_dump("Received PSK auth octets", sig_pbs->cur, sig_len); DBG_dump("Calculated PSK auth octets", calc_hash, hash_len)); if (memeq(sig_pbs->cur, calc_hash, hash_len) ) { return STF_OK; } else { libreswan_log("AUTH mismatch: Received AUTH != computed AUTH"); return STF_FAIL; } }
err_t try_RSA_signature_v2(const u_char hash_val[MAX_DIGEST_LEN] , size_t hash_len , const pb_stream *sig_pbs, struct pubkey *kr , struct state *st) { const u_char *sig_val = sig_pbs->cur; size_t sig_len = pbs_left(sig_pbs); u_char s[RSA_MAX_OCTETS]; /* for decrypted sig_val */ u_char *sig; unsigned int padlen; const struct RSA_public_key *k = &kr->u.rsa; if (k == NULL) return "1""no key available"; /* failure: no key to use */ /* decrypt the signature -- reversing RSA_sign_hash */ if (sig_len != k->k) { DBG_log("sig_len: %u != k->k: %u" , (unsigned int)sig_len, (unsigned int)k->k); return "1""SIG length does not match public key length"; } /* actual exponentiation; see PKCS#1 v2.0 5.1 */ { chunk_t temp_s; MP_INT c; n_to_mpz(&c, sig_val, sig_len); oswcrypto.mod_exp(&c, &c, &k->e, &k->n); temp_s = mpz_to_n(&c, sig_len); /* back to octets */ memcpy(s, temp_s.ptr, sig_len); pfree(temp_s.ptr); mpz_clear(&c); } /* check signature contents */ /* verify padding */ padlen = sig_len - 3 - (hash_len+der_digestinfo_len); /* now check padding */ sig = s; DBG(DBG_CRYPT, DBG_dump("v2rsa decrypted SIG1:", sig, sig_len)); if(sig[0] != 0x00 || sig[1] != 0x01 || sig[padlen+2] != 0x00) { return "2""SIG padding does not check out"; } /* skip padding */ sig += padlen+3; /* 2 verify that the has was done with SHA1 */ if(memcmp(der_digestinfo, sig, der_digestinfo_len)!=0) { return "SIG not performed with SHA1"; } sig += der_digestinfo_len; DBG(DBG_CRYPT, DBG_dump("v2rsa decrypted SIG:", hash_val, hash_len); DBG_dump("v2rsa computed hash:", sig, hash_len); );
/** * DPD out Responder * * @param st A state structure (phase 1) * @param n A notification (isakmp_notification) * @param pbs A PB Stream * @return stf_status */ stf_status dpd_inR(struct state *p1st , struct isakmp_notification *const n , pb_stream *pbs) { time_t tm = now(); u_int32_t seqno; if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) { loglog(RC_LOG_SERIOUS, "recevied R_U_THERE_ACK for unestablished ISKAMP SA"); return STF_FAIL; } if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) { loglog(RC_LOG_SERIOUS, "R_U_THERE_ACK has invalid SPI length (%d)", n->isan_spisize); return STF_FAIL + PAYLOAD_MALFORMED; } if (memcmp(pbs->cur, p1st->st_icookie, COOKIE_SIZE) != 0) { /* RFC states we *SHOULD* check cookies, not MUST. So invalid cookies are technically valid, as per Geoffrey Huang */ loglog(RC_LOG_SERIOUS, "R_U_THERE_ACK has invalid icookie"); } pbs->cur += COOKIE_SIZE; if (memcmp(pbs->cur, p1st->st_rcookie, COOKIE_SIZE) != 0) { /* RFC states we *SHOULD* check cookies, not MUST. So invalid cookies are technically valid, as per Geoffrey Huang */ loglog(RC_LOG_SERIOUS, "R_U_THERE_ACK has invalid rcookie (tolerated)"); } pbs->cur += COOKIE_SIZE; if (pbs_left(pbs) != sizeof(seqno)) { loglog(RC_LOG_SERIOUS, "R_U_THERE_ACK has invalid data length (%d)", (int) pbs_left(pbs)); return STF_FAIL + PAYLOAD_MALFORMED; } seqno = ntohl(*(u_int32_t *)pbs->cur); DBG(DBG_DPD, DBG_log("R_U_THERE_ACK, seqno received: %u expected: %u (state=#%lu)", seqno, p1st->st_dpd_expectseqno, p1st->st_serialno)); if (!p1st->st_dpd_expectseqno && seqno != p1st->st_dpd_expectseqno) { loglog(RC_LOG_SERIOUS, "R_U_THERE_ACK has unexpected sequence number (expected: %u got: %u", seqno, p1st->st_dpd_expectseqno); p1st->st_dpd_expectseqno = 0; /* do not update time stamp, so we'll send a new one sooner */ } else { /* update the time stamp */ p1st->st_last_dpd = tm; } p1st->st_dpd_expectseqno = 0; /* * since there was activity, kill any EVENT_DPD_TIMEOUT that might * be waiting. */ if(p1st->st_dpd_event != NULL && p1st->st_dpd_event->ev_type == EVENT_DPD_TIMEOUT) { delete_dpd_event(p1st); } return STF_IGNORE; }
/** * DPD in Initiator, out Responder * * @param st A state structure (the phase 1 state) * @param n A notification (isakmp_notification) * @param pbs A PB Stream * @return stf_status */ stf_status dpd_inI_outR(struct state *p1st , struct isakmp_notification *const n , pb_stream *pbs) { time_t tm = now(); u_int32_t seqno; if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) { loglog(RC_LOG_SERIOUS, "DPD Error: received R_U_THERE for unestablished ISKAMP SA"); return STF_IGNORE; } if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) { loglog(RC_LOG_SERIOUS, "DPD Error: R_U_THERE has invalid SPI length (%d)", n->isan_spisize); return STF_FAIL + PAYLOAD_MALFORMED; } if (memcmp(pbs->cur, p1st->st_icookie, COOKIE_SIZE) != 0) { /* RFC states we *SHOULD* check cookies, not MUST. So invalid cookies are technically valid, as per Geoffrey Huang */ loglog(RC_LOG_SERIOUS, "DPD Warning: R_U_THERE has invalid icookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (memcmp(pbs->cur, p1st->st_rcookie, COOKIE_SIZE) != 0) { loglog(RC_LOG_SERIOUS, "DPD Warning: R_U_THERE has invalid rcookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (pbs_left(pbs) != sizeof(seqno)) { loglog(RC_LOG_SERIOUS, "DPD Error: R_U_THERE has invalid data length (%d)", (int) pbs_left(pbs)); return STF_FAIL + PAYLOAD_MALFORMED; } seqno = ntohl(*(u_int32_t *)pbs->cur); if (p1st->st_dpd_peerseqno && seqno <= p1st->st_dpd_peerseqno) { loglog(RC_LOG_SERIOUS, "DPD Info: received old or duplicate R_U_THERE"); return STF_IGNORE; } DBG(DBG_DPD, DBG_log("received R_U_THERE seq:%u time:%lu (state=#%lu name=\"%s\")" , seqno , (unsigned long)tm , p1st->st_serialno, p1st->st_connection->name)); p1st->st_dpd_peerseqno = seqno; if (send_isakmp_notification(p1st, R_U_THERE_ACK , pbs->cur, pbs_left(pbs)) != STF_IGNORE) { loglog(RC_LOG_SERIOUS, "DPD Info: could not send R_U_THERE_ACK"); return STF_IGNORE; } /* update the time stamp */ p1st->st_last_dpd = tm; /* * since there was activity, kill any EVENT_DPD_TIMEOUT that might * be waiting. */ if(p1st->st_dpd_event != NULL && p1st->st_dpd_event->ev_type == EVENT_DPD_TIMEOUT) { delete_dpd_event(p1st); } return STF_IGNORE; }
/***************************************************************************************** 函数名称: dpd_inR 功能描述: 处理R_U_THERE_ACK 报文,判断报文长度是否正确以及通过序列号判断 报文是否是自己想要的 输入参数: p1st,n,pbs 输出参数: 无 返 回 值: stf_status ------------------------------------------------------------------------------------------- 最近一次修改记录 : 修改作者: 王之云 修改目的: ACk处理 修改日期: 2012年3月20日 ********************************************************************************************/ stf_status dpd_inR(struct state *p1st, struct isakmp_notification *const n, pb_stream *pbs) { EV_ADD ev_arg; if(!p1st->hidden_variables.st_is_dp_dev) { time_t tm = now(); u_int32_t seqno; char buf_remoteip[20] = {0}; addrtot(&p1st->st_remoteaddr, 0, buf_remoteip, sizeof(buf_remoteip)); if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "recevied R_U_THERE_ACK for unestablished ISKAMP SA"); return STF_FAIL; } if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) { IPSEC_log(IPSEC_LOGLEVEL_ERROR, "<%s> < %s > R_U_THERE_ACK has invalid SPI length (%d)", p1st->st_connection->name, buf_remoteip, n->isan_spisize); return STF_FAIL + PAYLOAD_MALFORMED; } if (memcmp(pbs->cur, p1st->st_icookie, COOKIE_SIZE) != 0) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "R_U_THERE_ACK has invalid icookie"); } pbs->cur += COOKIE_SIZE; if (memcmp(pbs->cur, p1st->st_rcookie, COOKIE_SIZE) != 0) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "R_U_THERE_ACK has invalid rcookie (tolerated)"); } pbs->cur += COOKIE_SIZE; if (pbs_left(pbs) != sizeof(seqno)) { IPSEC_log(IPSEC_LOGLEVEL_ERROR, "<%s> < %s > R_U_THERE_ACK has invalid data length (%d)", p1st->st_connection->name, buf_remoteip, (int) pbs_left(pbs)); return STF_FAIL + PAYLOAD_MALFORMED; } seqno = ntohl(*(u_int32_t *)pbs->cur); DBG(DBG_DPD, DBG_log("R_U_THERE_ACK, seqno received: %u expected: %u (state=#%lu)", seqno, p1st->st_dpd_expectseqno, p1st->st_serialno)); if (seqno != p1st->st_dpd_expectseqno) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "R_U_THERE_ACK has unexpected sequence number (expected: %u got: %u", seqno, p1st->st_dpd_expectseqno); return STF_IGNORE; } else { p1st->st_last_dpd = tm; } } if(p1st->st_dpd_timeout_event != NULL) { ev_arg.u.st = p1st; disable_event(EVENT_DPD_TIMEOUT, &ev_arg); } return STF_IGNORE; }
/***************************************************************************************** 函数名称: dpd_inI_outR 功能描述: 处理R_U_THERE报文,判断R_U_THERE报文长度是否正确以及 通过序列号判断自己想要的报文 输入参数: p1st,n,pbs 输出参数: stf_status 返 回 值: 无 ------------------------------------------------------------------------------------------- 最近一次修改记录 : 修改作者: 王之云 修改目的: dpd_outI处理 修改日期: 2012年3月20日 ********************************************************************************************/ stf_status dpd_inI_outR(struct state *p1st, struct isakmp_notification *const n, pb_stream *pbs) { time_t tm = now(); u_int32_t seqno; char buf_remoteip[20] = {0}; EV_ADD ev_arg; addrtot(&p1st->st_remoteaddr, 0, buf_remoteip, sizeof(buf_remoteip)); if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) { return STF_IGNORE; } if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) { IPSEC_log(IPSEC_LOGLEVEL_ERROR, "<%s> < %s > DPD Error: R_U_THERE has invalid SPI length (%d)", p1st->st_connection->name, buf_remoteip, n->isan_spisize); return STF_FAIL + PAYLOAD_MALFORMED; } if (memcmp(pbs->cur, p1st->st_icookie, COOKIE_SIZE) != 0) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Warning: R_U_THERE has invalid icookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (memcmp(pbs->cur, p1st->st_rcookie, COOKIE_SIZE) != 0) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Warning: R_U_THERE has invalid rcookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (pbs_left(pbs) != sizeof(seqno)) { IPSEC_log(IPSEC_LOGLEVEL_ERROR, "<%s> < %s > DPD Error: R_U_THERE has invalid data length (%d)", p1st->st_connection->name, buf_remoteip, (int) pbs_left(pbs)); return STF_FAIL + PAYLOAD_MALFORMED; } seqno = ntohl(*(u_int32_t *)pbs->cur); if (p1st->st_dpd_peerseqno && seqno <= p1st->st_dpd_peerseqno) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Info: received old or duplicate R_U_THERE"); return STF_IGNORE; } DBG(DBG_DPD, DBG_log("received R_U_THERE seq:%u time:%lu (state=#%lu name=\"%s\")" , seqno , (unsigned long)tm , p1st->st_serialno, p1st->st_connection->name)); p1st->st_dpd_peerseqno = seqno; if (send_isakmp_notification(p1st, R_U_THERE_ACK , pbs->cur, pbs_left(pbs)) != STF_IGNORE) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Info: could not send R_U_THERE_ACK"); return STF_IGNORE; } p1st->st_last_dpd = tm; if(p1st->st_dpd_timeout_event != NULL) { ev_arg.u.st = p1st; disable_event(EVENT_DPD_TIMEOUT, &ev_arg); } return STF_IGNORE; }
err_t parse_redirect_payload(pb_stream *input_pbs, const char *allowed_targets_list, const chunk_t *nonce, ip_address *redirect_ip /* result */) { struct ikev2_redirect_part gw_info; if (!in_struct(&gw_info, &ikev2_redirect_desc, input_pbs, NULL)) return "received deformed REDIRECT payload"; int af; switch (gw_info.gw_identity_type) { case GW_IPV4: af = AF_INET; break; case GW_IPV6: af = AF_INET6; break; case GW_FQDN: af = AF_UNSPEC; break; default: return "bad GW Ident Type"; } /* in_raw() actual GW Identity */ switch (af) { case AF_UNSPEC: { /* * The FQDN string isn't NUL-terminated. * * The length is stored in a byte so it cannot be * larger than 0xFF. * Some helpful compilers moan about this test being always true * so I eliminated it: * passert(gw_info.gw_identity_len <= 0xFF); */ unsigned char gw_str[0xFF]; if (!in_raw(&gw_str, gw_info.gw_identity_len, input_pbs, "GW Identity")) return "error while extracting GW Identity from variable part of IKEv2_REDIRECT Notify payload"; err_t ugh = ttoaddr((char *) gw_str, gw_info.gw_identity_len, AF_UNSPEC, redirect_ip); if (ugh != NULL) return ugh; break; } case AF_INET: case AF_INET6: { if (pbs_left(input_pbs) < gw_info.gw_identity_len) return "variable part of payload is smaller than transfered GW Identity Length"; /* parse address directly to redirect_ip */ err_t ugh = initaddr(input_pbs->cur, gw_info.gw_identity_len, af, redirect_ip); if (ugh != NULL) return ugh; DBG(DBG_PARSING, { ip_address_buf b; DBG_log(" GW Identity IP: %s", ipstr(redirect_ip, &b)); }); input_pbs->cur += gw_info.gw_identity_len; break; } }
bool in_struct(void *struct_ptr, struct_desc *sd , pb_stream *ins, pb_stream *obj_pbs) { err_t ugh = NULL; u_int8_t *cur = ins->cur; if (ins->roof - cur < (ptrdiff_t)sd->size) { ugh = builddiag("not enough room in input packet for %s", sd->name); } else { u_int8_t *roof = cur + sd->size; /* may be changed by a length field */ u_int8_t *outp = struct_ptr; bool immediate = FALSE; field_desc *fp; for (fp = sd->fields; ugh == NULL; fp++) { size_t i = fp->size; passert(ins->roof - cur >= (ptrdiff_t)i); passert(cur - ins->cur <= (ptrdiff_t)(sd->size - i)); passert(outp - (cur - ins->cur) == struct_ptr); #if 0 DBG(DBG_PARSING, DBG_log("%d %s" , (int) (cur - ins->cur), fp->name == NULL? "" : fp->name)); #endif switch (fp->field_type) { case ft_mbz: /* must be zero */ for (; i != 0; i--) { if (*cur++ != 0) { ugh = builddiag("byte %d of %s must be zero, but is not" , (int) (cur - ins->cur), sd->name); break; } *outp++ = '\0'; /* probably redundant */ } break; case ft_nat: /* natural number (may be 0) */ case ft_len: /* length of this struct and any following crud */ case ft_lv: /* length/value field of attribute */ case ft_enum: /* value from an enumeration */ case ft_loose_enum: /* value from an enumeration with only some names known */ case ft_af_enum: /* Attribute Format + value from an enumeration */ case ft_set: /* bits representing set */ { u_int32_t n = 0; for (; i != 0; i--) n = (n << BITS_PER_BYTE) | *cur++; switch (fp->field_type) { case ft_len: /* length of this struct and any following crud */ case ft_lv: /* length/value field of attribute */ { u_int32_t len = fp->field_type == ft_len? n : immediate? sd->size : n + sd->size; if (len < sd->size) { ugh = builddiag("%s of %s is smaller than minimum" , fp->name, sd->name); } else if (pbs_left(ins) < len) { ugh = builddiag("%s of %s is larger than can fit" , fp->name, sd->name); } else { roof = ins->cur + len; } break; } case ft_af_enum: /* Attribute Format + value from an enumeration */ if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV) immediate = TRUE; /* FALL THROUGH */ case ft_enum: /* value from an enumeration */ if (enum_name(fp->desc, n) == NULL) { ugh = builddiag("%s of %s has an unknown value: %lu" , fp->name, sd->name, (unsigned long)n); } /* FALL THROUGH */ case ft_loose_enum: /* value from an enumeration with only some names known */ break; case ft_set: /* bits representing set */ if (!testset(fp->desc, n)) { ugh = builddiag("bitset %s of %s has unknown member(s): %s" , fp->name, sd->name, bitnamesof(fp->desc, n)); } break; default: break; } i = fp->size; switch (i) { case 8/BITS_PER_BYTE: *(u_int8_t *)outp = n; break; case 16/BITS_PER_BYTE: *(u_int16_t *)outp = n; break; case 32/BITS_PER_BYTE: *(u_int32_t *)outp = n; break; default: impossible(); } outp += i; break; } case ft_raw: /* bytes to be left in network-order */ for (; i != 0; i--) { *outp++ = *cur++; } break; case ft_end: /* end of field list */ passert(cur == ins->cur + sd->size); if (obj_pbs != NULL) { init_pbs(obj_pbs, ins->cur, roof - ins->cur, sd->name); obj_pbs->container = ins; obj_pbs->desc = sd; obj_pbs->cur = cur; } ins->cur = roof; DBG(DBG_PARSING , DBG_prefix_print_struct(ins, "parse ", struct_ptr, sd, TRUE)); return TRUE; default: impossible(); } } } /* some failure got us here: report it */ loglog(RC_LOG_SERIOUS, ugh); return FALSE; }
int pbs_left_get(pb_stream *pbs) { return pbs_left(pbs); }
void nat_traversal_natd_lookup(struct msg_digest *md) { char hash[MAX_DIGEST_LEN]; struct payload_digest *p; struct state *st = md->st; int i; if (!st || !md->iface || !st->st_oakley.hasher) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d", __FILE__, __LINE__); return; } /** Count NAT-D **/ for (p = md->chain[ISAKMP_NEXT_NATD_R], i=0; p != NULL; p = p->next, i++); /** * We need at least 2 NAT-D (1 for us, many for peer) */ if (i < 2) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: Only %d NAT-D - Aborting NAT-Traversal negotiation", i); st->nat_traversal = 0; return; } #ifdef NAT_D_DEBUG else loglog(RC_LOG_SERIOUS, "NAT-Traversal: %d NAT-D - Continuing NAT-Traversal negotiation", i); #endif /** * First one with my IP & port */ p = md->chain[ISAKMP_NEXT_NATD_R]; _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, st->st_rcookie, &(md->iface->addr), ntohs(st->st_connection->this.host_port)); if (!( (pbs_left(&p->pbs) == st->st_oakley.hasher->hash_digest_len) && (memcmp(p->pbs.cur, hash, st->st_oakley.hasher->hash_digest_len)==0) )) { #ifdef NAT_D_DEBUG DBG_log("NAT_TRAVERSAL_NAT_BHND_ME"); DBG_dump("expected NAT-D:", hash, st->st_oakley.hasher->hash_digest_len); DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs)); #endif st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME); } /** * The others with sender IP & port */ _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, st->st_rcookie, &(md->sender), ntohs(md->sender_port)); for (p = p->next, i=0 ; p != NULL; p = p->next) { if ( (pbs_left(&p->pbs) == st->st_oakley.hasher->hash_digest_len) && (memcmp(p->pbs.cur, hash, st->st_oakley.hasher->hash_digest_len)==0) ) { i++; } } if (!i) { #ifdef NAT_D_DEBUG DBG_log("NAT_TRAVERSAL_NAT_BHND_PEER"); DBG_dump("expected NAT-D:", hash, st->st_oakley.hasher->hash_digest_len); p = md->chain[ISAKMP_NEXT_NATD_R]; for (p = p->next, i=0 ; p != NULL; p = p->next) { DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs)); } #endif st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); } #ifdef FORCE_NAT_TRAVERSAL st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME); #endif }