diff options
author | bp <bp@FreeBSD.org> | 2001-04-10 07:59:06 +0000 |
---|---|---|
committer | bp <bp@FreeBSD.org> | 2001-04-10 07:59:06 +0000 |
commit | a414f03f5d87ade219aa2e4bcd0830eceaee6bd3 (patch) | |
tree | e02d13218bf41d783a93777341cba62b2921dc10 /sys/netsmb | |
parent | 88436d21df771b1b6d764b003c5daecf40dd767f (diff) | |
download | FreeBSD-src-a414f03f5d87ade219aa2e4bcd0830eceaee6bd3.zip FreeBSD-src-a414f03f5d87ade219aa2e4bcd0830eceaee6bd3.tar.gz |
Import kernel part of SMB/CIFS requester.
Add smbfs(CIFS) filesystem.
Userland part will be in the ports tree for a while.
Obtained from: smbfs-1.3.7-dev package.
Diffstat (limited to 'sys/netsmb')
-rw-r--r-- | sys/netsmb/netbios.h | 138 | ||||
-rw-r--r-- | sys/netsmb/smb.h | 388 | ||||
-rw-r--r-- | sys/netsmb/smb_conn.c | 874 | ||||
-rw-r--r-- | sys/netsmb/smb_conn.h | 464 | ||||
-rw-r--r-- | sys/netsmb/smb_crypt.c | 146 | ||||
-rw-r--r-- | sys/netsmb/smb_dev.c | 448 | ||||
-rw-r--r-- | sys/netsmb/smb_dev.h | 199 | ||||
-rw-r--r-- | sys/netsmb/smb_iod.c | 709 | ||||
-rw-r--r-- | sys/netsmb/smb_rq.c | 752 | ||||
-rw-r--r-- | sys/netsmb/smb_rq.h | 151 | ||||
-rw-r--r-- | sys/netsmb/smb_smb.c | 660 | ||||
-rw-r--r-- | sys/netsmb/smb_subr.c | 359 | ||||
-rw-r--r-- | sys/netsmb/smb_subr.h | 198 | ||||
-rw-r--r-- | sys/netsmb/smb_tran.h | 89 | ||||
-rw-r--r-- | sys/netsmb/smb_trantcp.c | 672 | ||||
-rw-r--r-- | sys/netsmb/smb_trantcp.h | 88 | ||||
-rw-r--r-- | sys/netsmb/smb_usr.c | 355 |
17 files changed, 6690 insertions, 0 deletions
diff --git a/sys/netsmb/netbios.h b/sys/netsmb/netbios.h new file mode 100644 index 0000000..961f5be --- /dev/null +++ b/sys/netsmb/netbios.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _NETSMB_NETBIOS_H_ +#define _NETSMB_NETBIOS_H_ + +/* + * make this file dirty... + */ +#ifndef _NETINET_IN_H_ +#include <netinet/in.h> +#endif + +#ifndef _NETIPX_IPX_H_ +#include <netipx/ipx.h> +#endif + +#define AF_NETBIOS AF_NS /* XXX: should go to socket.h */ +#define PF_NETBIOS AF_NETBIOS + +#define NBPROTO_TCPSSN 1 /* NETBIOS session over TCP */ +#define NBPROTO_IPXSSN 11 /* NETBIOS over IPX */ + +#define NB_NAMELEN 16 +#define NB_ENCNAMELEN NB_NAMELEN * 2 +#define NB_MAXLABLEN 63 + +#define NB_MINSALEN (sizeof(struct sockaddr_nb)) + +/* + * name types + */ +#define NBT_WKSTA 0x00 +#define NBT_SERVER 0x20 + +/* + * Session packet types + */ +#define NB_SSN_MESSAGE 0x0 +#define NB_SSN_REQUEST 0x81 +#define NB_SSN_POSRESP 0x82 +#define NB_SSN_NEGRESP 0x83 +#define NB_SSN_RTGRESP 0x84 +#define NB_SSN_KEEPALIVE 0x85 + +/* + * resolver: Opcodes + */ +#define NBNS_OPCODE_QUERY 0x00 +#define NBNS_OPCODE_REGISTER 0x05 +#define NBNS_OPCODE_RELEASE 0x06 +#define NBNS_OPCODE_WACK 0x07 +#define NBNS_OPCODE_REFRESH 0x08 +#define NBNS_OPCODE_RESPONSE 0x10 /* or'ed with other opcodes */ + +/* + * resolver: NM_FLAGS + */ +#define NBNS_NMFLAG_BCAST 0x01 +#define NBNS_NMFLAG_RA 0x08 /* recursion available */ +#define NBNS_NMFLAG_RD 0x10 /* recursion desired */ +#define NBNS_NMFLAG_TC 0x20 /* truncation occured */ +#define NBNS_NMFLAG_AA 0x40 /* authoritative answer */ + +/* + * resolver: Question types + */ +#define NBNS_QUESTION_TYPE_NB 0x0020 +#define NBNS_QUESTION_TYPE_NBSTAT 0x0021 + +/* + * resolver: Question class + */ +#define NBNS_QUESTION_CLASS_IN 0x0001 + +/* + * resolver: Limits + */ +#define NBNS_MAXREDIRECTS 3 /* maximum number of accepted redirects */ +#define NBDG_MAXSIZE 576 /* maximum nbns datagram size */ + +/* + * NETBIOS addressing + */ +union nb_tran { + struct sockaddr_in x_in; + struct sockaddr_ipx x_ipx; +}; + +struct nb_name { + u_int nn_type; + u_char nn_name[NB_NAMELEN + 1]; + u_char * nn_scope; +}; + +/* + * Socket address + */ +struct sockaddr_nb { + u_char snb_len; + u_char snb_family; + union nb_tran snb_tran; /* transport */ + u_char snb_name[1 + NB_ENCNAMELEN + 1]; /* encoded */ +}; + +#define snb_addrin snb_tran.x_in + +#endif /* !_NETSMB_NETBIOS_H_ */ diff --git a/sys/netsmb/smb.h b/sys/netsmb/smb.h new file mode 100644 index 0000000..4bd3096 --- /dev/null +++ b/sys/netsmb/smb.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * Common definintions and structures for SMB/CIFS protocol + */ + +#ifndef _NETSMB_SMB_H_ +#define _NETSMB_SMB_H_ + +#define SMB_TCP_PORT 139 +/* + * SMB dialects that we have to deal with. + */ +enum smb_dialects { + SMB_DIALECT_NONE, + SMB_DIALECT_CORE, /* PC NETWORK PROGRAM 1.0, PCLAN1.0 */ + SMB_DIALECT_COREPLUS, /* MICROSOFT NETWORKS 1.03 */ + SMB_DIALECT_LANMAN1_0, /* MICROSOFT NETWORKS 3.0, LANMAN1.0 */ + SMB_DIALECT_LANMAN2_0, /* LM1.2X002, DOS LM1.2X002, Samba */ + SMB_DIALECT_LANMAN2_1, /* DOS LANMAN2.1, LANMAN2.1 */ + SMB_DIALECT_NTLM0_12 /* NT LM 0.12, Windows for Workgroups 3.1a, + * NT LANMAN 1.0 */ +}; + +/* + * Formats of data/string buffers + */ +#define SMB_DT_DATA 1 +#define SMB_DT_DIALECT 2 +#define SMB_DT_PATHNAME 3 +#define SMB_DT_ASCII 4 +#define SMB_DT_VARIABLE 5 + +/* + * SMB header + */ +#define SMB_SIGNATURE "\xFFSMB" +#define SMB_SIGLEN 4 +#define SMB_HDRMID(p) (*(u_short*)((u_char*)(p) + 30)) +#define SMB_HDRLEN 32 +/* + * bits in the smb_flags field + */ +#define SMB_FLAGS_CASELESS 0x08 +#define SMB_FLAGS_SERVER_RESP 0x80 /* indicates a response */ + +/* + * bits in the smb_flags2 field + */ +#define SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001 +#define SMB_FLAGS2_KNOWS_EAS 0x0002 /* client know about EAs */ +#define SMB_FLAGS2_SECURITY_SIGNATURE 0x0004 /* check SMB integrity */ +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 /* any path name is a long name */ +#define SMB_FLAGS2_EXT_SEC 0x0800 /* client aware of Extended + * Security negotiation */ +#define SMB_FLAGS2_DFS 0x1000 /* resolve paths in DFS */ +#define SMB_FLAGS2_PAGING_IO 0x2000 /* for exec */ +#define SMB_FLAGS2_ERR_STATUS 0x4000 /* 1 - status.status */ +#define SMB_FLAGS2_UNICODE 0x8000 /* use Unicode for all strings */ + +#define SMB_UID_UNKNOWN 0xffff +#define SMB_TID_UNKNOWN 0xffff + +/* + * Security mode bits + */ +#define SMB_SM_USER 0x01 /* server in the user security mode */ +#define SMB_SM_ENCRYPT 0x02 /* use challenge/responce */ + +/* + * NTLM capabilities + */ +#define SMB_CAP_RAW_MODE 0x0001 +#define SMB_CAP_MPX_MODE 0x0002 +#define SMB_CAP_UNICODE 0x0004 +#define SMB_CAP_LARGE_FILES 0x0008 /* 64 bit offsets supported */ +#define SMB_CAP_NT_SMBS 0x0010 +#define SMB_CAP_NT_FIND 0x0200 +#define SMB_CAP_EXT_SECURITY 0x80000000 + +/* + * File attributes + */ +#define SMB_FA_RDONLY 0x01 +#define SMB_FA_HIDDEN 0x02 +#define SMB_FA_SYSTEM 0x04 +#define SMB_FA_VOLUME 0x08 +#define SMB_FA_DIR 0x10 +#define SMB_FA_ARCHIVE 0x20 + +/* + * Extended file attributes + */ +#define SMB_EFA_RDONLY 0x0001 +#define SMB_EFA_HIDDEN 0x0002 +#define SMB_EFA_SYSTEM 0x0004 +#define SMB_EFA_ARCHIVE 0x0020 +#define SMB_EFA_NORMAL 0x0080 +#define SMB_EFA_TEMPORARY 0x0100 +#define SMB_EFA_COMPRESSED 0x0800 +#define SMB_EFA_POSIX_SEMANTICS 0x00100000 +#define SMB_EFA_BACKUP_SEMANTICS 0x02000000 +#define SMB_EFA_DELETE_ON_CLOSE 0x04000000 +#define SMB_EFA_SEQUENTIAL_SCAN 0x08000000 +#define SMB_EFA_RANDOM_ACCESS 0x10000000 +#define SMB_EFA_NO_BUFFERING 0x20000000 +#define SMB_EFA_WRITE_THROUGH 0x80000000 + +/* + * Access Mode Encoding + */ +#define SMB_AM_OPENREAD 0x0000 +#define SMB_AM_OPENWRITE 0x0001 +#define SMB_AM_OPENRW 0x0002 +#define SMB_AM_OPENEXEC 0x0003 +#define SMB_SM_COMPAT 0x0000 +#define SMB_SM_EXCLUSIVE 0x0010 +#define SMB_SM_DENYWRITE 0x0020 +#define SMB_SM_DENYREADEXEC 0x0030 +#define SMB_SM_DENYNONE 0x0040 + +/* + * SMB commands + */ +#define SMB_COM_CREATE_DIRECTORY 0x00 +#define SMB_COM_DELETE_DIRECTORY 0x01 +#define SMB_COM_OPEN 0x02 +#define SMB_COM_CREATE 0x03 +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_FLUSH 0x05 +#define SMB_COM_DELETE 0x06 +#define SMB_COM_RENAME 0x07 +#define SMB_COM_QUERY_INFORMATION 0x08 +#define SMB_COM_SET_INFORMATION 0x09 +#define SMB_COM_READ 0x0A +#define SMB_COM_WRITE 0x0B +#define SMB_COM_LOCK_BYTE_RANGE 0x0C +#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D +#define SMB_COM_CREATE_TEMPORARY 0x0E +#define SMB_COM_CREATE_NEW 0x0F +#define SMB_COM_CHECK_DIRECTORY 0x10 +#define SMB_COM_PROCESS_EXIT 0x11 +#define SMB_COM_SEEK 0x12 +#define SMB_COM_LOCK_AND_READ 0x13 +#define SMB_COM_WRITE_AND_UNLOCK 0x14 +#define SMB_COM_READ_RAW 0x1A +#define SMB_COM_READ_MPX 0x1B +#define SMB_COM_READ_MPX_SECONDARY 0x1C +#define SMB_COM_WRITE_RAW 0x1D +#define SMB_COM_WRITE_MPX 0x1E +#define SMB_COM_WRITE_COMPLETE 0x20 +#define SMB_COM_SET_INFORMATION2 0x22 +#define SMB_COM_QUERY_INFORMATION2 0x23 +#define SMB_COM_LOCKING_ANDX 0x24 +#define SMB_COM_TRANSACTION 0x25 +#define SMB_COM_TRANSACTION_SECONDARY 0x26 +#define SMB_COM_IOCTL 0x27 +#define SMB_COM_IOCTL_SECONDARY 0x28 +#define SMB_COM_COPY 0x29 +#define SMB_COM_MOVE 0x2A +#define SMB_COM_ECHO 0x2B +#define SMB_COM_WRITE_AND_CLOSE 0x2C +#define SMB_COM_OPEN_ANDX 0x2D +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_CLOSE_AND_TREE_DISC 0x31 +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 +#define SMB_COM_FIND_NOTIFY_CLOSE 0x35 +#define SMB_COM_TREE_CONNECT 0x70 +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_QUERY_INFORMATION_DISK 0x80 +#define SMB_COM_SEARCH 0x81 +#define SMB_COM_FIND 0x82 +#define SMB_COM_FIND_UNIQUE 0x83 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 +#define SMB_COM_OPEN_PRINT_FILE 0xC0 +#define SMB_COM_WRITE_PRINT_FILE 0xC1 +#define SMB_COM_CLOSE_PRINT_FILE 0xC2 +#define SMB_COM_GET_PRINT_QUEUE 0xC3 +#define SMB_COM_READ_BULK 0xD8 +#define SMB_COM_WRITE_BULK 0xD9 +#define SMB_COM_WRITE_BULK_DATA 0xDA + +/* + * TRANS2 commands + */ +#define SMB_TRANS2_OPEN2 0x00 +#define SMB_TRANS2_FIND_FIRST2 0x01 +#define SMB_TRANS2_FIND_NEXT2 0x02 +#define SMB_TRANS2_QUERY_FS_INFORMATION 0x03 +#define SMB_TRANS2_QUERY_PATH_INFORMATION 0x05 +#define SMB_TRANS2_SET_PATH_INFORMATION 0x06 +#define SMB_TRANS2_QUERY_FILE_INFORMATION 0x07 +#define SMB_TRANS2_SET_FILE_INFORMATION 0x08 +#define SMB_TRANS2_FSCTL 0x09 +#define SMB_TRANS2_IOCTL2 0x0A +#define SMB_TRANS2_FIND_NOTIFY_FIRST 0x0B +#define SMB_TRANS2_FIND_NOTIFY_NEXT 0x0C +#define SMB_TRANS2_CREATE_DIRECTORY 0x0D +#define SMB_TRANS2_SESSION_SETUP 0x0E +#define SMB_TRANS2_GET_DFS_REFERRAL 0x10 +#define SMB_TRANS2_REPORT_DFS_INCONSISTENCY 0x11 + +/* + * SMB_TRANS2_QUERY_FS_INFORMATION levels + */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 + +/* + * SMB_TRANS2_FIND_FIRST2 information levels + */ +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104 + +/* + * Set PATH/FILE information levels + */ +#define SMB_SET_FILE_BASIC_INFO 0x101 + +/* + * LOCKING_ANDX LockType flags + */ +#define SMB_LOCKING_ANDX_SHARED_LOCK 0x01 +#define SMB_LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define SMB_LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define SMB_LOCKING_ANDX_CANCEL_LOCK 0x08 +#define SMB_LOCKING_ANDX_LARGE_FILES 0x10 + +/* + * Some names length limitations. Some of them aren't declared by specs, + * but we need reasonable limits. + */ +#define SMB_MAXSRVNAMELEN 15 /* NetBIOS limit */ +#define SMB_MAXUSERNAMELEN 128 +#define SMB_MAXPASSWORDLEN 128 +#define SMB_MAXSHARENAMELEN 128 +#define SMB_MAXPKTLEN 0x1FFFF +#define SMB_MAXCHALLENGELEN 8 +#define SMB_MAXFNAMELEN 255 /* Keep in sync with MAXNAMLEN */ + +#define SMB_MAXRCN 3 /* number of reconnect attempts */ + +/* + * Error classes + */ +#define SMBSUCCESS 0x00 +#define ERRDOS 0x01 +#define ERRSRV 0x02 +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* + * Error codes for the ERRDOS class + */ +#define ERRbadfunc 1 /* Invalid function */ +#define ERRbadfile 2 /* File not found (last component) */ +#define ERRbadpath 3 /* Directory invalid */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid file handle */ +#define ERRbadmcb 7 /* Memory control blocks destroyed (huh ?) */ +#define ERRnomem 8 /* Insufficient memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadformat 11 /* Invalid format */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data */ +#define ERRbaddrive 15 /* Invalid drive specified */ +#define ERRremcd 16 /* An attempt to delete current directory */ +#define ERRdiffdevice 17 /* cross fs rename/move */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode can't be granted */ +#define ERRlock 33 /* A lock request conflicts with existing lock */ +#define ERRfilexists 80 /* The file named in the request already exists */ + +/* + * Error codes for the ERRSRV class + */ +#define ERRerror 1 /* Non-specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRaccess 4 /* The client doesn't have enough access rights */ +#define ERRinvnid 5 /* The Tid specified in a command is invalid */ +#define ERRinvnetname 6 /* Invalid server name in the tree connect */ +#define ERRinvdevice 7 /* Printer and not printer devices are mixed */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Print queue full - no space */ +#define ERRinvpfid 52 /* Invalid print file FID */ +#define ERRsmbcmd 64 /* The server did not recognise the command */ +#define ERRsrverror 65 /* The server encountered and internal error */ +#define ERRfilespecs 67 /* The Fid and path name contains an invalid combination */ +#define ERRbadpermits 69 /* Access mode invalid */ +#define ERRsetattrmode 71 /* Attribute mode invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room to buffer message */ +#define ERRrmuns 87 /* Too many remote user names */ +#define ERRtimeout 88 /* Operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request */ +#define ERRtoomanyuids 90 /* Too many UIDs active on this session */ +#define ERRbaduid 91 /* The UID is not known in this session */ +#define ERRusempx 250 /* Temporarily unable to support Raw, use MPX mode */ +#define ERRusestd 251 /* Temporarily unable to support Raw, use stdandard r/w */ +#define ERRcontmpx 252 /* Continue in MPX mode */ +#define ERRnosupport 65535 /* Invalid function */ + +/* + * Error codes for the ERRHRD class + */ +#define ERRnowrite 19 /* write protected media */ +#define ERRbadunit 20 /* Unknown unit */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data error (CRC) */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 /* Seek error */ +#define ERRbadmedia 26 /* Unknown media type */ +#define ERRbadsector 27 /* Sector not found */ +#define ERRnopaper 28 /* Printer out of paper */ +#define ERRwrite 29 /* Write fault */ +#define ERRread 30 /* Read fault */ +#define ERRgeneral 31 /* General failure */ +#define ERRbadshare 32 /* A open conflicts with an existing open */ +#define ERRlock 33 /* lock/unlock conflict */ +#define ERRwrongdisk 34 /* The wrong disk was found in a drive */ +#define ERRFCBunavail 35 /* No FCBs available */ +#define ERRsharebufexc 36 /* A sharing buffer has been exceeded */ + +/* + * RAP error codes (it seems that they returned not only by RAP) + */ +#define SMB_ERROR_ACCESS_DENIED 5 +#define SMB_ERROR_NETWORK_ACCESS_DENIED 65 +#define SMB_ERROR_MORE_DATA 234 + +typedef u_int16_t smbfh; + +#endif /* _NETSMB_SMB_H_ */ diff --git a/sys/netsmb/smb_conn.c b/sys/netsmb/smb_conn.c new file mode 100644 index 0000000..54f5c0b --- /dev/null +++ b/sys/netsmb/smb_conn.c @@ -0,0 +1,874 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * Connection engine. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/sysctl.h> +#include <sys/socketvar.h> + +#include <sys/iconv.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_trantcp.h> + +static struct smb_connobj smb_vclist; +static int smb_vcnext = 1; /* next unique id for VC */ + +extern struct linker_set sysctl_net_smb; + +SYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol"); + +MALLOC_DEFINE(M_SMBCONN, "SMB conn", "SMB connection"); + +static void smb_co_init(struct smb_connobj *cp, int level, char *objname, + struct proc *p); +static void smb_co_done(struct smb_connobj *cp); +static int smb_co_lockstatus(struct smb_connobj *cp, struct proc *p); + +static int smb_vc_disconnect(struct smb_vc *vcp); +static void smb_vc_free(struct smb_connobj *cp); +static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred); +static smb_co_free_t smb_share_free; +static smb_co_gone_t smb_share_gone; + +static int smb_sysctl_treedump(SYSCTL_HANDLER_ARGS); + +SYSCTL_PROC(_net_smb, OID_AUTO, treedump, CTLFLAG_RD | CTLTYPE_OPAQUE, + NULL, 0, smb_sysctl_treedump, "S,treedump", "Requester tree"); + +int +smb_sm_init(void) +{ + + smb_co_init(&smb_vclist, SMBL_SM, "smbsm", curproc); + smb_co_unlock(&smb_vclist, 0, curproc); + return 0; +} + +int +smb_sm_done(void) +{ + + /* XXX: hold the mutex */ + if (smb_vclist.co_usecount > 1) { + SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1); + return EBUSY; + } + smb_co_done(&smb_vclist); + return 0; +} + +static int +smb_sm_lockvclist(int flags, struct proc *p) +{ + + return smb_co_lock(&smb_vclist, flags | LK_CANRECURSE, p); +} + +static void +smb_sm_unlockvclist(struct proc *p) +{ + + smb_co_unlock(&smb_vclist, LK_RELEASE, p); +} + +static int +smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + struct proc *p = scred->scr_p; + struct smb_vc *vcp; + int exact = 1; + int error; + + vcspec->shspec = shspec; + error = ENOENT; + SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) { + error = smb_vc_lock(vcp, LK_EXCLUSIVE, p); + if (error) + continue; + itry { + if ((vcp->obj.co_flags & SMBV_PRIVATE) || + !CONNADDREQ(vcp->vc_paddr, vcspec->sap) || + strcmp(vcp->vc_username, vcspec->username) != 0) + ithrow(1); + if (vcspec->owner != SMBM_ANY_OWNER) { + if (vcp->vc_uid != vcspec->owner) + ithrow(1); + } else + exact = 0; + if (vcspec->group != SMBM_ANY_GROUP) { + if (vcp->vc_grp != vcspec->group) + ithrow(1); + } else + exact = 0; + + if (vcspec->mode & SMBM_EXACT) { + if (!exact || + (vcspec->mode & SMBM_MASK) != vcp->vc_mode) + ithrow(1); + } + if (smb_vc_access(vcp, scred, vcspec->mode) != 0) + ithrow(1); + vcspec->ssp = NULL; + if (shspec) + ithrow(smb_vc_lookupshare(vcp, shspec, scred, &vcspec->ssp)); + error = 0; + break; + } icatch(error) { + smb_vc_unlock(vcp, 0, p); + } ifinally { + } iendtry; + if (error == 0) + break; + } + if (vcp) { + smb_vc_ref(vcp, p); + *vcpp = vcp; + } + return error; +} + +int +smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + struct proc *p = scred->scr_p; + struct smb_vc *vcp; + struct smb_share *ssp = NULL; + int error; + + *vcpp = vcp = NULL; + + error = smb_sm_lockvclist(LK_EXCLUSIVE, p); + if (error) + return error; + error = smb_sm_lookupint(vcspec, shspec, scred, vcpp); + if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) { + smb_sm_unlockvclist(p); + return error; + } + error = smb_sm_lookupint(vcspec, NULL, scred, &vcp); + if (error) { + error = smb_vc_create(vcspec, scred, &vcp); + if (error) + goto out; + error = smb_vc_connect(vcp, scred); + if (error) + goto out; + } + if (shspec == NULL) + goto out; + error = smb_share_create(vcp, shspec, scred, &ssp); + if (error) + goto out; + error = smb_smb_treeconnect(ssp, scred); + if (error == 0) + vcspec->ssp = ssp; + else + smb_share_put(ssp, scred); +out: + smb_sm_unlockvclist(p); + if (error == 0) + *vcpp = vcp; + else if (vcp) + smb_vc_put(vcp, scred); + return error; +} + +/* + * Common code for connection object + */ +static void +smb_co_init(struct smb_connobj *cp, int level, char *objname, struct proc *p) +{ + SLIST_INIT(&cp->co_children); + smb_sl_init(&cp->co_interlock, objname); + lockinit(&cp->co_lock, PZERO, objname, 0, 0); + cp->co_level = level; + cp->co_usecount = 1; + KASSERT(smb_co_lock(cp, LK_EXCLUSIVE, p) == 0, ("smb_co_init: lock failed")); +} + +static void +smb_co_done(struct smb_connobj *cp) +{ + smb_sl_destroy(&cp->co_interlock); + lockdestroy(&cp->co_lock); +} + +static void +smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct smb_connobj *parent; + + if (cp->co_gone) + cp->co_gone(cp, scred); + parent = cp->co_parent; + if (parent) { + smb_co_lock(parent, LK_EXCLUSIVE, scred->scr_p); + SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next); + smb_co_put(parent, scred); + } + if (cp->co_free) + cp->co_free(cp); +} + +void +smb_co_ref(struct smb_connobj *cp, struct proc *p) +{ + + SMB_CO_LOCK(cp); + cp->co_usecount++; + SMB_CO_UNLOCK(cp); +} + +void +smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct proc *p = scred->scr_p; + + SMB_CO_LOCK(cp); + if (cp->co_usecount > 1) { + cp->co_usecount--; + SMB_CO_UNLOCK(cp); + return; + } + if (cp->co_usecount == 0) { + SMBERROR("negative use_count for object %d", cp->co_level); + SMB_CO_UNLOCK(cp); + return; + } + cp->co_usecount--; + cp->co_flags |= SMBO_GONE; + + lockmgr(&cp->co_lock, LK_DRAIN | LK_INTERLOCK, &cp->co_interlock, p); + smb_co_gone(cp, scred); +} + +int +smb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred) +{ + int error; + + if ((flags & LK_INTERLOCK) == 0) + SMB_CO_LOCK(cp); + cp->co_usecount++; + error = smb_co_lock(cp, flags | LK_INTERLOCK, scred->scr_p); + if (error) { + SMB_CO_LOCK(cp); + cp->co_usecount--; + SMB_CO_UNLOCK(cp); + return error; + } + return 0; +} + +void +smb_co_put(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct proc *p = scred->scr_p; + int flags; + + flags = LK_RELEASE; + SMB_CO_LOCK(cp); + if (cp->co_usecount > 1) { + cp->co_usecount--; + } else if (cp->co_usecount == 1) { + cp->co_usecount--; + cp->co_flags |= SMBO_GONE; + flags = LK_DRAIN; + } else { + SMBERROR("negative usecount"); + } + lockmgr(&cp->co_lock, LK_RELEASE | LK_INTERLOCK, &cp->co_interlock, p); + if ((cp->co_flags & SMBO_GONE) == 0) + return; + lockmgr(&cp->co_lock, LK_DRAIN, NULL, p); + smb_co_gone(cp, scred); +} + +int +smb_co_lockstatus(struct smb_connobj *cp, struct proc *p) +{ + return lockstatus(&cp->co_lock, p); +} + +int +smb_co_lock(struct smb_connobj *cp, int flags, struct proc *p) +{ + + if (cp->co_flags & SMBO_GONE) + return EINVAL; + if ((flags & LK_TYPE_MASK) == 0) + flags |= LK_EXCLUSIVE; + if (smb_co_lockstatus(cp, p) == LK_EXCLUSIVE && + (flags & LK_CANRECURSE) == 0) { + SMBERROR("recursive lock for object %d\n", cp->co_level); + return 0; + } + return lockmgr(&cp->co_lock, flags, &cp->co_interlock, p); +} + +void +smb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p) +{ + (void)lockmgr(&cp->co_lock, flags | LK_RELEASE, &cp->co_interlock, p); +} + +static void +smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child) +{ + KASSERT(smb_co_lockstatus(parent, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: parent not locked")); + KASSERT(smb_co_lockstatus(child, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: child not locked")); + + smb_co_ref(parent, curproc); + SLIST_INSERT_HEAD(&parent->co_children, child, co_next); + child->co_parent = parent; +} + +/* + * Session implementation + */ + +int +smb_vc_create(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + struct smb_vc *vcp; + struct proc *p = scred->scr_p; + struct ucred *cred = scred->scr_cred; + uid_t uid = vcspec->owner; + gid_t gid = vcspec->group; + uid_t realuid = cred->cr_uid; + char *domain = vcspec->domain; + int error, isroot; + + isroot = smb_suser(cred) == 0; + /* + * Only superuser can create VCs with different uid and gid + */ + if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) + return EPERM; + if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) + return EPERM; + + vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK); + smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc", p); + vcp->obj.co_free = smb_vc_free; + vcp->obj.co_gone = smb_vc_gone; + vcp->vc_number = smb_vcnext++; + vcp->vc_timo = SMB_DEFRQTIMO; + vcp->vc_smbuid = SMB_UID_UNKNOWN; + vcp->vc_mode = vcspec->rights & SMBM_MASK; + vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE); + vcp->vc_tdesc = &smb_tran_nbtcp_desc; + + if (uid == SMBM_ANY_OWNER) + uid = realuid; + if (gid == SMBM_ANY_GROUP) + gid = cred->cr_groups[0]; + vcp->vc_uid = uid; + vcp->vc_grp = gid; + + smb_sl_init(&vcp->vc_stlock, "vcstlock"); + error = 0; + itry { + vcp->vc_paddr = dup_sockaddr(vcspec->sap, 1); + ierror(vcp->vc_paddr == NULL, ENOMEM); + + vcp->vc_laddr = dup_sockaddr(vcspec->lap, 1); + ierror(vcp->vc_laddr == NULL, ENOMEM); + + ierror((vcp->vc_pass = smb_strdup(vcspec->pass)) == NULL, ENOMEM); + + vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : "NODOMAIN"); + ierror(vcp->vc_domain == NULL, ENOMEM); + + ierror((vcp->vc_srvname = smb_strdup(vcspec->srvname)) == NULL, ENOMEM); + ierror((vcp->vc_username = smb_strdup(vcspec->username)) == NULL, ENOMEM); + + ithrow(iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower)); + ithrow(iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper)); + if (vcspec->servercs[0]) { + ithrow(iconv_open(vcspec->servercs, vcspec->localcs, + &vcp->vc_toserver)); + ithrow(iconv_open(vcspec->localcs, vcspec->servercs, + &vcp->vc_tolocal)); + } + + ithrow(smb_iod_create(vcp)); + *vcpp = vcp; + smb_co_addchild(&smb_vclist, VCTOCP(vcp)); + } icatch(error) { + smb_vc_put(vcp, scred); + } ifinally { + } iendtry; + return error; +} + +static void +smb_vc_free(struct smb_connobj *cp) +{ + struct smb_vc *vcp = CPTOVC(cp); + + if (vcp->vc_iod) + smb_iod_destroy(vcp->vc_iod); + SMB_STRFREE(vcp->vc_username); + SMB_STRFREE(vcp->vc_srvname); + SMB_STRFREE(vcp->vc_pass); + SMB_STRFREE(vcp->vc_domain); + if (vcp->vc_paddr) + free(vcp->vc_paddr, M_SONAME); + if (vcp->vc_laddr) + free(vcp->vc_laddr, M_SONAME); + if (vcp->vc_tolower) + iconv_close(vcp->vc_tolower); + if (vcp->vc_toupper) + iconv_close(vcp->vc_toupper); + if (vcp->vc_tolocal) + iconv_close(vcp->vc_tolocal); + if (vcp->vc_toserver) + iconv_close(vcp->vc_toserver); + smb_co_done(VCTOCP(vcp)); + smb_sl_destroy(&vcp->vc_stlock); + free(vcp, M_SMBCONN); +} + +/* + * Called when use count of VC dropped to zero. + * VC should be locked on enter with LK_DRAIN. + */ +static void +smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct smb_vc *vcp = CPTOVC(cp); + + smb_vc_disconnect(vcp); +} + +void +smb_vc_ref(struct smb_vc *vcp, struct proc *p) +{ + smb_co_ref(VCTOCP(vcp), p); +} + +void +smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_co_rele(VCTOCP(vcp), scred); +} + +int +smb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred) +{ + return smb_co_get(VCTOCP(vcp), flags, scred); +} + +void +smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_co_put(VCTOCP(vcp), scred); +} + +int +smb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p) +{ + return smb_co_lock(VCTOCP(vcp), flags, p); +} + +void +smb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p) +{ + smb_co_unlock(VCTOCP(vcp), flags, p); +} + +int +smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode) +{ + struct ucred *cred = scred->scr_cred; + + if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid) + return 0; + mode >>= 3; + if (!groupmember(vcp->vc_grp, cred)) + mode >>= 3; + return (vcp->vc_mode & mode) == mode ? 0 : EACCES; +} + +static int +smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp) +{ + int exact = 1; + + if (strcmp(ssp->ss_name, dp->name) != 0) + return 1; + if (dp->owner != SMBM_ANY_OWNER) { + if (ssp->ss_uid != dp->owner) + return 1; + } else + exact = 0; + if (dp->group != SMBM_ANY_GROUP) { + if (ssp->ss_grp != dp->group) + return 1; + } else + exact = 0; + + if (dp->mode & SMBM_EXACT) { + if (!exact) + return 1; + return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1; + } + if (smb_share_access(ssp, dp->scred, dp->mode) != 0) + return 1; + return 0; +} + +/* + * Lookup share in the given VC. Share referenced and locked on return. + * VC expected to be locked on entry and will be left locked on exit. + */ +int +smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct proc *p = scred->scr_p; + struct smb_share *ssp = NULL; + int error; + + *sspp = NULL; + dp->scred = scred; + SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) { + error = smb_share_lock(ssp, LK_EXCLUSIVE, p); + if (error) + continue; + if (smb_vc_cmpshare(ssp, dp) == 0) + break; + smb_share_unlock(ssp, 0, p); + } + if (ssp) { + smb_share_ref(ssp, p); + *sspp = ssp; + error = 0; + } else + error = ENOENT; + return error; +} + +int +smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred) +{ + + return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); +} + +/* + * Destroy VC to server, invalidate shares linked with it. + * Transport should be locked on entry. + */ +int +smb_vc_disconnect(struct smb_vc *vcp) +{ + + smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL); + return 0; +} + +static char smb_emptypass[] = ""; + +const char * +smb_vc_getpass(struct smb_vc *vcp) +{ + if (vcp->vc_pass) + return vcp->vc_pass; + return smb_emptypass; +} + +static int +smb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip) +{ + bzero(vip, sizeof(struct smb_vc_info)); + vip->itype = SMB_INFO_VC; + vip->usecount = vcp->obj.co_usecount; + vip->uid = vcp->vc_uid; + vip->gid = vcp->vc_grp; + vip->mode = vcp->vc_mode; + vip->flags = vcp->obj.co_flags; + vip->sopt = vcp->vc_sopt; + vip->iodstate = vcp->vc_iod->iod_state; + bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey)); + snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname); + snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username); + return 0; +} + +u_short +smb_vc_nextmid(struct smb_vc *vcp) +{ + u_short r; + + SMB_CO_LOCK(&vcp->obj); + r = vcp->vc_mid++; + SMB_CO_UNLOCK(&vcp->obj); + return r; +} + +/* + * Share implementation + */ +/* + * Allocate share structure and attach it to the given VC + * Connection expected to be locked on entry. Share will be returned + * in locked state. + */ +int +smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct smb_share *ssp; + struct proc *p = scred->scr_p; + struct ucred *cred = scred->scr_cred; + uid_t realuid = cred->cr_uid; + uid_t uid = shspec->owner; + gid_t gid = shspec->group; + int error, isroot; + + isroot = smb_suser(cred) == 0; + /* + * Only superuser can create shares with different uid and gid + */ + if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) + return EPERM; + if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) + return EPERM; + error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); + if (!error) { + smb_share_put(ssp, scred); + return EEXIST; + } + if (uid == SMBM_ANY_OWNER) + uid = realuid; + if (gid == SMBM_ANY_GROUP) + gid = cred->cr_groups[0]; + ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK); + smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss", p); + ssp->obj.co_free = smb_share_free; + ssp->obj.co_gone = smb_share_gone; + smb_sl_init(&ssp->ss_stlock, "ssstlock"); + ssp->ss_name = smb_strdup(shspec->name); + if (shspec->pass && shspec->pass[0]) + ssp->ss_pass = smb_strdup(shspec->pass); + ssp->ss_type = shspec->stype; + ssp->ss_tid = SMB_TID_UNKNOWN; + ssp->ss_uid = uid; + ssp->ss_grp = gid; + ssp->ss_mode = shspec->rights & SMBM_MASK; + smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); + *sspp = ssp; + return 0; +} + +static void +smb_share_free(struct smb_connobj *cp) +{ + struct smb_share *ssp = CPTOSS(cp); + + SMB_STRFREE(ssp->ss_name); + SMB_STRFREE(ssp->ss_pass); + smb_sl_destroy(&ssp->ss_stlock); + smb_co_done(SSTOCP(ssp)); + free(ssp, M_SMBCONN); +} + +static void +smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct smb_share *ssp = CPTOSS(cp); + + smb_smb_treedisconnect(ssp, scred); +} + +void +smb_share_ref(struct smb_share *ssp, struct proc *p) +{ + smb_co_ref(SSTOCP(ssp), p); +} + +void +smb_share_rele(struct smb_share *ssp, struct smb_cred *scred) +{ + smb_co_rele(SSTOCP(ssp), scred); +} + +int +smb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred) +{ + return smb_co_get(SSTOCP(ssp), flags, scred); +} + +void +smb_share_put(struct smb_share *ssp, struct smb_cred *scred) +{ + smb_co_put(SSTOCP(ssp), scred); +} + +int +smb_share_lock(struct smb_share *ssp, int flags, struct proc *p) +{ + return smb_co_lock(SSTOCP(ssp), flags, p); +} + +void +smb_share_unlock(struct smb_share *ssp, int flags, struct proc *p) +{ + smb_co_unlock(SSTOCP(ssp), flags, p); +} + +int +smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode) +{ + struct ucred *cred = scred->scr_cred; + + if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid) + return 0; + mode >>= 3; + if (!groupmember(ssp->ss_grp, cred)) + mode >>= 3; + return (ssp->ss_mode & mode) == mode ? 0 : EACCES; +} + +void +smb_share_invalidate(struct smb_share *ssp) +{ + ssp->ss_tid = SMB_TID_UNKNOWN; +} + +int +smb_share_valid(struct smb_share *ssp) +{ + return ssp->ss_tid != SMB_TID_UNKNOWN && + ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid; +} + +const char* +smb_share_getpass(struct smb_share *ssp) +{ + struct smb_vc *vcp; + + if (ssp->ss_pass) + return ssp->ss_pass; + vcp = SSTOVC(ssp); + if (vcp->vc_pass) + return vcp->vc_pass; + return smb_emptypass; +} + +static int +smb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip) +{ + bzero(sip, sizeof(struct smb_share_info)); + sip->itype = SMB_INFO_SHARE; + sip->usecount = ssp->obj.co_usecount; + sip->tid = ssp->ss_tid; + sip->type= ssp->ss_type; + sip->uid = ssp->ss_uid; + sip->gid = ssp->ss_grp; + sip->mode= ssp->ss_mode; + sip->flags = ssp->obj.co_flags; + snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name); + return 0; +} + +/* + * Dump an entire tree into sysctl call + */ +static int +smb_sysctl_treedump(SYSCTL_HANDLER_ARGS) +{ + struct proc *p = req->p; + struct smb_cred scred; + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_vc_info vci; + struct smb_share_info ssi; + int error, itype; + + smb_makescred(&scred, p, p->p_ucred); + error = smb_sm_lockvclist(LK_SHARED, p); + if (error) + return error; + SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) { + error = smb_vc_lock(vcp, LK_SHARED, p); + if (error) + continue; + smb_vc_getinfo(vcp, &vci); + error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info)); + if (error) { + smb_vc_unlock(vcp, 0, p); + break; + } + SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) { + error = smb_share_lock(ssp, LK_SHARED, p); + if (error) { + error = 0; + continue; + } + smb_share_getinfo(ssp, &ssi); + smb_share_unlock(ssp, 0, p); + error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info)); + if (error) + break; + } + smb_vc_unlock(vcp, 0, p); + if (error) + break; + } + if (!error) { + itype = SMB_INFO_NONE; + error = SYSCTL_OUT(req, &itype, sizeof(itype)); + } + smb_sm_unlockvclist(p); + return error; +} diff --git a/sys/netsmb/smb_conn.h b/sys/netsmb/smb_conn.h new file mode 100644 index 0000000..3467f9a --- /dev/null +++ b/sys/netsmb/smb_conn.h @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _NETINET_IN_H_ +#include <netinet/in.h> +#endif + +/* + * Two levels of connection hierarchy + */ +#define SMBL_SM 0 +#define SMBL_VC 1 +#define SMBL_SHARE 2 +#define SMBL_NUM 3 +#define SMBL_NONE (-1) + +#define SMB_CS_NONE 0x0000 +#define SMB_CS_UPPER 0x0001 /* convert passed string to upper case */ +#define SMB_CS_LOWER 0x0002 /* convert passed string to lower case */ + +/* + * Common object flags + */ +#define SMBO_GONE 0x1000000 + +/* + * access modes + */ +#define SMBM_READ 0400 /* read conn attrs.(like list shares) */ +#define SMBM_WRITE 0200 /* modify conn attrs */ +#define SMBM_EXEC 0100 /* can send SMB requests */ +#define SMBM_READGRP 0040 +#define SMBM_WRITEGRP 0020 +#define SMBM_EXECGRP 0010 +#define SMBM_READOTH 0004 +#define SMBM_WRITEOTH 0002 +#define SMBM_EXECOTH 0001 +#define SMBM_MASK 0777 +#define SMBM_EXACT 010000 /* check for specified mode exactly */ +#define SMBM_ALL (SMBM_READ | SMBM_WRITE | SMBM_EXEC) +#define SMBM_DEFAULT (SMBM_READ | SMBM_WRITE | SMBM_EXEC) +#define SMBM_ANY_OWNER ((uid_t)-1) +#define SMBM_ANY_GROUP ((gid_t)-1) + +/* + * VC flags + */ +#define SMBV_PERMANENT 0x0002 +#define SMBV_LONGNAMES 0x0004 /* connection is configured to use long names */ +#define SMBV_ENCRYPT 0x0008 /* server asked for encrypted password */ +#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */ +#define SMBV_PRIVATE 0x0020 /* connection can be used only by creator */ +#define SMBV_RECONNECTING 0x0040 /* conn is in the process of reconnection */ +#define SMBV_SINGLESHARE 0x0080 /* only one share connectin should be allowed */ +#define SMBV_CREATE 0x0100 /* lookup for create opeartion */ +/*#define SMBV_FAILED 0x0200*/ /* last reconnect attempt has failed */ + + +/* + * smb_share flags + */ +#define SMBS_PERMANENT 0x0001 +#define SMBS_RECONNECTING 0x0002 +#define SMBS_CONNECTED 0x0004 + +/* + * share types + */ +#define SMB_ST_DISK 0x0 /* A: */ +#define SMB_ST_PRINTER 0x1 /* LPT: */ +#define SMB_ST_PIPE 0x2 /* IPC */ +#define SMB_ST_COMM 0x3 /* COMM */ +#define SMB_ST_ANY 0x4 +#define SMB_ST_MAX 0x4 +#define SMB_ST_NONE 0xff /* not a part of protocol */ + +/* + * Negotiated protocol parameters + */ +struct smb_sopt { + int sv_proto; + int16_t sv_tz; /* offset in min relative to UTC */ + u_int32_t sv_maxtx; /* maximum transmit buf size */ + u_char sv_sm; /* security mode */ + u_int16_t sv_maxmux; /* max number of outstanding rq's */ + u_int16_t sv_maxvcs; /* max number of VCs */ + u_int16_t sv_rawmode; + u_int32_t sv_maxraw; /* maximum raw-buffer size */ + u_int32_t sv_skey; /* session key */ + u_int32_t sv_caps; /* capabilites SMB_CAP_ */ +}; + +/* + * network IO daemon states + */ +enum smbiod_state { + SMBIOD_ST_NOTCONN, /* no connect request was made */ + SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */ + SMBIOD_ST_TRANACTIVE, /* transport level is up */ + SMBIOD_ST_VCACTIVE, /* session established */ + SMBIOD_ST_DEAD /* connection broken, transport is down */ +}; + + +/* + * Info structures + */ +#define SMB_INFO_NONE 0 +#define SMB_INFO_VC 2 +#define SMB_INFO_SHARE 3 + +struct smb_vc_info { + int itype; + int usecount; + uid_t uid; /* user id of connection */ + gid_t gid; /* group of connection */ + mode_t mode; /* access mode */ + int flags; + enum smbiod_state iodstate; + struct smb_sopt sopt; + char srvname[SMB_MAXSRVNAMELEN]; + char vcname[128]; +}; + +struct smb_share_info { + int itype; + int usecount; + u_short tid; /* TID */ + int type; /* share type */ + uid_t uid; /* user id of connection */ + gid_t gid; /* group of connection */ + mode_t mode; /* access mode */ + int flags; + char sname[128]; +}; + +#ifdef _KERNEL + +#include <sys/lock.h> +#include <netsmb/smb_subr.h> + +#define CONNADDREQ(a1,a2) ((a1)->sa_len == (a2)->sa_len && \ + bcmp(a1, a2, (a1)->sa_len) == 0) + +struct smb_vc; +struct smb_share; +struct smb_cred; +struct smb_rq; +struct mbdata; +struct smbioc_oshare; +struct smbioc_ossn; +struct uio; + +TAILQ_HEAD(smb_rqhead, smb_rq); + +#define SMB_DEFRQTIMO 5 + +#define SMB_DIALECT(vcp) ((vcp)->vc_sopt.sv_proto) + +struct smb_tran_desc; + +/* + * Connection object + */ +struct smb_connobj; + +typedef void smb_co_gone_t (struct smb_connobj *cp, struct smb_cred *scred); +typedef void smb_co_free_t (struct smb_connobj *cp); + +#define SMB_CO_LOCK(cp) smb_sl_lock(&(cp)->co_interlock) +#define SMB_CO_UNLOCK(cp) smb_sl_unlock(&(cp)->co_interlock) + +struct smb_connobj { + int co_level; /* SMBL_ */ + int co_flags; + struct lock co_lock; + struct smb_slock co_interlock; + int co_usecount; + struct smb_connobj * co_parent; + SLIST_HEAD(,smb_connobj)co_children; + SLIST_ENTRY(smb_connobj)co_next; + smb_co_gone_t * co_gone; + smb_co_free_t * co_free; +}; + +#define SMBCO_FOREACH(var, cp) SLIST_FOREACH((var), &(cp)->co_children, co_next) + +/* + * Virtual Circuit (session) to a server. + * This is the most (over)complicated part of SMB protocol. + * For the user security level (usl), each session with different remote + * user name has its own VC. + * It is unclear however, should share security level (ssl) allow additional + * VCs, because user name is not used and can be the same. On other hand, + * multiple VCs allows us to create separate sessions to server on a per + * user basis. + */ + +/* + * This lock protects vc_flags + */ +#define SMBC_ST_LOCK(vcp) smb_sl_lock(&(vcp)->vc_stlock) +#define SMBC_ST_UNLOCK(vcp) smb_sl_unlock(&(vcp)->vc_stlock) + + +struct smb_vc { + struct smb_connobj obj; + char * vc_srvname; + struct sockaddr*vc_paddr; /* server addr */ + struct sockaddr*vc_laddr; /* local addr, if any */ + char * vc_username; + char * vc_pass; /* password for usl case */ + char * vc_domain; /* workgroup/primary domain */ + + u_int vc_timo; /* default request timeout */ + int vc_maxvcs; /* maximum number of VC per connection */ + + void * vc_tolower; /* local charset */ + void * vc_toupper; /* local charset */ + void * vc_toserver; /* local charset to server one */ + void * vc_tolocal; /* server charset to local one */ + int vc_number; /* number of this VC from the client side */ + int vc_genid; + uid_t vc_uid; /* user id of connection */ + gid_t vc_grp; /* group of connection */ + mode_t vc_mode; /* access mode */ + struct tnode * vc_tnode; /* backing object */ + u_short vc_smbuid; /* unique vc id assigned by server */ + + u_char vc_hflags; /* or'ed with flags in the smb header */ + u_short vc_hflags2; /* or'ed with flags in the smb header */ + void * vc_tdata; /* transport control block */ + struct smb_tran_desc *vc_tdesc; + int vc_chlen; /* actual challenge length */ + u_char vc_ch[SMB_MAXCHALLENGELEN]; + u_short vc_mid; /* multiplex id */ + struct smb_sopt vc_sopt; /* server options */ + struct smb_cred*vc_scred; /* used in reconnect procedure */ + int vc_txmax; /* max tx/rx packet size */ + struct smbiod * vc_iod; + struct smb_slock vc_stlock; +}; + +#define vc_maxmux vc_sopt.sv_maxmux +#define vc_flags obj.co_flags + + +/* + * smb_share structure describes connection to the given SMB share (tree). + * Connection to share is always built on top of the VC. + */ + +/* + * This lock protects ss_flags + */ +#define SMBS_ST_LOCK(ssp) smb_sl_lock(&(ssp)->ss_stlock) +#define SMBS_ST_LOCKPTR(ssp) (&(ssp)->ss_stlock) +#define SMBS_ST_UNLOCK(ssp) smb_sl_unlock(&(ssp)->ss_stlock) + +struct smb_share { + struct smb_connobj obj; + char * ss_name; + u_short ss_tid; /* TID */ + int ss_type; /* share type */ + uid_t ss_uid; /* user id of connection */ + gid_t ss_grp; /* group of connection */ + mode_t ss_mode; /* access mode */ + int ss_vcgenid; + char * ss_pass; /* password to a share, can be null */ + struct smb_slock ss_stlock; + struct smb_cred *ss_cred; /* used in reconnect procedure */ +}; + +#define ss_flags obj.co_flags + +#define CPTOVC(cp) ((struct smb_vc*)(cp)) +#define VCTOCP(vcp) (&(vcp)->obj) +#define CPTOSS(cp) ((struct smb_share*)(cp)) +#define SSTOVC(ssp) CPTOVC(((ssp)->obj.co_parent)) +#define SSTOCP(ssp) (&(ssp)->obj) + +struct smb_vcspec { + char * srvname; + struct sockaddr*sap; + struct sockaddr*lap; + int flags; + char * username; + char * pass; + char * domain; + mode_t mode; + mode_t rights; + uid_t owner; + gid_t group; + char * localcs; + char * servercs; + struct smb_sharespec *shspec; + struct smb_share *ssp; /* returned */ + /* + * The rest is an internal data + */ + struct smb_cred *scred; +}; + +struct smb_sharespec { + char * name; + char * pass; + mode_t mode; + mode_t rights; + uid_t owner; + gid_t group; + int stype; + /* + * The rest is an internal data + */ + struct smb_cred *scred; +}; + +/* + * Session level functions + */ +int smb_sm_init(void); +int smb_sm_done(void); +int smb_sm_lookup(struct smb_vcspec *vcspec, + struct smb_sharespec *shspec, struct smb_cred *scred, + struct smb_vc **vcpp); + +/* + * Connection object + */ +void smb_co_ref(struct smb_connobj *cp, struct proc *p); +void smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred); +int smb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred); +void smb_co_put(struct smb_connobj *cp, struct smb_cred *scred); +int smb_co_lock(struct smb_connobj *cp, int flags, struct proc *p); +void smb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p); + +/* + * session level functions + */ +int smb_vc_create(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp); +int smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred); +int smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode); +int smb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred); +void smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred); +void smb_vc_ref(struct smb_vc *vcp, struct proc *p); +void smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred); +int smb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p); +void smb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p); +int smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp); +const char * smb_vc_getpass(struct smb_vc *vcp); +u_short smb_vc_nextmid(struct smb_vc *vcp); + +/* + * share level functions + */ +int smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp); +int smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode); +void smb_share_ref(struct smb_share *ssp, struct proc *p); +void smb_share_rele(struct smb_share *ssp, struct smb_cred *scred); +int smb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred); +void smb_share_put(struct smb_share *ssp, struct smb_cred *scred); +int smb_share_lock(struct smb_share *ssp, int flags, struct proc *p); +void smb_share_unlock(struct smb_share *ssp, int flags, struct proc *p); +void smb_share_invalidate(struct smb_share *ssp); +int smb_share_valid(struct smb_share *ssp); +const char * smb_share_getpass(struct smb_share *ssp); + +/* + * SMB protocol level functions + */ +int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred); +int smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred); +int smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred); +int smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred); +int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred); + +/* + * smbiod thread + */ + +#define SMBIOD_EV_NEWRQ 0x0001 +#define SMBIOD_EV_SHUTDOWN 0x0002 +#define SMBIOD_EV_CONNECT 0x0003 +#define SMBIOD_EV_DISCONNECT 0x0004 +#define SMBIOD_EV_TREECONNECT 0x0005 +#define SMBIOD_EV_MASK 0x00ff +#define SMBIOD_EV_SYNC 0x0100 +#define SMBIOD_EV_PROCESSING 0x0200 + +struct smbiod_event { + int ev_type; + int ev_error; + void * ev_ident; + STAILQ_ENTRY(smbiod_event) ev_link; +}; + +#define SMBIOD_SHUTDOWN 0x0001 + +struct smbiod { + int iod_id; + int iod_flags; + enum smbiod_state iod_state; + int iod_muxcnt; /* number of active outstanding requests */ + int iod_sleeptimo; + struct smb_vc * iod_vc; + struct smb_slock iod_rqlock; /* iod_rqlist, iod_muxwant */ + struct smb_rqhead iod_rqlist; /* list of outstanding requests */ + int iod_muxwant; + struct proc * iod_p; + struct smb_cred iod_scred; + struct smb_slock iod_evlock; /* iod_evlist */ + STAILQ_HEAD(,smbiod_event) iod_evlist; + struct timespec iod_lastrqsent; + struct timespec iod_pingtimo; +}; + +int smb_iod_init(void); +int smb_iod_done(void); +int smb_iod_create(struct smb_vc *vcp); +int smb_iod_destroy(struct smbiod *iod); +int smb_iod_request(struct smbiod *iod, int event, void *ident); +int smb_iod_addrq(struct smb_rq *rqp); +int smb_iod_waitrq(struct smb_rq *rqp); +int smb_iod_removerq(struct smb_rq *rqp); + +#endif /* _KERNEL */ diff --git a/sys/netsmb/smb_crypt.c b/sys/netsmb/smb_crypt.c new file mode 100644 index 0000000..e72a385 --- /dev/null +++ b/sys/netsmb/smb_crypt.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <sys/md4.h> +#include <sys/iconv.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> + +#include "opt_netsmb.h" + +#ifdef NETSMBCRYPTO + +#include <crypto/des/des.h> + +static u_char N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + + +static void +smb_E(const u_char *key, u_char *data, u_char *dest) +{ + des_key_schedule *ksp; + u_char kk[8]; + + kk[0] = key[0] & 0xfe; + kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe); + kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe); + kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe); + kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe); + kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe); + kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe); + kk[7] = key[6] << 1; + ksp = malloc(sizeof(des_key_schedule), M_SMBTEMP, M_WAITOK); + des_set_key((C_Block*)kk, *ksp); + des_ecb_encrypt((C_Block*)data, (C_Block*)dest, *ksp, 1); + free(ksp, M_SMBTEMP); +} +#endif + + +int +smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN) +{ +#ifdef NETSMBCRYPTO + u_char *p, *P14, *S21; + + p = malloc(14 + 21, M_SMBTEMP, M_WAITOK); + bzero(p, 14 + 21); + P14 = p; + S21 = p + 14; + bcopy(apwd, P14, min(14, strlen(apwd))); + /* + * S21 = concat(Ex(P14, N8), zeros(5)); + */ + smb_E(P14, N8, S21); + smb_E(P14 + 7, N8, S21 + 8); + + smb_E(S21, C8, RN); + smb_E(S21 + 7, C8, RN + 8); + smb_E(S21 + 14, C8, RN + 16); + free(p, M_SMBTEMP); + return 0; +#else + SMBERROR("password encryption is not available\n"); + bzero(RN, 24); + return EAUTH; +#endif +} + +int +smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN) +{ +#ifdef NETSMBCRYPTO + u_char S21[21]; + u_int16_t *unipwd; + MD4_CTX *ctxp; + int len; + + len = strlen(apwd); + unipwd = malloc(len * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK); + /* + * S21 = concat(MD4(U(apwd)), zeros(5)); + */ + smb_strtouni(unipwd, apwd); + ctxp = malloc(sizeof(MD4_CTX), M_SMBTEMP, M_WAITOK); + MD4Init(ctxp); + MD4Update(ctxp, (u_char*)unipwd, len * sizeof(u_int16_t)); + free(unipwd, M_SMBTEMP); + bzero(S21, 21); + MD4Final(S21, ctxp); + free(ctxp, M_SMBTEMP); + + smb_E(S21, C8, RN); + smb_E(S21 + 7, C8, RN + 8); + smb_E(S21 + 14, C8, RN + 16); + return 0; +#else + SMBERROR("password encryption is not available\n"); + bzero(RN, 24); + return EAUTH; +#endif +} + diff --git a/sys/netsmb/smb_dev.c b/sys/netsmb/smb_dev.c new file mode 100644 index 0000000..fe00717 --- /dev/null +++ b/sys/netsmb/smb_dev.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/ioccom.h> +#include <sys/malloc.h> +#include <sys/uio.h> +#include <sys/conf.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/poll.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> + +#include <net/if.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> + +#define SMB_GETDEV(dev) ((struct smb_dev*)(dev)->si_drv1) +#define SMB_CHECKMINOR(dev) do { \ + sdp = SMB_GETDEV(dev); \ + if (sdp == NULL) return ENXIO; \ + } while(0) + +static d_open_t nsmb_dev_open; +static d_close_t nsmb_dev_close; +static d_read_t nsmb_dev_read; +static d_write_t nsmb_dev_write; +static d_ioctl_t nsmb_dev_ioctl; +static d_poll_t nsmb_dev_poll; + +MODULE_DEPEND(netsmb, libiconv, 1, 1, 1); +MODULE_VERSION(netsmb, NSMB_VERSION); + +static int smb_version = NSMB_VERSION; + + +SYSCTL_DECL(_net_smb); +SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, ""); + +static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device"); + + +/* +int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio); +*/ + +static struct cdevsw nsmb_cdevsw = { + /* open */ nsmb_dev_open, + /* close */ nsmb_dev_close, + /* read */ nsmb_dev_read, + /* write */ nsmb_dev_write, + /* ioctl */ nsmb_dev_ioctl, + /* poll */ nsmb_dev_poll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ NSMB_NAME, + /* maj */ NSMB_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +#ifndef FB_CURRENT + /* bmaj */ -1 +#endif +}; + +static eventhandler_tag nsmb_dev_tag; + +static void +nsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev) +{ + int min; + + if (*dev != NODEV) + return; + if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1) + return; + *dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min); +} + +static int +nsmb_dev_open(dev_t dev, int oflags, int devtype, struct proc *p) +{ + struct smb_dev *sdp; + struct ucred *cred = p->p_ucred; + int s; + + sdp = SMB_GETDEV(dev); + if (sdp && (sdp->sd_flags & NSMBFL_OPEN)) + return EBUSY; + if (sdp == NULL) { + sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK); + dev->si_drv1 = (void*)sdp; + } + /* + * XXX: this is just crazy - make a device for an already passed device... + * someone should take care of it. + */ + if ((dev->si_flags & SI_NAMED) == 0) + make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700, + NSMB_NAME"%d", dev2unit(dev)); + bzero(sdp, sizeof(*sdp)); +/* + STAILQ_INIT(&sdp->sd_rqlist); + STAILQ_INIT(&sdp->sd_rplist); + bzero(&sdp->sd_pollinfo, sizeof(struct selinfo)); +*/ + s = splimp(); + sdp->sd_level = -1; + sdp->sd_flags |= NSMBFL_OPEN; + splx(s); + return 0; +} + +static int +nsmb_dev_close(dev_t dev, int flag, int fmt, struct proc *p) +{ + struct smb_dev *sdp; + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_cred scred; + int s; + + SMB_CHECKMINOR(dev); + s = splimp(); + if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { + splx(s); + return EBADF; + } + smb_makescred(&scred, p, NULL); + ssp = sdp->sd_share; + if (ssp != NULL) + smb_share_rele(ssp, &scred); + vcp = sdp->sd_vc; + if (vcp != NULL) + smb_vc_rele(vcp, &scred); +/* + smb_flushq(&sdp->sd_rqlist); + smb_flushq(&sdp->sd_rplist); +*/ +#if __FreeBSD_version > 400001 + dev->si_drv1 = NULL; + free(sdp, M_NSMBDEV); + destroy_dev(dev); +#else + sdp->sd_flags &= ~NSMBFL_OPEN; +#endif + splx(s); + return 0; +} + + +static int +nsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct smb_dev *sdp; + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_cred scred; + int error = 0; + + SMB_CHECKMINOR(dev); + if ((sdp->sd_flags & NSMBFL_OPEN) == 0) + return EBADF; + + smb_makescred(&scred, p, NULL); + switch (cmd) { + case SMBIOC_OPENSESSION: + if (sdp->sd_vc) + return EISCONN; + error = smb_usr_opensession((struct smbioc_ossn*)data, + &scred, &vcp); + if (error) + break; + sdp->sd_vc = vcp; + smb_vc_unlock(vcp, 0, p); + sdp->sd_level = SMBL_VC; + break; + case SMBIOC_OPENSHARE: + if (sdp->sd_share) + return EISCONN; + if (sdp->sd_vc == NULL) + return ENOTCONN; + error = smb_usr_openshare(sdp->sd_vc, + (struct smbioc_oshare*)data, &scred, &ssp); + if (error) + break; + sdp->sd_share = ssp; + smb_share_unlock(ssp, 0, p); + sdp->sd_level = SMBL_SHARE; + break; + case SMBIOC_REQUEST: + if (sdp->sd_share == NULL) + return ENOTCONN; + error = smb_usr_simplerequest(sdp->sd_share, + (struct smbioc_rq*)data, &scred); + break; + case SMBIOC_T2RQ: + if (sdp->sd_share == NULL) + return ENOTCONN; + error = smb_usr_t2request(sdp->sd_share, + (struct smbioc_t2rq*)data, &scred); + break; + case SMBIOC_SETFLAGS: { + struct smbioc_flags *fl = (struct smbioc_flags*)data; + int on; + + if (fl->ioc_level == SMBL_VC) { + if (fl->ioc_mask & SMBV_PERMANENT) { + on = fl->ioc_flags & SMBV_PERMANENT; + if ((vcp = sdp->sd_vc) == NULL) + return ENOTCONN; + error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred); + if (error) + break; + if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) { + vcp->obj.co_flags |= SMBV_PERMANENT; + smb_vc_ref(vcp, p); + } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) { + vcp->obj.co_flags &= ~SMBV_PERMANENT; + smb_vc_rele(vcp, &scred); + } + smb_vc_put(vcp, &scred); + } else + error = EINVAL; + } else if (fl->ioc_level == SMBL_SHARE) { + if (fl->ioc_mask & SMBS_PERMANENT) { + on = fl->ioc_flags & SMBS_PERMANENT; + if ((ssp = sdp->sd_share) == NULL) + return ENOTCONN; + error = smb_share_get(ssp, LK_EXCLUSIVE, &scred); + if (error) + break; + if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) { + ssp->obj.co_flags |= SMBS_PERMANENT; + smb_share_ref(ssp, p); + } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) { + ssp->obj.co_flags &= ~SMBS_PERMANENT; + smb_share_rele(ssp, &scred); + } + smb_share_put(ssp, &scred); + } else + error = EINVAL; + break; + } else + error = EINVAL; + break; + } + case SMBIOC_LOOKUP: + if (sdp->sd_vc || sdp->sd_share) + return EISCONN; + vcp = NULL; + ssp = NULL; + error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp); + if (error) + break; + if (vcp) { + sdp->sd_vc = vcp; + smb_vc_unlock(vcp, 0, p); + sdp->sd_level = SMBL_VC; + } + if (ssp) { + sdp->sd_share = ssp; + smb_share_unlock(ssp, 0, p); + sdp->sd_level = SMBL_SHARE; + } + break; + case SMBIOC_READ: case SMBIOC_WRITE: { + struct smbioc_rw *rwrq = (struct smbioc_rw*)data; + struct uio auio; + struct iovec iov; + + if ((ssp = sdp->sd_share) == NULL) + return ENOTCONN; + iov.iov_base = rwrq->ioc_base; + iov.iov_len = rwrq->ioc_cnt; + auio.uio_iov = &iov; + auio.uio_iovcnt = 1; + auio.uio_offset = rwrq->ioc_offset; + auio.uio_resid = rwrq->ioc_cnt; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE; + auio.uio_procp = p; + if (cmd == SMBIOC_READ) + error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred); + else + error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred); + rwrq->ioc_cnt -= auio.uio_resid; + break; + } + default: + error = ENODEV; + } + return error; +} + +static int +nsmb_dev_read(dev_t dev, struct uio *uio, int flag) +{ + return EACCES; +} + +static int +nsmb_dev_write(dev_t dev, struct uio *uio, int flag) +{ + return EACCES; +} + +static int +nsmb_dev_poll(dev_t dev, int events, struct proc *p) +{ + return ENODEV; +} + +static int +nsmb_dev_load(module_t mod, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + error = smb_sm_init(); + if (error) + break; + error = smb_iod_init(); + if (error) { + smb_sm_done(); + break; + } +#if __FreeBSD_version > 400001 + cdevsw_add(&nsmb_cdevsw); +#endif +#if __FreeBSD_version > 500000 + nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000); +#endif + printf("netsmb_dev: loaded\n"); + break; + case MOD_UNLOAD: + smb_iod_done(); + error = smb_sm_done(); + error = 0; +#if __FreeBSD_version > 500000 + EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag); +#endif +#if __FreeBSD_version > 400001 + cdevsw_remove(&nsmb_cdevsw); +#endif + printf("netsmb_dev: unloaded\n"); + break; + default: + error = EINVAL; + break; + } + return error; +} + +#if __FreeBSD_version > 400000 +DEV_MODULE (dev_netsmb, nsmb_dev_load, 0); +#else +CDEV_MODULE(dev_netsmb, NSMB_MAJOR, nsmb_cdevsw, nsmb_dev_load, 0); +#endif + + +/* + * Convert a file descriptor to appropriate smb_share pointer + */ +static struct file* +nsmb_getfp(struct filedesc* fdp, int fd, int flag) +{ + struct file* fp; + + if (((u_int)fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[fd]) == NULL || + (fp->f_flag & flag) == 0) + return (NULL); + return (fp); +} + +int +smb_dev2share(int fd, int mode, struct smb_cred *scred, + struct smb_share **sspp) +{ + struct file *fp; + struct vnode *vp; + struct smb_dev *sdp; + struct smb_share *ssp; + dev_t dev; + int error; + + if ((fp = nsmb_getfp(scred->scr_p->p_fd, fd, FREAD | FWRITE)) == NULL) + return EBADF; + vp = (struct vnode*)fp->f_data; + if (vp == NULL) + return EBADF; + dev = vn_todev(vp); + if (dev == NODEV) + return EBADF; + SMB_CHECKMINOR(dev); + ssp = sdp->sd_share; + if (ssp == NULL) + return ENOTCONN; + error = smb_share_get(ssp, LK_EXCLUSIVE, scred); + if (error) + return error; + *sspp = ssp; + return 0; +} + diff --git a/sys/netsmb/smb_dev.h b/sys/netsmb/smb_dev.h new file mode 100644 index 0000000..52ab436 --- /dev/null +++ b/sys/netsmb/smb_dev.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _NETSMB_DEV_H_ +#define _NETSMB_DEV_H_ + +#define NSMB_NAME "nsmb" +#define NSMB_MAJOR 144 + +#define NSMB_VERMAJ 1 +#define NSMB_VERMIN 3006 +#define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN) + +#define NSMBFL_OPEN 0x0001 + +#define SMBVOPT_CREATE 0x0001 /* create object if necessary */ +#define SMBVOPT_PRIVATE 0x0002 /* connection should be private */ +#define SMBVOPT_SINGLESHARE 0x0004 /* keep only ane share at this VC */ +#define SMBVOPT_PERMANENT 0x0010 /* object will keep last reference */ + +#define SMBSOPT_CREATE 0x0001 /* create object if necessary */ +#define SMBSOPT_PERMANENT 0x0010 /* object will keep last reference */ + +/* + * SMBIOC_LOOKUP flags + */ +#define SMBLK_CREATE 0x0001 + +struct smbioc_ossn { + int ioc_opt; + int ioc_svlen; /* size of ioc_server address */ + struct sockaddr*ioc_server; + int ioc_lolen; /* size of ioc_local address */ + struct sockaddr*ioc_local; + char ioc_srvname[SMB_MAXSRVNAMELEN + 1]; + int ioc_timeout; + int ioc_retrycount; /* number of retries before giveup */ + char ioc_localcs[16];/* local charset */ + char ioc_servercs[16];/* server charset */ + char ioc_user[SMB_MAXUSERNAMELEN + 1]; + char ioc_workgroup[SMB_MAXUSERNAMELEN + 1]; + char ioc_password[SMB_MAXPASSWORDLEN + 1]; + uid_t ioc_owner; /* proposed owner */ + gid_t ioc_group; /* proposed group */ + mode_t ioc_mode; /* desired access mode */ + mode_t ioc_rights; /* SMBM_* */ +}; + +struct smbioc_oshare { + int ioc_opt; + int ioc_stype; /* share type */ + char ioc_share[SMB_MAXSHARENAMELEN + 1]; + char ioc_password[SMB_MAXPASSWORDLEN + 1]; + uid_t ioc_owner; /* proposed owner of share */ + gid_t ioc_group; /* proposed group of share */ + mode_t ioc_mode; /* desired access mode to share */ + mode_t ioc_rights; /* SMBM_* */ +}; + +struct smbioc_rq { + u_char ioc_cmd; + u_char ioc_twc; + void * ioc_twords; + u_short ioc_tbc; + void * ioc_tbytes; + int ioc_rpbufsz; + char * ioc_rpbuf; + u_char ioc_rwc; + u_short ioc_rbc; + u_int8_t ioc_errclass; + u_int16_t ioc_serror; + u_int32_t ioc_error; +}; + +struct smbioc_t2rq { + u_int16_t ioc_setup[3]; + int ioc_setupcnt; + char * ioc_name; + u_short ioc_tparamcnt; + void * ioc_tparam; + u_short ioc_tdatacnt; + void * ioc_tdata; + u_short ioc_rparamcnt; + void * ioc_rparam; + u_short ioc_rdatacnt; + void * ioc_rdata; +}; + +struct smbioc_flags { + int ioc_level; /* 0 - session, 1 - share */ + int ioc_mask; + int ioc_flags; +}; + +struct smbioc_lookup { + int ioc_level; + int ioc_flags; + struct smbioc_ossn ioc_ssn; + struct smbioc_oshare ioc_sh; +}; + +struct smbioc_rw { + smbfh ioc_fh; + char * ioc_base; + off_t ioc_offset; + int ioc_cnt; +}; + +/* + * Device IOCTLs + */ +#define SMBIOC_OPENSESSION _IOW('n', 100, struct smbioc_ossn) +#define SMBIOC_OPENSHARE _IOW('n', 101, struct smbioc_oshare) +#define SMBIOC_REQUEST _IOWR('n', 102, struct smbioc_rq) +#define SMBIOC_T2RQ _IOWR('n', 103, struct smbioc_t2rq) +#define SMBIOC_SETFLAGS _IOW('n', 104, struct smbioc_flags) +#define SMBIOC_LOOKUP _IOW('n', 106, struct smbioc_lookup) +#define SMBIOC_READ _IOWR('n', 107, struct smbioc_rw) +#define SMBIOC_WRITE _IOWR('n', 108, struct smbioc_rw) + +#ifdef _KERNEL + +#ifndef LK_SHARED +#include <sys/lock.h> +#endif + +#define IF_QEMPTY(ifq) ((ifq)->ifq_len == 0) + +#define SMBST_CONNECTED 1 + +STAILQ_HEAD(smbrqh, smb_rq); + +struct smb_dev { + int sd_opened; + int sd_level; + struct smb_vc * sd_vc; /* reference to VC */ + struct smb_share *sd_share; /* reference to share if any */ + int sd_poll; + int sd_seq; +/* struct ifqueue sd_rdqueue; + struct ifqueue sd_wrqueue; + struct selinfo sd_pollinfo; + struct smbrqh sd_rqlist; + struct smbrqh sd_rplist; + struct ucred *sd_owner;*/ + int sd_flags; +}; + +struct smb_cred; +/* + * Compound user interface + */ +int smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc **vcpp, struct smb_share **sspp); +int smb_usr_opensession(struct smbioc_ossn *data, + struct smb_cred *scred, struct smb_vc **vcpp); +int smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *data, + struct smb_cred *scred, struct smb_share **sspp); +int smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *data, + struct smb_cred *scred); +int smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *data, + struct smb_cred *scred); +int smb_dev2share(int fd, int mode, struct smb_cred *scred, + struct smb_share **sspp); + + +#endif /* _KERNEL */ + +#endif /* _NETSMB_DEV_H_ */ diff --git a/sys/netsmb/smb_iod.c b/sys/netsmb/smb_iod.c new file mode 100644 index 0000000..8053303 --- /dev/null +++ b/sys/netsmb/smb_iod.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/unistd.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_trantcp.h> + + +#define SMBIOD_SLEEP_TIMO 2 +#define SMBIOD_PING_TIMO 60 /* seconds */ + +#define SMB_IOD_EVLOCKPTR(iod) (&((iod)->iod_evlock)) +#define SMB_IOD_EVLOCK(iod) smb_sl_lock(&((iod)->iod_evlock)) +#define SMB_IOD_EVUNLOCK(iod) smb_sl_unlock(&((iod)->iod_evlock)) + +#define SMB_IOD_RQLOCKPTR(iod) (&((iod)->iod_rqlock)) +#define SMB_IOD_RQLOCK(iod) smb_sl_lock(&((iod)->iod_rqlock)) +#define SMB_IOD_RQUNLOCK(iod) smb_sl_unlock(&((iod)->iod_rqlock)) + +#define smb_iod_wakeup(iod) wakeup(&(iod)->iod_flags) + + +static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon"); + +static int smb_iod_next; + +static int smb_iod_sendall(struct smbiod *iod); +static int smb_iod_disconnect(struct smbiod *iod); +static void smb_iod_thread(void *); + +static __inline void +smb_iod_rqprocessed(struct smb_rq *rqp, int error) +{ + SMBRQ_SLOCK(rqp); + rqp->sr_lerror = error; + rqp->sr_rpgen++; + rqp->sr_state = SMBRQ_NOTIFIED; + wakeup(&rqp->sr_state); + SMBRQ_SUNLOCK(rqp); +} + +static void +smb_iod_invrq(struct smbiod *iod) +{ + struct smb_rq *rqp; + + /* + * Invalidate all outstanding requests for this connection + */ + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + if (rqp->sr_flags & SMBR_INTERNAL) + SMBRQ_SUNLOCK(rqp); + rqp->sr_flags |= SMBR_RESTART; + smb_iod_rqprocessed(rqp, ENOTCONN); + } + SMB_IOD_RQUNLOCK(iod); +} + +static void +smb_iod_closetran(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct proc *p = iod->iod_p; + + if (vcp->vc_tdata == NULL) + return; + SMB_TRAN_DISCONNECT(vcp, p); + SMB_TRAN_DONE(vcp, p); + vcp->vc_tdata = NULL; +} + +static void +smb_iod_dead(struct smbiod *iod) +{ + iod->iod_state = SMBIOD_ST_DEAD; + smb_iod_closetran(iod); + smb_iod_invrq(iod); +} + +static int +smb_iod_connect(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct proc *p = iod->iod_p; + int error; + + SMBIODEBUG("%d\n", iod->iod_state); + switch(iod->iod_state) { + case SMBIOD_ST_VCACTIVE: + SMBERROR("called for already opened connection\n"); + return EISCONN; + case SMBIOD_ST_DEAD: + return ENOTCONN; /* XXX: last error code ? */ + default: + break; + } + vcp->vc_genid++; + error = 0; + itry { + ithrow(SMB_TRAN_CREATE(vcp, p)); + SMBIODEBUG("tcreate\n"); + if (vcp->vc_laddr) { + ithrow(SMB_TRAN_BIND(vcp, vcp->vc_laddr, p)); + } + SMBIODEBUG("tbind\n"); + ithrow(SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, p)); + SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags); + iod->iod_state = SMBIOD_ST_TRANACTIVE; + SMBIODEBUG("tconnect\n"); +/* vcp->vc_mid = 0;*/ + ithrow(smb_smb_negotiate(vcp, &iod->iod_scred)); + SMBIODEBUG("snegotiate\n"); + ithrow(smb_smb_ssnsetup(vcp, &iod->iod_scred)); + iod->iod_state = SMBIOD_ST_VCACTIVE; + SMBIODEBUG("completed\n"); + smb_iod_invrq(iod); + } icatch(error) { + smb_iod_dead(iod); + } ifinally { + } iendtry; + return error; +} + +static int +smb_iod_disconnect(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + + SMBIODEBUG("\n"); + if (iod->iod_state == SMBIOD_ST_VCACTIVE) { + smb_smb_ssnclose(vcp, &iod->iod_scred); + iod->iod_state = SMBIOD_ST_TRANACTIVE; + } + vcp->vc_smbuid = SMB_UID_UNKNOWN; + smb_iod_closetran(iod); + iod->iod_state = SMBIOD_ST_NOTCONN; + return 0; +} + +static int +smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp) +{ + int error; + + if (iod->iod_state != SMBIOD_ST_VCACTIVE) { + if (iod->iod_state != SMBIOD_ST_DEAD) + return ENOTCONN; + iod->iod_state = SMBIOD_ST_RECONNECT; + error = smb_iod_connect(iod); + if (error) + return error; + } + SMBIODEBUG("tree reconnect\n"); + SMBS_ST_LOCK(ssp); + ssp->ss_flags |= SMBS_RECONNECTING; + SMBS_ST_UNLOCK(ssp); + error = smb_smb_treeconnect(ssp, &iod->iod_scred); + SMBS_ST_LOCK(ssp); + ssp->ss_flags &= ~SMBS_RECONNECTING; + SMBS_ST_UNLOCK(ssp); + wakeup(&ssp->ss_vcgenid); + return error; +} + +static int +smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp) +{ + struct proc *p = iod->iod_p; + struct smb_vc *vcp = iod->iod_vc; + struct smb_share *ssp = rqp->sr_share; + struct mbuf *m; + int error; + + SMBIODEBUG("iod_state = %d\n", iod->iod_state); + switch (iod->iod_state) { + case SMBIOD_ST_NOTCONN: + smb_iod_rqprocessed(rqp, ENOTCONN); + return 0; + case SMBIOD_ST_DEAD: + iod->iod_state = SMBIOD_ST_RECONNECT; + return 0; + case SMBIOD_ST_RECONNECT: + return 0; + default: + break; + } + if (rqp->sr_sendcnt == 0) { +#ifdef movedtoanotherplace + if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux) + return 0; +#endif + *rqp->sr_rqtid = htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN); + *rqp->sr_rquid = htoles(vcp ? vcp->vc_smbuid : 0); + mb_fixhdr(&rqp->sr_rq); + } + if (rqp->sr_sendcnt++ > 5) { + rqp->sr_flags |= SMBR_RESTART; + smb_iod_rqprocessed(rqp, rqp->sr_lerror); + /* + * If all attempts to send a request failed, then + * something is seriously hosed. + */ + return ENOTCONN; + } + SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0); + m_dumpm(rqp->sr_rq.mb_top); + m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_WAIT); + error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, p) : ENOBUFS; + if (error == 0) { + getnanotime(&rqp->sr_timesent); + iod->iod_lastrqsent = rqp->sr_timesent; + rqp->sr_flags |= SMBR_SENT; + rqp->sr_state = SMBRQ_SENT; + return 0; + } + /* + * Check for fatal errors + */ + if (SMB_TRAN_FATAL(vcp, error)) { + /* + * No further attempts should be made + */ + return ENOTCONN; + } + if (smb_rq_intr(rqp)) + smb_iod_rqprocessed(rqp, EINTR); + return 0; +} + +/* + * Process incoming packets + */ +static int +smb_iod_recvall(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct proc *p = iod->iod_p; + struct smb_rq *rqp; + struct mbuf *m; + u_char *hp; + u_short mid; + int error; + + switch (iod->iod_state) { + case SMBIOD_ST_NOTCONN: + case SMBIOD_ST_DEAD: + case SMBIOD_ST_RECONNECT: + return 0; + default: + break; + } + for (;;) { + m = NULL; + error = SMB_TRAN_RECV(vcp, &m, p); + if (error == EWOULDBLOCK) + break; + if (SMB_TRAN_FATAL(vcp, error)) { + smb_iod_dead(iod); + break; + } + if (error) + break; + if (m == NULL) { + SMBERROR("tran return NULL without error\n"); + error = EPIPE; + continue; + } + m = m_pullup(m, SMB_HDRLEN); + if (m == NULL) + continue; /* wait for a good packet */ + /* + * Now we got an entire and possibly invalid SMB packet. + * Be careful while parsing it. + */ + m_dumpm(m); + hp = mtod(m, u_char*); + if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) { + m_freem(m); + continue; + } + mid = SMB_HDRMID(hp); + SMBSDEBUG("mid %04x\n", (u_int)mid); + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + if (rqp->sr_mid != mid) + continue; + SMBRQ_SLOCK(rqp); + if (rqp->sr_rp.md_top == NULL) { + md_initm(&rqp->sr_rp, m); + } else { + if (rqp->sr_flags & SMBR_MULTIPACKET) { + md_append_record(&rqp->sr_rp, m); + } else { + SMBRQ_SUNLOCK(rqp); + SMBERROR("duplicate response %d (ignored)\n", mid); + break; + } + } + SMBRQ_SUNLOCK(rqp); + smb_iod_rqprocessed(rqp, 0); + break; + } + SMB_IOD_RQUNLOCK(iod); + if (rqp == NULL) { + SMBERROR("drop resp with mid %d\n", (u_int)mid); +/* smb_printrqlist(vcp);*/ + m_freem(m); + } + } + /* + * check for interrupts + */ + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + if (smb_proc_intr(rqp->sr_cred->scr_p)) { + smb_iod_rqprocessed(rqp, EINTR); + } + } + SMB_IOD_RQUNLOCK(iod); + return 0; +} + +int +smb_iod_request(struct smbiod *iod, int event, void *ident) +{ + struct smbiod_event *evp; + int error; + + SMBIODEBUG("\n"); + evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK); + evp->ev_type = event; + evp->ev_ident = ident; + SMB_IOD_EVLOCK(iod); + STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link); + if ((event & SMBIOD_EV_SYNC) == 0) { + SMB_IOD_EVUNLOCK(iod); + smb_iod_wakeup(iod); + return 0; + } + smb_iod_wakeup(iod); + msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0); + error = evp->ev_error; + free(evp, M_SMBIOD); + return error; +} + +/* + * Place request in the queue. + * Request from smbiod have a high priority. + */ +int +smb_iod_addrq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smbiod *iod = vcp->vc_iod; + int error; + + SMBIODEBUG("\n"); + if (rqp->sr_cred->scr_p == iod->iod_p) { + rqp->sr_flags |= SMBR_INTERNAL; + SMB_IOD_RQLOCK(iod); + TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + for (;;) { + if (smb_iod_sendrq(iod, rqp) != 0) { + smb_iod_dead(iod); + break; + } + /* + * we don't need to lock state field here + */ + if (rqp->sr_state != SMBRQ_NOTSENT) + break; + tsleep(&iod->iod_flags, PWAIT, "90sndw", hz); + } + if (rqp->sr_lerror) + smb_iod_removerq(rqp); + return rqp->sr_lerror; + } + + switch (iod->iod_state) { + case SMBIOD_ST_NOTCONN: + return ENOTCONN; + case SMBIOD_ST_DEAD: + error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); + if (error) + return error; + return EXDEV; + default: + break; + } + + SMB_IOD_RQLOCK(iod); + for (;;) { + if (vcp->vc_maxmux == 0) { + SMBERROR("maxmux == 0\n"); + break; + } + if (iod->iod_muxcnt < vcp->vc_maxmux) + break; + iod->iod_muxwant++; + msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod), + PWAIT, "90mux", 0); + } + iod->iod_muxcnt++; + TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + smb_iod_wakeup(iod); + return 0; +} + +int +smb_iod_removerq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smbiod *iod = vcp->vc_iod; + + SMBIODEBUG("\n"); + if (rqp->sr_flags & SMBR_INTERNAL) { + SMB_IOD_RQLOCK(iod); + TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + return 0; + } + SMB_IOD_RQLOCK(iod); + while (rqp->sr_flags & SMBR_XLOCK) { + rqp->sr_flags |= SMBR_XLOCKWANT; + msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0); + } + TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); + iod->iod_muxcnt--; + if (iod->iod_muxwant) { + iod->iod_muxwant--; + wakeup(&iod->iod_muxwant); + } + SMB_IOD_RQUNLOCK(iod); + return 0; +} + +int +smb_iod_waitrq(struct smb_rq *rqp) +{ + struct smbiod *iod = rqp->sr_vc->vc_iod; + int error; + + SMBIODEBUG("\n"); + if (rqp->sr_flags & SMBR_INTERNAL) { + for (;;) { + smb_iod_sendall(iod); + smb_iod_recvall(iod); + if (rqp->sr_rpgen != rqp->sr_rplast) + break; + tsleep(&iod->iod_flags, PWAIT, "90irq", hz); + } + smb_iod_removerq(rqp); + return rqp->sr_lerror; + + } + SMBRQ_SLOCK(rqp); + if (rqp->sr_rpgen == rqp->sr_rplast) + msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0); + rqp->sr_rplast++; + SMBRQ_SUNLOCK(rqp); + error = rqp->sr_lerror; + if (rqp->sr_flags & SMBR_MULTIPACKET) { + /* + * If request should stay in the list, then reinsert it + * at the end of queue so other waiters have chance to concur + */ + SMB_IOD_RQLOCK(iod); + TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); + TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + } else + smb_iod_removerq(rqp); + return error; +} + + +static int +smb_iod_sendall(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct smb_rq *rqp; + struct timespec ts, tstimeout; + int herror; + + herror = 0; + /* + * Loop through the list of requests and send them if possible + */ + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + switch (rqp->sr_state) { + case SMBRQ_NOTSENT: + rqp->sr_flags |= SMBR_XLOCK; + SMB_IOD_RQUNLOCK(iod); + herror = smb_iod_sendrq(iod, rqp); + SMB_IOD_RQLOCK(iod); + rqp->sr_flags &= ~SMBR_XLOCK; + if (rqp->sr_flags & SMBR_XLOCKWANT) { + rqp->sr_flags &= ~SMBR_XLOCKWANT; + wakeup(rqp); + } + break; + case SMBRQ_SENT: + SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout); + timespecadd(&tstimeout, &tstimeout); + getnanotime(&ts); + timespecsub(&ts, &tstimeout); + if (timespeccmp(&ts, &rqp->sr_timesent, >)) { + smb_iod_rqprocessed(rqp, ETIMEDOUT); + } + break; + default: + } + if (herror) + break; + } + SMB_IOD_RQUNLOCK(iod); + if (herror == ENOTCONN) + smb_iod_dead(iod); + return 0; +} + +/* + * "main" function for smbiod daemon + */ +static __inline void +smb_iod_main(struct smbiod *iod) +{ +/* struct smb_vc *vcp = iod->iod_vc;*/ + struct smbiod_event *evp; +/* struct timespec tsnow;*/ + int error; + + SMBIODEBUG("\n"); + error = 0; + + /* + * Check all interesting events + */ + for (;;) { + SMB_IOD_EVLOCK(iod); + evp = STAILQ_FIRST(&iod->iod_evlist); + if (evp == NULL) { + SMB_IOD_EVUNLOCK(iod); + break; + } + STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link); + evp->ev_type |= SMBIOD_EV_PROCESSING; + SMB_IOD_EVUNLOCK(iod); + switch (evp->ev_type & SMBIOD_EV_MASK) { + case SMBIOD_EV_CONNECT: + iod->iod_state = SMBIOD_ST_RECONNECT; + evp->ev_error = smb_iod_connect(iod); + break; + case SMBIOD_EV_DISCONNECT: + evp->ev_error = smb_iod_disconnect(iod); + break; + case SMBIOD_EV_TREECONNECT: + evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident); + break; + case SMBIOD_EV_SHUTDOWN: + iod->iod_flags |= SMBIOD_SHUTDOWN; + break; + case SMBIOD_EV_NEWRQ: + break; + } + if (evp->ev_type & SMBIOD_EV_SYNC) { + SMB_IOD_EVLOCK(iod); + wakeup(evp); + SMB_IOD_EVUNLOCK(iod); + } else + free(evp, M_SMBIOD); + } +#if 0 + if (iod->iod_state == SMBIOD_ST_VCACTIVE) { + getnanotime(&tsnow); + timespecsub(&tsnow, &iod->iod_pingtimo); + if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) { + smb_smb_echo(vcp, &iod->iod_scred); + } + } +#endif + smb_iod_sendall(iod); + smb_iod_recvall(iod); + return; +} + +#ifndef FB_CURRENT +#define kthread_create_compat kthread_create2 +#else +#define kthread_create_compat kthread_create +#endif + + +void +smb_iod_thread(void *arg) +{ + struct smbiod *iod = arg; + + mtx_lock(&Giant); + smb_makescred(&iod->iod_scred, iod->iod_p, NULL); + while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) { + smb_iod_main(iod); + SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo); +/* mtx_unlock(&Giant, MTX_DEF);*/ + if (iod->iod_flags & SMBIOD_SHUTDOWN) + break; + tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo); + } +/* mtx_lock(&Giant, MTX_DEF);*/ + kthread_exit(0); +} + +int +smb_iod_create(struct smb_vc *vcp) +{ + struct smbiod *iod; + int error; + + iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK); + iod->iod_id = smb_iod_next++; + iod->iod_state = SMBIOD_ST_NOTCONN; + iod->iod_vc = vcp; + iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO; + iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO; + getnanotime(&iod->iod_lastrqsent); + vcp->vc_iod = iod; + smb_sl_init(&iod->iod_rqlock, "90rql"); + TAILQ_INIT(&iod->iod_rqlist); + smb_sl_init(&iod->iod_evlock, "90evl"); + STAILQ_INIT(&iod->iod_evlist); + error = kthread_create_compat(smb_iod_thread, iod, &iod->iod_p, + RFNOWAIT, "smbiod%d", iod->iod_id); + if (error) { + SMBERROR("can't start smbiod: %d", error); + free(iod, M_SMBIOD); + return error; + } + return 0; +} + +int +smb_iod_destroy(struct smbiod *iod) +{ + smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL); + mtx_destroy(&iod->iod_rqlock); + mtx_destroy(&iod->iod_evlock); + free(iod, M_SMBIOD); + return 0; +} + +int +smb_iod_init(void) +{ + return 0; +} + +int +smb_iod_done(void) +{ + return 0; +} + diff --git a/sys/netsmb/smb_rq.c b/sys/netsmb/smb_rq.c new file mode 100644 index 0000000..9b105bc --- /dev/null +++ b/sys/netsmb/smb_rq.c @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/mbuf.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> + +MALLOC_DEFINE(M_SMBRQ, "SMBRQ", "SMB request"); + +MODULE_DEPEND(netsmb, libmchain, 1, 1, 1); + +static int smb_rq_reply(struct smb_rq *rqp); +static int smb_rq_enqueue(struct smb_rq *rqp); +static int smb_rq_getenv(struct smb_connobj *layer, + struct smb_vc **vcpp, struct smb_share **sspp); +static int smb_rq_new(struct smb_rq *rqp, u_char cmd); +static int smb_t2_reply(struct smb_t2rq *t2p); + +int +smb_rq_alloc(struct smb_connobj *layer, u_char cmd, struct smb_cred *scred, + struct smb_rq **rqpp) +{ + struct smb_rq *rqp; + int error; + + MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK); + if (rqp == NULL) + return ENOMEM; + error = smb_rq_init(rqp, layer, cmd, scred); + rqp->sr_flags |= SMBR_ALLOCED; + if (error) { + smb_rq_done(rqp); + return error; + } + *rqpp = rqp; + return 0; +} + +static char tzero[12]; + +int +smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd, + struct smb_cred *scred) +{ + int error; + + bzero(rqp, sizeof(*rqp)); + smb_sl_init(&rqp->sr_slock, "srslock"); + error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share); + if (error) + return error; + error = smb_vc_access(rqp->sr_vc, scred, SMBM_EXEC); + if (error) + return error; + if (rqp->sr_share) { + error = smb_share_access(rqp->sr_share, scred, SMBM_EXEC); + if (error) + return error; + } + rqp->sr_cred = scred; + rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc); + return smb_rq_new(rqp, cmd); +} + +static int +smb_rq_new(struct smb_rq *rqp, u_char cmd) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct mbchain *mbp = &rqp->sr_rq; + int error; + + rqp->sr_sendcnt = 0; + mb_done(mbp); + md_done(&rqp->sr_rp); + error = mb_init(mbp); + if (error) + return error; + mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM); + mb_put_uint8(mbp, cmd); + mb_put_uint32le(mbp, 0); /* DosError */ + mb_put_uint8(mbp, vcp->vc_hflags); + mb_put_uint16le(mbp, vcp->vc_hflags2); + mb_put_mem(mbp, tzero, 12, MB_MSYSTEM); + rqp->sr_rqtid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t)); + mb_put_uint16le(mbp, 1 /*scred->sc_p->p_pid & 0xffff*/); + rqp->sr_rquid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t)); + mb_put_uint16le(mbp, rqp->sr_mid); + return 0; +} + +void +smb_rq_done(struct smb_rq *rqp) +{ + mb_done(&rqp->sr_rq); + md_done(&rqp->sr_rp); + smb_sl_destroy(&rqp->sr_slock); + if (rqp->sr_flags & SMBR_ALLOCED) + free(rqp, M_SMBRQ); +} + +/* + * Simple request-reply exchange + */ +int +smb_rq_simple(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + int error = EINVAL, i; + + for (i = 0; i < SMB_MAXRCN; i++) { + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = vcp->vc_timo; + rqp->sr_state = SMBRQ_NOTSENT; + error = smb_rq_enqueue(rqp); + if (error) + return error; + error = smb_rq_reply(rqp); + if (error == 0) + break; + if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) != SMBR_RESTART) + break; + } + return error; +} + +static int +smb_rq_enqueue(struct smb_rq *rqp) +{ + struct smb_share *ssp = rqp->sr_share; + int error; + + if (ssp == NULL || rqp->sr_cred == &rqp->sr_vc->vc_iod->iod_scred) { + return smb_iod_addrq(rqp); + } + for (;;) { + SMBS_ST_LOCK(ssp); + if (ssp->ss_flags & SMBS_RECONNECTING) { + msleep(&ssp->ss_vcgenid, SMBS_ST_LOCKPTR(ssp), + PWAIT | PDROP, "90trcn", hz); + if (smb_proc_intr(rqp->sr_cred->scr_p)) + return EINTR; + continue; + } + if (smb_share_valid(ssp) || (ssp->ss_flags & SMBS_CONNECTED) == 0) { + SMBS_ST_UNLOCK(ssp); + } else { + SMBS_ST_UNLOCK(ssp); + error = smb_iod_request(rqp->sr_vc->vc_iod, + SMBIOD_EV_TREECONNECT | SMBIOD_EV_SYNC, ssp); + if (error) + return error; + } + error = smb_iod_addrq(rqp); + if (error != EXDEV) + break; + } + return error; +} + +void +smb_rq_wstart(struct smb_rq *rqp) +{ + rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof(u_int8_t)); + rqp->sr_rq.mb_count = 0; +} + +void +smb_rq_wend(struct smb_rq *rqp) +{ + if (rqp->sr_wcount == NULL) { + SMBERROR("no wcount\n"); /* actually panic */ + return; + } + if (rqp->sr_rq.mb_count & 1) + SMBERROR("odd word count\n"); + *rqp->sr_wcount = rqp->sr_rq.mb_count / 2; +} + +void +smb_rq_bstart(struct smb_rq *rqp) +{ + rqp->sr_bcount = (u_short*)mb_reserve(&rqp->sr_rq, sizeof(u_short)); + rqp->sr_rq.mb_count = 0; +} + +void +smb_rq_bend(struct smb_rq *rqp) +{ + int bcnt; + + if (rqp->sr_bcount == NULL) { + SMBERROR("no bcount\n"); /* actually panic */ + return; + } + bcnt = rqp->sr_rq.mb_count; + if (bcnt > 0xffff) + SMBERROR("byte count too large (%d)\n", bcnt); + *rqp->sr_bcount = bcnt; +} + +int +smb_rq_intr(struct smb_rq *rqp) +{ + struct proc *p = rqp->sr_cred->scr_p; + + if (rqp->sr_flags & SMBR_INTR) + return EINTR; + return smb_proc_intr(p); +} + +int +smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp) +{ + *mbpp = &rqp->sr_rq; + return 0; +} + +int +smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp) +{ + *mbpp = &rqp->sr_rp; + return 0; +} + +static int +smb_rq_getenv(struct smb_connobj *layer, + struct smb_vc **vcpp, struct smb_share **sspp) +{ + struct smb_vc *vcp = NULL; + struct smb_share *ssp = NULL; + struct smb_connobj *cp; + int error = 0; + + switch (layer->co_level) { + case SMBL_VC: + vcp = CPTOVC(layer); + if (layer->co_parent == NULL) { + SMBERROR("zombie VC %s\n", vcp->vc_srvname); + error = EINVAL; + break; + } + break; + case SMBL_SHARE: + ssp = CPTOSS(layer); + cp = layer->co_parent; + if (cp == NULL) { + SMBERROR("zombie share %s\n", ssp->ss_name); + error = EINVAL; + break; + } + error = smb_rq_getenv(cp, &vcp, NULL); + if (error) + break; + break; + default: + SMBERROR("invalid layer %d passed\n", layer->co_level); + error = EINVAL; + } + if (vcpp) + *vcpp = vcp; + if (sspp) + *sspp = ssp; + return error; +} + +/* + * Wait for reply on the request + */ +static int +smb_rq_reply(struct smb_rq *rqp) +{ + struct mdchain *mdp = &rqp->sr_rp; + u_int32_t tdw; + u_int8_t tb; + int error, rperror = 0; + + error = smb_iod_waitrq(rqp); + if (error) + return error; + error = md_get_uint32(mdp, &tdw); + if (error) + return error; + error = md_get_uint8(mdp, &tb); + if (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_ERR_STATUS) { + error = md_get_uint32le(mdp, &rqp->sr_error); + } else { + error = md_get_uint8(mdp, &rqp->sr_errclass); + error = md_get_uint8(mdp, &tb); + error = md_get_uint16le(mdp, &rqp->sr_serror); + if (!error) + rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror); + } + error = md_get_uint8(mdp, &rqp->sr_rpflags); + error = md_get_uint16le(mdp, &rqp->sr_rpflags2); + + error = md_get_uint32(mdp, &tdw); + error = md_get_uint32(mdp, &tdw); + error = md_get_uint32(mdp, &tdw); + + error = md_get_uint16le(mdp, &rqp->sr_rptid); + error = md_get_uint16le(mdp, &rqp->sr_rppid); + error = md_get_uint16le(mdp, &rqp->sr_rpuid); + error = md_get_uint16le(mdp, &rqp->sr_rpmid); + + SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n", + rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid, + rqp->sr_errclass, rqp->sr_serror); + return error ? error : rperror; +} + + +#define ALIGN4(a) (((a) + 3) & ~3) + +/* + * TRANS2 request implementation + */ +int +smb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred, + struct smb_t2rq **t2pp) +{ + struct smb_t2rq *t2p; + int error; + + MALLOC(t2p, struct smb_t2rq *, sizeof(*t2p), M_SMBRQ, M_WAITOK); + if (t2p == NULL) + return ENOMEM; + error = smb_t2_init(t2p, layer, setup, scred); + t2p->t2_flags |= SMBT2_ALLOCED; + if (error) { + smb_t2_done(t2p); + return error; + } + *t2pp = t2p; + return 0; +} + +int +smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, u_short setup, + struct smb_cred *scred) +{ + int error; + + bzero(t2p, sizeof(*t2p)); + t2p->t2_source = source; + t2p->t2_setupcount = 1; + t2p->t2_setupdata = t2p->t2_setup; + t2p->t2_setup[0] = setup; + t2p->t2_fid = 0xffff; + t2p->t2_cred = scred; + error = smb_rq_getenv(source, &t2p->t2_vc, NULL); + if (error) + return error; + return 0; +} + +void +smb_t2_done(struct smb_t2rq *t2p) +{ + mb_done(&t2p->t2_tparam); + mb_done(&t2p->t2_tdata); + md_done(&t2p->t2_rparam); + md_done(&t2p->t2_rdata); + if (t2p->t2_flags & SMBT2_ALLOCED) + free(t2p, M_SMBRQ); +} + +static int +smb_t2_placedata(struct mbuf *mtop, u_int16_t offset, u_int16_t count, + struct mdchain *mdp) +{ + struct mbuf *m, *m0; + int len; + + m0 = m_split(mtop, offset, M_WAIT); + if (m0 == NULL) + return EBADRPC; + for(len = 0, m = m0; m->m_next; m = m->m_next) + len += m->m_len; + len += m->m_len; + m->m_len -= len - count; + if (mdp->md_top == NULL) { + md_initm(mdp, m0); + } else + m_cat(mdp->md_top, m0); + return 0; +} + +static int +smb_t2_reply(struct smb_t2rq *t2p) +{ + struct mdchain *mdp; + struct smb_rq *rqp = t2p->t2_rq; + int error, totpgot, totdgot; + u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp; + u_int16_t tmp, bc, dcount; + u_int8_t wc; + + error = smb_rq_reply(rqp); + if (error) + return error; + if ((t2p->t2_flags & SMBT2_ALLSENT) == 0) { + /* + * this is an interim response, ignore it. + */ + SMBRQ_SLOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_SUNLOCK(rqp); + return 0; + } + /* + * Now we have to get all subseqent responses. The CIFS specification + * says that they can be misordered which is weird. + * TODO: timo + */ + totpgot = totdgot = 0; + totpcount = totdcount = 0xffff; + mdp = &rqp->sr_rp; + for (;;) { + m_dumpm(mdp->md_top); + if ((error = md_get_uint8(mdp, &wc)) != 0) + break; + if (wc < 10) { + error = ENOENT; + break; + } + if ((error = md_get_uint16le(mdp, &tmp)) != 0) + break; + if (totpcount > tmp) + totpcount = tmp; + md_get_uint16le(mdp, &tmp); + if (totdcount > tmp) + totdcount = tmp; + if ((error = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */ + (error = md_get_uint16le(mdp, &pcount)) != 0 || + (error = md_get_uint16le(mdp, &poff)) != 0 || + (error = md_get_uint16le(mdp, &pdisp)) != 0) + break; + if (pcount != 0 && pdisp != totpgot) { + SMBERROR("Can't handle misordered parameters %d:%d\n", + pdisp, totpgot); + error = EINVAL; + break; + } + if ((error = md_get_uint16le(mdp, &dcount)) != 0 || + (error = md_get_uint16le(mdp, &doff)) != 0 || + (error = md_get_uint16le(mdp, &ddisp)) != 0) + break; + if (dcount != 0 && ddisp != totdgot) { + SMBERROR("Can't handle misordered data\n"); + error = EINVAL; + break; + } + md_get_uint8(mdp, &wc); + md_get_uint8(mdp, NULL); + tmp = wc; + while (tmp--) + md_get_uint16(mdp, NULL); + if ((error = md_get_uint16le(mdp, &bc)) != 0) + break; +/* tmp = SMB_HDRLEN + 1 + 10 * 2 + 2 * wc + 2;*/ + if (dcount) { + error = smb_t2_placedata(mdp->md_top, doff, dcount, + &t2p->t2_rdata); + if (error) + break; + } + if (pcount) { + error = smb_t2_placedata(mdp->md_top, poff, pcount, + &t2p->t2_rparam); + if (error) + break; + } + totpgot += pcount; + totdgot += dcount; + if (totpgot >= totpcount && totdgot >= totdcount) { + error = 0; + t2p->t2_flags |= SMBT2_ALLRECV; + break; + } + /* + * We're done with this reply, look for the next one. + */ + SMBRQ_SLOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_SUNLOCK(rqp); + error = smb_rq_reply(rqp); + if (error) + break; + } + return error; +} + +/* + * Perform a full round of TRANS2 request + */ +static int +smb_t2_request_int(struct smb_t2rq *t2p) +{ + struct smb_vc *vcp = t2p->t2_vc; + struct smb_cred *scred = t2p->t2_cred; + struct mbchain *mbp; + struct mdchain *mdp, mbparam, mbdata; + struct mbuf *m; + struct smb_rq *rqp; + int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i; + int error, doff, poff, txdcount, txpcount, nmlen; + + m = t2p->t2_tparam.mb_top; + if (m) { + md_initm(&mbparam, m); /* do not free it! */ + totpcount = m_fixhdr(m); + if (totpcount > 0xffff) /* maxvalue for u_short */ + return EINVAL; + } else + totpcount = 0; + m = t2p->t2_tdata.mb_top; + if (m) { + md_initm(&mbdata, m); /* do not free it! */ + totdcount = m_fixhdr(m); + if (totdcount > 0xffff) + return EINVAL; + } else + totdcount = 0; + leftdcount = totdcount; + leftpcount = totpcount; + txmax = vcp->vc_txmax; + error = smb_rq_alloc(t2p->t2_source, t2p->t_name ? + SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp); + if (error) + return error; + rqp->sr_flags |= SMBR_MULTIPACKET; + t2p->t2_rq = rqp; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, totpcount); + mb_put_uint16le(mbp, totdcount); + mb_put_uint16le(mbp, t2p->t2_maxpcount); + mb_put_uint16le(mbp, t2p->t2_maxdcount); + mb_put_uint8(mbp, t2p->t2_maxscount); + mb_put_uint8(mbp, 0); /* reserved */ + mb_put_uint16le(mbp, 0); /* flags */ + mb_put_uint32le(mbp, 0); /* Timeout */ + mb_put_uint16le(mbp, 0); /* reserved 2 */ + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1), + * and need to decide which parts should go into the first request + */ + nmlen = t2p->t_name ? strlen(t2p->t_name) : 0; + len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1); + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + leftpcount -= txpcount; + leftdcount -= txdcount; + mb_put_uint16le(mbp, txpcount); + mb_put_uint16le(mbp, poff); + mb_put_uint16le(mbp, txdcount); + mb_put_uint16le(mbp, doff); + mb_put_uint8(mbp, t2p->t2_setupcount); + mb_put_uint8(mbp, 0); + for (i = 0; i < t2p->t2_setupcount; i++) + mb_put_uint16le(mbp, t2p->t2_setupdata[i]); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + /* TDUNICODE */ + if (t2p->t_name) + mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM); + mb_put_uint8(mbp, 0); /* terminating zero */ + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); /* incredible, but thats it... */ + error = smb_rq_enqueue(rqp); + if (error) + goto freerq; + if (leftpcount == 0 && leftdcount == 0) + t2p->t2_flags |= SMBT2_ALLSENT; + error = smb_t2_reply(t2p); + if (error) + goto bad; + while (leftpcount || leftdcount) { + error = smb_rq_new(rqp, t2p->t_name ? + SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY); + if (error) + goto bad; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, totpcount); + mb_put_uint16le(mbp, totdcount); + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one, + * and need to decide which parts should go into request + */ + len = ALIGN4(len + 6 * 2 + 2); + if (t2p->t_name == NULL) + len += 2; + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + mb_put_uint16le(mbp, txpcount); + mb_put_uint16le(mbp, poff); + mb_put_uint16le(mbp, totpcount - leftpcount); + mb_put_uint16le(mbp, txdcount); + mb_put_uint16le(mbp, doff); + mb_put_uint16le(mbp, totdcount - leftdcount); + leftpcount -= txpcount; + leftdcount -= txdcount; + if (t2p->t_name == NULL) + mb_put_uint16le(mbp, t2p->t2_fid); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, 0); /* name */ + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); + rqp->sr_state = SMBRQ_NOTSENT; + error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL); + if (error) + goto bad; + } /* while left params or data */ + t2p->t2_flags |= SMBT2_ALLSENT; + mdp = &t2p->t2_rdata; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } + mdp = &t2p->t2_rparam; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } +bad: + smb_iod_removerq(rqp); +freerq: + smb_rq_done(rqp); + if (error) { + if (rqp->sr_flags & SMBR_RESTART) + t2p->t2_flags |= SMBT2_RESTART; + md_done(&t2p->t2_rparam); + md_done(&t2p->t2_rdata); + } + return error; +} + +int +smb_t2_request(struct smb_t2rq *t2p) +{ + int error = EINVAL, i; + + for (i = 0; i < SMB_MAXRCN; i++) { + t2p->t2_flags &= ~SMBR_RESTART; + error = smb_t2_request_int(t2p); + if (error == 0) + break; + if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != SMBT2_RESTART) + break; + } + return error; +} diff --git a/sys/netsmb/smb_rq.h b/sys/netsmb/smb_rq.h new file mode 100644 index 0000000..d37647b --- /dev/null +++ b/sys/netsmb/smb_rq.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _NETSMB_SMB_RQ_H_ +#define _NETSMB_SMB_RQ_H_ + +#ifndef MB_MSYSTEM +#include <sys/mchain.h> +#endif + +#define SMBR_ALLOCED 0x0001 /* structure was malloced */ +#define SMBR_SENT 0x0002 /* request successfully transmitted */ +#define SMBR_REXMIT 0x0004 /* request should be retransmitted */ +#define SMBR_INTR 0x0008 /* request interrupted */ +#define SMBR_RESTART 0x0010 /* request should be repeated if possible */ +#define SMBR_NORESTART 0x0020 /* request is not restartable */ +#define SMBR_MULTIPACKET 0x0040 /* multiple packets can be sent and received */ +#define SMBR_INTERNAL 0x0080 /* request is internal to smbrqd */ +#define SMBR_XLOCK 0x0100 /* request locked and can't be moved */ +#define SMBR_XLOCKWANT 0x0200 /* waiter on XLOCK */ + +#define SMBT2_ALLSENT 0x0001 /* all data and params are sent */ +#define SMBT2_ALLRECV 0x0002 /* all data and params are received */ +#define SMBT2_ALLOCED 0x0004 +#define SMBT2_RESTART 0x0008 +#define SMBT2_NORESTART 0x0010 + +#define SMBRQ_SLOCK(rqp) smb_sl_lock(&(rqp)->sr_slock) +#define SMBRQ_SUNLOCK(rqp) smb_sl_unlock(&(rqp)->sr_slock) +#define SMBRQ_SLOCKPTR(rqp) (&(rqp)->sr_slock) + + +enum smbrq_state { + SMBRQ_NOTSENT, /* rq have data to send */ + SMBRQ_SENT, /* send procedure completed */ + SMBRQ_REPLYRECEIVED, + SMBRQ_NOTIFIED /* owner notified about completion */ +}; + +struct smb_vc; +struct smb_t2rq; + +struct smb_rq { + enum smbrq_state sr_state; + struct smb_vc * sr_vc; + struct smb_share* sr_share; + u_short sr_mid; + struct mbchain sr_rq; + u_int8_t sr_rqflags; + u_int16_t sr_rqflags2; + u_char * sr_wcount; + u_short * sr_bcount; + struct mdchain sr_rp; + int sr_rpgen; + int sr_rplast; + int sr_flags; /* SMBR_* */ + int sr_rpsize; + struct smb_cred * sr_cred; + int sr_timo; + int sr_rexmit; + int sr_sendcnt; + struct timespec sr_timesent; + int sr_lerror; + u_int16_t * sr_rqtid; + u_int16_t * sr_rquid; + u_int8_t sr_errclass; + u_int16_t sr_serror; + u_int32_t sr_error; + u_int8_t sr_rpflags; + u_int16_t sr_rpflags2; + u_int16_t sr_rptid; + u_int16_t sr_rppid; + u_int16_t sr_rpuid; + u_int16_t sr_rpmid; + struct smb_slock sr_slock; /* short term locks */ +/* struct smb_t2rq*sr_t2;*/ + TAILQ_ENTRY(smb_rq) sr_link; +}; + +struct smb_t2rq { + u_int16_t t2_setupcount; + u_int16_t * t2_setupdata; + u_int16_t t2_setup[2]; /* most of rqs has setupcount of 1 */ + u_int8_t t2_maxscount; /* max setup words to return */ + u_int16_t t2_maxpcount; /* max param bytes to return */ + u_int16_t t2_maxdcount; /* max data bytes to return */ + u_int16_t t2_fid; /* for T2 request */ + char * t_name; /* for T request, should be zero for T2 */ + int t2_flags; /* SMBT2_ */ + struct mbchain t2_tparam; /* parameters to transmit */ + struct mbchain t2_tdata; /* data to transmit */ + struct mdchain t2_rparam; /* received paramters */ + struct mdchain t2_rdata; /* received data */ + struct smb_cred*t2_cred; + struct smb_connobj *t2_source; + struct smb_rq * t2_rq; + struct smb_vc * t2_vc; +}; + +int smb_rq_alloc(struct smb_connobj *layer, u_char cmd, + struct smb_cred *scred, struct smb_rq **rqpp); +int smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd, + struct smb_cred *scred); +void smb_rq_done(struct smb_rq *rqp); +int smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp); +int smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp); +void smb_rq_wstart(struct smb_rq *rqp); +void smb_rq_wend(struct smb_rq *rqp); +void smb_rq_bstart(struct smb_rq *rqp); +void smb_rq_bend(struct smb_rq *rqp); +int smb_rq_intr(struct smb_rq *rqp); +int smb_rq_simple(struct smb_rq *rqp); + +int smb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred, + struct smb_t2rq **rqpp); +int smb_t2_init(struct smb_t2rq *rqp, struct smb_connobj *layer, u_short setup, + struct smb_cred *scred); +void smb_t2_done(struct smb_t2rq *t2p); +int smb_t2_request(struct smb_t2rq *t2p); + +#endif /* !_NETSMB_SMB_RQ_H_ */ diff --git a/sys/netsmb/smb_smb.c b/sys/netsmb/smb_smb.c new file mode 100644 index 0000000..0f3179d --- /dev/null +++ b/sys/netsmb/smb_smb.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +/* + * various SMB requests. Most of the routines merely packs data into mbufs. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <sys/iconv.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_tran.h> + +struct smb_dialect { + int d_id; + const char * d_name; +}; + +static struct smb_dialect smb_dialects[] = { + {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"}, + {SMB_DIALECT_COREPLUS, "MICROSOFT NETWORKS 1.03"}, + {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"}, + {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"}, + {SMB_DIALECT_LANMAN2_0, "LM1.2X002"}, + {SMB_DIALECT_LANMAN2_0, "Samba"}, + {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"}, + {SMB_DIALECT_NTLM0_12, "NT LM 0.12"}, + {-1, NULL} +}; + +#define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2) + +static int +smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name) +{ + if (scred->scr_p == vcp->vc_iod->iod_p) + return 0; + SMBERROR("wrong function called(%s)\n", name); + return EINVAL; +} + +int +smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_dialect *dp; + struct smb_sopt *sp = NULL; + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc, stime[8], sblen; + u_int16_t dindex, tw, tw1, swlen, bc; + int error, maxqsz; + + if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0) + return EINVAL; + vcp->vc_hflags = 0; + vcp->vc_hflags2 = 0; + vcp->obj.co_flags &= ~(SMBV_ENCRYPT); + sp = &vcp->vc_sopt; + bzero(sp, sizeof(struct smb_sopt)); + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + for(dp = smb_dialects; dp->d_id != -1; dp++) { + mb_put_uint8(mbp, SMB_DT_DIALECT); + smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); + } + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) + goto bad; + smb_rq_getreply(rqp, &mdp); + do { + error = md_get_uint8(mdp, &wc); + if (error) + break; + error = md_get_uint16le(mdp, &dindex); + if (error) + break; + if (dindex > 7) { + SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex); + error = EBADRPC; + break; + } + dp = smb_dialects + dindex; + sp->sv_proto = dp->d_id; + SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); + error = EBADRPC; + if (dp->d_id >= SMB_DIALECT_NTLM0_12) { + if (wc != 17) + break; + md_get_uint8(mdp, &sp->sv_sm); + md_get_uint16le(mdp, &sp->sv_maxmux); + md_get_uint16le(mdp, &sp->sv_maxvcs); + md_get_uint32le(mdp, &sp->sv_maxtx); + md_get_uint32le(mdp, &sp->sv_maxraw); + md_get_uint32le(mdp, &sp->sv_skey); + md_get_uint32le(mdp, &sp->sv_caps); + md_get_mem(mdp, stime, 8, MB_MSYSTEM); + md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); + md_get_uint8(mdp, &sblen); + if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) { + if (sblen != SMB_MAXCHALLENGELEN) { + SMBERROR("Unexpected length of security blob (%d)\n", sblen); + break; + } + error = md_get_uint16(mdp, &bc); + if (error) + break; + if (sp->sv_caps & SMB_CAP_EXT_SECURITY) + md_get_mem(mdp, NULL, 16, MB_MSYSTEM); + error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM); + if (error) + break; + vcp->vc_chlen = sblen; + vcp->obj.co_flags |= SMBV_ENCRYPT; + } + vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; + if (dp->d_id == SMB_DIALECT_NTLM0_12 && + sp->sv_maxtx < 4096 && + (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { + vcp->obj.co_flags |= SMBV_WIN95; + SMBSDEBUG("Win95 detected\n"); + } + } else if (dp->d_id > SMB_DIALECT_CORE) { + md_get_uint16le(mdp, &tw); + sp->sv_sm = tw; + md_get_uint16le(mdp, &tw); + sp->sv_maxtx = tw; + md_get_uint16le(mdp, &sp->sv_maxmux); + md_get_uint16le(mdp, &sp->sv_maxvcs); + md_get_uint16le(mdp, &tw); /* rawmode */ + md_get_uint32le(mdp, &sp->sv_skey); + if (wc == 13) { /* >= LANMAN1 */ + md_get_uint16(mdp, &tw); /* time */ + md_get_uint16(mdp, &tw1); /* date */ + md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); + md_get_uint16le(mdp, &swlen); + if (swlen > SMB_MAXCHALLENGELEN) + break; + md_get_uint16(mdp, NULL); /* mbz */ + if (md_get_uint16(mdp, &bc) != 0) + break; + if (bc < swlen) + break; + if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { + error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM); + if (error) + break; + vcp->vc_chlen = swlen; + vcp->obj.co_flags |= SMBV_ENCRYPT; + } + } + vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; + } else { /* an old CORE protocol */ + sp->sv_maxmux = 1; + } + error = 0; + } while (0); + if (error == 0) { + vcp->vc_maxvcs = sp->sv_maxvcs; + if (vcp->vc_maxvcs <= 1) { + if (vcp->vc_maxvcs == 0) + vcp->vc_maxvcs = 1; + } + if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff) + sp->sv_maxtx = 1024; + SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz); + vcp->vc_txmax = min(sp->sv_maxtx, maxqsz); + SMBSDEBUG("TZ = %d\n", sp->sv_tz); + SMBSDEBUG("CAPS = %x\n", sp->sv_caps); + SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux); + SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs); + SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw); + SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx); + } +bad: + smb_rq_done(rqp); + return error; +} + +int +smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; +/* u_int8_t wc; + u_int16_t tw, tw1;*/ + smb_uniptr unipp, ntencpass = NULL; + char *pp, *up, *pbuf, *encpass; + int error, plen, uniplen, ulen; + + vcp->vc_smbuid = SMB_UID_UNKNOWN; + + if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0) + return EINVAL; + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp); + if (error) + return error; + pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); + encpass = malloc(24, M_SMBTEMP, M_WAITOK); + if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { + iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp)); + iconv_convstr(vcp->vc_toserver, pbuf, pbuf); + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + uniplen = plen = 24; + smb_encrypt(pbuf, vcp->vc_ch, encpass); + ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); + iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp)); + smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass); + pp = encpass; + unipp = ntencpass; + } else { + plen = strlen(pbuf) + 1; + pp = pbuf; + uniplen = plen * 2; + ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); + smb_strtouni(ntencpass, smb_vc_getpass(vcp)); + plen--; + uniplen = 0/*-= 2*/; + unipp = ntencpass; + } + } else { + /* + * In the share security mode password will be used + * only in the tree authentication + */ + pp = ""; + plen = 1; + unipp = &smb_unieol; + uniplen = sizeof(smb_unieol); + } + smb_rq_wstart(rqp); + mbp = &rqp->sr_rq; + up = vcp->vc_username; + ulen = strlen(up) + 1; + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); + mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); + mb_put_uint16le(mbp, vcp->vc_number); + mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); + mb_put_uint16le(mbp, plen); + if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { + mb_put_uint32le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); + } else { + mb_put_uint16le(mbp, uniplen); + mb_put_uint32le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, 0); /* my caps */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM); + smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */ + smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */ + smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE); /* Client's OS */ + smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */ + } + smb_rq_bend(rqp); + if (ntencpass) + free(ntencpass, M_SMBTEMP); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) { + if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess) + error = EAUTH; + goto bad; + } + vcp->vc_smbuid = rqp->sr_rpuid; +bad: + free(encpass, M_SMBTEMP); + free(pbuf, M_SMBTEMP); + smb_rq_done(rqp); + return error; +} + +int +smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (vcp->vc_smbuid == SMB_UID_UNKNOWN) + return 0; + + if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0) + return EINVAL; + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return error; +} + +static char smb_any_share[] = "?????"; + +static char * +smb_share_typename(int stype) +{ + char *pp; + + switch (stype) { + case SMB_ST_DISK: + pp = "A:"; + break; + case SMB_ST_PRINTER: + pp = smb_any_share; /* can't use LPT: here... */ + break; + case SMB_ST_PIPE: + pp = "IPC"; + break; + case SMB_ST_COMM: + pp = "COMM"; + break; + case SMB_ST_ANY: + default: + pp = smb_any_share; + break; + } + return pp; +} + +int +smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_vc *vcp; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + char *pp, *pbuf, *encpass; + int error, plen, caseopt; + + ssp->ss_tid = SMB_TID_UNKNOWN; + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp); + if (error) + return error; + vcp = rqp->sr_vc; + caseopt = SMB_CS_NONE; + if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { + plen = 1; + pp = ""; + pbuf = NULL; + encpass = NULL; + } else { + pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); + encpass = malloc(24, M_SMBTEMP, M_WAITOK); + iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp)); + iconv_convstr(vcp->vc_toserver, pbuf, pbuf); + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + plen = 24; + smb_encrypt(pbuf, vcp->vc_ch, encpass); + pp = encpass; + } else { + plen = strlen(pbuf) + 1; + pp = pbuf; + } + } + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, 0); /* Flags */ + mb_put_uint16le(mbp, plen); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt); + pp = vcp->vc_srvname; + smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt); + smb_put_dmem(mbp, vcp, "\\", 1, caseopt); + pp = ssp->ss_name; + smb_put_dstring(mbp, vcp, pp, caseopt); + pp = smb_share_typename(ssp->ss_type); + smb_put_dstring(mbp, vcp, pp, caseopt); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) + goto bad; + ssp->ss_tid = rqp->sr_rptid; + ssp->ss_vcgenid = vcp->vc_genid; + ssp->ss_flags |= SMBS_CONNECTED; +bad: + if (encpass) + free(encpass, M_SMBTEMP); + if (pbuf) + free(pbuf, M_SMBTEMP); + smb_rq_done(rqp); + return error; +} + +int +smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (ssp->ss_tid == SMB_TID_UNKNOWN) + return 0; + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + ssp->ss_tid = SMB_TID_UNKNOWN; + return error; +} + +static __inline int +smb_smb_read(struct smb_share *ssp, u_int16_t fid, + int *len, int *rresid, struct uio *uio, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t resid, bc; + u_int8_t wc; + int error, rlen, blksz; + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp); + if (error) + return error; + + blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; + rlen = *len = min(blksz, *len); + + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + mb_put_uint16le(mbp, rlen); + mb_put_uint32le(mbp, uio->uio_offset); + mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + do { + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 5) { + error = EBADRPC; + break; + } + md_get_uint16le(mdp, &resid); + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); + md_get_uint16le(mdp, &bc); + md_get_uint8(mdp, NULL); /* ignore buffer type */ + md_get_uint16le(mdp, &resid); + if (resid == 0) { + *rresid = resid; + break; + } + error = md_get_uio(mdp, uio, resid); + if (error) + break; + *rresid = resid; + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred) +{ + int tsize, len, resid; + int error = 0; + + tsize = uio->uio_resid; + while (tsize > 0) { + len = tsize; + error = smb_smb_read(ssp, fid, &len, &resid, uio, scred); + if (error) + break; + tsize -= resid; + if (resid < len) + break; + } + return error; +} + +static __inline int +smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + struct uio *uio, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t resid; + u_int8_t wc; + int error, blksz; + + blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; + if (blksz > 0xffff) + blksz = 0xffff; + + resid = *len = min(blksz, *len); + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + mb_put_uint16le(mbp, resid); + mb_put_uint32le(mbp, uio->uio_offset); + mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_DATA); + mb_put_uint16le(mbp, resid); + do { + error = mb_put_uio(mbp, uio, resid); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 1) { + error = EBADRPC; + break; + } + md_get_uint16le(mdp, &resid); + *rresid = resid; + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred) +{ + int error = 0, len, tsize, resid; + struct uio olduio; + + /* + * review: manage iov more precisely + */ + if (uio->uio_iovcnt != 1) { + SMBERROR("can't handle iovcnt > 1\n"); + return EIO; + } + tsize = uio->uio_resid; + olduio = *uio; + while (tsize > 0) { + len = tsize; + error = smb_smb_write(ssp, fid, &len, &resid, uio, scred); + if (error) + break; + if (resid < len) { + error = EIO; + break; + } + tsize -= resid; + } + if (error) { + *uio = olduio; + } + return error; +} + +int +smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, 1); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint32le(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return error; +} diff --git a/sys/netsmb/smb_subr.c b/sys/netsmb/smb_subr.c new file mode 100644 index 0000000..8346629 --- /dev/null +++ b/sys/netsmb/smb_subr.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#include <sys/signalvar.h> +#include <sys/mbuf.h> + +#include <sys/iconv.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_subr.h> + +MALLOC_DEFINE(M_SMBDATA, "SMBDATA", "Misc netsmb data"); +MALLOC_DEFINE(M_SMBSTR, "SMBSTR", "netsmb string data"); +MALLOC_DEFINE(M_SMBTEMP, "SMBTEMP", "Temp netsmb data"); + +smb_unichar smb_unieol = 0; + +void +smb_makescred(struct smb_cred *scred, struct proc *p, struct ucred *cred) +{ + if (p) { + scred->scr_p = p; + scred->scr_cred = cred ? cred : p->p_ucred; + } else { + scred->scr_p = NULL; + scred->scr_cred = cred ? cred : NULL; + } +} + +int +smb_proc_intr(struct proc *p) +{ +#if __FreeBSD_version < 400009 + + if (p && p->p_siglist && + (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) & SMB_SIGMASK)) + return EINTR; + return 0; +#else + sigset_t tmpset; + + if (p == NULL) + return 0; + tmpset = p->p_siglist; + SIGSETNAND(tmpset, p->p_sigmask); + SIGSETNAND(tmpset, p->p_sigignore); + if (SIGNOTEMPTY(p->p_siglist) && SMB_SIGMASK(tmpset)) + return EINTR; + return 0; +#endif +} + +char * +smb_strdup(const char *s) +{ + char *p; + int len; + + len = s ? strlen(s) + 1 : 1; + p = malloc(len, M_SMBSTR, M_WAITOK); + if (s) + bcopy(s, p, len); + else + *p = 0; + return p; +} + +/* + * duplicate string from a user space. + */ +char * +smb_strdupin(char *s, int maxlen) +{ + char *p, bt; + int len = 0; + + for (p = s; ;p++) { + if (copyin(p, &bt, 1)) + return NULL; + len++; + if (maxlen && len > maxlen) + return NULL; + if (bt == 0) + break; + } + p = malloc(len, M_SMBSTR, M_WAITOK); + copyin(s, p, len); + return p; +} + +/* + * duplicate memory block from a user space. + */ +void * +smb_memdupin(void *umem, int len) +{ + char *p; + + if (len > 8 * 1024) + return NULL; + p = malloc(len, M_SMBSTR, M_WAITOK); + if (copyin(umem, p, len) == 0) + return p; + free(p, M_SMBSTR); + return NULL; +} + +/* + * duplicate memory block in the kernel space. + */ +void * +smb_memdup(const void *umem, int len) +{ + char *p; + + if (len > 8 * 1024) + return NULL; + p = malloc(len, M_SMBSTR, M_WAITOK); + if (p == NULL) + return NULL; + bcopy(umem, p, len); + return p; +} + +void +smb_strfree(char *s) +{ + free(s, M_SMBSTR); +} + +void +smb_memfree(void *s) +{ + free(s, M_SMBSTR); +} + +void * +smb_zmalloc(unsigned long size, struct malloc_type *type, int flags) +{ + + return malloc(size, type, flags | M_ZERO); +} + +void +smb_strtouni(u_int16_t *dst, const char *src) +{ + while (*src) { + *dst++ = htoles(*src++); + } + *dst = 0; +} + +#ifdef SMB_SOCKETDATA_DEBUG +void +m_dumpm(struct mbuf *m) { + char *p; + int len; + printf("d="); + while(m) { + p=mtod(m,char *); + len=m->m_len; + printf("(%d)",len); + while(len--){ + printf("%02x ",((int)*(p++)) & 0xff); + } + m=m->m_next; + }; + printf("\n"); +} +#endif + +int +smb_maperror(int eclass, int eno) +{ + if (eclass == 0 && eno == 0) + return 0; + switch (eclass) { + case ERRDOS: + switch (eno) { + case ERRbadfunc: + case ERRbadmcb: + case ERRbadenv: + case ERRbadformat: + case ERRrmuns: + return EINVAL; + case ERRbadfile: + case ERRbadpath: + case ERRremcd: + case 66: /* nt returns it when share not available */ + return ENOENT; + case ERRnofids: + return EMFILE; + case ERRnoaccess: + case ERRbadshare: + return EACCES; + case ERRbadfid: + return EBADF; + case ERRnomem: + return ENOMEM; /* actually remote no mem... */ + case ERRbadmem: + return EFAULT; + case ERRbadaccess: + return EACCES; + case ERRbaddata: + return E2BIG; + case ERRbaddrive: + case ERRnotready: /* nt */ + return ENXIO; + case ERRdiffdevice: + return EXDEV; + case ERRnofiles: + return 0; /* eeof ? */ + return ETXTBSY; + case ERRlock: + return EDEADLK; + case ERRfilexists: + return EEXIST; + case 123: /* dunno what is it, but samba maps as noent */ + return ENOENT; + case 145: /* samba */ + return ENOTEMPTY; + case 183: + return EEXIST; + } + break; + case ERRSRV: + switch (eno) { + case ERRerror: + return EINVAL; + case ERRbadpw: + return EAUTH; + case ERRaccess: + return EACCES; + case ERRinvnid: + return ENETRESET; + case ERRinvnetname: + SMBERROR("NetBIOS name is invalid\n"); + return EAUTH; + case 3: /* reserved and returned */ + return EIO; + case 2239: /* NT: account exists but disabled */ + return EPERM; + } + break; + case ERRHRD: + switch (eno) { + case ERRnowrite: + return EROFS; + case ERRbadunit: + return ENODEV; + case ERRnotready: + case ERRbadcmd: + case ERRdata: + return EIO; + case ERRbadreq: + return EBADRPC; + case ERRbadshare: + return ETXTBSY; + case ERRlock: + return EDEADLK; + } + break; + } + SMBERROR("Unmapped error %d:%d\n", eclass, eno); + return EBADRPC; +} + +static int +smb_copy_iconv(struct mbchain *mbp, c_caddr_t src, caddr_t dst, int len) +{ + int outlen = len; + + return iconv_conv((struct iconv_drv*)mbp->mb_udata, &src, &len, &dst, &outlen); +} + +int +smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src, + int size, int caseopt) +{ + struct iconv_drv *dp = vcp->vc_toserver; + + if (size == 0) + return 0; + if (dp == NULL) { + return mb_put_mem(mbp, src, size, MB_MSYSTEM); + } + mbp->mb_copy = smb_copy_iconv; + mbp->mb_udata = dp; + return mb_put_mem(mbp, src, size, MB_MCUSTOM); +} + +int +smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src, + int caseopt) +{ + int error; + + error = smb_put_dmem(mbp, vcp, src, strlen(src), caseopt); + if (error) + return error; + return mb_put_uint8(mbp, 0); +} + +int +smb_put_asunistring(struct smb_rq *rqp, const char *src) +{ + struct mbchain *mbp = &rqp->sr_rq; + struct iconv_drv *dp = rqp->sr_vc->vc_toserver; + u_char c; + int error; + + while (*src) { + iconv_convmem(dp, &c, src++, 1); + error = mb_put_uint16le(mbp, c); + if (error) + return error; + } + return mb_put_uint16le(mbp, 0); +} diff --git a/sys/netsmb/smb_subr.h b/sys/netsmb/smb_subr.h new file mode 100644 index 0000000..16ce212 --- /dev/null +++ b/sys/netsmb/smb_subr.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _NETSMB_SMB_SUBR_H_ +#define _NETSMB_SMB_SUBR_H_ + +#ifndef _KERNEL +#error "This file shouldn't be included from userland programs" +#endif + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_SMBTEMP); +#endif + +#if __FreeBSD_version > 500000 +#define FB_CURRENT +#else +# if __FreeBSD_version > 400000 +# define FB_RELENG4 +# else +# error "Unsupported version of FreeBSD" +# endif +#endif + +#define SMBERROR(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#define SMBPANIC(format, args...) printf("%s: "format, __FUNCTION__ ,## args) + +#ifdef SMB_SOCKET_DEBUG +#define SMBSDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define SMBSDEBUG(format, args...) +#endif + +#ifdef SMB_IOD_DEBUG +#define SMBIODEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define SMBIODEBUG(format, args...) +#endif + +#ifdef SMB_SOCKETDATA_DEBUG +void m_dumpm(struct mbuf *m); +#else +#define m_dumpm(m) +#endif + +#if __FreeBSD_version > 400009 +#define SMB_SIGMASK(set) \ + (SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \ + SIGISMEMBER(set, SIGHUP) || SIGISMEMBER(set, SIGKILL) || \ + SIGISMEMBER(set, SIGQUIT)) + +#define smb_suser(cred) suser_xxx(cred, NULL, 0) +#else +#define SMB_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \ + sigmask(SIGHUP)|sigmask(SIGQUIT)) + +#define smb_suser(cred) suser((cred), NULL) +#endif + +/* + * Compatibility wrappers for simple locks + */ +#if __FreeBSD_version < 500000 + +#include <sys/lock.h> + +#define lockdestroy(lock) +#define smb_slock simplelock +#define smb_sl_init(mtx, desc) simple_lock_init(mtx) +#define smb_sl_destroy(mtx) +#define smb_sl_lock(mtx) simple_lock(mtx) +#define smb_sl_unlock(mtx) simple_unlock(mtx) +/* +#define mtx lock +#define mtx_init(mtx, desc, flags) lockinit(mtx, PWAIT, desc, 0, 0) +#define mtx_lock(mtx) lockmgr(mtx, LK_EXCLUSIVE, NULL, curproc) +#define mtx_unlock(mtx) lockmgr(mtx, LK_RELEASE, NULL, curproc) +#define mtx_destroy(mtx) +*/ +#else + +#include <sys/mutex.h> + +#define smb_slock mtx +#define smb_sl_init(mtx, desc) mtx_init(mtx, desc, MTX_DEF) +#define smb_sl_destroy(mtx) mtx_destroy(mtx) +#define smb_sl_lock(mtx) mtx_lock(mtx) +#define smb_sl_unlock(mtx) mtx_unlock(mtx) + +#endif + +#define SMB_STRFREE(p) do { if (p) smb_strfree(p); } while(0) + +/* + * The simple try/catch/finally interface. + * With GCC it is possible to allow more than one try/finally block per + * function, but we'll avoid it to maintain portability. + */ +#define itry { \ + __label__ _finlab, _catchlab; \ + int _tval; \ + +#define icatch(var) \ + goto _finlab; \ + (void)&&_catchlab; \ + _catchlab: \ + var = _tval; + +#define ifinally (void)&&_finlab; \ + _finlab: +#define iendtry } + +#define inocatch \ + goto _finlab; \ + (void)&&_catchlab; \ + _catchlab: \ + +#define ithrow(t) do { \ + if ((_tval = (int)(t)) != 0) \ + goto _catchlab; \ + } while (0) + +#define ierror(t,e) do { \ + if (t) { \ + _tval = e; \ + goto _catchlab; \ + } \ + } while (0) + +typedef u_int16_t smb_unichar; +typedef smb_unichar *smb_uniptr; + +/* + * Crediantials of user/process being processing in the connection procedures + */ +struct smb_cred { + struct proc * scr_p; + struct ucred * scr_cred; +}; + +extern smb_unichar smb_unieol; + +struct mbchain; +struct smb_vc; +struct smb_rq; + +void smb_makescred(struct smb_cred *scred, struct proc *p, struct ucred *cred); +int smb_proc_intr(struct proc *); +char *smb_strdup(const char *s); +void *smb_memdup(const void *umem, int len); +char *smb_strdupin(char *s, int maxlen); +void *smb_memdupin(void *umem, int len); +void smb_strtouni(u_int16_t *dst, const char *src); +void smb_strfree(char *s); +void smb_memfree(void *s); +void *smb_zmalloc(unsigned long size, struct malloc_type *type, int flags); + +int smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN); +int smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN); +int smb_maperror(int eclass, int eno); +int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, + const char *src, int len, int caseopt); +int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, + const char *src, int caseopt); +int smb_put_string(struct smb_rq *rqp, const char *src); +int smb_put_asunistring(struct smb_rq *rqp, const char *src); + +#endif /* !_NETSMB_SMB_SUBR_H_ */ diff --git a/sys/netsmb/smb_tran.h b/sys/netsmb/smb_tran.h new file mode 100644 index 0000000..7403d21 --- /dev/null +++ b/sys/netsmb/smb_tran.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _NETSMB_SMB_TRAN_H_ +#define _NETSMB_SMB_TRAN_H_ + +#include <sys/socket.h> + +/* + * Known transports + */ +#define SMBT_NBTCP 1 + +/* + * Transport parameters + */ +#define SMBTP_SNDSZ 1 /* R - int */ +#define SMBTP_RCVSZ 2 /* R - int */ +#define SMBTP_TIMEOUT 3 /* RW - struct timespec */ +#define SMBTP_SELECTID 4 /* RW - (void *) */ + +struct smb_tran_ops; + +struct smb_tran_desc { + sa_family_t tr_type; + int (*tr_create)(struct smb_vc *vcp, struct proc *p); + int (*tr_done)(struct smb_vc *vcp, struct proc *p); + int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p); + int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p); + int (*tr_disconnect)(struct smb_vc *vcp, struct proc *p); + int (*tr_send)(struct smb_vc *vcp, struct mbuf *m0, struct proc *p); + int (*tr_recv)(struct smb_vc *vcp, struct mbuf **mpp, struct proc *p); + void (*tr_timo)(struct smb_vc *vcp); + void (*tr_intr)(struct smb_vc *vcp); + int (*tr_getparam)(struct smb_vc *vcp, int param, void *data); + int (*tr_setparam)(struct smb_vc *vcp, int param, void *data); + int (*tr_fatal)(struct smb_vc *vcp, int error); +#ifdef notyet + int (*tr_poll)(struct smb_vc *vcp, struct proc *p); + int (*tr_cmpaddr)(void *addr1, void *addr2); +#endif + LIST_ENTRY(smb_tran_desc) tr_link; +}; + +#define SMB_TRAN_CREATE(vcp,p) (vcp)->vc_tdesc->tr_create(vcp,p) +#define SMB_TRAN_DONE(vcp,p) (vcp)->vc_tdesc->tr_done(vcp,p) +#define SMB_TRAN_BIND(vcp,sap,p) (vcp)->vc_tdesc->tr_bind(vcp,sap,p) +#define SMB_TRAN_CONNECT(vcp,sap,p) (vcp)->vc_tdesc->tr_connect(vcp,sap,p) +#define SMB_TRAN_DISCONNECT(vcp,p) (vcp)->vc_tdesc->tr_disconnect(vcp,p) +#define SMB_TRAN_SEND(vcp,m0,p) (vcp)->vc_tdesc->tr_send(vcp,m0,p) +#define SMB_TRAN_RECV(vcp,m,p) (vcp)->vc_tdesc->tr_recv(vcp,m,p) +#define SMB_TRAN_TIMO(vcp) (vcp)->vc_tdesc->tr_timo(vcp) +#define SMB_TRAN_INTR(vcp) (vcp)->vc_tdesc->tr_intr(vcp) +#define SMB_TRAN_GETPARAM(vcp,par,data) (vcp)->vc_tdesc->tr_getparam(vcp, par, data) +#define SMB_TRAN_SETPARAM(vcp,par,data) (vcp)->vc_tdesc->tr_setparam(vcp, par, data) +#define SMB_TRAN_FATAL(vcp, error) (vcp)->vc_tdesc->tr_fatal(vcp, error) + +#endif /* _NETSMB_SMB_TRAN_H_ */ diff --git a/sys/netsmb/smb_trantcp.c b/sys/netsmb/smb_trantcp.c new file mode 100644 index 0000000..c9d0622 --- /dev/null +++ b/sys/netsmb/smb_trantcp.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/poll.h> +#include <sys/uio.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <sys/mchain.h> + +#include <netsmb/netbios.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_trantcp.h> +#include <netsmb/smb_subr.h> + +#define M_NBDATA M_PCB + +static int smb_tcpsndbuf = 10 * 1024; +static int smb_tcprcvbuf = 10 * 1024; + +SYSCTL_DECL(_net_smb); +SYSCTL_INT(_net_smb, OID_AUTO, tcpsndbuf, CTLFLAG_RW, &smb_tcpsndbuf, 0, ""); +SYSCTL_INT(_net_smb, OID_AUTO, tcprcvbuf, CTLFLAG_RW, &smb_tcprcvbuf, 0, ""); + +#define nb_sosend(so,m,flags,p) (so)->so_proto->pr_usrreqs->pru_sosend( \ + so, NULL, 0, m, 0, flags, p) + +static int nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp, + u_int8_t *rpcodep, struct proc *p); +static int smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p); + +static int +nb_setsockopt_int(struct socket *so, int level, int name, int val) +{ + struct sockopt sopt; + + bzero(&sopt, sizeof(sopt)); + sopt.sopt_level = level; + sopt.sopt_name = name; + sopt.sopt_val = &val; + sopt.sopt_valsize = sizeof(val); + return sosetopt(so, &sopt); +} + +static __inline int +nb_poll(struct nbpcb *nbp, int events, struct proc *p) +{ + return nbp->nbp_tso->so_proto->pr_usrreqs->pru_sopoll(nbp->nbp_tso, + events, NULL, p); +} + +static int +nbssn_rselect(struct nbpcb *nbp, struct timeval *tv, int events, struct proc *p) +{ + struct timeval atv, rtv, ttv; + int s, timo, error; + + if (tv) { + atv = *tv; + if (itimerfix(&atv)) { + error = EINVAL; + goto done; + } + getmicrouptime(&rtv); + timevaladd(&atv, &rtv); + } + timo = 0; +retry: + p->p_flag |= P_SELECT; + error = nb_poll(nbp, events, p); + if (error) { + error = 0; + goto done; + } + if (tv) { + getmicrouptime(&rtv); + if (timevalcmp(&rtv, &atv, >=)) + goto done; + ttv = atv; + timevalsub(&ttv, &rtv); + timo = tvtohz(&ttv); + } + s = splhigh(); + if ((p->p_flag & P_SELECT) == 0) { + splx(s); + goto retry; + } + p->p_flag &= ~P_SELECT; + error = tsleep((caddr_t)&selwait, PSOCK, "nbsel", timo); + splx(s); +done: + p->p_flag &= ~P_SELECT; + if (error == ERESTART) + return 0; + return error; +} + +static int +nb_intr(struct nbpcb *nbp, struct proc *p) +{ + return 0; +} + +static void +nb_upcall(struct socket *so, void *arg, int waitflag) +{ + struct nbpcb *nbp = arg; + + if (arg == NULL || nbp->nbp_selectid == NULL) + return; + wakeup(nbp->nbp_selectid); +} + +static int +nb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len) +{ + u_int32_t *p = mtod(m, u_int32_t *); + + *p = htonl((len & 0x1FFFF) | (type << 24)); + return 0; +} + +static int +nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb) +{ + int error; + u_char seglen, *cp; + + cp = snb->snb_name; + if (*cp == 0) + return EINVAL; + NBDEBUG("[%s]\n", cp); + for (;;) { + seglen = (*cp) + 1; + error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM); + if (error) + return error; + if (seglen == 1) + break; + cp += seglen; + } + return 0; +} + +static int +nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct proc *p) +{ + struct socket *so; + int error, s; + + error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP, p); + if (error) + return error; + nbp->nbp_tso = so; + so->so_upcallarg = (caddr_t)nbp; + so->so_upcall = nb_upcall; + so->so_rcv.sb_flags |= SB_UPCALL; + so->so_rcv.sb_timeo = (5 * hz); + so->so_snd.sb_timeo = (5 * hz); + error = soreserve(so, nbp->nbp_sndbuf, nbp->nbp_rcvbuf); + if (error) + goto bad; + nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1); + nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1); + so->so_rcv.sb_flags &= ~SB_NOINTR; + so->so_snd.sb_flags &= ~SB_NOINTR; + error = soconnect(so, (struct sockaddr*)to, p); + if (error) + goto bad; + s = splnet(); + while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { + tsleep(&so->so_timeo, PSOCK, "nbcon", 2 * hz); + if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 && + (error = nb_intr(nbp, p)) != 0) { + so->so_state &= ~SS_ISCONNECTING; + splx(s); + goto bad; + } + } + if (so->so_error) { + error = so->so_error; + so->so_error = 0; + splx(s); + goto bad; + } + splx(s); + return 0; +bad: + smb_nbst_disconnect(nbp->nbp_vc, p); + return error; +} + +static int +nbssn_rq_request(struct nbpcb *nbp, struct proc *p) +{ + struct mbchain mb, *mbp = &mb; + struct mdchain md, *mdp = &md; + struct mbuf *m0; + struct timeval tv; + struct sockaddr_in sin; + u_short port; + u_int8_t rpcode; + int error, rplen; + + error = mb_init(mbp); + if (error) + return error; + mb_put_uint32le(mbp, 0); + nb_put_name(mbp, nbp->nbp_paddr); + nb_put_name(mbp, nbp->nbp_laddr); + nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4); + error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, p); + if (!error) { + nbp->nbp_state = NBST_RQSENT; + } + mb_detach(mbp); + mb_done(mbp); + if (error) + return error; + TIMESPEC_TO_TIMEVAL(&tv, &nbp->nbp_timo); + error = nbssn_rselect(nbp, &tv, POLLIN, p); + if (error == EWOULDBLOCK) { /* Timeout */ + NBDEBUG("initial request timeout\n"); + return ETIMEDOUT; + } + if (error) /* restart or interrupt */ + return error; + error = nbssn_recv(nbp, &m0, &rplen, &rpcode, p); + if (error) { + NBDEBUG("recv() error %d\n", error); + return error; + } + /* + * Process NETBIOS reply + */ + if (m0) + md_initm(mdp, m0); + error = 0; + do { + if (rpcode == NB_SSN_POSRESP) { + nbp->nbp_state = NBST_SESSION; + nbp->nbp_flags |= NBF_CONNECTED; + break; + } + if (rpcode != NB_SSN_RTGRESP) { + error = ECONNABORTED; + break; + } + if (rplen != 6) { + error = ECONNABORTED; + break; + } + md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM); + md_get_uint16(mdp, &port); + sin.sin_port = port; + nbp->nbp_state = NBST_RETARGET; + smb_nbst_disconnect(nbp->nbp_vc, p); + error = nb_connect_in(nbp, &sin, p); + if (!error) + error = nbssn_rq_request(nbp, p); + if (error) { + smb_nbst_disconnect(nbp->nbp_vc, p); + break; + } + } while(0); + if (m0) + md_done(mdp); + return error; +} + +static int +nbssn_recvhdr(struct nbpcb *nbp, int *lenp, + u_int8_t *rpcodep, int flags, struct proc *p) +{ + struct socket *so = nbp->nbp_tso; + struct uio auio; + struct iovec aio; + u_int32_t len; + int error; + + aio.iov_base = (caddr_t)&len; + aio.iov_len = sizeof(len); + auio.uio_iov = &aio; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; + auio.uio_resid = sizeof(len); + auio.uio_procp = p; + error = so->so_proto->pr_usrreqs->pru_soreceive + (so, (struct sockaddr **)NULL, &auio, + (struct mbuf **)NULL, (struct mbuf **)NULL, &flags); + if (error) + return error; + if (auio.uio_resid > 0) { + SMBSDEBUG("short reply\n"); + return EPIPE; + } + len = ntohl(len); + *rpcodep = (len >> 24) & 0xFF; + len &= 0x1ffff; + if (len > SMB_MAXPKTLEN) { + SMBERROR("packet too long (%d)\n", len); + return EFBIG; + } + *lenp = len; + return 0; +} + +static int +nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp, + u_int8_t *rpcodep, struct proc *p) +{ + struct socket *so = nbp->nbp_tso; + struct uio auio; + struct mbuf *m; + u_int8_t rpcode; + int len; + int error, rcvflg; + + if (so == NULL) + return ENOTCONN; + + if (mpp) + *mpp = NULL; + for(;;) { + m = NULL; + error = nbssn_recvhdr(nbp, &len, &rpcode, MSG_DONTWAIT, p); + if (so->so_state & + (SS_ISDISCONNECTING | SS_ISDISCONNECTED | SS_CANTRCVMORE)) { + nbp->nbp_state = NBST_CLOSED; + NBDEBUG("session closed by peer\n"); + return ECONNRESET; + } + if (error) + return error; + if (len == 0 && nbp->nbp_state != NBST_SESSION) + break; + if (rpcode == NB_SSN_KEEPALIVE) + continue; + bzero(&auio, sizeof(auio)); + auio.uio_resid = len; + auio.uio_procp = p; + do { + rcvflg = MSG_WAITALL; + error = so->so_proto->pr_usrreqs->pru_soreceive + (so, (struct sockaddr **)NULL, + &auio, &m, (struct mbuf **)NULL, &rcvflg); + } while (error == EWOULDBLOCK || error == EINTR || + error == ERESTART); + if (error) + break; + if (auio.uio_resid > 0) { + SMBERROR("packet is shorter than expected\n"); + error = EPIPE; + break; + } + if (nbp->nbp_state == NBST_SESSION && + rpcode == NB_SSN_MESSAGE) + break; + NBDEBUG("non-session packet %x\n", rpcode); + if (m) + m_freem(m); + } + if (error) { + if (m) + m_freem(m); + return error; + } + if (mpp) + *mpp = m; + else + m_freem(m); + *lenp = len; + *rpcodep = rpcode; + return 0; +} + +/* + * SMB transport interface + */ +static int +smb_nbst_create(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp; + + MALLOC(nbp, struct nbpcb *, sizeof *nbp, M_NBDATA, M_WAITOK); + bzero(nbp, sizeof *nbp); + nbp->nbp_timo.tv_sec = 15; /* XXX: sysctl ? */ + nbp->nbp_state = NBST_CLOSED; + nbp->nbp_vc = vcp; + nbp->nbp_sndbuf = smb_tcpsndbuf; + nbp->nbp_rcvbuf = smb_tcprcvbuf; + vcp->vc_tdata = nbp; + return 0; +} + +static int +smb_nbst_done(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + if (nbp == NULL) + return ENOTCONN; + smb_nbst_disconnect(vcp, p); + if (nbp->nbp_laddr) + free(nbp->nbp_laddr, M_SONAME); + if (nbp->nbp_paddr) + free(nbp->nbp_paddr, M_SONAME); + free(nbp, M_NBDATA); + return 0; +} + +static int +smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct sockaddr_nb *snb; + int error, slen; + + NBDEBUG("\n"); + error = EINVAL; + do { + if (nbp->nbp_flags & NBF_LOCADDR) + break; + /* + * It is possible to create NETBIOS name in the kernel, + * but nothing prevents us to do it in the user space. + */ + if (sap == NULL) + break; + slen = sap->sa_len; + if (slen < NB_MINSALEN) + break; + snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1); + if (snb == NULL) { + error = ENOMEM; + break; + } + nbp->nbp_laddr = snb; + nbp->nbp_flags |= NBF_LOCADDR; + error = 0; + } while(0); + return error; +} + +static int +smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct sockaddr_in sin; + struct sockaddr_nb *snb; + struct timespec ts1, ts2; + int error, slen; + + NBDEBUG("\n"); + if (nbp->nbp_tso != NULL) + return EISCONN; + if (nbp->nbp_laddr == NULL) + return EINVAL; + slen = sap->sa_len; + if (slen < NB_MINSALEN) + return EINVAL; + if (nbp->nbp_paddr) { + free(nbp->nbp_paddr, M_SONAME); + nbp->nbp_paddr = NULL; + } + snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1); + if (snb == NULL) + return ENOMEM; + nbp->nbp_paddr = snb; + sin = snb->snb_addrin; + getnanotime(&ts1); + error = nb_connect_in(nbp, &sin, p); + if (error) + return error; + getnanotime(&ts2); + timespecsub(&ts2, &ts1); + if (ts2.tv_sec == 0 && ts2.tv_sec == 0) + ts2.tv_sec = 1; + nbp->nbp_timo = ts2; + timespecadd(&nbp->nbp_timo, &ts2); + timespecadd(&nbp->nbp_timo, &ts2); + timespecadd(&nbp->nbp_timo, &ts2); /* * 4 */ + error = nbssn_rq_request(nbp, p); + if (error) + smb_nbst_disconnect(vcp, p); + return error; +} + +static int +smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct socket *so; + + if (nbp == NULL || nbp->nbp_tso == NULL) + return ENOTCONN; + if ((so = nbp->nbp_tso) != NULL) { + nbp->nbp_flags &= ~NBF_CONNECTED; + nbp->nbp_tso = (struct socket *)NULL; + soshutdown(so, 2); + soclose(so); + } + if (nbp->nbp_state != NBST_RETARGET) { + nbp->nbp_state = NBST_CLOSED; + } + return 0; +} + +static int +smb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + int error; + + if (nbp->nbp_state != NBST_SESSION) { + error = ENOTCONN; + goto abort; + } + M_PREPEND(m0, 4, M_WAITOK); + if (m0 == NULL) + return ENOBUFS; + nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4); + error = nb_sosend(nbp->nbp_tso, m0, 0, p); + return error; +abort: + if (m0) + m_freem(m0); + return error; +} + + +static int +smb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + u_int8_t rpcode; + int error, rplen; + + nbp->nbp_flags |= NBF_RECVLOCK; + error = nbssn_recv(nbp, mpp, &rplen, &rpcode, p); + nbp->nbp_flags &= ~NBF_RECVLOCK; + return error; +} + +static void +smb_nbst_timo(struct smb_vc *vcp) +{ + return; +} + +static void +smb_nbst_intr(struct smb_vc *vcp) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + if (nbp == NULL || nbp->nbp_tso == NULL) + return; + sorwakeup(nbp->nbp_tso); + sowwakeup(nbp->nbp_tso); +} + +static int +smb_nbst_getparam(struct smb_vc *vcp, int param, void *data) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + switch (param) { + case SMBTP_SNDSZ: + *(int*)data = nbp->nbp_sndbuf; + break; + case SMBTP_RCVSZ: + *(int*)data = nbp->nbp_rcvbuf; + break; + case SMBTP_TIMEOUT: + *(struct timespec*)data = nbp->nbp_timo; + break; + default: + return EINVAL; + } + return 0; +} + +static int +smb_nbst_setparam(struct smb_vc *vcp, int param, void *data) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + switch (param) { + case SMBTP_SELECTID: + nbp->nbp_selectid = data; + break; + default: + return EINVAL; + } + return 0; +} + +/* + * Check for fatal errors + */ +static int +smb_nbst_fatal(struct smb_vc *vcp, int error) +{ + switch (error) { + case ENOTCONN: + case ENETRESET: + case ECONNABORTED: + return 1; + } + return 0; +} + + +struct smb_tran_desc smb_tran_nbtcp_desc = { + SMBT_NBTCP, + smb_nbst_create, smb_nbst_done, + smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect, + smb_nbst_send, smb_nbst_recv, + smb_nbst_timo, smb_nbst_intr, + smb_nbst_getparam, smb_nbst_setparam, + smb_nbst_fatal +}; + diff --git a/sys/netsmb/smb_trantcp.h b/sys/netsmb/smb_trantcp.h new file mode 100644 index 0000000..6c625fa --- /dev/null +++ b/sys/netsmb/smb_trantcp.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _NETSMB_SMB_TRANTCP_H_ +#define _NETSMB_SMB_TRANTCP_H_ + +#ifdef _KERNEL + +#ifdef NB_DEBUG +#define NBDEBUG(format, args...) printf("%s(%d): "format, \ + __FUNCTION__ , __LINE__ ,## args) +#else +#define NBDEBUG(format, args...) +#endif + +enum nbstate { + NBST_CLOSED, + NBST_RQSENT, + NBST_SESSION, + NBST_RETARGET, + NBST_REFUSED +}; + + +/* + * socket specific data + */ +struct nbpcb { + struct smb_vc * nbp_vc; + struct socket * nbp_tso; /* transport socket */ + struct sockaddr_nb *nbp_laddr; /* local address */ + struct sockaddr_nb *nbp_paddr; /* peer address */ + + int nbp_flags; +#define NBF_LOCADDR 0x0001 /* has local addr */ +#define NBF_CONNECTED 0x0002 +#define NBF_RECVLOCK 0x0004 + + enum nbstate nbp_state; + struct timespec nbp_timo; + int nbp_sndbuf; + int nbp_rcvbuf; + void * nbp_selectid; + +/* LIST_ENTRY(nbpcb) nbp_link;*/ +}; + +/* + * Nominal space allocated per a NETBIOS socket. + */ +#define NB_SNDQ (10 * 1024) +#define NB_RCVQ (20 * 1024) + +extern struct smb_tran_desc smb_tran_nbtcp_desc; + +#endif /* _KERNEL */ + +#endif /* !_NETSMB_SMB_TRANTCP_H_ */ diff --git a/sys/netsmb/smb_usr.c b/sys/netsmb/smb_usr.c new file mode 100644 index 0000000..2759173 --- /dev/null +++ b/sys/netsmb/smb_usr.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <sys/iconv.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> + +/* + * helpers for nsmb device. Can be moved to the smb_dev.c file. + */ +static void smb_usr_vcspec_free(struct smb_vcspec *spec); + +static int +smb_usr_vc2spec(struct smbioc_ossn *dp, struct smb_vcspec *spec) +{ + int flags = 0; + + bzero(spec, sizeof(*spec)); + if (dp->ioc_user[0] == 0) + return EINVAL; + if (dp->ioc_server == NULL) + return EINVAL; + if (dp->ioc_localcs[0] == 0) { + SMBERROR("no local charset ?\n"); + return EINVAL; + } + + spec->sap = smb_memdupin(dp->ioc_server, dp->ioc_svlen); + if (spec->sap == NULL) + return ENOMEM; + if (dp->ioc_local) { + spec->lap = smb_memdupin(dp->ioc_local, dp->ioc_lolen); + if (spec->lap == NULL) { + smb_usr_vcspec_free(spec); + return ENOMEM; + } + } + spec->srvname = dp->ioc_srvname; + spec->pass = dp->ioc_password; + spec->domain = dp->ioc_workgroup; + spec->username = dp->ioc_user; + spec->mode = dp->ioc_mode; + spec->rights = dp->ioc_rights; + spec->owner = dp->ioc_owner; + spec->group = dp->ioc_group; + spec->localcs = dp->ioc_localcs; + spec->servercs = dp->ioc_servercs; + if (dp->ioc_opt & SMBVOPT_PRIVATE) + flags |= SMBV_PRIVATE; + if (dp->ioc_opt & SMBVOPT_SINGLESHARE) + flags |= SMBV_PRIVATE | SMBV_SINGLESHARE; + spec->flags = flags; + return 0; +} + +static void +smb_usr_vcspec_free(struct smb_vcspec *spec) +{ + if (spec->sap) + smb_memfree(spec->sap); + if (spec->lap) + smb_memfree(spec->lap); +} + +static int +smb_usr_share2spec(struct smbioc_oshare *dp, struct smb_sharespec *spec) +{ + bzero(spec, sizeof(*spec)); + spec->mode = dp->ioc_mode; + spec->rights = dp->ioc_rights; + spec->owner = dp->ioc_owner; + spec->group = dp->ioc_group; + spec->name = dp->ioc_share; + spec->stype = dp->ioc_stype; + spec->pass = dp->ioc_password; + return 0; +} + +int +smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc **vcpp, struct smb_share **sspp) +{ + struct smb_vc *vcp = NULL; + struct smb_vcspec vspec; + struct smb_sharespec sspec, *sspecp = NULL; + int error; + + if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) + return EINVAL; + error = smb_usr_vc2spec(&dp->ioc_ssn, &vspec); + if (error) + return error; + if (dp->ioc_flags & SMBLK_CREATE) + vspec.flags |= SMBV_CREATE; + + if (dp->ioc_level >= SMBL_SHARE) { + error = smb_usr_share2spec(&dp->ioc_sh, &sspec); + if (error) + goto out; + sspecp = &sspec; + } + error = smb_sm_lookup(&vspec, sspecp, scred, &vcp); + if (error == 0) { + *vcpp = vcp; + *sspp = vspec.ssp; + } +out: + smb_usr_vcspec_free(&vspec); + return error; +} + +/* + * Connect to the resource specified by smbioc_ossn structure. + * It may either find an existing connection or try to establish a new one. + * If no errors occured smb_vc returned locked and referenced. + */ +int +smb_usr_opensession(struct smbioc_ossn *dp, struct smb_cred *scred, + struct smb_vc **vcpp) +{ + struct smb_vc *vcp = NULL; + struct smb_vcspec vspec; + int error; + + error = smb_usr_vc2spec(dp, &vspec); + if (error) + return error; + if (dp->ioc_opt & SMBVOPT_CREATE) + vspec.flags |= SMBV_CREATE; + + error = smb_sm_lookup(&vspec, NULL, scred, &vcp); + smb_usr_vcspec_free(&vspec); + return error; +} + +int +smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *dp, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct smb_share *ssp; + struct smb_sharespec shspec; + int error; + + error = smb_usr_share2spec(dp, &shspec); + if (error) + return error; + error = smb_vc_lookupshare(vcp, &shspec, scred, &ssp); + if (error == 0) { + *sspp = ssp; + return 0; + } + if ((dp->ioc_opt & SMBSOPT_CREATE) == 0) + return error; + error = smb_share_create(vcp, &shspec, scred, &ssp); + if (error) + return error; + error = smb_smb_treeconnect(ssp, scred); + if (error) { + smb_share_put(ssp, scred); + } else + *sspp = ssp; + return error; +} + +int +smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + u_int16_t bc; + int error; + + switch (dp->ioc_cmd) { + case SMB_COM_TRANSACTION2: + case SMB_COM_TRANSACTION2_SECONDARY: + case SMB_COM_CLOSE_AND_TREE_DISC: + case SMB_COM_TREE_CONNECT: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_NEGOTIATE: + case SMB_COM_SESSION_SETUP_ANDX: + case SMB_COM_LOGOFF_ANDX: + case SMB_COM_TREE_CONNECT_ANDX: + return EPERM; + } + error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + error = mb_put_mem(mbp, dp->ioc_twords, dp->ioc_twc * 2, MB_MUSER); + if (error) + goto bad; + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + error = mb_put_mem(mbp, dp->ioc_tbytes, dp->ioc_tbc, MB_MUSER); + if (error) + goto bad; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + goto bad; + mdp = &rqp->sr_rp; + md_get_uint8(mdp, &wc); + dp->ioc_rwc = wc; + wc *= 2; + if (wc > dp->ioc_rpbufsz) { + error = EBADRPC; + goto bad; + } + error = md_get_mem(mdp, dp->ioc_rpbuf, wc, MB_MUSER); + if (error) + goto bad; + md_get_uint16le(mdp, &bc); + if ((wc + bc) > dp->ioc_rpbufsz) { + error = EBADRPC; + goto bad; + } + dp->ioc_rbc = bc; + error = md_get_mem(mdp, dp->ioc_rpbuf + wc, bc, MB_MUSER); +bad: + dp->ioc_errclass = rqp->sr_errclass; + dp->ioc_serror = rqp->sr_serror; + dp->ioc_error = rqp->sr_error; + smb_rq_done(rqp); + return error; + +} + +static int +smb_cpdatain(struct mbchain *mbp, int len, caddr_t data) +{ + int error; + + if (len == 0) + return 0; + error = mb_init(mbp); + if (error) + return error; + return mb_put_mem(mbp, data, len, MB_MUSER); +} + +int +smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *dp, + struct smb_cred *scred) +{ + struct smb_t2rq t2, *t2p = &t2; + struct mdchain *mdp; + int error, len; + + if (dp->ioc_tparamcnt > 0xffff || dp->ioc_tdatacnt > 0xffff || + dp->ioc_setupcnt > 3) + return EINVAL; + error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup[0], scred); + if (error) + return error; + len = t2p->t2_setupcount = dp->ioc_setupcnt; + if (len > 1) + t2p->t2_setupdata = dp->ioc_setup; + if (dp->ioc_name) { + t2p->t_name = smb_strdupin(dp->ioc_name, 128); + if (t2p->t_name == NULL) { + error = ENOMEM; + goto bad; + } + } + t2p->t2_maxscount = 0; + t2p->t2_maxpcount = dp->ioc_rparamcnt; + t2p->t2_maxdcount = dp->ioc_rdatacnt; + error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt, dp->ioc_tparam); + if (error) + goto bad; + error = smb_cpdatain(&t2p->t2_tdata, dp->ioc_tdatacnt, dp->ioc_tdata); + if (error) + goto bad; + error = smb_t2_request(t2p); + if (error) + goto bad; + mdp = &t2p->t2_rparam; + if (mdp->md_top) { + len = m_fixhdr(mdp->md_top); + if (len > dp->ioc_rparamcnt) { + error = EMSGSIZE; + goto bad; + } + dp->ioc_rparamcnt = len; + error = md_get_mem(mdp, dp->ioc_rparam, len, MB_MUSER); + if (error) + goto bad; + } else + dp->ioc_rparamcnt = 0; + mdp = &t2p->t2_rdata; + if (mdp->md_top) { + len = m_fixhdr(mdp->md_top); + if (len > dp->ioc_rdatacnt) { + error = EMSGSIZE; + goto bad; + } + dp->ioc_rdatacnt = len; + error = md_get_mem(mdp, dp->ioc_rdata, len, MB_MUSER); + } else + dp->ioc_rdatacnt = 0; +bad: + if (t2p->t_name) + smb_strfree(t2p->t_name); + smb_t2_done(t2p); + return error; +} |