diff options
author | Jeremy Allison <jra@samba.org> | 2005-10-07 01:46:19 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:04:54 -0500 |
commit | e127501d4589a5a9c92e2f400fc67bda5a8e6855 (patch) | |
tree | 69886234b25530aaa8a7027e8aeb96cc93d822d3 | |
parent | 8cbe35f6f8fea2757fb3df49c2b9baa94cf5fe84 (diff) | |
download | samba-e127501d4589a5a9c92e2f400fc67bda5a8e6855.tar.gz samba-e127501d4589a5a9c92e2f400fc67bda5a8e6855.tar.bz2 samba-e127501d4589a5a9c92e2f400fc67bda5a8e6855.zip |
r10792: Fix the "schannel not stored across client disconnects" problem.
Based on the Samba4 solution - stores data in
$samba/private/schannel_store.tdb.
This tdb is not left open but open and closed on demand.
Jeremy.
(This used to be commit a6d8a4b1ff31c5552075455dbd98cb58795958a9)
-rw-r--r-- | source3/include/secrets.h | 2 | ||||
-rw-r--r-- | source3/passdb/secrets.c | 209 | ||||
-rw-r--r-- | source3/rpc_server/srv_netlog_nt.c | 10 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe.c | 29 |
4 files changed, 231 insertions, 19 deletions
diff --git a/source3/include/secrets.h b/source3/include/secrets.h index 4b8d5db66b..f2d1afd96b 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -98,4 +98,6 @@ struct afs_keyfile { #define SECRETS_AFS_KEYFILE "SECRETS/AFS_KEYFILE" +#define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL" + #endif /* _SECRETS_H */ diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c index 743bdd1942..35ccb2c725 100644 --- a/source3/passdb/secrets.c +++ b/source3/passdb/secrets.c @@ -942,3 +942,212 @@ void secrets_fetch_ipc_userpass(char **username, char **domain, char **password) } } +/****************************************************************************** + Open or create the schannel session store tdb. +*******************************************************************************/ + +static TDB_CONTEXT *open_schannel_session_store(TALLOC_CTX *mem_ctx) +{ + TDB_DATA vers; + uint32 ver; + TDB_CONTEXT *tdb_sc = NULL; + char *fname = talloc_asprintf(mem_ctx, "%s/schannel_store.tdb", lp_private_dir()); + + if (!fname) { + return NULL; + } + + tdb_sc = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + + if (!tdb_sc) { + DEBUG(0,("open_schannel_session_store: Failed to open %s\n", fname)); + talloc_free(fname); + return NULL; + } + + vers = tdb_fetch_bystring(tdb_sc, "SCHANNEL_STORE_VERSION"); + if (vers.dptr == NULL) { + /* First opener, no version. */ + SIVAL(&ver,0,1); + vers.dptr = (char *)&ver; + vers.dsize = 4; + tdb_store_bystring(tdb_sc, "SCHANNEL_STORE_VERSION", vers, TDB_REPLACE); + vers.dptr = NULL; + } else if (vers.dsize == 4) { + ver = IVAL(vers.dptr,0); + if (ver != 1) { + tdb_close(tdb_sc); + tdb_sc = NULL; + DEBUG(0,("open_schannel_session_store: wrong version number %d in %s\n", + (int)ver, fname )); + } + } else { + tdb_close(tdb_sc); + tdb_sc = NULL; + DEBUG(0,("open_schannel_session_store: wrong version number size %d in %s\n", + (int)vers.dsize, fname )); + } + + SAFE_FREE(vers.dptr); + talloc_free(fname); + + return tdb_sc; +} + +/****************************************************************************** + Store the schannel state after an AUTH2 call. + Note we must be root here. +*******************************************************************************/ + +BOOL secrets_store_schannel_session_info(TALLOC_CTX *mem_ctx, const struct dcinfo *pdc) +{ + TDB_CONTEXT *tdb_sc = NULL; + TDB_DATA value; + BOOL ret; + char *keystr = talloc_asprintf(mem_ctx, "%s/%s", SECRETS_SCHANNEL_STATE, + pdc->remote_machine); + if (!keystr) { + return False; + } + + strupper_m(keystr); + + /* Work out how large the record is. */ + value.dsize = tdb_pack(NULL, 0, "dBBBBBfff", + pdc->sequence, + 8, pdc->seed_chal.data, + 8, pdc->clnt_chal.data, + 8, pdc->srv_chal.data, + 8, pdc->sess_key, + 16, pdc->mach_pw, + pdc->mach_acct, + pdc->remote_machine, + pdc->domain); + + value.dptr = TALLOC(mem_ctx, value.dsize); + if (!value.dptr) { + talloc_free(keystr); + return False; + } + + value.dsize = tdb_pack(value.dptr, value.dsize, "dBBBBBfff", + pdc->sequence, + 8, pdc->seed_chal.data, + 8, pdc->clnt_chal.data, + 8, pdc->srv_chal.data, + 8, pdc->sess_key, + 16, pdc->mach_pw, + pdc->mach_acct, + pdc->remote_machine, + pdc->domain); + + tdb_sc = open_schannel_session_store(mem_ctx); + if (!tdb_sc) { + talloc_free(keystr); + talloc_free(value.dptr); + return False; + } + + ret = (tdb_store_bystring(tdb_sc, keystr, value, TDB_REPLACE) == 0 ? True : False); + + DEBUG(3,("secrets_store_schannel_session_info: stored schannel info with key %s\n", + keystr )); + + tdb_close(tdb_sc); + talloc_free(keystr); + talloc_free(value.dptr); + return ret; +} + +/****************************************************************************** + Restore the schannel state on a client reconnect. + Note we must be root here. +*******************************************************************************/ + +BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx, + const char *remote_machine, + struct dcinfo *pdc) +{ + TDB_CONTEXT *tdb_sc = NULL; + TDB_DATA value; + unsigned char *pseed_chal = NULL; + unsigned char *pclnt_chal = NULL; + unsigned char *psrv_chal = NULL; + unsigned char *psess_key = NULL; + unsigned char *pmach_pw = NULL; + uint32 l1, l2, l3, l4, l5; + int ret; + char *keystr = talloc_asprintf(mem_ctx, "%s/%s", SECRETS_SCHANNEL_STATE, + remote_machine); + + ZERO_STRUCTP(pdc); + + if (!keystr) { + return False; + } + + strupper_m(keystr); + + tdb_sc = open_schannel_session_store(mem_ctx); + if (!tdb_sc) { + talloc_free(keystr); + return False; + } + + value = tdb_fetch_bystring(tdb_sc, keystr); + if (!value.dptr) { + DEBUG(0,("secrets_restore_schannel_session_info: Failed to find entry with key %s\n", + keystr )); + tdb_close(tdb_sc); + return False; + } + + tdb_close(tdb_sc); + + /* Retrieve the record. */ + ret = tdb_unpack(value.dptr, value.dsize, "dBBBBBfff", + &pdc->sequence, + &l1, &pseed_chal, + &l2, &pclnt_chal, + &l3, &psrv_chal, + &l4, &psess_key, + &l5, &pmach_pw, + &pdc->mach_acct, + &pdc->remote_machine, + &pdc->domain); + + if (ret == -1 || l1 != 8 || l2 != 8 || l3 != 8 || l4 != 8 || l5 != 16) { + talloc_free(keystr); + SAFE_FREE(pseed_chal); + SAFE_FREE(pclnt_chal); + SAFE_FREE(psrv_chal); + SAFE_FREE(psess_key); + SAFE_FREE(pmach_pw); + SAFE_FREE(value.dptr); + ZERO_STRUCTP(pdc); + return False; + } + + memcpy(pdc->seed_chal.data, pseed_chal, 8); + memcpy(pdc->clnt_chal.data, pclnt_chal, 8); + memcpy(pdc->srv_chal.data, psrv_chal, 8); + memcpy(pdc->sess_key, psess_key, 8); + memcpy(pdc->mach_pw, pmach_pw, 16); + + /* We know these are true so didn't bother to store them. */ + pdc->challenge_sent = True; + pdc->authenticated = True; + + DEBUG(3,("secrets_store_schannel_session_info: restored schannel info key %s\n", + keystr )); + + SAFE_FREE(pseed_chal); + SAFE_FREE(pclnt_chal); + SAFE_FREE(psrv_chal); + SAFE_FREE(psess_key); + SAFE_FREE(pmach_pw); + + talloc_free(keystr); + SAFE_FREE(value.dptr); + return True; +} diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c index 1ad058b519..2dd8b821d8 100644 --- a/source3/rpc_server/srv_netlog_nt.c +++ b/source3/rpc_server/srv_netlog_nt.c @@ -26,8 +26,6 @@ #include "includes.h" -extern struct dcinfo last_dcinfo; -extern BOOL server_auth2_negotiated; extern userdom_struct current_user_info; #undef DBGC_CLASS @@ -438,10 +436,14 @@ NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u) fstrcpy(p->dc->mach_acct, mach_acct); fstrcpy(p->dc->remote_machine, remote_machine); + fstrcpy(p->dc->domain, lp_workgroup() ); - server_auth2_negotiated = True; p->dc->authenticated = True; - last_dcinfo = *p->dc; + + /* Store off the state so we can continue after client disconnect. */ + become_root(); + secrets_store_schannel_session_info(p->mem_ctx, p->dc); + unbecome_root(); return r_u->status; } diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index ba6d9704e8..1ca5210842 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -36,15 +36,6 @@ extern struct current_user current_user; #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; -BOOL server_auth2_negotiated = False; - static void free_pipe_ntlmssp_auth_data(struct pipe_auth_data *auth) { AUTH_NTLMSSP_STATE *a = auth->a_u.auth_ntlmssp_state; @@ -1218,15 +1209,23 @@ static BOOL pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, RPC_HDR_AUTH auth_info; RPC_AUTH_SCHANNEL_NEG neg; RPC_AUTH_VERIFIER auth_verifier; + BOOL ret; + struct dcinfo stored_dcinfo; uint32 flags; - if (!server_auth2_negotiated) { - DEBUG(0, ("pipe_schannel_auth_bind: Attempt to bind using schannel without successful serverauth2\n")); + if (!smb_io_rpc_auth_schannel_neg("", &neg, rpc_in_p, 0)) { + DEBUG(0,("pipe_schannel_auth_bind: Could not unmarshal SCHANNEL auth neg\n")); return False; } - if (!smb_io_rpc_auth_schannel_neg("", &neg, rpc_in_p, 0)) { - DEBUG(0,("pipe_schannel_auth_bind: Could not unmarshal SCHANNEL auth neg\n")); + ZERO_STRUCT(stored_dcinfo); + + become_root(); + ret = secrets_restore_schannel_session_info(p->mem_ctx, neg.myname, &stored_dcinfo); + unbecome_root(); + + if (!ret) { + DEBUG(0, ("pipe_schannel_auth_bind: Attempt to bind using schannel without successful serverauth2\n")); return False; } @@ -1236,7 +1235,7 @@ static BOOL pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, } memset(p->auth.a_u.schannel_auth->sess_key, 0, sizeof(p->auth.a_u.schannel_auth->sess_key)); - memcpy(p->auth.a_u.schannel_auth->sess_key, last_dcinfo.sess_key, sizeof(last_dcinfo.sess_key)); + memcpy(p->auth.a_u.schannel_auth->sess_key, stored_dcinfo.sess_key, sizeof(stored_dcinfo.sess_key)); p->auth.a_u.schannel_auth->seq_num = 0; @@ -1253,7 +1252,7 @@ static BOOL pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, if (!p->dc) { return False; } - *p->dc = last_dcinfo; + *p->dc = stored_dcinfo; init_rpc_hdr_auth(&auth_info, RPC_SCHANNEL_AUTH_TYPE, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1); if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) { |