Since we now have the functionality to provide the secrets driver
with an encryption key, and the newly introduced attribute to store
the cipher, we can use the key to save and load secrets.

Encrypt all secrets stored on disk by default.
When loading secrets, identify the decryption method by matching the file
extension against known encryptionSchemeType values, iterating from the most 
recent.
If no matching scheme is found, the secret is skipped. If the encryption
key is changed across restarts, then also the secret driver will fail to load
the secrets on the disk that were encrypted with the former key.

Signed-off-by: Arun Menon <[email protected]>
---
 src/conf/virsecretobj.c    | 159 +++++++++++++++++++++++++++++--------
 src/conf/virsecretobj.h    |  13 ++-
 src/secret/secret_driver.c |  27 +++++--
 3 files changed, 156 insertions(+), 43 deletions(-)

diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c
index a3dd7983bb..8b658a6f4c 100644
--- a/src/conf/virsecretobj.c
+++ b/src/conf/virsecretobj.c
@@ -31,6 +31,10 @@
 #include "virhash.h"
 #include "virlog.h"
 #include "virstring.h"
+#include "virsecret.h"
+#include "virrandom.h"
+#include "vircrypto.h"
+#include "virsecureerase.h"
 
 #define VIR_FROM_THIS VIR_FROM_SECRET
 
