secret_driver.c 28.6 KB
Newer Older
1 2 3
/*
 * secret_driver.c: local driver for secret manipulation API
 *
4
 * Copyright (C) 2009-2016 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
enum { SECRET_MAX_XML_FILE = 10*1024*1024 };

53
/* Internal driver state */
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

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

72
static virSecretDriverStatePtr driver;
73 74

static void
75
secretDriverLock(void)
76 77 78 79 80
{
    virMutexLock(&driver->lock);
}

static void
81
secretDriverUnlock(void)
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
{
    virMutexUnlock(&driver->lock);
}

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

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

static void
97 98
listInsert(virSecretEntryPtr *pptr,
           virSecretEntryPtr secret)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
{
    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);
}

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

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

static virSecretEntryPtr
132 133
secretFindByUsage(int usageType,
                  const char *usageID)
134
{
135
    virSecretEntryPtr *pptr, s;
136

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

140 141
        if (s->def->usage_type != usageType)
            continue;
142

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

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

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

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

167
/* Permament secret storage */
168 169 170 171 172 173 174

/* 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
175 176 177
replaceFile(const char *filename,
            void *data,
            size_t size)
178 179 180 181
{
    char *tmp_path = NULL;
    int fd = -1, ret = -1;

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

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

    if (rename(tmp_path, filename) < 0) {
207
        virReportSystemError(errno, _("rename(%s, %s) failed"), tmp_path,
208 209 210 211 212 213
                             filename);
        goto cleanup;
    }
    VIR_FREE(tmp_path);
    ret = 0;

214
 cleanup:
215
    VIR_FORCE_CLOSE(fd);
216 217 218 219 220 221 222 223
    if (tmp_path != NULL) {
        unlink(tmp_path);
        VIR_FREE(tmp_path);
    }
    return ret;
}

static char *
224 225
secretComputePath(const virSecretEntry *secret,
                  const char *suffix)
226
{
227 228
    char *ret;
    char uuidstr[VIR_UUID_STRING_BUFLEN];
229

230
    virUUIDFormat(secret->def->uuid, uuidstr);
231

232 233
    ignore_value(virAsprintf(&ret, "%s/%s%s", driver->directory,
                             uuidstr, suffix));
234 235 236 237
    return ret;
}

static char *
238
secretXMLPath(const virSecretEntry *secret)
239
{
240
    return secretComputePath(secret, ".xml");
241 242 243
}

static char *
244
secretBase64Path(const virSecretEntry *secret)
245
{
246
    return secretComputePath(secret, ".base64");
247 248 249
}

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

static int
261
secretSaveDef(const virSecretEntry *secret)
262 263 264 265
{
    char *filename = NULL, *xml = NULL;
    int ret = -1;

266
    if (secretEnsureDirectory() < 0)
267 268
        goto cleanup;

269
    if (!(filename = secretXMLPath(secret)))
270
        goto cleanup;
271 272

    if (!(xml = virSecretDefFormat(secret->def)))
273 274
        goto cleanup;

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

    ret = 0;

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

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

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

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

298
    if (!(filename = secretBase64Path(secret)))
299
        goto cleanup;
300

301 302 303
    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(const virSecretEntry *secret)
321 322 323 324
{
    char *xml_filename = NULL, *value_filename = NULL;
    int ret = -1;

325
    if (!(xml_filename = secretXMLPath(secret)))
326
        goto cleanup;
327 328

    if (!(value_filename = secretBase64Path(secret)))
329 330 331 332 333 334 335 336 337 338
        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);

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

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

351
    virUUIDFormat(def->uuid, uuidstr);
352

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

    return 0;
}

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

371
    if (!(filename = secretBase64Path(secret)))
372 373
        goto cleanup;

374
    if ((fd = open(filename, O_RDONLY)) == -1) {
375 376 377 378
        if (errno == ENOENT) {
            ret = 0;
            goto cleanup;
        }
379
        virReportSystemError(errno, _("cannot open '%s'"), filename);
380 381
        goto cleanup;
    }
382

383
    if (fstat(fd, &st) < 0) {
384
        virReportSystemError(errno, _("cannot stat '%s'"), filename);
385 386
        goto cleanup;
    }
387

388
    if ((size_t)st.st_size != st.st_size) {
389 390
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("'%s' file does not fit in memory"), filename);
391 392 393
        goto cleanup;
    }

394
    if (VIR_ALLOC_N(contents, st.st_size) < 0)
395
        goto cleanup;
396

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

402
    VIR_FORCE_CLOSE(fd);
403 404

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

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

    ret = 0;

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

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

    if (virAsprintf(&xml_filename, "%s/%s", driver->directory,
440
                    xml_basename) < 0)
441
        goto cleanup;
442 443

    if (!(def = virSecretDefParseFile(xml_filename)))
444
        goto cleanup;
445

446 447
    VIR_FREE(xml_filename);

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

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

456
    if (secretLoadValue(secret) < 0)
457 458 459 460 461
        goto cleanup;

    ret = secret;
    secret = NULL;

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

static int
470
loadSecrets(virSecretEntryPtr *dest)
471 472 473 474 475
{
    DIR *dir = NULL;
    struct dirent *de;
    virSecretEntryPtr list = NULL;

476
    if (!(dir = opendir(driver->directory))) {
477 478
        if (errno == ENOENT)
            return 0;
479
        virReportSystemError(errno, _("cannot open '%s'"),
480
                             driver->directory);
481
        return -1;
482
    }
483

E
Eric Blake 已提交
484
    while (virDirRead(dir, &de, NULL) > 0) {
485 486 487 488
        virSecretEntryPtr secret;

        if (STREQ(de->d_name, ".") || STREQ(de->d_name, ".."))
            continue;
489

490 491 492
        if (!virFileHasSuffix(de->d_name, ".xml"))
            continue;

493
        if (!(secret = secretLoad(de->d_name))) {
494 495
            virErrorPtr err = virGetLastError();

496
            VIR_ERROR(_("Error reading secret: %s"),
497
                      err != NULL ? err->message: _("unknown error"));
498 499 500
            virResetError(err);
            continue;
        }
501

502 503
        listInsert(&list, secret);
    }
E
Eric Blake 已提交
504
    /* Ignore error reported by readdir, if any.  It's better to keep the
505 506 507 508 509 510 511 512 513
       secrets we managed to find. */

    while (list != NULL) {
        virSecretEntryPtr s;

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

514 515
    closedir(dir);
    return 0;
516 517
}

518
/* Driver functions */
519 520

static int
521
secretConnectNumOfSecrets(virConnectPtr conn)
522
{
523
    size_t i;
524 525
    virSecretEntryPtr secret;

526 527 528
    if (virConnectNumOfSecretsEnsureACL(conn) < 0)
        return -1;

529
    secretDriverLock();
530 531

    i = 0;
532 533 534 535 536
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
        if (virConnectNumOfSecretsCheckACL(conn,
                                           secret->def))
            i++;
    }
537

538
    secretDriverUnlock();
539 540 541 542
    return i;
}

