From f3cf2ac33838a0a34500b2104ca381a62ac606d6 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 20 Aug 2014 14:00:02 -0400 Subject: [PATCH] preliminary smb support --- main.c | 4 + makefile.mk | 3 +- prototypes.h | 1 + smb.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++ smb.h | 168 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 smb.c create mode 100644 smb.h diff --git a/main.c b/main.c index 81158bf..d532854 100644 --- a/main.c +++ b/main.c @@ -277,6 +277,10 @@ int main(int argc, char **argv) { do_http(url, &components); } + else if (components.schemeType == SCHEME_SMB) + { + do_smb(url, &components); + } else { fprintf(stderr, "Unsupported scheme.\n"); diff --git a/makefile.mk b/makefile.mk index 03f1b90..52871d5 100644 --- a/makefile.mk +++ b/makefile.mk @@ -1,7 +1,7 @@ CFLAGS += $(DEFINES) -v -w OBJS = main.o gopher.o url.o connection.o readline2.o scheme.o ftype.o \ mime.o setftype.o s16debug.o common.o http.o http.utils.o \ - dictionary.o options.o time.o + dictionary.o options.o time.o smb.o gopher: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ @@ -28,6 +28,7 @@ options.o: options.c options.h gopher.o: gopher.c url.h connection.h options.h http.o: http.c url.h connection.h options.h http.utils.o: http.utils.c +smb.o: smb.c smb.h url.h connection.h options.h data.o: data.c data.h dictionary.o: dictionary.c dictionary.h diff --git a/prototypes.h b/prototypes.h index 637dfcb..8acfa39 100644 --- a/prototypes.h +++ b/prototypes.h @@ -55,6 +55,7 @@ int CloseLoop(Connection *connection); #ifdef __url_h__ int do_gopher(const char *url, URLComponents *components); int do_http(const char *url, URLComponents *components); +int do_smb(const char *url, URLComponents *components); #endif #ifdef __TYPES__ diff --git a/smb.c b/smb.c new file mode 100644 index 0000000..23b12fa --- /dev/null +++ b/smb.c @@ -0,0 +1,201 @@ +#pragma optimize 79 +#pragma noroot + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include + +#include "url.h" +#include "connection.h" +#include "readline2.h" +#include "options.h" +#include "s16debug.h" + +#include "prototypes.h" +#include "smb.h" + +static struct smb2_header_sync header; + + +Handle read_response(Word ipid) +{ + static srBuff sr; + static rrBuff rb; + + // read an smb response. Check the first 4 bytes for the message length. + uint32_t size = 0; + uint8_t nbthead[4]; + LongWord qtick; + Word terr; + + qtick = GetTick() + 30 * 60; + for(;;) + { + + + TCPIPPoll(); + + terr = TCPIPStatusTCP(ipid, &sr); + + if (sr.srRcvQueued >= 4) break; + + + /* + * the only reasonable error is if the connection is closing. + * that's ok if there's still pending data (handled above) + */ + if (terr) return (Handle)0; + + + if (GetTick() >= qtick) + { + fprintf(stderr, "Read timed out.\n"); + return (Handle)0; + } + + } + + terr = TCPIPReadTCP(ipid, 0, (Ref)&nbthead, 4, &rb); + + if (nbthead[0] != 0) return (Handle)0; + size = nbthead[3]; + size |= nbthead[2] << 8; + size |= nbthead[1] << 16; + + for(;;) + { + + + TCPIPPoll(); + + terr = TCPIPStatusTCP(ipid, &sr); + + if (sr.srRcvQueued >= size) break; + + if (GetTick() >= qtick) + { + fprintf(stderr, "Read timed out.\n"); + return (Handle)0; + } + + } + terr = TCPIPReadTCP(ipid, 2, (Ref)0, size, &rb); + + return rb.rrBuffHandle; + +} + +int negotiate(Word ipid) +{ + static struct smb2_negotiate_request req; + + static uint16_t dialects[] = { 0x0202 }; + + uint8_t nbthead[4]; + uint32_t size = 0; + + + + memset(&header, 0, sizeof(header)); + memset(&req, 0, sizeof(req)); + + + header.protocol_id = SMB2_MAGIC; // '\xfeSMB'; + header.structure_size = 64; + header.command = SMB2_NEGOTIATE; + + req.structure_size = 36; + req.dialect_count = 1; // ? + req.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; + req.capabilities = 0; + + //req.dialects[0] = 0x202; // smb 2.002 + + + // http://support.microsoft.com/kb/204279 + size = sizeof(header) + sizeof(req) + sizeof(dialects); + nbthead[0] = 0; + nbthead[1] = size >> 16; + nbthead[2] = size >> 8; + nbthead[3] = size; + + TCPIPWriteTCP(ipid, (dataPtr)nbthead, sizeof(nbthead), false, false); + TCPIPWriteTCP(ipid, (dataPtr)&header, sizeof(header), false, false); + TCPIPWriteTCP(ipid, (dataPtr)&req, sizeof(req), false, false); + TCPIPWriteTCP(ipid, (dataPtr)&dialects, sizeof(dialects), true, false); + // push. + + // read a response... + + return 0; +} + + +int do_smb(char *url, URLComponents *components) +{ + static Connection connection; + LongWord qtick; + + char *host; + Word err; + Word terr; + Word ok; + FILE *file; + + if (!components->portNumber) components->portNumber = 445; + + host = URLComponentGetCMalloc(url, components, URLComponentHost); + + if (!host) + { + fprintf(stderr, "URL `%s': no host.", url); + return -1; + } + + ok = ConnectLoop(host, components->portNumber, &connection); + + if (!ok) + { + free(host); + if (file != stdout) fclose(file); + return -1; + } + + ok = negotiate(connection.ipid); + if (ok) return ok; + + + qtick = GetTick() + 30 * 60; + for(;;) + { + static srBuff sr; + + TCPIPPoll(); + + terr = TCPIPStatusTCP(connection.ipid, &sr); + + if (sr.srRcvQueued > 0) break; + + + if (GetTick() >= qtick) + { + fprintf(stderr, "Read timed out.\n"); + return -1; + } + + } + + CloseLoop(&connection); + + free(host); + return 0; +} diff --git a/smb.h b/smb.h new file mode 100644 index 0000000..98fd71a --- /dev/null +++ b/smb.h @@ -0,0 +1,168 @@ +#ifndef __SMB_H__ +#define __SMB_H__ + +#include + +#define SMB2_MAGIC 0x424d53fe + +// commands +enum { + SMB2_NEGOTIATE = 0x0000, + SMB2_SESSION_SETUP = 0x0001, + SMB2_LOGOFF = 0x0002, + SMB2_TREE_CONNECT = 0x0003, + SMB2_TREE_DISCONNECT = 0x0004, + SMB2_CREATE = 0x0005, + SMB2_CLOSE = 0x0006, + SMB2_FLUSH = 0x0007, + SMB2_READ = 0x0008, + SMB2_WRITE = 0x0009, + SMB2_LOCK = 0x000A, + SMB2_IOCTL = 0x000B, + SMB2_CANCEL = 0x000C, + SMB2_ECHO = 0x000D, + SMB2_QUERY_DIRECTORY = 0x000E, + SMB2_CHANGE_NOTIFY = 0x000F, + SMB2_QUERY_INFO = 0x0010, + SMB2_SET_INFO = 0x0011, + SMB2_OPLOCK_BREAK = 0x0012 +}; + +// flags +enum { + SMB2_FLAGS_SERVER_TO_REDIR = 0x00000001, + SMB2_FLAGS_ASYNC_COMMAND = 0x00000002, + SMB2_FLAGS_RELATED_OPERATIONS = 0x00000004, + SMB2_FLAGS_SIGNED = 0x00000008 + // integer overflow + /* + SMB2_FLAGS_DFS_OPERATIONS = 0x10000000, + SMB2_FLAGS_REPLAY_OPERATION = 0x20000000 + */ +}; + +#define SMB2_FLAGS_DFS_OPERATIONS 0x10000000 +#define SMB2_FLAGS_REPLAY_OPERATION 0x20000000 + +// symlink error response flags. +enum { + + SYMLINK_FLAG_RELATIVE = 0x00000001 +}; + +// negotiate flags +enum { + SMB2_NEGOTIATE_SIGNING_ENABLED = 0x0001, + SMB2_NEGOTIATE_SIGNING_REQUIRED = 0x0002, + + SMB2_GLOBAL_CAP_DFS = 0x00000001, + SMB2_GLOBAL_CAP_LEASING = 0x00000002, + SMB2_GLOBAL_CAP_LARGE_MTU = 0x00000004, + SMB2_GLOBAL_CAP_MULTI_CHANNEL = 0x00000008, + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES = 0x00000010, + SMB2_GLOBAL_CAP_DIRECTORY_LEASING = 0x00000020, + SMB2_GLOBAL_CAP_ENCRYPTION = 0x00000040 +}; + +// session setup flags +enum { + SMB2_SESSION_FLAG_BINDING = 0x01 +}; + +// session setup response flags +enum { + SMB2_SESSION_FLAG_IS_GUEST = 0x0001, + SMB2_SESSION_FLAG_IS_NULL = 0x0002, + SMB2_SESSION_FLAG_ENCRYPT_DATA = 0x0004 +}; + +typedef struct smb2_header_sync { + + uint32_t protocol_id; + uint16_t structure_size; + uint16_t credit_charge; + uint32_t status; + uint16_t command; + uint16_t credit; // _request / _response + uint32_t flags; + uint32_t next_command; + uint32_t message_id[2]; // uint64_t + uint32_t reserved; + uint32_t tree_id; + uint32_t session_id[2]; // uint64_t + uint8_t signature[16]; + +} smb2_header_sync; + +typedef struct smb2_error_response { + uint16_t structure_size; + uint16_t reserved; + uint32_t bytecount; + //uint8_t error_data[1]; // variable. +} smb2_error_response; + +typedef struct smb2_negotiate_request { + uint16_t structure_size; + uint16_t dialect_count; + uint16_t security_mode; + uint16_t reserved; + uint32_t capabilities; + uint8_t client_guid[16]; + uint32_t client_start_time[2]; + //uint16_t dialects[1]; // variable sized. + +} smb2_negotiate_request; + + + +typedef struct smb2_negotiate_response { + uint16_t structure_size; + uint16_t security_mode; + uint16_t dialect_revision; + uint16_t reserved; + uint8_t server_guid[16]; + uint32_t capabilities; + uint32_t max_transact_size; + uint32_t max_read_size; + uint32_t max_write_size; + uint32_t server_start_time[2]; + uint16_t security_buffer_offset; + uint16_t security_buffer_length; + uint32_t reserved2; + //uint8_t buffer[1]; // variable +} smb2_negotiate_response; + + +typedef struct smb2_session_setup_request { + uint16_t structure_size; + uint8_t flags; + uint8_t security_mode; + uint32_t capabilities; + uint32_t channel; + uint16_t security_buffer_offset; + uint16_t security_buffer_length; + uint32_t previous_session_id[2]; + //uint8t_t buffer[1]; // variable + +} smb2_sessions_setup_request; + + +typedef struct smb2_session_setup_response { + uint16_t structure_size; + uint16_t session_flags; + uint16_t security_buffer_offset; + uint16_t security_buffer_length; + //uint8t_t buffer[1]; // variable +} smb2_session_setup_response; + +typedef struct smb2_logoff_request { + uint16_t structure_size; + uint16_t reserved; +} smb2_logoff_request; + +typedef struct smb2_logoff_response { + uint16_t structure_size; + uint16_t reserved; +} smb2_logoff_response; + +#endif