diff --git a/include/ntfs/attrib.h b/include/ntfs/attrib.h index b9f93688..1785af1c 100644 --- a/include/ntfs/attrib.h +++ b/include/ntfs/attrib.h @@ -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, diff --git a/include/ntfs/layout.h b/include/ntfs/layout.h index 44a131c2..f1e31769 100644 --- a/include/ntfs/layout.h +++ b/include/ntfs/layout.h @@ -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 */ - diff --git a/ntfsprogs/decrypt.c b/ntfsprogs/decrypt.c index 0e3dff95..32f0dcc9 100644 --- a/ntfsprogs/decrypt.c +++ b/ntfsprogs/decrypt.c @@ -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; diff --git a/ntfsprogs/ntfsdecrypt.c b/ntfsprogs/ntfsdecrypt.c index 3a7ec04c..4c9f285f 100644 --- a/ntfsprogs/ntfsdecrypt.c +++ b/ntfsprogs/ntfsdecrypt.c @@ -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; } /**