s3-credentials: protect netlogon_creds_server_step() against NULL creds.
[ira/wip.git] / source3 / libsmb / credentials.c
index 0d521bae8ac42c7421f6db043bee74a48c0e3217..9dc0b9f01b24214323b87899e79e6202fbd57b08 100644 (file)
@@ -2,10 +2,11 @@
    Unix SMB/CIFS implementation.
    code to manipulate domain credentials
    Copyright (C) Andrew Tridgell 1997-1998
+   Largely rewritten by Jeremy Allison 2005.
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
 
 /****************************************************************************
-represent a credential as a string
+ Represent a credential as a string.
 ****************************************************************************/
-char *credstr(const uchar *cred)
+
+char *credstr(const unsigned char *cred)
 {
-       static fstring buf;
-       slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X",
-               cred[0], cred[1], cred[2], cred[3], 
-               cred[4], cred[5], cred[6], cred[7]);
-       return buf;
+       char *result;
+       result = talloc_asprintf(talloc_tos(),
+                                "%02X%02X%02X%02X%02X%02X%02X%02X",
+                                cred[0], cred[1], cred[2], cred[3],
+                                cred[4], cred[5], cred[6], cred[7]);
+       SMB_ASSERT(result != NULL);
+       return result;
 }
 
+/****************************************************************************
+ Setup the session key and the client and server creds in dc.
+ ADS-style 128 bit session keys.
+ Used by both client and server creds setup.
+****************************************************************************/
+
+static void creds_init_128(struct dcinfo *dc,
+                          const struct netr_Credential *clnt_chal_in,
+                          const struct netr_Credential *srv_chal_in,
+                          const unsigned char mach_pw[16])
+{
+       unsigned char zero[4], tmp[16];
+       HMACMD5Context ctx;
+       struct MD5Context md5;
+
+       /* Just in case this isn't already there */
+       memcpy(dc->mach_pw, mach_pw, 16);
+
+       ZERO_STRUCT(dc->sess_key);
+
+       memset(zero, 0, sizeof(zero));
+
+       hmac_md5_init_rfc2104(mach_pw, 16, &ctx);
+       MD5Init(&md5);
+       MD5Update(&md5, zero, sizeof(zero));
+       MD5Update(&md5, clnt_chal_in->data, 8);
+       MD5Update(&md5, srv_chal_in->data, 8);
+       MD5Final(tmp, &md5);
+       hmac_md5_update(tmp, sizeof(tmp), &ctx);
+       hmac_md5_final(dc->sess_key, &ctx);
+
+       /* debug output */
+       DEBUG(5,("creds_init_128\n"));
+       DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
+       DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
+       dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16);
+
+       /* Generate the next client and server creds. */
+       
+       des_crypt112(dc->clnt_chal.data,                /* output */
+                       clnt_chal_in->data,             /* input */
+                       dc->sess_key,                   /* input */
+                       1);
+
+       des_crypt112(dc->srv_chal.data,                 /* output */
+                       srv_chal_in->data,              /* input */
+                       dc->sess_key,                   /* input */
+                       1);
+
+       /* Seed is the client chal. */
+       memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
+}
 
 /****************************************************************************
-  setup the session key. 
-Input: 8 byte challenge block
-       8 byte server challenge block
-      16 byte md4 encrypted password
-Output:
-      8 byte session key
+ Setup the session key and the client and server creds in dc.
+ Used by both client and server creds setup.
 ****************************************************************************/
-void cred_session_key(const DOM_CHAL *clnt_chal, const DOM_CHAL *srv_chal, const uchar *pass, 
-                     uchar session_key[8])
+
+static void creds_init_64(struct dcinfo *dc,
+                         const struct netr_Credential *clnt_chal_in,
+                         const struct netr_Credential *srv_chal_in,
+                         const unsigned char mach_pw[16])
 {
        uint32 sum[2];
        unsigned char sum2[8];
 
-       sum[0] = IVAL(clnt_chal->data, 0) + IVAL(srv_chal->data, 0);
-       sum[1] = IVAL(clnt_chal->data, 4) + IVAL(srv_chal->data, 4);
+       /* Just in case this isn't already there */
+       if (dc->mach_pw != mach_pw) {
+               memcpy(dc->mach_pw, mach_pw, 16);
+       }
+
+       sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
+       sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
 
        SIVAL(sum2,0,sum[0]);
        SIVAL(sum2,4,sum[1]);
 
-       cred_hash1(session_key, sum2, pass);
+       ZERO_STRUCT(dc->sess_key);
+
+       des_crypt128(dc->sess_key, sum2, dc->mach_pw);
 
        /* debug output */
-       DEBUG(4,("cred_session_key\n"));
+       DEBUG(5,("creds_init_64\n"));
+       DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
+       DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
+       DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2)));
+       DEBUG(5,("\tsess_key_out : %s\n", credstr(dc->sess_key)));
 
