public inbox for linux-cifs@vger.kernel.org
 help / color / mirror / Atom feed
From: Samuel Cabrero <scabrero@suse.com>
To: linux-cifs@vger.kernel.org
Subject: PATCH: cifs.upcall: Retry krb5 TGS request with uppercase service name
Date: Wed, 08 Apr 2026 14:03:27 +0200	[thread overview]
Message-ID: <4d96fd377f12876438f33a09d4342edc6151ec28.camel@suse.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1652 bytes --]

This patch allows to mount Azure Files shares using KRB5 authentication
provided by Entra KDC for native Entra accounts, like:

   auser@tw:~> sudo mount -v -t cifs \
       //astorageaccount.file.core.windows.net/share1 /mnt/azfiles/ \
       -osec=krb5,cruid=auser,username=auser


After logging in with Himmelblau there will be a TGT in the user's
credential cache:

   auser@tw:~> klist
   Ticket cache: KEYRING:persistent:1333365971:1333365971
   Default principal:
   auser\@adomain.onmicrosoft.com@KERBEROS.MICROSOFTONLINE.COM
   
   Valid starting     Expires            Service principal
   08/04/26 13:29:52  08/04/26 23:29:52 
   krbtgt/KERBEROS.MICROSOFTONLINE.COM@KERBEROS.MICROSOFTONLINE.COM
   	renew until 15/04/26 13:29:52


But when trying to mount the Azure Files share it will fail because the
Entra KDC always issues CIFS service tickets with uppercase 'CIFS'
service name even when the request is for lowercase 'cifs', causing the
TGS response validation to fail.

This patch retries the TGS exchange with uppercase CIFS service name so
the service ticket is accepted and then used to mount the share.

   auser@tw:~> klist
   Ticket cache: KEYRING:persistent:1333365971:1333365971
   Default principal:
   auser\@adomain.onmicrosoft.com@KERBEROS.MICROSOFTONLINE.COM
   
   Valid starting     Expires            Service principal
   08/04/26 13:30:06  08/04/26 14:30:06 
   CIFS/astorageaccount.file.core.windows.net@KERBEROS.MICROSOFTONLINE.COM
   08/04/26 13:29:52  08/04/26 23:29:52 
   krbtgt/KERBEROS.MICROSOFTONLINE.COM@KERBEROS.MICROSOFTONLINE.COM
   	renew until 15/04/26 13:29:52

[-- Attachment #2: 0001-cifs.upcall-Retry-krb5-TGS-request-with-uppercase-se.patch --]
[-- Type: text/x-patch, Size: 7899 bytes --]

From a60ac5532ccf90ff8422b30c668ed14d7f1791fd Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.com>
Date: Mon, 30 Mar 2026 12:35:47 +0200
Subject: [PATCH] cifs.upcall: Retry krb5 TGS request with uppercase service
 name

The Entra KDC for Azure files always requires KRB5_NT_SRV_HST name
type in the TGS request and it issues service tickets with uppercase
'CIFS' service name even when the request is for lowercase 'cifs'
service name. This causes a TGS response validation error.

Signed-off-by: Samuel Cabrero <scabrero@suse.com>
---
 cifs.upcall.c | 166 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 99 insertions(+), 67 deletions(-)

diff --git a/cifs.upcall.c b/cifs.upcall.c
index 69e27a3..93e75c8 100644
--- a/cifs.upcall.c
+++ b/cifs.upcall.c
@@ -631,7 +631,13 @@ icfk_cleanup:
 	goto out;
 }
 
-#define CIFS_SERVICE_NAME "cifs"
+static struct cifs_service_name {
+	const char *name;
+	int type;
+} cifs_service_names[] = {
+	{ "cifs", KRB5_NT_UNKNOWN },
+	{ "CIFS", KRB5_NT_SRV_HST },
+};
 
 static krb5_error_code check_service_ticket_exists(krb5_ccache ccache,
 						   const char *hostname)
