From 02bb4e1b8ae931d9eefa2fbd4a6f5456aca99b2b Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 4 Apr 2003 15:21:04 +0000 Subject: This is a merge of the NETLOGON schannel server code from Samba 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 | 26 ++ .../docbook/smbdotconf/security/serverschannel.xml | 24 ++ source3/include/ntdomain.h | 7 + source3/include/rpc_dce.h | 39 +- source3/param/loadparm.c | 4 + source3/rpc_parse/parse_rpc.c | 95 +++++ source3/rpc_server/srv_netlog_nt.c | 34 +- source3/rpc_server/srv_pipe.c | 422 +++++++++++++++++++-- source3/rpc_server/srv_pipe_hnd.c | 15 +- 9 files changed, 631 insertions(+), 35 deletions(-) create mode 100644 docs/docbook/smbdotconf/security/serverschannel.xml diff --git a/docs/docbook/manpages/smb.conf.5.sgml b/docs/docbook/manpages/smb.conf.5.sgml index 2fbd27b934..40c4963c8d 100644 --- a/docs/docbook/manpages/smb.conf.5.sgml +++ b/docs/docbook/manpages/smb.conf.5.sgml @@ -744,6 +744,7 @@ alias|alias|alias|alias... root dir root directory security + server schannel server string set primary group script show add printer wizard @@ -6922,6 +6923,31 @@ print5|My Printer 5 + + server schannel (G) + + + This controls whether the server offers or even + demands the use of the netlogon schannel. + server schannel = no does not + offer the schannel, server schannel = + auto offers the schannel but does not + enforce it, and server schannel = + yes denies access if the client is not + able to speak netlogon schannel. This is only the case + for Windows NT4 before SP4. + + Please note that with this set to + no you will have to apply the + WindowsXP requireSignOrSeal-Registry patch found in + the docs/Registry subdirectory.Default: server schannel = auto + + Example: server schannel = yes/para> + + + server string (G) 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 index 0000000000..05261fa417 --- /dev/null +++ b/docs/docbook/smbdotconf/security/serverschannel.xml @@ -0,0 +1,24 @@ + + server schannel (G) + + + This controls whether the server offers or even + demands the use of the netlogon schannel. + server schannel = no does not + offer the schannel, server schannel = + auto offers the schannel but does not + enforce it, and server schannel = + yes denies access if the client is not + able to speak netlogon schannel. This is only the case + for Windows NT4 before SP4. + + Please note that with this set to + no you will have to apply the + WindowsXP requireSignOrSeal-Registry patch found in + the docs/Registry subdirectory.Default: server schannel = auto + + Example: server schannel = yes/para> + + \ No newline at end of file diff --git a/source3/include/ntdomain.h b/source3/include/ntdomain.h index cf5bb5046c..d02195b378 100644 --- a/source3/include/ntdomain.h +++ b/source3/include/ntdomain.h @@ -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. */ diff --git a/source3/include/rpc_dce.h b/source3/include/rpc_dce.h index 6a8c650650..7e8bc3949e 100644 --- a/source3/include/rpc_dce.h +++ b/source3/include/rpc_dce.h @@ -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; diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 4e2536cf89..dc922906cb 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -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) diff --git a/source3/rpc_parse/parse_rpc.c b/source3/rpc_parse/parse_rpc.c index fafbbb1965..baa70ae137 100644 --- a/source3/rpc_parse/parse_rpc.c +++ b/source3/rpc_parse/parse_rpc.c @@ -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; +} + diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c index 6182da53d9..76c1d98dab 100644 --- a/source3/rpc_server/srv_netlog_nt.c +++ b/source3/rpc_server/srv_netlog_nt.c @@ -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; diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index 5d8b7d39e9..9425ccbe4d 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -43,6 +43,13 @@ #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. ****************************************************************************/ diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index 8cb81b9c6d..125f603771 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -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. -- cgit