secret_driver.c 27.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
secretRewriteFile(int fd,
                  void *opaque)
177
{
178
    char *data = opaque;
179

180 181
    if (safewrite(fd, data, strlen(data)) < 0)
        return -1;
182

183
    return 0;
184 185 186
}

static char *
187 188
secretComputePath(const virSecretEntry *secret,
                  const char *suffix)
189
{
190 191
    char *ret;
    char uuidstr[VIR_UUID_STRING_BUFLEN];
192

193
    virUUIDFormat(secret->def->uuid, uuidstr);
194

195 196
    ignore_value(virAsprintf(&ret, "%s/%s%s", driver->directory,
                             uuidstr, suffix));
197 198 199 200
    return ret;
}

static char *
201
secretXMLPath(const virSecretEntry *secret)
202
{
203
    return secretComputePath(secret, ".xml");
204 205 206
}

static char *
207
secretBase64Path(const virSecretEntry *secret)
208
{
209
    return secretComputePath(secret, ".base64");
210 211 212
}

static int
213
secretEnsureDirectory(void)
214 215
{
    if (mkdir(driver->directory, S_IRWXU) < 0 && errno != EEXIST) {
216
        virReportSystemError(errno, _("cannot create '%s'"),
217 218 219 220 221 222 223
                             driver->directory);
        return -1;
    }
    return 0;
}

static int
224
secretSaveDef(const virSecretEntry *secret)
225 226 227 228
{
    char *filename = NULL, *xml = NULL;
    int ret = -1;

229
    if (secretEnsureDirectory() < 0)
230 231
        goto cleanup;

232
    if (!(filename = secretXMLPath(secret)))
233
        goto cleanup;
234 235

    if (!(xml = virSecretDefFormat(secret->def)))
236 237
        goto cleanup;

238 239
    if (virFileRewrite(filename, S_IRUSR | S_IWUSR,
                       secretRewriteFile, xml) < 0)
240 241 242 243
        goto cleanup;

    ret = 0;

244
 cleanup:
245 246 247 248 249 250
    VIR_FREE(xml);
    VIR_FREE(filename);
    return ret;
}

static int
251
secretSaveValue(const virSecretEntry *secret)
252 253 254 255 256 257 258
{
    char *filename = NULL, *base64 = NULL;
    int ret = -1;

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

259
    if (secretEnsureDirectory() < 0)
260 261
        goto cleanup;

262
    if (!(filename = secretBase64Path(secret)))
263
        goto cleanup;
264

265 266 267
    base64_encode_alloc((const char *)secret->value, secret->value_size,
                        &base64);
    if (base64 == NULL) {
268
        virReportOOMError();
269 270 271
        goto cleanup;
    }

272 273
    if (virFileRewrite(filename, S_IRUSR | S_IWUSR,
                       secretRewriteFile, base64) < 0)
274 275 276 277
        goto cleanup;

    ret = 0;

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

static int
285
secretDeleteSaved(const virSecretEntry *secret)
286 287 288 289
{
    char *xml_filename = NULL, *value_filename = NULL;
    int ret = -1;

290
    if (!(xml_filename = secretXMLPath(secret)))
291
        goto cleanup;
292 293

    if (!(value_filename = secretBase64Path(secret)))
294 295 296 297 298 299 300 301 302 303
        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);

304
 cleanup:
305 306 307 308 309 310
    VIR_FREE(value_filename);
    VIR_FREE(xml_filename);
    return ret;
}

static int
311
secretLoadValidateUUID(virSecretDefPtr def,
312 313
                       const char *xml_basename)
{
314
    char uuidstr[VIR_UUID_STRING_BUFLEN];
315

316
    virUUIDFormat(def->uuid, uuidstr);
317

318
    if (!virFileMatchesNameSuffix(xml_basename, uuidstr, ".xml")) {
319 320 321
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("<uuid> does not match secret file name '%s'"),
                       xml_basename);
322 323 324 325 326 327 328
        return -1;
    }

    return 0;
}

