XAUTH interoperability with Cisco
[strongswan.git] / src / pluto / modecfg.c
index 439293a..ab44a11 100644 (file)
@@ -2,7 +2,7 @@
  * 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 Andreas Steffen - Hochschule fuer Technik Rapperswil
+ * 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
 #include "crypto.h"
 #include "modecfg.h"
 #include "whack.h"
+#include "xauth.h"
 
-#define SUPPORTED_ATTR_SET   ( LELEM(INTERNAL_IP4_ADDRESS) \
-                             | LELEM(INTERNAL_IP4_NETMASK) \
-                             | LELEM(INTERNAL_IP4_DNS)     \
-                             | LELEM(INTERNAL_IP4_NBNS)    \
+#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
  */
@@ -53,10 +61,21 @@ typedef struct internal_addr internal_addr_t;
 
 struct internal_addr
 {
-    lset_t        attr_set;
-    ip_address    ipaddr;
-    ip_address    dns[2];
-    ip_address    wins[2];
+    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;
 };
 
 /*
@@ -66,6 +85,14 @@ 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]);
@@ -79,8 +106,6 @@ init_internal_addr(internal_addr_t *ia)
 static void
 get_internal_addr(struct connection *c, internal_addr_t *ia)
 {
-    init_internal_addr(ia);
-
     if (isanyaddr(&c->spd.that.host_srcip))
     {
        /* not defined in connection - fetch it from LDAP */
@@ -101,10 +126,10 @@ get_internal_addr(struct connection *c, internal_addr_t *ia)
        c->spd.that.client.maskbits = 32;
        c->spd.that.has_client      = TRUE;
        
-       ia->attr_set |= LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_NETMASK);
+       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);
 
@@ -196,6 +221,8 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
        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;
@@ -209,16 +236,46 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
        dns_idx = 0;
        wins_idx = 0;
 
-       while (attr_set != 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 */
-               attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV;
+               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)
@@ -288,14 +345,42 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
                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");
+                       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;
+                   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));
@@ -312,7 +397,7 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
        close_message(&strattr);
     }
 
-    modecfg_hash(r_hashval, r_hash_start, rbody->cur,st);
+    modecfg_hash(r_hashval, r_hash_start, rbody->cur, st);
     close_message(rbody);
     encrypt_message(rbody, st);
     return STF_OK;
@@ -361,6 +446,7 @@ modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia)
                        , 0 /* XXX isama_id */
                     );
 
+    freeanychunk(st->st_tpacket);
     clonetochunk(st->st_tpacket, msg.start, pbs_offset(&msg), "ModeCfg msg");
 
     /* Transmit */
@@ -371,43 +457,10 @@ modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia)
        delete_event(st);
        event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);
     }
-    st->st_modecfg.started = TRUE;
     return STF_OK;
 }
 
 /*
- * Send ModeCfg request message from client to server in pull mode
- */
-stf_status
-modecfg_send_request(struct state *st)
-{
-    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;
-    return modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia);
-}
-
-/*
- * Send ModeCfg set message from server to client in push mode
- */
-stf_status
-modecfg_send_set(struct state *st)
-{
-    internal_addr_t ia;
-
-    get_internal_addr(st->st_connection, &ia);
-
-    plog("sending ModeCfg set");
-    st->st_state = STATE_MODE_CFG_R1;
-    return modecfg_send_msg(st, ISAKMP_CFG_SET, &ia);
-}
-
-/*
  * Parse a ModeCfg attribute payload
  */
 static stf_status
@@ -435,13 +488,82 @@ modecfg_parse_attributes(pb_stream *attrs, internal_addr_t *ia)
            {
                initaddr((char *)(strattr.cur), 4, AF_INET, &ia->ipaddr);
            }
