A bit better fix. Not done yet
[freeradius.git] / raddb / policy.conf
1 # -*- text -*-
2 ##
3 ## policy.conf  -- FreeRADIUS server configuration file.
4 ##
5 ##      http://www.freeradius.org/
6 ##      $Id$
7 ##
8
9 #
10 #  Policies are virtual modules, similar to those defined in the
11 #  "instantate" section of radiusd.conf.
12 #
13 #  Defining a policy here means that it can be referenced in multiple
14 #  places as a *name*, rather than as a series of conditions to match,
15 #  and actions to take.
16 #
17 #  Policies are something like subroutines in a normal language, but
18 #  they cannot be called recursively. They MUST be defined in order.
19 #  If policy A calls policy B, then B MUST be defined before A.
20 #
21 policy {
22         # We check for this prefix to determine whether the class
23         # value was generated by this server.  It should be changed
24         # so that it is globally unique.
25         class_value_prefix = 'ai:'
26
27         #
28         #       Overload the default acct_unique module, it's not
29         #       smart enough.
30         #
31         acct_unique {
32                 #
33                 #  If we have a class attribute in the format
34                 #  'auth_id:[0-9a-f]{32}' it'll have a local value
35                 #  (defined by insert_acct_class), this ensures
36                 #  uniqueness and suitability.
37                 #
38                 #  We could just use the Class attribute as
39                 #  Acct-Unique-Session-Id, but this may cause problems
40                 #  with NAS that carry Class values across between
41                 #  multiple linked sessions.  So we rehash class with
42                 #  Acct-Session-ID to provide a truely unique session
43                 #  identifier.
44                 #
45                 #  Using a Class/Session-ID combination is more robust
46                 #  than using elements in the Accounting-Request,
47                 #  which may be subject to change, such as
48                 #  NAS-IP-Address, Client-IP-Address and
49                 #  NAS-Port-ID/NAS-Port.
50                 #
51                 #  This policy should ensure that session data is not
52                 #  affected if NAS IP addresses change, or the client
53                 #  roams to a different 'port' whilst maintaining its
54                 #  initial authentication session (Common in a
55                 #  wireless environment).
56                 #        
57                 if("%{string:Class}" =~ /${policy.class_value_prefix}([0-9a-f]{32})/i) {
58                         update request {
59                                 Acct-Unique-Session-Id := "%{md5:%{1}%{Acct-Session-ID}}"
60                         }
61                 }        
62
63                 #
64                 #  Not All devices respect RFC 2865 when dealing with
65                 #  the class attribute, so be prepared to use the
66                 #  older style of hashing scheme if a class attribute
67                 #  is not included
68                 #
69                 else {
70                         update request {
71                                 Acct-Unique-Session-Id := "%{md5:%{User-Name}%{Acct-Session-ID}%{NAS-IP-Address}%{NAS-Port-ID:}%{NAS-Port}}" 
72                          }       
73                 }        
74         }        
75
76         #
77         #       Insert a (hopefully unique) value into class
78         #
79         insert_acct_class {
80                 update reply { 
81                         Class = "${policy.class_value_prefix}%{md5:%t%I%{Packet-Src-Port}%{Packet-Src-IP-Address}%{NAS-IP-Address}%{Calling-Station-ID}%{User-Name}}"
82                 }
83         }
84
85         #
86         #       Forbid all EAP types.  Enable this by putting "forbid_eap"
87         #       into the "authorize" section.
88         #
89         forbid_eap {
90                 if (EAP-Message) {
91                         reject
92                 }
93         }
94         
95         #
96         #       Forbid all non-EAP types outside of an EAP tunnel.
97         #
98         permit_only_eap {
99                 if (!EAP-Message) {
100                         #  We MAY be inside of a TTLS tunnel.
101                         #  PEAP and EAP-FAST require EAP inside of
102                         #  the tunnel, so this check is OK.
103                         #  If so, then there MUST be an outer EAP message.
104                         if (!"%{outer.request:EAP-Message}") {
105                                 reject
106                         }
107                 }
108         }
109
110         #
111         #       Remove Reply-Message from response if were doing EAP
112         #
113         #  Be RFC 3579 2.6.5 compliant - EAP-Message and Reply-Message should
114         #  not be present in the same response.
115         #
116         remove_reply_message_if_eap {
117                 if(reply:EAP-Message && reply:Reply-Message) {
118                         update reply {
119                                 Reply-Message !* ANY
120                         }
121                 }
122                 else {
123                         noop
124                 }
125         }
126
127         #
128         #       Split User-Name in NAI format (RFC 4282) into components
129         #
130         #  This policy writes the Username and Domain portions of the
131         #  NAI into the Stripped-User-Name and Stripped-User-Domain
132         #  attributes.
133         #
134         #  The regular expression to do this is not strictly compliant
135         #  with the standard, but it is not possible to write a
136         #  compliant regexp without perl style regular expressions (or
137         #  at least not a legible one).
138         #
139         nai_regexp = "^([^@]*)(@([-[:alnum:]]+\\.[-[:alnum:].]+))?$"    
140
141         split_username_nai {
142                 if(User-Name =~ /${policy.nai_regexp}/){
143                         update request {
144                                 Stripped-User-Name := "%{1}"
145                                 Stripped-User-Domain = "%{3}"
146                         }
147
148                         # If any of the expansions result in a null
149                         # string, the update section may return
150                         # something other than updated...
151                         updated
152                 }
153                 else {
154                         noop
155                 }
156         }
157
158         #       
159         #  If called in post-proxy we modify the proxy-reply message
160         #
161         split_username_nai.post-proxy { 
162                 if(proxy-reply:User-Name =~ /${policy.nai_regexp}/){
163                         update proxy-reply {
164                                 Stripped-User-Name := "%{1}"
165                                 Stripped-User-Domain = "%{3}"
166                         }
167                         updated
168                 }
169                 else {
170                         noop
171                 }
172         }
173
174         #
175         #       Example of forbidding all attempts to login via
176         #       realms.
177         #
178         deny_realms {
179                 if (User-Name =~ /@|\\/) {
180                         reject
181                 }
182         }
183
184         #
185         #  If you want the server to pretend that it is dead,
186         #  then use the "do_not_respond" policy.
187         #
188         do_not_respond {
189                 update control {
190                         Response-Packet-Type := Do-Not-Respond
191                 }
192
193                 handled
194         }
195
196         #
197         #       Filter the username
198         #
199         #  Force some sanity on User-Name. This helps to avoid issues
200         #  issues where the back-end database is "forgiving" about
201         #  what constitutes a user name.
202         #
203         filter_username {
204                 # spaces at the start: reject
205                 if (User-Name =~ /^ /) {
206                         reject
207                 }
208
209                 # spaces at the end: reject
210                 if (User-Name =~ / $$/) {
211                         reject
212                 }
213
214                 # Mixed case: reject
215                 if (User-Name != "%{tolower:%{User-Name}}") {
216                         reject
217                 }
218         }
219
220
221         #       
222         #  The following policies are for the Chargeable-User-Identity
223         #  (CUI) configuration.
224         #
225         #  The policies below can be called as just 'cui' (not
226         #  cui.authorize etc..)  from the various config sections.
227         #
228
229         #
230         #  The client indicates it can do CUI by sending a CUI attribute        
231         #  containing one zero byte
232         #
233         cui.authorize {
234                 update request {
235                         Chargeable-User-Identity:='\\000'
236                 }
237         }
238
239         #
240         #  Add a CUI attribute based on the User-Name, and a secret key
241         #  known only to this server.
242         #
243         cui.post-auth {
244                 if (FreeRadius-Proxied-To == 127.0.0.1) {
245                         if (outer.request:Chargeable-User-Identity) {
246                                 update outer.reply {
247                                         Chargeable-User-Identity:="%{md5:%{config:cui_hash_key}%{User-Name}}"
248                                 }
249                         }
250                 }
251                 else {
252                         if (Chargeable-User-Identity) {
253                                 update reply {
254                                         Chargeable-User-Identity="%{md5:%{config:cui_hash_key}%{User-Name}}"
255                                 }
256                         }
257                 }
258         }
259
260
261         #
262         #  If we had stored a CUI for the User, add it to the request.
263         #
264         cui.accounting {
265                 #
266                 #  If the CUI isn't in the packet, see if we can find it
267                 #  in the DB.
268                 #
269                 if (!Chargeable-User-Identity) {
270                         update control {
271                                 Chargable-User-Identity := "%{cui: SELECT cui FROM cui WHERE clientipaddress = '%{Client-IP-Address}' AND callingstationid = '%{Calling-Station-Id}' AND username = '%{User-Name}'}"
272                         }
273                 }
274
275                 #
276                 #  If it exists now, then write out when we last saw
277                 #  this CUI.
278                 #
279                 if (Chargeable-User-Identity && (Chargeable-User-Identity != "")) {
280                         cui
281                 }
282         }
283
284         #
285         #  If there is a CUI attribute in the reply, add it to the DB.
286         #
287         cui_updatedb {
288                 if (reply:Chargeable-User-Identity) {
289                         cui
290                 }
291         }
292
293         #
294         #  Normalize the MAC Addresses in the Calling/Called-Station-Id
295         #
296         mac-addr-regexp = ([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})
297         
298         #
299         #  Add "rewrite_called_station_id" in the "authorize" and
300         #  "preacct" sections.
301         #
302         rewrite_called_station_id {
303                 if(Called-Station-Id =~ /^${policy.mac-addr-regexp}(:(.+))?$/i) {
304                         update request {
305                                 Called-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
306                         }
307
308                         # SSID component?
309                         if ("%{8}") {
310                                 update request {
311                                         Called-Station-SSID := "%{8}"
312                                 }
313                         }
314                         updated
315                 }
316                 else {
317                         noop
318                 }
319         }
320
321         #
322         #  Add "rewrite_calling_station_id" in the "authorize" and
323         #  "preacct" sections.
324         #
325         rewrite_calling_station_id {
326                 if(Calling-Station-Id =~ /^${policy.mac-addr-regexp}$/i) {
327                         update request {
328                                 Calling-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
329                         }
330                         updated
331                 }
332                 else {
333                         noop
334                 }
335         }
336
337         #  Assign compatibility data to request for sqlippool
338         dhcp_sqlippool.post-auth {
339
340
341                 #  Do some minor hacks to the request so that it looks
342                 #  like a RADIUS request to the SQL IP Pool module.
343                 update request {
344                         User-Name = "DHCP-%{DHCP-Client-Hardware-Address}"
345                         Calling-Station-Id = "%{DHCP-Client-Hardware-Address}"
346                         NAS-IP-Address = "%{%{DHCP-Gateway-IP-Address}:-127.0.0.1}"
347                         Acct-Status-Type = Start
348                 }
349
350                 #  Call the actual module
351                 dhcp_sqlippool
352
353                 #  Convert Framed-IP-Address to DHCP, but only if we
354                 #  actually allocated an address.
355                 if (ok) {
356                         update reply {
357                                 DHCP-Your-IP-Address = "%{reply:Framed-IP-Address}"
358                         }
359                 }
360         }
361 }