Files
m241Neelima/components/ip_net2-6.9/ipeap/src/ipeap_peap.c
T
2026-07-30 11:19:44 +05:30

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