00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069 #include <string.h>
00070 #include "radius_pdu.h"
00071
00072 #define MIN_RADIUS_PDU_LEN 20
00073 #define MAX_RADIUS_PDU_LEN 4095
00074
00075
00076 static unsigned long decode_integer(Octstr *os, long pos, int octets)
00077 {
00078 unsigned long u;
00079 int i;
00080
00081 gw_assert(octstr_len(os) >= pos + octets);
00082
00083 u = 0;
00084 for (i = 0; i < octets; ++i)
00085 u = (u << 8) | octstr_get_char(os, pos + i);
00086
00087 return u;
00088 }
00089
00090
00091 static void append_encoded_integer(Octstr *os, unsigned long u, long octets)
00092 {
00093 long i;
00094
00095 for (i = 0; i < octets; ++i)
00096 octstr_append_char(os, (u >> ((octets - i - 1) * 8)) & 0xFF);
00097 }
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123 RADIUS_PDU *radius_pdu_create(int type, RADIUS_PDU *req)
00124 {
00125 RADIUS_PDU *pdu;
00126
00127 pdu = gw_malloc(sizeof(*pdu));
00128 pdu->type = type;
00129
00130 switch (type) {
00131 #define INTEGER(name, octets) \
00132 if (strcmp(#name, "code") == 0) p->name = type; \
00133 else p->name = 0;
00134 #define OCTETS(name, field_giving_octets) p->name = NULL;
00135 #define PDU(name, id, fields) \
00136 case id: { \
00137 struct name *p = &pdu->u.name; \
00138 pdu->type_name = #name; \
00139 fields \
00140 } break;
00141 #include "radius_pdu.def"
00142 default:
00143 error(0, "Unknown RADIUS_PDU type, internal error.");
00144 gw_free(pdu);
00145 return NULL;
00146 }
00147 #define ATTR(attr, type, string, min, max)
00148 #define UNASSIGNED(attr)
00149 #define ATTRIBUTES(fields) \
00150 pdu->attr = dict_create(20, (void (*)(void *))octstr_destroy);
00151 #include "radius_attributes.def"
00152
00153 return pdu;
00154 }
00155
00156 void radius_pdu_destroy(RADIUS_PDU *pdu)
00157 {
00158 if (pdu == NULL)
00159 return;
00160
00161 switch (pdu->type) {
00162 #define INTEGER(name, octets) p->name = 0;
00163 #define OCTETS(name, field_giving_octets) octstr_destroy(p->name);
00164 #define PDU(name, id, fields) \
00165 case id: { struct name *p = &pdu->u.name; fields } break;
00166 #include "radius_pdu.def"
00167 default:
00168 error(0, "Unknown RADIUS_PDU type, internal error while destroying.");
00169 }
00170
00171 #define ATTR(attr, type, string, min, max)
00172 #define UNASSIGNED(attr)
00173 #define ATTRIBUTES(fields) dict_destroy(pdu->attr);
00174 #include "radius_attributes.def"
00175
00176 gw_free(pdu);
00177 }
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210 static Octstr *radius_attr_pack(RADIUS_PDU *pdu)
00211 {
00212 Octstr *os;
00213
00214 os = octstr_create("");
00215
00216 gw_assert(pdu != NULL);
00217
00218 #define ATTR(atype, type, string, pmin, pmax) \
00219 { \
00220 Octstr *attr_strg = octstr_create(string); \
00221 Octstr *attr_val = dict_get(p->attr, attr_str); \
00222 if (attr_str != NULL) { \
00223 int attr_len = octstr_len(attr_val) + 2; \
00224 octstr_format_append(os, "%02X", atype); \
00225 octstr_append_data(os, (char*) &attr_len, 2); \
00226 radius_type_append(&os, type, pmin, pmax, attr_val); \
00227 } \
00228 octstr_destroy(attr_str); \
00229 }
00230 #define UNASSIGNED(attr)
00231 #define ATTRIBUTES(fields)
00232 #include "radius_attributes.def"
00233
00234 return os;
00235 }
00236
00237 Octstr *radius_pdu_pack(RADIUS_PDU *pdu)
00238 {
00239 Octstr *os,*oos;
00240 Octstr *temp;
00241
00242 os = octstr_create("");
00243
00244 gw_assert(pdu != NULL);
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260 switch (pdu->type) {
00261 #define INTEGER(name, octets) \
00262 append_encoded_integer(os, p->name, octets);
00263 #define OCTETS(name, field_giving_octets) \
00264 octstr_append(os, p->name);
00265 #define PDU(name, id, fields) \
00266 case id: { struct name *p = &pdu->u.name; fields; oos = radius_attr_pack(pdu); \
00267 octstr_append(os, oos);octstr_destroy(oos); } break;
00268 #include "radius_pdu.def"
00269 default:
00270 error(0, "Unknown RADIUS_PDU type, internal error while packing.");
00271 }
00272
00273
00274 temp = octstr_create("");
00275 append_encoded_integer(temp, octstr_len(os), 2);
00276 octstr_delete(os, 2, 2);
00277 octstr_insert(os, temp, 2);
00278 octstr_destroy(temp);
00279
00280 return os;
00281 }
00282
00283 static Octstr *radius_type_convert(int type, Octstr *value)
00284 {
00285 Octstr *ret = NULL;
00286 int i;
00287
00288 switch (type) {
00289 case t_int:
00290 ret = octstr_format("%ld", decode_integer(value, 0, 4));
00291 break;
00292 case t_string:
00293 ret = octstr_format("%s", octstr_get_cstr(value));
00294 break;
00295 case t_ipaddr:
00296 ret = octstr_create("");
00297 for (i = 0; i < 4; i++) {
00298 int c = octstr_get_char(value, i);
00299 Octstr *b = octstr_format("%d", c);
00300 octstr_append(ret, b);
00301 if (i < 3)
00302 octstr_append_cstr(ret, ".");
00303 octstr_destroy(b);
00304 }
00305 break;
00306 default:
00307 panic(0, "RADIUS: Attribute type %d does not exist.", type);
00308 break;
00309 }
00310
00311 return ret;
00312 }
00313
00314 static void radius_attr_unpack(ParseContext **context, RADIUS_PDU **pdu)
00315 {
00316 #define ATTR(atype, type, string, pmin, pmax) \
00317 if (atype == attr_type) { \
00318 Octstr *tmp, *value; \
00319 if ((attr_len-2) < pmin || (attr_len-2) > pmax) { \
00320 error(0, "RADIUS: Attribute (%d) `%s' has invalid len %d, droppped.", \
00321 attr_type, string, (attr_len-2)); \
00322 continue; \
00323 } \
00324 attr_val = parse_get_octets(*context, attr_len - 2); \
00325 tmp = octstr_format("RADIUS: Attribute (%d) `%s', len %d", \
00326 attr_type, string, attr_len - 2); \
00327 value = radius_type_convert(type, attr_val); \
00328 octstr_destroy(attr_val); \
00329 octstr_dump_short(value, 0, octstr_get_cstr(tmp)); \
00330 octstr_destroy(tmp); \
00331 attr_str = octstr_create(string); \
00332 dict_put((*pdu)->attr, attr_str, value); \
00333 octstr_destroy(attr_str); \
00334 value = NULL; \
00335 } else
00336 #define UNASSIGNED(attr) \
00337 if (attr == attr_type) { \
00338 error(0, "RADIUS: Attribute (%d) is unassigned and should not be used.", \
00339 attr_type); \
00340 continue; \
00341 } else
00342 #define ATTRIBUTES(fields) \
00343 while (parse_octets_left(*context) > 0 && !parse_error(*context)) { \
00344 int attr_type, attr_len; \
00345 Octstr *attr_val = NULL; \
00346 Octstr *attr_str = NULL; \
00347 attr_type = parse_get_char(*context); \
00348 attr_len = parse_get_char(*context); \
00349 fields \
00350 { \
00351 debug("radius.unpack", 0, "RADIUS: Unknown attribute type (0x%03lx) " \
00352 "len %d in PDU `%s'.", \
00353 (long unsigned int)attr_type, attr_len, (*pdu)->type_name); \
00354 parse_skip(*context, attr_len - 2); \
00355 } \
00356 }
00357 #include "radius_attributes.def"
00358 }
00359
00360 RADIUS_PDU *radius_pdu_unpack(Octstr *data_without_len)
00361 {
00362 RADIUS_PDU *pdu;
00363 int type, ident;
00364 long len, pos;
00365 ParseContext *context;
00366 Octstr *authenticator;
00367
00368 len = octstr_len(data_without_len);
00369
00370 if (len < 20) {
00371 error(0, "RADIUS: PDU was too short (%ld bytes).",
00372 octstr_len(data_without_len));
00373 return NULL;
00374 }
00375
00376 context = parse_context_create(data_without_len);
00377
00378 type = parse_get_char(context);
00379 ident = parse_get_char(context);
00380 pdu = radius_pdu_create(type, NULL);
00381 if (pdu == NULL)
00382 return NULL;
00383
00384 len = decode_integer(data_without_len, 2, 2) - 19;
00385 parse_skip(context, 2);
00386 debug("radius", 0, "RADIUS: Attributes len is %ld", len);
00387
00388 authenticator = parse_get_octets(context, 16);
00389 octstr_dump_short(authenticator, 0, "RADIUS: Authenticator (md5) is:");
00390
00391
00392 parse_context_destroy(context);
00393 context = parse_context_create(data_without_len);
00394
00395 switch (type) {
00396 #define INTEGER(name, octets) \
00397 pos = octstr_len(data_without_len) - parse_octets_left(context); \
00398 p->name = decode_integer(data_without_len, pos, octets); \
00399 parse_skip(context, octets);
00400 #define OCTETS(name, field_giving_octets) \
00401 p->name = parse_get_octets(context, field_giving_octets);
00402 #define PDU(name, id, fields) \
00403 case id: { struct name *p = &pdu->u.name; fields; \
00404 radius_attr_unpack(&context, &pdu); } break;
00405 #include "radius_pdu.def"
00406 default:
00407 error(0, "Unknown RADIUS_PDU type, internal error while unpacking.");
00408 }
00409
00410 parse_context_destroy(context);
00411 octstr_destroy(authenticator);
00412
00413 return pdu;
00414 }
00415
00416 int radius_authenticate_pdu(RADIUS_PDU *pdu, Octstr **data, Octstr *secret)
00417 {
00418 int rc = 0;
00419 Octstr *stream;
00420 Octstr *attributes;
00421 Octstr *digest;
00422
00423 stream = attributes = digest = NULL;
00424
00425
00426
00427
00428 if (octstr_len(*data) > 20)
00429 attributes = octstr_copy(*data, 20, octstr_len(*data)-20);
00430
00431 switch (pdu->type) {
00432 case 0x04:
00433 stream = octstr_copy(*data, 0, 4);
00434 octstr_append_data(stream, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16);
00435 octstr_append(stream, attributes);
00436 octstr_append(stream, secret);
00437 digest = md5(stream);
00438 rc = octstr_compare(pdu->u.Accounting_Request.authenticator,
00439 digest) == 0 ? 1 : 0;
00440 break;
00441 case 0x05:
00442 stream = octstr_duplicate(*data);
00443 octstr_append(stream, secret);
00444 digest = md5(stream);
00445 octstr_delete(*data, 4, 16);
00446 octstr_insert(*data, digest, 4);
00447 break;
00448 default:
00449 break;
00450 }
00451
00452 octstr_destroy(attributes);
00453 octstr_destroy(stream);
00454 octstr_destroy(digest);
00455
00456 return rc;
00457 }
00458
00459 static void radius_attr_dump(RADIUS_PDU *pdu)
00460 {
00461 #define UNASSIGNED(attr)
00462 #define ATTR(atype, type, string, pmin, pmax) \
00463 id = atype; \
00464 key = octstr_create(string); \
00465 val = dict_get(pdu->attr, key); \
00466 if (val != NULL) \
00467 octstr_dump_short(val, 2, #atype); \
00468 octstr_destroy(key);
00469 #define ATTRIBUTES(fields) \
00470 if (pdu->attr != NULL) { \
00471 Octstr *key = NULL, *val = NULL; \
00472 int id; \
00473 fields \
00474 }
00475 #include "radius_attributes.def"
00476 }
00477
00478 void radius_pdu_dump(RADIUS_PDU *pdu)
00479 {
00480 debug("radius", 0, "RADIUS PDU %p dump:", (void *) pdu);
00481 debug("radius", 0, " type_name: %s", pdu->type_name);
00482 switch (pdu->type) {
00483 #define INTEGER(name, octets) \
00484 debug("radius", 0, " %s: %lu = 0x%08lx", #name, p->name, p->name);
00485 #define OCTETS(name, field_giving_octets) \
00486 octstr_dump_short(p->name, 2, #name);
00487 #define PDU(name, id, fields) \
00488 case id: { struct name *p = &pdu->u.name; fields; \
00489 radius_attr_dump(pdu); } break;
00490 #include "radius_pdu.def"
00491 default:
00492 error(0, "Unknown RADIUS_PDU type, internal error.");
00493 break;
00494 }
00495 debug("radius", 0, "RADIUS PDU dump ends.");
00496 }
00497
00498 Octstr *radius_get_attribute(RADIUS_PDU *pdu, Octstr *attribute)
00499 {
00500 gw_assert(pdu != NULL);
00501
00502 if (pdu->attr == NULL)
00503 return NULL;
00504
00505 return dict_get(pdu->attr, attribute);
00506 }
00507
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.