Deprecate attribute 'Password' in favor of 'User-Password'.
[freeradius.git] / src / main / proxy.c
1 /*
2  * proxy.c      Proxy stuff.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Chris Parker <cparker@starnetusa.com>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include "autoconf.h"
28 #include "libradius.h"
29
30 #include <sys/socket.h>
31
32 #if HAVE_NETINET_IN_H
33 #       include <netinet/in.h>
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <string.h>
40
41 #include "radiusd.h"
42 #include "rad_assert.h"
43
44
45 static uint32_t proxy_id = 1;
46
47 /*
48  *      We received a response from a remote radius server.
49  *      Find the original request, then return.
50  *      Returns:   1 replication don't reply
51  *                 0 proxy found
52  *                -1 error don't reply
53  */
54 int proxy_receive(REQUEST *request)
55 {
56         VALUE_PAIR *proxypair;
57         VALUE_PAIR *replicatepair;
58         VALUE_PAIR *realmpair;
59         int replicating;
60         REALM *realm;
61         char *realmname;
62
63         proxypair = pairfind(request->config_items, PW_PROXY_TO_REALM);
64         replicatepair = pairfind(request->config_items, PW_REPLICATE_TO_REALM);
65         if(proxypair) {
66                 realmpair = proxypair;
67                 replicating = 0;
68         } else if(replicatepair) {
69                 realmpair = replicatepair;
70                 replicating = 1;
71         } else {
72                 radlog(L_PROXY, "Proxy reply to packet with no Realm");
73                 return -1;
74         }
75
76         realmname = (char *) realmpair->strvalue;
77         realm = realm_find(realmname);
78
79         /*
80          *      Don't touch the reply VP's.  Assume that a module
81          *      takes care of that...
82          */
83
84         return replicating?1:0;
85 }
86
87 /*
88  *      Add a proxy-pair to the end of the request.
89  */
90 static void proxy_addinfo(REQUEST *request)
91 {
92         VALUE_PAIR *proxy_pair;
93
94         proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
95         if (proxy_pair == NULL) {
96                 radlog(L_ERR|L_CONS, "no memory");
97                 exit(1);
98         }
99         sprintf((char *)proxy_pair->strvalue, "%d", request->packet->id);
100         proxy_pair->length = strlen((char *)proxy_pair->strvalue);
101
102         pairadd(&request->proxy->vps, proxy_pair);
103 }
104
105 /*
106  *      Relay the request to a remote server.
107  *      Returns:  2 success (we replicate, caller replies normally)
108  *                1 success (we reply, caller returns without replying)
109  *                0 fail (caller falls through to normal processing)
110  *               -1 fail (we don't reply, caller returns without replying)
111  */
112 int proxy_send(REQUEST *request)
113 {
114         VALUE_PAIR *proxypair;
115         VALUE_PAIR *replicatepair;
116         VALUE_PAIR *realmpair;
117         VALUE_PAIR *namepair;
118         VALUE_PAIR *strippednamepair;
119         VALUE_PAIR *delaypair;
120         VALUE_PAIR *vp, *vps;
121         REALM *realm;
122         char *realmname;
123         int replicating;
124
125         /* 
126          *      The timestamp is used below to figure the
127          *      next_try. The request needs to "hang around" until
128          *      either the other server sends a reply or the retry
129          *      count has been exceeded.  Until then, it should not
130          *      be eligible for the time-based cleanup.  --Pac. */
131
132         /* Look for proxy/replicate signs */
133         /* FIXME - What to do if multiple Proxy-To/Replicate-To attrs are
134          * set...  Log an error? Actually replicate to multiple places? That
135          * would be cool. For now though, I'll just take the first one and
136          * ignore the rest. */
137         proxypair = pairfind(request->config_items, PW_PROXY_TO_REALM);
138         replicatepair = pairfind(request->config_items, PW_REPLICATE_TO_REALM);
139         if (proxypair) {
140                 realmpair = proxypair;
141                 replicating = 0;
142         } else if (replicatepair) {
143                 realmpair = replicatepair;
144                 replicating = 1;
145         } else {
146                 /*
147                  *      Neither proxy or replicate attributes are set,
148                  *      so we can exit from the proxy code.
149                  */
150                 return 0;
151         }
152
153         /*
154          *      Length == 0 means it exists, but there's no realm.
155          *      Don't proxy it.
156          */
157         if (realmpair->length == 0) {
158                 return 0;
159         }
160
161         realmname = (char *)realmpair->strvalue;
162
163         /*
164          *      Look for the realm, letting realm_find take care
165          *      of the "NULL" realm.
166          *
167          *      If there is no such realm, then exit.
168          *      Maybe we should log an error?
169          */
170         realm = realm_find(realmname);
171         if (realm == NULL) {
172                 return 0;
173         }
174
175         /*
176          *      Remember that we sent the request to a Realm.
177          */
178         pairadd(&request->packet->vps,
179                 pairmake("Realm", realm->realm, T_OP_EQ));
180         
181
182         /*
183          *      Access-Request: look for LOCAL realm.
184          *      Accounting-Request: look for LOCAL realm.
185          */
186         if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
187              (realm->ipaddr == htonl(INADDR_NONE))) ||
188             ((request->packet->code == PW_ACCOUNTING_REQUEST) &&            
189              (realm->acct_ipaddr == htonl(INADDR_NONE)))) {
190                 return 0;
191         }
192         
193         /*
194          *      Copy the request, then look up
195          *      name and plain-text password in the copy.
196          *
197          *      Note that the User-Name attribute is the *original*
198          *      as sent over by the client.  The Stripped-User-Name
199          *      attribute is the one hacked through the 'hints' file.
200          */
201         vps = paircopy(request->packet->vps);
202         namepair = pairfind(vps, PW_USER_NAME);
203         strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME);
204
205         /*
206          *      If there's a Stripped-User-Name attribute in the
207          *      request, then use THAT as the User-Name for the
208          *      proxied request, instead of the original name.
209          *
210          *      This is done by making a copy of the Stripped-User-Name
211          *      attribute, turning it into a User-Name attribute,
212          *      deleting the Stripped-User-Name and User-Name attributes
213          *      from the vps list, and making the new User-Name
214          *      the head of the vps list.
215          */
216         if (strippednamepair) {
217                 vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
218                 if (!vp) {
219                         radlog(L_ERR|L_CONS, "no memory");
220                         exit(1);
221                 }
222                 memcpy(vp->strvalue, strippednamepair->strvalue,
223                                 sizeof(vp->strvalue));
224                 vp->length = strippednamepair->length;
225                 pairdelete(&vps, PW_USER_NAME);
226                 pairdelete(&vps, PW_STRIPPED_USER_NAME);
227                 vp->next = vps;
228                 namepair = vp;
229                 vps = vp;
230         }
231
232         /*
233          *      Now build a new RADIUS_PACKET and send it.
234          *
235          *      FIXME: it could be that the id wraps around too fast if
236          *      we have a lot of requests, it might be better to keep
237          *      a seperate ID value per remote server.
238          *
239          *      OTOH the remote radius server should be smart enough to
240          *      compare _both_ ID and vector. Right ?
241          */
242         if ((request->proxy = rad_alloc(TRUE)) == NULL) {
243                 radlog(L_ERR|L_CONS, "no memory");
244                 exit(1);
245         }
246
247         /*
248          *      Proxied requests get sent out the proxy FD ONLY.
249          */
250         request->proxy->sockfd = proxyfd;
251
252         request->proxy->code = request->packet->code;
253         if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
254                 request->proxy->dst_port = realm->auth_port;
255                 request->proxy->dst_ipaddr = realm->ipaddr;
256         } else {
257                 request->proxy->dst_port = realm->acct_port;
258                 request->proxy->dst_ipaddr = realm->acct_ipaddr;
259         }
260         rad_assert(request->proxy->vps == NULL);
261         request->proxy->vps = vps;
262
263         /*
264          *      Add the request to the list of outstanding requests.
265          *      Note that request->proxy->id is a 16 bits value,
266          *      while rad_send sends only the 8 least significant
267          *      bits of that same value.
268          */
269         request->proxy->id = (proxy_id++) & 0xff;
270         proxy_id &= 0xffff;
271
272         /*
273          *      Add PROXY_STATE attribute.
274          */
275         proxy_addinfo(request);
276
277         /*
278          *      Encrypt the User-Password with the proxy server's secret.
279          */
280         if ((vp = pairfind(vps, PW_PASSWORD)) != NULL) {
281
282                 rad_pwencode((char *)vp->strvalue,
283                                 &(vp->length),
284                                 (char *)realm->secret,
285                                 (char *)request->proxy->vector);
286  
287         /*
288          *      If there is no PW_CHAP_CHALLENGE attribute but there
289          *      is a PW_CHAP_PASSWORD we need to add it since we can't
290          *      use the request authenticator anymore - we changed it.
291          */
292         } else if (pairfind(vps, PW_CHAP_PASSWORD) &&
293                 pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
294                 vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
295                 if (!vp) {
296                         radlog(L_ERR|L_CONS, "no memory");
297                         exit(1);
298                 }
299                 vp->length = AUTH_VECTOR_LEN;
300                 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
301                 pairadd(&vps, vp);
302         }
303
304         /*
305          *      Send the request.
306          */
307         rad_send(request->proxy, NULL, (char *)realm->secret);
308         memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
309         request->proxy_is_replicate = replicating;
310         request->proxy_try_count = proxy_retry_count - 1;
311         request->proxy_next_try = request->timestamp + proxy_retry_delay;
312         delaypair = pairfind(vps, PW_ACCT_DELAY_TIME);
313         request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0);
314
315         /*
316          *      Do NOT free proxy->vps, the pairs are needed for the
317          *      retries! --Pac.
318          */
319         return replicating?2:1;
320 }