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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 #include <sys/param.h> 27 #include <ldap.h> 28 #include <stdlib.h> 29 #include <sys/types.h> 30 #include <sys/socket.h> 31 #include <netinet/in.h> 32 #include <arpa/inet.h> 33 #include <sys/time.h> 34 #include <netdb.h> 35 #include <pthread.h> 36 #include <unistd.h> 37 #include <arpa/nameser.h> 38 #include <resolv.h> 39 #include <sys/synch.h> 40 #include <string.h> 41 #include <strings.h> 42 #include <fcntl.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <assert.h> 46 #include <sasl/sasl.h> 47 #include <note.h> 48 #include <errno.h> 49 #include <cryptoutil.h> 50 #include <ads/dsgetdc.h> 51 52 #include <smbsrv/libsmbns.h> 53 #include <smbns_dyndns.h> 54 #include <smbns_krb.h> 55 56 #define SMB_ADS_AF_UNKNOWN(x) (((x)->ipaddr.a_family != AF_INET) && \ 57 ((x)->ipaddr.a_family != AF_INET6)) 58 59 #define SMB_ADS_MAXBUFLEN 100 60 #define SMB_ADS_DN_MAX 300 61 #define SMB_ADS_MAXMSGLEN 512 62 #define SMB_ADS_COMPUTERS_CN "Computers" 63 #define SMB_ADS_COMPUTER_NUM_ATTR 8 64 #define SMB_ADS_SHARE_NUM_ATTR 3 65 #define SMB_ADS_SITE_MAX MAXHOSTNAMELEN 66 67 #define SMB_ADS_MSDCS_SRV_DC_RR "_ldap._tcp.dc._msdcs" 68 #define SMB_ADS_MSDCS_SRV_SITE_RR "_ldap._tcp.%s._sites.dc._msdcs" 69 70 /* 71 * domainControllerFunctionality 72 * 73 * This rootDSE attribute indicates the functional level of the DC. 74 */ 75 #define SMB_ADS_ATTR_DCLEVEL "domainControllerFunctionality" 76 #define SMB_ADS_DCLEVEL_W2K 0 77 #define SMB_ADS_DCLEVEL_W2K3 2 78 #define SMB_ADS_DCLEVEL_W2K8 3 79 #define SMB_ADS_DCLEVEL_W2K8_R2 4 80 81 /* 82 * msDs-supportedEncryptionTypes (Windows Server 2008 only) 83 * 84 * This attribute defines the encryption types supported by the system. 85 * Encryption Types: 86 * - DES cbc mode with CRC-32 87 * - DES cbc mode with RSA-MD5 88 * - ArcFour with HMAC/md5 89 * - AES-128 90 * - AES-256 91 */ 92 #define SMB_ADS_ATTR_ENCTYPES "msDs-supportedEncryptionTypes" 93 #define SMB_ADS_ENC_DES_CRC 1 94 #define SMB_ADS_ENC_DES_MD5 2 95 #define SMB_ADS_ENC_RC4 4 96 #define SMB_ADS_ENC_AES128 8 97 #define SMB_ADS_ENC_AES256 16 98 99 static krb5_enctype w2k8enctypes[] = { 100 ENCTYPE_AES256_CTS_HMAC_SHA1_96, 101 ENCTYPE_AES128_CTS_HMAC_SHA1_96, 102 ENCTYPE_ARCFOUR_HMAC, 103 ENCTYPE_DES_CBC_CRC, 104 ENCTYPE_DES_CBC_MD5, 105 }; 106 107 static krb5_enctype pre_w2k8enctypes[] = { 108 ENCTYPE_ARCFOUR_HMAC, 109 ENCTYPE_DES_CBC_CRC, 110 ENCTYPE_DES_CBC_MD5, 111 }; 112 113 #define SMB_ADS_ATTR_SAMACCT "sAMAccountName" 114 #define SMB_ADS_ATTR_UPN "userPrincipalName" 115 #define SMB_ADS_ATTR_SPN "servicePrincipalName" 116 #define SMB_ADS_ATTR_CTL "userAccountControl" 117 #define SMB_ADS_ATTR_DNSHOST "dNSHostName" 118 #define SMB_ADS_ATTR_KVNO "msDS-KeyVersionNumber" 119 #define SMB_ADS_ATTR_DN "distinguishedName" 120 121 /* 122 * UserAccountControl flags: manipulate user account properties. 123 * 124 * The hexadecimal value of the following property flags are based on MSDN 125 * article # 305144. 126 */ 127 #define SMB_ADS_USER_ACCT_CTL_SCRIPT 0x00000001 128 #define SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE 0x00000002 129 #define SMB_ADS_USER_ACCT_CTL_HOMEDIR_REQUIRED 0x00000008 130 #define SMB_ADS_USER_ACCT_CTL_LOCKOUT 0x00000010 131 #define SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD 0x00000020 132 #define SMB_ADS_USER_ACCT_CTL_PASSWD_CANT_CHANGE 0x00000040 133 #define SMB_ADS_USER_ACCT_CTL_ENCRYPTED_TEXT_PWD_ALLOWED 0x00000080 134 #define SMB_ADS_USER_ACCT_CTL_TMP_DUP_ACCT 0x00000100 135 #define SMB_ADS_USER_ACCT_CTL_NORMAL_ACCT 0x00000200 136 #define SMB_ADS_USER_ACCT_CTL_INTERDOMAIN_TRUST_ACCT 0x00000800 137 #define SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT 0x00001000 138 #define SMB_ADS_USER_ACCT_CTL_SRV_TRUST_ACCT 0x00002000 139 #define SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD 0x00010000 140 #define SMB_ADS_USER_ACCT_CTL_MNS_LOGON_ACCT 0x00020000 141 #define SMB_ADS_USER_ACCT_CTL_SMARTCARD_REQUIRED 0x00040000 142 #define SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION 0x00080000 143 #define SMB_ADS_USER_ACCT_CTL_NOT_DELEGATED 0x00100000 144 #define SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY 0x00200000 145 #define SMB_ADS_USER_ACCT_CTL_DONT_REQ_PREAUTH 0x00400000 146 #define SMB_ADS_USER_ACCT_CTL_PASSWD_EXPIRED 0x00800000 147 #define SMB_ADS_USER_ACCT_CTL_TRUSTED_TO_AUTH_FOR_DELEGATION 0x01000000 148 149 /* 150 * Length of "dc=" prefix. 151 */ 152 #define SMB_ADS_DN_PREFIX_LEN 3 153 154 static char *smb_ads_computer_objcls[] = { 155 "top", "person", "organizationalPerson", 156 "user", "computer", NULL 157 }; 158 159 static char *smb_ads_share_objcls[] = { 160 "top", "leaf", "connectionPoint", "volume", NULL 161 }; 162 163 /* Cached ADS server to communicate with */ 164 static smb_ads_host_info_t *smb_ads_cached_host_info = NULL; 165 static mutex_t smb_ads_cached_host_mtx; 166 167 /* 168 * SMB ADS config cache is maintained to facilitate the detection of 169 * changes in configuration that is relevant to AD selection. 170 */ 171 typedef struct smb_ads_config { 172 char c_site[SMB_ADS_SITE_MAX]; 173 mutex_t c_mtx; 174 } smb_ads_config_t; 175 176 static smb_ads_config_t smb_ads_cfg; 177 178 179 /* attribute/value pair */ 180 typedef struct smb_ads_avpair { 181 char *avp_attr; 182 char *avp_val; 183 } smb_ads_avpair_t; 184 185 /* query status */ 186 typedef enum smb_ads_qstat { 187 SMB_ADS_STAT_ERR = -2, 188 SMB_ADS_STAT_DUP, 189 SMB_ADS_STAT_NOT_FOUND, 190 SMB_ADS_STAT_FOUND 191 } smb_ads_qstat_t; 192 193 typedef struct smb_ads_host_list { 194 int ah_cnt; 195 smb_ads_host_info_t *ah_list; 196 } smb_ads_host_list_t; 197 198 static int smb_ads_open_main(smb_ads_handle_t **, char *, char *, char *); 199 static int smb_ads_add_computer(smb_ads_handle_t *, int, char *); 200 static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *); 201 static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *); 202 static smb_ads_qstat_t smb_ads_lookup_computer_n_attr(smb_ads_handle_t *, 203 smb_ads_avpair_t *, int, char *); 204 static int smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *, int, char *); 205 static krb5_kvno smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *, char *); 206 static void smb_ads_free_cached_host(void); 207 static int smb_ads_alloc_attr(LDAPMod **, int); 208 static void smb_ads_free_attr(LDAPMod **); 209 static int smb_ads_get_dc_level(smb_ads_handle_t *); 210 static smb_ads_qstat_t smb_ads_find_computer(smb_ads_handle_t *, char *); 211 static smb_ads_qstat_t smb_ads_getattr(LDAP *, LDAPMessage *, 212 smb_ads_avpair_t *); 213 static smb_ads_qstat_t smb_ads_get_qstat(smb_ads_handle_t *, LDAPMessage *, 214 smb_ads_avpair_t *); 215 static boolean_t smb_ads_is_same_domain(char *, char *); 216 static smb_ads_host_info_t *smb_ads_dup_host_info(smb_ads_host_info_t *); 217 static char *smb_ads_get_sharedn(const char *, const char *, const char *); 218 static krb5_enctype *smb_ads_get_enctypes(int, int *); 219 220 /* 221 * smb_ads_init 222 * 223 * Initializes the ADS config cache. 224 */ 225 void 226 smb_ads_init(void) 227 { 228 (void) mutex_lock(&smb_ads_cfg.c_mtx); 229 (void) smb_config_getstr(SMB_CI_ADS_SITE, 230 smb_ads_cfg.c_site, SMB_ADS_SITE_MAX); 231 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 232 233 /* Force -lads to load, for dtrace. */ 234 DsFreeDcInfo(NULL); 235 } 236 237 void 238 smb_ads_fini(void) 239 { 240 smb_ads_free_cached_host(); 241 } 242 243 /* 244 * smb_ads_refresh 245 * 246 * This function will be called when smb/server SMF service is refreshed. 247 * (See smbd_join.c) 248 * 249 * Clearing the smb_ads_cached_host_info would allow the next DC 250 * discovery process to pick up an AD based on the new AD configuration. 251 */ 252 void 253 smb_ads_refresh(boolean_t force_rediscovery) 254 { 255 char new_site[SMB_ADS_SITE_MAX]; 256 257 (void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, SMB_ADS_SITE_MAX); 258 (void) mutex_lock(&smb_ads_cfg.c_mtx); 259 (void) strlcpy(smb_ads_cfg.c_site, new_site, SMB_ADS_SITE_MAX); 260 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 261 262 smb_ads_free_cached_host(); 263 264 if (force_rediscovery) { 265 (void) _DsForceRediscovery(NULL, 0); 266 } 267 } 268 269 270 /* 271 * smb_ads_build_unc_name 272 * 273 * Construct the UNC name of the share object in the format of 274 * \\hostname.domain\shareUNC 275 * 276 * Returns 0 on success, -1 on error. 277 */ 278 int 279 smb_ads_build_unc_name(char *unc_name, int maxlen, 280 const char *hostname, const char *shareUNC) 281 { 282 char my_domain[MAXHOSTNAMELEN]; 283 284 if (smb_getfqdomainname(my_domain, sizeof (my_domain)) != 0) 285 return (-1); 286 287 (void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s", 288 hostname, my_domain, shareUNC); 289 return (0); 290 } 291 292 /* 293 * The cached ADS host is no longer valid if one of the following criteria 294 * is satisfied: 295 * 296 * 1) not in the specified domain 297 * 2) not the sought host (if specified) 298 * 3) not reachable 299 * 300 * The caller is responsible for acquiring the smb_ads_cached_host_mtx lock 301 * prior to calling this function. 302 * 303 * Return B_TRUE if the cache host is still valid. Otherwise, return B_FALSE. 304 */ 305 static boolean_t 306 smb_ads_validate_cache_host(char *domain) 307 { 308 if (!smb_ads_cached_host_info) 309 return (B_FALSE); 310 311 if (!smb_ads_is_same_domain(smb_ads_cached_host_info->name, domain)) 312 return (B_FALSE); 313 314 return (B_TRUE); 315 } 316 317 /* 318 * smb_ads_match_hosts_same_domain 319 * 320 * Returns true, if the cached ADS host is in the same domain as the 321 * current (given) domain. 322 */ 323 static boolean_t 324 smb_ads_is_same_domain(char *cached_host_name, char *current_domain) 325 { 326 char *cached_host_domain; 327 328 if ((cached_host_name == NULL) || (current_domain == NULL)) 329 return (B_FALSE); 330 331 cached_host_domain = strchr(cached_host_name, '.'); 332 if (cached_host_domain == NULL) 333 return (B_FALSE); 334 335 ++cached_host_domain; 336 if (smb_strcasecmp(cached_host_domain, current_domain, 0)) 337 return (B_FALSE); 338 339 return (B_TRUE); 340 } 341 342 /* 343 * smb_ads_dup_host_info 344 * 345 * Duplicates the passed smb_ads_host_info_t structure. 346 * Caller must free memory allocated by this method. 347 * 348 * Returns a reference to the duplicated smb_ads_host_info_t structure. 349 * Returns NULL on error. 350 */ 351 static smb_ads_host_info_t * 352 smb_ads_dup_host_info(smb_ads_host_info_t *ads_host) 353 { 354 smb_ads_host_info_t *dup_host; 355 356 if (ads_host == NULL) 357 return (NULL); 358 359 dup_host = malloc(sizeof (smb_ads_host_info_t)); 360 361 if (dup_host != NULL) 362 bcopy(ads_host, dup_host, sizeof (smb_ads_host_info_t)); 363 364 return (dup_host); 365 } 366 367 /* 368 * smb_ads_find_host 369 * 370 * Finds an ADS host in a given domain. 371 * 372 * If the cached host is valid, it will be used. Otherwise, a DC will 373 * be selected based on the following criteria: 374 * 375 * 1) pdc (aka preferred DC) configuration 376 * 2) AD site configuration - the scope of the DNS lookup will be 377 * restricted to the specified site. 378 * 3) DC on the same subnet 379 * 4) DC with the lowest priority/highest weight 380 * 381 * The above items are listed in decreasing preference order. The selected 382 * DC must be online. 383 * 384 * If this function is called during domain join, the specified kpasswd server 385 * takes precedence over preferred DC, AD site, and so on. 386 * 387 * Parameters: 388 * domain: fully-qualified domain name. 389 * 390 * Returns: 391 * A copy of the cached host info is returned. The caller is responsible 392 * for deallocating the memory returned by this function. 393 */ 394 /*ARGSUSED*/ 395 smb_ads_host_info_t * 396 smb_ads_find_host(char *domain) 397 { 398 smb_ads_host_info_t *host = NULL; 399 DOMAIN_CONTROLLER_INFO *dci = NULL; 400 struct sockaddr_storage *ss; 401 uint32_t flags = DS_DS_FLAG; 402 uint32_t status; 403 int tries; 404 405 (void) mutex_lock(&smb_ads_cached_host_mtx); 406 if (smb_ads_validate_cache_host(domain)) { 407 host = smb_ads_dup_host_info(smb_ads_cached_host_info); 408 (void) mutex_unlock(&smb_ads_cached_host_mtx); 409 return (host); 410 } 411 412 (void) mutex_unlock(&smb_ads_cached_host_mtx); 413 smb_ads_free_cached_host(); 414 415 /* 416 * The _real_ DC Locator is over in idmapd. 417 * Door call over there to get it. 418 */ 419 tries = 15; 420 again: 421 status = _DsGetDcName( 422 NULL, /* ComputerName */ 423 domain, 424 NULL, /* DomainGuid */ 425 NULL, /* SiteName */ 426 flags, 427 &dci); 428 switch (status) { 429 case 0: 430 break; 431 /* 432 * We can see these errors when joining a domain, if we race 433 * asking idmap for the DC before it knows the new domain. 434 */ 435 case NT_STATUS_NO_SUCH_DOMAIN: /* Specified domain unknown */ 436 case NT_STATUS_INVALID_SERVER_STATE: /* not in domain mode. */ 437 if (--tries > 0) { 438 (void) sleep(1); 439 goto again; 440 } 441 /* FALLTHROUGH */ 442 case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: 443 case NT_STATUS_CANT_WAIT: /* timeout over in idmap */ 444 default: 445 return (NULL); 446 } 447 448 host = calloc(1, sizeof (*host)); 449 if (host == NULL) 450 goto out; 451 452 (void) strlcpy(host->name, dci->DomainControllerName, MAXHOSTNAMELEN); 453 ss = (void *)dci->_sockaddr; 454 switch (ss->ss_family) { 455 case AF_INET: { 456 struct sockaddr_in *sin = (void *)ss; 457 host->port = ntohs(sin->sin_port); 458 host->ipaddr.a_family = AF_INET; 459 (void) memcpy(&host->ipaddr.a_ipv4, &sin->sin_addr, 460 sizeof (in_addr_t)); 461 break; 462 } 463 case AF_INET6: { 464 struct sockaddr_in6 *sin6 = (void *)ss; 465 host->port = ntohs(sin6->sin6_port); 466 host->ipaddr.a_family = AF_INET6; 467 (void) memcpy(&host->ipaddr.a_ipv6, &sin6->sin6_addr, 468 sizeof (in6_addr_t)); 469 break; 470 } 471 default: 472 syslog(LOG_ERR, "no addr for DC %s", 473 dci->DomainControllerName); 474 free(host); 475 host = NULL; 476 goto out; 477 } 478 479 (void) mutex_lock(&smb_ads_cached_host_mtx); 480 if (!smb_ads_cached_host_info) 481 smb_ads_cached_host_info = smb_ads_dup_host_info(host); 482 host = smb_ads_dup_host_info(smb_ads_cached_host_info); 483 (void) mutex_unlock(&smb_ads_cached_host_mtx); 484 485 out: 486 DsFreeDcInfo(dci); 487 return (host); 488 } 489 490 /* 491 * Return the number of dots in a string. 492 */ 493 static int 494 smb_ads_count_dots(const char *s) 495 { 496 int ndots = 0; 497 498 while (*s) { 499 if (*s++ == '.') 500 ndots++; 501 } 502 503 return (ndots); 504 } 505 506 /* 507 * Convert a domain name in dot notation to distinguished name format, 508 * for example: sun.com -> dc=sun,dc=com. 509 * 510 * Returns a pointer to an allocated buffer containing the distinguished 511 * name. 512 */ 513 static char * 514 smb_ads_convert_domain(const char *domain_name) 515 { 516 const char *s; 517 char *dn_name; 518 char buf[2]; 519 int ndots; 520 int len; 521 522 if (domain_name == NULL || *domain_name == 0) 523 return (NULL); 524 525 ndots = smb_ads_count_dots(domain_name); 526 ++ndots; 527 len = strlen(domain_name) + (ndots * SMB_ADS_DN_PREFIX_LEN) + 1; 528 529 if ((dn_name = malloc(len)) == NULL) 530 return (NULL); 531 532 bzero(dn_name, len); 533 (void) strlcpy(dn_name, "dc=", len); 534 535 buf[1] = '\0'; 536 s = domain_name; 537 538 while (*s) { 539 if (*s == '.') { 540 (void) strlcat(dn_name, ",dc=", len); 541 } else { 542 buf[0] = *s; 543 (void) strlcat(dn_name, buf, len); 544 } 545 ++s; 546 } 547 548 return (dn_name); 549 } 550 551 /* 552 * smb_ads_free_cached_host 553 * 554 * Free the memory use by the global smb_ads_cached_host_info & set it to NULL. 555 */ 556 static void 557 smb_ads_free_cached_host(void) 558 { 559 (void) mutex_lock(&smb_ads_cached_host_mtx); 560 if (smb_ads_cached_host_info) { 561 free(smb_ads_cached_host_info); 562 smb_ads_cached_host_info = NULL; 563 } 564 (void) mutex_unlock(&smb_ads_cached_host_mtx); 565 } 566 567 /* 568 * smb_ads_open 569 * Open a LDAP connection to an ADS server if the system is in domain mode. 570 * Acquire both Kerberos TGT and LDAP service tickets for the host principal. 571 * 572 * This function should only be called after the system is successfully joined 573 * to a domain. 574 */ 575 smb_ads_handle_t * 576 smb_ads_open(void) 577 { 578 char domain[MAXHOSTNAMELEN]; 579 smb_ads_handle_t *h; 580 smb_ads_status_t err; 581 582 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 583 return (NULL); 584 585 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 586 return (NULL); 587 588 err = smb_ads_open_main(&h, domain, NULL, NULL); 589 if (err != 0) { 590 smb_ads_log_errmsg(err); 591 return (NULL); 592 } 593 594 return (h); 595 } 596 597 static int 598 smb_ads_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) 599 { 600 NOTE(ARGUNUSED(ld, defaults)); 601 sasl_interact_t *interact; 602 603 if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE) 604 return (LDAP_PARAM_ERROR); 605 606 /* There should be no extra arguemnts for SASL/GSSAPI authentication */ 607 for (interact = prompts; interact->id != SASL_CB_LIST_END; 608 interact++) { 609 interact->result = NULL; 610 interact->len = 0; 611 } 612 return (LDAP_SUCCESS); 613 } 614 615 /* 616 * smb_ads_open_main 617 * Open a LDAP connection to an ADS server. 618 * If ADS is enabled and the administrative username, password, and 619 * ADS domain are defined then query DNS to find an ADS server if this is the 620 * very first call to this routine. After an ADS server is found then this 621 * server will be used everytime this routine is called until the system is 622 * rebooted or the ADS server becomes unavailable then an ADS server will 623 * be queried again. After the connection is made then an ADS handle 624 * is created to be returned. 625 * 626 * After the LDAP connection, the LDAP version will be set to 3 using 627 * ldap_set_option(). 628 * 629 * The LDAP connection is bound before the ADS handle is returned. 630 * Parameters: 631 * domain - fully-qualified domain name 632 * user - the user account for whom the Kerberos TGT ticket and ADS 633 * service tickets are acquired. 634 * password - password of the specified user 635 * 636 * Returns: 637 * NULL : can't connect to ADS server or other errors 638 * smb_ads_handle_t* : handle to ADS server 639 */ 640 static int 641 smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user, 642 char *password) 643 { 644 smb_ads_handle_t *ah; 645 LDAP *ld; 646 int version = 3; 647 smb_ads_host_info_t *ads_host = NULL; 648 int err, rc; 649 650 *hp = NULL; 651 652 if (user != NULL) { 653 err = smb_kinit(domain, user, password); 654 if (err != 0) 655 return (err); 656 user = NULL; 657 password = NULL; 658 } 659 660 ads_host = smb_ads_find_host(domain); 661 if (ads_host == NULL) 662 return (SMB_ADS_CANT_LOCATE_DC); 663 664 ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t)); 665 if (ah == NULL) { 666 free(ads_host); 667 return (ENOMEM); 668 } 669 670 (void) memset(ah, 0, sizeof (smb_ads_handle_t)); 671 672 if ((ld = ldap_init(ads_host->name, ads_host->port)) == NULL) { 673 syslog(LOG_ERR, "smbns: ldap_init failed"); 674 smb_ads_free_cached_host(); 675 free(ah); 676 free(ads_host); 677 return (SMB_ADS_LDAP_INIT); 678 } 679 680 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) 681 != LDAP_SUCCESS) { 682 smb_ads_free_cached_host(); 683 free(ah); 684 free(ads_host); 685 (void) ldap_unbind(ld); 686 return (SMB_ADS_LDAP_SETOPT); 687 } 688 689 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 690 ah->ld = ld; 691 ah->domain = strdup(domain); 692 693 if (ah->domain == NULL) { 694 smb_ads_close(ah); 695 free(ads_host); 696 return (SMB_ADS_LDAP_SETOPT); 697 } 698 699 /* 700 * ah->domain is often used for generating service principal name. 701 * Convert it to lower case for RFC 4120 section 6.2.1 conformance. 702 */ 703 (void) smb_strlwr(ah->domain); 704 ah->domain_dn = smb_ads_convert_domain(domain); 705 if (ah->domain_dn == NULL) { 706 smb_ads_close(ah); 707 free(ads_host); 708 return (SMB_ADS_LDAP_SET_DOM); 709 } 710 711 ah->hostname = strdup(ads_host->name); 712 if (ah->hostname == NULL) { 713 smb_ads_close(ah); 714 free(ads_host); 715 return (ENOMEM); 716 } 717 (void) mutex_lock(&smb_ads_cfg.c_mtx); 718 if (*smb_ads_cfg.c_site != '\0') { 719 if ((ah->site = strdup(smb_ads_cfg.c_site)) == NULL) { 720 smb_ads_close(ah); 721 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 722 free(ads_host); 723 return (ENOMEM); 724 } 725 } else { 726 ah->site = NULL; 727 } 728 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 729 730 rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL, 731 LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL); 732 if (rc != LDAP_SUCCESS) { 733 syslog(LOG_ERR, "smbns: ldap_sasl_..._bind_s failed (%s)", 734 ldap_err2string(rc)); 735 smb_ads_close(ah); 736 free(ads_host); 737 return (SMB_ADS_LDAP_SASL_BIND); 738 } 739 740 free(ads_host); 741 *hp = ah; 742 743 return (SMB_ADS_SUCCESS); 744 } 745 746 /* 747 * smb_ads_close 748 * Close connection to ADS server and free memory allocated for ADS handle. 749 * LDAP unbind is called here. 750 * Parameters: 751 * ah: handle to ADS server 752 * Returns: 753 * void 754 */ 755 void 756 smb_ads_close(smb_ads_handle_t *ah) 757 { 758 if (ah == NULL) 759 return; 760 /* close and free connection resources */ 761 if (ah->ld) 762 (void) ldap_unbind(ah->ld); 763 764 free(ah->domain); 765 free(ah->domain_dn); 766 free(ah->hostname); 767 free(ah->site); 768 free(ah); 769 } 770 771 /* 772 * smb_ads_alloc_attr 773 * 774 * Since the attrs is a null-terminated array, all elements 775 * in the array (except the last one) will point to allocated 776 * memory. 777 */ 778 static int 779 smb_ads_alloc_attr(LDAPMod *attrs[], int num) 780 { 781 int i; 782 783 bzero(attrs, num * sizeof (LDAPMod *)); 784 for (i = 0; i < (num - 1); i++) { 785 attrs[i] = (LDAPMod *)malloc(sizeof (LDAPMod)); 786 if (attrs[i] == NULL) { 787 smb_ads_free_attr(attrs); 788 return (-1); 789 } 790 } 791 792 return (0); 793 } 794 795 /* 796 * smb_ads_free_attr 797 * Free memory allocated when publishing a share. 798 * Parameters: 799 * attrs: an array of LDAPMod pointers 800 * Returns: 801 * None 802 */ 803 static void 804 smb_ads_free_attr(LDAPMod *attrs[]) 805 { 806 int i; 807 for (i = 0; attrs[i]; i++) { 808 free(attrs[i]); 809 } 810 } 811 812 /* 813 * Returns share DN in an allocated buffer. The format of the DN is 814 * cn=<sharename>,<container RDNs>,<domain DN> 815 * 816 * If the domain DN is not included in the container parameter, 817 * then it will be appended to create the share DN. 818 * 819 * The caller must free the allocated buffer. 820 */ 821 static char * 822 smb_ads_get_sharedn(const char *sharename, const char *container, 823 const char *domain_dn) 824 { 825 char *share_dn; 826 int rc, offset, container_len, domain_len; 827 boolean_t append_domain = B_TRUE; 828 829 container_len = strlen(container); 830 domain_len = strlen(domain_dn); 831 832 if (container_len >= domain_len) { 833 834 /* offset to last domain_len characters */ 835 offset = container_len - domain_len; 836 837 if (smb_strcasecmp(container + offset, 838 domain_dn, domain_len) == 0) 839 append_domain = B_FALSE; 840 } 841 842 if (append_domain) 843 rc = asprintf(&share_dn, "cn=%s,%s,%s", sharename, 844 container, domain_dn); 845 else 846 rc = asprintf(&share_dn, "cn=%s,%s", sharename, 847 container); 848 849 return ((rc == -1) ? NULL : share_dn); 850 } 851 852 /* 853 * smb_ads_add_share 854 * Call by smb_ads_publish_share to create share object in ADS. 855 * This routine specifies the attributes of an ADS LDAP share object. The first 856 * attribute and values define the type of ADS object, the share object. The 857 * second attribute and value define the UNC of the share data for the share 858 * object. The LDAP synchronous add command is used to add the object into ADS. 859 * The container location to add the object needs to specified. 860 * Parameters: 861 * ah : handle to ADS server 862 * adsShareName: name of share object to be created in ADS 863 * shareUNC : share name on NetForce 864 * adsContainer: location in ADS to create share object 865 * 866 * Returns: 867 * -1 : error 868 * 0 : success 869 */ 870 int 871 smb_ads_add_share(smb_ads_handle_t *ah, const char *adsShareName, 872 const char *unc_name, const char *adsContainer) 873 { 874 LDAPMod *attrs[SMB_ADS_SHARE_NUM_ATTR]; 875 int j = 0; 876 char *share_dn; 877 int ret; 878 char *unc_names[] = {(char *)unc_name, NULL}; 879 880 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 881 ah->domain_dn)) == NULL) 882 return (-1); 883 884 if (smb_ads_alloc_attr(attrs, SMB_ADS_SHARE_NUM_ATTR) != 0) { 885 free(share_dn); 886 return (-1); 887 } 888 889 attrs[j]->mod_op = LDAP_MOD_ADD; 890 attrs[j]->mod_type = "objectClass"; 891 attrs[j]->mod_values = smb_ads_share_objcls; 892 893 attrs[++j]->mod_op = LDAP_MOD_ADD; 894 attrs[j]->mod_type = "uNCName"; 895 attrs[j]->mod_values = unc_names; 896 897 if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) { 898 if (ret == LDAP_NO_SUCH_OBJECT) { 899 syslog(LOG_ERR, "Failed to publish share %s in" \ 900 " AD. Container does not exist: %s.\n", 901 adsShareName, share_dn); 902 903 } else { 904 syslog(LOG_ERR, "Failed to publish share %s in" \ 905 " AD: %s (%s).\n", adsShareName, share_dn, 906 ldap_err2string(ret)); 907 } 908 smb_ads_free_attr(attrs); 909 free(share_dn); 910 return (ret); 911 } 912 free(share_dn); 913 smb_ads_free_attr(attrs); 914 915 return (0); 916 } 917 918 /* 919 * smb_ads_del_share 920 * Call by smb_ads_remove_share to remove share object from ADS. The container 921 * location to remove the object needs to specified. The LDAP synchronous 922 * delete command is used. 923 * Parameters: 924 * ah : handle to ADS server 925 * adsShareName: name of share object in ADS to be removed 926 * adsContainer: location of share object in ADS 927 * Returns: 928 * -1 : error 929 * 0 : success 930 */ 931 static int 932 smb_ads_del_share(smb_ads_handle_t *ah, const char *adsShareName, 933 const char *adsContainer) 934 { 935 char *share_dn; 936 int ret; 937 938 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 939 ah->domain_dn)) == NULL) 940 return (-1); 941 942 if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { 943 smb_tracef("ldap_delete: %s", ldap_err2string(ret)); 944 free(share_dn); 945 return (-1); 946 } 947 free(share_dn); 948 949 return (0); 950 } 951 952 953 /* 954 * smb_ads_escape_search_filter_chars 955 * 956 * This routine will escape the special characters found in a string 957 * that will later be passed to the ldap search filter. 958 * 959 * RFC 1960 - A String Representation of LDAP Search Filters 960 * 3. String Search Filter Definition 961 * If a value must contain one of the characters '*' OR '(' OR ')', 962 * these characters 963 * should be escaped by preceding them with the backslash '\' character. 964 * 965 * RFC 2252 - LDAP Attribute Syntax Definitions 966 * a backslash quoting mechanism is used to escape 967 * the following separator symbol character (such as "'", "$" or "#") if 968 * it should occur in that string. 969 */ 970 static int 971 smb_ads_escape_search_filter_chars(const char *src, char *dst) 972 { 973 int avail = SMB_ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ 974 975 if (src == NULL || dst == NULL) 976 return (-1); 977 978 while (*src) { 979 if (!avail) { 980 *dst = 0; 981 return (-1); 982 } 983 984 switch (*src) { 985 case '\\': 986 case '\'': 987 case '$': 988 case '#': 989 case '*': 990 case '(': 991 case ')': 992 *dst++ = '\\'; 993 avail--; 994 /* fall through */ 995 996 default: 997 *dst++ = *src++; 998 avail--; 999 } 1000 } 1001 1002 *dst = 0; 1003 1004 return (0); 1005 } 1006 1007 /* 1008 * smb_ads_lookup_share 1009 * The search filter is set to search for a specific share name in the 1010 * specified ADS container. The LDSAP synchronous search command is used. 1011 * Parameters: 1012 * ah : handle to ADS server 1013 * adsShareName: name of share object in ADS to be searched 1014 * adsContainer: location of share object in ADS 1015 * Returns: 1016 * -1 : error 1017 * 0 : not found 1018 * 1 : found 1019 */ 1020 int 1021 smb_ads_lookup_share(smb_ads_handle_t *ah, const char *adsShareName, 1022 const char *adsContainer, char *unc_name) 1023 { 1024 char *attrs[4], filter[SMB_ADS_MAXBUFLEN]; 1025 char *share_dn; 1026 int ret; 1027 LDAPMessage *res; 1028 char tmpbuf[SMB_ADS_MAXBUFLEN]; 1029 1030 if (adsShareName == NULL || adsContainer == NULL) 1031 return (-1); 1032 1033 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 1034 ah->domain_dn)) == NULL) 1035 return (-1); 1036 1037 res = NULL; 1038 attrs[0] = "cn"; 1039 attrs[1] = "objectClass"; 1040 attrs[2] = "uNCName"; 1041 attrs[3] = NULL; 1042 1043 if (smb_ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { 1044 free(share_dn); 1045 return (-1); 1046 } 1047 1048 (void) snprintf(filter, sizeof (filter), 1049 "(&(objectClass=volume)(uNCName=%s))", tmpbuf); 1050 1051 if ((ret = ldap_search_s(ah->ld, share_dn, 1052 LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { 1053 if (ret != LDAP_NO_SUCH_OBJECT) 1054 smb_tracef("%s: ldap_search: %s", share_dn, 1055 ldap_err2string(ret)); 1056 1057 (void) ldap_msgfree(res); 1058 free(share_dn); 1059 return (0); 1060 } 1061 1062 (void) free(share_dn); 1063 1064 /* no match is found */ 1065 if (ldap_count_entries(ah->ld, res) == 0) { 1066 (void) ldap_msgfree(res); 1067 return (0); 1068 } 1069 1070 /* free the search results */ 1071 (void) ldap_msgfree(res); 1072 1073 return (1); 1074 } 1075 1076 /* 1077 * smb_ads_publish_share 1078 * Publish share into ADS. If a share name already exist in ADS in the same 1079 * container then the existing share object is removed before adding the new 1080 * share object. 1081 * Parameters: 1082 * ah : handle return from smb_ads_open 1083 * adsShareName: name of share to be added to ADS directory 1084 * shareUNC : name of share on client, can be NULL to use the same name 1085 * as adsShareName 1086 * adsContainer: location for share to be added in ADS directory, ie 1087 * ou=share_folder 1088 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR 1089 * to use host ip addr for UNC. 1090 * Returns: 1091 * -1 : error 1092 * 0 : success 1093 */ 1094 int 1095 smb_ads_publish_share(smb_ads_handle_t *ah, const char *adsShareName, 1096 const char *shareUNC, const char *adsContainer, const char *hostname) 1097 { 1098 int ret; 1099 char unc_name[SMB_ADS_MAXBUFLEN]; 1100 1101 if (adsShareName == NULL || adsContainer == NULL) 1102 return (-1); 1103 1104 if (shareUNC == 0 || *shareUNC == 0) 1105 shareUNC = adsShareName; 1106 1107 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1108 hostname, shareUNC) < 0) 1109 return (-1); 1110 1111 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1112 1113 switch (ret) { 1114 case 1: 1115 (void) smb_ads_del_share(ah, adsShareName, adsContainer); 1116 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1117 adsContainer); 1118 break; 1119 1120 case 0: 1121 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1122 adsContainer); 1123 if (ret == LDAP_ALREADY_EXISTS) 1124 ret = -1; 1125 1126 break; 1127 1128 case -1: 1129 default: 1130 /* return with error code */ 1131 ret = -1; 1132 } 1133 1134 return (ret); 1135 } 1136 1137 /* 1138 * smb_ads_remove_share 1139 * Remove share from ADS. A search is done first before explicitly removing 1140 * the share. 1141 * Parameters: 1142 * ah : handle return from smb_ads_open 1143 * adsShareName: name of share to be removed from ADS directory 1144 * adsContainer: location for share to be removed from ADS directory, ie 1145 * ou=share_folder 1146 * Returns: 1147 * -1 : error 1148 * 0 : success 1149 */ 1150 int 1151 smb_ads_remove_share(smb_ads_handle_t *ah, const char *adsShareName, 1152 const char *shareUNC, const char *adsContainer, const char *hostname) 1153 { 1154 int ret; 1155 char unc_name[SMB_ADS_MAXBUFLEN]; 1156 1157 if (adsShareName == NULL || adsContainer == NULL) 1158 return (-1); 1159 if (shareUNC == 0 || *shareUNC == 0) 1160 shareUNC = adsShareName; 1161 1162 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1163 hostname, shareUNC) < 0) 1164 return (-1); 1165 1166 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1167 if (ret == 0) 1168 return (0); 1169 if (ret == -1) 1170 return (-1); 1171 1172 return (smb_ads_del_share(ah, adsShareName, adsContainer)); 1173 } 1174 1175 /* 1176 * smb_ads_get_default_comp_container_dn 1177 * 1178 * Build the distinguished name for the default computer conatiner (i.e. the 1179 * pre-defined Computers container). 1180 */ 1181 static void 1182 smb_ads_get_default_comp_container_dn(smb_ads_handle_t *ah, char *buf, 1183 size_t buflen) 1184 { 1185 (void) snprintf(buf, buflen, "cn=%s,%s", SMB_ADS_COMPUTERS_CN, 1186 ah->domain_dn); 1187 } 1188 1189 /* 1190 * smb_ads_get_default_comp_dn 1191 * 1192 * Build the distinguished name for this system. 1193 */ 1194 static void 1195 smb_ads_get_default_comp_dn(smb_ads_handle_t *ah, char *buf, size_t buflen) 1196 { 1197 char nbname[NETBIOS_NAME_SZ]; 1198 char container_dn[SMB_ADS_DN_MAX]; 1199 1200 (void) smb_getnetbiosname(nbname, sizeof (nbname)); 1201 smb_ads_get_default_comp_container_dn(ah, container_dn, SMB_ADS_DN_MAX); 1202 (void) snprintf(buf, buflen, "cn=%s,%s", nbname, container_dn); 1203 } 1204 1205 /* 1206 * smb_ads_add_computer 1207 * 1208 * Returns 0 upon success. Otherwise, returns -1. 1209 */ 1210 static int 1211 smb_ads_add_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1212 { 1213 return (smb_ads_computer_op(ah, LDAP_MOD_ADD, dclevel, dn)); 1214 } 1215 1216 /* 1217 * smb_ads_modify_computer 1218 * 1219 * Returns 0 upon success. Otherwise, returns -1. 1220 */ 1221 static int 1222 smb_ads_modify_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1223 { 1224 return (smb_ads_computer_op(ah, LDAP_MOD_REPLACE, dclevel, dn)); 1225 } 1226 1227 /* 1228 * smb_ads_get_dc_level 1229 * 1230 * Returns the functional level of the DC upon success. 1231 * Otherwise, -1 is returned. 1232 */ 1233 static int 1234 smb_ads_get_dc_level(smb_ads_handle_t *ah) 1235 { 1236 LDAPMessage *res, *entry; 1237 char *attr[2]; 1238 char **vals; 1239 int rc = -1; 1240 1241 res = NULL; 1242 attr[0] = SMB_ADS_ATTR_DCLEVEL; 1243 attr[1] = NULL; 1244 if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, 1245 0, &res) != LDAP_SUCCESS) { 1246 (void) ldap_msgfree(res); 1247 return (-1); 1248 } 1249 1250 /* no match for the specified attribute is found */ 1251 if (ldap_count_entries(ah->ld, res) == 0) { 1252 (void) ldap_msgfree(res); 1253 return (-1); 1254 } 1255 1256 entry = ldap_first_entry(ah->ld, res); 1257 if (entry) { 1258 if ((vals = ldap_get_values(ah->ld, entry, 1259 SMB_ADS_ATTR_DCLEVEL)) == NULL) { 1260 /* 1261 * Observed the values aren't populated 1262 * by the Windows 2000 server. 1263 */ 1264 (void) ldap_msgfree(res); 1265 return (SMB_ADS_DCLEVEL_W2K); 1266 } 1267 1268 if (vals[0] != NULL) 1269 rc = atoi(vals[0]); 1270 1271 ldap_value_free(vals); 1272 } 1273 1274 (void) ldap_msgfree(res); 1275 return (rc); 1276 } 1277 1278 /* 1279 * The fully-qualified hostname returned by this function is often used for 1280 * constructing service principal name. Return the fully-qualified hostname 1281 * in lower case for RFC 4120 section 6.2.1 conformance. 1282 */ 1283 static int 1284 smb_ads_getfqhostname(smb_ads_handle_t *ah, char *fqhost, int len) 1285 { 1286 if (smb_gethostname(fqhost, len, SMB_CASE_LOWER) != 0) 1287 return (-1); 1288 1289 (void) snprintf(fqhost, len, "%s.%s", fqhost, 1290 ah->domain); 1291 1292 return (0); 1293 } 1294 1295 static int 1296 smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn) 1297 { 1298 LDAPMod *attrs[SMB_ADS_COMPUTER_NUM_ATTR]; 1299 char *sam_val[2]; 1300 char *ctl_val[2], *fqh_val[2]; 1301 char *encrypt_val[2]; 1302 int j = -1; 1303 int ret, usrctl_flags = 0; 1304 char sam_acct[SMB_SAMACCT_MAXLEN]; 1305 char fqhost[MAXHOSTNAMELEN]; 1306 char usrctl_buf[16]; 1307 char encrypt_buf[16]; 1308 int max; 1309 smb_krb5_pn_set_t spn, upn; 1310 1311 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 1312 return (-1); 1313 1314 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1315 return (-1); 1316 1317 /* The SPN attribute is multi-valued and must be 1 or greater */ 1318 if (smb_krb5_get_pn_set(&spn, SMB_PN_SPN_ATTR, ah->domain) == 0) 1319 return (-1); 1320 1321 /* The UPN attribute is single-valued and cannot be zero */ 1322 if (smb_krb5_get_pn_set(&upn, SMB_PN_UPN_ATTR, ah->domain) != 1) { 1323 smb_krb5_free_pn_set(&spn); 1324 smb_krb5_free_pn_set(&upn); 1325 return (-1); 1326 } 1327 1328 max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)) 1329 - (dclevel >= SMB_ADS_DCLEVEL_W2K8 ? 0 : 1); 1330 1331 if (smb_ads_alloc_attr(attrs, max) != 0) { 1332 smb_krb5_free_pn_set(&spn); 1333 smb_krb5_free_pn_set(&upn); 1334 return (-1); 1335 } 1336 1337 /* objectClass attribute is not modifiable. */ 1338 if (op == LDAP_MOD_ADD) { 1339 attrs[++j]->mod_op = op; 1340 attrs[j]->mod_type = "objectClass"; 1341 attrs[j]->mod_values = smb_ads_computer_objcls; 1342 } 1343 1344 attrs[++j]->mod_op = op; 1345 attrs[j]->mod_type = SMB_ADS_ATTR_SAMACCT; 1346 sam_val[0] = sam_acct; 1347 sam_val[1] = 0; 1348 attrs[j]->mod_values = sam_val; 1349 1350 attrs[++j]->mod_op = op; 1351 attrs[j]->mod_type = SMB_ADS_ATTR_UPN; 1352 attrs[j]->mod_values = upn.s_pns; 1353 1354 attrs[++j]->mod_op = op; 1355 attrs[j]->mod_type = SMB_ADS_ATTR_SPN; 1356 attrs[j]->mod_values = spn.s_pns; 1357 1358 attrs[++j]->mod_op = op; 1359 attrs[j]->mod_type = SMB_ADS_ATTR_CTL; 1360 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1361 SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1362 SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1363 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1364 ctl_val[0] = usrctl_buf; 1365 ctl_val[1] = 0; 1366 attrs[j]->mod_values = ctl_val; 1367 1368 attrs[++j]->mod_op = op; 1369 attrs[j]->mod_type = SMB_ADS_ATTR_DNSHOST; 1370 fqh_val[0] = fqhost; 1371 fqh_val[1] = 0; 1372 attrs[j]->mod_values = fqh_val; 1373 1374 /* enctypes support starting in Windows Server 2008 */ 1375 if (dclevel > SMB_ADS_DCLEVEL_W2K3) { 1376 attrs[++j]->mod_op = op; 1377 attrs[j]->mod_type = SMB_ADS_ATTR_ENCTYPES; 1378 (void) snprintf(encrypt_buf, sizeof (encrypt_buf), "%d", 1379 SMB_ADS_ENC_AES256 + SMB_ADS_ENC_AES128 + SMB_ADS_ENC_RC4 + 1380 SMB_ADS_ENC_DES_MD5 + SMB_ADS_ENC_DES_CRC); 1381 encrypt_val[0] = encrypt_buf; 1382 encrypt_val[1] = 0; 1383 attrs[j]->mod_values = encrypt_val; 1384 } 1385 1386 switch (op) { 1387 case LDAP_MOD_ADD: 1388 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1389 syslog(LOG_NOTICE, "ldap_add: %s", 1390 ldap_err2string(ret)); 1391 ret = -1; 1392 } 1393 break; 1394 1395 case LDAP_MOD_REPLACE: 1396 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1397 syslog(LOG_NOTICE, "ldap_modify: %s", 1398 ldap_err2string(ret)); 1399 ret = -1; 1400 } 1401 break; 1402 1403 default: 1404 ret = -1; 1405 1406 } 1407 1408 smb_ads_free_attr(attrs); 1409 smb_krb5_free_pn_set(&spn); 1410 smb_krb5_free_pn_set(&upn); 1411 1412 return (ret); 1413 } 1414 1415 /* 1416 * Delete an ADS computer account. 1417 */ 1418 static void 1419 smb_ads_del_computer(smb_ads_handle_t *ah, char *dn) 1420 { 1421 int rc; 1422 1423 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) 1424 smb_tracef("ldap_delete: %s", ldap_err2string(rc)); 1425 } 1426 1427 /* 1428 * Gets the value of the given attribute. 1429 */ 1430 static smb_ads_qstat_t 1431 smb_ads_getattr(LDAP *ld, LDAPMessage *entry, smb_ads_avpair_t *avpair) 1432 { 1433 char **vals; 1434 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 1435 1436 assert(avpair); 1437 avpair->avp_val = NULL; 1438 vals = ldap_get_values(ld, entry, avpair->avp_attr); 1439 if (!vals) 1440 return (SMB_ADS_STAT_NOT_FOUND); 1441 1442 if (!vals[0]) { 1443 ldap_value_free(vals); 1444 return (SMB_ADS_STAT_NOT_FOUND); 1445 } 1446 1447 avpair->avp_val = strdup(vals[0]); 1448 if (!avpair->avp_val) 1449 rc = SMB_ADS_STAT_ERR; 1450 1451 ldap_value_free(vals); 1452 return (rc); 1453 } 1454 1455 /* 1456 * Process query's result. 1457 */ 1458 static smb_ads_qstat_t 1459 smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, 1460 smb_ads_avpair_t *avpair) 1461 { 1462 char fqhost[MAXHOSTNAMELEN]; 1463 smb_ads_avpair_t dnshost_avp; 1464 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 1465 LDAPMessage *entry; 1466 1467 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1468 return (SMB_ADS_STAT_ERR); 1469 1470 if (ldap_count_entries(ah->ld, res) == 0) 1471 return (SMB_ADS_STAT_NOT_FOUND); 1472 1473 if ((entry = ldap_first_entry(ah->ld, res)) == NULL) 1474 return (SMB_ADS_STAT_ERR); 1475 1476 dnshost_avp.avp_attr = SMB_ADS_ATTR_DNSHOST; 1477 rc = smb_ads_getattr(ah->ld, entry, &dnshost_avp); 1478 1479 switch (rc) { 1480 case SMB_ADS_STAT_FOUND: 1481 /* 1482 * Returns SMB_ADS_STAT_DUP to avoid overwriting 1483 * the computer account of another system whose 1484 * NetBIOS name collides with that of the current 1485 * system. 1486 */ 1487 if (strcasecmp(dnshost_avp.avp_val, fqhost)) 1488 rc = SMB_ADS_STAT_DUP; 1489 1490 free(dnshost_avp.avp_val); 1491 break; 1492 1493 case SMB_ADS_STAT_NOT_FOUND: 1494 /* 1495 * Pre-created computer account doesn't have 1496 * the dNSHostname attribute. It's been observed 1497 * that the dNSHostname attribute is only set after 1498 * a successful domain join. 1499 * Returns SMB_ADS_STAT_FOUND as the account is 1500 * pre-created for the current system. 1501 */ 1502 rc = SMB_ADS_STAT_FOUND; 1503 break; 1504 1505 default: 1506 break; 1507 } 1508 1509 if (rc != SMB_ADS_STAT_FOUND) 1510 return (rc); 1511 1512 if (avpair) 1513 rc = smb_ads_getattr(ah->ld, entry, avpair); 1514 1515 return (rc); 1516 1517 } 1518 1519 /* 1520 * smb_ads_lookup_computer_n_attr 1521 * 1522 * If avpair is NULL, checks the status of the specified computer account. 1523 * Otherwise, looks up the value of the specified computer account's attribute. 1524 * If found, the value field of the avpair will be allocated and set. The 1525 * caller should free the allocated buffer. 1526 * 1527 * Return: 1528 * SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is 1529 * found. 1530 * SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute 1531 * is not found. 1532 * SMB_ADS_STAT_DUP - if the computer account is already used by other systems 1533 * in the AD. This could happen if the hostname of multiple 1534 * systems resolved to the same NetBIOS name. 1535 * SMB_ADS_STAT_ERR - any failure. 1536 */ 1537 static smb_ads_qstat_t 1538 smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair, 1539 int scope, char *dn) 1540 { 1541 char *attrs[3], filter[SMB_ADS_MAXBUFLEN]; 1542 LDAPMessage *res; 1543 char sam_acct[SMB_SAMACCT_MAXLEN], sam_acct2[SMB_SAMACCT_MAXLEN]; 1544 smb_ads_qstat_t rc; 1545 1546 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 1547 return (SMB_ADS_STAT_ERR); 1548 1549 res = NULL; 1550 attrs[0] = SMB_ADS_ATTR_DNSHOST; 1551 attrs[1] = NULL; 1552 attrs[2] = NULL; 1553 1554 if (avpair) { 1555 if (!avpair->avp_attr) 1556 return (SMB_ADS_STAT_ERR); 1557 1558 attrs[1] = avpair->avp_attr; 1559 } 1560 1561 if (smb_ads_escape_search_filter_chars(sam_acct, sam_acct2) != 0) 1562 return (SMB_ADS_STAT_ERR); 1563 1564 (void) snprintf(filter, sizeof (filter), 1565 "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT, 1566 sam_acct2); 1567 1568 if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, 1569 &res) != LDAP_SUCCESS) { 1570 (void) ldap_msgfree(res); 1571 return (SMB_ADS_STAT_NOT_FOUND); 1572 } 1573 1574 rc = smb_ads_get_qstat(ah, res, avpair); 1575 /* free the search results */ 1576 (void) ldap_msgfree(res); 1577 return (rc); 1578 } 1579 1580 /* 1581 * smb_ads_find_computer 1582 * 1583 * Starts by searching for the system's AD computer object in the default 1584 * container (i.e. cn=Computers). If not found, searches the entire directory. 1585 * If found, 'dn' will be set to the distinguished name of the system's AD 1586 * computer object. 1587 */ 1588 static smb_ads_qstat_t 1589 smb_ads_find_computer(smb_ads_handle_t *ah, char *dn) 1590 { 1591 smb_ads_qstat_t stat; 1592 smb_ads_avpair_t avpair; 1593 1594 avpair.avp_attr = SMB_ADS_ATTR_DN; 1595 smb_ads_get_default_comp_container_dn(ah, dn, SMB_ADS_DN_MAX); 1596 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_ONELEVEL, 1597 dn); 1598 1599 if (stat == SMB_ADS_STAT_NOT_FOUND) { 1600 (void) strlcpy(dn, ah->domain_dn, SMB_ADS_DN_MAX); 1601 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, 1602 LDAP_SCOPE_SUBTREE, dn); 1603 } 1604 1605 if (stat == SMB_ADS_STAT_FOUND) { 1606 (void) strlcpy(dn, avpair.avp_val, SMB_ADS_DN_MAX); 1607 free(avpair.avp_val); 1608 } 1609 1610 return (stat); 1611 } 1612 1613 /* 1614 * smb_ads_update_computer_cntrl_attr 1615 * 1616 * Modify the user account control attribute of an existing computer 1617 * object on AD. 1618 * 1619 * Returns LDAP error code. 1620 */ 1621 static int 1622 smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *ah, int flags, char *dn) 1623 { 1624 LDAPMod *attrs[2]; 1625 char *ctl_val[2]; 1626 int ret = 0; 1627 char usrctl_buf[16]; 1628 1629 if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) != 0) 1630 return (LDAP_NO_MEMORY); 1631 1632 attrs[0]->mod_op = LDAP_MOD_REPLACE; 1633 attrs[0]->mod_type = SMB_ADS_ATTR_CTL; 1634 1635 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", flags); 1636 ctl_val[0] = usrctl_buf; 1637 ctl_val[1] = 0; 1638 attrs[0]->mod_values = ctl_val; 1639 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1640 syslog(LOG_NOTICE, "ldap_modify: %s", ldap_err2string(ret)); 1641 } 1642 1643 smb_ads_free_attr(attrs); 1644 return (ret); 1645 } 1646 1647 /* 1648 * smb_ads_lookup_computer_attr_kvno 1649 * 1650 * Lookup the value of the Kerberos version number attribute of the computer 1651 * account. 1652 */ 1653 static krb5_kvno 1654 smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) 1655 { 1656 smb_ads_avpair_t avpair; 1657 int kvno = 1; 1658 1659 avpair.avp_attr = SMB_ADS_ATTR_KVNO; 1660 if (smb_ads_lookup_computer_n_attr(ah, &avpair, 1661 LDAP_SCOPE_BASE, dn) == SMB_ADS_STAT_FOUND) { 1662 kvno = atoi(avpair.avp_val); 1663 free(avpair.avp_val); 1664 } 1665 1666 return (kvno); 1667 } 1668 1669 /* 1670 * smb_ads_join 1671 * 1672 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 1673 * provides the domain join using Kerberos Authentication, Keberos 1674 * Change & Set password, and LDAP protocols. Basically, AD join 1675 * operation would require the following tickets to be acquired for the 1676 * the user account that is provided for the domain join. 1677 * 1678 * 1) a Keberos TGT ticket, 1679 * 2) a ldap service ticket, and 1680 * 3) kadmin/changpw service ticket 1681 * 1682 * The ADS client first sends a ldap search request to find out whether 1683 * or not the workstation trust account already exists in the Active Directory. 1684 * The existing computer object for this workstation will be removed and 1685 * a new one will be added. The machine account password is randomly 1686 * generated and set for the newly created computer object using KPASSWD 1687 * protocol (See RFC 3244). Once the password is set, our ADS client 1688 * finalizes the machine account by modifying the user acount control 1689 * attribute of the computer object. Kerberos keys derived from the machine 1690 * account password will be stored locally in /etc/krb5/krb5.keytab file. 1691 * That would be needed while acquiring Kerberos TGT ticket for the host 1692 * principal after the domain join operation. 1693 */ 1694 smb_ads_status_t 1695 smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) 1696 { 1697 smb_ads_handle_t *ah = NULL; 1698 krb5_context ctx = NULL; 1699 krb5_principal *krb5princs = NULL; 1700 krb5_kvno kvno; 1701 boolean_t delete = B_TRUE; 1702 smb_ads_status_t rc; 1703 boolean_t new_acct; 1704 int dclevel, num, usrctl_flags = 0; 1705 smb_ads_qstat_t qstat; 1706 char dn[SMB_ADS_DN_MAX]; 1707 char tmpfile[] = SMBNS_KRB5_KEYTAB_TMP; 1708 int cnt, x; 1709 smb_krb5_pn_set_t spns; 1710 krb5_enctype *encptr; 1711 1712 rc = smb_ads_open_main(&ah, domain, user, usr_passwd); 1713 if (rc != 0) { 1714 smb_ccache_remove(SMB_CCACHE_PATH); 1715 return (rc); 1716 } 1717 1718 if ((dclevel = smb_ads_get_dc_level(ah)) == -1) { 1719 smb_ads_close(ah); 1720 smb_ccache_remove(SMB_CCACHE_PATH); 1721 return (SMB_ADJOIN_ERR_GET_DCLEVEL); 1722 } 1723 1724 qstat = smb_ads_find_computer(ah, dn); 1725 switch (qstat) { 1726 case SMB_ADS_STAT_FOUND: 1727 new_acct = B_FALSE; 1728 if (smb_ads_modify_computer(ah, dclevel, dn) != 0) { 1729 smb_ads_close(ah); 1730 smb_ccache_remove(SMB_CCACHE_PATH); 1731 return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT); 1732 } 1733 break; 1734 1735 case SMB_ADS_STAT_NOT_FOUND: 1736 new_acct = B_TRUE; 1737 smb_ads_get_default_comp_dn(ah, dn, SMB_ADS_DN_MAX); 1738 if (smb_ads_add_computer(ah, dclevel, dn) != 0) { 1739 smb_ads_close(ah); 1740 smb_ccache_remove(SMB_CCACHE_PATH); 1741 return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT); 1742 } 1743 break; 1744 1745 default: 1746 if (qstat == SMB_ADS_STAT_DUP) 1747 rc = SMB_ADJOIN_ERR_DUP_TRUST_ACCT; 1748 else 1749 rc = SMB_ADJOIN_ERR_TRUST_ACCT; 1750 smb_ads_close(ah); 1751 smb_ccache_remove(SMB_CCACHE_PATH); 1752 return (rc); 1753 } 1754 1755 if (smb_krb5_ctx_init(&ctx) != 0) { 1756 rc = SMB_ADJOIN_ERR_INIT_KRB_CTX; 1757 goto adjoin_cleanup; 1758 } 1759 1760 if (smb_krb5_get_pn_set(&spns, SMB_PN_KEYTAB_ENTRY, ah->domain) == 0) { 1761 rc = SMB_ADJOIN_ERR_GET_SPNS; 1762 goto adjoin_cleanup; 1763 } 1764 1765 if (smb_krb5_get_kprincs(ctx, spns.s_pns, spns.s_cnt, &krb5princs) 1766 != 0) { 1767 smb_krb5_free_pn_set(&spns); 1768 rc = SMB_ADJOIN_ERR_GET_SPNS; 1769 goto adjoin_cleanup; 1770 } 1771 1772 cnt = spns.s_cnt; 1773 smb_krb5_free_pn_set(&spns); 1774 1775 /* New machine_passwd was filled in by our caller. */ 1776 if (smb_krb5_setpwd(ctx, ah->domain, machine_passwd) != 0) { 1777 rc = SMB_ADJOIN_ERR_KSETPWD; 1778 goto adjoin_cleanup; 1779 } 1780 1781 kvno = smb_ads_lookup_computer_attr_kvno(ah, dn); 1782 1783 /* 1784 * Only members of Domain Admins and Enterprise Admins can set 1785 * the TRUSTED_FOR_DELEGATION userAccountControl flag. 1786 * Try to set this, but don't fail the join if we can't. 1787 * Look into just removing this... 1788 */ 1789 usrctl_flags = ( 1790 SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1791 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 1792 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 1793 set_ctl_again: 1794 x = smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn); 1795 if (x != LDAP_SUCCESS && (usrctl_flags & 1796 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION) != 0) { 1797 syslog(LOG_NOTICE, "Unable to set the " 1798 "TRUSTED_FOR_DELEGATION userAccountControl flag on the " 1799 "machine account in Active Directory. It may be necessary " 1800 "to set that via Active Directory administration."); 1801 usrctl_flags &= 1802 ~SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION; 1803 goto set_ctl_again; 1804 } 1805 if (x != LDAP_SUCCESS) { 1806 rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR; 1807 goto adjoin_cleanup; 1808 } 1809 1810 if (mktemp(tmpfile) == NULL) { 1811 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 1812 goto adjoin_cleanup; 1813 } 1814 1815 encptr = smb_ads_get_enctypes(dclevel, &num); 1816 if (smb_krb5_kt_populate(ctx, ah->domain, krb5princs, cnt, 1817 tmpfile, kvno, machine_passwd, encptr, num) != 0) { 1818 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 1819 goto adjoin_cleanup; 1820 } 1821 1822 delete = B_FALSE; 1823 rc = SMB_ADS_SUCCESS; 1824 1825 adjoin_cleanup: 1826 if (new_acct && delete) 1827 smb_ads_del_computer(ah, dn); 1828 1829 if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) { 1830 if (rc != SMB_ADJOIN_ERR_GET_SPNS) 1831 smb_krb5_free_kprincs(ctx, krb5princs, cnt); 1832 smb_krb5_ctx_fini(ctx); 1833 } 1834 1835 /* commit keytab file */ 1836 if (rc == SMB_ADS_SUCCESS) { 1837 if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) { 1838 (void) unlink(tmpfile); 1839 rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB; 1840 } 1841 } else { 1842 (void) unlink(tmpfile); 1843 } 1844 1845 smb_ads_close(ah); 1846 smb_ccache_remove(SMB_CCACHE_PATH); 1847 return (rc); 1848 } 1849 1850 struct xlate_table { 1851 int err; 1852 const char const *msg; 1853 }; 1854 1855 static const struct xlate_table 1856 adjoin_table[] = { 1857 { SMB_ADS_SUCCESS, "Success" }, 1858 { SMB_ADS_KRB5_INIT_CTX, 1859 "Failed creating a Kerberos context." }, 1860 { SMB_ADS_KRB5_CC_DEFAULT, 1861 "Failed to resolve default credential cache." }, 1862 { SMB_ADS_KRB5_PARSE_PRINCIPAL, 1863 "Failed parsing the user principal name." }, 1864 { SMB_ADS_KRB5_GET_INIT_CREDS_PW, 1865 "Failed getting initial credentials. (Wrong password?)" }, 1866 { SMB_ADS_KRB5_CC_INITIALIZE, 1867 "Failed initializing the credential cache." }, 1868 { SMB_ADS_KRB5_CC_STORE_CRED, 1869 "Failed to update the credential cache." }, 1870 { SMB_ADS_CANT_LOCATE_DC, 1871 "Failed to locate a domain controller." }, 1872 { SMB_ADS_LDAP_INIT, 1873 "Failed to create an LDAP handle." }, 1874 { SMB_ADS_LDAP_SETOPT, 1875 "Failed to set an LDAP option." }, 1876 { SMB_ADS_LDAP_SET_DOM, 1877 "Failed to set the LDAP handle DN." }, 1878 { SMB_ADS_LDAP_SASL_BIND, 1879 "Failed to bind the LDAP handle. " 1880 "Usually indicates an authentication problem." }, 1881 1882 { SMB_ADJOIN_ERR_GEN_PWD, 1883 "Failed to generate machine password." }, 1884 { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of " 1885 "the domain controller. The rootDSE attribute named " 1886 "\"domainControllerFunctionality\" is missing from the " 1887 "Active Directory." }, 1888 { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the " 1889 "workstation trust account." }, 1890 { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the " 1891 "workstation trust account." }, 1892 { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the " 1893 "workstation trust account because its name is already " 1894 "in use." }, 1895 { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the " 1896 "workstation trust account" }, 1897 { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos " 1898 "context." }, 1899 { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos " 1900 "principals." }, 1901 { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." }, 1902 { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify " 1903 "userAccountControl attribute of the workstation trust " 1904 "account." }, 1905 { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local " 1906 "keytab file (i.e /etc/krb5/krb5.keytab)." }, 1907 { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap " 1908 "configuration." }, 1909 { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap " 1910 "service." }, 1911 { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to " 1912 "local keytab file (i.e. /etc/krb5/krb5.keytab)." }, 1913 { SMB_ADJOIN_ERR_AUTH_NETLOGON, 1914 "Failed to authenticate using the new computer account." }, 1915 { SMB_ADJOIN_ERR_STORE_PROPS, 1916 "Failed to store computer account information locally." }, 1917 { 0, NULL } 1918 }; 1919 1920 /* 1921 * smb_ads_strerror 1922 * 1923 * Lookup an error message for the specific adjoin error code. 1924 */ 1925 const char * 1926 smb_ads_strerror(int err) 1927 { 1928 const struct xlate_table *xt; 1929 1930 if (err > 0 && err < SMB_ADS_ERRNO_GAP) 1931 return (strerror(err)); 1932 1933 for (xt = adjoin_table; xt->msg; xt++) 1934 if (xt->err == err) 1935 return (xt->msg); 1936 1937 return ("Unknown error code."); 1938 } 1939 1940 void 1941 smb_ads_log_errmsg(smb_ads_status_t err) 1942 { 1943 const char *s = smb_ads_strerror(err); 1944 syslog(LOG_NOTICE, "%s", s); 1945 } 1946 1947 1948 /* 1949 * smb_ads_lookup_msdcs 1950 * 1951 * If server argument is set, try to locate the specified DC. 1952 * If it is set to empty string, locate any DCs in the specified domain. 1953 * Returns the discovered DC via buf. 1954 * 1955 * fqdn - fully-qualified domain name 1956 * dci - the name and address of the found DC 1957 */ 1958 uint32_t 1959 smb_ads_lookup_msdcs(char *fqdn, smb_dcinfo_t *dci) 1960 { 1961 smb_ads_host_info_t *hinfo = NULL; 1962 char ipstr[INET6_ADDRSTRLEN]; 1963 1964 if (!fqdn || !dci) 1965 return (NT_STATUS_INTERNAL_ERROR); 1966 1967 ipstr[0] = '\0'; 1968 if ((hinfo = smb_ads_find_host(fqdn)) == NULL) 1969 return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); 1970 1971 (void) smb_inet_ntop(&hinfo->ipaddr, ipstr, 1972 SMB_IPSTRLEN(hinfo->ipaddr.a_family)); 1973 smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, ipstr); 1974 1975 (void) strlcpy(dci->dc_name, hinfo->name, sizeof (dci->dc_name)); 1976 dci->dc_addr = hinfo->ipaddr; 1977 1978 free(hinfo); 1979 return (NT_STATUS_SUCCESS); 1980 } 1981 1982 static krb5_enctype * 1983 smb_ads_get_enctypes(int dclevel, int *num) 1984 { 1985 krb5_enctype *encptr; 1986 1987 if (dclevel >= SMB_ADS_DCLEVEL_W2K8) { 1988 *num = sizeof (w2k8enctypes) / sizeof (krb5_enctype); 1989 encptr = w2k8enctypes; 1990 } else { 1991 *num = sizeof (pre_w2k8enctypes) / sizeof (krb5_enctype); 1992 encptr = pre_w2k8enctypes; 1993 } 1994 1995 return (encptr); 1996 } 1997