diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index e6e612d3..0c441e40 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -95,56 +95,37 @@ def authorize(data): """Fonction qui aiguille entre nas, wifi et filaire pour authorize On se contecte de faire une verification basique de ce que contien la requète pour déterminer la fonction à utiliser""" - if data.get('NAS-Port-Type', '')==u'Ethernet': + if data.get('Service-Type', '')==u'NAS-Prompt-User' or data.get('Service-Type', '')==u'Administrative-User': + return authorize_user(data) + else: return authorize_fil(data) - elif u"Wireless" in data.get('NAS-Port-Type', ''): - return authorize_wifi(data) @radius_event -def authorize_wifi(data): - """Section authorize pour le wifi - (NB: le filaire est en accept pour tout le monde) - Éxécuté avant l'authentification proprement dite. On peut ainsi remplir les - champs login et mot de passe qui serviront ensuite à l'authentification - (MschapV2/PEAP ou MschapV2/TTLS)""" +def authorize_user(data): + nas = data.get('NAS-IP-Address', None) + nas_id = data.get('NAS-Identifier', None) + user = data.get('User-Name', None) + password = data.get('User-Password', None) + out = subprocess.check_output(['/usr/bin/python3', '/var/www/re2o/freeradius_utils/authenticate_user.py', user, password]) + if out[:-1] == u"TRUE": + if data.get('Service-Type', '')==u'NAS-Prompt-User': + logger.info(u"Access of user %s on %s (%s)" % (user, nas, nas_id)) + elif data.get('Service-Type', '')==u'Administrative-User': + logger.info(u"Enable manager for %s on %s (%s)" % (user, nas, nas_id)) + return (radiusd.RLM_MODULE_UPDATED, + (), + ( + ("Auth-Type", "Accept"), + ), + ) + else: + return (radiusd.RLM_MODULE_UPDATED, + (), + ( + ("Auth-Type", "Reject"), + ), + ) - items = get_machines(data) - - if not items: - logger.error('Nothing found') - return radiusd.RLM_MODULE_NOTFOUND - - if len(items) > 1: - logger.warn('lc_ldap: Too many results (taking first)') - - machine = items[0] - - proprio = machine.proprio() - if isinstance(proprio, lc_ldap.objets.AssociationCrans): - logger.error('Crans machine trying to authenticate !') - return radiusd.RLM_MODULE_INVALID - - for bl in machine.blacklist_actif(): - if bl.value['type'] in BL_REJECT: - return radiusd.RLM_MODULE_REJECT - # Kludge : vlan isolement pas possible, donc reject quand-même - if not WIFI_DYN_VLAN and bl.value['type'] in BL_ISOLEMENT: - return radiusd.RLM_MODULE_REJECT - - - if not machine.get('ipsec', False): - logger.error('WiFi auth but machine has no password') - return radiusd.RLM_MODULE_REJECT - - password = machine['ipsec'][0].value.encode('ascii', 'ignore') - - # TODO: feed cert here - return (radiusd.RLM_MODULE_UPDATED, - (), - ( - ("Cleartext-Password", password), - ), - ) @radius_event def authorize_fil(data): @@ -152,34 +133,6 @@ def authorize_fil(data): Check le challenge chap, et accepte. """ - chap_ok = False - # Teste l'authentification chap fournie - # password et challenge doivent être données - # en hexa (avec ou sans le 0x devant) - # le User-Name est en réalité la mac ( xx:xx:xx:xx:xx ) - password = data.get('CHAP-Password', '') - challenge = data.get('CHAP-Challenge', '') - mac = data.get('User-Name', '') - - logger.debug('(fil) authorize(%r)' % ((password, challenge, mac),)) - - try: - challenge = binascii.a2b_hex(challenge.replace('0x','')) - password = binascii.a2b_hex(password.replace('0x','')) - if hashlib.md5(password[0] + mac + challenge).digest() == password[1:]: - logger.info("(fil) Chap ok") - chap_ok = True - else: - logger.info("(fil) Chap wrong") - except Exception as err: - logger.info("(fil) Chap challenge check failed with %r" % err) - - if not chap_ok: - if TEST_SERVER: - logger.debug('(fil) Continue auth (debug)') - else: - return radiusd.RLM_MODULE_REJECT - return (radiusd.RLM_MODULE_UPDATED, (), ( @@ -230,14 +183,16 @@ def post_auth_fil(data): """Idem, mais en filaire. """ - nas = data.get('NAS-Identifier', None) + nas = data.get('NAS-IP-Address', None) port = data.get('NAS-Port', None) mac = data.get('Calling-Station-Id', None) + # Hack, à cause d'une numérotation cisco baroque + port = port[-2:] out = subprocess.check_output(['/usr/bin/python3', '/var/www/re2o/freeradius_utils/authenticate_filaire.py', nas, port, mac]) - reason, vlan_id = make_tuple(out) + sw_name, reason, vlan_id = make_tuple(out) log_message = '(fil) %s -> %s [%s%s]' % \ - (nas + u":" + port, mac, vlan_id, (reason and u': ' + reason).encode('utf-8')) + (sw_name + u":" + port, mac, vlan_id, (reason and u': ' + reason).encode('utf-8')) logger.info(log_message) # Filaire diff --git a/freeradius_utils/authenticate_filaire.py b/freeradius_utils/authenticate_filaire.py index b0cc4fb5..3640240a 100755 --- a/freeradius_utils/authenticate_filaire.py +++ b/freeradius_utils/authenticate_filaire.py @@ -14,7 +14,7 @@ application = get_wsgi_application() import argparse -from machines.models import Interface +from machines.models import Interface, IpList from topologie.models import Room, Port, Switch from users.models import User @@ -23,53 +23,60 @@ from re2o.settings import RADIUS_VLAN_DECISION VLAN_NOK = RADIUS_VLAN_DECISION['VLAN_NOK'] VLAN_OK = RADIUS_VLAN_DECISION['VLAN_OK'] -def decide_vlan(switch_name, port_number, mac_address): +def decide_vlan(switch_ip, port_number, mac_address): # Get port from switch and port number - switch = Switch.objects.filter(switch_interface=Interface.objects.filter(dns=switch_name)) + switch = Switch.objects.filter(switch_interface=Interface.objects.filter(ipv4=IpList.objects.filter(ipv4=switch_ip))) if switch: + sw_name = str(switch[0].switch_interface) port = Port.objects.filter(switch=switch[0], port=port_number) if port: port = port[0] if port.radius == 'NO': # Aucune authentification sur ce port - decision = ("Pas d'authentification sur ce port", VLAN_OK) + decision = (sw_name, "Pas d'authentification sur ce port", VLAN_OK) elif port.radius == 'BLOQ': - # Prise désactivée - decision = ('Port desactive', VLAN_NOK) - elif port.radius == 'COMMON' or port.radius == 'STRICT': + # Prise désactivée + decision = (sw_name, 'Port desactive', VLAN_NOK) + elif port.radius == 'COMMON': # Authentification par mac interface = Interface.objects.filter(mac_address=mac_address) if not interface: - decision = ('Mac not found', VLAN_NOK) - elif interface[0].is_active(): - # Verification de la prise - if port.radius == 'STRICT': - if port.room: - user = User.objects.filter(room=Room.objects.filter(name=port.room)) - if not user: - decision = ('Chambre non cotisante', VLAN_NOK) - elif user[0].has_access(): - decision = ('Machine OK, Proprio OK', VLAN_OK) - else: - decision = ('Chambre inconnue', VLAN_NOK) - else: - # Mode COMMON - decision = ('Machine OK', VLAN_OK) + decision = (sw_name, 'Mac not found', VLAN_NOK) + elif not interface[0].is_active(): + decision = (sw_name, 'Machine non active / adherent non cotisant', VLAN_NOK) else: - decision = ('Machine non active / adherent non cotisant', VLAN_NOK) + decision = (sw_name, 'Machine OK', VLAN_OK) + elif port.radius == 'STRICT': + if port.room: + user = User.objects.filter(room=Room.objects.filter(name=port.room)) + if not user: + decision = (sw_name, 'Chambre non cotisante', VLAN_NOK) + elif not user[0].has_access(): + decision = (sw_name, 'Resident desactive', VLAN_NOK) + else: + # Verification de la mac + interface = Interface.objects.filter(mac_address=mac_address) + if not interface: + decision = (sw_name, 'Chambre Ok, but mac not found', VLAN_NOK) + elif not interface[0].is_active(): + decision = (sw_name, 'Chambre Ok, but machine non active / adherent non cotisant', VLAN_NOK) + else: + decision = (sw_name, 'Machine OK, Proprio OK', VLAN_OK) + else: + decision = (sw_name, 'Chambre inconnue', VLAN_NOK) else: - decision = ('VLAN forced', int(port.radius)) + decision = (sw_name, 'VLAN forced', int(port.radius)) else: - decision = ('port not found!', VLAN_OK) + decision = (sw_name, 'port not found!', VLAN_OK) else: - decision = ('switch not found!', VLAN_OK) + decision = ('?', 'switch not found!', VLAN_OK) return decision if __name__ == '__main__': parser = argparse.ArgumentParser(description='Decide radius vlan attribution') - parser.add_argument('switch_name', action="store") + parser.add_argument('switch_ip', action="store") parser.add_argument('port_number', action="store", type=int) parser.add_argument('mac_address', action="store") args = parser.parse_args() - print(decide_vlan(args.switch_name, args.port_number, args.mac_address)) + print(decide_vlan(args.switch_ip, args.port_number, args.mac_address)) diff --git a/freeradius_utils/authenticate_user.py b/freeradius_utils/authenticate_user.py new file mode 100755 index 00000000..933ba9a3 --- /dev/null +++ b/freeradius_utils/authenticate_user.py @@ -0,0 +1,37 @@ +import os, sys + +proj_path = "/var/www/re2o/" +# This is so Django knows where to find stuff. +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings") +sys.path.append(proj_path) + +# This is so my local_settings.py gets loaded. +os.chdir(proj_path) + +# This is so models get loaded. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +import argparse + +from django.contrib.auth import authenticate +from users.models import User + + +def authorize_user(user, password): + user = authenticate(username=user, password=password) + if user: + if User.objects.get(pseudo=user): + return "TRUE" + else: + return "FALSE" + else: + return "FALSE" + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Authorize user') + parser.add_argument('user', action="store") + parser.add_argument('password', action="store") + args = parser.parse_args() + print(authorize_user(args.user, args.password)) +