static int
543 544 545
secretConnectListSecrets(virConnectPtr conn,
                         char **uuids,
                         int maxuuids)
546
{
547
    size_t i;
548 549 550 551
    virSecretEntryPtr secret;

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

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

555
    secretDriverLock();
556 557 558

    i = 0;
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
559
        char *uuidstr;
560 561 562
        if (!virConnectListSecretsCheckACL(conn,
                                           secret->def))
            continue;
563 564
        if (i == maxuuids)
            break;
565
        if (VIR_ALLOC_N(uuidstr, VIR_UUID_STRING_BUFLEN) < 0)
566
            goto cleanup;
567 568
        virUUIDFormat(secret->def->uuid, uuidstr);
        uuids[i] = uuidstr;
569 570 571
        i++;
    }

572
    secretDriverUnlock();
573 574
    return i;

575
 cleanup:
576
    secretDriverUnlock();
577 578 579 580 581 582 583

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

    return -1;
}

584 585 586 587 588 589 590 591 592 593
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 已提交
594 595 596
    case VIR_SECRET_USAGE_TYPE_CEPH:
        return def->usage.ceph;

597 598 599
    case VIR_SECRET_USAGE_TYPE_ISCSI:
        return def->usage.target;

600 601 602 603 604
    default:
        return NULL;
    }
}

O
Osier Yang 已提交
605 606
#define MATCH(FLAG) (flags & (FLAG))
static int
607 608
secretConnectListAllSecrets(virConnectPtr conn,
                            virSecretPtr **secrets,
609 610
                            unsigned int flags)
{
O
Osier Yang 已提交
611 612 613 614 615
    virSecretPtr *tmp_secrets = NULL;
    int nsecrets = 0;
    int ret_nsecrets = 0;
    virSecretPtr secret = NULL;
    virSecretEntryPtr entry = NULL;
616
    size_t i = 0;
O
Osier Yang 已提交
617 618 619 620
    int ret = -1;

    virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);

