Browse Source

Adds Win7 compatible authentication (yes!). Adds Tree Connect support. Implements a fd-like mecanism for smb sessions (untested yet)

Julien 'Lta' BALLET 11 years ago
parent
commit
b9144b7a15
14 changed files with 887 additions and 20 deletions
  1. 3 0
      Makefile
  2. 1 1
      README.md
  3. 35 5
      dsm.c
  4. 168 12
      include/bdsm/smb_defs.h
  5. 3 0
      include/bdsm/smb_message.h
  6. 46 0
      include/bdsm/smb_ntlm.h
  7. 42 1
      include/bdsm/smb_session.h
  8. 37 0
      include/bdsm/smb_share.h
  9. 29 0
      include/bdsm/smb_utils.h
  10. 26 0
      src/smb_message.c
  11. 135 0
      src/smb_ntlm.c
  12. 98 1
      src/smb_session.c
  13. 221 0
      src/smb_share.c
  14. 43 0
      src/smb_utils.c

+ 3 - 0
Makefile

@@ -31,8 +31,11 @@ UTIL_SRC		= dsm.c 							    \
 							src/netbios_ns_entry.c	\
 							src/netbios_query.c     \
 							src/netbios_session.c		\
+							src/smb_utils.c					\
 							src/smb_message.c				\
 							src/smb_session.c				\
+							src/smb_ntlm.c					\
+							src/smb_share.c					\
 							src/context.c
 
 UTIL_OBJS		= $(UTIL_SRC:.c=.o)

+ 1 - 1
README.md

@@ -18,7 +18,7 @@ Here's a list of features i intend to support in this project :
   ** Hacky LAN SMB servers discovery (Listing all the smb servers on the LAN, no WINS, etc.)
   ** Basic NETBIOS Session transport layer
 * SMB
-  ** Support only required parts of 'NT LM 0.12' (aka CIFS) dialect.
+  ** Support only required parts of 'NT LM 0.12' (aka CIFS?) dialect.
   ** User based authentication
   ** List Shares
   ** Browse folders

+ 35 - 5
dsm.c

@@ -36,14 +36,17 @@
 #include "bdsm/netbios_utils.h"
 #include "bdsm/netbios_session.h"
 #include "bdsm/smb_session.h"
+#include "bdsm/smb_ntlm.h"
+#include "bdsm/smb_share.h"
+
+#include <openssl/md4.h>
+#include <openssl/md5.h>
 
 int main(int ac, char **av)
 {
   struct sockaddr_in  addr;
   bdsm_context_t      *ctx;
 
-  printf("sizeof(smb_header_t) = %lu", sizeof(smb_header_t));
-
   ctx = bdsm_context_new();
   assert(ctx);
   addr.sin_addr.s_addr = netbios_ns_resolve(ctx->ns, av[1], NETBIOS_FILESERVER);
@@ -73,14 +76,41 @@ int main(int ac, char **av)
     printf("Unable to connect to %s\n", av[1]);
     exit(42);
   }
-  if (smb_session_negotiate_protocol(session))
+  if (smb_negotiate(session))
   {
     fprintf(stderr, "Dialect/Security Mode negotation success.\n");
-    fprintf(stderr, "Session key is 0x%lx\n", session->srv.session_key);
-    fprintf(stderr, "Challenge key is 0x%llx\n", session->srv.challenge);
+    fprintf(stderr, "Session key is 0x%x\n", session->srv.session_key);
+    fprintf(stderr, "Challenge key is 0x%lx\n", session->srv.challenge);
   }
   else
+  {
     printf("Unable to negotiate SMB Dialect\n");
+    exit(42);
+  }
+
+
+  if (smb_authenticate(session, av[1], av[2], av[3]))
+  {
+    if (session->guest)
+      printf("Login FAILED but we were logged in as GUEST \n");
+    else
+      printf("Successfully logged in as %s\\%s\n", av[1], av[2]);
+  }
+  else
+  {
+    printf("Authentication FAILURE.\n");
+    exit(42);
+  }
+
+  smb_tid ipc  = smb_tree_connect(session, "\\\\CERBERE\\IPC$");
+  smb_tid test = smb_tree_connect(session, "\\\\CERBERE\\TEST");
+
+  if (ipc == 0)
+  {
+    fprintf(stderr, "Unable to connect to IPC$ share\n");
+    exit(42);
+  }
+  fprintf(stderr, "Connected to IPC$ share\n");
 
   smb_session_destroy(session);
   bdsm_context_destroy(ctx);

+ 168 - 12
include/bdsm/smb_defs.h

@@ -35,7 +35,7 @@
 
 #define SMB_CMD_CLOSE           0x04
 #define SMB_CMD_TRANS2          0x32
-#define SMD_CMD_TREE_DISCONNECT 0x71
+#define SMB_CMD_TREE_DISCONNECT 0x71
 #define SMB_CMD_NEGOTIATE       0x72
 #define SMB_CMD_SETUP           0x73 // Session Setup AndX
 #define SMB_CMD_TREE_CONNECT    0x75 // Tree Connect AndX
@@ -43,9 +43,12 @@
 #define SMB_CMD_READ            0x2e // Read AndX
 #define SMB_CMD_CREATE          0xa2 // NT Create AndX
 
-#define SMB_SET_FLAG            0
-#define SMB_SET_FLAG2           0
+///////////////////////////////////////////////////////////////////////////////
+//// Flags definitions
 
+// Many aren't use in libdsm but are here for possible later use
+
+// Protocol negotiation flags (flags field in spec)
 #define SMB_FLAG_RESPONSE       (1 << 7)
 #define SMB_FLAG_NOTIFY         (1 << 6)
 #define SMB_FLAG_OPLOCK         (1 << 5)
@@ -53,7 +56,7 @@
 #define SMB_FLAG_CASELESS       (1 << 3)
 #define SMB_FLAG_BUFFER_POSTED  (1 << 1)
 #define SMB_FLAG_LOCK_AND_READ  (1 << 0)
-
+// More Protocol negotiation flags (flags2 field in spec)
 #define SMB_FLAG_UNICODE        (1 << (15 + 8))
 #define SMB_FLAG_NT_ERRORS      (1 << (14 + 8))
 #define SMB_FLAG_EXECUTE_ONLY   (1 << (13 + 8))
@@ -66,10 +69,80 @@
 #define SMB_FLAG_SIGN_SUPPORT   (1 << (2 + 8))
 #define SMB_FLAG_EXT_ATTR       (1 << (1 + 8))
 #define SMB_FLAG_LONG_NAMES_OK  (1 << (0 + 8))
+// File creation/open flags
+#define SMB_CREATE_OPLOCK       (1 << 1)
+#define SMB_CREATE_BATCH_OPLOCK (1 << 2)
+#define SMB_CREATE_MKDIR        (1 << 3)
+#define SMB_CREATE_EXT_RESP     (1 << 4)
+#define SMB_CREATE_DEFAULTS     (0)
+// File access rights
+#define SMB_MOD_READ            (1 << 0)
+#define SMB_MOD_WRITE           (1 << 1)
+#define SMB_MOD_APPEND          (1 << 2)
+#define SMB_MOD_READ_EXT        (1 << 3)
+#define SMB_MOD_WRITE_EXT       (1 << 4)
+#define SMB_MOD_EXEC            (1 << 5)
+#define SMB_MOD_RMCHILD         (1 << 6)
+#define SMB_MOD_READ_ATTR       (1 << 7)
+#define SMB_MOD_WRITE_ATTR      (1 << 8)
+#define SMB_MOD_RM              (1 << 16)
+#define SMB_MOD_READ_CTL        (1 << 17)
+#define SMB_MOD_WRITE_DAC       (1 << 18)
+#define SMB_MOD_CHOWN           (1 << 19)
+#define SMB_MOD_SYNC            (1 << 20)
+#define SMB_MOD_SYS             (1 << 24)
+#define SMB_MOD_MAX_ALLOWED     (1 << 25)
+#define SMB_MOD_GENERIC_ALL     (1 << 28)
+#define SMB_MOD_GENERIC_EXEC    (1 << 29)
+#define SMB_MOD_GENERIC_READ    (1 << 30)
+#define SMB_MOD_GENERIC_WRITE   (1 << 31)
+// File attributes
+#define SMB_ATTR_RO             (1 << 0)
+#define SMB_ATTR_HIDDEN         (1 << 1)
+#define SMB_ATTR_SYS            (1 << 2)
+#define SMB_ATTR_VOLID          (1 << 3)  // Volume ID
+#define SMB_ATTR_DIR            (1 << 4)
+#define SMB_ATTR_ARCHIVE        (1 << 5)  // Modified since last archive (!?)
+#define SMB_ATTR_DEVICE         (1 << 6)
+#define SMB_ATTR_NORMAL         (1 << 7)
+#define SMB_ATTR_TEMP           (1 << 8)
+#define SMB_ATTR_SPARSE         (1 << 9)
+#define SMB_ATTR_REPARSE_PT     (1 << 10)
+#define SMB_ATTR_COMPRESSED     (1 << 11)
+#define SMB_ATTR_OFFLINE        (1 << 12)
+#define SMB_ATTR_INDEXED        (1 << 13) // Not set = May be indexed
+#define SMB_ATTR_ENCRYPTED      (1 << 14)
+// Share access flags
+#define SMB_SHARE_READ          (1 << 0)
+#define SMB_SHARE_WRITE         (1 << 1)
+#define SMB_SHARE_DELETE        (1 << 2)
+
+
+
+
+
+#define SMB_NTLM_HASH_SIZE      16
+#define SMB_NTLM2_BLOB_SIZE     64
+#define SMB_LM2_BLOB_SIZE       8
+
+#define SMB_OS                  "Unix"
+#define SMB_LANMAN              "libdsm-dev"
 
 #define NT_STATUS_SUCCESS                   0x00000000
 #define NT_STATUS_MORE_PROCESSING_REQUIRED  0xc0000016
 