-       DEBUG(5,("      clnt_chal: %s\n", credstr(clnt_chal->data)));
-       DEBUG(5,("      srv_chal : %s\n", credstr(srv_chal->data)));
-       DEBUG(5,("      clnt+srv : %s\n", credstr(sum2)));
-       DEBUG(5,("      sess_key : %s\n", credstr(session_key)));
+       /* Generate the next client and server creds. */
+       
+       des_crypt112(dc->clnt_chal.data,                /* output */
+                       clnt_chal_in->data,             /* input */
+                       dc->sess_key,                   /* input */
+                       1);
+
+       des_crypt112(dc->srv_chal.data,                 /* output */
+                       srv_chal_in->data,              /* input */
+                       dc->sess_key,                   /* input */
+                       1);
+
+       /* Seed is the client chal. */
+       memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
 }
 
-
 /****************************************************************************
-create a credential
-
-Input:
-      8 byte sesssion key
-      8 byte stored credential
-      4 byte timestamp
-
-Output:
-      8 byte credential
+ Utility function to step credential chain one forward.
+ Deliberately doesn't update the seed. See reseed comment below.
 ****************************************************************************/
-void cred_create(uchar session_key[8], DOM_CHAL *stor_cred, UTIME timestamp, 
-                DOM_CHAL *cred)
+
+static void creds_step(struct dcinfo *dc)
 {
-       DOM_CHAL time_cred;
+       struct netr_Credential time_chal;
 
-       SIVAL(time_cred.data, 0, IVAL(stor_cred->data, 0) + timestamp.time);
-       SIVAL(time_cred.data, 4, IVAL(stor_cred->data, 4));
+       DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
 
-       cred_hash2(cred->data, time_cred.data, session_key);
+       DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
 
-       /* debug output*/
-       DEBUG(4,("cred_create\n"));
+       SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
+       SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
+                                                                                                   
+       DEBUG(5,("\tseed+seq   %s\n", credstr(time_chal.data) ));
 
-       DEBUG(5,("      sess_key : %s\n", credstr(session_key)));
-       DEBUG(5,("      stor_cred: %s\n", credstr(stor_cred->data)));
-       DEBUG(5,("      timestamp: %x\n"    , timestamp.time));
-       DEBUG(5,("      timecred : %s\n", credstr(time_cred.data)));
-       DEBUG(5,("      calc_cred: %s\n", credstr(cred->data)));
-}
+       des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
 
+       DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
 
-/****************************************************************************
-  check a supplied credential
+       SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
+       SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
 
-Input:
-      8 byte received credential
-      8 byte sesssion key
-      8 byte stored credential
-      4 byte timestamp
+       DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
 
-Output:
-      returns 1 if computed credential matches received credential
-      returns 0 otherwise
+       des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
+
+       DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
+}
+
+/****************************************************************************
+ Create a server credential struct.
 ****************************************************************************/
-int cred_assert(DOM_CHAL *cred, uchar session_key[8], DOM_CHAL *stored_cred,
-               UTIME timestamp)
-{
-       DOM_CHAL cred2;
 
-       cred_create(session_key, stored_cred, timestamp, &cred2);
+void creds_server_init(uint32 neg_flags,
+                       struct dcinfo *dc,
+                       struct netr_Credential *clnt_chal,
+                       struct netr_Credential *srv_chal,
+                       const unsigned char mach_pw[16],
+                       struct netr_Credential *init_chal_out)
+{
+       DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags));
+       DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
+       DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
+       dump_data_pw("creds_server_init: machine pass", mach_pw, 16);
+
+       /* Generate the session key and the next client and server creds. */
+       if (neg_flags & NETLOGON_NEG_128BIT) {
+               creds_init_128(dc,
+                       clnt_chal,
+                       srv_chal,
+                       mach_pw);
+       } else {
+               creds_init_64(dc,
+                       clnt_chal,
+                       srv_chal,
+                       mach_pw);
+       }
 
-       /* debug output*/
-       DEBUG(4,("cred_assert\n"));
+       dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
 
-       DEBUG(5,("      challenge : %s\n", credstr(cred->data)));
-       DEBUG(5,("      calculated: %s\n", credstr(cred2.data)));
+       DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
+       DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
+       DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
 
-       if (memcmp(cred->data, cred2.data, 8) == 0)
-       {
-               DEBUG(5, ("credentials check ok\n"));
-               return True;
-       }
-       else
-       {
-               DEBUG(5, ("credentials check wrong\n"));
-               return False;
-       }
+       memcpy(init_chal_out->data, dc->srv_chal.data, 8);
 }
 
-
 /****************************************************************************
-  checks credentials; generates next step in the credential chain
+ Check a credential sent by the client.
 ****************************************************************************/
