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.
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.
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]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
37 #include <arpa/nameser.h>
39 #include <sys/synch.h>
43 #include <sys/types.h>
46 #include <sasl/sasl.h>
49 #include <cryptoutil.h>
50 #include <ads/dsgetdc.h>
52 #include <smbsrv/libsmbns.h>
53 #include <smbns_dyndns.h>
54 #include <smbns_krb.h>
56 #define SMB_ADS_AF_UNKNOWN(x) (((x)->ipaddr.a_family != AF_INET) && \
57 ((x)->ipaddr.a_family != AF_INET6))
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
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"
71 * domainControllerFunctionality
73 * This rootDSE attribute indicates the functional level of the DC.
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
82 * msDs-supportedEncryptionTypes (Windows Server 2008 only)
84 * This attribute defines the encryption types supported by the system.
86 * - DES cbc mode with CRC-32
87 * - DES cbc mode with RSA-MD5
88 * - ArcFour with HMAC/md5
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
99 static krb5_enctype w2k8enctypes
[] = {
100 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
101 ENCTYPE_AES128_CTS_HMAC_SHA1_96
,
102 ENCTYPE_ARCFOUR_HMAC
,
107 static krb5_enctype pre_w2k8enctypes
[] = {
108 ENCTYPE_ARCFOUR_HMAC
,
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"
122 * UserAccountControl flags: manipulate user account properties.
124 * The hexadecimal value of the following property flags are based on MSDN
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
150 * Length of "dc=" prefix.
152 #define SMB_ADS_DN_PREFIX_LEN 3
154 static char *smb_ads_computer_objcls
[] = {
155 "top", "person", "organizationalPerson",
156 "user", "computer", NULL
159 static char *smb_ads_share_objcls
[] = {
160 "top", "leaf", "connectionPoint", "volume", NULL
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
;
168 * SMB ADS config cache is maintained to facilitate the detection of
169 * changes in configuration that is relevant to AD selection.
171 typedef struct smb_ads_config
{
172 char c_site
[SMB_ADS_SITE_MAX
];
176 static smb_ads_config_t smb_ads_cfg
;
179 /* attribute/value pair */
180 typedef struct smb_ads_avpair
{
186 typedef enum smb_ads_qstat
{
187 SMB_ADS_STAT_ERR
= -2,
189 SMB_ADS_STAT_NOT_FOUND
,
193 typedef struct smb_ads_host_list
{
195 smb_ads_host_info_t
*ah_list
;
196 } smb_ads_host_list_t
;
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
*,
213 static smb_ads_qstat_t
smb_ads_get_qstat(smb_ads_handle_t
*, LDAPMessage
*,
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 *);
223 * Initializes the ADS config cache.
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
);
233 /* Force -lads to load, for dtrace. */
240 smb_ads_free_cached_host();
246 * This function will be called when smb/server SMF service is refreshed.
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.
253 smb_ads_refresh(boolean_t force_rediscovery
)
255 char new_site
[SMB_ADS_SITE_MAX
];
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
);
262 smb_ads_free_cached_host();
264 if (force_rediscovery
) {
265 (void) _DsForceRediscovery(NULL
, 0);
271 * smb_ads_build_unc_name
273 * Construct the UNC name of the share object in the format of
274 * \\hostname.domain\shareUNC
276 * Returns 0 on success, -1 on error.
279 smb_ads_build_unc_name(char *unc_name
, int maxlen
,
280 const char *hostname
, const char *shareUNC
)
282 char my_domain
[MAXHOSTNAMELEN
];
284 if (smb_getfqdomainname(my_domain
, sizeof (my_domain
)) != 0)
287 (void) snprintf(unc_name
, maxlen
, "\\\\%s.%s\\%s",
288 hostname
, my_domain
, shareUNC
);
293 * The cached ADS host is no longer valid if one of the following criteria
296 * 1) not in the specified domain
297 * 2) not the sought host (if specified)
300 * The caller is responsible for acquiring the smb_ads_cached_host_mtx lock
301 * prior to calling this function.
303 * Return B_TRUE if the cache host is still valid. Otherwise, return B_FALSE.
306 smb_ads_validate_cache_host(char *domain
)
308 if (!smb_ads_cached_host_info
)
311 if (!smb_ads_is_same_domain(smb_ads_cached_host_info
->name
, domain
))
318 * smb_ads_match_hosts_same_domain
320 * Returns true, if the cached ADS host is in the same domain as the
321 * current (given) domain.
324 smb_ads_is_same_domain(char *cached_host_name
, char *current_domain
)
326 char *cached_host_domain
;
328 if ((cached_host_name
== NULL
) || (current_domain
== NULL
))
331 cached_host_domain
= strchr(cached_host_name
, '.');
332 if (cached_host_domain
== NULL
)
335 ++cached_host_domain
;
336 if (smb_strcasecmp(cached_host_domain
, current_domain
, 0))
343 * smb_ads_dup_host_info
345 * Duplicates the passed smb_ads_host_info_t structure.
346 * Caller must free memory allocated by this method.
348 * Returns a reference to the duplicated smb_ads_host_info_t structure.
349 * Returns NULL on error.
351 static smb_ads_host_info_t
*
352 smb_ads_dup_host_info(smb_ads_host_info_t
*ads_host
)
354 smb_ads_host_info_t
*dup_host
;
356 if (ads_host
== NULL
)
359 dup_host
= malloc(sizeof (smb_ads_host_info_t
));
361 if (dup_host
!= NULL
)
362 bcopy(ads_host
, dup_host
, sizeof (smb_ads_host_info_t
));
370 * Finds an ADS host in a given domain.
372 * If the cached host is valid, it will be used. Otherwise, a DC will
373 * be selected based on the following criteria:
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
381 * The above items are listed in decreasing preference order. The selected
384 * If this function is called during domain join, the specified kpasswd server
385 * takes precedence over preferred DC, AD site, and so on.
388 * domain: fully-qualified domain name.
391 * A copy of the cached host info is returned. The caller is responsible
392 * for deallocating the memory returned by this function.
395 smb_ads_host_info_t
*
396 smb_ads_find_host(char *domain
)
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
;
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
);
412 (void) mutex_unlock(&smb_ads_cached_host_mtx
);
413 smb_ads_free_cached_host();
416 * The _real_ DC Locator is over in idmapd.
417 * Door call over there to get it.
421 status
= _DsGetDcName(
422 NULL
, /* ComputerName */
424 NULL
, /* DomainGuid */
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.
435 case NT_STATUS_NO_SUCH_DOMAIN
: /* Specified domain unknown */
436 case NT_STATUS_INVALID_SERVER_STATE
: /* not in domain mode. */
442 case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
:
443 case NT_STATUS_CANT_WAIT
: /* timeout over in idmap */
448 host
= calloc(1, sizeof (*host
));
452 (void) strlcpy(host
->name
, dci
->DomainControllerName
, MAXHOSTNAMELEN
);
453 ss
= (void *)dci
->_sockaddr
;
454 switch (ss
->ss_family
) {
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
,
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
));
472 syslog(LOG_ERR
, "no addr for DC %s",
473 dci
->DomainControllerName
);
479 (void) mutex_lock(&smb_ads_cached_host_mtx
);
480 if (!smb_ads_cached_host_info
)
481 smb_ads_cached_host_info
= smb_ads_dup_host_info(host
);
482 host
= smb_ads_dup_host_info(smb_ads_cached_host_info
);
483 (void) mutex_unlock(&smb_ads_cached_host_mtx
);
491 * Return the number of dots in a string.
494 smb_ads_count_dots(const char *s
)
507 * Convert a domain name in dot notation to distinguished name format,
508 * for example: sun.com -> dc=sun,dc=com.
510 * Returns a pointer to an allocated buffer containing the distinguished
514 smb_ads_convert_domain(const char *domain_name
)
522 if (domain_name
== NULL
|| *domain_name
== 0)
525 ndots
= smb_ads_count_dots(domain_name
);
527 len
= strlen(domain_name
) + (ndots
* SMB_ADS_DN_PREFIX_LEN
) + 1;
529 if ((dn_name
= malloc(len
)) == NULL
)
533 (void) strlcpy(dn_name
, "dc=", len
);
540 (void) strlcat(dn_name
, ",dc=", len
);
543 (void) strlcat(dn_name
, buf
, len
);
552 * smb_ads_free_cached_host
554 * Free the memory use by the global smb_ads_cached_host_info & set it to NULL.
557 smb_ads_free_cached_host(void)
559 (void) mutex_lock(&smb_ads_cached_host_mtx
);
560 if (smb_ads_cached_host_info
) {
561 free(smb_ads_cached_host_info
);
562 smb_ads_cached_host_info
= NULL
;
564 (void) mutex_unlock(&smb_ads_cached_host_mtx
);
569 * Open a LDAP connection to an ADS server if the system is in domain mode.
570 * Acquire both Kerberos TGT and LDAP service tickets for the host principal.
572 * This function should only be called after the system is successfully joined
578 char domain
[MAXHOSTNAMELEN
];
580 smb_ads_status_t err
;
582 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN
)
585 if (smb_getfqdomainname(domain
, MAXHOSTNAMELEN
) != 0)
588 err
= smb_ads_open_main(&h
, domain
, NULL
, NULL
);
590 smb_ads_log_errmsg(err
);
598 smb_ads_saslcallback(LDAP
*ld
, unsigned flags
, void *defaults
, void *prompts
)
600 NOTE(ARGUNUSED(ld
, defaults
));
601 sasl_interact_t
*interact
;
603 if (prompts
== NULL
|| flags
!= LDAP_SASL_INTERACTIVE
)
604 return (LDAP_PARAM_ERROR
);
606 /* There should be no extra arguemnts for SASL/GSSAPI authentication */
607 for (interact
= prompts
; interact
->id
!= SASL_CB_LIST_END
;
609 interact
->result
= NULL
;
612 return (LDAP_SUCCESS
);
617 * Open a LDAP connection to an ADS server.
618 * If ADS is enabled and the administrative username, password, and
619 * ADS domain are defined then query DNS to find an ADS server if this is the
620 * very first call to this routine. After an ADS server is found then this
621 * server will be used everytime this routine is called until the system is
622 * rebooted or the ADS server becomes unavailable then an ADS server will
623 * be queried again. After the connection is made then an ADS handle
624 * is created to be returned.
626 * After the LDAP connection, the LDAP version will be set to 3 using
629 * The LDAP connection is bound before the ADS handle is returned.
631 * domain - fully-qualified domain name
632 * user - the user account for whom the Kerberos TGT ticket and ADS
633 * service tickets are acquired.
634 * password - password of the specified user
637 * NULL : can't connect to ADS server or other errors
638 * smb_ads_handle_t* : handle to ADS server
641 smb_ads_open_main(smb_ads_handle_t
**hp
, char *domain
, char *user
,
644 smb_ads_handle_t
*ah
;
647 smb_ads_host_info_t
*ads_host
= NULL
;
653 err
= smb_kinit(domain
, user
, password
);
660 ads_host
= smb_ads_find_host(domain
);
661 if (ads_host
== NULL
)
662 return (SMB_ADS_CANT_LOCATE_DC
);
664 ah
= (smb_ads_handle_t
*)malloc(sizeof (smb_ads_handle_t
));
670 (void) memset(ah
, 0, sizeof (smb_ads_handle_t
));
672 if ((ld
= ldap_init(ads_host
->name
, ads_host
->port
)) == NULL
) {
673 syslog(LOG_ERR
, "smbns: ldap_init failed");
674 smb_ads_free_cached_host();
677 return (SMB_ADS_LDAP_INIT
);
680 if (ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
)
682 smb_ads_free_cached_host();
685 (void) ldap_unbind(ld
);
686 return (SMB_ADS_LDAP_SETOPT
);
689 (void) ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
691 ah
->domain
= strdup(domain
);
693 if (ah
->domain
== NULL
) {
696 return (SMB_ADS_LDAP_SETOPT
);
700 * ah->domain is often used for generating service principal name.
701 * Convert it to lower case for RFC 4120 section 6.2.1 conformance.
703 (void) smb_strlwr(ah
->domain
);
704 ah
->domain_dn
= smb_ads_convert_domain(domain
);
705 if (ah
->domain_dn
== NULL
) {
708 return (SMB_ADS_LDAP_SET_DOM
);
711 ah
->hostname
= strdup(ads_host
->name
);
712 if (ah
->hostname
== NULL
) {
717 (void) mutex_lock(&smb_ads_cfg
.c_mtx
);
718 if (*smb_ads_cfg
.c_site
!= '\0') {
719 if ((ah
->site
= strdup(smb_ads_cfg
.c_site
)) == NULL
) {
721 (void) mutex_unlock(&smb_ads_cfg
.c_mtx
);
728 (void) mutex_unlock(&smb_ads_cfg
.c_mtx
);
730 rc
= ldap_sasl_interactive_bind_s(ah
->ld
, "", "GSSAPI", NULL
, NULL
,
731 LDAP_SASL_INTERACTIVE
, &smb_ads_saslcallback
, NULL
);
732 if (rc
!= LDAP_SUCCESS
) {
733 syslog(LOG_ERR
, "smbns: ldap_sasl_..._bind_s failed (%s)",
734 ldap_err2string(rc
));
737 return (SMB_ADS_LDAP_SASL_BIND
);
743 return (SMB_ADS_SUCCESS
);
748 * Close connection to ADS server and free memory allocated for ADS handle.
749 * LDAP unbind is called here.
751 * ah: handle to ADS server
756 smb_ads_close(smb_ads_handle_t
*ah
)
760 /* close and free connection resources */
762 (void) ldap_unbind(ah
->ld
);
774 * Since the attrs is a null-terminated array, all elements
775 * in the array (except the last one) will point to allocated
779 smb_ads_alloc_attr(LDAPMod
*attrs
[], int num
)
783 bzero(attrs
, num
* sizeof (LDAPMod
*));
784 for (i
= 0; i
< (num
- 1); i
++) {
785 attrs
[i
] = (LDAPMod
*)malloc(sizeof (LDAPMod
));
786 if (attrs
[i
] == NULL
) {
787 smb_ads_free_attr(attrs
);
797 * Free memory allocated when publishing a share.
799 * attrs: an array of LDAPMod pointers
804 smb_ads_free_attr(LDAPMod
*attrs
[])
807 for (i
= 0; attrs
[i
]; i
++) {
813 * Returns share DN in an allocated buffer. The format of the DN is
814 * cn=<sharename>,<container RDNs>,<domain DN>
816 * If the domain DN is not included in the container parameter,
817 * then it will be appended to create the share DN.
819 * The caller must free the allocated buffer.
822 smb_ads_get_sharedn(const char *sharename
, const char *container
,
823 const char *domain_dn
)
826 int rc
, offset
, container_len
, domain_len
;
827 boolean_t append_domain
= B_TRUE
;
829 container_len
= strlen(container
);
830 domain_len
= strlen(domain_dn
);
832 if (container_len
>= domain_len
) {
834 /* offset to last domain_len characters */
835 offset
= container_len
- domain_len
;
837 if (smb_strcasecmp(container
+ offset
,
838 domain_dn
, domain_len
) == 0)
839 append_domain
= B_FALSE
;
843 rc
= asprintf(&share_dn
, "cn=%s,%s,%s", sharename
,
844 container
, domain_dn
);
846 rc
= asprintf(&share_dn
, "cn=%s,%s", sharename
,
849 return ((rc
== -1) ? NULL
: share_dn
);
854 * Call by smb_ads_publish_share to create share object in ADS.
855 * This routine specifies the attributes of an ADS LDAP share object. The first
856 * attribute and values define the type of ADS object, the share object. The
857 * second attribute and value define the UNC of the share data for the share
858 * object. The LDAP synchronous add command is used to add the object into ADS.
859 * The container location to add the object needs to specified.
861 * ah : handle to ADS server
862 * adsShareName: name of share object to be created in ADS
863 * shareUNC : share name on NetForce
864 * adsContainer: location in ADS to create share object
871 smb_ads_add_share(smb_ads_handle_t
*ah
, const char *adsShareName
,
872 const char *unc_name
, const char *adsContainer
)
874 LDAPMod
*attrs
[SMB_ADS_SHARE_NUM_ATTR
];
878 char *unc_names
[] = {(char *)unc_name
, NULL
};
880 if ((share_dn
= smb_ads_get_sharedn(adsShareName
, adsContainer
,
881 ah
->domain_dn
)) == NULL
)
884 if (smb_ads_alloc_attr(attrs
, SMB_ADS_SHARE_NUM_ATTR
) != 0) {
889 attrs
[j
]->mod_op
= LDAP_MOD_ADD
;
890 attrs
[j
]->mod_type
= "objectClass";
891 attrs
[j
]->mod_values
= smb_ads_share_objcls
;
893 attrs
[++j
]->mod_op
= LDAP_MOD_ADD
;
894 attrs
[j
]->mod_type
= "uNCName";
895 attrs
[j
]->mod_values
= unc_names
;
897 if ((ret
= ldap_add_s(ah
->ld
, share_dn
, attrs
)) != LDAP_SUCCESS
) {
898 if (ret
== LDAP_NO_SUCH_OBJECT
) {
899 syslog(LOG_ERR
, "Failed to publish share %s in" \
900 " AD. Container does not exist: %s.\n",
901 adsShareName
, share_dn
);
904 syslog(LOG_ERR
, "Failed to publish share %s in" \
905 " AD: %s (%s).\n", adsShareName
, share_dn
,
906 ldap_err2string(ret
));
908 smb_ads_free_attr(attrs
);
913 smb_ads_free_attr(attrs
);
920 * Call by smb_ads_remove_share to remove share object from ADS. The container
921 * location to remove the object needs to specified. The LDAP synchronous
922 * delete command is used.
924 * ah : handle to ADS server
925 * adsShareName: name of share object in ADS to be removed
926 * adsContainer: location of share object in ADS
932 smb_ads_del_share(smb_ads_handle_t
*ah
, const char *adsShareName
,
933 const char *adsContainer
)
938 if ((share_dn
= smb_ads_get_sharedn(adsShareName
, adsContainer
,
939 ah
->domain_dn
)) == NULL
)
942 if ((ret
= ldap_delete_s(ah
->ld
, share_dn
)) != LDAP_SUCCESS
) {
943 smb_tracef("ldap_delete: %s", ldap_err2string(ret
));
954 * smb_ads_escape_search_filter_chars
956 * This routine will escape the special characters found in a string
957 * that will later be passed to the ldap search filter.
959 * RFC 1960 - A String Representation of LDAP Search Filters
960 * 3. String Search Filter Definition
961 * If a value must contain one of the characters '*' OR '(' OR ')',
963 * should be escaped by preceding them with the backslash '\' character.
965 * RFC 2252 - LDAP Attribute Syntax Definitions
966 * a backslash quoting mechanism is used to escape
967 * the following separator symbol character (such as "'", "$" or "#") if
968 * it should occur in that string.
971 smb_ads_escape_search_filter_chars(const char *src
, char *dst
)
973 int avail
= SMB_ADS_MAXBUFLEN
- 1; /* reserve a space for NULL char */
975 if (src
== NULL
|| dst
== NULL
)
1008 * smb_ads_lookup_share
1009 * The search filter is set to search for a specific share name in the
1010 * specified ADS container. The LDSAP synchronous search command is used.
1012 * ah : handle to ADS server
1013 * adsShareName: name of share object in ADS to be searched
1014 * adsContainer: location of share object in ADS
1021 smb_ads_lookup_share(smb_ads_handle_t
*ah
, const char *adsShareName
,
1022 const char *adsContainer
, char *unc_name
)
1024 char *attrs
[4], filter
[SMB_ADS_MAXBUFLEN
];
1028 char tmpbuf
[SMB_ADS_MAXBUFLEN
];
1030 if (adsShareName
== NULL
|| adsContainer
== NULL
)
1033 if ((share_dn
= smb_ads_get_sharedn(adsShareName
, adsContainer
,
1034 ah
->domain_dn
)) == NULL
)
1039 attrs
[1] = "objectClass";
1040 attrs
[2] = "uNCName";
1043 if (smb_ads_escape_search_filter_chars(unc_name
, tmpbuf
) != 0) {
1048 (void) snprintf(filter
, sizeof (filter
),
1049 "(&(objectClass=volume)(uNCName=%s))", tmpbuf
);
1051 if ((ret
= ldap_search_s(ah
->ld
, share_dn
,
1052 LDAP_SCOPE_BASE
, filter
, attrs
, 0, &res
)) != LDAP_SUCCESS
) {
1053 if (ret
!= LDAP_NO_SUCH_OBJECT
)
1054 smb_tracef("%s: ldap_search: %s", share_dn
,
1055 ldap_err2string(ret
));
1057 (void) ldap_msgfree(res
);
1062 (void) free(share_dn
);
1064 /* no match is found */
1065 if (ldap_count_entries(ah
->ld
, res
) == 0) {
1066 (void) ldap_msgfree(res
);
1070 /* free the search results */
1071 (void) ldap_msgfree(res
);
1077 * smb_ads_publish_share
1078 * Publish share into ADS. If a share name already exist in ADS in the same
1079 * container then the existing share object is removed before adding the new
1082 * ah : handle return from smb_ads_open
1083 * adsShareName: name of share to be added to ADS directory
1084 * shareUNC : name of share on client, can be NULL to use the same name
1086 * adsContainer: location for share to be added in ADS directory, ie
1088 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR
1089 * to use host ip addr for UNC.
1095 smb_ads_publish_share(smb_ads_handle_t
*ah
, const char *adsShareName
,
1096 const char *shareUNC
, const char *adsContainer
, const char *hostname
)
1099 char unc_name
[SMB_ADS_MAXBUFLEN
];
1101 if (adsShareName
== NULL
|| adsContainer
== NULL
)
1104 if (shareUNC
== 0 || *shareUNC
== 0)
1105 shareUNC
= adsShareName
;
1107 if (smb_ads_build_unc_name(unc_name
, sizeof (unc_name
),
1108 hostname
, shareUNC
) < 0)
1111 ret
= smb_ads_lookup_share(ah
, adsShareName
, adsContainer
, unc_name
);
1115 (void) smb_ads_del_share(ah
, adsShareName
, adsContainer
);
1116 ret
= smb_ads_add_share(ah
, adsShareName
, unc_name
,
1121 ret
= smb_ads_add_share(ah
, adsShareName
, unc_name
,
1123 if (ret
== LDAP_ALREADY_EXISTS
)
1130 /* return with error code */
1138 * smb_ads_remove_share
1139 * Remove share from ADS. A search is done first before explicitly removing
1142 * ah : handle return from smb_ads_open
1143 * adsShareName: name of share to be removed from ADS directory
1144 * adsContainer: location for share to be removed from ADS directory, ie
1151 smb_ads_remove_share(smb_ads_handle_t
*ah
, const char *adsShareName
,
1152 const char *shareUNC
, const char *adsContainer
, const char *hostname
)
1155 char unc_name
[SMB_ADS_MAXBUFLEN
];
1157 if (adsShareName
== NULL
|| adsContainer
== NULL
)
1159 if (shareUNC
== 0 || *shareUNC
== 0)
1160 shareUNC
= adsShareName
;
1162 if (smb_ads_build_unc_name(unc_name
, sizeof (unc_name
),
1163 hostname
, shareUNC
) < 0)
1166 ret
= smb_ads_lookup_share(ah
, adsShareName
, adsContainer
, unc_name
);
1172 return (smb_ads_del_share(ah
, adsShareName
, adsContainer
));
1176 * smb_ads_get_default_comp_container_dn
1178 * Build the distinguished name for the default computer conatiner (i.e. the
1179 * pre-defined Computers container).
1182 smb_ads_get_default_comp_container_dn(smb_ads_handle_t
*ah
, char *buf
,
1185 (void) snprintf(buf
, buflen
, "cn=%s,%s", SMB_ADS_COMPUTERS_CN
,
1190 * smb_ads_get_default_comp_dn
1192 * Build the distinguished name for this system.
1195 smb_ads_get_default_comp_dn(smb_ads_handle_t
*ah
, char *buf
, size_t buflen
)
1197 char nbname
[NETBIOS_NAME_SZ
];
1198 char container_dn
[SMB_ADS_DN_MAX
];
1200 (void) smb_getnetbiosname(nbname
, sizeof (nbname
));
1201 smb_ads_get_default_comp_container_dn(ah
, container_dn
, SMB_ADS_DN_MAX
);
1202 (void) snprintf(buf
, buflen
, "cn=%s,%s", nbname
, container_dn
);
1206 * smb_ads_add_computer
1208 * Returns 0 upon success. Otherwise, returns -1.
1211 smb_ads_add_computer(smb_ads_handle_t
*ah
, int dclevel
, char *dn
)
1213 return (smb_ads_computer_op(ah
, LDAP_MOD_ADD
, dclevel
, dn
));
1217 * smb_ads_modify_computer
1219 * Returns 0 upon success. Otherwise, returns -1.
1222 smb_ads_modify_computer(smb_ads_handle_t
*ah
, int dclevel
, char *dn
)
1224 return (smb_ads_computer_op(ah
, LDAP_MOD_REPLACE
, dclevel
, dn
));
1228 * smb_ads_get_dc_level
1230 * Returns the functional level of the DC upon success.
1231 * Otherwise, -1 is returned.
1234 smb_ads_get_dc_level(smb_ads_handle_t
*ah
)
1236 LDAPMessage
*res
, *entry
;
1242 attr
[0] = SMB_ADS_ATTR_DCLEVEL
;
1244 if (ldap_search_s(ah
->ld
, "", LDAP_SCOPE_BASE
, NULL
, attr
,
1245 0, &res
) != LDAP_SUCCESS
) {
1246 (void) ldap_msgfree(res
);
1250 /* no match for the specified attribute is found */
1251 if (ldap_count_entries(ah
->ld
, res
) == 0) {
1252 (void) ldap_msgfree(res
);
1256 entry
= ldap_first_entry(ah
->ld
, res
);
1258 if ((vals
= ldap_get_values(ah
->ld
, entry
,
1259 SMB_ADS_ATTR_DCLEVEL
)) == NULL
) {
1261 * Observed the values aren't populated
1262 * by the Windows 2000 server.
1264 (void) ldap_msgfree(res
);
1265 return (SMB_ADS_DCLEVEL_W2K
);
1268 if (vals
[0] != NULL
)
1271 ldap_value_free(vals
);
1274 (void) ldap_msgfree(res
);
1279 * The fully-qualified hostname returned by this function is often used for
1280 * constructing service principal name. Return the fully-qualified hostname
1281 * in lower case for RFC 4120 section 6.2.1 conformance.
1284 smb_ads_getfqhostname(smb_ads_handle_t
*ah
, char *fqhost
, int len
)
1286 if (smb_gethostname(fqhost
, len
, SMB_CASE_LOWER
) != 0)
1289 (void) snprintf(fqhost
, len
, "%s.%s", fqhost
,
1296 smb_ads_computer_op(smb_ads_handle_t
*ah
, int op
, int dclevel
, char *dn
)
1298 LDAPMod
*attrs
[SMB_ADS_COMPUTER_NUM_ATTR
];
1300 char *ctl_val
[2], *fqh_val
[2];
1301 char *encrypt_val
[2];
1303 int ret
, usrctl_flags
= 0;
1304 char sam_acct
[SMB_SAMACCT_MAXLEN
];
1305 char fqhost
[MAXHOSTNAMELEN
];
1306 char usrctl_buf
[16];
1307 char encrypt_buf
[16];
1309 smb_krb5_pn_set_t spn
, upn
;
1311 if (smb_getsamaccount(sam_acct
, sizeof (sam_acct
)) != 0)
1314 if (smb_ads_getfqhostname(ah
, fqhost
, MAXHOSTNAMELEN
))
1317 /* The SPN attribute is multi-valued and must be 1 or greater */
1318 if (smb_krb5_get_pn_set(&spn
, SMB_PN_SPN_ATTR
, ah
->domain
) == 0)
1321 /* The UPN attribute is single-valued and cannot be zero */
1322 if (smb_krb5_get_pn_set(&upn
, SMB_PN_UPN_ATTR
, ah
->domain
) != 1) {
1323 smb_krb5_free_pn_set(&spn
);
1324 smb_krb5_free_pn_set(&upn
);
1328 max
= (SMB_ADS_COMPUTER_NUM_ATTR
- ((op
!= LDAP_MOD_ADD
) ? 1 : 0))
1329 - (dclevel
>= SMB_ADS_DCLEVEL_W2K8
? 0 : 1);
1331 if (smb_ads_alloc_attr(attrs
, max
) != 0) {
1332 smb_krb5_free_pn_set(&spn
);
1333 smb_krb5_free_pn_set(&upn
);
1337 /* objectClass attribute is not modifiable. */
1338 if (op
== LDAP_MOD_ADD
) {
1339 attrs
[++j
]->mod_op
= op
;
1340 attrs
[j
]->mod_type
= "objectClass";
1341 attrs
[j
]->mod_values
= smb_ads_computer_objcls
;
1344 attrs
[++j
]->mod_op
= op
;
1345 attrs
[j
]->mod_type
= SMB_ADS_ATTR_SAMACCT
;
1346 sam_val
[0] = sam_acct
;
1348 attrs
[j
]->mod_values
= sam_val
;
1350 attrs
[++j
]->mod_op
= op
;
1351 attrs
[j
]->mod_type
= SMB_ADS_ATTR_UPN
;
1352 attrs
[j
]->mod_values
= upn
.s_pns
;
1354 attrs
[++j
]->mod_op
= op
;
1355 attrs
[j
]->mod_type
= SMB_ADS_ATTR_SPN
;
1356 attrs
[j
]->mod_values
= spn
.s_pns
;
1358 attrs
[++j
]->mod_op
= op
;
1359 attrs
[j
]->mod_type
= SMB_ADS_ATTR_CTL
;
1360 usrctl_flags
|= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT
|
1361 SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD
|
1362 SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE
);
1363 (void) snprintf(usrctl_buf
, sizeof (usrctl_buf
), "%d", usrctl_flags
);
1364 ctl_val
[0] = usrctl_buf
;
1366 attrs
[j
]->mod_values
= ctl_val
;
1368 attrs
[++j
]->mod_op
= op
;
1369 attrs
[j
]->mod_type
= SMB_ADS_ATTR_DNSHOST
;
1370 fqh_val
[0] = fqhost
;
1372 attrs
[j
]->mod_values
= fqh_val
;
1374 /* enctypes support starting in Windows Server 2008 */
1375 if (dclevel
> SMB_ADS_DCLEVEL_W2K3
) {
1376 attrs
[++j
]->mod_op
= op
;
1377 attrs
[j
]->mod_type
= SMB_ADS_ATTR_ENCTYPES
;
1378 (void) snprintf(encrypt_buf
, sizeof (encrypt_buf
), "%d",
1379 SMB_ADS_ENC_AES256
+ SMB_ADS_ENC_AES128
+ SMB_ADS_ENC_RC4
+
1380 SMB_ADS_ENC_DES_MD5
+ SMB_ADS_ENC_DES_CRC
);
1381 encrypt_val
[0] = encrypt_buf
;
1383 attrs
[j
]->mod_values
= encrypt_val
;
1388 if ((ret
= ldap_add_s(ah
->ld
, dn
, attrs
)) != LDAP_SUCCESS
) {
1389 syslog(LOG_NOTICE
, "ldap_add: %s",
1390 ldap_err2string(ret
));
1395 case LDAP_MOD_REPLACE
:
1396 if ((ret
= ldap_modify_s(ah
->ld
, dn
, attrs
)) != LDAP_SUCCESS
) {
1397 syslog(LOG_NOTICE
, "ldap_modify: %s",
1398 ldap_err2string(ret
));
1408 smb_ads_free_attr(attrs
);
1409 smb_krb5_free_pn_set(&spn
);
1410 smb_krb5_free_pn_set(&upn
);
1416 * Delete an ADS computer account.
1419 smb_ads_del_computer(smb_ads_handle_t
*ah
, char *dn
)
1423 if ((rc
= ldap_delete_s(ah
->ld
, dn
)) != LDAP_SUCCESS
)
1424 smb_tracef("ldap_delete: %s", ldap_err2string(rc
));
1428 * Gets the value of the given attribute.
1430 static smb_ads_qstat_t
1431 smb_ads_getattr(LDAP
*ld
, LDAPMessage
*entry
, smb_ads_avpair_t
*avpair
)
1434 smb_ads_qstat_t rc
= SMB_ADS_STAT_FOUND
;
1437 avpair
->avp_val
= NULL
;
1438 vals
= ldap_get_values(ld
, entry
, avpair
->avp_attr
);
1440 return (SMB_ADS_STAT_NOT_FOUND
);
1443 ldap_value_free(vals
);
1444 return (SMB_ADS_STAT_NOT_FOUND
);
1447 avpair
->avp_val
= strdup(vals
[0]);
1448 if (!avpair
->avp_val
)
1449 rc
= SMB_ADS_STAT_ERR
;
1451 ldap_value_free(vals
);
1456 * Process query's result.
1458 static smb_ads_qstat_t
1459 smb_ads_get_qstat(smb_ads_handle_t
*ah
, LDAPMessage
*res
,
1460 smb_ads_avpair_t
*avpair
)
1462 char fqhost
[MAXHOSTNAMELEN
];
1463 smb_ads_avpair_t dnshost_avp
;
1464 smb_ads_qstat_t rc
= SMB_ADS_STAT_FOUND
;
1467 if (smb_ads_getfqhostname(ah
, fqhost
, MAXHOSTNAMELEN
))
1468 return (SMB_ADS_STAT_ERR
);
1470 if (ldap_count_entries(ah
->ld
, res
) == 0)
1471 return (SMB_ADS_STAT_NOT_FOUND
);
1473 if ((entry
= ldap_first_entry(ah
->ld
, res
)) == NULL
)
1474 return (SMB_ADS_STAT_ERR
);
1476 dnshost_avp
.avp_attr
= SMB_ADS_ATTR_DNSHOST
;
1477 rc
= smb_ads_getattr(ah
->ld
, entry
, &dnshost_avp
);
1480 case SMB_ADS_STAT_FOUND
:
1482 * Returns SMB_ADS_STAT_DUP to avoid overwriting
1483 * the computer account of another system whose
1484 * NetBIOS name collides with that of the current
1487 if (strcasecmp(dnshost_avp
.avp_val
, fqhost
))
1488 rc
= SMB_ADS_STAT_DUP
;
1490 free(dnshost_avp
.avp_val
);
1493 case SMB_ADS_STAT_NOT_FOUND
:
1495 * Pre-created computer account doesn't have
1496 * the dNSHostname attribute. It's been observed
1497 * that the dNSHostname attribute is only set after
1498 * a successful domain join.
1499 * Returns SMB_ADS_STAT_FOUND as the account is
1500 * pre-created for the current system.
1502 rc
= SMB_ADS_STAT_FOUND
;
1509 if (rc
!= SMB_ADS_STAT_FOUND
)
1513 rc
= smb_ads_getattr(ah
->ld
, entry
, avpair
);
1520 * smb_ads_lookup_computer_n_attr
1522 * If avpair is NULL, checks the status of the specified computer account.
1523 * Otherwise, looks up the value of the specified computer account's attribute.
1524 * If found, the value field of the avpair will be allocated and set. The
1525 * caller should free the allocated buffer.
1528 * SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is
1530 * SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute
1532 * SMB_ADS_STAT_DUP - if the computer account is already used by other systems
1533 * in the AD. This could happen if the hostname of multiple
1534 * systems resolved to the same NetBIOS name.
1535 * SMB_ADS_STAT_ERR - any failure.
1537 static smb_ads_qstat_t
1538 smb_ads_lookup_computer_n_attr(smb_ads_handle_t
*ah
, smb_ads_avpair_t
*avpair
,
1539 int scope
, char *dn
)
1541 char *attrs
[3], filter
[SMB_ADS_MAXBUFLEN
];
1543 char sam_acct
[SMB_SAMACCT_MAXLEN
], sam_acct2
[SMB_SAMACCT_MAXLEN
];
1546 if (smb_getsamaccount(sam_acct
, sizeof (sam_acct
)) != 0)
1547 return (SMB_ADS_STAT_ERR
);
1550 attrs
[0] = SMB_ADS_ATTR_DNSHOST
;
1555 if (!avpair
->avp_attr
)
1556 return (SMB_ADS_STAT_ERR
);
1558 attrs
[1] = avpair
->avp_attr
;
1561 if (smb_ads_escape_search_filter_chars(sam_acct
, sam_acct2
) != 0)
1562 return (SMB_ADS_STAT_ERR
);
1564 (void) snprintf(filter
, sizeof (filter
),
1565 "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT
,
1568 if (ldap_search_s(ah
->ld
, dn
, scope
, filter
, attrs
, 0,
1569 &res
) != LDAP_SUCCESS
) {
1570 (void) ldap_msgfree(res
);
1571 return (SMB_ADS_STAT_NOT_FOUND
);
1574 rc
= smb_ads_get_qstat(ah
, res
, avpair
);
1575 /* free the search results */
1576 (void) ldap_msgfree(res
);
1581 * smb_ads_find_computer
1583 * Starts by searching for the system's AD computer object in the default
1584 * container (i.e. cn=Computers). If not found, searches the entire directory.
1585 * If found, 'dn' will be set to the distinguished name of the system's AD
1588 static smb_ads_qstat_t
1589 smb_ads_find_computer(smb_ads_handle_t
*ah
, char *dn
)
1591 smb_ads_qstat_t stat
;
1592 smb_ads_avpair_t avpair
;
1594 avpair
.avp_attr
= SMB_ADS_ATTR_DN
;
1595 smb_ads_get_default_comp_container_dn(ah
, dn
, SMB_ADS_DN_MAX
);
1596 stat
= smb_ads_lookup_computer_n_attr(ah
, &avpair
, LDAP_SCOPE_ONELEVEL
,
1599 if (stat
== SMB_ADS_STAT_NOT_FOUND
) {
1600 (void) strlcpy(dn
, ah
->domain_dn
, SMB_ADS_DN_MAX
);
1601 stat
= smb_ads_lookup_computer_n_attr(ah
, &avpair
,
1602 LDAP_SCOPE_SUBTREE
, dn
);
1605 if (stat
== SMB_ADS_STAT_FOUND
) {
1606 (void) strlcpy(dn
, avpair
.avp_val
, SMB_ADS_DN_MAX
);
1607 free(avpair
.avp_val
);
1614 * smb_ads_update_computer_cntrl_attr
1616 * Modify the user account control attribute of an existing computer
1619 * Returns LDAP error code.
1622 smb_ads_update_computer_cntrl_attr(smb_ads_handle_t
*ah
, int flags
, char *dn
)
1627 char usrctl_buf
[16];
1629 if (smb_ads_alloc_attr(attrs
, sizeof (attrs
) / sizeof (LDAPMod
*)) != 0)
1630 return (LDAP_NO_MEMORY
);
1632 attrs
[0]->mod_op
= LDAP_MOD_REPLACE
;
1633 attrs
[0]->mod_type
= SMB_ADS_ATTR_CTL
;
1635 (void) snprintf(usrctl_buf
, sizeof (usrctl_buf
), "%d", flags
);
1636 ctl_val
[0] = usrctl_buf
;
1638 attrs
[0]->mod_values
= ctl_val
;
1639 if ((ret
= ldap_modify_s(ah
->ld
, dn
, attrs
)) != LDAP_SUCCESS
) {
1640 syslog(LOG_NOTICE
, "ldap_modify: %s", ldap_err2string(ret
));
1643 smb_ads_free_attr(attrs
);
1648 * smb_ads_lookup_computer_attr_kvno
1650 * Lookup the value of the Kerberos version number attribute of the computer
1654 smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t
*ah
, char *dn
)
1656 smb_ads_avpair_t avpair
;
1659 avpair
.avp_attr
= SMB_ADS_ATTR_KVNO
;
1660 if (smb_ads_lookup_computer_n_attr(ah
, &avpair
,
1661 LDAP_SCOPE_BASE
, dn
) == SMB_ADS_STAT_FOUND
) {
1662 kvno
= atoi(avpair
.avp_val
);
1663 free(avpair
.avp_val
);
1672 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also
1673 * provides the domain join using Kerberos Authentication, Keberos
1674 * Change & Set password, and LDAP protocols. Basically, AD join
1675 * operation would require the following tickets to be acquired for the
1676 * the user account that is provided for the domain join.
1678 * 1) a Keberos TGT ticket,
1679 * 2) a ldap service ticket, and
1680 * 3) kadmin/changpw service ticket
1682 * The ADS client first sends a ldap search request to find out whether
1683 * or not the workstation trust account already exists in the Active Directory.
1684 * The existing computer object for this workstation will be removed and
1685 * a new one will be added. The machine account password is randomly
1686 * generated and set for the newly created computer object using KPASSWD
1687 * protocol (See RFC 3244). Once the password is set, our ADS client
1688 * finalizes the machine account by modifying the user acount control
1689 * attribute of the computer object. Kerberos keys derived from the machine
1690 * account password will be stored locally in /etc/krb5/krb5.keytab file.
1691 * That would be needed while acquiring Kerberos TGT ticket for the host
1692 * principal after the domain join operation.
1695 smb_ads_join(char *domain
, char *user
, char *usr_passwd
, char *machine_passwd
)
1697 smb_ads_handle_t
*ah
= NULL
;
1698 krb5_context ctx
= NULL
;
1699 krb5_principal
*krb5princs
= NULL
;
1701 boolean_t
delete = B_TRUE
;
1702 smb_ads_status_t rc
;
1704 int dclevel
, num
, usrctl_flags
= 0;
1705 smb_ads_qstat_t qstat
;
1706 char dn
[SMB_ADS_DN_MAX
];
1707 char tmpfile
[] = SMBNS_KRB5_KEYTAB_TMP
;
1709 smb_krb5_pn_set_t spns
;
1710 krb5_enctype
*encptr
;
1712 rc
= smb_ads_open_main(&ah
, domain
, user
, usr_passwd
);
1714 smb_ccache_remove(SMB_CCACHE_PATH
);
1718 if ((dclevel
= smb_ads_get_dc_level(ah
)) == -1) {
1720 smb_ccache_remove(SMB_CCACHE_PATH
);
1721 return (SMB_ADJOIN_ERR_GET_DCLEVEL
);
1724 qstat
= smb_ads_find_computer(ah
, dn
);
1726 case SMB_ADS_STAT_FOUND
:
1728 if (smb_ads_modify_computer(ah
, dclevel
, dn
) != 0) {
1730 smb_ccache_remove(SMB_CCACHE_PATH
);
1731 return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT
);
1735 case SMB_ADS_STAT_NOT_FOUND
:
1737 smb_ads_get_default_comp_dn(ah
, dn
, SMB_ADS_DN_MAX
);
1738 if (smb_ads_add_computer(ah
, dclevel
, dn
) != 0) {
1740 smb_ccache_remove(SMB_CCACHE_PATH
);
1741 return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT
);
1746 if (qstat
== SMB_ADS_STAT_DUP
)
1747 rc
= SMB_ADJOIN_ERR_DUP_TRUST_ACCT
;
1749 rc
= SMB_ADJOIN_ERR_TRUST_ACCT
;
1751 smb_ccache_remove(SMB_CCACHE_PATH
);
1755 if (smb_krb5_ctx_init(&ctx
) != 0) {
1756 rc
= SMB_ADJOIN_ERR_INIT_KRB_CTX
;
1757 goto adjoin_cleanup
;
1760 if (smb_krb5_get_pn_set(&spns
, SMB_PN_KEYTAB_ENTRY
, ah
->domain
) == 0) {
1761 rc
= SMB_ADJOIN_ERR_GET_SPNS
;
1762 goto adjoin_cleanup
;
1765 if (smb_krb5_get_kprincs(ctx
, spns
.s_pns
, spns
.s_cnt
, &krb5princs
)
1767 smb_krb5_free_pn_set(&spns
);
1768 rc
= SMB_ADJOIN_ERR_GET_SPNS
;
1769 goto adjoin_cleanup
;
1773 smb_krb5_free_pn_set(&spns
);
1775 /* New machine_passwd was filled in by our caller. */
1776 if (smb_krb5_setpwd(ctx
, ah
->domain
, machine_passwd
) != 0) {
1777 rc
= SMB_ADJOIN_ERR_KSETPWD
;
1778 goto adjoin_cleanup
;
1781 kvno
= smb_ads_lookup_computer_attr_kvno(ah
, dn
);
1784 * Only members of Domain Admins and Enterprise Admins can set
1785 * the TRUSTED_FOR_DELEGATION userAccountControl flag.
1786 * Try to set this, but don't fail the join if we can't.
1787 * Look into just removing this...
1790 SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT
|
1791 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION
|
1792 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD
);
1794 x
= smb_ads_update_computer_cntrl_attr(ah
, usrctl_flags
, dn
);
1795 if (x
!= LDAP_SUCCESS
&& (usrctl_flags
&
1796 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION
) != 0) {
1797 syslog(LOG_NOTICE
, "Unable to set the "
1798 "TRUSTED_FOR_DELEGATION userAccountControl flag on the "
1799 "machine account in Active Directory. It may be necessary "
1800 "to set that via Active Directory administration.");
1802 ~SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION
;
1805 if (x
!= LDAP_SUCCESS
) {
1806 rc
= SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR
;
1807 goto adjoin_cleanup
;
1810 if (mktemp(tmpfile
) == NULL
) {
1811 rc
= SMB_ADJOIN_ERR_WRITE_KEYTAB
;
1812 goto adjoin_cleanup
;
1815 encptr
= smb_ads_get_enctypes(dclevel
, &num
);
1816 if (smb_krb5_kt_populate(ctx
, ah
->domain
, krb5princs
, cnt
,
1817 tmpfile
, kvno
, machine_passwd
, encptr
, num
) != 0) {
1818 rc
= SMB_ADJOIN_ERR_WRITE_KEYTAB
;
1819 goto adjoin_cleanup
;
1823 rc
= SMB_ADS_SUCCESS
;
1826 if (new_acct
&& delete)
1827 smb_ads_del_computer(ah
, dn
);
1829 if (rc
!= SMB_ADJOIN_ERR_INIT_KRB_CTX
) {
1830 if (rc
!= SMB_ADJOIN_ERR_GET_SPNS
)
1831 smb_krb5_free_kprincs(ctx
, krb5princs
, cnt
);
1832 smb_krb5_ctx_fini(ctx
);
1835 /* commit keytab file */
1836 if (rc
== SMB_ADS_SUCCESS
) {
1837 if (rename(tmpfile
, SMBNS_KRB5_KEYTAB
) != 0) {
1838 (void) unlink(tmpfile
);
1839 rc
= SMB_ADJOIN_ERR_COMMIT_KEYTAB
;
1842 (void) unlink(tmpfile
);
1846 smb_ccache_remove(SMB_CCACHE_PATH
);
1850 struct xlate_table
{
1852 const char const *msg
;
1855 static const struct xlate_table
1857 { SMB_ADS_SUCCESS
, "Success" },
1858 { SMB_ADS_KRB5_INIT_CTX
,
1859 "Failed creating a Kerberos context." },
1860 { SMB_ADS_KRB5_CC_DEFAULT
,
1861 "Failed to resolve default credential cache." },
1862 { SMB_ADS_KRB5_PARSE_PRINCIPAL
,
1863 "Failed parsing the user principal name." },
1864 { SMB_ADS_KRB5_GET_INIT_CREDS_PW
,
1865 "Failed getting initial credentials. (Wrong password?)" },
1866 { SMB_ADS_KRB5_CC_INITIALIZE
,
1867 "Failed initializing the credential cache." },
1868 { SMB_ADS_KRB5_CC_STORE_CRED
,
1869 "Failed to update the credential cache." },
1870 { SMB_ADS_CANT_LOCATE_DC
,
1871 "Failed to locate a domain controller." },
1872 { SMB_ADS_LDAP_INIT
,
1873 "Failed to create an LDAP handle." },
1874 { SMB_ADS_LDAP_SETOPT
,
1875 "Failed to set an LDAP option." },
1876 { SMB_ADS_LDAP_SET_DOM
,
1877 "Failed to set the LDAP handle DN." },
1878 { SMB_ADS_LDAP_SASL_BIND
,
1879 "Failed to bind the LDAP handle. "
1880 "Usually indicates an authentication problem." },
1882 { SMB_ADJOIN_ERR_GEN_PWD
,
1883 "Failed to generate machine password." },
1884 { SMB_ADJOIN_ERR_GET_DCLEVEL
, "Unknown functional level of "
1885 "the domain controller. The rootDSE attribute named "
1886 "\"domainControllerFunctionality\" is missing from the "
1887 "Active Directory." },
1888 { SMB_ADJOIN_ERR_ADD_TRUST_ACCT
, "Failed to create the "
1889 "workstation trust account." },
1890 { SMB_ADJOIN_ERR_MOD_TRUST_ACCT
, "Failed to modify the "
1891 "workstation trust account." },
1892 { SMB_ADJOIN_ERR_DUP_TRUST_ACCT
, "Failed to create the "
1893 "workstation trust account because its name is already "
1895 { SMB_ADJOIN_ERR_TRUST_ACCT
, "Error in querying the "
1896 "workstation trust account" },
1897 { SMB_ADJOIN_ERR_INIT_KRB_CTX
, "Failed to initialize Kerberos "
1899 { SMB_ADJOIN_ERR_GET_SPNS
, "Failed to get Kerberos "
1901 { SMB_ADJOIN_ERR_KSETPWD
, "Failed to set machine password." },
1902 { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR
, "Failed to modify "
1903 "userAccountControl attribute of the workstation trust "
1905 { SMB_ADJOIN_ERR_WRITE_KEYTAB
, "Error in writing to local "
1906 "keytab file (i.e /etc/krb5/krb5.keytab)." },
1907 { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN
, "Failed to update idmap "
1909 { SMB_ADJOIN_ERR_IDMAP_REFRESH
, "Failed to refresh idmap "
1911 { SMB_ADJOIN_ERR_COMMIT_KEYTAB
, "Failed to commit changes to "
1912 "local keytab file (i.e. /etc/krb5/krb5.keytab)." },
1913 { SMB_ADJOIN_ERR_AUTH_NETLOGON
,
1914 "Failed to authenticate using the new computer account." },
1915 { SMB_ADJOIN_ERR_STORE_PROPS
,
1916 "Failed to store computer account information locally." },
1923 * Lookup an error message for the specific adjoin error code.
1926 smb_ads_strerror(int err
)
1928 const struct xlate_table
*xt
;
1930 if (err
> 0 && err
< SMB_ADS_ERRNO_GAP
)
1931 return (strerror(err
));
1933 for (xt
= adjoin_table
; xt
->msg
; xt
++)
1937 return ("Unknown error code.");
1941 smb_ads_log_errmsg(smb_ads_status_t err
)
1943 const char *s
= smb_ads_strerror(err
);
1944 syslog(LOG_NOTICE
, "%s", s
);
1949 * smb_ads_lookup_msdcs
1951 * If server argument is set, try to locate the specified DC.
1952 * If it is set to empty string, locate any DCs in the specified domain.
1953 * Returns the discovered DC via buf.
1955 * fqdn - fully-qualified domain name
1956 * dci - the name and address of the found DC
1959 smb_ads_lookup_msdcs(char *fqdn
, smb_dcinfo_t
*dci
)
1961 smb_ads_host_info_t
*hinfo
= NULL
;
1962 char ipstr
[INET6_ADDRSTRLEN
];
1965 return (NT_STATUS_INTERNAL_ERROR
);
1968 if ((hinfo
= smb_ads_find_host(fqdn
)) == NULL
)
1969 return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
);
1971 (void) smb_inet_ntop(&hinfo
->ipaddr
, ipstr
,
1972 SMB_IPSTRLEN(hinfo
->ipaddr
.a_family
));
1973 smb_tracef("msdcsLookupADS: %s [%s]", hinfo
->name
, ipstr
);
1975 (void) strlcpy(dci
->dc_name
, hinfo
->name
, sizeof (dci
->dc_name
));
1976 dci
->dc_addr
= hinfo
->ipaddr
;
1979 return (NT_STATUS_SUCCESS
);
1982 static krb5_enctype
*
1983 smb_ads_get_enctypes(int dclevel
, int *num
)
1985 krb5_enctype
*encptr
;
1987 if (dclevel
>= SMB_ADS_DCLEVEL_W2K8
) {
1988 *num
= sizeof (w2k8enctypes
) / sizeof (krb5_enctype
);
1989 encptr
= w2k8enctypes
;
1991 *num
= sizeof (pre_w2k8enctypes
) / sizeof (krb5_enctype
);
1992 encptr
= pre_w2k8enctypes
;