r10656: BIG merge from trunk. Features not copied over
[tprouty/samba.git] / source3 / libsmb / credentials.c
index d56598e98a36199f66a285f899303d7e136316a0..3f2dcd850b33b887270d507146445abb3ae7bc26 100644 (file)
@@ -1,8 +1,8 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    code to manipulate domain credentials
-   Copyright (C) Andrew Tridgell 1997
+   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
 
 #include "includes.h"
 
-extern int DEBUGLEVEL;
 /****************************************************************************
-  setup the session key. 
-Input: 8 byte challenge block
+ Represent a credential as a string.
+****************************************************************************/
+
+char *credstr(const uchar *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;
+}
+
+
+/****************************************************************************
+ Setup the session key. 
+ Input: 8 byte challenge block
        8 byte server challenge block
       16 byte md4 encrypted password
-Output:
-      8 byte session key
+ Output:
+      16 byte session key (last 8 bytes zero).
 ****************************************************************************/
-void cred_session_key(DOM_CHAL *clnt_chal, DOM_CHAL *srv_chal, char *pass, 
-                      uint32 session_key[2])
+
+static void cred_create_session_key(const DOM_CHAL *clnt_chal_in,
+                       const DOM_CHAL *srv_chal_in,
+                       const uchar *pass_in, 
+                       uchar session_key_out[16])
 {
        uint32 sum[2];
-       char sum2[8];
-       char buf[8];
-       char netsesskey[8];
+       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);
+       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]);
 
-       smbhash(buf, sum2, pass);
-       smbhash(netsesskey, buf, pass+9);
-
-       session_key[0] = IVAL(netsesskey, 0);
-       session_key[1] = IVAL(netsesskey, 4);
+       cred_hash1(session_key_out, sum2, pass_in);
+       memset(&session_key_out[8], '\0', 8);
 
        /* debug output */
-       DEBUG(4,("cred_session_key\n"));
+       DEBUG(4,("cred_create_session_key\n"));
 
-       DEBUG(5,("      clnt_chal: %lx %lx\n", clnt_chal->data[0], clnt_chal->data[1]));
-       DEBUG(5,("      srv_chal : %lx %lx\n", srv_chal ->data[0], srv_chal ->data[1]));
-       DEBUG(5,("      clnt+srv : %lx %lx\n", sum            [0], sum            [1]));
-       DEBUG(5,("      sess_key : %lx %lx\n", session_key    [0], session_key    [1]));
+       DEBUG(5,("      clnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
+       DEBUG(5,("      srv_chal_in : %s\n", credstr(srv_chal_in->data)));
+       DEBUG(5,("      clnt+srv : %s\n", credstr(sum2)));
+       DEBUG(5,("      sess_key_out : %s\n", credstr(session_key_out)));
 }
 
-
 /****************************************************************************
-create a credential
+ Utility function to step credential chain one forward.
+ Deliberately doesn't update the seed. See reseed comment below.
+****************************************************************************/
+
+static void creds_step(struct dcinfo *dc)
+{
+       DOM_CHAL time_chal;
+
+       DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
+
+       DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
+
+       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) ));
+
+       cred_hash2(dc->clnt_chal.data, time_chal.data, dc->sess_key);
 
-Input:
-      8 byte sesssion key
-      8 byte stored credential
-      4 byte timestamp
+       DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
 
-Output:
-      8 byte 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));
+
+       DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
+
+       cred_hash2(dc->srv_chal.data, time_chal.data, dc->sess_key);
+
+       DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
+}
+
+
+/****************************************************************************
+ Create a server credential struct.
 ****************************************************************************/
-void cred_create(uint32 session_key[2], DOM_CHAL *stor_cred, UTIME timestamp, 
-                DOM_CHAL *cred)
+
+void creds_server_init(struct dcinfo *dc,
+                       DOM_CHAL *clnt_chal,
+                       DOM_CHAL *srv_chal,
+                       const char mach_pw[16],
+                       DOM_CHAL *init_chal_out)
 {
-       char key2[7];
-       char buf[8];
-       char calc_cred[8];
-       char timecred[8];
-       char netsesskey[8];
+       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);
+
+       /* Just in case this isn't already there */
+       memcpy(dc->mach_pw, mach_pw, 16);
 
-       SIVAL(netsesskey, 0, session_key[0]);
-       SIVAL(netsesskey, 4, session_key[1]);
+       /* Generate the session key. */
+       cred_create_session_key(clnt_chal,              /* Stored client challenge. */
+                               srv_chal,               /* Stored server challenge. */
+                               dc->mach_pw,              /* input machine password. */
+                               dc->sess_key);            /* output session key. */
 
-       SIVAL(timecred, 0, IVAL(stor_cred, 0) + timestamp.time);
-       SIVAL(timecred, 4, IVAL(stor_cred, 4));
+       dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
 
-       smbhash(buf, timecred, netsesskey);
-       memset(key2, 0, 7);
-       key2[0] = netsesskey[7];
-       smbhash(calc_cred, buf, key2);
+       /* Generate the next client and server creds. */
+       cred_hash2(dc->clnt_chal.data,                  /* output */
+                       clnt_chal->data,                /* input */
+                       dc->sess_key);                  /* input */
 
-       cred->data[0] = IVAL(calc_cred, 0);
-       cred->data[1] = IVAL(calc_cred, 4);
+       cred_hash2(dc->srv_chal.data,                   /* output */
+                       srv_chal->data,                 /* input */
+                       dc->sess_key);                  /* input */
 
