secret_driver.c 30.8 KB
Newer Older
1 2 3
/*
 * secret_driver.c: local driver for secret manipulation API
 *
E
Eric Blake 已提交
4
 * Copyright (C) 2009-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * Red Hat Author: Miloslav Trmač <mitr@redhat.com>
 */

#include <config.h>

#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "internal.h"
#include "base64.h"
#include "datatypes.h"
#include "driver.h"
35
#include "virlog.h"
36
#include "viralloc.h"
37 38
#include "secret_conf.h"
#include "secret_driver.h"
39
#include "virthread.h"
40
#include "viruuid.h"
41
#include "virerror.h"
E
Eric Blake 已提交
42
#include "virfile.h"
43
#include "configmake.h"
44
#include "virstring.h"
45
#include "viraccessapicheck.h"
46 47 48

#define VIR_FROM_THIS VIR_FROM_SECRET

49 50
VIR_LOG_INIT("secret.secret_driver");

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
enum { SECRET_MAX_XML_FILE = 10*1024*1024 };

 /* Internal driver state */

typedef struct _virSecretEntry virSecretEntry;
typedef virSecretEntry *virSecretEntryPtr;
struct _virSecretEntry {
    virSecretEntryPtr next;
    virSecretDefPtr def;
    unsigned char *value;       /* May be NULL */
    size_t value_size;
};

typedef struct _virSecretDriverState virSecretDriverState;
typedef virSecretDriverState *virSecretDriverStatePtr;
struct _virSecretDriverState {
    virMutex lock;
    virSecretEntry *secrets;
    char *directory;
};

static virSecretDriverStatePtr driverState;

static void
secretDriverLock(virSecretDriverStatePtr driver)
{
    virMutexLock(&driver->lock);
}

static void
secretDriverUnlock(virSecretDriverStatePtr driver)
{
    virMutexUnlock(&driver->lock);
}

static virSecretEntryPtr
listUnlink(virSecretEntryPtr *pptr)
{
    virSecretEntryPtr secret;

    secret = *pptr;
    *pptr = secret->next;
    return secret;
}

static void
listInsert(virSecretEntryPtr *pptr, virSecretEntryPtr secret)
{
    secret->next = *pptr;
    *pptr = secret;
}

static void
secretFree(virSecretEntryPtr secret)
{
    if (secret == NULL)
        return;

    virSecretDefFree(secret->def);
    if (secret->value != NULL) {
        memset(secret->value, 0, secret->value_size);
        VIR_FREE(secret->value);
    }
    VIR_FREE(secret);
}

117 118
static virSecretEntryPtr
secretFindByUUID(virSecretDriverStatePtr driver, const unsigned char *uuid)
119 120 121 122 123
{
    virSecretEntryPtr *pptr, s;

    for (pptr = &driver->secrets; *pptr != NULL; pptr = &s->next) {
        s = *pptr;
124
        if (memcmp(s->def->uuid, uuid, VIR_UUID_BUFLEN) == 0)
125
            return s;
126 127 128 129 130
    }
    return NULL;
}

static virSecretEntryPtr
131
secretFindByUsage(virSecretDriverStatePtr driver, int usageType, const char *usageID)
132
{
133
    virSecretEntryPtr *pptr, s;
134

135 136
    for (pptr = &driver->secrets; *pptr != NULL; pptr = &s->next) {
        s = *pptr;
137

138 139
        if (s->def->usage_type != usageType)
            continue;
140

141 142 143 144
        switch (usageType) {
        case VIR_SECRET_USAGE_TYPE_NONE:
            /* never match this */
            break;
145

146 147 148 149
        case VIR_SECRET_USAGE_TYPE_VOLUME:
            if (STREQ(s->def->usage.volume, usageID))
                return s;
            break;
S
Sage Weil 已提交
150 151 152 153 154

        case VIR_SECRET_USAGE_TYPE_CEPH:
            if (STREQ(s->def->usage.ceph, usageID))
                return s;
            break;
155 156 157 158 159

        case VIR_SECRET_USAGE_TYPE_ISCSI:
            if (STREQ(s->def->usage.target, usageID))
                return s;
            break;
160
        }
161
    }
162
    return NULL;
163 164
}

165
/* Permament secret storage */
166 167 168 169 170 171 172

