1*b3700b07SGordon Ross /* 2*b3700b07SGordon Ross * This file and its contents are supplied under the terms of the 3*b3700b07SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0. 4*b3700b07SGordon Ross * You may only use this file in accordance with the terms of version 5*b3700b07SGordon Ross * 1.0 of the CDDL. 6*b3700b07SGordon Ross * 7*b3700b07SGordon Ross * A full copy of the text of the CDDL should have accompanied this 8*b3700b07SGordon Ross * source. A copy of the CDDL is also available via the Internet at 9*b3700b07SGordon Ross * http://www.illumos.org/license/CDDL. 10*b3700b07SGordon Ross */ 11*b3700b07SGordon Ross 12*b3700b07SGordon Ross /* 13*b3700b07SGordon Ross * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 14*b3700b07SGordon Ross */ 15*b3700b07SGordon Ross 16*b3700b07SGordon Ross #include <stdio.h> 17*b3700b07SGordon Ross #include <string.h> 18*b3700b07SGordon Ross #include <strings.h> 19*b3700b07SGordon Ross #include <unistd.h> 20*b3700b07SGordon Ross #include <assert.h> 21*b3700b07SGordon Ross #include <stdlib.h> 22*b3700b07SGordon Ross #include <net/if.h> 23*b3700b07SGordon Ross #include <net/if.h> 24*b3700b07SGordon Ross #include <sys/types.h> 25*b3700b07SGordon Ross #include <sys/socket.h> 26*b3700b07SGordon Ross #include <sys/sockio.h> 27*b3700b07SGordon Ross #include <netinet/in.h> 28*b3700b07SGordon Ross #include <arpa/inet.h> 29*b3700b07SGordon Ross #include <arpa/nameser.h> 30*b3700b07SGordon Ross #include <resolv.h> 31*b3700b07SGordon Ross #include <netdb.h> 32*b3700b07SGordon Ross #include <ctype.h> 33*b3700b07SGordon Ross #include <errno.h> 34*b3700b07SGordon Ross #include <ldap.h> 35*b3700b07SGordon Ross #include <lber.h> 36*b3700b07SGordon Ross #include <syslog.h> 37*b3700b07SGordon Ross #include "adutils_impl.h" 38*b3700b07SGordon Ross #include "addisc_impl.h" 39*b3700b07SGordon Ross 40*b3700b07SGordon Ross #define LDAP_PORT 389 41*b3700b07SGordon Ross 42*b3700b07SGordon Ross #define NETLOGON_ATTR_NAME "NetLogon" 43*b3700b07SGordon Ross #define NETLOGON_NT_VERSION_1 0x00000001 44*b3700b07SGordon Ross #define NETLOGON_NT_VERSION_5 0x00000002 45*b3700b07SGordon Ross #define NETLOGON_NT_VERSION_5EX 0x00000004 46*b3700b07SGordon Ross #define NETLOGON_NT_VERSION_5EX_WITH_IP 0x00000008 47*b3700b07SGordon Ross #define NETLOGON_NT_VERSION_WITH_CLOSEST_SITE 0x00000010 48*b3700b07SGordon Ross #define NETLOGON_NT_VERSION_AVOID_NT4EMUL 0x01000000 49*b3700b07SGordon Ross 50*b3700b07SGordon Ross typedef enum { 51*b3700b07SGordon Ross OPCODE = 0, 52*b3700b07SGordon Ross SBZ, 53*b3700b07SGordon Ross FLAGS, 54*b3700b07SGordon Ross DOMAIN_GUID, 55*b3700b07SGordon Ross FOREST_NAME, 56*b3700b07SGordon Ross DNS_DOMAIN_NAME, 57*b3700b07SGordon Ross DNS_HOST_NAME, 58*b3700b07SGordon Ross NET_DOMAIN_NAME, 59*b3700b07SGordon Ross NET_COMP_NAME, 60*b3700b07SGordon Ross USER_NAME, 61*b3700b07SGordon Ross DC_SITE_NAME, 62*b3700b07SGordon Ross CLIENT_SITE_NAME, 63*b3700b07SGordon Ross SOCKADDR_SIZE, 64*b3700b07SGordon Ross SOCKADDR, 65*b3700b07SGordon Ross NEXT_CLOSEST_SITE_NAME, 66*b3700b07SGordon Ross NTVER, 67*b3700b07SGordon Ross LM_NT_TOKEN, 68*b3700b07SGordon Ross LM_20_TOKEN 69*b3700b07SGordon Ross } field_5ex_t; 70*b3700b07SGordon Ross 71*b3700b07SGordon Ross struct _berelement { 72*b3700b07SGordon Ross char *ber_buf; 73*b3700b07SGordon Ross char *ber_ptr; 74*b3700b07SGordon Ross char *ber_end; 75*b3700b07SGordon Ross }; 76*b3700b07SGordon Ross 77*b3700b07SGordon Ross extern int ldap_put_filter(BerElement *ber, char *); 78*b3700b07SGordon Ross static void send_to_cds(ad_disc_cds_t *, char *, size_t, int); 79*b3700b07SGordon Ross static ad_disc_cds_t *find_cds_by_addr(ad_disc_cds_t *, struct sockaddr_in6 *); 80*b3700b07SGordon Ross static boolean_t addrmatch(struct addrinfo *, struct sockaddr_in6 *); 81*b3700b07SGordon Ross static void save_ai(ad_disc_cds_t *, struct addrinfo *); 82*b3700b07SGordon Ross 83*b3700b07SGordon Ross static void 84*b3700b07SGordon Ross cldap_escape_le64(char *buf, uint64_t val, int bytes) 85*b3700b07SGordon Ross { 86*b3700b07SGordon Ross char *p = buf; 87*b3700b07SGordon Ross 88*b3700b07SGordon Ross while (bytes != 0) { 89*b3700b07SGordon Ross p += sprintf(p, "\\%.2x", (uint8_t)(val & 0xff)); 90*b3700b07SGordon Ross val >>= 8; 91*b3700b07SGordon Ross bytes--; 92*b3700b07SGordon Ross } 93*b3700b07SGordon Ross *p = '\0'; 94*b3700b07SGordon Ross } 95*b3700b07SGordon Ross 96*b3700b07SGordon Ross /* 97*b3700b07SGordon Ross * Construct CLDAPMessage PDU for NetLogon search request. 98*b3700b07SGordon Ross * 99*b3700b07SGordon Ross * CLDAPMessage ::= SEQUENCE { 100*b3700b07SGordon Ross * messageID MessageID, 101*b3700b07SGordon Ross * protocolOp searchRequest SearchRequest; 102*b3700b07SGordon Ross * } 103*b3700b07SGordon Ross * 104*b3700b07SGordon Ross * SearchRequest ::= 105*b3700b07SGordon Ross * [APPLICATION 3] SEQUENCE { 106*b3700b07SGordon Ross * baseObject LDAPDN, 107*b3700b07SGordon Ross * scope ENUMERATED { 108*b3700b07SGordon Ross * baseObject (0), 109*b3700b07SGordon Ross * singleLevel (1), 110*b3700b07SGordon Ross * wholeSubtree (2) 111*b3700b07SGordon Ross * }, 112*b3700b07SGordon Ross * derefAliases ENUMERATED { 113*b3700b07SGordon Ross * neverDerefAliases (0), 114*b3700b07SGordon Ross * derefInSearching (1), 115*b3700b07SGordon Ross * derefFindingBaseObj (2), 116*b3700b07SGordon Ross * derefAlways (3) 117*b3700b07SGordon Ross * }, 118*b3700b07SGordon Ross * sizeLimit INTEGER (0 .. MaxInt), 119*b3700b07SGordon Ross * timeLimit INTEGER (0 .. MaxInt), 120*b3700b07SGordon Ross * attrsOnly BOOLEAN, 121*b3700b07SGordon Ross * filter Filter, 122*b3700b07SGordon Ross * attributes SEQUENCE OF AttributeType 123*b3700b07SGordon Ross * } 124*b3700b07SGordon Ross */ 125*b3700b07SGordon Ross BerElement * 126*b3700b07SGordon Ross cldap_build_request(const char *dname, 127*b3700b07SGordon Ross const char *host, uint32_t ntver, uint16_t msgid) 128*b3700b07SGordon Ross { 129*b3700b07SGordon Ross BerElement *ber; 130*b3700b07SGordon Ross int len = 0; 131*b3700b07SGordon Ross char *basedn = ""; 132*b3700b07SGordon Ross int scope = LDAP_SCOPE_BASE, deref = LDAP_DEREF_NEVER, 133*b3700b07SGordon Ross sizelimit = 0, timelimit = 0, attrsonly = 0; 134*b3700b07SGordon Ross char filter[512]; 135*b3700b07SGordon Ross char ntver_esc[13]; 136*b3700b07SGordon Ross char *p, *pend; 137*b3700b07SGordon Ross 138*b3700b07SGordon Ross /* 139*b3700b07SGordon Ross * Construct search filter in LDAP format. 140*b3700b07SGordon Ross */ 141*b3700b07SGordon Ross p = filter; 142*b3700b07SGordon Ross pend = p + sizeof (filter); 143*b3700b07SGordon Ross 144*b3700b07SGordon Ross len = snprintf(p, pend - p, "(&(DnsDomain=%s)", dname); 145*b3700b07SGordon Ross if (len >= (pend - p)) 146*b3700b07SGordon Ross goto fail; 147*b3700b07SGordon Ross p += len; 148*b3700b07SGordon Ross 149*b3700b07SGordon Ross if (host != NULL) { 150*b3700b07SGordon Ross len = snprintf(p, (pend - p), "(Host=%s)", host); 151*b3700b07SGordon Ross if (len >= (pend - p)) 152*b3700b07SGordon Ross goto fail; 153*b3700b07SGordon Ross p += len; 154*b3700b07SGordon Ross } 155*b3700b07SGordon Ross 156*b3700b07SGordon Ross if (ntver != 0) { 157*b3700b07SGordon Ross /* 158*b3700b07SGordon Ross * Format NtVer as little-endian with LDAPv3 escapes. 159*b3700b07SGordon Ross */ 160*b3700b07SGordon Ross cldap_escape_le64(ntver_esc, ntver, sizeof (ntver)); 161*b3700b07SGordon Ross len = snprintf(p, (pend - p), "(NtVer=%s)", ntver_esc); 162*b3700b07SGordon Ross if (len >= (pend - p)) 163*b3700b07SGordon Ross goto fail; 164*b3700b07SGordon Ross p += len; 165*b3700b07SGordon Ross } 166*b3700b07SGordon Ross 167*b3700b07SGordon Ross len = snprintf(p, pend - p, ")"); 168*b3700b07SGordon Ross if (len >= (pend - p)) 169*b3700b07SGordon Ross goto fail; 170*b3700b07SGordon Ross p += len; 171*b3700b07SGordon Ross 172*b3700b07SGordon Ross /* 173*b3700b07SGordon Ross * Encode CLDAPMessage and beginning of SearchRequest sequence. 174*b3700b07SGordon Ross */ 175*b3700b07SGordon Ross 176*b3700b07SGordon Ross if ((ber = ber_alloc()) == NULL) 177*b3700b07SGordon Ross goto fail; 178*b3700b07SGordon Ross 179*b3700b07SGordon Ross if (ber_printf(ber, "{it{seeiib", msgid, 180*b3700b07SGordon Ross LDAP_REQ_SEARCH, basedn, scope, deref, 181*b3700b07SGordon Ross sizelimit, timelimit, attrsonly) < 0) 182*b3700b07SGordon Ross goto fail; 183*b3700b07SGordon Ross 184*b3700b07SGordon Ross /* 185*b3700b07SGordon Ross * Encode Filter sequence. 186*b3700b07SGordon Ross */ 187*b3700b07SGordon Ross if (ldap_put_filter(ber, filter) < 0) 188*b3700b07SGordon Ross goto fail; 189*b3700b07SGordon Ross /* 190*b3700b07SGordon Ross * Encode attribute and close Filter and SearchRequest sequences. 191*b3700b07SGordon Ross */ 192*b3700b07SGordon Ross if (ber_printf(ber, "{s}}}", NETLOGON_ATTR_NAME) < 0) 193*b3700b07SGordon Ross goto fail; 194*b3700b07SGordon Ross 195*b3700b07SGordon Ross /* 196*b3700b07SGordon Ross * Success 197*b3700b07SGordon Ross */ 198*b3700b07SGordon Ross return (ber); 199*b3700b07SGordon Ross 200*b3700b07SGordon Ross fail: 201*b3700b07SGordon Ross if (ber != NULL) 202*b3700b07SGordon Ross ber_free(ber, 1); 203*b3700b07SGordon Ross return (NULL); 204*b3700b07SGordon Ross } 205*b3700b07SGordon Ross 206*b3700b07SGordon Ross /* 207*b3700b07SGordon Ross * Parse incoming search responses and attribute to correct hosts. 208*b3700b07SGordon Ross * 209*b3700b07SGordon Ross * CLDAPMessage ::= SEQUENCE { 210*b3700b07SGordon Ross * messageID MessageID, 211*b3700b07SGordon Ross * searchResponse SEQUENCE OF 212*b3700b07SGordon Ross * SearchResponse; 213*b3700b07SGordon Ross * } 214*b3700b07SGordon Ross * 215*b3700b07SGordon Ross * SearchResponse ::= 216*b3700b07SGordon Ross * CHOICE { 217*b3700b07SGordon Ross * entry [APPLICATION 4] SEQUENCE { 218*b3700b07SGordon Ross * objectName LDAPDN, 219*b3700b07SGordon Ross * attributes SEQUENCE OF SEQUENCE { 220*b3700b07SGordon Ross * AttributeType, 221*b3700b07SGordon Ross * SET OF 222*b3700b07SGordon Ross * AttributeValue 223*b3700b07SGordon Ross * } 224*b3700b07SGordon Ross * }, 225*b3700b07SGordon Ross * resultCode [APPLICATION 5] LDAPResult 226*b3700b07SGordon Ross * } 227*b3700b07SGordon Ross */ 228*b3700b07SGordon Ross 229*b3700b07SGordon Ross static int 230*b3700b07SGordon Ross decode_name(uchar_t *base, uchar_t *cp, char *str) 231*b3700b07SGordon Ross { 232*b3700b07SGordon Ross uchar_t *tmp = NULL, *st = cp; 233*b3700b07SGordon Ross uint8_t len; 234*b3700b07SGordon Ross 235*b3700b07SGordon Ross /* 236*b3700b07SGordon Ross * there should probably be some boundary checks on str && cp 237*b3700b07SGordon Ross * maybe pass in strlen && msglen ? 238*b3700b07SGordon Ross */ 239*b3700b07SGordon Ross while (*cp != 0) { 240*b3700b07SGordon Ross if (*cp == 0xc0) { 241*b3700b07SGordon Ross if (tmp == NULL) 242*b3700b07SGordon Ross tmp = cp + 2; 243*b3700b07SGordon Ross cp = base + *(cp + 1); 244*b3700b07SGordon Ross } 245*b3700b07SGordon Ross for (len = *cp++; len > 0; len--) 246*b3700b07SGordon Ross *str++ = *cp++; 247*b3700b07SGordon Ross *str++ = '.'; 248*b3700b07SGordon Ross } 249*b3700b07SGordon Ross if (cp != st) 250*b3700b07SGordon Ross *(str-1) = '\0'; 251*b3700b07SGordon Ross else 252*b3700b07SGordon Ross *str = '\0'; 253*b3700b07SGordon Ross 254*b3700b07SGordon Ross return ((tmp == NULL ? cp + 1 : tmp) - st); 255*b3700b07SGordon Ross } 256*b3700b07SGordon Ross 257*b3700b07SGordon Ross static int 258*b3700b07SGordon Ross cldap_parse(ad_disc_t ctx, ad_disc_cds_t *cds, BerElement *ber) 259*b3700b07SGordon Ross { 260*b3700b07SGordon Ross ad_disc_ds_t *dc = &cds->cds_ds; 261*b3700b07SGordon Ross uchar_t *base = NULL, *cp = NULL; 262*b3700b07SGordon Ross char val[512]; /* how big should val be? */ 263*b3700b07SGordon Ross int l, msgid, rc = 0; 264*b3700b07SGordon Ross uint16_t opcode; 265*b3700b07SGordon Ross field_5ex_t f = OPCODE; 266*b3700b07SGordon Ross 267*b3700b07SGordon Ross /* 268*b3700b07SGordon Ross * Later, compare msgid's/some validation? 269*b3700b07SGordon Ross */ 270*b3700b07SGordon Ross 271*b3700b07SGordon Ross if (ber_scanf(ber, "{i{x{{x[la", &msgid, &l, &cp) == LBER_ERROR) { 272*b3700b07SGordon Ross rc = 1; 273*b3700b07SGordon Ross goto out; 274*b3700b07SGordon Ross } 275*b3700b07SGordon Ross 276*b3700b07SGordon Ross for (base = cp; ((cp - base) < l) && (f <= LM_20_TOKEN); f++) { 277*b3700b07SGordon Ross val[0] = '\0'; 278*b3700b07SGordon Ross switch (f) { 279*b3700b07SGordon Ross case OPCODE: 280*b3700b07SGordon Ross /* opcode = *(uint16_t *)cp; */ 281*b3700b07SGordon Ross /* cp +=2; */ 282*b3700b07SGordon Ross opcode = *cp++; 283*b3700b07SGordon Ross opcode |= (*cp++ << 8); 284*b3700b07SGordon Ross break; 285*b3700b07SGordon Ross case SBZ: 286*b3700b07SGordon Ross cp += 2; 287*b3700b07SGordon Ross break; 288*b3700b07SGordon Ross case FLAGS: 289*b3700b07SGordon Ross /* dci->Flags = *(uint32_t *)cp; */ 290*b3700b07SGordon Ross /* cp +=4; */ 291*b3700b07SGordon Ross dc->flags = *cp++; 292*b3700b07SGordon Ross dc->flags |= (*cp++ << 8); 293*b3700b07SGordon Ross dc->flags |= (*cp++ << 16); 294*b3700b07SGordon Ross dc->flags |= (*cp++ << 26); 295*b3700b07SGordon Ross break; 296*b3700b07SGordon Ross case DOMAIN_GUID: 297*b3700b07SGordon Ross if (ctx != NULL) 298*b3700b07SGordon Ross auto_set_DomainGUID(ctx, cp); 299*b3700b07SGordon Ross cp += 16; 300*b3700b07SGordon Ross break; 301*b3700b07SGordon Ross case FOREST_NAME: 302*b3700b07SGordon Ross cp += decode_name(base, cp, val); 303*b3700b07SGordon Ross if (ctx != NULL) 304*b3700b07SGordon Ross auto_set_ForestName(ctx, val); 305*b3700b07SGordon Ross break; 306*b3700b07SGordon Ross case DNS_DOMAIN_NAME: 307*b3700b07SGordon Ross /* 308*b3700b07SGordon Ross * We always have this already. 309*b3700b07SGordon Ross * (Could validate it here.) 310*b3700b07SGordon Ross */ 311*b3700b07SGordon Ross cp += decode_name(base, cp, val); 312*b3700b07SGordon Ross break; 313*b3700b07SGordon Ross case DNS_HOST_NAME: 314*b3700b07SGordon Ross cp += decode_name(base, cp, val); 315*b3700b07SGordon Ross if (0 != strcasecmp(val, dc->host)) { 316*b3700b07SGordon Ross logger(LOG_ERR, "DC name %s != %s?", 317*b3700b07SGordon Ross val, dc->host); 318*b3700b07SGordon Ross } 319*b3700b07SGordon Ross break; 320*b3700b07SGordon Ross case NET_DOMAIN_NAME: 321*b3700b07SGordon Ross /* 322*b3700b07SGordon Ross * This is the "Flat" domain name. 323*b3700b07SGordon Ross * (i.e. the NetBIOS name) 324*b3700b07SGordon Ross * ignore for now. 325*b3700b07SGordon Ross */ 326*b3700b07SGordon Ross cp += decode_name(base, cp, val); 327*b3700b07SGordon Ross break; 328*b3700b07SGordon Ross case NET_COMP_NAME: 329*b3700b07SGordon Ross /* not needed */ 330*b3700b07SGordon Ross cp += decode_name(base, cp, val); 331*b3700b07SGordon Ross break; 332*b3700b07SGordon Ross case USER_NAME: 333*b3700b07SGordon Ross /* not needed */ 334*b3700b07SGordon Ross cp += decode_name(base, cp, val); 335*b3700b07SGordon Ross break; 336*b3700b07SGordon Ross case DC_SITE_NAME: 337*b3700b07SGordon Ross cp += decode_name(base, cp, val); 338*b3700b07SGordon Ross (void) strlcpy(dc->site, val, sizeof (dc->site)); 339*b3700b07SGordon Ross break; 340*b3700b07SGordon Ross case CLIENT_SITE_NAME: 341*b3700b07SGordon Ross cp += decode_name(base, cp, val); 342*b3700b07SGordon Ross if (ctx != NULL) 343*b3700b07SGordon Ross auto_set_SiteName(ctx, val); 344*b3700b07SGordon Ross break; 345*b3700b07SGordon Ross /* 346*b3700b07SGordon Ross * These are all possible, but we don't really care about them. 347*b3700b07SGordon Ross * Sockaddr_size && sockaddr might be useful at some point 348*b3700b07SGordon Ross */ 349*b3700b07SGordon Ross case SOCKADDR_SIZE: 350*b3700b07SGordon Ross case SOCKADDR: 351*b3700b07SGordon Ross case NEXT_CLOSEST_SITE_NAME: 352*b3700b07SGordon Ross case NTVER: 353*b3700b07SGordon Ross case LM_NT_TOKEN: 354*b3700b07SGordon Ross case LM_20_TOKEN: 355*b3700b07SGordon Ross break; 356*b3700b07SGordon Ross default: 357*b3700b07SGordon Ross rc = 3; 358*b3700b07SGordon Ross goto out; 359*b3700b07SGordon Ross } 360*b3700b07SGordon Ross } 361*b3700b07SGordon Ross 362*b3700b07SGordon Ross out: 363*b3700b07SGordon Ross if (base) 364*b3700b07SGordon Ross free(base); 365*b3700b07SGordon Ross else if (cp) 366*b3700b07SGordon Ross free(cp); 367*b3700b07SGordon Ross return (rc); 368*b3700b07SGordon Ross } 369*b3700b07SGordon Ross 370*b3700b07SGordon Ross 371*b3700b07SGordon Ross /* 372*b3700b07SGordon Ross * Filter out unresponsive servers, and save the domain info 373*b3700b07SGordon Ross * returned by the "LDAP ping" in the returned object. 374*b3700b07SGordon Ross * If ctx != NULL, this is a query for a DC, in which case we 375*b3700b07SGordon Ross * also save the Domain GUID, Site name, and Forest name as 376*b3700b07SGordon Ross * "auto" (discovered) values in the ctx. 377*b3700b07SGordon Ross * 378*b3700b07SGordon Ross * Only return the "winner". (We only want one DC/GC) 379*b3700b07SGordon Ross */ 380*b3700b07SGordon Ross ad_disc_ds_t * 381*b3700b07SGordon Ross ldap_ping(ad_disc_t ctx, ad_disc_cds_t *dclist, char *dname, int reqflags) 382*b3700b07SGordon Ross { 383*b3700b07SGordon Ross struct sockaddr_in6 addr6; 384*b3700b07SGordon Ross socklen_t addrlen; 385*b3700b07SGordon Ross struct pollfd pingchk; 386*b3700b07SGordon Ross ad_disc_cds_t *send_ds; 387*b3700b07SGordon Ross ad_disc_cds_t *recv_ds = NULL; 388*b3700b07SGordon Ross ad_disc_ds_t *ret_ds = NULL; 389*b3700b07SGordon Ross BerElement *req = NULL; 390*b3700b07SGordon Ross BerElement *res = NULL; 391*b3700b07SGordon Ross struct _berelement *be, *rbe; 392*b3700b07SGordon Ross size_t be_len, rbe_len; 393*b3700b07SGordon Ross int fd = -1; 394*b3700b07SGordon Ross int tries = 3; 395*b3700b07SGordon Ross int waitsec; 396*b3700b07SGordon Ross int r; 397*b3700b07SGordon Ross uint16_t msgid; 398*b3700b07SGordon Ross 399*b3700b07SGordon Ross /* One plus a null entry. */ 400*b3700b07SGordon Ross ret_ds = calloc(2, sizeof (ad_disc_ds_t)); 401*b3700b07SGordon Ross if (ret_ds == NULL) 402*b3700b07SGordon Ross goto fail; 403*b3700b07SGordon Ross 404*b3700b07SGordon Ross if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) 405*b3700b07SGordon Ross goto fail; 406*b3700b07SGordon Ross 407*b3700b07SGordon Ross (void) memset(&addr6, 0, sizeof (addr6)); 408*b3700b07SGordon Ross addr6.sin6_family = AF_INET6; 409*b3700b07SGordon Ross addr6.sin6_addr = in6addr_any; 410*b3700b07SGordon Ross if (bind(fd, (struct sockaddr *)&addr6, sizeof (addr6)) < 0) 411*b3700b07SGordon Ross goto fail; 412*b3700b07SGordon Ross 413*b3700b07SGordon Ross /* 414*b3700b07SGordon Ross * semi-unique msgid... 415*b3700b07SGordon Ross */ 416*b3700b07SGordon Ross msgid = gethrtime() & 0xffff; 417*b3700b07SGordon Ross 418*b3700b07SGordon Ross /* 419*b3700b07SGordon Ross * Is ntver right? It certainly works on w2k8... If others are needed, 420*b3700b07SGordon Ross * that might require changes to cldap_parse 421*b3700b07SGordon Ross */ 422*b3700b07SGordon Ross req = cldap_build_request(dname, NULL, 423*b3700b07SGordon Ross NETLOGON_NT_VERSION_5EX, msgid); 424*b3700b07SGordon Ross if (req == NULL) 425*b3700b07SGordon Ross goto fail; 426*b3700b07SGordon Ross be = (struct _berelement *)req; 427*b3700b07SGordon Ross be_len = be->ber_end - be->ber_buf; 428*b3700b07SGordon Ross 429*b3700b07SGordon Ross if ((res = ber_alloc()) == NULL) 430*b3700b07SGordon Ross goto fail; 431*b3700b07SGordon Ross rbe = (struct _berelement *)res; 432*b3700b07SGordon Ross rbe_len = rbe->ber_end - rbe->ber_buf; 433*b3700b07SGordon Ross 434*b3700b07SGordon Ross pingchk.fd = fd; 435*b3700b07SGordon Ross pingchk.events = POLLIN; 436*b3700b07SGordon Ross pingchk.revents = 0; 437*b3700b07SGordon Ross 438*b3700b07SGordon Ross try_again: 439*b3700b07SGordon Ross send_ds = dclist; 440*b3700b07SGordon Ross waitsec = 5; 441*b3700b07SGordon Ross while (recv_ds == NULL && waitsec > 0) { 442*b3700b07SGordon Ross 443*b3700b07SGordon Ross /* 444*b3700b07SGordon Ross * If there is another candidate, send to it. 445*b3700b07SGordon Ross */ 446*b3700b07SGordon Ross if (send_ds->cds_ds.host[0] != '\0') { 447*b3700b07SGordon Ross send_to_cds(send_ds, be->ber_buf, be_len, fd); 448*b3700b07SGordon Ross send_ds++; 449*b3700b07SGordon Ross 450*b3700b07SGordon Ross /* 451*b3700b07SGordon Ross * Wait 1/10 sec. before the next send. 452*b3700b07SGordon Ross */ 453*b3700b07SGordon Ross r = poll(&pingchk, 1, 100); 454*b3700b07SGordon Ross #if 0 /* DEBUG */ 455*b3700b07SGordon Ross /* Drop all responses 1st pass. */ 456*b3700b07SGordon Ross if (waitsec == 5) 457*b3700b07SGordon Ross r = 0; 458*b3700b07SGordon Ross #endif 459*b3700b07SGordon Ross } else { 460*b3700b07SGordon Ross /* 461*b3700b07SGordon Ross * No more candidates to "ping", so 462*b3700b07SGordon Ross * just wait a sec for responses. 463*b3700b07SGordon Ross */ 464*b3700b07SGordon Ross r = poll(&pingchk, 1, 1000); 465*b3700b07SGordon Ross if (r == 0) 466*b3700b07SGordon Ross --waitsec; 467*b3700b07SGordon Ross } 468*b3700b07SGordon Ross 469*b3700b07SGordon Ross if (r > 0) { 470*b3700b07SGordon Ross /* 471*b3700b07SGordon Ross * Got a response. 472*b3700b07SGordon Ross */ 473*b3700b07SGordon Ross (void) memset(&addr6, 0, addrlen = sizeof (addr6)); 474*b3700b07SGordon Ross r = recvfrom(fd, rbe->ber_buf, rbe_len, 0, 475*b3700b07SGordon Ross (struct sockaddr *)&addr6, &addrlen); 476*b3700b07SGordon Ross 477*b3700b07SGordon Ross recv_ds = find_cds_by_addr(dclist, &addr6); 478*b3700b07SGordon Ross if (recv_ds == NULL) 479*b3700b07SGordon Ross continue; 480*b3700b07SGordon Ross 481*b3700b07SGordon Ross (void) cldap_parse(ctx, recv_ds, res); 482*b3700b07SGordon Ross if ((recv_ds->cds_ds.flags & reqflags) != reqflags) { 483*b3700b07SGordon Ross logger(LOG_ERR, "Skip %s" 484*b3700b07SGordon Ross "due to flags 0x%X", 485*b3700b07SGordon Ross recv_ds->cds_ds.host, 486*b3700b07SGordon Ross recv_ds->cds_ds.flags); 487*b3700b07SGordon Ross recv_ds = NULL; 488*b3700b07SGordon Ross } 489*b3700b07SGordon Ross } 490*b3700b07SGordon Ross } 491*b3700b07SGordon Ross 492*b3700b07SGordon Ross if (recv_ds == NULL) { 493*b3700b07SGordon Ross if (--tries <= 0) 494*b3700b07SGordon Ross goto fail; 495*b3700b07SGordon Ross goto try_again; 496*b3700b07SGordon Ross } 497*b3700b07SGordon Ross 498*b3700b07SGordon Ross (void) memcpy(ret_ds, recv_ds, sizeof (*ret_ds)); 499*b3700b07SGordon Ross 500*b3700b07SGordon Ross ber_free(res, 1); 501*b3700b07SGordon Ross ber_free(req, 1); 502*b3700b07SGordon Ross (void) close(fd); 503*b3700b07SGordon Ross return (ret_ds); 504*b3700b07SGordon Ross 505*b3700b07SGordon Ross fail: 506*b3700b07SGordon Ross ber_free(res, 1); 507*b3700b07SGordon Ross ber_free(req, 1); 508*b3700b07SGordon Ross (void) close(fd); 509*b3700b07SGordon Ross free(ret_ds); 510*b3700b07SGordon Ross return (NULL); 511*b3700b07SGordon Ross } 512*b3700b07SGordon Ross 513*b3700b07SGordon Ross /* 514*b3700b07SGordon Ross * Attempt a send of the LDAP request to all known addresses 515*b3700b07SGordon Ross * for this candidate server. 516*b3700b07SGordon Ross */ 517*b3700b07SGordon Ross static void 518*b3700b07SGordon Ross send_to_cds(ad_disc_cds_t *send_cds, char *ber_buf, size_t be_len, int fd) 519*b3700b07SGordon Ross { 520*b3700b07SGordon Ross struct sockaddr_in6 addr6; 521*b3700b07SGordon Ross struct addrinfo *ai; 522*b3700b07SGordon Ross int err; 523*b3700b07SGordon Ross 524*b3700b07SGordon Ross if (DBG(DISC, 2)) { 525*b3700b07SGordon Ross logger(LOG_DEBUG, "send to: %s", send_cds->cds_ds.host); 526*b3700b07SGordon Ross } 527*b3700b07SGordon Ross 528*b3700b07SGordon Ross for (ai = send_cds->cds_ai; ai != NULL; ai = ai->ai_next) { 529*b3700b07SGordon Ross 530*b3700b07SGordon Ross /* 531*b3700b07SGordon Ross * Build the "to" address. 532*b3700b07SGordon Ross */ 533*b3700b07SGordon Ross (void) memset(&addr6, 0, sizeof (addr6)); 534*b3700b07SGordon Ross if (ai->ai_family == AF_INET6) { 535*b3700b07SGordon Ross (void) memcpy(&addr6, ai->ai_addr, sizeof (addr6)); 536*b3700b07SGordon Ross } else if (ai->ai_family == AF_INET) { 537*b3700b07SGordon Ross struct sockaddr_in *sin = 538*b3700b07SGordon Ross (void *)ai->ai_addr; 539*b3700b07SGordon Ross addr6.sin6_family = AF_INET6; 540*b3700b07SGordon Ross IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, 541*b3700b07SGordon Ross &addr6.sin6_addr); 542*b3700b07SGordon Ross } else { 543*b3700b07SGordon Ross continue; 544*b3700b07SGordon Ross } 545*b3700b07SGordon Ross addr6.sin6_port = htons(LDAP_PORT); 546*b3700b07SGordon Ross 547*b3700b07SGordon Ross /* 548*b3700b07SGordon Ross * Send the "ping" to this address. 549*b3700b07SGordon Ross */ 550*b3700b07SGordon Ross err = sendto(fd, ber_buf, be_len, 0, 551*b3700b07SGordon Ross (struct sockaddr *)&addr6, sizeof (addr6)); 552*b3700b07SGordon Ross err = (err < 0) ? errno : 0; 553*b3700b07SGordon Ross 554*b3700b07SGordon Ross if (DBG(DISC, 2)) { 555*b3700b07SGordon Ross char abuf[INET6_ADDRSTRLEN]; 556*b3700b07SGordon Ross const char *pa; 557*b3700b07SGordon Ross 558*b3700b07SGordon Ross pa = inet_ntop(AF_INET6, 559*b3700b07SGordon Ross &addr6.sin6_addr, 560*b3700b07SGordon Ross abuf, sizeof (abuf)); 561*b3700b07SGordon Ross logger(LOG_ERR, " > %s rc=%d", 562*b3700b07SGordon Ross pa ? pa : "?", err); 563*b3700b07SGordon Ross } 564*b3700b07SGordon Ross } 565*b3700b07SGordon Ross } 566*b3700b07SGordon Ross 567*b3700b07SGordon Ross /* 568*b3700b07SGordon Ross * We have a response from some address. Find the candidate with 569*b3700b07SGordon Ross * this address. In case a candidate had multiple addresses, we 570*b3700b07SGordon Ross * keep track of which the response came from. 571*b3700b07SGordon Ross */ 572*b3700b07SGordon Ross static ad_disc_cds_t * 573*b3700b07SGordon Ross find_cds_by_addr(ad_disc_cds_t *dclist, struct sockaddr_in6 *sin6from) 574*b3700b07SGordon Ross { 575*b3700b07SGordon Ross char abuf[INET6_ADDRSTRLEN]; 576*b3700b07SGordon Ross ad_disc_cds_t *ds; 577*b3700b07SGordon Ross struct addrinfo *ai; 578*b3700b07SGordon Ross int eai; 579*b3700b07SGordon Ross 580*b3700b07SGordon Ross if (DBG(DISC, 1)) { 581*b3700b07SGordon Ross eai = getnameinfo((void *)sin6from, sizeof (*sin6from), 582*b3700b07SGordon Ross abuf, sizeof (abuf), NULL, 0, NI_NUMERICHOST); 583*b3700b07SGordon Ross if (eai != 0) 584*b3700b07SGordon Ross (void) strlcpy(abuf, "?", sizeof (abuf)); 585*b3700b07SGordon Ross logger(LOG_DEBUG, "LDAP ping resp: addr=%s", abuf); 586*b3700b07SGordon Ross } 587*b3700b07SGordon Ross 588*b3700b07SGordon Ross /* 589*b3700b07SGordon Ross * Find the DS this response came from. 590*b3700b07SGordon Ross * (don't accept unexpected responses) 591*b3700b07SGordon Ross */ 592*b3700b07SGordon Ross for (ds = dclist; ds->cds_ds.host[0] != '\0'; ds++) { 593*b3700b07SGordon Ross ai = ds->cds_ai; 594*b3700b07SGordon Ross while (ai != NULL) { 595*b3700b07SGordon Ross if (addrmatch(ai, sin6from)) 596*b3700b07SGordon Ross goto found; 597*b3700b07SGordon Ross ai = ai->ai_next; 598*b3700b07SGordon Ross } 599*b3700b07SGordon Ross } 600*b3700b07SGordon Ross if (DBG(DISC, 1)) { 601*b3700b07SGordon Ross logger(LOG_DEBUG, " (unexpected)"); 602*b3700b07SGordon Ross } 603*b3700b07SGordon Ross return (NULL); 604*b3700b07SGordon Ross 605*b3700b07SGordon Ross found: 606*b3700b07SGordon Ross if (DBG(DISC, 2)) { 607*b3700b07SGordon Ross logger(LOG_DEBUG, " from %s", ds->cds_ds.host); 608*b3700b07SGordon Ross } 609*b3700b07SGordon Ross save_ai(ds, ai); 610*b3700b07SGordon Ross return (ds); 611*b3700b07SGordon Ross } 612*b3700b07SGordon Ross 613*b3700b07SGordon Ross static boolean_t 614*b3700b07SGordon Ross addrmatch(struct addrinfo *ai, struct sockaddr_in6 *sin6from) 615*b3700b07SGordon Ross { 616*b3700b07SGordon Ross 617*b3700b07SGordon Ross /* 618*b3700b07SGordon Ross * Note: on a GC query, the ds->addr port numbers are 619*b3700b07SGordon Ross * the GC port, and our from addr has the LDAP port. 620*b3700b07SGordon Ross * Just compare the IP addresses. 621*b3700b07SGordon Ross */ 622*b3700b07SGordon Ross 623*b3700b07SGordon Ross if (ai->ai_family == AF_INET6) { 624*b3700b07SGordon Ross struct sockaddr_in6 *sin6p = (void *)ai->ai_addr; 625*b3700b07SGordon Ross 626*b3700b07SGordon Ross if (!memcmp(&sin6from->sin6_addr, &sin6p->sin6_addr, 627*b3700b07SGordon Ross sizeof (struct in6_addr))) 628*b3700b07SGordon Ross return (B_TRUE); 629*b3700b07SGordon Ross } 630*b3700b07SGordon Ross 631*b3700b07SGordon Ross if (ai->ai_family == AF_INET) { 632*b3700b07SGordon Ross struct in6_addr in6; 633*b3700b07SGordon Ross struct sockaddr_in *sin4p = (void *)ai->ai_addr; 634*b3700b07SGordon Ross 635*b3700b07SGordon Ross IN6_INADDR_TO_V4MAPPED(&sin4p->sin_addr, &in6); 636*b3700b07SGordon Ross if (!memcmp(&sin6from->sin6_addr, &in6, 637*b3700b07SGordon Ross sizeof (struct in6_addr))) 638*b3700b07SGordon Ross return (B_TRUE); 639*b3700b07SGordon Ross } 640*b3700b07SGordon Ross 641*b3700b07SGordon Ross return (B_FALSE); 642*b3700b07SGordon Ross } 643*b3700b07SGordon Ross 644*b3700b07SGordon Ross static void 645*b3700b07SGordon Ross save_ai(ad_disc_cds_t *cds, struct addrinfo *ai) 646*b3700b07SGordon Ross { 647*b3700b07SGordon Ross ad_disc_ds_t *ds = &cds->cds_ds; 648*b3700b07SGordon Ross struct sockaddr_in *sin; 649*b3700b07SGordon Ross struct sockaddr_in6 *sin6; 650*b3700b07SGordon Ross 651*b3700b07SGordon Ross /* 652*b3700b07SGordon Ross * If this DS already saw a response, keep the first 653*b3700b07SGordon Ross * address from which we received a response. 654*b3700b07SGordon Ross */ 655*b3700b07SGordon Ross if (ds->addr.ss_family != 0) { 656*b3700b07SGordon Ross if (DBG(DISC, 2)) 657*b3700b07SGordon Ross logger(LOG_DEBUG, "already have an address"); 658*b3700b07SGordon Ross return; 659*b3700b07SGordon Ross } 660*b3700b07SGordon Ross 661*b3700b07SGordon Ross switch (ai->ai_family) { 662*b3700b07SGordon Ross case AF_INET: 663*b3700b07SGordon Ross sin = (void *)&ds->addr; 664*b3700b07SGordon Ross (void) memcpy(sin, ai->ai_addr, sizeof (*sin)); 665*b3700b07SGordon Ross sin->sin_port = htons(ds->port); 666*b3700b07SGordon Ross break; 667*b3700b07SGordon Ross 668*b3700b07SGordon Ross case AF_INET6: 669*b3700b07SGordon Ross sin6 = (void *)&ds->addr; 670*b3700b07SGordon Ross (void) memcpy(sin6, ai->ai_addr, sizeof (*sin6)); 671*b3700b07SGordon Ross sin6->sin6_port = htons(ds->port); 672*b3700b07SGordon Ross break; 673*b3700b07SGordon Ross 674*b3700b07SGordon Ross default: 675*b3700b07SGordon Ross logger(LOG_ERR, "bad AF %d", ai->ai_family); 676*b3700b07SGordon Ross } 677*b3700b07SGordon Ross } 678