Beispiel #1
0
/* Generate and match a NAT-D hash against the NAT-D payload (pl.) data.  */
static int
nat_t_match_nat_d_payload(struct message *msg, struct sockaddr *sa)
{
	struct payload *p;
	u_int8_t *hbuf;
	size_t	 hbuflen;
	int	 found = 0;

	/*
	 * If there are no NAT-D payloads in the message, return "found"
	 * as this will avoid NAT-T (see nat_t_exchange_check_nat_d()).
	 */
	if ((p = payload_first(msg, ISAKMP_PAYLOAD_NAT_D_DRAFT)) == NULL &&
	    (p = payload_first(msg, ISAKMP_PAYLOAD_NAT_D)) == NULL)
		return 1;

	hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen);
	if (!hbuf)
		return 0;

	for (; p; p = TAILQ_NEXT(p, link)) {
		if (GET_ISAKMP_GEN_LENGTH (p->p) !=
		    hbuflen + ISAKMP_NAT_D_DATA_OFF)
			continue;

		if (memcmp(p->p + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen) == 0) {
			found++;
			break;
		}
	}
	free(hbuf);
	return found;
}
Beispiel #2
0
/*
 * Check an incoming message for NAT-T capability markers.
 */
void
nat_t_check_vendor_payload(struct message *msg, struct payload *p)
{
	u_int8_t *pbuf = p->p;
	size_t	  vlen;
	int	  i;

	if (disable_nat_t)
		return;

	vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ;

	for (i = 0; i < NUMNATTCAP; i++) {
		if (vlen != isakmp_nat_t_cap[i].hashsize) {
			continue;
		}
		if (memcmp(isakmp_nat_t_cap[i].hash, pbuf + ISAKMP_GEN_SZ,
		    vlen) == 0) {
			/* This peer is NAT-T capable.  */
			msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_CAP_PEER;
			msg->exchange->flags |= isakmp_nat_t_cap[i].flags;
			LOG_DBG((LOG_EXCHANGE, 10,
			    "nat_t_check_vendor_payload: "
			    "NAT-T capable peer detected"));
			p->flags |= PL_MARK;
		}
	}

	return;
}
Beispiel #3
0
/*
 * Accept a set of transforms offered by the initiator and chose one we can
 * handle.
 */
