/*
- 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;
+}