Shad Ansari | 2f7f9be | 2017-06-07 13:34:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | <:copyright-BRCM:2016:DUAL/GPL:standard |
| 3 | |
| 4 | Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| 5 | All Rights Reserved |
| 6 | |
| 7 | Unless you and Broadcom execute a separate written software license |
| 8 | agreement governing use of this software, this software is licensed |
| 9 | to you under the terms of the GNU General Public License version 2 |
| 10 | (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| 11 | with the following added to such license: |
| 12 | |
| 13 | As a special exception, the copyright holders of this software give |
| 14 | you permission to link this software with independent modules, and |
| 15 | to copy and distribute the resulting executable under terms of your |
| 16 | choice, provided that you also meet, for each linked independent |
| 17 | module, the terms and conditions of the license of that module. |
| 18 | An independent module is a module which is not derived from this |
| 19 | software. The special exception does not apply to any modifications |
| 20 | of the software. |
| 21 | |
| 22 | Not withstanding the above, under no circumstances may you combine |
| 23 | this software in any way with any other Broadcom software provided |
| 24 | under a license other than the GPL, without Broadcom's express prior |
| 25 | written consent. |
| 26 | |
| 27 | :> |
| 28 | */ |
| 29 | |
| 30 | #include "dpoe_eap_tls.h" |
| 31 | #include "bcmolt_buf.h" |
| 32 | #include "dpoe_sec_fsm.h" |
| 33 | |
| 34 | /* The destination multicast MAC address for EAPOL packets. */ |
| 35 | static const bcmos_mac_address eapol_dst_mac = { { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 } }; |
| 36 | |
| 37 | /* The length of the RSA key used in the server_key_exchange TLS message sent to the ONU during DPoE bi-directional |
| 38 | authentication. */ |
| 39 | #define DPOE_BI_RSA_KEY_SIZE 2048 |
| 40 | |
| 41 | /* 2 1200 byte certs + some overhead.... */ |
| 42 | #define EAPOL_PKT_SIZE 5000 |
| 43 | |
| 44 | typedef enum |
| 45 | { |
| 46 | EAP_CODE_REQUEST = 1, |
| 47 | EAP_CODE_RESPONSE, |
| 48 | EAP_CODE_SUCCESS, |
| 49 | EAP_CODE_FAILURE |
| 50 | } eap_code; |
| 51 | |
| 52 | typedef struct |
| 53 | { |
| 54 | uint8_t eap_code; |
| 55 | uint8_t id; |
| 56 | uint16_t length; |
| 57 | } eap_frame; |
| 58 | |
| 59 | #define EAP_FRAME_SIZE 4 |
| 60 | |
| 61 | typedef enum |
| 62 | { |
| 63 | EAP_TYPE_IDENTITY = 1, |
| 64 | EAP_TYPE_NOTIFICATION, |
| 65 | EAP_TYPE_NAK, |
| 66 | EAP_TYPE_MD5, |
| 67 | EAP_TYPE_OTP, |
| 68 | EAP_TYPE_GENERIC_TOKEN_CARD, |
| 69 | EAP_TYPE_TLS = 13 |
| 70 | } eap_type; |
| 71 | |
| 72 | #define TLS_MAJOR_VERSION 3 |
| 73 | #define TLS_MINOR_VERSION 2 |
| 74 | |
| 75 | /* First the EAP structures.... */ |
| 76 | typedef struct |
| 77 | { |
| 78 | uint8_t auth_sub_type; |
| 79 | uint8_t eap_tls_flags; |
| 80 | } eap_tls_hdr; |
| 81 | |
| 82 | #define EAP_TLS_HDR_SIZE 2 |
| 83 | |
| 84 | #define EAP_HDR_SIZE (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE) |
| 85 | |
| 86 | typedef enum |
| 87 | { |
| 88 | TLS_CHANGE_CIPHER_SPEC_ID = 20, |
| 89 | TLS_ALERT_ID = 21, |
| 90 | TLS_HANDSHAKE_ID = 22, |
| 91 | TLS_APPLICATION_DATA_ID = 23, |
| 92 | } tls_content_type; |
| 93 | |
| 94 | typedef struct |
| 95 | { |
| 96 | uint8_t content_type; |
| 97 | tls_protocol_version tls_version; |
| 98 | uint16_t tls_record_length; |
| 99 | } tls_record_hdr; |
| 100 | |
| 101 | #define TLS_RECORD_HDR_SIZE (3 + PROTOCOL_VERSION_SIZE) |
| 102 | |
| 103 | #define EAP_TLS_FLAG_LENGTH_OFFSET 7 |
| 104 | #define EAP_TLS_FLAG_MORE_OFFSET 6 |
| 105 | #define EAP_TLS_FLAG_START_OFFSET 5 |
| 106 | #define EAP_TLS_FLAG_LENGTH_BIT (1U << EAP_TLS_FLAG_LENGTH_OFFSET) |
| 107 | #define EAP_TLS_FLAG_MORE_BIT (1U << EAP_TLS_FLAG_MORE_OFFSET) |
| 108 | #define EAP_TLS_FLAG_START_BIT (1U << EAP_TLS_FLAG_START_OFFSET) |
| 109 | |
| 110 | typedef struct |
| 111 | { |
| 112 | eap_frame hdr; |
| 113 | eap_tls_hdr tls; |
| 114 | } eap_msg_buf; |
| 115 | |
| 116 | typedef struct |
| 117 | { |
| 118 | eapol_msg_hdr hdr; |
| 119 | eap_msg_buf eap_msg; |
| 120 | } eapol_msg; |
| 121 | |
| 122 | /* Now the TLS records. */ |
| 123 | typedef struct tls_session_id |
| 124 | { |
| 125 | uint8_t length; |
| 126 | uint8_t session_id[SIZE_OF_TLS_SESSION_ID]; |
| 127 | } tls_session_id; |
| 128 | |
| 129 | typedef enum |
| 130 | { |
| 131 | cm_null = 0 |
| 132 | } compression_method; |
| 133 | |
| 134 | typedef struct |
| 135 | { |
| 136 | uint8_t length; |
| 137 | uint8_t compression_method; |
| 138 | } compression_methods; |
| 139 | |
| 140 | typedef enum |
| 141 | { |
| 142 | RSA_SIGN = 1, |
| 143 | DSS_SIGN = 2, |
| 144 | RSA_FIXED_DH = 3, |
| 145 | DSS_FIXED_DH = 4, |
| 146 | RSA_EPHEMERAL_DH_RESERVED = 5, |
| 147 | DSS_EPHEMERAL_DH_RESERVED = 6, |
| 148 | FORTEZZA_DMS_RESERVED = 20, |
| 149 | } client_certificate_type; |
| 150 | |
| 151 | /* For 2048 bit RSA key */ |
| 152 | #define SIZE_OF_RSA_MODULUS 256 |
| 153 | #define SIZE_OF_RSA_EXPONENT 3 |
| 154 | |
| 155 | typedef enum |
| 156 | { |
| 157 | HS_HELLO_REQUEST = 0, |
| 158 | HS_CLIENT_HELLO = 1, |
| 159 | HS_SERVER_HELLO = 2, |
| 160 | HS_CERTIFICATE = 11, |
| 161 | HS_SERVER_KEY_EXCHANGE = 12, |
| 162 | HS_CERTIFICATE_REQUEST = 13, |
| 163 | HS_SERVER_HELLO_DONE = 14, |
| 164 | HS_CERTIFICATE_VERIFY = 15, |
| 165 | HS_CLIENT_KEY_EXCHANGE = 16, |
| 166 | HS_FINISHED = 20, |
| 167 | } handshake_type; |
| 168 | |
| 169 | typedef struct |
| 170 | { |
| 171 | uint8_t msg_id; |
| 172 | uint32_t msg_length; /* actually U24 */ |
| 173 | } tls_handshake; |
| 174 | |
| 175 | #define TLS_HANDSHAKE_SIZE 4 |
| 176 | |
| 177 | typedef struct |
| 178 | { |
| 179 | uint8_t *eapol; |
| 180 | uint8_t *eap; |
| 181 | uint8_t *eap_tls; |
| 182 | uint8_t *tls_rec; |
| 183 | } eap_length_fields; |
| 184 | |
| 185 | static f_dpoe_sec_cert_trust_cb _cert_trust; |
| 186 | static f_dpoe_sec_auth_cb _auth_complete; |
| 187 | |
| 188 | static void _buffer_hash_data(dpoe_sec_link_rec *link_rec, const uint8_t *buf, uint32_t len) |
| 189 | { |
| 190 | /* Parameter checks. */ |
| 191 | BUG_ON(link_rec == NULL); |
| 192 | BUG_ON(buf == NULL); |
| 193 | |
| 194 | dpoe_sec_sha1_update(&link_rec->auth_ctrl.trans_data.sha1_hash, buf, len); |
| 195 | dpoe_sec_md5_update(&link_rec->auth_ctrl.trans_data.md5_hash, buf, len); |
| 196 | } |
| 197 | |
| 198 | static void _dpoe_eap_tls_compute_master_session_key(auth_trans_data *trans_data) |
| 199 | { |
| 200 | uint8_t seed[(2* COUNT_OF_RANDOM_BYTES)]; |
| 201 | uint32_t seed_len = 0; |
| 202 | |
| 203 | /* Parameter checks. */ |
| 204 | BUG_ON(trans_data == NULL); |
| 205 | |
| 206 | /* The seed to the PRF is the client random value concatenated with the server random value. */ |
| 207 | memcpy(&seed[seed_len], trans_data->client_random, COUNT_OF_RANDOM_BYTES); |
| 208 | seed_len += COUNT_OF_RANDOM_BYTES; |
| 209 | memcpy(&seed[seed_len], trans_data->server_random, COUNT_OF_RANDOM_BYTES); |
| 210 | seed_len += COUNT_OF_RANDOM_BYTES; |
| 211 | |
| 212 | dpoe_sec_prf_expand_4346( |
| 213 | trans_data->pre_master_secret, |
| 214 | sizeof(trans_data->pre_master_secret), |
| 215 | (const uint8_t *)"master secret", |
| 216 | strlen("master secret"), |
| 217 | seed, |
| 218 | seed_len, |
| 219 | trans_data->master_secret, |
| 220 | sizeof(trans_data->master_secret)); |
| 221 | dpoe_sec_prf_expand_4346( |
| 222 | trans_data->master_secret, |
| 223 | sizeof(trans_data->master_secret), |
| 224 | (const uint8_t *)"client EAP encryption", |
| 225 | strlen("client EAP encryption"), |
| 226 | seed, |
| 227 | seed_len, |
| 228 | trans_data->key_material, |
| 229 | sizeof(trans_data->key_material)); |
| 230 | |
| 231 | memcpy(trans_data->master_session_key, trans_data->key_material, sizeof(trans_data->master_session_key)); |
| 232 | } |
| 233 | |
| 234 | static bcmos_bool _dpoe_eap_tls_prepare_security_data(dpoe_sec_link_rec *link_rec) |
| 235 | { |
| 236 | /* Parameter checks. */ |
| 237 | BUG_ON(link_rec == NULL); |
| 238 | |
| 239 | /* If bidirectional, compute the master session key and generate the RSA public/private key pair */ |
| 240 | if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI) |
| 241 | { |
| 242 | _dpoe_eap_tls_compute_master_session_key(&link_rec->auth_ctrl.trans_data); |
| 243 | } |
| 244 | |
| 245 | return BCMOS_TRUE; |
| 246 | } |
| 247 | |
| 248 | static bcmos_bool _dpoe_eap_tls_skip_length(bcmolt_buf *buf, uint32_t skip, uint8_t** len) |
| 249 | { |
| 250 | *len = bcmolt_buf_snap_get(buf); |
| 251 | return bcmolt_buf_skip(buf, skip); |
| 252 | } |
| 253 | |
| 254 | static bcmos_bool _dpoe_eap_tls_init_eap_hdr( |
| 255 | dpoe_sec_link_rec *link_rec, |
| 256 | bcmolt_buf *buf, |
| 257 | eap_code code, |
| 258 | eap_length_fields *lf) |
| 259 | { |
| 260 | /* Parameter checks. */ |
| 261 | BUG_ON(link_rec == NULL); |
| 262 | BUG_ON(buf == NULL); |
| 263 | |
| 264 | return |
| 265 | bcmolt_buf_write_mac_address(buf, eapol_dst_mac) && |
| 266 | bcmolt_buf_write_mac_address(buf, link_rec->ni_mac) && |
| 267 | bcmolt_buf_write_u16_be(buf, ETHERTYPE_EAPOL) && |
| 268 | bcmolt_buf_write_u8(buf, EAPOL_PROTOCOL_VERSION_DPOE) && |
| 269 | bcmolt_buf_write_u8(buf, EAPOL_TYPE_PACKET) && |
| 270 | /* Eapol Length - we re-compute this after building the packet */ |
| 271 | _dpoe_eap_tls_skip_length(buf, sizeof(uint16_t), &lf->eapol) && |
| 272 | /* EAP layer headers */ |
| 273 | bcmolt_buf_write_u8(buf, code) && |
| 274 | bcmolt_buf_write_u8(buf, link_rec->auth_ctrl.current_packet_id++) && |
| 275 | /* EAP length - again, computed when the packet is packed! */ |
| 276 | _dpoe_eap_tls_skip_length(buf, sizeof(uint16_t), &lf->eap); |
| 277 | } |
| 278 | |
| 279 | static bcmos_bool _dpoe_eap_tls_init_tls_hdr(bcmolt_buf *buf, uint8_t eapTlsFlags, eap_length_fields *lf) |
| 280 | { |
| 281 | bcmos_bool rc = BCMOS_FALSE; |
| 282 | |
| 283 | /* Parameter checks. */ |
| 284 | BUG_ON(buf == NULL); |
| 285 | |
| 286 | if (bcmolt_buf_write_u8(buf, EAP_TYPE_TLS) && |
| 287 | bcmolt_buf_write_u8(buf, eapTlsFlags)) |
| 288 | { |
| 289 | if ((eapTlsFlags & EAP_TLS_FLAG_LENGTH_BIT) == EAP_TLS_FLAG_LENGTH_BIT) |
| 290 | { |
| 291 | /* EAP-TLS length - computed at pack completion only allocate room for this if EapTlsFlagLengthBit is set |
| 292 | But if the EapTlsFlagLengthBit is set, we also write the TLS Record header. */ |
| 293 | if (_dpoe_eap_tls_skip_length(buf, sizeof(uint32_t), &lf->eap_tls) && |
| 294 | bcmolt_buf_write_u8(buf, TLS_HANDSHAKE_ID) && |
| 295 | bcmolt_buf_write_u8(buf, TLS_MAJOR_VERSION) && |
| 296 | bcmolt_buf_write_u8(buf, TLS_MINOR_VERSION) && |
| 297 | /* Another length field, the TLS record length */ |
| 298 | _dpoe_eap_tls_skip_length(buf, sizeof(uint16_t), &lf->tls_rec)) |
| 299 | { |
| 300 | rc = BCMOS_TRUE; |
| 301 | } |
| 302 | } |
| 303 | else |
| 304 | { |
| 305 | rc = BCMOS_TRUE; |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | return rc; |
| 310 | } |
| 311 | |
| 312 | static void _dpoe_eap_tls_pack_handshake_length( |
| 313 | dpoe_sec_link_rec *link_rec, |
| 314 | bcmolt_buf *buf, |
| 315 | uint8_t *start, |
| 316 | uint8_t *len) |
| 317 | { |
| 318 | uint32_t rec_len; |
| 319 | uint8_t *end; |
| 320 | |
| 321 | /* Now go back and fill in the length field. This is the length of the TlsHandshake record */ |
| 322 | end = bcmolt_buf_snap_get(buf); |
| 323 | rec_len = end - start; |
| 324 | bcmolt_buf_snap_restore(buf, len); |
| 325 | bcmolt_buf_write_u24(buf, uint32_to_24(rec_len - TLS_HANDSHAKE_SIZE)); |
| 326 | bcmolt_buf_snap_restore(buf, end); |
| 327 | _buffer_hash_data(link_rec, start, rec_len); |
| 328 | } |
| 329 | |
| 330 | static bcmos_bool _dpoe_eap_tls_pack_server_hello_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 331 | { |
| 332 | uint8_t *start; |
| 333 | uint8_t *len; |
| 334 | |
| 335 | /* Parameter checks. */ |
| 336 | BUG_ON(link_rec == NULL); |
| 337 | BUG_ON(buf == NULL); |
| 338 | |
| 339 | start = bcmolt_buf_snap_get(buf); |
| 340 | if (bcmolt_buf_write_u8(buf, HS_SERVER_HELLO) && |
| 341 | (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))) && |
| 342 | bcmolt_buf_write_u8(buf, link_rec->auth_ctrl.version.major) && |
| 343 | bcmolt_buf_write_u8(buf, link_rec->auth_ctrl.version.minor) && |
| 344 | bcmolt_buf_write(buf, link_rec->auth_ctrl.trans_data.server_random, COUNT_OF_RANDOM_BYTES) && |
| 345 | bcmolt_buf_write_u8(buf, COUNT_OF_RANDOM_BYTES) && |
| 346 | bcmolt_buf_write(buf, link_rec->auth_ctrl.trans_data.server_random, COUNT_OF_RANDOM_BYTES) && |
| 347 | |
| 348 | /* Cipher Suites - we write 0 as the length of the Cipher Suites |
| 349 | Short circuit if using pre-release authentication */ |
| 350 | ((link_rec->auth_ctrl.version.minor == 3) || bcmolt_buf_write_u16_be(buf, 0)) && |
| 351 | |
| 352 | /* CompressionMethods also write 0 as the length */ |
| 353 | bcmolt_buf_write_u8(buf, 0)) |
| 354 | { |
| 355 | _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len); |
| 356 | return BCMOS_TRUE; |
| 357 | } |
| 358 | return BCMOS_FALSE; |
| 359 | } |
| 360 | |
| 361 | static bcmos_bool _dpoe_eap_tls_pack_server_key_exchange_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 362 | { |
| 363 | uint8_t *start; |
| 364 | uint8_t *len; |
| 365 | uint8_t rsa_modulus[SIZE_OF_RSA_MODULUS] = {}; |
| 366 | uint8_t rsa_exponent[SIZE_OF_RSA_EXPONENT] = {}; |
| 367 | |
| 368 | /* Parameter checks. */ |
| 369 | BUG_ON(link_rec == NULL); |
| 370 | BUG_ON(buf == NULL); |
| 371 | |
| 372 | /* If not bidirectional, just return BCMOS_TRUE */ |
| 373 | if (link_rec->cfg.enc_mode != BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI) |
| 374 | { |
| 375 | return BCMOS_TRUE; |
| 376 | } |
| 377 | |
| 378 | /* Verify that the length of the public key (modulus) of the private key matches the expected length. Also, verify |
| 379 | that the modulus is successfully read from the RSA key. */ |
| 380 | start = bcmolt_buf_snap_get(buf); |
| 381 | if ((dpoe_sec_rsa_public_get(link_rec->auth_ctrl.trans_data.rsa, rsa_modulus, rsa_exponent)) && |
| 382 | bcmolt_buf_write_u8(buf, HS_SERVER_KEY_EXCHANGE) && |
| 383 | (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))) && |
| 384 | bcmolt_buf_write(buf, rsa_modulus, sizeof(rsa_modulus)) && |
| 385 | bcmolt_buf_write(buf, rsa_exponent, sizeof(rsa_exponent))) |
| 386 | { |
| 387 | _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len); |
| 388 | return BCMOS_TRUE; |
| 389 | } |
| 390 | return BCMOS_FALSE; |
| 391 | } |
| 392 | |
| 393 | static bcmos_bool _dpoe_eap_tls_pack_cert_req_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 394 | { |
| 395 | uint8_t *start; |
| 396 | uint8_t *len; |
| 397 | |
| 398 | /* Parameter checks. */ |
| 399 | BUG_ON(link_rec == NULL); |
| 400 | BUG_ON(buf == NULL); |
| 401 | |
| 402 | start = bcmolt_buf_snap_get(buf); |
| 403 | if (bcmolt_buf_write_u8(buf, HS_CERTIFICATE_REQUEST) && |
| 404 | (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t))) && |
| 405 | bcmolt_buf_write_u8(buf, 1) && /* length of 'Certificate Type' */ |
| 406 | bcmolt_buf_write_u8(buf, RSA_SIGN) && /* Certificate Type */ |
| 407 | bcmolt_buf_write_u16_be(buf, 0)) /* zero length for cert authorities */ |
| 408 | { |
| 409 | _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len); |
| 410 | return BCMOS_TRUE; |
| 411 | } |
| 412 | return BCMOS_FALSE; |
| 413 | } |
| 414 | |
| 415 | static bcmos_bool _dpoe_eap_tls_pack_server_hello_done_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 416 | { |
| 417 | uint8_t *start; |
| 418 | uint8_t *len; |
| 419 | |
| 420 | /* Parameter checks. */ |
| 421 | BUG_ON(link_rec == NULL); |
| 422 | BUG_ON(buf == NULL); |
| 423 | |
| 424 | start = bcmolt_buf_snap_get(buf); |
| 425 | if (bcmolt_buf_write_u8(buf, HS_SERVER_HELLO_DONE) && |
| 426 | (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t)))) |
| 427 | { |
| 428 | _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len); |
| 429 | return BCMOS_TRUE; |
| 430 | } |
| 431 | return BCMOS_FALSE; |
| 432 | } |
| 433 | |
| 434 | static bcmos_bool _dpoe_eap_tls_pack_finished_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 435 | { |
| 436 | uint8_t *start; |
| 437 | uint8_t *len; |
| 438 | |
| 439 | start = bcmolt_buf_snap_get(buf); |
| 440 | if (bcmolt_buf_write_u8(buf, HS_FINISHED) && |
| 441 | (len = bcmolt_buf_snap_get(buf), bcmolt_buf_skip(buf, sizeof(uint24_t)))) |
| 442 | { |
| 443 | _dpoe_eap_tls_pack_handshake_length(link_rec, buf, start, len); |
| 444 | return BCMOS_TRUE; |
| 445 | } |
| 446 | return BCMOS_FALSE; |
| 447 | } |
| 448 | |
| 449 | static bcmos_bool _dpoe_eap_tls_pack_msg_lengths(bcmolt_buf *buf, uint8_t eap_tls_flags, eap_length_fields *lf) |
| 450 | { |
| 451 | uint16_t eapol_length; |
| 452 | uint32_t eap_tls_length; |
| 453 | uint8_t *snap = bcmolt_buf_snap_get(buf); |
| 454 | |
| 455 | /* Parameter checks. */ |
| 456 | BUG_ON(buf == NULL); |
| 457 | BUG_ON(lf == NULL); |
| 458 | |
| 459 | eapol_length = bcmolt_buf_get_used(buf) - EAPOL_MSG_HDR_SIZE; |
| 460 | |
| 461 | /* Seems odd that these two would be the same. But eapolLength is defined as the length of the body fields of the |
| 462 | Eapol buffer, which does not include the Eapol header. EAP is different, the EAP length is define to include the |
| 463 | entire EAP packet, including the header. */ |
| 464 | bcmolt_buf_snap_restore(buf, lf->eapol); |
| 465 | if (!bcmolt_buf_write_u16_be(buf, eapol_length)) |
| 466 | { |
| 467 | bcmolt_buf_snap_restore(buf, snap); |
| 468 | return BCMOS_FALSE; |
| 469 | } |
| 470 | |
| 471 | bcmolt_buf_snap_restore(buf, lf->eap); |
| 472 | if (!bcmolt_buf_write_u16_be(buf, eapol_length)) |
| 473 | { |
| 474 | bcmolt_buf_snap_restore(buf, snap); |
| 475 | return BCMOS_FALSE; |
| 476 | } |
| 477 | |
| 478 | if ((eap_tls_flags & EAP_TLS_FLAG_LENGTH_BIT) == EAP_TLS_FLAG_LENGTH_BIT) |
| 479 | { |
| 480 | /* The TLS message length - take away the EapTlsHdr, AND the EapMsgHdr which, surprisingly was not removed in |
| 481 | the previous step. */ |
| 482 | eap_tls_length = eapol_length - (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE + sizeof(uint32_t)); |
| 483 | |
| 484 | bcmolt_buf_snap_restore(buf, lf->eap_tls); |
| 485 | if (!bcmolt_buf_write_u32_be(buf, eap_tls_length)) |
| 486 | { |
| 487 | bcmolt_buf_snap_restore(buf, snap); |
| 488 | return BCMOS_FALSE; |
| 489 | } |
| 490 | |
| 491 | bcmolt_buf_snap_restore(buf, lf->tls_rec); |
| 492 | if (!bcmolt_buf_write_u16_be(buf, (uint16_t)eap_tls_length - TLS_RECORD_HDR_SIZE)) |
| 493 | { |
| 494 | bcmolt_buf_snap_restore(buf, snap); |
| 495 | return BCMOS_FALSE; |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | bcmolt_buf_snap_restore(buf, snap); |
| 500 | return BCMOS_TRUE; |
| 501 | } |
| 502 | |
| 503 | static bcmos_errno _dpoe_eap_tls_pack_start(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 504 | { |
| 505 | bcmos_errno rc = BCM_ERR_NOMEM; |
| 506 | uint8_t flags = EAP_TLS_FLAG_START_BIT; |
| 507 | eap_length_fields lf; |
| 508 | |
| 509 | /* Parameter checks. */ |
| 510 | BUG_ON(link_rec == NULL); |
| 511 | BUG_ON(buf == NULL); |
| 512 | |
| 513 | if (_dpoe_eap_tls_init_eap_hdr(link_rec, buf, EAP_CODE_REQUEST, &lf) && |
| 514 | _dpoe_eap_tls_init_tls_hdr(buf, flags, &lf) && |
| 515 | _dpoe_eap_tls_pack_msg_lengths(buf, flags, &lf)) |
| 516 | { |
| 517 | rc = BCM_ERR_OK; |
| 518 | } |
| 519 | |
| 520 | return rc; |
| 521 | } |
| 522 | |
| 523 | static bcmos_bool _dpoe_eap_tls_send_cert_request(dpoe_sec_link_rec *link_rec) |
| 524 | { |
| 525 | bcmos_bool rc = BCMOS_FALSE; |
| 526 | bcmos_errno err; |
| 527 | bcmolt_buf buf; |
| 528 | uint8_t *msg = NULL; |
| 529 | eap_length_fields lf; |
| 530 | |
| 531 | /* Parameter checks. */ |
| 532 | BUG_ON(link_rec == NULL); |
| 533 | |
| 534 | msg = bcmos_calloc(EAPOL_PKT_SIZE); |
| 535 | if (msg == NULL) |
| 536 | { |
| 537 | return BCMOS_FALSE; |
| 538 | } |
| 539 | |
| 540 | /* Initialize the maximum supported length EAP-TLS message buffer. The various TLS records contained in an EAP-TLS |
| 541 | message are variable size. However, this can only be a combined maximum length. */ |
| 542 | bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED); |
| 543 | if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, EAP_CODE_REQUEST, &lf) && |
| 544 | _dpoe_eap_tls_init_tls_hdr(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf)) |
| 545 | { |
| 546 | /* The Cert request contains 3 or 4 TLS records (3 for DS-only and 4 for bi-directional). The TLS records are |
| 547 | ordered as: |
| 548 | - Server Hello |
| 549 | - Server Key Exchange (if bi-directional) |
| 550 | - Certificate Request |
| 551 | - Server Hello Done */ |
| 552 | if (_dpoe_eap_tls_pack_server_hello_hs(link_rec, &buf) && |
| 553 | _dpoe_eap_tls_pack_server_key_exchange_hs(link_rec, &buf) && |
| 554 | _dpoe_eap_tls_pack_cert_req_hs(link_rec, &buf) && |
| 555 | _dpoe_eap_tls_pack_server_hello_done_hs(link_rec, &buf)) |
| 556 | { |
| 557 | _dpoe_eap_tls_pack_msg_lengths(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf); |
| 558 | |
| 559 | /* Send the message */ |
| 560 | err = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf)); |
| 561 | if (err != BCM_ERR_OK) |
| 562 | { |
| 563 | bcmos_free(msg); |
| 564 | return BCMOS_FALSE; |
| 565 | } |
| 566 | |
| 567 | /* Authentication has begun. */ |
| 568 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_EAP_START; |
| 569 | rc = BCMOS_TRUE; |
| 570 | } |
| 571 | } |
| 572 | |
| 573 | /* Free the message. */ |
| 574 | bcmos_free(msg); |
| 575 | |
| 576 | return rc; |
| 577 | } |
| 578 | |
| 579 | static bcmos_errno _dpoe_eap_tls_send_finished_hs(dpoe_sec_link_rec *link_rec) |
| 580 | { |
| 581 | bcmos_errno rc = BCM_ERR_OVERFLOW; |
| 582 | bcmolt_buf buf; |
| 583 | uint8_t *msg = NULL; |
| 584 | eap_length_fields lf; |
| 585 | |
| 586 | /* Parameter checks. */ |
| 587 | BUG_ON(link_rec == NULL); |
| 588 | |
| 589 | msg = bcmos_calloc(EAPOL_PKT_SIZE); |
| 590 | if (msg == NULL) |
| 591 | { |
| 592 | return BCM_ERR_NOMEM; |
| 593 | } |
| 594 | |
| 595 | /* Initialize the EAP-TLS message buffer. */ |
| 596 | bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED); |
| 597 | if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, EAP_CODE_REQUEST, &lf) && |
| 598 | _dpoe_eap_tls_init_tls_hdr(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf) && |
| 599 | _dpoe_eap_tls_pack_finished_hs(link_rec, &buf)) |
| 600 | { |
| 601 | _dpoe_eap_tls_pack_msg_lengths(&buf, EAP_TLS_FLAG_LENGTH_BIT, &lf); |
| 602 | /* No error detection here. We have already authenticated. this message is just for standards compliance */ |
| 603 | rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf)); |
| 604 | } |
| 605 | |
| 606 | /* Free the message. */ |
| 607 | bcmos_free(msg); |
| 608 | |
| 609 | return rc; |
| 610 | } |
| 611 | |
| 612 | static bcmos_errno _dpoe_eap_tls_send_result(dpoe_sec_link_rec *link_rec, eap_code result) |
| 613 | { |
| 614 | uint8_t *msg; |
| 615 | bcmolt_buf buf; |
| 616 | bcmos_errno rc = BCM_ERR_INTERNAL; |
| 617 | eap_length_fields lf; |
| 618 | |
| 619 | /* Parameter checks */ |
| 620 | BUG_ON(link_rec == NULL); |
| 621 | |
| 622 | msg = bcmos_calloc(EAPOL_PKT_SIZE); |
| 623 | if (msg == NULL) |
| 624 | { |
| 625 | return BCM_ERR_NOMEM; |
| 626 | } |
| 627 | |
| 628 | /* Initialize the EAP-TLS message buffer. */ |
| 629 | bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED); |
| 630 | /* Encode the EAP-Success message to the buffer. */ |
| 631 | if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, result, &lf) && |
| 632 | _dpoe_eap_tls_pack_msg_lengths(&buf, 0, &lf)) |
| 633 | { |
| 634 | rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf)); |
| 635 | } |
| 636 | |
| 637 | bcmos_free(msg); |
| 638 | |
| 639 | return rc; |
| 640 | } |
| 641 | |
| 642 | static bcmos_bool _dpoe_eap_tls_parse_certificate(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 643 | { |
| 644 | bcmos_bool rc = BCMOS_FALSE; |
| 645 | uint24_t length; |
| 646 | |
| 647 | /* Parameter checks. */ |
| 648 | BUG_ON(link_rec == NULL); |
| 649 | BUG_ON(buf == NULL); |
| 650 | |
| 651 | link_rec->auth_ctrl.certificate = bcmos_calloc(EAPOL_PKT_SIZE); |
| 652 | if (link_rec->auth_ctrl.certificate == NULL) |
| 653 | { |
| 654 | return BCMOS_FALSE; |
| 655 | } |
| 656 | |
| 657 | if (bcmolt_buf_read_u24(buf, &length) && |
| 658 | ((link_rec->auth_ctrl.certLen = (uint16_t)uint24_to_32(length)), (link_rec->auth_ctrl.certLen <= EAPOL_PKT_SIZE)) && |
| 659 | bcmolt_buf_read(buf, link_rec->auth_ctrl.certificate, link_rec->auth_ctrl.certLen)) |
| 660 | { |
| 661 | rc = BCMOS_TRUE; |
| 662 | } |
| 663 | |
| 664 | return rc; |
| 665 | } |
| 666 | |
| 667 | static bcmos_bool _dpoe_eap_tls_parse_client_key_exchange(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 668 | { |
| 669 | bcmos_bool rc = BCMOS_FALSE; |
| 670 | uint16_t length; |
| 671 | uint8_t encrypted_buf[SIZE_OF_RSA_ENCRYPTED_BLOCK] = {}; |
| 672 | |
| 673 | /* Parameter checks. */ |
| 674 | BUG_ON(link_rec == NULL); |
| 675 | BUG_ON(buf == NULL); |
| 676 | |
| 677 | if (bcmolt_buf_read_u16_be(buf, &length) && |
| 678 | (length == (SIZE_OF_RSA_ENCRYPTED_BLOCK)) && |
| 679 | bcmolt_buf_read(buf, encrypted_buf, sizeof(encrypted_buf))) |
| 680 | { |
| 681 | int val; |
| 682 | |
| 683 | val = dpoe_sec_rsa_private_decrypt( |
| 684 | sizeof(encrypted_buf), |
| 685 | encrypted_buf, |
| 686 | link_rec->auth_ctrl.trans_data.pre_master_secret, |
| 687 | link_rec->auth_ctrl.trans_data.rsa); |
| 688 | |
| 689 | if (val == SIZE_OF_PRE_MASTER_SECRET) |
| 690 | { |
| 691 | /* We now need the security data - we have the "client random", "server random", and "pre master secret". */ |
| 692 | _dpoe_eap_tls_prepare_security_data(link_rec); |
| 693 | rc = BCMOS_TRUE; |
| 694 | } |
| 695 | else |
| 696 | { |
| 697 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "RSA decrypt pre-master secret failed: %d\n", val); |
| 698 | } |
| 699 | } |
| 700 | else |
| 701 | { |
| 702 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to parse client key exchange: %u\n", length); |
| 703 | } |
| 704 | |
| 705 | return rc; |
| 706 | } |
| 707 | |
| 708 | static bcmos_bool _dpoe_eap_tls_parse_cert_verify(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 709 | { |
| 710 | uint16_t length; |
| 711 | bcmos_bool ret = BCMOS_FALSE; |
| 712 | |
| 713 | if (bcmolt_buf_read_u16_be(buf, &length) && |
| 714 | (length <= (2 * SIZE_OF_RSA_ENCRYPTED_BLOCK))) |
| 715 | { |
| 716 | bcmolt_buf cert_buf; |
| 717 | uint24_t temp; |
| 718 | const uint8_t *cert; |
| 719 | uint32_t cert_len = 0; |
| 720 | uint8_t cert_verify[sizeof(dpoe_sec_sha1_digest) + sizeof(dpoe_sec_md5_digest)]; |
| 721 | uint8_t *enc_buf; |
| 722 | dpoe_sec_rsa_key *rsa; |
| 723 | |
| 724 | bcmolt_buf_init(&cert_buf, link_rec->auth_ctrl.certLen, link_rec->auth_ctrl.certificate, BCMOLT_BUF_ENDIAN_FIXED); |
| 725 | |
| 726 | /* Get the pointer to the ONU cert and its length */ |
| 727 | bcmolt_buf_read_u24(&cert_buf, &temp); |
| 728 | cert_len = uint24_to_32(temp); |
| 729 | cert = bcmolt_buf_snap_get(&cert_buf); |
| 730 | |
| 731 | enc_buf = bcmolt_buf_snap_get(buf); |
| 732 | bcmolt_buf_skip(buf, length); |
| 733 | |
| 734 | /* Get the public key from the ONU cert. */ |
| 735 | rsa = dpoe_sec_x509_pub_key_get(cert, cert_len); |
| 736 | |
| 737 | /* decrypt the remainder of the buf with the ONU cert. */ |
| 738 | if (rsa != NULL) |
| 739 | { |
| 740 | if (dpoe_sec_rsa_public_decrypt(length, enc_buf, cert_verify, rsa) >= 0) |
| 741 | { |
| 742 | /* RSA_size() returns the size of the modulus in bytes. Convert to bits. */ |
| 743 | link_rec->auth_ctrl.onu_cert_key_size = dpoe_sec_rsa_size(rsa) * 8; |
| 744 | |
| 745 | /* Compare the output (should be 36 bytes) to sha1/md5 messages digests If they match, we are good */ |
| 746 | if ((memcmp(&cert_verify[0], |
| 747 | link_rec->auth_ctrl.trans_data.md5_digest, |
| 748 | sizeof(dpoe_sec_md5_digest)) == 0) && |
| 749 | (memcmp(&cert_verify[sizeof(dpoe_sec_md5_digest)], |
| 750 | link_rec->auth_ctrl.trans_data.sha1_digest, |
| 751 | sizeof(dpoe_sec_sha1_digest)) == 0)) |
| 752 | { |
| 753 | bcmolt_buf_init(&cert_buf, link_rec->auth_ctrl.certLen, link_rec->auth_ctrl.certificate, BCMOLT_BUF_ENDIAN_FIXED); |
| 754 | |
| 755 | /* Get the pointer to the ONU certificate. */ |
| 756 | bcmolt_buf_read_u24(&cert_buf, &temp); |
| 757 | link_rec->auth_ctrl.onu_cert_len = uint24_to_32(temp); |
| 758 | link_rec->auth_ctrl.onu_cert = bcmolt_buf_snap_get(&cert_buf); |
| 759 | |
| 760 | bcmolt_buf_skip(&cert_buf, link_rec->auth_ctrl.onu_cert_len); |
| 761 | |
| 762 | /* Get the pointer to the Manufacturer CA certificate. */ |
| 763 | bcmolt_buf_read_u24(&cert_buf, &temp); |
| 764 | link_rec->auth_ctrl.mfg_cert_len = uint24_to_32(temp); |
| 765 | link_rec->auth_ctrl.mfg_cert = bcmolt_buf_snap_get(&cert_buf); |
| 766 | |
| 767 | ret = BCMOS_TRUE; |
| 768 | } |
| 769 | else |
| 770 | { |
| 771 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "Cert Verify failed\n"); |
| 772 | } |
| 773 | } |
| 774 | else |
| 775 | { |
| 776 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "RSA public decrypt/verify failed\n"); |
| 777 | } |
| 778 | } |
| 779 | else |
| 780 | { |
| 781 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to retrieve RSA key\n"); |
| 782 | } |
| 783 | |
| 784 | dpoe_sec_rsa_key_free(rsa); |
| 785 | } |
| 786 | |
| 787 | return ret; |
| 788 | } |
| 789 | |
| 790 | static bcmos_bool _dpoe_eap_tls_parse_verify_data(dpoe_sec_link_rec *link_rec) |
| 791 | { |
| 792 | bcmos_bool rc = BCMOS_FALSE; |
| 793 | |
| 794 | /* Parameter checks. */ |
| 795 | BUG_ON(link_rec == NULL); |
| 796 | |
| 797 | if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION) |
| 798 | { |
| 799 | /* This version of authentication does not parse verify data */ |
| 800 | rc = BCMOS_TRUE; |
| 801 | } |
| 802 | return rc; |
| 803 | } |
| 804 | |
| 805 | static bcmos_bool _dpoe_eap_tls_parse_client_hello(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 806 | { |
| 807 | tls_protocol_version version; |
| 808 | tls_session_id session_id; |
| 809 | |
| 810 | /* Parameter checks. */ |
| 811 | BUG_ON(link_rec == NULL); |
| 812 | BUG_ON(buf == NULL); |
| 813 | |
| 814 | if (bcmolt_buf_read_u8(buf, &version.major) && |
| 815 | (version.major == TLS_MAJOR_VERSION) && |
| 816 | bcmolt_buf_read_u8(buf, &version.minor) && |
| 817 | (version.minor >= TLS_MINOR_VERSION) && |
| 818 | bcmolt_buf_read(buf, link_rec->auth_ctrl.trans_data.client_random, COUNT_OF_RANDOM_BYTES) && |
| 819 | bcmolt_buf_read_u8(buf, &session_id.length) && |
| 820 | (session_id.length <= sizeof(session_id.session_id)) && |
| 821 | bcmolt_buf_read(buf, session_id.session_id, session_id.length)) |
| 822 | { |
| 823 | compression_methods comp_methods; |
| 824 | uint16_t tlvLen; |
| 825 | uint8_t cipher_suites[2]; |
| 826 | uint8_t *cm_snap = bcmolt_buf_snap_get(buf); |
| 827 | |
| 828 | memset(&comp_methods, 0, sizeof(comp_methods)); |
| 829 | |
| 830 | /* Cipher Suites may be present -if so parse over it and it will be ignored. Still check that comp methods is |
| 831 | NULL */ |
| 832 | if (bcmolt_buf_read_u16_be(buf, &tlvLen) && /* Cipher Suites */ |
| 833 | (tlvLen <= sizeof(uint16_t)) && |
| 834 | bcmolt_buf_read(buf, cipher_suites, tlvLen) && |
| 835 | bcmolt_buf_read_u8(buf, &comp_methods.length) && /* Compression Methods */ |
| 836 | (comp_methods.length == sizeof(comp_methods.compression_method)) && |
| 837 | bcmolt_buf_read(buf, &comp_methods.compression_method, comp_methods.length) && |
| 838 | (comp_methods.compression_method == cm_null) ) |
| 839 | { |
| 840 | link_rec->auth_ctrl.version.major = version.major; |
| 841 | link_rec->auth_ctrl.version.minor = version.minor; |
| 842 | return BCMOS_TRUE; |
| 843 | } |
| 844 | |
| 845 | /* No cipher suites, check that comp method is null */ |
| 846 | memset(&comp_methods, 0, sizeof(comp_methods)); |
| 847 | bcmolt_buf_snap_restore(buf, cm_snap); |
| 848 | if (bcmolt_buf_read_u8(buf, &comp_methods.length) && /* Compression Methods */ |
| 849 | (comp_methods.length == sizeof(comp_methods.compression_method)) && |
| 850 | bcmolt_buf_read(buf, &comp_methods.compression_method, comp_methods.length) && |
| 851 | (comp_methods.compression_method == cm_null) ) |
| 852 | { |
| 853 | link_rec->auth_ctrl.version.major = version.major; |
| 854 | link_rec->auth_ctrl.version.minor = version.minor; |
| 855 | return BCMOS_TRUE; |
| 856 | } |
| 857 | |
| 858 | /* Ok we failed to decode what was sent as cipher suites and comp methods. Do we care? Absolutely Not! */ |
| 859 | link_rec->auth_ctrl.version.major = version.major; |
| 860 | link_rec->auth_ctrl.version.minor = version.minor; |
| 861 | return BCMOS_TRUE; |
| 862 | } |
| 863 | |
| 864 | return BCMOS_FALSE; |
| 865 | } |
| 866 | |
| 867 | static bcmos_bool _dpoe_eap_tls_handle_client_hello_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 868 | { |
| 869 | /* Parameter checks. */ |
| 870 | BUG_ON(link_rec == NULL); |
| 871 | BUG_ON(buf == NULL); |
| 872 | |
| 873 | if (_dpoe_eap_tls_parse_client_hello(link_rec, buf)) |
| 874 | { |
| 875 | /* We now have the data, build the EAP session ID (see RFC 5216) */ |
| 876 | link_rec->auth_ctrl.trans_data.session_id[0] = 0xd; |
| 877 | memcpy( |
| 878 | &link_rec->auth_ctrl.trans_data.session_id[1], |
| 879 | link_rec->auth_ctrl.trans_data.client_random, |
| 880 | COUNT_OF_RANDOM_BYTES); |
| 881 | memcpy( |
| 882 | &link_rec->auth_ctrl.trans_data.session_id[COUNT_OF_RANDOM_BYTES + 1], |
| 883 | link_rec->auth_ctrl.trans_data.server_random, |
| 884 | COUNT_OF_RANDOM_BYTES); |
| 885 | |
| 886 | if (_dpoe_eap_tls_send_cert_request(link_rec)) |
| 887 | { |
| 888 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CERT_REQUEST; |
| 889 | return BCMOS_TRUE; |
| 890 | } |
| 891 | } |
| 892 | |
| 893 | return BCMOS_FALSE; |
| 894 | } |
| 895 | |
| 896 | static bcmos_bool _dpoe_eap_tls_handle_certificate_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 897 | { |
| 898 | /* Parameter checks. */ |
| 899 | BUG_ON(link_rec == NULL); |
| 900 | BUG_ON(buf == NULL); |
| 901 | |
| 902 | if (_dpoe_eap_tls_parse_certificate(link_rec, buf)) |
| 903 | { |
| 904 | if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION) |
| 905 | { |
| 906 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CERT_RECEIVED; |
| 907 | return BCMOS_TRUE; |
| 908 | } |
| 909 | } |
| 910 | return BCMOS_FALSE; |
| 911 | } |
| 912 | |
| 913 | /******************************************************************************/ |
| 914 | static bcmos_bool _dpoe_eap_tls_handle_client_key_exchange_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 915 | { |
| 916 | /* Parameter checks. */ |
| 917 | BUG_ON(link_rec == NULL); |
| 918 | BUG_ON(buf == NULL); |
| 919 | |
| 920 | if (_dpoe_eap_tls_parse_client_key_exchange(link_rec, buf)) |
| 921 | { |
| 922 | if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION) |
| 923 | { |
| 924 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CLIENT_KEY_RECEIVED; |
| 925 | return BCMOS_TRUE; |
| 926 | } |
| 927 | else |
| 928 | { |
| 929 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "Wrong TLS version in client key exchange: %u\n", |
| 930 | link_rec->auth_ctrl.version.minor); |
| 931 | } |
| 932 | } |
| 933 | else |
| 934 | { |
| 935 | DPOE_SEC_LINK_LOG(ERROR, link_rec, "Failed to parse client key exchange\n"); |
| 936 | } |
| 937 | |
| 938 | return BCMOS_FALSE; |
| 939 | } |
| 940 | |
| 941 | static bcmos_bool _dpoe_eap_tls_handle_cert_verify_hs(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 942 | { |
| 943 | bcmos_bool success; |
| 944 | |
| 945 | /* Parameter checks. */ |
| 946 | BUG_ON(link_rec == NULL); |
| 947 | BUG_ON(buf == NULL); |
| 948 | |
| 949 | success = _dpoe_eap_tls_parse_cert_verify(link_rec, buf); |
| 950 | |
| 951 | if (success) |
| 952 | { |
| 953 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_CERT_VALIDATED; |
| 954 | } |
| 955 | else |
| 956 | { |
| 957 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_FAILED; |
| 958 | if (_auth_complete != NULL) |
| 959 | { |
| 960 | _auth_complete(link_rec, BCM_ERR_ONU_ERR_RESP); |
| 961 | } |
| 962 | } |
| 963 | |
| 964 | return success; |
| 965 | } |
| 966 | |
| 967 | static bcmos_bool _dpoe_eap_tls_handle_finished_hs(dpoe_sec_link_rec *link_rec) |
| 968 | { |
| 969 | bcmos_bool success; |
| 970 | |
| 971 | /* Parameter checks. */ |
| 972 | BUG_ON(link_rec == NULL); |
| 973 | |
| 974 | success = _dpoe_eap_tls_parse_verify_data(link_rec); |
| 975 | |
| 976 | /* And send the final Finished Hs to the ONU */ |
| 977 | if (link_rec->auth_ctrl.version.minor == TLS_MINOR_VERSION) |
| 978 | { |
| 979 | _dpoe_eap_tls_send_finished_hs(link_rec); |
| 980 | } |
| 981 | |
| 982 | if (success) |
| 983 | { |
| 984 | if ((_cert_trust == NULL) || (_cert_trust(link_rec))) |
| 985 | { |
| 986 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_AUTHENTICATED; |
| 987 | _dpoe_eap_tls_send_result(link_rec, EAP_CODE_SUCCESS); |
| 988 | if (_auth_complete != NULL) |
| 989 | { |
| 990 | _auth_complete(link_rec, BCM_ERR_OK); |
| 991 | } |
| 992 | } |
| 993 | else |
| 994 | { |
| 995 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_FAILED; |
| 996 | _dpoe_eap_tls_send_result(link_rec, EAP_CODE_FAILURE); |
| 997 | success = BCMOS_FALSE; |
| 998 | if (_auth_complete != NULL) |
| 999 | { |
| 1000 | _auth_complete(link_rec, BCM_ERR_INTERNAL); |
| 1001 | } |
| 1002 | } |
| 1003 | } |
| 1004 | else |
| 1005 | { |
| 1006 | /* Reset ALL the state variables! */ |
| 1007 | memset(&link_rec->auth_ctrl, 0, sizeof(link_rec->auth_ctrl)); |
| 1008 | } |
| 1009 | |
| 1010 | return success; |
| 1011 | } |
| 1012 | |
| 1013 | static bcmos_bool _dpoe_eap_tls_alloc_tls_frag_buffer(onu_auth_control *auth_ctrl) |
| 1014 | { |
| 1015 | /* Parameter checks. */ |
| 1016 | BUG_ON(auth_ctrl == NULL); |
| 1017 | |
| 1018 | if (auth_ctrl->tls_frag_buffer == NULL) |
| 1019 | { |
| 1020 | auth_ctrl->tls_frag_buffer = bcmos_calloc(EAPOL_PKT_SIZE); |
| 1021 | auth_ctrl->tls_frag_length = 0; |
| 1022 | auth_ctrl->tls_total_length = 0; |
| 1023 | } |
| 1024 | |
| 1025 | if (auth_ctrl->tls_frag_buffer == NULL) |
| 1026 | { |
| 1027 | return BCMOS_FALSE; |
| 1028 | } |
| 1029 | |
| 1030 | return BCMOS_TRUE; |
| 1031 | } |
| 1032 | |
| 1033 | static void _dpoe_eap_tls_free_tls_frag_buffer(onu_auth_control *auth_ctrl) |
| 1034 | { |
| 1035 | /* Parameter checks. */ |
| 1036 | BUG_ON(auth_ctrl == NULL); |
| 1037 | |
| 1038 | if (auth_ctrl->tls_frag_buffer != NULL) |
| 1039 | { |
| 1040 | bcmos_free(auth_ctrl->tls_frag_buffer); |
| 1041 | auth_ctrl->tls_frag_buffer = NULL; |
| 1042 | auth_ctrl->tls_frag_length = 0; |
| 1043 | auth_ctrl->tls_total_length = 0; |
| 1044 | } |
| 1045 | } |
| 1046 | |
| 1047 | static bcmos_errno _dpoe_eap_tls_send_tls_frag_ack_to_onu(dpoe_sec_link_rec *link_rec) |
| 1048 | { |
| 1049 | bcmos_errno rc; |
| 1050 | bcmolt_buf buf; |
| 1051 | uint8_t *msg; |
| 1052 | eap_length_fields lf; |
| 1053 | |
| 1054 | /* Parameter checks. */ |
| 1055 | BUG_ON(link_rec == NULL); |
| 1056 | |
| 1057 | /* Allocate a message buffer */ |
| 1058 | msg = bcmos_calloc(EAPOL_PKT_SIZE); |
| 1059 | if (NULL == msg) |
| 1060 | { |
| 1061 | return BCM_ERR_NOMEM; |
| 1062 | } |
| 1063 | |
| 1064 | /* Initialize the EAP-TLS buffer. */ |
| 1065 | bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED); |
| 1066 | if (_dpoe_eap_tls_init_eap_hdr(link_rec, &buf, EAP_CODE_REQUEST, &lf) && |
| 1067 | _dpoe_eap_tls_init_tls_hdr(&buf, 0, &lf) && |
| 1068 | _dpoe_eap_tls_pack_msg_lengths(&buf, 0, &lf)) |
| 1069 | { |
| 1070 | /* Send the message */ |
| 1071 | rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf)); |
| 1072 | if (rc != BCM_ERR_OK) |
| 1073 | { |
| 1074 | bcmos_free(msg); |
| 1075 | return rc; |
| 1076 | } |
| 1077 | } |
| 1078 | else |
| 1079 | { |
| 1080 | rc = BCM_ERR_OVERFLOW; |
| 1081 | } |
| 1082 | |
| 1083 | bcmos_free(msg); |
| 1084 | |
| 1085 | return rc; |
| 1086 | } |
| 1087 | |
| 1088 | static bcmos_bool _dpoe_eap_tls_unpack_tls_handshake(bcmolt_buf *buf, tls_handshake* hs) |
| 1089 | { |
| 1090 | uint24_t temp; |
| 1091 | bcmos_bool rc = BCMOS_FALSE; |
| 1092 | |
| 1093 | if (bcmolt_buf_read_u8(buf, &hs->msg_id) && |
| 1094 | bcmolt_buf_read_u24(buf, &temp)) |
| 1095 | { |
| 1096 | hs->msg_length = uint24_to_32(temp); |
| 1097 | rc = BCMOS_TRUE; |
| 1098 | } |
| 1099 | |
| 1100 | return rc; |
| 1101 | } |
| 1102 | |
| 1103 | static bcmos_errno _dpoe_eap_tls_run_auth_state_machine(dpoe_sec_link_rec *link_rec, bcmolt_buf *buf) |
| 1104 | { |
| 1105 | bcmos_errno rc = BCM_ERR_OK; |
| 1106 | uint8_t *hs_snap; |
| 1107 | tls_handshake handshake; |
| 1108 | |
| 1109 | /* Parameter checks. */ |
| 1110 | BUG_ON(link_rec == NULL); |
| 1111 | |
| 1112 | while (hs_snap = bcmolt_buf_snap_get(buf), _dpoe_eap_tls_unpack_tls_handshake(buf, &handshake)) |
| 1113 | { |
| 1114 | bcmos_bool success = BCMOS_TRUE; |
| 1115 | |
| 1116 | if (handshake.msg_length > EAPOL_PKT_SIZE) |
| 1117 | { |
| 1118 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "Handshake length is insane: msg %u, len %u\n", |
| 1119 | handshake.msg_id, handshake.msg_length); |
| 1120 | return BCM_ERR_PARSE; |
| 1121 | } |
| 1122 | |
| 1123 | if ((handshake.msg_id == HS_CLIENT_HELLO) && |
| 1124 | (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_EAP_START)) |
| 1125 | { |
| 1126 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n", |
| 1127 | handshake.msg_id, link_rec->auth_ctrl.onu_auth_state); |
| 1128 | _buffer_hash_data(link_rec, hs_snap, handshake.msg_length + TLS_HANDSHAKE_SIZE); |
| 1129 | success = _dpoe_eap_tls_handle_client_hello_hs(link_rec, buf); |
| 1130 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state); |
| 1131 | } |
| 1132 | else if ((handshake.msg_id == HS_CERTIFICATE) && |
| 1133 | (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_REQUEST)) |
| 1134 | { |
| 1135 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n", |
| 1136 | handshake.msg_id, link_rec->auth_ctrl.onu_auth_state); |
| 1137 | _buffer_hash_data(link_rec, hs_snap, handshake.msg_length + TLS_HANDSHAKE_SIZE); |
| 1138 | success = _dpoe_eap_tls_handle_certificate_hs(link_rec, buf); |
| 1139 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state); |
| 1140 | } |
| 1141 | else if ((handshake.msg_id == HS_CLIENT_KEY_EXCHANGE) && |
| 1142 | (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_RECEIVED)) |
| 1143 | { |
| 1144 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n", |
| 1145 | handshake.msg_id, link_rec->auth_ctrl.onu_auth_state); |
| 1146 | _buffer_hash_data(link_rec, hs_snap, handshake.msg_length + TLS_HANDSHAKE_SIZE); |
| 1147 | success = _dpoe_eap_tls_handle_client_key_exchange_hs(link_rec, buf); |
| 1148 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state); |
| 1149 | } |
| 1150 | else if ((handshake.msg_id == HS_CERTIFICATE_VERIFY) && |
| 1151 | ((link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_RECEIVED) || |
| 1152 | (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CLIENT_KEY_RECEIVED))) |
| 1153 | |
| 1154 | { |
| 1155 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n", |
| 1156 | handshake.msg_id, link_rec->auth_ctrl.onu_auth_state); |
| 1157 | dpoe_sec_sha1_final(link_rec->auth_ctrl.trans_data.sha1_digest, &link_rec->auth_ctrl.trans_data.sha1_hash); |
| 1158 | dpoe_sec_md5_final(link_rec->auth_ctrl.trans_data.md5_digest, &link_rec->auth_ctrl.trans_data.md5_hash); |
| 1159 | success = _dpoe_eap_tls_handle_cert_verify_hs(link_rec, buf); |
| 1160 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state); |
| 1161 | } |
| 1162 | else if ((handshake.msg_id == HS_FINISHED) && |
| 1163 | (link_rec->auth_ctrl.onu_auth_state == DPOE_ONU_AUTH_STATE_CERT_VALIDATED)) |
| 1164 | { |
| 1165 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n", |
| 1166 | handshake.msg_id, link_rec->auth_ctrl.onu_auth_state); |
| 1167 | success = _dpoe_eap_tls_handle_finished_hs(link_rec); |
| 1168 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "New auth state %u\n", link_rec->auth_ctrl.onu_auth_state); |
| 1169 | } |
| 1170 | else |
| 1171 | { |
| 1172 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS RX msg %u in state %u\n", |
| 1173 | handshake.msg_id, link_rec->auth_ctrl.onu_auth_state); |
| 1174 | success = BCMOS_FALSE; |
| 1175 | } |
| 1176 | |
| 1177 | if (!success) |
| 1178 | { |
| 1179 | /* Something went wrong, just abort... */ |
| 1180 | DPOE_SEC_LINK_LOG(DEBUG, link_rec, "EAP TLS handshake processing failed: %u\n", handshake.msg_id); |
| 1181 | rc = BCM_ERR_PARSE; |
| 1182 | } |
| 1183 | } |
| 1184 | |
| 1185 | return rc; |
| 1186 | } |
| 1187 | |
| 1188 | static bcmos_bool _dpoe_eap_tls_unpack_tls_record_hdr(bcmolt_buf *buf, tls_record_hdr *msg) |
| 1189 | { |
| 1190 | return bcmolt_buf_read_u8(buf, &msg->content_type) && |
| 1191 | bcmolt_buf_read_u8(buf, &msg->tls_version.major) && |
| 1192 | bcmolt_buf_read_u8(buf, &msg->tls_version.minor) && |
| 1193 | bcmolt_buf_read_u16_be(buf, &msg->tls_record_length); |
| 1194 | } |
| 1195 | |
| 1196 | static bcmos_errno _dpoe_eap_tls_process_tls_records(dpoe_sec_link_rec *link_rec) |
| 1197 | { |
| 1198 | bcmos_errno rc = BCM_ERR_OK; |
| 1199 | bcmolt_buf buf; |
| 1200 | tls_record_hdr tls_hdr; |
| 1201 | |
| 1202 | /* Parameter checks. */ |
| 1203 | BUG_ON(link_rec == NULL); |
| 1204 | |
| 1205 | bcmolt_buf_init(&buf, link_rec->auth_ctrl.tls_frag_length, link_rec->auth_ctrl.tls_frag_buffer, BCMOLT_BUF_ENDIAN_FIXED); |
| 1206 | while ((rc == BCM_ERR_OK) && _dpoe_eap_tls_unpack_tls_record_hdr(&buf, &tls_hdr)) |
| 1207 | { |
| 1208 | rc = _dpoe_eap_tls_run_auth_state_machine(link_rec, &buf); |
| 1209 | } |
| 1210 | |
| 1211 | return rc; |
| 1212 | } |
| 1213 | |
| 1214 | static bcmos_bool _dpoe_eap_tls_check_tls_version(const tls_record_hdr *tls_rec_hdr) |
| 1215 | { |
| 1216 | /* Parameter checks. */ |
| 1217 | BUG_ON(tls_rec_hdr == NULL); |
| 1218 | |
| 1219 | if ((tls_rec_hdr->content_type != TLS_HANDSHAKE_ID) || |
| 1220 | (tls_rec_hdr->tls_version.major != TLS_MAJOR_VERSION) || |
| 1221 | (tls_rec_hdr->tls_version.minor < TLS_MINOR_VERSION)) |
| 1222 | { |
| 1223 | return BCMOS_TRUE; |
| 1224 | } |
| 1225 | return BCMOS_FALSE; |
| 1226 | } |
| 1227 | |
| 1228 | static void _dpoe_eap_tls_buffer_tls_data(dpoe_sec_link_rec *link_rec, const void *data, uint32_t length) |
| 1229 | { |
| 1230 | /* Parameter checks. */ |
| 1231 | BUG_ON(link_rec == NULL); |
| 1232 | BUG_ON(data == NULL); |
| 1233 | |
| 1234 | /* do not overrun the buffer, no matter how much data received */ |
| 1235 | if ((link_rec->auth_ctrl.tls_frag_length + length) > EAPOL_PKT_SIZE) |
| 1236 | { |
| 1237 | length = EAPOL_PKT_SIZE - link_rec->auth_ctrl.tls_frag_length; |
| 1238 | } |
| 1239 | |
| 1240 | memcpy(&link_rec->auth_ctrl.tls_frag_buffer[link_rec->auth_ctrl.tls_frag_length], data, length); |
| 1241 | link_rec->auth_ctrl.tls_frag_length += length; |
| 1242 | } |
| 1243 | |
| 1244 | static bcmos_bool _dpoe_eap_tls_unpack_msg(bcmolt_buf *buf, eapol_msg *msg) |
| 1245 | { |
| 1246 | return dpoe_sec_eapol_unpack(buf, &msg->hdr) && |
| 1247 | bcmolt_buf_read_u8(buf, &msg->eap_msg.hdr.eap_code) && |
| 1248 | bcmolt_buf_read_u8(buf, &msg->eap_msg.hdr.id) && |
| 1249 | bcmolt_buf_read_u16_be(buf, &msg->eap_msg.hdr.length) && |
| 1250 | bcmolt_buf_read_u8(buf, &msg->eap_msg.tls.auth_sub_type) && |
| 1251 | bcmolt_buf_read_u8(buf, &msg->eap_msg.tls.eap_tls_flags); |
| 1252 | } |
| 1253 | |
| 1254 | static bcmos_errno _dpoe_eap_tls_gen_key(auth_trans_data *trans_data) |
| 1255 | { |
| 1256 | bcmos_errno rc = BCM_ERR_OK; |
| 1257 | dpoe_sec_rsa_key *rsa = NULL; |
| 1258 | |
| 1259 | /* Parameter checks */ |
| 1260 | BUG_ON(trans_data == NULL); |
| 1261 | |
| 1262 | /* Generate the RSA key to be used in the ServerKeyExchange and ClientKeyExchange */ |
| 1263 | rsa = dpoe_sec_rsa_generate_key(DPOE_BI_RSA_KEY_SIZE); |
| 1264 | if (rsa == NULL) |
| 1265 | { |
| 1266 | rc = BCM_ERR_INTERNAL; |
| 1267 | } |
| 1268 | else |
| 1269 | { |
| 1270 | /* Store the pointer to the key. */ |
| 1271 | trans_data->rsa = rsa; |
| 1272 | } |
| 1273 | |
| 1274 | return rc; |
| 1275 | } |
| 1276 | |
| 1277 | bcmos_errno dpoe_eap_tls_send_start(dpoe_sec_link_rec *link_rec) |
| 1278 | { |
| 1279 | bcmos_errno rc = BCM_ERR_OK; |
| 1280 | bcmolt_buf buf; |
| 1281 | uint8_t *msg; |
| 1282 | time_t unix_time; |
| 1283 | |
| 1284 | /* Parameter checks */ |
| 1285 | BUG_ON(link_rec == NULL); |
| 1286 | |
| 1287 | /* Initialize the authentication control data. */ |
| 1288 | memset(&link_rec->auth_ctrl, 0, sizeof(link_rec->auth_ctrl)); |
| 1289 | dpoe_sec_sha1_init(&link_rec->auth_ctrl.trans_data.sha1_hash); |
| 1290 | dpoe_sec_md5_init(&link_rec->auth_ctrl.trans_data.md5_hash); |
| 1291 | |
| 1292 | /* Per standard, the first four bytes of the server random value should be "unix time" */ |
| 1293 | unix_time = time(NULL); |
| 1294 | memcpy(link_rec->auth_ctrl.trans_data.server_random, &unix_time, sizeof(unix_time)); |
| 1295 | dpoe_sec_generate_n_byte_random_number(&link_rec->auth_ctrl.trans_data.server_random[sizeof(unix_time)], COUNT_OF_RANDOM_BYTES - sizeof(unix_time)); |
| 1296 | |
| 1297 | /* If bidirectional, generate the RSA public/private key pair for the ServerKeyExchange and ClientKeyExchange. */ |
| 1298 | if (link_rec->cfg.enc_mode == BCMOLT_EPON_OAM_DPOE_ENCRYPTION_MODE_TEN_BI) |
| 1299 | { |
| 1300 | rc = _dpoe_eap_tls_gen_key(&link_rec->auth_ctrl.trans_data); |
| 1301 | BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc); |
| 1302 | } |
| 1303 | |
| 1304 | /* Allocate a message buffer */ |
| 1305 | msg = bcmos_calloc(EAPOL_PKT_SIZE); |
| 1306 | if (NULL == msg) |
| 1307 | { |
| 1308 | return BCM_ERR_NOMEM; |
| 1309 | } |
| 1310 | |
| 1311 | /* Initialize the EAP-TLS buffer. */ |
| 1312 | bcmolt_buf_init(&buf, EAPOL_PKT_SIZE, msg, BCMOLT_BUF_ENDIAN_FIXED); |
| 1313 | |
| 1314 | /* Encode the EAP-TLS Start message to the buffer. */ |
| 1315 | _dpoe_eap_tls_pack_start(link_rec, &buf); |
| 1316 | |
| 1317 | /* Send the message */ |
| 1318 | rc = dpoe_sec_send_eapol(link_rec->device, &link_rec->key, msg, bcmolt_buf_get_used(&buf)); |
| 1319 | bcmos_free(msg); |
| 1320 | BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc); |
| 1321 | |
| 1322 | /* Authentication has begun. */ |
| 1323 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_EAP_START; |
| 1324 | |
| 1325 | return rc; |
| 1326 | } |
| 1327 | |
| 1328 | bcmos_errno dpoe_eap_tls_process_eapol_pkt(dpoe_sec_link_rec *link_rec, uint8_t *msg, uint32_t msg_len) |
| 1329 | { |
| 1330 | bcmos_errno rc = BCM_ERR_INTERNAL; |
| 1331 | uint32_t eap_tls_length; |
| 1332 | tls_record_hdr tls_rec_hdr; |
| 1333 | eapol_msg unpacked_msg; |
| 1334 | bcmolt_buf buf; |
| 1335 | |
| 1336 | /* Parameter checks */ |
| 1337 | BUG_ON(link_rec == NULL); |
| 1338 | BUG_ON(msg == NULL); |
| 1339 | |
| 1340 | bcmolt_buf_init(&buf, msg_len, msg, BCMOLT_BUF_ENDIAN_FIXED); |
| 1341 | _dpoe_eap_tls_unpack_msg(&buf, &unpacked_msg); |
| 1342 | |
| 1343 | if (unpacked_msg.eap_msg.tls.auth_sub_type == EAP_TYPE_NAK) |
| 1344 | { |
| 1345 | link_rec->auth_ctrl.onu_auth_state = DPOE_ONU_AUTH_STATE_FAILED; |
| 1346 | return BCM_ERR_ONU_ERR_RESP; |
| 1347 | } |
| 1348 | |
| 1349 | if (unpacked_msg.hdr.eapol_length != unpacked_msg.eap_msg.hdr.length) |
| 1350 | { |
| 1351 | return BCM_ERR_PARSE; |
| 1352 | } |
| 1353 | |
| 1354 | /* We should only read the length if the length bit is set. */ |
| 1355 | if ((unpacked_msg.eap_msg.tls.eap_tls_flags & EAP_TLS_FLAG_LENGTH_BIT) != 0) |
| 1356 | { |
| 1357 | bcmolt_buf_read_u32_be(&buf, &eap_tls_length); |
| 1358 | uint8_t *tls_data = bcmolt_buf_snap_get(&buf); |
| 1359 | _dpoe_eap_tls_unpack_tls_record_hdr(&buf, &tls_rec_hdr); |
| 1360 | if (_dpoe_eap_tls_check_tls_version(&tls_rec_hdr)) |
| 1361 | { |
| 1362 | /* Invalid TLS record, discard... */ |
| 1363 | return BCM_ERR_PARSE; |
| 1364 | } |
| 1365 | |
| 1366 | /* Allocate the fragmentation buffer. Multiple calls to the frag alloc function is allowed since it allocates a |
| 1367 | new buffer only if there is no frag buffer currently allocated. The frag buffer is also freed if the EAP-TLS |
| 1368 | session goes down for some reason. */ |
| 1369 | if (!_dpoe_eap_tls_alloc_tls_frag_buffer(&link_rec->auth_ctrl)) |
| 1370 | { |
| 1371 | return BCM_ERR_NOMEM; |
| 1372 | } |
| 1373 | |
| 1374 | /* If the length bit is set, and the more bit is not, this is likely a single fragment packet. In that case |
| 1375 | the TlsMsg length must be present, and match the size of the buffer. It is also possible that this is a |
| 1376 | subsequent fragment and the client always adds the length (which per standard is the length of the set of |
| 1377 | fragments), in which case we cannot validate this now. The best we can do is save this size, and validate |
| 1378 | when all fragments have been received. */ |
| 1379 | if (link_rec->auth_ctrl.tls_total_length == 0) |
| 1380 | { |
| 1381 | link_rec->auth_ctrl.tls_total_length = eap_tls_length; |
| 1382 | } |
| 1383 | |
| 1384 | if (link_rec->auth_ctrl.tls_total_length != eap_tls_length) |
| 1385 | { |
| 1386 | return BCM_ERR_PARSE; |
| 1387 | } |
| 1388 | |
| 1389 | _dpoe_eap_tls_buffer_tls_data( |
| 1390 | link_rec, |
| 1391 | tls_data, |
| 1392 | unpacked_msg.hdr.eapol_length - (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE + sizeof(uint32_t))); |
| 1393 | |
| 1394 | if ((unpacked_msg.eap_msg.tls.eap_tls_flags & EAP_TLS_FLAG_MORE_BIT) != 0) |
| 1395 | { |
| 1396 | /* Need to acknowledge the fragment */ |
| 1397 | _dpoe_eap_tls_send_tls_frag_ack_to_onu(link_rec); |
| 1398 | rc = BCM_ERR_IN_PROGRESS; |
| 1399 | } |
| 1400 | else |
| 1401 | { |
| 1402 | /* Either a single fragment packet, or the last fragment of a multi-fragment packet. If it is the last |
| 1403 | fragment of a multi-fragment, then all we can check is that eapTlsLength is one TlsRecordHdr bigger than |
| 1404 | the buffered (authCtrl->tlsFragLen) data stream. The same logic works on a single frag packet. */ |
| 1405 | if ((eap_tls_length != link_rec->auth_ctrl.tls_total_length) || |
| 1406 | (eap_tls_length != link_rec->auth_ctrl.tls_frag_length)) |
| 1407 | { |
| 1408 | return BCM_ERR_PARSE; |
| 1409 | } |
| 1410 | |
| 1411 | rc = _dpoe_eap_tls_process_tls_records(link_rec); |
| 1412 | _dpoe_eap_tls_free_tls_frag_buffer(&link_rec->auth_ctrl); |
| 1413 | } |
| 1414 | } |
| 1415 | else |
| 1416 | { |
| 1417 | uint8_t *tls_data = bcmolt_buf_snap_get(&buf); |
| 1418 | |
| 1419 | /* No length field - must be fragmented, and not first */ |
| 1420 | if (link_rec->auth_ctrl.tls_frag_buffer == NULL) |
| 1421 | { |
| 1422 | return BCM_ERR_OUT_OF_SYNC; |
| 1423 | } |
| 1424 | |
| 1425 | _dpoe_eap_tls_buffer_tls_data( |
| 1426 | link_rec, |
| 1427 | tls_data, |
| 1428 | unpacked_msg.hdr.eapol_length - (EAP_FRAME_SIZE + EAP_TLS_HDR_SIZE)); |
| 1429 | |
| 1430 | /* If the more bit is set, wait for the next fragment. Otherwise dump what we have into the state machine. */ |
| 1431 | if ((unpacked_msg.eap_msg.tls.eap_tls_flags & EAP_TLS_FLAG_MORE_BIT) != 0) |
| 1432 | { |
| 1433 | /* Need to acknowledge the fragment? The standard does not define a message set that will get us here. */ |
| 1434 | _dpoe_eap_tls_send_tls_frag_ack_to_onu(link_rec); |
| 1435 | rc = BCM_ERR_IN_PROGRESS; |
| 1436 | } |
| 1437 | else |
| 1438 | { |
| 1439 | if (link_rec->auth_ctrl.tls_total_length != link_rec->auth_ctrl.tls_frag_length) |
| 1440 | { |
| 1441 | return BCM_ERR_PARSE; |
| 1442 | } |
| 1443 | |
| 1444 | rc = _dpoe_eap_tls_process_tls_records(link_rec); |
| 1445 | _dpoe_eap_tls_free_tls_frag_buffer(&link_rec->auth_ctrl); |
| 1446 | } |
| 1447 | } |
| 1448 | |
| 1449 | return rc; |
| 1450 | } |
| 1451 | |
| 1452 | void dpoe_eap_tls_cleanup(dpoe_sec_link_rec *link_rec) |
| 1453 | { |
| 1454 | /* Parameter checks */ |
| 1455 | BUG_ON(link_rec == NULL); |
| 1456 | |
| 1457 | // We are done with this. |
| 1458 | if (link_rec->auth_ctrl.certificate != NULL) |
| 1459 | { |
| 1460 | bcmos_free(link_rec->auth_ctrl.certificate); |
| 1461 | } |
| 1462 | |
| 1463 | if (link_rec->auth_ctrl.trans_data.rsa != NULL) |
| 1464 | { |
| 1465 | dpoe_sec_rsa_key_free(link_rec->auth_ctrl.trans_data.rsa); |
| 1466 | link_rec->auth_ctrl.trans_data.rsa = NULL; |
| 1467 | } |
| 1468 | |
| 1469 | link_rec->auth_ctrl.certLen = 0; |
| 1470 | |
| 1471 | _dpoe_eap_tls_free_tls_frag_buffer(&link_rec->auth_ctrl); |
| 1472 | |
| 1473 | memset(&link_rec->auth_ctrl, 0, sizeof(link_rec->auth_ctrl)); |
| 1474 | } |
| 1475 | |
| 1476 | bcmos_errno dpoe_eap_tls_init(f_dpoe_sec_auth_cb auth_cb, f_dpoe_sec_cert_trust_cb cert_trust_cb) |
| 1477 | { |
| 1478 | _auth_complete = auth_cb; |
| 1479 | _cert_trust = cert_trust_cb; |
| 1480 | |
| 1481 | return dpoe_sec_rng_seed(); |
| 1482 | } |
| 1483 | |