int
ike_phase_1_responder_recv_SA (struct message *msg)
{
    struct exchange *exchange = msg->exchange;
    struct sa *sa = TAILQ_FIRST (&exchange->sa_list);
    struct ipsec_sa *isa = sa->data;
    struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]);
    struct payload *prop = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL]);
    struct ipsec_exch *ie = exchange->data;

    /* Mark the SA as handled.  */
    sa_p->flags |= PL_MARK;

    /* IKE requires that only one SA with only one proposal exists.  */
    if (TAILQ_NEXT (sa_p, link) || TAILQ_NEXT (prop, link))
    {
        log_print ("ike_phase_1_responder_recv_SA: "
                   "multiple SA or proposal payloads in phase 1");
        /* XXX Is there a better notification type?  */
        message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
        return -1;
    }

    /* Chose a transform from the SA.  */
    if (message_negotiate_sa (msg, ike_phase_1_validate_prop)
            || !TAILQ_FIRST (&sa->protos))
        return -1;

    /* XXX Move into message_negotiate_sa?  */
    ipsec_decode_transform (msg, sa, TAILQ_FIRST (&sa->protos),
                            TAILQ_FIRST (&sa->protos)->chosen->p);

    ie->group = group_get (isa->group_desc);

    /*
     * Check that the mandatory attributes: encryption, hash, authentication
     * method and Diffie-Hellman group description, has been supplied.
     */
    if (!exchange->crypto || !ie->hash || !ie->ike_auth || !ie->group)
    {
        message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
        return -1;
    }

    /* Save the body for later hash computation.  */
    ie->sa_i_b_len = GET_ISAKMP_GEN_LENGTH (sa_p->p) - ISAKMP_GEN_SZ;
    ie->sa_i_b = malloc (ie->sa_i_b_len);
    if (!ie->sa_i_b)
    {
        /* XXX How to notify peer?  */
        log_error ("ike_phase_1_responder_recv_SA: malloc (%lu) failed",
                   (unsigned long)ie->sa_i_b_len);
        return -1;
    }
    memcpy (ie->sa_i_b, sa_p->p + ISAKMP_GEN_SZ, ie->sa_i_b_len);

    return 0;
}
int
cfg_verify_hash(struct message *msg)
{
	struct payload *hashp = payload_first(msg, ISAKMP_PAYLOAD_HASH);
	struct ipsec_sa *isa = msg->isakmp_sa->data;
	struct prf     *prf;
	u_int8_t       *hash, *comp_hash;
	size_t          hash_len;

	if (!hashp) {
		log_print("cfg_verify_hash: phase 2 message missing HASH");
		message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
		    0, 1, 0);
		return -1;
	}
	hash = hashp->p;
	hash_len = GET_ISAKMP_GEN_LENGTH(hash);
	comp_hash = malloc(hash_len - ISAKMP_GEN_SZ);
	if (!comp_hash) {
		log_error("cfg_verify_hash: malloc (%lu) failed",
		    (unsigned long)hash_len - ISAKMP_GEN_SZ);
		return -1;
	}
	/* Verify hash.  */
	prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a,
	    isa->skeyid_len);
	if (!prf) {
		free(comp_hash);
		return -1;
	}
	prf->Init(prf->prfctx);
	prf->Update(prf->prfctx, msg->exchange->message_id,
	    ISAKMP_HDR_MESSAGE_ID_LEN);
	prf->Update(prf->prfctx, hash + hash_len,
	    msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len);
	prf->Final(comp_hash, prf->prfctx);
	prf_free(prf);

	if (memcmp(hash + ISAKMP_GEN_SZ, comp_hash, hash_len - ISAKMP_GEN_SZ)
	    != 0) {
		message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
		    0, 1, 0);
		free(comp_hash);
		return -1;
	}
	free(comp_hash);

	/* Mark the HASH as handled.  */
	hashp->flags |= PL_MARK;

	/* Mark message authenticated. */
	msg->flags |= MSG_AUTHENTICATED;

	return 0;
}
Beispiel #5
0
static int
pre_shared_decode_hash (struct message *msg)
{
  struct exchange *exchange = msg->exchange;
  struct ipsec_exch *ie = exchange->data;
  struct payload *payload;
  size_t hashsize = ie->hash->hashsize;
  char header[80];
  int initiator = exchange->initiator;
  u_int8_t **hash_p;

  /* Choose the right fields to fill-in.  */
  hash_p = initiator ? &ie->hash_r : &ie->hash_i;

  payload = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]);
  if (!payload)
    {
      log_print ("pre_shared_decode_hash: no HASH payload found");
      return -1;
    }

  /* Check that the hash is of the correct size.  */
  if (GET_ISAKMP_GEN_LENGTH (payload->p) - ISAKMP_GEN_SZ != hashsize)
    return -1;

  /* XXX Need this hash be in the SA?  */
  *hash_p = malloc (hashsize);
  if (!*hash_p)
    {
      log_error ("pre_shared_decode_hash: malloc (%lu) failed",
	(unsigned long)hashsize);
      return -1;
    }

  memcpy (*hash_p, payload->p + ISAKMP_HASH_DATA_OFF, hashsize);
  snprintf (header, sizeof header, "pre_shared_decode_hash: HASH_%c",
	    initiator ? 'R' : 'I');
  LOG_DBG_BUF ((LOG_MISC, 80, header, *hash_p, hashsize));

  payload->flags |= PL_MARK;

  return 0;
}
Beispiel #6
0
/*
 * This function is used to validate the returned proposal (protection suite)
 * we get from the responder against a proposal we sent. Only run as initiator.
 * We return 0 if a match is found (in any transform of this proposal), 1
 * otherwise. Also see note in sa_add_transform() below.
 */
