Bläddra i källkod

Very dirty, but working Extended security login

Julien 'Lta' BALLET 11 år sedan
förälder
incheckning
ea35edef3c
13 ändrade filer med 767 tillägg och 285 borttagningar
  1. 1 0
      .gitignore
  2. 14 5
      Makefile.am
  3. 7 8
      bin/dsm.c
  4. 0 4
      include/bdsm/smb_defs.h
  5. 78 9
      include/bdsm/smb_ntlm.h
  6. 31 9
      include/bdsm/smb_packets.h
  7. 5 1
      include/bdsm/smb_session.h
  8. 4 4
      include/bdsm/smb_gss.h
  9. 13 9
      include/bdsm/smb_types.h
  10. 0 179
      src/smb_gss.c
  11. 241 20
      src/smb_ntlm.c
  12. 40 37
      src/smb_session.c
  13. 333 0
      src/smb_spnego.c

+ 1 - 0
.gitignore

@@ -49,3 +49,4 @@ libdsm.pc
 
 doc/html/*
 doc/Doxyfile
+dsm_ntlm

+ 14 - 5
Makefile.am

@@ -36,15 +36,19 @@ bdsm_HEADERS = \
 noinst_HEADERS = \
     include/bdsm/smb_packets.h    \
     include/bdsm/smb_transport.h  \
-    include/bdsm/smb_gss.h        \
+    include/bdsm/smb_spnego.h     \
+    contrib/spnego/spnego_asn1.h  \
     contrib/mdx/md4.h             \
-    contrib/mdx/md5.h
+    contrib/mdx/md5.h             \
+    contrib/rc4/rc4.h
 
 lib_LTLIBRARIES = libdsm.la
 
 libdsm_la_SOURCES = \
     contrib/mdx/md4.c   \
     contrib/mdx/md5.c   \
+    contrib/rc4/rc4.c   \
+    contrib/spnego/spnego_asn1.c  \
     src/hmac_md5.c      \
     src/netbios_ns.c    \
     src/netbios_ns_entry.c  \
@@ -53,7 +57,7 @@ libdsm_la_SOURCES = \
     src/netbios_utils.c     \
     src/smb_fd.c        \
     src/smb_file.c      \
-    src/smb_gss.c       \
+    src/smb_spnego.c    \
     src/smb_message.c   \
     src/smb_ntlm.c      \
     src/smb_session.c   \
@@ -66,13 +70,13 @@ libdsm_la_SOURCES = \
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libdsm.pc
 
-libdsm_la_LDFLAGS = -version-info @BDSM_LIBTOOL_VERSION@ LTLIBICONV @TASN1_LIBS@
+libdsm_la_LDFLAGS = -version-info @BDSM_LIBTOOL_VERSION@ LTLIBICONV @TASN1_LIBS@ -lbsd
 
 
 bin_PROGRAMS =
 
 if PROGRAMS
-bin_PROGRAMS += dsm dsm_discover dsm_inverse dsm_lookup
+bin_PROGRAMS += dsm dsm_discover dsm_inverse dsm_lookup dsm_ntlm
 endif
 
 dsm_SOURCES = bin/dsm.c
@@ -83,6 +87,8 @@ dsm_inverse_SOURCES = bin/inverse.c
 
 dsm_lookup_SOURCES = bin/lookup.c
 
+dsm_ntlm_SOURCES = bin/ntlm.c
+
 LDADD = libdsm.la
 
 if HAVE_DOXYGEN
@@ -95,6 +101,9 @@ doc:
 	echo "Doxygen wasn't detected by configure, it's support has been disabled"
 endif
 
+contrib/spnego/spnego_asn1.c: contrib/spnego/spnego.asn1
+	asn1Parser -o $@ -n spnego_asn1_conf $<
+
 a : all
 c : clean
 re : clean all

+ 7 - 8
bin/dsm.c

@@ -136,7 +136,6 @@ int main(int ac, char **av)
   if (smb_session_connect(session, host, addr.sin_addr.s_addr, SMB_TRANSPORT_TCP))
   {
     printf("Successfully connected to %s\n", host);
-    fprintf(stderr, "Session key is 0x%x\n", session->srv.session_key);
     fprintf(stderr, "Challenge key is 0x%lx\n", session->srv.challenge);
   }
   else
@@ -152,13 +151,13 @@ int main(int ac, char **av)
     else
       printf("Successfully logged in as %s\\%s\n", host, login);
   }
-  else if (smb_session_login(session, "WORKGROUP", login, password))
-  {
-    if (session->guest)
-      printf("Login FAILED but we were logged in as GUEST \n");
-    else
-      printf("Successfully logged in as %s\\%s\n", host, login);
-  }
+  // else if (smb_session_login(session, "WORKGROUP", login, password))
+  // {
+  //   if (session->guest)
+  //     printf("Login FAILED but we were logged in as GUEST \n");
+  //   else
+  //     printf("Successfully logged in as %s\\%s\n", host, login);
+  // }
   else
   {
     printf("Authentication FAILURE.\n");

+ 0 - 4
include/bdsm/smb_defs.h

@@ -270,8 +270,4 @@ enum smb_session_supports_what
                                    SMB_FIND2_FLAG_RESUME)
 
 
-#define SMB_NTLM_HASH_SIZE      16
-#define SMB_NTLM2_BLOB_SIZE     64
-#define SMB_LM2_BLOB_SIZE       8
-
 #endif

+ 78 - 9
include/bdsm/smb_ntlm.h

@@ -21,7 +21,10 @@
 
 #include "bdsm/smb_defs.h"
 
-typedef uint8_t smb_ntlmh_t[SMB_NTLM_HASH_SIZE];
+#define SMB_LM2_BLOB_SIZE         8
+#define SMB_NTLM_HASH_SIZE        16
+
+typedef uint8_t smb_ntlmh[SMB_NTLM_HASH_SIZE];
 
 typedef struct
 {
@@ -30,17 +33,83 @@ typedef struct
   uint64_t    timestamp;
   uint64_t    challenge;
   uint32_t    unknown;
-  uint8_t     domain[];
-} __attribute__((packed)) smb_ntlm_blob_t;
+  uint8_t     target[];
+} __attribute__((packed)) smb_ntlm_blob;
+
+
+
+#define SMB_NTLMSSP_CMD_NEGO      0x01
+#define SMB_NTLMSSP_CMD_AUTH      0x03
+
+#define _NTLMSSP_COMMON     \
+  char        id[8];        \
+  uint32_t    type;
+
+#define _NTLMSSP_FIELD(FIELD)    \
+  uint16_t  FIELD ## _len;       \
+  uint16_t  FIELD ## _maxlen;    \
+  uint32_t  FIELD ## _offset;
+
+typedef struct
+{
+  _NTLMSSP_COMMON
+  uint32_t    flags;
+  _NTLMSSP_FIELD(domain)
+  _NTLMSSP_FIELD(host)
+  uint8_t     names[];
+} __attribute__((packed)) smb_ntlmssp_nego;
+
+typedef struct
+{
+  _NTLMSSP_COMMON
+  _NTLMSSP_FIELD(name)
+  uint32_t    flags;
+  uint64_t    challenge;
+  uint64_t    reserved;
+  _NTLMSSP_FIELD(tgt) // Target Info
+  uint8_t     data[];
+} __attribute__((packed)) smb_ntlmssp_challenge;
+
+typedef struct
+{
+  _NTLMSSP_COMMON
+  _NTLMSSP_FIELD(lm)
+  _NTLMSSP_FIELD(ntlm)
+  _NTLMSSP_FIELD(domain)
+  _NTLMSSP_FIELD(user)
+  _NTLMSSP_FIELD(host)
+  _NTLMSSP_FIELD(session_key)
+
+  uint32_t    flags;
+  uint8_t     data[];
+} __attribute__((packed)) smb_ntlmssp_auth;
 
 uint64_t    smb_ntlm_generate_challenge();
-void        smb_ntlm_hash(const char *password, smb_ntlmh_t *hash);
+void        smb_ntlm_generate_xkey(smb_ntlmh *cli_session_key);
+void        smb_ntlm_hash(const char *password, smb_ntlmh *hash);
 void        smb_ntlm2_hash(const char *username, const char *password,
-                           const char *destination, smb_ntlmh_t *hash);
+                           const char *destination, smb_ntlmh *hash);
+// Precompute the blob that will be HMAC'ed to produce NTLM2 Response
+// You have to free() the blob after usage
+size_t      smb_ntlm_make_blob(smb_ntlm_blob **blob, uint64_t ts,
+                               uint64_t user_challenge, void *tgt,
+                               size_t tgt_sz, uint64_t ts2);
 // 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     *smb_ntlm2_response(smb_ntlmh *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);
+// Returned response is 24 bytes long. You'll have to free it.
+uint8_t     *smb_lm2_response(smb_ntlmh *hash_v2, uint64_t srv_challenge,
+                              uint64_t user_challenge);
+// You have to allocate session key
+void        smb_ntlm2_session_key(smb_ntlmh *hash_v2, void *ntlm2,
+                                  smb_ntlmh *xkey, smb_ntlmh *enc_xkey);
+
+void        smb_ntlmssp_negotiate(const char *host, const char *domain,
+                                  void **token, size_t *token_sz);
+void        smb_ntlmssp_response(uint64_t srv_challenge, uint64_t srv_ts,
+                                 const char *host, const char *domain,
+                                 const char *user, const char *password,
+                                 void *tgt, size_t tgt_sz,
+                                 void **token, size_t *token_sz);
+
 #endif

+ 31 - 9
include/bdsm/smb_packets.h

@@ -3,7 +3,7 @@
 //  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
 //   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
 //   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
-//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+// li|______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
 //          \/        \/        \/        \/  )/           \/        \/   \/
 //
 // This file is part of libdsm. Copyright © 2014 VideoLabs SAS
@@ -96,18 +96,18 @@ typedef struct
   uint8_t         gssapi[];
 } __attribute__((packed))   smb_nego_xsec_resp;
 
-
+#define SMB_SESSION_REQ_COMMON \
+  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 */
 
 //-> Session Setup
 typedef struct
 {
-  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
+  SMB_SESSION_REQ_COMMON
   uint16_t        oem_pass_len; // Length of LM2 response
   uint16_t        uni_pass_len; // Length of NTLM2 response
   uint32_t        reserved2;    // 0x00000000
@@ -116,6 +116,18 @@ typedef struct
   uint8_t         payload[];
 } __attribute__((packed))   smb_session_req;
 
+//-> Session Setup
+typedef struct
+{
+  SMB_SESSION_REQ_COMMON
+  uint16_t        xsec_blob_size; // Length of GSSAPI/SPNEGO blob
+  uint32_t        reserved2;      // 0x00000000
+  uint32_t        caps;           // Capabilities
+  uint16_t        payload_size;
+  uint8_t         payload[];
+} __attribute__((packed))   smb_session_xsec_req;
+
+
 //<- Session Setup
 typedef struct
 {
@@ -126,6 +138,16 @@ typedef struct
   uint8_t         bullshit[];
 } __attribute__((packed))   smb_session_resp;
 
+typedef struct
+{
+  uint8_t         wct;
+  SMB_ANDX_MEMBERS
+  uint16_t        action;
+  uint16_t        xsec_blob_size;
+  uint16_t        payload_size;
+  uint8_t         payload[];
+} __attribute__((packed))   smb_session_xsec_resp;
+
 
 
 //-> Tree Connect

+ 5 - 1
include/bdsm/smb_session.h

@@ -55,7 +55,10 @@
  */
 #define SMB_FD(tid, fid)  ((((smb_fd)tid) << 16) | (((smb_fd) fid)))
 
-
+#define SMB_SESSION_MAX_BUFFER (SMB_IO_BUFSIZE                    \
+                                - sizeof(smb_header)              \
+                                - sizeof(netbios_session_packet)  \
+                                - sizeof(smb_packet))
 
 /**
  * @brief Allocates a new Session object
@@ -156,4 +159,5 @@ const char      *smb_session_server_name(smb_session *s);
  */
 int             smb_session_supports(smb_session *s, int what);
 
+
 #endif

+ 4 - 4
include/bdsm/smb_gss.h

@@ -17,13 +17,13 @@
 //----------------------------------------------------------------------------
 
 
-#ifndef __BDSM_SMB_GSS_H_
-#define __BDSM_SMB_GSS_H_
+#ifndef __BDSM_SMB_SPNEGO_H_
+#define __BDSM_SMB_SPNEGO_H_
 
 #include "bdsm/smb_types.h"
 
-int             smb_session_login_gss(smb_session *s, const char *domain,
-                                      const char *user, const char *password);
+int             smb_session_login_spnego(smb_session *s, const char *domain,
+                                         const char *user, const char *password);
 
 
 #endif

+ 13 - 9
include/bdsm/smb_types.h

@@ -26,9 +26,7 @@
 
 #include <netinet/ip.h>
 #include <stddef.h>
-
-#include <gssapi/gssapi_spnego.h>
-#include <gssapi/gssapi_ntlm.h>
+#include <libtasn1.h>
 
 #include "bdsm/smb_packets.h"
 
@@ -103,23 +101,29 @@ typedef struct
 {
   int                 state;
   int                 guest;            // boolean, are we logged as guest ?
-  uint16_t            uid;              // uid attributed by the server
 
   // Informations about the smb server we are connected to.
   struct {
     char                name[16];       // The server name
     uint16_t            dialect;        // The selected dialect
     uint16_t            security_mode;  // Security mode
+    uint16_t            uid;            // uid attributed by the server.
+    uint32_t            session_key;    // The session key sent by the server on protocol negotiate
     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;
   struct {
-    gss_cred_id_t     credentials;
-    gss_ctx_id_t      ctx;
-    gss_buffer_desc   spnego;
-  }                   gss;              // eXtended SECurity negociation data
+    void                *init;
+    size_t              init_sz;
+    ASN1_TYPE           asn1_def;
+    //uint32_t            flags;          // NTLMSSP negotiation flags
+  }                   spnego;           // eXtended SECurity negociation data
+  struct {
+    uint32_t            flags;
+    void                *tgt_info;
+    size_t              tgt_info_sz;
+  }                   xsec;
 
   smb_transport       transport;
   struct smb_share_s  *shares;          // shares->files | Map fd <-> smb_file

+ 0 - 179
src/smb_gss.c

@@ -1,179 +0,0 @@
-//---------------------------------------------------------------------------
-//  __________________    _________  _____            _____  .__         ._.
-//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
-//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
-//   |    |   \|    `   \/        /    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 <stdio.h>
-#include <assert.h>
-
-#include "bdsm/debug.h"
-#include "bdsm/smb_session.h"
-#include "bdsm/smb_ntlm.h"
-
-void            smb_gss_display_status(OM_uint32 major,
-                                       OM_uint32 minor,
-                                       const gss_OID mech_type)
-{
-  OM_uint32 msg_ctx = 0, local_minor;
-  gss_buffer_desc maj_str, min_str;
-
-  do {
-    local_minor = minor;
-    gss_display_status(&local_minor, major, GSS_C_GSS_CODE,
-                       mech_type, &msg_ctx, &maj_str);
-    gss_display_status(&local_minor, minor, GSS_C_MECH_CODE,
-                       mech_type, &msg_ctx, &min_str);
-
-    BDSM_dbg("smb_gss_status: (0x%x, 0x%x) %.*s: %.*s\n", major, minor,
-             (int)maj_str.length,
-             (char *)maj_str.value,
-             (int)min_str.length,
-             (char *)min_str.value);
-
-    gss_release_buffer(&local_minor, &maj_str);
-    gss_release_buffer(&local_minor, &min_str);
-
-   } while (msg_ctx != 0);
-}
-
-int             smb_session_login_gss(smb_session *s, const char *domain,
-                                      const char *user, const char *password)
-{
-  OM_uint32         major_status, minor_status;
-  gss_name_t        gss_user, gss_server;
-  gss_OID_set_desc  mechs, *mechsp = GSS_C_NO_OID_SET;
-  gss_buffer_desc   input_buf, output_buf, tmp_buf;
-  gss_OID           req_mech_type, actual_mech_type;
-  gss_cred_id_t     creds;
-
-
-  assert(s != NULL && user != NULL && password != NULL);
-
-  //tmp_buf.value  = user;
-  asprintf(&tmp_buf.value, "%s\\%s", domain, user);
-  tmp_buf.length = strlen(tmp_buf.value);
-  gss_import_name(&minor_status, &tmp_buf, GSS_C_NT_USER_NAME, &gss_user);
-
-  //tmp_buf.value  = domain;
-  asprintf(&tmp_buf.value, "%s@%s", "smb", domain);
-  tmp_buf.length = strlen(tmp_buf.value);
-  gss_import_name(&minor_status, &tmp_buf, GSS_C_NT_HOSTBASED_SERVICE, &gss_server);
-
-  // We are using SPNEGO, so set this mech OID
-  mechs.elements = gss_mech_spnego;
-  mechs.count = 1;
-  mechsp = &mechs;
-
-  tmp_buf.value   = password;
-  tmp_buf.length  = strlen(password);
-
-  // major_status = gss_acquire_cred_with_password(&minor_status,
-  //                                               gss_user,
-  //                                               &tmp_buf,
-  //                                               GSS_C_INDEFINITE,
-  //                                               GSS_C_NO_OID_SET,
-  //                                               GSS_C_INITIATE,
-  //                                               &creds, NULL, NULL);
-  // smb_gss_display_status(major_status, minor_status, GSS_C_NO_OID);
-
-  major_status = gss_acquire_cred(&minor_status,
-                                  GSS_C_NO_NAME,
-                                  0,
-                                  GSS_C_NO_OID_SET,
-                                  GSS_C_INITIATE,
-                                  &creds, NULL, NULL);
-  smb_gss_display_status(major_status, minor_status, GSS_C_NO_OID);
-
-  input_buf.value   = NULL;
-  input_buf.length  = 0;
-
-  gss_OID_set supported;
-
-  req_mech_type = gss_mech_spnego;
-
-  gss_inquire_names_for_mech(&minor_status, gss_mech_spnego, &supported);
-
-  //gss_acquire_cred(cname, GSS_C_INITIATE);
-
-  major_status = gss_init_sec_context(&minor_status,
-    GSS_C_NO_CREDENTIAL,
-    &s->gss.ctx,
-    gss_server,
-    req_mech_type,
-    GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG,
-    GSS_C_INDEFINITE,
-    GSS_C_NO_CHANNEL_BINDINGS,
-    &input_buf,//&s->gss.spnego,
-    &actual_mech_type,
-    &output_buf,
-    NULL, NULL);
-  //major_status  = gss_accept_sec_conten
-
-  smb_gss_display_status(major_status, minor_status, req_mech_type);
-}
-
-int             smb_session_login_gss2(smb_session *s, const char *domain,
-                                      const char *user, const char *password)
-{
-  OM_uint32         major_status, minor_status;
-  gss_buffer_desc   user_buf, pass_buf;
-  gss_name_t        gss_user = GSS_C_NO_NAME;
-  gss_OID_set_desc  mechs, *mechsp = GSS_C_NO_OID_SET;
-
-
-  assert(s != NULL && user != NULL && password != NULL);
-
-  // We are using SPNEGO, so set this mech OID
-  mechs.elements = gss_mech_spnego;
-  mechs.count = 1;
-  mechsp = &mechs;
-
-  // Filling buffers with user and pass
-  user_buf.value  = user;
-  user_buf.length = strlen(user);
-  pass_buf.value  = password;
-  pass_buf.length = strlen(password);
-
-  major_status = gss_import_name(&minor_status, &user_buf,
-                                 (gss_OID) GSS_C_NT_USER_NAME, &gss_user);
-  if (major_status == GSS_S_COMPLETE)
-    BDSM_dbg("GSSAPI: Imported name\n");
-  else
-  {
-    BDSM_dbg("GSSAPI: Unable to import name\n");
-    return (0);
-  }
-
-  s->gss.credentials = GSS_C_NO_CREDENTIAL;
-  major_status = gss_acquire_cred_with_password(&minor_status,
-                                                gss_user,
-                                                &pass_buf, 0,
-                                                mechsp, GSS_C_ACCEPT,
-                                                &s->gss.credentials,
-                                                NULL, NULL);
-  if (major_status == GSS_S_COMPLETE)
-    BDSM_dbg("GSSAPI: Acquired credentials\n");
-  else
-  {
-    BDSM_dbg("GSSAPI: Unable to acquire credentials: 0x%x, 0x%x\n",
-             major_status, minor_status);
-    return (0);
-  }
-
-  return(1);
-}

+ 241 - 20
src/smb_ntlm.c

@@ -18,11 +18,15 @@
 
 #include <assert.h>
 #include <ctype.h>
+#include <wctype.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
+#include <bsd/string.h>
 
 #include "mdx/md4.h"
+#include "rc4/rc4.h"
+#include "bdsm/debug.h"
 #include "bdsm/hmac_md5.h"
 #include "bdsm/smb_utils.h"
 #include "bdsm/smb_ntlm.h"
@@ -43,7 +47,15 @@ uint64_t    smb_ntlm_generate_challenge()
   return (result);
 }
 
-void        smb_ntlm_hash(const char *password, smb_ntlmh_t *hash)
+void        smb_ntlm_generate_xkey(smb_ntlmh *cli_session_key)
+{
+  uint64_t  *key = (uint64_t *)cli_session_key;
+
+  key[0] = smb_ntlm_generate_challenge();
+  key[1] = smb_ntlm_generate_challenge();
+}
+
+void        smb_ntlm_hash(const char *password, smb_ntlmh *hash)
 {
   MD4_CTX   ctx;
   char      *ucs2le_pass;
@@ -61,17 +73,31 @@ void        smb_ntlm_hash(const char *password, smb_ntlmh_t *hash)
   free(ucs2le_pass);
 }
 
+static void _upcase(char *str)
+{
+  assert (str != NULL);
+
+  while (*str)
+  {
+    if (isalpha(*str))
+      *str = toupper(*str);
+    str++;
+  }
+}
+
 void        smb_ntlm2_hash(const char *user, const char *password,
-                           const char *dest, smb_ntlmh_t *hash)
+                           const char *dest, smb_ntlmh *hash)
 {
-  smb_ntlmh_t   hash_v1;
-  char          *ucs_user, *ucs_dest, *data;
+  smb_ntlmh     hash_v1;
+  char          *ucs_user, *ucs_dest, *data, user_upper[64];
   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);
+  strlcpy(user_upper, user, 64);
+  _upcase(user_upper);
+
+  ucs_user_len  = smb_to_utf16(user_upper, strlen(user_upper), &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);
@@ -85,7 +111,7 @@ void        smb_ntlm2_hash(const char *user, const char *password,
   free(ucs_dest);
 }
 
-uint8_t     *smb_ntlm2_response(smb_ntlmh_t *hash_v2, uint64_t srv_challenge,
+uint8_t     *smb_ntlm2_response(smb_ntlmh *hash_v2, uint64_t srv_challenge,
                                 uint8_t *blob, size_t blob_size)
 {
   uint8_t         *data, *response, hmac[16];
@@ -108,26 +134,221 @@ uint8_t     *smb_ntlm2_response(smb_ntlmh_t *hash_v2, uint64_t srv_challenge,
   return (response);
 }
 
-size_t      smb_ntlm_blob(smb_ntlm_blob_t *blob, uint64_t ts,
-                          uint64_t user_challenge, const char *domain)
+uint8_t     *smb_lm2_response(smb_ntlmh *hash_v2, uint64_t srv_challenge,
+                              uint64_t user_challenge)
+{
+  smb_ntlm2_response(hash_v2, srv_challenge, &user_challenge, 8);
+}
+
+static void   _wcamelcase(char *str)
 {
-  char      *ucs_domain;
-  size_t    ucs_domain_len;
+  int first = 1;
+
+  assert (str != NULL);
+
+  while(*str)
+  {
+    if (isalpha(*str))
+    {
+      if (first)
+        *str = toupper(*str);
+      else
+        *str = tolower(*str);
+    }
+    first = 0;
+    str += 2;
+  }
+}
+
+#define __NAME_ENCODE_APPEND(type, item)  \
+  *res = type;                            \
+  res += 2;                               \
+  *(uint16_t *)res = item##_sz - 2;       \
+  res += 2;                               \
+  memcpy(res, item, item##_sz - 2);       \
+  res += item##_sz - 2;                   \
+
+static size_t _ntlm_name_encode(char **names, const char *domain,
+                                const char *host, uint64_t ts2)
+{
+  char    *wdomain, *whost, *wdomain_camel, *whost_camel;
+  size_t  wdomain_sz, whost_sz, wdomain_camel_sz, whost_camel_sz;
+  char    *res;
+  size_t  res_sz;
+
+  assert(names != NULL && domain != NULL && host != NULL);
+
+  wdomain_sz        = smb_to_utf16(domain, strlen(domain) + 1, &wdomain);
+  wdomain_camel_sz  = smb_to_utf16(domain, strlen(domain) + 1, &wdomain_camel);
+  whost_sz          = smb_to_utf16(host, strlen(host) + 1, &whost);
+  whost_camel_sz    = smb_to_utf16(host, strlen(host) + 1, &whost_camel);
+
+  _wcamelcase(wdomain_camel);
+  _wcamelcase(whost_camel);
+
+  res_sz = (wdomain_sz - 2) * 2 + (whost_sz - 2) * 2 + 8 + 6 * 4;
+  *names = res = malloc(res_sz);
+  assert(res != NULL);
+  memset(res, 0, res_sz);
+
+  __NAME_ENCODE_APPEND(2, wdomain)
+  __NAME_ENCODE_APPEND(1, whost)
+  __NAME_ENCODE_APPEND(4, wdomain_camel)
+  __NAME_ENCODE_APPEND(3, whost_camel)
+
+  *res = 7;
+  res += 2;
+  *res = 8;
+  res += 2;
+  *(uint64_t *)res = ts2;
+  res += 8;
+
+  free(wdomain);
+  free(wdomain_camel);
+  free(whost);
+  free(whost_camel);
+
+  return (res_sz);
+}
+
+size_t      smb_ntlm_make_blob(smb_ntlm_blob **out_blob, uint64_t ts,
+                               uint64_t user_challenge, void *tgt_info,
+                               size_t tgt_sz, uint64_t ts2)
+{
+  smb_ntlm_blob *blob;
+  //char          *names;
+  //size_t        names_sz;
+
+  assert(blob != NULL && tgt_info != NULL);
 
-  assert(blob != NULL && domain != NULL);
+  //names_sz = _ntlm_name_encode(&names, domain, host, ts2);
+  blob = malloc(tgt_sz + sizeof(smb_ntlm_blob));
 
-  memset((void *)blob, 0, sizeof(smb_ntlm_blob_t));
+  memset((void *)blob, 0, sizeof(smb_ntlm_blob));
   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->target, tgt_info, tgt_sz);
+  //free(names);
+
+  *out_blob = blob;
+  return (sizeof(smb_ntlm_blob) + tgt_sz);
+}
+
+void        smb_ntlm2_session_key(smb_ntlmh *hash_v2, void *ntlm2,
+                                  smb_ntlmh *xkey, smb_ntlmh *xkey_crypt)
+{
+  struct rc4_state  rc4;
+  smb_ntlmh         hmac_ntlm2;
+
+  HMAC_MD5(&hash_v2, 16, ntlm2, 16, &hmac_ntlm2);
+
+  rc4_init(&rc4, hmac_ntlm2, 16);
+  rc4_crypt(&rc4, xkey, xkey_crypt, 16);
+}
+
+
+void        smb_ntlmssp_negotiate(const char *host, const char *domain,
+                                  void **token, size_t *token_sz)
+{
+  smb_ntlmssp_nego  *nego;
+
+  assert(host != NULL && domain != NULL && token != NULL && token_sz != NULL);
+
+  *token_sz = sizeof(smb_ntlmssp_nego) + strlen(host) + strlen(domain);
+  if (*token_sz % 2) // Align on Word
+    *token_sz += 1;
+
+  // fprintf(stderr, "Token size if %ld\n", *token_sz);
+
+  nego      = (smb_ntlmssp_nego *)malloc(*token_sz);
+  assert(nego != NULL);
+
+  nego->type  = SMB_NTLMSSP_CMD_NEGO;
+  nego->flags = 0x60088215;//0x20080205;
+  nego->domain_len = nego->domain_maxlen = strlen(domain);
+  nego->domain_offset = 32;
+  nego->host_len = nego->host_maxlen = strlen(host);
+  nego->host_offset = 32 + strlen(domain);
+
+  memcpy(nego->id, "NTLMSSP", 8);
+  memcpy(nego->names, domain, strlen(domain));
+  memcpy(nego->names + strlen(domain), domain, strlen(domain));
+
+  *token = (void *)nego;
+}
+
+#define __AUTH_APPEND(FIELD, value, size, cursor)           \
+  memcpy(auth->data + cursor, value, size);                 \
+  auth-> FIELD ## _len    = auth->FIELD ## _maxlen = size;   \
+  auth-> FIELD ## _offset = 64 + cursor;                     \
+  cursor += size;
+
+void        smb_ntlmssp_response(uint64_t srv_challenge, uint64_t srv_ts,
+                                 const char *host, const char *domain,
+                                 const char *user, const char *password,
+                                 void *tgt, size_t tgt_sz,
+                                 void **token, size_t *token_sz)
+{
+  smb_ntlmssp_auth      *auth;
+  smb_ntlm_blob         *blob;
+  smb_ntlmh             hash_v2, xkey, xkey_crypt;
+  void                  *lm2, *ntlm2;
+  size_t                blob_size, utf_sz, cursor = 0;
+  uint64_t              user_challenge;
+  char                  *utf;
+
+  assert(host != NULL && domain != NULL && user != NULL && password != NULL);
+  assert(token != NULL && token_sz != NULL);
+
+  //// We compute most of the data first to know the final token size
+  smb_ntlm2_hash(user, password, domain, &hash_v2);
+  user_challenge = smb_ntlm_generate_challenge();
+  smb_ntlm_generate_xkey(&xkey);
+  blob_size = smb_ntlm_make_blob(&blob, srv_ts, user_challenge, tgt, tgt_sz,
+                                 srv_ts + 4200);
+
+  lm2   = smb_lm2_response(&hash_v2, srv_challenge, smb_ntlm_generate_challenge());
+  ntlm2 = smb_ntlm2_response(&hash_v2, srv_challenge, blob, blob_size);
+  smb_ntlm2_session_key(&hash_v2, ntlm2, &xkey, &xkey_crypt);
+
+  *token_sz = sizeof(smb_ntlmssp_auth)
+              + strlen(host) * 2
+              + strlen(domain) * 2
+              + strlen(user) * 2
+              + blob_size + 16 // Blob + HMAC
+              + 8 + 16  // LM2 Response (miniblob=user_challenge + HMAC)
+              + 16;     // Session Key
+  if (*token_sz % 2) // Align on Word
+    *token_sz += 1;
+
+  auth = (smb_ntlmssp_auth *)malloc(*token_sz);
+  assert(auth != NULL);
+  memset(auth, 0, *token_sz);
+
+  memcpy(auth->id, "NTLMSSP", 8);
+  auth->type  = SMB_NTLMSSP_CMD_AUTH;
+  auth->flags = 0x60088215;
+
+
+  __AUTH_APPEND(lm, lm2, 24, cursor)
+  __AUTH_APPEND(ntlm, ntlm2, blob_size + 16, cursor)
+
+  utf_sz = smb_to_utf16(domain, strlen(domain), &utf);
+  __AUTH_APPEND(domain, utf, utf_sz, cursor)
+  free(utf);
+  utf_sz = smb_to_utf16(user, strlen(user), &utf);
+  __AUTH_APPEND(user, utf, utf_sz, cursor)
+  free(utf);
+  utf_sz = smb_to_utf16(host, strlen(host), &utf);
+  __AUTH_APPEND(host, utf, utf_sz, cursor)
+  free(utf);
+
+  __AUTH_APPEND(session_key, &xkey_crypt, 16, cursor)
 
-  memcpy(blob->domain + 4, (void *)ucs_domain, ucs_domain_len);
-  memset(blob->domain + 4 + ucs_domain_len, 0, 4);
-  free(ucs_domain);
+  free(lm2);
+  free(ntlm2);
 
-  return (sizeof(smb_ntlm_blob_t) + ucs_domain_len + 8);
+  *token = auth;
 }

+ 40 - 37
src/smb_session.c

@@ -24,7 +24,7 @@
 #include "bdsm/debug.h"
 #include "bdsm/smb_session.h"
 #include "bdsm/smb_ntlm.h"
-#include "bdsm/smb_gss.h"
+#include "bdsm/smb_spnego.h"
 #include "bdsm/smb_transport.h"
 
 static int        smb_negotiate(smb_session *s);
@@ -38,12 +38,12 @@ smb_session   *smb_session_new()
   memset((void *)s, 0, sizeof(smb_session));
 
   // Explicitly sets pointer to NULL, insted of 0
-  s->gss.spnego.value   = NULL;
+  s->spnego.init        = NULL;
+  s->spnego.asn1_def    = NULL;
+  s->xsec.tgt_info      = NULL;
   s->transport.session  = NULL;
   s->shares             = NULL;
 
-  s->gss.ctx = GSS_C_NO_CONTEXT;
-
   return (s);
 }
 
@@ -58,9 +58,12 @@ void            smb_session_destroy(smb_session *s)
       s->transport.session = NULL;
     }
 
