1 /* This file is part of GNU Radius.
2    Copyright (C) 2000,2002,2003,2004,2006,2007,
3    2008 Free Software Foundation, Inc.
4 
5    Written by Sergey Poznyakoff
6 
7    GNU Radius is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    GNU Radius is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with GNU Radius; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/time.h>
28 #include <netinet/in.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <netdb.h>
34 #include <pwd.h>
35 #include <time.h>
36 #include <ctype.h>
37 #include <string.h>
38 
39 #include <radiusd.h>
40 
41 /* ************************************************************************* */
42 /* Functions local to this module */
43 
44 /* Decode a password and encode it again. */
45 static void
passwd_recode(grad_avp_t * pass_pair,char * new_secret,char * new_authenticator,radiusd_request_t * req)46 passwd_recode(grad_avp_t *pass_pair, char *new_secret, char *new_authenticator,
47 	      radiusd_request_t *req)
48 {
49         char password[GRAD_STRING_LENGTH+1];
50         req_decrypt_password(password, req->request, pass_pair);
51         grad_free(pass_pair->avp_strvalue);
52         grad_encrypt_password(pass_pair, password, new_authenticator, new_secret);
53         /* Don't let the cleantext hang around */
54         memset(password, 0, GRAD_STRING_LENGTH);
55 }
56 
57 /* Decode a password and encode it again. */
58 static void
tunnel_passwd_recode(grad_avp_t * pass_pair,char * new_secret,char * new_authenticator,radiusd_request_t * req)59 tunnel_passwd_recode(grad_avp_t *pass_pair, char *new_secret, char *new_authenticator,
60 		     radiusd_request_t *req)
61 {
62         char password[GRAD_STRING_LENGTH+1];
63 	u_char tag;
64 
65 	grad_decrypt_tunnel_password(password,
66 				     &tag, pass_pair,
67 				     req->request->authenticator,
68 				     req->request->secret);
69         grad_free(pass_pair->avp_strvalue);
70 	grad_encrypt_tunnel_password(pass_pair,
71 				     tag, password,
72 				     new_authenticator, new_secret);
73 	memset(password, 0, GRAD_STRING_LENGTH);
74 }
75 
76 grad_avp_t *
proxy_request_recode(radiusd_request_t * radreq,grad_avp_t * plist,u_char * secret,u_char * authenticator)77 proxy_request_recode(radiusd_request_t *radreq, grad_avp_t *plist,
78 		     u_char *secret, u_char *authenticator)
79 {
80 	grad_avp_t *p;
81 
82 	/* Recode password pair(s) */
83 	for (p = plist; p; p = p->next) {
84 		if (p->prop & GRAD_AP_ENCRYPT_RFC2138)
85 			passwd_recode(p, secret, authenticator, radreq);
86 		else if (p->prop & GRAD_AP_ENCRYPT_RFC2868)
87 			tunnel_passwd_recode(p, secret, authenticator, radreq);
88 	}
89 	return plist;
90 }
91 
92 /* ************************************************************************* */
93 /* Functions for finding the matching request in the list of outstanding ones.
94  */
95 
96 int
proxy_cmp(radiusd_request_t * qr,radiusd_request_t * r)97 proxy_cmp(radiusd_request_t *qr, radiusd_request_t *r)
98 {
99 	grad_avp_t *p, *proxy_state_pair = NULL;
100 	grad_server_t *server;
101 
102 	if (!qr->realm) {
103 		GRAD_DEBUG(100,"no proxy realm");
104 		return 1;
105 	}
106 	server = grad_list_item(qr->realm->queue->servers, qr->server_no);
107 	if (!server) {
108 		GRAD_DEBUG(100,"no proxy server");
109 		return 1;
110 	}
111 
112         /* Find the last PROXY_STATE attribute. */
113         for (p = r->request->avlist; p; p = p->next) {
114                 if (p->attribute == DA_PROXY_STATE)
115                         proxy_state_pair = p;
116         }
117 
118 	if (proxy_state_pair
119 	    && proxy_state_pair->avp_strlength == sizeof(PROXY_STATE)) {
120 		PROXY_STATE *state;
121 
122 		state = (PROXY_STATE *)proxy_state_pair->avp_strvalue;
123 
124 		GRAD_DEBUG4(1,
125 		      "state: ipaddr %08x, id %u, proxy_id %u, remote_ip %08x",
126 		       state->client_ip,
127 		       state->id,
128 		       state->proxy_id,
129 		       state->remote_ip);
130 
131                 if (state->ref_ip == ref_ip
132 		    && state->proxy_id == r->request->id
133 		    && state->remote_ip == r->request->ipaddr) {
134 			GRAD_DEBUG8(10,
135                                     "(old=data) id %d %d, ipaddr %#8x %#8x, "
136                                     "proxy_id %d %d, server_addr %#8x %#8x",
137 				    qr->request->id, state->id,
138 				    qr->request->ipaddr, state->client_ip,
139 				    qr->server_id, state->proxy_id,
140  				    server->addr, state->remote_ip);
141 
142 			if (state->client_ip == qr->request->ipaddr
143 			    && state->id  == qr->request->id
144 			    && state->proxy_id == qr->server_id
145 			    && state->remote_ip == server->addr) {
146 				GRAD_DEBUG(1,"EQUAL!!!");
147 				return 0;
148 			}
149 		}
150 	}
151 
152 	return 1;
153 }
154 
155 /* ************************************************************************* */
156 /* Reply functions. Possibly these should go to libclient? */
157 
158 /* Send the request */
159 static int
proxy_send_pdu(int fd,grad_server_t * server,radiusd_request_t * radreq,void * pdu,size_t size)160 proxy_send_pdu(int fd, grad_server_t *server, radiusd_request_t *radreq,
161 	       void *pdu, size_t size)
162 {
163 	struct sockaddr_in sin;
164 
165 	memset(&sin, 0, sizeof (sin));
166 	sin.sin_family = AF_INET;
167 	sin.sin_addr.s_addr = htonl(server->addr);
168 
169 	sin.sin_port = htons((radreq->request->code == RT_ACCESS_REQUEST) ?
170 			     server->port[GRAD_PORT_AUTH]
171 			     : server->port[GRAD_PORT_ACCT]);
172 
173 	GRAD_DEBUG2(1, "Proxying id %d to %lx",
174 		    radreq->request->id, (u_long)server->addr);
175 
176 	return sendto(fd, pdu, size, 0, (struct sockaddr *)&sin, sizeof(sin));
177 }
178 
179 int
proxy_send_request(int fd,radiusd_request_t * radreq)180 proxy_send_request(int fd, radiusd_request_t *radreq)
181 {
182 	grad_avp_t *plist, *p;
183 	void *pdu;
184 	size_t size;
185 	grad_server_t *server;
186 	int rc;
187 	PROXY_STATE *proxy_state;
188 
189 	if (radreq->attempt_no >= radreq->realm->queue->retries) {
190 		radreq->server_no++;
191 		radreq->attempt_no = 0;
192 	}
193 	server = grad_list_item(radreq->realm->queue->servers,
194 				radreq->server_no);
195 
196 	if (!server) {
197 		grad_log_req(GRAD_LOG_NOTICE, radreq->request,
198 		             _("couldn't send request to realm %s"),
199 		             radreq->realm->realm);
200 		return 0;
201 	}
202 	if (radreq->attempt_no == 0)
203 		radreq->server_id = grad_client_message_id(server);
204 	radreq->attempt_no++;
205 
206 	grad_client_random_authenticator(radreq->remote_auth);
207 	plist = proxy_request_recode(radreq,
208 				     grad_avl_dup(radreq->request->avlist),
209 				     server->secret,
210 				     radreq->remote_auth);
211 
212 	/* Add a proxy-pair to the end of the request. */
213 	p = grad_avp_alloc();
214 	p->name = "Proxy-State";
215 	p->attribute = DA_PROXY_STATE;
216 	p->type = GRAD_TYPE_STRING;
217 	p->avp_strlength = sizeof(PROXY_STATE);
218 	p->avp_strvalue = grad_emalloc(p->avp_strlength);
219 
220 	proxy_state = (PROXY_STATE *)p->avp_strvalue;
221 
222 	proxy_state->ref_ip = ref_ip;
223 	proxy_state->client_ip = radreq->request->ipaddr;
224 	proxy_state->id = radreq->request->id;
225 	proxy_state->proxy_id = radreq->server_id;
226 	proxy_state->remote_ip = server->addr;
227 
228 	grad_avl_add_pair(&plist, p);
229 
230 	/* Create the pdu */
231 	size = grad_create_pdu(&pdu,
232 			       radreq->request->code,
233 			       radreq->server_id,
234 			       radreq->remote_auth,
235 			       server->secret,
236 			       plist,
237 			       NULL);
238 	grad_avl_free(plist);
239 
240 	if (!radiusd_master()) {
241 		RADIUS_UPDATE *upd;
242 		size_t size;
243 
244                 /* Prepare update data. */
245 		size = sizeof(*upd) + strlen(radreq->realm->realm) + 1;
246 		upd = grad_emalloc(size);
247 		upd->id = radreq->request->id;
248 		upd->proxy_id = radreq->server_id;
249 		upd->server_no = radreq->server_no;
250 		strcpy(upd->realmname, radreq->realm->realm);
251 
252 		GRAD_DEBUG4(100,
253 		      "Update id=%d, proxy_id=%d, realm=%s, server_no=%d",
254 		       upd->id,upd->proxy_id,upd->realmname,
255 		       upd->server_no);
256 
257 		rpp_update(upd, size);
258 		grad_free(upd);
259 	}
260 
261 	rc = proxy_send_pdu(fd, server, radreq, pdu, size);
262 	grad_free(pdu);
263 	return rc;
264 }
265 
266 /* ************************************************************************* */
267 /* Interface functions */
268 
269 static grad_realm_t *
proxy_lookup_realm(radiusd_request_t * req,char * name)270 proxy_lookup_realm(radiusd_request_t *req, char *name)
271 {
272 	grad_realm_t *realm = grad_realm_lookup_name(name);
273 	static char *var[] = { "auth", "acct" };
274 	int t;
275 
276 	if (realm) {
277 		int rc;
278 		switch (req->request->code) {
279 		case RT_ACCESS_REQUEST:
280 			t = R_AUTH;
281 			break;
282 
283 		case RT_ACCOUNTING_REQUEST:
284 			t = R_ACCT;
285 			break;
286 
287 		default:
288 			/* Should not happen.. */
289 			grad_insist_fail("unexpected request code");
290 		}
291 
292 		rc = grad_envar_lookup_int(realm->args, var[t], -1);
293 		if (rc == -1) {
294 			/* Neither {var} nor no{var} is specified. Check
295 			   the orthogonal variable. If it is not set, proxying
296 			   is enabled for both authentication and
297 			   accounting. */
298 			rc = !grad_envar_lookup_int(realm->args, var[!t], 0);
299 		}
300 		if (!rc)
301 			realm = NULL;
302 
303 	}
304 	return realm;
305 }
306 
307 /* Relay the request to a remote server
308    Returns:  1 success (we reply, caller returns without replying)
309              0 fail (caller falls through to normal processing)
310              -1 fail (we don't reply, caller returns without replying) */
311 
312 int
proxy_send(REQUEST * req)313 proxy_send(REQUEST *req)
314 {
315 	radiusd_request_t *radreq = req->data;
316         char *username;
317         grad_avp_t *namepair;
318         grad_avp_t *vp;
319         char *realmname;
320         grad_realm_t *realm;
321 
322         /* Look up name. */
323         namepair = grad_avl_find(radreq->request->avlist, DA_USER_NAME);
324         if (grad_avp_null_string_p(namepair))
325                 return 0;
326 
327         username = namepair->avp_strvalue;
328 
329         /* Find the realm from the _end_ so that we can cascade realms:
330            user@realm1@realm2. A special realms NOREALM matches usual
331 	   user name (without explicit realm). A realm named DEFAULT is
332 	   returned by grad_realm_lookup_name() if no other realm name
333 	   matches.
334 
335 	   In addition, a keyword LOCAL in server list fields indicates
336 	   that the realm should be handled locally */
337 
338         if ((realmname = strrchr(username, '@')) == NULL) {
339                 if ((realm = grad_realm_lookup_name("NOREALM")) == NULL) {
340                         return 0;
341                 }
342         } else if ((realm = proxy_lookup_realm(radreq, realmname + 1))
343 		     == NULL) {
344                 /* If the realm is not found, we treat it as usual. */
345                 return 0;
346         }
347 
348 	if (realmname && grad_realm_strip_p(realm)) {
349 		*realmname = 0;
350 		namepair->avp_strlength = strlen(namepair->avp_strvalue);
351 	}
352 
353 	if (realm->queue == NULL) /* This is a LOCAL realm */
354                 return 0;
355 
356 	radreq->realm = realm;
357         radreq->server_no = 0;
358 	radreq->attempt_no = 0;
359 	/* Actual user name may be altered, but the remote user name
360 	   should remain the same */
361 	radreq->remote_user = grad_estrdup(username);
362 
363 	/* Add a Realm-Name pair */
364 	grad_avl_add_pair(&radreq->request->avlist,
365 			  grad_avp_create_string(DA_REALM_NAME, realm->realm));
366 
367         /* If there is a DA_CHAP_PASSWORD attribute, there is
368 	   also a DA_CHAP_CHALLENGE. If you change the code of
369 	   radius_auth_req_decode(), you will have
370 	   to manually take care of this. */
371 
372 	proxy_send_request(req->fd, radreq);
373 
374         return 1;
375 }
376 
377 /* FIXME! server timeout is not used */
378 void
proxy_retry(radiusd_request_t * req,int fd)379 proxy_retry(radiusd_request_t *req, int fd)
380 {
381 	grad_avp_t *namepair;
382 	char *saved_username;
383 
384         namepair = grad_avl_find(req->request->avlist, DA_USER_NAME);
385         if (namepair == NULL)
386                 return;
387 
388         saved_username = namepair->avp_strvalue;
389         namepair->avp_strvalue = req->remote_user;
390         namepair->avp_strlength = strlen(namepair->avp_strvalue);
391 
392 	proxy_send_request(fd, req);
393 
394 	/* restore username */
395 	namepair->avp_strvalue = saved_username;
396         namepair->avp_strlength = strlen(namepair->avp_strvalue);
397 }
398 
399 static int
select_propagated(void * null ARG_UNUSED,grad_avp_t * pair)400 select_propagated(void *null ARG_UNUSED, grad_avp_t *pair)
401 {
402         return pair->prop & GRAD_AP_PROPAGATE;
403 }
404 
405 /* Called when a response from a remote radius server has been received.
406    The function finds the original request and replaces all fields in
407    radreq, except `request->avlist', with the original data.
408    Return:   0 proxy found
409             -1 error don't reply */
410 int
proxy_receive(radiusd_request_t * radreq,radiusd_request_t * oldreq,int fd)411 proxy_receive(radiusd_request_t *radreq, radiusd_request_t *oldreq, int fd)
412 {
413         grad_avp_t *vp, *proxy_state_pair, *prev, *x;
414         grad_avp_t *allowed_pairs;
415 
416         /* Remove the last proxy pair from the list. */
417         proxy_state_pair = x = prev = NULL;
418 
419         for (vp = radreq->request->avlist; vp; vp = vp->next) {
420                 if (vp->attribute == DA_PROXY_STATE) {
421                         prev = x;
422                         proxy_state_pair = vp;
423                 }
424                 x = vp;
425         }
426 
427         if (proxy_state_pair) {
428                 if (prev)
429                         prev->next = proxy_state_pair->next;
430                 else
431                         radreq->request->avlist = proxy_state_pair->next;
432                 grad_avp_free(proxy_state_pair);
433         }
434 
435         /* Only allow some attributes to be propagated from
436            the remote server back to the NAS, for security. */
437         allowed_pairs = NULL;
438         grad_avl_move_pairs(&allowed_pairs, &radreq->request->avlist,
439 			    select_propagated, NULL);
440         grad_avl_free(radreq->request->avlist);
441 
442         /* Rebuild the radiusd_request_t struct, so that the normal functions
443            can process it. Take care not to modify oldreq! */
444 
445 	memcpy(radreq->request->authenticator,
446 	       oldreq->remote_auth, sizeof radreq->request->authenticator);
447  	radreq->server_reply = proxy_request_recode(radreq, allowed_pairs,
448 						    oldreq->request->secret,
449 						    oldreq->request->authenticator);
450         radreq->validated    = 1;
451         radreq->server_code  = radreq->request->code;
452         radreq->request->code = oldreq->request->code;
453 
454         radreq->request->ipaddr       = oldreq->request->ipaddr;
455         radreq->request->udp_port     = oldreq->request->udp_port;
456         radreq->request->id           = oldreq->request->id;
457 
458 	memcpy(radreq->request->authenticator,
459 	       oldreq->request->authenticator,
460 	       sizeof radreq->request->authenticator);
461         radreq->request->secret       = oldreq->request->secret;
462         radreq->request->avlist       = grad_avl_dup(oldreq->request->avlist);
463 
464         /* Proxy support fields */
465         radreq->realm         = oldreq->realm;
466         radreq->server_no     = oldreq->server_no;
467         radreq->server_id     = oldreq->server_id;
468 
469         return 0;
470 }
471 
472