
Quand une machine se connecte pour la première fois, et qu'elle a une mac auto on enregistre la vraie, et on trigger generate sur komaz.
268 lines
8.7 KiB
Python
268 lines
8.7 KiB
Python
#!/bin/bash /usr/scripts/python.sh
|
|
# ⁻*- coding: utf-8 -*-
|
|
#
|
|
# Draft de fichier d'authentification
|
|
#
|
|
# Ce fichier contient la définition de plusieurs fonctions d'interface à freeradius
|
|
# qui peuvent être appelées (suivant les configurations) à certains moment de
|
|
# l'éxécution.
|
|
#
|
|
# Une telle fonction prend un uniquement argument, qui est une liste de tuples
|
|
# (clé, valeur)
|
|
# et renvoie un triplet dont les composantes sont :
|
|
# * le code de retour (voir radiusd.RLM_MODULE_* )
|
|
# * un tuple de couples (clé, valeur) pour les valeurs de réponse
|
|
# (access ok et autres trucs du genre)
|
|
# * un tuple de couples (clé, valeur) pour les valeurs internes à mettre à jour
|
|
# (mot de passe par exemple)
|
|
#
|
|
# Voir des exemples plus complets ici:
|
|
# https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/
|
|
|
|
from lc_ldap.shortcuts import with_ldap_conn, lc_ldap_admin
|
|
from lc_ldap.crans_utils import escape as escape_ldap
|
|
import lc_ldap.crans_utils
|
|
from gestion.config.config import vlans
|
|
import lc_ldap.objets
|
|
import radiusd
|
|
import netaddr
|
|
from gestion.gen_confs.generate import trigger as trigger_generate
|
|
|
|
# Voilà, pour faire marcher le V6Only, il faut se retirer l'ipv4 de sa machine
|
|
# ou en enregistrer une nouvelle (sans ipv4) avec une autre mac. Moi j'ai la
|
|
# flemme donc je hardcode les MAC qui doivent toujours être placées sur le vlan v6only
|
|
test_v6 = [
|
|
u'dc:9f:db:5c:c3:ea', # polynice-wlan0
|
|
#u'00:15:6d:ee:e8:f8', # atree-wlan0
|
|
u'00:26:c7:a6:9e:16', # cerveaulent
|
|
]
|
|
|
|
bl_reject = [u'bloq']
|
|
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 LOGSSSSS
|
|
bl_accueil = [u'carte_etudiant', u'chambre_invalide', u'paiement']
|
|
|
|
# Decorateur utilisé plus tard (same connection)
|
|
use_ldap = with_ldap_conn(retries=2, delay=5, constructor=lc_ldap_admin)
|
|
|
|
@use_ldap
|
|
def get_machines(auth_data, conn):
|
|
mac = None
|
|
username = None
|
|
|
|
# Beware: les valeurs scalaires sont entre guillemets
|
|
# Calling-Station-Id: "une_adresse_mac"
|
|
for (key, value) in auth_data:
|
|
if key == 'Calling-Station-Id':
|
|
try:
|
|
value = value.decode('ascii', 'ignore').replace('"','')
|
|
mac = lc_ldap.crans_utils.format_mac(value)
|
|
except:
|
|
radiusd.radlog(radiusd.L_ERR, 'Cannot format MAC !')
|
|
if key == 'User-Name':
|
|
username = escape_ldap(value.decode('ascii', 'ignore').replace('"',''))
|
|
|
|
# TODO
|
|
# format username (strip .wifi.crans.org)
|
|
|
|
base = u'(objectclass=machine)'
|
|
|
|
if mac is None:
|
|
radiusd.radlog(radiusd.L_ERR, 'Cannot read client MAC from AP !')
|
|
return []
|
|
|
|
mac = escape_ldap(mac)
|
|
username = escape_ldap(username)
|
|
|
|
search_strats = [
|
|
# Case 1: Search by mac (reported by AP)
|
|
u'(&%s(macAddress=%s))' % (base, mac),
|
|
# Case 2: unregistered mac
|
|
u'(&%s(macAddress=<automatique>)(host=%s.wifi.crans.org))' %
|
|
(base, username),
|
|
]
|
|
|
|
for filter_s in search_strats:
|
|
res = conn.search(filter_s, mode='rw')
|
|
if res:
|
|
break
|
|
|
|
return res
|
|
|
|
def get_prise(auth_data):
|
|
"""Extrait la prise"""
|
|
## Regarder dans
|
|
## Filaire: NAS-Identifier => contient le nom du switch (batm-3.adm.crans.org)
|
|
## Nas-Port => port du switch (ex 42)
|
|
## WiFi: NAS-Identifier => vide
|
|
## Nas-Port => numéro sur l'interface
|
|
## Nas-IP-Address => adresse IP de la borne
|
|
is_wifi = True
|
|
bat_name = None
|
|
bat_num = None
|
|
port = None
|
|
|
|
for (key, value) in auth_data:
|
|
if key == 'NAS-Identifier':
|
|
if value.startswith('bat'):
|
|
nas = value.split('.', 1)[0]
|
|
try:
|
|
bat_name = nas[3]
|
|
bat_num = nas.split('-', 1)[1]
|
|
except IndexError:
|
|
pass
|
|
|
|
if key == 'Nas-Port':
|
|
port = int(value)
|
|
|
|
if bat_num and bat_name and port:
|
|
return bat_name + "%01d%02d" % (bat_num, port)
|
|
|
|
|
|
@use_ldap
|
|
def register_mac(auth_data, machine, conn):
|
|
for (key, value) in auth_data:
|
|
if key == 'Calling-Station-Id':
|
|
try:
|
|
value = value.decode('ascii', 'ignore').replace('"','')
|
|
mac = lc_ldap.crans_utils.format_mac(value)
|
|
except:
|
|
radiusd.radlog(radiusd.L_ERR, 'Cannot format MAC !')
|
|
if mac is not None:
|
|
mac = unicode(mac.lower())
|
|
machine['macAddress'] = mac
|
|
machine.history_add(u'auth.py', u'macAddress ( %s )' % mac)
|
|
machine.save()
|
|
radiusd.radlog(radiusd.L_INFO, 'Mac set')
|
|
trigger_generate('komaz', background=True)
|
|
else:
|
|
radiusd.radlog(radiusd.L_ERR, 'Cannot find MAC')
|
|
|
|
@use_ldap
|
|
def instantiate(p, conn):
|
|
"""Utile pour initialiser la connexion ldap une première fois (otherwise,
|
|
do nothing)"""
|
|
pass
|
|
|
|
@use_ldap
|
|
def wifi_authorize(auth_data, conn):
|
|
"""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)"""
|
|
|
|
items = get_machines(auth_data)
|
|
|
|
if not items:
|
|
radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Nobody found')
|
|
return radiusd.RLM_MODULE_NOTFOUND
|
|
|
|
if len(items) > 1:
|
|
radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Too many results (took first)')
|
|
|
|
machine = items[0]
|
|
|
|
if '<automatique>' in machine['macAddress']:
|
|
register_mac(auth_data, machine)
|
|
|
|
|
|
proprio = machine.proprio()
|
|
if isinstance(proprio, lc_ldap.objets.AssociationCrans):
|
|
radiusd.radlog(radiusd.L_ERR, '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
|
|
|
|
if not machine.get('ipsec', False):
|
|
radiusd.radlog(radiusd.L_ERR, 'WiFi authentication 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),
|
|
),
|
|
)
|
|
|
|
@use_ldap
|
|
def post_auth(auth_data, conn):
|
|
"""Appelé une fois que l'authentification est ok.
|
|
On peut rajouter quelques éléments dans la réponse radius ici.
|
|
Comme par exemple le vlan sur lequel placer le client"""
|
|
|
|
vlan_name = None
|
|
reason = ''
|
|
identity = "" #TODO
|
|
prise = "" #TODO
|
|
items = get_machines(auth_data)
|
|
|
|
decision = 'adherent',''
|
|
if not items:
|
|
decision = 'accueil', 'Machine inconnue'
|
|
|
|
machine = items[0]
|
|
proprio = machine.proprio()
|
|
|
|
if isinstance(machine, lc_ldap.objets.machineWifi):
|
|
decision = 'wifi', ''
|
|
|
|
if not machine['ipHostNumber'] or unicode(machine['macAddress'][0]) in test_v6:
|
|
decision = 'v6only', 'No IPv4'
|
|
elif machine['ipHostNumber'][0].value in netaddr.IPNetwork('10.2.9.0/24'):
|
|
# Cas des personnels logés dans les appartements de l'ENS
|
|
decision = 'appts', 'Personnel ENS'
|
|
|
|
for bl in machine.blacklist_actif():
|
|
if bl.value['type'] in bl_isolement:
|
|
decision = 'isolement', unicode(bl)
|
|
if bl.value['type'] in bl_accueil:
|
|
decision = 'accueil', unicode(bl)
|
|
|
|
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'))
|
|
)
|
|
|
|
#<!>
|
|
#
|
|
# # Si l'adhérent n'est pas membre actif, il doit se brancher depuis la prise
|
|
# # d'un autre adhérent à jour de cotisation
|
|
# if not proprio.droits():
|
|
# try:
|
|
# chbre = prise[0] + annuaires_pg.reverse(prise[0], prise[1:])[0]
|
|
# except IndexError:
|
|
# return (0, "Chambre inconnue", "accueil")
|
|
# hebergeurs = conn.search('chambre=' + chbre)
|
|
# for hebergeur in hebergeurs['adherent'] + hebergeurs['club']:
|
|
# if paiement_ok(hebergeur):
|
|
# break
|
|
# else:
|
|
# return (0, "Hébergeur non à jour", "accueil")
|
|
#
|
|
#<!>
|
|
return radiusd.RLM_MODULE_OK
|
|
return (radiusd.RLM_MODULE_UPDATED,
|
|
(
|
|
("Tunnel-Type", "VLAN"),
|
|
("Tunnel-Medium-Type", "IEEE-802"),
|
|
("Tunnel-Private-Group-Id", '%d' % vlan),
|
|
),
|
|
()
|
|
)
|
|
|
|
def dummy_fun(p):
|
|
return radiusd.RLM_MODULE_OK
|
|
|
|
def detach(p=None):
|
|
"""Appelé lors du déchargement du module (enfin, normalement)"""
|
|
print "*** goodbye from example.py ***"
|
|
return radiusd.RLM_MODULE_OK
|