/* Secrets are stored in virSecretDriverStatePtr->directory.  Each secret
   has virSecretDef stored as XML in "$basename.xml".  If a value of the
   secret is defined, it is stored as base64 (with no formatting) in
   "$basename.base64".  "$basename" is in both cases the base64-encoded UUID. */

static int
173
replaceFile(const char *filename, void *data, size_t size)
174 175 176 177
{
    char *tmp_path = NULL;
    int fd = -1, ret = -1;

178
    if (virAsprintf(&tmp_path, "%sXXXXXX", filename) < 0)
179
        goto cleanup;
180
    fd = mkostemp(tmp_path, O_CLOEXEC);
181
    if (fd == -1) {
182
        virReportSystemError(errno, _("mkostemp('%s') failed"), tmp_path);
183 184 185
        goto cleanup;
    }
    if (fchmod(fd, S_IRUSR | S_IWUSR) != 0) {
186
        virReportSystemError(errno, _("fchmod('%s') failed"), tmp_path);
187 188 189 190 191
        goto cleanup;
    }

    ret = safewrite(fd, data, size);
    if (ret < 0) {
192
        virReportSystemError(errno, _("error writing to '%s'"),
193 194 195
                              tmp_path);
        goto cleanup;
    }
196
    if (VIR_CLOSE(fd) < 0) {
197
        virReportSystemError(errno, _("error closing '%s'"), tmp_path);
198 199 200 201 202
        goto cleanup;
    }
    fd = -1;

    if (rename(tmp_path, filename) < 0) {
203
        virReportSystemError(errno, _("rename(%s, %s) failed"), tmp_path,
204 205 206 207 208 209
                             filename);
        goto cleanup;
    }
    VIR_FREE(tmp_path);
    ret = 0;

210
 cleanup:
211
    VIR_FORCE_CLOSE(fd);
212 213 214 215 216 217 218 219
    if (tmp_path != NULL) {
        unlink(tmp_path);
        VIR_FREE(tmp_path);
    }
    return ret;
}

static char *
220
secretComputePath(virSecretDriverStatePtr driver,
221 222
                  const virSecretEntry *secret, const char *suffix)
{
223 224
    char *ret;
    char uuidstr[VIR_UUID_STRING_BUFLEN];
225

226
    virUUIDFormat(secret->def->uuid, uuidstr);
227

228
    ignore_value(virAsprintf(&ret, "%s/%s%s", driver->directory, uuidstr, suffix));
229 230 231 232
    return ret;
}

static char *
233
secretXMLPath(virSecretDriverStatePtr driver,
234 235
              const virSecretEntry *secret)
{
236
    return secretComputePath(driver, secret, ".xml");
237 238 239
}

static char *
240
secretBase64Path(virSecretDriverStatePtr driver,
241 242
                 const virSecretEntry *secret)
{
243
    return secretComputePath(driver, secret, ".base64");
244 245 246
}

static int
247
secretEnsureDirectory(virSecretDriverStatePtr driver)
248 249
{
    if (mkdir(driver->directory, S_IRWXU) < 0 && errno != EEXIST) {
250
        virReportSystemError(errno, _("cannot create '%s'"),
251 252 253 254 255 256 257
                             driver->directory);
        return -1;
    }
    return 0;
}

static int
258
secretSaveDef(virSecretDriverStatePtr driver,
259 260 261 262 263
              const virSecretEntry *secret)
{
    char *filename = NULL, *xml = NULL;
    int ret = -1;

264
    if (secretEnsureDirectory(driver) < 0)
265 266
        goto cleanup;

267
    filename = secretXMLPath(driver, secret);
268 269
    if (filename == NULL)
        goto cleanup;
270
    xml = virSecretDefFormat(secret->def);
271 272 273
    if (xml == NULL)
        goto cleanup;

274
    if (replaceFile(filename, xml, strlen(xml)) < 0)
275 276 277 278
        goto cleanup;

    ret = 0;

279
 cleanup:
280 281 282 283 284 285
    VIR_FREE(xml);
    VIR_FREE(filename);
    return ret;
}

