secret_driver.c 30.8 KB
Newer Older
1 2 3
/*
 * secret_driver.c: local driver for secret manipulation API
 *
4
 * Copyright (C) 2009-2012, 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 487 488 489 490 491 492 493 494
                             driver->directory);
        goto cleanup;
    }
    while ((de = readdir(dir)) != NULL) {
        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 506 507 508 509 510 511 512 513 514 515 516 517
            virResetError(err);
            continue;
        }
        listInsert(&list, secret);
    }
    /* Ignore error reported by readdir(), if any.  It's better to keep the
       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 703 704 705 706 707 708 709 710 711 712 713 714
        /* 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) {
        for (i = 0; i < ret_nsecrets; i ++) {
            if (tmp_secrets[i])
                virSecretFree(tmp_secrets[i]);
        }
    }
    VIR_FREE(tmp_secrets);

    return ret;
}
#undef MATCH


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

    secretDriverLock(driver);

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

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

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

741
 cleanup:
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
    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) {
758 759
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching usage '%s'"), usageID);
760 761 762
        goto cleanup;
    }

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

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

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


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

787 788
    virCheckFlags(0, NULL);

789
    new_attrs = virSecretDefParseString(xml);
790 791 792 793 794
    if (new_attrs == NULL)
        return NULL;

    secretDriverLock(driver);

795 796 797
    if (virSecretDefineXMLEnsureACL(conn, new_attrs) < 0)
        goto cleanup;

798 799 800 801 802 803 804 805
    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);
806 807 808
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("a secret with UUID %s already defined for use with %s"),
                           uuidstr, usageID);
809 810
            goto cleanup;
        }
811

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

816 817 818 819 820 821 822 823
        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);
824 825 826
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("a secret with UUID %s is already defined for use with %s"),
                           uuidstr, oldUsageID);
827 828 829 830
            goto cleanup;
        }

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

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

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

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

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

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

885
 cleanup:
886 887 888 889 890 891 892
    virSecretDefFree(new_attrs);
    secretDriverUnlock(driver);

    return ret;
}

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

899 900
    virCheckFlags(0, NULL);

901 902
    secretDriverLock(driver);

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

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

915
    ret = virSecretDefFormat(secret->def);
916

917
 cleanup:
918 919 920 921 922 923 924
    secretDriverUnlock(driver);

    return ret;
}

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

933 934
    virCheckFlags(0, -1);

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

    secretDriverLock(driver);

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

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

952 953 954 955 956 957 958
    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) {
959
        if (secretSaveValue(driver, secret) < 0)
960 961
            goto restore_backup;
    }
962
    /* Saved successfully - drop old value */
963 964 965 966 967 968 969 970 971
    if (old_value != NULL) {
        memset(old_value, 0, old_value_size);
        VIR_FREE(old_value);
    }
    new_value = NULL;

    ret = 0;
    goto cleanup;

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

978
 cleanup:
979 980 981 982 983 984 985 986
    secretDriverUnlock(driver);

    VIR_FREE(new_value);

    return ret;
}

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

994 995
    virCheckFlags(0, NULL);

996 997
    secretDriverLock(driver);

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

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

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

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

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

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

    return ret;
}

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

    secretDriverLock(driver);

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

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

1057
    if (!secret->def->ephemeral &&
1058
        secretDeleteSaved(driver, secret) < 0)
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
        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;
1069 1070 1071 1072 1073
    }
    secretFree(secret);

    ret = 0;

1074
 cleanup:
1075 1076 1077 1078 1079 1080
    secretDriverUnlock(driver);

    return ret;
}

static int
1081
secretStateCleanup(void)
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
{
    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
1104 1105 1106
secretStateInitialize(bool privileged,
                      virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                      void *opaque ATTRIBUTE_UNUSED)
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
{
    char *base = NULL;

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

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

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

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

    secretDriverUnlock(driverState);
    return 0;

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

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

    if (!driverState)
        return -1;

    secretDriverLock(driverState);

1154
    if (loadSecrets(driverState, &new_secrets) < 0)
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
        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",
1177 1178
    .secretOpen = secretOpen, /* 0.7.1 */
    .secretClose = secretClose, /* 0.7.1 */
1179 1180 1181
    .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
    .connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
    .connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
1182 1183 1184 1185 1186 1187 1188
    .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 */
1189 1190 1191
};

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

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