msktname.cpp (msktutil-1.1.tar.bz2) | : | msktname.cpp (msktutil-1.2.1) | ||
---|---|---|---|---|
skipping to change at line 36 | skipping to change at line 36 | |||
along with this program; if not, write to the Free Software | along with this program; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||
* | * | |||
*----------------------------------------------------------------------------- | *----------------------------------------------------------------------------- | |||
*/ | */ | |||
#include "msktutil.h" | #include "msktutil.h" | |||
#include <algorithm> | #include <algorithm> | |||
#include <cctype> | #include <cctype> | |||
#include <sstream> | ||||
std::string complete_hostname(const std::string &hostname, | std::string complete_hostname(const std::string &hostname, | |||
bool no_canonical_name) | bool no_canonical_name) | |||
{ | { | |||
/* Ask the kerberos lib to canonicalize the hostname, and then | /* Ask the kerberos lib to canonicalize the hostname, and then | |||
* pull it out of the principal. */ | * pull it out of the principal. */ | |||
int32_t type = KRB5_NT_SRV_HST; | int32_t type = KRB5_NT_SRV_HST; | |||
krb5_principal temp_princ_raw = NULL; | krb5_principal temp_princ_raw = NULL; | |||
/* do not canonicalize, use supplied hostname */ | /* do not canonicalize, use supplied hostname */ | |||
skipping to change at line 57 | skipping to change at line 58 | |||
type = KRB5_NT_UNKNOWN; | type = KRB5_NT_UNKNOWN; | |||
} | } | |||
krb5_error_code ret = krb5_sname_to_principal(g_context, | krb5_error_code ret = krb5_sname_to_principal(g_context, | |||
hostname.c_str(), | hostname.c_str(), | |||
"host", | "host", | |||
type, | type, | |||
&temp_princ_raw); | &temp_princ_raw); | |||
if (ret != 0) { | if (ret != 0) { | |||
fprintf(stderr, | fprintf(stderr, | |||
"Warning: hostname canonicalization for %s failed (%s)\n", | "Warning: hostname canonicalization for %s failed: %s\n", | |||
hostname.c_str(), | hostname.c_str(), | |||
error_message(ret) | error_message(ret) | |||
); | ); | |||
return hostname; | return hostname; | |||
} | } | |||
KRB5Principal temp_princ(temp_princ_raw); | KRB5Principal temp_princ(temp_princ_raw); | |||
#ifdef HEIMDAL | #ifdef HEIMDAL | |||
const char *comp = krb5_principal_get_comp_string(g_context, | const char *comp = krb5_principal_get_comp_string(g_context, | |||
skipping to change at line 135 | skipping to change at line 136 | |||
"hostname, strange value from " | "hostname, strange value from " | |||
"krb5_sname_to_principal."); | "krb5_sname_to_principal."); | |||
} | } | |||
#ifdef HEIMDAL | #ifdef HEIMDAL | |||
return std::string(comp); | return std::string(comp); | |||
#else | #else | |||
return std::string(comp->data, comp->length); | return std::string(comp->data, comp->length); | |||
#endif | #endif | |||
} | } | |||
bool DnsSrvHost::validate(bool nocanon) { | bool DnsSrvHost::validate(bool nocanon, std::string service) { | |||
int sock = -1; | int ret, sock = -1; | |||
struct hostent *host; | /* used to call into C function, so we prefer char[] over std::string */ | |||
char host[NI_MAXHOST]; | ||||
struct addrinfo *hostaddrinfo = NULL; | ||||
// The order of the struct addrinfo members is not portable, | ||||
// therefore it is not possible to use struct initialization here. | ||||
// Use default initialization and set potentially nonzero members explicitly | ||||
. | ||||
// See issue #161 | ||||
struct addrinfo hints = {}; | ||||
hints.ai_family = AF_UNSPEC; | ||||
hints.ai_socktype = SOCK_STREAM; | ||||
hints.ai_protocol = IPPROTO_TCP; | ||||
if (!validated_name.empty()) { | if (!validated_name.empty()) { | |||
return true; | return true; | |||
} | } | |||
host = gethostbyname(srvname.c_str()); | /* so far we don't require C++11, so no ::to_string(), yet */ | |||
if (service.empty()) { | ||||
std::stringstream srvtmp; | ||||
srvtmp << m_port; | ||||
std::string service = srvtmp.str(); | ||||
} | ||||
ret = getaddrinfo(srvname.c_str(), service.c_str(), &hints, &hostaddrinfo); | ||||
if (!host) { | if (ret != 0) { | |||
VERBOSE("Error: gethostbyname failed for %s\n", srvname.c_str()); | VERBOSE("Error: gethostbyname failed for %s: %s\n", srvname.c_str(), | |||
ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret)); | ||||
if (hostaddrinfo) { | ||||
freeaddrinfo(hostaddrinfo); | ||||
} | ||||
return false; | return false; | |||
} | } | |||
VERBOSE("Found DC: %s. Checking availability...", srvname.c_str()); | VERBOSE("Found DC: %s. Checking availability...", srvname.c_str()); | |||
for (int i = 0; host->h_addr_list[i]; i++) { | for (struct addrinfo *ai = hostaddrinfo; ai; ai = ai->ai_next) { | |||
struct sockaddr_in addr; | ||||
struct hostent *hp; | ||||
memcpy(&(addr.sin_addr.s_addr), host->h_addr_list[i], host->h_length); | ||||
/* Now let's try and open and close a socket to see if the domain contro ller is up or not */ | /* Now let's try and open and close a socket to see if the domain contro ller is up or not */ | |||
addr.sin_family = AF_INET; | ||||
addr.sin_port = htons(LDAP_PORT); | ||||
if (sock != -1) { | if (sock != -1) { | |||
close(sock); | close(sock); | |||
} | } | |||
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { | if ((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == | |||
VERBOSE("Failed to open socket"); | -1) { | |||
VERBOSE("Failed to open socket: %s", strerror(errno)); | ||||
continue; | continue; | |||
} | } | |||
if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { | if (connect(sock, (struct sockaddr *) ai->ai_addr, ai->ai_addrlen) == -1 | |||
VERBOSE("LDAP connection (%d) failed", i); | ) { | |||
int err = errno; | ||||
char addrstr[INET6_ADDRSTRLEN] = ""; | ||||
(void) inet_ntop(ai->ai_family, ai->ai_addr, addrstr, INET6_ADDRSTRL | ||||
EN); | ||||
VERBOSE("LDAP connection to %s failed: %s", addrstr, strerror(err)); | ||||
continue; | continue; | |||
} | } | |||
/* See if this is the 'lowest' domain controller name... the idea is to always try to | /* See if this is the 'lowest' domain controller name... the idea is to always try to | |||
* use the same domain controller. Things may become inconsistent otherw ise. | * use the same domain controller. Things may become inconsistent otherw ise. | |||
* This optimization is only possible if we're told to canonify the host name. Otherwise, | * This optimization is only possible if we're told to canonify the host name. Otherwise, | |||
* we're not allowed to touch it, but we can make sure that it resolves to at least | * we're not allowed to touch it, but we can make sure that it resolves to at least | |||
* one working IP address. */ | * one working IP address. */ | |||
if (nocanon) { | if (nocanon) { | |||
validated_name = srvname; | validated_name = srvname; | |||
break; | break; | |||
} | } | |||
hp = gethostbyaddr((char *) &addr.sin_addr.s_addr, sizeof(addr.sin_addr. | ||||
s_addr), AF_INET); | ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, host, sizeof(host), NULL, | |||
if (!hp) { | 0, NI_NAMEREQD); | |||
VERBOSE("Error: gethostbyaddr failed for %s\n", srvname.c_str()); | ||||
if (ret != 0) { | ||||
int err = errno; | ||||
char addrstr[INET6_ADDRSTRLEN] = ""; | ||||
(void) inet_ntop(ai->ai_family, ai->ai_addr, addrstr, INET6_ADDRSTRL | ||||
EN); | ||||
VERBOSE("Error: getnameinfo failed for %s: %s\n", addrstr, | ||||
ret == EAI_SYSTEM ? strerror(err) : gai_strerror(ret)); | ||||
continue; | continue; | |||
} | } | |||
if (!validated_name.empty() && std::string(hp->h_name) > validated_name) | ||||
{ | if (!validated_name.empty() && std::string(host) > validated_name) { | |||
VERBOSE("Connection to DC %s ok, but we already prefer %s", hp->h_na | VERBOSE("Connection to DC %s ok, but we already prefer %s", host, va | |||
me, validated_name.c_str()); | lidated_name.c_str()); | |||
continue; | continue; | |||
} | } | |||
validated_name = std::string(hp->h_name); | validated_name = std::string(host); | |||
} | } | |||
if (sock != -1) { | if (sock != -1) { | |||
close(sock); | close(sock); | |||
} | } | |||
if (hostaddrinfo) { | ||||
freeaddrinfo(hostaddrinfo); | ||||
} | ||||
return ! validated_name.empty(); | return ! validated_name.empty(); | |||
}; | }; | |||
DnsSrvQuery::DnsSrvQuery(const std::string& domain, const std::string& service, const std::string& protocol) | DnsSrvQuery::DnsSrvQuery(const std::string& domain, const std::string& service, const std::string& protocol) | |||
{ | { | |||
#if defined(HAVE_LIBUDNS) | #if defined(HAVE_LIBUDNS) | |||
struct dns_ctx *nsctx = NULL; | struct dns_ctx *nsctx = NULL; | |||
dns_reset(NULL); | dns_reset(NULL); | |||
if ((nsctx = dns_new(NULL)) != NULL) { | if ((nsctx = dns_new(NULL)) != NULL) { | |||
skipping to change at line 262 | skipping to change at line 289 | |||
const bool no_reverse_lookups) | const bool no_reverse_lookups) | |||
{ | { | |||
std::string dc; | std::string dc; | |||
int i; | int i; | |||
DnsSrvQuery dcsrvs; | DnsSrvQuery dcsrvs; | |||
std::string bestdc; | std::string bestdc; | |||
std::string protocols[] = { "tcp", "udp" }; | std::string protocols[] = { "tcp", "udp" }; | |||
if (!site_name.empty()) { | if (!site_name.empty()) { | |||
for (i = 0; i < (int)(sizeof(protocols) / sizeof(protocols[0])); i++) { | for (i = 0; i < (int)(sizeof(protocols) / sizeof(protocols[0])); i++) { | |||
VERBOSE("Attempting to find site-specific Domain Controller to use v ia " | VERBOSE("Attempting to find site-specific domain controller to use v ia " | |||
"DNS SRV record in domain %s for site %s and procoto l %s", | "DNS SRV record in domain %s for site %s and procoto l %s", | |||
realm_name.c_str(), site_name.c_str(), protocols[i]. c_str()); | realm_name.c_str(), site_name.c_str(), protocols[i]. c_str()); | |||
dcsrvs = DnsSrvQuery(site_name + "._sites.dc._msdcs." + realm_name, "kerberos", protocols[i]); | dcsrvs = DnsSrvQuery(site_name + "._sites.dc._msdcs." + realm_name, "kerberos", protocols[i]); | |||
if (!dcsrvs.empty()) { | if (!dcsrvs.empty()) { | |||
break; | break; | |||
} | } | |||
} | } | |||
} | } | |||
if (dcsrvs.empty()) { | if (dcsrvs.empty()) { | |||
for (i = 0; i < (int)(sizeof(protocols) / sizeof(protocols[0])); i++) { | for (i = 0; i < (int)(sizeof(protocols) / sizeof(protocols[0])); i++) { | |||
VERBOSE("Attempting to find Domain Controller to use via " | VERBOSE("Attempting to find domain controller to use via " | |||
"DNS SRV record in domain %s for procotol %s", | "DNS SRV record in domain %s for procotol %s", | |||
realm_name.c_str(), protocols[i].c_str()); | realm_name.c_str(), protocols[i].c_str()); | |||
dcsrvs = DnsSrvQuery("dc._msdcs." + realm_name, "kerberos", protocol s[i]); | dcsrvs = DnsSrvQuery("dc._msdcs." + realm_name, "kerberos", protocol s[i]); | |||
if (!dcsrvs.empty()) { | if (!dcsrvs.empty()) { | |||
break; | break; | |||
} | } | |||
} | } | |||
} | } | |||
if (dcsrvs.empty()) { | if (dcsrvs.empty()) { | |||
VERBOSE("Attempting to find a Domain Controller to use (DNS domain)"); | VERBOSE("Attempting to find a domain controller to use (DNS domain)"); | |||
dcsrvs = DnsSrvQuery(DnsSrvHost(realm_name, 0, 0, 0)); | dcsrvs = DnsSrvQuery(DnsSrvHost(realm_name, 0, 0, 0)); | |||
} | } | |||
for (std::vector<DnsSrvHost>::iterator it=dcsrvs.begin(); it != dcsrvs.end() ; it++) { | for (std::vector<DnsSrvHost>::iterator it=dcsrvs.begin(); it != dcsrvs.end() ; it++) { | |||
if (it->validate(no_reverse_lookups)) { | /* Don't validate host availability by checking the KRB5 port returned | |||
* from the SRV record, but the hard-coded, standard LDAP port (389). | ||||
* This is a short cut that should work as long as each DC runs both | ||||
* KRB5 and LDAP on default ports. | ||||
*/ | ||||
if (it->validate(no_reverse_lookups, stringify(LDAP_PORT))) { | ||||
bestdc = it->name(); | bestdc = it->name(); | |||
break; | break; | |||
} | } | |||
} | } | |||
VERBOSE("Found preferred Domain Controller: %s", bestdc.c_str()); | VERBOSE("Found preferred domain controller: %s", bestdc.c_str()); | |||
return bestdc; | return bestdc; | |||
} | } | |||
std::string get_host_os() | ||||
{ | ||||
struct utsname info; | ||||
int ret; | ||||
ret = uname(&info); | ||||
if (ret == -1) { | ||||
fprintf(stderr, "Error: uname failed (%d)\n", ret); | ||||
return NULL; | ||||
} | ||||
return std::string(info.sysname); | ||||
} | ||||
/* Return true if <str> ends with <suffix>, false otherwise */ | /* Return true if <str> ends with <suffix>, false otherwise */ | |||
static bool ends_with(std::string const &str, std::string const &suffix) | static bool ends_with(std::string const &str, std::string const &suffix) | |||
{ | { | |||
if (suffix.size() > str.size()) | if (suffix.size() > str.size()) | |||
return false; | return false; | |||
return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()); | return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()); | |||
} | } | |||
/* Default sAMAccountName for current host: | /* Default sAMAccountName for current host: | |||
End of changes. 19 change blocks. | ||||
46 lines changed or deleted | 69 lines changed or added |