size_t size = 0;
/* Create per-job session encryption context */
- jcr->pki_session = crypto_session_new(cipher, jcr->pki_readers);
+ jcr->pki_session = crypto_session_new(cipher, jcr->pki_recipients);
/* Get the session data size */
if (crypto_session_encode(jcr->pki_session, NULL, &size) == false) {
me->pki_sign = true;
}
- if ((me->pki_encrypt || me->pki_sign) && !me->pki_keypairfile) {
+ if ((me->pki_encrypt || me->pki_sign) && !me->pki_keypair_file) {
Emsg2(M_FATAL, 0, _("\"PKI Key Pair\" must be defined for File"
" daemon \"%s\" in %s if either \"PKI Sign\" or"
" \"PKI Encrypt\" are enabled.\n"), me->hdr.name, configfile);
/* If everything is well, attempt to initialize our public/private keys */
if (OK && (me->pki_encrypt || me->pki_sign)) {
char *filepath;
-
/* Load our keypair */
me->pki_keypair = crypto_keypair_new();
if (!me->pki_keypair) {
Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n"));
OK = false;
} else {
- if (!crypto_keypair_load_cert(me->pki_keypair, me->pki_keypairfile)) {
+ if (!crypto_keypair_load_cert(me->pki_keypair, me->pki_keypair_file)) {
Emsg2(M_FATAL, 0, _("Failed to load public certificate for File"
" daemon \"%s\" in %s.\n"), me->hdr.name, configfile);
OK = false;
}
- if (!crypto_keypair_load_key(me->pki_keypair, me->pki_keypairfile, NULL, NULL)) {
+ if (!crypto_keypair_load_key(me->pki_keypair, me->pki_keypair_file, NULL, NULL)) {
Emsg2(M_FATAL, 0, _("Failed to load private key for File"
" daemon \"%s\" in %s.\n"), me->hdr.name, configfile);
OK = false;
* Trusted Signers. We're always trusted.
*/
me->pki_signers = New(alist(10, not_owned_by_alist));
- me->pki_signers->append(crypto_keypair_dup(me->pki_keypair));
+ if (me->pki_keypair) {
+ me->pki_signers->append(crypto_keypair_dup(me->pki_keypair));
+ }
- /* If additional trusted keys have been specified, load them up */
- if (me->pki_trustedkeys) {
- foreach_alist(filepath, me->pki_trustedkeys) {
+ /* If additional signing public keys have been specified, load them up */
+ if (me->pki_signing_key_files) {
+ foreach_alist(filepath, me->pki_signing_key_files) {
X509_KEYPAIR *keypair;
keypair = crypto_keypair_new();
} else {
if (crypto_keypair_load_cert(keypair, filepath)) {
me->pki_signers->append(keypair);
+
+ /* Attempt to load a private key, if available */
+ if (crypto_keypair_has_key(filepath)) {
+ if (!crypto_keypair_load_key(keypair, filepath, NULL, NULL)) {
+ Emsg3(M_FATAL, 0, _("Failed to load private key from file %s for File"
+ " daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile);
+ OK = false;
+ }
+ }
+
} else {
Emsg3(M_FATAL, 0, _("Failed to load trusted signer certificate"
" from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile);
}
}
- if (me->pki_encrypt) {
- /*
- * Trusted readers. We're always trusted.
- * The symmetric session key will be encrypted for each of these readers.
- */
- me->pki_readers = New(alist(10, not_owned_by_alist));
- me->pki_readers->append(crypto_keypair_dup(me->pki_keypair));
+ /*
+ * Crypto recipients. We're always included as a recipient.
+ * The symmetric session key will be encrypted for each of these readers.
+ */
+ me->pki_recipients = New(alist(10, not_owned_by_alist));
+ if (me->pki_keypair) {
+ me->pki_recipients->append(crypto_keypair_dup(me->pki_keypair));
+ }
- /* If additional keys have been specified, load them up */
- if (me->pki_masterkeys) {
- foreach_alist(filepath, me->pki_masterkeys) {
- X509_KEYPAIR *keypair;
+ /* If additional keys have been specified, load them up */
+ if (me->pki_master_key_files) {
+ foreach_alist(filepath, me->pki_master_key_files) {
+ X509_KEYPAIR *keypair;
- keypair = crypto_keypair_new();
- if (!keypair) {
- Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n"));
- OK = false;
- } else {
- if (crypto_keypair_load_cert(keypair, filepath)) {
- me->pki_signers->append(keypair);
- } else {
- Emsg3(M_FATAL, 0, _("Failed to load master key certificate"
- " from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile);
- OK = false;
+ keypair = crypto_keypair_new();
+ if (!keypair) {
+ Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n"));
+ OK = false;
+ } else {
+ if (crypto_keypair_load_cert(keypair, filepath)) {
+ me->pki_recipients->append(keypair);
+
+ /* Attempt to load a private key, if available */
+ if (crypto_keypair_has_key(filepath)) {
+ if (!crypto_keypair_load_key(keypair, filepath, NULL, NULL)) {
+ Emsg3(M_FATAL, 0, _("Failed to load private key from file %s for File"
+ " daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile);
+ OK = false;
+ }
}
+
+ } else {
+ Emsg3(M_FATAL, 0, _("Failed to load master key certificate"
+ " from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile);
+ OK = false;
}
}
}
{"maximumnetworkbuffersize", store_pint, ITEM(res_client.max_network_buffer_size), 0, 0, 0},
{"pkisignatures", store_yesno, ITEM(res_client.pki_sign), 1, ITEM_DEFAULT, 0},
{"pkiencryption", store_yesno, ITEM(res_client.pki_encrypt), 1, ITEM_DEFAULT, 0},
- {"pkikeypair", store_dir, ITEM(res_client.pki_keypairfile), 0, 0, 0},
- {"pkitrustedsigner", store_alist_str, ITEM(res_client.pki_trustedkeys), 0, 0, 0},
- {"pkimasterkey", store_alist_str, ITEM(res_client.pki_masterkeys), 0, 0, 0},
+ {"pkikeypair", store_dir, ITEM(res_client.pki_keypair_file), 0, 0, 0},
+ {"pkisigner", store_alist_str, ITEM(res_client.pki_signing_key_files), 0, 0, 0},
+ {"pkimasterkey", store_alist_str, ITEM(res_client.pki_master_key_files), 0, 0, 0},
{"tlsenable", store_yesno, ITEM(res_client.tls_enable), 1, 0, 0},
{"tlsrequire", store_yesno, ITEM(res_client.tls_require), 1, 0, 0},
{"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
if (res->res_client.FDaddrs) {
free_addresses(res->res_client.FDaddrs);
}
- if (res->res_client.pki_keypairfile) {
- free(res->res_client.pki_keypairfile);
+
+ if (res->res_client.pki_keypair_file) {
+ free(res->res_client.pki_keypair_file);
}
if (res->res_client.pki_keypair) {
- crypto_keypair_free(res->res_client.pki_keypair);
+ crypto_keypair_free(res->res_client.pki_keypair);
}
- /* Also frees res_client.pki_keypair */
- if (res->res_client.pki_trustedkeys) {
- delete res->res_client.pki_trustedkeys;
+
+ if (res->res_client.pki_signing_key_files) {
+ delete res->res_client.pki_signing_key_files;
}
if (res->res_client.pki_signers) {
X509_KEYPAIR *keypair;
}
delete res->res_client.pki_signers;
}
- if (res->res_client.pki_masterkeys) {
- delete res->res_client.pki_masterkeys;
+
+ if (res->res_client.pki_master_key_files) {
+ delete res->res_client.pki_master_key_files;
}
- if (res->res_client.pki_readers) {
+
+ if (res->res_client.pki_recipients) {
X509_KEYPAIR *keypair;
- foreach_alist(keypair, res->res_client.pki_readers) {
+ foreach_alist(keypair, res->res_client.pki_recipients) {
crypto_keypair_free(keypair);
}
- delete res->res_client.pki_signers;
+ delete res->res_client.pki_recipients;
}
if (res->res_client.tls_ctx) {
if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_dir.hdr.name)) == NULL) {
Emsg1(M_ABORT, 0, _("Cannot find Client resource %s\n"), res_all.res_dir.hdr.name);
}
- res->res_client.pki_trustedkeys = res_all.res_client.pki_trustedkeys;
+ res->res_client.pki_signing_key_files = res_all.res_client.pki_signing_key_files;
+ res->res_client.pki_master_key_files = res_all.res_client.pki_master_key_files;
+
res->res_client.pki_signers = res_all.res_client.pki_signers;
+ res->res_client.pki_recipients = res_all.res_client.pki_recipients;
+
res->res_client.messages = res_all.res_client.messages;
break;
default:
uint32_t max_network_buffer_size; /* max network buf size */
int pki_sign; /* Enable Data Integrity Verification via Digital Signatures */
int pki_encrypt; /* Enable Data Encryption */
- char *pki_keypairfile; /* PKI Key Pair File */
- alist *pki_trustedkeys; /* PKI Trusted Public Keys */
- alist *pki_masterkeys; /* PKI Master Keys */
+ char *pki_keypair_file; /* PKI Key Pair File */
+ alist *pki_signing_key_files; /* PKI Signing Key Files */
+ alist *pki_master_key_files; /* PKI Master Key Files */
int tls_enable; /* Enable TLS */
int tls_require; /* Require TLS */
char *tls_ca_certfile; /* TLS CA Certificate File */
X509_KEYPAIR *pki_keypair; /* Shared PKI Public/Private Keypair */
alist *pki_signers; /* Shared PKI Trusted Signers */
- alist *pki_readers; /* Shared PKI Recipients */
+ alist *pki_recipients; /* Shared PKI Recipients */
TLS_CONTEXT *tls_ctx; /* Shared TLS Context */
};
jcr->pki_encrypt = me->pki_encrypt;
jcr->pki_keypair = me->pki_keypair;
jcr->pki_signers = me->pki_signers;
- jcr->pki_readers = me->pki_readers;
+ jcr->pki_recipients = me->pki_recipients;
dir->jcr = jcr;
enable_backup_privileges(NULL, 1 /* ignore_errors */);
uint64_t alt_addr = 0; /* Write address for alternative stream */
intmax_t alt_size = 0; /* Size of alternate stream */
SIGNATURE *sig = NULL; /* Cryptographic signature (if any) for file */
+ CRYPTO_SESSION *cs = NULL; /* Cryptographic session data (if any) for file */
int flags; /* Options for extract_data() */
int stat;
ATTR *attr;
} else {
Dmsg1(100, "Signature good on %s\n", jcr->last_fname);
}
- crypto_sign_free(sig);
- sig = NULL;
} else {
Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"), jcr->last_fname);
}
}
+
+ /* Free Signature */
+ if (sig) {
+ crypto_sign_free(sig);
+ sig = NULL;
+ }
+
+ if (cs) {
+ crypto_session_free(cs);
+ cs = NULL;
+ }
+
Dmsg0(30, "Stop extracting.\n");
} else if (is_bopen(&bfd)) {
Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n"));
/* Data stream */
case STREAM_ENCRYPTED_SESSION_DATA:
- // TODO landonf: Implement
- // sig = crypto_sign_decode(sd->msg, (size_t) sd->msglen);
Dmsg1(30, "Stream=Encrypted Session Data, size: %d\n", sd->msglen);
+ /* Save session keys . */
+ switch(crypto_session_decode(sd->msg, (size_t) sd->msglen, jcr->pki_recipients, &cs)) {
+ case CRYPTO_ERROR_NONE:
+ /* Success */
+ break;
+ case CRYPTO_ERROR_NORECIPIENT:
+ Jmsg(jcr, M_ERROR, 0, _("Missing private key required to decrypt encrypted backup data."));
+ break;
+ case CRYPTO_ERROR_DECRYPTION:
+ Jmsg(jcr, M_ERROR, 0, _("Decrypt of the session key failed."));
+ break;
+ default:
+ /* Shouldn't happen */
+ Jmsg(jcr, M_ERROR, 0, _("An error occured while decoding encrypted session data stream."));
+ break;
+ }
+
break;
case STREAM_FILE_DATA:
DIGEST *digest; /* Last file's digest context */
X509_KEYPAIR *pki_keypair; /* Encryption key pair */
alist *pki_signers; /* Trusted Signers */
- alist *pki_readers; /* Trusted Readers */
+ alist *pki_recipients; /* Trusted Recipients */
CRYPTO_SESSION *pki_session; /* PKE Public Keys + Symmetric Session Keys */
void *pki_session_encoded; /* Cached DER-encoded copy of pki_session */
size_t pki_session_encoded_size; /* Size of DER-encoded pki_session */
/* Encryption Session Data */
struct Crypto_Session {
CryptoData *cryptoData; /* ASN.1 Structure */
- EVP_CIPHER *openssl_cipher; /* OpenSSL Cipher Object */
- unsigned char session_key[EVP_MAX_KEY_LENGTH]; /* Private symmetric session key */
+ unsigned char *session_key; /* Private symmetric session key */
size_t session_key_len; /* Symmetric session key length */
};
return (ctx->pem_callback(buf, size, ctx->pem_userdata));
}
+/*
+ * Check a PEM-encoded file
+ * for the existence of a private key.
+ * Returns: true if a private key is found
+ * false otherwise
+ */
+bool crypto_keypair_has_key (const char *file) {
+ BIO *bio;
+ char *name = NULL;
+ char *header = NULL;
+ unsigned char *data = NULL;
+ bool retval = false;
+ long len;
+
+ if (!(bio = BIO_new_file(file, "r"))) {
+ openssl_post_errors(M_ERROR, _("Unable to open private key file"));
+ return false;
+ }
+
+ while (PEM_read_bio(bio, &name, &header, &data, &len)) {
+ /* We don't care what the data is, just that it's there */
+ OPENSSL_free(header);
+ OPENSSL_free(data);
+
+ /*
+ * PEM Header Found, check for a private key
+ * Due to OpenSSL limitations, we must specifically
+ * list supported PEM private key encodings.
+ */
+ if (strcmp(name, PEM_STRING_RSA) == 0
+ || strcmp(name, PEM_STRING_DSA) == 0
+ || strcmp(name, PEM_STRING_PKCS8) == 0
+ || strcmp(name, PEM_STRING_PKCS8INF) == 0) {
+ retval = true;
+ OPENSSL_free(name);
+ break;
+ } else {
+ OPENSSL_free(name);
+ }
+ }
+
+ /* Free our bio */
+ BIO_free(bio);
+
+ /* Post PEM-decoding error messages, if any */
+ openssl_post_errors(M_ERROR, _("Unable to read private key from file"));
+ return retval;
+}
+
/*
* Load a PEM-encoded private key.
* Returns: true on success
return NULL;
}
+ /* Initialize required fields */
+ cs->session_key = NULL;
+
+ /* Allocate a CryptoData structure */
cs->cryptoData = CryptoData_new();
if (!cs->cryptoData) {
/* Generate a symmetric session key */
cs->session_key_len = EVP_CIPHER_key_length(ec);
+ cs->session_key = (unsigned char *) malloc(cs->session_key_len);
if (RAND_bytes(cs->session_key, cs->session_key_len) <= 0) {
/* OpenSSL failure */
crypto_session_free(cs);
if (RAND_bytes(iv, iv_len) <= 0) {
/* OpenSSL failure */
crypto_session_free(cs);
+ free(iv);
return NULL;
}
if (!M_ASN1_OCTET_STRING_set(cs->cryptoData->iv, iv, iv_len)) {
/* Allocation failed in OpenSSL */
crypto_session_free(cs);
+ free(iv);
return NULL;
}
+ free(iv);
}
/*
*
* Returns: CRYPTO_SESSION instance on success.
* NULL on failure.
+ * Returns: CRYPTO_ERROR_NONE and a pointer to a newly allocated CRYPTO_SESSION structure in *session on success.
+ * A crypto_error_t value on failure.
*/
-// TODO landonf: Unimplemented, requires a private key to decrypt session key
-CRYPTO_SESSION *crypto_session_decode(const void *data, size_t length)
+crypto_error_t crypto_session_decode(const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session)
{
CRYPTO_SESSION *cs;
+ X509_KEYPAIR *keypair;
+ STACK_OF(RecipientInfo) *recipients;
+ crypto_error_t retval = CRYPTO_ERROR_NONE;
#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
const unsigned char *p = (const unsigned char *) data;
#else
cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
if (!cs) {
- return NULL;
+ return CRYPTO_ERROR_INTERNAL;
}
/* d2i_CryptoData modifies the supplied pointer */
if (!cs->cryptoData) {
/* Allocation / Decoding failed in OpenSSL */
openssl_post_errors(M_ERROR, _("CryptoData decoding failed"));
- return NULL;
+ retval = CRYPTO_ERROR_INTERNAL;
+ goto err;
}
- return cs;
+ recipients = cs->cryptoData->recipientInfo;
+
+ /*
+ * Find a matching RecipientInfo structure for a supplied
+ * public key
+ */
+ foreach_alist(keypair, keypairs) {
+ RecipientInfo *ri;
+ int i;
+
+ /* Private key available? */
+ if (keypair->privkey == NULL) {
+ continue;
+ }
+
+ for (i = 0; i < sk_RecipientInfo_num(recipients); i++) {
+ ri = sk_RecipientInfo_value(recipients, i);
+
+ /* Match against the subjectKeyIdentifier */
+ if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, ri->subjectKeyIdentifier) == 0) {
+ /* Match found, extract symmetric encryption session data */
+
+ /* RSA is required. */
+ assert(EVP_PKEY_type(keypair->privkey->type) == EVP_PKEY_RSA);
+
+ /* If we recieve a RecipientInfo structure that does not use
+ * RSA, return an error */
+ if (OBJ_obj2nid(ri->keyEncryptionAlgorithm) != NID_rsaEncryption) {
+ retval = CRYPTO_ERROR_INVALID_CRYPTO;
+ goto err;
+ }
+
+ /* Decrypt the session key */
+ /* Allocate sufficient space for the largest possible decrypted data */
+ cs->session_key = (unsigned char *) malloc(EVP_PKEY_size(keypair->privkey));
+ cs->session_key_len = EVP_PKEY_decrypt(cs->session_key, M_ASN1_STRING_data(ri->encryptedKey),
+ M_ASN1_STRING_length(ri->encryptedKey), keypair->privkey);
+
+ if (cs->session_key_len <= 0) {
+ openssl_post_errors(M_ERROR, _("Failure decrypting the session key"));
+ retval = CRYPTO_ERROR_DECRYPTION;
+ goto err;
+ }
+
+ /* Session key successfully extracted, return the CRYPTO_SESSION structure */
+ *session = cs;
+ return CRYPTO_ERROR_NONE;
+ }
+ }
+ }
+
+ /* No matching recipient found */
+ return CRYPTO_ERROR_NORECIPIENT;
+
+err:
+ crypto_session_free(cs);
+ return retval;
}
/*
*/
void crypto_session_free (CRYPTO_SESSION *cs)
{
- CryptoData_free(cs->cryptoData);
+ if (cs->cryptoData) {
+ CryptoData_free(cs->cryptoData);
+ }
+ if (cs->session_key){
+ free(cs->session_key);
+ }
free(cs);
}
X509_KEYPAIR *crypto_keypair_new (void) { return NULL; }
X509_KEYPAIR *crypto_keypair_dup (X509_KEYPAIR *keypair) { return NULL; }
int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file) { return false; }
+bool crypto_keypair_has_key (const char *file) { return false; }
int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata) { return false; }
void crypto_keypair_free (X509_KEYPAIR *keypair) { }
CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys) { return NULL; }
void crypto_session_free (CRYPTO_SESSION *cs) { }
bool crypto_session_encode(CRYPTO_SESSION *cs, void *dest, size_t *length) { return false; }
-CRYPTO_SESSION *crypto_session_decode(const void *data, size_t length) { return NULL; }
+crypto_error_t crypto_session_decode(const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session) { return CRYPTO_ERROR_INTERNAL; }
#endif /* HAVE_CRYPTO */
return "No error";
case CRYPTO_ERROR_NOSIGNER:
return "Signer not found";
+ case CRYPTO_ERROR_NORECIPIENT:
+ return "Recipient not found";
case CRYPTO_ERROR_INVALID_DIGEST:
return "Unsupported digest algorithm";
+ case CRYPTO_ERROR_INVALID_CRYPTO:
+ return "Unsupported encryption algorithm";
case CRYPTO_ERROR_BAD_SIGNATURE:
return "Signature is invalid";
+ case CRYPTO_ERROR_DECRYPTION:
+ return "Decryption error";
case CRYPTO_ERROR_INTERNAL:
/* This shouldn't happen */
return "Internal error";
typedef enum {
CRYPTO_ERROR_NONE = 0, /* No error */
CRYPTO_ERROR_NOSIGNER = 1, /* Signer not found */
- CRYPTO_ERROR_INVALID_DIGEST = 2, /* Unsupported digest algorithm */
- CRYPTO_ERROR_BAD_SIGNATURE = 3, /* Signature is invalid */
- CRYPTO_ERROR_INTERNAL = 4 /* Internal Error */
+ CRYPTO_ERROR_NORECIPIENT = 2, /* Recipient not found */
+ CRYPTO_ERROR_INVALID_DIGEST = 3, /* Unsupported digest algorithm */
+ CRYPTO_ERROR_INVALID_CRYPTO = 4, /* Unsupported encryption algorithm */
+ CRYPTO_ERROR_BAD_SIGNATURE = 5, /* Signature is invalid */
+ CRYPTO_ERROR_DECRYPTION = 6, /* Decryption error */
+ CRYPTO_ERROR_INTERNAL = 7 /* Internal Error */
} crypto_error_t;
/* Message Digest Sizes */
CRYPTO_SESSION * crypto_session_new (crypto_cipher_t cipher, alist *pubkeys);
void crypto_session_free (CRYPTO_SESSION *cs);
bool crypto_session_encode (CRYPTO_SESSION *cs, void *dest, size_t *length);
+crypto_error_t crypto_session_decode (const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session);
CRYPTO_SESSION * crypto_session_decode (const void *data, size_t length);
X509_KEYPAIR * crypto_keypair_new (void);
X509_KEYPAIR * crypto_keypair_dup (X509_KEYPAIR *keypair);
int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file);
+bool crypto_keypair_has_key (const char *file);
int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata);
void crypto_keypair_free (X509_KEYPAIR *keypair);
int crypto_default_pem_callback (char *buf, int size, const void *userdata);