2003-12-01 07:13:43 +03:00
/*
Unix SMB / CIFS implementation .
code to manipulate domain credentials
Copyright ( C ) Andrew Tridgell 1997 - 2003
2004-05-15 11:51:38 +04:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2004
2003-12-01 07:13:43 +03:00
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
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
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 0213 9 , USA .
*/
# include "includes.h"
2004-11-02 03:24:21 +03:00
# include "system/time.h"
2004-11-02 05:57:18 +03:00
# include "auth/auth.h"
2003-12-01 07:13:43 +03:00
2003-12-01 15:41:54 +03:00
/*
2004-06-04 03:15:16 +04:00
initialise the credentials state for old - style 64 bit session keys
2003-12-02 01:13:11 +03:00
this call is made after the netr_ServerReqChallenge call
2003-12-01 15:41:54 +03:00
*/
2004-06-04 03:15:16 +04:00
static void creds_init_64bit ( struct creds_CredentialState * creds ,
const struct netr_Credential * client_challenge ,
const struct netr_Credential * server_challenge ,
2004-06-05 07:14:59 +04:00
const struct samr_Password * machine_password )
2003-12-01 07:13:43 +03:00
{
2004-05-25 20:24:13 +04:00
uint32_t sum [ 2 ] ;
2004-05-25 21:50:17 +04:00
uint8_t sum2 [ 8 ] ;
2003-12-01 07:13:43 +03:00
sum [ 0 ] = IVAL ( client_challenge - > data , 0 ) + IVAL ( server_challenge - > data , 0 ) ;
sum [ 1 ] = IVAL ( client_challenge - > data , 4 ) + IVAL ( server_challenge - > data , 4 ) ;
SIVAL ( sum2 , 0 , sum [ 0 ] ) ;
SIVAL ( sum2 , 4 , sum [ 1 ] ) ;
2004-06-04 03:15:16 +04:00
ZERO_STRUCT ( creds - > session_key ) ;
2003-12-01 07:13:43 +03:00
2004-06-05 07:14:59 +04:00
des_crypt128 ( creds - > session_key , sum2 , machine_password - > hash ) ;
2003-12-01 07:13:43 +03:00
2004-06-04 03:15:16 +04:00
des_crypt112 ( creds - > client . data , client_challenge - > data , creds - > session_key , 1 ) ;
des_crypt112 ( creds - > server . data , server_challenge - > data , creds - > session_key , 1 ) ;
creds - > seed = creds - > client ;
}
/*
initialise the credentials state for ADS - style 128 bit session keys
this call is made after the netr_ServerReqChallenge call
*/
static void creds_init_128bit ( struct creds_CredentialState * creds ,
const struct netr_Credential * client_challenge ,
const struct netr_Credential * server_challenge ,
2004-06-05 07:14:59 +04:00
const struct samr_Password * machine_password )
2004-06-04 03:15:16 +04:00
{
unsigned char zero [ 4 ] , tmp [ 16 ] ;
HMACMD5Context ctx ;
struct MD5Context md5 ;
ZERO_STRUCT ( creds - > session_key ) ;
memset ( zero , 0 , sizeof ( zero ) ) ;
2004-06-05 07:14:59 +04:00
hmac_md5_init_rfc2104 ( machine_password - > hash , sizeof ( machine_password - > hash ) , & ctx ) ;
2004-06-04 03:15:16 +04:00
MD5Init ( & md5 ) ;
MD5Update ( & md5 , zero , sizeof ( zero ) ) ;
MD5Update ( & md5 , client_challenge - > data , 8 ) ;
MD5Update ( & md5 , server_challenge - > data , 8 ) ;
MD5Final ( tmp , & md5 ) ;
2004-06-05 07:14:59 +04:00
hmac_md5_update ( tmp , sizeof ( tmp ) , & ctx ) ;
2004-06-04 03:15:16 +04:00
hmac_md5_final ( creds - > session_key , & ctx ) ;
creds - > client = * client_challenge ;
creds - > server = * server_challenge ;
des_crypt112 ( creds - > client . data , client_challenge - > data , creds - > session_key , 1 ) ;
des_crypt112 ( creds - > server . data , server_challenge - > data , creds - > session_key , 1 ) ;
2003-12-02 01:13:11 +03:00
2003-12-02 05:15:33 +03:00
creds - > seed = creds - > client ;
}
/*
2003-12-02 06:06:21 +03:00
step the credentials to the next element in the chain , updating the
current client and server credentials and the seed
2003-12-02 05:15:33 +03:00
*/
2004-05-09 16:42:18 +04:00
static void creds_step ( struct creds_CredentialState * creds )
2003-12-02 05:15:33 +03:00
{
struct netr_Credential time_cred ;
DEBUG ( 5 , ( " \t seed %08x:%08x \n " ,
IVAL ( creds - > seed . data , 0 ) , IVAL ( creds - > seed . data , 4 ) ) ) ;
SIVAL ( time_cred . data , 0 , IVAL ( creds - > seed . data , 0 ) + creds - > sequence ) ;
SIVAL ( time_cred . data , 4 , IVAL ( creds - > seed . data , 4 ) ) ;
DEBUG ( 5 , ( " \t seed+time %08x:%08x \n " , IVAL ( time_cred . data , 0 ) , IVAL ( time_cred . data , 4 ) ) ) ;
2004-06-04 03:15:16 +04:00
des_crypt112 ( creds - > client . data , time_cred . data , creds - > session_key , 1 ) ;
2003-12-02 05:15:33 +03:00
DEBUG ( 5 , ( " \t CLIENT %08x:%08x \n " ,
IVAL ( creds - > client . data , 0 ) , IVAL ( creds - > client . data , 4 ) ) ) ;
SIVAL ( time_cred . data , 0 , IVAL ( creds - > seed . data , 0 ) + creds - > sequence + 1 ) ;
SIVAL ( time_cred . data , 4 , IVAL ( creds - > seed . data , 4 ) ) ;
DEBUG ( 5 , ( " \t seed+time+1 %08x:%08x \n " ,
IVAL ( time_cred . data , 0 ) , IVAL ( time_cred . data , 4 ) ) ) ;
2004-06-04 03:15:16 +04:00
des_crypt112 ( creds - > server . data , time_cred . data , creds - > session_key , 1 ) ;
2003-12-01 07:13:43 +03:00
2003-12-02 05:15:33 +03:00
DEBUG ( 5 , ( " \t SERVER %08x:%08x \n " ,
IVAL ( creds - > server . data , 0 ) , IVAL ( creds - > server . data , 4 ) ) ) ;
creds - > seed = time_cred ;
2003-12-01 07:13:43 +03:00
}
2004-05-17 01:30:48 +04:00
2004-05-09 16:42:18 +04:00
/*
DES encrypt a 16 byte password buffer using the session key
*/
2004-06-04 15:58:46 +04:00
void creds_des_encrypt ( struct creds_CredentialState * creds , struct samr_Password * pass )
2004-05-09 16:42:18 +04:00
{
2004-06-04 15:58:46 +04:00
struct samr_Password tmp ;
des_crypt112_16 ( tmp . hash , pass - > hash , creds - > session_key , 1 ) ;
2004-05-09 16:42:18 +04:00
* pass = tmp ;
}
2004-05-17 01:30:48 +04:00
/*
DES decrypt a 16 byte password buffer using the session key
*/
2004-06-04 15:58:46 +04:00
void creds_des_decrypt ( struct creds_CredentialState * creds , struct samr_Password * pass )
2004-05-17 01:30:48 +04:00
{
2004-06-04 15:58:46 +04:00
struct samr_Password tmp ;
des_crypt112_16 ( tmp . hash , pass - > hash , creds - > session_key , 0 ) ;
2004-05-17 01:30:48 +04:00
* pass = tmp ;
}
2004-05-09 16:42:18 +04:00
/*
ARCFOUR encrypt / decrypt a password buffer using the session key
*/
void creds_arcfour_crypt ( struct creds_CredentialState * creds , char * data , size_t len )
{
2004-06-04 03:15:16 +04:00
DATA_BLOB session_key = data_blob ( creds - > session_key , 16 ) ;
2004-05-09 16:42:18 +04:00
2004-06-04 03:15:16 +04:00
arcfour_crypt_blob ( data , len , & session_key ) ;
2003-12-02 01:13:11 +03:00
2004-05-09 16:42:18 +04:00
data_blob_free ( & session_key ) ;
}
2003-12-02 06:06:21 +03:00
/*****************************************************************
The above functions are common to the client and server interface
next comes the client specific functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-12-01 15:41:54 +03:00
/*
2003-12-02 05:15:33 +03:00
initialise the credentials chain and return the first client
credentials
2003-12-01 15:41:54 +03:00
*/
2004-05-09 16:42:18 +04:00
void creds_client_init ( struct creds_CredentialState * creds ,
2003-12-02 05:15:33 +03:00
const struct netr_Credential * client_challenge ,
const struct netr_Credential * server_challenge ,
2004-06-05 07:14:59 +04:00
const struct samr_Password * machine_password ,
2004-06-04 03:15:16 +04:00
struct netr_Credential * initial_credential ,
uint32_t negotiate_flags )
2003-12-01 07:13:43 +03:00
{
2004-05-15 11:51:38 +04:00
creds - > sequence = time ( NULL ) ;
2004-06-04 03:15:16 +04:00
creds - > negotiate_flags = negotiate_flags ;
dump_data_pw ( " Client chall " , client_challenge - > data , sizeof ( client_challenge - > data ) ) ;
dump_data_pw ( " Server chall " , server_challenge - > data , sizeof ( server_challenge - > data ) ) ;
2004-06-05 07:14:59 +04:00
dump_data_pw ( " Machine Pass " , machine_password - > hash , sizeof ( machine_password - > hash ) ) ;
2004-06-04 03:15:16 +04:00
if ( negotiate_flags & NETLOGON_NEG_128BIT ) {
creds_init_128bit ( creds , client_challenge , server_challenge , machine_password ) ;
} else {
creds_init_64bit ( creds , client_challenge , server_challenge , machine_password ) ;
}
dump_data_pw ( " Session key " , creds - > session_key , 16 ) ;
dump_data_pw ( " Credential " , creds - > client . data , 8 ) ;
2003-12-02 05:15:33 +03:00
* initial_credential = creds - > client ;
}
2003-12-01 07:13:43 +03:00
2004-05-17 01:30:48 +04:00
/*
step the 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_authenticator ( struct creds_CredentialState * creds ,
struct netr_Authenticator * next )
{
creds - > sequence + = 2 ;
creds_step ( creds ) ;
next - > cred = creds - > client ;
next - > timestamp = creds - > sequence ;
}
2003-12-02 05:15:33 +03:00
/*
check that a credentials reply from a server is correct
*/
2004-05-09 16:42:18 +04:00
BOOL creds_client_check ( struct creds_CredentialState * creds ,
2003-12-02 05:15:33 +03:00
const struct netr_Credential * received_credentials )
{
2004-05-15 11:51:38 +04:00
if ( ! received_credentials | |
memcmp ( received_credentials - > data , creds - > server . data , 8 ) ! = 0 ) {
2003-12-01 15:41:54 +03:00
DEBUG ( 2 , ( " credentials check failed \n " ) ) ;
2003-12-01 07:13:43 +03:00
return False ;
}
2003-12-02 01:13:11 +03:00
return True ;
}
2003-12-01 07:13:43 +03:00
2003-12-02 03:31:54 +03:00
2004-05-15 11:51:38 +04:00
/*****************************************************************
The above functions are common to the client and server interface
next comes the server specific functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
initialise the credentials chain and return the first server
credentials
*/
void creds_server_init ( struct creds_CredentialState * creds ,
const struct netr_Credential * client_challenge ,
const struct netr_Credential * server_challenge ,
2004-06-05 07:14:59 +04:00
const struct samr_Password * machine_password ,
2004-06-04 03:15:16 +04:00
struct netr_Credential * initial_credential ,
uint32_t negotiate_flags )
2004-05-15 11:51:38 +04:00
{
2004-06-04 03:15:16 +04:00
if ( negotiate_flags & NETLOGON_NEG_128BIT ) {
creds_init_128bit ( creds , client_challenge , server_challenge ,
machine_password ) ;
} else {
creds_init_64bit ( creds , client_challenge , server_challenge ,
machine_password ) ;
}
2004-05-15 11:51:38 +04:00
* initial_credential = creds - > server ;
}
/*
check that a credentials reply from a server is correct
*/
BOOL creds_server_check ( const struct creds_CredentialState * creds ,
const struct netr_Credential * received_credentials )
{
if ( memcmp ( received_credentials - > data , creds - > client . data , 8 ) ! = 0 ) {
DEBUG ( 2 , ( " credentials check failed \n " ) ) ;
dump_data_pw ( " client creds " , creds - > client . data , 8 ) ;
dump_data_pw ( " calc creds " , received_credentials - > data , 8 ) ;
return False ;
}
return True ;
}
2004-05-17 01:30:48 +04:00
BOOL creds_server_step_check ( struct creds_CredentialState * creds ,
struct netr_Authenticator * received_authenticator ,
struct netr_Authenticator * return_authenticator )
{
2004-06-14 11:28:05 +04:00
/* TODO: this may allow the a replay attack on a non-signed
connection . Should we check that this is increasing ? */
2004-05-17 01:30:48 +04:00
creds - > sequence = received_authenticator - > timestamp ;
creds_step ( creds ) ;
if ( creds_server_check ( creds , & received_authenticator - > cred ) ) {
return_authenticator - > cred = creds - > server ;
return_authenticator - > timestamp = creds - > sequence ;
return True ;
} else {
ZERO_STRUCTP ( return_authenticator ) ;
return False ;
}
}