This is a merge of the NETLOGON schannel server code from Samba
authorVolker Lendecke <vlendec@samba.org>
Fri, 4 Apr 2003 15:21:04 +0000 (15:21 +0000)
committerVolker Lendecke <vlendec@samba.org>
Fri, 4 Apr 2003 15:21:04 +0000 (15:21 +0000)
TNG. Actually, it exists in the main Samba cvs tree in APPLIANCE_TNG
as I found out later :-)

It adds a new parameter: server schannel = yes/auto/no defaulting to
auto.

What does this mean to the user: No requireSignOrSeal registry patch
for XP anymore.

Many thanks for this code to Luke Leighton, Elrond and anybody else I
forgot to mention.

My next thing will be to see if this applies cleanly to 3_0.

Please test and comment!

Volker
(This used to be commit e1f953241eb020f19fe657f29afdae28dcf5a03b)

docs/docbook/manpages/smb.conf.5.sgml
docs/docbook/smbdotconf/security/serverschannel.xml [new file with mode: 0644]
source3/include/ntdomain.h
source3/include/rpc_dce.h
source3/param/loadparm.c
source3/rpc_parse/parse_rpc.c
source3/rpc_server/srv_netlog_nt.c
source3/rpc_server/srv_pipe.c
source3/rpc_server/srv_pipe_hnd.c

index 2fbd27b934423148a4376959d7797c37b3ab9a62..40c4963c8da3feea8d26bfa3b558c75d230c6df8 100644 (file)
@@ -744,6 +744,7 @@ alias|alias|alias|alias...
                <listitem><para><link linkend="ROOTDIR"><parameter>root dir</parameter></link></para></listitem>
                <listitem><para><link linkend="ROOTDIRECTORY"><parameter>root directory</parameter></link></para></listitem>
                <listitem><para><link linkend="SECURITY"><parameter>security</parameter></link></para></listitem>
+               <listitem><para><link linkend="SERVERSCHANNEL"><parameter>server schannel</parameter></link></para></listitem>
                <listitem><para><link linkend="SERVERSTRING"><parameter>server string</parameter></link></para></listitem>
                <listitem><para><link linkend="SETPRIMARYGROUPSCRIPT"><parameter>set primary group script</parameter></link></para></listitem>
                <listitem><para><link linkend="SHOWADDPRINTERWIZARD"><parameter>show add printer wizard</parameter></link></para></listitem>
@@ -6922,6 +6923,31 @@ print5|My Printer 5
                </varlistentry>
 
 
+               <varlistentry>
+               <term><anchor id="SERVERSCHANNEL"/>server schannel (G)</term>
+               <listitem>
+
+               <para>This controls whether the server offers or even
+               demands the use of the netlogon schannel.
+               <parameter>server schannel = no</parameter> does not
+               offer the schannel, <parameter>server schannel =
+               auto</parameter> offers the schannel but does not
+               enforce it, and <parameter>server schannel =
+               yes</parameter> denies access if the client is not
+               able to speak netlogon schannel. This is only the case
+               for Windows NT4 before SP4.</para>
+
+               <para>Please note that with this set to
+               <parameter>no</parameter> you will have to apply the
+               WindowsXP requireSignOrSeal-Registry patch found in
+               the docs/Registry subdirectory.</para
+
+               <para>Default: <command>server schannel = auto</command></para>
+
+               <para>Example: <command>server schannel = yes</command>/para>
+               </listitem>
+               </varlistentry>
+
                <varlistentry>
                <term><anchor id="SERVERSTRING"/>server string (G)</term>
                <listitem><para>This controls what string will show up in the 
diff --git a/docs/docbook/smbdotconf/security/serverschannel.xml b/docs/docbook/smbdotconf/security/serverschannel.xml
new file mode 100644 (file)
index 0000000..05261fa
--- /dev/null
@@ -0,0 +1,24 @@
+<samba:parameter xmlns:samba="http://samba.org/common">
+               <term><anchor id="SERVERSCHANNEL"/>server schannel (G)</term>
+               <listitem>
+
+               <para>This controls whether the server offers or even
+               demands the use of the netlogon schannel.
+               <parameter>server schannel = no</parameter> does not
+               offer the schannel, <parameter>server schannel =
+               auto</parameter> offers the schannel but does not
+               enforce it, and <parameter>server schannel =
+               yes</parameter> denies access if the client is not
+               able to speak netlogon schannel. This is only the case
+               for Windows NT4 before SP4.</para>
+
+               <para>Please note that with this set to
+               <parameter>no</parameter> you will have to apply the
+               WindowsXP requireSignOrSeal-Registry patch found in
+               the docs/Registry subdirectory.</para
+
+               <para>Default: <command>server schannel = auto</command></para>
+
+               <para>Example: <command>server schannel = yes</command>/para>
+               </listitem>
+               <samba:parameter>
\ No newline at end of file
index cf5bb5046c7386d5267df58bd42d7f2abf441f7a..d02195b378b6a244123f1e2d76f5e4b5e9e9e272 100644 (file)
@@ -192,6 +192,13 @@ typedef struct pipes_struct
        uint32 ntlmssp_seq_num;
        struct dcinfo dc; /* Keeps the creds data. */
 
+        /* Hmm. In my understanding the authentication happens
+           implicitly later, so there are no two stages for
+           schannel. */
+
+       BOOL netsec_auth_validated;
+       struct netsec_auth_struct netsec_auth;
+
        /*
         * Windows user info.
         */
index 6a8c6506509698b02ee026bf38dd61087e71d55f..7e8bc3949e3821c67498aa5c0a9aaf218df1d36f 100644 (file)
@@ -55,6 +55,13 @@ enum RPC_PKT_TYPE
 #define NTLMSSP_AUTH_TYPE 0xa
 #define NTLMSSP_AUTH_LEVEL 0x6
 
+/* Netlogon schannel auth type and level */
+#define NETSEC_AUTH_TYPE 0x44
+#define NETSEC_AUTH_LEVEL 0x6
+#define NETSEC_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
+#define RPC_AUTH_NETSEC_CHK_LEN 0x20
+#define NETLOGON_NEG_SCHANNEL    0x40000000
+
 /* Maximum PDU fragment size. */
 #define MAX_PDU_FRAG_LEN 0x1630
 /* #define MAX_PDU_FRAG_LEN 0x10b8             this is what w2k sets */
@@ -199,6 +206,34 @@ typedef struct rpc_hdr_auth_info
 
 #define RPC_HDR_AUTH_LEN 8
 
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind request */
+/* RPC_AUTH_NETSEC_NEG */
+typedef struct rpc_auth_netsec_neg_info
+{
+       uint32 unknown1;
+       uint32 unknown2;
+       fstring domain; /* calling workstations's domain */
+       fstring myname; /* calling workstation's name */
+} RPC_AUTH_NETSEC_NEG;
+
+/* attached to the end of encrypted rpc requests and responses */
+/* RPC_AUTH_NETSEC_CHK */
+typedef struct rpc_auth_netsec_chk_info
+{
+       uint8 sig  [8]; /* 77 00 7a 00 ff ff 00 00 */
+       uint8 data1[8];
+       uint8 data3[8]; /* verifier, seq num */
+       uint8 data8[8]; /* random 8-byte nonce */
+} RPC_AUTH_NETSEC_CHK;
+
+struct netsec_auth_struct
+{
+       RPC_AUTH_NETSEC_NEG netsec_neg;
+       uchar sess_key[16];
+       uint32 seq_num;
+};
+
 /* RPC_BIND_REQ - ms req bind */
 typedef struct rpc_bind_req_info
 {
@@ -248,8 +283,8 @@ typedef struct rpc_hdr_ba_info
 /* RPC_AUTH_VERIFIER */
 typedef struct rpc_auth_verif_info
 {
-       fstring signature; /* "NTLMSSP" */
-       uint32  msg_type; /* NTLMSSP_MESSAGE_TYPE (1,2,3) */
+       fstring signature; /* "NTLMSSP".. Ok, not quite anymore */
+       uint32  msg_type; /* NTLMSSP_MESSAGE_TYPE (1,2,3) and 5 for schannel */
 
 } RPC_AUTH_VERIFIER;
 
index 4e2536cf8946e19f19a4590f47bcba320102915a..dc922906cb35999f86993be0b74adf6ef4860652 100644 (file)
@@ -242,6 +242,7 @@ typedef struct
        BOOL bDomainLogons;
        BOOL bEncryptPasswords;
        BOOL bUpdateEncrypt;
+       int  serverSchannel;
        BOOL bStripDot;
        BOOL bNullPasswords;
        BOOL bObeyPamRestrictions;
@@ -750,6 +751,7 @@ static struct parm_struct parm_table[] = {
        {"auth methods", P_LIST, P_GLOBAL, &Globals.AuthMethods, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
        {"encrypt passwords", P_BOOL, P_GLOBAL, &Globals.bEncryptPasswords, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
        {"update encrypted", P_BOOL, P_GLOBAL, &Globals.bUpdateEncrypt, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"server schannel", P_ENUM, P_GLOBAL, &Globals.serverSchannel, NULL, enum_bool_auto, FLAG_BASIC},
        {"allow trusted domains", P_BOOL, P_GLOBAL, &Globals.bAllowTrustedDomains, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
        {"hosts equiv", P_STRING, P_GLOBAL, &Globals.szHostsEquiv, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
        {"idmap backend", P_STRING, P_GLOBAL, &Globals.szIdmapBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
@@ -1352,6 +1354,7 @@ static void init_globals(void)
        Globals.paranoid_server_security = True;
        Globals.bEncryptPasswords = True;
        Globals.bUpdateEncrypt = False;
+       Globals.serverSchannel = Auto;
        Globals.bReadRaw = True;
        Globals.bWriteRaw = True;
        Globals.bReadPrediction = False;
@@ -1690,6 +1693,7 @@ FN_GLOBAL_BOOL(lp_obey_pam_restrictions, &Globals.bObeyPamRestrictions)
 FN_GLOBAL_BOOL(lp_strip_dot, &Globals.bStripDot)
 FN_GLOBAL_BOOL(lp_encrypted_passwords, &Globals.bEncryptPasswords)
 FN_GLOBAL_BOOL(lp_update_encrypted, &Globals.bUpdateEncrypt)
+FN_GLOBAL_INTEGER(lp_server_schannel, &Globals.serverSchannel)
 FN_GLOBAL_BOOL(lp_syslog_only, &Globals.bSyslogOnly)
 FN_GLOBAL_BOOL(lp_timestamp_logs, &Globals.bTimestampLogs)
 FN_GLOBAL_BOOL(lp_debug_hires_timestamp, &Globals.bDebugHiresTimestamp)
index fafbbb1965ad43a3a1a7b1817a8b409a0d78a8a8..baa70ae13785983bdefad9151011f3a5b8d0b473 100644 (file)
@@ -691,6 +691,30 @@ BOOL smb_io_rpc_auth_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_stru
        return True;
 }
 
+/*******************************************************************
+ This parses an RPC_AUTH_VERIFIER for NETLOGON schannel. I thing
+ assuming "NTLMSSP" in sm_io_rpc_auth_verifier is somewhat wrong.
+ I have to look at that later...
+********************************************************************/
+
+BOOL smb_io_rpc_netsec_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_struct *ps, int depth)
+{
+       if (rav == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_verifier");
+       depth++;
+
+       /* "NTLMSSP" */
+       if(!prs_string("signature", ps, depth, rav->signature, strlen(rav->signature),
+                       sizeof(rav->signature)))
+               return False;
+       if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type)) /* NTLMSSP_MESSAGE_TYPE */
+               return False;
+
+       return True;
+}
+
 /*******************************************************************
  Inits an RPC_AUTH_NTLMSSP_NEG structure.
 ********************************************************************/
@@ -1104,3 +1128,74 @@ BOOL smb_io_rpc_auth_ntlmssp_chk(const char *desc, RPC_AUTH_NTLMSSP_CHK *chk, pr
 
        return True;
 }
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NETSEC_NEG structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_netsec_neg(const char *desc, RPC_AUTH_NETSEC_NEG *neg,
+                               prs_struct *ps, int depth)
+{
+       if (neg == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_netsec_neg");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("unknown1", ps, depth, &neg->unknown1))
+               return False;
+       if(!prs_uint32("unknown2", ps, depth, &neg->unknown2))
+               return False;
+       if(!prs_string("domain  ", ps, depth, neg->domain,
+                      strlen(&ps->data_p[ps->data_offset]), sizeof(neg->domain)))
+               return False;
+       if(!prs_string("myname  ", ps, depth, neg->myname, 
+                      strlen(&ps->data_p[ps->data_offset]), sizeof(neg->myname)))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+creates an RPC_AUTH_NETSEC_CHK structure.
+********************************************************************/
+BOOL init_rpc_auth_netsec_chk(RPC_AUTH_NETSEC_CHK * chk,
+                             const uchar sig[8],
+                             const uchar data1[8],
+                             const uchar data3[8], const uchar data8[8])
+{
+       if (chk == NULL)
+               return False;
+
+       memcpy(chk->sig, sig, sizeof(chk->sig));
+       memcpy(chk->data1, data1, sizeof(chk->data1));
+       memcpy(chk->data3, data3, sizeof(chk->data3));
+       memcpy(chk->data8, data8, sizeof(chk->data8));
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an RPC_AUTH_NETSEC_CHK structure.
+********************************************************************/
+BOOL smb_io_rpc_auth_netsec_chk(const char *desc, RPC_AUTH_NETSEC_CHK * chk,
+                               prs_struct *ps, int depth)
+{
+       if (chk == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_netsec_chk");
+       depth++;
+
+       prs_uint8s(False, "sig  ", ps, depth, chk->sig, sizeof(chk->sig));
+       prs_uint8s(False, "data3", ps, depth, chk->data3, sizeof(chk->data3));
+       prs_uint8s(False, "data1", ps, depth, chk->data1, sizeof(chk->data1));
+       prs_uint8s(False, "data8", ps, depth, chk->data8, sizeof(chk->data8));
+
+       return True;
+}
+
index 6182da53d99fa5f9573a2685bdccc86c89d2ead9..76c1d98dabd99a0fdbe21d26441dcc4f0d7cfd68 100644 (file)
@@ -334,6 +334,13 @@ NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u)
 
        srv_time.time = 0;
 
+       if ( (lp_server_schannel() == True) &&
+            ((q_u->clnt_flgs.neg_flags & NETLOGON_NEG_SCHANNEL) == 0) ) {
+
+               /* schannel must be used, but client did not offer it. */
+               status = NT_STATUS_ACCESS_DENIED;
+       }
+
        rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
 
        if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
@@ -366,9 +373,18 @@ NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u)
        
        srv_flgs.neg_flags = 0x000001ff;
 
+       if (lp_server_schannel() != False) {
+               srv_flgs.neg_flags |= NETLOGON_NEG_SCHANNEL;
+       }
+
        /* set up the LSA AUTH 2 response */
        init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status);
 
+       if (NT_STATUS_IS_OK(status)) {
+               extern struct dcinfo last_dcinfo;
+               last_dcinfo = p->dc;
+       }
+
        return r_u->status;
 }
 
@@ -523,7 +539,23 @@ NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *
  
        if (!get_valid_user_struct(p->vuid))
                return NT_STATUS_NO_SUCH_USER;
-    
+
+
+       if ( (lp_server_schannel() == True) && (!p->netsec_auth_validated) ) {
+               /* 'server schannel = yes' should enforce use of
+                  schannel, the client did offer it in auth2, but
+                  obviously did not use it. */
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (p->netsec_auth_validated) {
+               /* The client opens a second RPC NETLOGON pipe without
+                   doing a auth2. The session key for the schannel is
+                   re-used from the auth2 the client did before. */
+               extern struct dcinfo last_dcinfo;
+               p->dc = last_dcinfo;
+       }
+
        /* checks and updates credentials.  creates reply credentials */
        if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->sam_id.client.cred, &srv_cred)))
                return NT_STATUS_INVALID_HANDLE;
index 5d8b7d39e91193cf7f575c2162b2706e4dab7b36..9425ccbe4d5bb09753d43bc42c58aae67f6a917a 100644 (file)
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_RPC_SRV
 
+/*************************************************************
+ HACK Alert!
+ We need to transfer the session key from one rpc bind to the
+ next. This is the way the netlogon schannel works.
+**************************************************************/
+struct dcinfo last_dcinfo;
+
 static void NTLMSSPcalc_p( pipes_struct *p, unsigned char *data, int len)
 {
     unsigned char *hash = p->ntlmssp_hash;
@@ -115,6 +122,9 @@ BOOL create_next_pdu(pipes_struct *p)
        if(p->ntlmssp_auth_validated)
                data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN);
 
+       if(p->netsec_auth_validated)
+               data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_CHK_LEN);
+
        /*
         * The amount we send is the minimum of the available
         * space and the amount left to send.
@@ -148,6 +158,10 @@ BOOL create_next_pdu(pipes_struct *p)
                p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
                                        RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN;
                p->hdr.auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+       } else if (p->netsec_auth_validated) {
+               p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
+                       RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_CHK_LEN;
+               p->hdr.auth_len = RPC_AUTH_NETSEC_CHK_LEN;
        } else {
                p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len;
                p->hdr.auth_len = 0;
@@ -192,7 +206,7 @@ BOOL create_next_pdu(pipes_struct *p)
                return False;
        }
 
-       if (p->hdr.auth_len > 0) {
+       if (p->ntlmssp_auth_validated) {
                uint32 crc32 = 0;
                char *data;
 
@@ -239,6 +253,47 @@ BOOL create_next_pdu(pipes_struct *p)
                }
        }
 
+       if (p->netsec_auth_validated) {
+               char *data;
+               RPC_HDR_AUTH auth_info;
+               static const uchar netsec_sig[8] = NETSEC_SIGNATURE;
+               static const uchar nullbytes[8] = { 0,0,0,0,0,0,0,0 };
+
+               RPC_AUTH_NETSEC_CHK verf;
+               prs_struct rverf;
+               prs_struct rauth;
+
+               uchar sign[8];
+
+               data = prs_data_p(&outgoing_pdu) + data_pos;
+
+               init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, NETSEC_AUTH_LEVEL, 
+                                 RPC_HDR_AUTH_LEN, 1);
+
+               if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+                       DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
+                       prs_mem_free(&outgoing_pdu);
+                       return False;
+               }
+
+               prs_init(&rverf, 0, p->mem_ctx, MARSHALL);
+               prs_init(&rauth, 0, p->mem_ctx, MARSHALL);
+
+               memset(sign, 0, sizeof(sign));
+               sign[3] = 0x01;
+
+               init_rpc_auth_netsec_chk(&verf, netsec_sig, nullbytes, sign, nullbytes);
+
+               if (!netsec_encode(&p->netsec_auth, &verf, data, data_len)) {
+                       DEBUG(0,("create_next_pdu: failed encode data.\n"));
+                       prs_mem_free(&outgoing_pdu);
+                       return False;
+               }
+
+               smb_io_rpc_auth_netsec_chk("", &verf, &outgoing_pdu, 0);
+               p->netsec_auth.seq_num++;
+       }
+
        /*
         * Setup the counts for this PDU.
         */
@@ -775,6 +830,7 @@ BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
        enum RPC_PKT_TYPE reply_pkt_type;
 
        p->ntlmssp_auth_requested = False;
+       p->netsec_auth_validated = False;
 
        DEBUG(5,("api_pipe_bind_req: decode request. %d\n", __LINE__));
 
@@ -840,39 +896,62 @@ BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
                        return False;
                }
 
-               /*
-                * We only support NTLMSSP_AUTH_TYPE requests.
-                */
+               if(auth_info.auth_type == NTLMSSP_AUTH_TYPE) {
 
-               if(auth_info.auth_type != NTLMSSP_AUTH_TYPE) {
-                       DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n",
-                               auth_info.auth_type ));
-                       return False;
-               }
+                       if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+                               DEBUG(0,("api_pipe_bind_req: unable to "
+                                        "unmarshall RPC_HDR_AUTH struct.\n"));
+                               return False;
+                       }
 
-               if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
-                       DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n"));
-                       return False;
-               }
+                       if(!strequal(auth_verifier.signature, "NTLMSSP")) {
+                               DEBUG(0,("api_pipe_bind_req: "
+                                        "auth_verifier.signature != NTLMSSP\n"));
+                               return False;
+                       }
 
-               if(!strequal(auth_verifier.signature, "NTLMSSP")) {
-                       DEBUG(0,("api_pipe_bind_req: auth_verifier.signature != NTLMSSP\n"));
-                       return False;
-               }
+                       if(auth_verifier.msg_type != NTLMSSP_NEGOTIATE) {
+                               DEBUG(0,("api_pipe_bind_req: "
+                                        "auth_verifier.msg_type (%d) != NTLMSSP_NEGOTIATE\n",
+                                        auth_verifier.msg_type));
+                               return False;
+                       }
 
-               if(auth_verifier.msg_type != NTLMSSP_NEGOTIATE) {
-                       DEBUG(0,("api_pipe_bind_req: auth_verifier.msg_type (%d) != NTLMSSP_NEGOTIATE\n",
-                               auth_verifier.msg_type));
-                       return False;
-               }
+                       if(!smb_io_rpc_auth_ntlmssp_neg("", &ntlmssp_neg, rpc_in_p, 0)) {
+                               DEBUG(0,("api_pipe_bind_req: "
+                                        "Failed to unmarshall RPC_AUTH_NTLMSSP_NEG.\n"));
+                               return False;
+                       }
+
+                       p->ntlmssp_chal_flags = SMBD_NTLMSSP_NEG_FLAGS;
+                       p->ntlmssp_auth_requested = True;
+
+               } else if (auth_info.auth_type == NETSEC_AUTH_TYPE) {
+
+                       RPC_AUTH_NETSEC_NEG neg;
+                       struct netsec_auth_struct *a = &(p->netsec_auth);
+
+                       if (!smb_io_rpc_auth_netsec_neg("", &neg, rpc_in_p, 0)) {
+                               DEBUG(0,("api_pipe_bind_req: "
+                                        "Could not unmarshal SCHANNEL auth neg\n"));
+                               return False;
+                       }
 
-               if(!smb_io_rpc_auth_ntlmssp_neg("", &ntlmssp_neg, rpc_in_p, 0)) {
-                       DEBUG(0,("api_pipe_bind_req: Failed to unmarshall RPC_AUTH_NTLMSSP_NEG.\n"));
+                       p->netsec_auth_validated = True;
+
+                       memset(a->sess_key, 0, sizeof(a->sess_key));
+                       memcpy(a->sess_key, last_dcinfo.sess_key, sizeof(last_dcinfo.sess_key));
+
+                       a->seq_num = 0;
+
+                       DEBUG(10,("schannel auth: domain [%s] myname [%s]\n",
+                                 neg.domain, neg.myname));
+
+               } else {
+                       DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n",
+                                auth_info.auth_type ));
                        return False;
                }
-
-               p->ntlmssp_chal_flags = SMBD_NTLMSSP_NEG_FLAGS;
-               p->ntlmssp_auth_requested = True;
        }
 
        switch(p->hdr.pkt_type) {
@@ -1003,6 +1082,33 @@ BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
                auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
        }
 
+       if (p->netsec_auth_validated) {
+               RPC_AUTH_VERIFIER auth_verifier;
+               uint32 flags;
+
+               init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, NETSEC_AUTH_LEVEL, RPC_HDR_AUTH_LEN, 1);
+               if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
+                       goto err_exit;
+               }
+
+               /*** NETSEC verifier ***/
+
+               init_rpc_auth_verifier(&auth_verifier, "\001", 0x0);
+               if(!smb_io_rpc_netsec_verifier("", &auth_verifier, &out_auth, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+                       goto err_exit;
+               }
+
+               prs_align(&out_auth);
+
+               flags = 5;
+               if(!prs_uint32("flags ", &out_auth, 0, &flags))
+                       goto err_exit;
+
+               auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+       }
+
        /*
         * Create the header, now we know the length.
         */
@@ -1030,7 +1136,8 @@ BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
                goto err_exit;
        }
 
-       if(p->ntlmssp_auth_requested && !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
+       if((p->ntlmssp_auth_requested|p->netsec_auth_validated) &&
+          !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
                DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n"));
                goto err_exit;
        }
@@ -1162,6 +1269,265 @@ BOOL api_pipe_auth_process(pipes_struct *p, prs_struct *rpc_in)
        return True;
 }
 
+static void netsechash(uchar * key, uchar * data, int data_len)
+{
+       uchar hash[256];
+       uchar index_i = 0;
+       uchar index_j = 0;
+       uchar j = 0;
+       int ind;
+
+       for (ind = 0; ind < 256; ind++)
+       {
+               hash[ind] = (uchar) ind;
+       }
+
+       for (ind = 0; ind < 256; ind++)
+       {
+               uchar tc;
+
+               j += (hash[ind] + key[ind % 16]);
+
+               tc = hash[ind];
+               hash[ind] = hash[j];
+               hash[j] = tc;
+       }
+
+       for (ind = 0; ind < data_len; ind++)
+       {
+               uchar tc;
+               uchar t;
+
+               index_i++;
+               index_j += hash[index_i];
+
+               tc = hash[index_i];
+               hash[index_i] = hash[index_j];
+               hash[index_j] = tc;
+
+               t = hash[index_i] + hash[index_j];
+               data[ind] ^= hash[t];
+       }
+}
+
+void dump_data_pw(const char *msg, const uchar * data, size_t len)
+{
+#ifdef DEBUG_PASSWORD
+       DEBUG(11, ("%s", msg));
+       if (data != NULL && len > 0)
+       {
+               dump_data(11, data, len);
+       }
+#endif
+}
+
+BOOL netsec_encode(struct netsec_auth_struct *a,
+                  RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
+{
+       uchar dataN[4];
+       uchar digest1[16];
+       struct MD5Context ctx3;
+       uchar sess_kf0[16];
+       int i;
+
+       /* store the sequence number */
+       SIVAL(dataN, 0, a->seq_num);
+
+       for (i = 0; i < sizeof(sess_kf0); i++)
+       {
+               sess_kf0[i] = a->sess_key[i] ^ 0xf0;
+       }
+
+       dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
+       dump_data_pw("a->seq_num :\n", dataN, sizeof(dataN));
+
+       MD5Init(&ctx3);
+       MD5Update(&ctx3, dataN, 0x4);
+       MD5Update(&ctx3, verf->sig, 8);
+
+       MD5Update(&ctx3, verf->data8, 8);
+
+       dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
+       dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
+
+       hmac_md5(sess_kf0, dataN, 0x4, digest1);
+       dump_data_pw("digest1 (ebp-8):\n", digest1, sizeof(digest1));
+       hmac_md5(digest1, verf->data3, 8, digest1);
+       dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
+       netsechash(digest1, verf->data8, 8);
+
+       dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
+
+       dump_data_pw("data   :\n", data, data_len);
+       MD5Update(&ctx3, data, data_len);
+
+       {
+               char digest_tmp[16];
+               char digest2[16];
+               MD5Final(digest_tmp, &ctx3);
+               hmac_md5(a->sess_key, digest_tmp, 16, digest2);
+               dump_data_pw("digest_tmp:\n", digest_tmp, sizeof(digest_tmp));
+               dump_data_pw("digest:\n", digest2, sizeof(digest2));
+               memcpy(verf->data1, digest2, sizeof(verf->data1));
+       }
+
+       netsechash(digest1, data, data_len);
+       dump_data_pw("data:\n", data, data_len);
+
+       hmac_md5(a->sess_key, dataN, 0x4, digest1);
+       dump_data_pw("ctx:\n", digest1, sizeof(digest1));
+
+       hmac_md5(digest1, verf->data1, 8, digest1);
+
+       dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
+
+       dump_data_pw("verf->data3:\n", verf->data3, sizeof(verf->data3));
+       netsechash(digest1, verf->data3, 8);
+       dump_data_pw("verf->data3:\n", verf->data3, sizeof(verf->data3));
+
+       return True;
+}
+
+BOOL netsec_decode(struct netsec_auth_struct *a,
+                  RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
+{
+       uchar dataN[4];
+       uchar digest1[16];
+       struct MD5Context ctx3;
+       uchar sess_kf0[16];
+       int i;
+
+       /* store the sequence number */
+       SIVAL(dataN, 0, a->seq_num);
+
+       for (i = 0; i < sizeof(sess_kf0); i++)
+       {
+               sess_kf0[i] = a->sess_key[i] ^ 0xf0;
+       }
+
+       dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
+       dump_data_pw("a->seq_num :\n", dataN, sizeof(dataN));
+       hmac_md5(a->sess_key, dataN, 0x4, digest1);
+       dump_data_pw("ctx:\n", digest1, sizeof(digest1));
+
+       hmac_md5(digest1, verf->data1, 8, digest1);
+
+       dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
+       dump_data_pw("verf->data3:\n", verf->data3, sizeof(verf->data3));
+       netsechash(digest1, verf->data3, 8);
+       dump_data_pw("verf->data3_dec:\n", verf->data3, sizeof(verf->data3));
+
+       MD5Init(&ctx3);
+       MD5Update(&ctx3, dataN, 0x4);
+       MD5Update(&ctx3, verf->sig, 8);
+
+       dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
+
+       hmac_md5(sess_kf0, dataN, 0x4, digest1);
+       dump_data_pw("digest1 (ebp-8):\n", digest1, sizeof(digest1));
+       hmac_md5(digest1, verf->data3, 8, digest1);
+       dump_data_pw("netsechashkey:\n", digest1, sizeof(digest1));
+
+       dump_data_pw("verf->data8:\n", verf->data8, sizeof(verf->data8));
+       netsechash(digest1, verf->data8, 8);
+       dump_data_pw("verf->data8_dec:\n", verf->data8, sizeof(verf->data8));
+       MD5Update(&ctx3, verf->data8, 8);
+
+       dump_data_pw("data   :\n", data, data_len);
+       netsechash(digest1, data, data_len);
+       dump_data_pw("datadec:\n", data, data_len);
+
+       MD5Update(&ctx3, data, data_len);
+       {
+               uchar digest_tmp[16];
+               MD5Final(digest_tmp, &ctx3);
+               hmac_md5(a->sess_key, digest_tmp, 16, digest1);
+               dump_data_pw("digest_tmp:\n", digest_tmp, sizeof(digest_tmp));
+       }
+
+       dump_data_pw("digest:\n", digest1, sizeof(digest1));
+       dump_data_pw("verf->data1:\n", verf->data1, sizeof(verf->data1));
+
+       return memcmp(digest1, verf->data1, sizeof(verf->data1)) == 0;
+}
+
+/****************************************************************************
+ Deal with schannel processing on an RPC request.
+****************************************************************************/
+BOOL api_pipe_netsec_process(pipes_struct *p, prs_struct *rpc_in)
+{
+       /*
+        * We always negotiate the following two bits....
+        */
+       int data_len;
+       int auth_len;
+       uint32 old_offset;
+       RPC_HDR_AUTH auth_info;
+       RPC_AUTH_NETSEC_CHK netsec_chk;
+
+
+       auth_len = p->hdr.auth_len;
+
+       if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) {
+               DEBUG(0,("Incorrect auth_len %d.\n", auth_len ));
+               return False;
+       }
+
+       /*
+        * The following is that length of the data we must verify or unseal.
+        * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+        * preceeding the auth_data.
+        */
+
+       data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - 
+               RPC_HDR_AUTH_LEN - auth_len;
+       
+       DEBUG(5,("data %d auth %d\n", data_len, auth_len));
+
+       old_offset = prs_offset(rpc_in);
+
+       if(!prs_set_offset(rpc_in, old_offset + data_len)) {
+               DEBUG(0,("cannot move offset to %u.\n",
+                        (unsigned int)old_offset + data_len ));
+               return False;
+       }
+
+       if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+               DEBUG(0,("failed to unmarshall RPC_HDR_AUTH.\n"));
+               return False;
+       }
+
+       if ((auth_info.auth_type != NETSEC_AUTH_TYPE) ||
+           (auth_info.auth_level != NETSEC_AUTH_LEVEL)) {
+               DEBUG(0,("Invalid auth info %d or level %d on schannel\n",
+                        auth_info.auth_type, auth_info.auth_level));
+               return False;
+       }
+
+       if(!smb_io_rpc_auth_netsec_chk("", &netsec_chk, rpc_in, 0)) {
+               DEBUG(0,("failed to unmarshal RPC_AUTH_NETSEC_CHK.\n"));
+               return False;
+       }
+
+       if (!netsec_decode(&p->netsec_auth, &netsec_chk,
+                          prs_data_p(rpc_in)+old_offset, data_len)) {
+               DEBUG(0,("failed to decode PDU\n"));
+               return False;
+       }
+
+       /*
+        * Return the current pointer to the data offset.
+        */
+
+       if(!prs_set_offset(rpc_in, old_offset)) {
+               DEBUG(0,("failed to set offset back to %u\n",
+                        (unsigned int)old_offset ));
+               return False;
+       }
+
+       return True;
+}
+
 /****************************************************************************
  Return a user struct for a pipe user.
 ****************************************************************************/
index 8cb81b9c6d6e03ddfcbf15b894869f48e04988c6..125f6037710b88dd8f59c50eb2f61e74deb69cfe 100644 (file)
@@ -594,11 +594,18 @@ static BOOL process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p)
                 * Authentication _was_ requested and it already failed.
                 */
 
-               DEBUG(0,("process_request_pdu: RPC request received on pipe %s where \
-authentication failed. Denying the request.\n", p->name));
+               DEBUG(0,("process_request_pdu: RPC request received on pipe %s "
+                        "where authentication failed. Denying the request.\n",
+                        p->name));
                set_incoming_fault(p);
-        return False;
-    }
+               return False;
+       }
+
+       if (p->netsec_auth_validated && !api_pipe_netsec_process(p, rpc_in_p)) {
+               DEBUG(0,("process_request_pdu: failed to do schannel processing.\n"));
+               set_incoming_fault(p);
+               return False;
+       }
 
        /*
         * Check the data length doesn't go over the 15Mb limit.