secret_driver.c 18.8 KB
Newer Older
1 2 3
/*
 * secret_driver.c: local driver for secret manipulation API
 *
4
 * Copyright (C) 2009-2016 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * Red Hat Author: Miloslav Trmač <mitr@redhat.com>
 */

#include <config.h>

#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "internal.h"
#include "base64.h"
#include "datatypes.h"
#include "driver.h"
35
#include "virlog.h"
36
#include "viralloc.h"
37
#include "secret_conf.h"
38
#include "virsecretobj.h"
39
#include "secret_driver.h"
40
#include "virthread.h"
41
#include "viruuid.h"
42
#include "virerror.h"
E
Eric Blake 已提交
43
#include "virfile.h"
44
#include "configmake.h"
45
#include "virstring.h"
46
#include "viraccessapicheck.h"
47 48 49

#define VIR_FROM_THIS VIR_FROM_SECRET

50 51
VIR_LOG_INIT("secret.secret_driver");

52 53
enum { SECRET_MAX_XML_FILE = 10*1024*1024 };

54
/* Internal driver state */
55 56 57 58 59

typedef struct _virSecretDriverState virSecretDriverState;
typedef virSecretDriverState *virSecretDriverStatePtr;
struct _virSecretDriverState {
    virMutex lock;
60
    virSecretObjListPtr secrets;
61
    char *configDir;
62 63
};

64
static virSecretDriverStatePtr driver;
65 66

static void
67
secretDriverLock(void)
68 69 70 71 72
{
    virMutexLock(&driver->lock);
}

static void
73
secretDriverUnlock(void)
74 75 76 77
{
    virMutexUnlock(&driver->lock);
}

J
John Ferlan 已提交
78

J
John Ferlan 已提交
79 80 81 82 83 84 85

static virSecretObjPtr
secretObjFromSecret(virSecretPtr secret)
{
    virSecretObjPtr obj;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

86
    if (!(obj = virSecretObjListFindByUUID(driver->secrets, secret->uuid))) {
J
John Ferlan 已提交
87 88 89 90 91 92 93 94 95
        virUUIDFormat(secret->uuid, uuidstr);
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
        return NULL;
    }
    return obj;
}


96
/* Permament secret storage */
97

98
/* Secrets are stored in virSecretDriverStatePtr->configDir.  Each secret
99 100 101 102 103
   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
104 105
secretRewriteFile(int fd,
                  void *opaque)
106
{
107
    char *data = opaque;
108

109 110
    if (safewrite(fd, data, strlen(data)) < 0)
        return -1;
111

112
    return 0;
113 114 115 116
}


static int
117
secretEnsureDirectory(void)
118
{
119
    if (mkdir(driver->configDir, S_IRWXU) < 0 && errno != EEXIST) {
120
        virReportSystemError(errno, _("cannot create '%s'"),
121
                             driver->configDir);
122 123 124 125 126 127
        return -1;
    }
    return 0;
}

static int
J
John Ferlan 已提交
128
secretSaveDef(const virSecretObj *secret)
129
{
130
    char *xml = NULL;
131 132
    int ret = -1;

133
    if (secretEnsureDirectory() < 0)
134 135
        goto cleanup;

136
    if (!(xml = virSecretDefFormat(secret->def)))
137 138
        goto cleanup;

139
    if (virFileRewrite(secret->configFile, S_IRUSR | S_IWUSR,
140
                       secretRewriteFile, xml) < 0)
141 142 143 144
        goto cleanup;

    ret = 0;

145
 cleanup:
146 147 148 149 150
    VIR_FREE(xml);
    return ret;
}

static int
J
John Ferlan 已提交
151
secretSaveValue(const virSecretObj *secret)
152
{
153
    char *base64 = NULL;
154 155 156 157 158
    int ret = -1;

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

159
    if (secretEnsureDirectory() < 0)
160 161 162 163 164
        goto cleanup;

    base64_encode_alloc((const char *)secret->value, secret->value_size,
                        &base64);
    if (base64 == NULL) {
165
        virReportOOMError();
166 167 168
        goto cleanup;
    }

169
    if (virFileRewrite(secret->base64File, S_IRUSR | S_IWUSR,
170
                       secretRewriteFile, base64) < 0)
171 172 173 174
        goto cleanup;

    ret = 0;

175
 cleanup:
176 177 178 179 180
    VIR_FREE(base64);
    return ret;
}

static int
J
John Ferlan 已提交
181
secretDeleteSaved(const virSecretObj *secret)
182
{
183
    if (unlink(secret->configFile) < 0 && errno != ENOENT)
184 185
        return -1;

186 187
    /* When the XML is missing, the rest may waste disk space, but the secret
       won't be loaded again, so we have succeeded already. */
188
    (void)unlink(secret->base64File);
189

190
    return 0;
191 192 193
}

static int
194
secretLoadValidateUUID(virSecretDefPtr def,
195
                       const char *file)
196
{
197
    char uuidstr[VIR_UUID_STRING_BUFLEN];
198

199
    virUUIDFormat(def->uuid, uuidstr);
200

201
    if (!virFileMatchesNameSuffix(file, uuidstr, ".xml")) {
202 203
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("<uuid> does not match secret file name '%s'"),
204
                       file);
205 206 207 208 209 210 211
        return -1;
    }

    return 0;
}

static int
J
John Ferlan 已提交
212
secretLoadValue(virSecretObjPtr secret)
213 214 215
{
    int ret = -1, fd = -1;
    struct stat st;
216
    char *contents = NULL, *value = NULL;
217 218
    size_t value_size;

219
    if ((fd = open(secret->base64File, O_RDONLY)) == -1) {
220 221 222 223
        if (errno == ENOENT) {
            ret = 0;
            goto cleanup;
        }
224 225
        virReportSystemError(errno, _("cannot open '%s'"),
                             secret->base64File);
226 227
        goto cleanup;
    }
228

229
    if (fstat(fd, &st) < 0) {
230 231
        virReportSystemError(errno, _("cannot stat '%s'"),
                             secret->base64File);
232 233
        goto cleanup;
    }
234

235
    if ((size_t)st.st_size != st.st_size) {
236
        virReportError(VIR_ERR_INTERNAL_ERROR,
237 238
                       _("'%s' file does not fit in memory"),
                       secret->base64File);
239 240 241
        goto cleanup;
    }

242
    if (VIR_ALLOC_N(contents, st.st_size) < 0)
243
        goto cleanup;
244

245
    if (saferead(fd, contents, st.st_size) != st.st_size) {
246 247
        virReportSystemError(errno, _("cannot read '%s'"),
                             secret->base64File);
248 249
        goto cleanup;
    }
250

251
    VIR_FORCE_CLOSE(fd);
252 253

    if (!base64_decode_alloc(contents, st.st_size, &value, &value_size)) {
254
        virReportError(VIR_ERR_INTERNAL_ERROR,
255 256
                       _("invalid base64 in '%s'"),
                       secret->base64File);
257 258
        goto cleanup;
    }
259
    if (value == NULL)
260 261 262 263 264 265 266 267
        goto cleanup;

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

    ret = 0;

268
 cleanup:
269 270 271 272 273 274 275 276
    if (value != NULL) {
        memset(value, 0, value_size);
        VIR_FREE(value);
    }
    if (contents != NULL) {
        memset(contents, 0, st.st_size);
        VIR_FREE(contents);
    }
277
    VIR_FORCE_CLOSE(fd);
278 279 280
    return ret;
}

J
John Ferlan 已提交
281

J
John Ferlan 已提交
282
static virSecretObjPtr
283
secretLoad(virSecretObjListPtr secrets,
J
John Ferlan 已提交
284
           const char *file,
285
           const char *path,
286
           const char *configDir)
287
{
288
    virSecretDefPtr def = NULL;
J
John Ferlan 已提交
289
    virSecretObjPtr secret = NULL, ret = NULL;
290

291
    if (!(def = virSecretDefParseFile(path)))
292
        goto cleanup;
293

294
    if (secretLoadValidateUUID(def, file) < 0)
295 296
        goto cleanup;

297
    if (!(secret = virSecretObjListAdd(secrets, def, configDir, NULL)))
298 299 300
        goto cleanup;
    def = NULL;

301
    if (secretLoadValue(secret) < 0)
302 303 304 305 306
        goto cleanup;

    ret = secret;
    secret = NULL;

307
 cleanup:
308 309
    if (secret)
        virSecretObjListRemove(secrets, secret);
310 311 312 313
    virSecretDefFree(def);
    return ret;
}

314

315
static int
316
secretLoadAllConfigs(virSecretObjListPtr secrets,
J
John Ferlan 已提交
317
                     const char *configDir)
318 319 320 321
{
    DIR *dir = NULL;
    struct dirent *de;

J
John Ferlan 已提交
322
    if (!(dir = opendir(configDir))) {
323 324
        if (errno == ENOENT)
            return 0;
J
John Ferlan 已提交
325
        virReportSystemError(errno, _("cannot open '%s'"), configDir);
326
        return -1;
327
    }
328

329 330
    /* Ignore errors reported by readdir or other calls within the
     * loop (if any).  It's better to keep the secrets we managed to find. */
E
Eric Blake 已提交
331
    while (virDirRead(dir, &de, NULL) > 0) {
332
        char *path;
J
John Ferlan 已提交
333
        virSecretObjPtr secret;
334 335 336

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

338 339 340
        if (!virFileHasSuffix(de->d_name, ".xml"))
            continue;

J
John Ferlan 已提交
341
        if (!(path = virFileBuildPath(configDir, de->d_name, NULL)))
342 343
            continue;

344
        if (!(secret = secretLoad(secrets, de->d_name, path, configDir))) {
345 346
            virErrorPtr err = virGetLastError();

347
            VIR_ERROR(_("Error reading secret: %s"),
348
                      err != NULL ? err->message: _("unknown error"));
349
            virResetError(err);
350
            VIR_FREE(path);
351 352
            continue;
        }
353

354
        VIR_FREE(path);
355
        virSecretObjEndAPI(&secret);
356 357
    }

358 359
    closedir(dir);
    return 0;
360 361
}

362
/* Driver functions */
363 364

static int
365
secretConnectNumOfSecrets(virConnectPtr conn)
366
{
367 368 369
    if (virConnectNumOfSecretsEnsureACL(conn) < 0)
        return -1;

370 371 372
    return virSecretObjListNumOfSecrets(driver->secrets,
                                        virConnectNumOfSecretsCheckACL,
                                        conn);
373 374 375
}

static int
376 377 378
secretConnectListSecrets(virConnectPtr conn,
                         char **uuids,
                         int maxuuids)
379 380 381
{
    memset(uuids, 0, maxuuids * sizeof(*uuids));

382 383 384
    if (virConnectListSecretsEnsureACL(conn) < 0)
        return -1;

385 386
    return virSecretObjListGetUUIDs(driver->secrets, uuids, maxuuids,
                                    virConnectListSecretsCheckACL, conn);
387 388
}

389

O
Osier Yang 已提交
390
static int
391 392
secretConnectListAllSecrets(virConnectPtr conn,
                            virSecretPtr **secrets,
393 394
                            unsigned int flags)
{
O
Osier Yang 已提交
395 396
    virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);

397 398 399
    if (virConnectListAllSecretsEnsureACL(conn) < 0)
        return -1;

400 401 402
    return virSecretObjListExport(conn, driver->secrets, secrets,
                                  virConnectListAllSecretsCheckACL,
                                  flags);
O
Osier Yang 已提交
403 404 405
}


406
static virSecretPtr
407 408
secretLookupByUUID(virConnectPtr conn,
                   const unsigned char *uuid)
409 410
{
    virSecretPtr ret = NULL;
J
John Ferlan 已提交
411
    virSecretObjPtr secret;
412

413
    if (!(secret = virSecretObjListFindByUUID(driver->secrets, uuid))) {
414 415
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(uuid, uuidstr);
416 417
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching uuid '%s'"), uuidstr);
418 419 420
        goto cleanup;
    }

421 422 423
    if (virSecretLookupByUUIDEnsureACL(conn, secret->def) < 0)
        goto cleanup;

424 425 426
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
427
                       virSecretUsageIDForDef(secret->def));
428

429
 cleanup:
430
    virSecretObjEndAPI(&secret);
431 432 433 434 435
    return ret;
}


static virSecretPtr
436 437 438
secretLookupByUsage(virConnectPtr conn,
                    int usageType,
                    const char *usageID)
439 440
{
    virSecretPtr ret = NULL;
J
John Ferlan 已提交
441
    virSecretObjPtr secret;
442

443 444
    if (!(secret = virSecretObjListFindByUsage(driver->secrets,
                                               usageType, usageID))) {
445 446
        virReportError(VIR_ERR_NO_SECRET,
                       _("no secret with matching usage '%s'"), usageID);
447 448 449
        goto cleanup;
    }

450 451 452
    if (virSecretLookupByUsageEnsureACL(conn, secret->def) < 0)
        goto cleanup;

453 454 455
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
456
                       virSecretUsageIDForDef(secret->def));
457

458
 cleanup:
459
    virSecretObjEndAPI(&secret);
460 461 462 463 464
    return ret;
}


static virSecretPtr
465 466
secretDefineXML(virConnectPtr conn,
                const char *xml,
467
                unsigned int flags)
468 469
{
    virSecretPtr ret = NULL;
J
John Ferlan 已提交
470
    virSecretObjPtr secret = NULL;
471 472
    virSecretDefPtr backup = NULL;
    virSecretDefPtr new_attrs;
473

474 475
    virCheckFlags(0, NULL);

476
    if (!(new_attrs = virSecretDefParseString(xml)))
477 478
        return NULL;

479 480 481
    if (virSecretDefineXMLEnsureACL(conn, new_attrs) < 0)
        goto cleanup;

482 483 484
    if (!(secret = virSecretObjListAdd(driver->secrets, new_attrs,
                                       driver->configDir, &backup)))
        goto cleanup;
485 486

    if (!new_attrs->ephemeral) {
487
        if (backup && backup->ephemeral) {
488
            if (secretSaveValue(secret) < 0)
489 490
                goto restore_backup;
        }
491
        if (secretSaveDef(secret) < 0) {
492
            if (backup && backup->ephemeral) {
493
                /* Undo the secretSaveValue() above; ignore errors */
494
                (void)unlink(secret->base64File);
495 496 497
            }
            goto restore_backup;
        }
498
    } else if (backup && !backup->ephemeral) {
499
        if (secretDeleteSaved(secret) < 0)
500 501
            goto restore_backup;
    }
502
    /* Saved successfully - drop old values */
503 504 505
    new_attrs = NULL;
    virSecretDefFree(backup);

506 507 508
    ret = virGetSecret(conn,
                       secret->def->uuid,
                       secret->def->usage_type,
509
                       virSecretUsageIDForDef(secret->def));
510 511
    goto cleanup;

512
 restore_backup:
513 514 515 516 517
    /* If we have a backup, then secret was defined before, so just restore
     * the backup. The current secret->def (new_attrs) will be handled below.
     * Otherwise, this is a new secret, thus remove it.
     */
    if (backup)
518
        secret->def = backup;
519 520
    else
        virSecretObjListRemove(driver->secrets, secret);
521

522
 cleanup:
523
    virSecretDefFree(new_attrs);
524
    virSecretObjEndAPI(&secret);
525 526 527 528 529

    return ret;
}

static char *
530 531
secretGetXMLDesc(virSecretPtr obj,
                 unsigned int flags)