621 622 623
    if (virConnectListAllSecretsEnsureACL(conn) < 0)
        return -1;

624
    secretDriverLock();
O
Osier Yang 已提交
625 626 627 628

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

629 630
    if (secrets && VIR_ALLOC_N(tmp_secrets, nsecrets + 1) < 0)
        goto cleanup;
O
Osier Yang 已提交
631 632

    for (entry = driver->secrets; entry != NULL; entry = entry->next) {
633 634 635 636
        if (!virConnectListAllSecretsCheckACL(conn,
                                              entry->def))
            continue;

O
Osier Yang 已提交
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
        /* 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:
674
    secretDriverUnlock();
O
Osier Yang 已提交
675
    if (tmp_secrets) {
676 677
        for (i = 0; i < ret_nsecrets; i ++)
            virObjectUnref(tmp_secrets[i]);
O
Osier Yang 已提交
678 679 680 681 682 683 684 685
    }
    VIR_FREE(tmp_secrets);

    return ret;
}
#undef MATCH


686
static virSecretPtr
687 688
secretLookupByUUID(virConnectPtr conn,
                   const unsigned char *uuid)
689 690
{
    virSecretPtr ret = NULL;
691
    virSecretEntryPtr secret;
692

693
    secretDriverLock();
694

695
    if (!(secret = secretFindByUUID(uuid))) {
696 697
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(uuid, uuidstr);
698 699
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
700 701 702
        goto cleanup;
    }

703 704 705
    if (virSecretLookupByUUIDEnsureACL(conn, secret->def) < 0)
        goto cleanup;

706 707 708 709 710
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));

711
 cleanup:
712
    secretDriverUnlock();
713 714 715 716 717
    return ret;
}


static virSecretPtr
718 719 720
secretLookupByUsage(virConnectPtr conn,
                    int usageType,
                    const char *usageID)
721 722 723 724
{
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;

725
    secretDriverLock();
726

727
    if (!(secret = secretFindByUsage(usageType, usageID))) {
728 729
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching usage '%s'"), usageID);
730 731 732
        goto cleanup;
    }

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

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

741
 cleanup:
742
    secretDriverUnlock();
743 744 745 746 747
    return ret;
}


static virSecretPtr
748 749
secretDefineXML(virConnectPtr conn,
                const char *xml,
750
                unsigned int flags)
751 752 753
{
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;
754 755
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
756

757 758
    virCheckFlags(0, NULL);

759
    if (!(new_attrs = virSecretDefParseString(xml)))
760 761
        return NULL;

762
    secretDriverLock();
763

764 765 766
    if (virSecretDefineXMLEnsureACL(conn, new_attrs) < 0)
        goto cleanup;

767 768 769
    if (!(secret = secretFindByUUID(new_attrs->uuid))) {
        /* No existing secret with same UUID,
         * try look for matching usage instead */
770
        const char *usageID = secretUsageIDForDef(new_attrs);
771
        if ((secret = secretFindByUsage(new_attrs->usage_type, usageID))) {
772 773
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(secret->def->uuid, uuidstr);
774
            virReportError(VIR_ERR_INTERNAL_ERROR,
775 776
                           _("a secret with UUID %s already defined for "
                             "use with %s"),
777
                           uuidstr, usageID);
778 779
            goto cleanup;
        }
780

781
        /* No existing secret at all, create one */
782
        if (VIR_ALLOC(secret) < 0)
783
            goto cleanup;
784

785 786 787 788 789 790 791 792
        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);
793
            virReportError(VIR_ERR_INTERNAL_ERROR,
794 795
                           _("a secret with UUID %s is already defined "
                             "for use with %s"),
796
                           uuidstr, oldUsageID);
797 798 799 800
            goto cleanup;
        }

        if (secret->def->private && !new_attrs->private) {
801 802
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cannot change private flag on existing secret"));
803 804 805 806 807 808
            goto cleanup;
        }

        /* Got an existing secret matches attrs, so reuse that */
        backup = secret->def;
        secret->def = new_attrs;
