/*
* 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
*/
/*
*/
/*
* Client NDR RPC interface.
*/
#include <errno.h>
#include <time.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <thread.h>
#include <unistd.h>
#include <syslog.h>
#include <synch.h>
#include <pthread.h>
#include <netsmb/libsmbfs.h>
#include <smbsrv/libntsvcs.h>
#include <ntsvcs.h>
#include <smbsrv/libsmbns.h>
#ifndef EAUTH
#endif
typedef struct ndr_svinfo {
} ndr_svinfo_t;
/*
* info cache is used to hold information about domain controllers.
*
* Writers: Both DC discovery and failover threads make SRVSVC NetServerGetInfo
* request to obtain information about the selected DC. On success,
* ndr_svinfo_update() is called to update the cache.
* NOTE: this cache is updated every 20 minutes due to the periodic
* discovery cycle or upon a successful DC failover.
* Readers: DC Monitor and door service threads that are launched to make
* non-SRVSVC MS-RPC requests call ndr_svinfo_lookup() to obtain
* information about the selected DC from the cache.
*
* In the future, if our non-SRVSVC MS-RPC clients ever need to talk to
* remote servers other than domain controllers, then entries for those remote
* servers can be added by the door service threads which are launched to
* process the MS-RPC requests.
*/
typedef struct ndr_svlist {
} ndr_svlist_t;
static void ndr_rpc_init(void);
static void ndr_rpc_fini(void);
static void ndr_xa_release(ndr_client_t *);
static void ndr_rpc_uncgen(const char *, const char *, char *, size_t);
static int ndr_svinfo_lookup(const char *, const char *,
static boolean_t ndr_svinfo_match(const char *, const char *, const
ndr_svinfo_t *);
/* Bindings to remote MS-RPC services are now serialized on bind_mutex. */
/*
* All NDR RPC service initialization is invoked from here.
*/
void
ntsvcs_init(void)
{
(void) mutex_lock(&libntsvcs_mutex);
if (!initialized) {
smb_ipc_init();
ndr_rpc_init();
}
(void) mutex_unlock(&libntsvcs_mutex);
}
void
ntsvcs_fini(void)
{
(void) mutex_lock(&libntsvcs_mutex);
if (initialized) {
ndr_rpc_fini();
}
(void) mutex_unlock(&libntsvcs_mutex);
}
/*
* Initialize the RPC client interface: create the server info cache.
*/
static void
ndr_rpc_init(void)
{
if (!ndr_svlist.svl_init) {
}
}
/*
* Terminate the RPC client interface: flush and destroy the server info
* cache.
*/
static void
ndr_rpc_fini(void)
{
if (ndr_svlist.svl_init) {
}
}
}
/*
* Returns Kerberos realm from the passed domain name.
* Realm is obtained from the resolver by comparing the passed domain name with
* - the resolver domain name or
* - the first part of the resolver domain name.
* Caller should free memory allocated to the returned realm.
*/
static char *
{
char *dc_realm, *p;
return (NULL);
return (NULL);
*p = '\0';
return (NULL);
}
}
return (dc_realm);
}
/*
* This functions sets up Kerberos credentials for authenticated outbound
* requests to the domain controller. If the credentials cannot be acquired,
* smb_kinit() function is called to establish credentials.
*
* Returns 0 if username is anonymous, setting cred cache or kinit fails.
* Returns -1 in case of errors.
*/
static int
{
int rc = 0;
return (0);
if (smb_is_samaccount(username)) {
return (-1);
return (-1);
} else {
if (smb_krb5_ccache_set(SMB_KRB5_CCACHE4USER) != 0)
return (0);
return (-1);
}
if (rc != 0)
return (0);
}
/*
* Setup libsmbfs keychain for authenticating the connection.
*/
static int
{
int rc;
return (rc);
}
/*
* This call must be made to initialize an RPC client structure and bind
* to the remote service before any RPCs can be exchanged with that service.
*
* The mlsvc_handle_t is a wrapper that is used to associate an RPC handle
* with the client context for an instance of the interface. The handle
* is zeroed to ensure that it doesn't look like a valid handle -
* handle content is provided by the remove service.
*
* The client points to this top-level handle so that we know when to
* unbind and teardown the connection. As each handle is initialized it
* will inherit a reference to the client context.
*
* it from the UNC path and use it to open a socket to the server.
* Using the NetBIOS name may not always work since there is no guarantee
* that NetBIOS name resolution will be available.
*/
int
{
const char *errmsg;
int fid;
int rc;
char *realm;
return (-1);
(void) mutex_lock(&bind_mutex);
(void) mutex_unlock(&bind_mutex);
return (-1);
}
/* Anonymous connection. */
username = "";
}
/* Setup Kerberos credentials for libsmbfs */
if (rc != 0) {
" kerberos setup failed",
(void) mutex_unlock(&bind_mutex);
return (-1);
}
}
/* Setup Keychain for libsmbfs */
if (rc != 0) {
"ndr_rpc_bind[tid=%d] %s %s: keychain add failed",
(void) mutex_unlock(&bind_mutex);
return (-1);
}
/*
* Set the default based on the assumption that most
* servers will be Windows 2000 or later.
* Don't lookup the svinfo if this is a SRVSVC request
* because the SRVSVC is used to get the server info.
* None of the SRVSVC calls depend on the server info.
*/
svinfo.sv_version_minor = 0;
/*
* It's rare that the lookup could fail since DC discovery
* and failover services are responsible for creating
* entries for the currently selected DC at service startup
* and the previously selected DC where failures have been
* reported, respectively. But it could happen when the
* machine password stored locally no longer matches with
* what stored in Active Directory.
*/
(void) mutex_unlock(&bind_mutex);
== 0)
&svinfo);
(void) mutex_lock(&bind_mutex);
}
}
(void) mutex_unlock(&bind_mutex);
return (-1);
}
switch (rc) {
case ENOENT:
errmsg = "server does not support this named pipe";
break;
case ENODATA:
errmsg = "unable to resolve server name";
break;
case EAUTH:
break;
default:
break;
}
(void) mutex_unlock(&bind_mutex);
return (-1);
}
(void) smbfs_fh_close(fid);
(void) mutex_unlock(&bind_mutex);
return (-1);
}
if (NDR_DRC_IS_FAULT(rc)) {
(void) smbfs_fh_close(fid);
(void) mutex_unlock(&bind_mutex);
return (-1);
}
(void) mutex_unlock(&bind_mutex);
return (0);
}
/*
* Check the errno to determine whether the binding failure is caused
* by the the selected DC being unreachable.
*
* ENODATA
* Solaris SMB client sets ENODATA upon name->IP resolution failures.
*
* ETIME
* Solaris SMB client sets ETIME if the data doesn't arrive before the
* timeout. See nb_ssn_pollin().
*
* EPROTO
* Solaris SMB client sets EPROTO when it fails to send or receive NetBIOS
*
* NOTE: this function relies on the errno set by SMB client API to determine
* whether a remote server is reachable or not. Since the errno(s) are not a
* committed interface and can, and most likely will change in the future,
* any changes to the SMB client APIs that are consumed by ndr_rpc_bind() today
* will need to regression tested the DC failover implementation and the
* following errno(s) should be updated accordingly.
*/
{
switch (err) {
case EBUSY: /* 16 */
case ENODATA: /* 61 */
case ETIME: /* 62 */
case EPROTO: /* 71 */
case ENETUNREACH: /* 128 */
case ENETRESET: /* 129 */
case ECONNABORTED: /* 130 */
case ECONNRESET: /* 131 */
case ETIMEDOUT: /* 145 */
case EHOSTDOWN: /* 147 */
case EHOSTUNREACH: /* 148 */
return (B_TRUE);
default:
return (B_FALSE);
}
}
/*
* The server parameter is expected to be a fully qualified hostname, hostname,
* NetBIOS name or an IP address.
*/
static void
{
const char *ep;
ep += 4;
}
if (*server == '\0') {
} else {
}
}
/*
* Unbind and close the pipe to an RPC service.
*
* If the heap has been preserved we need to go through an xa release.
* The heap is preserved during an RPC call because that's where data
* returned from the server is stored.
*
* Otherwise we destroy the heap directly.
*/
void
{
if (clnt->heap_preserved)
else
}
/*
* Call the RPC function identified by opnum. The remote service is
* identified by the handle, which should have been initialized by
* ndr_rpc_bind.
*
* If the RPC call is successful (returns 0), the caller must call
* ndr_rpc_release to release the heap. Otherwise, we release the
* heap here.
*/
int
{
int rc;
return (-1);
/*
* Always clear the nonull flag to ensure
* it is not applied to subsequent calls.
*/
if (NDR_DRC_IS_FAULT(rc)) {
return (-1);
}
return (0);
}
/*
* Some MSRPC services declare an RPC binding handle based on the
* server's NetBIOS name prefixed (UNC style) by two backslashes.
* The NetBIOS name is derived from the server's hostname.
* The services are inconsistent on handle validation by the server.
*
* The RPC binding handle may be explicitly declared as a handle
* in the IDL (as shown below) or it may simply appear as a regular
* wchar_t parameter to an RPC.
*
* typedef [handle] wchar_t *RPC_HANDLE;
*/
void
{
char *p;
*p = '\0';
(void) smb_strupr(nbname);
}
void *
{
char *nbhandle;
return (NULL);
return (nbhandle);
}
/*
* Outgoing strings should not be null terminated.
*/
void
{
}
/*
* Return a reference to the server info.
*/
const srvsvc_server_info_t *
{
}
/*
* Return the RPC server OS level.
*/
{
}
/*
* Get the session key from a bound RPC client handle.
*
* The key returned is the 16-byte "user session key"
* established by the underlying authentication protocol
* (either Kerberos or NTLM). This key is needed for
* SAM RPC calls such as SamrSetInformationUser, etc.
* See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25.
*
* The RPC endpoint must be bound when this is called
* (so that clnt->fid is an open named pipe)
*
* Returns zero (success) or an errno.
*/
int
{
int rc;
return (EINVAL);
return (rc);
}
void *
{
return (NULL);
}
{
}
/*
* Must be called by RPC clients to free the heap after a successful RPC
* call, i.e. ndr_rpc_call returned 0. The caller should take a copy
* of any data returned by the RPC prior to calling this function because
* returned data is in the heap.
*/
void
{
if (clnt->heap_preserved)
else
}
/*
* Returns true if the handle is null.
* Otherwise returns false.
*/
{
return (B_TRUE);
return (B_TRUE);
return (B_FALSE);
}
/*
* Returns true if the handle is the top level bind handle.
* Otherwise returns false.
*/
{
}
/*
* Pass the client reference from parent to child.
*/
void
{
}
void
{
char *s = "unknown";
switch (NT_SC_SEVERITY(status)) {
s = "success";
break;
s = "info";
break;
s = "warning";
break;
case NT_STATUS_SEVERITY_ERROR:
if (status == NT_STATUS_NONE_MAPPED)
s = "debug";
else
s = "error";
break;
}
if (handle) {
}
smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
}
/*
* The following functions provide the client callback interface.
* If the caller hasn't provided a heap, create one here.
*/
static int
{
int rc;
return (-1);
}
if (rc == 0)
if (rc != 0) {
return (-1);
}
return (0);
}
/*
* This is the entry pointy for an RPC client call exchange with
* a server, which will result in an SmbTransact request.
* On success, the receive stream pdu_size indicates the number
* of bytes received.
*
* TBD: handling overflow.
*/
static int
{
int nbytes;
int overflow;
int rc;
if (nbytes > NDR_DEFAULT_FRAGSZ)
if (rc) {
return (-1);
}
return (0);
}
/*
* This entry point will be invoked if the xa-exchange response contained
* only the first fragment of a multi-fragment response. The RPC client
* code will then make repeated xa-read requests to obtain the remaining
* fragments, which will result in SmbReadX requests.
*
* SmbReadX should return the number of bytes received, in which case we
* expand the PDU size to include the received data, or a negative error
* code.
*/
static int
{
int len;
int nbytes;
return (-1);
if (len > NDR_XA_READSZ)
len = NDR_XA_READSZ;
if (nbytes < 0) {
return (-1);
}
return (-1);
}
return (nbytes);
}
/*
* Preserve the heap so that the client application has access to data
* returned from the server after an RPC call.
*/
static void
{
}
/*
* Dispose of the transaction streams. If the heap has not been
* preserved, we can destroy it here.
*/
static void
{
if (!clnt->heap_preserved) {
}
}
/*
* Dispose of a preserved heap.
*/
static void
{
if (clnt->heap_preserved) {
}
}
/*
* Lookup platform, type and version information about a server.
*/
static int
{
if (!ndr_svlist.svl_init) {
return (-1);
}
sizeof (srvsvc_server_info_t));
return (0);
}
}
return (-1);
}
/*
* Adding a new entry or replacing an existing entry for the
* specified server in the server info cache.
*/
int
const srvsvc_server_info_t *svinfo)
{
if (!ndr_svlist.svl_init) {
return (-1);
}
continue;
}
}
return (-1);
}
return (0);
}
/*
* Compare the specified server and domain against the server and domain fields
* of each cache entry, respectively . If we find a cache entry with a matching
* server name but its corresponding domain information doesn't match, it's
* likely that the specified domain and the cached domain are stored in
* different formats. Converts the domain names from the fully-qualified
* format to NetBIOS format for further comparison.
*/
static boolean_t
const ndr_svinfo_t *svi)
{
return (B_TRUE);
return (B_FALSE);
return (B_FALSE);
}
*p = '\0';
*p = '\0';
return (found);
}
return (B_FALSE);
}
/*
* Compare the time here with the remote time on the server
* and report clock skew.
*/
int
{
int priority;
return (-1);
}
else
if (tdiff != 0) {
}
return (0);
}