532 533
{
    char *ret = NULL;
J
John Ferlan 已提交
534
    virSecretObjPtr secret;
535

536 537
    virCheckFlags(0, NULL);

J
John Ferlan 已提交
538
    if (!(secret = secretObjFromSecret(obj)))
539 540
        goto cleanup;

541 542 543
    if (virSecretGetXMLDescEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

544
    ret = virSecretDefFormat(secret->def);
545

546
 cleanup:
547
    virSecretObjEndAPI(&secret);
548 549 550 551 552

    return ret;
}

static int
553 554 555 556
secretSetValue(virSecretPtr obj,
               const unsigned char *value,
               size_t value_size,
               unsigned int flags)
557 558 559 560
{
    int ret = -1;
    unsigned char *old_value, *new_value;
    size_t old_value_size;
J
John Ferlan 已提交
561
    virSecretObjPtr secret;
562

563 564
    virCheckFlags(0, -1);

565
    if (VIR_ALLOC_N(new_value, value_size) < 0)
566 567
        return -1;

J
John Ferlan 已提交
568
    if (!(secret = secretObjFromSecret(obj)))
569 570
        goto cleanup;

571 572 573
    if (virSecretSetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

574 575 576 577 578 579 580
    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) {
581
        if (secretSaveValue(secret) < 0)
582 583
            goto restore_backup;
    }
584
    /* Saved successfully - drop old value */
585 586 587 588 589 590 591 592 593
    if (old_value != NULL) {
        memset(old_value, 0, old_value_size);
        VIR_FREE(old_value);
    }
    new_value = NULL;

    ret = 0;
    goto cleanup;

594
 restore_backup:
595 596 597 598 599
    /* Error - restore previous state and free new value */
    secret->value = old_value;
    secret->value_size = old_value_size;
    memset(new_value, 0, value_size);

600
 cleanup:
601
    virSecretObjEndAPI(&secret);
602 603 604 605 606 607 608

    VIR_FREE(new_value);

    return ret;
}

static unsigned char *
609 610 611
secretGetValue(virSecretPtr obj,
               size_t *value_size,
               unsigned int flags,
612
               unsigned int internalFlags)
613 614
{
    unsigned char *ret = NULL;
J
John Ferlan 已提交
615
    virSecretObjPtr secret;
616

617 618
    virCheckFlags(0, NULL);

J
John Ferlan 已提交
619
    if (!(secret = secretObjFromSecret(obj)))
620
        goto cleanup;
621

622 623 624
    if (virSecretGetValueEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

625
    if (secret->value == NULL) {
626 627
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(obj->uuid, uuidstr);
628 629
        virReportError(VIR_ERR_NO_SECRET,
                       _("secret '%s' does not have a value"), uuidstr);
630 631 632
        goto cleanup;
    }

633
    if ((internalFlags & VIR_SECRET_GET_VALUE_INTERNAL_CALL) == 0 &&
634
        secret->def->private) {
635
        virReportError(VIR_ERR_INVALID_SECRET, "%s",
636
                       _("secret is private"));
637 638 639
        goto cleanup;
    }

640
    if (VIR_ALLOC_N(ret, secret->value_size) < 0)
641 642 643 644
        goto cleanup;
    memcpy(ret, secret->value, secret->value_size);
    *value_size = secret->value_size;

645
 cleanup:
646
    virSecretObjEndAPI(&secret);
647 648 649 650 651 652 653 654

    return ret;
}

static int
secretUndefine(virSecretPtr obj)
{
    int ret = -1;
J
John Ferlan 已提交
655
    virSecretObjPtr secret;
656

J
John Ferlan 已提交
657
    if (!(secret = secretObjFromSecret(obj)))
658 659
        goto cleanup;

660 661 662
    if (virSecretUndefineEnsureACL(obj->conn, secret->def) < 0)
        goto cleanup;

663
    if (!secret->def->ephemeral &&
664
        secretDeleteSaved(secret) < 0)
665 666
        goto cleanup;

667
    virSecretObjListRemove(driver->secrets, secret);
668 669 670

    ret = 0;

671
 cleanup:
672
    virSecretObjEndAPI(&secret);
673 674 675 676 677

    return ret;
}

static int
678
secretStateCleanup(void)
679
{
680
    if (!driver)
681 682
        return -1;

683
    secretDriverLock();
684

685
    virObjectUnref(driver->secrets);
686
    VIR_FREE(driver->configDir);
687

688 689 690
    secretDriverUnlock();
    virMutexDestroy(&driver->lock);
    VIR_FREE(driver);
691 692 693 694 695

    return 0;
}

static int
696 697 698
secretStateInitialize(bool privileged,
                      virStateInhibitCallback callback ATTRIBUTE_UNUSED,
                      void *opaque ATTRIBUTE_UNUSED)
699 700 701
{
    char *base = NULL;

702
    if (VIR_ALLOC(driver) < 0)
703 704
        return -1;

705 706
    if (virMutexInit(&driver->lock) < 0) {
        VIR_FREE(driver);
707 708
        return -1;
    }
709
    secretDriverLock();
710 711

    if (privileged) {
712 713
        if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0)
            goto error;
714
    } else {
715
        if (!(base = virGetUserConfigDirectory()))
716 717
            goto error;
    }
718
    if (virAsprintf(&driver->configDir, "%s/secrets", base) < 0)
719
        goto error;
720 721
    VIR_FREE(base);

722 723 724 725
    if (!(driver->secrets = virSecretObjListNew()))
        goto error;

    if (secretLoadAllConfigs(driver->secrets, driver->configDir) < 0)
726 727
        goto error;

728
    secretDriverUnlock();
729 730 731 732
    return 0;

 error:
    VIR_FREE(base);
733
    secretDriverUnlock();
734
    secretStateCleanup();
735 736 737 738
    return -1;
}

static int
739
secretStateReload(void)
740
{
741
    if (!driver)
742 743
        return -1;

744
    secretDriverLock();
745

746
    ignore_value(secretLoadAllConfigs(driver->secrets, driver->configDir));
747

748
    secretDriverUnlock();
749 750 751 752 753
    return 0;
}

static virSecretDriver secretDriver = {
    .name = "secret",
754 755 756
    .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
    .connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
    .connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
757 758 759 760 761 762 763
    .secretLookupByUUID = secretLookupByUUID, /* 0.7.1 */
    .secretLookupByUsage = secretLookupByUsage, /* 0.7.1 */
    .secretDefineXML = secretDefineXML, /* 0.7.1 */
    .secretGetXMLDesc = secretGetXMLDesc, /* 0.7.1 */
    .secretSetValue = secretSetValue, /* 0.7.1 */
    .secretGetValue = secretGetValue, /* 0.7.1 */
    .secretUndefine = secretUndefine, /* 0.7.1 */
764 765 766
};

static virStateDriver stateDriver = {
767
    .name = "secret",
768 769 770
    .stateInitialize = secretStateInitialize,
    .stateCleanup = secretStateCleanup,
    .stateReload = secretStateReload,
771 772 773 774 775
};

int
secretRegister(void)
{
776
    if (virSetSharedSecretDriver(&secretDriver) < 0)
777 778 779
        return -1;
    if (virRegisterStateDriver(&stateDriver) < 0)
        return -1;
780 781
    return 0;
}