xref: /illumos-gate/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c (revision 02bc52be7430b2f7fafe1a2c981bff49ef11d6fa)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/param.h>
29 #include <ldap.h>
30 #include <stdlib.h>
31 #include <gssapi/gssapi.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <sys/time.h>
37 #include <netdb.h>
38 #include <pthread.h>
39 #include <unistd.h>
40 #include <arpa/nameser.h>
41 #include <resolv.h>
42 #include <sys/synch.h>
43 #include <string.h>
44 #include <strings.h>
45 #include <syslog.h>
46 #include <fcntl.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 
50 #include <smbsrv/libsmbns.h>
51 #include <smbns_ads.h>
52 #include <smbns_dyndns.h>
53 #include <smbns_krb.h>
54 
55 #define	ADS_DN_MAX	300
56 #define	ADS_MAXMSGLEN 512
57 #define	ADS_HOST_PREFIX "host/"
58 #define	ADS_COMPUTERS_CN "Computers"
59 
60 /* current ADS server to communicate with */
61 ADS_HOST_INFO *ads_host_info = NULL;
62 mutex_t ads_mtx;
63 
64 /*
65  * adjoin_errmsg
66  *
67  * Use the adjoin return status defined in adjoin_status_t as the index
68  * to this table.
69  */
70 static char *adjoin_errmsg[] = {
71 	"ADJOIN succeeded.",
72 	"ADJOIN failed to get handle.",
73 	"ADJOIN failed to generate machine password.",
74 	"ADJOIN failed to add workstation trust account.",
75 	"ADJOIN failed to get list of encryption types.",
76 	"ADJOIN failed to get host principal.",
77 	"ADJOIN failed to initialize kerberos context.",
78 	"ADJOIN failed to get kerberos principal.",
79 	"ADJOIN failed to set machine account password on AD.",
80 	"ADJOIN failed to modify workstation trust account.",
81 	"ADJOIN failed to write Keberos keytab file.",
82 	"ADJOIN failed to configure idmap(mapping domain).",
83 	"ADJOIN failed to configure idmap(global catalog).",
84 	"ADJOIN failed to refresh idmap service."
85 	"ADJOIN failed to remove idmap ccache."
86 };
87 
88 static int ads_bind(ADS_HANDLE *);
89 static void ads_get_computer_dn(ADS_HANDLE *, char *, size_t);
90 static char *ads_get_host_principal(char *fqhost);
91 static char *ads_get_host_principal_w_realm(char *princ, char *domain);
92 static int ads_get_host_principals(char *fqhost, char *domain,
93     char **princ, char **princ_r);
94 static int ads_add_computer(ADS_HANDLE *ah);
95 static void ads_del_computer(ADS_HANDLE *ah);
96 static int ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val);
97 static int ads_modify_computer(ADS_HANDLE *ah, int des_only);
98 static krb5_kvno ads_lookup_computer_attr_kvno(ADS_HANDLE *ah);
99 static int ads_gen_machine_passwd(char *machine_passwd, int bufsz);
100 static void ads_set_host_info(ADS_HOST_INFO *host);
101 static ADS_HOST_INFO *ads_get_host_info(void);
102 
103 /*
104  * ads_build_unc_name
105  *
106  * Construct the UNC name of the share object in the format of
107  * \\hostname.domain\shareUNC
108  *
109  * Returns 0 on success, -1 on error.
110  */
111 int
112 ads_build_unc_name(char *unc_name, int maxlen,
113     const char *hostname, const char *shareUNC)
114 {
115 	char my_domain[ADS_MAXBUFLEN];
116 
117 	if (smb_getdomainname(my_domain, sizeof (my_domain)) != 0)
118 		return (-1);
119 
120 	(void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s",
121 	    hostname, my_domain, shareUNC);
122 	return (0);
123 }
124 
125 /*
126  * ads_skip_domain_name
127  * Skip domain name format in DNS message.  The format is a sequence of
128  * ascii labels with each label having a length byte at the beginning.
129  * The domain name is terminated with a NULL character.
130  * i.e. 3sun3com0
131  * Parameters:
132  *   bufptr: address of pointer of buffer that contains domain name
133  * Returns:
134  *   bufptr: points to the data after the domain name label
135  */
136 static void
137 ads_skip_domain_name(char **bufptr)
138 {
139 	int i = 0;
140 	unsigned char c, d;
141 
142 	c = (*bufptr)[i++];
143 	d = c & 0xC0;
144 	while (c != 0 && (d != 0xC0)) {	/* do nothing */
145 		c = (*bufptr)[i++];
146 		d = c & 0xC0;
147 	}
148 
149 	if (d == 0xC0)
150 		/* skip 2nd byte in 2 byte ptr info */
151 		i++;
152 	*bufptr += i;
153 }
154 
155 static int
156 ads_is_ptr(char *buf, int len, char *offset_ptr, char **new_loc)
157 {
158 	uint16_t offset;
159 	unsigned char c;
160 
161 	c = len & 0xC0;
162 	if (c == 0xC0) {
163 		offset_ptr = dyndns_get_nshort(offset_ptr, &offset);
164 		offset &= 0x3FFF;
165 		if (offset > NS_PACKETSZ) {
166 			return (-1);
167 		}
168 		*new_loc = buf + offset;
169 		return (1);
170 	}
171 	return (0);
172 }
173 
174 /*
175  * ads_get_domain_name
176  * Converts the domain name format in DNS message back to string format.
177  * The format is a sequence of ascii labels with each label having a length
178  * byte at the beginning.  The domain name is terminated with a NULL
179  * character.
180  * i.e. 6procom3com0 -> procom.com
181  * Parameters:
182  *   bufptr   : address of pointer to buffer that contains domain name
183  *   dname_len: length of domain name in label format
184  * Returns:
185  *   NULL       : error
186  *   domain name: in string format using allocated memory
187  *   bufptr     : points to the data after the domain name label
188  */
189 static char *
190 ads_get_domain_name(char *buf, char **bufptr)
191 {
192 	char str[256], *ptr, *new_loc;
193 	int i, j, k, len, ret;
194 	int skip = 0;
195 	i = 0;
196 	k = 0;
197 	ptr = *bufptr;
198 
199 	/* get len of first label */
200 	len = ptr[i++];
201 	if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) {
202 		if (skip == 0) {
203 			/* skip up to first ptr */
204 			skip = i;
205 		}
206 
207 		i = 0;
208 		ptr = new_loc;
209 
210 		/* get len of first label */
211 		len = ptr[i++];
212 	} else {
213 		if (ret == -1) {
214 			return (NULL);
215 		}
216 	}
217 
218 	while (len) {
219 		if ((len > 63) || (k >= 255))
220 			return (NULL);
221 
222 		for (j = 0; j < len; j++)
223 			str[k++] = ptr[i++];
224 
225 		/* get len of next label */
226 		len = ptr[i++];
227 		if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) {
228 			if (skip == 0) {
229 				/* skip up to first ptr */
230 				skip = i;
231 			}
232 			i = 0;
233 			ptr = new_loc;
234 
235 			/* get len of first label */
236 			len = ptr[i++];
237 		} else if (ret == -1) {
238 			return (NULL);
239 		}
240 
241 		if (len) {
242 			/* replace label len or ptr with '.' */
243 			str[k++] = '.';
244 		}
245 	}
246 
247 	str[k] = 0;
248 
249 	if (skip) {
250 		/* skip name with ptr or just ptr */
251 		*bufptr += skip + 1;
252 	} else {
253 		/* skip name */
254 		*bufptr += i;
255 	}
256 
257 	return (strdup(str));
258 }
259 
260 /*
261  * ads_ping
262  * Ping IP without displaying log.  This is used to ping an ADS server to see
263  * if it is still alive before connecting to it with TCP.
264  * Taken from os/service/ping.c
265  * Parameters:
266  *   hostinetaddr: 4 bytes IP address in network byte order
267  * Returns:
268  *   -1: error
269  *    0: successful
270  */
271 /*ARGSUSED*/
272 static int
273 ads_ping(unsigned long hostinetaddr)
274 {
275 	return (0);
276 }
277 
278 /*
279  * ads_free_host_list
280  */
281 static void
282 ads_free_host_list(ADS_HOST_INFO *host_list, int count)
283 {
284 	int i;
285 	for (i = 0; i < count; i++) {
286 		free(host_list[i].name);
287 	}
288 	free(host_list);
289 }
290 
291 /*
292  * ads_set_host_info
293  * Cache the result of the ADS discovery if the cache is empty.
294  */
295 static void
296 ads_set_host_info(ADS_HOST_INFO *host)
297 {
298 	(void) mutex_lock(&ads_mtx);
299 	if (!ads_host_info)
300 		ads_host_info = host;
301 	(void) mutex_unlock(&ads_mtx);
302 }
303 
304 /*
305  * ads_get_host_info
306  * Get the cached ADS host info.
307  */
308 static ADS_HOST_INFO *
309 ads_get_host_info(void)
310 {
311 	ADS_HOST_INFO *host;
312 
313 	(void) mutex_lock(&ads_mtx);
314 	host = ads_host_info;
315 	(void) mutex_unlock(&ads_mtx);
316 	return (host);
317 }
318 /*
319  * ads_find_host
320  * This routine builds a DNS service location message and sends it to the
321  * DNS server via UDP to query it for a list of ADS server(s).  Once a reply
322  * is received, the reply message is parsed to get the hostname and IP
323  * addresses of the ADS server(s).  One ADS server will be selected from the
324  * list.  A ping is sent to each host at a time and the one that respond will
325  * be selected.
326  *
327  * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to
328  * guarantee that Microsoft domain controllers are returned.  Microsoft domain
329  * controllers are also ADS servers.
330  *
331  * The ADS hostnames are stored in the answer section of the DNS reply message.
332  * The IP addresses are stored in the additional section.  If the additional
333  * section does not contain any IP addresses then a DNS query by hostname is
334  * sent to get the IP address of the hostname.  This is very unlikely.
335  *
336  * The DNS reply message may be in compress formed.  The compression is done
337  * on repeating domain name label in the message.  i.e hostname.
338  * Parameters:
339  *   ns: Nameserver to use to find the ADS host
340  *   domain: domain of ADS host.
341  * Returns:
342  *   ADS host: fully qualified hostname, ip address, ldap port
343  *   port    : LDAP port of ADS host
344  */
345 /*ARGSUSED*/
346 ADS_HOST_INFO *
347 ads_find_host(char *ns, char *domain, int *port, char *service, int *go_next)
348 {
349 	int s;
350 	uint16_t id, rid, data_len, eport;
351 	int ipaddr;
352 	struct hostent *h;
353 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
354 	char *bufptr, *str;
355 	int i, ret;
356 	int queryReq;
357 	uint16_t query_cnt, ans_cnt, namser_cnt, addit_cnt;
358 	int quest_type, quest_class;
359 	int dns_ip, decode_ip;
360 	struct in_addr addr;
361 	uint16_t flags = 0;
362 	int force_recurs = 0;
363 	ADS_HOST_INFO *ads_hosts_list = NULL, *ads_host;
364 	ADS_HOST_INFO *ads_hosts_list2 = NULL;
365 
366 	*go_next = 0;
367 
368 	/*
369 	 * If we have already found an ADS server, skip the ads_find_host
370 	 * process. Returns the ADS host from the cache.
371 	 */
372 	ads_host = ads_get_host_info();
373 	if (ads_host)
374 		return (ads_host);
375 
376 	if (ns == NULL || *ns == 0) {
377 		return (NULL);
378 	}
379 	dns_ip = inet_addr(ns);
380 
381 	if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0)
382 		return (NULL);
383 
384 retry:
385 	/* build query request */
386 	queryReq = REQ_QUERY;
387 	query_cnt = 1;
388 	ans_cnt = 0;
389 	namser_cnt = 0;
390 	addit_cnt = 0;
391 
392 	(void) memset(buf, 0, NS_PACKETSZ);
393 	bufptr = buf;
394 	id = smb_get_next_resid();
395 	if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), id, queryReq,
396 	    query_cnt, ans_cnt, namser_cnt, addit_cnt, flags) == -1) {
397 		(void) close(s);
398 		return (NULL);
399 	}
400 
401 	quest_type = ns_t_srv;
402 	quest_class = ns_c_in;
403 
404 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), service,
405 	    quest_type, quest_class) == -1) {
406 		(void) close(s);
407 		return (NULL);
408 	}
409 
410 	if (dyndns_udp_send_recv(s, buf, bufptr - buf, buf2) == -1) {
411 		(void) close(s);
412 		syslog(LOG_ERR, "smb_ads: send/receive error");
413 		*go_next = 1;
414 		return (NULL);
415 	}
416 	(void) close(s);
417 
418 	(void) dyndns_get_nshort(buf2, &rid);
419 	if (id != rid)
420 		return (NULL);
421 
422 	/*
423 	 * check if query is successful by checking error
424 	 * field in UDP
425 	 */
426 	ret = buf2[3] & 0xf;
427 	if (ret != NOERROR) {
428 		syslog(LOG_ERR, "smb_ads: DNS query for ADS host error: %d: ",
429 		    ret);
430 		dyndns_msg_err(ret);
431 		*go_next = 1;
432 		return (NULL);
433 	}
434 
435 	bufptr = buf2;
436 	bufptr += 2;		/* Skip ID section */
437 	bufptr = dyndns_get_nshort(bufptr, &flags);
438 	bufptr = dyndns_get_nshort(bufptr, &query_cnt);
439 	bufptr = dyndns_get_nshort(bufptr, &ans_cnt);
440 	bufptr = dyndns_get_nshort(bufptr, &namser_cnt);
441 	bufptr = dyndns_get_nshort(bufptr, &addit_cnt);
442 
443 	if (ans_cnt == 0) {
444 		/* Check if the server supports recursive queries */
445 		if (force_recurs++ == 0 && (flags & DNSF_RECUR_SUPP) != 0) {
446 			flags = DNSF_RECUR_QRY;
447 			goto retry;
448 		}
449 
450 		syslog(LOG_DEBUG, "smb_ads: No ADS host found: "
451 		    "No answer section\n");
452 		return (NULL);
453 	}
454 
455 	/* skip question section */
456 	if (query_cnt == 1) {
457 		ads_skip_domain_name(&bufptr);
458 		bufptr += 4;
459 	} else {
460 		syslog(LOG_ERR, "smb_ads: No ADS host found, malformed "
461 		    "question section, query_cnt: %d???\n", query_cnt);
462 		return (NULL);
463 	}
464 
465 	ads_hosts_list = (ADS_HOST_INFO *)
466 	    malloc(sizeof (ADS_HOST_INFO)*ans_cnt);
467 	if (ads_hosts_list == NULL)
468 		return (NULL);
469 
470 	bzero(ads_hosts_list, sizeof (ADS_HOST_INFO) * ans_cnt);
471 
472 	/* check answer section */
473 	for (i = 0; i < ans_cnt; i++) {
474 		ads_skip_domain_name(&bufptr);
475 
476 		/* skip type, class, ttl */
477 		bufptr += 8;
478 
479 		/* len of data after this point */
480 		bufptr = dyndns_get_nshort(bufptr, &data_len);
481 
482 		/* skip priority, weight */
483 		bufptr += 4;
484 		bufptr = dyndns_get_nshort(bufptr, &eport);
485 		ads_hosts_list[i].port = eport;
486 
487 		if ((str = ads_get_domain_name(buf2, &bufptr)) == NULL) {
488 			syslog(LOG_ERR, "smb_ads: No ADS host found, "
489 			    "error decoding DNS answer section\n");
490 			ads_free_host_list(ads_hosts_list, ans_cnt);
491 			return (NULL);
492 		}
493 		ads_hosts_list[i].name = str;
494 	}
495 
496 	/* check authority section */
497 	for (i = 0; i < namser_cnt; i++) {
498 		ads_skip_domain_name(&bufptr);
499 
500 		/* skip type, class, ttl */
501 		bufptr += 8;
502 
503 		/* get len of data */
504 		bufptr = dyndns_get_nshort(bufptr, &data_len);
505 
506 		/* skip data */
507 		bufptr += data_len;
508 	}
509 
510 	/* check additional section to get IP address of ads host */
511 	decode_ip = 1;
512 	smb_config_rdlock();
513 	if (smb_config_getyorn(SMB_CI_ADS_IPLOOKUP) == 1)
514 		decode_ip = 0;
515 	smb_config_unlock();
516 
517 	if (decode_ip && (addit_cnt > 0)) {
518 		int j;
519 
520 		ads_hosts_list2 = (ADS_HOST_INFO *)
521 		    malloc(sizeof (ADS_HOST_INFO) * addit_cnt);
522 		if (ads_hosts_list2 == NULL) {
523 			ads_free_host_list(ads_hosts_list, ans_cnt);
524 			return (NULL);
525 		}
526 
527 		bzero(ads_hosts_list2, sizeof (ADS_HOST_INFO) * addit_cnt);
528 
529 		for (i = 0; i < addit_cnt; i++) {
530 
531 			if ((str = ads_get_domain_name(buf2,
532 			    &bufptr)) == NULL) {
533 				syslog(LOG_ERR, "smb_ads: No ADS host found, "
534 				    "error decoding DNS additional section\n");
535 				ads_free_host_list(ads_hosts_list, ans_cnt);
536 				ads_free_host_list(ads_hosts_list2, addit_cnt);
537 				return (NULL);
538 			}
539 
540 			ads_hosts_list2[i].name = str;
541 			bufptr += 10;
542 			bufptr = dyndns_get_int(bufptr, &ipaddr);
543 			ads_hosts_list2[i].ip_addr = ipaddr;
544 		}
545 
546 		/* pick a host that is up */
547 		for (i = 0; i < addit_cnt; i++) {
548 			if (ads_ping(ads_hosts_list2[i].ip_addr) != 0) {
549 				continue;
550 			}
551 			for (j = 0; j < ans_cnt; j++)
552 				if (strcmp(ads_hosts_list2[i].name,
553 				    ads_hosts_list[j].name) == 0)
554 					break;
555 			if (j == ans_cnt) {
556 				ads_free_host_list(ads_hosts_list, ans_cnt);
557 				ads_free_host_list(ads_hosts_list2, addit_cnt);
558 				return (NULL);
559 			}
560 			ads_host = (ADS_HOST_INFO *)
561 			    malloc(sizeof (ADS_HOST_INFO));
562 			if (ads_host == NULL) {
563 				ads_free_host_list(ads_hosts_list, ans_cnt);
564 				ads_free_host_list(ads_hosts_list2, addit_cnt);
565 				return (NULL);
566 			}
567 			bzero(ads_host, sizeof (ADS_HOST_INFO));
568 			ads_host->name = strdup(ads_hosts_list[j].name);
569 			if (ads_host->name == NULL) {
570 				ads_free_host_list(ads_hosts_list, ans_cnt);
571 				ads_free_host_list(ads_hosts_list2, addit_cnt);
572 				return (NULL);
573 			}
574 			ads_host->ip_addr = ads_hosts_list2[i].ip_addr;
575 			ads_host->port = ads_hosts_list[j].port;
576 			*port = ads_host->port;
577 			addr.s_addr = ads_host->ip_addr;
578 			syslog(LOG_DEBUG, "smb_ads: Found ADS server: %s (%s)"
579 			    " from %s\n", ads_host->name, inet_ntoa(addr), ns);
580 			ads_free_host_list(ads_hosts_list, ans_cnt);
581 			ads_free_host_list(ads_hosts_list2, addit_cnt);
582 			ads_set_host_info(ads_host);
583 			return (ads_host);
584 		}
585 		ads_free_host_list(ads_hosts_list2, addit_cnt);
586 	} else {
587 		/* use DNS to get IP address of ads host */
588 		/*
589 		 * Shouldn't get here unless entries exist in
590 		 * DNS but DNS server did
591 		 * not put them in additional section of DNS reply packet.
592 		 */
593 		for (i = 0; i < ans_cnt; i++) {
594 			h = gethostbyname(ads_hosts_list[i].name);
595 			if (h == NULL)
596 				continue;
597 			if (h->h_addr == NULL)
598 				continue;
599 			(void) memcpy(&ads_hosts_list[i].ip_addr,
600 			    h->h_addr, sizeof (addr.s_addr));
601 			if (ads_ping(ads_hosts_list[i].ip_addr) == 0) {
602 				ads_host = (ADS_HOST_INFO *)
603 				    malloc(sizeof (ADS_HOST_INFO));
604 				if (ads_host == NULL) {
605 					ads_free_host_list(ads_hosts_list,
606 					    ans_cnt);
607 					return (NULL);
608 				}
609 				bzero(ads_host, sizeof (ADS_HOST_INFO));
610 				ads_host->name = strdup(ads_hosts_list[i].name);
611 				if (ads_host->name == NULL) {
612 					ads_free_host_list(ads_hosts_list,
613 					    ans_cnt);
614 					return (NULL);
615 				}
616 				ads_host->ip_addr = ads_hosts_list[i].ip_addr;
617 				ads_host->port = ads_hosts_list[i].port;
618 				*port = ads_host->port;
619 				addr.s_addr = ads_host->ip_addr;
620 				syslog(LOG_DEBUG, "smb_ads: Found ADS server"
621 				    " using DNS: %s (%s) port %d",
622 				    ads_host->name, inet_ntoa(addr),
623 				    ads_host->port);
624 				ads_free_host_list(ads_hosts_list, ans_cnt);
625 				ads_set_host_info(ads_host);
626 				return (ads_host);
627 			}
628 		}
629 	}
630 	syslog(LOG_ERR, "smb_ads: Can't get IP for "
631 	    "ADS host or ADS host is down.\n");
632 	ads_free_host_list(ads_hosts_list, ans_cnt);
633 
634 	*go_next = 1;
635 	return (NULL);
636 }
637 
638 /*
639  * ads_convert_domain
640  * Converts a domain string into its distinguished name i.e. a unique
641  * name for an entry in the Directory Service.
642  * Memory is allocated
643  * for the new string.
644  * i.e. procom.com -> dc=procom,dc=com
645  * Parameters:
646  *   s: fully qualified DNS domain string
647  * Returns:
648  *   NULL if error
649  *   DNS domain in LDAP DN string format
650  */
651 static char *
652 ads_convert_domain(char *s)
653 {
654 	char *t, *s2, *t2;
655 	int len, cnt;
656 
657 	if (s == NULL || *s == 0)
658 		return (NULL);
659 
660 	cnt = 0;
661 	t = s;
662 	while (*t) {
663 		if (*t++ == '.') {
664 			cnt++;
665 		}
666 	}
667 
668 	len = 3 + strlen(s) + cnt*3 + 1;
669 
670 	s2 = (char *)malloc(len);
671 	if (s2 == NULL)
672 		return (NULL);
673 
674 	bzero(s2, len);
675 
676 	t = s2;
677 	(void) strncpy(t, "dc=", 3);
678 	t += 3;
679 	t2 = s;
680 	while (*s) {
681 		if (*s == '.') {
682 			if (t + 3 >= s2 + len - 1) {
683 				syslog(LOG_ERR, "[ads_convert_domain] "
684 				    "buffer overrun for string "
685 				    "conversion of %s: tot buf "
686 				    "sz alloc: %d, last "
687 				    "written buf offset: %d\n",
688 				    t2, len, t+3-s2);
689 				free(s2);
690 				return (NULL);
691 			}
692 			(void) strncpy(t, ",dc=", 4);
693 			t += 4;
694 			s++;
695 		} else {
696 			if (t >= s2 + len - 1) {
697 				syslog(LOG_ERR, "[ads_convert_domain] "
698 				    "buffer overrun for string "
699 				    "conversion of %s: tot buf "
700 				    "sz alloc: %d, last "
701 				    "written buf offset: %d\n",
702 				    t2, len, t-s2);
703 				free(s2);
704 				return (NULL);
705 			}
706 			*t++ = *s++;
707 		}
708 	}
709 	*t = '\0';
710 	return (s2);
711 }
712 
713 /*
714  * ads_free_host_info
715  * Free the memory use by the global ads_host_info and set it to NULL.
716  */
717 void
718 ads_free_host_info(void)
719 {
720 	(void) mutex_lock(&ads_mtx);
721 	if (ads_host_info) {
722 		free(ads_host_info->name);
723 		free(ads_host_info);
724 		ads_host_info = NULL;
725 	}
726 	(void) mutex_unlock(&ads_mtx);
727 }
728 
729 /*
730  * ads_open
731  * Open a LDAP connection to an ADS server.
732  * If ADS is enabled and the administrative username, password, container, and
733  * ADS domain are defined then query DNS to find an ADS server if this is the
734  * very first call to this routine.  After an ADS server is found then this
735  * server will be used everytime this routine is called until the system is
736  * rebooted or the ADS server becomes unavailable then an ADS server will
737  * be queried again.  The ADS server is always ping before an LDAP connection
738  * is made to it.  If the pings fail then DNS is used once more to find an
739  * available ADS server.  If the ping is successful then an LDAP connection
740  * is made to the ADS server.  After the connection is made then an ADS handle
741  * is created to be returned.
742  *
743  * After the LDAP connection, the LDAP version will be set to 3 using
744  * ldap_set_option().
745  *
746  * The ads_bind() routine is also called before the ADS handle is returned.
747  * Parameters:
748  *   None
749  * Returns:
750  *   NULL        : can't connect to ADS server or other errors
751  *   ADS_HANDLE* : handle to ADS server
752  */
753 ADS_HANDLE *
754 ads_open(void)
755 {
756 	ADS_HANDLE *ah;
757 	LDAP *ld;
758 	int version = 3, ads_port, find_ads_retry;
759 	char *adminUser, *password, *container;
760 	char domain[MAXHOSTNAMELEN];
761 	int enable;
762 	ADS_HOST_INFO *ads_host = NULL;
763 	struct in_addr addr;
764 	char *site, *service = NULL, *site_service = NULL;
765 	int service_sz;
766 	struct in_addr ns_list[MAXNS];
767 	int i, cnt, go_next;
768 
769 	if (smb_getdomainname(domain, MAXHOSTNAMELEN) != 0)
770 		return (NULL);
771 
772 	smb_config_rdlock();
773 	enable = smb_config_getyorn(SMB_CI_ADS_ENABLE);
774 	if (!enable) {
775 		smb_config_unlock();
776 		return (NULL);
777 	}
778 	adminUser = smb_config_getstr(SMB_CI_ADS_USER);
779 	if (adminUser == NULL || *adminUser == 0) {
780 		syslog(LOG_ERR, "smb_ads: admin user is not set");
781 		smb_config_unlock();
782 		return (NULL);
783 	}
784 	password = smb_config_getstr(SMB_CI_ADS_PASSWD);
785 	if (password == NULL || *password == 0) {
786 		syslog(LOG_ERR, "smb_ads: admin user password is not set");
787 		smb_config_unlock();
788 		return (NULL);
789 	}
790 	container = smb_config_getstr(SMB_CI_ADS_USER_CONTAINER);
791 	if (container == NULL || *container == 0)
792 		container = "cn=Users";
793 
794 	site = smb_config_getstr(SMB_CI_ADS_SITE);
795 	smb_config_unlock();
796 
797 
798 
799 	find_ads_retry = 0;
800 find_ads_host:
801 
802 	ads_host = ads_get_host_info();
803 	if (!ads_host) {
804 		if (site && *site != 0) {
805 			service_sz = strlen("_ldap._tcp.._sites.dc._msdcs.") +
806 			    strlen(site) + strlen(domain) + 1;
807 			site_service = (char *)malloc(service_sz);
808 			if (site_service == NULL) {
809 				syslog(LOG_ERR, "smb_ads: No ADS host found"
810 				    " malloc failed...");
811 				return (NULL);
812 			}
813 			(void) snprintf(site_service, service_sz,
814 			    "_ldap._tcp.%s._sites.dc._msdcs.%s", site, domain);
815 		}
816 		service_sz = strlen("_ldap._tcp.dc._msdcs.") + strlen(domain)
817 		    + 1;
818 		service = (char *)malloc(service_sz);
819 		if (service == NULL) {
820 			syslog(LOG_ERR, "smb_ads: No ADS host found malloc"
821 			    " failed...");
822 			if (site_service != NULL)
823 				(void) free(site_service);
824 			return (NULL);
825 		}
826 		(void) snprintf(service, service_sz, "_ldap._tcp.dc._msdcs.%s",
827 		    domain);
828 
829 		cnt = smb_get_nameservers(ns_list, MAXNS);
830 
831 		ads_host = NULL;
832 		go_next = 0;
833 		for (i = 0; i < cnt; i++) {
834 			if (site_service != NULL) {
835 				ads_host = ads_find_host(inet_ntoa(ns_list[i]),
836 				    domain, &ads_port, site_service, &go_next);
837 			}
838 			if (ads_host == NULL) {
839 				ads_host = ads_find_host(inet_ntoa(ns_list[i]),
840 				    domain, &ads_port, service, &go_next);
841 			}
842 			if (ads_host != NULL)
843 				break;
844 			if (go_next == 0)
845 				break;
846 		}
847 	}
848 
849 	if (site_service)
850 		(void) free(site_service);
851 	if (service)
852 		(void) free(service);
853 
854 	if (ads_host == NULL) {
855 		syslog(LOG_ERR, "smb_ads: No ADS host found from "
856 		    "configured nameservers");
857 		return (NULL);
858 	}
859 
860 	if (ads_ping(ads_host->ip_addr) != 0) {
861 		ads_free_host_info();
862 		ads_host = NULL;
863 		if (find_ads_retry == 0) {
864 			find_ads_retry = 1;
865 			goto find_ads_host;
866 		}
867 		return (NULL);
868 	}
869 
870 	ah = (ADS_HANDLE *)malloc(sizeof (ADS_HANDLE));
871 	if (ah == NULL) {
872 		return (NULL);
873 	}
874 	(void) memset(ah, 0, sizeof (ADS_HANDLE));
875 
876 	addr.s_addr = ads_host->ip_addr;
877 	if ((ld = ldap_init((char *)inet_ntoa(addr), ads_host->port)) == NULL) {
878 		syslog(LOG_ERR, "smb_ads: Could not open connection "
879 		    "to host: %s\n", ads_host->name);
880 		ads_free_host_info();
881 		ads_host = NULL;
882 		free(ah);
883 		if (find_ads_retry == 0) {
884 			find_ads_retry = 1;
885 			goto find_ads_host;
886 		}
887 		return (NULL);
888 	}
889 
890 	if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)
891 	    != LDAP_SUCCESS) {
892 		syslog(LOG_ERR, "smb_ads: Could not set "
893 		    "LDAP_OPT_PROTOCOL_VERSION %d\n", version);
894 		ads_free_host_info();
895 		free(ah);
896 		(void) ldap_unbind(ld);
897 		return (NULL);
898 	}
899 
900 	ah->ld = ld;
901 	ah->user = strdup(adminUser);
902 	ah->pwd = strdup(password);
903 	ah->container = strdup(container);
904 	ah->domain = strdup(domain);
905 
906 	if ((ah->user == NULL) || (ah->pwd == NULL) ||
907 	    (ah->container == NULL) || (ah->domain == NULL)) {
908 		ads_close(ah);
909 		return (NULL);
910 	}
911 
912 	ah->domain_dn = ads_convert_domain(domain);
913 	if (ah->domain_dn == NULL) {
914 		ads_close(ah);
915 		return (NULL);
916 	}
917 
918 	ah->hostname = strdup(ads_host->name);
919 	if (ah->hostname == NULL) {
920 		ads_close(ah);
921 		return (NULL);
922 	}
923 	if (site) {
924 		ah->site = strdup(site);
925 		if (ah->site == NULL) {
926 			ads_close(ah);
927 			return (NULL);
928 		}
929 	} else {
930 		ah->site = NULL;
931 	}
932 
933 	if (ads_bind(ah) == -1) {
934 		ads_close(ah);
935 		return (NULL);
936 	}
937 
938 	return (ah);
939 }
940 
941 /*
942  * ads_close
943  * Close connection to ADS server and free memory allocated for ADS handle.
944  * LDAP unbind is called here.
945  * Parameters:
946  *   ah: handle to ADS server
947  * Returns:
948  *   void
949  */
950 void
951 ads_close(ADS_HANDLE *ah)
952 {
953 	int len;
954 
955 	if (ah == NULL)
956 		return;
957 	/* close and free connection resources */
958 	if (ah->ld)
959 		(void) ldap_unbind(ah->ld);
960 
961 	free(ah->user);
962 	if (ah->pwd) {
963 		len = strlen(ah->pwd);
964 		/* zero out the memory that contains user's password */
965 		if (len > 0)
966 			bzero(ah->pwd, len);
967 		free(ah->pwd);
968 	}
969 	free(ah->container);
970 	free(ah->domain);
971 	free(ah->domain_dn);
972 	free(ah->hostname);
973 	free(ah->site);
974 	free(ah);
975 }
976 
977 /*
978  * ads_display_stat
979  * Display error message for GSS-API routines.
980  * Parameters:
981  *   maj:  GSS major status
982  *   min:  GSS minor status
983  * Returns:
984  *   None
985  */
986 static void
987 ads_display_stat(OM_uint32 maj, OM_uint32 min)
988 {
989 	gss_buffer_desc msg;
990 	OM_uint32 msg_ctx = 0;
991 	OM_uint32 min2;
992 	(void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
993 	    &msg_ctx, &msg);
994 	syslog(LOG_ERR, "smb_ads: major status error: %s\n", (char *)msg.value);
995 	(void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
996 	    &msg_ctx, &msg);
997 	syslog(LOG_ERR, "smb_ads: minor status error: %s\n", (char *)msg.value);
998 }
999 
1000 /*
1001  * free_attr
1002  * Free memory allocated when publishing a share.
1003  * Parameters:
1004  *   addattrs: an array of LDAPMod pointers
1005  * Returns:
1006  *   None
1007  */
1008 static void
1009 free_attr(LDAPMod *addattrs[])
1010 {
1011 	int i;
1012 	for (i = 0; addattrs[i]; i++) {
1013 		free(addattrs[i]);
1014 	}
1015 }
1016 
1017 /*
1018  * ads_acquire_cred
1019  * Called by ads_bind() to get a handle to administrative user's credential
1020  * stored locally on the system.  The credential is the TGT.  If the attempt at
1021  * getting handle fails then a second attempt will be made after getting a
1022  * new TGT.
1023  * Please look at ads_bind() for more information.
1024  *
1025  * Paramters:
1026  *   ah         : handle to ADS server
1027  *   kinit_retry: if 0 then a second attempt will be made to get handle to the
1028  *                credential if the first attempt fails
1029  * Returns:
1030  *   cred_handle: handle to the administrative user's credential (TGT)
1031  *   oid        : contains Kerberos 5 object identifier
1032  *   kinit_retry: A 1 indicates that a second attempt has been made to get
1033  *                handle to the credential and no further attempts can be made
1034  *   -1         : error
1035  *    0         : success
1036  */
1037 static int
1038 ads_acquire_cred(ADS_HANDLE *ah, gss_cred_id_t *cred_handle, gss_OID *oid,
1039 	int *kinit_retry)
1040 {
1041 	return (krb5_acquire_cred_kinit(ah->user, ah->pwd, cred_handle, oid,
1042 	    kinit_retry, "ads"));
1043 }
1044 
1045 /*
1046  * ads_establish_sec_context
1047  * Called by ads_bind() to establish a security context to an LDAP service on
1048  * an ADS server. If the attempt at establishing the security context fails
1049  * then a second attempt will be made by ads_bind() if a new TGT has not been
1050  * already obtained in ads_acquire_cred.  The second attempt, if allowed, will
1051  * obtained a new TGT here and a new handle to the credential will also be
1052  * obtained in ads_acquire_cred.  LDAP SASL bind is used to send and receive
1053  * the GSS tokens to and from the ADS server.
1054  * Please look at ads_bind for more information.
1055  * Paramters:
1056  *   ah             : handle to ADS server
1057  *   cred_handle    : handle to administrative user's credential (TGT)
1058  *   oid            : Kerberos 5 object identifier
1059  *   kinit_retry    : if 0 then a second attempt can be made to establish a
1060  *                    security context with ADS server if first attempt fails
1061  * Returns:
1062  *   gss_context    : security context to ADS server
1063  *   sercred        : encrypted ADS server's supported security layers
1064  *   do_acquire_cred: if 1 then a second attempt will be made to establish a
1065  *                    security context with ADS server after getting a new
1066  *                    handle to the user's credential
1067  *   kinit_retry    : if 1 then a second attempt will be made to establish a
1068  *                    a security context and no further attempts can be made
1069  *   -1             : error
1070  *    0             : success
1071  */
1072 static int
1073 ads_establish_sec_context(ADS_HANDLE *ah, gss_ctx_id_t *gss_context,
1074     gss_cred_id_t cred_handle, gss_OID oid, struct berval **sercred,
1075     int *kinit_retry, int *do_acquire_cred)
1076 {
1077 	OM_uint32 maj, min, time_rec;
1078 	char service_name[ADS_MAXBUFLEN], *user_dn;
1079 	gss_buffer_desc send_tok, service_buf;
1080 	gss_name_t target_name;
1081 	gss_buffer_desc input;
1082 	gss_buffer_desc *inputptr;
1083 	struct berval cred;
1084 	OM_uint32 ret_flags;
1085 	int stat, len;
1086 	int gss_flags;
1087 
1088 	/*
1089 	 * 6 additional bytes for the "cn=,, " and the null terminator
1090 	 */
1091 	len =  strlen(ah->user) + strlen(ah->container) +
1092 	    strlen(ah->domain_dn) + 6;
1093 
1094 	if ((user_dn = (char *)malloc(len)) == NULL)
1095 		return (-1);
1096 
1097 	(void) snprintf(user_dn, len, "cn=%s,%s,%s", ah->user, ah->container,
1098 	    ah->domain_dn);
1099 
1100 	(void) snprintf(service_name, ADS_MAXBUFLEN, "ldap@%s", ah->hostname);
1101 	service_buf.value = service_name;
1102 	service_buf.length = strlen(service_name)+1;
1103 	if ((maj = gss_import_name(&min, &service_buf,
1104 	    (gss_OID) gss_nt_service_name,
1105 	    &target_name)) != GSS_S_COMPLETE) {
1106 		ads_display_stat(maj, min);
1107 		(void) gss_release_oid(&min, &oid);
1108 		free(user_dn);
1109 		return (-1);
1110 	}
1111 
1112 	*gss_context = GSS_C_NO_CONTEXT;
1113 	*sercred = NULL;
1114 	inputptr = GSS_C_NO_BUFFER;
1115 	gss_flags = GSS_C_MUTUAL_FLAG;
1116 	do {
1117 		if (krb5_establish_sec_ctx_kinit(ah->user, ah->pwd,
1118 		    cred_handle, gss_context, target_name, oid,
1119 		    gss_flags, inputptr, &send_tok,
1120 		    &ret_flags, &time_rec, kinit_retry,
1121 		    do_acquire_cred, &maj, "ads") == -1) {
1122 			(void) gss_release_oid(&min, &oid);
1123 			(void) gss_release_name(&min, &target_name);
1124 			free(user_dn);
1125 			return (-1);
1126 		}
1127 
1128 		cred.bv_val = send_tok.value;
1129 		cred.bv_len = send_tok.length;
1130 		if (*sercred) {
1131 			ber_bvfree(*sercred);
1132 			*sercred = NULL;
1133 		}
1134 		stat = ldap_sasl_bind_s(ah->ld, user_dn, "GSSAPI",
1135 		    &cred, NULL, NULL, sercred);
1136 		if (stat != LDAP_SUCCESS &&
1137 		    stat != LDAP_SASL_BIND_IN_PROGRESS) {
1138 			/* LINTED - E_SEC_PRINTF_VAR_FMT */
1139 			syslog(LOG_ERR, ldap_err2string(stat));
1140 			(void) gss_release_oid(&min, &oid);
1141 			(void) gss_release_name(&min, &target_name);
1142 			(void) gss_release_buffer(&min, &send_tok);
1143 			free(user_dn);
1144 			return (-1);
1145 		}
1146 		input.value = (*sercred)->bv_val;
1147 		input.length = (*sercred)->bv_len;
1148 		inputptr = &input;
1149 		if (send_tok.length > 0)
1150 			(void) gss_release_buffer(&min, &send_tok);
1151 	} while (maj != GSS_S_COMPLETE);
1152 
1153 	(void) gss_release_oid(&min, &oid);
1154 	(void) gss_release_name(&min, &target_name);
1155 	free(user_dn);
1156 
1157 	return (0);
1158 }
1159 
1160 /*
1161  * ads_negotiate_sec_layer
1162  * Call by ads_bind() to negotiate additional security layer for further
1163  * communication after security context establishment.  No additional security
1164  * is needed so a "no security layer" is negotiated.  The security layer is
1165  * described in the SASL RFC 2478 and this step is needed for secure LDAP
1166  * binding.  LDAP SASL bind is used to send and receive the GSS tokens to and
1167  * from the ADS server.
1168  * Please look at ads_bind for more information.
1169  *
1170  * Paramters:
1171  *   ah         : handle to ADS server
1172  *   gss_context: security context to ADS server
1173  *   sercred    : encrypted ADS server's supported security layers
1174  * Returns:
1175  *   -1         : error
1176  *    0         : success
1177  */
1178 static int
1179 ads_negotiate_sec_layer(ADS_HANDLE *ah, gss_ctx_id_t gss_context,
1180     struct berval *sercred)
1181 {
1182 	OM_uint32 maj, min;
1183 	gss_buffer_desc unwrap_inbuf, unwrap_outbuf;
1184 	gss_buffer_desc wrap_inbuf, wrap_outbuf;
1185 	int conf_state, sec_layer;
1186 	char auth_id[5];
1187 	struct berval cred;
1188 	int stat;
1189 	gss_qop_t qt;
1190 
1191 	/* check for server supported security layer */
1192 	unwrap_inbuf.value = sercred->bv_val;
1193 	unwrap_inbuf.length = sercred->bv_len;
1194 	if ((maj = gss_unwrap(&min, gss_context,
1195 	    &unwrap_inbuf, &unwrap_outbuf,
1196 	    &conf_state, &qt)) != GSS_S_COMPLETE) {
1197 		ads_display_stat(maj, min);
1198 		if (sercred)
1199 			ber_bvfree(sercred);
1200 		return (-1);
1201 	}
1202 	sec_layer = *((char *)unwrap_outbuf.value);
1203 	(void) gss_release_buffer(&min, &unwrap_outbuf);
1204 	if (!(sec_layer & 1)) {
1205 		syslog(LOG_ERR, "smb_ads: ADS server does not support "
1206 		    "no security layer!\n");
1207 		if (sercred) ber_bvfree(sercred);
1208 		return (-1);
1209 	}
1210 	if (sercred) ber_bvfree(sercred);
1211 
1212 	/* no security layer needed after successful binding */
1213 	auth_id[0] = 0x01;
1214 
1215 	/* byte 2-4: max client recv size in network byte order */
1216 	auth_id[1] = 0x00;
1217 	auth_id[2] = 0x40;
1218 	auth_id[3] = 0x00;
1219 	wrap_inbuf.value = auth_id;
1220 	wrap_inbuf.length = 4;
1221 	conf_state = 0;
1222 	if ((maj = gss_wrap(&min, gss_context, conf_state, 0, &wrap_inbuf,
1223 	    &conf_state, &wrap_outbuf)) != GSS_S_COMPLETE) {
1224 		ads_display_stat(maj, min);
1225 		return (-1);
1226 	}
1227 
1228 	cred.bv_val = wrap_outbuf.value;
1229 	cred.bv_len = wrap_outbuf.length;
1230 	sercred = NULL;
1231 	stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", &cred, NULL, NULL,
1232 	    &sercred);
1233 	if (stat != LDAP_SUCCESS && stat != LDAP_SASL_BIND_IN_PROGRESS) {
1234 		/* LINTED - E_SEC_PRINTF_VAR_FMT */
1235 		syslog(LOG_ERR, ldap_err2string(stat));
1236 		(void) gss_release_buffer(&min, &wrap_outbuf);
1237 		return (-1);
1238 	}
1239 
1240 	(void) gss_release_buffer(&min, &wrap_outbuf);
1241 	if (sercred)
1242 		ber_bvfree(sercred);
1243 
1244 	return (0);
1245 }
1246 
1247 /*
1248  * ads_bind
1249  * Use secure binding to bind to ADS server.
1250  * Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with
1251  * Kerberos 5 as the security mechanisn to authenticate, obtain a security
1252  * context, and securely bind an administrative user so that other LDAP
1253  * commands can be used, i.e. add and delete.
1254  *
1255  * To obtain the security context, a Kerberos ticket-granting ticket (TGT)
1256  * for the user is needed to obtain a ticket for the LDAP service.  To get
1257  * a TGT for the user, the username and password is needed.  Once a TGT is
1258  * obtained then it will be stored locally and used until it is expired.
1259  * This routine will automatically obtained a TGT for the first time or when
1260  * it expired.  LDAP SASL bind is then finally used to send GSS tokens to
1261  * obtain a security context for the LDAP service on the ADS server.  If
1262  * there is any problem getting the security context then a new TGT will be
1263  * obtain to try getting the security context once more.
1264  *
1265  * After the security context is obtain and established, the LDAP SASL bind
1266  * is used to negotiate an additional security layer.  No further security is
1267  * needed so a "no security layer" is negotiated.  After this the security
1268  * context can be deleted and further LDAP commands can be sent to the ADS
1269  * server until a LDAP unbind command is issued to the ADS server.
1270  * Paramaters:
1271  *   ah: handle to ADS server
1272  * Returns:
1273  *  -1: error
1274  *   0: success
1275  */
1276 static int
1277 ads_bind(ADS_HANDLE *ah)
1278 {
1279 	OM_uint32 min;
1280 	gss_cred_id_t cred_handle;
1281 	gss_ctx_id_t gss_context;
1282 	OM_uint32 maj;
1283 	gss_OID oid;
1284 	struct berval *sercred;
1285 	int kinit_retry, do_acquire_cred;
1286 
1287 	kinit_retry = 0;
1288 	do_acquire_cred = 0;
1289 	acquire_cred:
1290 
1291 	if (ads_acquire_cred(ah, &cred_handle, &oid, &kinit_retry))
1292 		return (-1);
1293 
1294 	if (ads_establish_sec_context(ah, &gss_context, cred_handle,
1295 	    oid, &sercred, &kinit_retry, &do_acquire_cred)) {
1296 		(void) gss_release_cred(&min, &cred_handle);
1297 		if (do_acquire_cred) {
1298 			do_acquire_cred = 0;
1299 			goto acquire_cred;
1300 		}
1301 		return (-1);
1302 	}
1303 
1304 	if (ads_negotiate_sec_layer(ah, gss_context, sercred)) {
1305 		(void) gss_release_cred(&min, &cred_handle);
1306 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1307 		return (-1);
1308 	}
1309 
1310 	if ((maj = gss_release_cred(&min, &cred_handle))
1311 	    != GSS_S_COMPLETE) {
1312 		syslog(LOG_ERR, "smb_ads: Can't release credential handle\n");
1313 		ads_display_stat(maj, min);
1314 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1315 		return (-1);
1316 	}
1317 
1318 	if ((maj = gss_delete_sec_context(&min, &gss_context, NULL))
1319 	    != GSS_S_COMPLETE) {
1320 		syslog(LOG_ERR, "smb_ads: Can't delete security context\n");
1321 		ads_display_stat(maj, min);
1322 		return (-1);
1323 	}
1324 
1325 	return (0);
1326 }
1327 
1328 /*
1329  * ads_add_share
1330  * Call by ads_publish_share to create share object in ADS.
1331  * This routine specifies the attributes of an ADS LDAP share object. The first
1332  * attribute and values define the type of ADS object, the share object.  The
1333  * second attribute and value define the UNC of the share data for the share
1334  * object. The LDAP synchronous add command is used to add the object into ADS.
1335  * The container location to add the object needs to specified.
1336  * Parameters:
1337  *   ah          : handle to ADS server
1338  *   adsShareName: name of share object to be created in ADS
1339  *   shareUNC    : share name on NetForce
1340  *   adsContainer: location in ADS to create share object
1341  *
1342  * Returns:
1343  *   -1          : error
1344  *    0          : success
1345  */
1346 int
1347 ads_add_share(ADS_HANDLE *ah, const char *adsShareName,
1348     const char *unc_name, const char *adsContainer)
1349 {
1350 	LDAPMod *addattrs[3];
1351 	char *tmp1[5], *tmp2[5];
1352 	int j = -1;
1353 	char *share_dn;
1354 	char buf[ADS_MAXMSGLEN];
1355 
1356 	int len, ret;
1357 
1358 	len = 5 + strlen(adsShareName) + strlen(adsContainer) +
1359 	    strlen(ah->domain_dn) + 1;
1360 
1361 	share_dn = (char *)malloc(len);
1362 	if (share_dn == NULL)
1363 		return (-1);
1364 
1365 	(void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName,
1366 	    adsContainer, ah->domain_dn);
1367 
1368 	addattrs[++j] = (LDAPMod *)malloc(sizeof (LDAPMod));
1369 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1370 	addattrs[j]->mod_type = "objectClass";
1371 	tmp1[0] = "top";
1372 	tmp1[1] = "leaf";
1373 	tmp1[2] = "connectionPoint";
1374 	tmp1[3] = "volume";
1375 	tmp1[4] = 0;
1376 	addattrs[j]->mod_values = tmp1;
1377 
1378 	addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
1379 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1380 	addattrs[j]->mod_type = "uNCName";
1381 
1382 	tmp2[0] = (char *)unc_name;
1383 	tmp2[1] = 0;
1384 	addattrs[j]->mod_values = tmp2;
1385 
1386 	addattrs[++j] = 0;
1387 
1388 	if ((ret = ldap_add_s(ah->ld, share_dn, addattrs)) != LDAP_SUCCESS) {
1389 		(void) snprintf(buf, ADS_MAXMSGLEN,
1390 		    "ads_add_share: %s:", share_dn);
1391 		/* LINTED - E_SEC_PRINTF_VAR_FMT */
1392 		syslog(LOG_ERR, ldap_err2string(ret));
1393 		free_attr(addattrs);
1394 		free(share_dn);
1395 		return (ret);
1396 	}
1397 	free(share_dn);
1398 	free_attr(addattrs);
1399 
1400 	(void) snprintf(buf, ADS_MAXMSGLEN,
1401 	    "Share %s has been added to ADS container: %s.\n", adsShareName,
1402 	    adsContainer);
1403 	syslog(LOG_DEBUG, "smb_ads: %s", buf);
1404 
1405 	return (0);
1406 }
1407 
1408 /*
1409  * ads_del_share
1410  * Call by ads_remove_share to remove share object from ADS.  The container
1411  * location to remove the object needs to specified.  The LDAP synchronous
1412  * delete command is used.
1413  * Parameters:
1414  *   ah          : handle to ADS server
1415  *   adsShareName: name of share object in ADS to be removed
1416  *   adsContainer: location of share object in ADS
1417  * Returns:
1418  *   -1          : error
1419  *    0          : success
1420  */
1421 static int
1422 ads_del_share(ADS_HANDLE *ah, const char *adsShareName,
1423     const char *adsContainer)
1424 {
1425 	char *share_dn, buf[ADS_MAXMSGLEN];
1426 	int len, ret;
1427 
1428 	len = 5 + strlen(adsShareName) + strlen(adsContainer) +
1429 	    strlen(ah->domain_dn) + 1;
1430 
1431 	share_dn = (char *)malloc(len);
1432 	if (share_dn == NULL)
1433 		return (-1);
1434 
1435 	(void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName,
1436 	    adsContainer, ah->domain_dn);
1437 	if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) {
1438 		/* LINTED - E_SEC_PRINTF_VAR_FMT */
1439 		syslog(LOG_ERR, ldap_err2string(ret));
1440 		free(share_dn);
1441 		return (-1);
1442 	}
1443 	free(share_dn);
1444 
1445 	(void) snprintf(buf, ADS_MAXMSGLEN,
1446 	    "Share %s has been removed from ADS container: %s.\n",
1447 	    adsShareName, adsContainer);
1448 	syslog(LOG_DEBUG, "smb_ads: %s", buf);
1449 
1450 	return (0);
1451 }
1452 
1453 
1454 /*
1455  * ads_escape_search_filter_chars
1456  *
1457  * This routine will escape the special characters found in a string
1458  * that will later be passed to the ldap search filter.
1459  *
1460  * RFC 1960 - A String Representation of LDAP Search Filters
1461  * 3.  String Search Filter Definition
1462  * If a value must contain one of the characters '*' OR '(' OR ')',
1463  * these characters
1464  * should be escaped by preceding them with the backslash '\' character.
1465  *
1466  * RFC 2252 - LDAP Attribute Syntax Definitions
1467  * a backslash quoting mechanism is used to escape
1468  * the following separator symbol character (such as "'", "$" or "#") if
1469  * it should occur in that string.
1470  */
1471 static int
1472 ads_escape_search_filter_chars(const char *src, char *dst)
1473 {
1474 	int avail = ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */
1475 
1476 	if (src == NULL || dst == NULL)
1477 		return (-1);
1478 
1479 	while (*src) {
1480 		if (!avail) {
1481 			*dst = 0;
1482 			return (-1);
1483 		}
1484 
1485 		switch (*src) {
1486 		case '\\':
1487 		case '\'':
1488 		case '$':
1489 		case '#':
1490 		case '*':
1491 		case '(':
1492 		case ')':
1493 			*dst++ = '\\';
1494 			avail--;
1495 			/* fall through */
1496 
1497 		default:
1498 			*dst++ = *src++;
1499 			avail--;
1500 		}
1501 	}
1502 
1503 	*dst = 0;
1504 
1505 	return (0);
1506 }
1507 
1508 /*
1509  * ads_lookup_share
1510  * The search filter is set to search for a specific share name in the
1511  * specified ADS container.  The LDSAP synchronous search command is used.
1512  * Parameters:
1513  *   ah          : handle to ADS server
1514  *   adsShareName: name of share object in ADS to be searched
1515  *   adsContainer: location of share object in ADS
1516  * Returns:
1517  *   -1          : error
1518  *    0          : not found
1519  *    1          : found
1520  */
1521 int
1522 ads_lookup_share(ADS_HANDLE *ah, const char *adsShareName,
1523     const char *adsContainer, char *unc_name)
1524 {
1525 	char *attrs[4], filter[ADS_MAXBUFLEN];
1526 	char *share_dn;
1527 	int len, ret;
1528 	LDAPMessage *res;
1529 	char tmpbuf[ADS_MAXBUFLEN];
1530 
1531 	if (adsShareName == NULL || adsContainer == NULL)
1532 		return (-1);
1533 
1534 	len = 5 + strlen(adsShareName) + strlen(adsContainer) +
1535 	    strlen(ah->domain_dn) + 1;
1536 
1537 	share_dn = (char *)malloc(len);
1538 	if (share_dn == NULL)
1539 		return (-1);
1540 
1541 	(void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName,
1542 	    adsContainer, ah->domain_dn);
1543 
1544 	res = NULL;
1545 	attrs[0] = "cn";
1546 	attrs[1] = "objectClass";
1547 	attrs[2] = "uNCName";
1548 	attrs[3] = NULL;
1549 
1550 	if (ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) {
1551 		free(share_dn);
1552 		return (-1);
1553 	}
1554 
1555 	(void) snprintf(filter, sizeof (filter),
1556 	    "(&(objectClass=volume)(uNCName=%s))", tmpbuf);
1557 
1558 	if ((ret = ldap_search_s(ah->ld, share_dn,
1559 	    LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) {
1560 		/* LINTED - E_SEC_PRINTF_VAR_FMT */
1561 		syslog(LOG_ERR, ldap_err2string(ret));
1562 		(void) ldap_msgfree(res);
1563 		free(share_dn);
1564 		return (0);
1565 	}
1566 
1567 	(void) free(share_dn);
1568 
1569 	/* no match is found */
1570 	if (ldap_count_entries(ah->ld, res) == 0) {
1571 		(void) ldap_msgfree(res);
1572 		return (0);
1573 	}
1574 
1575 	/* free the search results */
1576 	(void) ldap_msgfree(res);
1577 
1578 	return (1);
1579 }
1580 
1581 /*
1582  * ads_convert_directory
1583  * Convert relative share directory to UNC to be appended to hostname.
1584  * i.e. cvol/a/b -> cvol\a\b
1585  */
1586 char *
1587 ads_convert_directory(char *rel_dir)
1588 {
1589 	char *t, *s2;
1590 	int len;
1591 
1592 	if (rel_dir == NULL)
1593 		return (NULL);
1594 
1595 	len = strlen(rel_dir) + 1;
1596 	s2 = (char *)malloc(len);
1597 	if (s2 == NULL)
1598 		return (NULL);
1599 
1600 	t = s2;
1601 	while (*rel_dir) {
1602 		if (*rel_dir == '/') {
1603 			*t++ = '\\';
1604 			rel_dir++;
1605 		} else {
1606 			*t++ = *rel_dir++;
1607 		}
1608 	}
1609 	*t = '\0';
1610 	return (s2);
1611 }
1612 
1613 /*
1614  * ads_publish_share
1615  * Publish share into ADS.  If a share name already exist in ADS in the same
1616  * container then the existing share object is removed before adding the new
1617  * share object.
1618  * Parameters:
1619  *   ah          : handle return from ads_open
1620  *   adsShareName: name of share to be added to ADS directory
1621  *   shareUNC    : name of share on client, can be NULL to use the same name
1622  *                 as adsShareName
1623  *   adsContainer: location for share to be added in ADS directory, ie
1624  *                   ou=share_folder
1625  *   uncType     : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR
1626  *                   to use host ip addr for UNC.
1627  * Returns:
1628  *   -1          : error
1629  *    0          : success
1630  */
1631 int
1632 ads_publish_share(ADS_HANDLE *ah, const char *adsShareName,
1633     const char *shareUNC, const char *adsContainer, const char *hostname)
1634 {
1635 	int ret;
1636 	char unc_name[ADS_MAXBUFLEN];
1637 
1638 	if (adsShareName == NULL || adsContainer == NULL)
1639 		return (-1);
1640 
1641 	if (shareUNC == 0 || *shareUNC == 0)
1642 		shareUNC = adsShareName;
1643 
1644 	if (ads_build_unc_name(unc_name, sizeof (unc_name),
1645 	    hostname, shareUNC) < 0) {
1646 		syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' "
1647 		    "[missing UNC name]", shareUNC);
1648 		return (-1);
1649 	}
1650 
1651 	ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name);
1652 
1653 	switch (ret) {
1654 	case 1:
1655 		(void) ads_del_share(ah, adsShareName, adsContainer);
1656 		ret = ads_add_share(ah, adsShareName, unc_name, adsContainer);
1657 		break;
1658 
1659 	case 0:
1660 		ret = ads_add_share(ah, adsShareName, unc_name, adsContainer);
1661 		if (ret == LDAP_ALREADY_EXISTS) {
1662 			syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' "
1663 			    "[name is already in use]", adsShareName);
1664 			ret = -1;
1665 		}
1666 		break;
1667 
1668 	case -1:
1669 	default:
1670 		/* return with error code */
1671 		ret = -1;
1672 	}
1673 
1674 	return (ret);
1675 }
1676 
1677 /*
1678  * ads_remove_share
1679  * Remove share from ADS.  A search is done first before explicitly removing
1680  * the share.
1681  * Parameters:
1682  *   ah          : handle return from ads_open
1683  *   adsShareName: name of share to be removed from ADS directory
1684  *   adsContainer: location for share to be removed from ADS directory, ie
1685  *                   ou=share_folder
1686  * Returns:
1687  *   -1          : error
1688  *    0          : success
1689  */
1690 int
1691 ads_remove_share(ADS_HANDLE *ah, const char *adsShareName, const char *shareUNC,
1692     const char *adsContainer, const char *hostname)
1693 {
1694 	int ret;
1695 	char unc_name[ADS_MAXBUFLEN];
1696 
1697 	if (adsShareName == NULL || adsContainer == NULL)
1698 		return (-1);
1699 	if (shareUNC == 0 || *shareUNC == 0)
1700 		shareUNC = adsShareName;
1701 
1702 	if (ads_build_unc_name(unc_name, sizeof (unc_name),
1703 	    hostname, shareUNC) < 0) {
1704 		syslog(LOG_DEBUG, "smb_ads: Unable to remove share '%s' from "
1705 		    "ADS [missing UNC name]", shareUNC);
1706 		return (-1);
1707 	}
1708 
1709 	ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name);
1710 	if (ret == 0)
1711 		return (0);
1712 	if (ret == -1)
1713 		return (-1);
1714 
1715 	return (ads_del_share(ah, adsShareName, adsContainer));
1716 }
1717 
1718 /*
1719  * ads_get_computer_dn
1720  *
1721  * Build the distinguish name for this system.
1722  */
1723 static void
1724 ads_get_computer_dn(ADS_HANDLE *ah, char *buf, size_t buflen)
1725 {
1726 	char hostname[MAXHOSTNAMELEN];
1727 
1728 	(void) smb_gethostname(hostname, MAXHOSTNAMELEN, 0);
1729 	(void) snprintf(buf, buflen, "cn=%s,cn=%s,%s",
1730 	    hostname, ADS_COMPUTERS_CN, ah->domain_dn);
1731 }
1732 
1733 static char *
1734 ads_get_host_principal(char *fqhost)
1735 {
1736 	int len;
1737 	char *princ;
1738 
1739 	if (!fqhost)
1740 		return (NULL);
1741 
1742 	len = strlen(ADS_HOST_PREFIX) + strlen(fqhost) + 1;
1743 	princ = (char *)malloc(len);
1744 
1745 	if (!princ) {
1746 		syslog(LOG_ERR, "ads_get_host_principal: resource shortage");
1747 		return (NULL);
1748 	}
1749 	(void) snprintf(princ, len, "%s%s", ADS_HOST_PREFIX,
1750 	    fqhost);
1751 
1752 	return (princ);
1753 }
1754 
1755 static char *
1756 ads_get_host_principal_w_realm(char *princ, char *domain)
1757 {
1758 	int len;
1759 	char *realm;
1760 	char *princ_r;
1761 
1762 	if (!princ || !domain)
1763 		return (NULL);
1764 
1765 	realm = strdup(domain);
1766 	if (!realm)
1767 		return (NULL);
1768 
1769 	(void) utf8_strupr(realm);
1770 
1771 	len = strlen(princ) + 1 + strlen(realm) + 1;
1772 	princ_r = (char *)malloc(len);
1773 	if (!princ_r) {
1774 		syslog(LOG_ERR, "ads_get_host_principal_w_realm: resource"
1775 		    " shortage");
1776 		free(realm);
1777 		return (NULL);
1778 	}
1779 
1780 	(void) snprintf(princ_r, len, "%s@%s", princ, realm);
1781 	free(realm);
1782 
1783 	return (princ_r);
1784 }
1785 
1786 /*
1787  * ads_get_host_principals
1788  *
1789  * If fqhost is NULL, this function will attempt to obtain fully qualified
1790  * hostname prior to generating the host principals.
1791  */
1792 static int
1793 ads_get_host_principals(char *fqhost, char *domain, char **princ,
1794     char **princ_r)
1795 {
1796 	char hostname[MAXHOSTNAMELEN];
1797 
1798 	*princ = *princ_r = NULL;
1799 
1800 	if (fqhost) {
1801 		(void) strlcpy(hostname, fqhost, MAXHOSTNAMELEN);
1802 	} else {
1803 		if (smb_getfqhostname(hostname, MAXHOSTNAMELEN) != 0)
1804 			return (-1);
1805 	}
1806 
1807 	if ((*princ = ads_get_host_principal(hostname)) == NULL) {
1808 		return (-1);
1809 	}
1810 
1811 	*princ_r = ads_get_host_principal_w_realm(*princ, domain);
1812 	if (*princ_r == NULL) {
1813 		free(*princ);
1814 		return (-1);
1815 	}
1816 
1817 	return (0);
1818 }
1819 
1820 /*
1821  * ads_add_computer
1822  *
1823  * Returns 0 upon success. Otherwise, returns -1.
1824  */
1825 static int
1826 ads_add_computer(ADS_HANDLE *ah)
1827 {
1828 	LDAPMod *addattrs[7];
1829 	char *oc_vals[6], *sam_val[2], *usr_val[2];
1830 	char *svc_val[2], *ctl_val[2], *fqh_val[2];
1831 	int j = -1;
1832 	int ret, usrctl_flags = 0;
1833 	char sam_acct[MAXHOSTNAMELEN + 1];
1834 	char fqhost[MAXHOSTNAMELEN];
1835 	char dn[ADS_DN_MAX];
1836 	char *user_principal, *svc_principal;
1837 	char usrctl_buf[16];
1838 
1839 	if (smb_getfqhostname(fqhost, MAXHOSTNAMELEN) != 0)
1840 		return (-1);
1841 
1842 	if (smb_gethostname(sam_acct, MAXHOSTNAMELEN, 0) != 0)
1843 		return (-1);
1844 
1845 	(void) strlcat(sam_acct, "$", MAXHOSTNAMELEN + 1);
1846 
1847 	if (ads_get_host_principals(fqhost, ah->domain, &svc_principal,
1848 	    &user_principal) == -1) {
1849 		syslog(LOG_ERR,
1850 		    "ads_add_computer: unable to get host principal");
1851 		return (-1);
1852 	}
1853 
1854 	ads_get_computer_dn(ah, dn, ADS_DN_MAX);
1855 
1856 	addattrs[++j] = (LDAPMod *)malloc(sizeof (LDAPMod));
1857 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1858 	addattrs[j]->mod_type = "objectClass";
1859 	oc_vals[0] = "top";
1860 	oc_vals[1] = "person";
1861 	oc_vals[2] = "organizationalPerson";
1862 	oc_vals[3] = "user";
1863 	oc_vals[4] = "computer";
1864 	oc_vals[5] = 0;
1865 	addattrs[j]->mod_values = oc_vals;
1866 
1867 	addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
1868 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1869 	addattrs[j]->mod_type = "sAMAccountName";
1870 
1871 	sam_val[0] = sam_acct;
1872 	sam_val[1] = 0;
1873 	addattrs[j]->mod_values = sam_val;
1874 
1875 
1876 	addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
1877 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1878 	addattrs[j]->mod_type = "userPrincipalName";
1879 
1880 	usr_val[0] = user_principal;
1881 	usr_val[1] = 0;
1882 	addattrs[j]->mod_values = usr_val;
1883 
1884 	addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
1885 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1886 	addattrs[j]->mod_type = "servicePrincipalName";
1887 
1888 	svc_val[0] = svc_principal;
1889 	svc_val[1] = 0;
1890 	addattrs[j]->mod_values = svc_val;
1891 
1892 	addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
1893 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1894 	addattrs[j]->mod_type = "userAccountControl";
1895 
1896 	usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
1897 	    ADS_USER_ACCT_CTL_PASSWD_NOTREQD |
1898 	    ADS_USER_ACCT_CTL_ACCOUNTDISABLE);
1899 
1900 	(void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags);
1901 	ctl_val[0] = usrctl_buf;
1902 	ctl_val[1] = 0;
1903 	addattrs[j]->mod_values = ctl_val;
1904 	addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
1905 	addattrs[j]->mod_op = LDAP_MOD_ADD;
1906 	addattrs[j]->mod_type = "dNSHostName";
1907 
1908 	fqh_val[0] = fqhost;
1909 	fqh_val[1] = 0;
1910 	addattrs[j]->mod_values = fqh_val;
1911 	addattrs[++j] = 0;
1912 
1913 	if ((ret = ldap_add_s(ah->ld, dn, addattrs)) != LDAP_SUCCESS) {
1914 		syslog(LOG_ERR, "ads_add_computer: %s", ldap_err2string(ret));
1915 		ret = -1;
1916 	}
1917 
1918 	free_attr(addattrs);
1919 	free(user_principal);
1920 	free(svc_principal);
1921 
1922 	return (ret);
1923 }
1924 
1925 /*
1926  * Delete an ADS computer account.
1927  */
1928 static void
1929 ads_del_computer(ADS_HANDLE *ah)
1930 {
1931 	char dn[ADS_DN_MAX];
1932 	int rc;
1933 
1934 	ads_get_computer_dn(ah, dn, ADS_DN_MAX);
1935 
1936 	if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) {
1937 		syslog(LOG_DEBUG, "ads_del_computer: %s",
1938 		    ldap_err2string(rc));
1939 	}
1940 }
1941 
1942 /*
1943  * ads_lookup_computer_n_attr
1944  *
1945  * Lookup the value of the specified attribute on the computer
1946  * object. If the specified attribute can be found, its value is returned
1947  * via 'val' parameter.
1948  *
1949  * 'attr' parameter can be set to NULL if you only attempt to
1950  * see whether the computer object exists on AD or not.
1951  *
1952  * Return:
1953  *  1 if both the computer and the specified attribute is found.
1954  *  0 if either the computer or the specified attribute is not found.
1955  * -1 on error.
1956  */
1957 static int
1958 ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val)
1959 {
1960 	char *attrs[2], filter[ADS_MAXBUFLEN];
1961 	LDAPMessage *res, *entry;
1962 	char **vals;
1963 	char tmpbuf[ADS_MAXBUFLEN];
1964 	char my_hostname[MAXHOSTNAMELEN], sam_acct[MAXHOSTNAMELEN + 1];
1965 	char dn[ADS_DN_MAX];
1966 
1967 	if (smb_gethostname(my_hostname, MAXHOSTNAMELEN, 0) != 0)
1968 		return (-1);
1969 
1970 	(void) snprintf(sam_acct, sizeof (sam_acct), "%s$", my_hostname);
1971 	ads_get_computer_dn(ah, dn, ADS_DN_MAX);
1972 
1973 	res = NULL;
1974 	attrs[0] = attr;
1975 	attrs[1] = NULL;
1976 
1977 	if (ads_escape_search_filter_chars(sam_acct, tmpbuf) != 0) {
1978 		return (-1);
1979 	}
1980 
1981 	(void) snprintf(filter, sizeof (filter),
1982 	    "(&(objectClass=computer)(sAMAccountName=%s))",
1983 	    tmpbuf);
1984 
1985 	if (ldap_search_s(ah->ld, dn, LDAP_SCOPE_BASE, filter, attrs, 0,
1986 	    &res) != LDAP_SUCCESS) {
1987 		(void) ldap_msgfree(res);
1988 		return (0);
1989 	}
1990 
1991 	if (attr) {
1992 		/* no match for the specified attribute is found */
1993 		if (ldap_count_entries(ah->ld, res) == 0) {
1994 			if (val)
1995 				*val = NULL;
1996 
1997 			(void) ldap_msgfree(res);
1998 			return (0);
1999 		}
2000 
2001 		entry = ldap_first_entry(ah->ld, res);
2002 		if (entry) {
2003 			vals = ldap_get_values(ah->ld, entry, attr);
2004 			if (!vals && val) {
2005 				*val = NULL;
2006 				(void) ldap_msgfree(res);
2007 				return (0);
2008 			}
2009 
2010 			if (vals[0] != NULL && val)
2011 				*val = strdup(vals[0]);
2012 		}
2013 	}
2014 
2015 	/* free the search results */
2016 	(void) ldap_msgfree(res);
2017 	return (1);
2018 }
2019 
2020 /*
2021  * ads_find_computer
2022  *
2023  * Return:
2024  *  1 if found.
2025  *  0 if not found or encounters error.
2026  */
2027 static int
2028 ads_find_computer(ADS_HANDLE *ah)
2029 {
2030 	return (ads_lookup_computer_n_attr(ah, NULL, NULL) == 1);
2031 }
2032 
2033 /*
2034  * ads_modify_computer
2035  *
2036  * Modify the user account control attribute of an existing computer
2037  * object on AD.
2038  *
2039  * Returns 0 on success. Otherwise, returns -1.
2040  */
2041 static int
2042 ads_modify_computer(ADS_HANDLE *ah, int des_only)
2043 {
2044 	LDAPMod *attrs[6];
2045 	char *ctl_val[2];
2046 	int j = -1;
2047 	int ret, usrctl_flags = 0;
2048 	char dn[ADS_DN_MAX];
2049 	char usrctl_buf[16];
2050 
2051 	ads_get_computer_dn(ah, dn, ADS_DN_MAX);
2052 
2053 	attrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
2054 	attrs[j]->mod_op = LDAP_MOD_REPLACE;
2055 	attrs[j]->mod_type = "userAccountControl";
2056 
2057 	usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
2058 	    ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION |
2059 	    ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
2060 
2061 	if (des_only)
2062 		usrctl_flags |= ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY;
2063 
2064 	(void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags);
2065 	ctl_val[0] = usrctl_buf;
2066 	ctl_val[1] = 0;
2067 	attrs[j]->mod_values = ctl_val;
2068 
2069 	attrs[++j] = 0;
2070 
2071 	if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) {
2072 		syslog(LOG_ERR, "ads_modify_computer: %s",
2073 		    ldap_err2string(ret));
2074 		ret = -1;
2075 	}
2076 
2077 	free_attr(attrs);
2078 	return (ret);
2079 }
2080 
2081 /*
2082  * ads_lookup_computer_attr_kvno
2083  *
2084  * Lookup the value of the Kerberos version number attribute of the computer
2085  * account.
2086  */
2087 static krb5_kvno
2088 ads_lookup_computer_attr_kvno(ADS_HANDLE *ah)
2089 {
2090 	char *val = NULL;
2091 	int kvno = 1;
2092 
2093 	if (ads_lookup_computer_n_attr(ah, "ms-DS-KeyVersionNumber",
2094 	    &val) == 1) {
2095 		if (val) {
2096 			kvno = atoi(val);
2097 			free(val);
2098 		}
2099 	}
2100 
2101 	return (kvno);
2102 }
2103 
2104 /*
2105  * ads_gen_machine_passwd
2106  *
2107  * Returned a null-terminated machine password generated randomly
2108  * from [0-9a-zA-Z] character set. In order to pass the password
2109  * quality check (three character classes), an uppercase letter is
2110  * used as the first character of the machine password.
2111  */
2112 static int
2113 ads_gen_machine_passwd(char *machine_passwd, int bufsz)
2114 {
2115 	char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK"
2116 	    "LMNOPQRSTUVWXYZ";
2117 	int datalen = strlen(data);
2118 	int i, data_idx;
2119 
2120 	if (!machine_passwd || bufsz == 0)
2121 		return (-1);
2122 
2123 	/*
2124 	 * The decimal value of upper case 'A' is 65. Randomly pick
2125 	 * an upper-case letter from the ascii table.
2126 	 */
2127 	machine_passwd[0] = (random() % 26) + 65;
2128 	for (i = 1; i < bufsz - 1; i++) {
2129 		data_idx = random() % datalen;
2130 		machine_passwd[i] = data[data_idx];
2131 	}
2132 
2133 	machine_passwd[bufsz - 1] = 0;
2134 	return (0);
2135 }
2136 
2137 /*
2138  * adjoin
2139  *
2140  * Besides the NT-4 style domain join (using MS-RPC), CIFS server also
2141  * provides the domain join using Kerberos Authentication, Keberos
2142  * Change & Set password, and LDAP protocols. Basically, adjoin
2143  * operation would require the following tickets to be acquired for the
2144  * the user account that is provided for the domain join.
2145  *
2146  * 1) a Keberos TGT ticket,
2147  * 2) a ldap service ticket, and
2148  * 3) kadmin/changpw service ticket
2149  *
2150  * The ADS client first sends a ldap search request to find out whether
2151  * or not the workstation trust account already exists in the Active Directory.
2152  * The existing computer object for this workstation will be removed and
2153  * a new one will be added. The machine account password is randomly
2154  * generated and set for the newly created computer object using KPASSD
2155  * protocol (See RFC 3244). Once the password is set, our ADS client
2156  * finalizes the machine account by modifying the user acount control
2157  * attribute of the computer object. Kerberos keys derived from the machine
2158  * account password will be stored locally in /etc/krb5/krb5.keytab file.
2159  * That would be needed while acquiring Kerberos TGT ticket for the host
2160  * principal after the domain join operation.
2161  */
2162 adjoin_status_t
2163 adjoin(char *machine_passwd, int len)
2164 {
2165 	ADS_HANDLE *ah = NULL;
2166 	krb5_enctype enctypes[10];
2167 	krb5_context ctx = NULL;
2168 	krb5_principal krb5princ;
2169 	krb5_kvno kvno;
2170 	char *princ, *princ_r;
2171 	int des_only, delete = 1, fini_krbctx = 1;
2172 	adjoin_status_t rc = ADJOIN_SUCCESS;
2173 	struct stat fstat;
2174 
2175 	char *idmap_ccache = "/var/run/idmap/ccache";
2176 
2177 	if ((ah = ads_open()) == NULL)
2178 		return (ADJOIN_ERR_GET_HANDLE);
2179 
2180 	if (ads_gen_machine_passwd(machine_passwd, len) != 0) {
2181 		ads_close(ah);
2182 		return (ADJOIN_ERR_GEN_PASSWD);
2183 	}
2184 
2185 	if (ads_find_computer(ah))
2186 		ads_del_computer(ah);
2187 
2188 	if (ads_add_computer(ah) != 0) {
2189 		ads_close(ah);
2190 		return (ADJOIN_ERR_ADD_TRUST_ACCT);
2191 	}
2192 
2193 	/*
2194 	 * Call library functions that can be used to get
2195 	 * the list of encryption algorithms available on the system.
2196 	 * (similar to what 'encrypt -l' CLI does). For now,
2197 	 * unless someone has modified the configuration of the
2198 	 * cryptographic framework (very unlikely), the following is the
2199 	 * list of algorithms available on any system running Nevada
2200 	 * by default.
2201 	 */
2202 	enctypes[0] = ENCTYPE_DES_CBC_CRC;
2203 	enctypes[1] = ENCTYPE_DES_CBC_MD5;
2204 	enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
2205 	enctypes[3] = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
2206 
2207 	des_only = 0;
2208 
2209 	/*
2210 	 * If we are talking to a Longhorn server, we need to set up
2211 	 * the msDS-SupportedEncryptionTypes attribute of the computer
2212 	 * object accordingly
2213 	 *
2214 	 * The code to modify the msDS-SupportedEncryptionTypes can be
2215 	 * added once we figure out why the Longhorn server rejects the
2216 	 * SmbSessionSetup request sent by SMB redirector.
2217 	 */
2218 
2219 	if (ads_get_host_principals(NULL, ah->domain, &princ, &princ_r) == -1) {
2220 		ads_del_computer(ah);
2221 		ads_close(ah);
2222 		return (ADJOIN_ERR_GET_HOST_PRINC);
2223 	}
2224 
2225 	if (smb_krb5_ctx_init(&ctx) != 0) {
2226 		fini_krbctx = 0;
2227 		rc = ADJOIN_ERR_INIT_KRB_CTX;
2228 		goto adjoin_cleanup;
2229 	}
2230 
2231 	if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) {
2232 		rc = ADJOIN_ERR_GET_KRB_PRINC;
2233 		goto adjoin_cleanup;
2234 	}
2235 
2236 	if (smb_krb5_setpwd(ctx, krb5princ, machine_passwd) != 0) {
2237 		rc = ADJOIN_ERR_KSETPWD;
2238 		goto adjoin_cleanup;
2239 	}
2240 
2241 	kvno = ads_lookup_computer_attr_kvno(ah);
2242 	if (ads_modify_computer(ah, des_only) != 0) {
2243 		rc = ADJOIN_ERR_MOD_TRUST_ACCT;
2244 		goto adjoin_cleanup;
2245 	}
2246 	if (smb_krb5_write_keytab(ctx, krb5princ, "/etc/krb5/krb5.keytab", kvno,
2247 	    machine_passwd, enctypes, 4) != 0) {
2248 		rc = ADJOIN_ERR_WRITE_KEYTAB;
2249 		goto adjoin_cleanup;
2250 	}
2251 
2252 	/* Set IDMAP config */
2253 	if (smb_config_set_idmap_domain(ah->domain) != 0) {
2254 		rc = ADJOIN_ERR_IDMAP_SET_DOMAIN;
2255 		goto adjoin_cleanup;
2256 	}
2257 
2258 	if (smb_config_set_idmap_gc(ah->hostname) != 0) {
2259 		rc = ADJOIN_ERR_IDMAP_SET_GC;
2260 		goto adjoin_cleanup;
2261 	}
2262 
2263 	/* Refresh IDMAP service */
2264 	if (smb_config_refresh_idmap() != 0) {
2265 		rc = ADJOIN_ERR_IDMAP_REFRESH;
2266 		goto adjoin_cleanup;
2267 	}
2268 
2269 	/* Remove the idmap ccache */
2270 	if (stat(idmap_ccache, &fstat) == 0) {
2271 		if (remove(idmap_ccache) != 0) {
2272 			rc = ADJOIN_ERR_IDMAP_CCACHE;
2273 			goto adjoin_cleanup;
2274 		}
2275 	}
2276 
2277 	delete = 0;
2278 adjoin_cleanup:
2279 	if (delete)
2280 		ads_del_computer(ah);
2281 
2282 	if (fini_krbctx)
2283 		smb_krb5_ctx_fini(ctx);
2284 
2285 	ads_close(ah);
2286 	free(princ);
2287 	free(princ_r);
2288 
2289 	return (rc);
2290 }
2291 
2292 /*
2293  * adjoin_report_err
2294  *
2295  * Display error message for the specific adjoin error code.
2296  */
2297 char *
2298 adjoin_report_err(adjoin_status_t status)
2299 {
2300 	if (status < 0 || status >= ADJOIN_NUM_STATUS)
2301 		return ("ADJOIN: unknown status");
2302 
2303 	return (adjoin_errmsg[status]);
2304 }
2305