secret_driver.c 27.4 KB
Newer Older
1 2 3
/*
 * secret_driver.c: local driver for secret manipulation API
 *
E
Eric Blake 已提交
4
 * Copyright (C) 2009-2011 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 *
 * 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
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * 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"
#include "logging.h"
#include "memory.h"
#include "secret_conf.h"
#include "secret_driver.h"
#include "threads.h"
#include "util.h"
#include "uuid.h"
#include "virterror_internal.h"
43
#include "files.h"
44
#include "configmake.h"
45 46 47 48 49 50 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

#define VIR_FROM_THIS VIR_FROM_SECRET

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

114 115
static virSecretEntryPtr
secretFindByUUID(virSecretDriverStatePtr driver, const unsigned char *uuid)
116 117 118 119 120
{
    virSecretEntryPtr *pptr, s;

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

static virSecretEntryPtr
128
secretFindByUsage(virSecretDriverStatePtr driver, int usageType, const char *usageID)
129
{
130
    virSecretEntryPtr *pptr, s;
131

132 133
    for (pptr = &driver->secrets; *pptr != NULL; pptr = &s->next) {
        s = *pptr;
134

135 136
        if (s->def->usage_type != usageType)
            continue;
137

138 139 140 141
        switch (usageType) {
        case VIR_SECRET_USAGE_TYPE_NONE:
            /* never match this */
            break;
142

143 144 145 146 147
        case VIR_SECRET_USAGE_TYPE_VOLUME:
            if (STREQ(s->def->usage.volume, usageID))
                return s;
            break;
        }
148
    }
149
    return NULL;
150 151
}

152
/* Permament secret storage */
153 154 155 156 157 158 159

/* 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
160
replaceFile(const char *filename, void *data, size_t size)
161 162 163 164 165
{
    char *tmp_path = NULL;
    int fd = -1, ret = -1;

    if (virAsprintf(&tmp_path, "%sXXXXXX", filename) < 0) {
166
        virReportOOMError();
167 168 169 170
        goto cleanup;
    }
    fd = mkstemp (tmp_path);
    if (fd == -1) {
171
        virReportSystemError(errno, _("mkstemp('%s') failed"), tmp_path);
172 173 174
        goto cleanup;
    }
    if (fchmod(fd, S_IRUSR | S_IWUSR) != 0) {
175
        virReportSystemError(errno, _("fchmod('%s') failed"), tmp_path);
176 177 178 179 180
        goto cleanup;
    }

    ret = safewrite(fd, data, size);
    if (ret < 0) {
181
        virReportSystemError(errno, _("error writing to '%s'"),
182 183 184
                              tmp_path);
        goto cleanup;
    }
185
    if (VIR_CLOSE(fd) < 0) {
186
        virReportSystemError(errno, _("error closing '%s'"), tmp_path);
187 188 189 190 191
        goto cleanup;
    }
    fd = -1;

    if (rename(tmp_path, filename) < 0) {
192
        virReportSystemError(errno, _("rename(%s, %s) failed"), tmp_path,
193 194 195 196 197 198 199
                             filename);
        goto cleanup;
    }
    VIR_FREE(tmp_path);
    ret = 0;

cleanup:
200
    VIR_FORCE_CLOSE(fd);
201 202 203 204 205 206 207 208
    if (tmp_path != NULL) {
        unlink(tmp_path);
        VIR_FREE(tmp_path);
    }
    return ret;
}

static char *
209
secretComputePath(virSecretDriverStatePtr driver,
210 211
                  const virSecretEntry *secret, const char *suffix)
{
212 213
    char *ret;
    char uuidstr[VIR_UUID_STRING_BUFLEN];
214

215
    virUUIDFormat(secret->def->uuid, uuidstr);
216

217
    if (virAsprintf(&ret, "%s/%s%s", driver->directory, uuidstr, suffix) < 0)
218
        /* ret is NULL */
219
        virReportOOMError();
220

221 222 223 224
    return ret;
}

static char *
225
secretXMLPath(virSecretDriverStatePtr driver,
226 227
              const virSecretEntry *secret)
{
228
    return secretComputePath(driver, secret, ".xml");
229 230 231
}

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

