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