From 9fe618e7654016024b1ac4a9f8bcf1b9947588cb Mon Sep 17 00:00:00 2001 From: eabdullin Date: Mon, 30 Sep 2024 15:12:53 +0000 Subject: [PATCH] import CS cifs-utils-7.0-5.el9 --- ...-CLDAP-Ping-to-find-the-closest-site.patch | 781 ++++++++++++++++++ ...UAF-in-get_cachename_from_process_en.patch | 46 ++ ...s.rst-add-missing-reference-for-sssd.patch | 49 ++ ...pdate-section-about-xattr-acl-suppor.patch | 59 ++ ...x-warning-on-NULL-arg-passed-to-s-in.patch | 40 + SPECS/cifs-utils.spec | 29 +- 6 files changed, 1003 insertions(+), 1 deletion(-) create mode 100644 SOURCES/Implement-CLDAP-Ping-to-find-the-closest-site.patch create mode 100644 SOURCES/cifs.upcall-fix-UAF-in-get_cachename_from_process_en.patch create mode 100644 SOURCES/mount.cifs.rst-add-missing-reference-for-sssd.patch create mode 100644 SOURCES/mount.cifs.rst-update-section-about-xattr-acl-suppor.patch create mode 100644 SOURCES/pam_cifscreds-fix-warning-on-NULL-arg-passed-to-s-in.patch diff --git a/SOURCES/Implement-CLDAP-Ping-to-find-the-closest-site.patch b/SOURCES/Implement-CLDAP-Ping-to-find-the-closest-site.patch new file mode 100644 index 0000000..5f1d037 --- /dev/null +++ b/SOURCES/Implement-CLDAP-Ping-to-find-the-closest-site.patch @@ -0,0 +1,781 @@ +From 770e891a8b7ad53d4d700e08cf8d3154028b4588 Mon Sep 17 00:00:00 2001 +From: David Voit +Date: Wed, 3 Apr 2024 07:24:48 +0200 +Subject: [PATCH] Implement CLDAP Ping to find the closest site + +For domain based DFS we always need to contact the domain controllers. +On setups, which are using bigger AD installations you could get random dc on the other side of the world, +if you don't have site support. This can lead to network timeouts and other problems. + +CLDAP-Ping uses ASN.1 + UDP (CLDAP) and custom-DCE encoding including DName compressions without +field separation. Finally after finding the sitename we now need to do a DNS SRV lookups to find +the correct IPs to our closest site and fill up the remaining IPs from the global list. + +Signed-off-by: David Voit +--- + Makefile.am | 15 ++- + cldap_ping.c | 345 +++++++++++++++++++++++++++++++++++++++++++++++++ + cldap_ping.h | 14 ++ + mount.cifs.c | 5 +- + resolve_host.c | 256 ++++++++++++++++++++++++++++++++---- + resolve_host.h | 6 +- + 6 files changed, 604 insertions(+), 37 deletions(-) + create mode 100644 cldap_ping.c + create mode 100644 cldap_ping.h + +Index: cifs-utils/Makefile.am +=================================================================== +--- cifs-utils.orig/Makefile.am ++++ cifs-utils/Makefile.am +@@ -3,8 +3,8 @@ ACLOCAL_AMFLAGS = -I aclocal + + root_sbindir = $(ROOTSBINDIR) + root_sbin_PROGRAMS = mount.cifs +-mount_cifs_SOURCES = mount.cifs.c mtab.c resolve_host.c util.c +-mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) $(RT_LDADD) ++mount_cifs_SOURCES = mount.cifs.c mtab.c $(resolve_hosts_SOURCES) util.c ++mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) $(RT_LDADD) $(resolve_hosts_LDADD) + include_HEADERS = cifsidmap.h + rst_man_pages = mount.cifs.8 + +@@ -28,6 +28,9 @@ bin_PROGRAMS = + bin_SCRIPTS = + sbin_PROGRAMS = + ++resolve_hosts_SOURCES = data_blob.c asn1.c cldap_ping.c resolve_host.c ++resolve_hosts_LDADD = -ltalloc -lresolv ++ + if CONFIG_CIFSUPCALL + sbin_PROGRAMS += cifs.upcall + cifs_upcall_SOURCES = cifs.upcall.c data_blob.c asn1.c spnego.c +@@ -43,8 +46,8 @@ endif + + if CONFIG_CIFSCREDS + bin_PROGRAMS += cifscreds +-cifscreds_SOURCES = cifscreds.c cifskey.c resolve_host.c util.c +-cifscreds_LDADD = -lkeyutils ++cifscreds_SOURCES = cifscreds.c cifskey.c $(resolve_hosts_SOURCES) util.c ++cifscreds_LDADD = -lkeyutils $(resolve_hosts_LDADD) + + rst_man_pages += cifscreds.1 + +@@ -105,8 +108,8 @@ endif + if CONFIG_PAM + pam_PROGRAMS = pam_cifscreds.so + rst_man_pages += pam_cifscreds.8 +-pam_cifscreds.so: pam_cifscreds.c cifskey.c resolve_host.c util.c +- $(CC) $(DEFS) $(CFLAGS) $(AM_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ -lpam -lkeyutils ++pam_cifscreds.so: pam_cifscreds.c cifskey.c $(resolve_hosts_SOURCES) util.c ++ $(CC) $(DEFS) $(CFLAGS) $(AM_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ -lpam -lkeyutils $(resolve_hosts_LDADD) + + endif + +Index: cifs-utils/cldap_ping.c +=================================================================== +--- /dev/null ++++ cifs-utils/cldap_ping.c +@@ -0,0 +1,345 @@ ++/* ++ * CLDAP Ping to find closest ClientSiteName ++ * ++ * Copyright (C) 2024 David Voit (david.voit@gmail.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "data_blob.h" ++#include "asn1.h" ++#include "cldap_ping.h" ++ ++#define LDAP_DNS_DOMAIN "DnsDomain" ++#define LDAP_DNS_DOMAIN_LEN strlen(LDAP_DNS_DOMAIN) ++#define LDAP_NT_VERSION "NtVer" ++#define LDAP_NT_VERSION_LEN strlen(LDAP_NT_VERSION) ++#define LDAP_ATTRIBUTE_NETLOGON "NetLogon" ++#define LDAP_ATTRIBUTE_NETLOGON_LEN strlen(LDAP_ATTRIBUTE_NETLOGON) ++ ++ ++// Parse a ASN.1 BER tag size-field, returns start of payload of tag ++char *parse_ber_size(char *buf, size_t *tag_size) { ++ size_t size = *buf & 0xff; ++ char *ret = (buf + 1); ++ if (size >= 0x81) { ++ switch (size) { ++ case 0x81: ++ size = *ret & 0xff; ++ ret += 1; ++ break; ++ case 0x82: ++ size = (*ret << 8) | (*(ret + 1) & 0xff); ++ ret += 2; ++ break; ++ case 0x83: ++ size = (*ret << 16) | (*(ret + 1) << 8) | (*(ret + 2) & 0xff); ++ ret += 3; ++ break; ++ case 0x84: ++ size = (*ret << 24) | (*(ret + 1) << 16) | (*(ret + 2) << 8) | (*(ret + 3) & 0xff); ++ ret += 4; ++ break; ++ default: ++ return NULL; ++ } ++ } ++ ++ *tag_size = size; ++ return ret; ++} ++ ++// simple wrapper over dn_expand which also calculates the new offset for the next compressed dn ++int read_dns_string(char *buf, size_t buf_size, char *dest, size_t dest_size, size_t *offset) { ++ int compressed_length = dn_expand((u_char *)buf, (u_char *)buf+buf_size, (u_char *)buf + *offset, dest, (int)dest_size); ++ if (compressed_length < 0) { ++ return -1; ++ } ++ ++ *offset = *offset+compressed_length; ++ ++ return 0; ++} ++ ++// LDAP request for: (&(DnsDomain=DOMAIN_HERE)(NtVer=\\06\\00\\00\\00)) ++ASN1_DATA *generate_cldap_query(char *domain) { ++ ASN1_DATA *data; ++ TALLOC_CTX *mem_ctx = talloc_init("cldap"); ++ ++ data = asn1_init(mem_ctx); ++ asn1_push_tag(data, ASN1_SEQUENCE(0)); ++ ++ // Message id ++ asn1_push_tag(data, ASN1_INTEGER); ++ asn1_write_uint8(data, 1); ++ asn1_pop_tag(data); ++ ++ // SearchRequest ++ asn1_push_tag(data, ASN1_APPLICATION(3)); ++ ++ // empty baseObject ++ asn1_push_tag(data, ASN1_OCTET_STRING); ++ asn1_pop_tag(data); ++ ++ // scope 0 = baseObject ++ asn1_push_tag(data, ASN1_ENUMERATED); ++ asn1_write_uint8(data, 0); ++ asn1_pop_tag(data); ++ ++ // derefAliasses 0=neverDerefAlias ++ asn1_push_tag(data, ASN1_ENUMERATED); ++ asn1_write_uint8(data, 0); ++ asn1_pop_tag(data); ++ ++ // sizeLimit ++ asn1_push_tag(data, ASN1_INTEGER); ++ asn1_write_uint8(data, 0); ++ asn1_pop_tag(data); ++ ++ // timeLimit ++ asn1_push_tag(data, ASN1_INTEGER); ++ asn1_write_uint8(data, 0); ++ asn1_pop_tag(data); ++ ++ // typesOnly ++ asn1_push_tag(data, ASN1_BOOLEAN); ++ asn1_write_uint8(data, 0); ++ asn1_pop_tag(data); ++ ++ // AND ++ asn1_push_tag(data, ASN1_CONTEXT(0)); ++ // equalityMatch ++ asn1_push_tag(data, ASN1_CONTEXT(3)); ++ asn1_write_OctetString(data, LDAP_DNS_DOMAIN, LDAP_DNS_DOMAIN_LEN); ++ asn1_write_OctetString(data, domain, strlen(domain)); ++ asn1_pop_tag(data); ++ ++ // equalityMatch ++ asn1_push_tag(data, ASN1_CONTEXT(3)); ++ asn1_write_OctetString(data, LDAP_NT_VERSION, LDAP_NT_VERSION_LEN); ++ // Bitmask NETLOGON_NT_VERSION_5 & NETLOGON_NT_VERSION_5EX -> To get NETLOGON_SAM_LOGON_RESPONSE_EX as response ++ asn1_write_OctetString(data, "\x06\x00\x00\x00", 4); ++ asn1_pop_tag(data); ++ ++ // End AND ++ asn1_pop_tag(data); ++ ++ asn1_push_tag(data, ASN1_SEQUENCE(0)); ++ asn1_write_OctetString(data, LDAP_ATTRIBUTE_NETLOGON, LDAP_ATTRIBUTE_NETLOGON_LEN); ++ asn1_pop_tag(data); ++ ++ // End SearchRequest ++ asn1_pop_tag(data); ++ // End Sequence ++ asn1_pop_tag(data); ++ ++ return data; ++} ++ ++// Input is a cldap response, output is a pointer to the NETLOGON_SAM_LOGON_RESPONSE_EX payload ++ssize_t extract_netlogon_section(char *buffer, size_t buffer_size, char **netlogon_payload) { ++ size_t ber_size; ++ size_t netlogon_payload_size; ++ // Not enough space to read initial sequence - not an correct cldap response ++ if (buffer_size < 7) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ // Sequence tag ++ if (*buffer != 0x30) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *message_id_tag = parse_ber_size(buffer + 1, &ber_size); ++ ++ if (ber_size > buffer_size) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ if (*message_id_tag != 0x02) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *message_id = parse_ber_size(message_id_tag + 1, &ber_size); ++ ++ if (ber_size != 1 || *message_id != 1) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ // SearchResultEntry ++ if (*(message_id+1) != 0x64) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *object_name_tag = parse_ber_size(message_id+2, &ber_size); ++ if (object_name_tag == NULL) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *object_name = parse_ber_size(object_name_tag+1, &ber_size); ++ if (object_name == NULL) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ if (*object_name_tag != 4 || ber_size != 0) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *partial_attribute_list_tag = parse_ber_size(object_name+1, &ber_size); ++ if (partial_attribute_list_tag == NULL) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ if (*partial_attribute_list_tag != 0x30) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ ++ char *partial_attribute_tag = parse_ber_size(partial_attribute_list_tag+1, &ber_size); ++ if (partial_attribute_tag == NULL) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *attribute_name = parse_ber_size(partial_attribute_tag+1, &ber_size); ++ if (attribute_name == NULL) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ if (ber_size != LDAP_ATTRIBUTE_NETLOGON_LEN) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ if (strncasecmp(LDAP_ATTRIBUTE_NETLOGON, attribute_name, LDAP_ATTRIBUTE_NETLOGON_LEN) != 0) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ // SET ++ if (*(attribute_name+LDAP_ATTRIBUTE_NETLOGON_LEN) != 0x31) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ char *start_of_data = parse_ber_size(attribute_name+LDAP_ATTRIBUTE_NETLOGON_LEN+1, &ber_size); ++ if (start_of_data == NULL) { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ // octat-string of NetLogon data ++ if (*start_of_data != '\x04') { ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ *netlogon_payload = parse_ber_size(start_of_data + 1, &netlogon_payload_size); ++ ++ if (*netlogon_payload == NULL) { ++ *netlogon_payload = NULL; ++ return CLDAP_PING_PARSE_ERROR_LDAP; ++ } ++ ++ return (ssize_t)netlogon_payload_size; ++} ++ ++int netlogon_get_client_site(char *netlogon_response, size_t netlogon_size, char *sitename) { ++ // 24 mandatory bytes ++ if (netlogon_size < 25) { ++ return CLDAP_PING_PARSE_ERROR_NETLOGON; ++ } ++ ++ // LOGON_SAM_PAUSE_RESPONSE_EX -> Netlogon service is not in-sync try next dc instead ++ if (*netlogon_response == 0x18 && *(netlogon_response + 1) == 0x00) { ++ return CLDAP_PING_TRYNEXT; ++ } ++ ++ // NETLOGON_SAM_LOGON_RESPONSE_EX Opcode: 0x17 ++ if (*netlogon_response != 0x17 || *(netlogon_response + 1) != 0x00) { ++ return CLDAP_PING_PARSE_ERROR_NETLOGON; ++ } ++ ++ // skip over sbz, ds_flags and domain_guid ++ // and start directly at variable string portion of NETLOGON_SAM_LOGON_RESPONSE_EX ++ size_t offset = 24; ++ ++ for (int i=0; i < 8; i++) { ++ // iterate over DnsForestName, DnsDomainName, NetbiosDomainName, NetbiosComputerName, UserName, DcSiteName ++ // to finally get to our desired ClientSiteName field ++ if (read_dns_string(netlogon_response, netlogon_size, sitename, MAXCDNAME, &offset) < 0) { ++ return CLDAP_PING_PARSE_ERROR_NETLOGON; ++ } ++ } ++ ++ return 0; ++} ++ ++int cldap_ping(char *domain, sa_family_t family, void *addr, char *site_name) { ++ char buffer[8196]; ++ ssize_t response_size; ++ char *netlogon_response; ++ ssize_t netlogon_size; ++ struct sockaddr_storage socketaddr; ++ size_t addr_size; ++ int sock = socket(family, SOCK_DGRAM, 0); ++ if (sock < 0) { ++ return CLDAP_PING_NETWORK_ERROR; ++ } ++ ++ ASN1_DATA *data = generate_cldap_query(domain); ++ ++ if (family == AF_INET6) { ++ addr_size = sizeof(struct sockaddr_in6); ++ bzero((void *) &socketaddr, addr_size); ++ socketaddr.ss_family = AF_INET6; ++ ((struct sockaddr_in6 *)&socketaddr)->sin6_addr = *((struct in6_addr*)addr); ++ ((struct sockaddr_in6 *)&socketaddr)->sin6_port = htons(389); ++ } else { ++ addr_size = sizeof(struct sockaddr_in); ++ bzero((void *) &socketaddr, addr_size); ++ socketaddr.ss_family = AF_INET; ++ ((struct sockaddr_in *)&socketaddr)->sin_addr = *((struct in_addr*)addr); ++ ((struct sockaddr_in *)&socketaddr)->sin_port = htons(389); ++ } ++ ++ struct timeval timeout = {.tv_sec = 2, .tv_usec = 0}; ++ if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) { ++ return CLDAP_PING_NETWORK_ERROR; ++ } ++ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { ++ return CLDAP_PING_NETWORK_ERROR; ++ } ++ ++ if (sendto(sock, data->data, data->length, 0, (struct sockaddr *)&socketaddr, addr_size) < 0) { ++ close(sock); ++ return CLDAP_PING_TRYNEXT; ++ } ++ ++ asn1_free(data); ++ response_size = recv(sock, buffer, sizeof(buffer), 0); ++ close(sock); ++ ++ if (response_size < 0) { ++ return CLDAP_PING_TRYNEXT; ++ } ++ ++ netlogon_size = extract_netlogon_section(buffer, response_size, &netlogon_response); ++ if (netlogon_size < 0) { ++ return (int)netlogon_size; ++ } ++ ++ return netlogon_get_client_site(netlogon_response, netlogon_size, site_name); ++} +Index: cifs-utils/cldap_ping.h +=================================================================== +--- /dev/null ++++ cifs-utils/cldap_ping.h +@@ -0,0 +1,14 @@ ++#ifndef _CLDAP_PING_H_ ++#define _CLDAP_PING_H_ ++ ++#define CLDAP_PING_NETWORK_ERROR -1 ++#define CLDAP_PING_TRYNEXT -2 ++#define CLDAP_PING_PARSE_ERROR_LDAP -3 ++#define CLDAP_PING_PARSE_ERROR_NETLOGON -4 ++ ++// returns CLDAP_PING_TRYNEXT if you should use another dc ++// any other error code < 0 is a fatal error ++// site_name must be of MAXCDNAME size! ++int cldap_ping(char *domain, sa_family_t family, void *addr, char *site_name); ++ ++#endif /* _CLDAP_PING_H_ */ +Index: cifs-utils/mount.cifs.c +=================================================================== +--- cifs-utils.orig/mount.cifs.c ++++ cifs-utils/mount.cifs.c +@@ -1889,8 +1889,11 @@ assemble_mountinfo(struct parsed_mount_i + if (rc) + goto assemble_exit; + +- if (parsed_info->addrlist[0] == '\0') ++ if (parsed_info->addrlist[0] == '\0') { + rc = resolve_host(parsed_info->host, parsed_info->addrlist); ++ if (rc == 0 && parsed_info->verboseflag) ++ fprintf(stderr, "Host \"%s\" resolved to the following IP addresses: %s\n", parsed_info->host, parsed_info->addrlist); ++ } + + switch (rc) { + case EX_USAGE: +Index: cifs-utils/resolve_host.c +=================================================================== +--- cifs-utils.orig/resolve_host.c ++++ cifs-utils/resolve_host.c +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2010 Jeff Layton (jlayton@samba.org) + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com) ++ * Copyright (C) 2024 David Voit (david.voit@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -27,15 +28,16 @@ + #include + #include + #include ++#include + #include "mount.h" + #include "util.h" ++#include "cldap_ping.h" + #include "resolve_host.h" + + /* + * resolve hostname to comma-separated list of address(es) + */ +-int resolve_host(const char *host, char *addrstr) +-{ ++int resolve_host(const char *host, char *addrstr) { + int rc; + /* 10 for max width of decimal scopeid */ + char tmpbuf[NI_MAXHOST + 1 + 10 + 1]; +@@ -44,6 +46,7 @@ int resolve_host(const char *host, char + struct addrinfo *addrlist, *addr; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; ++ size_t count_v4 = 0, count_v6 = 0; + + rc = getaddrinfo(host, NULL, NULL, &addrlist); + if (rc != 0) +@@ -59,34 +62,45 @@ int resolve_host(const char *host, char + } + + switch (addr->ai_addr->sa_family) { +- case AF_INET6: +- sin6 = (struct sockaddr_in6 *)addr->ai_addr; +- ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, +- sizeof(tmpbuf)); +- if (!ipaddr) { +- rc = EX_SYSERR; +- goto resolve_host_out; +- } +- +- if (sin6->sin6_scope_id) { +- len = strnlen(tmpbuf, sizeof(tmpbuf)); +- snprintf(tmpbuf + len, sizeof(tmpbuf) - len, "%%%u", +- sin6->sin6_scope_id); +- } +- break; +- case AF_INET: +- sin = (struct sockaddr_in *)addr->ai_addr; +- ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, +- sizeof(tmpbuf)); +- if (!ipaddr) { +- rc = EX_SYSERR; +- goto resolve_host_out; +- } +- +- break; +- default: +- addr = addr->ai_next; +- continue; ++ case AF_INET6: ++ count_v6++; ++ if (count_v6 + count_v4 > MAX_ADDRESSES) { ++ addr = addr->ai_next; ++ continue; ++ } ++ ++ sin6 = (struct sockaddr_in6 *) addr->ai_addr; ++ ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, ++ sizeof(tmpbuf)); ++ if (!ipaddr) { ++ rc = EX_SYSERR; ++ goto resolve_host_out; ++ } ++ ++ ++ if (sin6->sin6_scope_id) { ++ len = strnlen(tmpbuf, sizeof(tmpbuf)); ++ snprintf(tmpbuf + len, sizeof(tmpbuf) - len, "%%%u", ++ sin6->sin6_scope_id); ++ } ++ break; ++ case AF_INET: ++ count_v4++; ++ if (count_v6 + count_v4 > MAX_ADDRESSES) { ++ addr = addr->ai_next; ++ continue; ++ } ++ sin = (struct sockaddr_in *) addr->ai_addr; ++ ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, ++ sizeof(tmpbuf)); ++ if (!ipaddr) { ++ rc = EX_SYSERR; ++ goto resolve_host_out; ++ } ++ break; ++ default: ++ addr = addr->ai_next; ++ continue; + } + + if (addr == addrlist) +@@ -98,6 +112,192 @@ int resolve_host(const char *host, char + addr = addr->ai_next; + } + ++ ++ // Is this a DFS domain where we need to do a cldap ping to find the closest node? ++ if (count_v4 > 1 || count_v6 > 1) { ++ int res; ++ ns_msg global_domain_handle; ++ unsigned char global_domain_lookup[4096]; ++ ns_msg site_domain_handle; ++ unsigned char site_domain_lookup[4096]; ++ char dname[MAXCDNAME]; ++ int srv_cnt; ++ ++ res = res_init(); ++ if (res != 0) ++ goto resolve_host_out; ++ ++ res = snprintf(dname, MAXCDNAME, "_ldap._tcp.dc._msdcs.%s", host); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ res = res_query(dname, C_IN, ns_t_srv, global_domain_lookup, sizeof(global_domain_lookup)); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ // res is also the size of the response_buffer ++ res = ns_initparse(global_domain_lookup, res, &global_domain_handle); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ srv_cnt = ns_msg_count (global_domain_handle, ns_s_an); ++ ++ // No or just one DC we are done ++ if (srv_cnt < 2) ++ goto resolve_host_out; ++ ++ char site_name[MAXCDNAME]; ++ // We assume that AD always sends the ip addresses in the addtional data block ++ for (int i = 0; i < ns_msg_count(global_domain_handle, ns_s_ar); i++) { ++ ns_rr rr; ++ res = ns_parserr(&global_domain_handle, ns_s_ar, i, &rr); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ switch (ns_rr_type(rr)) { ++ case ns_t_aaaa: ++ if (ns_rr_rdlen(rr) != NS_IN6ADDRSZ) ++ continue; ++ res = cldap_ping((char *) host, AF_INET6, (void *)ns_rr_rdata(rr), site_name); ++ break; ++ case ns_t_a: ++ if (ns_rr_rdlen(rr) != NS_INADDRSZ) ++ continue; ++ res = cldap_ping((char *) host, AF_INET, (void *)ns_rr_rdata(rr), site_name); ++ break; ++ default: ++ continue; ++ } ++ ++ if (res == CLDAP_PING_TRYNEXT) { ++ continue; ++ } ++ ++ if (res < 0) { ++ goto resolve_host_out; ++ } ++ ++ if (site_name[0] == '\0') { ++ goto resolve_host_out; ++ } else { ++ // site found - leave loop ++ break; ++ } ++ } ++ ++ res = snprintf(dname, MAXCDNAME, "_ldap._tcp.%s._sites.dc._msdcs.%s", site_name, host); ++ if (res < 0) { ++ goto resolve_host_out; ++ } ++ ++ res = res_query(dname, C_IN, ns_t_srv, site_domain_lookup, sizeof(site_domain_lookup)); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ // res is also the size of the response_buffer ++ res = ns_initparse(site_domain_lookup, res, &site_domain_handle); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ int number_addresses = 0; ++ for (int i = 0; i < ns_msg_count(site_domain_handle, ns_s_ar); i++) { ++ if (i > MAX_ADDRESSES) ++ break; ++ ++ ns_rr rr; ++ res = ns_parserr(&site_domain_handle, ns_s_ar, i, &rr); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ switch (ns_rr_type(rr)) { ++ case ns_t_aaaa: ++ if (ns_rr_rdlen(rr) != NS_IN6ADDRSZ) ++ continue; ++ ipaddr = inet_ntop(AF_INET6, ns_rr_rdata(rr), tmpbuf, ++ sizeof(tmpbuf)); ++ if (!ipaddr) { ++ rc = EX_SYSERR; ++ goto resolve_host_out; ++ } ++ break; ++ case ns_t_a: ++ if (ns_rr_rdlen(rr) != NS_INADDRSZ) ++ continue; ++ ipaddr = inet_ntop(AF_INET, ns_rr_rdata(rr), tmpbuf, ++ sizeof(tmpbuf)); ++ if (!ipaddr) { ++ rc = EX_SYSERR; ++ goto resolve_host_out; ++ } ++ break; ++ default: ++ continue; ++ } ++ ++ number_addresses++; ++ ++ if (i == 0) ++ *addrstr = '\0'; ++ else ++ strlcat(addrstr, ",", MAX_ADDR_LIST_LEN); ++ ++ strlcat(addrstr, tmpbuf, MAX_ADDR_LIST_LEN); ++ } ++ ++ // Preferred site ips is now the first entry in addrstr, fill up with other sites till MAX_ADDRESS ++ for (int i = 0; i < ns_msg_count(global_domain_handle, ns_s_ar); i++) { ++ if (number_addresses > MAX_ADDRESSES) ++ break; ++ ++ ns_rr rr; ++ res = ns_parserr(&global_domain_handle, ns_s_ar, i, &rr); ++ if (res < 0) ++ goto resolve_host_out; ++ ++ switch (ns_rr_type(rr)) { ++ case ns_t_aaaa: ++ if (ns_rr_rdlen(rr) != NS_IN6ADDRSZ) ++ continue; ++ ipaddr = inet_ntop(AF_INET6, ns_rr_rdata(rr), tmpbuf, ++ sizeof(tmpbuf)); ++ if (!ipaddr) { ++ rc = EX_SYSERR; ++ goto resolve_host_out; ++ } ++ break; ++ case ns_t_a: ++ if (ns_rr_rdlen(rr) != NS_INADDRSZ) ++ continue; ++ ipaddr = inet_ntop(AF_INET, ns_rr_rdata(rr), tmpbuf, ++ sizeof(tmpbuf)); ++ if (!ipaddr) { ++ rc = EX_SYSERR; ++ goto resolve_host_out; ++ } ++ break; ++ default: ++ continue; ++ } ++ ++ char *found = strstr(addrstr, tmpbuf); ++ ++ if (found) { ++ // We only have a real match if the substring is between ',' or it's the last/first entry in the list ++ char previous_seperator = found > addrstr ? *(found-1) : '\0'; ++ char next_seperator = *(found+strlen(tmpbuf)); ++ ++ if ((next_seperator == ',' || next_seperator == '\0') ++ && (previous_seperator == ',' || previous_seperator == '\0')) { ++ continue; ++ } ++ } ++ ++ number_addresses++; ++ strlcat(addrstr, ",", MAX_ADDR_LIST_LEN); ++ strlcat(addrstr, tmpbuf, MAX_ADDR_LIST_LEN); ++ } ++ } ++ + resolve_host_out: + freeaddrinfo(addrlist); + return rc; +Index: cifs-utils/resolve_host.h +=================================================================== +--- cifs-utils.orig/resolve_host.h ++++ cifs-utils/resolve_host.h +@@ -26,8 +26,10 @@ + /* currently maximum length of IPv6 address string */ + #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN + +-/* limit list of addresses to 16 max-size addrs */ +-#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * 16) ++#define MAX_ADDRESSES 16 ++ ++/* limit list of addresses to MAX_ADDRESSES max-size addrs */ ++#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * MAX_ADDRESSES) + + extern int resolve_host(const char *host, char *addrstr); + diff --git a/SOURCES/cifs.upcall-fix-UAF-in-get_cachename_from_process_en.patch b/SOURCES/cifs.upcall-fix-UAF-in-get_cachename_from_process_en.patch new file mode 100644 index 0000000..156ff46 --- /dev/null +++ b/SOURCES/cifs.upcall-fix-UAF-in-get_cachename_from_process_en.patch @@ -0,0 +1,46 @@ +From 73146385da0945c78af0fbdc08d2bf260db709d5 Mon Sep 17 00:00:00 2001 +From: Paulo Alcantara +Date: Fri, 8 Mar 2024 12:06:15 -0300 +Subject: [PATCH] cifs.upcall: fix UAF in get_cachename_from_process_env() + +Whether lseek(2) fails or @bufsize * 2 > ENV_BUF_MAX, then @buf would +end up being freed twice. For instance: + + cifs-utils-7.0/cifs.upcall.c:501: freed_arg: "free" frees "buf". + cifs-utils-7.0/cifs.upcall.c:524: double_free: Calling "free" frees + pointer "buf" which has already been freed. + 522| } + 523| out_close: + 524|-> free(buf); + 525| close(fd); + 526| return cachename; + +Fix this by setting @buf to NULL after freeing it to prevent UAF. + +Fixes: ed97e4ecab4e ("cifs.upcall: allow scraping of KRB5CCNAME out of initiating task's /proc//environ file") +Signed-off-by: Paulo Alcantara (Red Hat) +--- + cifs.upcall.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/cifs.upcall.c b/cifs.upcall.c +index 52c03280dbe0..ff6f2bd271bc 100644 +--- a/cifs.upcall.c ++++ b/cifs.upcall.c +@@ -498,10 +498,11 @@ retry: + /* We read to the end of the buffer. Double and try again */ + syslog(LOG_DEBUG, "%s: read to end of buffer (%zu bytes)\n", + __func__, bufsize); +- free(buf); +- bufsize *= 2; + if (lseek(fd, 0, SEEK_SET) < 0) + goto out_close; ++ free(buf); ++ buf = NULL; ++ bufsize *= 2; + goto retry; + } + +-- +2.44.0 + diff --git a/SOURCES/mount.cifs.rst-add-missing-reference-for-sssd.patch b/SOURCES/mount.cifs.rst-add-missing-reference-for-sssd.patch new file mode 100644 index 0000000..f5dbe18 --- /dev/null +++ b/SOURCES/mount.cifs.rst-add-missing-reference-for-sssd.patch @@ -0,0 +1,49 @@ +From e7ec0032898d855be144c0cdc9d9e3f78ae01bf2 Mon Sep 17 00:00:00 2001 +From: Paulo Alcantara +Date: Sun, 10 Mar 2024 22:24:24 -0300 +Subject: [PATCH 1/2] mount.cifs.rst: add missing reference for sssd + +Reference sssd in mount.cifs(8) as it can be used instead of winbind +via cifs.idmap utility. It's also enabled by default in most systems. + +Signed-off-by: Paulo Alcantara (Red Hat) +--- + mount.cifs.rst | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/mount.cifs.rst b/mount.cifs.rst +index 3becf200e038..64127b23cf17 100644 +--- a/mount.cifs.rst ++++ b/mount.cifs.rst +@@ -773,10 +773,10 @@ specified in the following Microsoft TechNet document: + In order to map SIDs to/from UIDs and GIDs, the following is required: + + - a kernel upcall to the ``cifs.idmap`` utility set up via request-key.conf(5) +-- winbind support configured via nsswitch.conf(5) and smb.conf(5) ++- winbind or sssd support configured via nsswitch.conf(5) + +-Please refer to the respective manpages of cifs.idmap(8) and +-winbindd(8) for more information. ++Please refer to the respective manpages of cifs.idmap(8), winbindd(8) ++and sssd(8) for more information. + + Security descriptors for a file object can be retrieved and set + directly using extended attribute named ``system.cifs_acl``. The +@@ -792,10 +792,10 @@ Some of the things to consider while using this mount option: + - The mapping between a CIFS/NTFS ACL and POSIX file permission bits + is imperfect and some ACL information may be lost in the + translation. +-- If either upcall to cifs.idmap is not setup correctly or winbind is +- not configured and running, ID mapping will fail. In that case uid +- and gid will default to either to those values of the share or to +- the values of uid and/or gid mount options if specified. ++- If either upcall to cifs.idmap is not setup correctly or winbind or ++ sssd is not configured and running, ID mapping will fail. In that ++ case uid and gid will default to either to those values of the share ++ or to the values of uid and/or gid mount options if specified. + + ********************************** + ACCESSING FILES WITH BACKUP INTENT +-- +2.44.0 + diff --git a/SOURCES/mount.cifs.rst-update-section-about-xattr-acl-suppor.patch b/SOURCES/mount.cifs.rst-update-section-about-xattr-acl-suppor.patch new file mode 100644 index 0000000..8cb506c --- /dev/null +++ b/SOURCES/mount.cifs.rst-update-section-about-xattr-acl-suppor.patch @@ -0,0 +1,59 @@ +From 4718e09e4b15b957bf9d729793bc3de7caad8134 Mon Sep 17 00:00:00 2001 +From: Paulo Alcantara +Date: Sun, 10 Mar 2024 22:24:25 -0300 +Subject: [PATCH 2/2] mount.cifs.rst: update section about xattr/acl support + +Update section about required xattr/acl support for UID/GID mapping. + +Signed-off-by: Paulo Alcantara (Red Hat) +--- + mount.cifs.rst | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +diff --git a/mount.cifs.rst b/mount.cifs.rst +index 64127b23cf17..d82a13c932b3 100644 +--- a/mount.cifs.rst ++++ b/mount.cifs.rst +@@ -321,11 +321,12 @@ soft + noacl + Do not allow POSIX ACL operations even if server would support them. + +- The CIFS client can get and set POSIX ACLs (getfacl, setfacl) to Samba +- servers version 3.0.10 and later. Setting POSIX ACLs requires enabling +- both ``CIFS_XATTR`` and then ``CIFS_POSIX`` support in the CIFS +- configuration options when building the cifs module. POSIX ACL support +- can be disabled on a per mount basis by specifying ``noacl`` on mount. ++ The CIFS client can get and set POSIX ACLs (getfacl, setfacl) to ++ Samba servers version 3.0.10 and later. Setting POSIX ACLs requires ++ enabling both ``CONFIG_CIFS_XATTR`` and then ``CONFIG_CIFS_POSIX`` ++ support in the CIFS configuration options when building the cifs ++ module. POSIX ACL support can be disabled on a per mount basis by ++ specifying ``noacl`` on mount. + + cifsacl + This option is used to map CIFS/NTFS ACLs to/from Linux permission +@@ -762,8 +763,19 @@ bits, and POSIX ACL as user authentication model. This is the most + common authentication model for CIFS servers and is the one used by + Windows. + +-Support for this requires both CIFS_XATTR and CIFS_ACL support in the +-CIFS configuration options when building the cifs module. ++Support for this requires cifs kernel module built with both ++``CONFIG_CIFS_XATTR`` and ``CONFIG_CIFS_ACL`` options enabled. Since ++Linux 5.3, ``CONFIG_CIFS_ACL`` option no longer exists as CIFS/NTFS ++ACL support is always built into cifs kernel module. ++ ++Most distribution kernels will already have those options enabled by ++default, but you can still check if they are enabled with:: ++ ++ cat /lib/modules/$(uname -r)/build/.config ++ ++Alternatively, if kernel is configured with ``CONFIG_IKCONFIG_PROC``:: ++ ++ zcat /proc/config.gz + + A CIFS/NTFS ACL is mapped to file permission bits using an algorithm + specified in the following Microsoft TechNet document: +-- +2.44.0 + diff --git a/SOURCES/pam_cifscreds-fix-warning-on-NULL-arg-passed-to-s-in.patch b/SOURCES/pam_cifscreds-fix-warning-on-NULL-arg-passed-to-s-in.patch new file mode 100644 index 0000000..ae27a68 --- /dev/null +++ b/SOURCES/pam_cifscreds-fix-warning-on-NULL-arg-passed-to-s-in.patch @@ -0,0 +1,40 @@ +From dac330136368a9b8d9ccf8227f56ea35de57a4d2 Mon Sep 17 00:00:00 2001 +From: Paulo Alcantara +Date: Fri, 8 Mar 2024 13:25:22 -0300 +Subject: [PATCH] pam_cifscreds: fix warning on NULL arg passed to %s in + pam_syslog() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix the following compiler warning with -Wformat-overflow in +cifscreds_pam_update(): + + pam_cifscreds.c: In function ‘cifscreds_pam_update’: + pam_cifscreds.c:340:83: warning: ‘%s’ directive argument is null + [-Wformat-overflow=] + 340 | pam_syslog(ph, LOG_ERR, "error: Update credential key for %s: %s", + | ^~ + +Fixes: cbbcd6e71c0a ("cifscreds: create PAM module to insert credentials at login") +Signed-off-by: Paulo Alcantara (Red Hat) +--- + pam_cifscreds.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pam_cifscreds.c b/pam_cifscreds.c +index 5d99c2db3038..eb9851d52a7d 100644 +--- a/pam_cifscreds.c ++++ b/pam_cifscreds.c +@@ -338,7 +338,7 @@ static int cifscreds_pam_update(pam_handle_t *ph, const char *user, const char * + key_serial_t key = key_add(currentaddress, user, password, keytype); + if (key <= 0) { + pam_syslog(ph, LOG_ERR, "error: Update credential key for %s: %s", +- currentaddress, strerror(errno)); ++ (currentaddress ?: "(null)"), strerror(errno)); + } + } + +-- +2.44.0 + diff --git a/SPECS/cifs-utils.spec b/SPECS/cifs-utils.spec index 6028994..cb0402f 100644 --- a/SPECS/cifs-utils.spec +++ b/SPECS/cifs-utils.spec @@ -3,7 +3,7 @@ Name: cifs-utils Version: 7.0 -Release: 1%{pre_release}%{?dist} +Release: 5%{pre_release}%{?dist} Summary: Utilities for mounting and managing CIFS mounts License: GPLv3 @@ -21,6 +21,11 @@ Requires(preun): /usr/sbin/alternatives Recommends: %{name}-info%{?_isa} = %{version}-%{release} Source0: https://download.samba.org/pub/linux-cifs/cifs-utils/%{name}-%{version}.tar.bz2 +Patch0: cifs.upcall-fix-UAF-in-get_cachename_from_process_en.patch +Patch1: pam_cifscreds-fix-warning-on-NULL-arg-passed-to-s-in.patch +Patch2: mount.cifs.rst-add-missing-reference-for-sssd.patch +Patch3: mount.cifs.rst-update-section-about-xattr-acl-suppor.patch +Patch4: Implement-CLDAP-Ping-to-find-the-closest-site.patch %description The SMB/CIFS protocol is a standard file sharing protocol widely deployed @@ -53,6 +58,11 @@ provide these credentials to the kernel automatically at login. %prep %setup -q -n %{name}-%{version}%{pre_release} +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 %build fgrep -r -l '/usr/bin/env python' | xargs -n1 sed -i 's@/usr/bin/env python.*@%python3@g' @@ -124,6 +134,23 @@ about CIFS mount. %{_mandir}/man1/smbinfo.* %changelog +* Mon Apr 22 2024 Paulo Alcantara - 7.0-5 +- Implement CLDAP Ping to find the closest site +- Resolves: RHELPLAN-17597 + +* Tue Apr 16 2024 Paulo Alcantara - 7.0-4 +- mount.cifs.rst: add missing reference for sssd +- mount.cifs.rst: update section about xattr/acl support +- Resolves: RHEL-22495 + +* Fri Apr 12 2024 Paulo Alcantara - 7.0-3 +- pam_cifscreds: fix NULL arg warning passed to pam_syslog() +- Resolves: RHEL-28050 + +* Fri Apr 12 2024 Paulo Alcantara - 7.0-2 +- cifs.upcall: fix UAF in get_cachename_from_process_env() +- Resolves: RHEL-28047 + * Mon Jan 30 2023 Pavel Filipenský - 7.0-1 - Update to cifs-utils-7.0 - Resolves: rhbz#2163303