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>
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 * Maybe they're proxying it to a LOCAL realm, in which
186 if ((realm->ipaddr == htonl(INADDR_LOOPBACK)) &&
187 (realm->auth_port == auth_port) &&
188 (realm->acct_port == acct_port)) {
193 * Copy the request, then look up
194 * name and plain-text password in the copy.
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.
200 vps = paircopy(request->packet->vps);
201 namepair = pairfind(vps, PW_USER_NAME);
202 strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME);
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.
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.
215 if (strippednamepair) {
216 vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
218 radlog(L_ERR|L_CONS, "no memory");
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);
232 * Now build a new RADIUS_PACKET and send it.
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.
238 * OTOH the remote radius server should be smart enough to
239 * compare _both_ ID and vector. Right ?
241 if ((request->proxy = rad_alloc(TRUE)) == NULL) {
242 radlog(L_ERR|L_CONS, "no memory");
247 * Proxied requests get sent out the proxy FD ONLY.
249 request->proxy->sockfd = proxyfd;
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;
256 request->proxy->dst_port = realm->acct_port;
257 assert(request->proxy->vps == NULL);
258 request->proxy->vps = vps;
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.
266 request->proxy->id = (proxy_id++) & 0xff;
270 * Add PROXY_STATE attribute.
272 proxy_addinfo(request);
275 * Encrypt the Password with the proxy server's secret.
277 if ((vp = pairfind(vps, PW_PASSWORD)) != NULL) {
279 rad_pwencode((char *)vp->strvalue,
281 realm->secret, (char *)request->proxy->vector);
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.
288 } else if (pairfind(vps, PW_CHAP_PASSWORD) &&
289 pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
290 vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
292 radlog(L_ERR|L_CONS, "no memory");
295 vp->length = AUTH_VECTOR_LEN;
296 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
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);
312 * Do NOT free proxy->vps, the pairs are needed for the
315 return replicating?2:1;