From: Alan T. DeKok Date: Sat, 18 Sep 2021 13:56:44 +0000 (-0400) Subject: add and document ENUM X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c00d37c62f4550c9225703ce2a0f8669ab48c6f7;p=thirdparty%2Ffreeradius-server.git add and document ENUM we'll add more complex tests later --- diff --git a/man/man5/dictionary.5 b/man/man5/dictionary.5 index 74a6387fa0..ac4d7822bd 100644 --- a/man/man5/dictionary.5 +++ b/man/man5/dictionary.5 @@ -10,28 +10,28 @@ .RE .sp .. -.TH dictionary 5 "09 Oct 2020" +.TH dictionary 5 "18 Sep 2021" .SH NAME -dictionary \- RADIUS dictionary file +dictionary \- FreeRADIUS dictionary file .SH DESCRIPTION -The master RADIUS dictionary file resides in -\fI/etc/raddb/dictionary\fP. It references other \fIdictionary\fP -files located in \fI/usr/local/share/freeradius/\fP. Each dictionary -file contains a list of RADIUS attributes and values, which the server -uses to map between descriptive names and on-the-wire data. The names -have no meaning outside of the RADIUS server itself, and are never -exchanged between server and clients. +The local dictionary file resides in \fI/etc/raddb/dictionary\fP. It +references other \fIdictionary\fP files located in +\fI/usr/local/share/freeradius/\fP. Each dictionary file contains a +list of protocol-specific attributes and values, which the server uses +to map between descriptive names and on-the-wire data. The names have +no meaning outside of the server, and are never sent "on the wire" +between server and clients. .PP That is, editing the dictionaries will have NO EFFECT on anything other than the server that is reading those files. Adding new -attributes to the dictionaries will have NO EFFECT on RADIUS clients, -and will not make RADIUS clients magically understand those -attributes. The dictionaries are solely for local administrator -convenience, and are specific to each version of FreeRADIUS. +attributes to the dictionaries will have NO EFFECT on clients, and +will not make clients magically understand those attributes. The +dictionaries are solely for local administrator convenience, and are +specific to each version of FreeRADIUS. .PP The dictionaries in \fI/usr/local/share\fP SHOULD NOT be edited unless you know exactly what you are doing. Changing them will most likely -break your RADIUS deployment. +break the FreeRADIUS system. .PP If you need to add new attributes, please edit the \fI/etc/raddb/dictionary\fP file. It's sole purpose is to contain @@ -127,11 +127,6 @@ definition is, however, accepted: long-extended use "long-extended" as a flag instead time use "date" instead -FreeRADIUS will accept a VALUE definition for any "base" data type. -For example, you can define VALUEs for IP addresses, Ethernet -addresses, etc. VALUEs cannot be defined for "structural" data types -such as struct, tlv, vsa, group, etc. - The "struct" type is a compound type. An attribute of data type "struct" can have multiple sub-attributes defined, just as with TLVs. Each sub-attribute has to be numbered sequentially, starting at 1. @@ -148,8 +143,9 @@ This usage is no longer allowed. The options are: - encrypt=... set encryption type 1, 2, or 3. + encrypt=... set encryption type 1, 2, or 3. has_tag The attribute can have an RFC 2868 style tag + clone=... copy another attribute subtree The "encrypt" flag marks the attribute as being encrypted with one of three possible methods. "1" means that the attribute is encrypted @@ -164,12 +160,26 @@ tag, as defined in \fIRFC2868\fP. The purpose of the tag is to allow grouping of attributes for tunneled users. See \fIRFC2868\fP for more details. -When the server receives an encoded attribute in a RADIUS packet, it -looks up that attribute by number in the dictionary, and uses the -definition found there for printing diagnostic and log messages. When -the server sees an attribute name in a configuration file, it looks up -that attribute by name in the dictionary, and uses the definition -found there. +When the server receives an encoded attribute in a packet, it looks up +that attribute by number in the dictionary, and uses the definition +found there for printing diagnostic and log messages. When the server +sees an attribute name in a configuration file, it looks up that +attribute by name in the dictionary, and uses the definition found +there. + +.TP 0.5i +.B ENUM name type +Define an enumerated type, which can contain VALUEs. + +The \fIname\fP field has the same definition as for an ATTRIBUTE. + + +The \fItype\fP field has the same definition as for an attribute. + +The purpose of ENUM is to define a common set of VALUEs, which can be +re-used across multiple ATTRIBUTEs. Each ATTRIBUTE which needs to +refer to an ENUM should set the "clone" flag, which refers to the ENUM +name. For example. "ATTRIBUTE foo 1 uint16 enum=name_of_enum". .TP 0.5i .B MEMBER name type [flags] @@ -201,9 +211,15 @@ be any non-space text, but is usually taken from \fIRFC2865\fP, or other documents.. The \fInumber\fP field is also taken from the relevant documents, for that name. -When the server receives an encoded value in a RADIUS packet, it looks -up the value of that attribute by number in the dictionary, and uses -the name found there for printing diagnostic and log messages. +When the server receives an encoded value in a packet, it looks up the +value of that attribute by number in the dictionary, and uses the name +found there for printing diagnostic and log messages. + +FreeRADIUS will accept a VALUE definition for any "base" data type. +For example, you can define VALUEs for IP addresses, Ethernet +addresses, etc. VALUEs cannot be defined for "structural" data types +such as struct, tlv, vsa, group, etc. + .TP 0.5i .B VENDOR vendor-name number [format=...] Define a Vendor Specific Attribute encapsulation for \fIvendor-name\fP diff --git a/src/lib/util/dict_tokenize.c b/src/lib/util/dict_tokenize.c index 914b74a191..e52a2643b0 100644 --- a/src/lib/util/dict_tokenize.c +++ b/src/lib/util/dict_tokenize.c @@ -485,6 +485,31 @@ static int dict_process_flag_field(dict_tokenize_ctx_t *ctx, char *name, fr_type */ *ref = talloc_strdup(ctx->fixup.pool, value); + } else if (strcmp(key, "enum") == 0) { + /* + * Allow enum=... as a synonym for + * "clone". We check the sources and not + * the targets, because that's easier. + * + * Plus, ENUMs are really just normal attributes + * in disguise. + */ + if (!value) { + fr_strerror_const("Missing ENUM name for 'enum=...'"); + return -1; + } + + switch (type) { + case FR_TYPE_LEAF: + break; + + default: + fr_strerror_const("ENUM references be used for structural types"); + return -1; + } + + *ref = talloc_strdup(ctx->fixup.pool, value); + } else if (ctx->dict->subtype_table) { int subtype; @@ -909,6 +934,100 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in } +/* + * Process the ENUM command + */ +static int dict_read_process_enum(dict_tokenize_ctx_t *ctx, char **argv, int argc, + fr_dict_attr_flags_t *base_flags) +{ + int attr = -1; + fr_type_t type; + fr_dict_attr_flags_t flags; + fr_dict_attr_t const *parent, *da; + + if (argc != 2) { + fr_strerror_const("Invalid ENUM syntax"); + return -1; + } + + /* + * Dictionaries need to have real names, not shitty ones. + */ + if (strncmp(argv[0], "Attr-", 5) == 0) { + fr_strerror_const("Invalid ENUM name"); + return -1; + } + + flags = *base_flags; + + if (dict_process_type_field(ctx, argv[1], &type, &flags) < 0) 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; + } + + switch (type) { + case FR_TYPE_LEAF: + break; + + default: + fr_strerror_printf("ENUMs can only be a leaf type, not %s", + fr_table_str_by_value(fr_value_box_type_table, type, "?Unknown?")); + break; + } + + parent = ctx->stack[ctx->stack_depth].da; + if (!parent) { + fr_strerror_const("Invalid location for ENUM"); + return -1; + } + + /* + * ENUMs cannot have a flag field, so we don't parse that. + * + * Maybe we do want a flag field for named time deltas? + */ + +#ifdef __clang_analyzer__ + if (!ctx->dict) return -1; +#endif + + /* + * We have to call this first in order to allocate a + * number for the attribute. + */ + if (!dict_attr_fields_valid(ctx->dict, parent, argv[0], &attr, type, &flags)) return -1; + + /* + * Add in an attribute + */ + if (fr_dict_attr_add(ctx->dict, parent, argv[0], attr, type, &flags) < 0) return -1; + + /* + * If we need to set the previous attribute, we have to + * look it up by number. This lets us set the + * *canonical* previous attribute, and not any potential + * duplicate which was just added. + */ + da = dict_attr_child_by_num(parent, attr); + if (!da) { + fr_strerror_printf("Failed to find attribute '%s' we just added.", argv[0]); + return -1; + } + +#ifndef NDEBUG + if (!dict_attr_by_name(NULL, parent, argv[0])) { + fr_strerror_printf("Failed to find attribute '%s' we just added.", argv[0]); + return -1; + } +#endif + + memcpy(&ctx->value_attr, &da, sizeof(da)); + + return 0; +} + /* * Process the MEMBER command */ @@ -1823,6 +1942,16 @@ static int _dict_from_file(dict_tokenize_ctx_t *ctx, continue; } + /* + * Perhaps this is an enum. + */ + if (strcasecmp(argv[0], "ENUM") == 0) { + if (dict_read_process_enum(ctx, + argv + 1, argc - 1, + &base_flags) == -1) goto error; + continue; + } + /* * Process FLAGS lines. */