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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "@(#)smbns_ads.c 1.12 08/08/06 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 SMB_ADS_DN_MAX 300 56 #define SMB_ADS_MAXMSGLEN 512 57 #define SMB_ADS_COMPUTERS_CN "Computers" 58 #define SMB_ADS_COMPUTER_NUM_ATTR 8 59 #define SMB_ADS_SHARE_NUM_ATTR 3 60 #define SMB_ADS_SITE_MAX MAXHOSTNAMELEN 61 62 #define SMB_ADS_MSDCS_SRV_DC_RR "_ldap._tcp.dc._msdcs" 63 #define SMB_ADS_MSDCS_SRV_SITE_RR "_ldap._tcp.%s._sites.dc._msdcs" 64 65 /* 66 * domainControllerFunctionality 67 * 68 * This rootDSE attribute indicates the functional level of the DC. 69 */ 70 #define SMB_ADS_ATTR_DCLEVEL "domainControllerFunctionality" 71 #define SMB_ADS_DCLEVEL_W2K 0 72 #define SMB_ADS_DCLEVEL_W2K3 2 73 #define SMB_ADS_DCLEVEL_W2K8 3 74 75 /* 76 * msDs-supportedEncryptionTypes (Windows Server 2008 only) 77 * 78 * This attribute defines the encryption types supported by the system. 79 * Encryption Types: 80 * - DES cbc mode with CRC-32 81 * - DES cbc mode with RSA-MD5 82 * - ArcFour with HMAC/md5 83 * - AES-128 84 * - AES-256 85 */ 86 #define SMB_ADS_ATTR_ENCTYPES "msDs-supportedEncryptionTypes" 87 #define SMB_ADS_ENC_DES_CRC 1 88 #define SMB_ADS_ENC_DES_MD5 2 89 #define SMB_ADS_ENC_RC4 4 90 #define SMB_ADS_ENC_AES128 8 91 #define SMB_ADS_ENC_AES256 16 92 93 #define SMB_ADS_ATTR_SAMACCT "sAMAccountName" 94 #define SMB_ADS_ATTR_UPN "userPrincipalName" 95 #define SMB_ADS_ATTR_SPN "servicePrincipalName" 96 #define SMB_ADS_ATTR_CTL "userAccountControl" 97 #define SMB_ADS_ATTR_DNSHOST "dNSHostName" 98 #define SMB_ADS_ATTR_KVNO "msDS-KeyVersionNumber" 99 #define SMB_ADS_ATTR_DN "distinguishedName" 100 101 /* current ADS server to communicate with */ 102 static smb_ads_host_info_t *ads_host_info = NULL; 103 static mutex_t ads_host_mtx; 104 static char ads_site[SMB_ADS_SITE_MAX]; 105 static mutex_t ads_site_mtx; 106 107 /* 108 * adjoin_errmsg 109 * 110 * Use the adjoin return status defined in adjoin_status_t as the index 111 * to this table. 112 */ 113 static char *adjoin_errmsg[] = { 114 "ADJOIN succeeded.", 115 "ADJOIN failed to get handle.", 116 "ADJOIN failed to generate machine password.", 117 "ADJOIN failed to add workstation trust account.", 118 "ADJOIN failed to modify workstation trust account.", 119 "ADJOIN failed to get list of encryption types.", 120 "ADJOIN failed to initialize kerberos context.", 121 "ADJOIN failed to get Kerberos principal.", 122 "ADJOIN failed to set machine account password on AD.", 123 "ADJOIN failed to modify CONTROL attribute of the account.", 124 "ADJOIN failed to write Kerberos keytab file.", 125 "ADJOIN failed to configure domain_name property for idmapd.", 126 "ADJOIN failed to refresh idmap service." 127 }; 128 129 static smb_ads_handle_t *smb_ads_open_main(char *, char *, char *); 130 static int smb_ads_bind(smb_ads_handle_t *); 131 static int smb_ads_add_computer(smb_ads_handle_t *, int, char *); 132 static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *); 133 static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *); 134 static int smb_ads_lookup_computer_n_attr(smb_ads_handle_t *, char *, char **, 135 int, char *); 136 static int smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *, int, char *); 137 static krb5_kvno smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *, char *); 138 static int smb_ads_gen_machine_passwd(char *, int); 139 static smb_ads_host_info_t *smb_ads_get_host_info(void); 140 static void smb_ads_set_host_info(smb_ads_host_info_t *); 141 static void smb_ads_free_host_info(void); 142 static int smb_ads_get_spnset(char *, char **); 143 static void smb_ads_free_spnset(char **); 144 static int smb_ads_alloc_attr(LDAPMod **, int); 145 static void smb_ads_free_attr(LDAPMod **); 146 static int smb_ads_get_dc_level(smb_ads_handle_t *); 147 static smb_ads_host_info_t *smb_ads_select_dc(smb_ads_host_list_t *); 148 149 /* 150 * smb_ads_init 151 * 152 * Initializes the ads_site global variable. 153 */ 154 void 155 smb_ads_init(void) 156 { 157 (void) mutex_lock(&ads_site_mtx); 158 (void) smb_config_getstr(SMB_CI_ADS_SITE, ads_site, sizeof (ads_site)); 159 (void) mutex_unlock(&ads_site_mtx); 160 } 161 162 /* 163 * smb_ads_refresh 164 * 165 * If the ads_site has changed, clear the ads_host_info cache. 166 */ 167 void 168 smb_ads_refresh(void) 169 { 170 char new_site[SMB_ADS_SITE_MAX]; 171 172 (void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, sizeof (new_site)); 173 (void) mutex_lock(&ads_site_mtx); 174 if (strcasecmp(ads_site, new_site)) { 175 (void) strlcpy(ads_site, new_site, sizeof (ads_site)); 176 smb_ads_free_host_info(); 177 } 178 (void) mutex_unlock(&ads_site_mtx); 179 } 180 181 /* 182 * smb_ads_build_unc_name 183 * 184 * Construct the UNC name of the share object in the format of 185 * \\hostname.domain\shareUNC 186 * 187 * Returns 0 on success, -1 on error. 188 */ 189 int 190 smb_ads_build_unc_name(char *unc_name, int maxlen, 191 const char *hostname, const char *shareUNC) 192 { 193 char my_domain[MAXHOSTNAMELEN]; 194 195 if (smb_getfqdomainname(my_domain, sizeof (my_domain)) != 0) 196 return (-1); 197 198 (void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s", 199 hostname, my_domain, shareUNC); 200 return (0); 201 } 202 203 /* 204 * smb_ads_ldap_ping 205 * 206 * This is used to bind to an ADS server to see 207 * if it is still alive. 208 * 209 * Returns: 210 * -1: error 211 * 0: successful 212 */ 213 /*ARGSUSED*/ 214 static int 215 smb_ads_ldap_ping(smb_ads_host_info_t *ads_host) 216 { 217 struct in_addr addr; 218 int ldversion = LDAP_VERSION3, status, timeoutms = 5 * 1000; 219 LDAP *ld = NULL; 220 221 addr.s_addr = ads_host->ip_addr; 222 223 ld = ldap_init((char *)inet_ntoa(addr), ads_host->port); 224 if (ld == NULL) 225 return (-1); 226 227 ldversion = LDAP_VERSION3; 228 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion); 229 /* setup TCP/IP connect timeout */ 230 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms); 231 232 status = ldap_bind_s(ld, "", NULL, LDAP_AUTH_SIMPLE); 233 234 if (status != LDAP_SUCCESS) { 235 (void) ldap_unbind(ld); 236 return (-1); 237 } 238 239 (void) ldap_unbind(ld); 240 241 return (0); 242 } 243 244 /* 245 * smb_ads_set_host_info 246 * Cache the result of the ADS discovery if the cache is empty. 247 */ 248 static void 249 smb_ads_set_host_info(smb_ads_host_info_t *host) 250 { 251 (void) mutex_lock(&ads_host_mtx); 252 if (!ads_host_info) 253 ads_host_info = host; 254 (void) mutex_unlock(&ads_host_mtx); 255 } 256 257 /* 258 * smb_ads_get_host_info 259 * Get the cached ADS host info. 260 */ 261 static smb_ads_host_info_t * 262 smb_ads_get_host_info(void) 263 { 264 smb_ads_host_info_t *host; 265 266 (void) mutex_lock(&ads_host_mtx); 267 host = ads_host_info; 268 (void) mutex_unlock(&ads_host_mtx); 269 return (host); 270 } 271 272 /* 273 * smb_ads_skip_ques_sec 274 * Skips the question section. 275 */ 276 static int 277 smb_ads_skip_ques_sec(int qcnt, uchar_t **ptr, uchar_t *eom) 278 { 279 int i, len; 280 281 for (i = 0; i < qcnt; i++) { 282 if ((len = dn_skipname(*ptr, eom)) < 0) { 283 syslog(LOG_ERR, "DNS query invalid message format"); 284 return (-1); 285 } 286 *ptr += len + QFIXEDSZ; 287 } 288 289 return (0); 290 } 291 292 /* 293 * smb_ads_decode_host_ans_sec 294 * Decodes ADS hosts, priority, weight and port number from the answer 295 * section based on the current buffer pointer. 296 */ 297 static int 298 smb_ads_decode_host_ans_sec(int ans_cnt, uchar_t **ptr, uchar_t *eom, 299 uchar_t *buf, smb_ads_host_info_t *ads_host_list) 300 { 301 int i, len; 302 smb_ads_host_info_t *ads_host; 303 304 for (i = 0; i < ans_cnt; i++) { 305 ads_host = &ads_host_list[i]; 306 307 if ((len = dn_skipname(*ptr, eom)) < 0) { 308 syslog(LOG_ERR, "DNS query invalid message format"); 309 return (-1); 310 } 311 312 *ptr += len; 313 314 /* skip type, class, ttl */ 315 *ptr += 8; 316 /* data size */ 317 *ptr += 2; 318 319 /* Get priority, weight */ 320 /* LINTED: E_CONSTANT_CONDITION */ 321 NS_GET16(ads_host->priority, *ptr); 322 /* LINTED: E_CONSTANT_CONDITION */ 323 NS_GET16(ads_host->weight, *ptr); 324 325 /* port */ 326 /* LINTED: E_CONSTANT_CONDITION */ 327 NS_GET16(ads_host->port, *ptr); 328 /* domain name */ 329 len = dn_expand(buf, eom, *ptr, ads_host->name, MAXHOSTNAMELEN); 330 if (len < 0) { 331 syslog(LOG_ERR, "DNS query invalid SRV record"); 332 return (-1); 333 } 334 *ptr += len; 335 } 336 337 return (0); 338 } 339 340 /* 341 * smb_ads_skip_auth_sec 342 * Skips the authority section. 343 */ 344 static int 345 smb_ads_skip_auth_sec(int ns_cnt, uchar_t **ptr, uchar_t *eom) 346 { 347 int i, len; 348 uint16_t size; 349 350 for (i = 0; i < ns_cnt; i++) { 351 if ((len = dn_skipname(*ptr, eom)) < 0) { 352 syslog(LOG_ERR, "DNS query invalid message format"); 353 return (-1); 354 } 355 *ptr += len; 356 /* skip type, class, ttl */ 357 *ptr += 8; 358 /* get len of data */ 359 /* LINTED: E_CONSTANT_CONDITION */ 360 NS_GET16(size, *ptr); 361 if ((*ptr + size) > eom) { 362 syslog(LOG_ERR, "DNS query invalid message format"); 363 return (-1); 364 } 365 366 *ptr += size; 367 } 368 369 return (0); 370 } 371 372 /* 373 * smb_ads_decode_host_addi_sec 374 * Decodes ADS hosts and IP Addresses from the additional section based 375 * on the current buffer pointer. 376 */ 377 static int 378 smb_ads_decode_host_addi_sec(int addit_cnt, uchar_t **ptr, uchar_t *eom, 379 uchar_t *buf, smb_ads_host_info_t *ads_host_list) 380 { 381 int i, len; 382 in_addr_t ipaddr; 383 smb_ads_host_info_t *ads_host; 384 385 for (i = 0; i < addit_cnt; i++) { 386 ads_host = &ads_host_list[i]; 387 388 /* domain name */ 389 len = dn_expand(buf, eom, *ptr, ads_host->name, MAXHOSTNAMELEN); 390 if (len < 0) { 391 syslog(LOG_ERR, "DNS query invalid SRV record"); 392 return (-1); 393 } 394 *ptr += len; 395 396 /* skip type, class, TTL, data len */ 397 *ptr += 10; 398 399 /* LINTED: E_CONSTANT_CONDITION */ 400 NS_GET32(ipaddr, *ptr); 401 402 ads_host->ip_addr = htonl(ipaddr); 403 } 404 405 return (0); 406 } 407 408 static void 409 smb_ads_init_host_info(smb_ads_host_info_t *ads_host, 410 char *name, in_addr_t ipaddr, int priority, int weight, int port) 411 { 412 (void) strlcpy(ads_host->name, name, MAXHOSTNAMELEN); 413 ads_host->ip_addr = ipaddr; 414 ads_host->priority = priority; 415 ads_host->weight = weight; 416 ads_host->port = port; 417 } 418 419 /* 420 * smb_ads_hlist_alloc 421 */ 422 smb_ads_host_list_t * 423 smb_ads_hlist_alloc(int count) 424 { 425 int size; 426 smb_ads_host_list_t *hlist; 427 428 size = sizeof (smb_ads_host_info_t) * count; 429 hlist = (smb_ads_host_list_t *)malloc(sizeof (smb_ads_host_list_t)); 430 if (hlist == NULL) 431 return (NULL); 432 433 hlist->ah_cnt = count; 434 hlist->ah_list = (smb_ads_host_info_t *)malloc(size); 435 if (hlist->ah_list == NULL) { 436 free(hlist); 437 return (NULL); 438 } 439 440 bzero(hlist->ah_list, size); 441 return (hlist); 442 } 443 444 /* 445 * smb_ads_hlist_free 446 */ 447 static void 448 smb_ads_hlist_free(smb_ads_host_list_t *host_list) 449 { 450 if (host_list == NULL) 451 return; 452 453 free(host_list->ah_list); 454 free(host_list); 455 } 456 457 /* 458 * smb_ads_find_host 459 * 460 * This routine sends a DNS service location (SRV) query message to the 461 * DNS server via TCP to query it for a list of ADS server(s). Once a reply 462 * is received, the reply message is parsed to get the hostname. A host 463 * lookup by name is done to get the IP address if the additional section of 464 * the reply packet does not contain any IP addresses. If there are IP 465 * addresses populated in the additional section then the additional section 466 * is parsed to obtain the IP addresses. 467 * 468 * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to 469 * guarantee that Microsoft domain controllers are returned. Microsoft domain 470 * controllers are also ADS servers. 471 * 472 * The ADS hostnames are stored in the answer section of the DNS reply message. 473 * The IP addresses are stored in the additional section. 474 * 475 * The DNS reply message may be in compress formed. The compression is done 476 * on repeating domain name label in the message. i.e hostname. 477 * 478 * An ADS host is returned from the list of ADS servers. 479 * 480 * Parameters: 481 * domain: domain of ADS host. 482 * sought: the ADS host to be sought. It can be set to empty string to find 483 * any ADS hosts in that domain. 484 * 485 * Returns: 486 * ADS host: fully qualified hostname, ip address, ldap port. 487 * port : LDAP port of ADS host. 488 * 489 */ 490 /*ARGSUSED*/ 491 smb_ads_host_info_t * 492 smb_ads_find_host(char *domain, char *sought, int *port) 493 { 494 int i, error; 495 struct hostent *h; 496 struct in_addr addr; 497 smb_ads_host_list_t *ans_hlist, *adt_hlist, *res_hlist; 498 smb_ads_host_info_t *ads_host = NULL, *res_host = NULL; 499 smb_ads_host_info_t *ans_hep; /* Pointer to answer host entry */ 500 smb_ads_host_info_t *adt_hep; /* Pointer to additional host entry */ 501 char *curdom = NULL; 502 boolean_t same_domain, istrunc = B_FALSE; 503 int len, qcnt, ans_cnt, ns_cnt, addit_cnt; 504 uchar_t *ptr, *eom; 505 struct __res_state res_state; 506 union { 507 HEADER hdr; 508 uchar_t buf[NS_MAXMSG]; 509 } msg; 510 char site_service[MAXHOSTNAMELEN]; 511 char *svc_name[2] = {site_service, SMB_ADS_MSDCS_SRV_DC_RR}; 512 int hlist_cnt = 0; 513 514 /* 515 * If we have already found an ADS server in the given domain, return 516 * the cached ADS host if either the sought host is not specified or 517 * the cached ADS host matches the sought host. 518 */ 519 res_host = smb_ads_get_host_info(); 520 if (res_host) { 521 curdom = strchr(res_host->name, '.'); 522 same_domain = (curdom && !strcasecmp(++curdom, domain)); 523 if (same_domain && (!sought || 524 (sought && !strncasecmp(res_host->name, sought, 525 strlen(sought))))) { 526 if (smb_ads_ldap_ping(res_host) == 0) 527 return (res_host); 528 } 529 530 smb_ads_free_host_info(); 531 } 532 533 bzero(&res_state, sizeof (struct __res_state)); 534 if (res_ninit(&res_state) < 0) 535 return (NULL); 536 537 /* use TCP */ 538 res_state.options |= RES_USEVC; 539 540 (void) mutex_lock(&ads_site_mtx); 541 if (*ads_site == '\0') 542 *site_service = '\0'; 543 else 544 (void) snprintf(site_service, sizeof (site_service), 545 SMB_ADS_MSDCS_SRV_SITE_RR, ads_site); 546 (void) mutex_unlock(&ads_site_mtx); 547 548 /* 549 * First look for ADS hosts in ADS site if configured. Then try 550 * without ADS site info. 551 */ 552 for (i = 0; i < 2; i++) { 553 if (*svc_name[i] == '\0') 554 continue; 555 556 len = res_nquerydomain(&res_state, svc_name[i], domain, 557 C_IN, T_SRV, msg.buf, sizeof (msg.buf)); 558 559 smb_tracef("Querying DNS for SRV RRs named '%s'", svc_name[i]); 560 561 if (len < 0) { 562 smb_tracef("DNS query for '%s' failed (%s)", 563 svc_name[i], hstrerror(res_state.res_h_errno)); 564 continue; 565 } 566 if (len > sizeof (msg.buf)) { 567 syslog(LOG_ERR, "DNS query %ib message doesn't fit" 568 " into %ib buffer", 569 len, sizeof (msg.buf)); 570 continue; 571 } 572 573 /* 2. parse the reply, skip header and question sections */ 574 575 ptr = msg.buf + sizeof (msg.hdr); 576 eom = msg.buf + len; 577 578 /* check truncated message bit */ 579 if (msg.hdr.tc) 580 istrunc = B_TRUE; 581 582 qcnt = ntohs(msg.hdr.qdcount); 583 ans_cnt = ntohs(msg.hdr.ancount); 584 ns_cnt = ntohs(msg.hdr.nscount); 585 addit_cnt = ntohs(msg.hdr.arcount); 586 587 if (smb_ads_skip_ques_sec(qcnt, &ptr, eom) != 0) { 588 res_ndestroy(&res_state); 589 return (NULL); 590 } 591 592 ans_hlist = smb_ads_hlist_alloc(ans_cnt); 593 if (ans_hlist == NULL) { 594 res_ndestroy(&res_state); 595 return (NULL); 596 } 597 598 ans_hep = ans_hlist->ah_list; 599 600 /* 3. walk through the answer section */ 601 602 if (smb_ads_decode_host_ans_sec(ans_cnt, &ptr, eom, msg.buf, 603 ans_hep) != 0) { 604 smb_ads_hlist_free(ans_hlist); 605 res_ndestroy(&res_state); 606 return (NULL); 607 } 608 609 /* check authority section */ 610 if (ns_cnt > 0) { 611 if (smb_ads_skip_auth_sec(ns_cnt, &ptr, eom) != 0) { 612 smb_ads_hlist_free(ans_hlist); 613 res_ndestroy(&res_state); 614 return (NULL); 615 } 616 } 617 618 /* 619 * Check additional section to get IP address of ADS host. 620 * If additional section contains no IP address(es) then 621 * do a host lookup by hostname to get the IP address. 622 */ 623 if (addit_cnt > 0) { 624 int j; 625 626 adt_hlist = smb_ads_hlist_alloc(addit_cnt); 627 if (adt_hlist == NULL) { 628 smb_ads_hlist_free(ans_hlist); 629 res_ndestroy(&res_state); 630 return (NULL); 631 } 632 adt_hep = adt_hlist->ah_list; 633 634 if (smb_ads_decode_host_addi_sec(addit_cnt, &ptr, eom, 635 msg.buf, adt_hep) != 0) { 636 smb_ads_hlist_free(ans_hlist); 637 smb_ads_hlist_free(adt_hlist); 638 res_ndestroy(&res_state); 639 return (NULL); 640 } 641 642 res_hlist = smb_ads_hlist_alloc(addit_cnt); 643 if (res_hlist == NULL) { 644 smb_ads_hlist_free(ans_hlist); 645 smb_ads_hlist_free(adt_hlist); 646 res_ndestroy(&res_state); 647 return (NULL); 648 } 649 ads_host = res_hlist->ah_list; 650 651 /* pick a host that is up */ 652 for (i = 0; i < addit_cnt; i++) { 653 if ((sought) && 654 (strncasecmp(sought, 655 adt_hep[i].name, 656 strlen(sought)) != 0)) 657 continue; 658 /* 659 * find the host in the list of hosts from 660 * the answer section to get the port number. 661 */ 662 for (j = 0; j < ans_cnt; j++) 663 if (strcmp(adt_hep[i].name, 664 ans_hep[j].name) == 0) 665 break; 666 667 if (j == ans_cnt) { 668 smb_ads_hlist_free(ans_hlist); 669 smb_ads_hlist_free(adt_hlist); 670 smb_ads_hlist_free(res_hlist); 671 res_ndestroy(&res_state); 672 return (NULL); 673 } 674 675 smb_ads_init_host_info(ads_host, 676 adt_hep[i].name, adt_hep[i].ip_addr, 677 ans_hep[j].priority, ans_hep[j].weight, 678 ans_hep[j].port); 679 680 *port = ads_host->port; 681 addr.s_addr = ads_host->ip_addr; 682 smb_tracef("smb_ads: Found ADS server: " 683 "%s (%s) [%d][%d]", ads_host->name, 684 inet_ntoa(addr), ads_host->priority, 685 ads_host->weight); 686 hlist_cnt++; 687 ads_host++; 688 } 689 smb_ads_hlist_free(ans_hlist); 690 smb_ads_hlist_free(adt_hlist); 691 res_ndestroy(&res_state); 692 693 if (hlist_cnt != 0) { 694 res_hlist->ah_cnt = hlist_cnt; 695 res_host = smb_ads_select_dc(res_hlist); 696 smb_ads_set_host_info(res_host); 697 } 698 smb_ads_hlist_free(res_hlist); 699 return (res_host); 700 } else { 701 res_hlist = smb_ads_hlist_alloc(ans_cnt); 702 if (res_hlist == NULL) { 703 smb_ads_hlist_free(ans_hlist); 704 res_ndestroy(&res_state); 705 return (NULL); 706 } 707 ads_host = res_hlist->ah_list; 708 709 /* 710 * Shouldn't get here unless entries exist in DNS but 711 * DNS server did not put them in additional section of 712 * DNS reply packet. 713 */ 714 for (i = 0; i < ans_cnt; i++) { 715 if ((sought) && 716 (strncasecmp(sought, ans_hep[i].name, 717 strlen(sought)) != 0)) 718 continue; 719 720 h = getipnodebyname(ans_hep[i].name, 721 AF_INET, 0, &error); 722 723 if (h == NULL || h->h_addr == NULL) 724 continue; 725 726 (void) memcpy(&ans_hep[i].ip_addr, 727 h->h_addr, sizeof (addr.s_addr)); 728 729 freehostent(h); 730 731 smb_ads_init_host_info(ads_host, 732 ans_hep[i].name, ans_hep[i].ip_addr, 733 ans_hep[i].priority, ans_hep[i].weight, 734 ans_hep[i].port); 735 736 *port = ads_host->port; 737 addr.s_addr = ads_host->ip_addr; 738 smb_tracef("smb_ads: Found ADS server: " 739 "%s (%s) [%d][%d]", ads_host->name, 740 inet_ntoa(addr), ads_host->priority, 741 ads_host->weight); 742 hlist_cnt++; 743 ads_host++; 744 } 745 smb_ads_hlist_free(ans_hlist); 746 res_ndestroy(&res_state); 747 748 if (hlist_cnt != 0) { 749 res_hlist->ah_cnt = hlist_cnt; 750 res_host = smb_ads_select_dc(res_hlist); 751 smb_ads_set_host_info(res_host); 752 } 753 smb_ads_hlist_free(res_hlist); 754 return (res_host); 755 } 756 } 757 res_ndestroy(&res_state); 758 syslog(LOG_ERR, "smb_ads: ADS server is either not found or offline."); 759 760 if (istrunc && sought && *sought != 0) 761 syslog(LOG_WARNING, "smb_ads: Truncated TCP reply message is " 762 "detected for DNS query (SRV) of ADS hosts.\n"); 763 764 return (NULL); 765 } 766 767 /* 768 * smb_ads_convert_domain 769 * Converts a domain string into its distinguished name i.e. a unique 770 * name for an entry in the Directory Service. 771 * Memory is allocated 772 * for the new string. 773 * i.e. procom.com -> dc=procom,dc=com 774 * Parameters: 775 * s: fully qualified DNS domain string 776 * Returns: 777 * NULL if error 778 * DNS domain in LDAP DN string format 779 */ 780 static char * 781 smb_ads_convert_domain(char *s) 782 { 783 char *t, *s2, *t2; 784 int len, cnt; 785 786 if (s == NULL || *s == 0) 787 return (NULL); 788 789 cnt = 0; 790 t = s; 791 while (*t) { 792 if (*t++ == '.') { 793 cnt++; 794 } 795 } 796 797 len = 3 + strlen(s) + cnt*3 + 1; 798 799 s2 = (char *)malloc(len); 800 if (s2 == NULL) 801 return (NULL); 802 803 bzero(s2, len); 804 805 t = s2; 806 (void) strncpy(t, "dc=", 3); 807 t += 3; 808 t2 = s; 809 while (*s) { 810 if (*s == '.') { 811 if (t + 3 >= s2 + len - 1) { 812 syslog(LOG_ERR, "[smb_ads_convert_domain] " 813 "buffer overrun for string " 814 "conversion of %s: tot buf " 815 "sz alloc: %d, last " 816 "written buf offset: %d\n", 817 t2, len, t+3-s2); 818 free(s2); 819 return (NULL); 820 } 821 (void) strncpy(t, ",dc=", 4); 822 t += 4; 823 s++; 824 } else { 825 if (t >= s2 + len - 1) { 826 syslog(LOG_ERR, "[smb_ads_convert_domain] " 827 "buffer overrun for string " 828 "conversion of %s: tot buf " 829 "sz alloc: %d, last " 830 "written buf offset: %d\n", 831 t2, len, t-s2); 832 free(s2); 833 return (NULL); 834 } 835 *t++ = *s++; 836 } 837 } 838 *t = '\0'; 839 return (s2); 840 } 841 842 /* 843 * smb_ads_free_host_info 844 * Free the memory use by the global ads_host_info and set it to NULL. 845 */ 846 static void 847 smb_ads_free_host_info(void) 848 { 849 (void) mutex_lock(&ads_host_mtx); 850 if (ads_host_info) { 851 free(ads_host_info); 852 ads_host_info = NULL; 853 } 854 (void) mutex_unlock(&ads_host_mtx); 855 } 856 857 /* 858 * smb_ads_open 859 * Open a LDAP connection to an ADS server if the system is in domain mode. 860 * Acquire both Kerberos TGT and LDAP service tickets for the host principal. 861 * 862 * This function should only be called after the system is successfully joined 863 * to a domain. 864 */ 865 smb_ads_handle_t * 866 smb_ads_open(void) 867 { 868 char domain[MAXHOSTNAMELEN]; 869 870 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 871 return (NULL); 872 873 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 874 return (NULL); 875 876 return (smb_ads_open_main(domain, NULL, NULL)); 877 } 878 879 /* 880 * smb_ads_open_main 881 * Open a LDAP connection to an ADS server. 882 * If ADS is enabled and the administrative username, password, and 883 * ADS domain are defined then query DNS to find an ADS server if this is the 884 * very first call to this routine. After an ADS server is found then this 885 * server will be used everytime this routine is called until the system is 886 * rebooted or the ADS server becomes unavailable then an ADS server will 887 * be queried again. After the connection is made then an ADS handle 888 * is created to be returned. 889 * 890 * After the LDAP connection, the LDAP version will be set to 3 using 891 * ldap_set_option(). 892 * 893 * The smb_ads_bind() routine is also called before the ADS handle is returned. 894 * Parameters: 895 * domain - fully-qualified domain name 896 * user - the user account for whom the Kerberos TGT ticket and ADS 897 * service tickets are acquired. 898 * password - password of the specified user 899 * 900 * Returns: 901 * NULL : can't connect to ADS server or other errors 902 * smb_ads_handle_t* : handle to ADS server 903 */ 904 static smb_ads_handle_t * 905 smb_ads_open_main(char *domain, char *user, char *password) 906 { 907 smb_ads_handle_t *ah; 908 LDAP *ld; 909 int version = 3, ads_port; 910 smb_ads_host_info_t *ads_host = NULL; 911 struct in_addr addr; 912 913 ads_host = smb_ads_get_host_info(); 914 if (!ads_host) { 915 ads_host = smb_ads_find_host(domain, NULL, &ads_port); 916 if (ads_host == NULL) { 917 syslog(LOG_ERR, "smb_ads: No ADS host found from " 918 "configured nameservers"); 919 return (NULL); 920 } 921 } 922 923 ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t)); 924 if (ah == NULL) 925 return (NULL); 926 (void) memset(ah, 0, sizeof (smb_ads_handle_t)); 927 928 addr.s_addr = ads_host->ip_addr; 929 if ((ld = ldap_init((char *)inet_ntoa(addr), ads_host->port)) == NULL) { 930 syslog(LOG_ERR, "smb_ads: Could not open connection " 931 "to host: %s\n", ads_host->name); 932 smb_ads_free_host_info(); 933 free(ah); 934 return (NULL); 935 } 936 937 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) 938 != LDAP_SUCCESS) { 939 syslog(LOG_ERR, "smb_ads: Could not set " 940 "LDAP_OPT_PROTOCOL_VERSION %d\n", version); 941 smb_ads_free_host_info(); 942 free(ah); 943 (void) ldap_unbind(ld); 944 return (NULL); 945 } 946 947 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 948 ah->ld = ld; 949 ah->user = (user) ? strdup(user) : NULL; 950 ah->pwd = (password) ? strdup(password) : NULL; 951 ah->domain = strdup(domain); 952 953 if (ah->domain == NULL) { 954 smb_ads_close(ah); 955 return (NULL); 956 } 957 958 ah->domain_dn = smb_ads_convert_domain(domain); 959 if (ah->domain_dn == NULL) { 960 smb_ads_close(ah); 961 return (NULL); 962 } 963 964 ah->hostname = strdup(ads_host->name); 965 if (ah->hostname == NULL) { 966 smb_ads_close(ah); 967 return (NULL); 968 } 969 (void) mutex_lock(&ads_site_mtx); 970 if (*ads_site != '\0') { 971 if ((ah->site = strdup(ads_site)) == NULL) { 972 smb_ads_close(ah); 973 (void) mutex_unlock(&ads_site_mtx); 974 return (NULL); 975 } 976 } else { 977 ah->site = NULL; 978 } 979 (void) mutex_unlock(&ads_site_mtx); 980 981 if (smb_ads_bind(ah) == -1) { 982 smb_ads_close(ah); 983 return (NULL); 984 } 985 986 return (ah); 987 } 988 989 /* 990 * smb_ads_close 991 * Close connection to ADS server and free memory allocated for ADS handle. 992 * LDAP unbind is called here. 993 * Parameters: 994 * ah: handle to ADS server 995 * Returns: 996 * void 997 */ 998 void 999 smb_ads_close(smb_ads_handle_t *ah) 1000 { 1001 int len; 1002 1003 if (ah == NULL) 1004 return; 1005 /* close and free connection resources */ 1006 if (ah->ld) 1007 (void) ldap_unbind(ah->ld); 1008 1009 free(ah->user); 1010 if (ah->pwd) { 1011 len = strlen(ah->pwd); 1012 /* zero out the memory that contains user's password */ 1013 if (len > 0) 1014 bzero(ah->pwd, len); 1015 free(ah->pwd); 1016 } 1017 free(ah->domain); 1018 free(ah->domain_dn); 1019 free(ah->hostname); 1020 free(ah->site); 1021 free(ah); 1022 } 1023 1024 /* 1025 * smb_ads_display_stat 1026 * Display error message for GSS-API routines. 1027 * Parameters: 1028 * maj: GSS major status 1029 * min: GSS minor status 1030 * Returns: 1031 * None 1032 */ 1033 static void 1034 smb_ads_display_stat(OM_uint32 maj, OM_uint32 min) 1035 { 1036 gss_buffer_desc msg; 1037 OM_uint32 msg_ctx = 0; 1038 OM_uint32 min2; 1039 (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, 1040 &msg_ctx, &msg); 1041 syslog(LOG_ERR, "smb_ads: major status error: %s\n", (char *)msg.value); 1042 (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, 1043 &msg_ctx, &msg); 1044 syslog(LOG_ERR, "smb_ads: minor status error: %s\n", (char *)msg.value); 1045 } 1046 1047 /* 1048 * smb_ads_alloc_attr 1049 * 1050 * Since the attrs is a null-terminated array, all elements 1051 * in the array (except the last one) will point to allocated 1052 * memory. 1053 */ 1054 static int 1055 smb_ads_alloc_attr(LDAPMod *attrs[], int num) 1056 { 1057 int i; 1058 1059 bzero(attrs, num * sizeof (LDAPMod *)); 1060 for (i = 0; i < (num - 1); i++) { 1061 attrs[i] = (LDAPMod *)malloc(sizeof (LDAPMod)); 1062 if (attrs[i] == NULL) { 1063 smb_ads_free_attr(attrs); 1064 return (-1); 1065 } 1066 } 1067 1068 return (0); 1069 } 1070 1071 /* 1072 * smb_ads_free_attr 1073 * Free memory allocated when publishing a share. 1074 * Parameters: 1075 * attrs: an array of LDAPMod pointers 1076 * Returns: 1077 * None 1078 */ 1079 static void 1080 smb_ads_free_attr(LDAPMod *attrs[]) 1081 { 1082 int i; 1083 for (i = 0; attrs[i]; i++) { 1084 free(attrs[i]); 1085 } 1086 } 1087 1088 /* 1089 * smb_ads_get_spnset 1090 * 1091 * Derives the core set of SPNs based on the FQHN. 1092 * The spn_set is a null-terminated array of char pointers. 1093 * 1094 * Returns 0 upon success. Otherwise, returns -1. 1095 */ 1096 static int 1097 smb_ads_get_spnset(char *fqhost, char **spn_set) 1098 { 1099 int i; 1100 1101 bzero(spn_set, (SMBKRB5_SPN_IDX_MAX + 1) * sizeof (char *)); 1102 for (i = 0; i < SMBKRB5_SPN_IDX_MAX; i++) { 1103 if ((spn_set[i] = smb_krb5_get_spn(i, fqhost)) == NULL) { 1104 smb_ads_free_spnset(spn_set); 1105 return (-1); 1106 } 1107 } 1108 1109 return (0); 1110 } 1111 1112 /* 1113 * smb_ads_free_spnset 1114 * 1115 * Free the memory allocated for the set of SPNs. 1116 */ 1117 static void 1118 smb_ads_free_spnset(char **spn_set) 1119 { 1120 int i; 1121 for (i = 0; spn_set[i]; i++) 1122 free(spn_set[i]); 1123 } 1124 1125 /* 1126 * smb_ads_acquire_cred 1127 * Called by smb_ads_bind() to get a handle to administrative user's credential 1128 * stored locally on the system. The credential is the TGT. If the attempt at 1129 * getting handle fails then a second attempt will be made after getting a 1130 * new TGT. 1131 * Please look at smb_ads_bind() for more information. 1132 * 1133 * Paramters: 1134 * ah : handle to ADS server 1135 * kinit_retry: if 0 then a second attempt will be made to get handle to the 1136 * credential if the first attempt fails 1137 * Returns: 1138 * cred_handle: handle to the administrative user's credential (TGT) 1139 * oid : contains Kerberos 5 object identifier 1140 * kinit_retry: A 1 indicates that a second attempt has been made to get 1141 * handle to the credential and no further attempts can be made 1142 * -1 : error 1143 * 0 : success 1144 */ 1145 static int 1146 smb_ads_acquire_cred(smb_ads_handle_t *ah, gss_cred_id_t *cred_handle, 1147 gss_OID *oid, int *kinit_retry) 1148 { 1149 return (krb5_acquire_cred_kinit(ah->user, ah->pwd, cred_handle, oid, 1150 kinit_retry, "ads")); 1151 } 1152 1153 /* 1154 * smb_ads_establish_sec_context 1155 * Called by smb_ads_bind() to establish a security context to an LDAP service 1156 * on an ADS server. If the attempt at establishing the security context fails 1157 * then a second attempt will be made by smb_ads_bind() if a new TGT has not 1158 * been already obtained in ads_acquire_cred. The second attempt, if allowed, 1159 * will obtained a new TGT here and a new handle to the credential will also be 1160 * obtained in ads_acquire_cred. LDAP SASL bind is used to send and receive 1161 * the GSS tokens to and from the ADS server. 1162 * Please look at ads_bind for more information. 1163 * Paramters: 1164 * ah : handle to ADS server 1165 * cred_handle : handle to administrative user's credential (TGT) 1166 * oid : Kerberos 5 object identifier 1167 * kinit_retry : if 0 then a second attempt can be made to establish a 1168 * security context with ADS server if first attempt fails 1169 * Returns: 1170 * gss_context : security context to ADS server 1171 * sercred : encrypted ADS server's supported security layers 1172 * do_acquire_cred: if 1 then a second attempt will be made to establish a 1173 * security context with ADS server after getting a new 1174 * handle to the user's credential 1175 * kinit_retry : if 1 then a second attempt will be made to establish a 1176 * a security context and no further attempts can be made 1177 * -1 : error 1178 * 0 : success 1179 */ 1180 static int 1181 smb_ads_establish_sec_context(smb_ads_handle_t *ah, gss_ctx_id_t *gss_context, 1182 gss_cred_id_t cred_handle, gss_OID oid, struct berval **sercred, 1183 int *kinit_retry, int *do_acquire_cred) 1184 { 1185 OM_uint32 maj, min, time_rec; 1186 char service_name[SMB_ADS_MAXBUFLEN]; 1187 gss_buffer_desc send_tok, service_buf; 1188 gss_name_t target_name; 1189 gss_buffer_desc input; 1190 gss_buffer_desc *inputptr; 1191 struct berval cred; 1192 OM_uint32 ret_flags; 1193 int stat; 1194 int gss_flags; 1195 1196 (void) snprintf(service_name, SMB_ADS_MAXBUFLEN, "ldap@%s", 1197 ah->hostname); 1198 service_buf.value = service_name; 1199 service_buf.length = strlen(service_name)+1; 1200 if ((maj = gss_import_name(&min, &service_buf, 1201 (gss_OID) gss_nt_service_name, 1202 &target_name)) != GSS_S_COMPLETE) { 1203 smb_ads_display_stat(maj, min); 1204 if (oid != GSS_C_NO_OID) 1205 (void) gss_release_oid(&min, &oid); 1206 return (-1); 1207 } 1208 1209 *gss_context = GSS_C_NO_CONTEXT; 1210 *sercred = NULL; 1211 inputptr = GSS_C_NO_BUFFER; 1212 gss_flags = GSS_C_MUTUAL_FLAG; 1213 do { 1214 if (krb5_establish_sec_ctx_kinit(ah->user, ah->pwd, 1215 cred_handle, gss_context, target_name, oid, 1216 gss_flags, inputptr, &send_tok, 1217 &ret_flags, &time_rec, kinit_retry, 1218 do_acquire_cred, &maj, "ads") == -1) { 1219 if (oid != GSS_C_NO_OID) 1220 (void) gss_release_oid(&min, &oid); 1221 (void) gss_release_name(&min, &target_name); 1222 return (-1); 1223 } 1224 1225 cred.bv_val = send_tok.value; 1226 cred.bv_len = send_tok.length; 1227 if (*sercred) { 1228 ber_bvfree(*sercred); 1229 *sercred = NULL; 1230 } 1231 stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", 1232 &cred, NULL, NULL, sercred); 1233 if (stat != LDAP_SUCCESS && 1234 stat != LDAP_SASL_BIND_IN_PROGRESS) { 1235 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1236 syslog(LOG_ERR, ldap_err2string(stat)); 1237 if (oid != GSS_C_NO_OID) 1238 (void) gss_release_oid(&min, &oid); 1239 (void) gss_release_name(&min, &target_name); 1240 (void) gss_release_buffer(&min, &send_tok); 1241 return (-1); 1242 } 1243 input.value = (*sercred)->bv_val; 1244 input.length = (*sercred)->bv_len; 1245 inputptr = &input; 1246 if (send_tok.length > 0) 1247 (void) gss_release_buffer(&min, &send_tok); 1248 } while (maj != GSS_S_COMPLETE); 1249 1250 if (oid != GSS_C_NO_OID) 1251 (void) gss_release_oid(&min, &oid); 1252 (void) gss_release_name(&min, &target_name); 1253 1254 return (0); 1255 } 1256 1257 /* 1258 * smb_ads_negotiate_sec_layer 1259 * Call by smb_ads_bind() to negotiate additional security layer for further 1260 * communication after security context establishment. No additional security 1261 * is needed so a "no security layer" is negotiated. The security layer is 1262 * described in the SASL RFC 2478 and this step is needed for secure LDAP 1263 * binding. LDAP SASL bind is used to send and receive the GSS tokens to and 1264 * from the ADS server. 1265 * Please look at smb_ads_bind for more information. 1266 * 1267 * Paramters: 1268 * ah : handle to ADS server 1269 * gss_context: security context to ADS server 1270 * sercred : encrypted ADS server's supported security layers 1271 * Returns: 1272 * -1 : error 1273 * 0 : success 1274 */ 1275 static int 1276 smb_ads_negotiate_sec_layer(smb_ads_handle_t *ah, gss_ctx_id_t gss_context, 1277 struct berval *sercred) 1278 { 1279 OM_uint32 maj, min; 1280 gss_buffer_desc unwrap_inbuf, unwrap_outbuf; 1281 gss_buffer_desc wrap_inbuf, wrap_outbuf; 1282 int conf_state, sec_layer; 1283 char auth_id[5]; 1284 struct berval cred; 1285 int stat; 1286 gss_qop_t qt; 1287 1288 /* check for server supported security layer */ 1289 unwrap_inbuf.value = sercred->bv_val; 1290 unwrap_inbuf.length = sercred->bv_len; 1291 if ((maj = gss_unwrap(&min, gss_context, 1292 &unwrap_inbuf, &unwrap_outbuf, 1293 &conf_state, &qt)) != GSS_S_COMPLETE) { 1294 smb_ads_display_stat(maj, min); 1295 if (sercred) 1296 ber_bvfree(sercred); 1297 return (-1); 1298 } 1299 sec_layer = *((char *)unwrap_outbuf.value); 1300 (void) gss_release_buffer(&min, &unwrap_outbuf); 1301 if (!(sec_layer & 1)) { 1302 syslog(LOG_ERR, "smb_ads: ADS server does not support " 1303 "no security layer!\n"); 1304 if (sercred) ber_bvfree(sercred); 1305 return (-1); 1306 } 1307 if (sercred) ber_bvfree(sercred); 1308 1309 /* no security layer needed after successful binding */ 1310 auth_id[0] = 0x01; 1311 1312 /* byte 2-4: max client recv size in network byte order */ 1313 auth_id[1] = 0x00; 1314 auth_id[2] = 0x40; 1315 auth_id[3] = 0x00; 1316 wrap_inbuf.value = auth_id; 1317 wrap_inbuf.length = 4; 1318 conf_state = 0; 1319 if ((maj = gss_wrap(&min, gss_context, conf_state, 0, &wrap_inbuf, 1320 &conf_state, &wrap_outbuf)) != GSS_S_COMPLETE) { 1321 smb_ads_display_stat(maj, min); 1322 return (-1); 1323 } 1324 1325 cred.bv_val = wrap_outbuf.value; 1326 cred.bv_len = wrap_outbuf.length; 1327 sercred = NULL; 1328 stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", &cred, NULL, NULL, 1329 &sercred); 1330 if (stat != LDAP_SUCCESS && stat != LDAP_SASL_BIND_IN_PROGRESS) { 1331 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1332 syslog(LOG_ERR, ldap_err2string(stat)); 1333 (void) gss_release_buffer(&min, &wrap_outbuf); 1334 return (-1); 1335 } 1336 1337 (void) gss_release_buffer(&min, &wrap_outbuf); 1338 if (sercred) 1339 ber_bvfree(sercred); 1340 1341 return (0); 1342 } 1343 1344 /* 1345 * smb_ads_bind 1346 * Use secure binding to bind to ADS server. 1347 * Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with 1348 * Kerberos 5 as the security mechanisn to authenticate, obtain a security 1349 * context, and securely bind an administrative user so that other LDAP 1350 * commands can be used, i.e. add and delete. 1351 * 1352 * To obtain the security context, a Kerberos ticket-granting ticket (TGT) 1353 * for the user is needed to obtain a ticket for the LDAP service. To get 1354 * a TGT for the user, the username and password is needed. Once a TGT is 1355 * obtained then it will be stored locally and used until it is expired. 1356 * This routine will automatically obtained a TGT for the first time or when 1357 * it expired. LDAP SASL bind is then finally used to send GSS tokens to 1358 * obtain a security context for the LDAP service on the ADS server. If 1359 * there is any problem getting the security context then a new TGT will be 1360 * obtain to try getting the security context once more. 1361 * 1362 * After the security context is obtain and established, the LDAP SASL bind 1363 * is used to negotiate an additional security layer. No further security is 1364 * needed so a "no security layer" is negotiated. After this the security 1365 * context can be deleted and further LDAP commands can be sent to the ADS 1366 * server until a LDAP unbind command is issued to the ADS server. 1367 * Paramaters: 1368 * ah: handle to ADS server 1369 * Returns: 1370 * -1: error 1371 * 0: success 1372 */ 1373 static int 1374 smb_ads_bind(smb_ads_handle_t *ah) 1375 { 1376 OM_uint32 min; 1377 gss_cred_id_t cred_handle; 1378 gss_ctx_id_t gss_context; 1379 gss_OID oid; 1380 struct berval *sercred; 1381 int kinit_retry, do_acquire_cred; 1382 int rc = 0; 1383 1384 kinit_retry = 0; 1385 do_acquire_cred = 0; 1386 1387 acquire_cred: 1388 1389 if (smb_ads_acquire_cred(ah, &cred_handle, &oid, &kinit_retry)) 1390 return (-1); 1391 1392 if (smb_ads_establish_sec_context(ah, &gss_context, cred_handle, 1393 oid, &sercred, &kinit_retry, &do_acquire_cred)) { 1394 (void) gss_release_cred(&min, &cred_handle); 1395 if (do_acquire_cred) { 1396 do_acquire_cred = 0; 1397 goto acquire_cred; 1398 } 1399 return (-1); 1400 } 1401 rc = smb_ads_negotiate_sec_layer(ah, gss_context, sercred); 1402 1403 if (cred_handle != GSS_C_NO_CREDENTIAL) 1404 (void) gss_release_cred(&min, &cred_handle); 1405 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1406 1407 return ((rc) ? -1 : 0); 1408 } 1409 1410 /* 1411 * smb_ads_add_share 1412 * Call by smb_ads_publish_share to create share object in ADS. 1413 * This routine specifies the attributes of an ADS LDAP share object. The first 1414 * attribute and values define the type of ADS object, the share object. The 1415 * second attribute and value define the UNC of the share data for the share 1416 * object. The LDAP synchronous add command is used to add the object into ADS. 1417 * The container location to add the object needs to specified. 1418 * Parameters: 1419 * ah : handle to ADS server 1420 * adsShareName: name of share object to be created in ADS 1421 * shareUNC : share name on NetForce 1422 * adsContainer: location in ADS to create share object 1423 * 1424 * Returns: 1425 * -1 : error 1426 * 0 : success 1427 */ 1428 int 1429 smb_ads_add_share(smb_ads_handle_t *ah, const char *adsShareName, 1430 const char *unc_name, const char *adsContainer) 1431 { 1432 LDAPMod *attrs[SMB_ADS_SHARE_NUM_ATTR]; 1433 char *tmp1[5], *tmp2[5]; 1434 int j = 0; 1435 char *share_dn; 1436 char buf[SMB_ADS_MAXMSGLEN]; 1437 int len, ret; 1438 1439 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1440 strlen(ah->domain_dn) + 1; 1441 1442 share_dn = (char *)malloc(len); 1443 if (share_dn == NULL) 1444 return (-1); 1445 1446 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1447 adsContainer, ah->domain_dn); 1448 1449 if (smb_ads_alloc_attr(attrs, SMB_ADS_SHARE_NUM_ATTR) != 0) { 1450 free(share_dn); 1451 return (-1); 1452 } 1453 1454 attrs[j]->mod_op = LDAP_MOD_ADD; 1455 attrs[j]->mod_type = "objectClass"; 1456 tmp1[0] = "top"; 1457 tmp1[1] = "leaf"; 1458 tmp1[2] = "connectionPoint"; 1459 tmp1[3] = "volume"; 1460 tmp1[4] = 0; 1461 attrs[j]->mod_values = tmp1; 1462 1463 attrs[++j]->mod_op = LDAP_MOD_ADD; 1464 attrs[j]->mod_type = "uNCName"; 1465 tmp2[0] = (char *)unc_name; 1466 tmp2[1] = 0; 1467 attrs[j]->mod_values = tmp2; 1468 1469 if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) { 1470 (void) snprintf(buf, SMB_ADS_MAXMSGLEN, 1471 "smb_ads_add_share: %s:", share_dn); 1472 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1473 syslog(LOG_ERR, ldap_err2string(ret)); 1474 smb_ads_free_attr(attrs); 1475 free(share_dn); 1476 return (ret); 1477 } 1478 free(share_dn); 1479 smb_ads_free_attr(attrs); 1480 1481 (void) snprintf(buf, SMB_ADS_MAXMSGLEN, 1482 "Share %s has been added to ADS container: %s.\n", adsShareName, 1483 adsContainer); 1484 smb_tracef("smb_ads: %s", buf); 1485 1486 return (0); 1487 } 1488 1489 /* 1490 * smb_ads_del_share 1491 * Call by smb_ads_remove_share to remove share object from ADS. The container 1492 * location to remove the object needs to specified. The LDAP synchronous 1493 * delete command is used. 1494 * Parameters: 1495 * ah : handle to ADS server 1496 * adsShareName: name of share object in ADS to be removed 1497 * adsContainer: location of share object in ADS 1498 * Returns: 1499 * -1 : error 1500 * 0 : success 1501 */ 1502 static int 1503 smb_ads_del_share(smb_ads_handle_t *ah, const char *adsShareName, 1504 const char *adsContainer) 1505 { 1506 char *share_dn, buf[SMB_ADS_MAXMSGLEN]; 1507 int len, ret; 1508 1509 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1510 strlen(ah->domain_dn) + 1; 1511 1512 share_dn = (char *)malloc(len); 1513 if (share_dn == NULL) 1514 return (-1); 1515 1516 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1517 adsContainer, ah->domain_dn); 1518 if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { 1519 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1520 syslog(LOG_ERR, ldap_err2string(ret)); 1521 free(share_dn); 1522 return (-1); 1523 } 1524 free(share_dn); 1525 1526 (void) snprintf(buf, SMB_ADS_MAXMSGLEN, 1527 "Share %s has been removed from ADS container: %s.\n", 1528 adsShareName, adsContainer); 1529 smb_tracef("smb_ads: %s", buf); 1530 1531 return (0); 1532 } 1533 1534 1535 /* 1536 * smb_ads_escape_search_filter_chars 1537 * 1538 * This routine will escape the special characters found in a string 1539 * that will later be passed to the ldap search filter. 1540 * 1541 * RFC 1960 - A String Representation of LDAP Search Filters 1542 * 3. String Search Filter Definition 1543 * If a value must contain one of the characters '*' OR '(' OR ')', 1544 * these characters 1545 * should be escaped by preceding them with the backslash '\' character. 1546 * 1547 * RFC 2252 - LDAP Attribute Syntax Definitions 1548 * a backslash quoting mechanism is used to escape 1549 * the following separator symbol character (such as "'", "$" or "#") if 1550 * it should occur in that string. 1551 */ 1552 static int 1553 smb_ads_escape_search_filter_chars(const char *src, char *dst) 1554 { 1555 int avail = SMB_ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ 1556 1557 if (src == NULL || dst == NULL) 1558 return (-1); 1559 1560 while (*src) { 1561 if (!avail) { 1562 *dst = 0; 1563 return (-1); 1564 } 1565 1566 switch (*src) { 1567 case '\\': 1568 case '\'': 1569 case '$': 1570 case '#': 1571 case '*': 1572 case '(': 1573 case ')': 1574 *dst++ = '\\'; 1575 avail--; 1576 /* fall through */ 1577 1578 default: 1579 *dst++ = *src++; 1580 avail--; 1581 } 1582 } 1583 1584 *dst = 0; 1585 1586 return (0); 1587 } 1588 1589 /* 1590 * smb_ads_lookup_share 1591 * The search filter is set to search for a specific share name in the 1592 * specified ADS container. The LDSAP synchronous search command is used. 1593 * Parameters: 1594 * ah : handle to ADS server 1595 * adsShareName: name of share object in ADS to be searched 1596 * adsContainer: location of share object in ADS 1597 * Returns: 1598 * -1 : error 1599 * 0 : not found 1600 * 1 : found 1601 */ 1602 int 1603 smb_ads_lookup_share(smb_ads_handle_t *ah, const char *adsShareName, 1604 const char *adsContainer, char *unc_name) 1605 { 1606 char *attrs[4], filter[SMB_ADS_MAXBUFLEN]; 1607 char *share_dn; 1608 int len, ret; 1609 LDAPMessage *res; 1610 char tmpbuf[SMB_ADS_MAXBUFLEN]; 1611 1612 if (adsShareName == NULL || adsContainer == NULL) 1613 return (-1); 1614 1615 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1616 strlen(ah->domain_dn) + 1; 1617 1618 share_dn = (char *)malloc(len); 1619 if (share_dn == NULL) 1620 return (-1); 1621 1622 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1623 adsContainer, ah->domain_dn); 1624 1625 res = NULL; 1626 attrs[0] = "cn"; 1627 attrs[1] = "objectClass"; 1628 attrs[2] = "uNCName"; 1629 attrs[3] = NULL; 1630 1631 if (smb_ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { 1632 free(share_dn); 1633 return (-1); 1634 } 1635 1636 (void) snprintf(filter, sizeof (filter), 1637 "(&(objectClass=volume)(uNCName=%s))", tmpbuf); 1638 1639 if ((ret = ldap_search_s(ah->ld, share_dn, 1640 LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { 1641 if (ret != LDAP_NO_SUCH_OBJECT) { 1642 syslog(LOG_ERR, "ldap[%s]: %s", share_dn, 1643 ldap_err2string(ret)); 1644 } 1645 (void) ldap_msgfree(res); 1646 free(share_dn); 1647 return (0); 1648 } 1649 1650 (void) free(share_dn); 1651 1652 /* no match is found */ 1653 if (ldap_count_entries(ah->ld, res) == 0) { 1654 (void) ldap_msgfree(res); 1655 return (0); 1656 } 1657 1658 /* free the search results */ 1659 (void) ldap_msgfree(res); 1660 1661 return (1); 1662 } 1663 1664 /* 1665 * smb_ads_convert_directory 1666 * Convert relative share directory to UNC to be appended to hostname. 1667 * i.e. cvol/a/b -> cvol\a\b 1668 */ 1669 char * 1670 smb_ads_convert_directory(char *rel_dir) 1671 { 1672 char *t, *s2; 1673 int len; 1674 1675 if (rel_dir == NULL) 1676 return (NULL); 1677 1678 len = strlen(rel_dir) + 1; 1679 s2 = (char *)malloc(len); 1680 if (s2 == NULL) 1681 return (NULL); 1682 1683 t = s2; 1684 while (*rel_dir) { 1685 if (*rel_dir == '/') { 1686 *t++ = '\\'; 1687 rel_dir++; 1688 } else { 1689 *t++ = *rel_dir++; 1690 } 1691 } 1692 *t = '\0'; 1693 return (s2); 1694 } 1695 1696 /* 1697 * smb_ads_publish_share 1698 * Publish share into ADS. If a share name already exist in ADS in the same 1699 * container then the existing share object is removed before adding the new 1700 * share object. 1701 * Parameters: 1702 * ah : handle return from smb_ads_open 1703 * adsShareName: name of share to be added to ADS directory 1704 * shareUNC : name of share on client, can be NULL to use the same name 1705 * as adsShareName 1706 * adsContainer: location for share to be added in ADS directory, ie 1707 * ou=share_folder 1708 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR 1709 * to use host ip addr for UNC. 1710 * Returns: 1711 * -1 : error 1712 * 0 : success 1713 */ 1714 int 1715 smb_ads_publish_share(smb_ads_handle_t *ah, const char *adsShareName, 1716 const char *shareUNC, const char *adsContainer, const char *hostname) 1717 { 1718 int ret; 1719 char unc_name[SMB_ADS_MAXBUFLEN]; 1720 1721 if (adsShareName == NULL || adsContainer == NULL) 1722 return (-1); 1723 1724 if (shareUNC == 0 || *shareUNC == 0) 1725 shareUNC = adsShareName; 1726 1727 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1728 hostname, shareUNC) < 0) { 1729 smb_tracef("smb_ads: Cannot publish share '%s' " 1730 "[missing UNC name]", shareUNC); 1731 return (-1); 1732 } 1733 1734 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1735 1736 switch (ret) { 1737 case 1: 1738 (void) smb_ads_del_share(ah, adsShareName, adsContainer); 1739 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1740 adsContainer); 1741 break; 1742 1743 case 0: 1744 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1745 adsContainer); 1746 if (ret == LDAP_ALREADY_EXISTS) { 1747 smb_tracef("smb_ads: Cannot publish share '%s' " 1748 "[name is already in use]", adsShareName); 1749 ret = -1; 1750 } 1751 break; 1752 1753 case -1: 1754 default: 1755 /* return with error code */ 1756 ret = -1; 1757 } 1758 1759 return (ret); 1760 } 1761 1762 /* 1763 * smb_ads_remove_share 1764 * Remove share from ADS. A search is done first before explicitly removing 1765 * the share. 1766 * Parameters: 1767 * ah : handle return from smb_ads_open 1768 * adsShareName: name of share to be removed from ADS directory 1769 * adsContainer: location for share to be removed from ADS directory, ie 1770 * ou=share_folder 1771 * Returns: 1772 * -1 : error 1773 * 0 : success 1774 */ 1775 int 1776 smb_ads_remove_share(smb_ads_handle_t *ah, const char *adsShareName, 1777 const char *shareUNC, const char *adsContainer, const char *hostname) 1778 { 1779 int ret; 1780 char unc_name[SMB_ADS_MAXBUFLEN]; 1781 1782 if (adsShareName == NULL || adsContainer == NULL) 1783 return (-1); 1784 if (shareUNC == 0 || *shareUNC == 0) 1785 shareUNC = adsShareName; 1786 1787 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1788 hostname, shareUNC) < 0) { 1789 smb_tracef("smb_ads: Unable to remove share '%s' from " 1790 "ADS [missing UNC name]", shareUNC); 1791 return (-1); 1792 } 1793 1794 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1795 if (ret == 0) 1796 return (0); 1797 if (ret == -1) 1798 return (-1); 1799 1800 return (smb_ads_del_share(ah, adsShareName, adsContainer)); 1801 } 1802 1803 /* 1804 * smb_ads_get_computer_dn 1805 * 1806 * Build the distinguish name for this system. 1807 */ 1808 static void 1809 smb_ads_get_computer_dn(smb_ads_handle_t *ah, char *buf, size_t buflen) 1810 { 1811 char hostname[MAXHOSTNAMELEN]; 1812 1813 (void) smb_gethostname(hostname, MAXHOSTNAMELEN, 0); 1814 (void) snprintf(buf, buflen, "cn=%s,cn=%s,%s", 1815 hostname, SMB_ADS_COMPUTERS_CN, ah->domain_dn); 1816 } 1817 1818 /* 1819 * smb_ads_add_computer 1820 * 1821 * Returns 0 upon success. Otherwise, returns -1. 1822 */ 1823 static int 1824 smb_ads_add_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1825 { 1826 return (smb_ads_computer_op(ah, LDAP_MOD_ADD, dclevel, dn)); 1827 } 1828 1829 /* 1830 * smb_ads_modify_computer 1831 * 1832 * Returns 0 upon success. Otherwise, returns -1. 1833 */ 1834 static int 1835 smb_ads_modify_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1836 { 1837 return (smb_ads_computer_op(ah, LDAP_MOD_REPLACE, dclevel, dn)); 1838 } 1839 1840 /* 1841 * smb_ads_get_dc_level 1842 * 1843 * Returns the functional level of the DC upon success. 1844 * Otherwise, -1 is returned. 1845 */ 1846 static int 1847 smb_ads_get_dc_level(smb_ads_handle_t *ah) 1848 { 1849 LDAPMessage *res, *entry; 1850 char *attr[2]; 1851 char **vals; 1852 int rc = -1; 1853 1854 res = NULL; 1855 attr[0] = SMB_ADS_ATTR_DCLEVEL; 1856 attr[1] = NULL; 1857 if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, 1858 0, &res) != LDAP_SUCCESS) { 1859 (void) ldap_msgfree(res); 1860 return (-1); 1861 } 1862 1863 /* no match for the specified attribute is found */ 1864 if (ldap_count_entries(ah->ld, res) == 0) { 1865 (void) ldap_msgfree(res); 1866 return (-1); 1867 } 1868 1869 entry = ldap_first_entry(ah->ld, res); 1870 if (entry) { 1871 if ((vals = ldap_get_values(ah->ld, entry, 1872 SMB_ADS_ATTR_DCLEVEL)) == NULL) { 1873 /* 1874 * Observed the values aren't populated 1875 * by the Windows 2000 server. 1876 */ 1877 (void) ldap_msgfree(res); 1878 return (SMB_ADS_DCLEVEL_W2K); 1879 } 1880 1881 if (vals[0] != NULL) 1882 rc = atoi(vals[0]); 1883 1884 ldap_value_free(vals); 1885 } 1886 1887 (void) ldap_msgfree(res); 1888 return (rc); 1889 } 1890 1891 static int 1892 smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn) 1893 { 1894 LDAPMod *attrs[SMB_ADS_COMPUTER_NUM_ATTR]; 1895 char *oc_vals[6], *sam_val[2], *usr_val[2]; 1896 char *spn_set[SMBKRB5_SPN_IDX_MAX + 1], *ctl_val[2], *fqh_val[2]; 1897 char *encrypt_val[2]; 1898 int j = -1; 1899 int ret, usrctl_flags = 0; 1900 char sam_acct[MAXHOSTNAMELEN + 1]; 1901 char fqhost[MAXHOSTNAMELEN]; 1902 char *user_principal; 1903 char usrctl_buf[16]; 1904 char encrypt_buf[16]; 1905 int max; 1906 1907 if (smb_gethostname(fqhost, MAXHOSTNAMELEN, 0) != 0) 1908 return (-1); 1909 1910 (void) strlcpy(sam_acct, fqhost, MAXHOSTNAMELEN + 1); 1911 (void) strlcat(sam_acct, "$", MAXHOSTNAMELEN + 1); 1912 (void) snprintf(fqhost, MAXHOSTNAMELEN, "%s.%s", fqhost, 1913 ah->domain); 1914 1915 if (smb_ads_get_spnset(fqhost, spn_set) != 0) 1916 return (-1); 1917 1918 /* 1919 * Windows 2008 DC expects the UPN attribute to be host/fqhn while 1920 * both Windows 2000 & 2003 expect it to be host/fqhn@realm. 1921 */ 1922 if (dclevel == SMB_ADS_DCLEVEL_W2K8) 1923 user_principal = smb_krb5_get_spn(SMBKRB5_SPN_IDX_HOST, fqhost); 1924 else 1925 user_principal = smb_krb5_get_upn(spn_set[SMBKRB5_SPN_IDX_HOST], 1926 ah->domain); 1927 1928 if (user_principal == NULL) { 1929 smb_ads_free_spnset(spn_set); 1930 return (-1); 1931 } 1932 1933 max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)) 1934 - (dclevel == SMB_ADS_DCLEVEL_W2K8 ? 0 : 1); 1935 1936 if (smb_ads_alloc_attr(attrs, max) != 0) { 1937 free(user_principal); 1938 smb_ads_free_spnset(spn_set); 1939 return (-1); 1940 } 1941 1942 /* objectClass attribute is not modifiable. */ 1943 if (op == LDAP_MOD_ADD) { 1944 attrs[++j]->mod_op = op; 1945 attrs[j]->mod_type = "objectClass"; 1946 oc_vals[0] = "top"; 1947 oc_vals[1] = "person"; 1948 oc_vals[2] = "organizationalPerson"; 1949 oc_vals[3] = "user"; 1950 oc_vals[4] = "computer"; 1951 oc_vals[5] = 0; 1952 attrs[j]->mod_values = oc_vals; 1953 } 1954 1955 attrs[++j]->mod_op = op; 1956 attrs[j]->mod_type = SMB_ADS_ATTR_SAMACCT; 1957 sam_val[0] = sam_acct; 1958 sam_val[1] = 0; 1959 attrs[j]->mod_values = sam_val; 1960 1961 attrs[++j]->mod_op = op; 1962 attrs[j]->mod_type = SMB_ADS_ATTR_UPN; 1963 usr_val[0] = user_principal; 1964 usr_val[1] = 0; 1965 attrs[j]->mod_values = usr_val; 1966 1967 attrs[++j]->mod_op = op; 1968 attrs[j]->mod_type = SMB_ADS_ATTR_SPN; 1969 attrs[j]->mod_values = spn_set; 1970 1971 attrs[++j]->mod_op = op; 1972 attrs[j]->mod_type = SMB_ADS_ATTR_CTL; 1973 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1974 SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1975 SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1976 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1977 ctl_val[0] = usrctl_buf; 1978 ctl_val[1] = 0; 1979 attrs[j]->mod_values = ctl_val; 1980 1981 attrs[++j]->mod_op = op; 1982 attrs[j]->mod_type = SMB_ADS_ATTR_DNSHOST; 1983 fqh_val[0] = fqhost; 1984 fqh_val[1] = 0; 1985 attrs[j]->mod_values = fqh_val; 1986 1987 /* enctypes support starting in Windows Server 2008 */ 1988 if (dclevel > SMB_ADS_DCLEVEL_W2K3) { 1989 attrs[++j]->mod_op = op; 1990 attrs[j]->mod_type = SMB_ADS_ATTR_ENCTYPES; 1991 (void) snprintf(encrypt_buf, sizeof (encrypt_buf), "%d", 1992 SMB_ADS_ENC_AES256 + SMB_ADS_ENC_AES128 + SMB_ADS_ENC_RC4 + 1993 SMB_ADS_ENC_DES_MD5 + SMB_ADS_ENC_DES_CRC); 1994 encrypt_val[0] = encrypt_buf; 1995 encrypt_val[1] = 0; 1996 attrs[j]->mod_values = encrypt_val; 1997 } 1998 1999 switch (op) { 2000 case LDAP_MOD_ADD: 2001 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2002 syslog(LOG_ERR, "smb_ads_add_computer: %s", 2003 ldap_err2string(ret)); 2004 ret = -1; 2005 } 2006 break; 2007 2008 case LDAP_MOD_REPLACE: 2009 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2010 syslog(LOG_ERR, "smb_ads_modify_computer: %s", 2011 ldap_err2string(ret)); 2012 ret = -1; 2013 } 2014 break; 2015 2016 default: 2017 ret = -1; 2018 2019 } 2020 2021 smb_ads_free_attr(attrs); 2022 free(user_principal); 2023 smb_ads_free_spnset(spn_set); 2024 2025 return (ret); 2026 } 2027 2028 /* 2029 * Delete an ADS computer account. 2030 */ 2031 static void 2032 smb_ads_del_computer(smb_ads_handle_t *ah, char *dn) 2033 { 2034 int rc; 2035 2036 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) 2037 smb_tracef("smb_ads_del_computer: %s", ldap_err2string(rc)); 2038 } 2039 2040 /* 2041 * smb_ads_lookup_computer_n_attr 2042 * 2043 * Lookup the value of the specified attribute on the computer 2044 * object. If the specified attribute can be found, its value is returned 2045 * via 'val' parameter. 2046 * 2047 * 'attr' parameter can be set to NULL if you only attempt to 2048 * see whether the computer object exists on AD or not. 2049 * 2050 * Return: 2051 * 1 if both the computer and the specified attribute is found. 2052 * 0 if either the computer or the specified attribute is not found. 2053 * -1 on error. 2054 */ 2055 static int 2056 smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, char *attr, char **val, 2057 int scope, char *dn) 2058 { 2059 char *attrs[2], filter[SMB_ADS_MAXBUFLEN]; 2060 LDAPMessage *res, *entry; 2061 char **vals; 2062 char tmpbuf[SMB_ADS_MAXBUFLEN]; 2063 char my_hostname[MAXHOSTNAMELEN]; 2064 2065 if (smb_gethostname(my_hostname, MAXHOSTNAMELEN, 0) != 0) 2066 return (-1); 2067 2068 res = NULL; 2069 attrs[0] = attr; 2070 attrs[1] = NULL; 2071 2072 if (smb_ads_escape_search_filter_chars(my_hostname, tmpbuf) != 0) 2073 return (-1); 2074 2075 (void) snprintf(filter, sizeof (filter), 2076 "(&(objectClass=computer)(cn=%s))", tmpbuf); 2077 2078 if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, 2079 &res) != LDAP_SUCCESS) { 2080 (void) ldap_msgfree(res); 2081 return (0); 2082 } 2083 2084 if (attr) { 2085 /* no match for the specified attribute is found */ 2086 if (ldap_count_entries(ah->ld, res) == 0) { 2087 if (val) 2088 *val = NULL; 2089 2090 (void) ldap_msgfree(res); 2091 return (0); 2092 } 2093 2094 entry = ldap_first_entry(ah->ld, res); 2095 if (entry) { 2096 vals = ldap_get_values(ah->ld, entry, attr); 2097 if (!vals && val) { 2098 *val = NULL; 2099 (void) ldap_msgfree(res); 2100 return (0); 2101 } 2102 2103 if (vals[0] != NULL && val) 2104 *val = strdup(vals[0]); 2105 2106 ldap_value_free(vals); 2107 } 2108 } 2109 2110 /* free the search results */ 2111 (void) ldap_msgfree(res); 2112 return (1); 2113 } 2114 2115 /* 2116 * smb_ads_find_computer 2117 * 2118 * Starts by searching for the system's AD computer object in the default 2119 * container (i.e. cn=Computers). If not found, searches the entire directory. 2120 * 2121 * If found, B_TRUE is returned and 'dn' will be set to the 2122 * distinguishedName of the system's AD computer object. Otherwise, returns 2123 * B_FALSE. 2124 */ 2125 static boolean_t 2126 smb_ads_find_computer(smb_ads_handle_t *ah, char *dn) 2127 { 2128 boolean_t found; 2129 char *val = NULL; 2130 2131 smb_ads_get_computer_dn(ah, dn, SMB_ADS_DN_MAX); 2132 found = (smb_ads_lookup_computer_n_attr(ah, NULL, NULL, LDAP_SCOPE_BASE, 2133 dn) == 1); 2134 2135 if (!found) { 2136 (void) strlcpy(dn, ah->domain_dn, SMB_ADS_DN_MAX); 2137 found = (smb_ads_lookup_computer_n_attr(ah, SMB_ADS_ATTR_DN, 2138 &val, LDAP_SCOPE_SUBTREE, dn) == 1); 2139 2140 if (found && val) 2141 (void) strlcpy(dn, val, SMB_ADS_DN_MAX); 2142 else 2143 found = B_FALSE; 2144 } 2145 2146 return (found); 2147 } 2148 2149 /* 2150 * smb_ads_update_computer_cntrl_attr 2151 * 2152 * Modify the user account control attribute of an existing computer 2153 * object on AD. 2154 * 2155 * Returns 0 on success. Otherwise, returns -1. 2156 */ 2157 static int 2158 smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *ah, int des_only, char *dn) 2159 { 2160 LDAPMod *attrs[2]; 2161 char *ctl_val[2]; 2162 int ret, usrctl_flags = 0; 2163 char usrctl_buf[16]; 2164 2165 if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) != 0) 2166 return (-1); 2167 2168 attrs[0]->mod_op = LDAP_MOD_REPLACE; 2169 attrs[0]->mod_type = SMB_ADS_ATTR_CTL; 2170 2171 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2172 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 2173 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2174 2175 if (des_only) 2176 usrctl_flags |= SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; 2177 2178 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 2179 ctl_val[0] = usrctl_buf; 2180 ctl_val[1] = 0; 2181 attrs[0]->mod_values = ctl_val; 2182 2183 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2184 syslog(LOG_ERR, "smb_ads_modify_computer: %s", 2185 ldap_err2string(ret)); 2186 ret = -1; 2187 } 2188 2189 smb_ads_free_attr(attrs); 2190 return (ret); 2191 } 2192 2193 /* 2194 * smb_ads_update_attrs 2195 * 2196 * Updates the servicePrincipalName and dNSHostName attributes of the 2197 * system's AD computer object. 2198 */ 2199 int 2200 smb_ads_update_attrs(void) 2201 { 2202 smb_ads_handle_t *ah; 2203 LDAPMod *attrs[3]; 2204 char *fqh_val[2]; 2205 int i = 0; 2206 int ret; 2207 char fqhost[MAXHOSTNAMELEN]; 2208 char dn[SMB_ADS_DN_MAX]; 2209 char *spn_set[SMBKRB5_SPN_IDX_MAX + 1]; 2210 2211 if ((ah = smb_ads_open()) == NULL) 2212 return (-1); 2213 2214 if (!smb_ads_find_computer(ah, dn)) { 2215 smb_ads_close(ah); 2216 return (-1); 2217 } 2218 2219 if (smb_getfqhostname(fqhost, MAXHOSTNAMELEN) != 0) { 2220 smb_ads_close(ah); 2221 return (-1); 2222 } 2223 2224 if (smb_ads_get_spnset(fqhost, spn_set) != 0) { 2225 smb_ads_close(ah); 2226 return (-1); 2227 } 2228 2229 if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) 2230 != 0) { 2231 smb_ads_free_spnset(spn_set); 2232 smb_ads_close(ah); 2233 return (-1); 2234 } 2235 2236 attrs[i]->mod_op = LDAP_MOD_REPLACE; 2237 attrs[i]->mod_type = SMB_ADS_ATTR_SPN; 2238 attrs[i]->mod_values = spn_set; 2239 2240 attrs[++i]->mod_op = LDAP_MOD_REPLACE; 2241 attrs[i]->mod_type = SMB_ADS_ATTR_DNSHOST; 2242 fqh_val[0] = fqhost; 2243 fqh_val[1] = 0; 2244 attrs[i]->mod_values = fqh_val; 2245 2246 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2247 syslog(LOG_ERR, "smb_ads_update_attrs: %s", 2248 ldap_err2string(ret)); 2249 ret = -1; 2250 } 2251 2252 smb_ads_free_attr(attrs); 2253 smb_ads_free_spnset(spn_set); 2254 smb_ads_close(ah); 2255 2256 return (ret); 2257 2258 } 2259 2260 /* 2261 * smb_ads_lookup_computer_attr_kvno 2262 * 2263 * Lookup the value of the Kerberos version number attribute of the computer 2264 * account. 2265 */ 2266 static krb5_kvno 2267 smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) 2268 { 2269 char *val = NULL; 2270 int kvno = 1; 2271 2272 if (smb_ads_lookup_computer_n_attr(ah, SMB_ADS_ATTR_KVNO, &val, 2273 LDAP_SCOPE_BASE, dn) == 1) { 2274 if (val) { 2275 kvno = atoi(val); 2276 free(val); 2277 } 2278 } 2279 2280 return (kvno); 2281 } 2282 2283 /* 2284 * smb_ads_gen_machine_passwd 2285 * 2286 * Returned a null-terminated machine password generated randomly 2287 * from [0-9a-zA-Z] character set. In order to pass the password 2288 * quality check (three character classes), an uppercase letter is 2289 * used as the first character of the machine password. 2290 */ 2291 static int 2292 smb_ads_gen_machine_passwd(char *machine_passwd, int bufsz) 2293 { 2294 char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" 2295 "LMNOPQRSTUVWXYZ"; 2296 int datalen = strlen(data); 2297 int i, data_idx; 2298 2299 if (!machine_passwd || bufsz == 0) 2300 return (-1); 2301 2302 /* 2303 * The decimal value of upper case 'A' is 65. Randomly pick 2304 * an upper-case letter from the ascii table. 2305 */ 2306 machine_passwd[0] = (random() % 26) + 65; 2307 for (i = 1; i < bufsz - 1; i++) { 2308 data_idx = random() % datalen; 2309 machine_passwd[i] = data[data_idx]; 2310 } 2311 2312 machine_passwd[bufsz - 1] = 0; 2313 return (0); 2314 } 2315 2316 /* 2317 * smb_ads_domain_change_cleanup 2318 * 2319 * If we're attempting to join the system to a new domain, the keys for 2320 * the host principal regarding the old domain should be removed from 2321 * Kerberos keytab. Also, the ads_host_info cache should be cleared. 2322 * 2323 * newdom is fully-qualified domain name. It can be set to empty string 2324 * if user attempts to switch to workgroup mode. 2325 */ 2326 int 2327 smb_ads_domain_change_cleanup(char *newdom) 2328 { 2329 char origdom[MAXHOSTNAMELEN]; 2330 krb5_context ctx = NULL; 2331 krb5_principal krb5princs[SMBKRB5_SPN_IDX_MAX]; 2332 int rc; 2333 2334 if (smb_getfqdomainname(origdom, MAXHOSTNAMELEN)) { 2335 if (smb_getdomainname(origdom, MAXHOSTNAMELEN) == 0) 2336 if (strncasecmp(origdom, newdom, strlen(origdom))) 2337 smb_ads_free_host_info(); 2338 2339 return (0); 2340 } 2341 2342 if (strcasecmp(origdom, newdom) == 0) 2343 return (0); 2344 2345 smb_ads_free_host_info(); 2346 2347 if (smb_krb5_ctx_init(&ctx) != 0) 2348 return (-1); 2349 2350 if (smb_krb5_get_principals(origdom, ctx, krb5princs) != 0) { 2351 smb_krb5_ctx_fini(ctx); 2352 return (-1); 2353 2354 } 2355 2356 rc = smb_krb5_remove_keytab_entries(ctx, krb5princs, 2357 SMBNS_KRB5_KEYTAB); 2358 2359 smb_krb5_free_principals(ctx, krb5princs, SMBKRB5_SPN_IDX_MAX); 2360 smb_krb5_ctx_fini(ctx); 2361 2362 return (rc); 2363 } 2364 2365 2366 2367 /* 2368 * smb_ads_join 2369 * 2370 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 2371 * provides the domain join using Kerberos Authentication, Keberos 2372 * Change & Set password, and LDAP protocols. Basically, AD join 2373 * operation would require the following tickets to be acquired for the 2374 * the user account that is provided for the domain join. 2375 * 2376 * 1) a Keberos TGT ticket, 2377 * 2) a ldap service ticket, and 2378 * 3) kadmin/changpw service ticket 2379 * 2380 * The ADS client first sends a ldap search request to find out whether 2381 * or not the workstation trust account already exists in the Active Directory. 2382 * The existing computer object for this workstation will be removed and 2383 * a new one will be added. The machine account password is randomly 2384 * generated and set for the newly created computer object using KPASSWD 2385 * protocol (See RFC 3244). Once the password is set, our ADS client 2386 * finalizes the machine account by modifying the user acount control 2387 * attribute of the computer object. Kerberos keys derived from the machine 2388 * account password will be stored locally in /etc/krb5/krb5.keytab file. 2389 * That would be needed while acquiring Kerberos TGT ticket for the host 2390 * principal after the domain join operation. 2391 */ 2392 smb_adjoin_status_t 2393 smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd, 2394 int len) 2395 { 2396 smb_ads_handle_t *ah = NULL; 2397 krb5_context ctx = NULL; 2398 krb5_principal krb5princs[SMBKRB5_SPN_IDX_MAX]; 2399 krb5_kvno kvno; 2400 boolean_t des_only, delete = B_TRUE; 2401 smb_adjoin_status_t rc = SMB_ADJOIN_SUCCESS; 2402 boolean_t new_acct; 2403 int dclevel, num; 2404 char dn[SMB_ADS_DN_MAX]; 2405 2406 /* 2407 * Call library functions that can be used to get 2408 * the list of encryption algorithms available on the system. 2409 * (similar to what 'encrypt -l' CLI does). For now, 2410 * unless someone has modified the configuration of the 2411 * cryptographic framework (very unlikely), the following is the 2412 * list of algorithms available on any system running Nevada 2413 * by default. 2414 */ 2415 krb5_enctype w2k8enctypes[] = {ENCTYPE_AES256_CTS_HMAC_SHA1_96, 2416 ENCTYPE_AES128_CTS_HMAC_SHA1_96, ENCTYPE_ARCFOUR_HMAC, 2417 ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2418 }; 2419 2420 krb5_enctype pre_w2k8enctypes[] = {ENCTYPE_ARCFOUR_HMAC, 2421 ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2422 }; 2423 2424 krb5_enctype *encptr; 2425 2426 if ((ah = smb_ads_open_main(domain, user, usr_passwd)) == NULL) { 2427 smb_ccache_remove(SMB_CCACHE_PATH); 2428 return (SMB_ADJOIN_ERR_GET_HANDLE); 2429 } 2430 2431 if (smb_ads_gen_machine_passwd(machine_passwd, len) != 0) { 2432 smb_ads_close(ah); 2433 smb_ccache_remove(SMB_CCACHE_PATH); 2434 return (SMB_ADJOIN_ERR_GEN_PASSWD); 2435 } 2436 2437 if ((dclevel = smb_ads_get_dc_level(ah)) == -1) { 2438 smb_ads_close(ah); 2439 smb_ccache_remove(SMB_CCACHE_PATH); 2440 return (SMB_ADJOIN_ERR_GET_DCLEVEL); 2441 } 2442 2443 if (smb_ads_find_computer(ah, dn)) { 2444 new_acct = B_FALSE; 2445 if (smb_ads_modify_computer(ah, dclevel, dn) != 0) { 2446 smb_ads_close(ah); 2447 smb_ccache_remove(SMB_CCACHE_PATH); 2448 return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT); 2449 } 2450 } else { 2451 new_acct = B_TRUE; 2452 smb_ads_get_computer_dn(ah, dn, SMB_ADS_DN_MAX); 2453 if (smb_ads_add_computer(ah, dclevel, dn) != 0) { 2454 smb_ads_close(ah); 2455 smb_ccache_remove(SMB_CCACHE_PATH); 2456 return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT); 2457 } 2458 } 2459 2460 des_only = B_FALSE; 2461 2462 if (smb_krb5_ctx_init(&ctx) != 0) { 2463 rc = SMB_ADJOIN_ERR_INIT_KRB_CTX; 2464 goto adjoin_cleanup; 2465 } 2466 2467 if (smb_krb5_get_principals(ah->domain, ctx, krb5princs) != 0) { 2468 rc = SMB_ADJOIN_ERR_GET_SPNS; 2469 goto adjoin_cleanup; 2470 } 2471 2472 if (smb_krb5_setpwd(ctx, krb5princs[SMBKRB5_SPN_IDX_HOST], 2473 machine_passwd) != 0) { 2474 rc = SMB_ADJOIN_ERR_KSETPWD; 2475 goto adjoin_cleanup; 2476 } 2477 2478 kvno = smb_ads_lookup_computer_attr_kvno(ah, dn); 2479 2480 if (smb_ads_update_computer_cntrl_attr(ah, des_only, dn) 2481 != 0) { 2482 rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR; 2483 goto adjoin_cleanup; 2484 } 2485 2486 if (dclevel == SMB_ADS_DCLEVEL_W2K8) { 2487 num = sizeof (w2k8enctypes) / sizeof (krb5_enctype); 2488 encptr = w2k8enctypes; 2489 } else { 2490 num = sizeof (pre_w2k8enctypes) / sizeof (krb5_enctype); 2491 encptr = pre_w2k8enctypes; 2492 } 2493 2494 if (smb_krb5_update_keytab_entries(ctx, krb5princs, SMBNS_KRB5_KEYTAB, 2495 kvno, machine_passwd, encptr, num) != 0) { 2496 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 2497 goto adjoin_cleanup; 2498 } 2499 2500 /* Set IDMAP config */ 2501 if (smb_config_set_idmap_domain(ah->domain) != 0) { 2502 rc = SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN; 2503 goto adjoin_cleanup; 2504 } 2505 2506 /* Refresh IDMAP service */ 2507 if (smb_config_refresh_idmap() != 0) { 2508 rc = SMB_ADJOIN_ERR_IDMAP_REFRESH; 2509 goto adjoin_cleanup; 2510 } 2511 2512 delete = B_FALSE; 2513 adjoin_cleanup: 2514 if (new_acct && delete) 2515 smb_ads_del_computer(ah, dn); 2516 2517 if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) { 2518 if (rc != SMB_ADJOIN_ERR_GET_SPNS) 2519 smb_krb5_free_principals(ctx, krb5princs, 2520 SMBKRB5_SPN_IDX_MAX); 2521 smb_krb5_ctx_fini(ctx); 2522 } 2523 2524 smb_ads_close(ah); 2525 smb_ccache_remove(SMB_CCACHE_PATH); 2526 return (rc); 2527 } 2528 2529 /* 2530 * smb_adjoin_report_err 2531 * 2532 * Display error message for the specific adjoin error code. 2533 */ 2534 char * 2535 smb_adjoin_report_err(smb_adjoin_status_t status) 2536 { 2537 if (status < 0 || status >= SMB_ADJOIN_NUM_STATUS) 2538 return ("ADJOIN: unknown status"); 2539 2540 return (adjoin_errmsg[status]); 2541 } 2542 2543 /* 2544 * smb_ads_get_pdc_ip 2545 * 2546 * Check to see if there is any configured PDC. 2547 * If there is then converts the string IP to 2548 * integer format and returns it. 2549 */ 2550 static uint32_t 2551 smb_ads_get_pdc_ip(void) 2552 { 2553 char p[INET_ADDRSTRLEN]; 2554 uint32_t ipaddr = 0; 2555 int rc; 2556 2557 rc = smb_config_getstr(SMB_CI_DOMAIN_SRV, p, sizeof (p)); 2558 if (rc == SMBD_SMF_OK) { 2559 rc = inet_pton(AF_INET, p, &ipaddr); 2560 if (rc == 0) 2561 ipaddr = 0; 2562 } 2563 2564 return (ipaddr); 2565 } 2566 2567 /* 2568 * smb_ads_dc_compare 2569 * 2570 * Comparision function for sorting host entries (SRV records of DC) via qsort. 2571 */ 2572 static int 2573 smb_ads_dc_compare(const void *p, const void *q) 2574 { 2575 smb_ads_host_info_t *h1 = (smb_ads_host_info_t *)p; 2576 smb_ads_host_info_t *h2 = (smb_ads_host_info_t *)q; 2577 2578 if (h1->priority < h2->priority) 2579 return (-1); 2580 if (h1->priority > h2->priority) 2581 return (1); 2582 2583 /* Priorities are equal */ 2584 if (h1->weight < h2->weight) 2585 return (1); 2586 if (h1->weight > h2->weight) 2587 return (-1); 2588 2589 return (0); 2590 } 2591 2592 /* 2593 * smb_ads_dc_recommend 2594 * 2595 * RFC 2052/2782 are taken as reference, while implementing this algorithm. 2596 * 2597 * Domain Controllers(DCs) with lowest priority in their SRV DNS records 2598 * are selected first. If they have equal priorities, then DC with highest 2599 * weight in its SRV DNS record is selected. If the priority and weight are 2600 * both equal, then the DC at the top of the list is selected. 2601 * 2602 * Returns NULL if no DC found or if the found DC is not responding to 2603 * smb_ads_ldap_ping. 2604 */ 2605 static smb_ads_host_info_t * 2606 smb_ads_dc_recommend(smb_ads_host_list_t *hlist) 2607 { 2608 smb_ads_host_info_t *hinfo = NULL; 2609 int i; 2610 2611 if (hlist->ah_cnt == 1) { 2612 hinfo = hlist->ah_list; 2613 return (hinfo); 2614 } 2615 2616 qsort(hlist->ah_list, hlist->ah_cnt, 2617 sizeof (smb_ads_host_info_t), smb_ads_dc_compare); 2618 2619 /* We have the sorted list. Return the first DC that is up. */ 2620 for (i = 0; i < hlist->ah_cnt; i++) { 2621 hinfo = &hlist->ah_list[i]; 2622 if (smb_ads_ldap_ping(hinfo) == 0) { 2623 smb_tracef("smb_ads_dc_recommend: " 2624 "Selected DC %s(pri=%d wt=%d)", 2625 hinfo->name, hinfo->priority, hinfo->weight); 2626 return (hinfo); 2627 } 2628 } 2629 return (NULL); 2630 } 2631 2632 /* 2633 * smb_ads_select_pdc 2634 * 2635 * This method selects a Preferred DC (PDC) from the passed list of DCs (hlist), 2636 * if the configured PDC property is set and the selected PDC responds to 2637 * ldap_ping. 2638 * 2639 * The passed hinfo structure contains a pointer to the selected DC. 2640 * Returns true if the PDC is found, false otherwise. 2641 */ 2642 static boolean_t 2643 smb_ads_select_pdc(smb_ads_host_info_t *hinfo, smb_ads_host_list_t *hlist) 2644 { 2645 uint32_t ip; 2646 smb_ads_host_info_t *thinfo = NULL; 2647 int r; 2648 2649 if (ip = smb_ads_get_pdc_ip()) { 2650 for (r = 0; r < hlist->ah_cnt; r++) { 2651 if (hlist->ah_list[r].ip_addr == ip) { 2652 thinfo = &hlist->ah_list[r]; 2653 if (smb_ads_ldap_ping(thinfo) == 0) { 2654 smb_ads_init_host_info(hinfo, 2655 thinfo->name, thinfo->ip_addr, 2656 thinfo->priority, thinfo->weight, 2657 thinfo->port); 2658 return (B_TRUE); 2659 } else 2660 smb_tracef("PDC not responding."); 2661 } 2662 } 2663 } 2664 return (B_FALSE); 2665 } 2666 2667 /* 2668 * smb_ads_select_dcfromsubnet 2669 * 2670 * This method builds a list of DC's in the same subnet and sorts the list 2671 * according to the priority and weight of the DC entry. 2672 * Returns the DC from the top of the sorted list, that responds to ldap ping. 2673 * 2674 * The passed hinfo structure contains a pointer to the selected DC. 2675 * mem_err argument is set, if there are memory allocation issues. 2676 * Returns true if DC is found in the same subnet, false otherwise. 2677 */ 2678 static boolean_t 2679 smb_ads_select_dcfromsubnet(smb_ads_host_info_t *hinfo, 2680 smb_ads_host_list_t *hlist, int *mem_err) 2681 { 2682 smb_ads_host_list_t *snet_hlist = NULL; 2683 smb_ads_host_info_t *snet_hinfo = NULL, *thinfo = NULL; 2684 smb_nic_t *lnic; 2685 int r, snet_count = 0; 2686 smb_niciter_t ni; 2687 2688 snet_hlist = smb_ads_hlist_alloc(hlist->ah_cnt); 2689 if (snet_hlist == NULL) { 2690 *mem_err = 1; 2691 return (B_FALSE); 2692 } 2693 snet_hinfo = snet_hlist->ah_list; 2694 for (r = 0; r < hlist->ah_cnt; r++) { 2695 thinfo = &hlist->ah_list[r]; 2696 if (smb_nic_getfirst(&ni) != 0) 2697 break; 2698 do { 2699 lnic = &ni.ni_nic; 2700 2701 if ((thinfo->ip_addr & lnic->nic_mask) == 2702 (lnic->nic_ip & lnic->nic_mask)) { 2703 snet_count++; 2704 smb_ads_init_host_info(snet_hinfo, thinfo->name, 2705 thinfo->ip_addr, thinfo->priority, 2706 thinfo->weight, thinfo->port); 2707 snet_hinfo++; 2708 } 2709 } while (smb_nic_getnext(&ni) == 0); 2710 } 2711 snet_hlist->ah_cnt = snet_count; 2712 2713 if (snet_hlist->ah_cnt > 0) { 2714 thinfo = smb_ads_dc_recommend(snet_hlist); 2715 if (thinfo) { 2716 smb_ads_init_host_info(hinfo, thinfo->name, 2717 thinfo->ip_addr, thinfo->priority, thinfo->weight, 2718 thinfo->port); 2719 smb_ads_hlist_free(snet_hlist); 2720 return (B_TRUE); 2721 } 2722 } 2723 smb_ads_hlist_free(snet_hlist); 2724 return (B_FALSE); 2725 } 2726 2727 /* 2728 * smb_ads_select_dcfromlist 2729 * 2730 * This method sorts the list of DCs by priority and weight and 2731 * returns the first DC that responds to ldap ping. 2732 * 2733 * The passed hinfo structure contains a pointer to the selected DC. 2734 * Returns true if DC is found, false otherwise. 2735 */ 2736 static boolean_t 2737 smb_ads_select_dcfromlist(smb_ads_host_info_t *hinfo, 2738 smb_ads_host_list_t *hlist) 2739 { 2740 smb_ads_host_info_t *thinfo = NULL; 2741 2742 thinfo = smb_ads_dc_recommend(hlist); 2743 if (thinfo) { 2744 smb_ads_init_host_info(hinfo, 2745 thinfo->name, thinfo->ip_addr, 2746 thinfo->priority, thinfo->weight, 2747 thinfo->port); 2748 return (B_TRUE); 2749 } 2750 return (B_FALSE); 2751 } 2752 2753 /* 2754 * smb_ads_select_dc 2755 * 2756 * Given the list of DCs returned by ADS lookup, this routine returns 2757 * a DC according to the following. 2758 * 2759 * - If there is a configured PDC and it's in the ADS list, 2760 * then return the DC, if it responds to ldap ping. 2761 * - Build a list of DC's in the same subnet. Sort the list 2762 * according to priority and weight of the DC entry. 2763 * Return the DC from the top of the list, that responds to ldap ping. 2764 * - Else, sort DC list by priority & weight and return first DC that responds 2765 * to ldap ping. 2766 * 2767 * Returns NULL on error. 2768 */ 2769 static smb_ads_host_info_t * 2770 smb_ads_select_dc(smb_ads_host_list_t *hlist) 2771 { 2772 smb_ads_host_info_t *hinfo = NULL; 2773 int mem_err = 0; 2774 2775 hinfo = (smb_ads_host_info_t *)malloc(sizeof (smb_ads_host_info_t)); 2776 if (hinfo == NULL) 2777 return (NULL); 2778 2779 /* 1. Check PDC. */ 2780 if (smb_ads_select_pdc(hinfo, hlist)) 2781 return (hinfo); 2782 2783 /* 2. Check subnet. */ 2784 if (smb_ads_select_dcfromsubnet(hinfo, hlist, &mem_err)) 2785 return (hinfo); 2786 2787 if (mem_err) { 2788 free(hinfo); 2789 return (NULL); 2790 } 2791 2792 /* 2793 * 3. Sort DC list by priority & weight and return first DC that 2794 * responds to LDAP ping. 2795 */ 2796 if (smb_ads_select_dcfromlist(hinfo, hlist)) 2797 return (hinfo); 2798 2799 free(hinfo); 2800 return (NULL); 2801 } 2802