-    if (s->gss.spnego.value != NULL)
-      free(s->gss.spnego.value);
-
+    if (s->spnego.init != NULL)
+      free(s->spnego.init);
+    if (s->spnego.asn1_def != NULL)
+      asn1_delete_structure(&s->spnego.asn1_def);
+    if (s->xsec.tgt_info != NULL)
+      free(s->xsec.tgt_info);
     free(s);
   }
 }
@@ -119,7 +122,7 @@ int             smb_session_send_msg(smb_session *s, smb_message *msg)
   assert(s->transport.session != NULL);
   assert(msg != NULL && msg->packet != NULL);
 
-  msg->packet->header.uid = s->uid;
+  msg->packet->header.uid = s->srv.uid;
 
   s->transport.pkt_init(s->transport.session);
 
@@ -190,9 +193,8 @@ static int        smb_negotiate(smb_session *s)
   s->srv.dialect        = nego->dialect_index;
   s->srv.security_mode  = nego->security_mode;
   s->srv.caps           = nego->caps;
-  s->srv.session_key    = nego->session_key;
-  s->srv.challenge      = nego->challenge;
   s->srv.ts             = nego->ts;
+  s->srv.session_key    = nego->session_key;
 
   // Copy SPNEGO supported mechanisms  token for later usage (login_gss())
   if (smb_session_supports(s, SMB_SESSION_XSEC))
@@ -200,12 +202,15 @@ static int        smb_negotiate(smb_session *s)
     BDSM_dbg("Server is supporting extended security\n");
 
     nego_xsec             = (smb_nego_xsec_resp *)nego;
-    s->gss.spnego.length  = nego_xsec->bct - 16;
-    s->gss.spnego.value   = malloc(s->gss.spnego.length);
+    s->spnego.init_sz     = nego_xsec->bct - 16;
+    s->spnego.init        = malloc(s->spnego.init_sz);
 
-    assert(s->gss.spnego.value != NULL);
-    memcpy(s->gss.spnego.value, nego_xsec->gssapi, s->gss.spnego.length);
+    assert(s->spnego.init != NULL);
+    memcpy(s->spnego.init, nego_xsec->gssapi, s->spnego.init_sz);
   }
+  else
+    s->srv.challenge      = nego->challenge;
+
   // Yeah !
   s->state              = SMB_STATE_DIALECT_OK;
 
@@ -216,14 +221,15 @@ static int        smb_negotiate(smb_session *s)
     return (0);
 }
 
+
 static int        smb_session_login_ntlm(smb_session *s, const char *domain,
                                          const char *user, const char *password)
 {
-  smb_message         answer;
-  smb_message         *msg = NULL;
-  smb_session_req     *req = NULL;
+  smb_message           answer;
+  smb_message           *msg = NULL;
+  smb_session_req       *req = NULL;
   uint8_t               *ntlm2 = NULL;
-  smb_ntlmh_t           hash_v2;
+  smb_ntlmh             hash_v2;
   uint64_t              user_challenge;
   uint8_t               blob[128];
   size_t                blob_size;
@@ -231,16 +237,13 @@ static int        smb_session_login_ntlm(smb_session *s, const char *domain,
   msg = smb_message_new(SMB_CMD_SETUP, 512);
   smb_message_set_default_flags(msg);
   smb_message_set_andx_members(msg);
-  req = (smb_session_req *)msg->packet->payload;
 
-  req->wct              = (sizeof(smb_session_req) - 3) / 2;
-  req->max_buffer       = SMB_IO_BUFSIZE
-                          - sizeof(smb_header);
-  req->max_buffer      -= sizeof(netbios_session_packet);
-  req->max_buffer      -= sizeof(smb_packet);
+  req = (smb_session_req *)msg->packet->payload;
+  req->wct              = 13;
+  req->max_buffer       = SMB_IO_BUFSIZE;
   req->mpx_count        = 16; // XXX ?
   req->vc_count         = 1;
-  req->session_key      = s->srv.session_key;
+  //req->session_key      = s->srv.session_key; // XXX Useless on the wire?
   req->caps             = s->srv.caps; // XXX caps & our_caps_mask
 
   smb_message_advance(msg, sizeof(smb_session_req));
@@ -249,17 +252,16 @@ static int        smb_session_login_ntlm(smb_session *s, const char *domain,
 
   // LM2 Response
   smb_ntlm2_hash(user, password, domain, &hash_v2);
-  ntlm2 = smb_ntlm2_response(&hash_v2, s->srv.challenge,
-                             (void *)&user_challenge, 8);
+  ntlm2 = smb_lm2_response(&hash_v2, s->srv.challenge, user_challenge);
   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);
+  // NTLM2 Response (Win7 unsupported, replaced by extended security)
+  // blob_size = smb_ntlm_make_blob((smb_ntlm_blob *)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;
@@ -272,7 +274,7 @@ static int        smb_session_login_ntlm(smb_session *s, const char *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_put_utf16(msg, "", SMB_LANMAN, strlen(SMB_LANMAN));
   smb_message_put16(msg, 0);
 
   req->payload_size = msg->cursor - sizeof(smb_session_req);
@@ -300,8 +302,9 @@ static int        smb_session_login_ntlm(smb_session *s, const char *domain,
 
   if (r->action & 0x0001)
     s->guest = 1;
-  s->state  = SMB_STATE_SESSION_OK;
-  s->uid    = answer.packet->header.uid;
+
+  s->srv.uid  = answer.packet->header.uid;
+  s->state    = SMB_STATE_SESSION_OK;
 
   return (1);
 }
@@ -312,7 +315,7 @@ int             smb_session_login(smb_session *s, const char *domain,
   assert(s != NULL && user != NULL && password != NULL);
 
   if (smb_session_supports(s, SMB_SESSION_XSEC))
-    return(smb_session_login_gss(s, domain, user, password));
+    return(smb_session_login_spnego(s, domain, user, password));
   else
     return(smb_session_login_ntlm(s, domain, user, password));
 }

+ 333 - 0
src/smb_spnego.c

@@ -0,0 +1,333 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    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 <stdio.h>
+#include <assert.h>
+
+#include "bdsm/debug.h"
+#include "bdsm/smb_session.h"
+#include "bdsm/smb_ntlm.h"
+#include "spnego/spnego_asn1.h"
+
+static const char spnego_oid[]  = "1.3.6.1.5.5.2";
+static const char ntlmssp_oid[] = "1.3.6.1.4.1.311.2.2.10";
+
+static void     asn1_display_error(const char *where, int errcode)
+{
+  BDSM_dbg("%s error: %s\n", where, asn1_strerror(errcode));
+}
+
+static int      init_asn1(smb_session *s)
+{
+  int           res;
+
+  assert (s != NULL);
+
+  if (s->spnego.asn1_def != NULL)
+    return (1);
+
+  res = asn1_array2tree(spnego_asn1_conf, &s->spnego.asn1_def, NULL);
+  if (res != ASN1_SUCCESS)
+  {
+    asn1_display_error("init_asn1", res);
+    return (0);
+  }
+  else
+  {
+    BDSM_dbg("init_asn1: ASN.1 parser initialized\n");
+    return (1);
+  }
+}
+
+static void     clean_asn1(smb_session *s)
+{
+  assert (s != NULL);
+
+  if (s->spnego.asn1_def != NULL)
+    asn1_delete_structure(&s->spnego.asn1_def);
+}
+
+static int      negotiate(smb_session *s, const char *domain, const char *user)
+{
+  smb_message           *msg = NULL;
+  smb_session_xsec_req  *req = NULL;
+  ASN1_TYPE             token;
+  void                  *ntlm;
+  size_t                ntlm_size;
+  int                   res, der_size = 128;
+  char                  der[128], err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+  msg = smb_message_new(SMB_CMD_SETUP, 512);
+  smb_message_set_default_flags(msg);
+  smb_message_set_andx_members(msg);
+  req = (smb_session_xsec_req *)msg->packet->payload;
+  msg->packet->header.mux_id  = 1;
+
+  req->wct              = 12;
+  req->max_buffer       = SMB_IO_BUFSIZE;
+  req->mpx_count        = 16; // XXX ?
+  req->vc_count         = 1;
+  req->caps             = s->srv.caps; // XXX caps & our_caps_mask
+  req->session_key      = s->srv.session_key;
+
+  smb_message_advance(msg, sizeof(smb_session_xsec_req));
+
+
+  asn1_create_element(s->spnego.asn1_def, "SPNEGO.GSSAPIContextToken", &token);
+
+  res = asn1_write_value(token, "thisMech", spnego_oid, 1);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "spnego", "negTokenInit", 1);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "spnego.negTokenInit.mechTypes", "NEW", 1);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "spnego.negTokenInit.mechTypes.?1", ntlmssp_oid, 1);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "spnego.negTokenInit.reqFlags", NULL, 0);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "spnego.negTokenInit.mechListMIC", NULL, 0);
+  if (res != ASN1_SUCCESS) goto error;
+
+  smb_ntlmssp_negotiate(domain, domain, &ntlm, &ntlm_size);
+  res = asn1_write_value(token, "spnego.negTokenInit.mechToken", ntlm, ntlm_size);
+  free(ntlm);
+  if (res != ASN1_SUCCESS) goto error;
+
+  res = asn1_der_coding(token, "", der, &der_size, err_desc);
+  if (res != ASN1_SUCCESS)
+  {
+    smb_message_destroy(msg);
+    BDSM_dbg("Encoding error: %s", err_desc);
+    return (0);
+  }
+
+  smb_message_append(msg, der, der_size);
+  smb_message_put_utf16(msg, "", SMB_OS, strlen(SMB_OS));
+  smb_message_put16(msg, 0);
+  smb_message_put_utf16(msg, "", SMB_LANMAN, strlen(SMB_LANMAN));
+  smb_message_put16(msg, 0);
+  smb_message_put16(msg, 0);
+
+  req->xsec_blob_size = der_size;
+  req->payload_size   = msg->cursor - sizeof(smb_session_xsec_req);
+
+  asn1_delete_structure(&token);
+
+  if (!smb_session_send_msg(s, msg))
+  {
+    smb_message_destroy(msg);
+    BDSM_dbg("Unable to send Session Setup AndX (NTLMSSP_NEGOTIATE) message\n");
+    return (0);
+  }
+
+  smb_message_destroy(msg);
+  return (1);
+
+  error:
+    asn1_display_error("smb_session_login negotiate()", res);
+    smb_message_destroy(msg);
+    return(0);
+}
+
+static int      challenge(smb_session *s)
+{
+  char                  err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+  char                  resp_token[256];
+  smb_message           msg;
+  smb_session_xsec_resp *resp;
+  smb_ntlmssp_challenge *challenge;
+  ASN1_TYPE             token;
+  int                   res, resp_token_size = 256;
+
+  assert (s != NULL);
+
+  if (smb_session_recv_msg(s, &msg) == 0)
+  {
+    BDSM_dbg("spnego challenge(): Unable to receive message\n");
+    return (0);
+  }
+
+  if (msg.packet->header.status != NT_STATUS_MORE_PROCESSING_REQUIRED)
+  {
+    BDSM_dbg("spnego challenge(): Bad status (0x%x)\n",
+             msg.packet->header.status);
+    return (0);
+  }
+
+  resp = (smb_session_xsec_resp *)msg.packet->payload;
+
+  asn1_create_element(s->spnego.asn1_def, "SPNEGO.NegotiationToken",
+                      &token);
+  res = asn1_der_decoding(&token, resp->payload, resp->xsec_blob_size,
+                          err_desc);
+  if (res != ASN1_SUCCESS)
+  {
+    asn1_delete_structure(&token);
+    asn1_display_error("NegTokenResp parsing", res);
+    BDSM_dbg("Parsing error detail: %s\n", err_desc);
+    return (0);
+  }
+
+  // XXX Check the value of "negTokenResp.negResult"
+
+  res = asn1_read_value(token, "negTokenResp.responseToken", resp_token,
+                        &resp_token_size);
+  if (res != ASN1_SUCCESS)
+  {
+    asn1_delete_structure(&token);
+    asn1_display_error("NegTokenResp read responseToken", res);
+    return (0);
+  }
+
+  // We got the server challenge, yeaaah.
+  challenge = (smb_ntlmssp_challenge *)resp_token;
+  s->xsec.tgt_info_sz = challenge->tgt_len;
+  s->xsec.tgt_info = malloc(s->xsec.tgt_info_sz);
+  assert(s->xsec.tgt_info != NULL);
+  memcpy(s->xsec.tgt_info,
+         challenge->data + challenge->tgt_offset - sizeof(smb_ntlmssp_challenge),
+         s->xsec.tgt_info_sz);
+  //s->srv.challenge = *((uint64_t *)(resp_token + 24));
+  s->srv.challenge = challenge->challenge;
+  s->srv.uid       = msg.packet->header.uid;
+
+  fprintf(stderr, "Server challenge is 0x%lx\n", s->srv.challenge);
+
+  return (1);
+}
+
+static int      auth(smb_session *s, const char *domain, const char *user,
+                     const char *password)
+{
+  smb_message           *msg = NULL, resp;
+  smb_session_xsec_req  *req = NULL;
+  ASN1_TYPE             token;
+  void                  *ntlm;
+  size_t                ntlm_size;
+  int                   res, der_size = 512;
+  char                  der[512], err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+  msg = smb_message_new(SMB_CMD_SETUP, 512);
+  smb_message_set_default_flags(msg);
+  smb_message_set_andx_members(msg);
+  msg->packet->header.mux_id = 2;
+  req = (smb_session_xsec_req *)msg->packet->payload;
+
+  req->wct              = 12;
+  req->max_buffer       = SMB_IO_BUFSIZE;
+  req->mpx_count        = 16; // XXX ?
+  req->vc_count         = 1;
+  req->caps             = s->srv.caps; // XXX caps & our_caps_mask
+  req->session_key      = s->srv.session_key;
+
+  smb_message_advance(msg, sizeof(smb_session_xsec_req));
+
+
+  asn1_create_element(s->spnego.asn1_def, "SPNEGO.NegotiationToken", &token);
+
+  // Select a response message type
+  res = asn1_write_value(token, "", "negTokenResp", 1);
+  if (res != ASN1_SUCCESS) goto error;
+
+  // Delete all optionnal field except 'ResponseToken'
+  res = asn1_write_value(token, "negTokenResp.negResult", NULL, 0);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "negTokenResp.supportedMech", NULL, 0);
+  if (res != ASN1_SUCCESS) goto error;
+  res = asn1_write_value(token, "negTokenResp.mechListMIC", NULL, 0);
+  if (res != ASN1_SUCCESS) goto error;
+
+
+  smb_ntlmssp_response(s->srv.challenge, s->srv.ts - 4200, domain, domain, user,
+                       password, s->xsec.tgt_info, s->xsec.tgt_info_sz,
+                       &ntlm, &ntlm_size);
+  res = asn1_write_value(token, "negTokenResp.responseToken", ntlm, ntlm_size);
+  if (res != ASN1_SUCCESS) goto error;
+
+  res = asn1_der_coding(token, "", der, &der_size, err_desc);
+  if (res != ASN1_SUCCESS)
+  {
+    smb_message_destroy(msg);
+    BDSM_dbg("Encoding error: %s", err_desc);
+    return (0);
+  }
+
+  smb_message_append(msg, der, der_size);
+  if (msg->cursor % 2)
+    smb_message_put8(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_LANMAN));
+  smb_message_put16(msg, 0);
+  smb_message_put16(msg, 0); // Empty PDC name
+
+  req->xsec_blob_size = der_size;
+  req->payload_size   = msg->cursor - sizeof(smb_session_xsec_req);
+
+  asn1_delete_structure(&token);
+
+  if (!smb_session_send_msg(s, msg))
+  {
+    smb_message_destroy(msg);
+    BDSM_dbg("Unable to send Session Setup AndX (NTLMSSP_AUTH) message\n");
+    return (0);
+  }
+
+  if (smb_session_recv_msg(s, &resp) == 0)
+    return (0);
+
+  if (resp.packet->header.status != NT_STATUS_SUCCESS)
+    return (0);
+  else
+    return (1);
+
+  error:
+    asn1_display_error("smb_session_login auth()", res);
+    smb_message_destroy(msg);
+    return(0);
+}
+
+int             smb_session_login_spnego(smb_session *s, const char *domain,
+                                         const char *user, const char *password)
+{
+  smb_message   resp;
+  int           res;
+  assert(s != NULL && domain != NULL && user != NULL && password != NULL);
+
+  if (!init_asn1(s))
+    return (0);
+
+  if (!negotiate(s, domain, user))
+    goto error;
+  if (!challenge(s))
+    goto error;
+
+  res = auth(s, domain, user, password);
+
+  clean_asn1(s);
+
+  return (res);
+
+  error:
+    BDSM_dbg("login_spnego Interrupted\n");
+    clean_asn1(s);
+    return (0);
+}
+