]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
lib/chkname.*: Add UPN validation support
authorIker Pedrosa <ipedrosa@redhat.com>
Tue, 2 Jun 2026 11:15:42 +0000 (13:15 +0200)
committerAlejandro Colomar <foss+github@alejandro-colomar.es>
Thu, 18 Jun 2026 14:24:36 +0000 (16:24 +0200)
Add is_valid_upn() function to validate User Principal Name format.  UPN
validation splits on @ and validates the prefix using existing username
rules and suffix part using RFC 1035/1123 compliant domain name
validation.

Link: <https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.1>
Co-authored-by: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
Co-authored-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
lib/chkname.c
lib/chkname.h

index 1ef33ad3ebcbe8d080211a359b044b9828fd56d6..ee8ff9d108eefaf9408fb459291ef650319bb045 100644 (file)
@@ -7,8 +7,7 @@
 
 
 /*
- * is_valid_user_name(), is_valid_group_name() - check the new user/group
- * name for validity;
+ * check user/group names for valid syntax
  * return values:
  *   true  - OK
  *   false - bad name
 
 #include "defines.h"
 #include "chkname.h"
+#include "sizeof.h"
 #include "string/ctype/isascii.h"
 #include "string/strcmp/streq.h"
 #include "string/strcmp/strcaseeq.h"
+#include "string/strspn/strrcspn.h"
+#include "string/strtok/stpsep.h"
 #include "sysconf.h"
 
 
+#define DOMAIN_MAXLEN  255
+#define LABEL_MAXLEN   63
+
+
 int allow_bad_names = false;
 
 
@@ -126,3 +132,85 @@ is_valid_group_name(const char *name)
 
        return is_valid_name (name);
 }
+
+
+// Validate a single DNS domain label according to RFC 1035 2.3.1.
+static bool
+is_valid_domain_label(const char *label)
+{
+       if (strlen(label) > LABEL_MAXLEN) {
+               errno = EOVERFLOW;
+               return false;
+       }
+       if (!strspn(label, CTYPE_ALPHA_C) || !strrcspn(label, "-")) {
+               errno = EINVAL;
+               return false;
+       }
+       if (!streq(stpspn(label, CTYPE_ALNUM_C "-"), "")) {
+               errno = EINVAL;
+               return false;
+       }
+
+       return true;
+}
+
+
+/*
+ * Validate a domain name according to RFC 1035 by splitting
+ * it into labels and validating each label individually.
+ */
+static bool
+is_valid_domain_name(const char *domain)
+{
+       char        *d;
+       const char  *l;
+
+       if (!strcspn(domain, ".")) {
+               errno = EINVAL;
+               return false;
+       }
+
+       if (strlen(domain) + !!strrcspn(domain, ".") > DOMAIN_MAXLEN) {
+               errno = EOVERFLOW;
+               return false;
+       }
+       d = strdupa(domain);
+
+       while (NULL != (l = strsep(&d, "."))) {
+               if (d == NULL && streq(l, ""))  // trailing root label
+                       break;
+               if (!is_valid_domain_label(l))
+                       return false;
+       }
+
+       return true;
+}
+
+
+/*
+ * is_valid_upn - is valid User Principal Name
+ *
+ * Check UPN format (user@domain) for validity.
+ *
+ * This function only validates syntax, not whether the UPN exists
+ * in any authentication system.
+ */
+bool
+is_valid_upn(const char *upn)
+{
+       char  *u, *d;
+
+       if (strlen(upn) >= LOGIN_NAME_MAX + STRLEN("@") + DOMAIN_MAXLEN) {
+               errno = EOVERFLOW;
+               return false;
+       }
+       u = strdupa(upn);
+
+       d = stpsep(u, "@");
+       if (d == NULL) {
+               errno = EINVAL;
+               return false;
+       }
+
+       return is_valid_user_name(u) && is_valid_domain_name(d);
+}
index 1f8e4e08221ebd9e055c4c53b5df0a3eda9059ab..de93b8251a87c83b368597465824fb2fbdb678de 100644 (file)
@@ -28,5 +28,6 @@
 
 extern bool is_valid_user_name (const char *name);
 extern bool is_valid_group_name (const char *name);
+extern bool is_valid_upn (const char *name);
 
 #endif