public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Paulo Alcantara <paalcant@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/cifs-utils] f44: resolves: rhbz#2498627 - Update to cifs-utils-7.7
Date: Thu, 27 Aug 2026 17:09:00 GMT [thread overview]
Message-ID: <178785054013.1.11516730591200928640.rpms-cifs-utils-8b8dd55d853e@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/cifs-utils
Branch : f44
Commit : 8b8dd55d853e623ef53e0b736aa2d176a37f87fa
Author : Paulo Alcantara <paalcant@redhat.com>
Date : 2026-08-27T13:58:33-03:00
Stats : +419/-445 in 6 file(s)
URL : https://src.fedoraproject.org/rpms/cifs-utils/c/8b8dd55d853e623ef53e0b736aa2d176a37f87fa?branch=f44
Log:
resolves: rhbz#2498627 - Update to cifs-utils-7.7
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
---
diff --git a/.gitignore b/.gitignore
index a23b775..24e9fc3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,4 @@ cifs-utils-4.6.tar.bz2
/cifs-utils-7.4.tar.bz2
/cifs-utils-7.5.tar.bz2
/cifs-utils-7.6.tar.bz2
+/cifs-utils-7.7.tar.bz2
diff --git a/cifs-utils.spec b/cifs-utils.spec
index 487ff2e..b2a84c5 100644
--- a/cifs-utils.spec
+++ b/cifs-utils.spec
@@ -1,10 +1,8 @@
#% define pre_release rc1
%define pre_release %nil
-%global bash_completion_dir %(pkg-config --variable=completionsdir bash-completion || echo /etc/bash_completion.d)
-
Name: cifs-utils
-Version: 7.6
+Version: 7.7
Release: %autorelease
Summary: Utilities for mounting and managing CIFS mounts
@@ -24,8 +22,7 @@ Recommends: %{name}-info%{?_isa} = %{version}-%{release}
Source0: https://download.samba.org/pub/linux-cifs/cifs-utils/%{name}-%{version}.tar.bz2
-Patch0: cifs.upcall-fix-compiler-warning-with-Wvla.patch
-Patch1: cifs.upcall-fix-regression-with-krb5-creduid.patch
+Patch0: cifs.upcall-fix-krb5-regression-with-trust-dns.patch
%description
The SMB/CIFS protocol is a standard file sharing protocol widely deployed
@@ -72,7 +69,7 @@ mkdir -p %{buildroot}%{_sysconfdir}/%{name}
mkdir -p %{buildroot}%{_sysconfdir}/request-key.d
install -m 644 contrib/request-key.d/cifs.idmap.conf %{buildroot}%{_sysconfdir}/request-key.d
install -m 644 contrib/request-key.d/cifs.spnego.conf %{buildroot}%{_sysconfdir}/request-key.d
-install -Dpm 644 bash-completion/smbinfo %{buildroot}%{_datadir}%{bash_completion_dir}/smbinfo
+install -Dpm 644 bash-completion/smbinfo %{buildroot}%{bash_completions_dir}/smbinfo
%files
%doc
@@ -94,7 +91,7 @@ install -Dpm 644 bash-completion/smbinfo %{buildroot}%{_datadir}%{bash_completio
%{_mandir}/man8/mount.cifs.*
%{_mandir}/man8/mount.smb3.*
%{_mandir}/man8/idmapwb.*
-%{_datadir}%{bash_completion_dir}/smbinfo
+%{bash_completions_dir}/smbinfo
%dir %{_sysconfdir}/cifs-utils
%ghost %{_sysconfdir}/cifs-utils/idmap-plugin
%config(noreplace) %{_sysconfdir}/request-key.d/cifs.idmap.conf
diff --git a/cifs.upcall-fix-compiler-warning-with-Wvla.patch b/cifs.upcall-fix-compiler-warning-with-Wvla.patch
deleted file mode 100644
index 5ef0dc1..0000000
--- a/cifs.upcall-fix-compiler-warning-with-Wvla.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From d2f39a20d68aa55023b63e575f06941721e644c6 Mon Sep 17 00:00:00 2001
-From: Paulo Alcantara <pc@manguebit.org>
-Date: Tue, 23 Jun 2026 15:06:20 -0300
-Subject: [PATCH] cifs.upcall: fix compiler warning with -Wvla
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The length value for @path array in get_uidgid() needs to be evaluated
-at compile time, so replace strlen() with sizeof() when defining
-PROC_PID_PATH_MAXLEN and then fix the following warning:
-
- cifs.upcall.c: In function ‘get_uidgid’:
- cifs.upcall.c:1400:9: warning: ISO C90 forbids array ‘path’ whose size
- cannot be evaluated [-Wvla]
- 1400 | char path[PROC_PID_PATH_MAXLEN] = {}, buf[256];
- | ^~~~
-
-Fixes: 972c5b5ff95e ("cifs.upcall: remove getpwuid() dependency")
-Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
-Reviewed-by: David Howells <dhowells@redhat.com>
-Cc: Enzo Matsumiya <ematsumiya@suse.de>
-Cc: linux-cifs@vger.kernel.org
-Signed-off-by: Steve French <stfrench@microsoft.com>
----
- cifs.upcall.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/cifs.upcall.c b/cifs.upcall.c
-index 01690dfcade1..11dbc6186a74 100644
---- a/cifs.upcall.c
-+++ b/cifs.upcall.c
-@@ -1375,8 +1375,8 @@ static int ip_to_fqdn(const char *addrstr, char *host, size_t hostlen)
- return 0;
- }
-
--/* cover worst case/impossible scenarios, + 1 for NUL */
--#define PROC_PID_PATH_MAXLEN ((int)strlen("/proc/2147483647/status") + 1)
-+/* cover worst case/impossible scenarios */
-+#define PROC_PID_PATH_MAXLEN ((int)sizeof("/proc/2147483647/status"))
- /* max valid UID/GID is (UINT_MAX - 1) */
- #define INVALID_UIDGID UINT_MAX
-
---
-2.54.0
-
diff --git a/cifs.upcall-fix-krb5-regression-with-trust-dns.patch b/cifs.upcall-fix-krb5-regression-with-trust-dns.patch
new file mode 100644
index 0000000..1d860c8
--- /dev/null
+++ b/cifs.upcall-fix-krb5-regression-with-trust-dns.patch
@@ -0,0 +1,413 @@
+From 34aaaff80e536e4bb1b15bf640cf615d9c2afbbd Mon Sep 17 00:00:00 2001
+From: Paulo Alcantara <pc@manguebit.org>
+Date: Tue, 18 Aug 2026 16:16:06 -0300
+Subject: [PATCH] cifs.upcall: fix krb5 regression with --trust-dns
+
+Customer reported randomly failed krb5 authentications against certain
+servers when using --trust-dns parameter. Turned out that the client
+failed to establish sessions against those servers when using
+CIFS/KRB5_NT_SRV_HST service principals.
+
+Restore old behavior of always trying cifs/KRB5_NT_UNKNOWN service
+principal first when using --trust-dns parameter.
+
+Reported-by: Pierguido Lambri <plambri@redhat.com>
+Reported-by: Frank Sorenson <sorenson@redhat.com>
+Fixes: 562a6cb4f99c ("cifs.upcall: Retry krb5 TGS request with uppercase service name")
+Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+Cc: Steve French <stfrench@microsoft.com>
+Cc: Samuel Cabrero <scabrero@suse.com>
+Cc: linux-cifs@vger.kernel.org
+---
+ cifs.upcall.c | 281 +++++++++++++++++++++++++-------------------------
+ 1 file changed, 140 insertions(+), 141 deletions(-)
+
+diff --git a/cifs.upcall.c b/cifs.upcall.c
+index 42205e66a676..76e88b79a760 100644
+--- a/cifs.upcall.c
++++ b/cifs.upcall.c
+@@ -698,22 +698,18 @@ out_free_principal:
+ return rc;
+ }
+
+-static int
+-cifs_krb5_get_req(const char *host, krb5_ccache ccache,
+- DATA_BLOB * mechtoken, DATA_BLOB * sess_key)
++static int cifs_krb5_get_req(const struct cifs_service_name *svc,
++ const char *host, krb5_ccache ccache,
++ DATA_BLOB *mechtoken, DATA_BLOB *sess_key)
+ {
+ krb5_error_code ret;
+ krb5_keyblock *tokb;
+- krb5_creds in_creds, *out_creds;
++ krb5_creds in_creds = {}, *out_creds;
+ krb5_data apreq_pkt, in_data;
+ krb5_auth_context auth_context = NULL;
+ #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);
+ if (ret) {
+@@ -722,21 +718,16 @@ cifs_krb5_get_req(const char *host, krb5_ccache ccache,
+ return ret;
+ }
+
+- 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);
+- if (!ret) {
+- break;
+- }
++ ret = krb5_sname_to_principal(context, host, svc->name,
++ svc->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);
+ if (ret) {
+ syslog(LOG_DEBUG, "%s: unable to get credentials for %s",
+ __func__, host);
+@@ -849,8 +840,9 @@ void cifs_gss_display_status(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat)
+ cifs_gss_display_status_1(msg, min_stat, GSS_C_MECH_CODE);
+ }
+
+-static int
+-cifs_gss_get_req(const char *host, DATA_BLOB *mechtoken, DATA_BLOB *sess_key)
++static int cifs_gss_get_req(const struct cifs_service_name *svc,
++ const char *host, DATA_BLOB *mechtoken,
++ DATA_BLOB *sess_key)
+ {
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t target_name;
+@@ -858,52 +850,43 @@ 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;
++ gss_buffer_desc target_name_buf;
++ size_t service_name_len;
++ char *service_name;
+
+- 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;
+- }
+-
+- 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;
+- }
++ service_name_len = strlen(svc->name) + 1 /* @ */ + strlen(host) + 1;
++ 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;
++ }
+
+- 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 */
++ snprintf(service_name, service_name_len, "%s@%s", svc->name, host);
+
+- if (maj_stat == GSS_S_COMPLETE || maj_stat == GSS_S_CONTINUE_NEEDED) {
+- break;
+- }
+- (void) gss_release_name(&min_stat, &target_name);
++ 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_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);
+@@ -914,8 +897,7 @@ cifs_gss_get_req(const char *host, DATA_BLOB *mechtoken, DATA_BLOB *sess_key)
+ *mechtoken = data_blob(output_token.value, output_token.length);
+
+ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx, 1,
+- (void **)&lucid_ctx);
+-
++ (void **)&lucid_ctx);
+ if (GSS_ERROR(maj_stat)) {
+ cifs_gss_display_status("gss_krb5_export_lucid_sec_context",
+ maj_stat, min_stat);
+@@ -972,9 +954,9 @@ out:
+ *
+ * ret: 0 - success, others - failure
+ */
+-static int
+-handle_krb5_mech(const char *oid, const char *host, DATA_BLOB * secblob,
+- DATA_BLOB * sess_key, krb5_ccache ccache)
++static int handle_krb5_mech(const char *oid, const struct cifs_service_name *svc,
++ const char *host, DATA_BLOB *secblob,
++ DATA_BLOB *sess_key, krb5_ccache ccache)
+ {
+ int retval;
+ DATA_BLOB tkt_wrapped;
+@@ -987,7 +969,7 @@ handle_krb5_mech(const char *oid, const char *host, DATA_BLOB * secblob,
+ */
+ if (!ccache) {
+ syslog(LOG_DEBUG, "%s: using GSS-API", __func__);
+- retval = cifs_gss_get_req(host, &tkt_wrapped, sess_key);
++ retval = cifs_gss_get_req(svc, host, &tkt_wrapped, sess_key);
+ if (retval) {
+ syslog(LOG_DEBUG, "%s: failed to obtain service ticket via GSS (%d)",
+ __func__, retval);
+@@ -998,7 +980,7 @@ handle_krb5_mech(const char *oid, const char *host, DATA_BLOB * secblob,
+ syslog(LOG_DEBUG, "%s: using native krb5", __func__);
+
+ /* get a kerberos ticket for the service and extract the session key */
+- retval = cifs_krb5_get_req(host, ccache, &tkt, sess_key);
++ retval = cifs_krb5_get_req(svc, host, ccache, &tkt, sess_key);
+ if (retval) {
+ syslog(LOG_DEBUG, "%s: failed to obtain service ticket (%d)",
+ __func__, retval);
+@@ -1570,6 +1552,83 @@ static const struct option long_options[] = {
+ {NULL, 0, NULL, 0}
+ };
+
++static long get_spn_key_from_host(const struct decoded_args *arg,
++ const struct cifs_service_name *svc,
++ char *host, DATA_BLOB *secblob,
++ DATA_BLOB *sess_key, bool try_dns,
++ krb5_ccache ccache)
++{
++ char hostbuf[NI_MAXHOST] = {};
++ const char *oid;
++ long rc;
++
++ if (arg->sec == MS_KRB5)
++ oid = OID_KERBEROS5_OLD;
++ else
++ oid = OID_KERBEROS5;
++
++retry_new_hostname:
++ lowercase_string(host);
++ rc = handle_krb5_mech(oid, svc, host, secblob, sess_key, ccache);
++ if (!rc)
++ return rc;
++
++ /*
++ * If hostname has a '.', assume it's a FQDN, otherwise we
++ * want to guess the domainname.
++ */
++ if (!strchr(host, '.')) {
++ struct addrinfo hints = {};
++ struct addrinfo *ai;
++ char *domainname;
++ char fqdn[NI_MAXHOST];
++
++ /*
++ * use getaddrinfo() to resolve the hostname of the
++ * server and set ai_canonname.
++ */
++ hints.ai_family = AF_UNSPEC;
++ hints.ai_flags = AI_CANONNAME;
++ rc = getaddrinfo(host, NULL, &hints, &ai);
++ if (rc) {
++ syslog(LOG_ERR, "Unable to resolve host address: %s [%s]",
++ host, gai_strerror(rc));
++ return rc;
++ }
++
++ /* scan forward to first '.' in ai_canonnname */
++ domainname = strchr(ai->ai_canonname, '.');
++ if (!domainname) {
++ rc = -EINVAL;
++ freeaddrinfo(ai);
++ return rc;
++ }
++ lowercase_string(domainname);
++ rc = snprintf(fqdn, sizeof(fqdn), "%s%s",
++ host, domainname);
++ freeaddrinfo(ai);
++ if (rc < 0 || (size_t)rc >= sizeof(fqdn)) {
++ syslog(LOG_ERR, "Problem setting hostname in string: %ld", rc);
++ rc = -EINVAL;
++ return rc;
++ }
++
++ rc = handle_krb5_mech(oid, svc, fqdn, secblob, sess_key, ccache);
++ if (!rc)
++ return rc;
++ }
++
++ if (try_dns && (arg->have & DKD_HAVE_IP)) {
++ rc = ip_to_fqdn(arg->ip, hostbuf, sizeof(hostbuf));
++ if (rc)
++ return rc;
++ try_dns = false;
++ host = hostbuf;
++ goto retry_new_hostname;
++ }
++ return rc;
++}
++
+ int main(const int argc, char *const argv[])
+ {
+ struct cifs_spnego_msg *keydata = NULL;
+@@ -1582,9 +1641,8 @@ int main(const int argc, char *const argv[])
+ int mask;
+ bool try_dns = false, legacy_uid = false , env_probe = true;
+ char *buf;
+- char hostbuf[NI_MAXHOST], *host;
++ char *host;
+ struct decoded_args *arg = NULL;
+- const char *oid;
+ uid_t uid;
+ gid_t gid;
+ struct passwd *pw;
+@@ -1593,8 +1651,8 @@ int main(const int argc, char *const argv[])
+ krb5_ccache ccache = NULL;
+ unsigned expire_time = DNS_RESOLVER_DEFAULT_TIMEOUT;
+ const char *key_descr = NULL;
+-
+- hostbuf[0] = '\0';
++ size_t i;
++ size_t num_spns = sizeof(cifs_service_names) / sizeof(cifs_service_names[0]);
+
+ openlog(prog, 0, LOG_DAEMON);
+ mask = LOG_UPTO(LOG_ERR);
+@@ -1864,73 +1922,14 @@ int main(const int argc, char *const argv[])
+ * TRY only:
+ * cifs/bar.example.com@REALM
+ */
+- if (arg->sec == MS_KRB5)
+- oid = OID_KERBEROS5_OLD;
+- else
+- oid = OID_KERBEROS5;
+-
+-retry_new_hostname:
+- lowercase_string(host);
+- rc = handle_krb5_mech(oid, host, &secblob, &sess_key, ccache);
+- if (!rc)
+- break;
+-
+- /*
+- * If hostname has a '.', assume it's a FQDN, otherwise we
+- * want to guess the domainname.
+- */
+- if (!strchr(host, '.')) {
+- struct addrinfo hints;
+- struct addrinfo *ai;
+- char *domainname;
+- char fqdn[NI_MAXHOST];
+-
+- /*
+- * use getaddrinfo() to resolve the hostname of the
+- * server and set ai_canonname.
+- */
+- memset(&hints, 0, sizeof(hints));
+- hints.ai_family = AF_UNSPEC;
+- hints.ai_flags = AI_CANONNAME;
+- rc = getaddrinfo(host, NULL, &hints, &ai);
+- if (rc) {
+- syslog(LOG_ERR, "Unable to resolve host address: %s [%s]",
+- host, gai_strerror(rc));
+- break;
+- }
+-
+- /* scan forward to first '.' in ai_canonnname */
+- domainname = strchr(ai->ai_canonname, '.');
+- if (!domainname) {
+- rc = -EINVAL;
+- freeaddrinfo(ai);
+- break;
+- }
+- lowercase_string(domainname);
+- rc = snprintf(fqdn, sizeof(fqdn), "%s%s",
+- host, domainname);
+- freeaddrinfo(ai);
+- if (rc < 0 || (size_t)rc >= sizeof(fqdn)) {
+- syslog(LOG_ERR, "Problem setting hostname in string: %ld", rc);
+- rc = -EINVAL;
+- break;
+- }
+-
+- rc = handle_krb5_mech(oid, fqdn, &secblob, &sess_key, ccache);
++ for (i = 0; i < num_spns; i++) {
++ rc = get_spn_key_from_host(arg, &cifs_service_names[i],
++ host, &secblob, &sess_key,
++ try_dns, ccache);
+ if (!rc)
+ break;
+ }
+-
+- if (!try_dns || !(arg->have & DKD_HAVE_IP))
+- break;
+-
+- rc = ip_to_fqdn(arg->ip, hostbuf, sizeof(hostbuf));
+- if (rc)
+- break;
+-
+- try_dns = false;
+- host = hostbuf;
+- goto retry_new_hostname;
++ break;
+ default:
+ syslog(LOG_ERR, "sectype: %d is not implemented", arg->sec);
+ rc = 1;
+--
+2.55.0
+
diff --git a/cifs.upcall-fix-regression-with-krb5-creduid.patch b/cifs.upcall-fix-regression-with-krb5-creduid.patch
deleted file mode 100644
index 446c39b..0000000
--- a/cifs.upcall-fix-regression-with-krb5-creduid.patch
+++ /dev/null
@@ -1,391 +0,0 @@
-From e9495963e0d5c26f7d0137829d8dc625130b53cc Mon Sep 17 00:00:00 2001
-From: Enzo Matsumiya <ematsumiya@suse.de>
-Date: Mon, 6 Jul 2026 10:55:55 -0300
-Subject: [PATCH] cifs.upcall: fix regression with krb5 + creduid
-
-Commit 972c5b5ff95e ("cifs.upcall: remove getpwuid() dependency")
-introduced a regression when using creduid != uid (e.g.
-"mount.cifs -o sec=krb5,cruid=X"), so 'uid' local var is replaced
-with procfs "Uid" value (in the example, the one from mount.cifs).
-
-That commit ignored the fact that:
- mount UID can be different from creds UID, and that calling-app
- process (post-mount) can be different from both
-
-This patch "reverts" 972c5b5ff95e ("cifs.upcall: remove getpwuid()
-dependency"); transform the "emergency"-added function get_uidgid()
-into map_uidgid(), now only used to do NS UID/GID mapping.
-
-Also add getpwuid() back, but this time called while still on host
-namespace, so any possible custom NSS module is ran as allowed by
-sysadmin.
-
-Any scenario involving unmapped UIDs or GIDs is unsupported; this
-means that any UID:GID in a child user namespace _must_ map back to
-a valid and existing host UID:GID.
-
-Fixes: 972c5b5ff95e ("cifs.upcall: remove getpwuid() dependency")
-Reported-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
-Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
-Signed-off-by: Steve French <stfrench@microsoft.com>
----
- cifs.upcall.c | 279 ++++++++++++++++++++++++++++++++------------------
- 1 file changed, 177 insertions(+), 102 deletions(-)
-
-diff --git a/cifs.upcall.c b/cifs.upcall.c
-index 747617790576..42205e66a676 100644
---- a/cifs.upcall.c
-+++ b/cifs.upcall.c
-@@ -52,6 +52,7 @@
- #include <arpa/inet.h>
- #include <ctype.h>
- #include <grp.h>
-+#include <pwd.h>
- #include <stdbool.h>
- #include <errno.h>
- #include <sched.h>
-@@ -1375,118 +1376,170 @@ static int ip_to_fqdn(const char *addrstr, char *host, size_t hostlen)
- return 0;
- }
-
--/* cover worst case/impossible scenarios */
--#define PROC_PID_PATH_MAXLEN ((int)sizeof("/proc/2147483647/status"))
--/* max valid UID/GID is (UINT_MAX - 1) */
--#define INVALID_UIDGID UINT_MAX
-+#define ID_MAP_PATH_MAX ((int)sizeof("/proc/2147483647/uid_map"))
-
--/*
-- * get_uidgid - Get @pid's (real) UID and/or GID.
-- * @pid: process to get UID/GID from
-- * @uidp: pointer to store @pid's UID (can be NULL)
-- * @gidp: pointer to store @pid's GID (can be NULL)
-- *
-- * Extract "Uid:" and "Gid:" fields from /proc/@pid/status.
-- * Do so based on whether @uidp or @gidp are NULL.
-- *
-- * This function assumes we're on the same namespace as @pid.
-- *
-- * Return: 0 on success, -1 otherwise (errno set).
-- *
-- * On errors, *@uidp and *@gidp are set to INVALID_UIDGID.
-- */
--static int get_uidgid(pid_t pid, uid_t *uidp, gid_t *gidp)
-+static int map_id(pid_t pid, const char *map, unsigned int *idp)
- {
-- char path[PROC_PID_PATH_MAXLEN] = {}, buf[256];
-+ unsigned long long ns_start, host_start, range;
-+ char map_path[ID_MAP_PATH_MAX];
-+ int map_path_size = sizeof(map_path);
-+ unsigned int id;
- FILE *fp = NULL;
-- int ret;
-+ int ret = 1;
-
-- errno = 0;
-- if (pid < 0 || (!uidp && !gidp)) {
-- errno = EINVAL;
-- return -1;
-- }
-+ errno = EINVAL;
-+ if (pid < 0 || !map || !idp || *idp == UINT_MAX)
-+ goto out;
-
-- if (uidp)
-- *uidp = INVALID_UIDGID;
--
-- if (gidp)
-- *gidp = INVALID_UIDGID;
--
-- ret = snprintf(path, PROC_PID_PATH_MAXLEN, "/proc/%d/status", pid);
-- if (ret < 0 || ret >= PROC_PID_PATH_MAXLEN) {
-- if (!errno)
-+ ret = snprintf(map_path, map_path_size, "/proc/%d/%s", pid, map);
-+ if (ret < 0 || ret >= map_path_size) {
-+ if (ret >= map_path_size)
- errno = ENAMETOOLONG;
-- return -1;
-+ ret = 1;
-+ goto out;
- }
-
-- fp = fopen(path, "r");
-- if (!fp) {
-- ret = -1;
-+ ret = 1;
-+ fp = fopen(map_path, "r");
-+ if (!fp)
- goto out;
-- }
-
-- /* Parse /proc/pid/status fields */
-- errno = 0;
-- ret = -1;
-- while (fgets(buf, 256, fp)) {
-- unsigned long long val;
-+ /*
-+ * The map files have the same format:
-+ * <NS ID start> <host ID start> <range>
-+ * ... (<multiple entries are supported) ...
-+ *
-+ * Formula and validation:
-+ * <final NS ID> = (*@idp - <host ID start>) + <NS ID start>
-+ *
-+ * - IDs: [0, UINT_MAX - 1]
-+ * - range: [1, UINT_MAX], where range == UINT_MAX requires both ID ranges to start at 0,
-+ * which then means this is an init host NS mapping (and that's ok)
-+ *
-+ * The formula itself would be enough to "validate" a matching NS ID, but we don't want to
-+ * keep parsing a malformed map file, no matter how unlikely/impossible it is to happen.
-+ * Same reason values are parsed as 'unsigned long long', so we can check for bogus data.
-+ */
-+ id = UINT_MAX;
-+ errno = ENODATA;
-+ while (fscanf(fp, "%llu %llu %llu", &ns_start, &host_start, &range) == 3) {
-+ if (ns_start >= UINT_MAX || host_start >= UINT_MAX ||
-+ range > UINT_MAX || range == 0) {
-+ errno = EINVAL;
-+ ret = 1;
-+ break;
-+ }
-
-- errno = ENODATA;
-- if ((!uidp || strncmp(buf, "Uid:", 4)) && (!gidp || strncmp(buf, "Gid:", 4)))
-- continue;
-+ if (range == UINT_MAX && (ns_start != 0 || host_start != 0)) {
-+ errno = EINVAL;
-+ ret = 1;
-+ break;
-+ }
-
-- errno = 0;
-+ if (host_start + range > UINT_MAX || ns_start + range > UINT_MAX) {
-+ errno = EINVAL;
-+ ret = 1;
-+ break;
-+ }
-
- /*
-- * Example line format (same for both Uid/Gid):
-- * "Uid:\t%u\t%u\%u\%u"
-+ * Check if host ID fits this line.
-+ * Our desired NS ID may be in any line of the file.
- *
-- * Where the numbers represents:
-- * <real> <effective> <saved> <fsuid>
-+ * Note: new{uid,gid}map tools (that creates the map files) don't allow multiple
-+ * maps (NS IDs) to the same host ID.
- *
-- * We're only interested in the <real> value.
-+ * If we get a match here, we'll save it, but we continue parsing the file.
-+ * If we happen to find a duplicate, it's possible this is a rogue file trying to
-+ * bypass these checks.
- *
-- * (field names "Uid:"/"Gid:" parsed above, skip it)
-+ * In such cases, discard the match and return EOPNOTSUPP, as returning a
-+ * successful match could lead to disastrous results.
- */
-- ret = sscanf(&buf[0] + 4, "%llu", &val);
-- if (ret != 1) {
-- ret = -1;
-- if (errno)
-+ if (*idp >= host_start && *idp < host_start + range) {
-+ /* This means we found a duplicate */
-+ if (!ret) {
-+ ret = 1;
-+ errno = EOPNOTSUPP;
-+ syslog(LOG_ERR, "%s has multiple mapped IDs for %u (unsupported)",
-+ map, *idp);
- break;
-- continue;
-- }
-+ }
-
-- ret = -1;
-- if (val >= UINT_MAX) {
-- errno = EINVAL;
-- break;
-- }
--
-- if (uidp && !strncmp(buf, "Uid:", 4))
-- *uidp = (uid_t)val;
-- else
-- *gidp = (gid_t)val;
--
-- if ((!uidp || *uidp != INVALID_UIDGID) && (!gidp || *gidp != INVALID_UIDGID)) {
-- errno = 0;
-+ id = (*idp - host_start) + ns_start;
- ret = 0;
-- break;
-+ errno = 0;
- }
- }
-+
-+ /* This means errno was reset by fscanf() without finding anything */
-+ if (ret && errno == 0)
-+ errno = ENODATA;
- out:
-- if (fp)
-+ if (fp) {
-+ int err = errno;
-+
- fclose(fp);
-+ /* Ignore fclose() errors */
-+ errno = err;
-+ }
-
-+ if (!ret) {
-+ *idp = id;
-+ errno = 0;
-+ } else {
-+ syslog(LOG_DEBUG, "%s(pid=%d, map=%s, id=%u): %s", __func__, pid, map, *idp,
-+ strerror(errno));
-+ }
-+
-+ return ret;
-+}
-+
-+/*
-+ * map_uidgid() - Map (real) UID/GID from init host NS to user NS.
-+ * @pid: host NS PID
-+ * @uidp: (in) host UID, (out) NS UID
-+ * @gidp: (in) host GID, (out) NS GID
-+ *
-+ * Parse /proc/@pid/{uid,gid}_map files to get NS UID/GID values.
-+ * Since @pid is expected to be a host NS PID, this must be called before switching namespaces.
-+ *
-+ * Note: we can't use /proc/self here because we haven't switched NS yet, so {uid,gid}_map files
-+ * would contain host NS values.
-+ *
-+ * Return: 0 on success, 1 otherwise (errno set).
-+ */
-+static int map_uidgid(pid_t pid, uid_t *uidp, gid_t *gidp)
-+{
-+ uid_t orig_uid;
-+ gid_t orig_gid;
-+ int ret = 1;
-+
-+ errno = EINVAL;
-+ if (!uidp || !gidp)
-+ goto out;
-+
-+ orig_uid = *uidp;
-+ orig_gid = *gidp;
-+
-+ ret = map_id(pid, "uid_map", uidp);
- if (ret) {
-- syslog(LOG_DEBUG, "%s(pid=%d): %s", __func__, pid, strerror(errno));
-- if (uidp)
-- *uidp = INVALID_UIDGID;
--
-- if (gidp)
-- *gidp = INVALID_UIDGID;
-+ if (errno == ENODATA)
-+ syslog(LOG_ERR, "UID %u not mapped in this namespace (unsupported)",
-+ orig_uid);
-+ goto out;
- }
-
-+ ret = map_id(pid, "gid_map", gidp);
-+ if (ret && errno == ENODATA)
-+ syslog(LOG_ERR, "GID %u not mapped in this namespace (unsupported)",
-+ orig_gid);
-+out:
-+ if (ret && errno != ENODATA)
-+ syslog(LOG_ERR, "%s: %s", __func__, strerror(errno));
-+ else if (!ret)
-+ syslog(LOG_DEBUG, "host %u:%u -> NS %u:%u", orig_uid, orig_gid, *uidp, *gidp);
-+
- return ret;
- }
-
-@@ -1534,6 +1587,7 @@ int main(const int argc, char *const argv[])
- const char *oid;
- uid_t uid;
- gid_t gid;
-+ struct passwd *pw;
- char *keytab_name = NULL;
- char *env_cachename = NULL;
- krb5_ccache ccache = NULL;
-@@ -1680,12 +1734,52 @@ int main(const int argc, char *const argv[])
- goto out;
- }
-
-+ /*
-+ * 'uid' always points to a host UID, so we must get the corresponding host GID.
-+ * It's safe to call getpwuid() here because we're still on host NS, i.e. caller
-+ * application has no control over custom NSS modules.
-+ *
-+ * FIXME: if UID is from another NS, or a subuid, this will fail on mount.
-+ */
-+ errno = 0;
-+ pw = getpwuid(uid);
-+ if (!pw) {
-+ syslog(LOG_ERR, "failed to retrieve GID from UID %u: %s", uid,
-+ strerror(errno ? errno : ENOENT));
-+ rc = 1;
-+ goto out;
-+ }
-+
-+ gid = pw->pw_gid;
-+
-+ /*
-+ * We can't reasonably do this for root. When mounting a DFS share,
-+ * for instance we can end up with creds being overridden, but the env
-+ * variable left intact.
-+ *
-+ * Always check this before NS UID mapping.
-+ */
-+ if (uid == 0)
-+ env_probe = false;
-+
- /*
- * Change to the process's namespace. This means that things will work
- * acceptably in containers, because we'll be looking at the correct
- * filesystem and have the correct network configuration.
- */
- if (arg->upcall_target == UPTARGET_APP || arg->upcall_target == UPTARGET_UNSPECIFIED) {
-+ /*
-+ * Map host 'uid' and 'gid' to the target user NS.
-+ *
-+ * Any scenario that involves unmapped UIDs or primary GIDs is not supported -- we
-+ * don't have, and can't find, all the info that would be necessary to find a
-+ * UID/GID within all the possible NS combinations.
-+ */
-+ if (!in_same_user_ns(arg->pid, getpid())) {
-+ rc = map_uidgid(arg->pid, &uid, &gid);
-+ if (rc)
-+ goto out;
-+ }
- syslog(LOG_INFO, "upcall_target=app, switching namespaces to application thread");
- arg->upcall_target = UPTARGET_APP;
- rc = switch_to_process_ns(arg->pid);
-@@ -1700,25 +1794,6 @@ int main(const int argc, char *const argv[])
- syslog(LOG_INFO, "upcall_target=mount, not switching namespaces to application thread");
- }
-
-- /*
-- * We can't reasonably do this for root. When mounting a DFS share,
-- * for instance we can end up with creds being overridden, but the env
-- * variable left intact.
-- */
-- if (uid == 0)
-- env_probe = false;
--
-- /*
-- * FIXME: this only works if we haven't switched PID namespaces.
-- * If we did, /proc/arg->pid/ might not exist, or worse, point to something else.
-- */
-- rc = get_uidgid(arg->pid, &uid, &gid);
-- if (rc) {
-- syslog(LOG_ERR, "get_uidgid (NS): %s", strerror(errno));
-- rc = 1;
-- goto out;
-- }
--
- rc = setgid(gid);
- if (rc) {
- syslog(LOG_ERR, "setgid: %s", strerror(errno));
---
-2.54.0
-
diff --git a/sources b/sources
index f09ce0a..d25f776 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (cifs-utils-7.6.tar.bz2) = 000bca9eb0242c8c04dc1110244ad26cd39f11541cc038e36a1b36c2e683fde19d61a59234e0e592bf8d83ebebafb4d46a7e6978c745bba5e3e155e05908dcbc
+SHA512 (cifs-utils-7.7.tar.bz2) = cc94105a1ff639d2257a3e644e3b766259ef7a83725c9dddb465e8a8c7126f06a0a036a1c8cfb495943b4d1e370aae8df0bb51f85ff36e54b4894e27d8ddce29
reply other threads:[~2026-08-27 17:09 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=178785054013.1.11516730591200928640.rpms-cifs-utils-8b8dd55d853e@fedoraproject.org \
--to=paalcant@redhat.com \
--cc=git-commits@fedoraproject.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