dc08eeef4d2d6c4798685f4a9f5b1018b5fc2da4
[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 #include <assert.h>
41
42 #include "radiusd.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          *      Maybe they're proxying it to a LOCAL realm, in which
184          *      case do nothing.
185          */
186         if ((realm->ipaddr == htonl(INADDR_LOOPBACK)) &&
187                         (realm->auth_port == auth_port) &&
188                         (realm->acct_port == acct_port)) {
189                 return 0;
190         }
191         
192         /*
193          *      Copy the request, then look up
194          *      name and plain-text password in the copy.
195          *
196          *      Note that the User-Name attribute is the *original*
197          *      as sent over by the client.  The Stripped-User-Name
198          *      attribute is the one hacked through the 'hints' file.
199          */
200         vps = paircopy(request->packet->vps);
201         namepair = pairfind(vps, PW_USER_NAME);
202         strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME);
203
204         /*
205          *      If there's a Stripped-User-Name attribute in the
206          *      request, then use THAT as the User-Name for the
207          *      proxied request, instead of the original name.
208          *
209          *      This is done by making a copy of the Stripped-User-Name
210          *      attribute, turning it into a User-Name attribute,
211          *      deleting the Stripped-User-Name and User-Name attributes
212          *      from the vps list, and making the new User-Name
213          *      the head of the vps list.
214          */
215         if (strippednamepair) {
216                 vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
217                 if (!vp) {
218                         radlog(L_ERR|L_CONS, "no memory");
219                         exit(1);
220                 }
221                 memcpy(vp->strvalue, strippednamepair->strvalue,
222                                 sizeof(vp->strvalue));
223                 vp->length = strippednamepair->length;
224                 pairdelete(&vps, PW_USER_NAME);
225                 pairdelete(&vps, PW_STRIPPED_USER_NAME);
226                 vp->next = vps;
227                 namepair = vp;
228                 vps = vp;
229         }
230
231         /*
232          *      Now build a new RADIUS_PACKET and send it.
233          *
234          *      FIXME: it could be that the id wraps around too fast if
235          *      we have a lot of requests, it might be better to keep
236          *      a seperate ID value per remote server.
237          *
238          *      OTOH the remote radius server should be smart enough to
239          *      compare _both_ ID and vector. Right ?
240          */
241         if ((request->proxy = rad_alloc(TRUE)) == NULL) {
242                 radlog(L_ERR|L_CONS, "no memory");
243                 exit(1);
244         }
245
246         /*
247          *      Proxied requests get sent out the proxy FD ONLY.
248          */
249         request->proxy->sockfd = proxyfd;
250
251         request->proxy->code = request->packet->code;
252         request->proxy->dst_ipaddr = realm->ipaddr;
253         if (request->packet->code == PW_AUTHENTICATION_REQUEST)
254                 request->proxy->dst_port = realm->auth_port;
255         else
256                 request->proxy->dst_port = realm->acct_port;
257         assert(request->proxy->vps == NULL);
258         request->proxy->vps = vps;
259
260         /*
261          *      Add the request to the list of outstanding requests.
262          *      Note that request->proxy->id is a 16 bits value,
263          *      while rad_send sends only the 8 least significant
264          *      bits of that same value.
265          */
266         request->proxy->id = (proxy_id++) & 0xff;
267         proxy_id &= 0xffff;
268
269         /*
270          *      Add PROXY_STATE attribute.
271          */
272         proxy_addinfo(request);
273
274         /*
275          *      Encrypt the Password with the proxy server's secret.
276          */
277         if ((vp = pairfind(vps, PW_PASSWORD)) != NULL) {
278
279                 rad_pwencode((char *)vp->strvalue,
280                                 &(vp->length),
281                                 realm->secret, (char *)request->proxy->vector);
282  
283         /*
284          *      If there is no PW_CHAP_CHALLENGE attribute but there
285          *      is a PW_CHAP_PASSWORD we need to add it since we can't
286          *      use the request authenticator anymore - we changed it.
287          */
288         } else if (pairfind(vps, PW_CHAP_PASSWORD) &&
289                 pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
290                 vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
291                 if (!vp) {
292                         radlog(L_ERR|L_CONS, "no memory");
293                         exit(1);
294                 }
295                 vp->length = AUTH_VECTOR_LEN;
296                 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
297                 pairadd(&vps, vp);
298         }
299
300         /*
301          *      Send the request.
302          */
303         rad_send(request->proxy, (char *)realm->secret);
304         memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
305         request->proxy_is_replicate = replicating;
306         request->proxy_try_count = proxy_retry_count - 1;
307         request->proxy_next_try = request->timestamp + proxy_retry_delay;
308         delaypair = pairfind(vps, PW_ACCT_DELAY_TIME);
309         request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0);
310
311         /*
312          *      Do NOT free proxy->vps, the pairs are needed for the
313          *      retries! --Pac.
314          */
315         return replicating?2:1;
316 }