static int
239
secretEnsureDirectory(virSecretDriverStatePtr driver)
240 241
{
    if (mkdir(driver->directory, S_IRWXU) < 0 && errno != EEXIST) {
242
        virReportSystemError(errno, _("cannot create '%s'"),
243 244 245 246 247 248 249
                             driver->directory);
        return -1;
    }
    return 0;
}

static int
250
secretSaveDef(virSecretDriverStatePtr driver,
251 252 253 254 255
              const virSecretEntry *secret)
{
    char *filename = NULL, *xml = NULL;
    int ret = -1;

256
    if (secretEnsureDirectory(driver) < 0)
257 258
        goto cleanup;

259
    filename = secretXMLPath(driver, secret);
260 261
    if (filename == NULL)
        goto cleanup;
262
    xml = virSecretDefFormat(secret->def);
263 264 265
    if (xml == NULL)
        goto cleanup;

266
    if (replaceFile(filename, xml, strlen(xml)) < 0)
267 268 269 270 271 272 273 274 275 276 277
        goto cleanup;

    ret = 0;

cleanup:
    VIR_FREE(xml);
    VIR_FREE(filename);
    return ret;
}

static int
278
secretSaveValue(virSecretDriverStatePtr driver,
279 280 281 282 283 284 285 286
                const virSecretEntry *secret)
{
    char *filename = NULL, *base64 = NULL;
    int ret = -1;

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

287
    if (secretEnsureDirectory(driver) < 0)
288 289
        goto cleanup;

290
    filename = secretBase64Path(driver, secret);
291 292 293 294 295
    if (filename == NULL)
        goto cleanup;
    base64_encode_alloc((const char *)secret->value, secret->value_size,
                        &base64);
    if (base64 == NULL) {
296
        virReportOOMError();
297 298 299
        goto cleanup;
    }

300
    if (replaceFile(filename, base64, strlen(base64)) < 0)
301 302 303 304 305 306 307 308 309 310 311
        goto cleanup;

    ret = 0;

cleanup:
    VIR_FREE(base64);
    VIR_FREE(filename);
    return ret;
}

static int
312
secretDeleteSaved(virSecretDriverStatePtr driver,
313 314 315 316 317
                  const virSecretEntry *secret)
{
    char *xml_filename = NULL, *value_filename = NULL;
    int ret = -1;

318
    xml_filename = secretXMLPath(driver, secret);
319 320
    if (xml_filename == NULL)
        goto cleanup;
321
    value_filename = secretBase64Path(driver, secret);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
    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
340
secretLoadValidateUUID(virSecretDefPtr def,
341 342
                       const char *xml_basename)
{
343
    char uuidstr[VIR_UUID_STRING_BUFLEN];
344

345
    virUUIDFormat(def->uuid, uuidstr);
346

347
    if (!virFileMatchesNameSuffix(xml_basename, uuidstr, ".xml")) {
348
        virSecretReportError(VIR_ERR_INTERNAL_ERROR,
349 350 351 352 353 354 355 356 357
                             _("<uuid> does not match secret file name '%s'"),
                             xml_basename);
        return -1;
    }

    return 0;
}

static int
358
secretLoadValue(virSecretDriverStatePtr driver,
359 360 361 362 363 364 365
                virSecretEntryPtr secret)
{
    int ret = -1, fd = -1;
    struct stat st;
    char *filename = NULL, *contents = NULL, *value = NULL;
    size_t value_size;

366
    filename = secretBase64Path(driver, secret);
367 368 369 370 371 372 373 374 375
    if (filename == NULL)
        goto cleanup;

    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        if (errno == ENOENT) {
            ret = 0;
            goto cleanup;
        }
376
        virReportSystemError(errno, _("cannot open '%s'"), filename);
377 378 379
        goto cleanup;
    }
    if (fstat(fd, &st) < 0) {
380
        virReportSystemError(errno, _("cannot stat '%s'"), filename);
381 382 383
        goto cleanup;
    }
    if ((size_t)st.st_size != st.st_size) {
384
        virSecretReportError(VIR_ERR_INTERNAL_ERROR,
385 386 387 388 389
                             _("'%s' file does not fit in memory"), filename);
        goto cleanup;
    }

    if (VIR_ALLOC_N(contents, st.st_size) < 0) {
390
        virReportOOMError();
391 392 393
        goto cleanup;
    }
    if (saferead(fd, contents, st.st_size) != st.st_size) {
394
        virReportSystemError(errno, _("cannot read '%s'"), filename);
395 396
        goto cleanup;
    }
397
    VIR_FORCE_CLOSE(fd);
398 399

    if (!base64_decode_alloc(contents, st.st_size, &value, &value_size)) {
400
        virSecretReportError(VIR_ERR_INTERNAL_ERROR,
401 402 403 404
                             _("invalid base64 in '%s'"), filename);
        goto cleanup;
    }
    if (value == NULL) {
405
        virReportOOMError();
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
        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);
    }
