freeradius: draft d'auth wifi via python
This commit is contained in:
parent
0a141bf7a5
commit
24d754566e
3 changed files with 230 additions and 0 deletions
174
freeradius/auth.py
Executable file
174
freeradius/auth.py
Executable file
|
@ -0,0 +1,174 @@
|
||||||
|
#!/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
|
||||||
|
from lc_ldap.crans_utils import escape as escape_ldap
|
||||||
|
from gestion.config.config import vlans
|
||||||
|
import lc_ldap.objets
|
||||||
|
import radiusd
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
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':
|
||||||
|
mac = escape_ldap(value.decode('ascii', 'ignore').replace('"',''))
|
||||||
|
if key == 'User-Name':
|
||||||
|
username = escape_ldap(value.decode('ascii', 'ignore').replace('"',''))
|
||||||
|
# TODO
|
||||||
|
# format mac (done by preprocess & hints)
|
||||||
|
# format username (strip .wifi.crans.org)
|
||||||
|
|
||||||
|
base = u'(objectclass=machine)'
|
||||||
|
# Search by reported mac, then (if failure) search by username (mac or host)
|
||||||
|
return conn.search(u'(&%s(macAddress=%s))' % (base, mac)) or \
|
||||||
|
conn.search(u'(&%s(|(macAddress=%s)(host=%s.wifi.crans.org)))' %
|
||||||
|
(base, username, username))
|
||||||
|
|
||||||
|
# Decorateur utilisé plus tard (same connection)
|
||||||
|
use_ldap = with_ldap_conn(retries=2, delay=5)
|
||||||
|
|
||||||
|
@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, conn)
|
||||||
|
|
||||||
|
if not items:
|
||||||
|
radiusd.radlog(radiusd.L_ERR, 'Nobody found :(')
|
||||||
|
return radiusd.RLM_MODULE_NOTFOUND
|
||||||
|
if len(items) > 1:
|
||||||
|
radiusd.radlog(radiusd.L_ERR, 'Too much results from lc_ldap !')
|
||||||
|
|
||||||
|
machine = items[0]
|
||||||
|
|
||||||
|
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):
|
||||||
|
return radiusd.RLM_MODULE_REJECT
|
||||||
|
password = machine['ipsec'][0].value.encode('ascii', 'ignore')
|
||||||
|
|
||||||
|
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"""
|
||||||
|
|
||||||
|
items = get_machines(auth_data, conn)
|
||||||
|
|
||||||
|
if not items:
|
||||||
|
return radiusd.RLM_MODULE_NOTFOUND
|
||||||
|
machine = items[0]
|
||||||
|
proprio = machine.proprio()
|
||||||
|
|
||||||
|
vlan = vlans['adherent']
|
||||||
|
if isinstance(machine, lc_ldap.objets.machineWifi):
|
||||||
|
vlan = vlans['wifi']
|
||||||
|
|
||||||
|
if not machine['ipHostNumber']:
|
||||||
|
# No IP => vlan v6only
|
||||||
|
vlan = vlans['v6only']
|
||||||
|
elif machine['ipHostNumber'][0].value in netaddr.IPNetwork('10.2.9.0/24'):
|
||||||
|
# Cas des personnels logés dans les appartements de l'ENS
|
||||||
|
vlan = vlans['appts']
|
||||||
|
|
||||||
|
for bl in machine.blacklist_actif():
|
||||||
|
if bl in bl_isolement:
|
||||||
|
vlan = vlans['isolement']
|
||||||
|
if bl in bl_accueil:
|
||||||
|
vlan = vlans['accueil']
|
||||||
|
|
||||||
|
#<!>
|
||||||
|
#
|
||||||
|
# # 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_UPDATED,
|
||||||
|
(
|
||||||
|
("Tunnel-Type", "VLAN"),
|
||||||
|
("Tunnel-Medium-Type", "IEEE-802"),
|
||||||
|
("Tunnel-Private-Group-Id", '%d' % vlan),
|
||||||
|
),
|
||||||
|
()
|
||||||
|
)
|
||||||
|
|
||||||
|
def dummy_fun(p):
|
||||||
|
return radiusd.RLM_MODULE_OK
|
||||||
|
|
||||||
|
recv_coa = dummy_fun
|
||||||
|
send_coa = dummy_fun
|
||||||
|
preacct = dummy_fun
|
||||||
|
accounting = dummy_fun
|
||||||
|
pre_proxy = dummy_fun
|
||||||
|
post_proxy = dummy_fun
|
||||||
|
|
||||||
|
|
||||||
|
def detach():
|
||||||
|
"""Appelé lors du déchargement du module (enfin, normalement)"""
|
||||||
|
print "*** goodbye from example.py ***"
|
||||||
|
return radiusd.RLM_MODULE_OK
|
35
freeradius/rlm_python_wifi.conf
Normal file
35
freeradius/rlm_python_wifi.conf
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Configuration for the Python module.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
python crans_wifi {
|
||||||
|
mod_instantiate = freeradius.auth
|
||||||
|
func_instantiate = instantiate
|
||||||
|
|
||||||
|
# Spécifique au WiFi : rempli le mdp
|
||||||
|
mod_authorize = freeradius.auth
|
||||||
|
func_authorize = wifi_authorize
|
||||||
|
|
||||||
|
# Renseigne le vlan
|
||||||
|
mod_post_auth = freeradius.auth
|
||||||
|
func_post_auth = post_auth
|
||||||
|
|
||||||
|
# Le reste est dumb et inutile
|
||||||
|
mod_accounting = freeradius.auth
|
||||||
|
func_accounting = accounting
|
||||||
|
|
||||||
|
mod_pre_proxy = freeradius.auth
|
||||||
|
func_pre_proxy = pre_proxy
|
||||||
|
|
||||||
|
mod_post_proxy = freeradius.auth
|
||||||
|
func_post_proxy = post_proxy
|
||||||
|
|
||||||
|
mod_recv_coa = freeradius.auth
|
||||||
|
func_recv_coa = recv_coa
|
||||||
|
|
||||||
|
mod_send_coa = freeradius.auth
|
||||||
|
func_send_coa = send_coa
|
||||||
|
|
||||||
|
mod_detach = freeradius.auth
|
||||||
|
func_detach = detach
|
||||||
|
}
|
21
freeradius/test.py
Executable file
21
freeradius/test.py
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash /usr/scripts/python.sh
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import auth
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print "Give me a mac !"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Machine à s'authentifier (cerveaulent)
|
||||||
|
p=(('Calling-Station-Id', sys.argv[1]),)
|
||||||
|
|
||||||
|
print repr(auth.wifi_authorize(p))
|
||||||
|
print "wait for 3s, tu peux aller crasher le serveur pg ou ldap"
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
print repr(auth.post_auth(p))
|
Loading…
Add table
Add a link
Reference in a new issue