Initial patch to allow support for RFC 2868 Tagged Attributes.
authorcparker <cparker>
Wed, 31 Oct 2001 17:01:13 +0000 (17:01 +0000)
committercparker <cparker>
Wed, 31 Oct 2001 17:01:13 +0000 (17:01 +0000)
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
src/include/radius.h
src/lib/dict.c
src/lib/print.c
src/lib/radius.c

index 67cedd6..2a0571a 100644 (file)
@@ -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);
index 11f6397..c8adacf 100644 (file)
@@ -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
index 5e0e0ba..7bf431b 100644 (file)
@@ -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;
index 5c12bcd..dfa2c04 100644 (file)
@@ -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);
 }
index 36cc103..08bfdb4 100644 (file)
@@ -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: