1 /*
2  * secret_driver.c: local driver for secret manipulation API
3  *
4  * Copyright (C) 2009-2016 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 
27 #include "internal.h"
28 #include "datatypes.h"
29 #include "driver.h"
30 #include "virlog.h"
31 #include "viralloc.h"
32 #include "secret_conf.h"
33 #include "virsecretobj.h"
34 #include "secret_driver.h"
35 #include "virthread.h"
36 #include "viruuid.h"
37 #include "virerror.h"
38 #include "virfile.h"
39 #include "viridentity.h"
40 #include "virpidfile.h"
41 #include "configmake.h"
42 #include "virstring.h"
43 #include "viraccessapicheck.h"
44 #include "secret_event.h"
45 #include "virutil.h"
46 
47 #define VIR_FROM_THIS VIR_FROM_SECRET
48 
49 VIR_LOG_INIT("secret.secret_driver");
50 
51 enum { SECRET_MAX_XML_FILE = 10*1024*1024 };
52 
53 /* Internal driver state */
54 
55 typedef struct _virSecretDriverState virSecretDriverState;
56 struct _virSecretDriverState {
57     virMutex lock;
58     bool privileged; /* readonly */
59     char *embeddedRoot; /* readonly */
60     int embeddedRefs;
61     virSecretObjList *secrets;
62     char *stateDir;
63     char *configDir;
64 
65     /* pid file FD, ensures two copies of the driver can't use the same root */
66     int lockFD;
67 
68     /* Immutable pointer, self-locking APIs */
69     virObjectEventState *secretEventState;
70 };
71 
72 static virSecretDriverState *driver;
73 
74 static void
secretDriverLock(void)75 secretDriverLock(void)
76 {
77     virMutexLock(&driver->lock);
78 }
79 
80 
81 static void
secretDriverUnlock(void)82 secretDriverUnlock(void)
83 {
84     virMutexUnlock(&driver->lock);
85 }
86 
87 
88 static virSecretObj *
secretObjFromSecret(virSecretPtr secret)89 secretObjFromSecret(virSecretPtr secret)
90 {
91     virSecretObj *obj;
92     char uuidstr[VIR_UUID_STRING_BUFLEN];
93 
94     virUUIDFormat(secret->uuid, uuidstr);
95     if (!(obj = virSecretObjListFindByUUID(driver->secrets, uuidstr))) {
96         virReportError(VIR_ERR_NO_SECRET,
97                        _("no secret with matching uuid '%s'"), uuidstr);
98         return NULL;
99     }
100     return obj;
101 }
102 
103 
104 /* Driver functions */
105 
106 static int
secretConnectNumOfSecrets(virConnectPtr conn)107 secretConnectNumOfSecrets(virConnectPtr conn)
108 {
109     if (virConnectNumOfSecretsEnsureACL(conn) < 0)
110         return -1;
111 
112     return virSecretObjListNumOfSecrets(driver->secrets,
113                                         virConnectNumOfSecretsCheckACL,
114                                         conn);
115 }
116 
117 
118 static int
secretConnectListSecrets(virConnectPtr conn,char ** uuids,int maxuuids)119 secretConnectListSecrets(virConnectPtr conn,
120                          char **uuids,
121                          int maxuuids)
122 {
123     memset(uuids, 0, maxuuids * sizeof(*uuids));
124 
125     if (virConnectListSecretsEnsureACL(conn) < 0)
126         return -1;
127 
128     return virSecretObjListGetUUIDs(driver->secrets, uuids, maxuuids,
129                                     virConnectListSecretsCheckACL, conn);
130 }
131 
132 
133 static int
secretConnectListAllSecrets(virConnectPtr conn,virSecretPtr ** secrets,unsigned int flags)134 secretConnectListAllSecrets(virConnectPtr conn,
135                             virSecretPtr **secrets,
136                             unsigned int flags)
137 {
138     virCheckFlags(VIR_CONNECT_LIST_SECRETS_FILTERS_ALL, -1);
139 
140     if (virConnectListAllSecretsEnsureACL(conn) < 0)
141         return -1;
142 
143     return virSecretObjListExport(conn, driver->secrets, secrets,
144                                   virConnectListAllSecretsCheckACL,
145                                   flags);
146 }
147 
148 
149 static virSecretPtr
secretLookupByUUID(virConnectPtr conn,const unsigned char * uuid)150 secretLookupByUUID(virConnectPtr conn,
151                    const unsigned char *uuid)
152 {
153     virSecretPtr ret = NULL;
154     virSecretObj *obj;
155     virSecretDef *def;
156     char uuidstr[VIR_UUID_STRING_BUFLEN];
157 
158     virUUIDFormat(uuid, uuidstr);
159     if (!(obj = virSecretObjListFindByUUID(driver->secrets, uuidstr))) {
160         virReportError(VIR_ERR_NO_SECRET,
161                        _("no secret with matching uuid '%s'"), uuidstr);
162         goto cleanup;
163     }
164 
165     def = virSecretObjGetDef(obj);
166     if (virSecretLookupByUUIDEnsureACL(conn, def) < 0)
167         goto cleanup;
168 
169     ret = virGetSecret(conn,
170                        def->uuid,
171                        def->usage_type,
172                        def->usage_id);
173 
174  cleanup:
175     virSecretObjEndAPI(&obj);
176     return ret;
177 }
178 
179 
180 static virSecretPtr
secretLookupByUsage(virConnectPtr conn,int usageType,const char * usageID)181 secretLookupByUsage(virConnectPtr conn,
182                     int usageType,
183                     const char *usageID)
184 {
185     virSecretPtr ret = NULL;
186     virSecretObj *obj;
187     virSecretDef *def;
188 
189     if (!(obj = virSecretObjListFindByUsage(driver->secrets,
190                                             usageType, usageID))) {
191         virReportError(VIR_ERR_NO_SECRET,
192                        _("no secret with matching usage '%s'"), usageID);
193         goto cleanup;
194     }
195 
196     def = virSecretObjGetDef(obj);
197     if (virSecretLookupByUsageEnsureACL(conn, def) < 0)
198         goto cleanup;
199 
200     ret = virGetSecret(conn,
201                        def->uuid,
202                        def->usage_type,
203                        def->usage_id);
204 
205  cleanup:
206     virSecretObjEndAPI(&obj);
207     return ret;
208 }
209 
210 
211 static virSecretPtr
secretDefineXML(virConnectPtr conn,const char * xml,unsigned int flags)212 secretDefineXML(virConnectPtr conn,
213                 const char *xml,
214                 unsigned int flags)
215 {
216     virSecretPtr ret = NULL;
217     virSecretObj *obj = NULL;
218     virSecretDef *objDef;
219     virSecretDef *backup = NULL;
220     virSecretDef *def;
221     virObjectEvent *event = NULL;
222 
223     virCheckFlags(VIR_SECRET_DEFINE_VALIDATE, NULL);
224 
225     if (!(def = virSecretDefParseString(xml, flags)))
226         return NULL;
227 
228     if (virSecretDefineXMLEnsureACL(conn, def) < 0)
229         goto cleanup;
230 
231     if (!(obj = virSecretObjListAdd(driver->secrets, &def,
232                                     driver->configDir, &backup)))
233         goto cleanup;
234     objDef = virSecretObjGetDef(obj);
235 
236     if (!objDef->isephemeral) {
237         if (backup && backup->isephemeral) {
238             if (virSecretObjSaveData(obj) < 0)
239                 goto restore_backup;
240         }
241 
242         if (virSecretObjSaveConfig(obj) < 0) {
243             if (backup && backup->isephemeral) {
244                 /* Undo the virSecretObjSaveData() above; ignore errors */
245                 virSecretObjDeleteData(obj);
246             }
247             goto restore_backup;
248         }
249     } else if (backup && !backup->isephemeral) {
250         if (virSecretObjDeleteConfig(obj) < 0)
251             goto restore_backup;
252 
253         virSecretObjDeleteData(obj);
254     }
255     /* Saved successfully - drop old values */
256     virSecretDefFree(backup);
257 
258     event = virSecretEventLifecycleNew(objDef->uuid,
259                                        objDef->usage_type,
260                                        objDef->usage_id,
261                                        VIR_SECRET_EVENT_DEFINED,
262                                        0);
263 
264     ret = virGetSecret(conn,
265                        objDef->uuid,
266                        objDef->usage_type,
267                        objDef->usage_id);
268     goto cleanup;
269 
270  restore_backup:
271     /* If we have a backup, then secret was defined before, so just restore
272      * the backup; otherwise, this is a new secret, thus remove it. */
273     if (backup) {
274         virSecretObjSetDef(obj, backup);
275         def = g_steal_pointer(&objDef);
276     } else {
277         virSecretObjListRemove(driver->secrets, obj);
278         virObjectUnref(obj);
279         obj = NULL;
280     }
281 
282  cleanup:
283     virSecretDefFree(def);
284     virSecretObjEndAPI(&obj);
285     virObjectEventStateQueue(driver->secretEventState, event);
286 
287     return ret;
288 }
289 
290 
291 static char *
secretGetXMLDesc(virSecretPtr secret,unsigned int flags)292 secretGetXMLDesc(virSecretPtr secret,
293                  unsigned int flags)
294 {
295     char *ret = NULL;
296     virSecretObj *obj;
297     virSecretDef *def;
298 
299     virCheckFlags(0, NULL);
300 
301     if (!(obj = secretObjFromSecret(secret)))
302         goto cleanup;
303 
304     def = virSecretObjGetDef(obj);
305     if (virSecretGetXMLDescEnsureACL(secret->conn, def) < 0)
306         goto cleanup;
307 
308     ret = virSecretDefFormat(def);
309 
310  cleanup:
311     virSecretObjEndAPI(&obj);
312 
313     return ret;
314 }
315 
316 
317 static int
secretSetValue(virSecretPtr secret,const unsigned char * value,size_t value_size,unsigned int flags)318 secretSetValue(virSecretPtr secret,
319                const unsigned char *value,
320                size_t value_size,
321                unsigned int flags)
322 {
323     int ret = -1;
324     virSecretObj *obj;
325     virSecretDef *def;
326     virObjectEvent *event = NULL;
327 
328     virCheckFlags(0, -1);
329 
330     if (!(obj = secretObjFromSecret(secret)))
331         goto cleanup;
332 
333     def = virSecretObjGetDef(obj);
334     if (virSecretSetValueEnsureACL(secret->conn, def) < 0)
335         goto cleanup;
336 
337     if (virSecretObjSetValue(obj, value, value_size) < 0)
338         goto cleanup;
339 
340     event = virSecretEventValueChangedNew(def->uuid,
341                                           def->usage_type,
342                                           def->usage_id);
343     ret = 0;
344 
345  cleanup:
346     virSecretObjEndAPI(&obj);
347     virObjectEventStateQueue(driver->secretEventState, event);
348 
349     return ret;
350 }
351 
352 
353 static unsigned char *
secretGetValue(virSecretPtr secret,size_t * value_size,unsigned int flags)354 secretGetValue(virSecretPtr secret,
355                size_t *value_size,
356                unsigned int flags)
357 {
358     unsigned char *ret = NULL;
359     virSecretObj *obj;
360     virSecretDef *def;
361 
362     virCheckFlags(0, NULL);
363 
364     if (!(obj = secretObjFromSecret(secret)))
365         goto cleanup;
366 
367     def = virSecretObjGetDef(obj);
368     if (virSecretGetValueEnsureACL(secret->conn, def) < 0)
369         goto cleanup;
370 
371     /*
372      * For historical compat we want to deny access to
373      * private secrets, even if no ACL driver is
374      * present.
375      *
376      * We need to validate the identity requesting
377      * the secret value is running as the same user
378      * credentials as this driver.
379      *
380      * ie a non-root libvirt client should not be
381      * able to request the value from privileged
382      * libvirt driver.
383      *
384      * To apply restrictions to processes running under
385      * the same user account is out of scope.
386      */
387     if (def->isprivate) {
388         int rv = virIdentityIsCurrentElevated();
389         if (rv < 0)
390             goto cleanup;
391         if (rv == 0) {
392             virReportError(VIR_ERR_INVALID_SECRET, "%s",
393                            _("secret is private"));
394             goto cleanup;
395         }
396     }
397 
398     if (!(ret = virSecretObjGetValue(obj)))
399         goto cleanup;
400 
401     *value_size = virSecretObjGetValueSize(obj);
402 
403  cleanup:
404     virSecretObjEndAPI(&obj);
405 
406     return ret;
407 }
408 
409 
410 static int
secretUndefine(virSecretPtr secret)411 secretUndefine(virSecretPtr secret)
412 {
413     int ret = -1;
414     virSecretObj *obj;
415     virSecretDef *def;
416     virObjectEvent *event = NULL;
417 
418     if (!(obj = secretObjFromSecret(secret)))
419         goto cleanup;
420 
421     def = virSecretObjGetDef(obj);
422     if (virSecretUndefineEnsureACL(secret->conn, def) < 0)
423         goto cleanup;
424 
425     if (virSecretObjDeleteConfig(obj) < 0)
426         goto cleanup;
427 
428     event = virSecretEventLifecycleNew(def->uuid,
429                                        def->usage_type,
430                                        def->usage_id,
431                                        VIR_SECRET_EVENT_UNDEFINED,
432                                        0);
433 
434     virSecretObjDeleteData(obj);
435 
436     virSecretObjListRemove(driver->secrets, obj);
437     virObjectUnref(obj);
438     obj = NULL;
439 
440     ret = 0;
441 
442  cleanup:
443     virSecretObjEndAPI(&obj);
444     virObjectEventStateQueue(driver->secretEventState, event);
445 
446     return ret;
447 }
448 
449 
450 static int
secretStateCleanup(void)451 secretStateCleanup(void)
452 {
453     if (!driver)
454         return -1;
455 
456     secretDriverLock();
457 
458     virObjectUnref(driver->secrets);
459     VIR_FREE(driver->configDir);
460 
461     virObjectUnref(driver->secretEventState);
462 
463     if (driver->lockFD != -1)
464         virPidFileRelease(driver->stateDir, "driver", driver->lockFD);
465 
466     VIR_FREE(driver->stateDir);
467     secretDriverUnlock();
468     virMutexDestroy(&driver->lock);
469     VIR_FREE(driver);
470 
471     return 0;
472 }
473 
474 
475 static int
secretStateInitialize(bool privileged,const char * root,virStateInhibitCallback callback G_GNUC_UNUSED,void * opaque G_GNUC_UNUSED)476 secretStateInitialize(bool privileged,
477                       const char *root,
478                       virStateInhibitCallback callback G_GNUC_UNUSED,
479                       void *opaque G_GNUC_UNUSED)
480 {
481     driver = g_new0(virSecretDriverState, 1);
482 
483     driver->lockFD = -1;
484     if (virMutexInit(&driver->lock) < 0) {
485         VIR_FREE(driver);
486         return VIR_DRV_STATE_INIT_ERROR;
487     }
488     secretDriverLock();
489 
490     driver->secretEventState = virObjectEventStateNew();
491     driver->privileged = privileged;
492 
493     if (root) {
494         driver->embeddedRoot = g_strdup(root);
495         driver->configDir = g_strdup_printf("%s/etc/secrets", root);
496         driver->stateDir = g_strdup_printf("%s/run/secrets", root);
497     } else if (privileged) {
498         driver->configDir = g_strdup_printf("%s/libvirt/secrets", SYSCONFDIR);
499         driver->stateDir = g_strdup_printf("%s/libvirt/secrets", RUNSTATEDIR);
500     } else {
501         g_autofree char *rundir = NULL;
502         g_autofree char *cfgdir = NULL;
503 
504         cfgdir = virGetUserConfigDirectory();
505         driver->configDir = g_strdup_printf("%s/secrets/", cfgdir);
506 
507         rundir = virGetUserRuntimeDirectory();
508         driver->stateDir = g_strdup_printf("%s/secrets/run", rundir);
509     }
510 
511     if (g_mkdir_with_parents(driver->configDir, S_IRWXU) < 0) {
512         virReportSystemError(errno, _("cannot create config directory '%s'"),
513                              driver->configDir);
514         goto error;
515     }
516 
517     if (g_mkdir_with_parents(driver->stateDir, S_IRWXU) < 0) {
518         virReportSystemError(errno, _("cannot create state directory '%s'"),
519                              driver->stateDir);
520         goto error;
521     }
522 
523     if ((driver->lockFD =
524          virPidFileAcquire(driver->stateDir, "driver", false, getpid())) < 0)
525         goto error;
526 
527     if (!(driver->secrets = virSecretObjListNew()))
528         goto error;
529 
530     if (virSecretLoadAllConfigs(driver->secrets, driver->configDir) < 0)
531         goto error;
532 
533     secretDriverUnlock();
534     return VIR_DRV_STATE_INIT_COMPLETE;
535 
536  error:
537     secretDriverUnlock();
538     secretStateCleanup();
539     return VIR_DRV_STATE_INIT_ERROR;
540 }
541 
542 
543 static int
secretStateReload(void)544 secretStateReload(void)
545 {
546     if (!driver)
547         return -1;
548 
549     secretDriverLock();
550 
551     ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDir));
552 
553     secretDriverUnlock();
554     return 0;
555 }
556 
557 
558 static virDrvOpenStatus
secretConnectOpen(virConnectPtr conn,virConnectAuthPtr auth G_GNUC_UNUSED,virConf * conf G_GNUC_UNUSED,unsigned int flags)559 secretConnectOpen(virConnectPtr conn,
560                   virConnectAuthPtr auth G_GNUC_UNUSED,
561                   virConf *conf G_GNUC_UNUSED,
562                   unsigned int flags)
563 {
564     virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
565 
566     if (driver == NULL) {
567         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
568                        _("secret state driver is not active"));
569         return VIR_DRV_OPEN_ERROR;
570     }
571 
572     if (driver->embeddedRoot) {
573         const char *root = virURIGetParam(conn->uri, "root");
574         if (!root)
575             return VIR_DRV_OPEN_ERROR;
576 
577         if (STRNEQ(conn->uri->path, "/embed")) {
578             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
579                            _("URI must be secret:///embed"));
580             return VIR_DRV_OPEN_ERROR;
581         }
582 
583         if (STRNEQ(root, driver->embeddedRoot)) {
584             virReportError(VIR_ERR_INTERNAL_ERROR,
585                            _("Cannot open embedded driver at path '%s', "
586                              "already open with path '%s'"),
587                            root, driver->embeddedRoot);
588             return VIR_DRV_OPEN_ERROR;
589         }
590     } else {
591         if (!virConnectValidateURIPath(conn->uri->path,
592                                        "secret",
593                                        driver->privileged))
594             return VIR_DRV_OPEN_ERROR;
595     }
596 
597     if (virConnectOpenEnsureACL(conn) < 0)
598         return VIR_DRV_OPEN_ERROR;
599 
600     if (driver->embeddedRoot) {
601         secretDriverLock();
602         if (driver->embeddedRefs == 0)
603             virSetConnectSecret(conn);
604         driver->embeddedRefs++;
605         secretDriverUnlock();
606     }
607 
608     return VIR_DRV_OPEN_SUCCESS;
609 }
610 
secretConnectClose(virConnectPtr conn G_GNUC_UNUSED)611 static int secretConnectClose(virConnectPtr conn G_GNUC_UNUSED)
612 {
613     if (driver->embeddedRoot) {
614         secretDriverLock();
615         driver->embeddedRefs--;
616         if (driver->embeddedRefs == 0)
617             virSetConnectSecret(NULL);
618         secretDriverUnlock();
619     }
620     return 0;
621 }
622 
623 
secretConnectIsSecure(virConnectPtr conn G_GNUC_UNUSED)624 static int secretConnectIsSecure(virConnectPtr conn G_GNUC_UNUSED)
625 {
626     /* Trivially secure, since always inside the daemon */
627     return 1;
628 }
629 
630 
secretConnectIsEncrypted(virConnectPtr conn G_GNUC_UNUSED)631 static int secretConnectIsEncrypted(virConnectPtr conn G_GNUC_UNUSED)
632 {
633     /* Not encrypted, but remote driver takes care of that */
634     return 0;
635 }
636 
637 
secretConnectIsAlive(virConnectPtr conn G_GNUC_UNUSED)638 static int secretConnectIsAlive(virConnectPtr conn G_GNUC_UNUSED)
639 {
640     return 1;
641 }
642 
643 
644 static int
secretConnectSecretEventRegisterAny(virConnectPtr conn,virSecretPtr secret,int eventID,virConnectSecretEventGenericCallback callback,void * opaque,virFreeCallback freecb)645 secretConnectSecretEventRegisterAny(virConnectPtr conn,
646                                     virSecretPtr secret,
647                                     int eventID,
648                                     virConnectSecretEventGenericCallback callback,
649                                     void *opaque,
650                                     virFreeCallback freecb)
651 {
652     int callbackID = -1;
653 
654     if (virConnectSecretEventRegisterAnyEnsureACL(conn) < 0)
655         return -1;
656 
657     if (virSecretEventStateRegisterID(conn, driver->secretEventState,
658                                       secret, eventID, callback,
659                                       opaque, freecb, &callbackID) < 0)
660         callbackID = -1;
661 
662     return callbackID;
663 }
664 
665 
666 static int
secretConnectSecretEventDeregisterAny(virConnectPtr conn,int callbackID)667 secretConnectSecretEventDeregisterAny(virConnectPtr conn,
668                                       int callbackID)
669 {
670     if (virConnectSecretEventDeregisterAnyEnsureACL(conn) < 0)
671         return -1;
672 
673     if (virObjectEventStateDeregisterID(conn,
674                                         driver->secretEventState,
675                                         callbackID, true) < 0)
676         return -1;
677 
678     return 0;
679 }
680 
681 
682 static virSecretDriver secretDriver = {
683     .name = "secret",
684     .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */
685     .connectListSecrets = secretConnectListSecrets, /* 0.7.1 */
686     .connectListAllSecrets = secretConnectListAllSecrets, /* 0.10.2 */
687     .secretLookupByUUID = secretLookupByUUID, /* 0.7.1 */
688     .secretLookupByUsage = secretLookupByUsage, /* 0.7.1 */
689     .secretDefineXML = secretDefineXML, /* 0.7.1 */
690     .secretGetXMLDesc = secretGetXMLDesc, /* 0.7.1 */
691     .secretSetValue = secretSetValue, /* 0.7.1 */
692     .secretGetValue = secretGetValue, /* 0.7.1 */
693     .secretUndefine = secretUndefine, /* 0.7.1 */
694     .connectSecretEventRegisterAny = secretConnectSecretEventRegisterAny, /* 3.0.0 */
695     .connectSecretEventDeregisterAny = secretConnectSecretEventDeregisterAny, /* 3.0.0 */
696 };
697 
698 
699 static virHypervisorDriver secretHypervisorDriver = {
700     .name = "secret",
701     .connectOpen = secretConnectOpen, /* 4.1.0 */
702     .connectClose = secretConnectClose, /* 4.1.0 */
703     .connectIsEncrypted = secretConnectIsEncrypted, /* 4.1.0 */
704     .connectIsSecure = secretConnectIsSecure, /* 4.1.0 */
705     .connectIsAlive = secretConnectIsAlive, /* 4.1.0 */
706 };
707 
708 
709 static virConnectDriver secretConnectDriver = {
710     .localOnly = true,
711     .uriSchemes = (const char *[]){ "secret", NULL },
712     .embeddable = true,
713     .hypervisorDriver = &secretHypervisorDriver,
714     .secretDriver = &secretDriver,
715 };
716 
717 
718 static virStateDriver stateDriver = {
719     .name = "secret",
720     .stateInitialize = secretStateInitialize,
721     .stateCleanup = secretStateCleanup,
722     .stateReload = secretStateReload,
723 };
724 
725 
726 int
secretRegister(void)727 secretRegister(void)
728 {
729     if (virRegisterConnectDriver(&secretConnectDriver, false) < 0)
730         return -1;
731     if (virSetSharedSecretDriver(&secretDriver) < 0)
732         return -1;
733     if (virRegisterStateDriver(&stateDriver) < 0)
734         return -1;
735     return 0;
736 }
737