/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*/
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <resolv.h>
#include <netdb.h>
#include <ctype.h>
#include <errno.h>
#include <ldap.h>
#include <lber.h>
#include <syslog.h>
#include "adutils_impl.h"
#include "addisc_impl.h"
typedef enum {
OPCODE = 0,
SBZ,
} field_5ex_t;
struct _berelement {
char *ber_buf;
char *ber_ptr;
char *ber_end;
};
static void
{
char *p = buf;
while (bytes != 0) {
val >>= 8;
bytes--;
}
*p = '\0';
}
/*
* Construct CLDAPMessage PDU for NetLogon search request.
*
* CLDAPMessage ::= SEQUENCE {
* messageID MessageID,
* protocolOp searchRequest SearchRequest;
* }
*
* SearchRequest ::=
* [APPLICATION 3] SEQUENCE {
* baseObject LDAPDN,
* scope ENUMERATED {
* baseObject (0),
* singleLevel (1),
* wholeSubtree (2)
* },
* derefAliases ENUMERATED {
* neverDerefAliases (0),
* derefInSearching (1),
* derefFindingBaseObj (2),
* derefAlways (3)
* },
* sizeLimit INTEGER (0 .. MaxInt),
* timeLimit INTEGER (0 .. MaxInt),
* attrsOnly BOOLEAN,
* filter Filter,
* attributes SEQUENCE OF AttributeType
* }
*/
{
int len = 0;
char *p, *pend;
/*
* Construct search filter in LDAP format.
*/
p = filter;
goto fail;
p += len;
goto fail;
p += len;
}
if (ntver != 0) {
/*
* Format NtVer as little-endian with LDAPv3 escapes.
*/
goto fail;
p += len;
}
goto fail;
p += len;
/*
* Encode CLDAPMessage and beginning of SearchRequest sequence.
*/
goto fail;
goto fail;
/*
* Encode Filter sequence.
*/
goto fail;
/*
* Encode attribute and close Filter and SearchRequest sequences.
*/
goto fail;
/*
* Success
*/
return (ber);
fail:
return (NULL);
}
/*
* Parse incoming search responses and attribute to correct hosts.
*
* CLDAPMessage ::= SEQUENCE {
* messageID MessageID,
* searchResponse SEQUENCE OF
* SearchResponse;
* }
*
* SearchResponse ::=
* CHOICE {
* entry [APPLICATION 4] SEQUENCE {
* objectName LDAPDN,
* attributes SEQUENCE OF SEQUENCE {
* AttributeType,
* SET OF
* AttributeValue
* }
* },
* resultCode [APPLICATION 5] LDAPResult
* }
*/
static int
{
/*
* there should probably be some boundary checks on str && cp
* maybe pass in strlen && msglen ?
*/
while (*cp != 0) {
if (*cp == 0xc0) {
}
*str++ = '.';
}
else
*str = '\0';
}
static int
{
field_5ex_t f = OPCODE;
/*
*/
rc = 1;
goto out;
}
val[0] = '\0';
switch (f) {
case OPCODE:
/* opcode = *(uint16_t *)cp; */
/* cp +=2; */
break;
case SBZ:
cp += 2;
break;
case FLAGS:
/* dci->Flags = *(uint32_t *)cp; */
/* cp +=4; */
break;
case DOMAIN_GUID:
cp += 16;
break;
case FOREST_NAME:
break;
case DNS_DOMAIN_NAME:
/*
* We always have this already.
* (Could validate it here.)
*/
break;
case DNS_HOST_NAME:
}
break;
case NET_DOMAIN_NAME:
/*
* This is the "Flat" domain name.
* (i.e. the NetBIOS name)
* ignore for now.
*/
break;
case NET_COMP_NAME:
/* not needed */
break;
case USER_NAME:
/* not needed */
break;
case DC_SITE_NAME:
break;
case CLIENT_SITE_NAME:
break;
/*
* These are all possible, but we don't really care about them.
* Sockaddr_size && sockaddr might be useful at some point
*/
case SOCKADDR_SIZE:
case SOCKADDR:
case NEXT_CLOSEST_SITE_NAME:
case NTVER:
case LM_NT_TOKEN:
case LM_20_TOKEN:
break;
default:
rc = 3;
goto out;
}
}
out:
if (base)
else if (cp)
return (rc);
}
/*
* Filter out unresponsive servers, and save the domain info
* returned by the "LDAP ping" in the returned object.
* If ctx != NULL, this is a query for a DC, in which case we
* also save the Domain GUID, Site name, and Forest name as
* "auto" (discovered) values in the ctx.
*
*/
{
int waitsec;
int r;
/* One plus a null entry. */
goto fail;
goto fail;
goto fail;
/*
* semi-unique msgid...
*/
/*
* Is ntver right? It certainly works on w2k8... If others are needed,
* that might require changes to cldap_parse
*/
goto fail;
goto fail;
waitsec = 5;
/*
* If there is another candidate, send to it.
*/
send_ds++;
/*
* Wait 1/10 sec. before the next send.
*/
#if 0 /* DEBUG */
/* Drop all responses 1st pass. */
if (waitsec == 5)
r = 0;
#endif
} else {
/*
* No more candidates to "ping", so
* just wait a sec for responses.
*/
if (r == 0)
--waitsec;
}
if (r > 0) {
/*
* Got a response.
*/
continue;
"due to flags 0x%X",
}
}
}
if (--tries <= 0)
goto fail;
goto try_again;
}
return (ret_ds);
fail:
return (NULL);
}
/*
* Attempt a send of the LDAP request to all known addresses
* for this candidate server.
*/
static void
{
int err;
}
/*
* Build the "to" address.
*/
} else {
continue;
}
/*
* Send the "ping" to this address.
*/
const char *pa;
}
}
}
/*
* We have a response from some address. Find the candidate with
* this address. In case a candidate had multiple addresses, we
* keep track of which the response came from.
*/
static ad_disc_cds_t *
{
int eai;
if (eai != 0)
}
/*
* Find the DS this response came from.
* (don't accept unexpected responses)
*/
goto found;
}
}
}
return (NULL);
}
return (ds);
}
static boolean_t
{
/*
* Note: on a GC query, the ds->addr port numbers are
* the GC port, and our from addr has the LDAP port.
* Just compare the IP addresses.
*/
sizeof (struct in6_addr)))
return (B_TRUE);
}
sizeof (struct in6_addr)))
return (B_TRUE);
}
return (B_FALSE);
}
static void
{
/*
* If this DS already saw a response, keep the first
* address from which we received a response.
*/
return;
}
case AF_INET:
break;
case AF_INET6:
break;
default:
}
}