static int
286
secretSaveValue(virSecretDriverStatePtr driver,
287 288 289 290 291 292 293 294
                const virSecretEntry *secret)
{
    char *filename = NULL, *base64 = NULL;
    int ret = -1;

    if (secret->value == NULL)
        return 0;

295
    if (secretEnsureDirectory(driver) < 0)
296 297
        goto cleanup;

298
    filename = secretBase64Path(driver, secret);
299 300 301 302 303
    if (filename == NULL)
        goto cleanup;
    base64_encode_alloc((const char *)secret->value, secret->value_size,
                        &base64);
    if (base64 == NULL) {
304
        virReportOOMError();
305 306 307
        goto cleanup;
    }

308
    if (replaceFile(filename, base64, strlen(base64)) < 0)
309 310 311 312
        goto cleanup;

    ret = 0;

313
 cleanup:
314 315 316 317 318 319
    VIR_FREE(base64);
    VIR_FREE(filename);
    return ret;
}

static int
320
secretDeleteSaved(virSecretDriverStatePtr driver,
321 322 323 324 325
                  const virSecretEntry *secret)
{
    char *xml_filename = NULL, *value_filename = NULL;
    int ret = -1;

326
    xml_filename = secretXMLPath(driver, secret);
327 328
    if (xml_filename == NULL)
        goto cleanup;
329
    value_filename = secretBase64Path(driver, secret);
330 331 332 333 334 335 336 337 338 339 340
    if (value_filename == NULL)
        goto cleanup;

    if (unlink(xml_filename) < 0 && errno != ENOENT)
        goto cleanup;
    /* When the XML is missing, the rest may waste disk space, but the secret
       won't be loaded again, so we have succeeded already. */
    ret = 0;

    (void)unlink(value_filename);

341
 cleanup:
342 343 344 345 346 347
    VIR_FREE(value_filename);
    VIR_FREE(xml_filename);
    return ret;
}

static int
348
secretLoadValidateUUID(virSecretDefPtr def,
349 350
                       const char *xml_basename)
{
351
    char uuidstr[VIR_UUID_STRING_BUFLEN];
352

353
    virUUIDFormat(def->uuid, uuidstr);
354

355
    if (!virFileMatchesNameSuffix(xml_basename, uuidstr, ".xml")) {
356 357 358
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("<uuid> does not match secret file name '%s'"),
                       xml_basename);
359 360 361 362 363 364 365
        return -1;
    }

    return 0;
}

static int
366
secretLoadValue(virSecretDriverStatePtr driver,
367 368 369 370 371 372 373
                virSecretEntryPtr secret)
{
    int ret = -1, fd = -1;
    struct stat st;
    char *filename = NULL, *contents = NULL, *value = NULL;
    size_t value_size;

374
    filename = secretBase64Path(driver, secret);
375 376 377 378 379 380 381 382 383
    if (filename == NULL)
        goto cleanup;

    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        if (errno == ENOENT) {
            ret = 0;
            goto cleanup;
        }
384
        virReportSystemError(errno, _("cannot open '%s'"), filename);
385 386 387
        goto cleanup;
    }
    if (fstat(fd, &st) < 0) {
388
        virReportSystemError(errno, _("cannot stat '%s'"), filename);
389 390 391
        goto cleanup;
    }
    if ((size_t)st.st_size != st.st_size) {
392 393
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("'%s' file does not fit in memory"), filename);
394 395 396
        goto cleanup;
    }

397
    if (VIR_ALLOC_N(contents, st.st_size) < 0)
398 399
        goto cleanup;
    if (saferead(fd, contents, st.st_size) != st.st_size) {
400
        virReportSystemError(errno, _("cannot read '%s'"), filename);
401 402
        goto cleanup;
    }
403
    VIR_FORCE_CLOSE(fd);
404 405

    if (!base64_decode_alloc(contents, st.st_size, &value, &value_size)) {
406 407
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid base64 in '%s'"), filename);
408 409
        goto cleanup;
    }
410
    if (value == NULL)
411 412 413 414 415 416 417 418
        goto cleanup;

    secret->value = (unsigned char *)value;
    value = NULL;
    secret->value_size = value_size;

    ret = 0;

419
 cleanup:
420 421 422 423 424 425 426 427
    if (value != NULL) {
        memset(value, 0, value_size);
        VIR_FREE(value);
    }
    if (contents != NULL) {
        memset(contents, 0, st.st_size);
        VIR_FREE(contents);
    }
428
    VIR_FORCE_CLOSE(fd);
429 430 431 432 433
    VIR_FREE(filename);
    return ret;
}

