948 lines
32 KiB
C
948 lines
32 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipeap_peap.c,v $ $Revision: 1.11 $
|
|
* $Source: /home/interpeak/CVSRoot/ipeap/src/ipeap_peap.c,v $
|
|
* $Author: hchowdhu $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Roger Boden <roger@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipeap_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include "ipeap.h"
|
|
#include "ipeap_h.h"
|
|
#include "ipeap_common_tls.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPEAP_PEAP_VERSION_MASK 3
|
|
#define IPEAP_PEAP_GET_VERSION(flags) (flags & IPEAP_PEAP_VERSION_MASK)
|
|
#define IPEAP_PEAP_SET_VERSION(flags, version) (((flags) & IPEAP_PEAP_VERSION_MASK) | (version))
|
|
|
|
#define IPEAP_PEAP_VERSION_NOTSET (-1)
|
|
/* Min/Max supported versions + defaults values if sysvar configs are absent or incorrect */
|
|
#define IPEAP_PEAP_MIN_VERSION 0
|
|
#define IPEAP_PEAP_MAX_VERSION 1
|
|
|
|
#define IPEAP_PEAP_START_MASK 0x20
|
|
#define IPEAP_PEAP_IS_START(flags) (flags & IPEAP_PEAP_START_MASK)
|
|
|
|
#define IPEAP_PEAP_V0_AVP_TYPE_ACK_RESULT 3
|
|
#define IPEAP_PEAP_V0_AVP_TYPE_STATUS_SUCCESS 1
|
|
#define IPEAP_PEAP_V0_AVP_TYPE_STATUS_FAILURE 2
|
|
|
|
#define IPEAP_PEAP_V1_PRF_LABEL_DEFAULT "client EAP encryption"
|
|
#define IPEAP_PEAP_V1_PRF_LABEL_ALT "client PEAP encryption"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
typedef struct Ipeap_peap_ctx_st
|
|
{
|
|
int min_ver, max_ver, curr_ver; /* Min/Max/negotiated version */
|
|
Ipeap_tls_suppl_ctx* tls_ctx;
|
|
Ipeap_ctx* phase2_ctx;
|
|
char peap_prefix[16];
|
|
Ip_u8 msg_id; /* msg Id of last received request */
|
|
char peapv1_prf_label[32];
|
|
} Ipeap_peap_ctx;
|
|
|
|
typedef struct Ipeap_peap_msg_st
|
|
{
|
|
Ip_u8 code;
|
|
Ip_u8 id;
|
|
Ip_u16 length;
|
|
Ip_u8 type;
|
|
Ip_u8 flags; /* flags & version */
|
|
Ip_u8 data[1];
|
|
} Ipeap_peap_msg;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
IP_STATIC Ip_err ipeap_peap_suppl_process_msg(struct Ipeap_ctx_st *ctx,
|
|
struct Ipeap_pkt_st *in_pkt,
|
|
struct Ipeap_pkt_st *reply_pkt);
|
|
IP_STATIC Ip_err ipeap_peap_suppl_init(struct Ipeap_ctx_st *ctx);
|
|
IP_STATIC Ip_err ipeap_peap_suppl_free(Ipeap_ctx *ctx);
|
|
IP_STATIC Ip_err ipeap_peap_suppl_get_key(struct Ipeap_ctx_st* ctx, Ip_u8* key, int key_len);
|
|
|
|
IP_STATIC Ip_err ipeap_peap_handle_v0(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt);
|
|
IP_STATIC Ip_err ipeap_peap_handle_v1(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt);
|
|
IP_STATIC Ip_err ipeap_peap_check_ver(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt);
|
|
IP_STATIC Ip_err ipeap_peap_get_avp(Ip_u8 *buf, int buf_len, Ip_bool *mandatory, Ip_u16 *type, Ip_u8 *avp, Ip_u16 *avp_len);
|
|
IP_STATIC Ip_err ipeap_peap_put_avp(Ip_u8 *buf, int buf_len, Ip_bool mandatory, Ip_u16 type, Ip_u8 *avp, Ip_u16 avp_len);
|
|
IP_PUBLIC Ip_err ipeap_peap_suppl_display_status(struct Ipeap_ctx_st* ctx, char *str);
|
|
IP_STATIC Ip_err ipeap_peap_handle_user_input(Ipeap_ctx* ctx,
|
|
Ipeap_user_input* ui,
|
|
struct Ipeap_pkt_st *reply_pkt);
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
IP_STATIC Ipeap_methods ipeap_peap_suppl_methods =
|
|
{
|
|
ipeap_peap_suppl_process_msg,
|
|
ipeap_peap_suppl_init,
|
|
ipeap_peap_suppl_free,
|
|
ipeap_peap_suppl_get_key,
|
|
ipeap_peap_handle_user_input,
|
|
ipeap_peap_suppl_display_status,
|
|
IP_NULL
|
|
};
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_load_config
|
|
*===========================================================================
|
|
* Description: Load configuration from system variables.
|
|
* Parameters:
|
|
* Returns:
|
|
* IPCOM_SUCCESS
|
|
* IPCOM_ERR_INVALID_ARG
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_load_config(Ipeap_peap_ctx* peap_ctx)
|
|
{
|
|
Ip_err err = IPCOM_SUCCESS;
|
|
Ip_size_t len = sizeof(peap_ctx->peapv1_prf_label);
|
|
|
|
if (!ipcom_sysvar_get("ipeap.peap.v1.prf_label", peap_ctx->peapv1_prf_label, &len)) {
|
|
IPCOM_LOG1(WARNING, "%sfailed to retrieve ipeap.peap.v1.prf_label sysvar", peap_ctx->peap_prefix);
|
|
err = IPCOM_ERR_FAILED;
|
|
} else if ((ipcom_strcmp(peap_ctx->peapv1_prf_label, IPEAP_PEAP_V1_PRF_LABEL_DEFAULT) != 0) &&
|
|
(ipcom_strcmp(peap_ctx->peapv1_prf_label, IPEAP_PEAP_V1_PRF_LABEL_ALT) != 0)) {
|
|
IPCOM_LOG2(WARNING, "%sInvalid ipeap.peap.v1.prf_label sysvar value: \"%s\"", peap_ctx->peap_prefix, peap_ctx->peapv1_prf_label);
|
|
err = IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if (err == IPCOM_ERR_FAILED) {
|
|
ipcom_strncpy(peap_ctx->peapv1_prf_label, IPEAP_PEAP_V1_PRF_LABEL_DEFAULT, sizeof(peap_ctx->peapv1_prf_label));
|
|
err = IPCOM_SUCCESS;
|
|
}
|
|
IPCOM_LOG2(INFO, "%sSetting peapv1 prf label to: \"%s\"", peap_ctx->peap_prefix, peap_ctx->peapv1_prf_label);
|
|
|
|
peap_ctx->min_ver = ipcom_sysvar_get_as_int("ipeap.peap.min_version", IPEAP_PEAP_MIN_VERSION);
|
|
peap_ctx->max_ver = ipcom_sysvar_get_as_int("ipeap.peap.max_version", IPEAP_PEAP_MAX_VERSION);
|
|
|
|
if ((peap_ctx->min_ver < IPEAP_PEAP_MIN_VERSION) ||
|
|
(peap_ctx->max_ver > IPEAP_PEAP_MAX_VERSION) ||
|
|
(peap_ctx->min_ver > peap_ctx->max_ver)) {
|
|
IPCOM_LOG3(ERR, "%sInvalid version configuration, using default values: (min_ver=%d, max_ver=%d)",
|
|
peap_ctx->peap_prefix, IPEAP_PEAP_MIN_VERSION, IPEAP_PEAP_MAX_VERSION);
|
|
peap_ctx->min_ver = IPEAP_PEAP_MIN_VERSION;
|
|
peap_ctx->max_ver = IPEAP_PEAP_MAX_VERSION;
|
|
err = IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_ttls_suppl_process_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_suppl_process_msg(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt)
|
|
{
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
Ipeap_tls_msg *msg;
|
|
Ipeap_peap_ctx* peap_ctx;
|
|
|
|
msg = IPEAP_PKT2TLSMSG(in_pkt);
|
|
peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
|
|
ip_assert(peap_ctx);
|
|
ip_assert(peap_ctx->tls_ctx);
|
|
|
|
switch(msg->code)
|
|
{
|
|
case IPEAP_MSG_REQUEST:
|
|
if (msg->type != IPEAP_TYPE_PEAP)
|
|
{
|
|
IPCOM_LOG1(CRIT, "%s: Not a PEAP message!", peap_ctx->peap_prefix);
|
|
return IPEAP_DROPPED_PKT;
|
|
}
|
|
/* 1st we need to make sure we support the PEAP version sent in the 1st EAP-REQUEST
|
|
* or negotiate a version
|
|
*/
|
|
rc = ipeap_peap_check_ver(ctx, in_pkt, out_pkt);
|
|
if (rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
|
|
rc = ipeap_common_tls_handle_frag_and_handshake(ctx, peap_ctx->tls_ctx, in_pkt, out_pkt);
|
|
|
|
switch(rc)
|
|
{
|
|
case IPEAP_TLS_HANDSHAKE_COMPLETE:
|
|
/* Send empty response packet, wait for EAP packets over the TLS tunnel */
|
|
if(IPEAP_PKT_GET_SPACE_LEFT(out_pkt) < 6)
|
|
{
|
|
IPCOM_LOG2(ERR, "%stoo small out pkt: space left: %d", peap_ctx->peap_prefix, IPEAP_PKT_GET_SPACE_LEFT(out_pkt));
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
rc = ipeap_tls_pkt_set_header(out_pkt, IPEAP_PKT_RESPONSE, msg->id, 6, ctx->meth_id, (Ip_u8)peap_ctx->curr_ver);
|
|
if(rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
out_pkt->end += 6;
|
|
return IPEAP_PENDING_SEND_OUT_PKT;
|
|
|
|
case IPEAP_TLS_DATA_AVAILABLE:
|
|
/* An EAP packet is received over the TLS tunnel. Process */
|
|
switch (peap_ctx->curr_ver) {
|
|
case 0:
|
|
return ipeap_peap_handle_v0(ctx, in_pkt, out_pkt);
|
|
case 1:
|
|
return ipeap_peap_handle_v1(ctx, in_pkt, out_pkt);
|
|
default:
|
|
IPCOM_LOG2(CRIT, "%sunexpected curr_ver %d", peap_ctx->peap_prefix, peap_ctx->curr_ver);
|
|
return IPEAP_DROPPED_PKT;
|
|
}
|
|
default:
|
|
{
|
|
/* Need to set the proper flags */
|
|
Ipeap_tls_msg* msg = IPEAP_PKT2TLSMSG(out_pkt);
|
|
msg->flags = (Ip_u8)IPEAP_PEAP_SET_VERSION(msg->flags, peap_ctx->curr_ver);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IPEAP_MSG_RESPONSE:
|
|
IPCOM_LOG1(ERR, "%sreceived response msg, dropping msg", peap_ctx->peap_prefix);
|
|
return IPEAP_DROPPED_PKT;
|
|
|
|
case IPEAP_MSG_FAILURE:
|
|
IPCOM_LOG1(INFO, "%sreceived EAP failure", peap_ctx->peap_prefix);
|
|
return IPEAP_FAILURE;
|
|
|
|
case IPEAP_MSG_SUCCESS:
|
|
ctx->state = IPEAP_STATE_AUTHENTICATED;
|
|
IPCOM_LOG1(INFO, "%sreceived EAP success", peap_ctx->peap_prefix);
|
|
return IPEAP_SUCCESS;
|
|
|
|
default:
|
|
IPCOM_LOG1(ERR, "%sreceived unknown msg type: %d, dropping msg", peap_ctx->peap_prefix);
|
|
return IPEAP_DROPPED_PKT;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_suppl_init
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_suppl_init(Ipeap_ctx *eap_ctx)
|
|
{
|
|
Ipeap_peap_ctx *peap_ctx;
|
|
const char* peap_prefix = "PEAP: ";
|
|
|
|
peap_ctx = ipcom_malloc(sizeof(Ipeap_peap_ctx));
|
|
if(!peap_ctx)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
ipcom_memset(peap_ctx, 0, sizeof(Ipeap_peap_ctx));
|
|
ipeap_peap_load_config(peap_ctx);
|
|
peap_ctx->curr_ver = IPEAP_PEAP_VERSION_NOTSET;
|
|
eap_ctx->mech_data = peap_ctx;
|
|
strncpy(peap_ctx->peap_prefix, peap_prefix, sizeof(peap_ctx->peap_prefix));
|
|
|
|
peap_ctx->tls_ctx = ipeap_common_tls_ctx_new();
|
|
if(!peap_ctx->tls_ctx)
|
|
{
|
|
ipcom_free(peap_ctx);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
peap_ctx->phase2_ctx = ipeap_ctx_new(IPEAP_MODE_SUPPLICANT);
|
|
if(!peap_ctx->phase2_ctx)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sfailed to allocate inner EAP context", peap_ctx->peap_prefix);
|
|
(void)ipeap_peap_suppl_free(eap_ctx);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(ipcom_strlen(eap_ctx->syslog_prefix)+ipcom_strlen(peap_prefix) < sizeof(eap_ctx->syslog_prefix))
|
|
{
|
|
ipcom_strcpy(peap_ctx->phase2_ctx->syslog_prefix, eap_ctx->syslog_prefix);
|
|
ipcom_strcat(peap_ctx->phase2_ctx->syslog_prefix, peap_prefix);
|
|
}
|
|
else
|
|
{
|
|
ipcom_strcpy(peap_ctx->phase2_ctx->syslog_prefix, "<trunc>");
|
|
ipcom_strcat(peap_ctx->phase2_ctx->syslog_prefix, peap_prefix);
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_suppl_free
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_suppl_free(Ipeap_ctx *ctx)
|
|
{
|
|
Ipeap_peap_ctx* peap_ctx = ctx->mech_data;
|
|
|
|
ip_assert(peap_ctx);
|
|
|
|
if(peap_ctx)
|
|
{
|
|
if(peap_ctx->phase2_ctx)
|
|
ipeap_ctx_free(peap_ctx->phase2_ctx);
|
|
if(peap_ctx->tls_ctx)
|
|
ipeap_common_tls_ctx_free(peap_ctx->tls_ctx);
|
|
ipcom_free(peap_ctx);
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_handle_v0
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_handle_v0(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt)
|
|
{
|
|
Ip_err rc = IPCOM_ERR_FAILED;
|
|
Ipeap_tls_msg *msg;
|
|
Ipeap_msg *inner_msg;
|
|
Ipeap_msg *inner_out_msg;
|
|
Ipeap_pkt* tunneled_in_pkt = IP_NULL;
|
|
Ipeap_pkt* tunneled_out_pkt = IP_NULL;
|
|
int ssl_rc;
|
|
Ip_bool reconstruct_hdr = IP_TRUE;
|
|
Ipeap_peap_ctx *peap_ctx;
|
|
|
|
peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
msg = IPEAP_PKT2TLSMSG(in_pkt);
|
|
|
|
tunneled_in_pkt = ipeap_pkt_new(IPEAP_MTU);
|
|
if(!tunneled_in_pkt)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sout of memory", peap_ctx->peap_prefix);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
tunneled_out_pkt = ipeap_pkt_new(IPEAP_MTU);
|
|
if(!tunneled_out_pkt)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sout of memory", peap_ctx->peap_prefix);
|
|
rc = IPCOM_ERR_NO_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* In PEAPv0 the code, type and identifier fields are omitted in all packets
|
|
* except for EAP extension packets (type 33). Leave some room in
|
|
* tunneled_in_pkt to enable the missing field to be re-created from the
|
|
* outer packet. */
|
|
tunneled_in_pkt->start += 4;
|
|
tunneled_in_pkt->end += 4;
|
|
|
|
ssl_rc = SSL_read(peap_ctx->tls_ctx->ssl,
|
|
IPEAP_PKT_GET_END_PTR(tunneled_in_pkt),
|
|
IPEAP_PKT_GET_SPACE_LEFT(tunneled_in_pkt));
|
|
|
|
if(ssl_rc < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sfailed to read inner EAP packet", peap_ctx->peap_prefix);
|
|
goto cleanup;
|
|
}
|
|
tunneled_in_pkt->end += ssl_rc;
|
|
|
|
inner_msg = IPEAP_PKT2MSG(tunneled_in_pkt);
|
|
if(IPEAP_PKT_GET_LEN(tunneled_in_pkt)>=5)
|
|
{
|
|
/* Assume a full header is received and make some checks if this is the case */
|
|
if(inner_msg->code == IPEAP_PKT_REQUEST && inner_msg->type == IPEAP_TYPE_EXTENSIONS)
|
|
reconstruct_hdr = IP_FALSE;
|
|
/* Freeradius sends a full request-identity in PEAPv0 */
|
|
else if(IPEAP_PKT_GET_LEN(tunneled_in_pkt)==5
|
|
&& inner_msg->code == IPEAP_PKT_REQUEST
|
|
&& inner_msg->type == IPEAP_TYPE_IDENTITY)
|
|
{
|
|
reconstruct_hdr = IP_FALSE;
|
|
}
|
|
}
|
|
|
|
if(reconstruct_hdr)
|
|
{
|
|
tunneled_in_pkt->start -= 4;
|
|
inner_msg = IPEAP_PKT2MSG(tunneled_in_pkt);
|
|
inner_msg->code = msg->code;
|
|
inner_msg->id = msg->id;
|
|
inner_msg->length = ip_htons(ssl_rc+4);
|
|
}
|
|
|
|
IPCOM_LOG5(DEBUG2, "%sreceived inner packet, code: %d, id: %d, len: %d, type: %d",
|
|
peap_ctx->peap_prefix, inner_msg->code, inner_msg->id, ip_ntohs(inner_msg->length), inner_msg->type);
|
|
|
|
/* Process pkt */
|
|
if(inner_msg->type != IPEAP_TYPE_EXTENSIONS)
|
|
{
|
|
rc = ipeap_process_pkt(peap_ctx->phase2_ctx, tunneled_in_pkt, tunneled_out_pkt);
|
|
tunneled_out_pkt->start += 4;
|
|
}
|
|
else
|
|
{
|
|
/* Handle extensions packet */
|
|
Ip_bool mandatory;
|
|
Ip_u16 avp_type;
|
|
Ip_u16 avp_len;
|
|
Ip_u16 status;
|
|
Ip_u8* avp_buf;
|
|
int avp_buf_len;
|
|
|
|
avp_buf = &IPEAP_PKT_GET_START_PTR(tunneled_in_pkt)[5];
|
|
avp_buf_len = IPEAP_PKT_GET_LEN(tunneled_in_pkt)-5;
|
|
avp_len = sizeof(status);
|
|
if(ipeap_peap_get_avp(avp_buf, avp_buf_len, &mandatory, &avp_type, (Ip_u8*)&status, &avp_len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sfailed to unpack AVP in extension packet", peap_ctx->peap_prefix);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(avp_type != IPEAP_PEAP_V0_AVP_TYPE_ACK_RESULT)
|
|
{
|
|
IPCOM_LOG2(ERR, "%sUnexpected AVP type received in extension packet: %d", peap_ctx->peap_prefix, avp_type);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG, "%sReceived extension packet, status: %d", peap_ctx->peap_prefix, ip_ntohs(status));
|
|
|
|
switch(ip_ntohs(status))
|
|
{
|
|
case IPEAP_PEAP_V0_AVP_TYPE_STATUS_SUCCESS:
|
|
case IPEAP_PEAP_V0_AVP_TYPE_STATUS_FAILURE:
|
|
inner_out_msg = IPEAP_PKT2MSG(tunneled_out_pkt);
|
|
(void)ipeap_pkt_set_header(tunneled_out_pkt, IPEAP_MSG_RESPONSE, inner_msg->id, 11, IPEAP_TYPE_EXTENSIONS);
|
|
tunneled_out_pkt->end += 5;
|
|
|
|
if(ipeap_peap_put_avp(inner_out_msg->data, IPEAP_PKT_GET_SPACE_LEFT(tunneled_out_pkt), mandatory,
|
|
avp_type, (Ip_u8*)&status, 2) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sfailed to put status AVP in extension packet", peap_ctx->peap_prefix);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
tunneled_out_pkt->end += 6;
|
|
rc = IPEAP_PENDING_SEND_OUT_PKT;
|
|
break;
|
|
|
|
default:
|
|
IPCOM_LOG2(ERR, "%sUnexpected status received in extension packet: %d", peap_ctx->peap_prefix, status);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if(rc == IPEAP_PENDING_SEND_OUT_PKT)
|
|
{
|
|
ssl_rc = SSL_write(peap_ctx->tls_ctx->ssl,
|
|
IPEAP_PKT_GET_START_PTR(tunneled_out_pkt),
|
|
IPEAP_PKT_GET_LEN(tunneled_out_pkt));
|
|
if(ssl_rc != IPEAP_PKT_GET_LEN(tunneled_out_pkt))
|
|
{
|
|
IPCOM_LOG3(ERR, "%sfailed to write inner EAP packet, len: %d, ssl_rc: %d",
|
|
peap_ctx->peap_prefix, IPEAP_PKT_GET_LEN(tunneled_out_pkt), ssl_rc);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
rc = ipeap_common_tls_make_out_frag_pkt(peap_ctx->tls_ctx, out_pkt, IPEAP_MSG_RESPONSE, msg->id, IPEAP_TYPE_PEAP);
|
|
}
|
|
|
|
cleanup:
|
|
if(tunneled_in_pkt)
|
|
ipeap_pkt_free(tunneled_in_pkt);
|
|
if(tunneled_out_pkt)
|
|
ipeap_pkt_free(tunneled_out_pkt);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_handle_v1
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_handle_v1(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt)
|
|
{
|
|
Ip_err rc = IPCOM_ERR_FAILED;
|
|
Ipeap_tls_msg *in_msg, *out_msg;
|
|
Ipeap_msg *inner_msg;
|
|
Ipeap_pkt* tunneled_in_pkt = IP_NULL;
|
|
Ipeap_pkt* tunneled_out_pkt = IP_NULL;
|
|
int ssl_rc;
|
|
Ipeap_peap_ctx *peap_ctx;
|
|
|
|
peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
in_msg = IPEAP_PKT2TLSMSG(in_pkt);
|
|
|
|
peap_ctx->msg_id = in_msg->id; /* Save msg id for when we send response */
|
|
|
|
tunneled_in_pkt = ipeap_pkt_new(IPEAP_MTU);
|
|
if(!tunneled_in_pkt)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sout of memory", peap_ctx->peap_prefix);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
tunneled_out_pkt = ipeap_pkt_new(IPEAP_MTU);
|
|
if(!tunneled_out_pkt)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sout of memory", peap_ctx->peap_prefix);
|
|
rc = IPCOM_ERR_NO_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
ssl_rc = SSL_read(peap_ctx->tls_ctx->ssl,
|
|
IPEAP_PKT_GET_END_PTR(tunneled_in_pkt),
|
|
IPEAP_PKT_GET_SPACE_LEFT(tunneled_in_pkt));
|
|
if(ssl_rc < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sfailed to read inner EAP packet", peap_ctx->peap_prefix);
|
|
goto cleanup;
|
|
}
|
|
tunneled_in_pkt->end += ssl_rc;
|
|
|
|
inner_msg = IPEAP_PKT2MSG(tunneled_in_pkt);
|
|
IPCOM_LOG5(DEBUG2, "%sreceived inner packet, code: %d, id: %d, len: %d, type: %d",
|
|
peap_ctx->peap_prefix, inner_msg->code, inner_msg->id, ip_ntohs(inner_msg->length), inner_msg->type);
|
|
|
|
/* Process pkt */
|
|
rc = ipeap_process_pkt(peap_ctx->phase2_ctx, tunneled_in_pkt, tunneled_out_pkt);
|
|
|
|
if(rc == IPEAP_PENDING_SEND_OUT_PKT)
|
|
{
|
|
ssl_rc = SSL_write(peap_ctx->tls_ctx->ssl,
|
|
IPEAP_PKT_GET_START_PTR(tunneled_out_pkt),
|
|
IPEAP_PKT_GET_LEN(tunneled_out_pkt));
|
|
if(ssl_rc != IPEAP_PKT_GET_LEN(tunneled_out_pkt))
|
|
{
|
|
IPCOM_LOG3(ERR, "%sfailed to write inner EAP packet, len: %d, ssl_rc: %d",
|
|
peap_ctx->peap_prefix, IPEAP_PKT_GET_LEN(tunneled_out_pkt), ssl_rc);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
rc = ipeap_common_tls_make_out_frag_pkt(peap_ctx->tls_ctx, out_pkt, IPEAP_MSG_RESPONSE, in_msg->id, IPEAP_TYPE_PEAP);
|
|
out_msg = IPEAP_PKT2TLSMSG(out_pkt);
|
|
out_msg->flags = (Ip_u8)IPEAP_PEAP_SET_VERSION(out_msg->flags, peap_ctx->curr_ver);
|
|
} else if (rc == IPEAP_SUCCESS) {
|
|
/* for PEAPv1, we need to monitor for an inner EAP success and reply with an unencrypted (non-tunneled) response */
|
|
if(IPEAP_PKT_GET_SPACE_LEFT(out_pkt) < 6)
|
|
{
|
|
IPCOM_LOG2(ERR, "%stoo small out pkt: space left: %d", peap_ctx->peap_prefix, IPEAP_PKT_GET_SPACE_LEFT(out_pkt));
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
rc = ipeap_tls_pkt_set_header(out_pkt, IPEAP_PKT_RESPONSE, in_msg->id, 6, ctx->meth_id, (Ip_u8)peap_ctx->curr_ver);
|
|
if(rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
out_pkt->end += 6;
|
|
rc = IPEAP_PENDING_SEND_OUT_PKT;
|
|
}
|
|
|
|
cleanup:
|
|
if(tunneled_in_pkt)
|
|
ipeap_pkt_free(tunneled_in_pkt);
|
|
if(tunneled_out_pkt)
|
|
ipeap_pkt_free(tunneled_out_pkt);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_check_ver
|
|
*===========================================================================
|
|
* Description:
|
|
* Checks the version and performs version negotiation if necessary.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_check_ver(Ipeap_ctx *ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt)
|
|
{
|
|
Ip_err rc = IPCOM_ERR_FAILED;
|
|
Ipeap_peap_msg *msg;
|
|
Ipeap_peap_ctx *peap_ctx;
|
|
int version;
|
|
const char* peapv0_prefix = "PEAPv0: ";
|
|
const char* peapv1_prefix = "PEAPv1: ";
|
|
const char* peap_prefix;
|
|
|
|
peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
msg = (Ipeap_peap_msg *)IPEAP_PKT2MSG(in_pkt);
|
|
version = IPEAP_PEAP_GET_VERSION(msg->flags);
|
|
|
|
if (peap_ctx->curr_ver != IPEAP_PEAP_VERSION_NOTSET) {
|
|
/* A version was negotiated/set, so verify we are still using this version */
|
|
if (version != peap_ctx->curr_ver) {
|
|
IPCOM_LOG3(CRIT, "%s: ipeap_peap_check_ver :: wrong PEAP version %d (expecting %d)!",
|
|
peap_ctx->peap_prefix, version, peap_ctx->curr_ver);
|
|
rc = IPEAP_DROPPED_PKT;
|
|
} else {
|
|
rc = IPCOM_SUCCESS;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* We never negotiated/set a version, so do so now */
|
|
if (msg->code != IPEAP_MSG_REQUEST)
|
|
{
|
|
IPCOM_LOG1(CRIT, "%s: ipeap_peap_check_ver :: Got non EAP REQUEST before negotiating PEAP version!", peap_ctx->peap_prefix);
|
|
return IPEAP_DROPPED_PKT;
|
|
}
|
|
|
|
if ((version >= peap_ctx->min_ver) && (version <= peap_ctx->max_ver)) {
|
|
peap_ctx->curr_ver = version;
|
|
rc = IPCOM_SUCCESS;
|
|
if (peap_ctx->curr_ver == 0)
|
|
peap_prefix = peapv0_prefix;
|
|
else if (peap_ctx->curr_ver == 1)
|
|
peap_prefix = peapv1_prefix;
|
|
else
|
|
return rc;
|
|
|
|
strncpy(peap_ctx->peap_prefix, peap_prefix, sizeof(peap_ctx->peap_prefix));
|
|
if(ipcom_strlen(ctx->syslog_prefix)+ipcom_strlen(peap_prefix) < sizeof(ctx->syslog_prefix))
|
|
{
|
|
ipcom_strcpy(peap_ctx->phase2_ctx->syslog_prefix, ctx->syslog_prefix);
|
|
ipcom_strcat(peap_ctx->phase2_ctx->syslog_prefix, peap_prefix);
|
|
}
|
|
else
|
|
{
|
|
ipcom_strcpy(peap_ctx->phase2_ctx->syslog_prefix, "<trunc>");
|
|
ipcom_strcat(peap_ctx->phase2_ctx->syslog_prefix, peap_prefix);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* server version not supported */
|
|
if (version < peap_ctx->min_ver) {
|
|
/* Since according to "draft-josefsson-pppext-eap-tls-eap-05.txt",
|
|
* the server sends us its highest supported version,
|
|
* in this case we will not be able to come to an agreement so
|
|
* we send a NAK suggesting alternate methods
|
|
*/
|
|
if(IPEAP_PKT_GET_SPACE_LEFT(out_pkt) < 3)
|
|
{
|
|
IPCOM_LOG2(ERR, "%stoo small out pkt: space left: %d", peap_ctx->peap_prefix, IPEAP_PKT_GET_SPACE_LEFT(out_pkt));
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
IPCOM_LOG3(ERR, "%sserver PEAP version %d lower than min supported version %d. Sending NAK.",
|
|
peap_ctx->peap_prefix, version, peap_ctx->min_ver);
|
|
ipeap_mech_type_set_enabled(ctx, msg->type, IP_FALSE); /* disable this EAP temporarily for this ctx to avoid endless negotiation loop */
|
|
ipeap_make_nak_response(ctx, in_pkt, out_pkt);
|
|
ipeap_mech_type_set_enabled(ctx, msg->type, IP_TRUE); /* Reenable this EAP */
|
|
return IPEAP_PENDING_SEND_OUT_PKT;
|
|
}
|
|
/* Server's version is higher than our max, so indicate highest version we support */
|
|
if(IPEAP_PKT_GET_SPACE_LEFT(out_pkt) < 6)
|
|
{
|
|
IPCOM_LOG2(ERR, "%stoo small out pkt: space left: %d", peap_ctx->peap_prefix, IPEAP_PKT_GET_SPACE_LEFT(out_pkt));
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
rc = ipeap_tls_pkt_set_header(out_pkt, IPEAP_PKT_RESPONSE, msg->id, 6, ctx->meth_id, (Ip_u8)peap_ctx->max_ver);
|
|
if(rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
out_pkt->end += 6;
|
|
rc = IPEAP_PENDING_SEND_OUT_PKT;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_suppl_get_key
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_suppl_get_key(struct Ipeap_ctx_st* ctx, Ip_u8* key, int key_len)
|
|
{
|
|
Ipeap_peap_ctx* peap_ctx;
|
|
|
|
peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
|
|
if (peap_ctx->curr_ver == 0) {
|
|
return ipeap_tls_common_generate_key(peap_ctx->tls_ctx, "client EAP encryption", key, key_len);
|
|
}
|
|
return ipeap_tls_common_generate_key(peap_ctx->tls_ctx, peap_ctx->peapv1_prf_label, key, key_len);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_get_avp
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_get_avp(Ip_u8 *buf, int buf_len, Ip_bool *mandatory, Ip_u16 *type, Ip_u8 *avp, Ip_u16 *avp_len)
|
|
{
|
|
Ip_u16 tmp_type;
|
|
Ip_u16 tmp_len;
|
|
|
|
if(buf_len < 4)
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
tmp_len = IP_GET_NTOHS(buf+2);
|
|
if(tmp_len > *avp_len)
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
if(tmp_len > (buf_len-4))
|
|
return IPCOM_ERR_FAILED;;
|
|
|
|
if(*buf & 0x80)
|
|
*mandatory = IP_TRUE;
|
|
else
|
|
*mandatory = IP_FALSE;
|
|
|
|
tmp_type = IP_GET_NTOHS(buf);
|
|
tmp_type = (Ip_u16)(tmp_type & 0x3fff); /* Remove mandatory and reserved bits */
|
|
|
|
*type = tmp_type;
|
|
*avp_len = tmp_len;
|
|
ipcom_memcpy(avp, buf+4, tmp_len);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_put_avp
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_put_avp(Ip_u8 *buf, int buf_len, Ip_bool mandatory, Ip_u16 type, Ip_u8 *avp, Ip_u16 avp_len)
|
|
{
|
|
|
|
if(buf_len < (avp_len+4))
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
|
|
ipcom_memset(buf, 0, (Ip_size_t)(avp_len+4));
|
|
|
|
IP_SET_HTONS(buf, type);
|
|
if(mandatory)
|
|
buf[0] |= 0x80;
|
|
IP_SET_HTONS(buf+2, avp_len);
|
|
ipcom_memcpy(buf+4,avp, avp_len);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_handle_user_input
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_peap_handle_user_input(Ipeap_ctx* ctx, Ipeap_user_input* ui, Ipeap_pkt* out_pkt) {
|
|
Ip_err rc = IPCOM_ERR_FAILED;
|
|
Ipeap_tls_msg *out_msg;
|
|
Ipeap_pkt* tunneled_out_pkt = IP_NULL;
|
|
int ssl_rc;
|
|
Ipeap_peap_ctx* peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
|
|
if (peap_ctx->curr_ver != 1) {
|
|
IPCOM_LOG1(ERR, "%s doesn't support user input", peap_ctx->peap_prefix);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if (!peap_ctx->phase2_ctx) {
|
|
IPCOM_LOG1(ERR, "%sNo inner type to handle user input", peap_ctx->peap_prefix);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
tunneled_out_pkt = ipeap_pkt_new(IPEAP_MTU);
|
|
if(!tunneled_out_pkt)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sout of memory", peap_ctx->peap_prefix);
|
|
rc = IPCOM_ERR_NO_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = ipeap_handle_user_input(peap_ctx->phase2_ctx, ui, tunneled_out_pkt);
|
|
|
|
if (rc != IPEAP_PENDING_SEND_OUT_PKT)
|
|
goto cleanup;
|
|
|
|
ssl_rc = SSL_write(peap_ctx->tls_ctx->ssl,
|
|
IPEAP_PKT_GET_START_PTR(tunneled_out_pkt),
|
|
IPEAP_PKT_GET_LEN(tunneled_out_pkt));
|
|
if(ssl_rc != IPEAP_PKT_GET_LEN(tunneled_out_pkt))
|
|
{
|
|
IPCOM_LOG3(ERR, "%sfailed to write inner EAP packet, len: %d, ssl_rc: %d",
|
|
peap_ctx->peap_prefix, IPEAP_PKT_GET_LEN(tunneled_out_pkt), ssl_rc);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
rc = ipeap_common_tls_make_out_frag_pkt(peap_ctx->tls_ctx, out_pkt, IPEAP_MSG_RESPONSE, peap_ctx->msg_id, IPEAP_TYPE_PEAP);
|
|
out_msg = IPEAP_PKT2TLSMSG(out_pkt);
|
|
out_msg->flags = (Ip_u8)IPEAP_PEAP_SET_VERSION(out_msg->flags, peap_ctx->curr_ver);
|
|
|
|
cleanup:
|
|
if(tunneled_out_pkt)
|
|
ipeap_pkt_free(tunneled_out_pkt);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_suppl_methods_get
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ipeap_methods*
|
|
ipeap_peap_suppl_methods_get(void)
|
|
{
|
|
return &ipeap_peap_suppl_methods;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_peap_suppl_display_status
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipeap_peap_suppl_display_status(struct Ipeap_ctx_st* ctx, char *str) {
|
|
Ipeap_peap_ctx* peap_ctx;
|
|
peap_ctx = (Ipeap_peap_ctx*)ctx->mech_data;
|
|
|
|
ipcom_printf("%s", peap_ctx->peap_prefix);
|
|
if (peap_ctx->phase2_ctx) {
|
|
ipeap_display_status((struct Ipeap_ctx_st*)peap_ctx->phase2_ctx, "Inner ");
|
|
} else
|
|
ipcom_printf("INNER EAP NOT CREATED\n");
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|