freeradius/auth.py: préparation à l'auth filaire

This commit is contained in:
Daniel STAN 2014-07-22 19:49:06 +02:00
parent 5446bb8142
commit 77e0b1daad
4 changed files with 159 additions and 68 deletions

View file

@ -1,20 +1,21 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# ⁻*- coding: utf-8 -*- # ⁻*- coding: utf-8 -*-
# #
# Draft de fichier d'authentification
#
# Ce fichier contient la définition de plusieurs fonctions d'interface à freeradius # Ce fichier contient la définition de plusieurs fonctions d'interface à freeradius
# qui peuvent être appelées (suivant les configurations) à certains moment de # qui peuvent être appelées (suivant les configurations) à certains moment de
# l'éxécution. # l'éxécution.
# #
import logging
import traceback
import netaddr
import radiusd # Module magique freeradius (radiusd.py is dummy)
import lc_ldap.shortcuts import lc_ldap.shortcuts
from lc_ldap.crans_utils import escape as escape_ldap from lc_ldap.crans_utils import escape as escape_ldap
import lc_ldap.crans_utils import lc_ldap.crans_utils
from gestion.config.config import vlans
import lc_ldap.objets import lc_ldap.objets
import radiusd from gestion.config.config import vlans
import netaddr
import traceback
from gestion.gen_confs.trigger import trigger_generate_cochon as trigger_generate from gestion.gen_confs.trigger import trigger_generate_cochon as trigger_generate
import annuaires_pg import annuaires_pg
@ -27,13 +28,34 @@ test_v6 = [
USERNAME_SUFFIX = '.wifi.crans.org' USERNAME_SUFFIX = '.wifi.crans.org'
## -*- Logging -*-
# Initialisation d'un logger pour faire des stats etc
# pour l'instant, on centralise tout sur thot en mode debug
logger = logging.getLogger('auth.py')
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)s: [%(levelname)s] %(message)s')
handler = logging.handlers.SysLogHandler(address = '/dev/log')
try:
handler.addFormatter(formatter)
except AttributeError:
handler.formatter = formatter
logger.addHandler(handler)
## -*- Types de blacklists -*-
#: reject tout de suite
bl_reject = [u'bloq'] bl_reject = [u'bloq']
#: place sur le vlan isolement
bl_isolement = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra'] bl_isolement = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra']
# TODO carte_etudiant: dépend si sursis ou non (regarder lc_ldap) # TODO carte_etudiant: dépend si sursis ou non (regarder lc_ldap)
# TODO LOGSSSSS # TODO LOGSSSSS
#: place sur accueil
bl_accueil = [u'carte_etudiant', u'chambre_invalide', u'paiement'] bl_accueil = [u'carte_etudiant', u'chambre_invalide', u'paiement']
# Decorateur utilisé plus tard (same connection) ## -*- Decorateurs -*-
# À appliquer sur les fonctions qui ont besoin d'une conn ldap
use_ldap_admin = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, use_ldap_admin = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
constructor=lc_ldap.shortcuts.lc_ldap_admin) constructor=lc_ldap.shortcuts.lc_ldap_admin)
use_ldap = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, use_ldap = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
@ -145,13 +167,6 @@ def get_prise_chbre(data):
chbre = None chbre = None
return prise, chbre return prise, chbre
def get_ap(data):
"""Extrait la prise (wifi)"""
## WiFi: NAS-Identifier => vide
## Nas-Port => numéro sur l'interface
## Nas-IP-Address => adresse IP de la borne
pass
@use_ldap_admin @use_ldap_admin
def register_mac(data, machine, conn): def register_mac(data, machine, conn):
"""Enregistre la mac actuelle sur une machine donnée.""" """Enregistre la mac actuelle sur une machine donnée."""
@ -186,8 +201,7 @@ def instantiate(p, *conns):
pass pass
@radius_event @radius_event
@use_ldap def authorize_wifi(data):
def wifi_authorize(data, conn):
"""Section authorize pour le wifi """Section authorize pour le wifi
(NB: le filaire est en accept pour tout le monde) (NB: le filaire est en accept pour tout le monde)
Éxécuté avant l'authentification proprement dite. On peut ainsi remplir les Éxécuté avant l'authentification proprement dite. On peut ainsi remplir les
@ -233,38 +247,93 @@ def wifi_authorize(data, conn):
) )
@radius_event @radius_event
@use_ldap def authorize_fil(data):
def post_auth(data, conn): """For now, do nothing.
TODO: check bl_reject.
TODO: check chap auth
"""
return radiusd.RLM_MODULE_OK
@radius_event
def post_auth_wifi(data):
"""Appelé une fois que l'authentification est ok. """Appelé une fois que l'authentification est ok.
On peut rajouter quelques éléments dans la réponse radius ici. On peut rajouter quelques éléments dans la réponse radius ici.
Comme par exemple le vlan sur lequel placer le client""" Comme par exemple le vlan sur lequel placer le client"""
is_wifi = False port, vlan_name, reason = decide_vlan(data, True)
vlan_name = None mac = data.get('Calling-Station-Id', None)
reason = ''
identity = "" #TODO
prise = ""
chbre = None
items = get_machines(data)
decision = 'adherent',''
log_message = '(wifi) %s -> %s [%s%s]' % \
(port, mac, vlan_name, (reason and u': ' + reason).encode(u'utf-8'))
logger.info(log_message)
radiusd.radlog(radiusd.L_AUTH, log_message)
# Si NAS ayant des mapping particuliers, à signaler ici
vlan_id = vlans[vlan_name]
# WiFi : Pour l'instant, on ne met pas d'infos de vlans dans la réponse
# les bornes wifi ont du mal avec cela
return radiusd.RLM_MODULE_OK
@radius_event
def post_auth_fil(data):
"""Idem, mais en filaire.
Pas testé."""
port, vlan_name, reason = decide_vlan(data, True)
mac = data.get('Calling-Station-Id', None)
log_message = '(fil) %s -> %s [%s%s]' % \
(port, mac, vlan_name, (reason and u': ' + reason).encode(u'utf-8'))
logger.info(log_message)
radiusd.radlog(radiusd.L_AUTH, log_message)
# Si NAS ayant des mapping particuliers, à signaler ici
vlan_id = vlans[vlan_name]
# Filaire
return (radiusd.RLM_MODULE_UPDATED,
(
("Tunnel-Type", "VLAN"),
("Tunnel-Medium-Type", "IEEE-802"),
("Tunnel-Private-Group-Id", '%d' % vlan),
),
()
)
@use_ldap
def decide_vlan(data, is_wifi, conn):
"""Décide du vlan non-taggué à assigner, et donne une raison
à ce choix.
Retourne un (port, vlan_name, reason)
port = est une prise réseau / chambre (si filaire)
"wifi" si wifi
"""
if is_wifi:
decision = 'wifi',''
port = data.get('Called-Station-Id', '?')
else:
decision = 'adherent',''
prise, chbre = get_prise_chbre(data)
port = "%s/%s" % (prise, chbre)
items = get_machines(data)
if not items: if not items:
decision = 'accueil', 'Machine inconnue' return (port, 'accueil', 'Machine inconnue')
return radiusd.RLM_MODULE_NOTFOUND # TODO faire un truc plus propre
machine = items[0] machine = items[0]
proprio = machine.proprio() proprio = machine.proprio()
if isinstance(machine, lc_ldap.objets.machineWifi): if not machine['ipHostNumber']:
decision = 'wifi', ''
is_wifi = True
if not machine['ipHostNumber'] or unicode(machine['macAddress'][0]) in test_v6:
decision = 'v6only', 'No IPv4' decision = 'v6only', 'No IPv4'
elif unicode(machine['macAddress'][0]) in test_v6:
decision = 'v6only', 'Test machine v6'
elif machine['ipHostNumber'][0].value in netaddr.IPNetwork('10.2.9.0/24'): elif machine['ipHostNumber'][0].value in netaddr.IPNetwork('10.2.9.0/24'):
# Cas des personnels logés dans les appartements de l'ENS # Cas des personnels logés dans les appartements de l'ENS
decision = 'appts', 'Personnel ENS' decision = 'appts', 'Personnel ENS'
# Application des blacklists
for bl in machine.blacklist_actif(): for bl in machine.blacklist_actif():
if bl.value['type'] in bl_isolement: if bl.value['type'] in bl_isolement:
decision = 'isolement', unicode(bl) decision = 'isolement', unicode(bl)
@ -275,43 +344,28 @@ def post_auth(data, conn):
if not is_wifi: if not is_wifi:
# Si l'adhérent n'est pas membre actif, il doit se brancher depuis la # Si l'adhérent n'est pas membre actif, il doit se brancher depuis la
# prise d'un autre adhérent à jour de cotisation # prise d'un autre adhérent à jour de cotisation
prise, chbre = get_prise_chbre(data) force_ma = False
if proprio['droits']: if chbre is None and not proprio['droits']:
decision = decision[0], decision[1] + ' (force MA)'
elif chbre is None:
decision = "accueil", "Chambre inconnue" decision = "accueil", "Chambre inconnue"
else: elif chbre is not None:
chbre = escape_ldap(chbre) chbre = escape_ldap(chbre)
hebergeurs = conn.search(u'(&(chambre=%s)(cid=*)(aid=*))' % chbre) hebergeurs = conn.search(u'(&(chambre=%s)(cid=*)(aid=*))' % chbre)
for hebergeur in hebergeurs: for hebergeur in hebergeurs:
if not hebergeur.blacklist_actif(): if not hebergeur.blacklist_actif():
break break
else: else:
# Si tous les hebergeurs sont blacklistés, autorisé
# uniquement si MA
if not proprio['droits']:
decision = "accueil", "Hébergeur blacklisté" decision = "accueil", "Hébergeur blacklisté"
else:
force_ma = True
else:
force_ma = True
if force_ma:
decision = decision[0], decision[1] + ' (force MA)'
# Unpack and log return (port,) + decision
if chbre is not None:
prise += '/' + chbre
vlan_name, reason = decision
vlan = vlans[vlan_name]
radiusd.radlog(radiusd.L_INFO, 'auth.py: %s -> %s [%s%s]' %
(prise, identity, vlan_name, (reason and u': ' + reason).encode(u'utf-8'))
)
# WiFi : Pour l'instant, on ne met pas d'infos de vlans dans la réponse
# les bornes wifi ont du mal avec cela
if is_wifi:
return radiusd.RLM_MODULE_OK
return (radiusd.RLM_MODULE_UPDATED,
(
("Tunnel-Type", "VLAN"),
("Tunnel-Medium-Type", "IEEE-802"),
("Tunnel-Private-Group-Id", '%d' % vlan),
),
()
)
@radius_event @radius_event
def dummy_fun(p): def dummy_fun(p):