-           /* fall through to set attribute flags */
+           /* 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));
@@ -483,7 +605,7 @@ modecfg_parse_msg(struct msg_digest *md, int isama_type, u_int16_t *isama_id
            stat = modecfg_parse_attributes(&p->pbs, &ia_candidate);
            if (stat == STF_OK)
            {
-               /* retrun with a valid set of attributes */
+               /* return with a valid set of attributes */
                *ia = ia_candidate;
                return STF_OK;
            }
@@ -502,6 +624,28 @@ modecfg_parse_msg(struct msg_digest *md, int isama_type, u_int16_t *isama_id
     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)
  *
@@ -513,108 +657,359 @@ modecfg_inR0(struct msg_digest *md)
     struct state *const st = md->st;
     u_int16_t isama_id;
     internal_addr_t ia;
-    stf_status stat;
+    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);
 
-    /* build ISAKMP_CFG_REPLY */ 
-    stat = modecfg_build_msg(st, &md->rbody
-                              , ISAKMP_CFG_REPLY
-                              , &ia
-                              , isama_id);
-    if (stat != STF_OK)
+    if (want_unity_banner)
     {
-       /* notification payload - not exactly the right choice, but okay */
-       md->note = ATTRIBUTES_NOT_SUPPORTED;
-       return stat;
+       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_R1:
- * HDR*, HASH, ATTR(ACK,OK)
+/* STATE_MODE_CFG_I1:
+ * HDR*, HASH, ATTR(REPLY=IP)
  *
- * used in ModeCfg push mode, on the server (responder)
+ * used in ModeCfg pull mode, on the client (initiator) 
  */
 stf_status
-modecfg_inR1(struct msg_digest *md)
+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 ack");
+    plog("parsing ModeCfg reply");
 
-    stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia);
+    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;
 }
 
-/* STATE_MODE_CFG_I1:
- * HDR*, HASH, ATTR(REPLY=IP)
+
+/*
+ * 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 pull mode, on the client (initiator) 
+ * used in ModeCfg push mode, on the client (initiator).
  */
 stf_status
-modecfg_inI1(struct msg_digest *md)
+modecfg_inI0(struct msg_digest *md)
 {
     struct state *const st = md->st;
     u_int16_t isama_id;
     internal_addr_t ia;
-    stf_status stat;
+    lset_t attr_set, unity_attr_set;
+    stf_status stat, stat_build;
 
-    plog("parsing ModeCfg reply");
+    plog("parsing ModeCfg set");
 
-    stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia);
+    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_I2:
- *  HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK)
+/* STATE_MODE_CFG_R3:
+ * HDR*, HASH, ATTR(ACK,OK)
  *
- * used in ModeCfg push mode, on the client (initiator).
+ * used in ModeCfg push mode, on the server (responder)
  */
 stf_status
-modecfg_inI2(struct msg_digest *md)
+modecfg_inR3(struct msg_digest *md)
 {
     struct state *const st = md->st;
     u_int16_t isama_id;
     internal_addr_t ia;
-    lset_t attr_set;
     stf_status stat;
 
-    plog("parsing ModeCfg set");
+    plog("parsing ModeCfg ack");
 
-    stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia);
+    stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &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 XAUTH credentials request (username + password)
+ */
+stf_status
+xauth_send_request(struct state *st)
+{
+    stf_status stat;
+    internal_addr_t ia;
 
-    /* prepare ModeCfg ack which sends zero length attributes */
-    attr_set = ia.attr_set;
     init_internal_addr(&ia);
-    ia.attr_set = attr_set & SUPPORTED_ATTR_SET;
+    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;
+}
 
-    stat = modecfg_build_msg(st, &md->rbody
-                              , ISAKMP_CFG_ACK
-                              , &ia
-                              , isama_id);
+/* 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 */
@@ -622,6 +1017,62 @@ modecfg_inI2(struct msg_digest *md)
        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;
-    return STF_OK;
+    if (st->st_xauth.status)
+    {
+       return STF_OK;
+    }
+    else
+    {
+       delete_state(st);
+       return STF_IGNORE;
+    }
 }