strongswan/src/pluto/modecfg.c

1079 lines
26 KiB
C

/* Mode config related functions
* Copyright (C) 2001-2002 Colubris Networks
* Copyright (C) 2003 Sean Mathews - Nu Tech Software Solutions, inc.
* Copyright (C) 2003-2004 Xelerance Corporation
* Copyright (C) 2006-2007 Andreas Steffen - Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* RCSID $Id: modecfg.c,v 1.6 2006/04/24 20:44:57 as Exp $
*
* This code originally written by Colubris Networks, Inc.
* Extraction of patch and porting to 1.99 codebases by Xelerance Corporation
* Porting to 2.x by Sean Mathews
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freeswan.h>
#include "constants.h"
#include "defs.h"
#include "state.h"
#include "demux.h"
#include "timer.h"
#include "ipsec_doi.h"
#include "log.h"
#include "md5.h"
#include "sha1.h"
#include "crypto.h"
#include "modecfg.h"
#include "whack.h"
#include "xauth.h"
#define MAX_XAUTH_TRIES 3
#define SUPPORTED_ATTR_SET ( LELEM(INTERNAL_IP4_ADDRESS) \
| LELEM(INTERNAL_IP4_NETMASK) \
| LELEM(INTERNAL_IP4_DNS) \
| LELEM(INTERNAL_IP4_NBNS) \
| LELEM(APPLICATION_VERSION) \
)
#define SUPPORTED_UNITY_ATTR_SET ( LELEM(UNITY_BANNER - UNITY_BASE) )
#define UNITY_BANNER_STR "Welcome to strongSwan - the Linux VPN Solution!\n"
/*
* Addresses assigned (usually via ModeCfg) to the Initiator
*/
typedef struct internal_addr internal_addr_t;
struct internal_addr
{
lset_t attr_set;
lset_t xauth_attr_set;
lset_t unity_attr_set;
/* ModeCfg variables */
ip_address ipaddr;
ip_address dns[2];
ip_address wins[2];
char *unity_banner;
/* XAUTH variables */
u_int16_t xauth_type;
xauth_t xauth_secret;
bool xauth_status;
};
/*
* Initialize an internal_addr struct
*/
static void
init_internal_addr(internal_addr_t *ia)
{
ia->attr_set = LEMPTY;
ia->xauth_attr_set = LEMPTY;
ia->xauth_secret.user_name = empty_chunk;
ia->xauth_secret.user_password = empty_chunk;
ia->xauth_type = XAUTH_TYPE_GENERIC;
ia->xauth_status = XAUTH_STATUS_FAIL;
ia->unity_attr_set = LEMPTY;
ia->unity_banner = NULL;
anyaddr(AF_INET, &ia->ipaddr);
anyaddr(AF_INET, &ia->dns[0]);
anyaddr(AF_INET, &ia->dns[1]);
anyaddr(AF_INET, &ia->wins[0]);
anyaddr(AF_INET, &ia->wins[1]);
}
/*
* get internal IP address for a connection
*/
static void
get_internal_addr(struct connection *c, internal_addr_t *ia)
{
if (isanyaddr(&c->spd.that.host_srcip))
{
/* not defined in connection - fetch it from LDAP */
}
else
{
char srcip[ADDRTOT_BUF];
ia->ipaddr = c->spd.that.host_srcip;
addrtot(&ia->ipaddr, 0, srcip, sizeof(srcip));
plog("assigning virtual IP source address %s", srcip);
}
if (!isanyaddr(&ia->ipaddr)) /* We got an IP address, send it */
{
c->spd.that.client.addr = ia->ipaddr;
c->spd.that.client.maskbits = 32;
c->spd.that.has_client = TRUE;
ia->attr_set = LELEM(INTERNAL_IP4_ADDRESS)
| LELEM(INTERNAL_IP4_NETMASK);
}
if (!isanyaddr(&ia->dns[0])) /* We got DNS addresses, send them */
ia->attr_set |= LELEM(INTERNAL_IP4_DNS);
if (!isanyaddr(&ia->wins[0])) /* We got WINS addresses, send them */
ia->attr_set |= LELEM(INTERNAL_IP4_NBNS);
}
/*
* Set srcip and client subnet to internal IP address
*/
static bool
set_internal_addr(struct connection *c, internal_addr_t *ia)
{
if (ia->attr_set & LELEM(INTERNAL_IP4_ADDRESS)
&& !isanyaddr(&ia->ipaddr))
{
if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0
|| isanyaddr(&c->spd.this.host_srcip)
|| sameaddr(&c->spd.this.host_srcip, &ia->ipaddr))
{
char srcip[ADDRTOT_BUF];
addrtot(&ia->ipaddr, 0, srcip, sizeof(srcip));
plog("setting virtual IP source address to %s", srcip);
}
else
{
char old_srcip[ADDRTOT_BUF];
char new_srcip[ADDRTOT_BUF];
addrtot(&c->spd.this.host_srcip, 0, old_srcip, sizeof(old_srcip));
addrtot(&ia->ipaddr, 0, new_srcip, sizeof(new_srcip));
plog("replacing virtual IP source address %s by %s"
, old_srcip, new_srcip);
}
/* setting srcip */
c->spd.this.host_srcip = ia->ipaddr;
/* setting client subnet to srcip/32 */
addrtosubnet(&ia->ipaddr, &c->spd.this.client);
setportof(0, &c->spd.this.client.addr);
c->spd.this.has_client = TRUE;
return TRUE;
}
return FALSE;
}
/*
* Compute HASH of Mode Config.
*/
static size_t
modecfg_hash(u_char *dest, const u_char *start, const u_char *roof
, const struct state *st)
{
struct hmac_ctx ctx;
hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a);
hmac_update(&ctx, (const u_char *) &st->st_msgid, sizeof(st->st_msgid));
hmac_update(&ctx, start, roof-start);
hmac_final(dest, &ctx);
DBG(DBG_CRYPT,
DBG_log("ModeCfg HASH computed:");
DBG_dump("", dest, ctx.hmac_digest_size)
)
return ctx.hmac_digest_size;
}
/*
* Generate an IKE message containing ModeCfg information (eg: IP, DNS, WINS)
*/
static stf_status
modecfg_build_msg(struct state *st, pb_stream *rbody
, u_int16_t msg_type
, internal_addr_t *ia
, u_int16_t ap_id)
{
u_char *r_hash_start, *r_hashval;
START_HASH_PAYLOAD(*rbody, ISAKMP_NEXT_ATTR);
/* ATTR out */
{
struct isakmp_mode_attr attrh;
struct isakmp_attribute attr;
pb_stream strattr,attrval;
int attr_type;
int dns_idx, wins_idx;
bool dont_advance;
bool is_xauth_attr_set = ia->xauth_attr_set != LEMPTY;
bool is_unity_attr_set = ia->unity_attr_set != LEMPTY;
lset_t attr_set = ia->attr_set;
attrh.isama_np = ISAKMP_NEXT_NONE;
attrh.isama_type = msg_type;
attrh.isama_identifier = ap_id;
if (!out_struct(&attrh, &isakmp_attr_desc, rbody, &strattr))
return STF_INTERNAL_ERROR;
attr_type = 0;
dns_idx = 0;
wins_idx = 0;
while (attr_set != LEMPTY || is_xauth_attr_set || is_unity_attr_set)
{
if (attr_set == LEMPTY)
{
if (is_xauth_attr_set)
{
attr_set = ia->xauth_attr_set;
attr_type = XAUTH_BASE;
is_xauth_attr_set = FALSE;
}
else
{
attr_set = ia->unity_attr_set;
attr_type = UNITY_BASE;
is_unity_attr_set = FALSE;
}
}
dont_advance = FALSE;
if (attr_set & 1)
{
const u_char *byte_ptr;
u_int len;
/* ISAKMP attr out */
if (attr_type == XAUTH_TYPE)
{
attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TV;
attr.isaat_lv = ia->xauth_type;
}
else if (attr_type == XAUTH_STATUS)
{
attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TV;
attr.isaat_lv = ia->xauth_status;
}
else
{
attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV;
}
out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, &attrval);
switch (attr_type)
{
case INTERNAL_IP4_ADDRESS:
if (!isanyaddr(&ia->ipaddr))
{
len = addrbytesptr(&ia->ipaddr, &byte_ptr);
out_raw(byte_ptr, len, &attrval, "IP4_addr");
}
break;
case INTERNAL_IP4_NETMASK:
{
u_int mask;
#if 0
char mask[4],bits[8]={0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
int t,m=st->st_connection->that.host_addr.maskbit;
for (t=0; t<4; t++)
{
if (m < 8)
mask[t] = bits[m];
else
mask[t] = 0xff;
m -= 8;
}
#endif
if (st->st_connection->spd.this.client.maskbits == 0)
mask = 0;
else
mask = 0xffffffff * 1;
out_raw(&mask, 4, &attrval, "IP4_mask");
}
break;
case INTERNAL_IP4_SUBNET:
{
char mask[4];
char bits[8] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
int t;
int m = st->st_connection->spd.this.client.maskbits;
for (t = 0; t < 4; t++)
{
if (m < 8)
mask[t] = bits[m];
else
mask[t] = 0xff;
m -= 8;
if (m < 0)
m = 0;
}
len = addrbytesptr(&st->st_connection->spd.this.client.addr, &byte_ptr);
out_raw(byte_ptr, len, &attrval, "IP4_subnet");
out_raw(mask, sizeof(mask), &attrval, "IP4_submsk");
}
break;
case INTERNAL_IP4_DNS:
if (!isanyaddr(&ia->dns[dns_idx]))
{
len = addrbytesptr(&ia->dns[dns_idx++], &byte_ptr);
out_raw(byte_ptr, len, &attrval, "IP4_dns");
}
if (dns_idx < 2 && !isanyaddr(&ia->dns[dns_idx]))
{
dont_advance = TRUE;
}
break;
case INTERNAL_IP4_NBNS:
if (!isanyaddr(&ia->wins[wins_idx]))
{
len = addrbytesptr(&ia->wins[wins_idx++], &byte_ptr);
out_raw(byte_ptr, len, &attrval, "IP4_wins");
}
if (wins_idx < 2 && !isanyaddr(&ia->wins[wins_idx]))
{
dont_advance = TRUE;
}
break;
case XAUTH_TYPE:
break;
case XAUTH_USER_NAME:
if (ia->xauth_secret.user_name.ptr != NULL)
{
out_raw(ia->xauth_secret.user_name.ptr
, ia->xauth_secret.user_name.len
, &attrval, "xauth_user_name");
}
break;
case XAUTH_USER_PASSWORD:
if (ia->xauth_secret.user_password.ptr != NULL)
{
out_raw(ia->xauth_secret.user_password.ptr
, ia->xauth_secret.user_password.len
, &attrval, "xauth_user_password");
}
break;
case XAUTH_STATUS:
break;
case UNITY_BANNER:
if (ia->unity_banner != NULL)
{
out_raw(ia->unity_banner
, strlen(ia->unity_banner)
, &attrval, "UNITY_BANNER");
}
break;
default:
plog("attempt to send unsupported mode cfg attribute %s."
, enum_show(&modecfg_attr_names, attr_type));
break;
}
close_output_pbs(&attrval);
}
if (!dont_advance)
{
attr_type++;
attr_set >>= 1;
}
}
close_message(&strattr);
}
modecfg_hash(r_hashval, r_hash_start, rbody->cur, st);
close_message(rbody);
encrypt_message(rbody, st);
return STF_OK;
}
/*
* Send ModeCfg message
*/
static stf_status
modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia)
{
pb_stream msg;
pb_stream rbody;
char buf[BUF_LEN];
/* set up attr */
init_pbs(&msg, buf, sizeof(buf), "ModeCfg msg buffer");
/* this is the beginning of a new exchange */
st->st_msgid = generate_msgid(st);
init_phase2_iv(st, &st->st_msgid);
/* HDR out */
{
struct isakmp_hdr hdr;
zero(&hdr); /* default to 0 */
hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
hdr.isa_np = ISAKMP_NEXT_HASH;
hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG;
hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION;
memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);
hdr.isa_msgid = st->st_msgid;
if (!out_struct(&hdr, &isakmp_hdr_desc, &msg, &rbody))
{
return STF_INTERNAL_ERROR;
}
}
/* ATTR out */
modecfg_build_msg(st, &rbody
, isama_type
, ia
, 0 /* XXX isama_id */
);
freeanychunk(st->st_tpacket);
clonetochunk(st->st_tpacket, msg.start, pbs_offset(&msg), "ModeCfg msg");
/* Transmit */
send_packet(st, "ModeCfg msg");
if (st->st_event->ev_type != EVENT_RETRANSMIT)
{
delete_event(st);
event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);
}
return STF_OK;
}
/*
* Parse a ModeCfg attribute payload
*/
static stf_status
modecfg_parse_attributes(pb_stream *attrs, internal_addr_t *ia)
{
struct isakmp_attribute attr;
pb_stream strattr;
while (pbs_left(attrs) >= sizeof(struct isakmp_attribute))
{
u_int16_t attr_type;
u_int16_t attr_len;
if (!in_struct(&attr, &isakmp_modecfg_attribute_desc, attrs, &strattr))
{
return STF_FAIL;
}
attr_type = attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK;
attr_len = attr.isaat_lv;
switch (attr_type)
{
case INTERNAL_IP4_ADDRESS:
if (attr_len == 4)
{
initaddr((char *)(strattr.cur), 4, AF_INET, &ia->ipaddr);
}
/* fall through to set attribute flag */
case INTERNAL_IP4_NETMASK:
case INTERNAL_IP4_DNS:
case INTERNAL_IP4_SUBNET:
case INTERNAL_IP4_NBNS:
case INTERNAL_ADDRESS_EXPIRY:
case INTERNAL_IP4_DHCP:
case INTERNAL_IP6_ADDRESS:
case INTERNAL_IP6_NETMASK:
case INTERNAL_IP6_DNS:
case INTERNAL_IP6_NBNS:
case INTERNAL_IP6_DHCP:
case SUPPORTED_ATTRIBUTES:
case INTERNAL_IP6_SUBNET:
ia->attr_set |= LELEM(attr_type);
break;
case APPLICATION_VERSION:
if (attr_len > 0)
{
DBG(DBG_PARSING,
DBG_log(" '%.*s'", attr_len, strattr.cur)
)
}
ia->attr_set |= LELEM(attr_type);
break;
case XAUTH_TYPE:
ia->xauth_type = attr.isaat_lv;
ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE);
break;
case XAUTH_USER_NAME:
setchunk(ia->xauth_secret.user_name, strattr.cur, attr_len);
ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE);
break;
case XAUTH_USER_PASSWORD:
setchunk(ia->xauth_secret.user_password, strattr.cur, attr_len);
ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE);
break;
case XAUTH_STATUS:
ia->xauth_status = attr.isaat_lv;
ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE);
break;
case XAUTH_MESSAGE:
if (attr_len > 0)
{
DBG(DBG_PARSING,
DBG_log(" '%.*s'", attr_len, strattr.cur)
)
}
/* fall through to set attribute flag */
case XAUTH_PASSCODE:
case XAUTH_CHALLENGE:
case XAUTH_DOMAIN:
case XAUTH_NEXT_PIN:
case XAUTH_ANSWER:
ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE);
break;
case UNITY_DDNS_HOSTNAME:
if (attr_len > 0)
{
DBG(DBG_PARSING,
DBG_log(" '%.*s'", attr_len, strattr.cur)
)
}
/* fall through to set attribute flag */
case UNITY_BANNER:
case UNITY_SAVE_PASSWD:
case UNITY_DEF_DOMAIN:
case UNITY_SPLITDNS_NAME:
case UNITY_SPLIT_INCLUDE:
case UNITY_NATT_PORT:
case UNITY_LOCAL_LAN:
case UNITY_PFS:
case UNITY_FW_TYPE:
case UNITY_BACKUP_SERVERS:
ia->unity_attr_set |= LELEM(attr_type - UNITY_BASE);
break;
default:
plog("unsupported ModeCfg attribute %s received."
, enum_show(&modecfg_attr_names, attr_type));
break;
}
}
return STF_OK;
}
/*
* Parse a ModeCfg message
*/
static stf_status
modecfg_parse_msg(struct msg_digest *md, int isama_type, u_int16_t *isama_id
, internal_addr_t *ia)
{
struct state *const st = md->st;
struct payload_digest *p;
stf_status stat;
st->st_msgid = md->hdr.isa_msgid;
CHECK_QUICK_HASH(md, modecfg_hash(hash_val
, hash_pbs->roof
, md->message_pbs.roof, st)
, "MODECFG-HASH", "ISAKMP_CFG_MSG");
/* process the ModeCfg payloads received */
for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next)
{
internal_addr_t ia_candidate;
init_internal_addr(&ia_candidate);
if (p->payload.attribute.isama_type == isama_type)
{
*isama_id = p->payload.attribute.isama_identifier;
stat = modecfg_parse_attributes(&p->pbs, &ia_candidate);
if (stat == STF_OK)
{
/* return with a valid set of attributes */
*ia = ia_candidate;
return STF_OK;
}
}
else
{
plog("expected %s, got %s instead (ignored)"
, enum_name(&attr_msg_type_names, isama_type)
, enum_name(&attr_msg_type_names, p->payload.attribute.isama_type));
stat = modecfg_parse_attributes(&p->pbs, &ia_candidate);
}
if (stat != STF_OK)
return stat;
}
return STF_IGNORE;
}
/*
* Send ModeCfg request message from client to server in pull mode
*/
stf_status
modecfg_send_request(struct state *st)
{
stf_status stat;
internal_addr_t ia;
init_internal_addr(&ia);
ia.attr_set = LELEM(INTERNAL_IP4_ADDRESS)
| LELEM(INTERNAL_IP4_NETMASK);
plog("sending ModeCfg request");
st->st_state = STATE_MODE_CFG_I1;
stat = modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia);
if (stat == STF_OK)
st->st_modecfg.started = TRUE;
return stat;
}
/* STATE_MODE_CFG_R0:
* HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP)
*
* used in ModeCfg pull mode, on the server (responder)
*/
stf_status
modecfg_inR0(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
bool want_unity_banner;
stf_status stat, stat_build;
stat = modecfg_parse_msg(md, ISAKMP_CFG_REQUEST, &isama_id, &ia);
if (stat != STF_OK)
return stat;
want_unity_banner = (ia.unity_attr_set & LELEM(UNITY_BANNER - UNITY_BASE)) != LEMPTY;
init_internal_addr(&ia);
get_internal_addr(st->st_connection, &ia);
if (want_unity_banner)
{
ia.unity_banner = UNITY_BANNER_STR;
ia.unity_attr_set |= LELEM(UNITY_BANNER - UNITY_BASE);
}
plog("sending ModeCfg reply");
stat_build = modecfg_build_msg(st, &md->rbody
, ISAKMP_CFG_REPLY
, &ia
, isama_id);
if (stat_build != STF_OK)
return stat_build;
st->st_msgid = 0;
return STF_OK;
}
/* STATE_MODE_CFG_I1:
* HDR*, HASH, ATTR(REPLY=IP)
*
* used in ModeCfg pull mode, on the client (initiator)
*/
stf_status
modecfg_inI1(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
stf_status stat;
plog("parsing ModeCfg reply");
stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia);
if (stat != STF_OK)
return stat;
st->st_modecfg.vars_set = set_internal_addr(st->st_connection, &ia);
st->st_msgid = 0;
return STF_OK;
}
/*
* Send ModeCfg set message from server to client in push mode
*/
stf_status
modecfg_send_set(struct state *st)
{
stf_status stat;
internal_addr_t ia;
init_internal_addr(&ia);
get_internal_addr(st->st_connection, &ia);
#ifdef CISCO_QUIRKS
ia.unity_banner = UNITY_BANNER_STR;
ia.unity_attr_set |= LELEM(UNITY_BANNER - UNITY_BASE);
#endif
plog("sending ModeCfg set");
st->st_state = STATE_MODE_CFG_R3;
stat = modecfg_send_msg(st, ISAKMP_CFG_SET, &ia);
if (stat == STF_OK)
st->st_modecfg.started = TRUE;
return stat;
}
/* STATE_MODE_CFG_I0:
* HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK)
*
* used in ModeCfg push mode, on the client (initiator).
*/
stf_status
modecfg_inI0(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
lset_t attr_set, unity_attr_set;
stf_status stat, stat_build;
plog("parsing ModeCfg set");
stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia);
if (stat != STF_OK)
return stat;
st->st_modecfg.vars_set = set_internal_addr(st->st_connection, &ia);
/* prepare ModeCfg ack which sends zero length attributes */
attr_set = ia.attr_set;
unity_attr_set = ia.unity_attr_set;
init_internal_addr(&ia);
ia.attr_set = attr_set & SUPPORTED_ATTR_SET;
ia.unity_attr_set = unity_attr_set & SUPPORTED_UNITY_ATTR_SET;
plog("sending ModeCfg ack");
stat_build = modecfg_build_msg(st, &md->rbody
, ISAKMP_CFG_ACK
, &ia
, isama_id);
if (stat_build != STF_OK)
return stat_build;
st->st_msgid = 0;
return STF_OK;
}
/* STATE_MODE_CFG_R3:
* HDR*, HASH, ATTR(ACK,OK)
*
* used in ModeCfg push mode, on the server (responder)
*/
stf_status
modecfg_inR3(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
stf_status stat;
plog("parsing ModeCfg ack");
stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia);
if (stat != STF_OK)
return stat;
st->st_msgid = 0;
return STF_OK;
}
/*
* Send XAUTH credentials request (username + password)
*/
stf_status
xauth_send_request(struct state *st)
{
stf_status stat;
internal_addr_t ia;
init_internal_addr(&ia);
ia.xauth_attr_set = LELEM(XAUTH_USER_NAME - XAUTH_BASE)
| LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE);
plog("sending XAUTH request");
st->st_state = STATE_XAUTH_R1;
stat = modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia);
if (stat == STF_OK)
st->st_xauth.started = TRUE;
return stat;
}
/* STATE_XAUTH_I0:
* HDR*, HASH, ATTR(REQ) --> HDR*, HASH, ATTR(REPLY=USERNAME/PASSWORD)
*
* used on the XAUTH client (initiator)
*/
stf_status
xauth_inI0(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
stf_status stat, stat_build;
bool xauth_type_present;
plog("parsing XAUTH request");
stat = modecfg_parse_msg(md, ISAKMP_CFG_REQUEST, &isama_id, &ia);
if (stat != STF_OK)
return stat;
/* check XAUTH attributes */
xauth_type_present = (ia.xauth_attr_set & LELEM(XAUTH_TYPE - XAUTH_BASE)) != LEMPTY;
if (xauth_type_present && ia.xauth_type != XAUTH_TYPE_GENERIC)
{
plog("xauth type %s is not supported", enum_name(&xauth_type_names, ia.xauth_type));
stat = STF_FAIL;
}
else if ((ia.xauth_attr_set & LELEM(XAUTH_USER_NAME - XAUTH_BASE)) == LEMPTY)
{
plog("user name attribute is missing in XAUTH request");
stat = STF_FAIL;
}
else if ((ia.xauth_attr_set & LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE)) == LEMPTY)
{
plog("user password attribute is missing in XAUTH request");
stat = STF_FAIL;
}
/* prepare XAUTH reply */
init_internal_addr(&ia);
if (stat == STF_OK)
{
/* get user credentials using a plugin function */
if (!xauth_module.get_secret(&ia.xauth_secret))
{
plog("xauth user credentials not found");
stat = STF_FAIL;
}
}
if (stat == STF_OK)
{
DBG(DBG_CONTROL,
DBG_log("my xauth user name is '%.*s'"
, ia.xauth_secret.user_name.len
, ia.xauth_secret.user_name.ptr)
)
DBG(DBG_PRIVATE,
DBG_log("my xauth user password is '%.*s'"
, ia.xauth_secret.user_password.len
, ia.xauth_secret.user_password.ptr)
)
ia.xauth_attr_set = LELEM(XAUTH_USER_NAME - XAUTH_BASE)
| LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE);
if (xauth_type_present)
ia.xauth_attr_set |= LELEM(XAUTH_TYPE - XAUTH_BASE);
}
else
{
ia.xauth_attr_set = LELEM(XAUTH_STATUS - XAUTH_BASE);
ia.xauth_status = XAUTH_STATUS_FAIL;
}
plog("sending XAUTH reply");
stat_build = modecfg_build_msg(st, &md->rbody
, ISAKMP_CFG_REPLY
, &ia
, isama_id);
if (stat_build != STF_OK)
return stat_build;
if (stat == STF_OK)
{
st->st_xauth.started = TRUE;
st->st_msgid = 0;
return STF_OK;
}
else
{
/* send XAUTH reply msg and then delete ISAKMP SA */
freeanychunk(st->st_tpacket);
clonetochunk(st->st_tpacket, md->reply.start
, pbs_offset(&md->reply), "XAUTH reply msg");
send_packet(st, "XAUTH reply msg");
delete_state(st);
return STF_IGNORE;
}
}
/* STATE_XAUTH_R1:
* HDR*, HASH, ATTR(REPLY=USERNAME/PASSWORD) --> HDR*, HASH, ATTR(STATUS)
*
* used on the XAUTH server (responder)
*/
stf_status
xauth_inR1(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
stf_status stat, stat_build;
plog("parsing XAUTH reply");
stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia);
if (stat != STF_OK)
return stat;
/* did the client return an XAUTH FAIL status? */
if ((ia.xauth_attr_set & LELEM(XAUTH_STATUS - XAUTH_BASE)) != LEMPTY)
{
plog("received FAIL status in XAUTH reply");
/* client is not able to do XAUTH, delete ISAKMP SA */
delete_state(st);
return STF_IGNORE;
}
/* check XAUTH reply */
if ((ia.xauth_attr_set & LELEM(XAUTH_USER_NAME - XAUTH_BASE)) == LEMPTY)
{
plog("user name attribute is missing in XAUTH reply");
st->st_xauth.status = FALSE;
}
else if ((ia.xauth_attr_set & LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE)) == LEMPTY)
{
plog("user password attribute is missing in XAUTH reply");
st->st_xauth.status = FALSE;
}
else
{
DBG(DBG_CONTROL,
DBG_log("peer xauth user name is '%.*s'"
, ia.xauth_secret.user_name.len
, ia.xauth_secret.user_name.ptr)
)
DBG(DBG_PRIVATE,
DBG_log("peer xauth user password is '%.*s'"
, ia.xauth_secret.user_password.len
, ia.xauth_secret.user_password.ptr)
)
/* verify the user credentials using a plugn function */
st->st_xauth.status = xauth_module.verify_secret(&ia.xauth_secret);
plog("extended authentication %s", st->st_xauth.status? "was successful":"failed");
}
/* prepare XAUTH set which sends the authentication status */
init_internal_addr(&ia);
ia.xauth_attr_set = LELEM(XAUTH_STATUS - XAUTH_BASE);
ia.xauth_status = (st->st_xauth.status)? XAUTH_STATUS_OK : XAUTH_STATUS_FAIL;
plog("sending XAUTH status:");
stat_build = modecfg_send_msg(st, ISAKMP_CFG_SET, &ia);
if (stat_build != STF_OK)
return stat_build;
return STF_OK;
}
/* STATE_XAUTH_I1:
* HDR*, HASH, ATTR(STATUS) --> HDR*, HASH, ATTR(ACK)
*
* used on the XAUTH client (initiator)
*/
stf_status
xauth_inI1(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
stf_status stat, stat_build;
plog("parsing XAUTH status");
stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia);
if (stat != STF_OK)
{
/* notification payload - not exactly the right choice, but okay */
md->note = ATTRIBUTES_NOT_SUPPORTED;
return stat;
}
st->st_xauth.status = ia.xauth_status;
plog("extended authentication %s", st->st_xauth.status? "was successful":"failed");
plog("sending XAUTH ack");
init_internal_addr(&ia);
stat_build = modecfg_build_msg(st, &md->rbody
, ISAKMP_CFG_ACK
, &ia
, isama_id);
if (stat_build != STF_OK)
return stat_build;
if (st->st_xauth.status)
{
st->st_msgid = 0;
return STF_OK;
}
else
{
/* send XAUTH ack msg and then delete ISAKMP SA */
freeanychunk(st->st_tpacket);
clonetochunk(st->st_tpacket, md->reply.start
, pbs_offset(&md->reply), "XAUTH ack msg");
send_packet(st, "XAUTH ack msg");
delete_state(st);
return STF_IGNORE;
}
}
/* STATE_XAUTH_R2:
* HDR*, ATTR(STATUS), HASH --> Done
*
* used on the XAUTH server (responder)
*/
stf_status
xauth_inR2(struct msg_digest *md)
{
struct state *const st = md->st;
u_int16_t isama_id;
internal_addr_t ia;
stf_status stat;
plog("parsing XAUTH ack");
stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia);
if (stat != STF_OK)
return stat;
st->st_msgid = 0;
if (st->st_xauth.status)
{
return STF_OK;
}
else
{
delete_state(st);
return STF_IGNORE;
}
}