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