@@ -639,6 +645,8 @@ static krb5_error_code check_service_ticket_exists(krb5_ccache ccache,
 	krb5_creds mcreds, out_creds;
 	const char *errmsg;
 	krb5_error_code rc;
+	size_t count = sizeof(cifs_service_names) / sizeof(struct cifs_service_name);
+	size_t i = 0;
 
 	memset(&mcreds, 0, sizeof(mcreds));
 
@@ -651,32 +659,40 @@ static krb5_error_code check_service_ticket_exists(krb5_ccache ccache,
 		return rc;
 	}
 
-	rc = krb5_sname_to_principal(context, hostname, CIFS_SERVICE_NAME,
-			KRB5_NT_UNKNOWN, &mcreds.server);
-	if (rc) {
-		errmsg = krb5_get_error_message(context, rc);
-		syslog(LOG_DEBUG, "%s: unable to convert service name (%s) to principal: %s",
-		       __func__, hostname, errmsg);
-		krb5_free_error_message(context, errmsg);
-		krb5_free_principal(context, mcreds.client);
-		return rc;
-	}
-
-	rc = krb5_timeofday(context, &mcreds.times.endtime);
-	if (rc) {
-		errmsg = krb5_get_error_message(context, rc);
-		syslog(LOG_DEBUG, "%s: unable to get time: %s", __func__, errmsg);
-		krb5_free_error_message(context, errmsg);
-		goto out_free_principal;
-	}
+	for (i = 0; i < count; i++) {
+		rc = krb5_sname_to_principal(context, hostname,
+				cifs_service_names[i].name,
+				cifs_service_names[i].type, &mcreds.server);
+		if (rc) {
+			errmsg = krb5_get_error_message(context, rc);
+			syslog(LOG_DEBUG,
+				"%s: unable to convert service name (%s) to "
+				"principal: %s",
+				__func__, hostname, errmsg);
+			krb5_free_error_message(context, errmsg);
+			goto out_free_principal;
+		}
 
-	rc = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES, &mcreds, &out_creds);
+		rc = krb5_timeofday(context, &mcreds.times.endtime);
+		if (rc) {
+			errmsg = krb5_get_error_message(context, rc);
+			syslog(LOG_DEBUG, "%s: unable to get time: %s",
+				__func__, errmsg);
+			krb5_free_error_message(context, errmsg);
+			krb5_free_principal(context, mcreds.server);
+			goto out_free_principal;
+		}
 
-	if (!rc)
-		krb5_free_cred_contents(context, &out_creds);
+		rc = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES,
+					   &mcreds, &out_creds);
+		krb5_free_principal(context, mcreds.server);
+		if (!rc) {
+			krb5_free_cred_contents(context, &out_creds);
+			break;
+		}
+	}
 
 out_free_principal:
-	krb5_free_principal(context, mcreds.server);
 	krb5_free_principal(context, mcreds.client);
 
 	return rc;