@@ -323,12 +327,16 @@ virSecretObj *
 virSecretObjListAdd(virSecretObjList *secrets,
                     virSecretDef **newdef,
                     const char *configDir,
-                    virSecretDef **oldDef)
+                    virSecretDef **oldDef,
+                    virSecretDaemonConfig *driverConfig)
 {
     virSecretObj *obj;
     virSecretDef *objdef;
     virSecretObj *ret = NULL;
+    g_autofree char *encryptionScheme = NULL;
+    g_autofree char *encryptionSchemeExt = NULL;
     char uuidstr[VIR_UUID_STRING_BUFLEN];
+    virSecretEncryptionSchemeType latestEncryptionScheme;
 
     virObjectRWLockWrite(secrets);
 
@@ -379,10 +387,24 @@ virSecretObjListAdd(virSecretObjList *secrets,
             goto cleanup;
 
         /* Generate the possible configFile and secretValueFile strings
-         * using the configDir, uuidstr, and appropriate suffix
+         * using the configDir, uuidstr, and appropriate suffix.
+         * By default, the latest encryption scheme will be used to encrypt 
secrets.
          */
-        if (!(obj->configFile = virFileBuildPath(configDir, uuidstr, ".xml")) 
||
-            !(obj->secretValueFile = virFileBuildPath(configDir, uuidstr, 
".base64")))
+
+        latestEncryptionScheme = VIR_SECRET_ENCRYPTION_SCHEME_LAST - 1;
+        encryptionScheme = 
g_strdup(virSecretEncryptionSchemeTypeToString(latestEncryptionScheme));
+        encryptionSchemeExt = g_strconcat(".", encryptionScheme, NULL);
+
+        if (driverConfig->encrypt_data) {
+            if (!(obj->secretValueFile = virFileBuildPath(configDir, uuidstr, 
encryptionSchemeExt))) {
+                goto cleanup;
+            }
+        } else {
+            if (!(obj->secretValueFile = virFileBuildPath(configDir, uuidstr, 
".base64"))) {
+                goto cleanup;
+            }
+        }
+        if (!(obj->configFile = virFileBuildPath(configDir, uuidstr, ".xml")))
             goto cleanup;
 
         if (virHashAddEntry(secrets->objs, uuidstr, obj) < 0)
@@ -407,6 +429,7 @@ struct virSecretCountData {
     int count;
 };
 
+
 static int
 virSecretObjListNumOfSecretsCallback(void *payload,
                                      const char *name G_GNUC_UNUSED,
@@ -530,6 +553,7 @@ struct _virSecretObjListExportData {
     bool error;
 };
 
+
 static int
 virSecretObjListExportCallback(void *payload,
                                const char *name G_GNUC_UNUSED,
@@ -682,15 +706,38 @@ virSecretObjSaveConfig(virSecretObj *obj)
 
 
 int
-virSecretObjSaveData(virSecretObj *obj)
+virSecretObjSaveData(virSecretObj *obj,
+                     virSecretDaemonConfig *driverConfig)
 {
     g_autofree char *base64 = NULL;
+    g_autofree uint8_t *secret = NULL;
+    g_autofree uint8_t *encryptedValue = NULL;
+    size_t encryptedValueLen = 0;
+    size_t secretLen = 0;
+    uint8_t iv[16] = { 0 };
 
     if (!obj->value)
         return 0;
 
-    base64 = g_base64_encode(obj->value, obj->value_size);
-
+    if (driverConfig->encrypt_data && driverConfig->secrets_encryption_key) {
+        if (virRandomBytes(iv, sizeof(iv)) < 0) {
+            return -1;
+        }
+        if (virCryptoEncryptData(VIR_CRYPTO_CIPHER_AES256CBC,
+                                 driverConfig->secrets_encryption_key, 
driverConfig->secretsKeyLen,
+                                 iv, sizeof(iv),
+                                 (uint8_t *)obj->value, obj->value_size,
+                                 &encryptedValue, &encryptedValueLen) < 0) {
+            return -1;
+        }
+        secretLen = sizeof(iv) + encryptedValueLen;
+        secret = g_new0(uint8_t, secretLen);
+        memcpy(secret, iv, sizeof(iv));
+        memcpy(secret + sizeof(iv), encryptedValue, encryptedValueLen);
+        base64 = g_base64_encode(secret, secretLen);
+    } else {
+        base64 = g_base64_encode(obj->value, obj->value_size);
+    }
     if (virFileRewriteStr(obj->secretValueFile, S_IRUSR | S_IWUSR, base64) < 0)
         return -1;
 
@@ -737,23 +784,22 @@ virSecretObjGetValue(virSecretObj *obj)
 int
 virSecretObjSetValue(virSecretObj *obj,
                      const unsigned char *value,
-                     size_t value_size)
+                     size_t value_size,
+                     virSecretDaemonConfig *driverConfig)
 {
     virSecretDef *def = obj->def;
     g_autofree unsigned char *old_value = NULL;
     g_autofree unsigned char *new_value = NULL;
     size_t old_value_size;
-
     new_value = g_new0(unsigned char, value_size);
 
     old_value = obj->value;
     old_value_size = obj->value_size;
-
     memcpy(new_value, value, value_size);
     obj->value = g_steal_pointer(&new_value);
     obj->value_size = value_size;
 
-    if (!def->isephemeral && virSecretObjSaveData(obj) < 0)
+    if (!def->isephemeral && virSecretObjSaveData(obj, driverConfig) < 0)
         goto error;
 
     /* Saved successfully - drop old value */
@@ -807,11 +853,23 @@ virSecretLoadValidateUUID(virSecretDef *def,
 
 
 static int
-virSecretLoadValue(virSecretObj *obj)
+virSecretLoadValue(virSecretObj *obj,
+                   virSecretDaemonConfig *driverConfig)
 {
-    int ret = -1, fd = -1;
+    int ret = -1;
+    VIR_AUTOCLOSE fd = -1;
     struct stat st;
+
     g_autofree char *contents = NULL;
+    g_autofree uint8_t *contents_encrypted = NULL;
+    g_autofree uint8_t *decryptedValue = NULL;
+    g_autofree char *encryptionScheme = NULL;
+
+    size_t decryptedValueLen = 0;
+    uint8_t iv[16] = { 0 };
+    uint8_t *ciphertext = NULL;
+    size_t ciphertextLen = 0;
+    virSecretEncryptionSchemeType latestEncryptionScheme;
 
     if ((fd = open(obj->secretValueFile, O_RDONLY)) == -1) {
         if (errno == ENOENT) {
@@ -841,25 +899,60 @@ virSecretLoadValue(virSecretObj *obj)
         goto cleanup;
     }
 
-    contents = g_new0(char, st.st_size + 1);
+    /* Iterate over the encryption schemes starting with the latest one and
+     * decrypt the contents of the file on the disk, by matching the file
+     * extention with the encryption scheme. If there is no scheme matching
+     * the file extention, then that secret is not loaded. */
 
-    if (saferead(fd, contents, st.st_size) != st.st_size) {
-        virReportSystemError(errno, _("cannot read '%1$s'"),
-                             obj->secretValueFile);
-        goto cleanup;
+    if (virStringHasSuffix(obj->secretValueFile, ".base64")) {
+        contents = g_new0(char, st.st_size + 1);
+        if (saferead(fd, contents, st.st_size) != st.st_size) {
+            virReportSystemError(errno, _("cannot read '%1$s'"),
+                                 obj->secretValueFile);
+            goto cleanup;
+        }
+        contents[st.st_size] = '\0';
+        obj->value = g_base64_decode(contents, &obj->value_size);
+    } else {
+        for (latestEncryptionScheme = VIR_SECRET_ENCRYPTION_SCHEME_LAST-1; 
latestEncryptionScheme > 0; latestEncryptionScheme--) {
+            encryptionScheme = 
g_strdup(virSecretEncryptionSchemeTypeToString(latestEncryptionScheme));
+            if (virStringHasSuffix(obj->secretValueFile, encryptionScheme)) {
+                contents = g_new0(char, st.st_size + 1);
+                if (saferead(fd, contents, st.st_size) != st.st_size) {
+                    virReportSystemError(errno, _("cannot read '%1$s'"),
+                                         obj->secretValueFile);
+                    goto cleanup;
+                }
+                if ((st.st_size) < sizeof(iv)) {
+                    virReportError(VIR_ERR_INVALID_SECRET, "%s",
+                                   _("Encrypted secret size is invalid"));
+                    goto cleanup;
+                }
+                contents[st.st_size] = '\0';
+                contents_encrypted = g_base64_decode(contents, 
&obj->value_size);
+
+                memcpy(iv, contents_encrypted, sizeof(iv));
+                ciphertext = contents_encrypted + sizeof(iv);
+                ciphertextLen = st.st_size - sizeof(iv);
+                if (virCryptoDecryptData(VIR_CRYPTO_CIPHER_AES256CBC,
+                                         driverConfig->secrets_encryption_key, 
driverConfig->secretsKeyLen,
+                                         iv, sizeof(iv),
+                                         ciphertext, ciphertextLen,
+                                         &decryptedValue, &decryptedValueLen) 
< 0) {
+                    virReportError(VIR_ERR_INVALID_SECRET, "%s",
+                                   _("Decryption of secret value failed"));
+                    goto cleanup;
+                }
+                g_free(obj->value);
+                obj->value = g_steal_pointer(&decryptedValue);
+                obj->value_size = decryptedValueLen;
+            }
+        }
     }
-    contents[st.st_size] = '\0';
-
-    VIR_FORCE_CLOSE(fd);
-
-    obj->value = g_base64_decode(contents, &obj->value_size);
-
     ret = 0;
 
  cleanup:
-    if (contents != NULL)
-        memset(contents, 0, st.st_size);
-    VIR_FORCE_CLOSE(fd);
+    virSecureErase(iv, sizeof(iv));
     return ret;
 }
 
@@ -868,7 +961,8 @@ static virSecretObj *
 virSecretLoad(virSecretObjList *secrets,
               const char *file,
               const char *path,
-              const char *configDir)
+              const char *configDir,
+              virSecretDaemonConfig *driverConfig)
 {
     g_autoptr(virSecretDef) def = NULL;
     virSecretObj *obj = NULL;
@@ -879,10 +973,10 @@ virSecretLoad(virSecretObjList *secrets,
     if (virSecretLoadValidateUUID(def, file) < 0)
         return NULL;
 
-    if (!(obj = virSecretObjListAdd(secrets, &def, configDir, NULL)))
+    if (!(obj = virSecretObjListAdd(secrets, &def, configDir, NULL, 
driverConfig)))
         return NULL;
 
-    if (virSecretLoadValue(obj) < 0) {
+    if (virSecretLoadValue(obj, driverConfig) < 0) {
         virSecretObjListRemove(secrets, obj);
         g_clear_pointer(&obj, virObjectUnref);
         return NULL;
@@ -894,7 +988,8 @@ virSecretLoad(virSecretObjList *secrets,
 
 int
 virSecretLoadAllConfigs(virSecretObjList *secrets,
-                        const char *configDir)
+                        const char *configDir,
+                        virSecretDaemonConfig *driverConfig)
 {
     g_autoptr(DIR) dir = NULL;
     struct dirent *de;
@@ -915,7 +1010,7 @@ virSecretLoadAllConfigs(virSecretObjList *secrets,
         if (!(path = virFileBuildPath(configDir, de->d_name, NULL)))
             continue;
 
-        if (!(obj = virSecretLoad(secrets, de->d_name, path, configDir))) {
+        if (!(obj = virSecretLoad(secrets, de->d_name, path, configDir, 
driverConfig))) {
             VIR_ERROR(_("Error reading secret: %1$s"),
                       virGetLastErrorMessage());
             continue;
diff --git a/src/conf/virsecretobj.h b/src/conf/virsecretobj.h
index 17897c5513..78a1fb1a39 100644
--- a/src/conf/virsecretobj.h
+++ b/src/conf/virsecretobj.h
@@ -23,6 +23,7 @@
 #include "internal.h"
 
 #include "secret_conf.h"
+#include "secret_config.h"
 
 typedef struct _virSecretObj virSecretObj;
 
@@ -51,7 +52,8 @@ virSecretObj *
 virSecretObjListAdd(virSecretObjList *secrets,
                     virSecretDef **newdef,
                     const char *configDir,
-                    virSecretDef **oldDef);
+                    virSecretDef **oldDef,
+                    virSecretDaemonConfig *driverConfig);
 
 typedef bool
 (*virSecretObjListACLFilter)(virConnectPtr conn,
@@ -86,7 +88,8 @@ int
 virSecretObjSaveConfig(virSecretObj *obj);
 
 int
-virSecretObjSaveData(virSecretObj *obj);
+virSecretObjSaveData(virSecretObj *obj,
+                     virSecretDaemonConfig *driverConfig);
 
 virSecretDef *
 virSecretObjGetDef(virSecretObj *obj);
@@ -101,7 +104,8 @@ virSecretObjGetValue(virSecretObj *obj);
 int
 virSecretObjSetValue(virSecretObj *obj,
                      const unsigned char *value,
-                     size_t value_size);
+                     size_t value_size,
+                     virSecretDaemonConfig *driverConfig);
 
 size_t
 virSecretObjGetValueSize(virSecretObj *obj);
@@ -112,4 +116,5 @@ virSecretObjSetValueSize(virSecretObj *obj,
 
 int
 virSecretLoadAllConfigs(virSecretObjList *secrets,
-                        const char *configDir);
+                        const char *configDir,
+                        virSecretDaemonConfig *cfg);
diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c
index 04c3ca49f1..ba781e241e 100644
--- a/src/secret/secret_driver.c
+++ b/src/secret/secret_driver.c
@@ -30,6 +30,7 @@
 #include "virlog.h"
 #include "viralloc.h"
 #include "secret_conf.h"
+#include "secret_config.h"
 #include "virsecretobj.h"
 #include "secret_driver.h"
 #include "virthread.h"
@@ -42,6 +43,10 @@
 #include "secret_event.h"
 #include "virutil.h"
 #include "virinhibitor.h"
+#include "virfile.h"
+#include "virrandom.h"
+#include "vircrypto.h"
+#include "virsecureerase.h"
 
 #define VIR_FROM_THIS VIR_FROM_SECRET
 
@@ -70,6 +75,9 @@ struct _virSecretDriverState {
 
     /* Immutable pointer, self-locking APIs */
     virInhibitor *inhibitor;
+
+    /* Settings from secrets.conf file */
+    virSecretDaemonConfig *config;
 };
 
 static virSecretDriverState *driver;
@@ -218,13 +226,14 @@ secretDefineXML(virConnectPtr conn,
         goto cleanup;
 
     if (!(obj = virSecretObjListAdd(driver->secrets, &def,
-                                    driver->configDir, &backup)))
+                                    driver->configDir, &backup,
+                                    driver->config)))
         goto cleanup;
     objDef = virSecretObjGetDef(obj);
 
     if (!objDef->isephemeral) {
         if (backup && backup->isephemeral) {
-            if (virSecretObjSaveData(obj) < 0)
+            if (virSecretObjSaveData(obj, driver->config) < 0)
                 goto restore_backup;
         }
 
@@ -307,7 +316,6 @@ secretGetXMLDesc(virSecretPtr secret,
     return ret;
 }
 
-
 static int
 secretSetValue(virSecretPtr secret,
                const unsigned char *value,
@@ -327,8 +335,7 @@ secretSetValue(virSecretPtr secret,
     def = virSecretObjGetDef(obj);
     if (virSecretSetValueEnsureACL(secret->conn, def) < 0)
         goto cleanup;
-
-    if (virSecretObjSetValue(obj, value, value_size) < 0)
+    if (virSecretObjSetValue(obj, value, value_size, driver->config) < 0)
         goto cleanup;
 
     event = virSecretEventValueChangedNew(def->uuid,
@@ -454,6 +461,7 @@ secretStateCleanupLocked(void)
     VIR_FREE(driver->configDir);
 
     virObjectUnref(driver->secretEventState);
+    virObjectUnref(driver->config);
     virInhibitorFree(driver->inhibitor);
 
     if (driver->lockFD != -1)
@@ -518,6 +526,8 @@ secretStateInitialize(bool privileged,
                              driver->stateDir);
         goto error;
     }
+    if (!(driver->config = virSecretDaemonConfigNew(driver->privileged)))
+        goto error;
 
     driver->inhibitor = virInhibitorNew(
         VIR_INHIBITOR_WHAT_NONE,
@@ -534,7 +544,7 @@ secretStateInitialize(bool privileged,
     if (!(driver->secrets = virSecretObjListNew()))
         goto error;
 
-    if (virSecretLoadAllConfigs(driver->secrets, driver->configDir) < 0)
+    if (virSecretLoadAllConfigs(driver->secrets, driver->configDir, 
driver->config) < 0)
         goto error;
 
     return VIR_DRV_STATE_INIT_COMPLETE;
@@ -553,7 +563,10 @@ secretStateReload(void)
     if (!driver)
         return -1;
 
-    ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDir));
+    if (!(driver->config = virSecretDaemonConfigNew(driver->privileged)))
+        return -1;
+
+    ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDir, 
driver->config));
 
     return 0;
 }
-- 
2.51.1

Reply via email to