static int
sa_validate_proto_xf(struct proto *match, struct payload *xf, int phase)
{
	struct attr_validation_state *avs;
	struct proto_attr *pa;
	int             found = 0;
	size_t          i;
	u_int8_t        xf_id;

	if (!match->xf_cnt)
		return 0;

	if (match->proto != GET_ISAKMP_PROP_PROTO(xf->context->p)) {
		LOG_DBG((LOG_SA, 70, "sa_validate_proto_xf: proto %p (#%d) "
		    "protocol mismatch", match, match->no));
		return 1;
	}
	avs = (struct attr_validation_state *)calloc(1, sizeof *avs);
	if (!avs) {
		log_error("sa_validate_proto_xf: calloc (1, %lu)",
		    (unsigned long)sizeof *avs);
		return 1;
	}
	avs->phase = phase;

	/* Load the "proposal candidate" attribute set.  */
	(void)attribute_map(xf->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
	    GET_ISAKMP_GEN_LENGTH(xf->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
	    sa_validate_xf_attrs, avs);
	xf_id = GET_ISAKMP_TRANSFORM_ID(xf->p);

	/* Check against the transforms we suggested.  */
	avs->mode++;
	for (pa = TAILQ_FIRST(&match->xfs); pa && !found;
	    pa = TAILQ_NEXT(pa, next)) {
		if (xf_id != GET_ISAKMP_TRANSFORM_ID(pa->attrs))
			continue;

		bzero(avs->checked, sizeof avs->checked);
		if (attribute_map(pa->attrs + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
		    pa->len - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
		    sa_validate_xf_attrs, avs) == 0)
			found++;

		LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: attr_map "
		    "xf %p proto %p pa %p found %d", xf, match, pa, found));

		if (!found)
			continue;

		/*
		 * Require all attributes present and checked.  XXX perhaps
		 * not?
		 */
		for (i = 0; i < sizeof avs->checked; i++)
			if (avs->attrp[i] && !avs->checked[i])
				found = 0;

		LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: req_attr "
		    "xf %p proto %p pa %p found %d", xf, match, pa, found));
	}
	free(avs);
	return found ? 0 : 1;
}
/*
 * As "the server", this starts REQ/REPLY (initiated by the client).
 * As "the client", this starts SET/ACK (initiated by the server).
 */
static int
cfg_responder_recv_ATTR(struct message *msg)
{
	struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE);
	struct ipsec_exch *ie = msg->exchange->data;
	struct sa      *isakmp_sa = msg->isakmp_sa;
	struct isakmp_cfg_attr *attr;
	struct sockaddr *sa;
	char           *addr;

	if (msg->exchange->phase == 2)
		if (cfg_verify_hash(msg))
			return -1;

	ie->cfg_id = GET_ISAKMP_ATTRIBUTE_ID(attrp->p);
	ie->cfg_type = attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF];

	switch (ie->cfg_type) {
	case ISAKMP_CFG_REQUEST:
	case ISAKMP_CFG_SET:
		break;

	default:
		message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
		log_print("cfg_responder_recv_ATTR: "
		    "unexpected configuration message type %d", ie->cfg_type);
		return -1;
	}

	attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF,
	    GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
	    cfg_decode_attribute, ie);

	switch (ie->cfg_type) {
	case ISAKMP_CFG_REQUEST:
		/* We're done.  */
		break;

	case ISAKMP_CFG_SET: {
			/* SET/ACK -- Client side (SET from server) */
			const char     *uk_addr = "<unknown>";

			msg->transport->vtbl->get_dst(isakmp_sa->transport,
			    &sa);
			if (sockaddr2text(sa, &addr, 0) < 0)
				addr = (char *) uk_addr;

			for (attr = LIST_FIRST(&ie->attrs); attr;
			    attr = LIST_NEXT(attr, link))
				LOG_DBG((LOG_NEGOTIATION, 50,
				    "cfg_responder_recv_ATTR: "
				    "server %s asks us to SET attribute %s",
				    addr, constant_name(isakmp_cfg_attr_cst,
					attr->type)));

			/*
			 * XXX Here's the place to add code to walk through
			 * XXX each attribute and send them along to dhclient
			 * XXX or whatever. Each attribute that we act upon
			 * XXX (such as setting a netmask), should be marked
			 * XXX like this for us to send the proper ACK
			 * XXX response: attr->attr_used++;
			 */

			if (addr != uk_addr)
				free(addr);
		}
		break;

	default:
		break;
	}

	attrp->flags |= PL_MARK;
	return 0;
}
/*
 * As "the server", this ends SET/ACK.
 * As "the client", this ends REQ/REPLY.
 */