@@ -694,6 +710,9 @@ cifs_krb5_get_req(const char *host, krb5_ccache ccache,
 #if defined(HAVE_KRB5_AUTH_CON_SETADDRS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
 	static char gss_cksum[24] = { 0x10, 0x00, /* ... */};
 #endif
+	size_t count = sizeof(cifs_service_names) / sizeof(struct cifs_service_name);
+	size_t i = 0;
+
 	memset(&in_creds, 0, sizeof(in_creds));
 
 	ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
@@ -703,16 +722,21 @@ cifs_krb5_get_req(const char *host, krb5_ccache ccache,
 		return ret;
 	}
 
-	ret = krb5_sname_to_principal(context, host, CIFS_SERVICE_NAME,
-					KRB5_NT_UNKNOWN, &in_creds.server);
-	if (ret) {
-		syslog(LOG_DEBUG, "%s: unable to convert sname to princ (%s).",
-		       __func__, host);
-		goto out_free_principal;
-	}
+	for (i = 0; i < count; i++) {
+		ret = krb5_sname_to_principal(context, host, cifs_service_names[i].name,
+				cifs_service_names[i].type, &in_creds.server);
+		if (ret) {
+			syslog(LOG_DEBUG, "%s: unable to convert sname to princ (%s).",
+			       __func__, host);
+			goto out_free_principal;
+		}
 
-	ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds);
-	krb5_free_principal(context, in_creds.server);
+		ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds);
+		krb5_free_principal(context, in_creds.server);
+		if (!ret) {
+			break;
+		}
+	}
 	if (ret) {
 		syslog(LOG_DEBUG, "%s: unable to get credentials for %s",
 		       __func__, host);
@@ -834,44 +858,52 @@ cifs_gss_get_req(const char *host, DATA_BLOB *mechtoken, DATA_BLOB *sess_key)
 	gss_buffer_desc output_token;
 	gss_krb5_lucid_context_v1_t *lucid_ctx = NULL;
 	gss_krb5_lucid_key_t *key = NULL;
+	size_t count = sizeof(cifs_service_names) / sizeof(struct cifs_service_name);
+	size_t i;
+
+	for (i = 0; i < count; i++) {
+		size_t service_name_len = strlen(cifs_service_names[i].name) +
+			1 /* @ */ + strlen(host) + 1;
+		char *service_name = malloc(service_name_len);
+		if (!service_name) {
+			syslog(LOG_DEBUG, "out of memory allocating service name");
+			maj_stat = GSS_S_FAILURE;
+			goto out;
+		}
 
-	size_t service_name_len = sizeof(CIFS_SERVICE_NAME) + 1 /* @ */ +
-		strlen(host) + 1;
-	char *service_name = malloc(service_name_len);
-	if (!service_name) {
-		syslog(LOG_DEBUG, "out of memory allocating service name");
-		maj_stat = GSS_S_FAILURE;
-		goto out;
-	}
-
-	snprintf(service_name, service_name_len, "%s@%s", CIFS_SERVICE_NAME,
-		 host);
-	gss_buffer_desc target_name_buf;
-	target_name_buf.value = service_name;
-	target_name_buf.length = service_name_len;
+		snprintf(service_name, service_name_len, "%s@%s",
+			 cifs_service_names[i].name, host);
+		gss_buffer_desc target_name_buf;
+		target_name_buf.value = service_name;
+		target_name_buf.length = service_name_len;
+
+		maj_stat = gss_import_name(&min_stat, &target_name_buf,
+				GSS_C_NT_HOSTBASED_SERVICE, &target_name);
+		free(service_name);
+		if (GSS_ERROR(maj_stat)) {
+			cifs_gss_display_status("gss_import_name", maj_stat, min_stat);
+			goto out;
+		}
 
-	maj_stat = gss_import_name(&min_stat, &target_name_buf,
-			GSS_C_NT_HOSTBASED_SERVICE, &target_name);
-	free(service_name);
-	if (GSS_ERROR(maj_stat)) {
-		cifs_gss_display_status("gss_import_name", maj_stat, min_stat);
-		goto out;
+		maj_stat = gss_init_sec_context(&min_stat,
+				GSS_C_NO_CREDENTIAL, /* claimant_cred_handle */
+				&ctx,
+				target_name,
+				discard_const(gss_mech_krb5), /* force krb5 */
+				0, /* flags */
+				0, /* time_req */
+				GSS_C_NO_CHANNEL_BINDINGS, /* input_chan_bindings */
+				GSS_C_NO_BUFFER,
+				NULL, /* actual mech type */
+				&output_token,
+				NULL, /* ret_flags */
+				NULL); /* time_rec */
+
+		if (maj_stat == GSS_S_COMPLETE || maj_stat == GSS_S_CONTINUE_NEEDED) {
+			break;
+		}
+		(void) gss_release_name(&min_stat, &target_name);
 	}
-
-	maj_stat = gss_init_sec_context(&min_stat,
-			GSS_C_NO_CREDENTIAL, /* claimant_cred_handle */
-			&ctx,
-			target_name,
-			discard_const(gss_mech_krb5), /* force krb5 */
-			0, /* flags */
-			0, /* time_req */
-			GSS_C_NO_CHANNEL_BINDINGS, /* input_chan_bindings */
-			GSS_C_NO_BUFFER,
-			NULL, /* actual mech type */
-			&output_token,
-			NULL, /* ret_flags */
-			NULL); /* time_rec */
-
 	if (maj_stat != GSS_S_COMPLETE &&
 		maj_stat != GSS_S_CONTINUE_NEEDED) {
 		cifs_gss_display_status("init_sec_context", maj_stat, min_stat);
-- 
2.53.0


             reply	other threads:[~2026-04-08 12:03 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-08 12:03 Samuel Cabrero [this message]
2026-04-08 15:59 ` PATCH: cifs.upcall: Retry krb5 TGS request with uppercase service name Steve French

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4d96fd377f12876438f33a09d4342edc6151ec28.camel@suse.com \
    --to=scabrero@suse.com \
    --cc=linux-cifs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox