smbrdr_session.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This module provides the netbios and SMB negotiation, connect and
* disconnect interface.
*/
#include <unistd.h>
#include <syslog.h>
#include <synch.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <errno.h>
#include <inttypes.h>
#include <netdb.h>
#include <smbsrv/libsmbrdr.h>
#include <smbsrv/ntstatus.h>
#include <smbrdr.h>
#include <smbrdr_ipc_util.h>
static uint16_t smbrdr_ports[] = {
};
/*
* Pointer to the PDC location interface.
* To be set up by SMB when it loads.
*/
static mlsvc_locate_pdc_t mlsvc_locate_pdc;
/*
* This is a temporary hack to stop the DC from closing a session
* due to inactivity.
*/
#define MLSVC_SESSION_FORCE_KEEPALIVE 10
/*
* This is the session data table.
*
* The rwlock synchronizes access to the session table
*
* The mutex is to make session lookup and create atomic
* so we don't end up with two sessions with the same
* system.
*/
static mutex_t smbrdr_screate_mtx;
static unsigned int session_id = 0;
static int smbrdr_locate_dc(char *domain);
static void
{
}
/*
* mlsvc_install_pdc_cb
*
* Function to be called by SMB initialization code to set up a
* callback to the PDC location interface.
*/
void
{
}
/*
* mlsvc_locate_domain_controller
*
* Locate a domain controller. Note that this may close an existing
* connection to the current domain controller.
*/
int
{
if (mlsvc_locate_pdc)
return (mlsvc_locate_pdc(domain));
return (0);
}
/*
* Entry pointy for smbrdr initialization.
*/
void
smbrdr_init(void)
{
}
/*
* mlsvc_disconnect
*
* Disconnects the session with given server.
*/
void
mlsvc_disconnect(char *server)
{
struct sdb_session *session;
if (session) {
}
}
/*
* smbrdr_negotiate
*
* Negotiate a session with a domain controller in the specified domain.
* The domain must be one of values from the smbinfo that indicates the
* resource domain or the account domain.
*
* If a session already exists, we can use that one. Otherwise we create
* a new one. This sets up the session key and session security info that
* we'll need later to authenticate the user. The session security info
* is returned to support the SMB client pass-through authentication
* interface.
*
* Returns 0 on success, otherwise -1.
*/
int
smbrdr_negotiate(char *domain_name)
{
struct sdb_session *session = 0;
int retry = 1;
int res = 0;
if ((di = smb_getdomaininfo(0)) == 0) {
/*
* Attempting to locate a domain controller
* will shutdown an existing PDC connection.
*/
(void) smbrdr_locate_dc(domain_name);
di = smb_getdomaininfo(0);
}
if (di == 0) {
return (-1);
}
/*
* The mutex is to make session lookup and create atomic
* so we don't end up with two sessions with the same
* server.
*/
(void) mutex_lock(&smbrdr_screate_mtx);
while (retry > 0) {
if (session != 0) {
/* session is good, use it */
break;
} else {
/* stale session */
}
}
if (smbrdr_session_connect(di) != 0) {
if (retry > 0) {
/* Do we really need to do this here? */
(void) smbrdr_locate_dc(domain_name);
di = smb_getdomaininfo(0);
if (di == 0) {
" (cannot access domain)");
res = -1;
break;
}
retry--;
}
} else {
/* session is created */
retry = 0;
}
}
(void) mutex_unlock(&smbrdr_screate_mtx);
return (res);
}
/*
* smbrdr_session_connect
*
* This is the entry point for establishing an SMB connection to a
* domain controller. A session structure is allocated, a netbios
* session is set up and the SMB protocol is negotiated. If this is
* successful, the returned session structure can be used to logon
* to the the domain. A null pointer is returned if the connect fails.
*/
static int
{
struct sdb_session *session;
int rc = 0;
/*
* smbrdr_session_init() will lock the session so that it wouldn't
* be accessible until it's established otherwise another thread
* might get access to a session which is not fully established.
*/
return (-1);
}
smbrdr_ports[port]);
if (rc == 0) {
smbrdr_ports[port]);
break;
}
}
if (rc < 0) {
return (-1);
}
if (smbrdr_smb_negotiate(session) < 0) {
return (-1);
}
return (0);
}
/*
* smbrdr_trnsprt_connect
*
* standard socket sutff. The paranoia check for socket descriptor 0
* is because we had a problem with this value and the console telnet
*
* Return 0 on success. Otherwise return (-1) to indicate a problem.
*/
static int
{
char hostname[MAXHOSTNAMELEN];
struct sockaddr_in sin;
char server_name[SMB_PI_MAX_DOMAIN];
unsigned int cpid = oem_get_smb_cpid();
/*
* We should never see descriptor 0 (stdin).
*/
return (-1);
}
if (sock != 0)
return (-1);
}
if (rc == 0) {
if (sock != 0)
return (-1);
}
/*
* If we are using NetBIOS, we need to set up a NETBIOS session.
* This typically implies that we will be using port 139.
* Otherwise, we're doing NetBIOS-less SMB, i.e. SMB over TCP,
* which is typically on port 445.
*/
if (port == SSN_SRVC_TCP_PORT) {
if (sock != 0)
return (-1);
}
if (rc != 0) {
"smbrdr: NBT session request to %s failed %d",
server_name, rc);
if (sock != 0)
return (-1);
}
}
return (0);
}
/*
* smbrdr_smb_negotiate
*
* Negotiate the protocol we are going to use as described in CIFS
* section 4.1.1. The only protocol we support is NT LM 0.12, so we
* really expect to see dialect 0 in the response. The only other
* data gathered is the session key.
*
* Negotiate using ASCII strings.
*
* Return 0 on success. Otherwise return a -ve error code.
*/
static int
{
unsigned short dialect;
int rc;
if (status != NT_STATUS_SUCCESS) {
return (-1);
}
0, /* smb_wct */
12, /* smb_bcc */
0x02, /* dialect marker */
"NT LM 0.12"); /* only dialect we care about */
if (rc <= 0) {
return (-1);
}
if (status != NT_STATUS_SUCCESS) {
return (-1);
}
sess->challenge_len = 0;
"(wordcnt)1.(dialect)w(secm)b12.(skey)l(cap)l10.(klen)b2.",
&tmp_clen);
return (-1);
}
if (rc <= 0) {
return (-1);
}
}
return (0);
}
/*
* smbrdr_session_init
*
* Allocate an available slot in session table for the specified domain
* information.
*
* IMPORTANT! the returned session will be locked caller has to unlock
* it by calling smbrdr_session_unlock() after it's done with
* the pointer.
*/
static struct sdb_session *
{
struct sdb_session *session = 0;
int i;
char *p;
if (di == 0)
return (0);
for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
session = &session_table[i];
"Solaris", SMB_PI_MAX_NATIVE_OS);
"Windows NT 4.0", SMB_PI_MAX_LANMAN);
/*
* Note that by sending vc=0 server will shutdown all
* the other connections with NAS if there is any.
*/
return (session);
}
}
return (0);
}
/*
* smbrdr_session_disconnect
*
* This is the entry point for disconnecting an SMB connection. Ensure
* that all logons and shares associated with this session are
* terminated and then free the session.
*
* if 'cleanup' is 1 it means that only sessions that are not active
* should be cleaned up. if 'cleanup' is 0 disconnect the session in any
* states.
*/
static void
{
int state;
if (session == 0) {
return;
}
if ((state != SDB_SSTATE_DISCONNECTING) &&
(state != SDB_SSTATE_CLEANING) &&
(state != SDB_SSTATE_START)) {
/*
* if session is in stale state it means the connection
* is lost so no logoff, tdcon, or close can actually
* be sent, thus only cleanup our side.
*/
}
}
}
/*
* smbrdr_session_unlock
*
* Unlock given session structure.
*/
void
{
if (session)
}
/*
* smbrdr_session_lock
*
* Lookup the session associated with the specified domain controller.
* If a match is found, we return a pointer to the session, Otherwise
* we return null. Only sessions in "negotiated" state are checked.
* This mechanism is very simple and implies that we
* should only ever have one session open to any domain controller.
*
* IMPORTANT! the returned session will be locked caller has to unlock
* it by calling smbrdr_session_unlock() after it's done with
* the pointer.
*/
struct sdb_session *
{
struct sdb_session *session;
int i;
if (server == 0) {
return (0);
}
for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
session = &session_table[i];
if (username) {
if (strcasecmp(username,
return (session);
return (0);
}
return (session);
}
}
return (0);
}
/*
* mlsvc_session_native_values
*
* Given a file id (i.e. a named pipe fid), return the remote native
* OS and LM values for the associated session.
*/
int
{
struct sdb_session *session;
struct sdb_netuse *netuse;
return (-1);
}
"mlsvc_session_native_values: unknown file (%d)", fid);
return (-1);
}
if (pdc_type)
return (0);
}
/*
* smbrdr_disconnect_sessions
*
* Disconnects/cleanups all the sessions
*/
static void
{
struct sdb_session *session;
int i;
for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
session = &session_table[i];
}
}
/*
* mlsvc_check_sessions
*
* This function should be run in an independent thread. At the time of
* writing it is called periodically from an infinite loop in the start
* up thread once initialization is complete. It sends a NetBIOS keep-
* alive message on each active session and handles cleanup if a session
* is closed from the remote end. Testing demonstrated that the domain
* controller will close a session after 15 minutes of inactivity. Note
* that neither NetBIOS keep-alive nor SMB echo is deemed as activity
* in this case, however, RPC requests appear to reset the timeout and
* keep the session open. Note that the NetBIOS request does stop the
* remote NetBIOS layer from timing out the connection.
*/
void
mlsvc_check_sessions(void)
{
static int session_keep_alive;
struct sdb_session *session;
int i;
for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
session = &session_table[i];
continue;
}
/*
* NetBIOS is only used on with port 139. The keep alive
* is not relevant over NetBIOS-less SMB over port 445.
* This is just to see if the socket is still alive.
*/
continue;
}
}
if (smbrdr_smb_echo(session) != 0) {
"smbrdr: monitor[%s] cannot contact %s",
sizeof (smb_ntdomain_t));
if (smb_getdomaininfo(0) == 0)
}
} else
}
session_keep_alive = 0;
/* cleanup */
}
}
/*
* smbrdr_dump_sessions
*
* Debug function to dump the session table.
*/
void
smbrdr_dump_sessions(void)
{
struct sdb_session *session;
char ipstr[16];
int i;
for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
session = &session_table[i];
}
}
}
/*
* mlsvc_echo
*/
int
mlsvc_echo(char *server)
{
struct sdb_session *session;
int res = 0;
return (1);
if (smbrdr_smb_echo(session) != 0) {
res = -1;
}
return (res);
}
/*
* smbrdr_smb_echo
*
* This request can be used to test the connection to the server. The
* server should echo the data sent. The server should ignore the tid
* in the header, so this request when there are no tree connections.
* See CIFS/1.0 section 4.1.7.
*
* Return 0 on success. Otherwise return a -ve error code.
*/
static int
{
static char *echo_str = "smbrdr";
int rc;
return (-1);
}
if (status != NT_STATUS_SUCCESS) {
return (-1);
}
if (rc <= 0) {
return (-1);
}
if (status != NT_STATUS_SUCCESS) {
rc = -1;
} else {
rc = 0;
}
return (rc);
}
/*
* smbrdr_locate_dc
*
* Locate a domain controller. Note that this may close an existing
* connection to the current domain controller.
*/
static int
smbrdr_locate_dc(char *domain)
{
if (mlsvc_locate_pdc)
return (mlsvc_locate_pdc(domain));
return (0);
}