Kannel: Open Source WAP and SMS gateway  svn-r5335
radius_pdu.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * radius_pdu.c - parse and generate RADIUS Accounting PDUs
59  *
60  * Taken from gw/smsc/smpp_pdu.c writen by Lars Wirzenius.
61  * This makes heavy use of C pre-processor macro magic.
62  *
63  * References: RFC2866 - RADIUS Accounting
64  *
65  * Stipe Tolj <stolj@wapme.de>
66  */
67 
68 
69 #include <string.h>
70 #include "radius_pdu.h"
71 
72 #define MIN_RADIUS_PDU_LEN 20
73 #define MAX_RADIUS_PDU_LEN 4095
74 
75 
76 static unsigned long decode_integer(Octstr *os, long pos, int octets)
77 {
78  unsigned long u;
79  int i;
80 
81  gw_assert(octstr_len(os) >= pos + octets);
82 
83  u = 0;
84  for (i = 0; i < octets; ++i)
85  u = (u << 8) | octstr_get_char(os, pos + i);
86 
87  return u;
88 }
89 
90 
91 static void append_encoded_integer(Octstr *os, unsigned long u, long octets)
92 {
93  long i;
94 
95  for (i = 0; i < octets; ++i)
96  octstr_append_char(os, (u >> ((octets - i - 1) * 8)) & 0xFF);
97 }
98 
99 
100 /*
101 static void *get_header_element(RADIUS_PDU *pdu, unsigned char *e)
102 {
103  switch (pdu->type) {
104  #define INTEGER(name, octets) \
105  if (strcmp(#name, e) == 0) return (void*) *(&p->name);
106  #define NULTERMINATED(name, max_octets)
107  #define OCTETS(name, field_giving_octets) \
108  if (strcmp(#name, e) == 0) return (void*) p->name;
109  #define PDU(name, id, fields) \
110  case id: { \
111  struct name *p = &pdu->u.name; \
112  } break;
113  #include "radius_pdu.def"
114  default:
115  error(0, "Unknown RADIUS_PDU type, internal error.");
116  gw_free(pdu);
117  return NULL;
118  }
119 }
120 */
121 
122 
124 {
125  RADIUS_PDU *pdu;
126 
127  pdu = gw_malloc(sizeof(*pdu));
128  pdu->type = type;
129 
130  switch (type) {
131  #define INTEGER(name, octets) \
132  if (strcmp(#name, "code") == 0) p->name = type; \
133  else p->name = 0;
134  #define OCTETS(name, field_giving_octets) p->name = NULL;
135  #define PDU(name, id, fields) \
136  case id: { \
137  struct name *p = &pdu->u.name; \
138  pdu->type_name = #name; \
139  fields \
140  } break;
141  #include "radius_pdu.def"
142  default:
143  error(0, "Unknown RADIUS_PDU type, internal error.");
144  gw_free(pdu);
145  return NULL;
146  }
147  #define ATTR(attr, type, string, min, max)
148  #define UNASSIGNED(attr)
149  #define ATTRIBUTES(fields) \
150  pdu->attr = dict_create(20, (void (*)(void *))octstr_destroy);
151  #include "radius_attributes.def"
152 
153  return pdu;
154 }
155 
157 {
158  if (pdu == NULL)
159  return;
160 
161  switch (pdu->type) {
162  #define INTEGER(name, octets) p->name = 0;
163  #define OCTETS(name, field_giving_octets) octstr_destroy(p->name);
164  #define PDU(name, id, fields) \
165  case id: { struct name *p = &pdu->u.name; fields } break;
166  #include "radius_pdu.def"
167  default:
168  error(0, "Unknown RADIUS_PDU type, internal error while destroying.");
169  }
170 
171  #define ATTR(attr, type, string, min, max)
172  #define UNASSIGNED(attr)
173  #define ATTRIBUTES(fields) dict_destroy(pdu->attr);
174  #include "radius_attributes.def"
175 
176  gw_free(pdu);
177 }
178 
179 /*
180 static void radius_type_append(Octstr **os, int type, int pmin, int pmax,
181  Octstr *value)
182 {
183  long l;
184 
185  switch (type) {
186  case t_int:
187  octstr_parse_long(&l, value, 0, 10);
188  append_encoded_integer(*os, l, pmin);
189  break;
190  case t_string:
191  octstr_append(*os, value);
192  break;
193  case t_ipaddr:
194  ret = octstr_create("");
195  for (i = 0; i < 4; i++) {
196  int c = octstr_get_char(value, i);
197  Octstr *b = octstr_format("%d", c);
198  octstr_append(ret, b);
199  i < 3 ? octstr_append_cstr(ret, ".") : NULL;
200  octstr_destroy(b);
201  }
202  break;
203  default:
204  panic(0, "RADIUS: Attribute type %d does not exist.", type);
205  break;
206  }
207 }
208 */
209 
211 {
212  Octstr *os;
213 
214  os = octstr_create("");
215 
216  gw_assert(pdu != NULL);
217 
218  #define ATTR(atype, type, string, pmin, pmax) \
219  { \
220  Octstr *attr_strg = octstr_create(string); \
221  Octstr *attr_val = dict_get(p->attr, attr_str); \
222  if (attr_str != NULL) { \
223  int attr_len = octstr_len(attr_val) + 2; \
224  octstr_format_append(os, "%02X", atype); \
225  octstr_append_data(os, (char*) &attr_len, 2); \
226  radius_type_append(&os, type, pmin, pmax, attr_val); \
227  } \
228  octstr_destroy(attr_str); \
229  }
230  #define UNASSIGNED(attr)
231  #define ATTRIBUTES(fields)
232  #include "radius_attributes.def"
233 
234  return os;
235 }
236 
238 {
239  Octstr *os,*oos;
240  Octstr *temp;
241 
242  os = octstr_create("");
243 
244  gw_assert(pdu != NULL);
245 
246  /*
247  switch (pdu->type) {
248  #define INTEGER(name, octets) p = *(&p);
249  #define NULTERMINATED(name, max_octets) p = *(&p);
250  #define OCTETS(name, field_giving_octets) \
251  p->field_giving_octets = octstr_len(p->name);
252  #define PDU(name, id, fields) \
253  case id: { struct name *p = &pdu->u.name; fields } break;
254  #include "radius_pdu.def"
255  default:
256  error(0, "Unknown RADIUS_PDU type, internal error while packing.");
257  }
258  */
259 
260  switch (pdu->type) {
261  #define INTEGER(name, octets) \
262  append_encoded_integer(os, p->name, octets);
263  #define OCTETS(name, field_giving_octets) \
264  octstr_append(os, p->name);
265  #define PDU(name, id, fields) \
266  case id: { struct name *p = &pdu->u.name; fields; oos = radius_attr_pack(pdu); \
267  octstr_append(os, oos);octstr_destroy(oos); } break;
268  #include "radius_pdu.def"
269  default:
270  error(0, "Unknown RADIUS_PDU type, internal error while packing.");
271  }
272 
273  /* now set PDU length */
274  temp = octstr_create("");
275  append_encoded_integer(temp, octstr_len(os), 2);
276  octstr_delete(os, 2, 2);
277  octstr_insert(os, temp, 2);
278  octstr_destroy(temp);
279 
280  return os;
281 }
282 
283 static Octstr *radius_type_convert(int type, Octstr *value)
284 {
285  Octstr *ret = NULL;
286  int i;
287 
288  switch (type) {
289  case t_int:
290  ret = octstr_format("%ld", decode_integer(value, 0, 4));
291  break;
292  case t_string:
293  ret = octstr_format("%s", octstr_get_cstr(value));
294  break;
295  case t_ipaddr:
296  ret = octstr_create("");
297  for (i = 0; i < 4; i++) {
298  int c = octstr_get_char(value, i);
299  Octstr *b = octstr_format("%d", c);
300  octstr_append(ret, b);
301  if (i < 3)
302  octstr_append_cstr(ret, ".");
303  octstr_destroy(b);
304  }
305  break;
306  default:
307  panic(0, "RADIUS: Attribute type %d does not exist.", type);
308  break;
309  }
310 
311  return ret;
312 }
313 
315 {
316  #define ATTR(atype, type, string, pmin, pmax) \
317  if (atype == attr_type) { \
318  Octstr *tmp, *value; \
319  if ((attr_len-2) < pmin || (attr_len-2) > pmax) { \
320  error(0, "RADIUS: Attribute (%d) `%s' has invalid len %d, droppped.", \
321  attr_type, string, (attr_len-2)); \
322  continue; \
323  } \
324  attr_val = parse_get_octets(*context, attr_len - 2); \
325  tmp = octstr_format("RADIUS: Attribute (%d) `%s', len %d", \
326  attr_type, string, attr_len - 2); \
327  value = radius_type_convert(type, attr_val); \
328  octstr_destroy(attr_val); \
329  octstr_dump_short(value, 0, octstr_get_cstr(tmp)); \
330  octstr_destroy(tmp); \
331  attr_str = octstr_create(string); \
332  dict_put((*pdu)->attr, attr_str, value); \
333  octstr_destroy(attr_str); \
334  value = NULL; \
335  } else
336  #define UNASSIGNED(attr) \
337  if (attr == attr_type) { \
338  error(0, "RADIUS: Attribute (%d) is unassigned and should not be used.", \
339  attr_type); \
340  continue; \
341  } else
342  #define ATTRIBUTES(fields) \
343  while (parse_octets_left(*context) > 0 && !parse_error(*context)) { \
344  int attr_type, attr_len; \
345  Octstr *attr_val = NULL; \
346  Octstr *attr_str = NULL; \
347  attr_type = parse_get_char(*context); \
348  attr_len = parse_get_char(*context); \
349  fields \
350  { \
351  debug("radius.unpack", 0, "RADIUS: Unknown attribute type (0x%03lx) " \
352  "len %d in PDU `%s'.", \
353  (long unsigned int)attr_type, attr_len, (*pdu)->type_name); \
354  parse_skip(*context, attr_len - 2); \
355  } \
356  }
357  #include "radius_attributes.def"
358 }
359 
361 {
362  RADIUS_PDU *pdu;
363  int type;
364  long len, pos;
366  Octstr *authenticator;
367 
368  len = octstr_len(data_without_len);
369 
370  if (len < 20) {
371  error(0, "RADIUS: PDU was too short (%ld bytes).",
372  octstr_len(data_without_len));
373  return NULL;
374  }
375 
376  context = parse_context_create(data_without_len);
377 
380  pdu = radius_pdu_create(type, NULL);
381  if (pdu == NULL)
382  return NULL;
383 
384  len = decode_integer(data_without_len, 2, 2) - 19;
385  parse_skip(context, 2);
386  debug("radius", 0, "RADIUS: Attributes len is %ld", len);
387 
388  authenticator = parse_get_octets(context, 16);
389  octstr_dump_short(authenticator, 0, "RADIUS: Authenticator (md5) is:");
390 
391  /* skipping back to context start for macro magic */
393  context = parse_context_create(data_without_len);
394 
395  switch (type) {
396  #define INTEGER(name, octets) \
397  pos = octstr_len(data_without_len) - parse_octets_left(context); \
398  p->name = decode_integer(data_without_len, pos, octets); \
399  parse_skip(context, octets);
400  #define OCTETS(name, field_giving_octets) \
401  p->name = parse_get_octets(context, field_giving_octets);
402  #define PDU(name, id, fields) \
403  case id: { struct name *p = &pdu->u.name; fields; \
404  radius_attr_unpack(&context, &pdu); } break;
405  #include "radius_pdu.def"
406  default:
407  error(0, "Unknown RADIUS_PDU type, internal error while unpacking.");
408  }
409 
411  octstr_destroy(authenticator);
412 
413  return pdu;
414 }
415 
417 {
418  int rc = 0;
419  Octstr *stream;
420  Octstr *attributes;
421  Octstr *digest;
422 
423  stream = attributes = digest = NULL;
424 
425  /* first extract attributes from raw data, where
426  * the first 20 octets are code, idendifier, length
427  * and authenticator value as described in RFC2866, sec. 3 */
428  if (octstr_len(*data) > 20)
429  attributes = octstr_copy(*data, 20, octstr_len(*data)-20);
430 
431  switch (pdu->type) {
432  case 0x04: /* Accounting-Request, see RFC2866, page 6 */
433  stream = octstr_copy(*data, 0, 4);
434  octstr_append_data(stream, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16);
435  octstr_append(stream, attributes);
436  octstr_append(stream, secret);
437  digest = md5(stream);
438  rc = octstr_compare(pdu->u.Accounting_Request.authenticator,
439  digest) == 0 ? 1 : 0;
440  break;
441  case 0x05: /* Accounting-Response, create Response authenticator */
442  stream = octstr_duplicate(*data);
443  octstr_append(stream, secret);
444  digest = md5(stream);
445  octstr_delete(*data, 4, 16);
446  octstr_insert(*data, digest, 4);
447  break;
448  default:
449  break;
450  }
451 
452  octstr_destroy(attributes);
453  octstr_destroy(stream);
454  octstr_destroy(digest);
455 
456  return rc;
457 }
458 
459 static void radius_attr_dump(RADIUS_PDU *pdu)
460 {
461  #define UNASSIGNED(attr)
462  #define ATTR(atype, type, string, pmin, pmax) \
463  id = atype; \
464  key = octstr_create(string); \
465  val = dict_get(pdu->attr, key); \
466  if (val != NULL) \
467  octstr_dump_short(val, 2, #atype); \
468  octstr_destroy(key);
469  #define ATTRIBUTES(fields) \
470  if (pdu->attr != NULL) { \
471  Octstr *key = NULL, *val = NULL; \
472  int id; \
473  fields \
474  }
475  #include "radius_attributes.def"
476 }
477 
479 {
480  debug("radius", 0, "RADIUS PDU %p dump:", (void *) pdu);
481  debug("radius", 0, " type_name: %s", pdu->type_name);
482  switch (pdu->type) {
483  #define INTEGER(name, octets) \
484  debug("radius", 0, " %s: %lu = 0x%08lx", #name, p->name, p->name);
485  #define OCTETS(name, field_giving_octets) \
486  octstr_dump_short(p->name, 2, #name);
487  #define PDU(name, id, fields) \
488  case id: { struct name *p = &pdu->u.name; fields; \
489  radius_attr_dump(pdu); } break;
490  #include "radius_pdu.def"
491  default:
492  error(0, "Unknown RADIUS_PDU type, internal error.");
493  break;
494  }
495  debug("radius", 0, "RADIUS PDU dump ends.");
496 }
497 
499 {
500  gw_assert(pdu != NULL);
501 
502  if (pdu->attr == NULL)
503  return NULL;
504 
505  return dict_get(pdu->attr, attribute);
506 }
507 
void error(int err, const char *fmt,...)
Definition: log.c:648
Octstr * md5(Octstr *data)
Definition: md5.c:387
void octstr_append_data(Octstr *ostr, const char *data, long len)
Definition: octstr.c:1497
int parse_get_char(ParseContext *context)
Definition: parse.c:218
gw_assert(wtls_machine->packet_to_send !=NULL)
Octstr * radius_get_attribute(RADIUS_PDU *pdu, Octstr *attribute)
Definition: radius_pdu.c:498
Definition: parse.c:65
RADIUS_PDU * radius_pdu_unpack(Octstr *data_without_len)
Definition: radius_pdu.c:360
static void radius_attr_dump(RADIUS_PDU *pdu)
Definition: radius_pdu.c:459
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1504
union RADIUS_PDU::@72 u
Dict * attr
Definition: radius_pdu.h:93
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1517
int type
Definition: smsc_cimd2.c:215
void octstr_dump_short(Octstr *ostr, int level, const char *name)
Definition: octstr.c:2144
static Octstr * radius_type_convert(int type, Octstr *value)
Definition: radius_pdu.c:283
void octstr_append_cstr(Octstr *ostr, const char *cstr)
Definition: octstr.c:1511
static void radius_attr_unpack(ParseContext **context, RADIUS_PDU **pdu)
Definition: radius_pdu.c:314
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
#define octstr_copy(ostr, from, len)
Definition: octstr.h:178
void radius_pdu_destroy(RADIUS_PDU *pdu)
Definition: radius_pdu.c:156
int radius_authenticate_pdu(RADIUS_PDU *pdu, Octstr **data, Octstr *secret)
Definition: radius_pdu.c:416
void octstr_insert(Octstr *ostr1, const Octstr *ostr2, long pos)
Definition: octstr.c:1303
void * dict_get(Dict *dict, Octstr *key)
Definition: dict.c:286
void octstr_delete(Octstr *ostr1, long pos, long len)
Definition: octstr.c:1527
Octstr * parse_get_octets(ParseContext *context, long length)
Definition: parse.c:230
#define octstr_duplicate(ostr)
Definition: octstr.h:187
static void append_encoded_integer(Octstr *os, unsigned long u, long octets)
Definition: radius_pdu.c:91
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2464
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
#define octstr_create(cstr)
Definition: octstr.h:125
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
int parse_skip(ParseContext *context, long count)
Definition: parse.c:166
void parse_context_destroy(ParseContext *context)
Definition: parse.c:88
RADIUS_PDU * radius_pdu_create(int type, RADIUS_PDU *req)
Definition: radius_pdu.c:123
Definition: octstr.c:118
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
static unsigned long decode_integer(Octstr *os, long pos, int octets)
Definition: radius_pdu.c:76
void radius_pdu_dump(RADIUS_PDU *pdu)
Definition: radius_pdu.c:478
static Octstr * radius_attr_pack(RADIUS_PDU *pdu)
Definition: radius_pdu.c:210
#define panic
Definition: log.h:87
Octstr * radius_pdu_pack(RADIUS_PDU *pdu)
Definition: radius_pdu.c:237
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:406
ParseContext * parse_context_create(Octstr *str)
Definition: parse.c:74
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:871
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.