-       /* debug output*/
-       DEBUG(4,("cred_create\n"));
+       /* Seed is the client chal. */
+       memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
 
-       DEBUG(5,("      sess_key : %lx %lx\n", session_key    [0], session_key    [1]));
-       DEBUG(5,("      stor_cred: %lx %lx\n", stor_cred->data[0], stor_cred->data[1]));
-       DEBUG(5,("      timecred : %lx %lx\n", IVAL(timecred, 0) , IVAL(timecred, 4) ));
-       DEBUG(5,("      calc_cred: %lx %lx\n", cred     ->data[0], cred     ->data[1]));
+       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) ));
+
+       memcpy(init_chal_out->data, dc->srv_chal.data, 8);
 }
 
+/****************************************************************************
+ Check a credential sent by the client.
+****************************************************************************/
+
+BOOL creds_server_check(const struct dcinfo *dc, const DOM_CHAL *rcv_cli_chal_in)
+{
+       if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
+               DEBUG(5,("creds_server_check: challenge : %s\n", credstr(rcv_cli_chal_in->data)));
+               DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
+               DEBUG(0,("creds_server_check: credentials check failed.\n"));
+               return False;
+       }
+       DEBUG(10,("creds_server_check: credentials check OK.\n"));
+       return True;
+}
 
 /****************************************************************************
-  check a supplied credential
+ Replace current seed chal. Internal function - due to split server step below.
+****************************************************************************/
 
-Input:
-      8 byte received credential
-      8 byte sesssion key
-      8 byte stored credential
-      4 byte timestamp
+static void creds_reseed(struct dcinfo *dc)
+{
+       DOM_CHAL time_chal;
 
-Output:
-      returns 1 if computed credential matches received credential
-      returns 0 otherwise
+       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));
+
+       dc->seed_chal = time_chal;
+
+       DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
+}
+
+/****************************************************************************
+ Step the server credential chain one forward. 
 ****************************************************************************/
-int cred_assert(DOM_CHAL *cred, uint32 session_key[2], DOM_CHAL *stored_cred,
-               UTIME timestamp)
+
+BOOL creds_server_step(struct dcinfo *dc, const DOM_CRED *received_cred, DOM_CRED *cred_out)
 {
-       DOM_CHAL cred2;
+       dc->sequence = received_cred->timestamp.time;
 
-       cred_create(session_key, stored_cred, timestamp, &cred2);
+       creds_step(dc);
 
-       /* debug output*/
-       DEBUG(4,("cred_assert\n"));
+       /* Create the outgoing credentials */
+       cred_out->timestamp.time = dc->sequence + 1;
+       cred_out->challenge = dc->srv_chal;
 
-       DEBUG(5,("      challenge : %lx %lx\n", cred->data[0], cred->data[1]));
-       DEBUG(5,("      calculated: %lx %lx\n", cred2.data[0], cred2.data[1]));
+       creds_reseed(dc);
 
-       return memcmp(cred->data, cred2.data, 8) == 0;
+       return creds_server_check(dc, &received_cred->challenge);
 }
 
+/****************************************************************************
+ Create a client credential struct.
+****************************************************************************/
+
+void creds_client_init(struct dcinfo *dc,
+                       DOM_CHAL *clnt_chal,
+                       DOM_CHAL *srv_chal,
+                       const char mach_pw[16],
+                       DOM_CHAL *init_chal_out)
+{
+       dc->sequence = time(NULL);
+
+       DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
+       DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
+       dump_data_pw("creds_client_init: machine pass", mach_pw, 16);
+
+       /* Just in case this isn't already there */
+       memcpy(dc->mach_pw, mach_pw, 16);
+
+       /* Generate the session key. */
+       cred_create_session_key(clnt_chal,              /* Stored client challenge. */
+                               srv_chal,               /* Stored server challenge. */
+                               dc->mach_pw,              /* input machine password. */
+                               dc->sess_key);            /* output session key. */
+
+       dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
+
+       /* Generate the next client and server creds. */
+       cred_hash2(dc->clnt_chal.data,  /* output */
+                       clnt_chal->data,                /* input */
+                       dc->sess_key);                  /* input */
+
+       cred_hash2(dc->srv_chal.data,           /* output */
+                       srv_chal->data,                 /* input */
+                       dc->sess_key);                  /* input */
+
+       /* Seed is the client cred. */
+       memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
+
+       DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
+       DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
+       DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
+
+       memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
+}
+
+/****************************************************************************
+ Check a credential returned by the server.
+****************************************************************************/
+
+BOOL creds_client_check(const struct dcinfo *dc, const DOM_CHAL *rcv_srv_chal_in)
+{
+       if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data, 8)) {
+               DEBUG(5,("creds_client_check: challenge : %s\n", credstr(rcv_srv_chal_in->data)));
+               DEBUG(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
+               DEBUG(0,("creds_client_check: credentials check failed.\n"));
+               return False;
+       }
+       DEBUG(10,("creds_client_check: credentials check OK.\n"));
+       return True;
+}
+
+/****************************************************************************
+  Step the client credentials to the next element in the chain, updating the
+  current client and server credentials and the seed
+  produce the next authenticator in the sequence ready to send to
+  the server
+****************************************************************************/
+
+void creds_client_step(struct dcinfo *dc, DOM_CRED *next_cred_out)
+{
+        dc->sequence += 2;
+       creds_step(dc);
+       creds_reseed(dc);
+
+       next_cred_out->challenge = dc->clnt_chal;
+       next_cred_out->timestamp.time = dc->sequence;
+}