예제 #1
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;
}
예제 #2
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;
}
예제 #3
0
/* Receive HASH and check that the exchange has been consistent.  */
int
ike_phase_1_recv_AUTH (struct message *msg)
{
    struct exchange *exchange = msg->exchange;
    struct ipsec_exch *ie = exchange->data;
    struct prf *prf;
    struct hash *hash = ie->hash;
    char header[80];
    size_t hashsize = hash->hashsize;
    int initiator = exchange->initiator;
    u_int8_t **hash_p, *id;
    size_t id_len;

    /* 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;

    /* The decoded hash will be in ie->hash_r or ie->hash_i */
    if (ie->ike_auth->decode_hash (msg))
    {
        message_drop (msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1, 0);
        return -1;
    }

    /* Allocate the prf and start calculating his HASH.  */
    prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len);
    if (!prf)
    {
        /* XXX Log?  */
        return -1;
    }
    prf->Init (prf->prfctx);
    prf->Update (prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len);
    prf->Update (prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len);
    prf->Update (prf->prfctx,
                 exchange->cookies
                 + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF),
                 ISAKMP_HDR_ICOOKIE_LEN);
    prf->Update (prf->prfctx,
                 exchange->cookies
                 + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF),
                 ISAKMP_HDR_ICOOKIE_LEN);
    prf->Update (prf->prfctx, ie->sa_i_b, ie->sa_i_b_len);
    prf->Update (prf->prfctx, id, id_len);
    prf->Final (hash->digest, prf->prfctx);
    prf_free (prf);
    snprintf (header, sizeof header, "ike_phase_1_recv_AUTH: computed HASH_%c",
              initiator ? 'R' : 'I');
    LOG_DBG_BUF ((LOG_NEGOTIATION, 80, header, hash->digest, hashsize));

    /* Check that the hash we got matches the one we computed.  */
    if (memcmp (*hash_p, hash->digest, hashsize) != 0)
    {
        /* XXX Log?  */
        return -1;
    }

    return 0;
}
예제 #4
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;
}
예제 #5
0
/* Figure out what transform the responder chose.  */
int
ike_phase_1_initiator_recv_SA (struct message *msg)
{
    struct exchange *exchange = msg->exchange;
    struct sa *sa = TAILQ_FIRST (&exchange->sa_list);
    struct ipsec_exch *ie = exchange->data;
    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 payload *xf = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]);

    /*
     * IKE requires that only one SA with only one proposal exists and since
     * we are getting an answer on our transform offer, only one transform.
     */
    if (TAILQ_NEXT (sa_p, link) || TAILQ_NEXT (prop, link)
            || TAILQ_NEXT (xf, link))
    {
        log_print ("ike_phase_1_initiator_recv_SA: "
                   "multiple SA, proposal or transform payloads in phase 1");
        /* XXX Is there a better notification type?  */
        message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
        return -1;
    }

    /* Check that the chosen transform matches an offer.  */
    if (message_negotiate_sa (msg, ike_phase_1_validate_prop)
            || !TAILQ_FIRST (&sa->protos))
        return -1;

    ipsec_decode_transform (msg, sa, TAILQ_FIRST (&sa->protos), xf->p);

    /* XXX I don't like exchange-specific stuff in here.  */
    if (exchange->type != ISAKMP_EXCH_AGGRESSIVE)
        ie->group = group_get (isa->group_desc);

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

    return 0;
}
예제 #6
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;
}
예제 #7
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;
}