static virSecretEntryPtr
434
secretLoad(virSecretDriverStatePtr driver,
435 436
           const char *xml_basename)
{
437
    virSecretDefPtr def = NULL;
438 439 440 441
    virSecretEntryPtr secret = NULL, ret = NULL;
    char *xml_filename;

    if (virAsprintf(&xml_filename, "%s/%s", driver->directory,
442
                    xml_basename) < 0)
443
        goto cleanup;
444
    def = virSecretDefParseFile(xml_filename);
445 446 447 448
    if (def == NULL)
        goto cleanup;
    VIR_FREE(xml_filename);

449
    if (secretLoadValidateUUID(def, xml_basename) < 0)
450 451
        goto cleanup;

452
    if (VIR_ALLOC(secret) < 0)
453 454 455 456
        goto cleanup;
    secret->def = def;
    def = NULL;

457
    if (secretLoadValue(driver, secret) < 0)
458 459 460 461 462
        goto cleanup;

    ret = secret;
    secret = NULL;

463
 cleanup:
464 465 466 467 468 469 470
    secretFree(secret);
    virSecretDefFree(def);
    VIR_FREE(xml_filename);
    return ret;
}

static int
471
loadSecrets(virSecretDriverStatePtr driver,
472 473 474 475 476 477 478 479 480 481 482
            virSecretEntryPtr *dest)
{
    int ret = -1;
    DIR *dir = NULL;
    struct dirent *de;
    virSecretEntryPtr list = NULL;

    dir = opendir(driver->directory);
    if (dir == NULL) {
        if (errno == ENOENT)
            return 0;
483
        virReportSystemError(errno, _("cannot open '%s'"),
484 485 486
                             driver->directory);
        goto cleanup;
    }
E
Eric Blake 已提交
487
    while (virDirRead(dir, &de, NULL) > 0) {
488 489 490 491 492 493 494
        virSecretEntryPtr secret;

        if (STREQ(de->d_name, ".") || STREQ(de->d_name, ".."))
            continue;
        if (!virFileHasSuffix(de->d_name, ".xml"))
            continue;

495
        secret = secretLoad(driver, de->d_name);
496 497 498
        if (secret == NULL) {
            virErrorPtr err = virGetLastError();

499
            VIR_ERROR(_("Error reading secret: %s"),
500
                      err != NULL ? err->message: _("unknown error"));
501 502 503 504 505
            virResetError(err);
            continue;
        }
        listInsert(&list, secret);
    }
E
Eric Blake 已提交
506
    /* Ignore error reported by readdir, if any.  It's better to keep the
507 508 509 510 511 512 513 514 515 516 517
       secrets we managed to find. */

    while (list != NULL) {
        virSecretEntryPtr s;

        s = listUnlink(&list);
        listInsert(dest, s);
    }

    ret = 0;

518
 cleanup:
519 520 521 522 523 524 525 526 527
    if (dir != NULL)
        closedir(dir);
    return ret;
}

 /* Driver functions */

static virDrvOpenStatus
secretOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED,
528 529 530 531
           unsigned int flags)
{
    virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);

532 533 534 535 536 537 538 539
    if (driverState == NULL)
        return VIR_DRV_OPEN_DECLINED;

    conn->secretPrivateData = driverState;
    return VIR_DRV_OPEN_SUCCESS;
}

static int
540 541
secretClose(virConnectPtr conn)
{
542 543 544 545 546
    conn->secretPrivateData = NULL;
    return 0;
}

static int
547
secretConnectNumOfSecrets(virConnectPtr conn)
548 549
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
550
    size_t i;
551 552
    virSecretEntryPtr secret;

553 554 555
    if (virConnectNumOfSecretsEnsureACL(conn) < 0)
        return -1;

556 557 558
    secretDriverLock(driver);

    i = 0;
559 560 561 562 563
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
        if (virConnectNumOfSecretsCheckACL(conn,
                                           secret->def))
            i++;
    }
564 565 566 567 568 569

    secretDriverUnlock(driver);
    return i;
}

static int
570
secretConnectListSecrets(virConnectPtr conn, char **uuids, int maxuuids)
571 572
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
573
    size_t i;
574 575 576 577
    virSecretEntryPtr secret;

    memset(uuids, 0, maxuuids * sizeof(*uuids));

578 579 580
    if (virConnectListSecretsEnsureACL(conn) < 0)
        return -1;

581 582 583 584
    secretDriverLock(driver);

    i = 0;
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
585
        char *uuidstr;
586 587 588
        if (!virConnectListSecretsCheckACL(conn,
                                           secret->def))
            continue;
