smbns_ads.c revision 7f667e74610492ddbce8ce60f52ece95d2401949
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <ldap.h>
#include <stdlib.h>
#include <netdb.h>
#include <pthread.h>
#include <unistd.h>
#include <resolv.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <assert.h>
#include <smbsrv/libsmbns.h>
#include <smbns_ads.h>
#include <smbns_dyndns.h>
#include <smbns_krb.h>
#define SMB_ADS_DN_MAX 300
#define SMB_ADS_MAXMSGLEN 512
#define SMB_ADS_COMPUTERS_CN "Computers"
#define SMB_ADS_COMPUTER_NUM_ATTR 8
#define SMB_ADS_SHARE_NUM_ATTR 3
#define SMB_ADS_SITE_MAX MAXHOSTNAMELEN
#define SMB_ADS_MSDCS_SRV_DC_RR "_ldap._tcp.dc._msdcs"
#define SMB_ADS_MSDCS_SRV_SITE_RR "_ldap._tcp.%s._sites.dc._msdcs"
/*
* domainControllerFunctionality
*
* This rootDSE attribute indicates the functional level of the DC.
*/
#define SMB_ADS_ATTR_DCLEVEL "domainControllerFunctionality"
#define SMB_ADS_DCLEVEL_W2K 0
#define SMB_ADS_DCLEVEL_W2K3 2
#define SMB_ADS_DCLEVEL_W2K8 3
/*
* msDs-supportedEncryptionTypes (Windows Server 2008 only)
*
* This attribute defines the encryption types supported by the system.
* Encryption Types:
* - DES cbc mode with CRC-32
* - DES cbc mode with RSA-MD5
* - AES-128
* - AES-256
*/
#define SMB_ADS_ATTR_ENCTYPES "msDs-supportedEncryptionTypes"
#define SMB_ADS_ENC_DES_CRC 1
#define SMB_ADS_ENC_DES_MD5 2
#define SMB_ADS_ENC_RC4 4
#define SMB_ADS_ENC_AES128 8
#define SMB_ADS_ENC_AES256 16
#define SMB_ADS_ATTR_SAMACCT "sAMAccountName"
#define SMB_ADS_ATTR_UPN "userPrincipalName"
#define SMB_ADS_ATTR_SPN "servicePrincipalName"
#define SMB_ADS_ATTR_CTL "userAccountControl"
#define SMB_ADS_ATTR_DNSHOST "dNSHostName"
#define SMB_ADS_ATTR_KVNO "msDS-KeyVersionNumber"
#define SMB_ADS_ATTR_DN "distinguishedName"
/*
* Length of "dc=" prefix.
*/
#define SMB_ADS_DN_PREFIX_LEN 3
#define SMB_ADS_MSDCS_SVC_CNT 2
static char *smb_ads_computer_objcls[] = {
"top", "person", "organizationalPerson",
};
static char *smb_ads_share_objcls[] = {
};
/* Cached ADS server to communicate with */
static mutex_t smb_ads_cached_host_mtx;
static char smb_ads_site[SMB_ADS_SITE_MAX];
static mutex_t smb_ads_site_mtx;
/*
* smb_ads_adjoin_errmsg
*
* Use the adjoin return status defined in adjoin_status_t as the index
* to this table.
*/
static char *smb_ads_adjoin_errmsg[] = {
"ADJOIN succeeded.",
"ADJOIN failed to get handle.",
"ADJOIN failed to generate machine password.",
"ADJOIN failed to add workstation trust account.",
"ADJOIN failed to modify workstation trust account.",
"ADJOIN failed to get list of encryption types.",
"ADJOIN failed to initialize kerberos context.",
"ADJOIN failed to get Kerberos principal.",
"ADJOIN failed to set machine account password on AD.",
"ADJOIN failed to modify CONTROL attribute of the account.",
"ADJOIN failed to write Kerberos keytab file.",
"ADJOIN failed to configure domain_name property for idmapd.",
"ADJOIN failed to refresh idmap service."
};
typedef struct smb_ads_avpair {
char *avp_attr;
char *avp_val;
/* query status */
typedef enum smb_ads_qstat {
SMB_ADS_STAT_ERR = -2,
static smb_ads_handle_t *smb_ads_open_main(char *, char *, char *);
static int smb_ads_bind(smb_ads_handle_t *);
static int smb_ads_add_computer(smb_ads_handle_t *, int, char *);
static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *);
static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *);
smb_ads_avpair_t *, int, char *);
static int smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *, int, char *);
static int smb_ads_gen_machine_passwd(char *, int);
static smb_ads_host_info_t *smb_ads_get_cached_host(void);
static void smb_ads_set_cached_host(smb_ads_host_info_t *);
static void smb_ads_free_cached_host(void);
static int smb_ads_get_spnset(char *, char **);
static void smb_ads_free_spnset(char **);
static int smb_ads_alloc_attr(LDAPMod **, int);
static void smb_ads_free_attr(LDAPMod **);
static int smb_ads_get_dc_level(smb_ads_handle_t *);
smb_ads_avpair_t *);
smb_ads_avpair_t *);
/*
* smb_ads_init
*
* Initializes the smb_ads_site global variable.
*/
void
smb_ads_init(void)
{
(void) mutex_lock(&smb_ads_site_mtx);
(void) smb_config_getstr(SMB_CI_ADS_SITE,
smb_ads_site, sizeof (smb_ads_site));
(void) mutex_unlock(&smb_ads_site_mtx);
}
/*
* smb_ads_refresh
*
* If the smb_ads_site has changed, clear the smb_ads_cached_host_info cache.
*/
void
smb_ads_refresh(void)
{
char new_site[SMB_ADS_SITE_MAX];
(void) mutex_lock(&smb_ads_site_mtx);
}
(void) mutex_unlock(&smb_ads_site_mtx);
}
/*
* smb_ads_build_unc_name
*
* Construct the UNC name of the share object in the format of
* \\hostname.domain\shareUNC
*
* Returns 0 on success, -1 on error.
*/
int
{
char my_domain[MAXHOSTNAMELEN];
return (-1);
return (0);
}
/*
* smb_ads_ldap_ping
*
* This is used to bind to an ADS server to see
* if it is still alive.
*
* Returns:
* -1: error
* 0: successful
*/
/*ARGSUSED*/
static int
{
return (-1);
if (status != LDAP_SUCCESS) {
(void) ldap_unbind(ld);
return (-1);
}
(void) ldap_unbind(ld);
return (0);
}
/*
* smb_ads_set_cached_host
*
* Cache the result of the ADS discovery if the cache is empty.
*/
static void
{
(void) mutex_lock(&smb_ads_cached_host_mtx);
if (!smb_ads_cached_host_info)
(void) mutex_unlock(&smb_ads_cached_host_mtx);
}
/*
* smb_ads_get_cached_host
*
* Get the cached ADS host info.
*/
static smb_ads_host_info_t *
smb_ads_get_cached_host(void)
{
(void) mutex_lock(&smb_ads_cached_host_mtx);
(void) mutex_unlock(&smb_ads_cached_host_mtx);
return (host);
}
/*
* smb_ads_is_sought_host
*
* Returns true, if the sought host name matches the input host (host) name.
* The sought host is expected to be in Fully Qualified Domain Name (FQDN)
* format.
*/
static boolean_t
{
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* smb_ads_match_hosts_same_domain
*
* Returns true, if the cached ADS host is in the same domain as the
* current (given) domain.
*/
static boolean_t
{
char *cached_host_domain;
return (B_FALSE);
if (cached_host_domain == NULL)
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* smb_ads_skip_ques_sec
* Skips the question section.
*/
static int
{
int i, len;
for (i = 0; i < qcnt; i++) {
return (-1);
}
return (0);
}
/*
* smb_ads_decode_host_ans_sec
* Decodes ADS hosts, priority, weight and port number from the answer
* section based on the current buffer pointer.
*/
static int
{
int i, len;
for (i = 0; i < ans_cnt; i++) {
ads_host = &ads_host_list[i];
return (-1);
/* skip type, class, ttl */
*ptr += 8;
/* data size */
*ptr += 2;
/* Get priority, weight */
/* LINTED: E_CONSTANT_CONDITION */
/* LINTED: E_CONSTANT_CONDITION */
/* port */
/* LINTED: E_CONSTANT_CONDITION */
/* domain name */
if (len < 0)
return (-1);
}
return (0);
}
/*
* smb_ads_skip_auth_sec
* Skips the authority section.
*/
static int
{
int i, len;
for (i = 0; i < ns_cnt; i++) {
return (-1);
/* skip type, class, ttl */
*ptr += 8;
/* get len of data */
/* LINTED: E_CONSTANT_CONDITION */
return (-1);
}
return (0);
}
/*
* smb_ads_decode_host_ip
*
* Decodes ADS hosts and IP Addresses from the additional section based
* on the current buffer pointer.
*/
static int
{
int i, j, len;
char hostname[MAXHOSTNAMELEN];
char *name;
for (i = 0; i < addit_cnt; i++) {
/* domain name */
if (len < 0)
return (-1);
/* skip type, class, TTL, data len */
*ptr += 8;
/* LINTED: E_CONSTANT_CONDITION */
/* LINTED: E_CONSTANT_CONDITION */
#ifdef BIG_ENDIAN
#else
for (i = 0; i < IN6ADDRSZ; i++)
#endif
}
/*
* find the host in the list of DC records from
* the answer section, that matches the host in the
* additional section, and set its IP address.
*/
for (j = 0; j < ans_cnt; j++) {
continue;
}
}
}
return (0);
}
/*
* smb_ads_dup_host_info
*
* Duplicates the passed smb_ads_host_info_t structure.
* Caller must free memory allocated by this method.
*
* Returns a reference to the duplicated smb_ads_host_info_t structure.
* Returns NULL on error.
*/
static smb_ads_host_info_t *
{
return (NULL);
return (dup_host);
}
/*
* smb_ads_hlist_alloc
*/
smb_ads_hlist_alloc(int count)
{
int size;
if (count == 0)
return (NULL);
return (NULL);
return (NULL);
}
return (hlist);
}
/*
* smb_ads_hlist_free
*/
static void
{
return;
}
/*
* smb_ads_query_dns_server
*
* This routine sends a DNS service location (SRV) query message to the
* DNS server via TCP to query it for a list of ADS server(s). Once a reply
* is received, the reply message is parsed to get the hostname. If there are IP
* addresses populated in the additional section then the additional section
* is parsed to obtain the IP addresses.
*
* The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to
* guarantee that Microsoft domain controllers are returned. Microsoft domain
* controllers are also ADS servers.
*
* The ADS hostnames are stored in the answer section of the DNS reply message.
* The IP addresses are stored in the additional section.
*
* The DNS reply message may be in compress formed. The compression is done
* on repeating domain name label in the message. i.e hostname.
*
* Upon successful completion, host list of ADS server(s) is returned.
*/
static smb_ads_host_list_t *
{
struct __res_state res_state;
union {
} msg;
return (NULL);
/* use TCP */
if (len < 0) {
smb_tracef("smbns_ads: DNS query for '%s' failed (%s)",
return (NULL);
}
smb_tracef("smbns_ads: DNS query %s: message too big (%d)",
return (NULL);
}
/* parse the reply, skip header and question sections */
/* check truncated message bit */
smb_tracef("smbns_ads: DNS query for '%s' detected "
"truncated TCP reply message", msdcs_svc_name);
return (NULL);
}
return (NULL);
}
/* walk through the answer section */
return (NULL);
}
/* check authority section */
if (ns_cnt > 0) {
return (NULL);
}
}
/*
* Check additional section to get IP address of ADS host.
*/
if (addit_cnt > 0) {
return (NULL);
}
}
return (hlist);
}
/*
* smb_ads_set_site_service
*
* This method sets the name of the site, to look for the ADS domain.
*/
static void
{
(void) mutex_lock(&smb_ads_site_mtx);
if (*smb_ads_site == '\0')
*site_service = '\0';
else
(void) mutex_unlock(&smb_ads_site_mtx);
}
/*
* smb_ads_getipnodebyname
*
* This method gets the IP address by doing a host name lookup.
*/
static int
{
struct hostent *h;
int error;
case AF_INET6:
AI_DEFAULT, &error);
return (-1);
break;
case AF_INET:
0, &error);
return (-1);
break;
default:
return (-1);
}
freehostent(h);
return (0);
}
/*
* smb_ads_find_host
*
* Finds a ADS host in a given domain.
*
* Parameters:
* domain: domain of ADS host.
* sought: the ADS host to be sought.
*
* If the ADS host is cached and it responds to ldap ping,
* - Cached ADS host is returned, if sought host is not specified.
* OR
* - Cached ADS host is returned, if the sought host matches the
* cached ADS host AND the cached ADS host is in the same domain
* as the given domain.
*
* If the ADS host is not cached in the given domain, the ADS host
* is returned if it matches the sought host.
*
* Returns:
* ADS host: fully qualified hostname, ip address, ldap port.
*/
/*ARGSUSED*/
{
int i;
char site_service[MAXHOSTNAMELEN];
char *msdcs_svc_name[SMB_ADS_MSDCS_SVC_CNT] =
/*
* If the cached host responds to ldap ping,
* - return cached ADS host, if sought host is not specified OR
* - return cached ADS host, if the sought host matches the cached
* ADS host AND the cached ADS host is in the same domain as the
* given domain.
*/
if (host) {
if (smb_ads_ldap_ping(host) == 0) {
if (!sought)
return (host);
return (host);
}
}
/*
* First look for ADS hosts in ADS site if configured. Then try
* without ADS site info.
*/
for (i = 0; i < SMB_ADS_MSDCS_SVC_CNT; i++) {
if (*msdcs_svc_name[i] == '\0')
continue;
break;
}
return (NULL);
/* Do a host lookup by hostname to get the IP address */
if (smb_ads_getipnodebyname(&hlistp[i]) < 0)
continue;
}
/* If a dc is sought, return it here */
(smb_ads_ldap_ping(&hlistp[i]) == 0)) {
return (host);
}
}
/* Select DC from DC list */
return (host);
}
return (NULL);
}
/*
* Return the number of dots in a string.
*/
static int
smb_ads_count_dots(const char *s)
{
int ndots = 0;
while (*s) {
if (*s++ == '.')
ndots++;
}
return (ndots);
}
/*
* Convert a domain name in dot notation to distinguished name format,
* for example: sun.com -> dc=sun,dc=com.
*
* Returns a pointer to an allocated buffer containing the distinguished
* name.
*/
static char *
smb_ads_convert_domain(const char *domain_name)
{
const char *s;
char *dn_name;
char buf[2];
int ndots;
int len;
return (NULL);
++ndots;
return (NULL);
s = domain_name;
while (*s) {
if (*s == '.') {
} else {
buf[0] = *s;
}
++s;
}
return (dn_name);
}
/*
* smb_ads_free_cached_host
*
* Free the memory use by the global smb_ads_cached_host_info & set it to NULL.
*/
static void
smb_ads_free_cached_host(void)
{
(void) mutex_lock(&smb_ads_cached_host_mtx);
if (smb_ads_cached_host_info) {
}
(void) mutex_unlock(&smb_ads_cached_host_mtx);
}
/*
* smb_ads_open
* Open a LDAP connection to an ADS server if the system is in domain mode.
* Acquire both Kerberos TGT and LDAP service tickets for the host principal.
*
* This function should only be called after the system is successfully joined
* to a domain.
*/
smb_ads_open(void)
{
char domain[MAXHOSTNAMELEN];
if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
return (NULL);
return (NULL);
}
/*
* smb_ads_open_main
* Open a LDAP connection to an ADS server.
* If ADS is enabled and the administrative username, password, and
* ADS domain are defined then query DNS to find an ADS server if this is the
* very first call to this routine. After an ADS server is found then this
* server will be used everytime this routine is called until the system is
* rebooted or the ADS server becomes unavailable then an ADS server will
* be queried again. After the connection is made then an ADS handle
* is created to be returned.
*
* After the LDAP connection, the LDAP version will be set to 3 using
* ldap_set_option().
*
* The smb_ads_bind() routine is also called before the ADS handle is returned.
* Parameters:
* domain - fully-qualified domain name
* user - the user account for whom the Kerberos TGT ticket and ADS
* service tickets are acquired.
* password - password of the specified user
*
* Returns:
* NULL : can't connect to ADS server or other errors
* smb_ads_handle_t* : handle to ADS server
*/
static smb_ads_handle_t *
{
int version = 3;
return (NULL);
return (NULL);
return (NULL);
}
!= LDAP_SUCCESS) {
(void) ldap_unbind(ld);
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
(void) mutex_lock(&smb_ads_site_mtx);
if (*smb_ads_site != '\0') {
(void) mutex_unlock(&smb_ads_site_mtx);
return (NULL);
}
} else {
}
(void) mutex_unlock(&smb_ads_site_mtx);
return (NULL);
}
return (ah);
}
/*
* smb_ads_close
* Close connection to ADS server and free memory allocated for ADS handle.
* LDAP unbind is called here.
* Parameters:
* ah: handle to ADS server
* Returns:
* void
*/
void
{
int len;
return;
/* close and free connection resources */
/* zero out the memory that contains user's password */
if (len > 0)
}
}
/*
* smb_ads_display_stat
* Display error message for GSS-API routines.
* Parameters:
* maj: GSS major status
* min: GSS minor status
* Returns:
* None
*/
static void
{
}
/*
* smb_ads_alloc_attr
*
* Since the attrs is a null-terminated array, all elements
* in the array (except the last one) will point to allocated
* memory.
*/
static int
{
int i;
for (i = 0; i < (num - 1); i++) {
return (-1);
}
}
return (0);
}
/*
* smb_ads_free_attr
* Free memory allocated when publishing a share.
* Parameters:
* attrs: an array of LDAPMod pointers
* Returns:
* None
*/
static void
{
int i;
for (i = 0; attrs[i]; i++) {
}
}
/*
* smb_ads_get_spnset
*
* Derives the core set of SPNs based on the FQHN.
* The spn_set is a null-terminated array of char pointers.
*
* Returns 0 upon success. Otherwise, returns -1.
*/
static int
{
int i;
for (i = 0; i < SMBKRB5_SPN_IDX_MAX; i++) {
return (-1);
}
}
return (0);
}
/*
* smb_ads_free_spnset
*
* Free the memory allocated for the set of SPNs.
*/
static void
smb_ads_free_spnset(char **spn_set)
{
int i;
for (i = 0; spn_set[i]; i++)
}
/*
* smb_ads_acquire_cred
* Called by smb_ads_bind() to get a handle to administrative user's credential
* stored locally on the system. The credential is the TGT. If the attempt at
* getting handle fails then a second attempt will be made after getting a
* new TGT.
* Please look at smb_ads_bind() for more information.
*
* Paramters:
* ah : handle to ADS server
* kinit_retry: if 0 then a second attempt will be made to get handle to the
* credential if the first attempt fails
* Returns:
* cred_handle: handle to the administrative user's credential (TGT)
* oid : contains Kerberos 5 object identifier
* kinit_retry: A 1 indicates that a second attempt has been made to get
* handle to the credential and no further attempts can be made
* -1 : error
* 0 : success
*/
static int
{
kinit_retry, "ads"));
}
/*
* smb_ads_establish_sec_context
* Called by smb_ads_bind() to establish a security context to an LDAP service
* on an ADS server. If the attempt at establishing the security context fails
* then a second attempt will be made by smb_ads_bind() if a new TGT has not
* been already obtained in ads_acquire_cred. The second attempt, if allowed,
* will obtained a new TGT here and a new handle to the credential will also be
* obtained in ads_acquire_cred. LDAP SASL bind is used to send and receive
* the GSS tokens to and from the ADS server.
* Please look at ads_bind for more information.
* Paramters:
* ah : handle to ADS server
* cred_handle : handle to administrative user's credential (TGT)
* oid : Kerberos 5 object identifier
* kinit_retry : if 0 then a second attempt can be made to establish a
* security context with ADS server if first attempt fails
* Returns:
* gss_context : security context to ADS server
* sercred : encrypted ADS server's supported security layers
* do_acquire_cred: if 1 then a second attempt will be made to establish a
* security context with ADS server after getting a new
* handle to the user's credential
* kinit_retry : if 1 then a second attempt will be made to establish a
* a security context and no further attempts can be made
* -1 : error
* 0 : success
*/
static int
int *kinit_retry, int *do_acquire_cred)
{
char service_name[SMB_ADS_MAXBUFLEN];
int stat;
int gss_flags;
&target_name)) != GSS_S_COMPLETE) {
if (oid != GSS_C_NO_OID)
return (-1);
}
do {
if (oid != GSS_C_NO_OID)
return (-1);
}
if (*sercred) {
}
if (stat != LDAP_SUCCESS &&
smb_tracef("smbns_ads: ldap_sasl_bind error: %s",
if (oid != GSS_C_NO_OID)
return (-1);
}
} while (maj != GSS_S_COMPLETE);
if (oid != GSS_C_NO_OID)
return (0);
}
/*
* smb_ads_negotiate_sec_layer
* Call by smb_ads_bind() to negotiate additional security layer for further
* communication after security context establishment. No additional security
* is needed so a "no security layer" is negotiated. The security layer is
* described in the SASL RFC 2478 and this step is needed for secure LDAP
* binding. LDAP SASL bind is used to send and receive the GSS tokens to and
* from the ADS server.
* Please look at smb_ads_bind for more information.
*
* Paramters:
* ah : handle to ADS server
* gss_context: security context to ADS server
* sercred : encrypted ADS server's supported security layers
* Returns:
* -1 : error
* 0 : success
*/
static int
{
int conf_state, sec_layer;
char auth_id[5];
int stat;
/* check for server supported security layer */
if (sercred)
return (-1);
}
if (!(sec_layer & 1)) {
if (sercred)
return (-1);
}
/* no security layer needed after successful binding */
auth_id[0] = 0x01;
/* byte 2-4: max client recv size in network byte order */
conf_state = 0;
return (-1);
}
&sercred);
smb_tracef("smbns_ads: ldap_sasl_bind error: %s:",
return (-1);
}
if (sercred)
return (0);
}
/*
* smb_ads_bind
* Use secure binding to bind to ADS server.
* Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with
* Kerberos 5 as the security mechanisn to authenticate, obtain a security
* context, and securely bind an administrative user so that other LDAP
* commands can be used, i.e. add and delete.
*
* To obtain the security context, a Kerberos ticket-granting ticket (TGT)
* for the user is needed to obtain a ticket for the LDAP service. To get
* a TGT for the user, the username and password is needed. Once a TGT is
* obtained then it will be stored locally and used until it is expired.
* This routine will automatically obtained a TGT for the first time or when
* it expired. LDAP SASL bind is then finally used to send GSS tokens to
* obtain a security context for the LDAP service on the ADS server. If
* there is any problem getting the security context then a new TGT will be
* obtain to try getting the security context once more.
*
* After the security context is obtain and established, the LDAP SASL bind
* is used to negotiate an additional security layer. No further security is
* needed so a "no security layer" is negotiated. After this the security
* context can be deleted and further LDAP commands can be sent to the ADS
* server until a LDAP unbind command is issued to the ADS server.
* Paramaters:
* ah: handle to ADS server
* Returns:
* -1: error
* 0: success
*/
static int
{
int kinit_retry, do_acquire_cred;
int rc = 0;
kinit_retry = 0;
do_acquire_cred = 0;
return (-1);
if (do_acquire_cred) {
do_acquire_cred = 0;
goto acquire_cred;
}
return (-1);
}
if (cred_handle != GSS_C_NO_CREDENTIAL)
return ((rc) ? -1 : 0);
}
/*
* smb_ads_add_share
* Call by smb_ads_publish_share to create share object in ADS.
* This routine specifies the attributes of an ADS LDAP share object. The first
* attribute and values define the type of ADS object, the share object. The
* second attribute and value define the UNC of the share data for the share
* object. The LDAP synchronous add command is used to add the object into ADS.
* The container location to add the object needs to specified.
* Parameters:
* ah : handle to ADS server
* adsShareName: name of share object to be created in ADS
* shareUNC : share name on NetForce
* adsContainer: location in ADS to create share object
*
* Returns:
* -1 : error
* 0 : success
*/
int
const char *unc_name, const char *adsContainer)
{
int j = 0;
char *share_dn;
return (-1);
return (-1);
}
smb_tracef("smbns_ads: %s: ldap_add error: %s",
return (ret);
}
return (0);
}
/*
* smb_ads_del_share
* Call by smb_ads_remove_share to remove share object from ADS. The container
* location to remove the object needs to specified. The LDAP synchronous
* delete command is used.
* Parameters:
* ah : handle to ADS server
* adsShareName: name of share object in ADS to be removed
* adsContainer: location of share object in ADS
* Returns:
* -1 : error
* 0 : success
*/
static int
const char *adsContainer)
{
char *share_dn;
return (-1);
smb_tracef("smbns_ads: ldap_delete error: %s",
return (-1);
}
return (0);
}
/*
* smb_ads_escape_search_filter_chars
*
* This routine will escape the special characters found in a string
* that will later be passed to the ldap search filter.
*
* RFC 1960 - A String Representation of LDAP Search Filters
* 3. String Search Filter Definition
* If a value must contain one of the characters '*' OR '(' OR ')',
* these characters
* should be escaped by preceding them with the backslash '\' character.
*
* RFC 2252 - LDAP Attribute Syntax Definitions
* a backslash quoting mechanism is used to escape
* the following separator symbol character (such as "'", "$" or "#") if
* it should occur in that string.
*/
static int
{
return (-1);
while (*src) {
if (!avail) {
*dst = 0;
return (-1);
}
switch (*src) {
case '\\':
case '\'':
case '$':
case '#':
case '*':
case '(':
case ')':
*dst++ = '\\';
avail--;
/* fall through */
default:
avail--;
}
}
*dst = 0;
return (0);
}
/*
* smb_ads_lookup_share
* The search filter is set to search for a specific share name in the
* specified ADS container. The LDSAP synchronous search command is used.
* Parameters:
* ah : handle to ADS server
* adsShareName: name of share object in ADS to be searched
* adsContainer: location of share object in ADS
* Returns:
* -1 : error
* 0 : not found
* 1 : found
*/
int
const char *adsContainer, char *unc_name)
{
char *share_dn;
char tmpbuf[SMB_ADS_MAXBUFLEN];
return (-1);
return (-1);
attrs[0] = "cn";
return (-1);
}
"(&(objectClass=volume)(uNCName=%s))", tmpbuf);
if (ret != LDAP_NO_SUCH_OBJECT)
smb_tracef("smbns_ads: %s: ldap_search error: %s",
(void) ldap_msgfree(res);
return (0);
}
/* no match is found */
(void) ldap_msgfree(res);
return (0);
}
/* free the search results */
(void) ldap_msgfree(res);
return (1);
}
/*
* smb_ads_publish_share
* Publish share into ADS. If a share name already exist in ADS in the same
* container then the existing share object is removed before adding the new
* share object.
* Parameters:
* ah : handle return from smb_ads_open
* adsShareName: name of share to be added to ADS directory
* shareUNC : name of share on client, can be NULL to use the same name
* as adsShareName
* adsContainer: location for share to be added in ADS directory, ie
* ou=share_folder
* uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR
* to use host ip addr for UNC.
* Returns:
* -1 : error
* 0 : success
*/
int
{
int ret;
char unc_name[SMB_ADS_MAXBUFLEN];
return (-1);
return (-1);
switch (ret) {
case 1:
break;
case 0:
if (ret == LDAP_ALREADY_EXISTS)
ret = -1;
break;
case -1:
default:
/* return with error code */
ret = -1;
}
return (ret);
}
/*
* smb_ads_remove_share
* Remove share from ADS. A search is done first before explicitly removing
* the share.
* Parameters:
* ah : handle return from smb_ads_open
* adsShareName: name of share to be removed from ADS directory
* adsContainer: location for share to be removed from ADS directory, ie
* ou=share_folder
* Returns:
* -1 : error
* 0 : success
*/
int
{
int ret;
char unc_name[SMB_ADS_MAXBUFLEN];
return (-1);
return (-1);
if (ret == 0)
return (0);
if (ret == -1)
return (-1);
}
/*
* smb_ads_get_default_comp_container_dn
*
* Build the distinguished name for the default computer conatiner (i.e. the
* pre-defined Computers container).
*/
static void
{
}
/*
* smb_ads_get_default_comp_dn
*
* Build the distinguished name for this system.
*/
static void
{
char nbname[NETBIOS_NAME_SZ];
char container_dn[SMB_ADS_DN_MAX];
}
/*
* smb_ads_add_computer
*
* Returns 0 upon success. Otherwise, returns -1.
*/
static int
{
}
/*
* smb_ads_modify_computer
*
* Returns 0 upon success. Otherwise, returns -1.
*/
static int
{
}
/*
* smb_ads_get_dc_level
*
* Returns the functional level of the DC upon success.
* Otherwise, -1 is returned.
*/
static int
{
char *attr[2];
char **vals;
int rc = -1;
attr[0] = SMB_ADS_ATTR_DCLEVEL;
0, &res) != LDAP_SUCCESS) {
(void) ldap_msgfree(res);
return (-1);
}
/* no match for the specified attribute is found */
(void) ldap_msgfree(res);
return (-1);
}
if (entry) {
SMB_ADS_ATTR_DCLEVEL)) == NULL) {
/*
* Observed the values aren't populated
* by the Windows 2000 server.
*/
(void) ldap_msgfree(res);
return (SMB_ADS_DCLEVEL_W2K);
}
}
(void) ldap_msgfree(res);
return (rc);
}
static int
{
return (-1);
return (0);
}
static int
{
char *encrypt_val[2];
int j = -1;
int ret, usrctl_flags = 0;
char sam_acct[SMB_SAMACCT_MAXLEN];
char fqhost[MAXHOSTNAMELEN];
char *user_principal;
char usrctl_buf[16];
char encrypt_buf[16];
int max;
return (-1);
return (-1);
return (-1);
/*
*/
if (dclevel == SMB_ADS_DCLEVEL_W2K8)
else
if (user_principal == NULL) {
return (-1);
}
return (-1);
}
/* objectClass attribute is not modifiable. */
if (op == LDAP_MOD_ADD) {
}
sam_val[1] = 0;
usr_val[0] = user_principal;
usr_val[1] = 0;
ctl_val[0] = usrctl_buf;
ctl_val[1] = 0;
fqh_val[1] = 0;
/* enctypes support starting in Windows Server 2008 */
if (dclevel > SMB_ADS_DCLEVEL_W2K3) {
encrypt_val[0] = encrypt_buf;
encrypt_val[1] = 0;
}
switch (op) {
case LDAP_MOD_ADD:
smb_tracef("smbns_ads: ldap_add error: %s",
ret = -1;
}
break;
case LDAP_MOD_REPLACE:
smb_tracef("smbns_ads: ldap_replace error: %s",
ret = -1;
}
break;
default:
ret = -1;
}
return (ret);
}
/*
* Delete an ADS computer account.
*/
static void
{
int rc;
smb_tracef("smbns_ads: ldap_delete error: %s",
}
/*
* Gets the value of the given attribute.
*/
static smb_ads_qstat_t
{
char **vals;
if (!vals)
return (SMB_ADS_STAT_NOT_FOUND);
if (!vals[0]) {
return (SMB_ADS_STAT_NOT_FOUND);
}
return (rc);
}
/*
* Process query's result.
*/
static smb_ads_qstat_t
{
char fqhost[MAXHOSTNAMELEN];
return (SMB_ADS_STAT_ERR);
return (SMB_ADS_STAT_NOT_FOUND);
return (SMB_ADS_STAT_ERR);
switch (rc) {
case SMB_ADS_STAT_FOUND:
/*
* Returns SMB_ADS_STAT_DUP to avoid overwriting
* the computer account of another system whose
* NetBIOS name collides with that of the current
* system.
*/
break;
case SMB_ADS_STAT_NOT_FOUND:
/*
* Pre-created computer account doesn't have
* the dNSHostname attribute. It's been observed
* that the dNSHostname attribute is only set after
* a successful domain join.
* Returns SMB_ADS_STAT_FOUND as the account is
* pre-created for the current system.
*/
break;
default:
break;
}
if (rc != SMB_ADS_STAT_FOUND)
return (rc);
if (avpair)
return (rc);
}
/*
* smb_ads_lookup_computer_n_attr
*
* If avpair is NULL, checks the status of the specified computer account.
* Otherwise, looks up the value of the specified computer account's attribute.
* If found, the value field of the avpair will be allocated and set. The
* caller should free the allocated buffer.
*
* Return:
* SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is
* found.
* SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute
* is not found.
* SMB_ADS_STAT_DUP - if the computer account is already used by other systems
* in the AD. This could happen if the hostname of multiple
* systems resolved to the same NetBIOS name.
* SMB_ADS_STAT_ERR - any failure.
*/
static smb_ads_qstat_t
{
return (SMB_ADS_STAT_ERR);
attrs[0] = SMB_ADS_ATTR_DNSHOST;
if (avpair) {
return (SMB_ADS_STAT_ERR);
}
return (SMB_ADS_STAT_ERR);
"(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT,
&res) != LDAP_SUCCESS) {
(void) ldap_msgfree(res);
return (SMB_ADS_STAT_NOT_FOUND);
}
/* free the search results */
(void) ldap_msgfree(res);
return (rc);
}
/*
* smb_ads_find_computer
*
* Starts by searching for the system's AD computer object in the default
* container (i.e. cn=Computers). If not found, searches the entire directory.
* If found, 'dn' will be set to the distinguished name of the system's AD
* computer object.
*/
static smb_ads_qstat_t
{
dn);
if (stat == SMB_ADS_STAT_NOT_FOUND) {
}
if (stat == SMB_ADS_STAT_FOUND) {
}
return (stat);
}
/*
* smb_ads_update_computer_cntrl_attr
*
* Modify the user account control attribute of an existing computer
* object on AD.
*
* Returns LDAP error code.
*/
static int
{
char *ctl_val[2];
int ret = 0;
char usrctl_buf[16];
return (LDAP_NO_MEMORY);
ctl_val[0] = usrctl_buf;
ctl_val[1] = 0;
smb_tracef("smbns_ads: ldap_modify error: %s",
}
return (ret);
}
/*
* smb_ads_lookup_computer_attr_kvno
*
* Lookup the value of the Kerberos version number attribute of the computer
* account.
*/
static krb5_kvno
{
int kvno = 1;
}
return (kvno);
}
/*
* smb_ads_gen_machine_passwd
*
* Returned a null-terminated machine password generated randomly
* from [0-9a-zA-Z] character set. In order to pass the password
* quality check (three character classes), an uppercase letter is
* used as the first character of the machine password.
*/
static int
{
char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK"
"LMNOPQRSTUVWXYZ";
int i, data_idx;
if (!machine_passwd || bufsz == 0)
return (-1);
/*
* The decimal value of upper case 'A' is 65. Randomly pick
* an upper-case letter from the ascii table.
*/
}
return (0);
}
/*
* smb_ads_join
*
* Besides the NT-4 style domain join (using MS-RPC), CIFS server also
* provides the domain join using Kerberos Authentication, Keberos
* Change & Set password, and LDAP protocols. Basically, AD join
* operation would require the following tickets to be acquired for the
* the user account that is provided for the domain join.
*
* 1) a Keberos TGT ticket,
* 2) a ldap service ticket, and
*
* The ADS client first sends a ldap search request to find out whether
* or not the workstation trust account already exists in the Active Directory.
* The existing computer object for this workstation will be removed and
* a new one will be added. The machine account password is randomly
* generated and set for the newly created computer object using KPASSWD
* protocol (See RFC 3244). Once the password is set, our ADS client
* finalizes the machine account by modifying the user acount control
* attribute of the computer object. Kerberos keys derived from the machine
* account password will be stored locally in /etc/krb5/krb5.keytab file.
* That would be needed while acquiring Kerberos TGT ticket for the host
* principal after the domain join operation.
*/
int len)
{
char dn[SMB_ADS_DN_MAX];
char *tmpfile;
/*
* Call library functions that can be used to get
* the list of encryption algorithms available on the system.
* (similar to what 'encrypt -l' CLI does). For now,
* unless someone has modified the configuration of the
* cryptographic framework (very unlikely), the following is the
* list of algorithms available on any system running Nevada
* by default.
*/
};
};
return (SMB_ADJOIN_ERR_GET_HANDLE);
}
return (SMB_ADJOIN_ERR_GEN_PASSWD);
}
return (SMB_ADJOIN_ERR_GET_DCLEVEL);
}
switch (qstat) {
case SMB_ADS_STAT_FOUND:
return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT);
}
break;
case SMB_ADS_STAT_NOT_FOUND:
return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT);
}
break;
default:
if (qstat == SMB_ADS_STAT_DUP)
else
return (rc);
}
if (smb_krb5_ctx_init(&ctx) != 0) {
goto adjoin_cleanup;
}
goto adjoin_cleanup;
}
machine_passwd) != 0) {
goto adjoin_cleanup;
}
/*
* Only members of Domain Admins and Enterprise Admins can set
* the TRUSTED_FOR_DELEGATION userAccountControl flag.
*/
== LDAP_INSUFFICIENT_ACCESS) {
"TRUSTED_FOR_DELEGATION userAccountControl flag on "
"the machine account in Active Directory. Please refer "
"to the Troubleshooting guide for more information.");
} else {
}
if (des_only)
!= 0) {
goto adjoin_cleanup;
}
if (dclevel == SMB_ADS_DCLEVEL_W2K8) {
} else {
}
goto adjoin_cleanup;
}
if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) {
if (rc != SMB_ADJOIN_ERR_GET_SPNS)
}
/* commit keytab file */
if (rc == SMB_ADJOIN_SUCCESS) {
} else {
/* Set IDMAP config */
} else {
/* Refresh IDMAP service */
if (smb_config_refresh_idmap() != 0)
}
}
} else {
}
return (rc);
}
/*
* smb_adjoin_report_err
*
* Display error message for the specific adjoin error code.
*/
char *
{
return ("ADJOIN: unknown status");
return (smb_ads_adjoin_errmsg[status]);
}
/*
* smb_ads_select_pdc
*
* This method walks the list of DCs and returns the first DC record that
* responds to ldap ping and whose IP address is same as the IP address set in
* the Preferred Domain Controller (pdc) property.
*
* Returns a pointer to the found DC record.
* Returns NULL, on error or if no DC record is found.
*/
static smb_ads_host_info_t *
{
int i;
return (NULL);
for (i = 0; i < cnt; i++) {
(smb_ads_ldap_ping(hentry) == 0))
return (hentry);
}
return (NULL);
}
/*
* smb_ads_select_dcfromsubnet
*
* This method walks the list of DCs and returns the first DC record that
* responds to ldap ping and is in the same subnet as the host.
*
* Returns a pointer to the found DC record.
* Returns NULL, on error or if no DC record is found.
*/
static smb_ads_host_info_t *
{
int i;
if (smb_nic_getfirst(&ni) != 0)
return (NULL);
do {
for (i = 0; i < cnt; i++) {
if (smb_ads_ldap_ping(hentry) == 0)
return (hentry);
}
}
} while (smb_nic_getnext(&ni) == 0);
return (NULL);
}
/*
* smb_ads_select_dcfromlist
*
* This method walks the list of DCs and returns the first DC that
* responds to ldap ping.
*
* Returns a pointer to the found DC record.
* Returns NULL if no DC record is found.
*/
static smb_ads_host_info_t *
{
int i;
for (i = 0; i < cnt; i++) {
if (smb_ads_ldap_ping(hentry) == 0)
return (hentry);
}
return (NULL);
}
/*
* smb_ads_dc_compare
*
* Comparision function for sorting host entries (SRV records of DC) via qsort.
* RFC 2052/2782 are taken as reference, while implementing this algorithm.
*
* Domain Controllers(DCs) with lowest priority in their SRV DNS records
* are selected first. If they have equal priorities, then DC with highest
* weight in its SRV DNS record is selected. If the priority and weight are
* both equal, then the DC at the top of the list is selected.
*/
static int
smb_ads_dc_compare(const void *p, const void *q)
{
return (-1);
return (1);
/* Priorities are equal */
return (1);
return (-1);
return (0);
}
/*
* smb_ads_select_dc
*
* The list of ADS hosts returned by ADS lookup, is sorted by lowest priority
* and highest weight. On this sorted list, following additional rules are
* applied, to select a DC.
*
* - If there is a configured PDC and it's in the ADS list,
* then return the DC, if it responds to ldap ping.
* - If there is a DC in the same subnet, then return the DC,
* if it responds to ldap ping.
* - Else, return first DC that responds to ldap ping.
*
* A reference to the host entry from input host list is returned.
*
* Returns NULL on error.
*/
static smb_ads_host_info_t *
{
return (NULL);
if (smb_ads_ldap_ping(hentry) == 0)
return (hentry);
}
/* Sort the list by priority and weight */
sizeof (smb_ads_host_info_t), smb_ads_dc_compare);
return (hentry);
return (hentry);
return (hentry);
return (NULL);
}
/*
* smb_ads_lookup_msdcs
*
* If server argument is set, try to locate the specified DC.
* If it is set to empty string, locate any DCs in the specified domain.
* Returns the discovered DC via buf.
*
* fqdn - fully-qualified domain name
* server - fully-qualifed hostname of a DC
* buf - the hostname of the discovered DC
*/
{
char *p;
char *sought_host;
char ipstr[INET6_ADDRSTRLEN];
return (B_FALSE);
*buf = '\0';
return (B_FALSE);
/*
* Remove the domain extension
*/
*p = '\0';
return (B_TRUE);
}