+#define SMB_ANDX_MEMBERS  \
+  uint8_t         andx;         /* 0xff when no other command (recommended :)*/\
+  uint8_t         reserved;     /* 0x00 */                                     \
+  uint16_t        andx_offset;  /* 0x00 when no other command */
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Individual SMB command payload description
+
+
+//// Negotiate Protocol
+
 typedef struct
 {
   uint8_t         wct; // zero
@@ -88,25 +161,28 @@ typedef struct
   uint32_t        max_rawbuffer;  // Max raw buffer size requested by serv.
   uint32_t        session_key;    // 'MUST' be returned to server
   uint32_t        caps;
-  uint64_t        time;           // I don't give a fuck
+  uint64_t        ts;             // I don't give a fuck (or do i?)
   uint16_t        tz;             // Even less fuck given
-  uint8_t         key_length;     // Size of challenge key, if > 8 then shit
+  uint8_t         key_length;     // Size of challenge key, if != 8 then shit
   uint16_t        bct;
   uint64_t        challenge;      // Normally 8 bytes, if not then wtf monkey
   uint8_t         payload[];      // The rest isn't really meaningfull for us
 } __attribute__((packed))   smb_negotiate_resp_t;
 
+
+
+//// Session Setup
+
 typedef struct
 {
-  uint8_t         wct;          // +-12 :)
-  uint8_t         andx;         // 0xff when no other command (recommended :)
-  uint8_t         reserved;     // 0x00
-  uint16_t        andx_offset;  // 0x00 when no other command
+  uint8_t         wct;          // +-13 :)
+  SMB_ANDX_MEMBERS
   uint16_t        max_buffer;   // Maximum size we can receive
   uint16_t        mpx_count;    // maximum multiplexed session
   uint16_t        vc_count;     // Virtual ciruits -> 1!
   uint32_t        session_key;  // 0x00000000
-  uint16_t        blob_length;  // Length of Security Blob
+  uint16_t        oem_pass_len; // Length of LM2 response
+  uint16_t        uni_pass_len; // Length of NTLM2 response
   uint32_t        reserved2;    // 0x00000000
   uint32_t        caps;         // Capabilities
   uint16_t        payload_size;
@@ -115,9 +191,89 @@ typedef struct
 
 typedef struct
 {
-
+  uint8_t         wct;
+  SMB_ANDX_MEMBERS
+  uint16_t        action;
+  uint16_t        bct;
+  uint8_t         bullshit[];
 } __attribute__((packed))   smb_session_resp_t;
 
+
+
+//// Tree Connect
+
+typedef struct
+{
+  uint8_t         wct;              // 4
+  SMB_ANDX_MEMBERS
+  uint16_t        flags;
+  uint16_t        passwd_len;       // 1 if not used. Used in Share Level Auth
+  uint16_t        bct;
+  uint8_t         payload[];        // Password | Path | Service
+
+} __attribute__((packed))   smb_tree_connect_req_t;
+
+typedef struct
+{
+  uint8_t         wct;              // 7
+  SMB_ANDX_MEMBERS
+  uint16_t        opt_support;
+  uint32_t        max_rights;
+  uint32_t        guest_rights;
+  uint16_t        bct;
+  uint8_t         payload[];
+} __attribute__((packed))   smb_tree_connect_resp_t;
+
+
+
+//// Create (or Open) File
+
+typedef struct
+{
+  uint8_t         wct;                // 24
+  SMB_ANDX_MEMBERS
+  uint16_t        path_length;
+  uint32_t        flags;
+  uint32_t        root_fid;
+  uint32_t        access_mask;
+  uint64_t        alloc_sz;
+  uint32_t        file_attr;
+  uint32_t        share_access;
+  uint32_t        disposition;
+  uint32_t        create_opts;
+  uint32_t        impersonation;
+  uint8_t         security_flags;
+  uint16_t        bct;
+  uint8_t         path[];             // UTF16 Path, starting with '\'
+} __attribute__((packed))   smb_create_req_t;
+
+typedef struct
+{
+  uint8_t         wct;                // 34
+  SMB_ANDX_MEMBERS
+  uint8_t         oplock_level;
+  uint16_t        fid;
+  uint32_t        action;
+  uint64_t        created;            // File creation time
+  uint64_t        accessed;           // File last access time
+  uint64_t        written;            // File last write time
+  uint64_t        changed;            // File last modification time
+  uint32_t        file_attr;
+  uint64_t        alloc_sz;
+  uint64_t        eof;
+  uint16_t        filetype;
+  uint16_t        ipc_state;
+  uint8_t         is_dir;
+  uint16_t        bct;                // 0
+} __attribute__((packed))   smb_create_resp_t;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Main structures for holding packet data and building packets
+
 typedef struct
 {
   uint8_t         magic[4];     // { 0xff, 0x53, 0x4d, 0x42 } "\xffSMB"

+ 3 - 0
include/bdsm/smb_message.h

@@ -32,12 +32,15 @@ typedef struct
 
 smb_message_t   *smb_message_new(uint8_t cmd, size_t payload_size);
 void            smb_message_destroy(smb_message_t *msg);
+int             smb_message_advance(smb_message_t *msg, size_t size);
 int             smb_message_append(smb_message_t *msg, const void *data,
                                    size_t data_size);
 int             smb_message_put8(smb_message_t *msg, uint8_t data);
 int             smb_message_put16(smb_message_t *msg, uint16_t data);
 int             smb_message_put32(smb_message_t *msg, uint32_t data);
 int             smb_message_put64(smb_message_t *msg, uint32_t data);
+int             smb_message_put_utf16(smb_message_t *msg, const char *src_enc,
+                                      const char *str, size_t str_len);
 
 void            smb_message_set_default_flags(smb_message_t *msg);
 void            smb_message_flag(smb_message_t *msg, uint32_t flag, int value);

+ 46 - 0
include/bdsm/smb_ntlm.h

@@ -0,0 +1,46 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#ifndef __BDSM_SMB_NTLM_H_
+#define __BDSM_SMB_NTLM_H_
+
+#include "bdsm/smb_defs.h"
+
+typedef uint8_t smb_ntlmh_t[SMB_NTLM_HASH_SIZE];
+
+typedef struct
+{
+  uint32_t    header;
+  uint32_t    reserved;
+  uint64_t    timestamp;
+  uint64_t    challenge;
+  uint32_t    unknown;
+  uint8_t     domain[];
+} __attribute__((packed)) smb_ntlm_blob_t;
+
+uint64_t    smb_ntlm_generate_challenge();
+void        smb_ntlm_hash(const char *password, smb_ntlmh_t *hash);
+void        smb_ntlm2_hash(const char *username, const char *password,
+                           const char *destination, smb_ntlmh_t *hash);
+// Returned response is blob_size + 16 long. You'll have to free it
+uint8_t     *smb_ntlm2_response(smb_ntlmh_t *hash_v2, uint64_t srv_challenge,
+                                uint8_t *blob, size_t blob_size);
+// This method seems useless on Win7 (other untested)
+size_t      smb_ntlm_blob(smb_ntlm_blob_t *blob, uint64_t ts,
+                              uint64_t user_challenge, const char *domain);
+#endif

+ 42 - 1
include/bdsm/smb_session.h

@@ -23,22 +23,61 @@
 #include "bdsm/smb_defs.h"
 #include "bdsm/smb_message.h"
 
+#define SMB_STATE_SESSION_OK    3 // We are authenticated and can do stuff
 #define SMB_STATE_DIALECT_OK    2
 #define SMB_STATE_NETBIOS_OK    1
 #define SMB_STATE_NEW           0
 #define SMB_STATE_ERROR         -1
 
+// The id of a connection to a share, i.e. 'TreeID'
+typedef uint16_t    smb_tid;
+
+// The id of a file handle, i.e. 'FUID'
+typedef uint16_t    smb_fid;
+
+// Concatenation of the two above, representing a file inside of a session
+// First 4 bytes are the TreeID (smb_tid), last 4 are the File ID (FUID)
+typedef uint32_t    smb_fd;
+
+#define SMB_FD_TID(fd)    ((smb_tid) fd >> 16)
+#define SMB_FD_FID(fd)    ((smb_fid) fd & 0x0000ffff)
+#define SMB_FD(tid, fid)  ((((smb_fd)tid) << 16) | (((smb_fd) fid))
+
+typedef struct smb_file_s
+{
+  smb_fid             fid;
+  struct smb_file_s   *next;          // Next file in this share
+  smb_tid             tid;
+} smb_file_t;
+
+typedef struct smb_share_s
+{
+  smb_tid             tid;
+  struct smb_share_s  *next;          // Next share in this session
+  uint16_t            opts;           // Optionnal support opts
+  uint16_t            rights;         // Maximum rights field
+  uint16_t            guest_rights;
+  smb_file_t          *files;         // List of all open files for this share
+} smb_share_t;
+
 typedef struct
 {
   int                 state;
+  int                 guest;            // boolean, are we logged as guest ?
+  uint16_t            uid;              // uid attributed by the server
   netbios_session_t   *nb_session;
+
+  // Informations about the smb server we are connected to.
   struct {
     uint16_t            dialect;        // The selected dialect
     uint16_t            security_mode;  // Security mode
     uint32_t            caps;           // Server caps replyed during negotiate
     uint32_t            session_key;    // XXX Is this really usefull?
     uint64_t            challenge;      // For challenge response security
+    uint64_t            ts;             // It seems Win7 requires it :-/
   }                   srv;
+
+  smb_share_t         *shares;          // shares->files | Map fd <-> smb_file_t
 }                   smb_session_t;
 
 
@@ -50,6 +89,8 @@ int             smb_session_send_msg(smb_session_t *s, smb_message_t *msg);
 // memory. It'll be reused on next recv_msg
 size_t          smb_session_recv_msg(smb_session_t *s, smb_message_t *msg);
 int             smb_session_connect(smb_session_t *s, char *name, uint32_t ip);
-int             smb_session_negotiate_protocol(smb_session_t *s);
+int             smb_negotiate(smb_session_t *s);
+int             smb_authenticate(smb_session_t *s, const char *domain,
+                                 const char *user, const char *password);
 
 #endif

+ 37 - 0
include/bdsm/smb_share.h

@@ -0,0 +1,37 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#ifndef __BDSM_SMB_SHARE_H_
+#define __BDSM_SMB_SHARE_H_
+
+#include "bdsm/smb_session.h"
+
+void        smb_session_share_add(smb_session_t *s, smb_share_t *share);
+smb_share_t *smb_session_share_get(smb_session_t *s, smb_tid tid);
+smb_share_t *smb_session_share_remove(smb_session_t *s, smb_tid tid);
+
+int         smb_session_file_add(smb_session_t *s, smb_tid tid, smb_file_t *f);
+smb_file_t  *smb_session_file_get(smb_session_t *s, smb_fd fd);
+smb_file_t  *smb_session_file_remove(smb_session_t *s, smb_fd fd);
+
+smb_tid     smb_tree_connect(smb_session_t *s, const char *path);
+int         smb_tree_disconnect(smb_session_t *s, smb_tid tid);
+smb_fd      smb_fopen(smb_session_t *s, smb_tid tid, const char *path);
+void        smb_fclose(smb_session_t *s, smb_fd);
+
+#endif

+ 29 - 0
include/bdsm/smb_utils.h

@@ -0,0 +1,29 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#ifndef __BDSM_SMB_UTILS_H_
+#define __BDSM_SMB_UTILS_H_
+
+#include <stdint.h>
+
+// Only ASCII is supported yet as src_enc. dst will be set to newly allocated
+// memory You'll have to call free() on it after use
+size_t      smb_to_utf16(const char *src_enc, const char *src,
+                         size_t src_len, char **dst);
+
+#endif

+ 26 - 0
src/smb_message.c

@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include "bdsm/smb_message.h"
+#include "bdsm/smb_utils.h"
 
 smb_message_t   *smb_message_new(uint8_t cmd, size_t payload_size)
 {
@@ -93,6 +94,20 @@ int             smb_message_put64(smb_message_t *msg, uint32_t data)
     return(smb_message_append(msg, (void *)&data, 8));
 }
 
+int             smb_message_put_utf16(smb_message_t *msg, const char *src_enc,
+                                      const char *str, size_t str_len)
+{
+  char          *utf_str;
+  size_t        utf_str_len;
+  int           res;
+
+  utf_str_len = smb_to_utf16(src_enc, str, str_len, &utf_str);
+  res = smb_message_append(msg, utf_str, utf_str_len);
+  free(utf_str);
+
+  return(res);
+}
+
 void            smb_message_flag(smb_message_t *msg, uint32_t flag, int value)
 {
   uint32_t      *flags;
@@ -117,3 +132,14 @@ void            smb_message_set_default_flags(smb_message_t *msg)
   //msg->packet->header.flags2  = 0xc843; // w/ extended security;
   msg->packet->header.flags2  = 0xc043; // w/o extended security;
 }
+
+int             smb_message_advance(smb_message_t *msg, size_t size)
+{
+  assert(msg != NULL);
+
+  if (msg->cursor + size > msg->payload_size)
+    return (0);
+
+  msg->cursor += size;
+  return (1);
+}

+ 135 - 0
src/smb_ntlm.c

@@ -0,0 +1,135 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <openssl/hmac.h>
+#include <openssl/md4.h>
+#include <openssl/md5.h>
+
+#include "bdsm/smb_utils.h"
+#include "bdsm/smb_ntlm.h"
+
+uint64_t    smb_ntlm_generate_challenge()
+{
+  uint64_t        result;
+  uint64_t        *memory;
+  struct timeval  t;
+
+  gettimeofday(&t, NULL);
+  srandom(t.tv_usec);
+  // Get random data from uninitialized memory, and 'random' memory address
+  memory = malloc(sizeof(uint64_t));
+
+  result = random() + (random() << 32) + (uint64_t)memory * *memory;
+
+  return (result);
+}
+
+void        smb_ntlm_hash(const char *password, smb_ntlmh_t *hash)
+{
+  char      *ucs2le_pass;
+  size_t    sz;
+
+  assert(password != NULL && hash != NULL);
+
+  sz = smb_to_utf16("ASCII", password, strlen(password), &ucs2le_pass);
+  memset((void *)hash, 0, SMB_NTLM_HASH_SIZE);
+  MD4((uint8_t *)ucs2le_pass, sz, (uint8_t *)hash);
+  free(ucs2le_pass);
+}
+
+void        smb_ntlm2_hash(const char *user, const char *password,
+                           const char *dest, smb_ntlmh_t *hash)
+{
+  smb_ntlmh_t   hash_v1;
+  char          *ucs_user, *ucs_dest, *data;
+  size_t        ucs_user_len, ucs_dest_len, data_len;
+
+
+  smb_ntlm_hash(password, &hash_v1);
+
+  ucs_user_len  = smb_to_utf16("", user, strlen(user), &ucs_user);
+  ucs_dest_len  = smb_to_utf16("", dest, strlen(dest), &ucs_dest);
+  data_len      = ucs_user_len + ucs_dest_len;
+  data          = alloca(data_len);
+
+  memcpy(data, ucs_user, ucs_user_len);
+  memcpy(data + ucs_user_len, ucs_dest, ucs_dest_len);
+
+  HMAC(EVP_md5(),
+       (uint8_t *)hash_v1, SMB_NTLM_HASH_SIZE,
+       (uint8_t *)data, data_len,
+       (uint8_t *)hash, NULL);
+
+  free(ucs_user);
+  free(ucs_dest);
+}
+
+uint8_t     *smb_ntlm2_response(smb_ntlmh_t *hash_v2, uint64_t srv_challenge,
+                                uint8_t *blob, size_t blob_size)
+{
+  struct timeval  t;
+  uint8_t         *data, *response, hmac[16];
+  size_t          data_len;
+
+
+  data_len  = sizeof(uint64_t) + blob_size;
+  data      = alloca(data_len);
+  assert(data != NULL);
+  memcpy(data, (void *)&srv_challenge, sizeof(uint64_t));
+  memcpy(data + sizeof(uint64_t), blob, blob_size);
+
+  HMAC(EVP_md5(), (uint8_t *)hash_v2, SMB_NTLM_HASH_SIZE,
+       data, data_len, (uint8_t *)&hmac, 0);
+
+  response = malloc(blob_size + 16);
+  assert(response != NULL);
+  memcpy(response, (void *)hmac, 16);
+  memcpy(response + 16, blob, blob_size);
+
+  return (response);
+}
+
+size_t      smb_ntlm_blob(smb_ntlm_blob_t *blob, uint64_t ts,
+                          uint64_t user_challenge, const char *domain)
+{
+  char      *ucs_domain;
+  size_t    ucs_domain_len;
+
+  assert(blob != NULL && domain != NULL);
+
+  memset((void *)blob, 0, sizeof(smb_ntlm_blob_t));
+  blob->header    = 0x101;
+  blob->timestamp = ts;
+  blob->challenge = user_challenge;
+
+  ucs_domain_len = smb_to_utf16("", domain, strlen(domain), &ucs_domain);
+  *((uint16_t *)blob->domain)     = 0x0002;   // type == Netbios domain
+  *((uint16_t *)blob->domain + 1) = ucs_domain_len;
+
+  memcpy(blob->domain + 4, (void *)ucs_domain, ucs_domain_len);
+  memset(blob->domain + 4 + ucs_domain_len, 0, 4);
+  free(ucs_domain);
+
+  return (sizeof(smb_ntlm_blob_t) + ucs_domain_len + 8);
+}

+ 98 - 1
src/smb_session.c

@@ -22,6 +22,7 @@
 #include <assert.h>
 
 #include "bdsm/smb_session.h"
+#include "bdsm/smb_ntlm.h"
 
 smb_session_t   *smb_session_new()
 {
@@ -110,7 +111,7 @@ size_t          smb_session_recv_msg(smb_session_t *s, smb_message_t *msg)
   return(msg->payload_size);
 }
 
-int             smb_session_negotiate_protocol(smb_session_t *s)
+int             smb_negotiate(smb_session_t *s)
 {
   const char            *dialects[] = SMB_DIALECTS;
   smb_message_t         *msg = NULL;
@@ -148,6 +149,7 @@ int             smb_session_negotiate_protocol(smb_session_t *s)
   s->srv.caps           = nego->caps;
   s->srv.session_key    = nego->session_key;
   s->srv.challenge      = nego->challenge;
+  s->srv.ts             = nego->ts;
 
   // Yeah !
   s->state              = SMB_STATE_DIALECT_OK;
@@ -158,3 +160,98 @@ int             smb_session_negotiate_protocol(smb_session_t *s)
     s->state = SMB_STATE_ERROR;
     return (0);
 }
+
+int             smb_authenticate(smb_session_t *s, const char *domain,
+                                 const char *user, const char *password)
+{
+  smb_message_t         answer;
+  smb_message_t         *msg = NULL;
+  smb_session_req_t     *req = NULL;
+  uint8_t               *ntlm2 = NULL;
+  smb_ntlmh_t           hash_v2;
+  uint8_t               *ucs;
+  size_t                ucs_len;
+  uint64_t              user_challenge;
+  uint8_t               blob[128];
+  size_t                blob_size;
+
+  msg = smb_message_new(SMB_CMD_SETUP, 512);
+  smb_message_set_default_flags(msg);
+  req = (smb_session_req_t *)msg->packet->payload;
+
+  req->wct              = (sizeof(smb_session_req_t) - 3) / 2;
+  req->andx             = 0xFF;
+  req->reserved         = 0;
+  req->max_buffer       = NETBIOS_SESSION_PAYLOAD;
+  req->max_buffer      -= sizeof(netbios_session_packet_t);
+  req->max_buffer      -= sizeof(smb_packet_t);
+  req->mpx_count        = 16; // XXX ?
+  req->vc_count         = 1;
+  req->session_key      = s->srv.session_key;
+  req->caps             = s->srv.caps; // XXX caps & our_caps_mask
+
+  smb_message_advance(msg, sizeof(smb_session_req_t));
+
+  user_challenge = smb_ntlm_generate_challenge();
+
+  // LM2 Response
+  smb_ntlm2_hash(user, password, domain, &hash_v2);
+  ntlm2 = smb_ntlm2_response(&hash_v2, s->srv.challenge,
+                             (void *)&user_challenge, 8);
+  smb_message_append(msg, ntlm2, 16 + 8);
+  free(ntlm2);
+
+  // NTLM2 Response
+  blob_size = smb_ntlm_blob((smb_ntlm_blob_t *)blob, s->srv.ts,
+                            user_challenge, domain);
+  ntlm2 = smb_ntlm2_response(&hash_v2, s->srv.challenge, blob, blob_size);
+  //smb_message_append(msg, ntlm2, 16 + blob_size);
+  free(ntlm2);
+
+  req->oem_pass_len = 16 + SMB_LM2_BLOB_SIZE;
+  req->uni_pass_len = 0; //16 + blob_size; //SMB_NTLM2_BLOB_SIZE;
+  if (msg->cursor / 2) // Padding !
+    smb_message_put8(msg, 0);
+
+  smb_message_put_utf16(msg, "", user, strlen(user));
+  smb_message_put16(msg, 0);
+  smb_message_put_utf16(msg, "", domain, strlen(domain));
+  smb_message_put16(msg, 0);
+  smb_message_put_utf16(msg, "", SMB_OS, strlen(SMB_OS));
+  smb_message_put16(msg, 0);
+  smb_message_put_utf16(msg, "", SMB_LANMAN, strlen(SMB_OS));
+  smb_message_put16(msg, 0);
+
+  req->payload_size = msg->cursor - sizeof(smb_session_req_t);
+
+  if (!smb_session_send_msg(s, msg))
+  {
+    smb_message_destroy(msg);
+    if (BDSM_DEBUG)
+      fprintf(stderr, "Unable to send Session Setup AndX message\n");
+    return (0);
+  }
+  smb_message_destroy(msg);
+
+  if (smb_session_recv_msg(s, &answer) == 0)
+  {
+    if (BDSM_DEBUG)
+      fprintf(stderr, "Unable to get Session Setup AndX reply\n");
+    return (0);
+  }
+
+  smb_session_resp_t *r = (smb_session_resp_t *)answer.packet->payload;
+  if (answer.packet->header.status != NT_STATUS_SUCCESS)
+  {
+    if (BDSM_DEBUG)
+      fprintf(stderr, "Session Setup AndX : failure.\n");
+    return (0);
+  }
+
+  if (r->action & 0x0001)
+    s->guest = 1;
+  s->state  = SMB_STATE_SESSION_OK;
+  s->uid    = answer.packet->header.uid;
+
+  return (1);
+}

+ 221 - 0
src/smb_share.c

@@ -0,0 +1,221 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bdsm/smb_utils.h"
+#include "bdsm/smb_share.h"
+
+void        smb_session_share_add(smb_session_t *s, smb_share_t *share)
+{
+  smb_share_t *iter;
+
+  assert(s != NULL && share != NULL);
+
+  if (s->shares == NULL)
+  {
+    s->shares = share;
+    return;
+  }
+
+  iter = s->shares;
+  while(iter->next != NULL)
+    iter = iter->next;
+  iter->next = share;
+}
+
+smb_share_t *smb_session_share_get(smb_session_t *s, smb_tid tid)
+{
+  smb_share_t *iter;
+
+  assert(s != NULL && tid);
+
+  iter = s->shares;
+  while(iter != NULL && iter->tid != tid)
+    iter = iter->next;
+
+  return (iter);
+}
+
+smb_share_t *smb_session_share_remove(smb_session_t *s, smb_tid tid)
+{
+  smb_share_t *iter, *keep;
+
+  assert(s != NULL && tid);
+  iter = s->shares;
+
+  if (iter == NULL)
+    return (NULL);
+  if (iter->tid == tid)
+  {
+    s->shares = s->shares->next;
+    return (iter);
+  }
+
+  while(iter->next != NULL && iter->next->tid != tid)
+    iter = iter->next;
+
+  if (iter->next != NULL) // We found it
+  {
+    keep = iter->next;
+    iter->next = iter->next->next;
+    return (keep);
+  }
+  return (NULL);
+}
+
+int         smb_session_file_add(smb_session_t *s, smb_tid tid, smb_file_t *f)
+{
+  smb_share_t *share;
+  smb_file_t  *iter;
+
+  assert(s != NULL && tid && f != NULL);
+
+  if ((share = smb_session_share_get(s, tid)) == NULL)
+    return (0);
+
+  if (share->files == NULL)
+    share->files = f;
+  else
+  {
+    iter = share->files;
+    while (iter->next != NULL)
+      iter = iter->next;
+    iter->next = f;
+  }
+
+  return (1);
+}
+
+smb_file_t  *smb_session_file_get(smb_session_t *s, smb_fd fd)
+{
+  smb_share_t *share;
+  smb_file_t  *iter;
+
+  assert(s != NULL && fd);
+
+  if ((share = smb_session_share_get(s, SMB_FD_TID(fd))) == NULL)
+    return (NULL);
+
+  iter = share->files;
+  while(iter != NULL && iter->fid != SMB_FD_FID(fd))
+    iter = iter->next;
+
+  return (iter);
+}
+
+smb_file_t  *smb_session_file_remove(smb_session_t *s, smb_fd fd)
+{
+  smb_share_t *share;
+  smb_file_t  *iter, *keep;
+
+  assert(s != NULL && fd);
+
+  if ((share = smb_session_share_get(s, SMB_FD_TID(fd))) == NULL)
+    return (NULL);
+
+  iter = share->files;
+
+  if (iter == NULL)
+    return (NULL);
+  if (iter->fid == SMB_FD_FID(fd))
+  {
+    share->files = iter->next;
+    return (iter);
+  }
+
+  while(iter->next != NULL && iter->next->fid != SMB_FD_TID(fd))
+    iter = iter->next;
+  if (iter->next != NULL)
+  {
+    keep = iter->next;
+    iter->next = iter->next->next;
+    return (keep);
+  }
+  else
+    return (NULL);
+}
+
+smb_tid         smb_tree_connect(smb_session_t *s, const char *path)
+{
+  smb_tree_connect_req_t  *req;
+  smb_tree_connect_resp_t *resp;
+  smb_message_t   resp_msg;
+  smb_message_t   *req_msg;
+  smb_share_t     *share;
+
+  assert(s != NULL && path != NULL);
+
+  req_msg = smb_message_new(SMB_CMD_TREE_CONNECT, 128);
+
+  // Packet headers
+  smb_message_set_default_flags(req_msg);
+  req_msg->packet->header.uid      = s->uid;
+  req_msg->packet->header.tree_id  = 0xffff; // Behavior of libsmbclient
+
+  // Packet payload
+  req = (smb_tree_connect_req_t *)req_msg->packet->payload;
+  smb_message_advance(req_msg, sizeof(smb_tree_connect_req_t));
+  req->wct          = 4;
+  req->andx         = 0xff;
+  req->reserved     = 0;
+  req->andx_offset  = 0;
+  req->flags        = 0x0c; // (??)
+  req->passwd_len   = 1;    // Null byte
+
+  smb_message_put8(req_msg, 0); // Ze null byte password;
+  smb_message_put_utf16(req_msg, "", path, strlen(path) + 1);
+  smb_message_append(req_msg, "?????", strlen("?????") + 1);
+  req->bct = strlen(path) * 2 + 2 + 6 + 1;
+
+  if (!smb_session_send_msg(s, req_msg))
+  {
+    smb_message_destroy(req_msg);
+    return (0);
+  }
+  smb_message_destroy(req_msg);
+
+  if (!smb_session_recv_msg(s, &resp_msg))
+    return (0);
+  if (resp_msg.packet->header.status != NT_STATUS_SUCCESS)
+    return (0);
+
+  resp  = (smb_tree_connect_resp_t *)resp_msg.packet->payload;
+  share = calloc(1, sizeof(smb_share_t));
+  assert(share != NULL);
+
+  share->tid          = resp_msg.packet->header.tree_id;
+  share->opts         = resp->opt_support;
+  share->rights       = resp->max_rights;
+  share->guest_rights = resp->guest_rights;
+
+  smb_session_share_add(s, share);
+
+  return(share->tid);
+}
+
+int           smb_tree_disconnect(smb_session_t *s, smb_tid tid)
+{
+  assert(s != NULL && tid);
+
+  return (0);
+}

+ 43 - 0
src/smb_utils.c

@@ -0,0 +1,43 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "bdsm/smb_utils.h"
+
+size_t      smb_to_utf16(const char *src_enc, const char *src,
+                         size_t src_len, char **dst)
+{
+  size_t    res = src_len * 2;
+  char      *out;
+
+  out = malloc(res);
+  assert(out != NULL);
+  *dst = (char *)out;
+
+  for (size_t i = 0; i < src_len; i++)
+  {
+    out[2 * i]      = src[i];
+    out[2 * i + 1]  = 0;
+  }
+
+  return (res);
+
+}