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.
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.
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
20 * Copyright 2000 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Chris Parker <cparker@starnetusa.com>
25 static const char rcsid[] = "$Id$";
28 #include "libradius.h"
30 #include <sys/socket.h>
33 # include <netinet/in.h>
42 #include "rad_assert.h"
45 static uint32_t proxy_id = 1;
48 * We received a response from a remote radius server.
49 * Find the original request, then return.
50 * Returns: 1 replication don't reply
52 * -1 error don't reply
54 int proxy_receive(REQUEST *request)
56 VALUE_PAIR *proxypair;
57 VALUE_PAIR *replicatepair;
58 VALUE_PAIR *realmpair;
63 proxypair = pairfind(request->config_items, PW_PROXY_TO_REALM);
64 replicatepair = pairfind(request->config_items, PW_REPLICATE_TO_REALM);
66 realmpair = proxypair;
68 } else if(replicatepair) {
69 realmpair = replicatepair;
72 radlog(L_PROXY, "Proxy reply to packet with no Realm");
76 realmname = (char *) realmpair->strvalue;
77 realm = realm_find(realmname);
80 * Don't touch the reply VP's. Assume that a module
81 * takes care of that...
84 return replicating?1:0;
88 * Add a proxy-pair to the end of the request.
90 static void proxy_addinfo(REQUEST *request)
92 VALUE_PAIR *proxy_pair;
94 proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
95 if (proxy_pair == NULL) {
96 radlog(L_ERR|L_CONS, "no memory");
99 sprintf((char *)proxy_pair->strvalue, "%d", request->packet->id);
100 proxy_pair->length = strlen((char *)proxy_pair->strvalue);
102 pairadd(&request->proxy->vps, proxy_pair);
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)
112 int proxy_send(REQUEST *request)
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;
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. */
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);
140 realmpair = proxypair;
142 } else if (replicatepair) {
143 realmpair = replicatepair;
147 * Neither proxy or replicate attributes are set,
148 * so we can exit from the proxy code.
154 * Length == 0 means it exists, but there's no realm.
157 if (realmpair->length == 0) {
161 realmname = (char *)realmpair->strvalue;
164 * Look for the realm, letting realm_find take care
165 * of the "NULL" realm.
167 * If there is no such realm, then exit.
168 * Maybe we should log an error?
170 realm = realm_find(realmname);
176 * Remember that we sent the request to a Realm.
178 pairadd(&request->packet->vps,
179 pairmake("Realm", realm->realm, T_OP_EQ));
183 * Access-Request: look for LOCAL realm.
184 * Accounting-Request: look for LOCAL realm.
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)))) {
194 * Copy the request, then look up
195 * name and plain-text password in the copy.
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.
201 vps = paircopy(request->packet->vps);
202 namepair = pairfind(vps, PW_USER_NAME);
203 strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME);
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.
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.
216 if (strippednamepair) {
217 vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
219 radlog(L_ERR|L_CONS, "no memory");
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);
233 * Now build a new RADIUS_PACKET and send it.
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.
239 * OTOH the remote radius server should be smart enough to
240 * compare _both_ ID and vector. Right ?
242 if ((request->proxy = rad_alloc(TRUE)) == NULL) {
243 radlog(L_ERR|L_CONS, "no memory");
248 * Proxied requests get sent out the proxy FD ONLY.
250 request->proxy->sockfd = proxyfd;
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;
257 request->proxy->dst_port = realm->acct_port;
258 request->proxy->dst_ipaddr = realm->acct_ipaddr;
260 rad_assert(request->proxy->vps == NULL);
261 request->proxy->vps = vps;
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.
269 request->proxy->id = (proxy_id++) & 0xff;
273 * Add PROXY_STATE attribute.
275 proxy_addinfo(request);
278 * Encrypt the User-Password with the proxy server's secret.
280 if ((vp = pairfind(vps, PW_PASSWORD)) != NULL) {
282 rad_pwencode((char *)vp->strvalue,
284 (char *)realm->secret,
285 (char *)request->proxy->vector);
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.
292 } else if (pairfind(vps, PW_CHAP_PASSWORD) &&
293 pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
294 vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
296 radlog(L_ERR|L_CONS, "no memory");
299 vp->length = AUTH_VECTOR_LEN;
300 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
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);
316 * Do NOT free proxy->vps, the pairs are needed for the
319 return replicating?2:1;