From 7c93421281d88b78b5423dbbb75ac1f933954788 Mon Sep 17 00:00:00 2001 From: cparker Date: Wed, 31 Oct 2001 17:01:13 +0000 Subject: [PATCH] Initial patch to allow support for RFC 2868 Tagged Attributes. This patch adds to the ATTR_FLAGS struct for attributes, and will read tag and vendor options properly from the dictionary. It also parses tags into the FLAGS struct on received packets. 'dict_addattr' has an additional option to pass the ATTR_FLAGS struct when reading the dictionary. --- src/include/libradius.h | 7 ++- src/include/radius.h | 2 - src/lib/dict.c | 120 +++++++++++++++++++++++++++++++++--------------- src/lib/print.c | 106 ++++++++++++++++++++---------------------- src/lib/radius.c | 78 +++++++++++++++++++++++++++---- 5 files changed, 208 insertions(+), 105 deletions(-) diff --git a/src/include/libradius.h b/src/include/libradius.h index 67cedd6..2a0571a 100644 --- a/src/include/libradius.h +++ b/src/include/libradius.h @@ -73,6 +73,8 @@ putchar('\n'); \ } \ } while(0) +# define TAG_VALID(x) ((x) > 0 && (x) < 0x20) +# define TAG_VALID_ZERO(x) ((x) >= 0 && (x) < 0x20) #endif typedef struct attr_flags { @@ -88,6 +90,7 @@ typedef struct dict_attr { int attr; int type; int vendor; + ATTR_FLAGS flags; struct dict_attr *next; } DICT_ATTR; @@ -111,9 +114,9 @@ typedef struct value_pair { int type; int length; /* of strvalue */ uint32_t lvalue; - ATTR_FLAGS flags; LRAD_TOKEN operator; uint8_t strvalue[MAX_STRING_LEN]; + ATTR_FLAGS flags; struct value_pair *next; } VALUE_PAIR; @@ -156,7 +159,7 @@ void vp_printlist(FILE *, VALUE_PAIR *); * Dictionary functions. */ int dict_addvendor(const char *name, int value); -int dict_addattr(const char *name, int vendor, int type, int value); +int dict_addattr(const char *name, int vendor, int type, int value, ATTR_FLAGS flags); int dict_addvalue(const char *namestr, char *attrstr, int value); int dict_init(const char *dir, const char *fn); DICT_ATTR *dict_attrbyvalue(int attr); diff --git a/src/include/radius.h b/src/include/radius.h index 11f6397..c8adacf 100644 --- a/src/include/radius.h +++ b/src/include/radius.h @@ -12,8 +12,6 @@ #define PW_TYPE_DATE 3 #define PW_TYPE_ABINARY 4 #define PW_TYPE_OCTETS 5 -#define PW_TYPE_T_STRING 6 -#define PW_TYPE_T_INTEGER 7 #define PW_AUTHENTICATION_REQUEST 1 #define PW_AUTHENTICATION_ACK 2 diff --git a/src/lib/dict.c b/src/lib/dict.c index 5e0e0ba..7bf431b 100644 --- a/src/lib/dict.c +++ b/src/lib/dict.c @@ -31,8 +31,6 @@ static const char *dtypes[] = { "date", "abinary", "octets", - "t_string", - "t_integer", NULL, }; @@ -112,7 +110,7 @@ int dict_addvendor(const char *name, int value) return 0; } -int dict_addattr(const char *name, int vendor, int type, int value) +int dict_addattr(const char *name, int vendor, int type, int value, ATTR_FLAGS flags) { static int max_attr = 0; DICT_ATTR *attr; @@ -156,6 +154,8 @@ int dict_addattr(const char *name, int vendor, int type, int value) strcpy(attr->name, name); attr->attr = value; attr->type = type; + attr->flags = flags; + if (vendor) { attr->attr |= (vendor << 16); } else if ((attr->attr >= 0) && (attr->attr < 256)) { @@ -220,8 +220,8 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i char valstr[256]; char attrstr[256]; char typestr[256]; - char vendorstr[256]; - char *p; + char optstr[256]; + char *p, *s, *c; char *keyword; char *data; int line = 0; @@ -232,6 +232,7 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i int is_attrib; int vendor_usr_seen = 0; int is_nmc; + ATTR_FLAGS flags; if (strlen(fn) >= sizeof(dirtmp) / 2 || strlen(dir) >= sizeof(dirtmp) / 2) { @@ -314,9 +315,9 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i if (is_attrib) { vendor = 0; - vendorstr[0] = 0; + optstr[0] = 0; if(sscanf(data, "%s%s%s%s", namestr, - valstr, typestr, vendorstr) < 3) { + valstr, typestr, optstr) < 3) { librad_log( "dict_init: %s[%d]: invalid ATTRIBUTE line", fn, line); @@ -328,7 +329,7 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i * We might need to add USR to the list of * vendors first. */ - if (is_nmc && vendorstr[0] == 0) { + if (is_nmc && optstr[0] == 0) { if (!vendor_usr_seen) { if (dict_addvendor("USR", VENDORPEC_USR) < 0) { librad_log("dict_init: %s[%d]: %s", fn, line, librad_errstr); @@ -336,7 +337,7 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i } vendor_usr_seen = 1; } - strcpy(vendorstr, "USR"); + strcpy(optstr, "USR"); } /* @@ -368,35 +369,80 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i /* * Ignore comments */ - if (vendorstr[0] == '#') { - vendorstr[0] = '\0'; + if (optstr[0] == '#') { + optstr[0] = '\0'; } /* * Only look up the vendor if the string * is non-empty. */ - if (vendorstr[0]) { - vendor = dict_vendorname(vendorstr); - if (!vendor) { - librad_log( - "dict_init: %s[%d]: unknown vendor %s", - fn, line, vendorstr); - return -1; - } - } - if (block_vendor && vendorstr[0] && - (block_vendor != vendor)) { - librad_log( - "dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block", - fn, line, vendorstr); - return -1; + memset(&flags, 0, sizeof(flags)); + s = strtok(optstr, ","); + while(s) { + if (strcmp(s, "has_tag") == 0 || + strcmp(s, "has_tag=1") == 0) { + /* Boolean flag, means this is a + tagged attribute */ + flags.has_tag = 1; + } + else if (strncmp(s, "len+=", 5) == 0 || + strncmp(s, "len-=", 5) == 0) { + /* Length difference, to accomodate + braindead NASes & their vendors */ + flags.len_disp = strtol(s + 5, &c, 0); + if (*c) { + librad_log( + "dict_init: %s[%d] invalid option %s", + fn, line, s); + return -1; + } + if (s[3] == '-') { + flags.len_disp = -flags.len_disp; + } + } + else if (strncmp(s, "encrypt=", 8) == 0) { + /* Encryption method, defaults to 0 (none). + Currently valid is just type 1, + Tunnel-Password style, which can only + be applied to strings. */ + flags.encrypt = strtol(s + 8, &c, 0); + if (*c) { + librad_log( + "dict_init: %s[%d] invalid option %s", + fn, line, s); + return -1; + } + } + else { + /* Must be a vendor 'flag'... */ + if (strncmp(s, "vendor=", 5) == 0) { + /* New format */ + s += 5; + } + + vendor = dict_vendorname(s); + if (!vendor) { + librad_log( + "dict_init: %s[%d]: unknown vendor %s", + fn, line, optstr); + return -1; + } + if (block_vendor && optstr[0] && + (block_vendor != vendor)) { + librad_log( + "dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block", + fn, line, optstr); + return -1; + } + } + s = strtok(NULL, ","); } - + if (block_vendor) vendor = block_vendor; - if (dict_addattr(namestr, vendor, type, value) < 0) { + if (dict_addattr(namestr, vendor, type, value, flags) < 0) { librad_log("dict_init: %s[%d]: %s", fn, line, librad_errstr); return -1; @@ -490,19 +536,19 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i } if (DICT_STRCMP(keyword, "BEGIN-VENDOR") == 0) { - vendorstr[0] = 0; - if (sscanf(data, "%s", vendorstr) != 1) { + optstr[0] = 0; + if (sscanf(data, "%s", optstr) != 1) { librad_log( "dict_init: %s[%d] invalid BEGIN-VENDOR entry", fn, line); return -1; } - vendor = dict_vendorname(vendorstr); + vendor = dict_vendorname(optstr); if (!vendor) { librad_log( "dict_init: %s[%d]: unknown vendor %s", - fn, line, vendorstr); + fn, line, optstr); return -1; } block_vendor = vendor; @@ -510,26 +556,26 @@ static int my_dict_init(const char *dir, const char *fn, const char *src_file, i } if (DICT_STRCMP(keyword, "END-VENDOR") == 0) { - vendorstr[0] = 0; - if (sscanf(data, "%s", vendorstr) != 1) { + optstr[0] = 0; + if (sscanf(data, "%s", optstr) != 1) { librad_log( "dict_init: %s[%d] invalid END-VENDOR entry", fn, line); return -1; } - vendor = dict_vendorname(vendorstr); + vendor = dict_vendorname(optstr); if (!vendor) { librad_log( "dict_init: %s[%d]: unknown vendor %s", - fn, line, vendorstr); + fn, line, optstr); return -1; } if (vendor != block_vendor) { librad_log( "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR", - fn, line, vendorstr); + fn, line, optstr); return -1; } block_vendor = 0; diff --git a/src/lib/print.c b/src/lib/print.c index 5c12bcd..dfa2c04 100644 --- a/src/lib/print.c +++ b/src/lib/print.c @@ -88,7 +88,6 @@ int vp_prints_value(char * out, int outlen, VALUE_PAIR *vp, int delimitst) char buf[1024]; char *a; time_t t; - int offset; out[0] = 0; if (!vp) return 0; @@ -98,26 +97,45 @@ int vp_prints_value(char * out, int outlen, VALUE_PAIR *vp, int delimitst) if (vp->attribute == PW_NAS_PORT_ID) a = (char *)vp->strvalue; else { - if (delimitst) { - buf[0] = '"'; - librad_safeprint((char *)vp->strvalue, - vp->length, buf + 1, sizeof(buf) - 2); - strcat(buf, "\""); + if (delimitst && vp->flags.has_tag) { + /* Tagged attribute: print delimter and ignore tag */ + buf[0] = '"'; + librad_safeprint((char *)(vp->strvalue), + vp->length, buf + 1, sizeof(buf) - 2); + strcat(buf, "\""); + } else if (delimitst) { + /* Non-tagged attribute: print delimter */ + buf[0] = '"'; + librad_safeprint((char *)vp->strvalue, + vp->length, buf + 1, sizeof(buf) - 2); + strcat(buf, "\""); } else { - librad_safeprint((char *)vp->strvalue, - vp->length, buf, sizeof(buf)); + /* Non-tagged attribute: no delimiter */ + librad_safeprint((char *)vp->strvalue, + vp->length, buf, sizeof(buf)); } - a = buf; } break; case PW_TYPE_INTEGER: - if ((v = dict_valbyattr(vp->attribute, vp->lvalue)) - != NULL) - a = v->name; - else { - snprintf(buf, sizeof(buf), "%u", vp->lvalue); - a = buf; + if ( vp->flags.has_tag ) { + /* Attribute value has a tag, need to ignore it */ + if ((v = dict_valbyattr(vp->attribute, (vp->lvalue & 0xffffff))) + != NULL) + a = v->name; + else { + snprintf(buf, sizeof(buf), "%u", (vp->lvalue & 0xffffff)); + a = buf; + } + } else { + /* Normal, non-tagged attribute */ + if ((v = dict_valbyattr(vp->attribute, vp->lvalue)) + != NULL) + a = v->name; + else { + snprintf(buf, sizeof(buf), "%u", vp->lvalue); + a = buf; + } } break; case PW_TYPE_DATE: @@ -155,43 +173,6 @@ int vp_prints_value(char * out, int outlen, VALUE_PAIR *vp, int delimitst) a = buf; break; - case PW_TYPE_T_STRING: - if (delimitst) { - offset = snprintf(buf, sizeof(buf), - "\"%u:", vp->strvalue[0]); - librad_safeprint((char *)(vp->strvalue + 1), - vp->length - 1, - buf + offset, - sizeof(buf) - offset); - strcat(buf, "\""); - } else { - offset = snprintf(buf, sizeof(buf), - "%u:", vp->strvalue[0]); - librad_safeprint((char *)(vp->strvalue + 1), - vp->length - 1, - buf + offset, - sizeof(buf) - offset); - } - - a = buf; - - break; - - case PW_TYPE_T_INTEGER: - offset = snprintf(buf, sizeof(buf), "%u:", (vp->lvalue >> 24)); - if ((v = dict_valbyattr(vp->attribute, (vp->lvalue))) - != NULL) - snprintf(buf + offset, sizeof(buf) - offset, - "%s", v->name); - else { - snprintf(buf + offset, sizeof(buf) - offset, - "%u", vp->lvalue); - - } - - a = buf; - - break; default: a = 0; break; @@ -216,9 +197,24 @@ int vp_prints(char *out, int outlen, VALUE_PAIR *vp) return 0; } - snprintf(out, outlen, "%s = ", vp->name); - len = strlen(out); - vp_prints_value(out + len, outlen - len, vp, 1); + if( vp->flags.has_tag ) { + +#ifdef MERIT_STYLE_TAGS + snprintf(out, outlen, "%s = :%d:", vp->name, vp->flags.tag); +#else + snprintf(out, outlen, "%s:%d = ", vp->name, vp->flags.tag); +#endif + + len = strlen(out); + vp_prints_value(out + len, outlen - len, vp, 1); + + } else { + + snprintf(out, outlen, "%s = ", vp->name); + len = strlen(out); + vp_prints_value(out + len, outlen - len, vp, 1); + + } return strlen(out); } diff --git a/src/lib/radius.c b/src/lib/radius.c index 36cc103..08bfdb4 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -292,8 +292,27 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *s memcpy(reply->strvalue, digest, AUTH_VECTOR_LEN ); reply->length = AUTH_VECTOR_LEN; } + + /* + * TODO: This should encrypt attributes like Tunnel-Password + * here -- cparker@starnetusa.net + */ len = reply->length; - + + /* + * Set the TAG at the beginning of the string if tagged. + * If tag value is not valid for tagged attribute, make + * it 0x00 per RFC 2868. -cparker + */ + if(reply->flags.has_tag) { + len++; + if(TAG_VALID(reply->flags.tag)) { + *ptr++ = reply->flags.tag; + } else { + *ptr++ = 0x00; + } + } + /* * Ensure we don't go too far. * The 'length' of the attribute @@ -314,8 +333,14 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *s *length_ptr += len; if (vsa_length_ptr) *vsa_length_ptr += len; - memcpy(ptr, reply->strvalue,len); - ptr += len; + /* + * If we have tagged attributes we can't assume that + * len == reply->length. Use reply->length for copying + * the string data into the packet. Use len for the + * true length of the string+tags. + */ + memcpy(ptr, reply->strvalue, reply->length); + ptr += reply->length; total_length += len; break; @@ -323,10 +348,21 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *s case PW_TYPE_IPADDR: *length_ptr += 4; if (vsa_length_ptr) *vsa_length_ptr += 4; - if (reply->type != PW_TYPE_IPADDR) + + if (reply->type == PW_TYPE_INTEGER ) { + /* If tagged, the tag becomes the MSB of the value */ + if(reply->flags.has_tag) { + /* Tag must be ( 0x01 -> 0x1F ) OR 0x00 */ + if(!TAG_VALID(reply->flags.tag)) { + reply->flags.tag = 0x00; + } + lvalue = htonl((reply->lvalue & 0xffffff) | + ((reply->flags.tag & 0xff) << 24)); + } lvalue = htonl(reply->lvalue); - else + } else { lvalue = reply->lvalue; + } memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 4; @@ -977,6 +1013,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secre } else { strcpy(pair->name, attr->name); pair->type = attr->type; + pair->flags = attr->flags; } pair->attribute = attribute; pair->length = attrlen; @@ -987,7 +1024,6 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secre case PW_TYPE_OCTETS: case PW_TYPE_ABINARY: case PW_TYPE_STRING: - case PW_TYPE_T_STRING: /* * Hmm... this is based on names right * now. We really shouldn't do this. @@ -1002,15 +1038,28 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secre memcpy(pair->strvalue, my_digest, AUTH_VECTOR_LEN ); pair->strvalue[AUTH_VECTOR_LEN] = '\0'; pair->length = strlen(pair->strvalue); - } else + } else if (pair->flags.has_tag && + pair->type == PW_TYPE_STRING) { + if(TAG_VALID(*ptr)) + pair->flags.tag = *ptr; + else + pair->flags.tag = 0x00; + pair->length--; + memcpy(pair->strvalue, ptr + 1, + pair->length); + } else { /* attrlen always < MAX_STRING_LEN */ memcpy(pair->strvalue, ptr, attrlen); + pair->flags.tag = 0; + } + + /* FIXME: Decrypt attributes like Tunnel-Password here -cparker */ + break; case PW_TYPE_INTEGER: case PW_TYPE_DATE: case PW_TYPE_IPADDR: - case PW_TYPE_T_INTEGER: /* * Check for RFC compliance. If the * attribute isn't compliant, turn it @@ -1025,11 +1074,22 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secre pair->lvalue = 0xbaddbadd; break; } - memcpy(&lvalue, ptr, 4); + + memcpy(&lvalue, ptr, 4); + if (attr->type != PW_TYPE_IPADDR) pair->lvalue = ntohl(lvalue); else pair->lvalue = lvalue; + + /* + * Only PW_TYPE_INTEGER should have tags. + */ + if (pair->flags.has_tag && + pair->type == PW_TYPE_INTEGER) { + pair->flags.tag = (pair->lvalue >> 24) & 0xff; + pair->lvalue &= 0xffffff; + } break; default: -- 2.1.4