424
    VIR_FORCE_CLOSE(fd);
425 426 427 428 429
    VIR_FREE(filename);
    return ret;
}

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

    if (virAsprintf(&xml_filename, "%s/%s", driver->directory,
                    xml_basename) < 0) {
439
        virReportOOMError();
440 441
        goto cleanup;
    }
442
    def = virSecretDefParseFile(xml_filename);
443 444 445 446
    if (def == NULL)
        goto cleanup;
    VIR_FREE(xml_filename);

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

450
    if (VIR_ALLOC(secret) < 0) {
451
        virReportOOMError();
452
        goto cleanup;
453
    }
454 455 456
    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 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
    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
secretNumOfSecrets(virConnectPtr conn)
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    int i;
    virSecretEntryPtr secret;

    secretDriverLock(driver);

    i = 0;
    for (secret = driver->secrets; secret != NULL; secret = secret->next)
        i++;

    secretDriverUnlock(driver);
    return i;
}

static int
secretListSecrets(virConnectPtr conn, char **uuids, int maxuuids)
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    int i;
    virSecretEntryPtr secret;

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

    secretDriverLock(driver);

    i = 0;
    for (secret = driver->secrets; secret != NULL; secret = secret->next) {
575
        char *uuidstr;
576 577
        if (i == maxuuids)
            break;
578
        if (VIR_ALLOC_N(uuidstr, VIR_UUID_STRING_BUFLEN) < 0) {
579
            virReportOOMError();
580
            goto cleanup;
581
        }
582 583
        virUUIDFormat(secret->def->uuid, uuidstr);
        uuids[i] = uuidstr;
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
        i++;
    }

    secretDriverUnlock(driver);
    return i;

cleanup:
    secretDriverUnlock(driver);

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

    return -1;
}

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614

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;

    default:
        return NULL;
    }
}

615
static virSecretPtr
616
secretLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
617 618 619
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr ret = NULL;
620
    virSecretEntryPtr secret;
621 622 623

    secretDriverLock(driver);

624 625
    secret = secretFindByUUID(driver, uuid);
    if (secret == NULL) {
626 627
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(uuid, uuidstr);
628
        virSecretReportError(VIR_ERR_NO_SECRET,
629
                             _("no secret with matching uuid '%s'"), uuidstr);
630 631 632
        goto cleanup;
    }

633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
    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) {
655
        virSecretReportError(VIR_ERR_NO_SECRET,
656 657 658 659 660 661 662 663
                             _("no secret with matching usage '%s'"), usageID);
        goto cleanup;
    }

    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
664 665 666 667 668 669 670 671 672

cleanup:
    secretDriverUnlock(driver);
    return ret;
}


static virSecretPtr
secretDefineXML(virConnectPtr conn, const char *xml,
673
                unsigned int flags)
674 675 676 677
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;
678 679
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
680

681 682
    virCheckFlags(0, NULL);

683
    new_attrs = virSecretDefParseString(xml);
684 685 686 687 688
    if (new_attrs == NULL)
        return NULL;

    secretDriverLock(driver);

689 690 691 692 693 694 695 696
    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);
697
            virSecretReportError(VIR_ERR_INTERNAL_ERROR,
698 699 700 701
                                 _("a secret with UUID %s already defined for use with %s"),
                                 uuidstr, usageID);
            goto cleanup;
        }
