r10792: Fix the "schannel not stored across client disconnects" problem.
authorJeremy Allison <jra@samba.org>
Fri, 7 Oct 2005 01:46:19 +0000 (01:46 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 16:04:54 +0000 (11:04 -0500)
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.

source/include/secrets.h
source/passdb/secrets.c
source/rpc_server/srv_netlog_nt.c
source/rpc_server/srv_pipe.c

index 4b8d5db66b6473306a370784c5e977a66f2c15b1..f2d1afd96b3924eb74448f7a797e4de1d161081d 100644 (file)
@@ -98,4 +98,6 @@ struct afs_keyfile {
 
 #define SECRETS_AFS_KEYFILE "SECRETS/AFS_KEYFILE"
 
+#define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL"
+
 #endif /* _SECRETS_H */
index 743bdd194238f6f244701a7c6fb0150ebfda02e6..35ccb2c725b57a3296056fd0aecaa5f58379a99c 100644 (file)
@@ -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;
+}
index 1ad058b5199fb79d86a858554cb7ee60a2cf3b09..2dd8b821d81f4d63efac181b0d740e2c03e0fc76 100644 (file)
@@ -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;
 }
index ba6d9704e808375ef8c62fb685390bcabd548cf0..1ca5210842864e61b987958d3474147f79ecdb14 100644 (file)
@@ -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)) {