def access_accept(cls, request: AuthRequest, session: EapPeapSession): data = [ request.nas_ip, request.nas_name, request.auth_protocol, session.auth_user.peap_username, request.user_mac, request.ssid, request.ap_mac, ] log.info(f'OUT: accept|{"|".join(data)}|') reply = AuthResponse.create_access_accept(request=request) reply['State'] = session.session_id.encode() # octets 传入 bytes log.debug( f'msk: {session.msk}, secret: {reply.secret}, authenticator: {request.authenticator}' ) reply['MS-MPPE-Recv-Key'], reply[ 'MS-MPPE-Send-Key'] = create_mppe_recv_key_send_key( session.msk, reply.secret, request.authenticator) reply['EAP-Message'] = struct.pack('!B B H', EapPacket.CODE_EAP_SUCCESS, session.current_eap_id - 1, 4) # eap_id抓包是这样, 不要惊讶! request.reply_to(reply) session.set_reply(reply) SessionCache.clean(session_id=session.session_id)
def peap_challenge_change_cipher_spec(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): assert peap.tls_data p_tls_in_data = ctypes.create_string_buffer(peap.tls_data) tls_in_data_len = ctypes.c_ulonglong(len(peap.tls_data)) p_tls_in, p_tls_out = None, None try: p_tls_in = libhostapd.call_py_wpabuf_alloc(p_tls_in_data, tls_in_data_len) p_tls_out = libhostapd.call_tls_connection_server_handshake( tls_connection=session.tls_connection, p_tls_in=p_tls_in) tls_out_data_len = p_tls_out.contents.used tls_out_data = ctypes.string_at(p_tls_out.contents.buf, tls_out_data_len) peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) finally: libhostapd.call_free_alloc(p_tls_in) libhostapd.call_free_alloc(p_tls_out) # judge next move session.next_state = cls.PEAP_CHALLENGE_PHASE2_IDENTITY return
def peap_challenge_phase2_identity(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): # 返回数据 eap_identity = EapPacket(code=EapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, type_dict={ 'type': EapPacket.TYPE_EAP_IDENTITY, 'type_data': b'' }) tls_plaintext = eap_identity.ReplyPacket() # 加密 tls_out_data = libhostapd.encrypt(session.tls_connection, tls_plaintext) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_CHALLENGE_GTC_PASSWORD return
def peap_challenge_phase2_identity(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): # 返回数据 eap_identity = EapPacket(code=EapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, type_dict={ 'type': EapPacket.TYPE_EAP_IDENTITY, 'type_data': b'' }) tls_plaintext: bytes = eap_identity.ReplyPacket() # 加密 # EAP-PEAP: Encrypting Phase 2 data - hexdump(len=5): 01 06 00 05 01 tls_out_data: bytes = libhostapd.encrypt( session.tls_connection, tls_plaintext, peap_version=session.peap_version) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data, flag_version=session.peap_version) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_CHALLENGE_MSCHAPV2_RANDOM return
def peap_challenge_server_hello(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): # 客户端 PEAP 版本 log.debug(f'eap header, peap version: {peap.flag_version}') session.set_peap_version(peap.flag_version) # 初始化 tls_connection if session.tls_connection is None: session.tls_connection = libhostapd.call_tls_connection_init() assert peap.tls_data p_tls_in_data = ctypes.create_string_buffer(peap.tls_data) tls_in_data_len = ctypes.c_ulonglong(len(peap.tls_data)) p_tls_in, p_tls_out = None, None try: p_tls_in = libhostapd.call_py_wpabuf_alloc(p_tls_in_data, tls_in_data_len) p_tls_out = libhostapd.call_tls_connection_server_handshake( tls_connection=session.tls_connection, p_tls_in=p_tls_in) tls_out_data_len = p_tls_out.contents.used tls_out_data = ctypes.string_at(p_tls_out.contents.buf, tls_out_data_len) session.certificate_fragment = EapPeapPacket( code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data) reply = AuthResponse.create_peap_challenge( request=request, peap=session.certificate_fragment, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) finally: libhostapd.call_free_alloc(p_tls_in) libhostapd.call_free_alloc(p_tls_out) # judge next move if session.certificate_fragment.is_last_fragment(): # 不用分包 session.next_state = cls.PEAP_CHALLENGE_CHANGE_CIPHER_SPEC else: # 需要分包 session.next_state = cls.PEAP_CHALLENGE_SERVER_HELLO_FRAGMENT session.certificate_fragment.go_next_fragment() return
def peap_challenge_gtc_password(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): assert peap.tls_data # 解密 tls_decrypt_data = libhostapd.decrypt(session.tls_connection, peap.tls_data) eap_identity = EapPacket.parse(packet=tls_decrypt_data) account_name = eap_identity.type_data.decode() # 保存用户名 session.auth_user.set_peap_username(account_name) # 查找用户密码 account = Account.get(username=account_name) if not account or account.is_expired(): raise AccessReject() # 保存用户密码 session.auth_user.set_user_password(account.radius_password) # 返回数据 response_data = b'Password' type_data = struct.pack(f'!{len(response_data)}s', response_data) eap_password = EapPacket(code=EapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, type_dict={ 'type': EapPacket.TYPE_EAP_GTC, 'type_data': type_data }) tls_plaintext = eap_password.ReplyPacket() # 加密 tls_out_data = libhostapd.encrypt(session.tls_connection, tls_plaintext) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_CHALLENGE_SUCCESS return
def peap_challenge_success(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): # 解密. # v0: EAP-PEAP: Decrypted Phase 2 EAP - hexdump(len=2): 1a 03 # v1: EAP-PEAP: Decrypted Phase 2 EAP - hexdump(len=6): 02 08 00 06 1a 03 tls_decrypt_data = libhostapd.decrypt(session.tls_connection, peap.tls_data) # 返回数据 eap_tlv_success if session.peap_version == 0: type_data = struct.pack(f'!B B H H', 0x80, EapPacket.TYPE_RESULT_TLV, 2, EapPacket.TYPE_RESULT_TLV_SUCCESS) eap_tlv_success = EapPacket(code=EapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, type_dict={ 'type': EapPacket.TYPE_EAP_TLV, 'type_data': type_data }) tls_plaintext: bytes = eap_tlv_success.ReplyPacket() else: # 返回数据 eap_success eap_success = EapPacket(code=EapPacket.CODE_EAP_SUCCESS, id=session.current_eap_id) tls_plaintext = eap_success.ReplyPacket() # 加密. # v0: EAP-PEAP: Encrypting Phase 2 TLV data - hexdump(len=11): 01 08 00 0b 21 80 03 00 02 00 01 # v1: EAP-PEAP: Encrypting Phase 2 data - hexdump(len=4): 03 09 00 04 tls_out_data: bytes = libhostapd.encrypt(session.tls_connection, tls_plaintext) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data, flag_version=session.peap_version) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_ACCESS_ACCEPT return
def peap_challenge_success(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): # 解密 tls_decrypt_data = libhostapd.decrypt(session.tls_connection, peap.tls_data) eap_password = EapPacket.parse(packet=tls_decrypt_data) auth_password = eap_password.type_data.decode() log.debug( f'PEAP account: {session.auth_user.peap_username}, packet_password: {auth_password}' ) def is_correct_password() -> bool: return session.auth_user.user_password == auth_password if not is_correct_password(): log.error( f'user_password: {session.auth_user.user_password} not correct' ) # 返回数据 eap_failure eap_failure = EapPacket(code=EapPacket.CODE_EAP_FAILURE, id=session.current_eap_id) tls_plaintext = eap_failure.ReplyPacket() else: # 返回数据 eap_success eap_success = EapPacket(code=EapPacket.CODE_EAP_SUCCESS, id=session.current_eap_id) tls_plaintext = eap_success.ReplyPacket() # 加密 tls_out_data = libhostapd.encrypt(session.tls_connection, tls_plaintext) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_ACCESS_ACCEPT return
def peap_challenge_server_hello_fragment(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): session.certificate_fragment.id = session.current_eap_id reply = AuthResponse.create_peap_challenge( request=request, peap=session.certificate_fragment, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move if session.certificate_fragment.is_last_fragment(): # 分包结束 session.next_state = cls.PEAP_CHALLENGE_CHANGE_CIPHER_SPEC else: # 继续分包 session.next_state = cls.PEAP_CHALLENGE_SERVER_HELLO_FRAGMENT session.certificate_fragment.go_next_fragment() return
def peap_challenge_start(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): # EAP-Message: b'\x02\x01\x00\r\x01testuser' assert eap.type == EapPacket.TYPE_EAP_IDENTITY identity = eap.type_data.decode() log.debug(f'before PEAP Start, identity: {identity}') # 返回 support_peap_version = 1 eap_start = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, flag_start=1, flag_version=support_peap_version) reply = AuthResponse.create_peap_challenge( request=request, peap=eap_start, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_CHALLENGE_SERVER_HELLO return
def access_accept(cls, request: AuthRequest, session: BaseSession): data = [ request.nas_ip, request.nas_name, request.auth_protocol, request.username, request.user_mac, request.ssid, request.ap_mac, ] log.info(f'OUT: accept|{"|".join(data)}|') reply = AuthResponse.create_access_accept(request=request) return request.reply_to(reply)
def access_reject(cls, request: AuthRequest, auth_user: AuthUser): if not request and not auth_user: return data = [ request.nas_ip, request.nas_name, request.auth_protocol, request.username, request.user_mac, request.ssid, request.ap_mac, ] log.info(f'OUT: reject|{"|".join(data)}|') reply = AuthResponse.create_access_reject(request=request) # 增加上 EAP-Failure: 04000004 # TODO 验证reject有没有这个消息! # reply['EAP-Message'] = struct.pack('!B B H', EapPacket.CODE_EAP_SUCCESS, 0, 4) return request.reply_to(reply)
def state_machine(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): """ :param request: :param eap: :param peap: :param session: """ if session.prev_id == request.id or session.prev_eap_id == eap.id: # 重复请求 if session.reply: # 会话已经处理过 reply = session.reply request.reply_to(reply) log.warning( f'duplicate packet, resend. id: {reply.id}, username: {request.username},' f'mac: {request.user_mac}, next_state: {session.next_state}' ) return else: # 会话正在处理中 log.warning( f'processor handling. username: {request.username}, mac: {request.user_mac}, next_state: {session.next_state}' ) return # 第一个报文 OR 符合服务端预期的 response elif session.current_eap_id == -1 or session.current_eap_id == eap.id: # 正常eap-peap流程 session.current_eap_id = EapPacket.get_next_id(eap.id) log.info( f'peap auth. session_id: {session.session_id}, call next_state: {session.next_state}' ) if eap.type == EapPacket.TYPE_EAP_IDENTITY and session.next_state == cls.PEAP_CHALLENGE_START: return cls.peap_challenge_start(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_SERVER_HELLO: # peap: client hello return cls.peap_challenge_server_hello(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_SERVER_HELLO_FRAGMENT: # peap: return cls.peap_challenge_server_hello_fragment( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_CHANGE_CIPHER_SPEC: # peap: Client Key Exchange; Change Cipher Spec; Encrypted Handshake Message; return cls.peap_challenge_change_cipher_spec( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_PHASE2_IDENTITY: # peap: identity return cls.peap_challenge_phase2_identity( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_GTC_PASSWORD: return cls.peap_challenge_gtc_password(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_SUCCESS: return cls.peap_challenge_success(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_ACCESS_ACCEPT: return cls.peap_access_accept(request, eap, peap, session) # end move else: log.error('eap peap auth error. unknown eap packet type') raise AccessReject() log.error( f'id error. [prev, recv][{session.prev_id}, {request.id}][{session.prev_eap_id}, {eap.id}]' ) raise AccessReject()
def state_machine(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): """ :param request: :param eap: :param peap: :param session: """ if eap.type == EapPacket.TYPE_EAP_NAK: log.error('receive Nak. Client not support EAP-PEAP!') raise AccessReject() if session.prev_id == request.id or session.prev_eap_id == eap.id: # 重复请求 if session.reply: # 会话已经处理过 reply = session.reply request.reply_to(reply) log.warning( f'duplicate packet, resend. id: {reply.id}, username: {request.username},' f'mac: {request.user_mac}, next_state: {session.next_state}' ) return else: # 会话正在处理中 log.warning( f'processor handling. username: {request.username}, mac: {request.user_mac}, next_state: {session.next_state}' ) return # 第一个报文 OR 符合服务端预期的 response elif session.current_eap_id == -1 or session.current_eap_id == eap.id: # 正常eap-peap流程 session.current_eap_id = EapPacket.get_next_id(eap.id) log.info( f'peap auth. session_id: {session.session_id}, call next_state: {session.next_state}' ) if eap.type == EapPacket.TYPE_EAP_IDENTITY and session.next_state == cls.PEAP_CHALLENGE_START: return cls.peap_challenge_start(request, eap, peap, session) # 以下流程 request 报文 phase1 协商协议必须是 EAP-PEAP assert eap.type == EapPacket.TYPE_EAP_PEAP if peap is not None and session.next_state == cls.PEAP_CHALLENGE_SERVER_HELLO: return cls.peap_challenge_server_hello(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_SERVER_HELLO_FRAGMENT: return cls.peap_challenge_server_hello_fragment( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_CHANGE_CIPHER_SPEC: return cls.peap_challenge_change_cipher_spec( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_PHASE2_IDENTITY: return cls.peap_challenge_phase2_identity( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_MSCHAPV2_RANDOM: return cls.peap_challenge_mschapv2_random( request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_MSCHAPV2_NT: return cls.peap_challenge_mschapv2_nt(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_CHALLENGE_SUCCESS: return cls.peap_challenge_success(request, eap, peap, session) elif peap is not None and session.next_state == cls.PEAP_ACCESS_ACCEPT: return cls.peap_access_accept(request, eap, peap, session) # end move else: log.error('eap peap auth error. unknown eap packet type') raise AccessReject() log.error( f'id error. [prev, recv][{session.prev_id}, {request.id}][{session.prev_eap_id}, {eap.id}]' ) raise AccessReject()
def peap_challenge_mschapv2_nt(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): assert peap.tls_data # 解密 # v0: EAP-PEAP: Decrypted Phase 2 EAP - hexdump(len=63): 1a 02 06 00 3e 31 b1 3a 4c 4f 8d 2a 09 3d 89 b2 f8 eb c1 ec 53 f0 00 00 # 00 00 00 00 00 00 e5 39 9d 11 d6 06 0b b9 95 8e 16 f2 20 fc 4b c9 b0 ab 4e fd bc 62 01 39 00 74 65 73 74 75 73 65 72 # v1: EAP-PEAP: Decrypted Phase 2 EAP - hexdump(len=67): 02 07 00 43 1a 02 07 00 3e 31 16 79 ba 65 ad 16 7f 92 5c 74 c9 80 53 d6 # fc 4c 00 00 00 00 00 00 00 00 72 0e 3d a8 8d bd f8 a9 e8 bd 1a 95 d9 5f 08 03 7e 10 db 9f 01 d4 a5 fc 00 74 65 73 74 75 73 65 72 tls_decrypt_data = libhostapd.decrypt(session.tls_connection, peap.tls_data) # MSCHAPV2_OP_RESPONSE(02) + 与EAP_id相同(07) + MSCHAPV2_OP 到结束的长度(00 3e) + # 随机数长度(31) + # 24位随机数内含8位0(16 79 ba 65 ad 16 7f 92 5c 74 c9 80 53 d6 fc 4c + 00 00 00 00 00 00 00 00) + # 24位NT-Response(72 0e 3d a8 8d bd f8 a9 e8 bd 1a 95 d9 5f 08 03 7e 10 db 9f 01 d4 a5 fc) + # Flags(00) + # 用户名(74 65 73 74 75 73 65 72) mschapv2_random: EapMschapv2Packet = EapMschapv2Packet.parse( packet=tls_decrypt_data, peap_version=session.peap_version) log.trace(f'mschapv2_random: {mschapv2_random}') if mschapv2_random.type != EapPacket.TYPE_EAP_MSCHAPV2: log.error('not receive mschapv2_random') raise AccessReject() mschapv2_type, eap_id, mschapv2_length, fix_length = struct.unpack( '!B B H B', mschapv2_random.type_data[:5]) assert fix_length == 0x31 == 49 username_len = mschapv2_length - 5 - fix_length peer_challenge: bytes nt_response: bytes flag: bytes identity: bytes peer_challenge, nt_response, flag, identity = struct.unpack( f'!24s 24s B {username_len}s', mschapv2_random.type_data[5:]) peer_challenge = peer_challenge[:16] # 保存客户端随机数 session.auth_user.set_peer_challenge(peer_challenge) assert identity.decode() == session.auth_user.peap_username # 计算期望密码哈希值 p_username = ctypes.create_string_buffer( session.auth_user.peap_username.encode()) l_username_len = ctypes.c_ulonglong(username_len) p_password = ctypes.create_string_buffer( session.auth_user.user_password.encode()) l_password_len = ctypes.c_ulonglong( len(session.auth_user.user_password)) p_expect = libhostapd.call_generate_nt_response( p_auth_challenge=session.auth_user.server_challenge, p_peer_challenge=session.auth_user.peer_challenge, p_username=p_username, l_username_len=l_username_len, p_password=p_password, l_password_len=l_password_len, ) expect: bytes = ctypes.string_at(p_expect, len(p_expect)) log.trace(f'nt_response: {nt_response}') log.trace(f'expect: {expect}') # 判断密码是否正确 def is_correct_password() -> bool: return nt_response == expect if not is_correct_password(): # 密码整错 log.error(f'user_password not correct') # 返回数据 eap_failure eap_failure = EapPacket(code=EapPacket.CODE_EAP_FAILURE, id=session.current_eap_id) tls_plaintext: bytes = eap_failure.ReplyPacket() else: # 计算 md4(password) p_password_md4 = libhostapd.call_nt_password_hash( p_password=p_password, l_password_len=l_password_len) # 计算返回报文中的 authenticator_response p_peer_challenge = ctypes.create_string_buffer( session.auth_user.peer_challenge) p_auth_challenge = ctypes.create_string_buffer( session.auth_user.server_challenge) p_nt_response = ctypes.create_string_buffer(nt_response) p_out_auth_response = libhostapd.call_generate_authenticator_response_pwhash( p_password_md4=p_password_md4, p_peer_challenge=p_peer_challenge, p_auth_challenge=p_auth_challenge, p_username=p_username, l_username_len=l_username_len, p_nt_response=p_nt_response, ) authenticator_response: bytes = ctypes.string_at( p_out_auth_response, len(p_out_auth_response)) authenticator_response: bytes = authenticator_response.hex().upper( ).encode() # 返回数据 # MSCHAPV2_OP_SUCCESS(03) + EAP_id减一(07) + MSCHAPV2_OP 到结束的长度(00 33) + # S=(53 3d) + # 40个字符: generate_authenticator_response_pwhash 计算出来的哈希值再换成hex大写(37 43 36 39 38 34 37 38 39 44 34 39 44 30 38 32 33 34 35 45 35 31 43 44 45 38 46 35 36 30 33 42 41 44 31 43 34 34 37 33) # + 空格(20) + # M=(4d 3d) + # OK(4f 4b) response_msg = b'OK' response_msg_len = len(response_msg) size_of_auth_response = 20 size_of_mschapv2_hdr = 4 message = '' type_data_length = size_of_mschapv2_hdr + 2 + ( 2 * size_of_auth_response) + 1 + 2 + response_msg_len type_data = struct.pack( f'!B B H 2s {2 * size_of_auth_response}s 3s {response_msg_len}s', EapPacket.CODE_MSCHAPV2_SUCCESS, session.current_eap_id - 1, type_data_length, b'S=', authenticator_response, b' M=', response_msg) eap_ok = EapPacket(code=EapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, type_dict={ 'type': EapPacket.TYPE_EAP_MSCHAPV2, 'type_data': type_data }) tls_plaintext: bytes = eap_ok.ReplyPacket() # 加密 # v0, v1: EAP-PEAP: Encrypting Phase 2 data - hexdump(len=56): 01 07 00 38 1a 03 06 00 33 53 3d 45 37 35 35 44 37 30 42 43 42 42 35 44 31 # 43 38 41 45 33 35 35 42 30 38 41 42 31 39 36 42 37 45 33 44 42 43 38 46 31 36 20 4d 3d 4f 4b tls_out_data: bytes = libhostapd.encrypt( session.tls_connection, tls_plaintext, peap_version=session.peap_version) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data, flag_version=session.peap_version) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_CHALLENGE_SUCCESS return
def peap_challenge_mschapv2_random(cls, request: AuthRequest, eap: EapPacket, peap: EapPeapPacket, session: EapPeapSession): assert peap.tls_data # 解密 # v0: EAP-PEAP: Decrypted Phase 2 EAP - hexdump(len=9): 01 74 65 73 74 75 73 65 72 # v1: EAP-PEAP: Decrypted Phase 2 EAP - hexdump(len=13): 02 06 00 0d 01 74 65 73 74 75 73 65 72 tls_decrypt_data = libhostapd.decrypt(session.tls_connection, peap.tls_data) eap_identity = EapMschapv2Packet.parse( packet=tls_decrypt_data, peap_version=session.peap_version) log.trace(f'eap_identity: {eap_identity}') if eap_identity.type != EapPacket.TYPE_EAP_IDENTITY: log.error('not receive eap_identity') raise AccessReject() account_name = eap_identity.type_data.decode() # 保存用户名 session.auth_user.set_peap_username(account_name) # 查找用户密码 account = Account.get(username=account_name) if not account or account.is_expired(): raise AccessReject() # 保存用户密码 session.auth_user.set_user_password(account.radius_password) # 返回数据 # MSCHAPV2_OP_CHALLENGE(01) + 与EAP_id相同(07) + MSCHAPV2_OP 到结束的长度(00 1c) + # 随机数长度固定值(10) + # 16位随机数(2d ae 52 bf 07 d0 de 7b 28 c4 d8 d9 8f 87 da 6a) + server_id(68 6f 73 74 61 70 64) size_of_mschapv2_hdr = 4 server_id = b'hostapd' server_id_len = len(server_id) server_challenge_len = 16 server_challenge: bytes = EapPeapPacket.random_string( length=server_challenge_len) type_data_length = size_of_mschapv2_hdr + 1 + server_challenge_len + server_id_len type_data = struct.pack(f'!B B H B 16s {server_id_len}s', EapPacket.CODE_MSCHAPV2_CHALLENGE, session.current_eap_id, type_data_length, server_challenge_len, server_challenge, server_id) eap_random = EapPacket(code=EapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, type_dict={ 'type': EapPacket.TYPE_EAP_MSCHAPV2, 'type_data': type_data }) tls_plaintext: bytes = eap_random.ReplyPacket() # 保存服务端随机数 session.auth_user.set_server_challenge(server_challenge) # 加密. # v0, v1: EAP-PEAP: Encrypting Phase 2 data - hexdump(len=33): 01 07 00 21 1a 01 07 00 1c 10 2d ae 52 bf 07 d0 de 7b 28 c4 d8 d9 8f 87 da 6a 68 # 6f 73 74 61 70 64 tls_out_data: bytes = libhostapd.encrypt( session.tls_connection, tls_plaintext, peap_version=session.peap_version) # peap_reply = EapPeapPacket(code=EapPeapPacket.CODE_EAP_REQUEST, id=session.current_eap_id, tls_data=tls_out_data, flag_version=session.peap_version) reply = AuthResponse.create_peap_challenge( request=request, peap=peap_reply, session_id=session.session_id) request.reply_to(reply) session.set_reply(reply) # judge next move session.next_state = cls.PEAP_CHALLENGE_MSCHAPV2_NT return