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