809 810 811
    }

    if (!new_attrs->ephemeral) {
812
        if (backup && backup->ephemeral) {
813
            if (secretSaveValue(secret) < 0)
814 815
                goto restore_backup;
        }
816
        if (secretSaveDef(secret) < 0) {
817
            if (backup && backup->ephemeral) {
818 819 820
                char *filename;

                /* Undo the secretSaveValue() above; ignore errors */
821
                filename = secretBase64Path(secret);
822 823 824 825 826 827
                if (filename != NULL)
                    (void)unlink(filename);
                VIR_FREE(filename);
            }
            goto restore_backup;
        }
828
    } else if (backup && !backup->ephemeral) {
829
        if (secretDeleteSaved(secret) < 0)
830 831
            goto restore_backup;
    }
832
    /* Saved successfully - drop old values */
833 834 835
    new_attrs = NULL;
    virSecretDefFree(backup);

836 837 838 839
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
840 841
    goto cleanup;

842
 restore_backup:
843 844 845 846
    if (backup) {
        /* Error - restore previous state and free new attributes */
        secret->def = backup;
    } else {
847
        /* "secret" was added to the head of the list above */
848
        if (listUnlink(&driver->secrets) != secret)
849 850
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("list of secrets is inconsistent"));
851 852 853 854
        else
            secretFree(secret);
    }

855
 cleanup:
856
    virSecretDefFree(new_attrs);
857
    secretDriverUnlock();
858 859 860 861 862

    return ret;
}

static char *
863 864
secretGetXMLDesc(virSecretPtr obj,
                 unsigned int flags)