static int
329
secretLoadValue(virSecretEntryPtr secret)
330 331 332 333 334 335
{
    int ret = -1, fd = -1;
    struct stat st;
    char *filename = NULL, *contents = NULL, *value = NULL;
    size_t value_size;

336
    if (!(filename = secretBase64Path(secret)))
337 338
        goto cleanup;

339
    if ((fd = open(filename, O_RDONLY)) == -1) {
340 341 342 343
        if (errno == ENOENT) {
            ret = 0;
            goto cleanup;
        }
344
        virReportSystemError(errno, _("cannot open '%s'"), filename);
345 346
        goto cleanup;
    }
347

348
    if (fstat(fd, &st) < 0) {
349
        virReportSystemError(errno, _("cannot stat '%s'"), filename);
350 351
        goto cleanup;
    }
352

353
    if ((size_t)st.st_size != st.st_size) {
354 355
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("'%s' file does not fit in memory"), filename);
356 357 358
        goto cleanup;
    }

359
    if (VIR_ALLOC_N(contents, st.st_size) < 0)
360
        goto cleanup;
361

362
    if (saferead(fd, contents, st.st_size) != st.st_size) {
363
        virReportSystemError(errno, _("cannot read '%s'"), filename);
364 365
        goto cleanup;
    }
366

367
    VIR_FORCE_CLOSE(fd);
368 369

    if (!base64_decode_alloc(contents, st.st_size, &value, &value_size)) {
370 371
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid base64 in '%s'"), filename);
372 373
        goto cleanup;
    }
374
    if (value == NULL)
375 376 377 378 379 380 381 382
        goto cleanup;

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

    ret = 0;

383
 cleanup:
384 385 386 387 388 389 390 391
    if (value != NULL) {
        memset(value, 0, value_size);
        VIR_FREE(value);
    }
    if (contents != NULL) {
        memset(contents, 0, st.st_size);
        VIR_FREE(contents);
    }
392
    VIR_FORCE_CLOSE(fd);
393 394 395 396 397
    VIR_FREE(filename);
    return ret;
}

static virSecretEntryPtr
398
secretLoad(const char *xml_basename)
399
{
400
    virSecretDefPtr def = NULL;
401 402 403 404
    virSecretEntryPtr secret = NULL, ret = NULL;
    char *xml_filename;

    if (virAsprintf(&xml_filename, "%s/%s", driver->directory,
405
                    xml_basename) < 0)
406
        goto cleanup;
407 408

    if (!(def = virSecretDefParseFile(xml_filename)))
409
        goto cleanup;
410

411 412
    VIR_FREE(xml_filename);

413
    if (secretLoadValidateUUID(def, xml_basename) < 0)
414 415
        goto cleanup;

416
    if (VIR_ALLOC(secret) < 0)
417 418 419 420
        goto cleanup;
    secret->def = def;
    def = NULL;

421
    if (secretLoadValue(secret) < 0)
422 423 424 425 426
        goto cleanup;

    ret = secret;
    secret = NULL;

427
 cleanup:
428 429 430 431 432 433 434
    secretFree(secret);
    virSecretDefFree(def);
    VIR_FREE(xml_filename);
    return ret;
}

