1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 /*
28 * DNS query helper functions for addisc.c
29 */
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <net/if.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/sockio.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <arpa/nameser.h>
44 #include <resolv.h>
45 #include <netdb.h>
46 #include <ctype.h>
47 #include <errno.h>
48 #include <ldap.h>
49 #include <sasl/sasl.h>
50 #include <sys/u8_textprep.h>
51 #include <syslog.h>
52 #include <uuid/uuid.h>
53 #include <ads/dsgetdc.h>
54 #include "adutils_impl.h"
55 #include "addisc_impl.h"
56
57 static void save_addr(ad_disc_cds_t *, sa_family_t, uchar_t *, size_t);
58 static struct addrinfo *make_addrinfo(sa_family_t, uchar_t *, size_t);
59
60 static void do_getaddrinfo(ad_disc_cds_t *);
61 static ad_disc_cds_t *srv_parse(uchar_t *, int, int *, int *);
62 static void add_preferred(ad_disc_cds_t *, ad_disc_ds_t *, int *, int);
63 static void get_addresses(ad_disc_cds_t *, int);
64
65 /*
66 * Simplified version of srv_query() for domain auto-discovery.
67 */
68 int
srv_getdom(res_state state,const char * svc_name,char ** rrname)69 srv_getdom(res_state state, const char *svc_name, char **rrname)
70 {
71 union {
72 HEADER hdr;
73 uchar_t buf[NS_MAXMSG];
74 } msg;
75 int len, qdcount, ancount;
76 uchar_t *ptr, *eom;
77 char namebuf[NS_MAXDNAME];
78
79 /* query necessary resource records */
80
81 *rrname = NULL;
82 if (DBG(DNS, 1)) {
83 logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", svc_name);
84 }
85 len = res_nsearch(state, svc_name, C_IN, T_SRV,
86 msg.buf, sizeof (msg.buf));
87 if (len < 0) {
88 if (DBG(DNS, 0)) {
89 logger(LOG_DEBUG,
90 "DNS search for '%s' failed (%s)",
91 svc_name, hstrerror(state->res_h_errno));
92 }
93 return (-1);
94 }
95
96 if (len > sizeof (msg.buf)) {
97 logger(LOG_WARNING,
98 "DNS query %ib message doesn't fit into %ib buffer",
99 len, sizeof (msg.buf));
100 len = sizeof (msg.buf);
101 }
102
103 /* parse the reply header */
104
105 ptr = msg.buf + sizeof (msg.hdr);
106 eom = msg.buf + len;
107 qdcount = ntohs(msg.hdr.qdcount);
108 ancount = ntohs(msg.hdr.ancount);
109
110 /* skip the question section */
111
112 len = ns_skiprr(ptr, eom, ns_s_qd, qdcount);
113 if (len < 0) {
114 logger(LOG_ERR, "DNS query invalid message format");
115 return (-1);
116 }
117 ptr += len;
118
119 /* parse the answer section */
120 if (ancount < 1) {
121 logger(LOG_ERR, "DNS query - no answers");
122 return (-1);
123 }
124
125 len = dn_expand(msg.buf, eom, ptr, namebuf, sizeof (namebuf));
126 if (len < 0) {
127 logger(LOG_ERR, "DNS query invalid message format");
128 return (-1);
129 }
130 *rrname = strdup(namebuf);
131 if (*rrname == NULL) {
132 logger(LOG_ERR, "Out of memory");
133 return (-1);
134 }
135
136 return (0);
137 }
138
139
140 /*
141 * Compare SRC RRs; used with qsort(). Sort order:
142 * "Earliest" (lowest number) priority first,
143 * then weight highest to lowest.
144 */
145 static int
srvcmp(ad_disc_ds_t * s1,ad_disc_ds_t * s2)146 srvcmp(ad_disc_ds_t *s1, ad_disc_ds_t *s2)
147 {
148 if (s1->priority < s2->priority)
149 return (-1);
150 else if (s1->priority > s2->priority)
151 return (1);
152
153 if (s1->weight < s2->weight)
154 return (1);
155 else if (s1->weight > s2->weight)
156 return (-1);
157
158 return (0);
159 }
160
161 /*
162 * Query or search the SRV RRs for a given name.
163 *
164 * If dname == NULL then search (as in res_nsearch(3RESOLV), honoring any
165 * search list/option), else query (as in res_nquery(3RESOLV)).
166 *
167 * The output TTL will be the one of the SRV RR with the lowest TTL.
168 */
169 ad_disc_cds_t *
srv_query(res_state state,const char * svc_name,const char * dname,ad_disc_ds_t * prefer)170 srv_query(res_state state, const char *svc_name, const char *dname,
171 ad_disc_ds_t *prefer)
172 {
173 ad_disc_cds_t *cds_res = NULL;
174 uchar_t *msg = NULL;
175 int len, scnt, maxcnt;
176
177 msg = malloc(NS_MAXMSG);
178 if (msg == NULL) {
179 logger(LOG_ERR, "Out of memory");
180 return (NULL);
181 }
182
183 /* query necessary resource records */
184
185 /* Search, querydomain or query */
186 if (dname == NULL) {
187 dname = "*";
188 if (DBG(DNS, 1)) {
189 logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'",
190 svc_name);
191 }
192 len = res_nsearch(state, svc_name, C_IN, T_SRV,
193 msg, NS_MAXMSG);
194 if (len < 0) {
195 if (DBG(DNS, 0)) {
196 logger(LOG_DEBUG,
197 "DNS search for '%s' failed (%s)",
198 svc_name, hstrerror(state->res_h_errno));
199 }
200 goto errout;
201 }
202 } else { /* dname != NULL */
203 if (DBG(DNS, 1)) {
204 logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ",
205 svc_name, dname);
206 }
207
208 len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV,
209 msg, NS_MAXMSG);
210
211 if (len < 0) {
212 if (DBG(DNS, 0)) {
213 logger(LOG_DEBUG, "DNS: %s.%s: %s",
214 svc_name, dname,
215 hstrerror(state->res_h_errno));
216 }
217 goto errout;
218 }
219 }
220
221 if (len > NS_MAXMSG) {
222 logger(LOG_WARNING,
223 "DNS query %ib message doesn't fit into %ib buffer",
224 len, NS_MAXMSG);
225 len = NS_MAXMSG;
226 }
227
228
229 /* parse the reply header */
230
231 cds_res = srv_parse(msg, len, &scnt, &maxcnt);
232 if (cds_res == NULL)
233 goto errout;
234
235 if (prefer != NULL)
236 add_preferred(cds_res, prefer, &scnt, maxcnt);
237
238 get_addresses(cds_res, scnt);
239
240 /* sort list of candidates */
241 if (scnt > 1)
242 qsort(cds_res, scnt, sizeof (*cds_res),
243 (int (*)(const void *, const void *))srvcmp);
244
245 free(msg);
246 return (cds_res);
247
248 errout:
249 free(msg);
250 return (NULL);
251 }
252
253 static ad_disc_cds_t *
srv_parse(uchar_t * msg,int len,int * scnt,int * maxcnt)254 srv_parse(uchar_t *msg, int len, int *scnt, int *maxcnt)
255 {
256 ad_disc_cds_t *cds;
257 ad_disc_cds_t *cds_res = NULL;
258 HEADER *hdr;
259 int i, qdcount, ancount, nscount, arcount;
260 uchar_t *ptr, *eom;
261 uchar_t *end;
262 uint16_t type;
263 /* LINTED E_FUNC_SET_NOT_USED */
264 uint16_t class __unused;
265 uint32_t rttl;
266 uint16_t size;
267 char namebuf[NS_MAXDNAME];
268
269 eom = msg + len;
270 hdr = (void *)msg;
271 ptr = msg + sizeof (HEADER);
272
273 qdcount = ntohs(hdr->qdcount);
274 ancount = ntohs(hdr->ancount);
275 nscount = ntohs(hdr->nscount);
276 arcount = ntohs(hdr->arcount);
277
278 /* skip the question section */
279
280 len = ns_skiprr(ptr, eom, ns_s_qd, qdcount);
281 if (len < 0) {
282 logger(LOG_ERR, "DNS query invalid message format");
283 return (NULL);
284 }
285 ptr += len;
286
287 /*
288 * Walk through the answer section, building the result array.
289 * The array size is +2 because we (possibly) add the preferred
290 * DC if it was not there, and an empty one (null termination).
291 */
292
293 *maxcnt = ancount + 2;
294 cds_res = calloc(*maxcnt, sizeof (*cds_res));
295 if (cds_res == NULL) {
296 logger(LOG_ERR, "Out of memory");
297 return (NULL);
298 }
299
300 cds = cds_res;
301 for (i = 0; i < ancount; i++) {
302
303 len = dn_expand(msg, eom, ptr, namebuf,
304 sizeof (namebuf));
305 if (len < 0) {
306 logger(LOG_ERR, "DNS query invalid message format");
307 goto err;
308 }
309 ptr += len;
310 NS_GET16(type, ptr);
311 NS_GET16(class, ptr);
312 NS_GET32(rttl, ptr);
313 NS_GET16(size, ptr);
314 if ((end = ptr + size) > eom) {
315 logger(LOG_ERR, "DNS query invalid message format");
316 goto err;
317 }
318
319 if (type != T_SRV) {
320 ptr = end;
321 continue;
322 }
323
324 NS_GET16(cds->cds_ds.priority, ptr);
325 NS_GET16(cds->cds_ds.weight, ptr);
326 NS_GET16(cds->cds_ds.port, ptr);
327 len = dn_expand(msg, eom, ptr, cds->cds_ds.host,
328 sizeof (cds->cds_ds.host));
329 if (len < 0) {
330 logger(LOG_ERR, "DNS query invalid SRV record");
331 goto err;
332 }
333
334 cds->cds_ds.ttl = rttl;
335
336 if (DBG(DNS, 2)) {
337 logger(LOG_DEBUG, " %s", namebuf);
338 logger(LOG_DEBUG,
339 " ttl=%d pri=%d weight=%d %s:%d",
340 rttl, cds->cds_ds.priority, cds->cds_ds.weight,
341 cds->cds_ds.host, cds->cds_ds.port);
342 }
343 cds++;
344
345 /* move ptr to the end of current record */
346 ptr = end;
347 }
348 *scnt = (cds - cds_res);
349
350 /* skip the nameservers section (if any) */
351
352 len = ns_skiprr(ptr, eom, ns_s_ns, nscount);
353 if (len < 0) {
354 logger(LOG_ERR, "DNS query invalid message format");
355 goto err;
356 }
357 ptr += len;
358
359 /* walk through the additional records */
360 for (i = 0; i < arcount; i++) {
361 sa_family_t af;
362
363 len = dn_expand(msg, eom, ptr, namebuf,
364 sizeof (namebuf));
365 if (len < 0) {
366 logger(LOG_ERR, "DNS query invalid message format");
367 goto err;
368 }
369 ptr += len;
370 NS_GET16(type, ptr);
371 NS_GET16(class, ptr);
372 NS_GET32(rttl, ptr);
373 NS_GET16(size, ptr);
374 if ((end = ptr + size) > eom) {
375 logger(LOG_ERR, "DNS query invalid message format");
376 goto err;
377 }
378 switch (type) {
379 case ns_t_a:
380 af = AF_INET;
381 break;
382 case ns_t_aaaa:
383 af = AF_INET6;
384 break;
385 default:
386 continue;
387 }
388
389 if (DBG(DNS, 2)) {
390 char abuf[INET6_ADDRSTRLEN];
391 const char *ap;
392
393 ap = inet_ntop(af, ptr, abuf, sizeof (abuf));
394 logger(LOG_DEBUG, " %s %s %s",
395 (af == AF_INET) ? "A " : "AAAA",
396 (ap) ? ap : "?", namebuf);
397 }
398
399 /* Find the server, add to its address list. */
400 for (cds = cds_res; cds->cds_ds.host[0] != '\0'; cds++)
401 if (0 == strcmp(namebuf, cds->cds_ds.host))
402 save_addr(cds, af, ptr, size);
403
404 /* move ptr to the end of current record */
405 ptr = end;
406 }
407
408 return (cds_res);
409
410 err:
411 free(cds_res);
412 return (NULL);
413 }
414
415 /*
416 * Save this address on the server, if not already there.
417 */
418 static void
save_addr(ad_disc_cds_t * cds,sa_family_t af,uchar_t * addr,size_t alen)419 save_addr(ad_disc_cds_t *cds, sa_family_t af, uchar_t *addr, size_t alen)
420 {
421 struct addrinfo *ai, *new_ai, *last_ai;
422
423 new_ai = make_addrinfo(af, addr, alen);
424 if (new_ai == NULL)
425 return;
426
427 last_ai = NULL;
428 for (ai = cds->cds_ai; ai != NULL; ai = ai->ai_next) {
429 last_ai = ai;
430
431 if (new_ai->ai_family == ai->ai_family &&
432 new_ai->ai_addrlen == ai->ai_addrlen &&
433 0 == memcmp(new_ai->ai_addr, ai->ai_addr,
434 ai->ai_addrlen)) {
435 /* it's already there */
436 freeaddrinfo(new_ai);
437 return;
438 }
439 }
440
441 /* Not found. Append. */
442 if (last_ai != NULL) {
443 last_ai->ai_next = new_ai;
444 } else {
445 cds->cds_ai = new_ai;
446 }
447 }
448
449 static struct addrinfo *
make_addrinfo(sa_family_t af,uchar_t * addr,size_t alen)450 make_addrinfo(sa_family_t af, uchar_t *addr, size_t alen)
451 {
452 struct addrinfo *ai;
453 struct sockaddr *sa;
454 struct sockaddr_in *sin;
455 struct sockaddr_in6 *sin6;
456 int slen;
457
458 ai = calloc(1, sizeof (*ai));
459 sa = calloc(1, sizeof (struct sockaddr_in6));
460
461 if (ai == NULL || sa == NULL) {
462 logger(LOG_ERR, "Out of memory");
463 goto errout;
464 }
465
466 switch (af) {
467 case AF_INET:
468 sin = (void *)sa;
469 if (alen < sizeof (in_addr_t)) {
470 logger(LOG_ERR, "bad IPv4 addr len");
471 goto errout;
472 }
473 alen = sizeof (in_addr_t);
474 sin->sin_family = af;
475 (void) memcpy(&sin->sin_addr, addr, alen);
476 slen = sizeof (*sin);
477 break;
478
479 case AF_INET6:
480 sin6 = (void *)sa;
481 if (alen < sizeof (in6_addr_t)) {
482 logger(LOG_ERR, "bad IPv6 addr len");
483 goto errout;
484 }
485 alen = sizeof (in6_addr_t);
486 sin6->sin6_family = af;
487 (void) memcpy(&sin6->sin6_addr, addr, alen);
488 slen = sizeof (*sin6);
489 break;
490
491 default:
492 goto errout;
493 }
494
495 ai->ai_family = af;
496 ai->ai_addrlen = slen;
497 ai->ai_addr = sa;
498 sa->sa_family = af;
499 return (ai);
500
501 errout:
502 free(ai);
503 free(sa);
504 return (NULL);
505 }
506
507 /*
508 * Set a preferred candidate, which may already be in the list,
509 * in which case we just bump its priority, or else add it.
510 */
511 static void
add_preferred(ad_disc_cds_t * cds,ad_disc_ds_t * prefer,int * nds,int maxds)512 add_preferred(ad_disc_cds_t *cds, ad_disc_ds_t *prefer, int *nds, int maxds)
513 {
514 ad_disc_ds_t *ds;
515 int i;
516
517 assert(*nds < maxds);
518 for (i = 0; i < *nds; i++) {
519 ds = &cds[i].cds_ds;
520
521 if (strcasecmp(ds->host, prefer->host) == 0) {
522 /* Force this element to be sorted first. */
523 ds->priority = 0;
524 ds->weight = 200;
525 return;
526 }
527 }
528
529 /*
530 * The preferred DC was not found in this DNS response,
531 * so add it. Again arrange for it to be sorted first.
532 * Address info. is added later.
533 */
534 ds = &cds[i].cds_ds;
535 (void) memcpy(ds, prefer, sizeof (*ds));
536 ds->priority = 0;
537 ds->weight = 200;
538 *nds = i + 1;
539 }
540
541 /*
542 * Do another pass over the array to check for missing addresses and
543 * try resolving the names. Normally, the DNS response from AD will
544 * have supplied additional address records for all the SRV records.
545 */
546 static void
get_addresses(ad_disc_cds_t * cds,int cnt)547 get_addresses(ad_disc_cds_t *cds, int cnt)
548 {
549 int i;
550
551 for (i = 0; i < cnt; i++) {
552 if (cds[i].cds_ai == NULL) {
553 do_getaddrinfo(&cds[i]);
554 }
555 }
556 }
557
558 static void
do_getaddrinfo(ad_disc_cds_t * cds)559 do_getaddrinfo(ad_disc_cds_t *cds)
560 {
561 struct addrinfo hints;
562 struct addrinfo *ai;
563 ad_disc_ds_t *ds;
564 time_t t0, t1;
565 int err;
566
567 (void) memset(&hints, 0, sizeof (hints));
568 hints.ai_protocol = IPPROTO_TCP;
569 hints.ai_socktype = SOCK_STREAM;
570 ds = &cds->cds_ds;
571
572 /*
573 * This getaddrinfo call may take a LONG time, i.e. if our
574 * DNS servers are misconfigured or not responding.
575 * We need something like getaddrinfo_a(), with a timeout.
576 * For now, just log when this happens so we'll know
577 * if these calls are taking a long time.
578 */
579 if (DBG(DNS, 2))
580 logger(LOG_DEBUG, "getaddrinfo %s ...", ds->host);
581 t0 = time(NULL);
582 err = getaddrinfo(cds->cds_ds.host, NULL, &hints, &ai);
583 t1 = time(NULL);
584 if (DBG(DNS, 2))
585 logger(LOG_DEBUG, "getaddrinfo %s rc=%d", ds->host, err);
586 if (t1 > (t0 + 5)) {
587 logger(LOG_WARNING, "Lookup host (%s) took %u sec. "
588 "(Check DNS settings)", ds->host, (int)(t1 - t0));
589 }
590 if (err != 0) {
591 logger(LOG_ERR, "No address for host: %s (%s)",
592 ds->host, gai_strerror(err));
593 /* Make this sort at the end. */
594 ds->priority = 1 << 16;
595 return;
596 }
597
598 cds->cds_ai = ai;
599 }
600
601 void
srv_free(ad_disc_cds_t * cds_vec)602 srv_free(ad_disc_cds_t *cds_vec)
603 {
604 ad_disc_cds_t *cds;
605
606 for (cds = cds_vec; cds->cds_ds.host[0] != '\0'; cds++) {
607 if (cds->cds_ai != NULL) {
608 freeaddrinfo(cds->cds_ai);
609 }
610 }
611 free(cds_vec);
612 }
613