4 * Version: @(#)proxy.c 1.52 22-Jul-1999 miquels@cistron.nl
8 "@(#)proxy.c 1.52 Copyright 1999 Cistron Internet Services B.V.";
12 #include <sys/types.h>
13 #include <sys/socket.h>
15 #include <netinet/in.h>
31 static int proxy_id = 1;
32 static REQUEST *proxy_requests;
34 static int allowed [] = {
39 PW_FRAMED_COMPRESSION,
50 * Cleanup old outstanding requests.
52 static void proxy_cleanup(void)
54 REQUEST *a, *last, *next;
60 for (a = proxy_requests; a; a = next) {
62 if (a->timestamp + MAX_REQUEST_TIME < now) {
66 proxy_requests = a->next;
75 * Add a proxy-pair to the end of the request.
77 static void proxy_addinfo(RADIUS_PACKET *rp)
79 VALUE_PAIR *proxy_pair, *vp;
81 if (!(proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING))) {
82 log(L_ERR|L_CONS, "no memory");
85 sprintf(proxy_pair->strvalue, "%04x", rp->id);
86 proxy_pair->length = 4;
88 for (vp = rp->vps; vp && vp->next; vp = vp->next)
90 vp->next = proxy_pair;
94 * Add the request to the list.
96 int proxy_addrequest(REQUEST *request, int *proxy_id)
98 REQUEST *a, *last = NULL;
102 * See if we already have a similar outstanding request.
104 for (a = proxy_requests; a; a = a->next) {
105 if (a->packet->src_ipaddr == request->packet->src_ipaddr &&
106 a->packet->id == request->packet->id &&
107 !memcmp(a->packet->vector, request->packet->vector, 16))
113 * Yes, this is a retransmit so delete the
118 last->next = a->next;
120 proxy_requests = a->next;
129 request->next = NULL;
130 request->child_pid = -1;
131 request->timestamp = time(NULL);
133 request->next = proxy_requests;
134 proxy_requests = request;
141 * Relay the request to a remote server.
142 * Returns: 1 success (we reply, caller returns without replying)
143 * 0 fail (caller falls through to normal processing)
144 * -1 fail (we don't reply, caller returns without replying)
146 int proxy_send(REQUEST *request, int activefd)
148 VALUE_PAIR *namepair;
149 VALUE_PAIR *passpair;
150 VALUE_PAIR *vp, *vps;
156 * First copy the request, then look up
157 * name and (encrypted) password in the copy.
159 vps = paircopy(request->packet->vps);
160 namepair = pairfind(vps, PW_USER_NAME);
161 if (namepair == NULL) {
165 passpair = pairfind(vps, PW_PASSWORD);
168 * Use the original username if available. The one
169 * in the A/V pairs might have been stripped already.
171 if (request->username[0]) {
172 strncpy(namepair->strvalue, request->username,
173 sizeof(namepair->strvalue));
174 namepair->strvalue[sizeof(namepair->strvalue) - 1] = 0;
178 * Now check if we know this realm!
179 * A NULL realm is OK.
180 * If not found, we treat it as usual.
181 * Find the realm from the _end_ so that we can
182 * cascade realms: user@realm1@realm2.
184 if ((realmname = strrchr(namepair->strvalue, '@')) != NULL)
186 if ((realm = realm_find(realmname ? realmname : "NULL")) == NULL) {
190 if (realmname != NULL && realm->striprealm)
192 namepair->length = strlen(namepair->strvalue);
195 * Perhaps accounting proxying was turned off.
197 if (request->packet->code == PW_ACCOUNTING_REQUEST &&
198 realm-acct_port == 0) {
204 * The special server LOCAL ?
206 if (strcmp(realm->server, "LOCAL") == 0) {
208 namepair = pairfind(request->packet->vps, PW_USER_NAME);
209 if (realm->striprealm &&
210 ((realmname = strrchr(namepair->strvalue, '@')) != NULL)) {
212 namepair->length = strlen(namepair->strvalue);
218 * Find the remote server in the "client" list-
219 * we need the secret.
221 if ((client = client_find(realm->ipaddr)) == NULL) {
222 log(L_PROXY, "cannot find secret for server %s in clients file",
229 * Now build a new RADIUS_PACKET and send it.
231 * FIXME: it could be that the id wraps around too fast if
232 * we have a lot of requests, it might be better to keep
233 * a seperate ID value per remote server.
235 * OTOH the remote radius server should be smart enough to
236 * compare _both_ ID and vector. Right ?
238 if ((request->proxy = rad_alloc(0)) == NULL) {
239 log(L_ERR|L_CONS, "no memory");
243 request->proxy->code = request->packet->code;
244 request->proxy->dst_ipaddr = realm->ipaddr;
245 if (request->packet->code == PW_AUTHENTICATION_REQUEST)
246 request->proxy->dst_port = realm->auth_port;
248 request->proxy->dst_port = realm->acct_port;
249 request->proxy->vps = vps;
251 printf("Destination port: %d, server %s (%d/%d)\n",
252 request->proxy->dst_port, realm->server,
253 realm->auth_port, realm->acct_port);
256 * XXX: we re-use the vector from the original request
257 * here, since that's easy for retransmits ...
259 memcpy(request->proxy->vector, request->packet->vector,
263 * Add the request to the list of outstanding requests.
264 * Note that request->proxy->id is a 16 bits value,
265 * while rad_send sends only the 8 least significant
266 * bits of that same value.
268 request->proxy->id = proxy_addrequest(request, &proxy_id);
271 * Add PROXY_STATE attribute.
273 proxy_addinfo(request->proxy);
276 * We need to re-encode the password with
280 rad_pwdecode(passpair->strvalue, passpair->length,
281 request->secret, request->packet->vector);
282 rad_pwencode(passpair->strvalue, &(passpair->length),
283 client->secret, request->packet->vector);
287 * If there is no PW_CHAP_CHALLENGE attribute but there
288 * is a PW_CHAP_PASSWORD we need to add it since we can't
289 * use the request authenticator anymore - we changed it.
291 if (pairfind(vps, PW_CHAP_PASSWORD) &&
292 pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
293 if (!(vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING))) {
294 log(L_ERR|L_CONS, "no memory");
297 vp->length = AUTH_VECTOR_LEN;
298 memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
305 rad_send(request->proxy, activefd, client->secret);
308 * We can free proxy->vps now, not needed anymore.
310 pairfree(request->proxy->vps);
311 request->proxy->vps = NULL;
318 * We received a response from a remote radius server.
319 * Find the original request, then return.
320 * Returns: 0 proxy found
321 * -1 error don't reply
323 int proxy_receive(REQUEST *request, int activefd)
325 VALUE_PAIR *vp, *last, *prev, *x;
326 VALUE_PAIR *allowed_pairs;
327 REQUEST *oldreq, *lastreq;
333 * First cleanup old outstanding requests.
338 * FIXME: calculate md5 checksum!
342 * Find the last PROXY_STATE attribute.
350 for (vp = request->packet->vps; vp; vp = vp->next) {
351 if (vp->attribute == PW_PROXY_STATE) {
357 if (last && last->strvalue) {
359 * Merit really rapes the Proxy-State attribute.
360 * See if it still is a valid 4-digit hex number.
363 if (strlen(s) == 4 && isxdigit(s[0]) && isxdigit(s[1]) &&
364 isxdigit(s[2]) && isxdigit(s[3])) {
365 pp = strtol(last->strvalue, NULL, 16);
367 log(L_PROXY, "server %s mangled Proxy-State attribute",
368 client_name(request->packet->src_ipaddr));
373 * Now find it in the list of outstanding requests.
376 for (oldreq = proxy_requests; oldreq; oldreq = oldreq->next) {
378 * Some servers drop the proxy pair. So
379 * compare in another way if needed.
381 if (pp >= 0 && pp == oldreq->proxy->id)
384 request->packet->src_ipaddr == oldreq->proxy->dst_ipaddr &&
385 request->packet->id == (oldreq->proxy->id & 0xFF))
390 if (oldreq == NULL) {
391 log(L_PROXY, "Unreckognized proxy reply from server %s - ID %d",
392 client_name(request->packet->src_ipaddr),
393 request->packet->id);
398 * Remove oldreq from list.
401 lastreq->next = oldreq->next;
403 proxy_requests = oldreq->next;
406 * Remove proxy pair from list.
410 prev->next = last->next;
412 request->packet->vps = last->next;
416 * Only allow some attributes to be propagated from
417 * the remote server back to the NAS, for security.
419 allowed_pairs = NULL;
420 for(i = 0; allowed[i]; i++)
421 pairmove2(&allowed_pairs, &(request->packet->vps), allowed[i]);
424 * Now rebuild the AUTHREQ struct, so that the
425 * normal functions can process it.
427 request->proxy = oldreq->proxy;
428 oldreq->proxy = NULL;
429 request->proxy->vps = allowed_pairs;
430 request->proxy->code = request->packet->code;
432 pairfree(request->packet->vps);
433 free(request->packet);
434 request->packet = oldreq->packet;
435 oldreq->packet = NULL;
437 request->timestamp = oldreq->timestamp;
439 request_free(oldreq);