static int
435
loadSecrets(virSecretEntryPtr *dest)
436 437 438 439 440
{
    DIR *dir = NULL;
    struct dirent *de;
    virSecretEntryPtr list = NULL;

441
    if (!(dir = opendir(driver->directory))) {
442 443
        if (errno == ENOENT)
            return 0;
444
        virReportSystemError(errno, _("cannot open '%s'"),
445
                             driver->directory);
446
        return -1;
447
    }
448

E
Eric Blake 已提交
449
    while (virDirRead(dir, &de, NULL) > 0) {
450 451 452 453
        virSecretEntryPtr secret;

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

455 456 457
        if (!virFileHasSuffix(de->d_name, ".xml"))
            continue;

458
        if (!(secret = secretLoad(de->d_name))) {
459 460
            virErrorPtr err = virGetLastError();

461
            VIR_ERROR(_("Error reading secret: %s"),
462
                      err != NULL ? err->message: _("unknown error"));
463 464 465
            virResetError(err);
            continue;
        }
466

467 468
        listInsert(&list, secret);
    }
E
Eric Blake 已提交
469
    /* Ignore error reported by readdir, if any.  It's better to keep the
470 471 472 473 474 475 476 477 478
       secrets we managed to find. */

    while (list != NULL) {
        virSecretEntryPtr s;

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

479 480
    closedir(dir);
    return 0;
481 482
}

483
/* Driver functions */
484 485

static int
486
secretConnectNumOfSecrets(virConnectPtr conn)
487
{
488
    size_t i;
489 490
    virSecretEntryPtr secret;

491 492 493
    if (virConnectNumOfSecretsEnsureACL(conn) < 0)
        return -1;

494
    secretDriverLock();
495 496

    i = 0;
497 498 499 500 501
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
        if (virConnectNumOfSecretsCheckACL(conn,
                                           secret->def))
            i++;
    }
502

503
    secretDriverUnlock();
504 505 506 507
    return i;
}

static int
508 509 510
secretConnectListSecrets(virConnectPtr conn,
                         char **uuids,
                         int maxuuids)
511
{
512
    size_t i;
513 514 515 516
    virSecretEntryPtr secret;

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

517 518 519
    if (virConnectListSecretsEnsureACL(conn) < 0)
        return -1;

520
    secretDriverLock();
521 522 523

    i = 0;
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
524
        char *uuidstr;
525 526 527
        if (!virConnectListSecretsCheckACL(conn,
                                           secret->def))
            continue;
528 529
        if (i == maxuuids)
            break;
530
        if (VIR_ALLOC_N(uuidstr, VIR_UUID_STRING_BUFLEN) < 0)
531
            goto cleanup;
532 533
        virUUIDFormat(secret->def->uuid, uuidstr);
        uuids[i] = uuidstr;
534 535 536
        i++;
    }

537
    secretDriverUnlock();
538 539
    return i;

540
 cleanup:
541
    secretDriverUnlock();
542 543 544 545 546 547 548

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

    return -1;
}

549 550 551 552 553 554 555 556 557 558
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 已提交
559 560 561
    case VIR_SECRET_USAGE_TYPE_CEPH:
        return def->usage.ceph;

562 563 564
    case VIR_SECRET_USAGE_TYPE_ISCSI:
        return def->usage.target;

565 566 567 568 569
    default:
        return NULL;
    }
}

O
Osier Yang 已提交
570 571
#define MATCH(FLAG) (flags & (FLAG))
static int
572 573
secretConnectListAllSecrets(virConnectPtr conn,
                            virSecretPtr **secrets,
574 575
                            unsigned int flags)
{
O
Osier Yang 已提交
576 577 578 579 580
    virSecretPtr *tmp_secrets = NULL;
    int nsecrets = 0;
    int ret_nsecrets = 0;
    virSecretPtr secret = NULL;
    virSecretEntryPtr entry = NULL;
581
    size_t i = 0;
O
Osier Yang 已提交
582 583 584 585
    int ret = -1;

    virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);

586 587 588
    if (virConnectListAllSecretsEnsureACL(conn) < 0)
        return -1;

589
    secretDriverLock();
O
Osier Yang 已提交
590 591 592 593

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

594 595
    if (secrets && VIR_ALLOC_N(tmp_secrets, nsecrets + 1) < 0)
        goto cleanup;
O
Osier Yang 已提交
596 597

    for (entry = driver->secrets; entry != NULL; entry = entry->next) {
598 599 600 601
        if (!virConnectListAllSecretsCheckACL(conn,
                                              entry->def))
            continue;

O
Osier Yang 已提交
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
        /* 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:
639
    secretDriverUnlock();
O
Osier Yang 已提交
640
    if (tmp_secrets) {
641 642
        for (i = 0; i < ret_nsecrets; i ++)
            virObjectUnref(tmp_secrets[i]);
O
Osier Yang 已提交
643 644 645 646 647 648 649 650
    }
    VIR_FREE(tmp_secrets);

    return ret;
}
#undef MATCH


651
static virSecretPtr
652 653
secretLookupByUUID(virConnectPtr conn,
                   const unsigned char *uuid)
654 655
{
    virSecretPtr ret = NULL;
656
    virSecretEntryPtr secret;
657

658
    secretDriverLock();
659

660
    if (!(secret = secretFindByUUID(uuid))) {
661 662
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(uuid, uuidstr);
663 664
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
665 666 667
        goto cleanup;
    }

668 669 670
    if (virSecretLookupByUUIDEnsureACL(conn, secret->def) < 0)
        goto cleanup;

671 672 673 674 675
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));

676
 cleanup:
677
    secretDriverUnlock();
678 679 680 681 682
    return ret;
}


static virSecretPtr
683 684 685
secretLookupByUsage(virConnectPtr conn,
                    int usageType,
                    const char *usageID)
686 687 688 689
{
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;

690
    secretDriverLock();
691

692
    if (!(secret = secretFindByUsage(usageType, usageID))) {
693 694
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching usage '%s'"), usageID);
695 696 697
        goto cleanup;
    }

698 699 700
    if (virSecretLookupByUsageEnsureACL(conn, secret->def) < 0)
        goto cleanup;

701 702 703 704
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
705

706
 cleanup:
707
    secretDriverUnlock();
708 709 710 711 712
    return ret;
}


static virSecretPtr
713 714
secretDefineXML(virConnectPtr conn,
                const char *xml,
715
                unsigned int flags)
716 717 718
{
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;
719 720
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
721

722 723
    virCheckFlags(0, NULL);

724
    if (!(new_attrs = virSecretDefParseString(xml)))
725 726
        return NULL;

727
    secretDriverLock();
728

729 730 731
    if (virSecretDefineXMLEnsureACL(conn, new_attrs) < 0)
        goto cleanup;

732 733 734
    if (!(secret = secretFindByUUID(new_attrs->uuid))) {
        /* No existing secret with same UUID,
         * try look for matching usage instead */
735
        const char *usageID = secretUsageIDForDef(new_attrs);
736
        if ((secret = secretFindByUsage(new_attrs->usage_type, usageID))) {
737 738
            char uuidstr[VIR_UUID_STRING_BUFLEN];
            virUUIDFormat(secret->def->uuid, uuidstr);
739
            virReportError(VIR_ERR_INTERNAL_ERROR,
740 741
                           _("a secret with UUID %s already defined for "
                             "use with %s"),
742
                           uuidstr, usageID);
743 744
            goto cleanup;
        }
745

746
        /* No existing secret at all, create one */
747
        if (VIR_ALLOC(secret) < 0)
748
            goto cleanup;
749

750 751 752 753 754 755 756 757
        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);
758
            virReportError(VIR_ERR_INTERNAL_ERROR,
759 760
                           _("a secret with UUID %s is already defined "
                             "for use with %s"),
761
                           uuidstr, oldUsageID);
762 763 764 765
            goto cleanup;
        }

        if (secret->def->private && !new_attrs->private) {
766 767
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cannot change private flag on existing secret"));
768 769 770 771 772 773
            goto cleanup;
        }

        /* Got an existing secret matches attrs, so reuse that */
        backup = secret->def;
        secret->def = new_attrs;
774 775 776
    }

    if (!new_attrs->ephemeral) {
777
        if (backup && backup->ephemeral) {
778
            if (secretSaveValue(secret) < 0)
779 780
                goto restore_backup;
        }
781
        if (secretSaveDef(secret) < 0) {
782
            if (backup && backup->ephemeral) {
783 784 785
                char *filename;

                /* Undo the secretSaveValue() above; ignore errors */
786
                filename = secretBase64Path(secret);
787 788 789 790 791 792
                if (filename != NULL)
                    (void)unlink(filename);
                VIR_FREE(filename);
            }
            goto restore_backup;
        }
793
    } else if (backup && !backup->ephemeral) {
794
        if (secretDeleteSaved(secret) < 0)
795 796
            goto restore_backup;
    }
