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 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
            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,
           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) {
572
        char *uuidstr;
573 574
        if (i == maxuuids)
            break;
575
        if (VIR_ALLOC_N(uuidstr, VIR_UUID_STRING_BUFLEN) < 0) {
576
            virReportOOMError();
577
            goto cleanup;
578
        }
579 580
        virUUIDFormat(secret->def->uuid, uuidstr);
        uuids[i] = uuidstr;
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
        i++;
    }

    secretDriverUnlock(driver);
    return i;

cleanup:
    secretDriverUnlock(driver);

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

    return -1;
}

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611

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

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

    secretDriverLock(driver);

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

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

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

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;
675 676
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
677

678
    new_attrs = virSecretDefParseString(xml);
679 680 681 682 683
    if (new_attrs == NULL)
        return NULL;

    secretDriverLock(driver);

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

698 699
        /* No existing secret at all, create one */
        if (VIR_ALLOC(secret) < 0) {
700
            virReportOOMError();
701 702
            goto cleanup;
        }
703

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

        if (secret->def->private && !new_attrs->private) {
719
            virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
720 721 722 723 724 725 726
                                 _("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;
727 728 729
    }

    if (!new_attrs->ephemeral) {
730
        if (backup && backup->ephemeral) {
731
            if (secretSaveValue(driver, secret) < 0)
732 733
                goto restore_backup;
        }
734
        if (secretSaveDef(driver, secret) < 0) {
735
            if (backup && backup->ephemeral) {
736 737 738
                char *filename;

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

754 755 756 757
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
                       secretUsageIDForDef(secret->def));
758 759 760
    goto cleanup;

restore_backup:
761 762 763 764
    if (backup) {
        /* Error - restore previous state and free new attributes */
        secret->def = backup;
    } else {
765 766
        /* "secret" was added to the head of the list above */
        if (listUnlink(&driverState->secrets) != secret)
767
            virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
                                 _("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;
785
    virSecretEntryPtr secret;
786 787 788

    secretDriverLock(driver);

789 790
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
791 792
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
793
        virSecretReportError(VIR_ERR_NO_SECRET,
794
                             _("no secret with matching uuid '%s'"), uuidstr);
795 796 797
        goto cleanup;
    }

798
    ret = virSecretDefFormat(secret->def);
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813

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;
814
    virSecretEntryPtr secret;
815 816

    if (VIR_ALLOC_N(new_value, value_size) < 0) {
817
        virReportOOMError();
818 819 820 821 822
        return -1;
    }

    secretDriverLock(driver);

823 824
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
825 826
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
827
        virSecretReportError(VIR_ERR_NO_SECRET,
828
                             _("no secret with matching uuid '%s'"), uuidstr);
829 830 831 832 833 834 835 836 837 838
        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) {
839
        if (secretSaveValue(driver, secret) < 0)
840 841
            goto restore_backup;
    }
842
    /* Saved successfully - drop old value */
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
    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;
871
    virSecretEntryPtr secret;
872 873 874

    secretDriverLock(driver);

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

884
    if (secret->value == NULL) {
885 886
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
887
        virSecretReportError(VIR_ERR_NO_SECRET,
888
                             _("secret '%s' does not have a value"), uuidstr);
889 890 891 892 893
        goto cleanup;
    }

    if ((flags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
        secret->def->private) {
894
        virSecretReportError(VIR_ERR_OPERATION_DENIED, "%s",
895 896 897 898 899
                             _("secret is private"));
        goto cleanup;
    }

    if (VIR_ALLOC_N(ret, secret->value_size) < 0) {
900
        virReportOOMError();
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
        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;
917
    virSecretEntryPtr secret;
918 919 920

    secretDriverLock(driver);

921 922
    secret = secretFindByUUID(driver, obj->uuid);
    if (secret == NULL) {
923 924
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
925
        virSecretReportError(VIR_ERR_NO_SECRET,
926
                             _("no secret with matching uuid '%s'"), uuidstr);
927 928 929
        goto cleanup;
    }

930
    if (!secret->def->ephemeral &&
931
        secretDeleteSaved(driver, secret) < 0)
932 933 934 935 936 937 938 939 940 941
        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;
942 943 944 945 946 947 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
    }
    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) {
991
        base = strdup(SYSCONFDIR "/libvirt");
992 993 994 995
        if (base == NULL)
            goto out_of_memory;
    } else {
        uid_t uid = geteuid();
996
        char *userdir = virGetUserDirectory(uid);
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

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

1011
    if (loadSecrets(driverState, &driverState->secrets) < 0)
1012 1013 1014 1015 1016 1017
        goto error;

    secretDriverUnlock(driverState);
    return 0;

 out_of_memory:
1018
    VIR_ERROR(_("Out of memory initializing secrets"));
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
 error:
    VIR_FREE(base);
    secretDriverUnlock(driverState);
    secretDriverCleanup();
    return -1;
}

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

    if (!driverState)
        return -1;

    secretDriverLock(driverState);

1036
    if (loadSecrets(driverState, &new_secrets) < 0)
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
        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",
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
    .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 */
1070 1071 1072
};

static virStateDriver stateDriver = {
1073
    .name = "Secret",
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
    .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;
}