702

703 704
        /* No existing secret at all, create one */
        if (VIR_ALLOC(secret) < 0) {
705
            virReportOOMError();
706 707
            goto cleanup;
        }
708

709 710 711 712 713 714 715 716
        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);
717
            virSecretReportError(VIR_ERR_INTERNAL_ERROR,
718 719 720 721 722 723
                                 _("a secret with UUID %s is already defined for use with %s"),
                                 uuidstr, oldUsageID);
            goto cleanup;
        }

        if (secret->def->private && !new_attrs->private) {
724
            virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
725 726 727 728 729 730 731
                                 _("cannot change private flag on existing secret"));
            goto cleanup;
        }

        /* Got an existing secret matches attrs, so reuse that */
        backup = secret->def;
        secret->def = new_attrs;
732 733 734
    }

    if (!new_attrs->ephemeral) {
735
        if (backup && backup->ephemeral) {
736
            if (secretSaveValue(driver, secret) < 0)
737 738
                goto restore_backup;
        }
739
        if (secretSaveDef(driver, secret) < 0) {
740
            if (backup && backup->ephemeral) {
741 742 743
                char *filename;

                /* Undo the secretSaveValue() above; ignore errors */
744
                filename = secretBase64Path(driver, secret);
745 746 747 748 749 750
                if (filename != NULL)
                    (void)unlink(filename);
                VIR_FREE(filename);
            }
            goto restore_backup;
        }
751
    } else if (backup && !backup->ephemeral) {
752
        if (secretDeleteSaved(driver, secret) < 0)
753 754
            goto restore_backup;
    }
755
    /* Saved successfully - drop old values */
756 757 758
    new_attrs = NULL;
    virSecretDefFree(backup);

759 760 761 762
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
763 764 765
    goto cleanup;

restore_backup:
766 767 768 769
    if (backup) {
        /* Error - restore previous state and free new attributes */
        secret->def = backup;
    } else {
770 771
        /* "secret" was added to the head of the list above */
        if (listUnlink(&driverState->secrets) != secret)
772
            virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
773 774 775 776 777 778 779 780 781 782 783 784 785
                                 _("list of secrets is inconsistent"));
        else
            secretFree(secret);
    }

cleanup:
    virSecretDefFree(new_attrs);
    secretDriverUnlock(driver);

    return ret;
}

static char *
786
secretGetXMLDesc(virSecretPtr obj, unsigned int flags)
787 788 789
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    char *ret = NULL;
790
    virSecretEntryPtr secret;
791

792 793
    virCheckFlags(0, NULL);

794 795
    secretDriverLock(driver);

796 797
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
798 799
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
800
        virSecretReportError(VIR_ERR_NO_SECRET,
801
                             _("no secret with matching uuid '%s'"), uuidstr);
802 803 804
        goto cleanup;
    }

805
    ret = virSecretDefFormat(secret->def);
806 807 808 809 810 811 812 813 814

cleanup:
    secretDriverUnlock(driver);

    return ret;
}

static int
secretSetValue(virSecretPtr obj, const unsigned char *value,
815
               size_t value_size, unsigned int flags)
816 817 818 819 820
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    int ret = -1;
    unsigned char *old_value, *new_value;
    size_t old_value_size;
821
    virSecretEntryPtr secret;
822

823 824
    virCheckFlags(0, -1);

825
    if (VIR_ALLOC_N(new_value, value_size) < 0) {
826
        virReportOOMError();
827 828 829 830 831
        return -1;
    }

    secretDriverLock(driver);

832 833
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
834 835
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
836
        virSecretReportError(VIR_ERR_NO_SECRET,
837
                             _("no secret with matching uuid '%s'"), uuidstr);
838 839 840 841 842 843 844 845 846 847
        goto cleanup;
    }

    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) {
848
        if (secretSaveValue(driver, secret) < 0)
849 850
            goto restore_backup;
    }
851
    /* Saved successfully - drop old value */
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
    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 *
secretGetValue(virSecretPtr obj, size_t *value_size, unsigned int flags)
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    unsigned char *ret = NULL;
880
    virSecretEntryPtr secret;
