#! /usr/bin/env python # -*- coding: utf-8 -*- import os, hashlib, sys, binascii from syslog import syslog, openlog sys.path.append('/usr/scripts/gestion') from ldap_crans import crans_ldap, AssociationCrans, Club from config import ann_scol, dat, vlans, periode_transitoire import annuaires_pg from iptools import AddrInNet def chap_ok(password, challenge, clear_pass) : """ Test l'authentification chap fournie password et chalenge doivent être données en hexa (avec ou sans le 0x devant) retourne True si l'authentification est OK retourne False sinon """ try : challenge = binascii.a2b_hex(challenge.replace('0x','')) password = binascii.a2b_hex(password.replace('0x','')) if hashlib.md5(password[0] + clear_pass + challenge).digest() == password[1:] : return True except : return False def paiement_ok(adh): """Paiment ok ?""" global ann_scol paid = max(adh.paiement() + [0]) if periode_transitoire: # Si periode transitoire, on accepte les personnes n'ayant pas # réadhéré ann_scol -= 1 return ann_scol <= paid def do_auth(mac, prise): """Effectue l'authentification. Renvoie (success, msg, vlan). success est 0 si l'authentification est réussie, msg est pour les logs et vlan est le vlan non taggé à utiliser pour la prise.""" global ann_scol # Test chap (comme cela on est sur que c'est bien un switch qui demande) if not chap_ok(os.getenv('CHAP_PASSWORD'), os.getenv('CHAP_CHALLENGE'), mac): return (-1, "Échec test CHAP", "") # Mac dans la base LDAP conn = crans_ldap(readonly=True) m = conn.search('mac=%s' % mac)['machine'] if len(m) == 0: return (0, "Mac inconnue", "accueil") elif len(m) > 1: return (-1, "Pb recherche mac (nb résultat %d!=1)" % len(m), "") # N'appartient pas au Crans et n'a pas de prise attribuée # donc sur uplink ou switch non filtré # But : éviter le spoof d'une mac d'une machine clef proprio = m[0].proprietaire() if proprio.__class__ == AssociationCrans: return (-1, "Machine du crans", "") # blockliste bloq if 'bloq' in m[0].blacklist_actif(): return (-1, "Bloquage total des services pour cette machine", "") # les gens qui doivent être isolés if ('virus' in m[0].blacklist_actif() or 'ipv6_ra' in m[0].blacklist_actif() or 'autodisc_virus' in m[0].blacklist_actif()): return (0, "Bad boy", "isolement") # Paiement proprio ? if not paiement_ok(proprio): return (0, "N'a pas payé", "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") # Cas des personnels logés dans les appartements de l'ENS if (proprio.etudes(0) == 'Personnel ENS' or ('Nounou' in proprio.droits() and AddrInNet(m[0].ip(),'10.2.9.0/24'))): return (0, "Personnel ENS", "appts") # C'est bon return (0, "Accès adhérent OK", "adherent") if __name__ == '__main__' : mac = os.getenv('USER_NAME', '').replace('"', '') switch = os.getenv("NAS_IDENTIFIER", "").replace('"', '').split('.')[0] prise = (len(switch) == 6 and (switch[3] + switch[5]) or (switch + "-")) prise += "%02d" % int(os.getenv("NAS_PORT", 0)) # On vérifie si la mac est autorisée (r, msg, vlan) = do_auth(mac, prise) # On logue la prise sur laquelle a lieu la tentative openlog("radius_auth.py") syslog("%s -> %s [%s]" % (prise, mac, msg)) if vlan: # Cela indique au switch comment configurer le vlan par défaut # pour cette prise print ", ".join(["Tunnel-Type = VLAN", "Tunnel-Medium-Type = IEEE-802", "Tunnel-Private-Group-Id = \"%d\"" % vlans[vlan]]) # On tente de logguer la dernière mac sur une prise donnée try: f = open('/usr/scripts/var/last_macs/' + prise,'w') f.write(mac) f.close() except: pass sys.exit(r)