/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipeap_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #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, ""); 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, ""); 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 **************************************************************************** */