Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

radius_pdu.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * radius_pdu.c - parse and generate RADIUS Accounting PDUs
00059  *
00060  * Taken from gw/smsc/smpp_pdu.c writen by Lars Wirzenius.
00061  * This makes heavy use of C pre-processor macro magic.
00062  *
00063  * References: RFC2866 - RADIUS Accounting
00064  * 
00065  * Stipe Tolj <stolj@wapme.de>
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 static void *get_header_element(RADIUS_PDU *pdu, unsigned char *e) 
00102 {
00103     switch (pdu->type) {
00104     #define INTEGER(name, octets) \
00105     if (strcmp(#name, e) == 0) return (void*) *(&p->name);
00106     #define NULTERMINATED(name, max_octets)
00107     #define OCTETS(name, field_giving_octets) \
00108     if (strcmp(#name, e) == 0) return (void*) p->name;
00109     #define PDU(name, id, fields) \
00110         case id: { \
00111         struct name *p = &pdu->u.name; \
00112     } break;
00113     #include "radius_pdu.def"
00114     default:
00115         error(0, "Unknown RADIUS_PDU type, internal error.");
00116         gw_free(pdu);
00117        return NULL;
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 static void radius_type_append(Octstr **os, int type, int pmin, int pmax, 
00181                                Octstr *value) 
00182 {
00183     long l;
00184 
00185     switch (type) {
00186         case t_int:
00187             octstr_parse_long(&l, value, 0, 10);
00188             append_encoded_integer(*os, l, pmin);
00189             break;
00190         case t_string:
00191             octstr_append(*os, value);
00192             break;
00193         case t_ipaddr:
00194             ret = octstr_create("");
00195             for (i = 0; i < 4; i++) {
00196                 int c = octstr_get_char(value, i);
00197                 Octstr *b = octstr_format("%d", c);
00198                 octstr_append(ret, b);
00199                 i < 3 ? octstr_append_cstr(ret, ".") : NULL;
00200                 octstr_destroy(b);
00201             }
00202             break;
00203         default:
00204             panic(0, "RADIUS: Attribute type %d does not exist.", type);
00205             break;
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     switch (pdu->type) {
00248     #define INTEGER(name, octets) p = *(&p);
00249     #define NULTERMINATED(name, max_octets) p = *(&p);
00250     #define OCTETS(name, field_giving_octets) \
00251         p->field_giving_octets = octstr_len(p->name);
00252     #define PDU(name, id, fields) \
00253         case id: { struct name *p = &pdu->u.name; fields } break;
00254     #include "radius_pdu.def"
00255     default:
00256         error(0, "Unknown RADIUS_PDU type, internal error while packing.");
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     /* now set PDU length */
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     /* skipping back to context start for macro magic */
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     /* first extract attributes from raw data, where
00426      * the first 20 octets are code, idendifier, length
00427      * and authenticator value as described in RFC2866, sec. 3 */
00428     if (octstr_len(*data) > 20)
00429         attributes = octstr_copy(*data, 20, octstr_len(*data)-20);
00430   
00431     switch (pdu->type) {
00432         case 0x04:  /* Accounting-Request, see RFC2866, page 6 */
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:  /* Accounting-Response, create Response authenticator */
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.