589 590
        if (i == maxuuids)
            break;
591
        if (VIR_ALLOC_N(uuidstr, VIR_UUID_STRING_BUFLEN) < 0)
592
            goto cleanup;
593 594
        virUUIDFormat(secret->def->uuid, uuidstr);
        uuids[i] = uuidstr;
595 596 597 598 599 600
        i++;
    }

    secretDriverUnlock(driver);
    return i;

601
 cleanup:
602 603 604 605 606 607 608 609
    secretDriverUnlock(driver);

    for (i = 0; i < maxuuids; i++)
        VIR_FREE(uuids[i]);

    return -1;
}

610 611 612 613 614 615 616 617 618 619
static const char *
secretUsageIDForDef(virSecretDefPtr def)
{
    switch (def->usage_type) {
    case VIR_SECRET_USAGE_TYPE_NONE:
        return "";

    case VIR_SECRET_USAGE_TYPE_VOLUME:
        return def->usage.volume;

S
Sage Weil 已提交
620 621 622
    case VIR_SECRET_USAGE_TYPE_CEPH:
        return def->usage.ceph;

623 624 625
    case VIR_SECRET_USAGE_TYPE_ISCSI:
        return def->usage.target;

626 627 628 629 630
    default:
        return NULL;
    }
}

O
Osier Yang 已提交
631 632
#define MATCH(FLAG) (flags & (FLAG))
static int
633 634
secretConnectListAllSecrets(virConnectPtr conn,
                            virSecretPtr **secrets,
635 636
                            unsigned int flags)
{
O
Osier Yang 已提交
637 638 639 640 641 642
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr *tmp_secrets = NULL;
    int nsecrets = 0;
    int ret_nsecrets = 0;
    virSecretPtr secret = NULL;
    virSecretEntryPtr entry = NULL;
643
    size_t i = 0;
O
Osier Yang 已提交
644 645 646 647
    int ret = -1;

    virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);

648 649 650
    if (virConnectListAllSecretsEnsureACL(conn) < 0)
        return -1;

O
Osier Yang 已提交
651 652 653 654 655
    secretDriverLock(driver);

    for (entry = driver->secrets; entry != NULL; entry = entry->next)
        nsecrets++;

656 657
    if (secrets && VIR_ALLOC_N(tmp_secrets, nsecrets + 1) < 0)
        goto cleanup;
O
Osier Yang 已提交
658 659

    for (entry = driver->secrets; entry != NULL; entry = entry->next) {
660 661 662 663
        if (!virConnectListAllSecretsCheckACL(conn,
                                              entry->def))
            continue;

O
Osier Yang 已提交
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
        /* filter by whether it's ephemeral */
        if (MATCH(VIR_CONNECT_LIST_SECRETS_FILTERS_EPHEMERAL) &&
            !((MATCH(VIR_CONNECT_LIST_SECRETS_EPHEMERAL) &&
               entry->def->ephemeral) ||
              (MATCH(VIR_CONNECT_LIST_SECRETS_NO_EPHEMERAL) &&
               !entry->def->ephemeral)))
            continue;

        /* filter by whether it's private */
        if (MATCH(VIR_CONNECT_LIST_SECRETS_FILTERS_PRIVATE) &&
            !((MATCH(VIR_CONNECT_LIST_SECRETS_PRIVATE) &&
               entry->def->private) ||
              (MATCH(VIR_CONNECT_LIST_SECRETS_NO_PRIVATE) &&
               !entry->def->private)))
            continue;

        if (secrets) {
            if (!(secret = virGetSecret(conn,
                                        entry->def->uuid,
                                        entry->def->usage_type,
                                        secretUsageIDForDef(entry->def))))
                goto cleanup;
            tmp_secrets[ret_nsecrets] = secret;
        }
        ret_nsecrets++;
    }

    if (tmp_secrets) {
        /* trim the array to the final size */
        ignore_value(VIR_REALLOC_N(tmp_secrets, ret_nsecrets + 1));
        *secrets = tmp_secrets;
        tmp_secrets = NULL;
    }

    ret = ret_nsecrets;

 cleanup:
    secretDriverUnlock(driver);
    if (tmp_secrets) {
703 704
        for (i = 0; i < ret_nsecrets; i ++)
            virObjectUnref(tmp_secrets[i]);
O
Osier Yang 已提交
705 706 707 708 709 710 711 712
    }
    VIR_FREE(tmp_secrets);

    return ret;
}
#undef MATCH


713
static virSecretPtr
714
secretLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
715 716 717
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr ret = NULL;
718
    virSecretEntryPtr secret;
719 720 721

    secretDriverLock(driver);

722 723
    secret = secretFindByUUID(driver, uuid);
    if (secret == NULL) {
724 725
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(uuid, uuidstr);
726 727
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
728 729 730
        goto cleanup;
    }

731 732 733
    if (virSecretLookupByUUIDEnsureACL(conn, secret->def) < 0)
        goto cleanup;

734 735 736 737 738
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));

739
 cleanup:
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    secretDriverUnlock(driver);
    return ret;
}


static virSecretPtr
secretLookupByUsage(virConnectPtr conn, int usageType, const char *usageID)
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;

    secretDriverLock(driver);

    secret = secretFindByUsage(driver, usageType, usageID);
    if (secret == NULL) {
756 757
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching usage '%s'"), usageID);
758 759 760
        goto cleanup;
    }

761 762 763
    if (virSecretLookupByUsageEnsureACL(conn, secret->def) < 0)
        goto cleanup;

764 765 766 767
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
768

769
 cleanup:
770 771 772 773 774 775 776
    secretDriverUnlock(driver);
    return ret;
}


static virSecretPtr
secretDefineXML(virConnectPtr conn, const char *xml,
777
                unsigned int flags)
778 779 780 781
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;
782 783
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
784

785 786
    virCheckFlags(0, NULL);

787
    new_attrs = virSecretDefParseString(xml);
788 789 790 791 792
    if (new_attrs == NULL)
        return NULL;

    secretDriverLock(driver);

793 794 795
    if (virSecretDefineXMLEnsureACL(conn, new_attrs) < 0)
        goto cleanup;

796 797 798 799 800 801 802 803
    secret = secretFindByUUID(driver, new_attrs->uuid);
    if (secret == NULL) {
        /* No existing secret with same UUID, try look for matching usage instead */
        const char *usageID = secretUsageIDForDef(new_attrs);
        secret = secretFindByUsage(driver, new_attrs->usage_type, usageID);
        if (secret) {
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(secret->def->uuid, uuidstr);
804 805 806
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("a secret with UUID %s already defined for use with %s"),
                           uuidstr, usageID);
807 808
            goto cleanup;
        }
809

810
        /* No existing secret at all, create one */
811
        if (VIR_ALLOC(secret) < 0)
812
            goto cleanup;
813

814 815 816 817 818 819 820 821
        listInsert(&driver->secrets, secret);
        secret->def = new_attrs;
    } else {
        const char *newUsageID = secretUsageIDForDef(new_attrs);
        const char *oldUsageID = secretUsageIDForDef(secret->def);
        if (STRNEQ(oldUsageID, newUsageID)) {
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(secret->def->uuid, uuidstr);
822 823 824
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("a secret with UUID %s is already defined for use with %s"),
                           uuidstr, oldUsageID);
825 826 827 828
            goto cleanup;
        }

        if (secret->def->private && !new_attrs->private) {
829 830
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cannot change private flag on existing secret"));
831 832 833 834 835 836
            goto cleanup;
        }

        /* Got an existing secret matches attrs, so reuse that */
        backup = secret->def;
        secret->def = new_attrs;
837 838 839
    }

    if (!new_attrs->ephemeral) {
840
        if (backup && backup->ephemeral) {
841
            if (secretSaveValue(driver, secret) < 0)
842 843
                goto restore_backup;
        }
844
        if (secretSaveDef(driver, secret) < 0) {
845
            if (backup && backup->ephemeral) {
846 847 848
                char *filename;

                /* Undo the secretSaveValue() above; ignore errors */
849
                filename = secretBase64Path(driver, secret);
850 851 852 853 854 855
                if (filename != NULL)
                    (void)unlink(filename);
                VIR_FREE(filename);
            }
            goto restore_backup;
        }
856
    } else if (backup && !backup->ephemeral) {
857
        if (secretDeleteSaved(driver, secret) < 0)
858 859
            goto restore_backup;
    }
860
    /* Saved successfully - drop old values */
861 862 863
    new_attrs = NULL;
    virSecretDefFree(backup);

864 865 866 867
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
868 869
    goto cleanup;

870
 restore_backup:
