pycrate/pycrate_mobile/PPP.py

408 lines
12 KiB
Python

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2017. Benoit Michau. ANSSI.
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_mobile/PPP.py
# * Created : 2017-11-08
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
from pycrate_core.utils import *
from pycrate_core.elt import *
from pycrate_core.base import *
#------------------------------------------------------------------------------#
# IETF RFC 1661: The Point-to-Point Protocol (PPP)
# section 5: LCP packet format
#------------------------------------------------------------------------------#
# for LCP Code Configure-*
_LCPOptType_dict = {
1 : 'Maximum-Receive-Unit',
2 : 'Async-Control-Character-Map',
3 : 'Authentication-Protocol',
4 : 'Quality-Protocol',
5 : 'Magic-Number',
6 : 'RESERVED',
7 : 'Protocol-Field-Compression',
8 : 'Address-and-Control-Field-Compression'
}
class LCPOpt(Envelope):
_GEN = (
Uint8('Type', dic=_LCPOptType_dict),
Uint8('Len'),
Buf('Data', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: 2+self[2].get_len())
self[2].set_blauto(lambda: max(0, 8*(self[1].get_val()-2)))
class LCPDataConf(Sequence):
_GEN = LCPOpt()
# for LCP Code Terminate-* and Code-Reject
class LCPDataRaw(Buf):
_rep = REPR_HEX
# for LCP Code Protocol-Reject
class LCPDataProtRej(Envelope):
_GEN = (
Uint16('RejectedProtocol'),
Buf('RejectedInfo', rep=REPR_HEX)
)
# for LCP Code Echo-* and Discard-Request
class LCPDataEcho(Envelope):
_GEN = (
Uint32('Magic', rep=REPR_HEX),
Buf('Data', rep=REPR_HEX)
)
# global LCP format
_LCPCode_dict = {
1 : 'Configure-Request',
2 : 'Configure-Ack',
3 : 'Configure-Nak',
4 : 'Configure-Reject',
5 : 'Terminate-Request',
6 : 'Terminate-Ack',
7 : 'Code-Reject',
8 : 'Protocol-Reject',
9 : 'Echo-Request',
10 : 'Echo-Reply',
11 : 'Discard-Request'
}
class LCP(Envelope):
ENV_SEL_TRANS = False
_CConf = (1, 2, 3, 4)
_CEcho = (9, 10, 11)
_GEN = (
Uint8('Code', val=1, dic=_LCPCode_dict),
Uint8('Id'),
Uint16('Len'),
Buf('Data')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[2].set_valauto(lambda: self[3].get_len()+4)
self[3].set_blauto(lambda: max(0, 8*(self[2].get_val()-4)))
def _set_data(self, code):
data = None
if code in self._CConf:
data = LCPDataConf('Data')
elif code in self._CEcho:
data = LCPDataEcho('Data')
data[1].set_blauto(lambda: max(0, 8*(self[2].get_val()-8)))
elif code == 8:
data = LCPDataProtRej('Data')
data[1].set_blauto(lambda: max(0, 8*(self[2].get_val()-6)))
if data is not None:
self.replace(self[3], data)
def set_val(self, vals):
code = None
if isinstance(vals, (tuple, list)) and not isinstance(vals[-1], bytes_types):
code = vals[0]
elif isinstance(vals, dict) and 'Data' in vals and not isinstance(vals['Data'], bytes_types):
try:
code = vals['Code']
except:
code = 1
if code is not None:
self._set_data(code)
Envelope.set_val(self, vals)
def _from_char(self, char):
self[0]._from_char(char)
self[1]._from_char(char)
self[2]._from_char(char)
code, data = self[0].get_val(), None
self._set_data(code)
self[3]._from_char(char)
#------------------------------------------------------------------------------#
# IETF RFC 1332: The PPP Internet Protocol Control Protocol (IPCP)
# NCP packet format (variant of LCP)
#------------------------------------------------------------------------------#
# + RFC1877 (DNS@, NBNS@), RFC3241 (ROHC)
# for NCP Code Configure-*
_NCPOptType_dict = {
1 : 'IP-Addresses',
2 : 'IP-Compression-Protocol',
3 : 'IP-Address',
4 : 'Mobile-IPv4',
129 : 'Primary DNS Server Address',
130 : 'Primary NBNS Server Address',
131 : 'Secondary DNS Server Address',
132 : 'Secondary NBNS Server Address'
}
class NCPOpt(Envelope):
_GEN = (
Uint8('Type', dic=_NCPOptType_dict),
Uint8('Len'),
Buf('Data', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: 2+self[2].get_len())
self[2].set_blauto(lambda: max(0, 8*(self[1].get_val()-2)))
class NCPDataConf(Sequence):
_GEN = NCPOpt()
# global NCP format
_NCPCode_dict = {
1 : 'Configure-Request',
2 : 'Configure-Ack',
3 : 'Configure-Nak',
4 : 'Configure-Reject',
5 : 'Terminate-Request',
6 : 'Terminate-Ack',
7 : 'Code-Reject'
}
class NCP(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint8('Code', val=1, dic=_NCPCode_dict),
Uint8('Id'),
Uint16('Len'),
Buf('Data')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[2].set_valauto(lambda: self[3].get_len()+4)
self[3].set_blauto(lambda: max(0, 8*(self[2].get_val()-4)))
def set_val(self, vals):
code = None
if isinstance(vals, (tuple, list)):
data = vals[-1]
if not isinstance(data, bytes_types):
code = vals[0]
elif isinstance(vals, dict) and 'Data' in vals:
data = vals['Data']
if not isinstance(data, bytes_types):
try:
code = vals['Code']
except:
code = 1
if code is not None and 1 <= code <= 4:
Data = NCPDataConf('Data')
Data.set_num(len(data))
self.replace(self[3], Data)
Envelope.set_val(self, vals)
def _from_char(self, char):
self[0]._from_char(char)
self[1]._from_char(char)
self[2]._from_char(char)
code = self[0].get_val()
if 1 <= code <= 4:
Data = NCPDataConf('Data')
self.replace(self[3], Data)
self[3]._from_char(char)
#------------------------------------------------------------------------------#
# IETF RFC 1334: PPP Authentication Protocols (PAP and CHAP)
#------------------------------------------------------------------------------#
# PAP Authenticate-Request
class PAPAuthReq(Envelope):
_GEN = (
Uint8('PeerIDLen'),
Buf('PeerID'),
Uint8('PasswdLen'),
Buf('Passwd')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(lambda: self[1].get_len())
self[1].set_blauto(lambda: 8*self[0].get_val())
self[2].set_valauto(lambda: self[3].get_len())
self[3].set_blauto(lambda: 8*self[2].get_val())
# PAP Authenticate-Ack and Authentication-Nak
class _PAPAuthMsg(Envelope):
_GEN = (
Uint8('MsgLen'),
Buf('Msg')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(lambda: self[1].get_len())
self[1].set_blauto(lambda: 8*self[0].get_val())
class PAPAuthAck(_PAPAuthMsg):
pass
class PAPAuthNak(_PAPAuthMsg):
pass
# global PAP format
_PAPCode_dict = {
1 : 'Authenticate-Request',
2 : 'Authenticate-Ack',
3 : 'Authenticate-Nak',
}
class PAP(Envelope):
_GEN = (
Uint8('Code', val=1, dic=_PAPCode_dict),
Uint8('Id'),
Uint16('Len'),
Buf('Data')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[2].set_valauto(lambda: self[3].get_len()+4)
self[3].set_blauto(lambda: max(0, 8*(self[2].get_val()-4)))
def set_val(self, vals):
code = None
if isinstance(vals, (tuple, list)):
data = vals[-1]
if not isinstance(data, bytes_types):
code = vals[0]
elif isinstance(vals, dict) and 'Data' in vals:
data = vals['Data']
if not isinstance(data, bytes_types):
try:
code = vals['Code']
except:
code = 1
if code == 1:
self.replace(self[3], PAPAuthReq('Data'))
elif code == 2:
self.replace(self[3], PAPAuthAck('Data'))
elif code == 3:
self.replace(self[3], PAPAuthNak('Data'))
Envelope.set_val(self, vals)
def _from_char(self, char):
self[0]._from_char(char)
self[1]._from_char(char)
self[2]._from_char(char)
code = self[0].get_val()
if code == 1:
self.replace(self[3], PAPAuthReq('Data'))
elif code == 2:
self.replace(self[3], PAPAuthAck('Data'))
elif code == 3:
self.replace(self[3], PAPAuthNak('Data'))
self[3]._from_char(char)
# CHAP Challenge / Response
class _CHAPValue(Envelope):
_GEN = (
Uint8('ValueLen'),
Buf('Value', rep=REPR_HEX),
Buf('Name')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(lambda: self[1].get_len())
self[1].set_blauto(lambda: 8*self[0].get_val())
# Name will be bounded thanks to the Len field in the CHAP header
class CHAPChallenge(_CHAPValue):
pass
class CHAPResponse(_CHAPValue):
pass
# global CHAP format
_CHAPCode_dict = {
1 : 'Challenge',
2 : 'Response',
3 : 'Success',
4 : 'Failure'
}
class CHAP(Envelope):
_GEN = (
Uint8('Code', val=1, dic=_PAPCode_dict),
Uint8('Id'),
Uint16('Len'),
Buf('Data')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[2].set_valauto(lambda: self[3].get_len()+4)
self[3].set_blauto(lambda: max(0, 8*(self[2].get_val()-4)))
def set_val(self, vals):
code = None
if isinstance(vals, (tuple, list)):
data = vals[-1]
if not isinstance(data, bytes_types):
code = vals[0]
elif isinstance(vals, dict) and 'Data' in vals:
data = vals['Data']
if not isinstance(data, bytes_types):
try:
code = vals['Code']
except:
code = 1
if code == 1:
self.replace(self[3], CHAPChallenge('Data'))
elif code == 2:
self.replace(self[3], CHAPResponse('Data'))
Envelope.set_val(self, vals)
def _from_char(self, char):
Envelope._from_char(self, char)
code = self[0].get_val()
if code == 1:
data = CHAPChallenge('Data')
data.from_bytes( self[3].get_val() )
self.replace(self[3], data)
elif code == 2:
data = CHAPResponse('Data')
data.from_bytes( self[3].get_val() )
self.replace(self[3], data)