]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Make foreign dictionary references work.
authorAlan T. DeKok <aland@freeradius.org>
Thu, 25 Jan 2024 22:08:33 +0000 (17:08 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 25 Jan 2024 23:09:36 +0000 (18:09 -0500)
src/lib/util/dict_fixup.c
src/lib/util/dict_tokenize.c

index 5224d520c18fa8d7464ede91ee2a0bf3f5f2ea4e..03bff81355f48dc04002d05e42ff4bdf8ac51bb4 100644 (file)
@@ -229,86 +229,63 @@ static fr_dict_attr_t const *dict_find_or_load_reference(fr_dict_t **dict_def, c
 {
        fr_dict_t               *dict;
        fr_dict_attr_t const    *da;
-       char                    *p;
+       char const              *name;
        ssize_t                 slen;
 
-       da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_def), ref);
-       if (da) return da;
-
        /*
-        *      The attribute doesn't exist, and the reference
-        *      isn't in a "PROTO.ATTR" format, die.
+        *      Ref to attribute in existing dictionary.  The dictionary MUST be loaded by $INCLUDEs.
         */
-       p = strchr(ref, '.');
+       if (ref[0] != '.') {
+               da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_def), ref);
+               if (da) return da;
+
+               fr_strerror_printf("Invalid attribute reference '%s' at %s[%d]", ref,
+                                  fr_cwd_strip(filename), line);
+               return NULL;
+       }
+
+       name = ref + 2;         /* already checked when we insert it */
 
        /*
-        *      Get / skip protocol name.
+        *      Reference to foreign protocol.  Get the protocol name.
         */
-       slen = dict_by_protocol_substr(NULL,
-                                      &dict, &FR_SBUFF_IN(ref, strlen(ref)),
-                                      *dict_def);
+       slen = dict_by_protocol_substr(NULL, &dict, &FR_SBUFF_IN(name, strlen(name)), NULL);
        if (slen <= 0) {
-               fr_dict_t *other;
+               char *p;
 
+               p = strchr(name, '.');
                if (p) *p = '\0';
 
-               /*
-                *      Can't load the dictionary we're loading.
-                */
-               if (dict == *dict_def) {
-                       fr_strerror_printf("Cannot reference parent dictionary %s from within the same dictionary", fr_dict_root(dict)->name);
-                       return NULL;
-               }
-
-               if (fr_dict_protocol_afrom_file(&other, ref, NULL, filename) < 0) {
+               if (fr_dict_protocol_afrom_file(&dict, name, NULL, filename) < 0) {
+                       fr_strerror_printf("Unknown protocol '%s' at %s[%d]", name,
+                                          fr_cwd_strip(filename), line);
                        return NULL;
                }
 
-               if (p) *p = '.';
-
                /*
-                *      Grab the protocol name again
+                *      The reference is to the root of the foreign protocol, we're done.
                 */
-               dict = other;
                if (!p) {
-                       *dict_def = other;
-                       return other->root;
+                       *dict_def = dict;
+                       return dict->root;
                }
 
-               slen = p - ref;
-       }
-
-       if (slen < 0) {
-       invalid_reference:
-               fr_strerror_printf("Invalid attribute reference '%s' at %s[%d]",
-                                  ref,
-                                  fr_cwd_strip(filename), line);
-               return NULL;
-       }
-
-       /*
-        *      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 doesn't exist.
-        */
-       if (slen == 0) goto invalid_reference;
-
-       /*
-        *      Just a bare reference to the protocol.  Use the root.
-        */
-       if (!ref[slen]) {
-               *dict_def = dict;
-               return fr_dict_root(dict);
+               name = p + 1;
+       } else {
+               /*
+                *      The foreign dictionary was loaded by someone
+                *      else, try to resolve the attribute.
+                */
+               name += slen + 1;
        }
 
        /*
         *      Look up the attribute.
         */
-       da = fr_dict_attr_by_oid(NULL, fr_dict_root(dict), ref + slen + 1);
+       da = fr_dict_attr_by_oid(NULL, fr_dict_root(dict), name);
        if (!da) {
-               fr_strerror_printf("No such attribute '%s' in reference at %s[%d]",
-                                  ref + slen + 1, fr_cwd_strip(filename), line);
+               fr_strerror_printf("No such attribute '%s' in protocol '%s' at %s[%d]",
+                                  name, dict->root->name, fr_cwd_strip(filename), line);
                return NULL;
        }
 
index 965d74bd87646cfa9e1bd916da9655191b4d2080..779089c050cf0c98f677286bf066f9e73fee5876 100644 (file)
@@ -779,118 +779,118 @@ static int dict_read_process_alias(dict_tokenize_ctx_t *ctx, char **argv, int ar
  */
 static int dict_process_ref(dict_tokenize_ctx_t *ctx, fr_dict_attr_t const *parent, fr_dict_attr_t const *da, char *ref)
 {
+       fr_dict_t               *dict;
+       ssize_t                 slen;
+       char const              *name;
+
        /*
-        *      Groups can refer to other dictionaries, or other attributes.
+        *      It's a "clone" thing.
         */
-       if (da->type == FR_TYPE_GROUP) {
+       if (da->type != FR_TYPE_GROUP) {
+               if (!ref) return 0;
+
+               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) {
+               fail:
+                       talloc_free(ref);
+                       return -1;
+               }
+
+               talloc_free(ref);
+               return 0;
+       }
+
+       /*
+        *      No reference, just point it to the root of the current dictionary.
+        */
+       if (!ref) {
                fr_dict_attr_t          *self;
-               fr_dict_t               *dict;
-               char                    *p;
-               ssize_t                 slen;
 
-               memcpy(&self, &da, sizeof(self)); /* const issues */
+               dict = ctx->dict;
+               da = ctx->dict->root;
 
-               /*
-                *      No qualifiers, just point it to the root of the current dictionary.
-                */
-               if (!ref) {
-                       dict = ctx->dict;
-                       da = ctx->dict->root;
-                       goto check;
-               }
+set:
+               talloc_free(ref);
 
-               /*
-                *      Else we find the reference.
-                */
+               self = UNCONST(fr_dict_attr_t *, da);
+               self->dict = dict;
+
+               dict_attr_ref_set(self, da);    
+               return 0;
+       }
+
+       /*
+        *      Else refs refer to attributes in the current dictionary.
+        */
+       if (ref[0] != '.') {
                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;
+                       goto set;
                }
 
                /*
-                *      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.
+                *      The attribute doesn't exist (yet) save a reference to it.
                 */
-               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;
+       add_fixup:
+               if (dict_fixup_group(&ctx->fixup, CURRENT_FRAME(ctx)->filename, CURRENT_FRAME(ctx)->line,
+                                    UNCONST(fr_dict_attr_t *, da), ref, talloc_array_length(ref) - 1) < 0) goto fail;
+                       
+               talloc_free(ref);
+               return 0;
+       }
 
-               } 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;
-                       }
+       if (ref[1] != '.') {
+               fr_strerror_const("Invalid reference");
+               goto fail;
+       }
 