881 882 883

    secretDriverLock(driver);

884 885
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
886 887
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
888
        virSecretReportError(VIR_ERR_NO_SECRET,
889
                             _("no secret with matching uuid '%s'"), uuidstr);
890 891
        goto cleanup;
    }
892

893
    if (secret->value == NULL) {
894 895
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
896
        virSecretReportError(VIR_ERR_NO_SECRET,
897
                             _("secret '%s' does not have a value"), uuidstr);
898 899 900 901 902
        goto cleanup;
    }

    if ((flags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
        secret->def->private) {
903
        virSecretReportError(VIR_ERR_OPERATION_DENIED, "%s",
904 905 906 907 908
                             _("secret is private"));
        goto cleanup;
    }

    if (VIR_ALLOC_N(ret, secret->value_size) < 0) {
909
        virReportOOMError();
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
        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;
926
    virSecretEntryPtr secret;
927 928 929

    secretDriverLock(driver);

930 931
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
932 933
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
934
        virSecretReportError(VIR_ERR_NO_SECRET,
935
                             _("no secret with matching uuid '%s'"), uuidstr);
936 937 938
        goto cleanup;
    }

939
    if (!secret->def->ephemeral &&
940
        secretDeleteSaved(driver, secret) < 0)
941 942 943 944 945 946 947 948 949 950
        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;
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
    }
    secretFree(secret);

    ret = 0;

cleanup:
    secretDriverUnlock(driver);

    return ret;
}

static int
secretDriverCleanup(void)
{
    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
secretDriverStartup(int privileged)
{
    char *base = NULL;

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

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

    if (privileged) {
1000
        base = strdup(SYSCONFDIR "/libvirt");
1001 1002 1003 1004
        if (base == NULL)
            goto out_of_memory;
    } else {
        uid_t uid = geteuid();
1005
        char *userdir = virGetUserDirectory(uid);
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019

        if (!userdir)
            goto error;

        if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
            VIR_FREE(userdir);
            goto out_of_memory;
        }
        VIR_FREE(userdir);
    }
    if (virAsprintf(&driverState->directory, "%s/secrets", base) == -1)
        goto out_of_memory;
    VIR_FREE(base);

1020
    if (loadSecrets(driverState, &driverState->secrets) < 0)
1021 1022 1023 1024 1025 1026
        goto error;

    secretDriverUnlock(driverState);
    return 0;

 out_of_memory:
1027
    VIR_ERROR(_("Out of memory initializing secrets"));
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
 error:
    VIR_FREE(base);
    secretDriverUnlock(driverState);
    secretDriverCleanup();
    return -1;
}

static int
secretDriverReload(void)
{
    virSecretEntryPtr new_secrets = NULL;

    if (!driverState)
        return -1;

    secretDriverLock(driverState);

1045
    if (loadSecrets(driverState, &new_secrets) < 0)
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
        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",
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
    .open = secretOpen, /* 0.7.1 */
    .close = secretClose, /* 0.7.1 */
    .numOfSecrets = secretNumOfSecrets, /* 0.7.1 */
    .listSecrets = secretListSecrets, /* 0.7.1 */
    .lookupByUUID = secretLookupByUUID, /* 0.7.1 */
    .lookupByUsage = secretLookupByUsage, /* 0.7.1 */
    .defineXML = secretDefineXML, /* 0.7.1 */
    .getXMLDesc = secretGetXMLDesc, /* 0.7.1 */
    .setValue = secretSetValue, /* 0.7.1 */
    .getValue = secretGetValue, /* 0.7.1 */
    .undefine = secretUndefine, /* 0.7.1 */
1079 1080 1081
};

static virStateDriver stateDriver = {
1082
    .name = "Secret",
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
    .initialize = secretDriverStartup,
    .cleanup = secretDriverCleanup,
    .reload = secretDriverReload,
    .active = NULL      /* All persistent state is immediately saved to disk */
};

int
secretRegister(void)
{
    virRegisterSecretDriver(&secretDriver);
    virRegisterStateDriver(&stateDriver);
    return 0;
}