xref: /illumos-gate/usr/src/lib/libadutils/common/ldap_ping.c (revision b3700b074e637f8c6991b70754c88a2cfffb246b)
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
cldap_escape_le64(char * buf,uint64_t val,int bytes)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 *
cldap_build_request(const char * dname,const char * host,uint32_t ntver,uint16_t msgid)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
decode_name(uchar_t * base,uchar_t * cp,char * str)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
cldap_parse(ad_disc_t ctx,ad_disc_cds_t * cds,BerElement * ber)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 *
ldap_ping(ad_disc_t ctx,ad_disc_cds_t * dclist,char * dname,int reqflags)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
send_to_cds(ad_disc_cds_t * send_cds,char * ber_buf,size_t be_len,int fd)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 *
find_cds_by_addr(ad_disc_cds_t * dclist,struct sockaddr_in6 * sin6from)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
addrmatch(struct addrinfo * ai,struct sockaddr_in6 * sin6from)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
save_ai(ad_disc_cds_t * cds,struct addrinfo * ai)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