865 866
{
    char *ret = NULL;
867
    virSecretEntryPtr secret;
868

869 870
    virCheckFlags(0, NULL);

871
    secretDriverLock();
872

873
    if (!(secret = secretFindByUUID(obj->uuid))) {
874 875
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
876 877
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
878 879 880
        goto cleanup;
    }

881 882 883
    if (virSecretGetXMLDescEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

884
    ret = virSecretDefFormat(secret->def);
885

886
 cleanup:
887
    secretDriverUnlock();
888 889 890 891 892

    return ret;
}

static int
893 894 895 896
secretSetValue(virSecretPtr obj,
               const unsigned char *value,
               size_t value_size,
               unsigned int flags)
897 898 899 900
{
    int ret = -1;
    unsigned char *old_value, *new_value;
    size_t old_value_size;
901
    virSecretEntryPtr secret;
902

903 904
    virCheckFlags(0, -1);

905
    if (VIR_ALLOC_N(new_value, value_size) < 0)
906 907
        return -1;

908
    secretDriverLock();
909

910
    if (!(secret = secretFindByUUID(obj->uuid))) {
911 912
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
913 914
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
915 916 917
        goto cleanup;
    }

918 919 920
    if (virSecretSetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

921 922 923 924 925 926 927
    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) {
928
        if (secretSaveValue(secret) < 0)
929 930
            goto restore_backup;
    }
931
    /* Saved successfully - drop old value */
932 933 934 935 936 937 938 939 940
    if (old_value != NULL) {
        memset(old_value, 0, old_value_size);
        VIR_FREE(old_value);
    }
    new_value = NULL;

    ret = 0;
    goto cleanup;

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

947
 cleanup:
948
    secretDriverUnlock();
949 950 951 952 953 954 955

    VIR_FREE(new_value);

    return ret;
}

static unsigned char *
956 957 958
secretGetValue(virSecretPtr obj,
               size_t *value_size,
               unsigned int flags,
959
               unsigned int internalFlags)
960 961
{
    unsigned char *ret = NULL;
962
    virSecretEntryPtr secret;
963

964 965
    virCheckFlags(0, NULL);

966
    secretDriverLock();
967

968
    if (!(secret = secretFindByUUID(obj->uuid))) {
969 970
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
971 972
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
973 974
        goto cleanup;
    }
975

976 977 978
    if (virSecretGetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

979
    if (secret->value == NULL) {
980 981
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
982 983
        virReportError(VIR_ERR_NO_SECRET,
                       _("secret '%s' does not have a value"), uuidstr);
984 985 986
        goto cleanup;
    }

987
    if ((internalFlags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
988
        secret->def->private) {
989
        virReportError(VIR_ERR_INVALID_SECRET, "%s",
990
                       _("secret is private"));
991 992 993
        goto cleanup;
    }

994
    if (VIR_ALLOC_N(ret, secret->value_size) < 0)
995 996 997 998
        goto cleanup;
    memcpy(ret, secret->value, secret->value_size);
    *value_size = secret->value_size;

999
 cleanup:
1000
    secretDriverUnlock();
1001 1002 1003 1004 1005 1006 1007 1008

    return ret;
}

static int
secretUndefine(virSecretPtr obj)
{
    int ret = -1;
1009
    virSecretEntryPtr secret;
1010

1011
    secretDriverLock();
1012

1013
    if (!(secret = secretFindByUUID(obj->uuid))) {
1014 1015
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
1016 1017
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
1018 1019 1020
        goto cleanup;
    }

1021 1022 1023
    if (virSecretUndefineEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

1024
    if (!secret->def->ephemeral &&
1025
        secretDeleteSaved(secret) < 0)
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
        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;
1036 1037 1038 1039 1040
    }
    secretFree(secret);

    ret = 0;

1041
 cleanup:
1042
    secretDriverUnlock();
1043 1044 1045 1046 1047

    return ret;
}

static int
1048
secretStateCleanup(void)
1049
{
1050
    if (driver == NULL)
1051 1052
        return -1;

1053
    secretDriverLock();
1054

1055
    while (driver->secrets != NULL) {
1056 1057
        virSecretEntryPtr s;

1058
        s = listUnlink(&driver->secrets);
1059 1060
        secretFree(s);
    }
1061
    VIR_FREE(driver->directory);
1062

1063 1064 1065
    secretDriverUnlock();
    virMutexDestroy(&driver->lock);
    VIR_FREE(driver);
1066 1067 1068 1069 1070

    return 0;
}

static int
1071 1072 1073
secretStateInitialize(bool privileged,
                      virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                      void *opaque ATTRIBUTE_UNUSED)
1074 1075 1076
{
    char *base = NULL;

1077
    if (VIR_ALLOC(driver) < 0)
1078 1079
        return -1;

1080 1081
    if (virMutexInit(&driver->lock) < 0) {
        VIR_FREE(driver);
1082 1083
        return -1;
    }
1084
    secretDriverLock();
1085 1086

    if (privileged) {
1087 1088
        if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0)
            goto error;
1089
    } else {
1090
        if (!(base = virGetUserConfigDirectory()))
1091 1092
            goto error;
    }
1093
    if (virAsprintf(&driver->directory, "%s/secrets", base) < 0)
1094
        goto error;
1095 1096
    VIR_FREE(base);

1097
    if (loadSecrets(&driver->secrets) < 0)
1098 1099
        goto error;

1100
    secretDriverUnlock();
1101 1102 1103 1104
    return 0;

 error:
    VIR_FREE(base);
1105
    secretDriverUnlock();
1106
    secretStateCleanup();
1107 1108 1109 1110
    return -1;
}

static int
1111
secretStateReload(void)
1112 1113 1114
{
    virSecretEntryPtr new_secrets = NULL;

1115
    if (!driver)
1116 1117
        return -1;

1118
    secretDriverLock();
1119

1120
    if (loadSecrets(&new_secrets) < 0)
1121 1122
        goto end;

1123 1124 1125
    /* Keep ephemeral secrets from current state.
     * Discard non-ephemeral secrets that were removed
     * by the secrets directory.  */
1126
    while (driver->secrets != NULL) {
1127 1128
        virSecretEntryPtr s;

1129
        s = listUnlink(&driver->secrets);
1130 1131 1132 1133 1134
        if (s->def->ephemeral)
            listInsert(&new_secrets, s);
        else
            secretFree(s);
    }
1135
    driver->secrets = new_secrets;
1136 1137

 end:
1138
    secretDriverUnlock();
1139 1140 1141 1142 1143
    return 0;
}

static virSecretDriver secretDriver = {
    .name = "secret",
1144 1145 1146
    .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
    .connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
    .connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
1147 1148 1149 1150 1151 1152 1153
    .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 */
1154 1155 1156
};

static virStateDriver stateDriver = {
1157
    .name = "secret",
1158 1159 1160
    .stateInitialize = secretStateInitialize,
    .stateCleanup = secretStateCleanup,
    .stateReload = secretStateReload,
1161 1162 1163 1164 1165
};

int
secretRegister(void)
{
1166
    if (virSetSharedSecretDriver(&secretDriver) < 0)
1167 1168 1169
        return -1;
    if (virRegisterStateDriver(&stateDriver) < 0)
        return -1;
1170 1171
    return 0;
}