Exemple #1
0
static int
isakmp_responder(struct message *msg)
{
	struct payload *p;
	u_int16_t	type;

	switch (msg->exchange->type) {
	case ISAKMP_EXCH_INFO:
		for (p = payload_first(msg, ISAKMP_PAYLOAD_NOTIFY); p;
		    p = TAILQ_NEXT(p, link)) {
			type = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p);

			LOG_DBG((LOG_EXCHANGE, 10, "isakmp_responder: "
			    "got NOTIFY of type %s",
			    constant_name(isakmp_notify_cst,
			    type)));

			switch (type) {
			case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE:
			case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK:
				dpd_handle_notify(msg, p);
				break;

			default:
				p->flags |= PL_MARK;
				break;
			}
		}

		for (p = payload_first(msg, ISAKMP_PAYLOAD_DELETE); p;
		    p = TAILQ_NEXT(p, link)) {
			LOG_DBG((LOG_EXCHANGE, 10,
			    "isakmp_responder: got DELETE, ignoring"));
			p->flags |= PL_MARK;
		}
		return 0;

	case ISAKMP_EXCH_TRANSACTION:
		/* return 0 isakmp_cfg_responder (msg); */

	default:
		/* XXX So far we don't accept any proposals.  */
		if (payload_first(msg, ISAKMP_PAYLOAD_SA)) {
			message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN,
			    0, 1, 0);
			return -1;
		}
	}
	return 0;
}
/*
 * 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;
}
Exemple #4
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;
}
Exemple #5
0
int
ike_phase_1_send_ID (struct message *msg)
{
    struct exchange *exchange = msg->exchange;
    u_int8_t *buf;
    char header[80];
    ssize_t sz;
    struct sockaddr *src;
    int initiator = exchange->initiator;
    u_int8_t **id;
    size_t *id_len;
    char *my_id = 0;
    u_int8_t id_type;

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

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

    if (!my_id)
        my_id = conf_get_str ("General", "Default-phase-1-ID");

    msg->transport->vtbl->get_src (msg->transport, &src);
    sz = my_id ? ipsec_id_size (my_id, &id_type) : sockaddr_addrlen (src);
    if (sz == -1)
        return -1;

    sz += ISAKMP_ID_DATA_OFF;
    buf = malloc (sz);
    if (!buf)
    {
        log_error ("ike_phase_1_send_ID: malloc (%lu) failed",
                   (unsigned long)sz);
        return -1;
    }

    SET_IPSEC_ID_PROTO (buf + ISAKMP_ID_DOI_DATA_OFF, 0);
    SET_IPSEC_ID_PORT (buf + ISAKMP_ID_DOI_DATA_OFF, 0);
    if (my_id)
    {
        SET_ISAKMP_ID_TYPE (buf, id_type);
        switch (id_type)
        {
        case IPSEC_ID_IPV4_ADDR:
        case IPSEC_ID_IPV6_ADDR:
            /* Already in network byteorder.  */
            memcpy (buf + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src),
                    sockaddr_addrlen (src));
            break;

        case IPSEC_ID_FQDN:
        case IPSEC_ID_USER_FQDN:
        case IPSEC_ID_KEY_ID:
            memcpy (buf + ISAKMP_ID_DATA_OFF, conf_get_str (my_id, "Name"),
                    sz - ISAKMP_ID_DATA_OFF);
            break;

        default:
            log_print ("ike_phase_1_send_ID: unsupported ID type %d", id_type);
            free (buf);
            return -1;
        }
    }
    else
    {
        switch (src->sa_family)
        {
        case AF_INET:
            SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV4_ADDR);
            break;
        case AF_INET6:
            SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV6_ADDR);
            break;
        }
        /* Already in network byteorder.  */
        memcpy (buf + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src),
                sockaddr_addrlen (src));
    }

    if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, buf, sz, 1))
    {
        free (buf);
        return -1;
    }
    *id_len = sz - ISAKMP_GEN_SZ;
    *id = malloc (*id_len);
    if (!*id)
    {
        log_error ("ike_phase_1_send_ID: malloc (%lu) failed",
                   (unsigned long)*id_len);
        return -1;
    }
    memcpy (*id, buf + ISAKMP_GEN_SZ, *id_len);
    snprintf (header, sizeof header, "ike_phase_1_send_ID: %s",
              constant_name (ipsec_id_cst, GET_ISAKMP_ID_TYPE (buf)));
    LOG_DBG_BUF ((LOG_NEGOTIATION, 40, header, buf + ISAKMP_ID_DATA_OFF,
                  sz - ISAKMP_ID_DATA_OFF));

    return 0;
}
Exemple #6
0
/*
 * Look at the attribute of type TYPE, located at VALUE for LEN bytes forward.
 * The VVS argument holds a validation state kept across invocations.
 * If the attribute is unacceptable to use, return non-zero, otherwise zero.
 */
