[pve-devel] [PATCH access-control] ldap: fix ldap distinguished names regex
Stefan Sterz
s.sterz at proxmox.com
Wed May 17 15:42:18 CEST 2023
sorry just noticed i forgot: adding tests for this was
Suggested-by: Dominik Csapak <d.csapak at proxmox.com>
On 17.05.23 15:39, Stefan Sterz wrote:
> according to the current specification of the string representation of
> ldap distinguished names (DN) presented by RFC 4514 [1] the current
> regex checking ldap DNs still prevents users from entering valid
> DNs.
>
> for example we do not allow multi-valued RelativeDistinguishedNames as
> these are separated by a '+'. this was already reported as an issue in
> the subscription support.
>
> escaped sequences in unquoted AttributeValues are also not allowed.
> this includes commas (',') and quotes ('"'). for example, the
> following does not work even though the RFC explicitly mentions it as
> a valid example ([1], section 4):
>
> CN=James \"Jim\" Smith\, III,DC=example,DC=net
>
> while this may not be usable as a valid username, as argued
> previously [2], it seems arbitrary to allow this for quoted
> AttributeValues but not for unquoted ones.
>
> this commit also adds a test file that tests the regex against a
> number of common pitfalls. including distinguished names that are
> structurally similar to those reported as erroneously forbidden
> earlier. these tests should help avoid regressions in the future.
>
> [1]: https://datatracker.ietf.org/doc/html/rfc4514
> [2]: https://git.proxmox.com/?p=pve-access-control.git;h=1aa2355a
>
> Signed-off-by: Stefan Sterz <s.sterz at proxmox.com>
> ---
> would really appreciate another pair of eyes here. given the recent
> churn related to this regex. it's very likely i missed something too.
>
> src/PVE/Auth/LDAP.pm | 9 +++++--
> src/test/Makefile | 1 +
> src/test/dn-regex-test.pl | 54 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 62 insertions(+), 2 deletions(-)
> create mode 100755 src/test/dn-regex-test.pl
>
> diff --git a/src/PVE/Auth/LDAP.pm b/src/PVE/Auth/LDAP.pm
> index fc82a17..ccc6864 100755
> --- a/src/PVE/Auth/LDAP.pm
> +++ b/src/PVE/Auth/LDAP.pm
> @@ -10,8 +10,13 @@ use PVE::Tools;
>
> use base qw(PVE::Auth::Plugin);
>
> -my $dn_part_regex = qr!("[^"]+"|[^ ,+"/<>;=#][^,+"/<>;=]*[^ ,+"/<>;=]|[^ ,+"/<>;=#])!;
> -our $dn_regex = qr!\w+=${dn_part_regex}(,\s*\w+=${dn_part_regex})*!;
> +my $escaped = qr!\\(?:[ "#+,;<=>\\]|[0-9a-fA-F]{2})!;
> +my $start = qr!(?:${escaped}|[^"+,;<>\\\0 #])!;
> +my $middle = qr!(?:${escaped}|[^"+,;<>\\\0])!;
> +my $end = qr!(?:${escaped}|[^"+,;<>\\\0 ])!;
> +my $attr_val = qr!("[^"]+"|${start}(?:${middle}*${end})?)!;
> +
> +our $dn_regex = qr!\w+=${attr_val}([,\+]\s*\w+=${attr_val})*!;
>
> sub type {
> return 'ldap';
> diff --git a/src/test/Makefile b/src/test/Makefile
> index 859a84b..b4c05eb 100644
> --- a/src/test/Makefile
> +++ b/src/test/Makefile
> @@ -13,3 +13,4 @@ check:
> perl -I.. perm-test7.pl
> perl -I.. perm-test8.pl
> perl -I.. realm_sync_test.pl
> + perl -I.. dn-regex-test.pl
> diff --git a/src/test/dn-regex-test.pl b/src/test/dn-regex-test.pl
> new file mode 100755
> index 0000000..a2410af
> --- /dev/null
> +++ b/src/test/dn-regex-test.pl
> @@ -0,0 +1,54 @@
> +
> +use strict;
> +use warnings;
> +
> +use PVE::Auth::LDAP;
> +
> +# this test file attempts to check whether the ldap distinguished name regex
> +# defined in `PVE::Auth::LDAP` complies with RFC 4514.
> +#
> +# see: https://datatracker.ietf.org/doc/html/rfc4514
> +
> +my @pass = (
> + "ou=a", # single AttributeTypeValue
> + "ou=orga,dc=com,cn=name", # multiple RelativeDistinguishedNames
> + "STREET=a,cn=a,C=c", # single character AttributeValues
> + "UID=tt,cn=\"#+,;<>\\ \"", # forbidden characters are allowed when quoted
> + "c=\\\"\\#\\+\\;\\<\\=\\>", # specific characters allowed when escaped
> + "a=\\\\", # escaped backslashes are allowed
> + "ST=a,cn=\"Test, User\"", # allow un-escaped commas in quoted AttributeValues
> + "o2u=bc,cn=Test\\, User", # allow escaped commas
> + "T2=a #b", # spaces (' ') and '#' are allowed in the middle of AttributeValues
> + "word4word=ab#", # allow '#' at the end of an AttributeValue
> + "ou=orga+sub=ab", # allow '+' as separators for multi-valued RelativeDistinguishedName
> + "dc=\\f0\\Ac\\93", # allow escaping hex values in unquoted AttributeValues
> +
> + # regression tests
> + "ou=adf-bd,dc=abcd+efOuId=BL:BL:sldkf:704004,dc=or,dc=com",
> + "gvGid=DE:8A:wordCaps,ou=Service,dc=alsdkj+abOuId=UK:A8:137100,dc=edu,dc=de",
> +);
> +
> +my @fail = (
> + "", # no empty distinguished name
> + "ou=a,", # no empty AttributeTypeAndValue
> + "ou=a+", # no multi-valued RelativeDistinguishedName with empty second part
> + "ou", # missing separator and AttributeValue
> + "ou=", # no 'empty' AttributeValue
> + "ou=+", # forbidden character '+' in AttributeValue
> + "ou= or", # no space at the beginning of an AttributeValue
> + "ou=orgs ", # no space at the end of an AttributeValue
> + "ou=#value", # no '#' at the beginning an AttributeValue
> + "ou=\"+,;<>\\\0", # no un-escaped forbidden characters in unquoted AttributeValues
> + "ou=name\0", # no null value in AttributeValue
> + "ou=zy\\xw\\v" # no unescaped backslashes that are not escaping specific characters
> +);
> +
> +for my $test_case (@pass) {
> + die "\'" . $test_case . "\' unexpectedly did not match the ldap dn_regex\n"
> + if !($test_case =~ m/^$PVE::Auth::LDAP::dn_regex$/);
> +}
> +
> +for my $test_case (@fail) {
> + die "\'" . $test_case . "\' unexpectedly did match the the ldap dn_regex\n"
> + if $test_case =~ m/^$PVE::Auth::LDAP::dn_regex$/;
> +}
More information about the pve-devel
mailing list