1 /* This file is part of GNU Radius
2    Copyright (C) 2000,2002,2003,2004,2005,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 <stdlib.h>
26 #include <ctype.h>
27 
28 #include <sysdep.h>
29 #include <radiusd.h>
30 #include <rewrite.h>
31 
32 /* obstack_grow with quoting of potentially dangerous characters */
33 static void
obstack_grow_escaped(struct obstack * obp,char * str,int len)34 obstack_grow_escaped(struct obstack *obp, char *str, int len)
35 {
36         for (; len > 0; len--, str++) {
37                 switch (*str) {
38                 case '"':
39                 case '\'':
40                 case '\\':
41                         obstack_1grow(obp, '\\');
42 			obstack_1grow(obp, *str);
43 			break;
44 
45                 default:
46 			if (isprint(*str))
47 				obstack_1grow(obp, *str);
48 			else {
49 				char buf[4];
50 				snprintf(buf, sizeof(buf), "%03o",
51 					 *(u_char*)str);
52 				obstack_1grow(obp, '\\');
53 				obstack_grow(obp, buf, 3);
54 			}
55                 }
56         }
57 }
58 
59 /*
60  *      Replace %<whatever> in a string.
61  *
62  *      %Cnum          attribute number `num' from request pairs
63  *      %C{attr-name}  attribute `attr-name' from request pairs
64  *      %Rnum          attribute number `num' from reply pairs
65  *      %R{attr-name}  attribute `attr-name' from reply pairs
66  *      %D             current date/time (localtime)
67  *      %G             current date/time (GMT)
68  *      Shortcuts:
69  *      %p             Port number
70  *      %n             NAS IP address
71  *      %f             Framed IP address
72  *      %u             User name
73  *      %c             Callback-Number
74  *      %i             Calling-Station-Id
75  *      %t             MTU
76  *      %a             Protocol (SLIP/PPP)
77  *      %s             Speed (DA_CONNECT_INFO)
78  *
79  */
80 
81 /* Find attribute `attr' in pairlist `pairlist' and store it's formatted
82  * value into obstack.
83  * If no attribute found, store the provided default value (defval). If
84  * the latter is NULL, store "unknown" for string type and "0" for
85  * others.
86  */
87 static void
attr_to_str(struct obstack * obp,grad_request_t * req,grad_avp_t * pairlist,grad_dict_attr_t * attr,char * defval,int escape)88 attr_to_str(struct obstack *obp, grad_request_t *req, grad_avp_t *pairlist,
89             grad_dict_attr_t  *attr, char *defval, int escape)
90 {
91         grad_avp_t *pair;
92         int len;
93         char tmp[GRAD_STRING_LENGTH + 1];
94         char *str;
95 
96         if (!attr) {
97                 grad_log_req(GRAD_LOG_ERR, req, "attribute not found");
98                 return;
99         }
100 
101         if ((pair = grad_avl_find(pairlist, attr->value)) == NULL) {
102                 if (!defval) {
103                         if (attr->type == GRAD_TYPE_STRING)
104                                 defval = "-";
105                         else
106                                 defval = "-0";
107                 }
108 
109                 switch (*defval++) {
110                 case '-':
111                         len = strlen(defval);
112                         obstack_grow(obp, defval, len);
113                         break;
114                 case '+':
115                         break;
116                 case '?':
117                         if (*defval == 0)
118                                 defval = "Attribute is not present";
119                         grad_log_req(GRAD_LOG_ERR, req, "%s: %s",
120 				     attr->name, defval);
121                         break;
122                 case '=':
123                         if (pairlist) {
124 				grad_locus_t loc;
125 				loc.file = __FILE__; /*FIXME*/
126 				loc.line = __LINE__;
127                                 pair = grad_create_pair(&loc,
128 							attr->name,
129 							grad_operator_equal,
130 							defval);
131                                 if (pair)
132                                         grad_avl_add_list(&pairlist, pair);
133                         }
134                         break;
135                 default:
136                         if (defval)
137                                 grad_log(GRAD_LOG_ERR, "invalid : substitution: %s",
138                                          defval);
139                         else
140                                 grad_log(GRAD_LOG_ERR, "null : substitution");
141                         break;
142                 }
143 
144                 if (!pair)
145                         return;
146         } else if (defval && *defval == '+') {
147                 defval++;
148                 len = strlen(defval);
149                 obstack_grow(obp, defval, len);
150                 return;
151         }
152 
153         tmp[GRAD_STRING_LENGTH-1] = 0;
154         switch (attr->type) {
155         case GRAD_TYPE_STRING:
156                 if ((attr->prop & GRAD_AP_ENCRYPT) && req) {
157                         req_decrypt_password(tmp, req, pair);
158 			str = tmp;
159 		} else
160 			str = pair->avp_strvalue;
161 
162 		if (attr->prop & GRAD_AP_BINARY_STRING)
163 			len = pair->avp_strlength;
164 		else
165 			/* strvalue might include terminating zero
166 			   character, so we need to recalculate it */
167 			len = strlen(str);
168 		if (escape)
169 			obstack_grow_escaped(obp, str, len);
170 		else
171 			obstack_grow(obp, str, len);
172                 break;
173 
174         case GRAD_TYPE_INTEGER:
175 	{
176 		grad_dict_value_t *dval;
177 
178                 if (escape && (pair->prop & GRAD_AP_TRANSLATE))
179                         dval = grad_value_lookup(pair->avp_lvalue, pair->name);
180                 else
181                         dval = NULL;
182 
183                 if (!dval) {
184 			snprintf(tmp, sizeof(tmp), "%lu", pair->avp_lvalue);
185 			len = strlen(tmp);
186 			obstack_grow(obp, tmp, len);
187 		} else
188 			obstack_grow(obp, dval->name, strlen (dval->name));
189 		break;
190 	}
191 
192         case GRAD_TYPE_IPADDR:
193                 grad_ip_iptostr(pair->avp_lvalue, tmp);
194                 len = strlen(tmp);
195                 obstack_grow(obp, tmp, len);
196                 break;
197 
198         case GRAD_TYPE_DATE:
199                 snprintf(tmp, sizeof(tmp), "%ld", pair->avp_lvalue);
200                 len = strlen(tmp);
201                 obstack_grow(obp, tmp, len);
202                 break;
203 
204         default:
205                 grad_log(GRAD_LOG_CRIT,
206                          _("INTERNAL ERROR (%s:%d): attribute %d has bad type (%d)"),
207                          __FILE__, __LINE__,
208                          attr->value, attr->type);
209                 break;
210         }
211 }
212 
213 static void
curtime_to_str(struct obstack * obp,grad_avp_t * request,int gmt)214 curtime_to_str(struct obstack *obp, grad_avp_t *request, int gmt)
215 {
216         time_t curtime;
217         struct tm *tm, tms;
218         grad_avp_t *pair;
219         char tbuf[GRAD_STRING_LENGTH];
220         int len;
221 
222         curtime = time(NULL);
223         if (pair = grad_avl_find(request, DA_ACCT_DELAY_TIME))
224                 curtime -= pair->avp_lvalue;
225         if (gmt)
226                 tm = gmtime(&curtime);
227         else
228                 tm = localtime_r(&curtime, &tms);
229 
230         len = strftime(tbuf, GRAD_STRING_LENGTH, "%Y-%m-%d %H:%M:%S", tm);
231         obstack_grow(obp, tbuf, len);
232 }
233 
234 /* Find attribute number `attr_no' in pairlist `pairlist' and store it's
235  * formatted value into obstack.
236  * If no attribute found, use provided default value (see comment to
237  * attr_to_str)
238  */
239 static void
attrno_to_str(struct obstack * obp,grad_request_t * req,grad_avp_t * pairlist,int attr_no,char * defval,int escape_str)240 attrno_to_str(struct obstack *obp, grad_request_t *req, grad_avp_t *pairlist,
241               int attr_no, char *defval, int escape_str)
242 {
243         attr_to_str(obp, req, pairlist,
244 		    grad_attr_number_to_dict(attr_no), defval, escape_str);
245 }
246 
247 static grad_dict_attr_t *
parse_dict_attr(char * p,char ** endp,char ** defval)248 parse_dict_attr(char *p, char **endp, char **defval)
249 {
250         char namebuf[GRAD_MAX_DICTNAME];
251 
252         *defval = NULL;
253         if (isdigit(*p)) {
254                 return grad_attr_number_to_dict(strtol(p, endp, 10));
255         }
256 
257         if (*p == '{') {
258                 int len, off;
259 
260                 p++;
261                 len = strlen(p);
262                 off = strcspn(p, ":}");
263 
264                 if (off == len || off >= sizeof namebuf)
265                         return NULL;
266 
267                 strncpy(namebuf, p, off);
268                 namebuf[off] = 0;
269 
270                 p += off;
271                 if (*p == ':') {
272                         int size;
273                         char *start = p+1;
274 
275                         for (; *p && *p != '}'; p++) {
276                                 if (*p == '\\' && *++p == 0)
277                                         break;
278                         }
279                         if (*p == 0)
280                                 return NULL;
281 
282                         size = p - start + 1;
283                         *defval = grad_emalloc(size);
284                         memcpy(*defval, start, size-1);
285                         (*defval)[size-1] = 0;
286                 }
287                 *endp = p + 1;
288                 return grad_attr_name_to_dict(namebuf);
289         }
290         *endp = p;
291         return NULL;
292 }
293 
294 void
radius_xlate0(struct obstack * obp,char * str,grad_request_t * req,grad_avp_t * reply)295 radius_xlate0(struct obstack *obp, char *str, grad_request_t *req,
296               grad_avp_t *reply)
297 {
298         int c;
299         char *p;
300         grad_dict_attr_t *da;
301         char *defval;
302 	int escape;
303 
304         for (p = str; *p; ) {
305                 switch (c = *p++) {
306                 default:
307                         obstack_1grow(obp, c);
308                         break;
309 
310                 case 0:
311                         goto end;
312 
313 		case '\n':
314 			obstack_1grow(obp, '\r');
315 			obstack_1grow(obp, c);
316 			break;
317 
318                 case '%':
319                         if (!req) {
320                                 obstack_1grow(obp, c);
321                                 break;
322                         }
323 			escape = (p > str+1 && (p[-2] == '\'' || p[-2] == '"'));
324                         switch (c = *p++) {
325                         case '%':
326                                 obstack_1grow(obp, c);
327                                 break;
328 
329                         case 'D':
330                                 curtime_to_str(obp, req->avlist, 0);
331                                 break;
332 
333                         case 'G':
334                                 curtime_to_str(obp, req->avlist, 1);
335                                 break;
336 
337                         case 'f': /* Framed IP address */
338                                 attrno_to_str(obp, NULL, reply,
339                                               DA_FRAMED_IP_ADDRESS, NULL,
340 					      escape);
341                                 break;
342 
343                         case 'n': /* NAS IP address */
344                                 attrno_to_str(obp, req,
345 					      req->avlist,
346                                               DA_NAS_IP_ADDRESS, NULL,
347 					      escape);
348                                 break;
349 
350                         case 't': /* MTU */
351                                 attrno_to_str(obp, NULL, reply,
352                                               DA_FRAMED_MTU, NULL,
353 					      escape);
354                                 break;
355 
356                         case 'p': /* Port number */
357                                 attrno_to_str(obp, req, req->avlist,
358                                               DA_NAS_PORT_ID, NULL,
359 					      escape);
360                                 break;
361 
362                         case 'u': /* User name */
363                                 attrno_to_str(obp, req, req->avlist,
364                                               DA_USER_NAME, NULL,
365 					      escape);
366                                 break;
367 
368                         case 'c': /* Callback-Number */
369                                 attrno_to_str(obp, NULL, reply,
370                                               DA_CALLBACK_NUMBER, NULL,
371 					      escape);
372                                 break;
373 
374                         case 'i': /* Calling station ID */
375                                 attrno_to_str(obp, req, req->avlist,
376                                               DA_CALLING_STATION_ID, NULL,
377 					      escape);
378                                 break;
379 
380                         case 'a': /* Protocol: SLIP/PPP */
381                                 attrno_to_str(obp, NULL, reply,
382                                               DA_FRAMED_PROTOCOL, NULL,
383 					      escape);
384                                 break;
385 
386                         case 's': /* Speed */
387                                 attrno_to_str(obp, req, req->avlist,
388                                               DA_CONNECT_INFO, NULL,
389 					      escape);
390                                 break;
391 
392                         case 'C':
393 				if (*p == '\\') {
394 					escape = 1;
395 					p++;
396 				}
397                                 /* Request pair */
398                                 da = parse_dict_attr(p, &p, &defval);
399                                 attr_to_str(obp, req, req->avlist,
400                                             da, defval, escape);
401                                 grad_free(defval);
402                                 break;
403 
404                         case 'R':
405 				if (*p == '\\') {
406 					escape = 1;
407 					p++;
408 				}
409                                 /* Reply pair */
410                                 da = parse_dict_attr(p, &p, &defval);
411                                 attr_to_str(obp, NULL,
412                                             reply, da, defval, escape);
413                                 break;
414 
415                         default:
416                                 obstack_1grow(obp, '%');
417                                 obstack_1grow(obp, c);
418                                 break;
419                         }
420                         break;
421 
422                 case '\\':
423                         switch (c = *p++) {
424                         case 'a':
425                                 obstack_1grow(obp, '\a');
426                                 break;
427 
428                         case 'b':
429                                 obstack_1grow(obp, '\b');
430                                 break;
431 
432                         case 'f':
433                                 obstack_1grow(obp, '\f');
434                                 break;
435 
436                         case 'e':
437                                 obstack_1grow(obp, '\033');
438                                 break;
439 
440                         case 'n':
441                                 obstack_1grow(obp, '\n');
442                                 break;
443 
444                         case 'r':
445                                 obstack_1grow(obp, '\r');
446                                 break;
447 
448                         case 't':
449                                 obstack_1grow(obp, '\t');
450                                 break;
451 
452                         case 0:
453                                 goto end;
454 
455                         default:
456                                 obstack_1grow(obp, '\\');
457                                 obstack_1grow(obp, c);
458                                 break;
459                         }
460                 }
461         }
462 end:
463         return;
464 }
465 
466 char *
radius_xlate(struct obstack * obp,char * str,grad_request_t * req,grad_avp_t * reply)467 radius_xlate(struct obstack *obp, char *str,
468 	     grad_request_t *req, grad_avp_t *reply)
469 {
470         radius_xlate0(obp, str, req, reply);
471         obstack_1grow(obp, 0);
472         return obstack_finish(obp);
473 }
474 
475 char *
util_xlate(struct obstack * sp,char * fmt,grad_request_t * radreq)476 util_xlate(struct obstack *sp, char *fmt, grad_request_t *radreq)
477 {
478 	char *str;
479 
480 	if (fmt[0] == '=') {
481 		grad_value_t val;
482 
483 		/*FIXME: Should be compiled!*/
484 		if (rewrite_interpret(fmt+1, radreq, &val))
485 			return NULL;
486 		if (val.type != String) {
487 			grad_log(GRAD_LOG_ERR, "%s: %s",
488 			         fmt+1, _("wrong return type"));
489 			/* Nothing to free in val */
490 			return NULL;
491 		}
492 		obstack_grow(sp, val.datum.sval.data, val.datum.sval.size + 1);
493 		grad_value_free(&val);
494 		str = obstack_finish(sp);
495 	} else {
496 		str = radius_xlate(sp, fmt, radreq, NULL);
497 	}
498 	return str;
499 }
500 
501 static void
pair_set_value(grad_avp_t * p,grad_value_t * val)502 pair_set_value(grad_avp_t *p, grad_value_t *val)
503 {
504 	char buf[64];
505 	char *endp;
506 
507 	switch (val->type) {
508 	case Integer:
509 		switch (p->type) {
510 		case GRAD_TYPE_STRING:
511 			snprintf(buf, sizeof buf, "%lu", val->datum.ival);
512 			grad_string_replace(&p->avp_strvalue, buf);
513 			p->avp_strlength = strlen(p->avp_strvalue);
514 			break;
515 
516 		case GRAD_TYPE_INTEGER:
517 		case GRAD_TYPE_IPADDR:
518 		case GRAD_TYPE_DATE:
519 			p->avp_lvalue = val->datum.ival;
520 		}
521 		break;
522 
523 	case String:
524 		switch (p->type) {
525 		case GRAD_TYPE_STRING:
526 			grad_string_replace(&p->avp_strvalue,
527 					    val->datum.sval.data);
528 			p->avp_strlength = strlen(p->avp_strvalue);
529 			break;
530 
531 		case GRAD_TYPE_INTEGER:
532 		case GRAD_TYPE_IPADDR:
533 		case GRAD_TYPE_DATE:
534 			p->avp_lvalue = strtoul(val->datum.sval.data, &endp, 0);
535 			if (*endp)
536 				grad_log(GRAD_LOG_ERR,
537 					 _("cannot convert \"%s\" to integer"),
538 					 val->datum.sval);
539 			break;
540 		}
541 		break;
542 
543 	default:
544 		grad_insist_fail("bad datatype");
545 	}
546 	p->eval_type = grad_eval_const;
547 }
548 
549 /* Evaluate an A/P pair P in the context of RADIUS request REQ. If ALLOW_XLATE
550    is true, constant pairs will be evaluated using traditional method, a.k.a.
551    radius_xlate. In this case REPLY supplies optional reply pairs.
552 
553    FIXME: ALLOW_XLATE is actually a kludge (see function radius_eval_avl
554    below), and REPLY could be substituted by req->reply. */
555 int
radius_eval_avp(radiusd_request_t * req,grad_avp_t * p,grad_avp_t * reply,int allow_xlate)556 radius_eval_avp(radiusd_request_t *req, grad_avp_t *p, grad_avp_t *reply,
557 		int allow_xlate)
558 {
559 	grad_value_t val;
560 
561 	switch (p->eval_type) {
562 	case grad_eval_const:
563 		if (allow_xlate && strchr(p->avp_strvalue, '%')) {
564 			struct obstack s;
565 			char *ptr;
566 			obstack_init (&s);
567 			ptr = radius_xlate(&s, p->avp_strvalue,
568 					   req->request, reply);
569 			if (strcmp(ptr, p->avp_strvalue)) {
570 				grad_string_replace(&p->avp_strvalue, ptr);
571 				p->avp_strlength = strlen (p->avp_strvalue);
572 			}
573 			obstack_free (&s, NULL);
574 		}
575 		break;
576 
577 	case grad_eval_interpret:
578 		if (rewrite_interpret(p->avp_strvalue, req->request, &val))
579 			return 1;
580 		pair_set_value(p, &val);
581 		grad_value_free(&val);
582 		break;
583 
584 	case grad_eval_compiled:
585 		if (rewrite_eval(p->avp_strvalue, req->request, &val))
586 			return 1;
587 		pair_set_value(p, &val);
588 		grad_value_free(&val);
589 		break;
590 
591 	default:
592 		grad_insist_fail("bad eval_type");
593 		return 1;
594 	}
595 	p->eval_type = grad_eval_const;
596 	return 0;
597 }
598 
599 /* Evaluate A/V pair list P in the context of RADIUS request REQ.
600    For backward compatibility, no traditional attribute translation is
601    performed, this is actually the reason for existence of the last
602    argument to radius_eval_avp(). This will change in future versions. */
603 int
radius_eval_avl(radiusd_request_t * req,grad_avp_t * p)604 radius_eval_avl(radiusd_request_t *req, grad_avp_t *p)
605 {
606 	int status = 0;
607 	for (; p; p = p->next)
608 		status |= radius_eval_avp(req, p, NULL, 0);
609 	return status;
610 }
611 
612 radiusd_request_t *
radiusd_request_alloc(grad_request_t * req)613 radiusd_request_alloc(grad_request_t *req)
614 {
615 	radiusd_request_t *ret = grad_emalloc(sizeof(*ret));
616 	ret->request = req;
617 	return ret;
618 }
619 
620 void
radiusd_request_free(radiusd_request_t * radreq)621 radiusd_request_free(radiusd_request_t *radreq)
622 {
623 	grad_list_destroy(&radreq->locus_list, NULL, NULL);
624 	grad_free(radreq->remote_user);
625         grad_avl_free(radreq->reply_pairs);
626         grad_free(radreq->reply_msg);
627         grad_avl_free(radreq->server_reply);
628 	grad_request_free(radreq->request);
629 	grad_free(radreq);
630 }
631 
632