static int
attribute_unacceptable (u_int16_t type, u_int8_t *value, u_int16_t len,
                        void *vvs)
{
    struct validation_state *vs = vvs;
    struct conf_list *life_conf;
    struct conf_list_node *xf = vs->xf, *life;
    char *tag = constant_lookup (ike_attr_cst, type);
    char *str;
    struct constant_map *map;
    struct attr_node *node;
    int rv;

    if (!tag)
    {
        LOG_DBG ((LOG_NEGOTIATION, 60,
                  "attribute_unacceptable: attribute type %d not known", type));
        return 1;
    }

    switch (type)
    {
    case IKE_ATTR_ENCRYPTION_ALGORITHM:
    case IKE_ATTR_HASH_ALGORITHM:
    case IKE_ATTR_AUTHENTICATION_METHOD:
    case IKE_ATTR_GROUP_DESCRIPTION:
    case IKE_ATTR_GROUP_TYPE:
    case IKE_ATTR_PRF:
        str = conf_get_str (xf->field, tag);
        if (!str)
        {
            /* This attribute does not exist in this policy.  */
            LOG_DBG ((LOG_NEGOTIATION, 70,
                      "attribute_unacceptable: attr %s does not exist in %s",
                      tag, xf->field));
            return 1;
        }

        map = constant_link_lookup (ike_attr_cst, type);
        if (!map)
            return 1;

        if ((constant_value (map, str) == decode_16 (value)) ||
                (!strcmp (str, "ANY")))
        {
            /* Mark this attribute as seen.  */
            node = malloc (sizeof *node);
            if (!node)
            {
                log_error ("attribute_unacceptable: malloc (%lu) failed",
                           (unsigned long)sizeof *node);
                return 1;
            }
            node->type = type;
            LIST_INSERT_HEAD (&vs->attrs, node, link);
            return 0;
        }
        LOG_DBG ((LOG_NEGOTIATION, 30,
                  "attribute_unacceptable: %s: got '%s', expected '%s'", tag,
                  constant_name (map, decode_16 (value)), str));
        return 1;

    case IKE_ATTR_GROUP_PRIME:
    case IKE_ATTR_GROUP_GENERATOR_1:
    case IKE_ATTR_GROUP_GENERATOR_2:
    case IKE_ATTR_GROUP_CURVE_A:
    case IKE_ATTR_GROUP_CURVE_B:
        /* XXX Bignums not handled yet.  */
        return 1;

    case IKE_ATTR_LIFE_TYPE:
    case IKE_ATTR_LIFE_DURATION:
        life_conf = conf_get_list (xf->field, "Life");
        if (life_conf && !strcmp (conf_get_str (xf->field, "Life"), "ANY"))
            return 0;

        rv = 1;
        if (!life_conf)
        {
            /* Life attributes given, but not in our policy.  */
            LOG_DBG ((LOG_NEGOTIATION, 70, "attribute_unacceptable: "
                      "received unexpected life attribute"));
            return 1;
        }

        /*
         * Each lifetime type must match, otherwise we turn the proposal down.
         * In order to do this we need to find the specific section of our
         * policy's "Life" list and match its duration
         */
        switch (type)
        {
        case IKE_ATTR_LIFE_TYPE:
            for (life = TAILQ_FIRST (&life_conf->fields); life;
                    life = TAILQ_NEXT (life, link))
            {
                str = conf_get_str (life->field, "LIFE_TYPE");
                if (!str)
                {
                    LOG_DBG ((LOG_NEGOTIATION, 70, "attribute_unacceptable: "
                              "section [%s] has no LIFE_TYPE", life->field));
                    continue;
                }

                /*
                 * If this is the type we are looking at, save a pointer
                 * to this section in vs->life.
                 */
                if (constant_value (ike_duration_cst, str) == decode_16 (value))
                {
                    vs->life = strdup (life->field);
                    rv = 0;
                    goto bail_out;
                }
            }
            LOG_DBG ((LOG_NEGOTIATION, 70,
                      "attribute_unacceptable: unrecognized LIFE_TYPE %d",
                      decode_16 (value)));
            vs->life = 0;
            break;

        case IKE_ATTR_LIFE_DURATION:
            if (!vs->life)
            {
                LOG_DBG ((LOG_NEGOTIATION, 70, "attribute_unacceptable: "
                          "LIFE_DURATION without LIFE_TYPE"));
                rv = 1;
                goto bail_out;
            }

            str = conf_get_str (vs->life, "LIFE_DURATION");
            if (str)
            {
                if (!strcmp (str, "ANY"))
                    rv = 0;
                else
                    rv = !conf_match_num (vs->life, "LIFE_DURATION",
                                          len == 4 ? decode_32 (value) :
                                          decode_16 (value));
            }
            else
            {
                LOG_DBG ((LOG_NEGOTIATION, 70, "attribute_unacceptable: "
                          "section [%s] has no LIFE_DURATION", vs->life));
                rv = 1;
            }

            free (vs->life);
            vs->life = 0;
            break;
        }

bail_out:
        conf_free_list (life_conf);
        return rv;

    case IKE_ATTR_KEY_LENGTH:
    case IKE_ATTR_FIELD_SIZE:
    case IKE_ATTR_GROUP_ORDER:
        if (conf_match_num (xf->field, tag, decode_16 (value)))
        {
            /* Mark this attribute as seen.  */
            node = malloc (sizeof *node);
            if (!node)
            {
                log_error ("attribute_unacceptable: malloc (%lu) failed",
                           (unsigned long)sizeof *node);
                return 1;
            }
            node->type = type;
            LIST_INSERT_HEAD (&vs->attrs, node, link);
            return 0;
        }
        return 1;
    }
    return 1;
}
Exemple #7
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;
}