secret_driver.c 27.3 KB
Newer Older
1 2 3
/*
 * secret_driver.c: local driver for secret manipulation API
 *
4
 * Copyright (C) 2009-2010 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 43
 *
 * 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 <stdbool.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"
44
#include "files.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 528 529 530 531 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 575 576 577
            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:
    while (list != NULL) {
        virSecretEntryPtr s;

        s = listUnlink(&list);
        secretFree(s);
    }
    if (dir != NULL)
        closedir(dir);
    return ret;
}

 /* Driver functions */

static virDrvOpenStatus
secretOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED,
           int flags ATTRIBUTE_UNUSED) {
    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) {
578
        char *uuidstr;
579 580
        if (i == maxuuids)
            break;
581
        if (VIR_ALLOC_N(uuidstr, VIR_UUID_STRING_BUFLEN) < 0) {
582
            virReportOOMError();
583
            goto cleanup;
584
        }
585 586
        virUUIDFormat(secret->def->uuid, uuidstr);
        uuids[i] = uuidstr;
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
        i++;
    }

    secretDriverUnlock(driver);
    return i;

cleanup:
    secretDriverUnlock(driver);

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

    return -1;
}

602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617

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

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

    secretDriverLock(driver);

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

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

    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
667 668 669 670 671 672 673 674 675 676 677 678 679 680

cleanup:
    secretDriverUnlock(driver);
    return ret;
}


static virSecretPtr
secretDefineXML(virConnectPtr conn, const char *xml,
                unsigned int flags ATTRIBUTE_UNUSED)
{
    virSecretDriverStatePtr driver = conn->secretPrivateData;
    virSecretPtr ret = NULL;
    virSecretEntryPtr secret;
681 682
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
683

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

    secretDriverLock(driver);

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

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

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

        if (secret->def->private && !new_attrs->private) {
725
            virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
726 727 728 729 730 731 732
                                 _("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;
733 734 735
    }

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

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

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

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

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

    return ret;
}

static char *
secretGetXMLDesc(virSecretPtr obj, unsigned int flags ATTRIBUTE_UNUSED)
{
    virSecretDriverStatePtr driver = obj->conn->secretPrivateData;
    char *ret = NULL;
791
    virSecretEntryPtr secret;
792 793 794

    secretDriverLock(driver);

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

804
    ret = virSecretDefFormat(secret->def);
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819

cleanup:
    secretDriverUnlock(driver);

    return ret;
}

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

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

    secretDriverLock(driver);

829 830
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
831 832
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
833
        virSecretReportError(VIR_ERR_NO_SECRET,
834
                             _("no secret with matching uuid '%s'"), uuidstr);
835 836 837 838 839 840 841 842 843 844
        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) {
845
        if (secretSaveValue(driver, secret) < 0)
846 847
            goto restore_backup;
    }
848
    /* Saved successfully - drop old value */
849 850 851 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
    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;
877
    virSecretEntryPtr secret;
878 879 880

    secretDriverLock(driver);

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

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

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

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

    secretDriverLock(driver);

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

936
    if (!secret->def->ephemeral &&
937
        secretDeleteSaved(driver, secret) < 0)
938 939 940 941 942 943 944 945 946 947
        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;
948 949 950 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 1000 1001
    }
    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) {
        base = strdup(SYSCONF_DIR "/libvirt");
        if (base == NULL)
            goto out_of_memory;
    } else {
        uid_t uid = geteuid();
1002
        char *userdir = virGetUserDirectory(uid);
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016

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

1017
    if (loadSecrets(driverState, &driverState->secrets) < 0)
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
        goto error;

    secretDriverUnlock(driverState);
    return 0;

 out_of_memory:
    VIR_ERROR0(_("Out of memory initializing secrets"));
 error:
    VIR_FREE(base);
    secretDriverUnlock(driverState);
    secretDriverCleanup();
    return -1;
}

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

    if (!driverState)
        return -1;

    secretDriverLock(driverState);

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

static virStateDriver stateDriver = {
1079
    .name = "Secret",
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
    .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;
}