b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * This file and its contents are supplied under the terms of the
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * You may only use this file in accordance with the terms of version
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * 1.0 of the CDDL.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * A full copy of the text of the CDDL should have accompanied this
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * source. A copy of the CDDL is also available via the Internet at
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross#define NETLOGON_NT_VERSION_5EX_WITH_IP 0x00000008
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross#define NETLOGON_NT_VERSION_WITH_CLOSEST_SITE 0x00000010
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross#define NETLOGON_NT_VERSION_AVOID_NT4EMUL 0x01000000
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rosstypedef enum {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossextern int ldap_put_filter(BerElement *ber, char *);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossstatic void send_to_cds(ad_disc_cds_t *, char *, size_t, int);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossstatic ad_disc_cds_t *find_cds_by_addr(ad_disc_cds_t *, struct sockaddr_in6 *);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossstatic boolean_t addrmatch(struct addrinfo *, struct sockaddr_in6 *);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossstatic void save_ai(ad_disc_cds_t *, struct addrinfo *);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rosscldap_escape_le64(char *buf, uint64_t val, int bytes)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross while (bytes != 0) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross p += sprintf(p, "\\%.2x", (uint8_t)(val & 0xff));
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Construct CLDAPMessage PDU for NetLogon search request.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * CLDAPMessage ::= SEQUENCE {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * messageID MessageID,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * protocolOp searchRequest SearchRequest;
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * SearchRequest ::=
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * [APPLICATION 3] SEQUENCE {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * baseObject LDAPDN,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * scope ENUMERATED {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * baseObject (0),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * singleLevel (1),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * wholeSubtree (2)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * derefAliases ENUMERATED {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * neverDerefAliases (0),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * derefInSearching (1),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * derefFindingBaseObj (2),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * derefAlways (3)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * sizeLimit INTEGER (0 .. MaxInt),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * timeLimit INTEGER (0 .. MaxInt),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * attrsOnly BOOLEAN,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * filter Filter,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * attributes SEQUENCE OF AttributeType
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross const char *host, uint32_t ntver, uint16_t msgid)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross int scope = LDAP_SCOPE_BASE, deref = LDAP_DEREF_NEVER,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Construct search filter in LDAP format.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross len = snprintf(p, pend - p, "(&(DnsDomain=%s)", dname);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross len = snprintf(p, (pend - p), "(Host=%s)", host);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Format NtVer as little-endian with LDAPv3 escapes.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross cldap_escape_le64(ntver_esc, ntver, sizeof (ntver));
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross len = snprintf(p, (pend - p), "(NtVer=%s)", ntver_esc);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Encode CLDAPMessage and beginning of SearchRequest sequence.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Encode Filter sequence.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Encode attribute and close Filter and SearchRequest sequences.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross if (ber_printf(ber, "{s}}}", NETLOGON_ATTR_NAME) < 0)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Parse incoming search responses and attribute to correct hosts.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * CLDAPMessage ::= SEQUENCE {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * messageID MessageID,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * searchResponse SEQUENCE OF
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * SearchResponse;
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * SearchResponse ::=
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * entry [APPLICATION 4] SEQUENCE {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * objectName LDAPDN,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * attributes SEQUENCE OF SEQUENCE {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * AttributeType,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * AttributeValue
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * resultCode [APPLICATION 5] LDAPResult
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossdecode_name(uchar_t *base, uchar_t *cp, char *str)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * there should probably be some boundary checks on str && cp
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * maybe pass in strlen && msglen ?
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross while (*cp != 0) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rosscldap_parse(ad_disc_t ctx, ad_disc_cds_t *cds, BerElement *ber)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Later, compare msgid's/some validation?
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross if (ber_scanf(ber, "{i{x{{x[la", &msgid, &l, &cp) == LBER_ERROR) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross for (base = cp; ((cp - base) < l) && (f <= LM_20_TOKEN); f++) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross switch (f) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* opcode = *(uint16_t *)cp; */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* cp +=2; */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* dci->Flags = *(uint32_t *)cp; */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* cp +=4; */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * We always have this already.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * (Could validate it here.)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * This is the "Flat" domain name.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * (i.e. the NetBIOS name)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * ignore for now.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* not needed */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* not needed */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross (void) strlcpy(dc->site, val, sizeof (dc->site));
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * These are all possible, but we don't really care about them.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Sockaddr_size && sockaddr might be useful at some point
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Filter out unresponsive servers, and save the domain info
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * returned by the "LDAP ping" in the returned object.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * If ctx != NULL, this is a query for a DC, in which case we
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * also save the Domain GUID, Site name, and Forest name as
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * "auto" (discovered) values in the ctx.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Only return the "winner". (We only want one DC/GC)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossldap_ping(ad_disc_t ctx, ad_disc_cds_t *dclist, char *dname, int reqflags)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* One plus a null entry. */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross if (bind(fd, (struct sockaddr *)&addr6, sizeof (addr6)) < 0)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * semi-unique msgid...
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Is ntver right? It certainly works on w2k8... If others are needed,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * that might require changes to cldap_parse
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * If there is another candidate, send to it.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Wait 1/10 sec. before the next send.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross#if 0 /* DEBUG */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross /* Drop all responses 1st pass. */
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * No more candidates to "ping", so
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * just wait a sec for responses.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross if (r > 0) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Got a response.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross (void) memset(&addr6, 0, addrlen = sizeof (addr6));
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross if ((recv_ds->cds_ds.flags & reqflags) != reqflags) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross "due to flags 0x%X",
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross (void) memcpy(ret_ds, recv_ds, sizeof (*ret_ds));
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Attempt a send of the LDAP request to all known addresses
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * for this candidate server.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rosssend_to_cds(ad_disc_cds_t *send_cds, char *ber_buf, size_t be_len, int fd)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross logger(LOG_DEBUG, "send to: %s", send_cds->cds_ds.host);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross for (ai = send_cds->cds_ai; ai != NULL; ai = ai->ai_next) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Build the "to" address.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross (void) memcpy(&addr6, ai->ai_addr, sizeof (addr6));
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Send the "ping" to this address.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * We have a response from some address. Find the candidate with
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * this address. In case a candidate had multiple addresses, we
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * keep track of which the response came from.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossfind_cds_by_addr(ad_disc_cds_t *dclist, struct sockaddr_in6 *sin6from)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross eai = getnameinfo((void *)sin6from, sizeof (*sin6from),
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross logger(LOG_DEBUG, "LDAP ping resp: addr=%s", abuf);
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Find the DS this response came from.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * (don't accept unexpected responses)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross for (ds = dclist; ds->cds_ds.host[0] != '\0'; ds++) {
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Rossaddrmatch(struct addrinfo *ai, struct sockaddr_in6 *sin6from)
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Note: on a GC query, the ds->addr port numbers are
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * the GC port, and our from addr has the LDAP port.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * Just compare the IP addresses.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross struct sockaddr_in6 *sin6p = (void *)ai->ai_addr;
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross if (!memcmp(&sin6from->sin6_addr, &sin6p->sin6_addr,
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross sizeof (struct in6_addr)))
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross struct sockaddr_in *sin4p = (void *)ai->ai_addr;
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross sizeof (struct in6_addr)))
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * If this DS already saw a response, keep the first
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross * address from which we received a response.
b3700b074e637f8c6991b70754c88a2cfffb246bGordon Ross (void) memcpy(sin6, ai->ai_addr, sizeof (*sin6));