]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow for relative attribute names in files on disk
authorAlan T. DeKok <aland@freeradius.org>
Fri, 9 Apr 2021 21:10:19 +0000 (17:10 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 9 Apr 2021 21:10:19 +0000 (17:10 -0400)
foo = { },
.bar = baz,  # will go into foo

foo = { bar = baz }
.arg = 1 # will go into foo
yes = 43 # no leading '.', will not go into "foo"

This won't work for multiple layers of nesting.  i.e. you can't
do "..bar" to back 2 levels.

src/lib/util/pair_legacy.c
src/tests/auth/digest.attrs

index a16414419e8287fc9ab58d13f43374338ef7ccb4..60fb8cfa0cb7d6181ea1c0487e2370df8efd30f5 100644 (file)
@@ -272,19 +272,23 @@ fr_pair_t *fr_pair_make(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *
  * @param[in] list     where the parsed fr_pair_ts will be appended.
  * @param[in,out] token        The last token we parsed
  * @param[in] depth    the nesting depth for FR_TYPE_GROUP
+ * @param[in,out] relative_vp for relative attributes
  * @return
  *     - <= 0 on failure.
  *     - The number of bytes of name consumed on success.
  */
 static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *buffer,
-                                        fr_pair_list_t *list, fr_token_t *token, int depth)
+                                        fr_pair_list_t *list, fr_token_t *token, int depth, fr_pair_t **relative_vp)
 {
        fr_pair_list_t  tmp_list;
        fr_pair_t       *vp = NULL;
+       fr_pair_t       *my_relative_vp;
        char const      *p, *q, *next;
        fr_token_t      quote, last_token = T_INVALID;
        fr_pair_t_RAW   raw;
        fr_dict_attr_t  const *internal = fr_dict_root(fr_dict_internal());
+       fr_pair_list_t  *my_list;
+       TALLOC_CTX      *my_ctx;
 
        if (internal == parent) internal = NULL;
 
@@ -329,29 +333,48 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
                 */
                if (strncmp(p, "raw.", 4) == 0) goto do_unknown;
 
-               /*
-                *      Parse the name.
-                */
-               slen = fr_dict_attr_by_oid_substr(NULL, &da, parent,
-                                                 &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
-               if ((slen <= 0) && internal) {
-                       slen = fr_dict_attr_by_oid_substr(NULL, &da, internal,
+               if (*p == '.') {
+                       p++;
+
+                       if (!*relative_vp) {
+                               fr_strerror_const("Relative attributes can only be used immediately after an attribute of type 'group'");
+                               goto error;
+                       }
+
+                       slen = fr_dict_attr_by_oid_substr(NULL, &da, (*relative_vp)->da,
                                                          &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
-               }
-               if (slen <= 0) {
-               do_unknown:
-                       slen = fr_dict_unknown_afrom_oid_substr(ctx, NULL, &da_unknown, parent,
-                                                               &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+                       if (slen <= 0) goto error;
+
+                       my_list = &(*relative_vp)->vp_group;
+                       my_ctx = *relative_vp;
+               } else {
+                       /*
+                        *      Parse the name.
+                        */
+                       slen = fr_dict_attr_by_oid_substr(NULL, &da, parent,
+                                                         &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+                       if ((slen <= 0) && internal) {
+                               slen = fr_dict_attr_by_oid_substr(NULL, &da, internal,
+                                                         &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+                       }
                        if (slen <= 0) {
-                               p += -slen;
+                       do_unknown:
+                               slen = fr_dict_unknown_afrom_oid_substr(ctx, NULL, &da_unknown, parent,
+                                                                       &FR_SBUFF_IN(p, strlen(p)), &bareword_terminals);
+                               if (slen <= 0) {
+                                       p += -slen;
+
+                               error:
+                                       fr_pair_list_free(&tmp_list);
+                                       *token = T_INVALID;
+                                       return -(p - buffer);
+                               }
 
-                       error:
-                               fr_pair_list_free(&tmp_list);
-                               *token = T_INVALID;
-                               return -(p - buffer);
+                               da = da_unknown;
                        }
 
-                       da = da_unknown;
+                       my_list = &tmp_list;
+                       my_ctx = ctx;
                }
 
                next = p + slen;
@@ -392,7 +415,7 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
                        }
                        p++;
 
-                       vp = fr_pair_afrom_da(ctx, da);
+                       vp = fr_pair_afrom_da(my_ctx, da);
                        if (!vp) goto error;
 
                        /*
@@ -401,7 +424,13 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
                        parent = fr_dict_attr_ref(da);
                        if (!parent) parent = da;
 
-                       slen = fr_pair_list_afrom_substr(vp, vp->da, p, &vp->vp_group, &last_token, depth + 1);
+                       /*
+                        *      Parse nested attributes, but the
+                        *      attributes here are relative to each
+                        *      other, and not to our parent relative VP.
+                        */
+                       my_relative_vp = NULL;
+                       slen = fr_pair_list_afrom_substr(vp, vp->da, p, &vp->vp_group, &last_token, depth + 1, &my_relative_vp);
                        if (slen <= 0) {
                                talloc_free(vp);
                                goto error;
@@ -418,6 +447,12 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
                        fr_skip_whitespace(p);
                        if (*p != '}') goto failed_group;
                        p++;
+
+                       /*
+                        *      Cache which VP is now the one for
+                        *      relative references.
+                        */
+                       *relative_vp = vp;
                        break;
 
                case FR_TYPE_LEAF:
@@ -466,7 +501,7 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
                         *      @todo - note that they will also be escaped,
                         *      so we may need to fix that later.
                         */
-                       vp = fr_pair_afrom_da(ctx, da);
+                       vp = fr_pair_afrom_da(my_ctx, da);
                        if (!vp) goto error;
                        vp->op = raw.op;
 
@@ -509,7 +544,17 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
                fr_dict_unknown_free(&da);
 
                fr_assert(vp != NULL);
-               fr_pair_append(&tmp_list, vp);
+               fr_pair_append(my_list, vp);
+
+               /*
+                *      If there's a relative VP, and it's not the one
+                *      we just added above, and we're not adding this
+                *      VP to the relative one, then nuke the relative
+                *      VP.
+                */
+               if (*relative_vp && (vp != *relative_vp) && (my_ctx != *relative_vp)) {
+                       *relative_vp = NULL;
+               }
 
                /*
                 *      Now look for EOL, hash, etc.
@@ -565,8 +610,9 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_attr_t const *
 fr_token_t fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *buffer, fr_pair_list_t *list)
 {
        fr_token_t token;
+       fr_pair_t       *relative_vp = NULL;
 
-       (void) fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buffer, list, &token, 0);
+       (void) fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buffer, list, &token, 0, &relative_vp);
        return token;
 }
 
@@ -586,6 +632,7 @@ int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list
        fr_token_t      last_token = T_EOL;
        bool            found = false;
        char            buf[8192];
+       fr_pair_t       *relative_vp = NULL;
 
        while (fgets(buf, sizeof(buf), fp) != NULL) {
                fr_pair_list_t tmp_list;
@@ -617,13 +664,28 @@ int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list
                /*
                 *      Call our internal function, instead of the public wrapper.
                 */
-               if (fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buf, &tmp_list, &last_token, 0) < 0) {
+               if (fr_pair_list_afrom_substr(ctx, fr_dict_root(dict), buf, &tmp_list, &last_token, 0, &relative_vp) < 0) {
                        goto fail;
                }
 
+               /*
+                *      @todo - rely on actually checking the syntax, and "OK" result, instead of guessing.
+                *
+                *      The main issue is that it's OK to read no
+                *      attributes on a particular line, but only if
+                *      it's comments.
+                */
                if (fr_dlist_empty(&tmp_list.head)) {
                        if (last_token == T_EOL) break;
 
+                       /*
+                        *      This is allowed for relative attributes.
+                        */
+                       if (relative_vp && (last_token == T_COMMA)) {
+                               found = true;
+                               continue;
+                       }
+
                fail:
                        /*
                         *      Didn't read anything, but the previous
index eadb8a012edb4ff6ed2506c9dacea75fe5106ddf..c45018e206309f89a9446f0577a941a576f209a5 100644 (file)
 Packet-Type = Access-Request
 User-Name = "bob",
 Digest-Response = "bdbeebb2da6adb6bca02599c2239e192"
-Digest-Attributes = { Realm = "biloxi.com", Nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093", Method = "INVITE", URI = "sip:bob@biloxi.com", Algorithm = "MD5", User-Name = "bob", QOP = "auth-int", Nonce-Count = "00000001", CNonce = "0a4f113b", Body-Digest = "c1ed018b8ec4a3b170c0921f5b564e48" }
-
-#Digest-Attributes.Realm = "biloxi.com",
-#Digest-Attributes.Nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093",
-#Digest-Attributes.Method = "INVITE",
-#Digest-Attributes.URI = "sip:bob@biloxi.com",
-#Digest-Attributes.Algorithm = "MD5",
-#Digest-Attributes.User-Name = "bob",
-#Digest-Attributes.QOP = "auth-int",
-#Digest-Attributes.Nonce-Count = "00000001",
-#Digest-Attributes.CNonce = "0a4f113b",
-#Digest-Attributes.Body-Digest = "c1ed018b8ec4a3b170c0921f5b564e48",
+Digest-Attributes = { Realm = "biloxi.com" },
+.Nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093",
+.Method = "INVITE",
+.URI = "sip:bob@biloxi.com",
+.Algorithm = "MD5",
+.User-Name = "bob",
+.QOP = "auth-int",
+.Nonce-Count = "00000001",
+.CNonce = "0a4f113b",
+.Body-Digest = "c1ed018b8ec4a3b170c0921f5b564e48"
 
 Packet-Type == Access-Accept