-BOOL clnt_deal_with_creds(uchar sess_key[8],
-                         DOM_CRED *sto_clnt_cred, DOM_CRED *rcv_srv_cred)
+
+bool netlogon_creds_server_check(const struct dcinfo *dc,
+                                const struct netr_Credential *rcv_cli_chal_in)
 {
-       UTIME new_clnt_time;
-       uint32 new_cred;
+       if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
+               DEBUG(5,("netlogon_creds_server_check: challenge : %s\n",
+                       credstr(rcv_cli_chal_in->data)));
+               DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
+               DEBUG(2,("netlogon_creds_server_check: credentials check failed.\n"));
+               return false;
+       }
 
-       DEBUG(5,("clnt_deal_with_creds: %d\n", __LINE__));
+       DEBUG(10,("netlogon_creds_server_check: credentials check OK.\n"));
 
-       /* increment client time by one second */
-       new_clnt_time.time = sto_clnt_cred->timestamp.time + 1;
+       return true;
+}
+/****************************************************************************
+ Replace current seed chal. Internal function - due to split server step below.
+****************************************************************************/
 
-       /* check that the received server credentials are valid */
-       if (!cred_assert(&rcv_srv_cred->challenge, sess_key,
-                        &sto_clnt_cred->challenge, new_clnt_time))
-       {
-               return False;
-       }
+static void creds_reseed(struct dcinfo *dc)
+{
+       struct netr_Credential time_chal;
 
-       /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
-       new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
-       new_cred += new_clnt_time.time;
+       SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
+       SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
 
-       /* store new seed in client credentials */
-       SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+       dc->seed_chal = time_chal;
 
-       DEBUG(5,("      new clnt cred: %s\n", credstr(sto_clnt_cred->challenge.data)));
-       return True;
+       DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
 }
 
-
 /****************************************************************************
-  checks credentials; generates next step in the credential chain
+ Step the server credential chain one forward. 
 ****************************************************************************/
-BOOL deal_with_creds(uchar sess_key[8],
-                    DOM_CRED *sto_clnt_cred, 
-                    DOM_CRED *rcv_clnt_cred, DOM_CRED *rtn_srv_cred)
-{
-       UTIME new_clnt_time;
-       uint32 new_cred;
 
-       DEBUG(5,("deal_with_creds: %d\n", __LINE__));
+bool netlogon_creds_server_step(struct dcinfo *dc,
+                               const struct netr_Authenticator *received_cred,
+                               struct netr_Authenticator *cred_out)
+{
+       bool ret;
+       struct dcinfo tmp_dc = *dc;
 
-       /* check that the received client credentials are valid */
-       if (!cred_assert(&rcv_clnt_cred->challenge, sess_key,
-                    &sto_clnt_cred->challenge, rcv_clnt_cred->timestamp))
-       {
-               return False;
+       if (!received_cred || !cred_out) {
+               return false;
        }
 
-       /* increment client time by one second */
-       new_clnt_time.time = rcv_clnt_cred->timestamp.time + 1;
+       /* Do all operations on a temporary copy of the dc,
+          which we throw away if the checks fail. */
 
-       /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
-       new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
-       new_cred += new_clnt_time.time;
+       tmp_dc.sequence = received_cred->timestamp;
 
-       DEBUG(5,("deal_with_creds: new_cred[0]=%x\n", new_cred));
+       creds_step(&tmp_dc);
 
-       /* doesn't matter that server time is 0 */
-       rtn_srv_cred->timestamp.time = 0;
+       /* Create the outgoing credentials */
+       cred_out->timestamp = tmp_dc.sequence + 1;
+       memcpy(&cred_out->cred, &tmp_dc.srv_chal, sizeof(cred_out->cred));
 
-       DEBUG(5,("deal_with_creds: new_clnt_time=%x\n", new_clnt_time.time));
+       creds_reseed(&tmp_dc);
 
-       /* create return credentials for inclusion in the reply */
-       cred_create(sess_key, &sto_clnt_cred->challenge, new_clnt_time,
-                   &rtn_srv_cred->challenge);
-       
-       DEBUG(5,("deal_with_creds: clnt_cred=%s\n", credstr(sto_clnt_cred->challenge.data)));
+       ret = netlogon_creds_server_check(&tmp_dc, &received_cred->cred);
+       if (!ret) {
+               return false;
+       }
 
-       /* store new seed in client credentials */
-       SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+       /* creds step succeeded - replace the current creds. */
+       *dc = tmp_dc;
+       return true;
+}
+
+void cred_hash3(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
+{
+        unsigned char key2[8];
 
-       return True;
+       memset(key2,'\0',8);
+        des_crypt56(out, in, key, forw);
+        key2[0] = key[7];
+        des_crypt56(out + 8, in + 8, key2, forw);
 }