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 2019 Nexenta by DDN, 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 host->flags = dci->Flags; 480 481 (void) mutex_lock(&smb_ads_cached_host_mtx); 482 if (!smb_ads_cached_host_info) 483 smb_ads_cached_host_info = smb_ads_dup_host_info(host); 484 host = smb_ads_dup_host_info(smb_ads_cached_host_info); 485 (void) mutex_unlock(&smb_ads_cached_host_mtx); 486 487 out: 488 DsFreeDcInfo(dci); 489 return (host); 490 } 491 492 /* 493 * Return the number of dots in a string. 494 */ 495 static int 496 smb_ads_count_dots(const char *s) 497 { 498 int ndots = 0; 499 500 while (*s) { 501 if (*s++ == '.') 502 ndots++; 503 } 504 505 return (ndots); 506 } 507 508 /* 509 * Convert a domain name in dot notation to distinguished name format, 510 * for example: sun.com -> dc=sun,dc=com. 511 * 512 * Returns a pointer to an allocated buffer containing the distinguished 513 * name. 514 */ 515 static char * 516 smb_ads_convert_domain(const char *domain_name) 517 { 518 const char *s; 519 char *dn_name; 520 char buf[2]; 521 int ndots; 522 int len; 523 524 if (domain_name == NULL || *domain_name == 0) 525 return (NULL); 526 527 ndots = smb_ads_count_dots(domain_name); 528 ++ndots; 529 len = strlen(domain_name) + (ndots * SMB_ADS_DN_PREFIX_LEN) + 1; 530 531 if ((dn_name = malloc(len)) == NULL) 532 return (NULL); 533 534 bzero(dn_name, len); 535 (void) strlcpy(dn_name, "dc=", len); 536 537 buf[1] = '\0'; 538 s = domain_name; 539 540 while (*s) { 541 if (*s == '.') { 542 (void) strlcat(dn_name, ",dc=", len); 543 } else { 544 buf[0] = *s; 545 (void) strlcat(dn_name, buf, len); 546 } 547 ++s; 548 } 549 550 return (dn_name); 551 } 552 553 /* 554 * smb_ads_free_cached_host 555 * 556 * Free the memory use by the global smb_ads_cached_host_info & set it to NULL. 557 */ 558 static void 559 smb_ads_free_cached_host(void) 560 { 561 (void) mutex_lock(&smb_ads_cached_host_mtx); 562 if (smb_ads_cached_host_info) { 563 free(smb_ads_cached_host_info); 564 smb_ads_cached_host_info = NULL; 565 } 566 (void) mutex_unlock(&smb_ads_cached_host_mtx); 567 } 568 569 /* 570 * smb_ads_open 571 * Open a LDAP connection to an ADS server if the system is in domain mode. 572 * Acquire both Kerberos TGT and LDAP service tickets for the host principal. 573 * 574 * This function should only be called after the system is successfully joined 575 * to a domain. 576 */ 577 smb_ads_handle_t * 578 smb_ads_open(void) 579 { 580 char domain[MAXHOSTNAMELEN]; 581 smb_ads_handle_t *h; 582 smb_ads_status_t err; 583 584 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 585 return (NULL); 586 587 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 588 return (NULL); 589 590 err = smb_ads_open_main(&h, domain, NULL, NULL); 591 if (err != 0) { 592 smb_ads_log_errmsg(err); 593 return (NULL); 594 } 595 596 return (h); 597 } 598 599 static int 600 smb_ads_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) 601 { 602 NOTE(ARGUNUSED(ld, defaults)); 603 sasl_interact_t *interact; 604 605 if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE) 606 return (LDAP_PARAM_ERROR); 607 608 /* There should be no extra arguemnts for SASL/GSSAPI authentication */ 609 for (interact = prompts; interact->id != SASL_CB_LIST_END; 610 interact++) { 611 interact->result = NULL; 612 interact->len = 0; 613 } 614 return (LDAP_SUCCESS); 615 } 616 617 /* 618 * smb_ads_open_main 619 * Open a LDAP connection to an ADS server. 620 * If ADS is enabled and the administrative username, password, and 621 * ADS domain are defined then query DNS to find an ADS server if this is the 622 * very first call to this routine. After an ADS server is found then this 623 * server will be used everytime this routine is called until the system is 624 * rebooted or the ADS server becomes unavailable then an ADS server will 625 * be queried again. After the connection is made then an ADS handle 626 * is created to be returned. 627 * 628 * After the LDAP connection, the LDAP version will be set to 3 using 629 * ldap_set_option(). 630 * 631 * The LDAP connection is bound before the ADS handle is returned. 632 * Parameters: 633 * domain - fully-qualified domain name 634 * user - the user account for whom the Kerberos TGT ticket and ADS 635 * service tickets are acquired. 636 * password - password of the specified user 637 * 638 * Returns: 639 * NULL : can't connect to ADS server or other errors 640 * smb_ads_handle_t* : handle to ADS server 641 */ 642 static int 643 smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user, 644 char *password) 645 { 646 smb_ads_handle_t *ah; 647 LDAP *ld; 648 int version = 3; 649 smb_ads_host_info_t *ads_host = NULL; 650 int err, rc; 651 652 *hp = NULL; 653 654 if (user != NULL) { 655 err = smb_kinit(domain, user, password); 656 if (err != 0) 657 return (err); 658 user = NULL; 659 password = NULL; 660 } 661 662 ads_host = smb_ads_find_host(domain); 663 if (ads_host == NULL) 664 return (SMB_ADS_CANT_LOCATE_DC); 665 666 ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t)); 667 if (ah == NULL) { 668 free(ads_host); 669 return (ENOMEM); 670 } 671 672 (void) memset(ah, 0, sizeof (smb_ads_handle_t)); 673 674 if ((ld = ldap_init(ads_host->name, ads_host->port)) == NULL) { 675 syslog(LOG_ERR, "smbns: ldap_init failed"); 676 smb_ads_free_cached_host(); 677 free(ah); 678 free(ads_host); 679 return (SMB_ADS_LDAP_INIT); 680 } 681 682 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) 683 != LDAP_SUCCESS) { 684 smb_ads_free_cached_host(); 685 free(ah); 686 free(ads_host); 687 (void) ldap_unbind(ld); 688 return (SMB_ADS_LDAP_SETOPT); 689 } 690 691 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 692 ah->ld = ld; 693 ah->domain = strdup(domain); 694 695 if (ah->domain == NULL) { 696 smb_ads_close(ah); 697 free(ads_host); 698 return (SMB_ADS_LDAP_SETOPT); 699 } 700 701 /* 702 * ah->domain is often used for generating service principal name. 703 * Convert it to lower case for RFC 4120 section 6.2.1 conformance. 704 */ 705 (void) smb_strlwr(ah->domain); 706 ah->domain_dn = smb_ads_convert_domain(domain); 707 if (ah->domain_dn == NULL) { 708 smb_ads_close(ah); 709 free(ads_host); 710 return (SMB_ADS_LDAP_SET_DOM); 711 } 712 713 ah->hostname = strdup(ads_host->name); 714 if (ah->hostname == NULL) { 715 smb_ads_close(ah); 716 free(ads_host); 717 return (ENOMEM); 718 } 719 (void) mutex_lock(&smb_ads_cfg.c_mtx); 720 if (*smb_ads_cfg.c_site != '\0') { 721 if ((ah->site = strdup(smb_ads_cfg.c_site)) == NULL) { 722 smb_ads_close(ah); 723 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 724 free(ads_host); 725 return (ENOMEM); 726 } 727 } else { 728 ah->site = NULL; 729 } 730 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 731 732 rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL, 733 LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL); 734 if (rc != LDAP_SUCCESS) { 735 syslog(LOG_ERR, "smbns: ldap_sasl_..._bind_s failed (%s)", 736 ldap_err2string(rc)); 737 smb_ads_close(ah); 738 free(ads_host); 739 return (SMB_ADS_LDAP_SASL_BIND); 740 } 741 742 free(ads_host); 743 *hp = ah; 744 745 return (SMB_ADS_SUCCESS); 746 } 747 748 /* 749 * smb_ads_close 750 * Close connection to ADS server and free memory allocated for ADS handle. 751 * LDAP unbind is called here. 752 * Parameters: 753 * ah: handle to ADS server 754 * Returns: 755 * void 756 */ 757 void 758 smb_ads_close(smb_ads_handle_t *ah) 759 { 760 if (ah == NULL) 761 return; 762 /* close and free connection resources */ 763 if (ah->ld) 764 (void) ldap_unbind(ah->ld); 765 766 free(ah->domain); 767 free(ah->domain_dn); 768 free(ah->hostname); 769 free(ah->site); 770 free(ah); 771 } 772 773 /* 774 * smb_ads_alloc_attr 775 * 776 * Since the attrs is a null-terminated array, all elements 777 * in the array (except the last one) will point to allocated 778 * memory. 779 */ 780 static int 781 smb_ads_alloc_attr(LDAPMod *attrs[], int num) 782 { 783 int i; 784 785 bzero(attrs, num * sizeof (LDAPMod *)); 786 for (i = 0; i < (num - 1); i++) { 787 attrs[i] = (LDAPMod *)malloc(sizeof (LDAPMod)); 788 if (attrs[i] == NULL) { 789 smb_ads_free_attr(attrs); 790 return (-1); 791 } 792 } 793 794 return (0); 795 } 796 797 /* 798 * smb_ads_free_attr 799 * Free memory allocated when publishing a share. 800 * Parameters: 801 * attrs: an array of LDAPMod pointers 802 * Returns: 803 * None 804 */ 805 static void 806 smb_ads_free_attr(LDAPMod *attrs[]) 807 { 808 int i; 809 for (i = 0; attrs[i]; i++) { 810 free(attrs[i]); 811 } 812 } 813 814 /* 815 * Returns share DN in an allocated buffer. The format of the DN is 816 * cn=<sharename>,<container RDNs>,<domain DN> 817 * 818 * If the domain DN is not included in the container parameter, 819 * then it will be appended to create the share DN. 820 * 821 * The caller must free the allocated buffer. 822 */ 823 static char * 824 smb_ads_get_sharedn(const char *sharename, const char *container, 825 const char *domain_dn) 826 { 827 char *share_dn; 828 int rc, offset, container_len, domain_len; 829 boolean_t append_domain = B_TRUE; 830 831 container_len = strlen(container); 832 domain_len = strlen(domain_dn); 833 834 if (container_len >= domain_len) { 835 836 /* offset to last domain_len characters */ 837 offset = container_len - domain_len; 838 839 if (smb_strcasecmp(container + offset, 840 domain_dn, domain_len) == 0) 841 append_domain = B_FALSE; 842 } 843 844 if (append_domain) 845 rc = asprintf(&share_dn, "cn=%s,%s,%s", sharename, 846 container, domain_dn); 847 else 848 rc = asprintf(&share_dn, "cn=%s,%s", sharename, 849 container); 850 851 return ((rc == -1) ? NULL : share_dn); 852 } 853 854 /* 855 * smb_ads_add_share 856 * Call by smb_ads_publish_share to create share object in ADS. 857 * This routine specifies the attributes of an ADS LDAP share object. The first 858 * attribute and values define the type of ADS object, the share object. The 859 * second attribute and value define the UNC of the share data for the share 860 * object. The LDAP synchronous add command is used to add the object into ADS. 861 * The container location to add the object needs to specified. 862 * Parameters: 863 * ah : handle to ADS server 864 * adsShareName: name of share object to be created in ADS 865 * shareUNC : share name on NetForce 866 * adsContainer: location in ADS to create share object 867 * 868 * Returns: 869 * -1 : error 870 * 0 : success 871 */ 872 int 873 smb_ads_add_share(smb_ads_handle_t *ah, const char *adsShareName, 874 const char *unc_name, const char *adsContainer) 875 { 876 LDAPMod *attrs[SMB_ADS_SHARE_NUM_ATTR]; 877 int j = 0; 878 char *share_dn; 879 int ret; 880 char *unc_names[] = {(char *)unc_name, NULL}; 881 882 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 883 ah->domain_dn)) == NULL) 884 return (-1); 885 886 if (smb_ads_alloc_attr(attrs, SMB_ADS_SHARE_NUM_ATTR) != 0) { 887 free(share_dn); 888 return (-1); 889 } 890 891 attrs[j]->mod_op = LDAP_MOD_ADD; 892 attrs[j]->mod_type = "objectClass"; 893 attrs[j]->mod_values = smb_ads_share_objcls; 894 895 attrs[++j]->mod_op = LDAP_MOD_ADD; 896 attrs[j]->mod_type = "uNCName"; 897 attrs[j]->mod_values = unc_names; 898 899 if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) { 900 if (ret == LDAP_NO_SUCH_OBJECT) { 901 syslog(LOG_ERR, "Failed to publish share %s in" \ 902 " AD. Container does not exist: %s.\n", 903 adsShareName, share_dn); 904 905 } else { 906 syslog(LOG_ERR, "Failed to publish share %s in" \ 907 " AD: %s (%s).\n", adsShareName, share_dn, 908 ldap_err2string(ret)); 909 } 910 smb_ads_free_attr(attrs); 911 free(share_dn); 912 return (ret); 913 } 914 free(share_dn); 915 smb_ads_free_attr(attrs); 916 917 return (0); 918 } 919 920 /* 921 * smb_ads_del_share 922 * Call by smb_ads_remove_share to remove share object from ADS. The container 923 * location to remove the object needs to specified. The LDAP synchronous 924 * delete command is used. 925 * Parameters: 926 * ah : handle to ADS server 927 * adsShareName: name of share object in ADS to be removed 928 * adsContainer: location of share object in ADS 929 * Returns: 930 * -1 : error 931 * 0 : success 932 */ 933 static int 934 smb_ads_del_share(smb_ads_handle_t *ah, const char *adsShareName, 935 const char *adsContainer) 936 { 937 char *share_dn; 938 int ret; 939 940 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 941 ah->domain_dn)) == NULL) 942 return (-1); 943 944 if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { 945 smb_tracef("ldap_delete: %s", ldap_err2string(ret)); 946 free(share_dn); 947 return (-1); 948 } 949 free(share_dn); 950 951 return (0); 952 } 953 954 955 /* 956 * smb_ads_escape_search_filter_chars 957 * 958 * This routine will escape the special characters found in a string 959 * that will later be passed to the ldap search filter. 960 * 961 * RFC 1960 - A String Representation of LDAP Search Filters 962 * 3. String Search Filter Definition 963 * If a value must contain one of the characters '*' OR '(' OR ')', 964 * these characters 965 * should be escaped by preceding them with the backslash '\' character. 966 * 967 * RFC 2252 - LDAP Attribute Syntax Definitions 968 * a backslash quoting mechanism is used to escape 969 * the following separator symbol character (such as "'", "$" or "#") if 970 * it should occur in that string. 971 */ 972 static int 973 smb_ads_escape_search_filter_chars(const char *src, char *dst) 974 { 975 int avail = SMB_ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ 976 977 if (src == NULL || dst == NULL) 978 return (-1); 979 980 while (*src) { 981 if (!avail) { 982 *dst = 0; 983 return (-1); 984 } 985 986 switch (*src) { 987 case '\\': 988 case '\'': 989 case '$': 990 case '#': 991 case '*': 992 case '(': 993 case ')': 994 *dst++ = '\\'; 995 avail--; 996 /* fall through */ 997 998 default: 999 *dst++ = *src++; 1000 avail--; 1001 } 1002 } 1003 1004 *dst = 0; 1005 1006 return (0); 1007 } 1008 1009 /* 1010 * smb_ads_lookup_share 1011 * The search filter is set to search for a specific share name in the 1012 * specified ADS container. The LDSAP synchronous search command is used. 1013 * Parameters: 1014 * ah : handle to ADS server 1015 * adsShareName: name of share object in ADS to be searched 1016 * adsContainer: location of share object in ADS 1017 * Returns: 1018 * -1 : error 1019 * 0 : not found 1020 * 1 : found 1021 */ 1022 int 1023 smb_ads_lookup_share(smb_ads_handle_t *ah, const char *adsShareName, 1024 const char *adsContainer, char *unc_name) 1025 { 1026 char *attrs[4], filter[SMB_ADS_MAXBUFLEN]; 1027 char *share_dn; 1028 int ret; 1029 LDAPMessage *res; 1030 char tmpbuf[SMB_ADS_MAXBUFLEN]; 1031 1032 if (adsShareName == NULL || adsContainer == NULL) 1033 return (-1); 1034 1035 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 1036 ah->domain_dn)) == NULL) 1037 return (-1); 1038 1039 res = NULL; 1040 attrs[0] = "cn"; 1041 attrs[1] = "objectClass"; 1042 attrs[2] = "uNCName"; 1043 attrs[3] = NULL; 1044 1045 if (smb_ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { 1046 free(share_dn); 1047 return (-1); 1048 } 1049 1050 (void) snprintf(filter, sizeof (filter), 1051 "(&(objectClass=volume)(uNCName=%s))", tmpbuf); 1052 1053 if ((ret = ldap_search_s(ah->ld, share_dn, 1054 LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { 1055 if (ret != LDAP_NO_SUCH_OBJECT) 1056 smb_tracef("%s: ldap_search: %s", share_dn, 1057 ldap_err2string(ret)); 1058 1059 (void) ldap_msgfree(res); 1060 free(share_dn); 1061 return (0); 1062 } 1063 1064 (void) free(share_dn); 1065 1066 /* no match is found */ 1067 if (ldap_count_entries(ah->ld, res) == 0) { 1068 (void) ldap_msgfree(res); 1069 return (0); 1070 } 1071 1072 /* free the search results */ 1073 (void) ldap_msgfree(res); 1074 1075 return (1); 1076 } 1077 1078 /* 1079 * smb_ads_publish_share 1080 * Publish share into ADS. If a share name already exist in ADS in the same 1081 * container then the existing share object is removed before adding the new 1082 * share object. 1083 * Parameters: 1084 * ah : handle return from smb_ads_open 1085 * adsShareName: name of share to be added to ADS directory 1086 * shareUNC : name of share on client, can be NULL to use the same name 1087 * as adsShareName 1088 * adsContainer: location for share to be added in ADS directory, ie 1089 * ou=share_folder 1090 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR 1091 * to use host ip addr for UNC. 1092 * Returns: 1093 * -1 : error 1094 * 0 : success 1095 */ 1096 int 1097 smb_ads_publish_share(smb_ads_handle_t *ah, const char *adsShareName, 1098 const char *shareUNC, const char *adsContainer, const char *hostname) 1099 { 1100 int ret; 1101 char unc_name[SMB_ADS_MAXBUFLEN]; 1102 1103 if (adsShareName == NULL || adsContainer == NULL) 1104 return (-1); 1105 1106 if (shareUNC == 0 || *shareUNC == 0) 1107 shareUNC = adsShareName; 1108 1109 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1110 hostname, shareUNC) < 0) 1111 return (-1); 1112 1113 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1114 1115 switch (ret) { 1116 case 1: 1117 (void) smb_ads_del_share(ah, adsShareName, adsContainer); 1118 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1119 adsContainer); 1120 break; 1121 1122 case 0: 1123 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1124 adsContainer); 1125 if (ret == LDAP_ALREADY_EXISTS) 1126 ret = -1; 1127 1128 break; 1129 1130 case -1: 1131 default: 1132 /* return with error code */ 1133 ret = -1; 1134 } 1135 1136 return (ret); 1137 } 1138 1139 /* 1140 * smb_ads_remove_share 1141 * Remove share from ADS. A search is done first before explicitly removing 1142 * the share. 1143 * Parameters: 1144 * ah : handle return from smb_ads_open 1145 * adsShareName: name of share to be removed from ADS directory 1146 * adsContainer: location for share to be removed from ADS directory, ie 1147 * ou=share_folder 1148 * Returns: 1149 * -1 : error 1150 * 0 : success 1151 */ 1152 int 1153 smb_ads_remove_share(smb_ads_handle_t *ah, const char *adsShareName, 1154 const char *shareUNC, const char *adsContainer, const char *hostname) 1155 { 1156 int ret; 1157 char unc_name[SMB_ADS_MAXBUFLEN]; 1158 1159 if (adsShareName == NULL || adsContainer == NULL) 1160 return (-1); 1161 if (shareUNC == 0 || *shareUNC == 0) 1162 shareUNC = adsShareName; 1163 1164 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1165 hostname, shareUNC) < 0) 1166 return (-1); 1167 1168 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1169 if (ret == 0) 1170 return (0); 1171 if (ret == -1) 1172 return (-1); 1173 1174 return (smb_ads_del_share(ah, adsShareName, adsContainer)); 1175 } 1176 1177 /* 1178 * smb_ads_get_default_comp_container_dn 1179 * 1180 * Build the distinguished name for the default computer conatiner (i.e. the 1181 * pre-defined Computers container). 1182 */ 1183 static void 1184 smb_ads_get_default_comp_container_dn(smb_ads_handle_t *ah, char *buf, 1185 size_t buflen) 1186 { 1187 (void) snprintf(buf, buflen, "cn=%s,%s", SMB_ADS_COMPUTERS_CN, 1188 ah->domain_dn); 1189 } 1190 1191 /* 1192 * smb_ads_get_default_comp_dn 1193 * 1194 * Build the distinguished name for this system. 1195 */ 1196 static void 1197 smb_ads_get_default_comp_dn(smb_ads_handle_t *ah, char *buf, size_t buflen) 1198 { 1199 char nbname[NETBIOS_NAME_SZ]; 1200 char container_dn[SMB_ADS_DN_MAX]; 1201 1202 (void) smb_getnetbiosname(nbname, sizeof (nbname)); 1203 smb_ads_get_default_comp_container_dn(ah, container_dn, SMB_ADS_DN_MAX); 1204 (void) snprintf(buf, buflen, "cn=%s,%s", nbname, container_dn); 1205 } 1206 1207 /* 1208 * smb_ads_add_computer 1209 * 1210 * Returns 0 upon success. Otherwise, returns -1. 1211 */ 1212 static int 1213 smb_ads_add_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1214 { 1215 return (smb_ads_computer_op(ah, LDAP_MOD_ADD, dclevel, dn)); 1216 } 1217 1218 /* 1219 * smb_ads_modify_computer 1220 * 1221 * Returns 0 upon success. Otherwise, returns -1. 1222 */ 1223 static int 1224 smb_ads_modify_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1225 { 1226 return (smb_ads_computer_op(ah, LDAP_MOD_REPLACE, dclevel, dn)); 1227 } 1228 1229 /* 1230 * smb_ads_get_dc_level 1231 * 1232 * Returns the functional level of the DC upon success. 1233 * Otherwise, -1 is returned. 1234 */ 1235 static int 1236 smb_ads_get_dc_level(smb_ads_handle_t *ah) 1237 { 1238 LDAPMessage *res, *entry; 1239 char *attr[2]; 1240 char **vals; 1241 int rc = -1; 1242 1243 res = NULL; 1244 attr[0] = SMB_ADS_ATTR_DCLEVEL; 1245 attr[1] = NULL; 1246 if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, 1247 0, &res) != LDAP_SUCCESS) { 1248 (void) ldap_msgfree(res); 1249 return (-1); 1250 } 1251 1252 /* no match for the specified attribute is found */ 1253 if (ldap_count_entries(ah->ld, res) == 0) { 1254 (void) ldap_msgfree(res); 1255 return (-1); 1256 } 1257 1258 entry = ldap_first_entry(ah->ld, res); 1259 if (entry) { 1260 if ((vals = ldap_get_values(ah->ld, entry, 1261 SMB_ADS_ATTR_DCLEVEL)) == NULL) { 1262 /* 1263 * Observed the values aren't populated 1264 * by the Windows 2000 server. 1265 */ 1266 (void) ldap_msgfree(res); 1267 return (SMB_ADS_DCLEVEL_W2K); 1268 } 1269 1270 if (vals[0] != NULL) 1271 rc = atoi(vals[0]); 1272 1273 ldap_value_free(vals); 1274 } 1275 1276 (void) ldap_msgfree(res); 1277 return (rc); 1278 } 1279 1280 /* 1281 * The fully-qualified hostname returned by this function is often used for 1282 * constructing service principal name. Return the fully-qualified hostname 1283 * in lower case for RFC 4120 section 6.2.1 conformance. 1284 */ 1285 static int 1286 smb_ads_getfqhostname(smb_ads_handle_t *ah, char *fqhost, int len) 1287 { 1288 if (smb_gethostname(fqhost, len, SMB_CASE_LOWER) != 0) 1289 return (-1); 1290 1291 (void) strlcat(fqhost, ".", len); 1292 (void) strlcat(fqhost, ah->domain, len); 1293 1294 return (0); 1295 } 1296 1297 static int 1298 smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn) 1299 { 1300 LDAPMod *attrs[SMB_ADS_COMPUTER_NUM_ATTR]; 1301 char *sam_val[2]; 1302 char *ctl_val[2], *fqh_val[2]; 1303 char *encrypt_val[2]; 1304 int j = -1; 1305 int ret, usrctl_flags = 0; 1306 char sam_acct[SMB_SAMACCT_MAXLEN]; 1307 char fqhost[MAXHOSTNAMELEN]; 1308 char usrctl_buf[16]; 1309 char encrypt_buf[16]; 1310 int max; 1311 smb_krb5_pn_set_t spn, upn; 1312 1313 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 1314 return (-1); 1315 1316 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1317 return (-1); 1318 1319 /* The SPN attribute is multi-valued and must be 1 or greater */ 1320 if (smb_krb5_get_pn_set(&spn, SMB_PN_SPN_ATTR, ah->domain) == 0) 1321 return (-1); 1322 1323 /* The UPN attribute is single-valued and cannot be zero */ 1324 if (smb_krb5_get_pn_set(&upn, SMB_PN_UPN_ATTR, ah->domain) != 1) { 1325 smb_krb5_free_pn_set(&spn); 1326 smb_krb5_free_pn_set(&upn); 1327 return (-1); 1328 } 1329 1330 max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)) 1331 - (dclevel >= SMB_ADS_DCLEVEL_W2K8 ? 0 : 1); 1332 1333 if (smb_ads_alloc_attr(attrs, max) != 0) { 1334 smb_krb5_free_pn_set(&spn); 1335 smb_krb5_free_pn_set(&upn); 1336 return (-1); 1337 } 1338 1339 /* objectClass attribute is not modifiable. */ 1340 if (op == LDAP_MOD_ADD) { 1341 attrs[++j]->mod_op = op; 1342 attrs[j]->mod_type = "objectClass"; 1343 attrs[j]->mod_values = smb_ads_computer_objcls; 1344 } 1345 1346 attrs[++j]->mod_op = op; 1347 attrs[j]->mod_type = SMB_ADS_ATTR_SAMACCT; 1348 sam_val[0] = sam_acct; 1349 sam_val[1] = 0; 1350 attrs[j]->mod_values = sam_val; 1351 1352 attrs[++j]->mod_op = op; 1353 attrs[j]->mod_type = SMB_ADS_ATTR_UPN; 1354 attrs[j]->mod_values = upn.s_pns; 1355 1356 attrs[++j]->mod_op = op; 1357 attrs[j]->mod_type = SMB_ADS_ATTR_SPN; 1358 attrs[j]->mod_values = spn.s_pns; 1359 1360 attrs[++j]->mod_op = op; 1361 attrs[j]->mod_type = SMB_ADS_ATTR_CTL; 1362 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1363 SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1364 SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1365 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1366 ctl_val[0] = usrctl_buf; 1367 ctl_val[1] = 0; 1368 attrs[j]->mod_values = ctl_val; 1369 1370 attrs[++j]->mod_op = op; 1371 attrs[j]->mod_type = SMB_ADS_ATTR_DNSHOST; 1372 fqh_val[0] = fqhost; 1373 fqh_val[1] = 0; 1374 attrs[j]->mod_values = fqh_val; 1375 1376 /* enctypes support starting in Windows Server 2008 */ 1377 if (dclevel > SMB_ADS_DCLEVEL_W2K3) { 1378 attrs[++j]->mod_op = op; 1379 attrs[j]->mod_type = SMB_ADS_ATTR_ENCTYPES; 1380 (void) snprintf(encrypt_buf, sizeof (encrypt_buf), "%d", 1381 SMB_ADS_ENC_AES256 + SMB_ADS_ENC_AES128 + SMB_ADS_ENC_RC4 + 1382 SMB_ADS_ENC_DES_MD5 + SMB_ADS_ENC_DES_CRC); 1383 encrypt_val[0] = encrypt_buf; 1384 encrypt_val[1] = 0; 1385 attrs[j]->mod_values = encrypt_val; 1386 } 1387 1388 switch (op) { 1389 case LDAP_MOD_ADD: 1390 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1391 syslog(LOG_NOTICE, "ldap_add: %s", 1392 ldap_err2string(ret)); 1393 ret = -1; 1394 } 1395 break; 1396 1397 case LDAP_MOD_REPLACE: 1398 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1399 syslog(LOG_NOTICE, "ldap_modify: %s", 1400 ldap_err2string(ret)); 1401 ret = -1; 1402 } 1403 break; 1404 1405 default: 1406 ret = -1; 1407 1408 } 1409 1410 smb_ads_free_attr(attrs); 1411 smb_krb5_free_pn_set(&spn); 1412 smb_krb5_free_pn_set(&upn); 1413 1414 return (ret); 1415 } 1416 1417 /* 1418 * Delete an ADS computer account. 1419 */ 1420 static void 1421 smb_ads_del_computer(smb_ads_handle_t *ah, char *dn) 1422 { 1423 int rc; 1424 1425 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) 1426 smb_tracef("ldap_delete: %s", ldap_err2string(rc)); 1427 } 1428 1429 /* 1430 * Gets the value of the given attribute. 1431 */ 1432 static smb_ads_qstat_t 1433 smb_ads_getattr(LDAP *ld, LDAPMessage *entry, smb_ads_avpair_t *avpair) 1434 { 1435 char **vals; 1436 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 1437 1438 assert(avpair); 1439 avpair->avp_val = NULL; 1440 vals = ldap_get_values(ld, entry, avpair->avp_attr); 1441 if (!vals) 1442 return (SMB_ADS_STAT_NOT_FOUND); 1443 1444 if (!vals[0]) { 1445 ldap_value_free(vals); 1446 return (SMB_ADS_STAT_NOT_FOUND); 1447 } 1448 1449 avpair->avp_val = strdup(vals[0]); 1450 if (!avpair->avp_val) 1451 rc = SMB_ADS_STAT_ERR; 1452 1453 ldap_value_free(vals); 1454 return (rc); 1455 } 1456 1457 /* 1458 * Process query's result. 1459 */ 1460 static smb_ads_qstat_t 1461 smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, 1462 smb_ads_avpair_t *avpair) 1463 { 1464 char fqhost[MAXHOSTNAMELEN]; 1465 smb_ads_avpair_t dnshost_avp; 1466 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 1467 LDAPMessage *entry; 1468 1469 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1470 return (SMB_ADS_STAT_ERR); 1471 1472 if (ldap_count_entries(ah->ld, res) == 0) 1473 return (SMB_ADS_STAT_NOT_FOUND); 1474 1475 if ((entry = ldap_first_entry(ah->ld, res)) == NULL) 1476 return (SMB_ADS_STAT_ERR); 1477 1478 dnshost_avp.avp_attr = SMB_ADS_ATTR_DNSHOST; 1479 rc = smb_ads_getattr(ah->ld, entry, &dnshost_avp); 1480 1481 switch (rc) { 1482 case SMB_ADS_STAT_FOUND: 1483 /* 1484 * Returns SMB_ADS_STAT_DUP to avoid overwriting 1485 * the computer account of another system whose 1486 * NetBIOS name collides with that of the current 1487 * system. 1488 */ 1489 if (strcasecmp(dnshost_avp.avp_val, fqhost)) 1490 rc = SMB_ADS_STAT_DUP; 1491 1492 free(dnshost_avp.avp_val); 1493 break; 1494 1495 case SMB_ADS_STAT_NOT_FOUND: 1496 /* 1497 * Pre-created computer account doesn't have 1498 * the dNSHostname attribute. It's been observed 1499 * that the dNSHostname attribute is only set after 1500 * a successful domain join. 1501 * Returns SMB_ADS_STAT_FOUND as the account is 1502 * pre-created for the current system. 1503 */ 1504 rc = SMB_ADS_STAT_FOUND; 1505 break; 1506 1507 default: 1508 break; 1509 } 1510 1511 if (rc != SMB_ADS_STAT_FOUND) 1512 return (rc); 1513 1514 if (avpair) 1515 rc = smb_ads_getattr(ah->ld, entry, avpair); 1516 1517 return (rc); 1518 1519 } 1520 1521 /* 1522 * smb_ads_lookup_computer_n_attr 1523 * 1524 * If avpair is NULL, checks the status of the specified computer account. 1525 * Otherwise, looks up the value of the specified computer account's attribute. 1526 * If found, the value field of the avpair will be allocated and set. The 1527 * caller should free the allocated buffer. 1528 * 1529 * Return: 1530 * SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is 1531 * found. 1532 * SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute 1533 * is not found. 1534 * SMB_ADS_STAT_DUP - if the computer account is already used by other systems 1535 * in the AD. This could happen if the hostname of multiple 1536 * systems resolved to the same NetBIOS name. 1537 * SMB_ADS_STAT_ERR - any failure. 1538 */ 1539 static smb_ads_qstat_t 1540 smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair, 1541 int scope, char *dn) 1542 { 1543 char *attrs[3], filter[SMB_ADS_MAXBUFLEN]; 1544 LDAPMessage *res; 1545 char sam_acct[SMB_SAMACCT_MAXLEN], sam_acct2[SMB_SAMACCT_MAXLEN]; 1546 smb_ads_qstat_t rc; 1547 1548 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 1549 return (SMB_ADS_STAT_ERR); 1550 1551 res = NULL; 1552 attrs[0] = SMB_ADS_ATTR_DNSHOST; 1553 attrs[1] = NULL; 1554 attrs[2] = NULL; 1555 1556 if (avpair) { 1557 if (!avpair->avp_attr) 1558 return (SMB_ADS_STAT_ERR); 1559 1560 attrs[1] = avpair->avp_attr; 1561 } 1562 1563 if (smb_ads_escape_search_filter_chars(sam_acct, sam_acct2) != 0) 1564 return (SMB_ADS_STAT_ERR); 1565 1566 (void) snprintf(filter, sizeof (filter), 1567 "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT, 1568 sam_acct2); 1569 1570 if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, 1571 &res) != LDAP_SUCCESS) { 1572 (void) ldap_msgfree(res); 1573 return (SMB_ADS_STAT_NOT_FOUND); 1574 } 1575 1576 rc = smb_ads_get_qstat(ah, res, avpair); 1577 /* free the search results */ 1578 (void) ldap_msgfree(res); 1579 return (rc); 1580 } 1581 1582 /* 1583 * smb_ads_find_computer 1584 * 1585 * Starts by searching for the system's AD computer object in the default 1586 * container (i.e. cn=Computers). If not found, searches the entire directory. 1587 * If found, 'dn' will be set to the distinguished name of the system's AD 1588 * computer object. 1589 */ 1590 static smb_ads_qstat_t 1591 smb_ads_find_computer(smb_ads_handle_t *ah, char *dn) 1592 { 1593 smb_ads_qstat_t stat; 1594 smb_ads_avpair_t avpair; 1595 1596 avpair.avp_attr = SMB_ADS_ATTR_DN; 1597 smb_ads_get_default_comp_container_dn(ah, dn, SMB_ADS_DN_MAX); 1598 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_ONELEVEL, 1599 dn); 1600 1601 if (stat == SMB_ADS_STAT_NOT_FOUND) { 1602 (void) strlcpy(dn, ah->domain_dn, SMB_ADS_DN_MAX); 1603 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, 1604 LDAP_SCOPE_SUBTREE, dn); 1605 } 1606 1607 if (stat == SMB_ADS_STAT_FOUND) { 1608 (void) strlcpy(dn, avpair.avp_val, SMB_ADS_DN_MAX); 1609 free(avpair.avp_val); 1610 } 1611 1612 return (stat); 1613 } 1614 1615 /* 1616 * smb_ads_update_computer_cntrl_attr 1617 * 1618 * Modify the user account control attribute of an existing computer 1619 * object on AD. 1620 * 1621 * Returns LDAP error code. 1622 */ 1623 static int 1624 smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *ah, int flags, char *dn) 1625 { 1626 LDAPMod *attrs[2]; 1627 char *ctl_val[2]; 1628 int ret = 0; 1629 char usrctl_buf[16]; 1630 1631 if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) != 0) 1632 return (LDAP_NO_MEMORY); 1633 1634 attrs[0]->mod_op = LDAP_MOD_REPLACE; 1635 attrs[0]->mod_type = SMB_ADS_ATTR_CTL; 1636 1637 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", flags); 1638 ctl_val[0] = usrctl_buf; 1639 ctl_val[1] = 0; 1640 attrs[0]->mod_values = ctl_val; 1641 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1642 syslog(LOG_NOTICE, "ldap_modify: %s", ldap_err2string(ret)); 1643 } 1644 1645 smb_ads_free_attr(attrs); 1646 return (ret); 1647 } 1648 1649 /* 1650 * smb_ads_lookup_computer_attr_kvno 1651 * 1652 * Lookup the value of the Kerberos version number attribute of the computer 1653 * account. 1654 */ 1655 static krb5_kvno 1656 smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) 1657 { 1658 smb_ads_avpair_t avpair; 1659 int kvno = 1; 1660 1661 avpair.avp_attr = SMB_ADS_ATTR_KVNO; 1662 if (smb_ads_lookup_computer_n_attr(ah, &avpair, 1663 LDAP_SCOPE_BASE, dn) == SMB_ADS_STAT_FOUND) { 1664 kvno = atoi(avpair.avp_val); 1665 free(avpair.avp_val); 1666 } 1667 1668 return (kvno); 1669 } 1670 1671 /* 1672 * smb_ads_join 1673 * 1674 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 1675 * provides the domain join using Kerberos Authentication, Keberos 1676 * Change & Set password, and LDAP protocols. Basically, AD join 1677 * operation would require the following tickets to be acquired for the 1678 * the user account that is provided for the domain join. 1679 * 1680 * 1) a Keberos TGT ticket, 1681 * 2) a ldap service ticket, and 1682 * 3) kadmin/changpw service ticket 1683 * 1684 * The ADS client first sends a ldap search request to find out whether 1685 * or not the workstation trust account already exists in the Active Directory. 1686 * The existing computer object for this workstation will be removed and 1687 * a new one will be added. The machine account password is randomly 1688 * generated and set for the newly created computer object using KPASSWD 1689 * protocol (See RFC 3244). Once the password is set, our ADS client 1690 * finalizes the machine account by modifying the user acount control 1691 * attribute of the computer object. Kerberos keys derived from the machine 1692 * account password will be stored locally in /etc/krb5/krb5.keytab file. 1693 * That would be needed while acquiring Kerberos TGT ticket for the host 1694 * principal after the domain join operation. 1695 */ 1696 smb_ads_status_t 1697 smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) 1698 { 1699 smb_ads_handle_t *ah = NULL; 1700 krb5_context ctx = NULL; 1701 krb5_principal *krb5princs = NULL; 1702 krb5_kvno kvno; 1703 boolean_t delete = B_TRUE; 1704 smb_ads_status_t rc; 1705 boolean_t new_acct; 1706 int dclevel, num, usrctl_flags = 0; 1707 smb_ads_qstat_t qstat; 1708 char dn[SMB_ADS_DN_MAX]; 1709 char tmpfile[] = SMBNS_KRB5_KEYTAB_TMP; 1710 int cnt, x; 1711 smb_krb5_pn_set_t spns; 1712 krb5_enctype *encptr; 1713 1714 rc = smb_ads_open_main(&ah, domain, user, usr_passwd); 1715 if (rc != 0) { 1716 smb_ccache_remove(SMB_CCACHE_PATH); 1717 return (rc); 1718 } 1719 1720 if ((dclevel = smb_ads_get_dc_level(ah)) == -1) { 1721 smb_ads_close(ah); 1722 smb_ccache_remove(SMB_CCACHE_PATH); 1723 return (SMB_ADJOIN_ERR_GET_DCLEVEL); 1724 } 1725 1726 qstat = smb_ads_find_computer(ah, dn); 1727 switch (qstat) { 1728 case SMB_ADS_STAT_FOUND: 1729 new_acct = B_FALSE; 1730 if (smb_ads_modify_computer(ah, dclevel, dn) != 0) { 1731 smb_ads_close(ah); 1732 smb_ccache_remove(SMB_CCACHE_PATH); 1733 return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT); 1734 } 1735 break; 1736 1737 case SMB_ADS_STAT_NOT_FOUND: 1738 new_acct = B_TRUE; 1739 smb_ads_get_default_comp_dn(ah, dn, SMB_ADS_DN_MAX); 1740 if (smb_ads_add_computer(ah, dclevel, dn) != 0) { 1741 smb_ads_close(ah); 1742 smb_ccache_remove(SMB_CCACHE_PATH); 1743 return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT); 1744 } 1745 break; 1746 1747 default: 1748 if (qstat == SMB_ADS_STAT_DUP) 1749 rc = SMB_ADJOIN_ERR_DUP_TRUST_ACCT; 1750 else 1751 rc = SMB_ADJOIN_ERR_TRUST_ACCT; 1752 smb_ads_close(ah); 1753 smb_ccache_remove(SMB_CCACHE_PATH); 1754 return (rc); 1755 } 1756 1757 if (smb_krb5_ctx_init(&ctx) != 0) { 1758 rc = SMB_ADJOIN_ERR_INIT_KRB_CTX; 1759 goto adjoin_cleanup; 1760 } 1761 1762 if (smb_krb5_get_pn_set(&spns, SMB_PN_KEYTAB_ENTRY, ah->domain) == 0) { 1763 rc = SMB_ADJOIN_ERR_GET_SPNS; 1764 goto adjoin_cleanup; 1765 } 1766 1767 if (smb_krb5_get_kprincs(ctx, spns.s_pns, spns.s_cnt, &krb5princs) 1768 != 0) { 1769 smb_krb5_free_pn_set(&spns); 1770 rc = SMB_ADJOIN_ERR_GET_SPNS; 1771 goto adjoin_cleanup; 1772 } 1773 1774 cnt = spns.s_cnt; 1775 smb_krb5_free_pn_set(&spns); 1776 1777 /* New machine_passwd was filled in by our caller. */ 1778 if (smb_krb5_setpwd(ctx, ah->domain, machine_passwd) != 0) { 1779 rc = SMB_ADJOIN_ERR_KSETPWD; 1780 goto adjoin_cleanup; 1781 } 1782 1783 kvno = smb_ads_lookup_computer_attr_kvno(ah, dn); 1784 1785 /* 1786 * Only members of Domain Admins and Enterprise Admins can set 1787 * the TRUSTED_FOR_DELEGATION userAccountControl flag. 1788 * Try to set this, but don't fail the join if we can't. 1789 * Look into just removing this... 1790 */ 1791 usrctl_flags = ( 1792 SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1793 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 1794 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 1795 set_ctl_again: 1796 x = smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn); 1797 if (x != LDAP_SUCCESS && (usrctl_flags & 1798 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION) != 0) { 1799 syslog(LOG_NOTICE, "Unable to set the " 1800 "TRUSTED_FOR_DELEGATION userAccountControl flag on the " 1801 "machine account in Active Directory. It may be necessary " 1802 "to set that via Active Directory administration."); 1803 usrctl_flags &= 1804 ~SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION; 1805 goto set_ctl_again; 1806 } 1807 if (x != LDAP_SUCCESS) { 1808 rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR; 1809 goto adjoin_cleanup; 1810 } 1811 1812 if (mktemp(tmpfile) == NULL) { 1813 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 1814 goto adjoin_cleanup; 1815 } 1816 1817 encptr = smb_ads_get_enctypes(dclevel, &num); 1818 if (smb_krb5_kt_populate(ctx, ah->domain, krb5princs, cnt, 1819 tmpfile, kvno, machine_passwd, encptr, num) != 0) { 1820 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 1821 goto adjoin_cleanup; 1822 } 1823 1824 delete = B_FALSE; 1825 rc = SMB_ADS_SUCCESS; 1826 1827 adjoin_cleanup: 1828 if (new_acct && delete) 1829 smb_ads_del_computer(ah, dn); 1830 1831 if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) { 1832 if (rc != SMB_ADJOIN_ERR_GET_SPNS) 1833 smb_krb5_free_kprincs(ctx, krb5princs, cnt); 1834 smb_krb5_ctx_fini(ctx); 1835 } 1836 1837 /* commit keytab file */ 1838 if (rc == SMB_ADS_SUCCESS) { 1839 if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) { 1840 (void) unlink(tmpfile); 1841 rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB; 1842 } 1843 } else { 1844 (void) unlink(tmpfile); 1845 } 1846 1847 smb_ads_close(ah); 1848 smb_ccache_remove(SMB_CCACHE_PATH); 1849 return (rc); 1850 } 1851 1852 struct xlate_table { 1853 int err; 1854 const char * const msg; 1855 }; 1856 1857 static const struct xlate_table 1858 adjoin_table[] = { 1859 { SMB_ADS_SUCCESS, "Success" }, 1860 { SMB_ADS_KRB5_INIT_CTX, 1861 "Failed creating a Kerberos context." }, 1862 { SMB_ADS_KRB5_CC_DEFAULT, 1863 "Failed to resolve default credential cache." }, 1864 { SMB_ADS_KRB5_PARSE_PRINCIPAL, 1865 "Failed parsing the user principal name." }, 1866 { SMB_ADS_KRB5_GET_INIT_CREDS_PW, 1867 "Failed getting initial credentials. (Wrong password?)" }, 1868 { SMB_ADS_KRB5_CC_INITIALIZE, 1869 "Failed initializing the credential cache." }, 1870 { SMB_ADS_KRB5_CC_STORE_CRED, 1871 "Failed to update the credential cache." }, 1872 { SMB_ADS_CANT_LOCATE_DC, 1873 "Failed to locate a domain controller." }, 1874 { SMB_ADS_LDAP_INIT, 1875 "Failed to create an LDAP handle." }, 1876 { SMB_ADS_LDAP_SETOPT, 1877 "Failed to set an LDAP option." }, 1878 { SMB_ADS_LDAP_SET_DOM, 1879 "Failed to set the LDAP handle DN." }, 1880 { SMB_ADS_LDAP_SASL_BIND, 1881 "Failed to bind the LDAP handle. " 1882 "Usually indicates an authentication problem." }, 1883 1884 { SMB_ADJOIN_ERR_GEN_PWD, 1885 "Failed to generate machine password." }, 1886 { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of " 1887 "the domain controller. The rootDSE attribute named " 1888 "\"domainControllerFunctionality\" is missing from the " 1889 "Active Directory." }, 1890 { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the " 1891 "workstation trust account." }, 1892 { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the " 1893 "workstation trust account." }, 1894 { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the " 1895 "workstation trust account because its name is already " 1896 "in use." }, 1897 { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the " 1898 "workstation trust account" }, 1899 { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos " 1900 "context." }, 1901 { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos " 1902 "principals." }, 1903 { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." }, 1904 { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify " 1905 "userAccountControl attribute of the workstation trust " 1906 "account." }, 1907 { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local " 1908 "keytab file (i.e /etc/krb5/krb5.keytab)." }, 1909 { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap " 1910 "configuration." }, 1911 { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap " 1912 "service." }, 1913 { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to " 1914 "local keytab file (i.e. /etc/krb5/krb5.keytab)." }, 1915 { SMB_ADJOIN_ERR_AUTH_NETLOGON, 1916 "Failed to authenticate using the new computer account." }, 1917 { SMB_ADJOIN_ERR_STORE_PROPS, 1918 "Failed to store computer account information locally." }, 1919 { 0, NULL } 1920 }; 1921 1922 /* 1923 * smb_ads_strerror 1924 * 1925 * Lookup an error message for the specific adjoin error code. 1926 */ 1927 const char * 1928 smb_ads_strerror(int err) 1929 { 1930 const struct xlate_table *xt; 1931 1932 if (err > 0 && err < SMB_ADS_ERRNO_GAP) 1933 return (strerror(err)); 1934 1935 for (xt = adjoin_table; xt->msg; xt++) 1936 if (xt->err == err) 1937 return (xt->msg); 1938 1939 return ("Unknown error code."); 1940 } 1941 1942 void 1943 smb_ads_log_errmsg(smb_ads_status_t err) 1944 { 1945 const char *s = smb_ads_strerror(err); 1946 syslog(LOG_NOTICE, "%s", s); 1947 } 1948 1949 1950 /* 1951 * smb_ads_lookup_msdcs 1952 * 1953 * If server argument is set, try to locate the specified DC. 1954 * If it is set to empty string, locate any DCs in the specified domain. 1955 * Returns the discovered DC via buf. 1956 * 1957 * fqdn - fully-qualified domain name 1958 * dci - the name and address of the found DC 1959 */ 1960 uint32_t 1961 smb_ads_lookup_msdcs(char *fqdn, smb_dcinfo_t *dci) 1962 { 1963 smb_ads_host_info_t *hinfo = NULL; 1964 char ipstr[INET6_ADDRSTRLEN]; 1965 1966 if (!fqdn || !dci) 1967 return (NT_STATUS_INTERNAL_ERROR); 1968 1969 ipstr[0] = '\0'; 1970 if ((hinfo = smb_ads_find_host(fqdn)) == NULL) 1971 return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); 1972 1973 (void) smb_inet_ntop(&hinfo->ipaddr, ipstr, 1974 SMB_IPSTRLEN(hinfo->ipaddr.a_family)); 1975 smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, ipstr); 1976 1977 (void) strlcpy(dci->dc_name, hinfo->name, sizeof (dci->dc_name)); 1978 dci->dc_addr = hinfo->ipaddr; 1979 dci->dc_flags = hinfo->flags; 1980 1981 free(hinfo); 1982 return (NT_STATUS_SUCCESS); 1983 } 1984 1985 static krb5_enctype * 1986 smb_ads_get_enctypes(int dclevel, int *num) 1987 { 1988 krb5_enctype *encptr; 1989 1990 if (dclevel >= SMB_ADS_DCLEVEL_W2K8) { 1991 *num = sizeof (w2k8enctypes) / sizeof (krb5_enctype); 1992 encptr = w2k8enctypes; 1993 } else { 1994 *num = sizeof (pre_w2k8enctypes) / sizeof (krb5_enctype); 1995 encptr = pre_w2k8enctypes; 1996 } 1997 1998 return (encptr); 1999 } 2000