Add EFS structure definitions to layout.h and adapt ntfsdecrypt for it.
More fixes/cleanups to decrypt.c. This was all Yuval's work but I did some renaming afterwards and some whitespace cleanups.edge.strict_endians
parent
0a18d2fce7
commit
2546690ee1
|
@ -251,7 +251,7 @@ typedef union {
|
|||
EA_ATTR ea;
|
||||
PROPERTY_SET property_set;
|
||||
LOGGED_UTILITY_STREAM logged_util_stream;
|
||||
EFS_ATTR efs;
|
||||
EFS_ATTR_HEADER efs;
|
||||
} attr_val;
|
||||
|
||||
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* layout.h - Ntfs on-disk layout structures. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2005 Yuval Fledel
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
|
@ -2354,14 +2355,97 @@ typedef struct {
|
|||
* Operations on this attribute are logged to the journal ($LogFile) like
|
||||
* normal metadata changes.
|
||||
*
|
||||
* Used by the Encrypting File System (EFS). All encrypted files have this
|
||||
* attribute with the name $EFS.
|
||||
* Used by the Encrypting File System (EFS). All encrypted files have this
|
||||
* attribute with the name $EFS. See below for the relevant structures.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Can be anything the creator chooses. */
|
||||
/* EFS uses it as follows: */
|
||||
// FIXME: Type this info, verifying it along the way. (AIA)
|
||||
} __attribute__ ((__packed__)) LOGGED_UTILITY_STREAM, EFS_ATTR;
|
||||
} __attribute__ ((__packed__)) LOGGED_UTILITY_STREAM;
|
||||
|
||||
/*
|
||||
* $EFS Data Structure:
|
||||
*
|
||||
* The following information is about the data structures that are contained
|
||||
* inside a logged utility stream (0x100) with a name of "$EFS".
|
||||
*
|
||||
* The stream starts with an instance of EFS_ATTR_HEADER.
|
||||
*
|
||||
* Next, at offsets offset_to_ddf_array and offset_to_drf_array (unless any of
|
||||
* them is 0) there is a EFS_DF_ARRAY_HEADER immediately followed by a sequence
|
||||
* of multiple data decryption/recovery fields.
|
||||
*
|
||||
* Each data decryption/recovery field starts with a EFS_DF_HEADER and the next
|
||||
* one (if it exists) can be found by adding EFS_DF_HEADER->df_length bytes to
|
||||
* the offset of the beginning of the current EFS_DF_HEADER.
|
||||
*
|
||||
* The data decryption/recovery field contains an EFS_DF_CERTIFICATE_HEADER, a
|
||||
* SID, an optional GUID, an optional container name, a non-optional user name,
|
||||
* and the encrypted FEK.
|
||||
*
|
||||
* Note all the below are best guesses so may have mistakes/inaccuracies.
|
||||
* Corrections/clarifications/additions are always welcome!
|
||||
*/
|
||||
|
||||
/* The header of the 0x100 attribute named "$EFS". */
|
||||
typedef struct {
|
||||
/* 0*/ u32 efs_length; /* Length of attribute in bytes. */
|
||||
u32 unknown1; /* always 0? */
|
||||
u32 unknown2; /* number of DDFs? */
|
||||
u32 unknown3; /* number of DRFs? */
|
||||
/* 16*/ u8 unknown4[16]; /* MD5 hash related to DDFs? */
|
||||
/* 32*/ u8 unknown5[16]; /* MD5 hash related to DRFs? */
|
||||
/* 48*/ u8 unknown6[16]; /* always 0? */
|
||||
/* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data
|
||||
decryption fields (DDF), see below. Zero if
|
||||
no DDFs are present. */
|
||||
u32 offset_to_drf_array;/* Offset in bytes to the array of data
|
||||
recovery fields (DRF), see below. Zero if
|
||||
no DRFs are present. */
|
||||
} __attribute__ ((__packed__)) EFS_ATTR_HEADER;
|
||||
|
||||
typedef struct {
|
||||
u32 df_count; /* Number of data decryption/recovery fields in
|
||||
the array. */
|
||||
} __attribute__ ((__packed__)) EFS_DF_ARRAY_HEADER;
|
||||
|
||||
typedef struct {
|
||||
/* 0*/ u32 df_length; /* Length of this data decryption/recovery
|
||||
field in bytes. */
|
||||
u32 cred_header_offset; /* Offset in bytes to the credential header. */
|
||||
u32 fek_size; /* Size in bytes of the encrypted file
|
||||
encryption key (FEK). */
|
||||
u32 fek_offset; /* Offset in bytes to the FEK from the start of
|
||||
the data decryption/recovery field. */
|
||||
/* 16*/ u32 unknown1; /* always 0? */
|
||||
} __attribute__ ((__packed__)) EFS_DF_HEADER;
|
||||
|
||||
typedef struct {
|
||||
/* 0*/ u32 cred_length; /* Length of this credential in bytes. */
|
||||
u32 sid_offset; /* Offset in bytes to the user's sid from start
|
||||
of this structure. */
|
||||
u32 cred_version; /* always 3? */
|
||||
u32 cert_header_size; /* Size in bytes of the certificate header. */
|
||||
/* 16*/ u32 cert_header_offset; /* Offset in bytes to the certificate header
|
||||
from start of this structure. */
|
||||
u32 unknown1; /* always 0? */
|
||||
u32 unknown2; /* always 0? */
|
||||
} __attribute__ ((__packed__)) EFS_DF_CREDENTIAL_HEADER;
|
||||
|
||||
typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER;
|
||||
|
||||
typedef struct {
|
||||
/* 0*/ u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */
|
||||
u32 thumbprint_size; /* Size of thumbprint in bytes. */
|
||||
/* 8*/ u32 guid_offset; /* Offset in bytes to GUID from start
|
||||
if this structure or 0 if no GUID
|
||||
present. */
|
||||
u32 container_name_offset; /* Offset in bytes to the name of the
|
||||
container from start of this
|
||||
structure or 0 if no name present. */
|
||||
/* 16*/ u32 user_name_offset; /* Offset in bytes to the user name
|
||||
from start of this structure. */
|
||||
} __attribute__ ((__packed__)) EFS_DF_CERTIFICATE_HEADER;
|
||||
|
||||
typedef EFS_DF_CERTIFICATE_HEADER EFS_DF_CERT_HEADER;
|
||||
|
||||
#endif /* defined _NTFS_LAYOUT_H */
|
||||
|
||||
|
|
|
@ -161,8 +161,7 @@ ntfs_decrypt_user_key_session *ntfs_decrypt_user_key_session_open(void)
|
|||
return NULL;
|
||||
}
|
||||
if (!(hSystemStore = fnCertOpenStore(((LPCSTR)CERT_STORE_PROV_SYSTEM),
|
||||
0, (HCRYPTPROV)NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
||||
L"MY"))) {
|
||||
0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"MY"))) {
|
||||
fprintf(stderr, "Could not open system store.\n");
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
|
@ -195,6 +194,13 @@ void ntfs_decrypt_user_key_session_close(ntfs_decrypt_user_key_session *session)
|
|||
free(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* reverse_buffer -
|
||||
*
|
||||
* This is a utility function for reversing the order of a buffer in place.
|
||||
* Users of this function should be very careful not to sweep byte order
|
||||
* problems under the rug.
|
||||
*/
|
||||
static inline void reverse_buffer(unsigned char *buf, unsigned buf_size)
|
||||
{
|
||||
unsigned char t;
|
||||
|
@ -256,14 +262,14 @@ ntfs_decrypt_user_key *ntfs_decrypt_user_key_open(
|
|||
&key_size)) {
|
||||
fprintf(stderr, "Could not export key: Error 0x%x\n",
|
||||
(unsigned)GetLastError());
|
||||
errno = -1;
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
CryptDestroyKey(hCryptKey);
|
||||
rsa_pub_key = (RSAPUBKEY*)(key_blob + sizeof(PUBLICKEYSTRUC));
|
||||
if ((err = gcry_ac_open(&gcry_handle, GCRY_AC_RSA, 0))) {
|
||||
fprintf(stderr, "Could not init gcrypt handle\n");
|
||||
errno = -1;
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
|
||||
|
@ -302,8 +308,8 @@ ntfs_decrypt_user_key *ntfs_decrypt_user_key_open(
|
|||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if ((key = (ntfs_decrypt_user_key*)malloc(
|
||||
sizeof(NTFS_DECRYPT_USER_KEY))))
|
||||
if ((key = (ntfs_decrypt_user_key*)
|
||||
malloc(sizeof(NTFS_DECRYPT_USER_KEY))))
|
||||
((NTFS_DECRYPT_USER_KEY*)key)->sexp_key = sexp_key;
|
||||
// todo: release all
|
||||
return key;
|
||||
|
@ -312,8 +318,10 @@ decrypt_key_open_err:
|
|||
CryptDestroyKey(hCryptKey);
|
||||
if (pCert)
|
||||
fnCertFreeCertificateContext(pCert);
|
||||
#endif /* defined(__CYGWIN__) */
|
||||
errno = EINVAL;
|
||||
#else /* !defined(__CYGWIN__) */
|
||||
errno = ENOTSUP;
|
||||
#endif /* !defined(__CYGWIN__) */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -427,6 +435,14 @@ static void ntfs_desx_key_expand(const u8 *src, u32 *des_key,
|
|||
*in_whitening = *(u64*)(md + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128
|
||||
* @context: pointer to a variable of type ntfs_desx_ctx
|
||||
* @key: the 128 bit DES-X-MS128 key, concated with the DES handle
|
||||
* @keylen: must always be 16
|
||||
*
|
||||
* This is the libgcrypt set_key implementation for DES-X-MS128.
|
||||
*/
|
||||
static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key,
|
||||
unsigned keylen)
|
||||
{
|
||||
|
@ -606,8 +622,6 @@ ntfs_decrypt_data_key *ntfs_decrypt_data_key_open(unsigned char *data,
|
|||
case CALG_DESX:
|
||||
/* FIXME: This really needs locking so it is safe from races. */
|
||||
if (!ntfs_desx_module_count++) {
|
||||
gcry_error_t err;
|
||||
|
||||
if (!desx_key_expand_test() || !des_test()) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
|
|
|
@ -272,17 +272,60 @@ static int cat_decrypt(ntfs_inode *inode, ntfs_decrypt_data_key *fek)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ntfs_decrypt_data_key *get_fek_from_df_array(
|
||||
ntfs_decrypt_user_key_session *session,
|
||||
EFS_DF_ARRAY_HEADER *efs_df_array)
|
||||
{
|
||||
u32 ddf_count, hash_size, fek_size;
|
||||
EFS_DF_CREDENTIAL_HEADER *efs_df_cred;
|
||||
EFS_DF_CERTIFICATE_HEADER *efs_df_cert;
|
||||
EFS_DF_HEADER *efs_df_header;
|
||||
ntfs_decrypt_user_key *key;
|
||||
u8 *hash_data, *fek_buf;
|
||||
unsigned i;
|
||||
|
||||
efs_df_header = (EFS_DF_HEADER*)(efs_df_array + 1);
|
||||
ddf_count = le32_to_cpu(efs_df_array->df_count);
|
||||
for (i = 0; i < ddf_count; i++) {
|
||||
//Eprintf("ddf #%u.\n", i);
|
||||
efs_df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)efs_df_header +
|
||||
le32_to_cpu(efs_df_header->cred_header_offset));
|
||||
efs_df_cert = (EFS_DF_CERTIFICATE_HEADER*)((u8*)efs_df_cred +
|
||||
le32_to_cpu(efs_df_cred->cert_header_offset));
|
||||
hash_size = le32_to_cpu(efs_df_cert->thumbprint_size);
|
||||
hash_data = (u8*)efs_df_cert +
|
||||
le32_to_cpu(efs_df_cert->thumbprint_offset);
|
||||
fek_size = le32_to_cpu(efs_df_header->fek_size);
|
||||
fek_buf = (u8*)efs_df_header +
|
||||
le32_to_cpu(efs_df_header->fek_offset);
|
||||
if ((key = ntfs_decrypt_user_key_open(session, hash_data,
|
||||
hash_size))) {
|
||||
fek_size = ntfs_decrypt_user_key_decrypt(key, fek_buf,
|
||||
fek_size);
|
||||
ntfs_decrypt_user_key_close(key);
|
||||
if (fek_size)
|
||||
return ntfs_decrypt_data_key_open(fek_buf,
|
||||
fek_size);
|
||||
fprintf(stderr, "Failed to decrypt the FEK.\n");
|
||||
} else
|
||||
perror("Failed to open user key");
|
||||
efs_df_header = (EFS_DF_HEADER*)((u8*)efs_df_header +
|
||||
le32_to_cpu(efs_df_header->df_length));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_fek
|
||||
*/
|
||||
static ntfs_decrypt_data_key *get_fek(ntfs_inode *inode)
|
||||
{
|
||||
ntfs_attr *na;
|
||||
unsigned char *efs_buffer, *ddf, *certificate, *hash_data, *fek_buf;
|
||||
u32 ddf_count, hash_size, fek_size;
|
||||
unsigned i;
|
||||
ntfs_decrypt_user_key_session *session;
|
||||
ntfs_decrypt_user_key *key;
|
||||
EFS_DF_ARRAY_HEADER *efs_df_array;
|
||||
ntfs_decrypt_data_key *fek;
|
||||
EFS_ATTR_HEADER *efs_attr;
|
||||
u8 *efs_buffer;
|
||||
ntfs_attr *na;
|
||||
|
||||
/* Obtain the $EFS contents. */
|
||||
na = ntfs_attr_open(inode, AT_LOGGED_UTILITY_STREAM,
|
||||
|
@ -313,44 +356,19 @@ static ntfs_decrypt_data_key *get_fek(ntfs_inode *inode)
|
|||
return NULL;
|
||||
}
|
||||
/* Iterate through the DDFs & DRFs until we obtain a key. */
|
||||
ddf = efs_buffer + le32_to_cpu(*(u32*)(efs_buffer + 0x40));
|
||||
ddf_count = le32_to_cpu(*(u32*)ddf);
|
||||
|
||||
ddf = ddf + 0x04;
|
||||
for (i = 0; i < ddf_count; i++) {
|
||||
//Eprintf("ddf #%u.\n", i);
|
||||
if (*(u32*)(ddf + 0x18))
|
||||
certificate = (ddf + 0x30 +
|
||||
le32_to_cpu(*(u32*)(ddf + 0x18)));
|
||||
else
|
||||
certificate = (ddf + 0x30);
|
||||
hash_size = (unsigned)le32_to_cpu(*(u32*)certificate);
|
||||
hash_data = certificate + (unsigned)
|
||||
le32_to_cpu(*(u32*)(certificate + 0x04));
|
||||
fek_size = (unsigned)le32_to_cpu(*(u32*)(ddf + 0x08));
|
||||
fek_buf = ddf + (unsigned)le32_to_cpu(*(u32*)(ddf + 0x0c));
|
||||
if ((key = ntfs_decrypt_user_key_open(session, hash_data,
|
||||
hash_size))) {
|
||||
fek_size = ntfs_decrypt_user_key_decrypt(key, fek_buf,
|
||||
fek_size);
|
||||
ntfs_decrypt_user_key_close(key);
|
||||
if (fek_size) {
|
||||
ntfs_decrypt_data_key *fek;
|
||||
|
||||
ntfs_decrypt_user_key_session_close(session);
|
||||
fek = ntfs_decrypt_data_key_open(fek_buf,
|
||||
fek_size);
|
||||
free(efs_buffer);
|
||||
return fek;
|
||||
}
|
||||
fprintf(stderr, "Failed to decrypt the FEK.\n");
|
||||
} else
|
||||
perror("Failed to open user key");
|
||||
ddf = ddf + le32_to_cpu(*(u32*)(ddf + 0x08)) +
|
||||
le32_to_cpu(*(u32*)(ddf + 0x0c));
|
||||
efs_attr = (EFS_ATTR_HEADER*)efs_buffer;
|
||||
efs_df_array = (EFS_DF_ARRAY_HEADER*)(efs_buffer +
|
||||
le32_to_cpu(efs_attr->offset_to_ddf_array));
|
||||
fek = get_fek_from_df_array(session, efs_df_array);
|
||||
if (!fek) {
|
||||
efs_df_array = (EFS_DF_ARRAY_HEADER*)(efs_buffer +
|
||||
le32_to_cpu(efs_attr->offset_to_drf_array));
|
||||
fek = get_fek_from_df_array(session, efs_df_array);
|
||||
}
|
||||
/* Close all and return. */
|
||||
ntfs_decrypt_user_key_session_close(session);
|
||||
return NULL;
|
||||
free(efs_buffer);
|
||||
return fek;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue