qemu/crypto/secret.c
<<
>>
Prefs
   1/*
   2 * QEMU crypto secret support
   3 *
   4 * Copyright (c) 2015 Red Hat, Inc.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "crypto/secret.h"
  23#include "crypto/cipher.h"
  24#include "qapi/error.h"
  25#include "qom/object_interfaces.h"
  26#include "qemu/base64.h"
  27#include "trace.h"
  28
  29
  30static void
  31qcrypto_secret_load_data(QCryptoSecret *secret,
  32                         uint8_t **output,
  33                         size_t *outputlen,
  34                         Error **errp)
  35{
  36    char *data = NULL;
  37    size_t length = 0;
  38    GError *gerr = NULL;
  39
  40    *output = NULL;
  41    *outputlen = 0;
  42
  43    if (secret->file) {
  44        if (secret->data) {
  45            error_setg(errp,
  46                       "'file' and 'data' are mutually exclusive");
  47            return;
  48        }
  49        if (!g_file_get_contents(secret->file, &data, &length, &gerr)) {
  50            error_setg(errp,
  51                       "Unable to read %s: %s",
  52                       secret->file, gerr->message);
  53            g_error_free(gerr);
  54            return;
  55        }
  56        *output = (uint8_t *)data;
  57        *outputlen = length;
  58    } else if (secret->data) {
  59        *outputlen = strlen(secret->data);
  60        *output = (uint8_t *)g_strdup(secret->data);
  61    } else {
  62        error_setg(errp, "Either 'file' or 'data' must be provided");
  63    }
  64}
  65
  66
  67static void qcrypto_secret_decrypt(QCryptoSecret *secret,
  68                                   const uint8_t *input,
  69                                   size_t inputlen,
  70                                   uint8_t **output,
  71                                   size_t *outputlen,
  72                                   Error **errp)
  73{
  74    uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
  75    size_t keylen, ciphertextlen, ivlen;
  76    QCryptoCipher *aes = NULL;
  77    uint8_t *plaintext = NULL;
  78
  79    *output = NULL;
  80    *outputlen = 0;
  81
  82    if (qcrypto_secret_lookup(secret->keyid,
  83                              &key, &keylen,
  84                              errp) < 0) {
  85        goto cleanup;
  86    }
  87
  88    if (keylen != 32) {
  89        error_setg(errp, "Key should be 32 bytes in length");
  90        goto cleanup;
  91    }
  92
  93    if (!secret->iv) {
  94        error_setg(errp, "IV is required to decrypt secret");
  95        goto cleanup;
  96    }
  97
  98    iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
  99    if (!iv) {
 100        goto cleanup;
 101    }
 102    if (ivlen != 16) {
 103        error_setg(errp, "IV should be 16 bytes in length not %zu",
 104                   ivlen);
 105        goto cleanup;
 106    }
 107
 108    aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
 109                             QCRYPTO_CIPHER_MODE_CBC,
 110                             key, keylen,
 111                             errp);
 112    if (!aes) {
 113        goto cleanup;
 114    }
 115
 116    if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
 117        goto cleanup;
 118    }
 119
 120    if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
 121        ciphertext = qbase64_decode((const gchar*)input,
 122                                    inputlen,
 123                                    &ciphertextlen,
 124                                    errp);
 125        if (!ciphertext) {
 126            goto cleanup;
 127        }
 128        plaintext = g_new0(uint8_t, ciphertextlen + 1);
 129    } else {
 130        ciphertextlen = inputlen;
 131        plaintext = g_new0(uint8_t, inputlen + 1);
 132    }
 133    if (qcrypto_cipher_decrypt(aes,
 134                               ciphertext ? ciphertext : input,
 135                               plaintext,
 136                               ciphertextlen,
 137                               errp) < 0) {
 138        plaintext = NULL;
 139        goto cleanup;
 140    }
 141
 142    if (plaintext[ciphertextlen - 1] > 16 ||
 143        plaintext[ciphertextlen - 1] > ciphertextlen) {
 144        error_setg(errp, "Incorrect number of padding bytes (%d) "
 145                   "found on decrypted data",
 146                   (int)plaintext[ciphertextlen - 1]);
 147        g_free(plaintext);
 148        plaintext = NULL;
 149        goto cleanup;
 150    }
 151
 152    /* Even though plaintext may contain arbitrary NUL
 153     * ensure it is explicitly NUL terminated.
 154     */
 155    ciphertextlen -= plaintext[ciphertextlen - 1];
 156    plaintext[ciphertextlen] = '\0';
 157
 158    *output = plaintext;
 159    *outputlen = ciphertextlen;
 160
 161 cleanup:
 162    g_free(ciphertext);
 163    g_free(iv);
 164    g_free(key);
 165    qcrypto_cipher_free(aes);
 166}
 167
 168
 169static void qcrypto_secret_decode(const uint8_t *input,
 170                                  size_t inputlen,
 171                                  uint8_t **output,
 172                                  size_t *outputlen,
 173                                  Error **errp)
 174{
 175    *output = qbase64_decode((const gchar*)input,
 176                             inputlen,
 177                             outputlen,
 178                             errp);
 179}
 180
 181
 182static void
 183qcrypto_secret_prop_set_loaded(Object *obj,
 184                               bool value,
 185                               Error **errp)
 186{
 187    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 188
 189    if (value) {
 190        Error *local_err = NULL;
 191        uint8_t *input = NULL;
 192        size_t inputlen = 0;
 193        uint8_t *output = NULL;
 194        size_t outputlen = 0;
 195
 196        qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
 197        if (local_err) {
 198            error_propagate(errp, local_err);
 199            return;
 200        }
 201
 202        if (secret->keyid) {
 203            qcrypto_secret_decrypt(secret, input, inputlen,
 204                                   &output, &outputlen, &local_err);
 205            g_free(input);
 206            if (local_err) {
 207                error_propagate(errp, local_err);
 208                return;
 209            }
 210            input = output;
 211            inputlen = outputlen;
 212        } else {
 213            if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
 214                qcrypto_secret_decode(input, inputlen,
 215                                      &output, &outputlen, &local_err);
 216                g_free(input);
 217                if (local_err) {
 218                    error_propagate(errp, local_err);
 219                    return;
 220                }
 221                input = output;
 222                inputlen = outputlen;
 223            }
 224        }
 225
 226        secret->rawdata = input;
 227        secret->rawlen = inputlen;
 228    } else {
 229        g_free(secret->rawdata);
 230        secret->rawlen = 0;
 231    }
 232}
 233
 234
 235static bool
 236qcrypto_secret_prop_get_loaded(Object *obj,
 237                               Error **errp G_GNUC_UNUSED)
 238{
 239    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 240    return secret->data != NULL;
 241}
 242
 243
 244static void
 245qcrypto_secret_prop_set_format(Object *obj,
 246                               int value,
 247                               Error **errp G_GNUC_UNUSED)
 248{
 249    QCryptoSecret *creds = QCRYPTO_SECRET(obj);
 250
 251    creds->format = value;
 252}
 253
 254
 255static int
 256qcrypto_secret_prop_get_format(Object *obj,
 257                               Error **errp G_GNUC_UNUSED)
 258{
 259    QCryptoSecret *creds = QCRYPTO_SECRET(obj);
 260
 261    return creds->format;
 262}
 263
 264
 265static void
 266qcrypto_secret_prop_set_data(Object *obj,
 267                             const char *value,
 268                             Error **errp)
 269{
 270    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 271
 272    g_free(secret->data);
 273    secret->data = g_strdup(value);
 274}
 275
 276
 277static char *
 278qcrypto_secret_prop_get_data(Object *obj,
 279                             Error **errp)
 280{
 281    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 282    return g_strdup(secret->data);
 283}
 284
 285
 286static void
 287qcrypto_secret_prop_set_file(Object *obj,
 288                             const char *value,
 289                             Error **errp)
 290{
 291    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 292
 293    g_free(secret->file);
 294    secret->file = g_strdup(value);
 295}
 296
 297
 298static char *
 299qcrypto_secret_prop_get_file(Object *obj,
 300                             Error **errp)
 301{
 302    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 303    return g_strdup(secret->file);
 304}
 305
 306
 307static void
 308qcrypto_secret_prop_set_iv(Object *obj,
 309                           const char *value,
 310                           Error **errp)
 311{
 312    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 313
 314    g_free(secret->iv);
 315    secret->iv = g_strdup(value);
 316}
 317
 318
 319static char *
 320qcrypto_secret_prop_get_iv(Object *obj,
 321                           Error **errp)
 322{
 323    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 324    return g_strdup(secret->iv);
 325}
 326
 327
 328static void
 329qcrypto_secret_prop_set_keyid(Object *obj,
 330                              const char *value,
 331                              Error **errp)
 332{
 333    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 334
 335    g_free(secret->keyid);
 336    secret->keyid = g_strdup(value);
 337}
 338
 339
 340static char *
 341qcrypto_secret_prop_get_keyid(Object *obj,
 342                              Error **errp)
 343{
 344    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 345    return g_strdup(secret->keyid);
 346}
 347
 348
 349static void
 350qcrypto_secret_complete(UserCreatable *uc, Error **errp)
 351{
 352    object_property_set_bool(OBJECT(uc), true, "loaded", errp);
 353}
 354
 355
 356static void
 357qcrypto_secret_finalize(Object *obj)
 358{
 359    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 360
 361    g_free(secret->iv);
 362    g_free(secret->file);
 363    g_free(secret->keyid);
 364    g_free(secret->rawdata);
 365    g_free(secret->data);
 366}
 367
 368static void
 369qcrypto_secret_class_init(ObjectClass *oc, void *data)
 370{
 371    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
 372
 373    ucc->complete = qcrypto_secret_complete;
 374
 375    object_class_property_add_bool(oc, "loaded",
 376                                   qcrypto_secret_prop_get_loaded,
 377                                   qcrypto_secret_prop_set_loaded,
 378                                   NULL);
 379    object_class_property_add_enum(oc, "format",
 380                                   "QCryptoSecretFormat",
 381                                   QCryptoSecretFormat_lookup,
 382                                   qcrypto_secret_prop_get_format,
 383                                   qcrypto_secret_prop_set_format,
 384                                   NULL);
 385    object_class_property_add_str(oc, "data",
 386                                  qcrypto_secret_prop_get_data,
 387                                  qcrypto_secret_prop_set_data,
 388                                  NULL);
 389    object_class_property_add_str(oc, "file",
 390                                  qcrypto_secret_prop_get_file,
 391                                  qcrypto_secret_prop_set_file,
 392                                  NULL);
 393    object_class_property_add_str(oc, "keyid",
 394                                  qcrypto_secret_prop_get_keyid,
 395                                  qcrypto_secret_prop_set_keyid,
 396                                  NULL);
 397    object_class_property_add_str(oc, "iv",
 398                                  qcrypto_secret_prop_get_iv,
 399                                  qcrypto_secret_prop_set_iv,
 400                                  NULL);
 401}
 402
 403
 404int qcrypto_secret_lookup(const char *secretid,
 405                          uint8_t **data,
 406                          size_t *datalen,
 407                          Error **errp)
 408{
 409    Object *obj;
 410    QCryptoSecret *secret;
 411
 412    obj = object_resolve_path_component(
 413        object_get_objects_root(), secretid);
 414    if (!obj) {
 415        error_setg(errp, "No secret with id '%s'", secretid);
 416        return -1;
 417    }
 418
 419    secret = (QCryptoSecret *)
 420        object_dynamic_cast(obj,
 421                            TYPE_QCRYPTO_SECRET);
 422    if (!secret) {
 423        error_setg(errp, "Object with id '%s' is not a secret",
 424                   secretid);
 425        return -1;
 426    }
 427
 428    if (!secret->rawdata) {
 429        error_setg(errp, "Secret with id '%s' has no data",
 430                   secretid);
 431        return -1;
 432    }
 433
 434    *data = g_new0(uint8_t, secret->rawlen + 1);
 435    memcpy(*data, secret->rawdata, secret->rawlen);
 436    (*data)[secret->rawlen] = '\0';
 437    *datalen = secret->rawlen;
 438
 439    return 0;
 440}
 441
 442
 443char *qcrypto_secret_lookup_as_utf8(const char *secretid,
 444                                    Error **errp)
 445{
 446    uint8_t *data;
 447    size_t datalen;
 448
 449    if (qcrypto_secret_lookup(secretid,
 450                              &data,
 451                              &datalen,
 452                              errp) < 0) {
 453        return NULL;
 454    }
 455
 456    if (!g_utf8_validate((const gchar*)data, datalen, NULL)) {
 457        error_setg(errp,
 458                   "Data from secret %s is not valid UTF-8",
 459                   secretid);
 460        g_free(data);
 461        return NULL;
 462    }
 463
 464    return (char *)data;
 465}
 466
 467
 468char *qcrypto_secret_lookup_as_base64(const char *secretid,
 469                                      Error **errp)
 470{
 471    uint8_t *data;
 472    size_t datalen;
 473    char *ret;
 474
 475    if (qcrypto_secret_lookup(secretid,
 476                              &data,
 477                              &datalen,
 478                              errp) < 0) {
 479        return NULL;
 480    }
 481
 482    ret = g_base64_encode(data, datalen);
 483    g_free(data);
 484    return ret;
 485}
 486
 487
 488static const TypeInfo qcrypto_secret_info = {
 489    .parent = TYPE_OBJECT,
 490    .name = TYPE_QCRYPTO_SECRET,
 491    .instance_size = sizeof(QCryptoSecret),
 492    .instance_finalize = qcrypto_secret_finalize,
 493    .class_size = sizeof(QCryptoSecretClass),
 494    .class_init = qcrypto_secret_class_init,
 495    .interfaces = (InterfaceInfo[]) {
 496        { TYPE_USER_CREATABLE },
 497        { }
 498    }
 499};
 500
 501
 502static void
 503qcrypto_secret_register_types(void)
 504{
 505    type_register_static(&qcrypto_secret_info);
 506}
 507
 508
 509type_init(qcrypto_secret_register_types);
 510