]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow DEFINE, which is like ATTRIBUTE, but with no numbers
authorAlan T. DeKok <aland@freeradius.org>
Wed, 15 Feb 2023 19:11:50 +0000 (14:11 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 17 Feb 2023 02:54:18 +0000 (21:54 -0500)
src/lib/util/dict_tokenize.c

index b4d8193d3dadeb0851bb7c25bd2fa733bd84db64..f15337bc77f09bfedb97236f965a6c25dd088723 100644 (file)
@@ -720,6 +720,125 @@ static int dict_read_process_alias(dict_tokenize_ctx_t *ctx, char **argv, int ar
        return 0;
 }
 
+/*
+ *     Process references.
+ */
+static int dict_process_ref(dict_tokenize_ctx_t *ctx, fr_dict_attr_t const *parent, fr_dict_attr_t const *da, char *ref)
+{
+       /*
+        *      Groups can refer to other dictionaries, or other attributes.
+        */
+       if (da->type == FR_TYPE_GROUP) {
+               fr_dict_attr_t          *self;
+               fr_dict_t               *dict;
+               char                    *p;
+               ssize_t                 slen;
+
+               memcpy(&self, &da, sizeof(self)); /* const issues */
+
+               /*
+                *      No qualifiers, just point it to the root of the current dictionary.
+                */
+               if (!ref) {
+                       dict = ctx->dict;
+                       da = ctx->dict->root;
+                       goto check;
+               }
+
+               /*
+                *      Else we find the reference.
+                */
+               da = fr_dict_attr_by_oid(NULL, parent, ref);
+               if (da) {
+                       dict = ctx->dict;
+                       goto check;
+               }
+
+               /*
+                *      The attribute doesn't exist, and the reference
+                *      is FOO, it might be just a ref to a
+                *      dictionary.
+                */
+               p = strchr(ref, '.');
+               if (!p) goto fixup;
+
+               /*
+                *      Get / skip protocol name.
+                */
+               slen = dict_by_protocol_substr(NULL, &dict, &FR_SBUFF_IN(ref, strlen(ref)), ctx->dict);
+               if (slen < 0) {
+                       talloc_free(ref);
+                       return -1;
+               }
+
+               /*
+                *      No known dictionary, so we're asked to just
+                *      use the whole string.  Which we did above.  So
+                *      either it's a bad ref, OR it's a ref to a
+                *      dictionary which hasn't yet been loaded.
+                *
+                *      Save the fixup for later, when we've hopefully
+                *      loaded the dictionary.
+                */
+               if (slen == 0) {
+               fixup:
+                       if (dict_fixup_group(&ctx->fixup, CURRENT_FRAME(ctx)->filename, CURRENT_FRAME(ctx)->line,
+                                            self, ref, talloc_array_length(ref) - 1) < 0) {
+                       oom:
+                               talloc_free(ref);
+                               return -1;
+                       }
+                       talloc_free(ref);
+
+                       return 0;
+
+               } else if (ref[slen] == '\0') {
+                       da = dict->root;
+                       goto check;
+
+               } else {
+                       /*
+                        *      Look up the attribute.
+                        */
+                       da = fr_dict_attr_by_oid(NULL, parent, ref + slen);
+                       if (!da) {
+                               fr_strerror_printf("protocol loaded, but no attribute '%s'", ref + slen);
+                               talloc_free(ref);
+                               return -1;
+                       }
+
+               check:
+                       talloc_free(ref);
+
+                       if (da->type != FR_TYPE_TLV) {
+                               fr_strerror_const("References MUST be to an ATTRIBUTE of type 'tlv'");
+                               return -1;
+                       }
+
+                       if (fr_dict_attr_ref(da)) {
+                               fr_strerror_const("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...'");
+                               return -1;
+                       }
+
+                       self->dict = dict;
+
+                       dict_attr_ref_set(self, da);
+               }
+       }
+
+       /*
+        *      It's a "clone" thing.
+        */
+       if (ref) {
+               if (dict_fixup_clone(&ctx->fixup, CURRENT_FRAME(ctx)->filename, CURRENT_FRAME(ctx)->line,
+                                    fr_dict_attr_unconst(parent), fr_dict_attr_unconst(da),
+                                    ref, talloc_array_length(ref) - 1) < 0) goto oom;
+               talloc_free(ref);
+       }
+
+       return 0;
+}
+
 /*
  *     Process the ATTRIBUTE command
  */
@@ -869,132 +988,123 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in
 
        if (set_relative_attr) ctx->relative_attr = da;
 
+       if (dict_process_ref(ctx, parent, da, ref) < 0) return -1;
+
        /*
-        *      Groups can refer to other dictionaries, or other attributes.
+        *      Adding an attribute of type 'struct' is an implicit
+        *      BEGIN-STRUCT.
         */
-       if (type == FR_TYPE_GROUP) {
-               fr_dict_attr_t          *self;
-               fr_dict_t               *dict;
-               char                    *p;
-
-               memcpy(&self, &da, sizeof(self)); /* const issues */
+       if (type == FR_TYPE_STRUCT) {
+               if (dict_gctx_push(ctx, da) < 0) return -1;
+               ctx->value_attr = NULL;
+       } else {
+               memcpy(&ctx->value_attr, &da, sizeof(da));
+       }
 
-               /*
-                *      No qualifiers, just point it to the root of the current dictionary.
-                */
-               if (!ref) {
-                       dict = ctx->dict;
-                       da = ctx->dict->root;
-                       goto check;
-               }
+       return 0;
+}
 
-               /*
-                *      Else we find the reference.
-                */
-               da = fr_dict_attr_by_oid(NULL, parent, ref);
-               if (da) {
-                       dict = ctx->dict;
-                       goto check;
-               }
 
-               /*
-                *      The attribute doesn't exist, and the reference
-                *      is FOO, it might be just a ref to a
-                *      dictionary.
-                */
-               p = strchr(ref, '.');
-               if (!p) goto fixup;
+/*
+ *     Process the DEFINE command
+ *
+ *     Which is mostly like ATTRIBUTE, but does not have a number.
+ */
+static int dict_read_process_define(dict_tokenize_ctx_t *ctx, char **argv, int argc,
+                                   fr_dict_attr_flags_t *base_flags)
+{
+       fr_type_t               type;
+       fr_dict_attr_flags_t    flags;
+       fr_dict_attr_t const    *parent, *da;
+       char                    *ref = NULL;
 
-               /*
-                *      Get / skip protocol name.
-                */
-               slen = dict_by_protocol_substr(NULL, &dict, &FR_SBUFF_IN(ref, strlen(ref)), ctx->dict);
-               if (slen < 0) {
-                       talloc_free(ref);
-                       return -1;
-               }
+       if ((argc < 2) || (argc > 3)) {
+               fr_strerror_const("Invalid DEFINE syntax");
+               return -1;
+       }
 
-               /*
-                *      No known dictionary, so we're asked to just
-                *      use the whole string.  Which we did above.  So
-                *      either it's a bad ref, OR it's a ref to a
-                *      dictionary which hasn't yet been loaded.
-                *
-                *      Save the fixup for later, when we've hopefully
-                *      loaded the dictionary.
-                */
-               if (slen == 0) {
-               fixup:
-                       if (dict_fixup_group(&ctx->fixup, CURRENT_FRAME(ctx)->filename, CURRENT_FRAME(ctx)->line,
-                                            self, ref, talloc_array_length(ref) - 1) < 0) {
-                       oom:
-                               talloc_free(ref);
-                               return -1;
-                       }
-                       talloc_free(ref);
+       /*
+        *      Dictionaries need to have real names, not shitty ones.
+        */
+       if (strncmp(argv[0], "Attr-", 5) == 0) {
+               fr_strerror_const("Invalid DEFINE name");
+               return -1;
+       }
 
-                       return 0;
+       memcpy(&flags, base_flags, sizeof(flags));
 
-               } else if (ref[slen] == '\0') {
-                       da = dict->root;
-                       goto check;
+       if (dict_process_type_field(ctx, argv[1], &type, &flags) < 0) return -1;
 
-               } else {
-                       /*
-                        *      Look up the attribute.
-                        */
-                       da = fr_dict_attr_by_oid(NULL, parent, ref + slen);
-                       if (!da) {
-                               fr_strerror_printf("protocol loaded, but no attribute '%s'", ref + slen);
-                               talloc_free(ref);
-                               return -1;
-                       }
+       /*
+        *      We would like to add DEFINE for TLVs, but it's currently hard to
+        *      define children, and (for now) we avoid BEGIN-TLV.
+        *
+        *      In order to support BEGIN-TLV, we need to support
+        *      MEMBER-like allocation of TLV children.
+        */
+       switch (type) {
+       case FR_TYPE_STRUCT:
+       case FR_TYPE_TLV:
+       case FR_TYPE_VSA:
+       case FR_TYPE_VENDOR:
+       case FR_TYPE_VOID:
+       case FR_TYPE_VALUE_BOX:
+               fr_strerror_printf("DEFINE cannot be used for type '%s'", argv[1]);
+               return -1;
 
-               check:
-                       talloc_free(ref);
+       default:
+               break;
+       }
 
-                       if (da->type != FR_TYPE_TLV) {
-                               fr_strerror_const("References MUST be to an ATTRIBUTE of type 'tlv'");
-                               return -1;
-                       }
+       if (flags.extra && (flags.subtype == FLAG_BIT_FIELD)) {
+               fr_strerror_const("Bit fields can only be defined as a MEMBER of a STRUCT");
+               return -1;
+       }
 
-                       if (fr_dict_attr_ref(da)) {
-                               fr_strerror_const("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...'");
-                               return -1;
-                       }
+       parent = dict_gctx_unwind(ctx);
 
-                       self->dict = dict;
+       if (!fr_cond_assert(parent)) return -1; /* Should have provided us with a parent */
 
-                       dict_attr_ref_set(self, da);
-               }
+       /*
+        *      Members of a 'struct' MUST use MEMBER, not ATTRIBUTE.
+        */
+       if (parent->type == FR_TYPE_STRUCT) {
+               fr_strerror_printf("Member %s of parent %s type 'struct' MUST use the \"MEMBER\" keyword",
+                                  argv[0], parent->name);
+               return -1;
        }
 
        /*
-        *      It's a "clone" thing.
+        *      Parse options.
         */
-       if (ref) {
-               if (dict_fixup_clone(&ctx->fixup, CURRENT_FRAME(ctx)->filename, CURRENT_FRAME(ctx)->line,
-                                    fr_dict_attr_unconst(parent), fr_dict_attr_unconst(da),
-                                    ref, talloc_array_length(ref) - 1) < 0) goto oom;
-               talloc_free(ref);
-               return 0;
+       if (argc >= 3) {
+               if (dict_process_flag_field(ctx, argv[2], type, &flags, &ref) < 0) return -1;
+       } else {
+               if (!dict_attr_flags_valid(ctx->dict, parent, argv[2], NULL, type, &flags)) return -1;
        }
 
+#ifdef STATIC_ANALYZER
+       if (!ctx->dict) return -1;
+#endif
+
        /*
-        *      Adding an attribute of type 'struct' is an implicit
-        *      BEGIN-STRUCT.
+        *      Add in an attribute
         */
-       if (type == FR_TYPE_STRUCT) {
-               if (dict_gctx_push(ctx, da) < 0) return -1;
-               ctx->value_attr = NULL;
-       } else {
-               memcpy(&ctx->value_attr, &da, sizeof(da));
+       if (fr_dict_attr_add(ctx->dict, parent, argv[0], -1, type, &flags) < 0) return -1;
+
+       da = dict_attr_by_name(NULL, parent, argv[0]);
+       if (!da) {
+               fr_strerror_printf("Failed to find attribute '%s' we just added.", argv[0]);
+               return -1;
        }
 
+       if (dict_process_ref(ctx, parent, da, ref) < 0) return -1;
+
+       memcpy(&ctx->value_attr, &da, sizeof(da));
+
        return 0;
 }
 
-
 /*
  *     Process the ENUM command
  */
@@ -2051,6 +2161,16 @@ static int _dict_from_file(dict_tokenize_ctx_t *ctx,
                        continue;
                }
 
+               /*
+                *      Perhaps this is an attribute.
+                */
+               if (strcasecmp(argv[0], "DEFINE") == 0) {
+                       if (dict_read_process_define(ctx,
+                                                    argv + 1, argc - 1,
+                                                    &base_flags) == -1) goto error;
+                       continue;
+               }
+
                /*
                 *      Perhaps this is an enum.
                 */