From: Iker Pedrosa Date: Tue, 2 Jun 2026 11:15:42 +0000 (+0200) Subject: lib/chkname.*: Add UPN validation support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a33829890ce785f97b75bf8fcc895f99ae17b3de;p=thirdparty%2Fshadow.git lib/chkname.*: Add UPN validation support 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: Co-authored-by: Iker Pedrosa Signed-off-by: Iker Pedrosa Co-authored-by: Alejandro Colomar Signed-off-by: Alejandro Colomar Reviewed-by: Alejandro Colomar --- diff --git a/lib/chkname.c b/lib/chkname.c index 1ef33ad3e..ee8ff9d10 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -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 @@ -33,12 +32,19 @@ #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); +} diff --git a/lib/chkname.h b/lib/chkname.h index 1f8e4e082..de93b8251 100644 --- a/lib/chkname.h +++ b/lib/chkname.h @@ -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