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 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 210
                             filename);
        goto cleanup;
    }
    VIR_FREE(tmp_path);
    ret = 0;

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 279 280 281 282 283 284 285
        goto cleanup;

    ret = 0;

cleanup:
    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 313 314 315 316 317 318 319
        goto cleanup;

    ret = 0;

cleanup:
    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 341 342 343 344 345 346 347
    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);

cleanup:
    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 419 420 421 422 423 424 425 426 427
        goto cleanup;

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

    ret = 0;

cleanup:
    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 463 464 465 466 467 468 469 470
        goto cleanup;

    ret = secret;
    secret = NULL;

cleanup:
    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 518 519 520 521 522 523 524 525 526 527
            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;

cleanup:
    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 540 541 542 543 544 545
    if (driverState == NULL)
        return VIR_DRV_OPEN_DECLINED;

    conn->secretPrivateData = driverState;
    return VIR_DRV_OPEN_SUCCESS;
}

static int
secretClose(virConnectPtr conn) {
    conn->secretPrivateData = NULL;
    return 0;
}

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

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

555 556 557
    secretDriverLock(driver);

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

    secretDriverUnlock(driver);
    return i;
}

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

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

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

580 581 582 583
    secretDriverLock(driver);

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

    secretDriverUnlock(driver);
    return i;

cleanup:
    secretDriverUnlock(driver);

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

    return -1;
}

609 610 611 612 613 614 615 616 617 618
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 已提交
619 620 621
    case VIR_SECRET_USAGE_TYPE_CEPH:
        return def->usage.ceph;

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

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

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

    virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);

646 647 648
    if (virConnectListAllSecretsEnsureACL(conn) < 0)
        return -1;

O
Osier Yang 已提交
649 650 651 652 653
    secretDriverLock(driver);

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

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

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

O
Osier Yang 已提交
662 663 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
        /* 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


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 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));

cleanup:
    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 770 771 772 773 774 775 776

cleanup:
    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 870
    goto cleanup;

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 883 884 885 886 887 888 889 890
        else
            secretFree(secret);
    }

cleanup:
    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 916 917 918 919 920 921 922

cleanup:
    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 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
    if (old_value != NULL) {
        memset(old_value, 0, old_value_size);
        VIR_FREE(old_value);
    }
    new_value = NULL;

    ret = 0;
    goto cleanup;

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

cleanup:
    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 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
        goto cleanup;
    memcpy(ret, secret->value, secret->value_size);
    *value_size = secret->value_size;

cleanup:
    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 1072 1073 1074 1075 1076 1077 1078
    }
    secretFree(secret);

    ret = 0;

cleanup:
    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;
}