797
    /* Saved successfully - drop old values */
798 799 800
    new_attrs = NULL;
    virSecretDefFree(backup);

801 802 803 804
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
805 806
    goto cleanup;

807
 restore_backup:
808 809 810 811
    if (backup) {
        /* Error - restore previous state and free new attributes */
        secret->def = backup;
    } else {
812
        /* "secret" was added to the head of the list above */
813
        if (listUnlink(&driver->secrets) != secret)
814 815
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("list of secrets is inconsistent"));
816 817 818 819
        else
            secretFree(secret);
    }

820
 cleanup:
821
    virSecretDefFree(new_attrs);
822
    secretDriverUnlock();
823 824 825 826 827

    return ret;
}

static char *
828 829
secretGetXMLDesc(virSecretPtr obj,
                 unsigned int flags)
830 831
{
    char *ret = NULL;
832
    virSecretEntryPtr secret;
833

834 835
    virCheckFlags(0, NULL);

836
    secretDriverLock();
837

838
    if (!(secret = secretFindByUUID(obj->uuid))) {
839 840
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
841 842
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
843 844 845
        goto cleanup;
    }

846 847 848
    if (virSecretGetXMLDescEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

849
    ret = virSecretDefFormat(secret->def);
850

851
 cleanup:
852
    secretDriverUnlock();
853 854 855 856 857

    return ret;
}

static int
858 859 860 861
secretSetValue(virSecretPtr obj,
               const unsigned char *value,
               size_t value_size,
               unsigned int flags)
862 863 864 865
{
    int ret = -1;
    unsigned char *old_value, *new_value;
    size_t old_value_size;
866
    virSecretEntryPtr secret;
867

868 869
    virCheckFlags(0, -1);

870
    if (VIR_ALLOC_N(new_value, value_size) < 0)
871 872
        return -1;

873
    secretDriverLock();
874

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

883 884 885
    if (virSecretSetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

886 887 888 889 890 891 892
    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) {
893
        if (secretSaveValue(secret) < 0)
894 895
            goto restore_backup;
    }
896
    /* Saved successfully - drop old value */
897 898 899 900 901 902 903 904 905
    if (old_value != NULL) {
        memset(old_value, 0, old_value_size);
        VIR_FREE(old_value);
    }
    new_value = NULL;

    ret = 0;
    goto cleanup;

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

912
 cleanup:
913
    secretDriverUnlock();
914 915 916 917 918 919 920

    VIR_FREE(new_value);

    return ret;
}

static unsigned char *
921 922 923
secretGetValue(virSecretPtr obj,
               size_t *value_size,
               unsigned int flags,
924
               unsigned int internalFlags)
925 926
{
    unsigned char *ret = NULL;
927
    virSecretEntryPtr secret;
928

929 930
    virCheckFlags(0, NULL);

931
    secretDriverLock();
932

933
    if (!(secret = secretFindByUUID(obj->uuid))) {
934 935
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
936 937
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
938 939
        goto cleanup;
    }
940

941 942 943
    if (virSecretGetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

944
    if (secret->value == NULL) {
945 946
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
947 948
        virReportError(VIR_ERR_NO_SECRET,
                       _("secret '%s' does not have a value"), uuidstr);
949 950 951
        goto cleanup;
    }

952
    if ((internalFlags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
953
        secret->def->private) {
954
        virReportError(VIR_ERR_INVALID_SECRET, "%s",
955
                       _("secret is private"));
956 957 958
        goto cleanup;
    }

959
    if (VIR_ALLOC_N(ret, secret->value_size) < 0)
960 961 962 963
        goto cleanup;
    memcpy(ret, secret->value, secret->value_size);
    *value_size = secret->value_size;

964
 cleanup:
965
    secretDriverUnlock();
966 967 968 969 970 971 972 973

    return ret;
}

static int
secretUndefine(virSecretPtr obj)
{
    int ret = -1;
974
    virSecretEntryPtr secret;
975

976
    secretDriverLock();
977

978
    if (!(secret = secretFindByUUID(obj->uuid))) {
979 980
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
981 982
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
983 984 985
        goto cleanup;
    }

986 987 988
    if (virSecretUndefineEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

989
    if (!secret->def->ephemeral &&
990
        secretDeleteSaved(secret) < 0)
991 992 993 994 995 996 997 998 999 1000
        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;
1001 1002 1003 1004 1005
    }
    secretFree(secret);

    ret = 0;

1006
 cleanup:
1007
    secretDriverUnlock();
1008 1009 1010 1011 1012

    return ret;
}

static int
1013
secretStateCleanup(void)
1014
{
1015
    if (driver == NULL)
1016 1017
        return -1;

1018
    secretDriverLock();
1019

1020
    while (driver->secrets != NULL) {
1021 1022
        virSecretEntryPtr s;

1023
        s = listUnlink(&driver->secrets);
1024 1025
        secretFree(s);
    }
1026
    VIR_FREE(driver->directory);
1027

1028 1029 1030
    secretDriverUnlock();
    virMutexDestroy(&driver->lock);
    VIR_FREE(driver);
1031 1032 1033 1034 1035

    return 0;
}

static int
1036 1037 1038
secretStateInitialize(bool privileged,
                      virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                      void *opaque ATTRIBUTE_UNUSED)
1039 1040 1041
{
    char *base = NULL;

1042
    if (VIR_ALLOC(driver) < 0)
1043 1044
        return -1;

1045 1046
    if (virMutexInit(&driver->lock) < 0) {
        VIR_FREE(driver);
1047 1048
        return -1;
    }
1049
    secretDriverLock();
1050 1051

    if (privileged) {
1052 1053
        if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0)
            goto error;
1054
    } else {
1055
        if (!(base = virGetUserConfigDirectory()))
1056 1057
            goto error;
    }
1058
    if (virAsprintf(&driver->directory, "%s/secrets", base) < 0)
1059
        goto error;
1060 1061
    VIR_FREE(base);

1062
    if (loadSecrets(&driver->secrets) < 0)
1063 1064
        goto error;

1065
    secretDriverUnlock();
1066 1067 1068 1069
    return 0;

 error:
    VIR_FREE(base);
1070
    secretDriverUnlock();
1071
    secretStateCleanup();
1072 1073 1074 1075
    return -1;
}

static int
1076
secretStateReload(void)
1077 1078 1079
{
    virSecretEntryPtr new_secrets = NULL;

1080
    if (!driver)
1081 1082
        return -1;

1083
    secretDriverLock();
1084

1085
    if (loadSecrets(&new_secrets) < 0)
1086 1087
        goto end;

1088 1089 1090
    /* Keep ephemeral secrets from current state.
     * Discard non-ephemeral secrets that were removed
     * by the secrets directory.  */
1091
    while (driver->secrets != NULL) {
1092 1093
        virSecretEntryPtr s;

1094
        s = listUnlink(&driver->secrets);
1095 1096 1097 1098 1099
        if (s->def->ephemeral)
            listInsert(&new_secrets, s);
        else
            secretFree(s);
    }
1100
    driver->secrets = new_secrets;
1101 1102

 end:
1103
    secretDriverUnlock();
1104 1105 1106 1107 1108
    return 0;
}

static virSecretDriver secretDriver = {
    .name = "secret",
1109 1110 1111
    .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
    .connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
    .connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
1112 1113 1114 1115 1116 1117 1118
    .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 */
1119 1120 1121
};

static virStateDriver stateDriver = {
1122
    .name = "secret",
1123 1124 1125
    .stateInitialize = secretStateInitialize,
    .stateCleanup = secretStateCleanup,
    .stateReload = secretStateReload,
1126 1127 1128 1129 1130
};

int
secretRegister(void)
{
1131
    if (virSetSharedSecretDriver(&secretDriver) < 0)
1132 1133 1134
        return -1;
    if (virRegisterStateDriver(&stateDriver) < 0)
        return -1;
1135 1136
    return 0;
}