/**
* crypto.c - Routines for dealing with encrypted files. Part of the
* Linux-NTFS project.
*
* Copyright (c) 2005 Yuval Fledel
* Copyright (c) 2005-2007 Anton Altaparmakov
* Copyright (c) 2007 Yura Pakhuchiy
*
* 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 (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* TODO: Cleanup this file. Write nice descriptions for non-exported functions
* and maybe clean up namespace (not necessary for all functions to belong to
* ntfs_crypto, we can have ntfs_fek, ntfs_rsa, etc.., but there should be
* maximum 2-3 namespaces, not every function begins with it own namespace
* like now).
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SYS_TYPES_H
#endif
#ifdef HAVE_SYS_STAT_H
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include "compat.h"
#include "attrib.h"
#include "types.h"
#include "volume.h"
#include "debug.h"
#include "dir.h"
#include "layout.h"
#include "crypto.h"
#ifdef ENABLE_CRYPTO
#include <gcrypt.h>
#include <libconfig.h>
typedef enum {
/**
* enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit)
*
* To choose which one is used in Windows, create or set the REG_DWORD registry
* key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\
* AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX,
* set AlgorithmID to 0x6604.
*
* Note that the Windows versions I have tried so far (all are high crypto
* enabled) ignore the AlgorithmID value if it is not one of CALG_3DES,
* CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using
* this registry key. It would be interesting to check out encryption on one
* of the "crippled" crypto Windows versions...
*/
typedef enum {
/* If not one of the below three, fall back to standard Des. */
/**
* struct ntfs_fek - Decrypted, in-memory file encryption key.
*/
struct _ntfs_fek {
};
struct _ntfs_crypto_attr {
};
typedef struct {
};
typedef struct {
/*
* Yes, global variables sucks, but we need to keep whether we performed
*/
typedef struct {
int initialized;
int desx_alg_id;
int nr_rsa_keys;
.desx_alg_id = -1,
.desx_module = NULL,
};
/**
* ntfs_pkcs12_load_pfxfile
*/
unsigned *pfx_size)
{
ntfs_log_error("You have to specify the key file, a pointer "
"to hold the key file contents, and a pointer "
"to hold the size of the key file contents.\n");
return -1;
}
if (f == -1) {
ntfs_log_perror("Failed to open key file");
return -1;
}
ntfs_log_perror("Failed to stat key file");
goto file_out;
}
ntfs_log_error("Key file is not a regular file, cannot read "
"it.\n");
goto file_out;
}
ntfs_log_error("Key file has zero size.\n");
goto file_out;
}
if (!*pfx) {
ntfs_log_perror("Failed to allocate buffer for key file "
"contents");
goto file_out;
}
do {
if (br == -1) {
ntfs_log_perror("Failed to read from key file");
goto free_out;
}
if (!br)
attempts++;
close(f);
/* Make sure it is zero terminated. */
return 0;
close(f);
return -1;
}
/**
* ntfs_rsa_private_key_import_from_gnutls
*/
{
int i, j;
/* Extract the RSA parameters from the GNU TLS private key. */
ntfs_log_error("Failed to export rsa parameters. (Is the "
"key an RSA private key?)\n");
return NULL;
}
/* Convert each RSA parameter to MPI format. */
for (i = 0; i < 6; i++) {
ntfs_log_error("Failed to convert RSA parameter %i "
"to mpi format (size %d)\n", i,
break;
}
}
/* Release the no longer needed datum values. */
for (j = 0; j < 6; j++) {
}
/*
* Build the gcrypt private key, note libgcrypt uses p and q inversed
* to what gnutls uses.
*/
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
ntfs_log_error("Failed to build RSA private key s-exp.\n");
}
/* Release the no longer needed MPI values. */
for (j = 0; j < i; j++)
gcry_mpi_release(rm[j]);
return rsa_key;
}
/**
* ntfs_rsa_private_key_release
*/
{
if (rsa_key) {
}
}
/**
* ntfs_pkcs12_extract_rsa_key
*/
{
if (!rsa_key) {
return NULL;
}
/* Create a pkcs12 structure. */
if (err) {
ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n",
goto err;
}
/* Convert the PFX file (DER format) to native pkcs12 format. */
if (err) {
ntfs_log_error("Failed to convert the PFX file from DER to "
"native PKCS#12 format: %s\n",
goto err;
}
/*
* Verify that the password is correct and that the key file has not
* been tampered with. Note if the password has zero length and the
* verification fails, retry with password set to NULL. This is needed
* to get password less .pfx files generated with Windows XP SP1 (and
* probably earlier versions of Windows) to work.
*/
if (err) {
if (err == GNUTLS_E_MAC_VERIFY_FAILED &&
goto retry_verify;
}
ntfs_log_error("You are probably misspelled password to PFX "
"file.\n");
goto err;
}
if (err) {
ntfs_log_error("Failed to initialize PKCS#12 Bag "
"structure: %s\n",
goto err;
}
if (err) {
if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
err = 0;
break;
}
ntfs_log_error("Failed to obtain Bag from PKCS#12 "
"structure: %s\n",
goto err;
}
if (err < 0) {
ntfs_log_error("Failed to obtain Bag count: %s\n",
goto err;
}
if (err < 0) {
ntfs_log_error("Failed to determine Bag type: %s\n",
goto err;
}
flags = 0;
switch (err) {
case GNUTLS_BAG_PKCS8_KEY:
if (err < 0) {
ntfs_log_error("Failed to obtain Bag data: "
goto err;
}
if (err) {
ntfs_log_error("Failed to initialized "
"private key structure: %s\n",
goto err;
}
/* Decrypt the private key into GNU TLS format. */
if (err) {
ntfs_log_error("Failed to convert private "
"key from DER to GNU TLS "
"format: %s\n",
goto err;
}
#if 0
/*
* Export the key again, but unencrypted, and output it
* to stderr. Note the output has an RSA header so to
* compare to openssl pkcs12 -nodes -in myfile.pfx
* output need to ignore the part of the key between
* the first "MII..." up to the second "MII...". The
* actual RSA private key begins at the second "MII..."
* and in my testing at least was identical to openssl
* output and was also identical both on big and little
* endian so gnutls should be endianness safe.
*/
&bufsize);
if (err) {
ntfs_log_error("eek1\n");
exit(1);
}
#endif
/* Convert the private key to our internal format. */
goto err;
break;
case GNUTLS_BAG_ENCRYPTED:
if (err) {
ntfs_log_error("Failed to decrypt Bag: %s\n",
goto err;
}
goto check_again;
case GNUTLS_BAG_CERTIFICATE:
if (err < 0) {
ntfs_log_error("Failed to obtain Bag data: "
goto err;
}
if (err) {
ntfs_log_error("Failed to initialize "
"certificate structure: %s\n",
goto err;
}
if (err) {
ntfs_log_error("Failed to convert certificate "
"from DER to GNU TLS format: "
goto err;
}
if (err) {
ntfs_log_error("Failed to get key purpose "
"OID: %s\n",
goto err;
}
if (!strcmp(purpose_oid,
else if (!strcmp(purpose_oid,
else {
ntfs_log_error("Certificate has unknown "
"purpose OID %s.\n",
goto err;
}
/* Return the thumbprint to the caller. */
&tp_size);
if (err) {
ntfs_log_error("Failed to get thumbprint: "
goto err;
}
if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) {
ntfs_log_error("Invalid thumbprint size %zd. "
"Should be %d.\n", tp_size,
sizeof(rsa_key->thumbprint));
goto err;
}
break;
default:
/* We do not care about other types. */
break;
}
}
err:
!have_thumbprint) {
if (!err)
ntfs_log_error("Key type or thumbprint not found, "
"aborting.\n");
}
if (crt)
if (pkey)
if (bag)
if (pkcs12)
return rsa_key;
}
/**
* ntfs_buffer_reverse -
*
* 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.
*/
{
unsigned i;
u8 t;
for (i = 0; i < buf_size / 2; i++) {
t = buf[i];
}
}
#ifndef HAVE_STRNLEN
/**
* strnlen - strnlen is a gnu extension so emulate it if not present
*/
{
const char *p, *end;
/* Look for a '\0' character. */
;
return p - s;
}
#endif /* ! HAVE_STRNLEN */
/**
* ntfs_raw_fek_decrypt -
*
* Note: decrypting into the input buffer.
*/
{
/* Reverse the raw FEK. */
/* Convert the FEK to internal MPI format. */
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to convert file encryption key to "
"internal MPI format: %s\n",
gcry_strerror(err));
return 0;
}
/* Create an internal S-expression from the FEK. */
"(enc-val (flags) (rsa (a %m)))", fek_mpi);
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to create internal S-expression of "
"the file encryption key: %s\n",
gcry_strerror(err));
return 0;
}
/* Decrypt the FEK. */
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to decrypt the file encryption key: "
return 0;
}
/* Extract the actual FEK from the decrypted raw S-expression. */
if (!fek_sexp) {
ntfs_log_error("Failed to find the decrypted file encryption "
"key in the internal S-expression.\n");
return 0;
}
/* Convert the decrypted FEK S-expression into MPI format. */
if (!fek_mpi) {
ntfs_log_error("Failed to convert the decrypted file "
"encryption key S-expression to internal MPI "
"format.\n");
return 0;
}
/* Convert the decrypted FEK from MPI format to binary data. */
ntfs_log_error("Failed to convert decrypted file encryption "
"key from internal MPI format to binary data: "
return 0;
}
/*
* Finally, remove the PKCS#1 padding and return the size of the
* decrypted FEK.
*/
ntfs_log_error("Failed to remove PKCS#1 padding from "
"decrypted file encryption key.\n");
return 0;
}
return size;
}
/**
* ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key
* @src: source buffer containing 128-bit key
*
* Expands the on-disk 128-bit desx key to the needed des key, the in-, and the
* out-whitening keys required to perform desx {de,en}cryption.
*/
{
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to open MD5 digest.\n");
return err;
}
/* Hash the on-disk key. */
/* Copy the current hash for efficiency. */
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to copy MD5 digest object.\n");
goto out;
}
/* Hash with the first salt and store the result. */
/* Hash with the second salt and store the result. */
out:
return err;
}
/**
* 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.
*/
unsigned keylen)
{
if (keylen != 16) {
ntfs_log_error("Key length for desx must be 16.\n");
return GPG_ERR_INV_KEYLEN;
}
GCRY_CIPHER_MODE_ECB, 0);
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to open des cipher (error 0x%x).\n",
err);
return err;
}
&ctx->in_whitening);
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to expand desx key (error 0x%x).\n",
err);
return err;
}
if (err != GPG_ERR_NO_ERROR) {
return err;
}
/*
* Take a note of the ctx->gcry_cipher_hd since we need to close it at
* ntfs_decrypt_data_key_close() time.
*/
return GPG_ERR_NO_ERROR;
}
/**
* ntfs_desx_decrypt
*/
{
if (err != GPG_ERR_NO_ERROR)
ntfs_log_error("Failed to reset des cipher (error 0x%x).\n",
err);
if (err != GPG_ERR_NO_ERROR)
}
.name = "DES-X-MS128",
.blocksize = 8,
.keylen = 128,
.contextsize = sizeof(ntfs_desx_ctx),
};
#ifdef NTFS_TEST
/*
* Do not remove this test code from this file! (AIA)
* It would be nice to move all tests (these and runlist) out of the library
* (at least, into the separate file{,s}), so they would not annoy eyes. (Yura)
*/
/**
* ntfs_desx_key_expand_test
*/
{
0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f,
0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30
};
0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f,
};
0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d,
};
0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e
};
union {
} test_des_key;
if (err != GPG_ERR_NO_ERROR)
else
*(u64*)known_out_whitening &&
ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n",
return res;
}
/**
* ntfs_des_test
*/
{
0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f
};
0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f
};
0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09
};
int res;
GCRY_CIPHER_MODE_ECB, 0);
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to open des cipher (error 0x%x).\n",
err);
return FALSE;
}
sizeof(known_des_key));
if (err != GPG_ERR_NO_ERROR) {
return FALSE;
}
/*
* Apply DES decryption (ntfs actually uses encryption when decrypting).
*/
sizeof(test_decrypted_data), known_des_encrypted_data,
sizeof(known_des_encrypted_data));
if (err) {
ntfs_log_error("Failed to des decrypt test data (error "
"0x%x).\n", err);
return FALSE;
}
sizeof(known_decrypted_data));
ntfs_log_error("Testing whether des decryption works: %s\n",
return res;
}
#else /* !defined(NTFS_TEST) */
/**
* ntfs_desx_key_expand_test
*/
{
return TRUE;
}
/**
* ntfs_des_test
*/
{
return TRUE;
}
#endif /* !defined(NTFS_TEST) */
/**
* ntfs_fek_import_from_raw
*/
unsigned fek_size)
{
ntfs_log_debug("Invalid FEK. It was probably decrypted with "
"the incorrect RSA key.");
return NULL;
}
sizeof(gcry_cipher_hd_t));
if (!fek) {
return NULL;
}
case CALG_DESX:
if (!ntfs_crypto_ctx.desx_module) {
if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) {
goto out;
}
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to register desx "
"cipher: %s\n",
gcry_strerror(err));
goto out;
}
}
wanted_key_size = 16;
break;
case CALG_3DES:
wanted_key_size = 24;
break;
case CALG_AES_256:
wanted_key_size = 32;
break;
default:
wanted_key_size = 8;
ntfs_log_error("DES is not supported at present\n");
else
ntfs_log_error("Unknown crypto algorithm 0x%x\n",
ntfs_log_error(". Please email %s and say that you saw this "
"message. We will then try to implement "
"support for this algorithm.\n", NTFS_DEV_LIST);
err = EOPNOTSUPP;
goto out;
}
if (key_size != wanted_key_size) {
ntfs_log_error("%s key of %u bytes but needed size is %u "
"bytes, assuming corrupt or incorrect key. "
"Aborting.\n",
(unsigned)key_size, (unsigned)wanted_key_size);
goto out;
}
GCRY_CIPHER_MODE_CBC, 0);
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("gcry_cipher_open() failed: %s\n",
gcry_strerror(err));
goto out;
}
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("gcry_cipher_setkey() failed: %s\n",
gcry_strerror(err));
goto out;
}
return fek;
out:
return NULL;
}
/**
* ntfs_fek_release
*/
{
if (fek->des_gcry_cipher_hd_ptr)
}
/**
* ntfs_df_array_fek_get
*/
{
if (!df_count)
ntfs_log_error("There are no elements in the DF array.\n");
ntfs_log_debug("Credential type is not certificate "
"thumbprint, skipping DF entry.\n");
continue;
}
ntfs_log_error("Thumbprint size %d is not valid "
"(should be %d), skipping this DF "
"entry.\n",
continue;
}
ntfs_log_debug("Thumbprints do not match, skipping "
"this DF entry.\n");
continue;
}
/*
* The thumbprints match so this is probably the DF entry
* matching the RSA key. Try to decrypt the FEK with it.
*/
/* Decrypt the FEK. Note: This is done in place. */
if (fek_size) {
/* Convert the FEK to our internal format. */
if (fek)
return fek;
ntfs_log_error("Failed to convert the decrypted file "
"encryption key to internal format.\n");
} else
ntfs_log_error("Failed to decrypt the file "
"encryption key.\n");
}
return NULL;
}
/**
* ntfs_inode_fek_get -
*/
{
/* Obtain the $EFS contents. */
NULL);
if (!efs) {
ntfs_log_perror("Failed to read $EFS attribute");
return NULL;
}
/*
* Depending on whether the key is a normal key or a data recovery key,
* iterate through the DDF or DRF array, respectively.
*/
if (efs->offset_to_ddf_array)
else
ntfs_log_error("There are no entries in the DDF "
"array.\n");
if (efs->offset_to_drf_array)
else
ntfs_log_error("There are no entries in the DRF "
"array.\n");
} else
ntfs_log_error("Invalid DF type.\n");
if (df_array)
return fek;
}
/**
* ntfs_fek_decrypt_sector
*/
{
if (err != GPG_ERR_NO_ERROR) {
ntfs_log_error("Failed to reset cipher: %s\n",
gcry_strerror(err));
return -1;
}
/*
* Note: You may wonder why we are not calling gcry_cipher_setiv() here
* instead of doing it by hand after the decryption. The answer is
* that gcry_cipher_setiv() wants an iv of length 8 bytes but we give
* it a length of 16 for AES256 so it does not like it.
*/
if (err != GPG_ERR_NO_ERROR) {
return -1;
}
/* Apply the IV. */
} else {
/* All other algorithms (Des, 3Des, DesX) use the same IV. */
}
return 512;
}
/**
* ntfs_crypto_deinit - perform library-wide crypto deinitialization
*/
static void ntfs_crypto_deinit(void)
{
int i;
if (!ntfs_crypto_ctx.initialized)
return;
for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++)
if (ntfs_crypto_ctx.desx_module) {
}
}
{
unsigned pfx_size;
int i;
/* Search for crypto.keys list. */
if (!cfg_keys) {
ntfs_log_error("Unable to find crypto.keys in config file.\n");
return;
}
/* Iterate trough list of records about keys. */
/* Get path and password to key. */
if (!pfx_file) {
ntfs_log_error("Entry number %d in section crypto.keys "
"of configuration file formed "
"incorrectly.\n", i + 1);
continue;
}
if (!pfx_pwd)
pfx_pwd = "";
/* Load the PKCS#12 file containing the user's private key. */
ntfs_log_error("Failed to load key file %s.\n",
pfx_file);
continue;
}
/*
* Check whether we need to allocate memory for new key pointer.
* If yes, allocate memory for it and for 3 more pointers.
*/
sizeof(ntfs_rsa_private_key_t *) *
if (!new) {
ntfs_log_perror("Unable to store all keys");
break;
}
}
/* Obtain the user's private RSA key from the key file. */
if (key)
else
ntfs_log_error("Failed to obtain RSA key from %s\n",
pfx_file);
/* No longer need the pfx file contents. */
}
}
static void ntfs_crypto_read_configs(void)
{
char *home;
config_init(&cfg);
/* Load system configuration file. */
else
ntfs_log_error("Failed to read system configuration "
"file: %s (line %d).\n",
config_error_line(&cfg));
/* Load user configuration file. */
if (fd == -1) {
ntfs_log_error("Failed to open working directory.\n");
goto out;
}
if (!home) {
ntfs_log_error("Environment variable HOME is not set.\n");
goto out;
}
ntfs_log_perror("chdir() to home directory failed");
goto out;
}
else
ntfs_log_error("Failed to read user configuration "
"file: %s (line %d).\n",
config_error_line(&cfg));
ntfs_log_error("Failed to restore original working "
"directory.\n");
out:
if (fd != -1)
}
/**
* ntfs_crypto_init - perform library-wide crypto initializations
*
* This function is called during first call of ntfs_crypto_attr_open and
* performs gcrypt and GNU TLS initializations, then read list of PFX files
* from configuration files and load RSA keys from them.
*/
static int ntfs_crypto_init(void)
{
int err;
return 0;
/* Initialize gcrypt library. Note: Must come before GNU TLS init. */
ntfs_log_error("Failed to initialize the gcrypt library.\n");
return -1;
}
/* Initialize GNU TLS library. Note: Must come after libgcrypt init. */
err = gnutls_global_init();
if (err < 0) {
ntfs_log_error("Failed to initialize GNU TLS library: %s\n",
return -1;
}
/* Read crypto related sections of libntfs configuration files. */
return 0;
}
/**
* ntfs_crypto_attr_open - perform crypto related initialization for attribute
* @na: ntfs attribute to perform initialization for
*
* This function is called from ntfs_attr_open for encrypted attributes and
* tries to decrypt FEK enumerating all user submitted RSA keys. If we
* successfully obtained FEK, then @na->crypto is allocated and FEK stored
* inside. In the other case @na->crypto is set to NULL.
*
* Return 0 on success and -1 on error with errno set to the error code.
*/
{
int i;
return -1;
}
if (ntfs_crypto_init()) {
return -1;
}
for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++) {
if (fek) {
return -1;
return 0;
}
}
return -1;
}
/**
* ntfs_crypto_attr_close - perform crypto related deinit for attribute
* @na: ntfs attribute to perform deinitialization for
*
* This function is called from ntfs_attr_close for encrypted attributes and
* frees memory that were allocated for it handling.
*/
{
return;
}
}
/**
* ntfs_crypto_attr_pread - read from an encrypted attribute
* @na: ntfs attribute to read from
* @pos: byte position in the attribute to begin reading from
* @count: number of bytes to read
* @b: output data buffer
*
* This function is called from ntfs_attr_pread for encrypted attributes and
* should behave as described in ntfs_attr_pread description.
*/
{
unsigned char *buffer;
int i;
return -1;
}
if (!count)
return 0;
return -1;
}
if (!buffer)
return -1;
total = 0;
/* Calculate number of bytes we actually want. */
if (length < 0) {
total = -1;
ntfs_log_error("LIBRARY BUG!!! Please report that you "
"saw this message to %s. Thanks!",
break;
}
/* Just write zeros if @offset fully beyond initialized size. */
continue;
}
if (!bytes_read)
break;
if (bytes_read != NTFS_EFS_SECTOR_SIZE) {
ntfs_log_perror("%s(): ntfs_rl_pread returned %lld "
break;
}
offset)) < bytes_read) {
ntfs_log_error("%s(): Couldn't decrypt all data "
"(%u/%lld/%lld/%lld)!", "ntfs_crypto_attr_pread",
i, (long long)bytes_read,
break;
}
/* Handle partially in initialized size situation. */
else {
}
offset += bytes_read;
}
return total;
}
#else /* !ENABLE_CRYPTO */
/* Stubs for crypto-disabled version of libntfs. */
{
return -1;
}
{
}
void *b)
{
return -1;
}
#endif /* !ENABLE_CRYPTO */