static int
cfg_initiator_recv_ATTR(struct message *msg)
{
	struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE);
	struct ipsec_exch *ie = msg->exchange->data;
	struct sa      *isakmp_sa = msg->isakmp_sa;
	struct isakmp_cfg_attr *attr;
	struct sockaddr *sa;
	const char     *uk_addr = "<unknown>";
	char           *addr;

	if (msg->exchange->phase == 2)
		if (cfg_verify_hash(msg))
			return -1;

	/* Sanity.  */
	if (ie->cfg_id != GET_ISAKMP_ATTRIBUTE_ID(attrp->p)) {
		log_print("cfg_initiator_recv_ATTR: "
		    "cfg packet ID does not match!");
		message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
		return -1;
	}
	switch (attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]) {
	case ISAKMP_CFG_ACK:
		if (ie->cfg_type != ISAKMP_CFG_SET) {
			log_print("cfg_initiator_recv_ATTR: "
			    "bad packet type ACK");
			message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
			    0, 1, 0);
			return -1;
		}
		break;
	case ISAKMP_CFG_REPLY:
		if (ie->cfg_type != ISAKMP_CFG_REQUEST) {
			log_print("cfg_initiator_recv_ATTR: "
			    "bad packet type REPLY");
			message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
			    0, 1, 0);
			return -1;
		}
		break;

	default:
		log_print("cfg_initiator_recv_ATTR: unexpected configuration "
		    "message type %d", attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]);
		message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
		return -1;
	}

	attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF,
	    GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
	    cfg_decode_attribute, ie);

	switch (ie->cfg_type) {
	case ISAKMP_CFG_ACK: {
			/* SET/ACK -- Server side (ACK from client) */
			msg->transport->vtbl->get_src(isakmp_sa->transport,
			    &sa);
			if (sockaddr2text(sa, &addr, 0) < 0)
				addr = (char *) uk_addr;

			for (attr = LIST_FIRST(&ie->attrs); attr;
			    attr = LIST_NEXT(attr, link))
				LOG_DBG((LOG_NEGOTIATION, 50,
				    "cfg_initiator_recv_ATTR: "
				    "client %s ACKs attribute %s", addr,
				    constant_name(isakmp_cfg_attr_cst,
					attr->type)));

			if (addr != uk_addr)
				free(addr);
		}
		break;

	case ISAKMP_CFG_REPLY: {
			/*
			 * REQ/REPLY: effect attributes we've gotten
			 * responses on.
			 */
			msg->transport->vtbl->get_src(isakmp_sa->transport,
			    &sa);
			if (sockaddr2text(sa, &addr, 0) < 0)
				addr = (char *) uk_addr;

			for (attr = LIST_FIRST(&ie->attrs); attr;
			    attr = LIST_NEXT(attr, link))
				LOG_DBG((LOG_NEGOTIATION, 50,
				    "cfg_initiator_recv_ATTR: "
				    "server %s replied with attribute %s",
				    addr, constant_name(isakmp_cfg_attr_cst,
					attr->type)));

			if (addr != uk_addr)
				free(addr);
		}
		break;

	default:
		break;
	}

	attrp->flags |= PL_MARK;
	return 0;
}
Beispiel #9
0
/* Receive ID.  */
int
ike_phase_1_recv_ID (struct message *msg)
{
    struct exchange *exchange = msg->exchange;
    struct payload *payload;
    char header[80], *rs = 0, *rid = 0, *p;
    int initiator = exchange->initiator;
    u_int8_t **id, id_type;
    size_t *id_len, sz;
    struct sockaddr *sa;

    payload = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_ID]);

    if (exchange->name)
        rs = conf_get_str (exchange->name, "Remote-ID");

    if (rs)
    {
        sz = ipsec_id_size (rs, &id_type);
        if (sz == -1)
        {
            log_print ("ike_phase_1_recv_ID: could not handle specified "
                       "Remote-ID [%s]", rs);
            return -1;
        }

        rid = malloc (sz);
        if (!rid)
        {
            log_error ("ike_phase_1_recv_ID: malloc (%lu) failed",
                       (unsigned long)sz);
            return -1;
        }

        switch (id_type)
        {
        case IPSEC_ID_IPV4_ADDR:
        case IPSEC_ID_IPV6_ADDR:
            p = conf_get_str (rs, "Address");
            if (!p)
            {
                log_print ("ike_phase_1_recv_ID: "
                           "failed to get Address in Remote-ID section [%s]",
                           rs);
                free (rid);
                return -1;
            }

            if (text2sockaddr (p, 0, &sa) == -1)
            {
                log_print ("ike_phase_1_recv_ID: failed to parse address %s", p);
                free (rid);
                return -1;
            }

            if ((id_type == IPSEC_ID_IPV4_ADDR && sa->sa_family != AF_INET)
                    || (id_type == IPSEC_ID_IPV6_ADDR && sa->sa_family != AF_INET6))
            {
                log_print ("ike_phase_1_recv_ID: "
                           "address %s not of expected family", p);
                free (rid);
                free (sa);
                return -1;
            }

            memcpy (rid, sockaddr_addrdata (sa), sockaddr_addrlen (sa));
            free (sa);
            break;

        case IPSEC_ID_FQDN:
        case IPSEC_ID_USER_FQDN:
        case IPSEC_ID_KEY_ID:
            p = conf_get_str (rs, "Name");
            if (!p)
            {
                log_print ("ike_phase_1_recv_ID: "
                           "failed to get Name in Remote-ID section [%s]", rs);
                free (rid);
                return -1;
            }

            memcpy (rid, p, sz);
            break;

        default:
            log_print ("ike_phase_1_recv_ID: unsupported ID type %d", id_type);
            free (rid);
            return -1;
        }

        /* Compare expected/desired and received remote ID */
        if (bcmp (rid, payload->p + ISAKMP_ID_DATA_OFF, sz))
        {
            free (rid);
            log_print ("ike_phase_1_recv_ID: "
                       "received remote ID other than expected %s", p);
            return -1;
        }

        free (rid);
    }

    /* Choose the right fields to fill in */
    id = initiator ? &exchange->id_r : &exchange->id_i;
    id_len = initiator ? &exchange->id_r_len : &exchange->id_i_len;

    *id_len = GET_ISAKMP_GEN_LENGTH (payload->p) - ISAKMP_GEN_SZ;
    *id = malloc (*id_len);
    if (!*id)
    {
        log_error ("ike_phase_1_recv_ID: malloc (%lu) failed",
                   (unsigned long)*id_len);
        return -1;
    }
    memcpy (*id, payload->p + ISAKMP_GEN_SZ, *id_len);
    snprintf (header, sizeof header, "ike_phase_1_recv_ID: %s",
              constant_name (ipsec_id_cst, GET_ISAKMP_ID_TYPE (payload->p)));
    LOG_DBG_BUF ((LOG_NEGOTIATION, 40, header, payload->p + ISAKMP_ID_DATA_OFF,
                  *id_len + ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF));
    payload->flags |= PL_MARK;

    return 0;
}
Beispiel #10
0
/* Validate a proposal inside SA according to EXCHANGE's policy.  */
static int
ike_phase_1_validate_prop (struct exchange *exchange, struct sa *sa,
                           struct sa *isakmp_sa)
{
    struct conf_list *conf, *tags;
    struct conf_list_node *xf, *tag;
    struct proto *proto;
    struct validation_state vs;
    struct attr_node *node, *next_node;

    /* Get the list of transforms.  */
    conf = conf_get_list (exchange->policy, "Transforms");
    if (!conf)
        return 0;

    for (xf = TAILQ_FIRST (&conf->fields); xf; xf = TAILQ_NEXT (xf, link))
    {
        for (proto = TAILQ_FIRST (&sa->protos); proto;
                proto = TAILQ_NEXT (proto, link))
        {
            /* Mark all attributes in our policy as unseen.  */
            LIST_INIT (&vs.attrs);
            vs.xf = xf;
            vs.life = 0;
            if (attribute_map (proto->chosen->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
                               GET_ISAKMP_GEN_LENGTH (proto->chosen->p)
                               - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
                               attribute_unacceptable, &vs))
                goto try_next;

            /* Sweep over unseen tags in this section.  */
            tags = conf_get_tag_list (xf->field);
            if (tags)
            {
                for (tag = TAILQ_FIRST (&tags->fields); tag;
                        tag = TAILQ_NEXT (tag, link))
                    /*
                     * XXX Should we care about attributes we have, they do not
                     * provide?
                     */
                    for (node = LIST_FIRST (&vs.attrs); node;
                            node = next_node)
                    {
                        next_node = LIST_NEXT (node, link);
                        if (node->type
                                == constant_value (ike_attr_cst, tag->field))
                        {
                            LIST_REMOVE (node, link);
                            free (node);
                        }
                    }
                conf_free_list (tags);
            }

            /* Are there leftover tags in this section?  */
            node = LIST_FIRST (&vs.attrs);
            if (node)
                goto try_next;
        }

        /* All protocols were OK, we succeeded.  */
        LOG_DBG ((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: success"));
        conf_free_list (conf);
        if (vs.life)
            free (vs.life);
        return 1;

try_next:
        /* Are there leftover tags in this section?  */
        node = LIST_FIRST (&vs.attrs);
        while (node)
        {
            LIST_REMOVE (node, link);
            free (node);
            node = LIST_FIRST (&vs.attrs);
        }
        if (vs.life)
            free (vs.life);
    }

    LOG_DBG ((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: failure"));
    conf_free_list (conf);
    return 0;
}
Beispiel #11
0
/* Decrypt the HASH in SIG, we already need a parsed ID payload.  */
static int
rsa_sig_decode_hash (struct message *msg)
{
  struct cert_handler *handler;
  struct exchange *exchange = msg->exchange;
  struct ipsec_exch *ie = exchange->data;
  struct payload *p;
  void *cert = 0;
  u_int8_t *rawcert = 0;
  u_int32_t rawcertlen;
  RSA *key = 0;
  size_t hashsize = ie->hash->hashsize;
  char header[80];
  int len;
  int initiator = exchange->initiator;
  u_int8_t **hash_p, **id_cert, *id;
  u_int32_t *id_cert_len;
  size_t id_len;
  int found = 0, n, i, id_found;
#if defined (USE_DNSSEC)
  u_int8_t *rawkey = 0;
  u_int32_t rawkeylen;
#endif

  /* Choose the right fields to fill-in.  */
  hash_p = initiator ? &ie->hash_r : &ie->hash_i;
  id = initiator ? exchange->id_r : exchange->id_i;
  id_len = initiator ? exchange->id_r_len : exchange->id_i_len;

  if (!id || id_len == 0)
    {
      log_print ("rsa_sig_decode_hash: ID is missing");
      return -1;
    }

  /*
   * XXX Assume we should use the same kind of certification as the remote...
   * moreover, just use the first CERT payload to decide what to use.
   */
  p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT]);
  if (!p)
    handler = cert_get (ISAKMP_CERTENC_KEYNOTE);
  else
    handler = cert_get (GET_ISAKMP_CERT_ENCODING (p->p));
  if (!handler)
    {
      log_print ("rsa_sig_decode_hash: cert_get (%d) failed",
		 p ? GET_ISAKMP_CERT_ENCODING (p->p) : -1);
      return -1;
    }

#if defined (USE_POLICY) && defined (USE_KEYNOTE)
  /*
   * We need the policy session initialized now, so we can add
   * credentials etc.
   */
  exchange->policy_id = kn_init ();
  if (exchange->policy_id == -1)
    {
      log_print ("rsa_sig_decode_hash: failed to initialize policy session");
      return -1;
    }
#endif /* USE_POLICY || USE_KEYNOTE */

  /* Obtain a certificate from our certificate storage.  */
  if (handler->cert_obtain (id, id_len, 0, &rawcert, &rawcertlen))
    {
      if (handler->id == ISAKMP_CERTENC_X509_SIG)
        {
	  cert = handler->cert_get (rawcert, rawcertlen);
	  if (!cert)
	    LOG_DBG ((LOG_CRYPTO, 50,
		      "rsa_sig_decode_hash: certificate malformed"));
	  else
	    {
	      if (!handler->cert_get_key (cert, &key))
	        {
		  log_print ("rsa_sig_decode_hash: "
			     "decoding certificate failed");
		  handler->cert_free (cert);
		}
	      else
	        {
		  found++;
		  LOG_DBG ((LOG_CRYPTO, 40,
			    "rsa_sig_decode_hash: using cert of type %d",
			    handler->id));
		  exchange->recv_cert = cert;
		  exchange->recv_certtype = handler->id;
#if defined (USE_POLICY)
		  x509_generate_kn (exchange->policy_id, cert);
#endif /* USE_POLICY */
		}
	    }
	}
      else if (handler->id == ISAKMP_CERTENC_KEYNOTE)
	handler->cert_insert (exchange->policy_id, rawcert);
      free (rawcert);
    }

  /*
   * Walk over potential CERT payloads in this message.
   * XXX I believe this is the wrong spot for this.  CERTs can appear
   * anytime.
   */
  for (p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT]); p;
       p = TAILQ_NEXT (p, link))
    {
      p->flags |= PL_MARK;

      /* When we have found a key, just walk over the rest, marking them.  */
      if (found)
	continue;

      handler = cert_get (GET_ISAKMP_CERT_ENCODING (p->p));
      if (!handler)
	{
	  LOG_DBG ((LOG_MISC, 30,
		    "rsa_sig_decode_hash: no handler for %s CERT encoding",
		    constant_name (isakmp_certenc_cst,
				   GET_ISAKMP_CERT_ENCODING (p->p))));
	  continue;
	}

      cert = handler->cert_get (p->p + ISAKMP_CERT_DATA_OFF,
				GET_ISAKMP_GEN_LENGTH (p->p)
				- ISAKMP_CERT_DATA_OFF);
      if (!cert)
	{
	  log_print ("rsa_sig_decode_hash: can not get data from CERT");
	  continue;
	}

      if (!handler->cert_validate (cert))
	{
	  handler->cert_free (cert);
	  log_print ("rsa_sig_decode_hash: received CERT can't be validated");
	  continue;
	}

      if (GET_ISAKMP_CERT_ENCODING (p->p) == ISAKMP_CERTENC_X509_SIG)
        {
	  if (!handler->cert_get_subjects (cert, &n, &id_cert, &id_cert_len))
	    {
	      handler->cert_free (cert);
	      log_print ("rsa_sig_decode_hash: can not get subject from CERT");
	      continue;
	    }

	  id_found = 0;
	  for (i = 0; i < n; i++)
	    if (id_cert_len[i] == id_len
		&& id[0] == id_cert[i][0]
		&& memcmp (id + 4, id_cert[i] + 4, id_len - 4) == 0)
	      {
		id_found++;
		break;
	      }
	  if (!id_found)
	    {
	      handler->cert_free (cert);
	      log_print ("rsa_sig_decode_hash: no CERT subject match the ID");
	      free (id_cert);
	      continue;
	    }

	  cert_free_subjects (n, id_cert, id_cert_len);
	}

      if (!handler->cert_get_key (cert, &key))
	{
	  handler->cert_free (cert);
	  log_print ("rsa_sig_decode_hash: decoding payload CERT failed");
	  continue;
	}

      /* We validated the cert, cache it for later use.  */
      handler->cert_insert (exchange->policy_id, cert);

      exchange->recv_cert = cert;
      exchange->recv_certtype = GET_ISAKMP_CERT_ENCODING (p->p);

#if defined (USE_POLICY) && defined (USE_KEYNOTE)
      if (exchange->recv_certtype == ISAKMP_CERTENC_KEYNOTE)
        {
	  struct keynote_deckey dc;
	  char *pp;
	  int dclen;

	  dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA;
	  dc.dec_key = key;

	  pp = kn_encode_key (&dc, INTERNAL_ENC_PKCS1, ENCODING_HEX,
			      KEYNOTE_PUBLIC_KEY);
	  if (pp == NULL)
	    {
	      kn_free_key (&dc);
	      log_print ("rsa_sig_decode_hash: failed to ASCII-encode key");
	      return -1;
	    }

	  dclen = strlen (pp) + sizeof "rsa-hex:";
	  exchange->keynote_key = calloc (dclen, sizeof (char));
	  if (!exchange->keynote_key)
	    {
	      free (pp);
	      kn_free_key (&dc);
	      log_print ("rsa_sig_decode_hash: failed to allocate %d bytes",
			 dclen);
	      return -1;
	    }

	  snprintf (exchange->keynote_key, dclen, "rsa-hex:%s", pp);
	  free (pp);
	}
#endif

      found++;
    }

#if defined (USE_DNSSEC)
  /* If no certificate provided a key, try to find a validated DNSSEC KEY.  */
  if (!found)
    {
      rawkey = dns_get_key (IKE_AUTH_RSA_SIG, msg, &rawkeylen);

      /* We need to convert 'void *rawkey' into 'RSA *key'.  */
      if (dns_RSA_dns_to_x509 (rawkey, rawkeylen, &key) == 0)
	found++;
      else
	log_print ("rsa_sig_decode_hash: KEY to RSA key conversion failed");

      if (rawkey)
	free (rawkey);
    }
#endif /* USE_DNSSEC */

#if defined (USE_RAWKEY)
  /* If we still have not found a key, try to read it from a file. */
  if (!found)
    if (get_raw_key_from_file (IKE_AUTH_RSA_SIG, id, id_len, &key) != -1)
      found++;
#endif

