1 /* 2 * Copyright (c) 2004 - Michael Richardson <mcr@xelerance.com> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that: (1) source code distributions 6 * retain the above copyright notice and this paragraph in its entirety, (2) 7 * distributions including binary code include the above copyright notice and 8 * this paragraph in its entirety in the documentation or other materials 9 * provided with the distribution, and (3) all advertising materials mentioning 10 * features or use of this software display the following acknowledgement: 11 * ``This product includes software developed by the University of California, 12 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 13 * the University nor the names of its contributors may be used to endorse 14 * or promote products derived from this software without specific prior 15 * written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 */ 20 21 /* \summary: Extensible Authentication Protocol (EAP) printer */ 22 23 #include <sys/cdefs.h> 24 #ifndef lint 25 __RCSID("$NetBSD: print-eap.c,v 1.9 2024/09/02 16:15:31 christos Exp $"); 26 #endif 27 28 #include <config.h> 29 30 #include "netdissect-stdinc.h" 31 32 #include "netdissect.h" 33 #include "extract.h" 34 35 #define EAP_FRAME_TYPE_PACKET 0 36 #define EAP_FRAME_TYPE_START 1 37 #define EAP_FRAME_TYPE_LOGOFF 2 38 #define EAP_FRAME_TYPE_KEY 3 39 #define EAP_FRAME_TYPE_ENCAP_ASF_ALERT 4 40 41 struct eap_frame_t { 42 nd_uint8_t version; 43 nd_uint8_t type; 44 nd_uint16_t length; 45 }; 46 47 static const struct tok eap_frame_type_values[] = { 48 { EAP_FRAME_TYPE_PACKET, "EAP packet" }, 49 { EAP_FRAME_TYPE_START, "EAPOL start" }, 50 { EAP_FRAME_TYPE_LOGOFF, "EAPOL logoff" }, 51 { EAP_FRAME_TYPE_KEY, "EAPOL key" }, 52 { EAP_FRAME_TYPE_ENCAP_ASF_ALERT, "Encapsulated ASF alert" }, 53 { 0, NULL} 54 }; 55 56 /* RFC 3748 */ 57 struct eap_packet_t { 58 nd_uint8_t code; 59 nd_uint8_t id; 60 nd_uint16_t length; 61 }; 62 63 #define EAP_REQUEST 1 64 #define EAP_RESPONSE 2 65 #define EAP_SUCCESS 3 66 #define EAP_FAILURE 4 67 68 static const struct tok eap_code_values[] = { 69 { EAP_REQUEST, "Request" }, 70 { EAP_RESPONSE, "Response" }, 71 { EAP_SUCCESS, "Success" }, 72 { EAP_FAILURE, "Failure" }, 73 { 0, NULL} 74 }; 75 76 #define EAP_TYPE_NO_PROPOSED 0 77 #define EAP_TYPE_IDENTITY 1 78 #define EAP_TYPE_NOTIFICATION 2 79 #define EAP_TYPE_NAK 3 80 #define EAP_TYPE_MD5_CHALLENGE 4 81 #define EAP_TYPE_OTP 5 82 #define EAP_TYPE_GTC 6 83 #define EAP_TYPE_TLS 13 /* RFC 5216 */ 84 #define EAP_TYPE_SIM 18 /* RFC 4186 */ 85 #define EAP_TYPE_TTLS 21 /* RFC 5281, draft-funk-eap-ttls-v0-01.txt */ 86 #define EAP_TYPE_AKA 23 /* RFC 4187 */ 87 #define EAP_TYPE_FAST 43 /* RFC 4851 */ 88 #define EAP_TYPE_EXPANDED_TYPES 254 89 #define EAP_TYPE_EXPERIMENTAL 255 90 91 static const struct tok eap_type_values[] = { 92 { EAP_TYPE_NO_PROPOSED, "No proposed" }, 93 { EAP_TYPE_IDENTITY, "Identity" }, 94 { EAP_TYPE_NOTIFICATION, "Notification" }, 95 { EAP_TYPE_NAK, "Nak" }, 96 { EAP_TYPE_MD5_CHALLENGE, "MD5-challenge" }, 97 { EAP_TYPE_OTP, "OTP" }, 98 { EAP_TYPE_GTC, "GTC" }, 99 { EAP_TYPE_TLS, "TLS" }, 100 { EAP_TYPE_SIM, "SIM" }, 101 { EAP_TYPE_TTLS, "TTLS" }, 102 { EAP_TYPE_AKA, "AKA" }, 103 { EAP_TYPE_FAST, "FAST" }, 104 { EAP_TYPE_EXPANDED_TYPES, "Expanded types" }, 105 { EAP_TYPE_EXPERIMENTAL, "Experimental" }, 106 { 0, NULL} 107 }; 108 109 #define EAP_TLS_EXTRACT_BIT_L(x) (((x)&0x80)>>7) 110 111 /* RFC 5216 - EAP TLS bits */ 112 #define EAP_TLS_FLAGS_LEN_INCLUDED (1 << 7) 113 #define EAP_TLS_FLAGS_MORE_FRAGMENTS (1 << 6) 114 #define EAP_TLS_FLAGS_START (1 << 5) 115 116 static const struct tok eap_tls_flags_values[] = { 117 { EAP_TLS_FLAGS_LEN_INCLUDED, "L bit" }, 118 { EAP_TLS_FLAGS_MORE_FRAGMENTS, "More fragments bit"}, 119 { EAP_TLS_FLAGS_START, "Start bit"}, 120 { 0, NULL} 121 }; 122 123 #define EAP_TTLS_VERSION(x) ((x)&0x07) 124 125 /* EAP-AKA and EAP-SIM - RFC 4187 */ 126 #define EAP_AKA_CHALLENGE 1 127 #define EAP_AKA_AUTH_REJECT 2 128 #define EAP_AKA_SYNC_FAILURE 4 129 #define EAP_AKA_IDENTITY 5 130 #define EAP_SIM_START 10 131 #define EAP_SIM_CHALLENGE 11 132 #define EAP_AKA_NOTIFICATION 12 133 #define EAP_AKA_REAUTH 13 134 #define EAP_AKA_CLIENT_ERROR 14 135 136 static const struct tok eap_aka_subtype_values[] = { 137 { EAP_AKA_CHALLENGE, "Challenge" }, 138 { EAP_AKA_AUTH_REJECT, "Auth reject" }, 139 { EAP_AKA_SYNC_FAILURE, "Sync failure" }, 140 { EAP_AKA_IDENTITY, "Identity" }, 141 { EAP_SIM_START, "Start" }, 142 { EAP_SIM_CHALLENGE, "Challenge" }, 143 { EAP_AKA_NOTIFICATION, "Notification" }, 144 { EAP_AKA_REAUTH, "Reauth" }, 145 { EAP_AKA_CLIENT_ERROR, "Client error" }, 146 { 0, NULL} 147 }; 148 149 /* 150 * Print EAP requests / responses 151 */ 152 void 153 eap_print(netdissect_options *ndo, 154 const u_char *cp, 155 u_int length) 156 { 157 u_int type, subtype, len; 158 u_int count; 159 const char *sep; 160 161 ndo->ndo_protocol = "eap"; 162 type = GET_U_1(cp); 163 len = GET_BE_U_2(cp + 2); 164 if (len != length) { 165 /* 166 * Probably a fragment; in some cases the fragmentation might 167 * not put an EAP header on every packet, if reassembly can 168 * be done without that (e.g., fragmentation to make a message 169 * fit in multiple TLVs in a RADIUS packet). 170 */ 171 ND_PRINT("EAP fragment?"); 172 return; 173 } 174 ND_PRINT("%s (%u), id %u, len %u", 175 tok2str(eap_code_values, "unknown", type), 176 type, 177 GET_U_1((cp + 1)), 178 len); 179 if (len < 4) { 180 ND_PRINT(" (too short for EAP header)"); 181 return; 182 } 183 184 ND_TCHECK_LEN(cp, len); 185 186 if (type == EAP_REQUEST || type == EAP_RESPONSE) { 187 /* RFC 3748 Section 4.1 */ 188 if (len < 5) { 189 ND_PRINT(" (too short for EAP request/response)"); 190 return; 191 } 192 subtype = GET_U_1(cp + 4); 193 ND_PRINT("\n\t\t Type %s (%u)", 194 tok2str(eap_type_values, "unknown", subtype), 195 subtype); 196 197 switch (subtype) { 198 case EAP_TYPE_IDENTITY: 199 /* According to RFC 3748, the message is optional */ 200 if (len > 5) { 201 ND_PRINT(", Identity: "); 202 nd_printjnp(ndo, cp + 5, len - 5); 203 } 204 break; 205 206 case EAP_TYPE_NOTIFICATION: 207 /* According to RFC 3748, there must be at least one octet of message */ 208 if (len < 6) { 209 ND_PRINT(" (too short for EAP Notification request/response)"); 210 return; 211 } 212 ND_PRINT(", Notification: "); 213 nd_printjnp(ndo, cp + 5, len - 5); 214 break; 215 216 case EAP_TYPE_NAK: 217 /* 218 * one or more octets indicating 219 * the desired authentication 220 * type one octet per type 221 */ 222 if (len < 6) { 223 ND_PRINT(" (too short for EAP Legacy NAK request/response)"); 224 return; 225 } 226 sep = ""; 227 for (count = 5; count < len; count++) { 228 ND_PRINT("%s %s (%u)", sep, 229 tok2str(eap_type_values, "unknown", GET_U_1((cp + count))), 230 GET_U_1(cp + count)); 231 sep = ","; 232 } 233 break; 234 235 case EAP_TYPE_TTLS: 236 case EAP_TYPE_TLS: 237 if (len < 6) { 238 ND_PRINT(" (too short for EAP TLS/TTLS request/response)"); 239 return; 240 } 241 if (subtype == EAP_TYPE_TTLS) 242 ND_PRINT(" TTLSv%u", 243 EAP_TTLS_VERSION(GET_U_1((cp + 5)))); 244 ND_PRINT(" flags [%s] 0x%02x", 245 bittok2str(eap_tls_flags_values, "none", GET_U_1((cp + 5))), 246 GET_U_1(cp + 5)); 247 248 if (EAP_TLS_EXTRACT_BIT_L(GET_U_1(cp + 5))) { 249 if (len < 10) { 250 ND_PRINT(" (too short for EAP TLS/TTLS request/response with length)"); 251 return; 252 } 253 ND_PRINT(", len %u", GET_BE_U_4(cp + 6)); 254 } 255 break; 256 257 case EAP_TYPE_FAST: 258 if (len < 6) { 259 ND_PRINT(" (too short for EAP FAST request/response)"); 260 return; 261 } 262 ND_PRINT(" FASTv%u", 263 EAP_TTLS_VERSION(GET_U_1((cp + 5)))); 264 ND_PRINT(" flags [%s] 0x%02x", 265 bittok2str(eap_tls_flags_values, "none", GET_U_1((cp + 5))), 266 GET_U_1(cp + 5)); 267 268 if (EAP_TLS_EXTRACT_BIT_L(GET_U_1(cp + 5))) { 269 if (len < 10) { 270 ND_PRINT(" (too short for EAP FAST request/response with length)"); 271 return; 272 } 273 ND_PRINT(", len %u", GET_BE_U_4(cp + 6)); 274 } 275 276 /* FIXME - TLV attributes follow */ 277 break; 278 279 case EAP_TYPE_AKA: 280 case EAP_TYPE_SIM: 281 if (len < 6) { 282 ND_PRINT(" (too short for EAP SIM/AKA request/response)"); 283 return; 284 } 285 ND_PRINT(" subtype [%s] 0x%02x", 286 tok2str(eap_aka_subtype_values, "unknown", GET_U_1((cp + 5))), 287 GET_U_1(cp + 5)); 288 289 /* FIXME - TLV attributes follow */ 290 break; 291 292 case EAP_TYPE_MD5_CHALLENGE: 293 case EAP_TYPE_OTP: 294 case EAP_TYPE_GTC: 295 case EAP_TYPE_EXPANDED_TYPES: 296 case EAP_TYPE_EXPERIMENTAL: 297 default: 298 break; 299 } 300 } 301 return; 302 trunc: 303 nd_print_trunc(ndo); 304 } 305 306 void 307 eapol_print(netdissect_options *ndo, 308 const u_char *cp) 309 { 310 const struct eap_frame_t *eap; 311 u_int eap_type, eap_len; 312 313 ndo->ndo_protocol = "eap"; 314 eap = (const struct eap_frame_t *)cp; 315 ND_TCHECK_SIZE(eap); 316 eap_type = GET_U_1(eap->type); 317 318 ND_PRINT("%s (%u) v%u, len %u", 319 tok2str(eap_frame_type_values, "unknown", eap_type), 320 eap_type, 321 GET_U_1(eap->version), 322 GET_BE_U_2(eap->length)); 323 if (ndo->ndo_vflag < 1) 324 return; 325 326 cp += sizeof(struct eap_frame_t); 327 eap_len = GET_BE_U_2(eap->length); 328 329 switch (eap_type) { 330 case EAP_FRAME_TYPE_PACKET: 331 if (eap_len == 0) 332 goto trunc; 333 ND_PRINT(", "); 334 eap_print(ndo, cp, eap_len); 335 return; 336 case EAP_FRAME_TYPE_LOGOFF: 337 case EAP_FRAME_TYPE_ENCAP_ASF_ALERT: 338 default: 339 break; 340 } 341 return; 342 343 trunc: 344 nd_print_trunc(ndo); 345 } 346