871 872 873 874
    if (backup) {
        /* Error - restore previous state and free new attributes */
        secret->def = backup;
    } else {
875 876
        /* "secret" was added to the head of the list above */
        if (listUnlink(&driverState->secrets) != secret)
877 878
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("list of secrets is inconsistent"));
879 880 881 882
        else
            secretFree(secret);
    }

883
 cleanup:
884 885 886 887 888 889 890
    virSecretDefFree(new_attrs);
    secretDriverUnlock(driver);

    return ret;
}

static char *
891
secretGetXMLDesc(virSecretPtr obj, unsigned int flags)
892 893 894
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    char *ret = NULL;
895
    virSecretEntryPtr secret;
896

897 898
    virCheckFlags(0, NULL);

899 900
    secretDriverLock(driver);

901 902
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
903 904
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
905 906
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
907 908 909
        goto cleanup;
    }

910 911 912
    if (virSecretGetXMLDescEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

913
    ret = virSecretDefFormat(secret->def);
914

915
 cleanup:
916 917 918 919 920 921 922
    secretDriverUnlock(driver);

    return ret;
}

static int
secretSetValue(virSecretPtr obj, const unsigned char *value,
923
               size_t value_size, unsigned int flags)
924 925 926 927 928
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    int ret = -1;
    unsigned char *old_value, *new_value;
    size_t old_value_size;
929
    virSecretEntryPtr secret;
930

931 932
    virCheckFlags(0, -1);

933
    if (VIR_ALLOC_N(new_value, value_size) < 0)
934 935 936 937
        return -1;

    secretDriverLock(driver);

938 939
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
940 941
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
942 943
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
944 945 946
        goto cleanup;
    }

947 948 949
    if (virSecretSetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

950 951 952 953 954 955 956
    old_value = secret->value;
    old_value_size = secret->value_size;

    memcpy(new_value, value, value_size);
    secret->value = new_value;
    secret->value_size = value_size;
    if (!secret->def->ephemeral) {
957
        if (secretSaveValue(driver, secret) < 0)
958 959
            goto restore_backup;
    }
960
    /* Saved successfully - drop old value */
961 962 963 964 965 966 967 968 969
    if (old_value != NULL) {
        memset(old_value, 0, old_value_size);
        VIR_FREE(old_value);
    }
    new_value = NULL;

    ret = 0;
    goto cleanup;

970
 restore_backup:
971 972 973 974 975
    /* Error - restore previous state and free new value */
    secret->value = old_value;
    secret->value_size = old_value_size;
    memset(new_value, 0, value_size);

976
 cleanup:
977 978 979 980 981 982 983 984
    secretDriverUnlock(driver);

    VIR_FREE(new_value);

    return ret;
}

static unsigned char *
985 986
secretGetValue(virSecretPtr obj, size_t *value_size, unsigned int flags,
               unsigned int internalFlags)
987 988 989
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    unsigned char *ret = NULL;
990
    virSecretEntryPtr secret;
991

992 993
    virCheckFlags(0, NULL);

994 995
    secretDriverLock(driver);

996 997
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
998 999
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
1000 1001
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
1002 1003
        goto cleanup;
    }
1004

1005 1006 1007
    if (virSecretGetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

1008
    if (secret->value == NULL) {
1009 1010
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
1011 1012
        virReportError(VIR_ERR_NO_SECRET,
                       _("secret '%s' does not have a value"), uuidstr);
1013 1014 1015
        goto cleanup;
    }

1016
    if ((internalFlags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
1017
        secret->def->private) {
1018
        virReportError(VIR_ERR_INVALID_SECRET, "%s",
1019
                       _("secret is private"));
1020 1021 1022
        goto cleanup;
    }

1023
    if (VIR_ALLOC_N(ret, secret->value_size) < 0)
1024 1025 1026 1027
        goto cleanup;
    memcpy(ret, secret->value, secret->value_size);
    *value_size = secret->value_size;

1028
 cleanup:
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
    secretDriverUnlock(driver);

    return ret;
}

static int
secretUndefine(virSecretPtr obj)
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    int ret = -1;
1039
    virSecretEntryPtr secret;
1040 1041 1042

    secretDriverLock(driver);

1043 1044
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
1045 1046
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
1047 1048
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
1049 1050 1051
        goto cleanup;
    }