  if (!found)
    {
      log_print ("rsa_sig_decode_hash: no public key found");
      return -1;
    }

  p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG]);
  if (!p)
    {
      log_print ("rsa_sig_decode_hash: missing signature payload");
      RSA_free (key);
      return -1;
    }

  /* Check that the sig is of the correct size.  */
  len = GET_ISAKMP_GEN_LENGTH (p->p) - ISAKMP_SIG_SZ;
  if (len != RSA_size (key))
    {
      RSA_free (key);
      log_print ("rsa_sig_decode_hash: "
		 "SIG payload length does not match public key");
      return -1;
    }

  *hash_p = malloc (len);
  if (!*hash_p)
    {
      RSA_free (key);
      log_error ("rsa_sig_decode_hash: malloc (%d) failed", len);
      return -1;
    }

  len = RSA_public_decrypt (len, p->p + ISAKMP_SIG_DATA_OFF, *hash_p, key,
			    RSA_PKCS1_PADDING);
  if (len == -1)
    {
      RSA_free (key);
      log_print ("rsa_sig_decode_hash: RSA_public_decrypt () failed");
      return -1;
    }

  /* Store key for later use */
  exchange->recv_key = key;
  exchange->recv_keytype = ISAKMP_KEY_RSA;

  if (len != hashsize)
    {
      free (*hash_p);
      *hash_p = 0;
      log_print ("rsa_sig_decode_hash: len %lu != hashsize %lu",
	(unsigned long)len, (unsigned long)hashsize);
      return -1;
    }

  snprintf (header, sizeof header, "rsa_sig_decode_hash: HASH_%c",
	    initiator ? 'R' : 'I');
  LOG_DBG_BUF ((LOG_MISC, 80, header, *hash_p, hashsize));

  p->flags |= PL_MARK;

  return 0;
}