diff --git a/.cifs-utils.metadata b/.cifs-utils.metadata new file mode 100644 index 0000000..41c0d47 --- /dev/null +++ b/.cifs-utils.metadata @@ -0,0 +1 @@ +9df055a73d89ed3d536828d0cea304c9e04139d4 cifs-utils-7.0.tar.bz2 diff --git a/Implement-CLDAP-Ping-to-find-the-closest-site.patch b/Implement-CLDAP-Ping-to-find-the-closest-site.patch new file mode 100644 index 0000000..5f1d037 --- /dev/null +++ b/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/cifs-utils.spec b/cifs-utils.spec index f9cebbc..cb0402f 100644 --- a/cifs-utils.spec +++ b/cifs-utils.spec @@ -3,7 +3,7 @@ Name: cifs-utils Version: 7.0 -Release: 4%{pre_release}%{?dist} +Release: 5%{pre_release}%{?dist} Summary: Utilities for mounting and managing CIFS mounts License: GPLv3 @@ -25,6 +25,7 @@ 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 @@ -61,6 +62,7 @@ provide these credentials to the kernel automatically at login. %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' @@ -132,6 +134,10 @@ 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