1052 1053 1054
    if (virSecretUndefineEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

1055
    if (!secret->def->ephemeral &&
1056
        secretDeleteSaved(driver, secret) < 0)
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
        goto cleanup;

    if (driver->secrets == secret) {
        driver->secrets = secret->next;
    } else {
        virSecretEntryPtr tmp = driver->secrets;
        while (tmp && tmp->next != secret)
            tmp = tmp->next;
        if (tmp)
            tmp->next = secret->next;
1067 1068 1069 1070 1071
    }
    secretFree(secret);

    ret = 0;

1072
 cleanup:
1073 1074 1075 1076 1077 1078
    secretDriverUnlock(driver);

    return ret;
}

static int
1079
secretStateCleanup(void)
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
{
    if (driverState == NULL)
        return -1;

    secretDriverLock(driverState);

    while (driverState->secrets != NULL) {
        virSecretEntryPtr s;

        s = listUnlink(&driverState->secrets);
        secretFree(s);
    }
    VIR_FREE(driverState->directory);

    secretDriverUnlock(driverState);
    virMutexDestroy(&driverState->lock);
    VIR_FREE(driverState);

    return 0;
}

static int
1102 1103 1104
secretStateInitialize(bool privileged,
                      virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                      void *opaque ATTRIBUTE_UNUSED)
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
{
    char *base = NULL;

    if (VIR_ALLOC(driverState) < 0)
        return -1;

    if (virMutexInit(&driverState->lock) < 0) {
        VIR_FREE(driverState);
        return -1;
    }
    secretDriverLock(driverState);

    if (privileged) {
1118 1119
        if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0)
            goto error;
1120
    } else {
1121
        base = virGetUserConfigDirectory();
1122
        if (!base)
1123 1124
            goto error;
    }
1125 1126
    if (virAsprintf(&driverState->directory, "%s/secrets", base) < 0)
        goto error;
1127 1128
    VIR_FREE(base);

1129
    if (loadSecrets(driverState, &driverState->secrets) < 0)
1130 1131 1132 1133 1134 1135 1136 1137
        goto error;

    secretDriverUnlock(driverState);
    return 0;

 error:
    VIR_FREE(base);
    secretDriverUnlock(driverState);
1138
    secretStateCleanup();
1139 1140 1141 1142
    return -1;
}

static int
1143
secretStateReload(void)
1144 1145 1146 1147 1148 1149 1150 1151
{
    virSecretEntryPtr new_secrets = NULL;

    if (!driverState)
        return -1;

    secretDriverLock(driverState);

1152
    if (loadSecrets(driverState, &new_secrets) < 0)
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
        goto end;

    /* Keep ephemeral secrets from current state.  Discard non-ephemeral secrets
       that were removed by the secrets directory.  */
    while (driverState->secrets != NULL) {
        virSecretEntryPtr s;

        s = listUnlink(&driverState->secrets);
        if (s->def->ephemeral)
            listInsert(&new_secrets, s);
        else
            secretFree(s);
    }
    driverState->secrets = new_secrets;

 end:
    secretDriverUnlock(driverState);
    return 0;
}

static virSecretDriver secretDriver = {
    .name = "secret",
1175 1176
    .secretOpen = secretOpen, /* 0.7.1 */
    .secretClose = secretClose, /* 0.7.1 */
1177 1178 1179
    .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
    .connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
    .connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
1180 1181 1182 1183 1184 1185 1186
    .secretLookupByUUID = secretLookupByUUID, /* 0.7.1 */
    .secretLookupByUsage = secretLookupByUsage, /* 0.7.1 */
    .secretDefineXML = secretDefineXML, /* 0.7.1 */
    .secretGetXMLDesc = secretGetXMLDesc, /* 0.7.1 */
    .secretSetValue = secretSetValue, /* 0.7.1 */
    .secretGetValue = secretGetValue, /* 0.7.1 */
    .secretUndefine = secretUndefine, /* 0.7.1 */
1187 1188 1189
};

static virStateDriver stateDriver = {
1190
    .name = "Secret",
1191 1192 1193
    .stateInitialize = secretStateInitialize,
    .stateCleanup = secretStateCleanup,
    .stateReload = secretStateReload,
1194 1195 1196 1197 1198
};

int
secretRegister(void)
{
1199 1200 1201 1202
    if (virRegisterSecretDriver(&secretDriver) < 0)
        return -1;
    if (virRegisterStateDriver(&stateDriver) < 0)
        return -1;
1203 1204
    return 0;
}