Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
R
re2o
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Nounous
re2o
Commits
8b011439
Commit
8b011439
authored
Dec 08, 2016
by
Gabriel Detraz
Committed by
root
Dec 08, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add auth.py et module
parent
89729f9a
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
263 additions
and
0 deletions
+263
-0
freeradius_utils/auth.py
freeradius_utils/auth.py
+262
-0
freeradius_utils/modules/rlm_python_re2o.conf
freeradius_utils/modules/rlm_python_re2o.conf
+1
-0
No files found.
freeradius_utils/auth.py
0 → 100644
View file @
8b011439
# ⁻*- mode: python; coding: utf-8 -*-
"""
Backend python pour freeradius.
Ce fichier contient la définition de plusieurs fonctions d'interface à
freeradius qui peuvent être appelées (suivant les configurations) à certains
moment de l'authentification, en WiFi, filaire, ou par les NAS eux-mêmes.
Inspirés d'autres exemples trouvés ici :
https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/
"""
import
logging
import
netaddr
import
radiusd
# Module magique freeradius (radiusd.py is dummy)
import
os
import
binascii
import
hashlib
import
subprocess
import
os
,
sys
from
ast
import
literal_eval
as
make_tuple
#: Serveur radius de test (pas la prod)
TEST_SERVER
=
bool
(
os
.
getenv
(
'DBG_FREERADIUS'
,
False
))
## -*- Logging -*-
class
RadiusdHandler
(
logging
.
Handler
):
"""Handler de logs pour freeradius"""
def
emit
(
self
,
record
):
"""Process un message de log, en convertissant les niveaux"""
if
record
.
levelno
>=
logging
.
WARN
:
rad_sig
=
radiusd
.
L_ERR
elif
record
.
levelno
>=
logging
.
INFO
:
rad_sig
=
radiusd
.
L_INFO
else
:
rad_sig
=
radiusd
.
L_DBG
radiusd
.
radlog
(
rad_sig
,
record
.
msg
)
# Initialisation d'un logger (pour logguer unifié)
logger
=
logging
.
getLogger
(
'auth.py'
)
logger
.
setLevel
(
logging
.
DEBUG
)
formatter
=
logging
.
Formatter
(
'%(name)s: [%(levelname)s] %(message)s'
)
handler
=
RadiusdHandler
()
handler
.
setFormatter
(
formatter
)
logger
.
addHandler
(
handler
)
def
radius_event
(
fun
):
"""Décorateur pour les fonctions d'interfaces avec radius.
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 (accès ok
et autres trucs du genre)
* un tuple de couples (clé, valeur) pour les valeurs internes à mettre à
jour (mot de passe par exemple)
On se contente avec ce décorateur (pour l'instant) de convertir la liste de
tuples en entrée en un dictionnaire."""
def
new_f
(
auth_data
):
if
type
(
auth_data
)
==
dict
:
data
=
auth_data
else
:
data
=
dict
()
for
(
key
,
value
)
in
auth_data
or
[]:
# Beware: les valeurs scalaires sont entre guillemets
# Ex: Calling-Station-Id: "une_adresse_mac"
data
[
key
]
=
value
.
replace
(
'"'
,
''
)
try
:
# TODO s'assurer ici que les tuples renvoyés sont bien des (str,str)
# rlm_python ne digère PAS les unicodes
return
fun
(
data
)
except
Exception
as
err
:
logger
.
error
(
'Failed %r on data %r'
%
(
err
,
auth_data
))
raise
return
new_f
@
radius_event
def
instantiate
(
*
_
):
"""Utile pour initialiser les connexions ldap une première fois (otherwise,
do nothing)"""
logger
.
info
(
'Instantiation'
)
if
TEST_SERVER
:
logger
.
info
(
'DBG_FREERADIUS is enabled'
)
@
radius_event
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'
:
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)"""
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
):
"""
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
,
(),
(
(
"Auth-Type"
,
"Accept"
),
),
)
@
radius_event
def
post_auth
(
data
):
# On cherche quel est le type de machine, et quel sites lui appliquer
if
data
.
get
(
'NAS-Port-Type'
,
''
)
==
u
'Ethernet'
:
return
post_auth_fil
(
data
)
elif
u
"Wireless"
in
data
.
get
(
'NAS-Port-Type'
,
''
):
return
post_auth_wifi
(
data
)
@
radius_event
def
post_auth_wifi
(
data
):
"""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"""
port
,
vlan_name
,
reason
=
decide_vlan
(
data
,
True
)
mac
=
data
.
get
(
'Calling-Station-Id'
,
None
)
log_message
=
'(wifi) %s -> %s [%s%s]'
%
\
(
port
,
mac
,
vlan_name
,
(
reason
and
u
': '
+
reason
).
encode
(
'utf-8'
))
logger
.
info
(
log_message
)
# Si NAS ayant des mapping particuliers, à signaler ici
vlan_id
=
config
.
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
if
WIFI_DYN_VLAN
:
return
(
radiusd
.
RLM_MODULE_UPDATED
,
(
(
"Tunnel-Type"
,
"VLAN"
),
(
"Tunnel-Medium-Type"
,
"IEEE-802"
),
(
"Tunnel-Private-Group-Id"
,
'%d'
%
vlan_id
),
),
()
)
return
radiusd
.
RLM_MODULE_OK
@
radius_event
def
post_auth_fil
(
data
):
"""Idem, mais en filaire.
"""
nas
=
data
.
get
(
'NAS-Identifier'
,
None
)
port
=
data
.
get
(
'NAS-Port'
,
None
)
mac
=
data
.
get
(
'Calling-Station-Id'
,
None
)
out
=
subprocess
.
check_output
([
'/usr/bin/python3'
,
'/var/www/re2o/freeradius_utils/authenticate_filaire.py'
,
nas
,
port
,
mac
])
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'
))
logger
.
info
(
log_message
)
# Filaire
return
(
radiusd
.
RLM_MODULE_UPDATED
,
(
(
"Tunnel-Type"
,
"VLAN"
),
(
"Tunnel-Medium-Type"
,
"IEEE-802"
),
(
"Tunnel-Private-Group-Id"
,
'%d'
%
int
(
vlan_id
)),
),
()
)
@
radius_event
def
dummy_fun
(
_
):
"""Do nothing, successfully. (C'est pour avoir un truc à mettre)"""
return
radiusd
.
RLM_MODULE_OK
def
detach
(
_
=
None
):
"""Appelé lors du déchargement du module (enfin, normalement)"""
print
"*** goodbye from auth.py ***"
return
radiusd
.
RLM_MODULE_OK
freeradius_utils/modules/rlm_python_re2o.conf
0 → 120000
View file @
8b011439
../
rlm_python_re2o
.
conf
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment