/* * Copyright (c) 2010-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Portions of this software have been released under the following terms: * * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION * * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this file for any * purpose is hereby granted without fee, provided that the above * copyright notices and this notice appears in all source code copies, * and that none of the names of Open Software Foundation, Inc., Hewlett- * Packard Company or Digital Equipment Corporation be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Neither Open Software * Foundation, Inc., Hewlett-Packard Company nor Digital * Equipment Corporation makes any representations about the suitability * of this software for any purpose. * * Copyright (c) 2007, Novell, Inc. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Novell Inc. nor the names of its contributors * may be used to endorse or promote products derived from this * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @APPLE_LICENSE_HEADER_END@ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_SMBCLIENT_FRAMEWORK #include #include rpc_socket_error_t rpc_smb_ntstatus_to_rpc_error(NTSTATUS status); #if !defined(kSMBOptionAllowGuestAuth) #define kSMBOptionAllowGuestAuth 0 #endif #if !defined(kSMBOptionAllowAnonymousAuth) #define kSMBOptionAllowAnonymousAuth 0 #endif #if defined(kPropertiesVersion) #define HAVE_SMBCLIENT_SMBGETSERVERPROPERTIES 1 #endif #define SMBCLIENT_CONNECTION_FLAGS ( \ kSMBOptionNoPrompt | \ kSMBOptionAllowGuestAuth | \ kSMBOptionAllowAnonymousAuth \ ) #endif /* HAVE_SMBCLIENT_FRAMEWORK */ #if HAVE_LW_BASE_H #include #endif #if HAVE_LWIO_LWIO_H #include #endif #if HAVE_LWMAPSECURITY_LWMAPSECURITY_H #include #endif #define SMB_SOCKET_LOCK(sock) (rpc__smb_socket_lock(sock)) #define SMB_SOCKET_UNLOCK(sock) (rpc__smb_socket_unlock(sock)) #ifndef RPC_C_SOCKET_MAX_RCVBUF # define RPC_C_SOCKET_MAX_RCVBUF (64 * 1024) #endif #ifndef RPC_C_SOCKET_MAX_SNDBUF # define RPC_C_SOCKET_MAX_SNDBUF (64 * 1024) #endif typedef struct rpc_smb_transport_info_s { char* peer_principal; struct { unsigned16 length; unsigned char* data; } session_key; #if HAVE_LIKEWISE_LWIO PIO_CREDS creds; #endif } rpc_smb_transport_info_t, *rpc_smb_transport_info_p_t; typedef enum rpc_smb_state_e { SMB_STATE_SEND, SMB_STATE_RECV, SMB_STATE_LISTEN, SMB_STATE_ERROR } rpc_smb_state_t; typedef struct rpc_smb_buffer_s { size_t capacity; unsigned char* base; unsigned char* start_cursor; unsigned char* end_cursor; } rpc_smb_buffer_t, *rpc_smb_buffer_p_t; typedef struct rpc_smb_socket_s { rpc_smb_state_t volatile state; rpc_np_addr_t peeraddr; rpc_np_addr_t localaddr; rpc_smb_transport_info_t info; #if HAVE_LIKEWISE_LWIO PIO_CONTEXT context; IO_FILE_HANDLE np; #elif HAVE_SMBCLIENT_FRAMEWORK SMBHANDLE handle; SMBFID hFile; #endif size_t maxSendBufferSize; size_t maxRecvBufferSize; rpc_smb_buffer_t sendbuffer; rpc_smb_buffer_t recvbuffer; struct { #if HAVE_LIKEWISE_LWIO IO_FILE_HANDLE* queue; #endif size_t capacity; size_t length; int selectfd[2]; } accept_backlog; dcethread* listen_thread; dcethread_mutex lock; dcethread_cond event; } rpc_smb_socket_t, *rpc_smb_socket_p_t; #if HAVE_SMBCLIENT_FRAMEWORK rpc_socket_error_t rpc_smb_ntstatus_to_rpc_error( NTSTATUS status ) { switch (status) { case STATUS_SUCCESS: return RPC_C_SOCKET_OK; case STATUS_UNEXPECTED_IO_ERROR: return RPC_C_SOCKET_EIO; case STATUS_CONNECTION_REFUSED: return RPC_C_SOCKET_ECONNREFUSED; case STATUS_NO_SUCH_DEVICE: return RPC_C_SOCKET_ENETUNREACH; case STATUS_BUFFER_OVERFLOW: return RPC_C_SOCKET_ENOSPC; case STATUS_NO_MEMORY: return RPC_C_SOCKET_ENOMEM; case STATUS_OBJECT_PATH_SYNTAX_BAD: case STATUS_INVALID_HANDLE: case STATUS_INVALID_PARAMETER: case STATUS_UNSUCCESSFUL: return RPC_C_SOCKET_EINVAL; case STATUS_LOGON_FAILURE: return RPC_C_SOCKET_EAUTH; case STATUS_BAD_NETWORK_NAME: return RPC_C_SOCKET_ENOENT; default: RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc_smb_ntstatus_to_rpc_error - unmapped ntstatus 0x%x\n", status)); return (RPC_C_SOCKET_EIO); } } #endif void rpc_smb_transport_info_from_lwio_creds( void* creds ATTRIBUTE_UNUSED, rpc_transport_info_handle_t* info, unsigned32* st ) { rpc_smb_transport_info_p_t smb_info = NULL; smb_info = calloc(1, sizeof(*smb_info)); if (!smb_info) { *st = rpc_s_no_memory; goto error; } #if HAVE_LIKEWISE_LWIO if (LwIoCopyCreds(creds, &smb_info->creds) != 0) { *st = rpc_s_no_memory; goto error; } #endif *info = (rpc_transport_info_handle_t) smb_info; *st = rpc_s_ok; error: if (*st != rpc_s_ok && smb_info) { rpc_smb_transport_info_free((rpc_transport_info_handle_t) smb_info); } return; } INTERNAL void rpc__smb_transport_info_destroy( rpc_smb_transport_info_p_t smb_info ) { assert(smb_info != NULL); #if HAVE_LIKEWISE_LWIO if (smb_info->creds) { LwIoDeleteCreds(smb_info->creds); } #endif if (smb_info->session_key.data) { free(smb_info->session_key.data); } if (smb_info->peer_principal) { free(smb_info->peer_principal); } } void rpc_smb_transport_info_free( rpc_transport_info_handle_t info ) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc_smb_transport_info_free called\n")); if (info) { rpc__smb_transport_info_destroy((rpc_smb_transport_info_p_t) info); free(info); } } void rpc_smb_transport_info_inq_session_key( rpc_transport_info_handle_t info, unsigned char** sess_key, unsigned16* sess_key_len ) { rpc_smb_transport_info_p_t smb_info = (rpc_smb_transport_info_p_t) info; if (sess_key) { *sess_key = smb_info->session_key.data; } if (sess_key_len) { *sess_key_len = (unsigned32) smb_info->session_key.length; } } void rpc_smb_transport_info_inq_peer_principal_name( rpc_transport_info_handle_t info, unsigned char** principal ) { rpc_smb_transport_info_p_t smb_info = (rpc_smb_transport_info_p_t) info; if (principal) { *principal = (unsigned char*) smb_info->peer_principal; } } INTERNAL boolean rpc__smb_transport_info_equal( rpc_transport_info_handle_t info1, rpc_transport_info_handle_t info2 ) { rpc_smb_transport_info_p_t smb_info1 = (rpc_smb_transport_info_p_t) info1; rpc_smb_transport_info_p_t smb_info2 = (rpc_smb_transport_info_p_t) info2; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_transport_info_equal called\n")); #if HAVE_LIKEWISE_LWIO return (smb_info2 == NULL && smb_info1->creds == NULL) || (smb_info2 != NULL && ((smb_info1->creds == NULL && smb_info2->creds == NULL) || (smb_info1->creds != NULL && smb_info2->creds != NULL && LwIoCompareCredss(smb_info1->creds, smb_info2->creds)))); #else return (smb_info1 == smb_info2); #endif } INTERNAL inline size_t rpc__smb_buffer_pending( rpc_smb_buffer_p_t buffer ) { return buffer->end_cursor - buffer->start_cursor; } INTERNAL inline size_t rpc__smb_buffer_length( rpc_smb_buffer_p_t buffer ) { return buffer->end_cursor - buffer->base; } INTERNAL inline size_t rpc__smb_buffer_available( rpc_smb_buffer_p_t buffer ) { return (buffer->base + buffer->capacity) - buffer->end_cursor; } INTERNAL inline rpc_socket_error_t rpc__smb_buffer_ensure_available( rpc_smb_buffer_p_t buffer, size_t space ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; unsigned char* new_base = NULL; if (!buffer->base) { buffer->capacity = 2048; buffer->base = malloc(buffer->capacity); if (!buffer->base) { serr = RPC_C_SOCKET_ENOMEM; goto error; } buffer->end_cursor = buffer->start_cursor = buffer->base; } if (space > rpc__smb_buffer_available(buffer)) { while (space > rpc__smb_buffer_available(buffer)) { buffer->capacity *= 2; } new_base = realloc(buffer->base, buffer->capacity); if (!new_base) { serr = RPC_C_SOCKET_ENOMEM; goto error; } buffer->start_cursor = new_base + (buffer->start_cursor - buffer->base); buffer->end_cursor = new_base + (buffer->end_cursor - buffer->base); buffer->base = new_base; } error: return serr; } INTERNAL size_t rpc__smb_fragment_size( rpc_cn_common_hdr_p_t packet ) { uint16_t result; int packet_order = ((packet->drep[0] >> 4) & 1); int native_order; #if __LITTLE_ENDIAN__ native_order = (NDR_LOCAL_INT_REP == ndr_c_int_big_endian) ? 0 : 1; #else native_order = (NDR_LOCAL_INT_REP == ndr_c_int_little_endian) ? 0 : 1; #endif if (packet_order != native_order) { result = SWAB_16(packet->frag_len); } else { result = packet->frag_len; } return (size_t) result; } INTERNAL inline size_t rpc__smb_buffer_packet_size( rpc_smb_buffer_p_t buffer ) { rpc_cn_common_hdr_p_t packet = (rpc_cn_common_hdr_p_t) buffer->start_cursor; if (rpc__smb_buffer_pending(buffer) < sizeof(*packet)) { return sizeof(*packet); } else { return (rpc__smb_fragment_size(packet)); } } INTERNAL inline boolean rpc__smb_buffer_packet_is_last( rpc_smb_buffer_p_t buffer ) { rpc_cn_common_hdr_p_t packet = (rpc_cn_common_hdr_p_t) buffer->start_cursor; return (packet->flags & RPC_C_CN_FLAGS_LAST_FRAG) == RPC_C_CN_FLAGS_LAST_FRAG; } INTERNAL inline rpc_socket_error_t rpc__smb_buffer_append( rpc_smb_buffer_p_t buffer, void* data, size_t data_size ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; serr = rpc__smb_buffer_ensure_available(buffer, data_size); if (serr) { goto error; } memcpy(buffer->end_cursor, data, data_size); buffer->end_cursor += data_size; error: return serr; } INTERNAL inline void rpc__smb_buffer_settle( rpc_smb_buffer_p_t buffer ) { size_t filled = buffer->end_cursor - buffer->start_cursor; memmove(buffer->base, buffer->start_cursor, filled); buffer->start_cursor = buffer->base; buffer->end_cursor = buffer->base + filled; } /* Advance buffer start_cursor to the end of the last packet or the last packet that is the final fragment in a series, whichever comes first. If the final fragment is found, return true, otherwise false. */ INTERNAL inline boolean rpc__smb_buffer_advance_cursor(rpc_smb_buffer_p_t buffer, size_t* amount) { boolean last; size_t packet_size; while (rpc__smb_buffer_packet_size(buffer) <= rpc__smb_buffer_pending(buffer)) { last = rpc__smb_buffer_packet_is_last(buffer); packet_size = rpc__smb_buffer_packet_size(buffer); buffer->start_cursor += packet_size; if (last) { if (amount) { *amount = buffer->start_cursor - buffer->base; } return true; } } return false; } INTERNAL rpc_socket_error_t rpc__smb_socket_create( rpc_smb_socket_p_t* out ) { rpc_smb_socket_p_t sock = NULL; int err = 0; sock = calloc(1, sizeof(*sock)); if (!sock) { err = RPC_C_SOCKET_ENOMEM; goto done; } sock->accept_backlog.selectfd[0] = -1; sock->accept_backlog.selectfd[1] = -1; /* Set up reasonable default local endpoint */ sock->localaddr.rpc_protseq_id = rpc_c_protseq_id_ncacn_np; sock->localaddr.len = offsetof(rpc_np_addr_t, remote_host) + sizeof(sock->localaddr.remote_host); sock->localaddr.sa.sun_family = AF_UNIX; sock->localaddr.sa.sun_path[0] = '\0'; sock->localaddr.remote_host[0] = '\0'; dcethread_mutex_init_throw(&sock->lock, NULL); dcethread_cond_init_throw(&sock->event, NULL); #if HAVE_SMBCLIENT_FRAMEWORK sock->handle = NULL; sock->hFile = 0; sock->maxSendBufferSize = 0; sock->maxRecvBufferSize = 0; #else sock->maxSendBufferSize = 8192; sock->maxRecvBufferSize = 8192; #endif #if HAVE_LIKEWISE_LWIO err = LwNtStatusToErrno(LwIoOpenContextShared(&sock->context)); if (err) { goto error; } #endif *out = sock; done: return err; #if HAVE_LIKEWISE_LWIO error: if (sock) { if (sock->context) { LwIoCloseContext(sock->context); } dcethread_mutex_destroy_throw(&sock->lock); dcethread_cond_destroy_throw(&sock->event); } goto done; #endif } INTERNAL void rpc__smb_socket_destroy( rpc_smb_socket_p_t sock ) { #if HAVE_LIKEWISE_LWIO size_t i; #endif RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_destroy called\n")); if (!sock) { return; } #if HAVE_LIKEWISE_LWIO if (sock->accept_backlog.queue) { for (i = 0; i < sock->accept_backlog.capacity; i++) { if (sock->accept_backlog.queue[i]) { NtCtxCloseFile(sock->context, sock->accept_backlog.queue[i]); } } close(sock->accept_backlog.selectfd[0]); close(sock->accept_backlog.selectfd[1]); free(sock->accept_backlog.queue); } if (sock->np && sock->context) { NtCtxCloseFile(sock->context, sock->np); } if (sock->context) { LwIoCloseContext(sock->context); } #elif HAVE_SMBCLIENT_FRAMEWORK if (sock->hFile != 0) { SMBCloseFile(sock->handle, sock->hFile); sock->hFile = 0; } if (sock->handle) { SMBReleaseServer(sock->handle); sock->handle = NULL; } #endif if (sock->sendbuffer.base) { free(sock->sendbuffer.base); } if (sock->recvbuffer.base) { free(sock->recvbuffer.base); } rpc__smb_transport_info_destroy(&sock->info); dcethread_mutex_destroy_throw(&sock->lock); dcethread_cond_destroy_throw(&sock->event); free(sock); } INTERNAL inline void rpc__smb_socket_lock( rpc_smb_socket_p_t sock ) { dcethread_mutex_lock_throw(&sock->lock); } INTERNAL inline void rpc__smb_socket_unlock( rpc_smb_socket_p_t sock ) { dcethread_mutex_unlock_throw(&sock->lock); } INTERNAL inline void rpc__smb_socket_change_state( rpc_smb_socket_p_t sock, rpc_smb_state_t state ) { sock->state = state; dcethread_cond_broadcast_throw(&sock->event); } INTERNAL inline void rpc__smb_socket_wait( rpc_smb_socket_p_t sock ) { DCETHREAD_TRY { dcethread_cond_wait_throw(&sock->event, &sock->lock); } DCETHREAD_CATCH_ALL(e) { dcethread_mutex_unlock(&sock->lock); DCETHREAD_RAISE(*e); } DCETHREAD_ENDTRY; } INTERNAL rpc_socket_error_t rpc__smb_socket_construct( rpc_socket_t sock, rpc_protseq_id_t pseq_id ATTRIBUTE_UNUSED, #if HAVE_LIKEWISE_LWIO rpc_transport_info_handle_t info #else rpc_transport_info_handle_t info ATTRIBUTE_UNUSED #endif ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb_sock = NULL; #if HAVE_LIKEWISE_LWIO rpc_smb_transport_info_p_t smb_info = (rpc_smb_transport_info_p_t) info; #endif RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_construct called\n")); serr = rpc__smb_socket_create(&smb_sock); if (serr) { goto error; } #if HAVE_LIKEWISE_LWIO if (smb_info) { if (smb_info->creds) { serr = NtStatusToErrno(LwIoCopyCreds(smb_info->creds, &smb_sock->info.creds)); if (serr) { goto error; } } } #endif sock->data.pointer = (void*) smb_sock; done: return serr; error: if (smb_sock) { rpc__smb_socket_destroy(smb_sock); } goto done; } INTERNAL rpc_socket_error_t rpc__smb_socket_destruct( rpc_socket_t sock ) { rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; rpc__smb_socket_destroy(smb); sock->data.pointer = NULL; return RPC_C_SOCKET_OK; } INTERNAL rpc_socket_error_t rpc__smb_socket_bind( rpc_socket_t sock, rpc_addr_p_t addr ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_np_addr_p_t npaddr = (rpc_np_addr_p_t) addr; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; smb->localaddr = *npaddr; return serr; } #if HAVE_SMBCLIENT_FRAMEWORK INTERNAL rpc_socket_error_t rpc__smbclient_connect( rpc_smb_socket_p_t smb, const char * netaddr, const char * pipename ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; char * smbpath = NULL; NTSTATUS status; boolean have_mountpath = false; unsigned_char_p_t unescaped_netaddr; if (netaddr[0] == '/') { struct statfs fsBuf; /* see if its a mountpath instead of a host addr */ if (statfs ((char *) netaddr, &fsBuf) != 0) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - statfs failed errno %d\n", errno)); } else { /* its a mountpath, so use a different api to find the existing smb session to share */ have_mountpath = 1; } } if (have_mountpath == 0) { /* its not a mount path, so just pass in the URL string */ #if defined(kPropertiesVersion) /* unescape the netaddr */ rpc__string_netaddr_unescape((unsigned_char_p_t) netaddr, &unescaped_netaddr, &status); smbpath = SMBCreateURLString(NULL, NULL, NULL, (const char *) unescaped_netaddr, NULL, -1); /* free the escaped netaddr */ rpc_string_free (&unescaped_netaddr, &status); #else /* Ick. Max OS 10.6 or earlier ... only have the old API. */ asprintf(&smbpath, "smb://%s", netaddr); #endif if (smbpath == NULL) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - smbpath malloc failed\n")); serr = RPC_C_SOCKET_ENOMEM; goto error; } } /* Never have a username or password here. Either we use an already * existing authenticated session, or log in as guest, or fail. Never * prompt for a username or password so always set kSMBOptionNoPrompt, * but do allow guest or anonymous logins. */ if (have_mountpath == 0) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - SMBOpenServerEx <%s>\n", smbpath)); status = SMBOpenServerEx(smbpath, &smb->handle, SMBCLIENT_CONNECTION_FLAGS); } else { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - SMBOpenServerWithMountPoint <%s>\n", netaddr)); #if defined(kPropertiesVersion) status = SMBOpenServerWithMountPoint(netaddr, NULL, &smb->handle, SMBCLIENT_CONNECTION_FLAGS); #else status = 0xC00000CC; /* STATUS_BAD_NETWORK_NAME */ #endif } if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - SMBOpenServerEx failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); goto error; } status = SMBCreateFile(smb->handle, pipename, GENERIC_READ | GENERIC_WRITE, /* dwDesiredAccess */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ NULL, /* lpSecurityAttributes */ FILE_OPEN, /* dwCreateDisposition */ 0x0000, /* dwFlagsAndAttributes */ &smb->hFile); if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - SMBCreateFile failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); goto error; } error: if (smbpath) free(smbpath); return serr; } #endif /* HAVE_SMBCLIENT_FRAMEWORK */ INTERNAL rpc_socket_error_t rpc__smb_socket_connect( rpc_socket_t sock, rpc_addr_p_t addr, rpc_cn_assoc_t *assoc ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; char *netaddr = NULL; unsigned_char_t *endpoint = NULL; char *pipename = NULL; unsigned32 dbg_status = 0; #if HAVE_LIKEWISE_LWIO PSTR smbpath = NULL; PBYTE sesskey = NULL; USHORT sesskeylen = 0; IO_FILE_NAME filename = { 0 }; IO_STATUS_BLOCK io_status = { 0 }; size_t len; #endif RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect called\n")); SMB_SOCKET_LOCK(smb); /* Break address into host and endpoint */ rpc__naf_addr_inq_netaddr (addr, (unsigned_char_t**) &netaddr, &dbg_status); rpc__naf_addr_inq_endpoint (addr, (unsigned_char_t**) &endpoint, &dbg_status); RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - netaddr <%s>\n", netaddr)); RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - ep <%s>\n", endpoint)); if (rpc__np_is_valid_endpoint(endpoint, &dbg_status)) { pipename = (char *)endpoint + sizeof("\\pipe\\") - 1; } else { serr = RPC_C_SOCKET_EINVAL; goto error; } RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - pipename <%s>\n", pipename)); #if HAVE_LIKEWISE_LWIO len = strlen(netaddr) + strlen(pipename) + strlen("\\rdr\\\\IPC$\\") + 1; smbpath = malloc(len); if (smbpath == NULL) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_connect - smbpath malloc failed\n")); serr = RPC_C_SOCKET_ENOMEM; goto error; } snprintf (smbpath, len, "\\rdr\\%s\\IPC$\\%s", (char*) netaddr, (char*) pipename); #elif HAVE_SMBCLIENT_FRAMEWORK serr = rpc__smbclient_connect(smb, netaddr, pipename); #else serr = RPC_C_SOCKET_ENOTSUP; goto error; #endif #if HAVE_LIKEWISE_LWIO serr = NtStatusToErrno( LwRtlCStringAllocatePrintf( &smbpath, "\\rdr\\%s\\IPC$\\%s", (char*) netaddr, (char*) pipename)); if (serr) { goto error; } serr = NtStatusToErrno( LwRtlWC16StringAllocateFromCString( &filename.FileName, smbpath)); if (serr) { goto error; } serr = NtStatusToErrno( NtCtxCreateFile( smb->context, /* IO context */ smb->info.creds, /* Security token */ &smb->np, /* Created handle */ NULL, /* Async control block */ &io_status, /* Status block */ &filename, /* Filename */ NULL, /* Security descriptor */ NULL, /* Security QOS */ GENERIC_READ | GENERIC_WRITE, /* Access mode */ 0, /* Allocation size */ 0, /* File attributes */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* Sharing mode */ FILE_OPEN, /* Create disposition */ FILE_CREATE_TREE_CONNECTION, /* Create options */ NULL, /* EA buffer */ 0, /* EA buffer length */ NULL /* ECP List */ )); if (serr) { goto error; } serr = NtStatusToErrno( LwIoCtxGetSessionKey( smb->context, smb->np, &sesskeylen, &sesskey)); if (serr) { goto error; } smb->info.session_key.length = sesskeylen; smb->info.session_key.data = malloc(sesskeylen); if (!smb->info.session_key.data) { serr = RPC_C_SOCKET_ENOMEM; goto error; } memcpy(smb->info.session_key.data, sesskey, sesskeylen); #endif /* Save address for future inquiries on this socket */ memcpy(&smb->peeraddr, addr, sizeof(smb->peeraddr)); /* Since we did a connect, we will be sending first */ smb->state = SMB_STATE_SEND; done: #if HAVE_LIKEWISE_LWIO if (sesskey) { RtlMemoryFree(sesskey); } if (filename.FileName) { RtlMemoryFree(filename.FileName); } if (smbpath) { free(smbpath); } #endif SMB_SOCKET_UNLOCK(smb); // rpc_string_free handles when *ptr is NULL rpc_string_free((unsigned_char_t**) &netaddr, &dbg_status); rpc_string_free((unsigned_char_t**) &endpoint, &dbg_status); return serr; error: goto done; } INTERNAL rpc_socket_error_t rpc__smb_socket_accept( #if HAVE_LIKEWISE_LWIO rpc_socket_t sock, rpc_addr_p_t addr, rpc_socket_t *newsock #else rpc_socket_t sock ATTRIBUTE_UNUSED, rpc_addr_p_t addr ATTRIBUTE_UNUSED, rpc_socket_t *newsock ATTRIBUTE_UNUSED #endif ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_accept called\n")); #if !defined(HAVE_LIKEWISE_LWIO) serr = RPC_C_SOCKET_ENOTSUP; #else rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; rpc_socket_t npsock = NULL; rpc_smb_socket_p_t npsmb = NULL; IO_FILE_HANDLE np = NULL; size_t i; char c = 0; BYTE clientaddr[4] = {0, 0, 0, 0}; USHORT clientaddrlen = sizeof(clientaddr); *newsock = NULL; SMB_SOCKET_LOCK(smb); while (smb->accept_backlog.length == 0) { if (smb->state == SMB_STATE_ERROR) { serr = -1; goto error; } rpc__smb_socket_wait(smb); } for (i = 0; i < smb->accept_backlog.capacity; i++) { if (smb->accept_backlog.queue[i] != NULL) { np = smb->accept_backlog.queue[i]; smb->accept_backlog.queue[i] = NULL; smb->accept_backlog.length--; if (read(smb->accept_backlog.selectfd[0], &c, sizeof(c)) != sizeof(c)) { serr = errno; goto error; } dcethread_cond_broadcast_throw(&smb->event); break; } } serr = rpc__socket_open(sock->pseq_id, NULL, &npsock); if (serr) { goto error; } npsmb = (rpc_smb_socket_p_t) npsock->data.pointer; npsmb->np = np; np = NULL; npsmb->state = SMB_STATE_RECV; memcpy(&npsmb->localaddr, &smb->localaddr, sizeof(npsmb->localaddr)); /* Use our address as a template for client address */ memcpy(&npsmb->peeraddr, &smb->localaddr, sizeof(npsmb->peeraddr)); /* Query for client address */ serr = NtStatusToErrno( LwIoCtxGetPeerAddress( npsmb->context, npsmb->np, clientaddr, &clientaddrlen)); if (serr) { goto error; } if (clientaddrlen == sizeof(clientaddr)) { snprintf(npsmb->peeraddr.remote_host, sizeof(npsmb->peeraddr.remote_host) - 1, "%u.%u.%u.%u", clientaddr[0], clientaddr[1], clientaddr[2], clientaddr[3]); } if (addr) { memcpy(addr, &npsmb->peeraddr, sizeof(npsmb->peeraddr)); } serr = NtStatusToErrno( LwIoCtxGetPeerPrincipalName( npsmb->context, npsmb->np, &npsmb->info.peer_principal)); if (serr) { goto error; } serr = NtStatusToErrno( LwIoCtxGetSessionKey( npsmb->context, npsmb->np, &npsmb->info.session_key.length, &npsmb->info.session_key.data)); if (serr) { goto error; } *newsock = npsock; error: if (np) { NtCtxCloseFile(smb->context, np); } SMB_SOCKET_UNLOCK(smb); #endif return serr; } #if HAVE_LIKEWISE_LWIO INTERNAL void* rpc__smb_socket_listen_thread(void* data) { int serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) data; IO_STATUS_BLOCK status_block = { 0 }; char *endpoint = NULL; char *pipename = NULL; unsigned32 dbg_status = 0; PSTR smbpath = NULL; IO_FILE_NAME filename = { 0 }; size_t i; char c = 0; LONG64 default_timeout = 0; SMB_SOCKET_LOCK(smb); while (smb->state != SMB_STATE_LISTEN) { if (smb->state == SMB_STATE_ERROR) { goto error; } rpc__smb_socket_wait(smb); } /* Extract endpoint */ rpc__naf_addr_inq_endpoint ((rpc_addr_p_t) &smb->localaddr, (unsigned_char_t**) &endpoint, &dbg_status); if (rpc__np_is_valid_endpoint(endpoint, &dbg_status)) { pipename = endpoint + sizeof("\\pipe\\") - 1; } else { serr = RPC_C_SOCKET_EINVAL; goto error; } serr = NtStatusToErrno( LwRtlCStringAllocatePrintf( &smbpath, "\\npfs\\%s", (char*) pipename)); if (serr) { goto error; } serr = NtStatusToErrno( LwRtlWC16StringAllocateFromCString( &filename.FileName, smbpath)); if (serr) { goto error; } while (smb->state == SMB_STATE_LISTEN) { SMB_SOCKET_UNLOCK(smb); serr = NtStatusToErrno( LwNtCtxCreateNamedPipeFile( smb->context, /* IO context */ NULL, /* Security token */ &smb->np, /* NP handle */ NULL, /* Async control */ &status_block, /* IO status block */ &filename, /* Filename */ NULL, /* Security descriptor */ NULL, /* Security QOS */ GENERIC_READ | GENERIC_WRITE, /* Desired access mode */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* Share access mode */ FILE_CREATE, /* Create disposition */ 0, /* Create options */ 0, /* Named pipe type */ 0, /* Read mode */ 0, /* Completion mode */ smb->accept_backlog.capacity, /* Maximum instances */ 0, /* Inbound quota */ 0, /* Outbound quota */ &default_timeout /* Default timeout */ )); if (serr) { SMB_SOCKET_LOCK(smb); goto error; } serr = NtStatusToErrno( LwIoCtxConnectNamedPipe( smb->context, smb->np, NULL, &status_block)); if (serr) { SMB_SOCKET_LOCK(smb); goto error; } SMB_SOCKET_LOCK(smb); /* Wait for a slot to open in the accept queue */ while (smb->accept_backlog.length == smb->accept_backlog.capacity) { if (smb->state == SMB_STATE_ERROR) { goto error; } rpc__smb_socket_wait(smb); } /* Put the handle into the accept queue */ for (i = 0; i < smb->accept_backlog.capacity; i++) { if (smb->accept_backlog.queue[i] == NULL) { smb->accept_backlog.queue[i] = smb->np; smb->np = NULL; smb->accept_backlog.length++; if (write(smb->accept_backlog.selectfd[1], &c, sizeof(c)) != sizeof(c)) { serr = errno; goto error; } dcethread_cond_broadcast_throw(&smb->event); break; } } } error: if (filename.FileName) { RtlMemoryFree(filename.FileName); } if (smbpath) { RtlMemoryFree(smbpath); } // rpc_string_free handles when *ptr is NULL rpc_string_free((unsigned_char_t**) &endpoint, &dbg_status); if (serr) { rpc__smb_socket_change_state(smb, SMB_STATE_ERROR); } SMB_SOCKET_UNLOCK(smb); return NULL; } #endif INTERNAL rpc_socket_error_t rpc__smb_socket_listen( #if HAVE_LIKEWISE_LWIO rpc_socket_t sock, int backlog #else rpc_socket_t sock ATTRIBUTE_UNUSED, int backlog ATTRIBUTE_UNUSED #endif ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_listen called\n")); #if !defined(HAVE_LIKEWISE_LWIO) serr = RPC_C_SOCKET_ENOTSUP; return serr; #else rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; SMB_SOCKET_LOCK(smb); smb->accept_backlog.capacity = backlog; smb->accept_backlog.length = 0; smb->accept_backlog.queue = calloc(backlog, sizeof(*smb->accept_backlog.queue)); if (!smb->accept_backlog.queue) { serr = RPC_C_SOCKET_ENOMEM; goto error; } if (pipe(smb->accept_backlog.selectfd) != 0) { serr = errno; goto error; } smb->state = SMB_STATE_LISTEN; dcethread_create_throw(&smb->listen_thread, NULL, rpc__smb_socket_listen_thread, smb); error: SMB_SOCKET_UNLOCK(smb); #endif return serr; } #if HAVE_SMBCLIENT_FRAMEWORK INTERNAL rpc_socket_error_t smb_data_send( #if SMB_NP_NO_TRANSACTIONS rpc_socket_t sock #else rpc_socket_t sock ATTRIBUTE_UNUSED #endif ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb; unsigned char* cursor; size_t bytes_written = 0; #if SMB_NP_NO_TRANSACTIONS NTSTATUS status; smb = (rpc_smb_socket_p_t) sock->data.pointer; cursor = smb->sendbuffer.base; #endif do { #if SMB_NP_NO_TRANSACTIONS /* Write the data out without using transactions */ status = SMBWriteFile(smb->handle, smb->hFile, cursor, 0, smb->sendbuffer.start_cursor - cursor, &bytes_written); if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_do_send - SMBWriteFile failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); } #else /* for transactions, do nothing here. Later in the receive, we will do the send and receive in a single transaction. */ serr = RPC_C_SOCKET_OK; goto error; #endif if (serr) { goto error; } cursor += bytes_written; } while (cursor < smb->sendbuffer.start_cursor); /* Settle the remaining data (which hopefully should be zero if the runtime calls us with complete packets) to the start of the send buffer */ rpc__smb_buffer_settle(&smb->sendbuffer); error: return serr; } INTERNAL rpc_socket_error_t smb_data_do_recv( rpc_socket_t sock, size_t* count ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; size_t bytes_requested = 0; size_t bytes_read = 0; NTSTATUS status = STATUS_SUCCESS; #if !SMB_NP_NO_TRANSACTIONS unsigned char* cursor = smb->sendbuffer.base; rpc_cn_common_hdr_p_t packet; size_t frag_len; size_t bytes_written; int sent_data = 0; #endif *count = 0; do { serr = rpc__smb_buffer_ensure_available(&smb->recvbuffer, smb->maxRecvBufferSize); if (serr) { goto error; } bytes_read = 0; bytes_requested = rpc__smb_buffer_available(&smb->recvbuffer); #if SMB_NP_NO_TRANSACTIONS /* Read the data in without using transactions */ status = SMBReadFile(smb->handle, smb->hFile, smb->recvbuffer.end_cursor, 0, bytes_requested, &bytes_read); if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_do_recv - SMBReadFile failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); } #else if ((smb->sendbuffer.start_cursor - cursor) != 0) { /* Check to see if this is a last fragment or not */ packet = (rpc_cn_common_hdr_p_t) cursor; while (!(packet->flags & RPC_C_CN_FLAGS_LAST_FRAG)) { /* its not a last fragment, so send with SMBWriteFile */ frag_len = rpc__smb_fragment_size(packet); /* Safety check to make sure we are not past the end of the sendbuffer */ if ( (cursor + frag_len) > smb->sendbuffer.start_cursor) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_do_recv - past end of send buffer\n")); serr = RPC_C_SOCKET_EIO; goto error; } bytes_written = 0; sent_data = 1; status = SMBWriteFile(smb->handle, smb->hFile, cursor, 0, frag_len, &bytes_written); if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_do_send - SMBWriteFile failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); } if (serr) { goto error; } cursor += bytes_written; packet = (rpc_cn_common_hdr_p_t) cursor; } } if ((smb->sendbuffer.start_cursor - cursor) == 0) { /* Must be reading in a fragment, so read the data in without using transactions */ status = SMBReadFile(smb->handle, smb->hFile, smb->recvbuffer.end_cursor, 0, bytes_requested, &bytes_read); if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_do_recv - SMBReadFile failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); } } else { /* for transactions, do send and rcv in a single transaction. */ sent_data = 1; status = SMBTransactNamedPipe(smb->handle, smb->hFile, cursor, smb->sendbuffer.start_cursor - cursor, smb->recvbuffer.end_cursor, bytes_requested, &bytes_read); if (!NT_SUCCESS(status)) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_do_recv - SMBTransactNamedPipe failed 0x%x\n", status)); serr = rpc_smb_ntstatus_to_rpc_error (status); } } if (sent_data == 1) { /* assume all the data got sent */ rpc__smb_buffer_settle(&smb->sendbuffer); sent_data = 0; cursor = smb->sendbuffer.base; } #endif if (status == STATUS_END_OF_FILE) { serr = RPC_C_SOCKET_OK; bytes_read = 0; } else { smb->recvbuffer.end_cursor += bytes_read; } if (((size_t)*count + bytes_read) > SIZE_T_MAX || ((size_t)*count + bytes_read) < (size_t)*count) { serr = RPC_C_SOCKET_ENOSPC; goto error; } *count += (size_t)bytes_read; } while (NT_SUCCESS(status) && bytes_read == bytes_requested); error: return serr; } #endif INTERNAL rpc_socket_error_t rpc__smb_socket_do_send( rpc_socket_t sock ) { #if HAVE_SMBCLIENT_FRAMEWORK return (smb_data_send (sock)); #elif HAVE_LIKEWISE_LWIO rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; unsigned char* cursor = smb->sendbuffer.base; DWORD bytes_written = 0; IO_STATUS_BLOCK io_status = { 0 }; do { serr = NtStatusToErrno( NtCtxWriteFile( smb->context, /* IO context */ smb->np, /* File handle */ NULL, /* Async control block */ &io_status, /* IO status block */ smb->sendbuffer.base, /* Buffer */ smb->sendbuffer.start_cursor - cursor, /* Length */ NULL, /* Byte offset */ NULL /* Key */ )); if (serr) { goto error; } bytes_written = io_status.BytesTransferred; cursor += bytes_written; } while (cursor < smb->sendbuffer.start_cursor); /* Settle the remaining data (which hopefully should be zero if the runtime calls us with complete packets) to the start of the send buffer */ rpc__smb_buffer_settle(&smb->sendbuffer); error: return serr; #else return RPC_C_SOCKET_ENOTSUP; #endif } INTERNAL rpc_socket_error_t rpc__smb_socket_do_recv( rpc_socket_t sock, size_t* count ) { #if HAVE_SMBCLIENT_FRAMEWORK return (smb_data_do_recv (sock, count)); #elif HAVE_LIKEWISE_LWIO rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; DWORD bytes_requested = 0; DWORD bytes_read = 0; IO_STATUS_BLOCK io_status = { 0 }; NTSTATUS status = STATUS_SUCCESS; *count = 0; do { serr = rpc__smb_buffer_ensure_available(&smb->recvbuffer, smb->maxRecvBufferSize); if (serr) { goto error; } bytes_read = 0; bytes_requested = rpc__smb_buffer_available(&smb->recvbuffer); status = NtCtxReadFile( smb->context, /* IO context */ smb->np, /* File handle */ NULL, /* Async control block */ &io_status, /* IO status block */ smb->recvbuffer.end_cursor, /* Buffer */ bytes_requested, /* Length */ NULL, /* Byte offset */ NULL /* Key */ ); if (status == STATUS_END_OF_FILE) { serr = 0; bytes_read = 0; } else { serr = NtStatusToErrno(status); bytes_read = io_status.BytesTransferred; smb->recvbuffer.end_cursor += bytes_read; } if (((off_t)*count + bytes_read) > SIZE_T_MAX || ((off_t)*count + bytes_read) < (off_t)*count) { serr = RPC_C_SOCKET_ENOSPC; goto error; } *count += bytes_read; } while (NT_SUCCESS(status) && bytes_read == bytes_requested); error: return serr; #else return RPC_C_SOCKET_ENOTSUP; #endif } INTERNAL rpc_socket_error_t rpc__smb_socket_sendmsg( rpc_socket_t sock, rpc_socket_iovec_p_t iov, int iov_len, rpc_addr_p_t addr ATTRIBUTE_UNUSED, size_t *cc ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; int i; size_t pending = 0; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_sendmsg called\n")); SMB_SOCKET_LOCK(smb); /* Wait until we are in a state where we can send */ while (smb->state != SMB_STATE_SEND) { if (smb->state == SMB_STATE_ERROR) { serr = -1; goto error; } rpc__smb_socket_wait(smb); } *cc = 0; /* Append all fragments into a single buffer */ for (i = 0; i < iov_len; i++) { serr = rpc__smb_buffer_append(&smb->sendbuffer, iov[i].iov_base, iov[i].iov_len); if (serr) { goto error; } *cc += (int)iov[i].iov_len; } /* Look for the last fragment and do send if we find it */ if (rpc__smb_buffer_advance_cursor(&smb->sendbuffer, &pending)) { serr = rpc__smb_socket_do_send(sock); if (serr) { goto error; } /* Switch into recv mode */ rpc__smb_socket_change_state(smb, SMB_STATE_RECV); } cleanup: SMB_SOCKET_UNLOCK(smb); return serr; error: rpc__smb_socket_change_state(smb, SMB_STATE_ERROR); goto cleanup; } INTERNAL rpc_socket_error_t rpc__smb_socket_recvfrom( rpc_socket_t sock ATTRIBUTE_UNUSED, byte_p_t buf ATTRIBUTE_UNUSED, int len ATTRIBUTE_UNUSED, rpc_addr_p_t from ATTRIBUTE_UNUSED, size_t *cc ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_ENOTSUP; fprintf(stderr, "WARNING: unsupported smb socket function %s\n", __FUNCTION__); RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_recvfrom called\n")); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_recvmsg( rpc_socket_t sock, rpc_socket_iovec_p_t iov, int iov_len, rpc_addr_p_t addr, size_t *cc ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; int i; size_t pending; size_t count; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_recvmsg called\n")); SMB_SOCKET_LOCK(smb); while (smb->state != SMB_STATE_RECV) { if (smb->state == SMB_STATE_ERROR) { serr = -1; goto error; } rpc__smb_socket_wait(smb); } *cc = 0; if (rpc__smb_buffer_length(&smb->recvbuffer) == 0) { /* Nothing in buffer, read a complete message */ do { serr = rpc__smb_socket_do_recv(sock, &count); if (serr) { goto error; } if (count == 0) { break; } } while (!rpc__smb_buffer_advance_cursor(&smb->recvbuffer, NULL)); /* Reset cursor back to start to begin disperal into scatter buffer */ smb->recvbuffer.start_cursor = smb->recvbuffer.base; } for (i = 0; i < iov_len; i++) { pending = rpc__smb_buffer_pending(&smb->recvbuffer); if (iov[i].iov_len < pending) { memcpy(iov[i].iov_base, smb->recvbuffer.start_cursor, iov[i].iov_len); smb->recvbuffer.start_cursor += iov[i].iov_len; *cc += (int)iov[i].iov_len; } else { memcpy(iov[i].iov_base, smb->recvbuffer.start_cursor, pending); *cc += (int)pending; /* Reset buffer because we have emptied it */ smb->recvbuffer.start_cursor = smb->recvbuffer.end_cursor = smb->recvbuffer.base; /* Switch into send mode */ rpc__smb_socket_change_state(smb, SMB_STATE_SEND); } } if (addr) { memcpy(addr, &smb->peeraddr, sizeof(smb->peeraddr)); } cleanup: SMB_SOCKET_UNLOCK(smb); return serr; error: rpc__smb_socket_change_state(smb, SMB_STATE_ERROR); goto cleanup; } INTERNAL rpc_socket_error_t rpc__smb_socket_inq_endpoint( rpc_socket_t sock, rpc_addr_p_t addr ) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_inq_endpoint called\n")); rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; if (addr->len == 0) { addr->len = sizeof(addr->sa); } addr->rpc_protseq_id = smb->localaddr.rpc_protseq_id; memcpy(&addr->sa, &smb->localaddr.sa, addr->len); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_set_broadcast( rpc_socket_t sock ATTRIBUTE_UNUSED ) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_set_broadcast called\n")); rpc_socket_error_t serr = RPC_C_SOCKET_OK; return serr; } #if HAVE_SMBCLIENT_FRAMEWORK && HAVE_SMBCLIENT_SMBGETSERVERPROPERTIES INTERNAL rpc__smbclient_set_bufs( rpc_socket_t sock, unsigned32 *ntxsize, unsigned32 *nrxsize ) { rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; SMBServerPropertiesV1 server_properties; NTSTATUS status; if ( !smb->handle || (smb->maxSendBufferSize != 0) ) { return (RPC_C_SOCKET_OK); } memset(&server_properties, 0, sizeof(server_properties)); /* try to get the max transaction size that is supported */ status = SMBGetServerProperties(smb->handle, &server_properties, kPropertiesVersion, sizeof(server_properties)); if (NT_SUCCESS(status)) { smb->maxSendBufferSize = (unsigned32) MIN (server_properties.maxWriteBytes, server_properties.maxTransactBytes); smb->maxRecvBufferSize = (unsigned32) MIN (server_properties.maxReadBytes, server_properties.maxTransactBytes); /* round down to nearest 1K */ smb->maxSendBufferSize = (smb->maxSendBufferSize / 1024) * 1024; smb->maxRecvBufferSize = (smb->maxRecvBufferSize / 1024) * 1024; /* fragments can not be bigger than UInt16 */ if (smb->maxSendBufferSize > UINT16_MAX) smb->maxSendBufferSize = UINT16_MAX; if (smb->maxRecvBufferSize > UINT16_MAX) smb->maxRecvBufferSize = UINT16_MAX; } *ntxsize = (unsigned32) smb->maxSendBufferSize; *nrxsize = (unsigned32) smb->maxRecvBufferSize; return (RPC_C_SOCKET_OK); } #endif INTERNAL rpc_socket_error_t rpc__smb_socket_set_bufs( rpc_socket_t sock ATTRIBUTE_UNUSED, unsigned32 txsize ATTRIBUTE_UNUSED, unsigned32 rxsize ATTRIBUTE_UNUSED, unsigned32 *ntxsize ATTRIBUTE_UNUSED, unsigned32 *nrxsize ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_set_bufs called\n")); #if HAVE_SMBCLIENT_FRAMEWORK && HAVE_SMBCLIENT_SMBGETSERVERPROPERTIES serr = rpc__smbclient_set_bufs(sock, ntxsize, nrxsize); #else *ntxsize = MIN(RPC_C_CN_LARGE_FRAG_SIZE, RPC_C_SOCKET_MAX_SNDBUF); *nrxsize = MIN(RPC_C_CN_LARGE_FRAG_SIZE, RPC_C_SOCKET_MAX_RCVBUF); #endif return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_set_nbio( rpc_socket_t sock ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_ENOTSUP; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_set_nbio called\n")); fprintf(stderr, "WARNING: unsupported smb socket function %s\n", __FUNCTION__); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_set_close_on_exec( rpc_socket_t sock ATTRIBUTE_UNUSED ) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_set_close_on_exec called\n")); rpc_socket_error_t serr = RPC_C_SOCKET_OK; return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_getpeername( rpc_socket_t sock, rpc_addr_p_t addr ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_getpeername called\n")); SMB_SOCKET_LOCK(smb); #if HAVE_LIKEWISE_LWIO if (!smb->np) { serr = RPC_C_SOCKET_EINVAL; goto error; } #endif memcpy(addr, &smb->peeraddr, sizeof(smb->peeraddr)); error: SMB_SOCKET_UNLOCK(smb); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_get_if_id( rpc_socket_t sock ATTRIBUTE_UNUSED, rpc_network_if_id_t *network_if_id ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_get_if_id called\n")); *network_if_id = SOCK_STREAM; return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_set_keepalive( rpc_socket_t sock ATTRIBUTE_UNUSED ) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_set_keepalive called\n")); rpc_socket_error_t serr = RPC_C_SOCKET_OK; return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_nowriteblock_wait( rpc_socket_t sock ATTRIBUTE_UNUSED, struct timeval *tmo ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_ENOTSUP; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_nowriteblock_wait called\n")); fprintf(stderr, "WARNING: unsupported smb socket function %s\n", __FUNCTION__); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_set_rcvtimeo( rpc_socket_t sock ATTRIBUTE_UNUSED, struct timeval *tmo ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_set_rcvtimeo called\n")); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_getpeereid( rpc_socket_t sock ATTRIBUTE_UNUSED, uid_t *euid ATTRIBUTE_UNUSED, gid_t *egid ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_ENOTSUP; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_getpeereid called\n")); fprintf(stderr, "WARNING: unsupported smb socket function %s\n", __FUNCTION__); return serr; } INTERNAL int rpc__smb_socket_get_select_desc( rpc_socket_t sock ) { RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_get_select_desc called\n")); rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; return smb->accept_backlog.selectfd[0]; } INTERNAL rpc_socket_error_t rpc__smb_socket_enum_ifaces( rpc_socket_t sock ATTRIBUTE_UNUSED, rpc_socket_enum_iface_fn_p_t efun ATTRIBUTE_UNUSED, rpc_addr_vector_p_t *rpc_addr_vec ATTRIBUTE_UNUSED, rpc_addr_vector_p_t *netmask_addr_vec ATTRIBUTE_UNUSED, rpc_addr_vector_p_t *broadcast_addr_vec ATTRIBUTE_UNUSED ) { rpc_socket_error_t serr = RPC_C_SOCKET_ENOTSUP; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_enum_ifaces called\n")); fprintf(stderr, "WARNING: unsupported smb socket function %s\n", __FUNCTION__); return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_inq_transport_info( #if HAVE_LIKEWISE_LWIO rpc_socket_t sock, #else rpc_socket_t sock ATTRIBUTE_UNUSED, #endif rpc_transport_info_handle_t* info ) { rpc_socket_error_t serr = RPC_C_SOCKET_OK; #if HAVE_LIKEWISE_LWIO rpc_smb_socket_p_t smb = (rpc_smb_socket_p_t) sock->data.pointer; #endif rpc_smb_transport_info_p_t smb_info = NULL; RPC_DBG_PRINTF(rpc_e_dbg_general, 7, ("rpc__smb_socket_inq_transport_info called\n")); #if !defined(HAVE_LIKEWISE_LWIO) serr = RPC_C_SOCKET_ENOTSUP; goto error; #else smb_info = calloc(1, sizeof(*smb_info)); if (!smb_info) { serr = RPC_C_SOCKET_ENOMEM; goto error; } if (smb->info.creds) { serr = NtStatusToErrno(LwIoCopyCreds(smb->info.creds, &smb_info->creds)); if (serr) { goto error; } } if (smb->info.peer_principal) { smb_info->peer_principal = strdup(smb->info.peer_principal); if (!smb_info->peer_principal) { serr = RPC_C_SOCKET_ENOMEM; goto error; } } if (smb->info.session_key.data) { smb_info->session_key.data = malloc(smb->info.session_key.length); if (!smb_info->session_key.data) { serr = RPC_C_SOCKET_ENOMEM; goto error; } memcpy(smb_info->session_key.data, smb->info.session_key.data, smb->info.session_key.length); smb_info->session_key.length = smb->info.session_key.length; } *info = (rpc_transport_info_handle_t) smb_info; #endif error: if (serr) { *info = NULL; if (smb_info) { rpc_smb_transport_info_free((rpc_transport_info_handle_t) smb_info); } } return serr; } /* ======================================================================== */ /* * R P C _ _ S M B _ S O C K E T _ D U P L I C A T E * * Wrap the native socket representation in a rpc_socket_t. We duplicate the * socket file descriptor because we will eventually end up close(2)ing it. * * Note that we sneakily replace the socket's vtable, thereby turning it into * a BSD socket. This is necessary because the native socket representation * that comes down from inetd or launchd is a file descriptor. If we used the * real SMB vtable, then we would be hooked into the Likewise SMB redirector * codem which is not what we want. */ INTERNAL rpc_socket_error_t rpc__smb_socket_duplicate( rpc_socket_t sock, rpc_protseq_id_t protseq_id, const void * sockrep /* pointer to native representation */ ) { const int * sockfd = (const int *)sockrep; rpc_socket_error_t serr = RPC_C_SOCKET_OK; RPC_DBG_GPRINTF(("(rpc__smb_socket_duplicate) sockfd=%d\n", sockfd ? *sockfd : -1)); if (sockfd == NULL || *sockfd == -1) { return RPC_C_SOCKET_ENOTSOCK; } if (protseq_id != rpc_c_protseq_id_ncacn_np) { return RPC_C_SOCKET_EINVAL; } rpc__smb_socket_destruct(sock); serr = rpc_g_bsd_socket_vtbl.socket_construct(sock, rpc_c_protseq_id_ncalrpc, NULL); if (RPC_SOCKET_IS_ERR(serr)) { return serr; } // Flip the rpc_socket_t info ASAP in case some higer level // calls a generic rpc__socket routine. sock->vtbl = &rpc_g_bsd_socket_vtbl; sock->pseq_id = protseq_id; serr = rpc_g_bsd_socket_vtbl.socket_duplicate(sock, rpc_c_protseq_id_ncacn_np, sockrep); if (RPC_SOCKET_IS_ERR(serr)) { return serr; } return serr; } INTERNAL rpc_socket_error_t rpc__smb_socket_transport_inq_access_token( #if HAVE_LIKEWISE_LWMAPSECURITY rpc_transport_info_handle_t info, rpc_access_token_p_t* token #else rpc_transport_info_handle_t info ATTRIBUTE_UNUSED, rpc_access_token_p_t* token ATTRIBUTE_UNUSED #endif ) { #if HAVE_LIKEWISE_LWMAPSECURITY rpc_smb_transport_info_p_t smb_info = (rpc_smb_transport_info_p_t) info; NTSTATUS status = STATUS_SUCCESS; PLW_MAP_SECURITY_CONTEXT context = NULL; status = LwMapSecurityCreateContext(&context); if (status) goto error; status = LwMapSecurityCreateAccessTokenFromCStringUsername( context, token, smb_info->peer_principal); if (status) goto error; error: LwMapSecurityFreeContext(&context); return LwNtStatusToErrno(status); #else return RPC_C_SOCKET_ENOTSUP; #endif } rpc_socket_vtbl_t const rpc_g_smb_socket_vtbl = { .socket_duplicate = rpc__smb_socket_duplicate, .socket_construct = rpc__smb_socket_construct, .socket_destruct = rpc__smb_socket_destruct, .socket_bind = rpc__smb_socket_bind, .socket_connect = rpc__smb_socket_connect, .socket_accept = rpc__smb_socket_accept, .socket_listen = rpc__smb_socket_listen, .socket_sendmsg = rpc__smb_socket_sendmsg, .socket_recvfrom = rpc__smb_socket_recvfrom, .socket_recvmsg = rpc__smb_socket_recvmsg, .socket_inq_endpoint = rpc__smb_socket_inq_endpoint, .socket_set_broadcast = rpc__smb_socket_set_broadcast, .socket_set_bufs = rpc__smb_socket_set_bufs, .socket_set_nbio = rpc__smb_socket_set_nbio, .socket_set_close_on_exec = rpc__smb_socket_set_close_on_exec, .socket_getpeername = rpc__smb_socket_getpeername, .socket_get_if_id = rpc__smb_socket_get_if_id, .socket_set_keepalive = rpc__smb_socket_set_keepalive, .socket_nowriteblock_wait = rpc__smb_socket_nowriteblock_wait, .socket_set_rcvtimeo = rpc__smb_socket_set_rcvtimeo, .socket_getpeereid = rpc__smb_socket_getpeereid, .socket_get_select_desc = rpc__smb_socket_get_select_desc, .socket_enum_ifaces = rpc__smb_socket_enum_ifaces, .socket_inq_transport_info = rpc__smb_socket_inq_transport_info, .transport_info_free = rpc_smb_transport_info_free, .transport_info_equal = rpc__smb_transport_info_equal, .transport_inq_access_token = rpc__smb_socket_transport_inq_access_token };