smb_session.c revision 49b5df1eac768235abeb210f61310e88b7d172b6
/*
* 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 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
/*
* We track the keepalive in minutes, but this constant
* specifies it in seconds, so convert to minutes.
*/
static void smb_session_cancel(smb_session_t *);
static int smb_session_message(smb_session_t *);
static void smb_session_logoff(smb_session_t *);
static void smb_session_genkey(smb_session_t *);
void
{
/*
* Walk through the table and decrement each keep_alive
* timer that has not timed out yet. (keepalive > 0)
*/
if (session->keep_alive &&
session->keep_alive--;
}
}
void
{
/*
* Caller specifies seconds, but we track in minutes, so
* convert to minutes (rounded up).
*/
if (new_keep_alive == smb_keep_alive)
return;
/*
* keep alive == 0 means do not drop connection if it's idle
*/
/*
* Walk through the table and set each session to the new keep_alive
* value if they have not already timed out. Block clock interrupts.
*/
if (sn->keep_alive != 0)
}
}
/*
* Send a session message - supports SMB-over-NBT and SMB-over-TCP.
*
* The mbuf chain is copied into a contiguous buffer so that the whole
* message is submitted to smb_sosend as a single request. This should
* TCP_NODELAY has been set on the socket.
*
* If an mbuf chain is provided, it will be freed and set to NULL here.
*/
int
{
int rc;
}
return (ENOTCONN);
default:
break;
}
txr = smb_net_txr_alloc();
if (rc != 0) {
return (rc);
}
}
if (rc != 0) {
return (rc);
}
}
/*
* Read, process and respond to a NetBIOS session request.
*
* A NetBIOS session must be established for SMB-over-NetBIOS. Validate
* the calling and called name format and save the client NetBIOS name,
* which is used when a NetBIOS session is established to check for and
* cleanup leftover state from a previous session.
*
* Session requests are not valid for SMB-over-TCP, which is unfortunate
* because without the client name leftover state cannot be cleaned up
* if the client is behind a NAT server.
*/
static int
{
int rc;
char *calling_name;
char *called_name;
char client_name[NETBIOS_NAME_SZ];
struct mbuf_chain mbc;
char *p;
return (rc);
smb_xprt_t *, &hdr);
return (EINVAL);
}
return (rc);
}
called_name = &names[0];
&mbc);
return (EINVAL);
}
char *, calling_name, char *, client_name);
/*
* The client NetBIOS name is in oem codepage format.
* We need to convert it to unicode and store it in
* multi-byte format. We also need to strip off any
* spaces added as part of the NetBIOS name encoding.
*/
*p = '\0';
}
/*
* Read 4-byte header from the session socket and build an in-memory
* session transport header. See smb_xprt_t definition for header
* format information.
*
* Direct hosted NetBIOS-less SMB (SMB-over-TCP) uses port 445. The
* first byte of the four-byte header must be 0 and the next three
* bytes contain the length of the remaining data.
*/
int
{
int rc;
unsigned char buf[NETBIOS_HDR_SZ];
return (rc);
switch (session->s_local_port) {
case IPPORT_NETBIOS_SSN:
break;
case IPPORT_SMB:
return (EPROTO);
}
break;
default:
return (EPROTO);
}
return (0);
}
/*
* Encode a transport session packet header into a 4-byte buffer.
* See smb_xprt_t definition for header format information.
*/
static int
{
return (-1);
}
switch (session->s_local_port) {
case IPPORT_NETBIOS_SSN:
break;
case IPPORT_SMB:
break;
default:
return (-1);
}
return (0);
}
static void
{
/*
* Setup mbuf using the buffer we allocated.
*/
}
/*
* smb_request_cancel
*
* Handle a cancel for a request properly depending on the current request
* state.
*/
void
{
case SMB_REQ_STATE_SUBMITTED:
case SMB_REQ_STATE_ACTIVE:
case SMB_REQ_STATE_CLEANED_UP:
break;
/*
* This request is waiting on a lock. Wakeup everything
* waiting on the lock so that the relevant thread regains
* control and notices that is has been canceled. The
* other lock request threads waiting on this lock will go
* back to sleep when they discover they are still blocked.
*/
break;
/*
* This request is waiting in change notify.
*/
break;
case SMB_REQ_STATE_COMPLETED:
case SMB_REQ_STATE_CANCELED:
/*
* No action required for these states since the request
* is completing.
*/
break;
case SMB_REQ_STATE_FREE:
default:
SMB_PANIC();
}
}
/*
* smb_session_receiver
*
* Receives request from the network and dispatches them to a worker.
*/
void
{
int rc = 0;
if (rc != 0) {
return;
}
}
(void) smb_session_message(session);
/*
* At this point everything related to the session should have been
* cleaned up and we expect that nothing will attempt to use the
* socket.
*/
}
/*
* smb_session_disconnect
*
* Disconnects the session passed in.
*/
void
{
break;
}
}
/*
* Read and process SMB requests.
*
* Returns:
* 0 Success
* 1 Unable to read transport header
* 2 Invalid transport header type
* 3 Invalid SMB length (too small)
* 4 Unable to read SMB header
* 5 Invalid SMB header (bad magic number)
* 6 Unable to read SMB data
*/
static int
{
int rc;
for (;;) {
if (rc)
return (rc);
smb_xprt_t *, &hdr);
/*
* Anything other than SESSION_MESSAGE or
* SESSION_KEEP_ALIVE is an error. A SESSION_REQUEST
* may indicate a new session request but we need to
* close this session and we can treat it as an error
* here.
*/
continue;
}
return (EPROTO);
}
return (EPROTO);
/*
* Allocate a request context, read the SMB header and validate
* it. The sr includes a buffer large enough to hold the SMB
* request payload. If the header looks valid, read any
* remaining data.
*/
if (rc) {
return (rc);
}
if (SMB_PROTOCOL_MAGIC_INVALID(sr)) {
return (EPROTO);
}
if (resid > SMB_HEADER_LEN) {
resid -= SMB_HEADER_LEN;
if (rc) {
return (rc);
}
}
/*
* Initialize command MBC to represent the received data.
*/
if (SMB_IS_NT_CANCEL(sr)) {
sr->reply_seqnum = 0;
} else {
}
}
}
}
/*
* Port will be IPPORT_NETBIOS_SSN or IPPORT_SMB.
*/
int family)
{
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
return (NULL);
}
return (NULL);
}
now = ddi_get_lbolt64();
(void) ksocket_getsockname(new_so,
sizeof (in_addr_t));
(void) ksocket_getpeername(new_so,
sizeof (in_addr_t));
} else {
(void) ksocket_getsockname(new_so,
sizeof (in6_addr_t));
(void) ksocket_getpeername(new_so,
sizeof (in6_addr_t));
}
if (port == IPPORT_NETBIOS_SSN)
else
}
return (session);
}
void
{
else
}
}
static void
{
/* All the request currently being treated must be canceled. */
/*
* We wait for the completion of all the requests associated with
* this session.
*/
/*
* At this point the reference count of the users, trees, files,
* directories should be zero. It should be possible to destroy them
* without any problem.
*/
while (xa) {
}
}
/*
* Cancel requests. If a non-null tree is specified, only requests specific
* to that tree will be cancelled. If a non-null sr is specified, that sr
* will be not be cancelled - this would typically be the caller's sr.
*/
void
{
while (sr) {
if ((sr != exclude_sr) &&
}
}
void
smb_session_worker(void *arg)
{
case SMB_REQ_STATE_SUBMITTED:
if (smb_dispatch_request(sr)) {
}
break;
default:
break;
}
}
/*
* smb_session_lookup_user
*/
static smb_user_t *
{
while (user) {
if (smb_user_hold(user))
break;
}
}
return (user);
}
/*
* If a user attempts to log in subsequently from the specified session,
* duplicates the existing SMB user instance such that all SMB user
* instances that corresponds to the same user on the given session
* reference the same user's cred.
*
* Returns NULL if the given user hasn't yet logged in from this
* specified session. Otherwise, returns a user instance that corresponds
* to this subsequent login.
*/
{
if (orig_user) {
}
return (user);
}
/*
* Find a user on the specified session by SMB UID.
*/
{
while (user) {
if (!smb_user_hold(user))
break;
return (user);
}
}
return (NULL);
}
void
{
}
/*
* Find a tree by tree-id.
*/
{
while (tree) {
if (smb_tree_hold(tree)) {
return (tree);
} else {
return (NULL);
}
}
}
return (NULL);
}
/*
* Find the first connected tree that matches the specified sharename.
* If the specified tree is NULL the search starts from the beginning of
* the user's tree list. If a tree is provided the search starts just
* after that tree.
*/
const char *sharename,
{
if (tree) {
} else {
}
while (tree) {
if (smb_tree_hold(tree)) {
return (tree);
}
}
}
return (NULL);
}
/*
* Find the first connected tree that matches the specified volume name.
* If the specified tree is NULL the search starts from the beginning of
* the user's tree list. If a tree is provided the search starts just
* after that tree.
*/
const char *name,
{
if (tree) {
} else {
}
while (tree) {
if (smb_tree_hold(tree)) {
return (tree);
}
}
}
return (NULL);
}
/*
* Disconnect all trees that match the specified client process-id.
*/
void
{
while (tree) {
}
}
static void
smb_session_tree_dtor(void *t)
{
/* release the ref acquired during the traversal loop */
}
/*
* Disconnect all trees that this user has connected.
*/
void
{
while (tree) {
smb_tree_hold(tree)) {
/*
* smb_tree_hold() succeeded, hence we are in state
* SMB_TREE_STATE_CONNECTED; schedule this tree
* for asynchronous disconnect, which will fire
* after we drop the llist traversal lock.
*/
}
}
/* drop the lock and flush the dtor queue */
}
/*
* Disconnect all trees that this user has connected.
*/
void
{
while (tree) {
}
}
/*
* Disconnect all trees that match the specified share name.
*/
void
const char *sharename)
{
while (tree) {
}
}
void
{
}
/*
* Get the next connected tree in the list. A reference is taken on
* the tree, which can be released later with smb_tree_release().
*
* If the specified tree is NULL the search starts from the beginning of
* the tree list. If a tree is provided the search starts just after
* that tree.
*
* Returns NULL if there are no connected trees in the list.
*/
static smb_tree_t *
{
if (tree) {
} else {
}
while (tree) {
if (smb_tree_hold(tree))
break;
}
return (tree);
}
/*
* Logoff all users associated with the specified session.
*/
static void
{
while (user) {
if (smb_user_hold(user)) {
}
}
}
/*
* Copy the session workstation/client name to buf. If the workstation
* is an empty string (which it will be on TCP connections), use the
* client IP address.
*/
void
{
*buf = '\0';
return;
}
}
/*
* Check whether or not the specified client name is the client of this
* session. The name may be in UNC format (\\CLIENT).
*
* A workstation/client name is setup on NBT connections as part of the
* NetBIOS session request but that isn't available on TCP connections.
* If the session doesn't have a client name we typically return the
* client IP address as the workstation name on MSRPC requests. So we
* check for the IP address here in addition to the workstation name.
*/
{
return (B_TRUE);
return (B_TRUE);
return (B_FALSE);
}
/*
* smb_request_alloc
*
* Allocate an smb_request_t structure from the kmem_cache. Partially
*
* Returns pointer to a request
*/
{
/*
* Future: Use constructor to pre-initialize some fields. For now
* there are so many fields that it is easiest just to zero the
* whole thing and start over.
*/
if (req_length)
return (sr);
}
/*
* smb_request_free
*
* release the memories which have been allocated for a smb request.
*/
void
{
}
if (sr->sr_request_buf)
}
{
return (B_FALSE);
else
return (B_TRUE);
}
{
}
/*
* smb_session_oplock_break
*
* The session lock must NOT be held by the caller of this thread;
* as this would cause a deadlock.
*/
void
{
tid,
0xFFFF, 0, 0xFFFF, 8, 0xFF,
fid,
break;
break;
default:
SMB_PANIC();
}
}
static void
{
}