View file

@ -0,0 +1,37 @@
# Configuration for the Python module.
#
#
python crans_fil {
mod_instantiate = freeradius.auth
func_instantiate = instantiate
# Spécifique au WiFi : rempli le mdp
mod_authorize = freeradius.auth
func_authorize = authorize_fil
# Renseigne le vlan
# remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = freeradius.auth
func_post_auth = post_auth_fil
# Que faire avant de quitter
mod_detach = freeradius.auth
func_detach = detach
# Le reste est dumb et inutile
mod_accounting = freeradius.auth
func_accounting = dummy_fun
mod_pre_proxy = freeradius.auth
func_pre_proxy = dummy_fun
mod_post_proxy = freeradius.auth
func_post_proxy = dummy_fun
mod_recv_coa = freeradius.auth
func_recv_coa = dummy_fun
mod_send_coa = freeradius.auth
func_send_coa = dummy_fun
}

View file

@ -8,12 +8,12 @@ python crans_wifi {
# Spécifique au WiFi : rempli le mdp # Spécifique au WiFi : rempli le mdp
mod_authorize = freeradius.auth mod_authorize = freeradius.auth
func_authorize = wifi_authorize func_authorize = authorize_wifi
# Renseigne le vlan # Renseigne le vlan
# remplacer par dummy_fun pour ignorer le tagging de vlan # remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = freeradius.auth mod_post_auth = freeradius.auth
func_post_auth = post_auth func_post_auth = post_auth_wifi
# Que faire avant de quitter # Que faire avant de quitter
mod_detach = freeradius.auth mod_detach = freeradius.auth

View file

@ -17,14 +17,14 @@ delattr(sys, 'argv')
auth.instantiate(()) auth.instantiate(())
p=( p=(
('Calling-Station-Id', 'ba:27:eb:3c:54:d5'), ('Calling-Station-Id', 'b0:79:94:cf:d1:9a'),
('User-Name', 'test18'), ('User-Name', 'moo-torola'),
) )
print repr(auth.wifi_authorize(p)) print repr(auth.authorize_wifi(p))
print "wait for 3s, tu peux aller crasher le serveur pg ou ldap" print "wait for 3s, tu peux aller crasher le serveur pg ou ldap"
sys.stdout.flush() sys.stdout.flush()
time.sleep(3) time.sleep(3)
print repr(auth.post_auth(p)) print repr(auth.post_auth_wifi(p))