-               check:
-                       talloc_free(ref);
+       name = ref + 2;
 
-                       if (da->type != FR_TYPE_TLV) {
-                               fr_strerror_const("References MUST be to an ATTRIBUTE of type 'tlv'");
-                               return -1;
-                       }
+       /*
+        *      Get / skip protocol name.
+        */
+       slen = dict_by_protocol_substr(NULL, &dict, &FR_SBUFF_IN(name, strlen(name)), ctx->dict);
+       if (slen <= 0) goto add_fixup;
 
-                       if (fr_dict_attr_ref(da)) {
-                               fr_strerror_const("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...'");
-                               return -1;
-                       }
+       if (strncmp(name, ctx->dict->root->name, slen) == 0) {
+               fr_strerror_const("Cannot reference the current protocol dictionary");
+               goto fail;
+       }
 
-                       self->dict = dict;
+       /*
+        *      It's a reference to the root of the foreign dictionary.
+        */
+       if (name[slen] == '\0') {
+               da = dict->root;
+               goto set;
+       }
 
-                       dict_attr_ref_set(self, da);
-               }
+       /*
+        *      Look up the attribute.
+        */
+       da = fr_dict_attr_by_oid(NULL, dict->root, name + slen);
+       if (!da) {
+               fr_strerror_printf("protocol '%s' is loaded, but there is no such attribute '%s'",
+                                  dict->root->name, name + slen);
+               goto fail;
        }
 
        /*
-        *      It's a "clone" thing.
+        *      Refs must satisfy certain properties.
         */
-       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);
+       if (da->type != FR_TYPE_TLV) {
+               fr_strerror_const("References MUST be to an ATTRIBUTE of type 'tlv'");
+               goto fail;
        }
 
-       return 0;
+       if (fr_dict_attr_ref(da)) {
+               fr_strerror_const("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...'");
+               goto fail;
+       }
+
+       goto set;
 }
 
 /*