1 /*
2  * virsecretobj.c: internal <secret> objects handling
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 #include <dirent.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 
26 #include "datatypes.h"
27 #include "virsecretobj.h"
28 #include "viralloc.h"
29 #include "virerror.h"
30 #include "virfile.h"
31 #include "virhash.h"
32 #include "virlog.h"
33 #include "virstring.h"
34 
35 #define VIR_FROM_THIS VIR_FROM_SECRET
36 
37 VIR_LOG_INIT("conf.virsecretobj");
38 
39 struct _virSecretObj {
40     virObjectLockable parent;
41     char *configFile;
42     char *base64File;
43     virSecretDef *def;
44     unsigned char *value;       /* May be NULL */
45     size_t value_size;
46 };
47 
48 static virClass *virSecretObjClass;
49 static virClass *virSecretObjListClass;
50 static void virSecretObjDispose(void *obj);
51 static void virSecretObjListDispose(void *obj);
52 
53 struct _virSecretObjList {
54     virObjectRWLockable parent;
55 
56     /* uuid string -> virSecretObj  mapping
57      * for O(1), lockless lookup-by-uuid */
58     GHashTable *objs;
59 };
60 
61 struct virSecretSearchData {
62     int usageType;
63     const char *usageID;
64 };
65 
66 
67 static int
virSecretObjOnceInit(void)68 virSecretObjOnceInit(void)
69 {
70     if (!VIR_CLASS_NEW(virSecretObj, virClassForObjectLockable()))
71         return -1;
72 
73     if (!VIR_CLASS_NEW(virSecretObjList, virClassForObjectRWLockable()))
74         return -1;
75 
76     return 0;
77 }
78 
79 
80 VIR_ONCE_GLOBAL_INIT(virSecretObj);
81 
82 static virSecretObj *
virSecretObjNew(void)83 virSecretObjNew(void)
84 {
85     virSecretObj *obj;
86 
87     if (virSecretObjInitialize() < 0)
88         return NULL;
89 
90     if (!(obj = virObjectLockableNew(virSecretObjClass)))
91         return NULL;
92 
93     virObjectLock(obj);
94 
95     return obj;
96 }
97 
98 
99 void
virSecretObjEndAPI(virSecretObj ** obj)100 virSecretObjEndAPI(virSecretObj **obj)
101 {
102     if (!*obj)
103         return;
104 
105     virObjectUnlock(*obj);
106     virObjectUnref(*obj);
107     *obj = NULL;
108 }
109 
110 
111 virSecretObjList *
virSecretObjListNew(void)112 virSecretObjListNew(void)
113 {
114     virSecretObjList *secrets;
115 
116     if (virSecretObjInitialize() < 0)
117         return NULL;
118 
119     if (!(secrets = virObjectRWLockableNew(virSecretObjListClass)))
120         return NULL;
121 
122     if (!(secrets->objs = virHashNew(virObjectFreeHashData))) {
123         virObjectUnref(secrets);
124         return NULL;
125     }
126 
127     return secrets;
128 }
129 
130 
131 static void
virSecretObjDispose(void * opaque)132 virSecretObjDispose(void *opaque)
133 {
134     virSecretObj *obj = opaque;
135 
136     virSecretDefFree(obj->def);
137     if (obj->value) {
138         /* Wipe before free to ensure we don't leave a secret on the heap */
139         memset(obj->value, 0, obj->value_size);
140         g_free(obj->value);
141     }
142     g_free(obj->configFile);
143     g_free(obj->base64File);
144 }
145 
146 
147 static void
virSecretObjListDispose(void * obj)148 virSecretObjListDispose(void *obj)
149 {
150     virSecretObjList *secrets = obj;
151 
152     virHashFree(secrets->objs);
153 }
154 
155 
156 /**
157  * virSecretObjFindByUUIDLocked:
158  * @secrets: list of secret objects
159  * @uuid: secret uuid to find
160  *
161  * This functions requires @secrets to be locked already!
162  *
163  * Returns: not locked, but ref'd secret object.
164  */
165 static virSecretObj *
virSecretObjListFindByUUIDLocked(virSecretObjList * secrets,const char * uuidstr)166 virSecretObjListFindByUUIDLocked(virSecretObjList *secrets,
167                                  const char *uuidstr)
168 {
169     return virObjectRef(virHashLookup(secrets->objs, uuidstr));
170 }
171 
172 
173 /**
174  * virSecretObjFindByUUID:
175  * @secrets: list of secret objects
176  * @uuidstr: secret uuid to find
177  *
178  * This function locks @secrets and finds the secret object which
179  * corresponds to @uuid.
180  *
181  * Returns: locked and ref'd secret object.
182  */
183 virSecretObj *
virSecretObjListFindByUUID(virSecretObjList * secrets,const char * uuidstr)184 virSecretObjListFindByUUID(virSecretObjList *secrets,
185                            const char *uuidstr)
186 {
187     virSecretObj *obj;
188 
189     virObjectRWLockRead(secrets);
190     obj = virSecretObjListFindByUUIDLocked(secrets, uuidstr);
191     virObjectRWUnlock(secrets);
192     if (obj)
193         virObjectLock(obj);
194     return obj;
195 }
196 
197 
198 static int
virSecretObjSearchName(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)199 virSecretObjSearchName(const void *payload,
200                        const char *name G_GNUC_UNUSED,
201                        const void *opaque)
202 {
203     virSecretObj *obj = (virSecretObj *) payload;
204     virSecretDef *def;
205     struct virSecretSearchData *data = (struct virSecretSearchData *) opaque;
206     int found = 0;
207 
208     virObjectLock(obj);
209     def = obj->def;
210 
211     if (def->usage_type != data->usageType)
212         goto cleanup;
213 
214     if (data->usageType != VIR_SECRET_USAGE_TYPE_NONE &&
215         STREQ(def->usage_id, data->usageID))
216         found = 1;
217 
218  cleanup:
219     virObjectUnlock(obj);
220     return found;
221 }
222 
223 
224 /**
225  * virSecretObjFindByUsageLocked:
226  * @secrets: list of secret objects
227  * @usageType: secret usageType to find
228  * @usageID: secret usage string
229  *
230  * This functions requires @secrets to be locked already!
231  *
232  * Returns: not locked, but ref'd secret object.
233  */
234 static virSecretObj *
virSecretObjListFindByUsageLocked(virSecretObjList * secrets,int usageType,const char * usageID)235 virSecretObjListFindByUsageLocked(virSecretObjList *secrets,
236                                   int usageType,
237                                   const char *usageID)
238 {
239     virSecretObj *obj = NULL;
240     struct virSecretSearchData data = { .usageType = usageType,
241                                         .usageID = usageID };
242 
243     obj = virHashSearch(secrets->objs, virSecretObjSearchName, &data, NULL);
244     if (obj)
245         virObjectRef(obj);
246     return obj;
247 }
248 
249 
250 /**
251  * virSecretObjFindByUsage:
252  * @secrets: list of secret objects
253  * @usageType: secret usageType to find
254  * @usageID: secret usage string
255  *
256  * This function locks @secrets and finds the secret object which
257  * corresponds to @usageID of @usageType.
258  *
259  * Returns: locked and ref'd secret object.
260  */
261 virSecretObj *
virSecretObjListFindByUsage(virSecretObjList * secrets,int usageType,const char * usageID)262 virSecretObjListFindByUsage(virSecretObjList *secrets,
263                             int usageType,
264                             const char *usageID)
265 {
266     virSecretObj *obj;
267 
268     virObjectRWLockRead(secrets);
269     obj = virSecretObjListFindByUsageLocked(secrets, usageType, usageID);
270     virObjectRWUnlock(secrets);
271     if (obj)
272         virObjectLock(obj);
273     return obj;
274 }
275 
276 
277 /*
278  * virSecretObjListRemove:
279  * @secrets: list of secret objects
280  * @secret: a secret object
281  *
282  * Remove the object from the hash table.  The caller must hold the lock
283  * on the driver owning @secrets and must have also locked @secret to
284  * ensure no one else is either waiting for @secret or still using it.
285  */
286 void
virSecretObjListRemove(virSecretObjList * secrets,virSecretObj * obj)287 virSecretObjListRemove(virSecretObjList *secrets,
288                        virSecretObj *obj)
289 {
290     char uuidstr[VIR_UUID_STRING_BUFLEN];
291     virSecretDef *def;
292 
293     if (!obj)
294         return;
295     def = obj->def;
296 
297     virUUIDFormat(def->uuid, uuidstr);
298     virObjectRef(obj);
299     virObjectUnlock(obj);
300 
301     virObjectRWLockWrite(secrets);
302     virObjectLock(obj);
303     virHashRemoveEntry(secrets->objs, uuidstr);
304     virSecretObjEndAPI(&obj);
305     virObjectRWUnlock(secrets);
306 }
307 
308 
309 /*
310  * virSecretObjListAdd:
311  * @secrets: list of secret objects
312  * @newdef: new secret definition
313  * @configDir: directory to place secret config files
314  * @oldDef: Former secret def (e.g. a reload path perhaps)
315  *
316  * Add the new @newdef to the secret obj table hash. Upon
317  * successful the virSecret object is the owner of @newdef and
318  * callers should use virSecretObjGetDef() if they need to access
319  * the definition as @newdef is set to NULL.
320  *
321  * Returns: locked and ref'd secret or NULL if failure to add
322  */
323 virSecretObj *
virSecretObjListAdd(virSecretObjList * secrets,virSecretDef ** newdef,const char * configDir,virSecretDef ** oldDef)324 virSecretObjListAdd(virSecretObjList *secrets,
325                     virSecretDef **newdef,
326                     const char *configDir,
327                     virSecretDef **oldDef)
328 {
329     virSecretObj *obj;
330     virSecretDef *objdef;
331     virSecretObj *ret = NULL;
332     char uuidstr[VIR_UUID_STRING_BUFLEN];
333 
334     virObjectRWLockWrite(secrets);
335 
336     if (oldDef)
337         *oldDef = NULL;
338 
339     virUUIDFormat((*newdef)->uuid, uuidstr);
340 
341     /* Is there a secret already matching this UUID */
342     if ((obj = virSecretObjListFindByUUIDLocked(secrets, uuidstr))) {
343         virObjectLock(obj);
344         objdef = obj->def;
345 
346         if (STRNEQ_NULLABLE(objdef->usage_id, (*newdef)->usage_id)) {
347             virReportError(VIR_ERR_INTERNAL_ERROR,
348                            _("a secret with UUID %s is already defined for "
349                              "use with %s"),
350                            uuidstr, objdef->usage_id);
351             goto cleanup;
352         }
353 
354         if (objdef->isprivate && !(*newdef)->isprivate) {
355             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
356                            _("cannot change private flag on existing secret"));
357             goto cleanup;
358         }
359 
360         if (oldDef)
361             *oldDef = objdef;
362         else
363             virSecretDefFree(objdef);
364         obj->def = g_steal_pointer(newdef);
365     } else {
366         /* No existing secret with same UUID,
367          * try look for matching usage instead */
368         if ((obj = virSecretObjListFindByUsageLocked(secrets,
369                                                      (*newdef)->usage_type,
370                                                      (*newdef)->usage_id))) {
371             virObjectLock(obj);
372             objdef = obj->def;
373             virUUIDFormat(objdef->uuid, uuidstr);
374             virReportError(VIR_ERR_INTERNAL_ERROR,
375                            _("a secret with UUID %s already defined for "
376                              "use with %s"),
377                            uuidstr, (*newdef)->usage_id);
378             goto cleanup;
379         }
380 
381         if (!(obj = virSecretObjNew()))
382             goto cleanup;
383 
384         /* Generate the possible configFile and base64File strings
385          * using the configDir, uuidstr, and appropriate suffix
386          */
387         if (!(obj->configFile = virFileBuildPath(configDir, uuidstr, ".xml")) ||
388             !(obj->base64File = virFileBuildPath(configDir, uuidstr, ".base64")))
389             goto cleanup;
390 
391         if (virHashAddEntry(secrets->objs, uuidstr, obj) < 0)
392             goto cleanup;
393 
394         obj->def = g_steal_pointer(newdef);
395         virObjectRef(obj);
396     }
397 
398     ret = g_steal_pointer(&obj);
399 
400  cleanup:
401     virSecretObjEndAPI(&obj);
402     virObjectRWUnlock(secrets);
403     return ret;
404 }
405 
406 
407 struct virSecretCountData {
408     virConnectPtr conn;
409     virSecretObjListACLFilter filter;
410     int count;
411 };
412 
413 static int
virSecretObjListNumOfSecretsCallback(void * payload,const char * name G_GNUC_UNUSED,void * opaque)414 virSecretObjListNumOfSecretsCallback(void *payload,
415                                      const char *name G_GNUC_UNUSED,
416                                      void *opaque)
417 {
418     struct virSecretCountData *data = opaque;
419     virSecretObj *obj = payload;
420     virSecretDef *def;
421 
422     virObjectLock(obj);
423     def = obj->def;
424 
425     if (data->filter && !data->filter(data->conn, def))
426         goto cleanup;
427 
428     data->count++;
429 
430  cleanup:
431     virObjectUnlock(obj);
432     return 0;
433 }
434 
435 
436 struct virSecretListData {
437     virConnectPtr conn;
438     virSecretObjListACLFilter filter;
439     int nuuids;
440     char **uuids;
441     int maxuuids;
442     bool error;
443 };
444 
445 
446 static int
virSecretObjListGetUUIDsCallback(void * payload,const char * name G_GNUC_UNUSED,void * opaque)447 virSecretObjListGetUUIDsCallback(void *payload,
448                                  const char *name G_GNUC_UNUSED,
449                                  void *opaque)
450 {
451     struct virSecretListData *data = opaque;
452     virSecretObj *obj = payload;
453     virSecretDef *def;
454 
455     if (data->error)
456         return 0;
457 
458     if (data->maxuuids >= 0 && data->nuuids == data->maxuuids)
459         return 0;
460 
461     virObjectLock(obj);
462     def = obj->def;
463 
464     if (data->filter && !data->filter(data->conn, def))
465         goto cleanup;
466 
467     if (data->uuids) {
468         char *uuidstr;
469 
470         uuidstr = g_new0(char, VIR_UUID_STRING_BUFLEN);
471 
472         virUUIDFormat(def->uuid, uuidstr);
473         data->uuids[data->nuuids++] = uuidstr;
474     }
475 
476  cleanup:
477     virObjectUnlock(obj);
478     return 0;
479 }
480 
481 
482 int
virSecretObjListNumOfSecrets(virSecretObjList * secrets,virSecretObjListACLFilter filter,virConnectPtr conn)483 virSecretObjListNumOfSecrets(virSecretObjList *secrets,
484                              virSecretObjListACLFilter filter,
485                              virConnectPtr conn)
486 {
487     struct virSecretCountData data = {
488         .conn = conn, .filter = filter, .count = 0 };
489 
490     virObjectRWLockRead(secrets);
491     virHashForEach(secrets->objs, virSecretObjListNumOfSecretsCallback, &data);
492     virObjectRWUnlock(secrets);
493 
494     return data.count;
495 }
496 
497 
498 #define MATCH(FLAG) (flags & (FLAG))
499 static bool
virSecretObjMatch(virSecretObj * obj,unsigned int flags)500 virSecretObjMatch(virSecretObj *obj,
501                   unsigned int flags)
502 {
503     virSecretDef *def = obj->def;
504 
505     /* filter by whether it's ephemeral */
506     if (MATCH(VIR_CONNECT_LIST_SECRETS_FILTERS_EPHEMERAL) &&
507         !((MATCH(VIR_CONNECT_LIST_SECRETS_EPHEMERAL) &&
508            def->isephemeral) ||
509           (MATCH(VIR_CONNECT_LIST_SECRETS_NO_EPHEMERAL) &&
510            !def->isephemeral)))
511         return false;
512 
513     /* filter by whether it's private */
514     if (MATCH(VIR_CONNECT_LIST_SECRETS_FILTERS_PRIVATE) &&
515         !((MATCH(VIR_CONNECT_LIST_SECRETS_PRIVATE) &&
516            def->isprivate) ||
517           (MATCH(VIR_CONNECT_LIST_SECRETS_NO_PRIVATE) &&
518            !def->isprivate)))
519         return false;
520 
521     return true;
522 }
523 #undef MATCH
524 
525 
526 typedef struct _virSecretObjListExportData virSecretObjListExportData;
527 struct _virSecretObjListExportData {
528     virConnectPtr conn;
529     virSecretPtr *secrets;
530     virSecretObjListACLFilter filter;
531     unsigned int flags;
532     int nsecrets;
533     bool error;
534 };
535 
536 static int
virSecretObjListExportCallback(void * payload,const char * name G_GNUC_UNUSED,void * opaque)537 virSecretObjListExportCallback(void *payload,
538                                const char *name G_GNUC_UNUSED,
539                                void *opaque)
540 {
541     virSecretObjListExportData *data = opaque;
542     virSecretObj *obj = payload;
543     virSecretDef *def;
544     virSecretPtr secret = NULL;
545 
546     if (data->error)
547         return 0;
548 
549     virObjectLock(obj);
550     def = obj->def;
551 
552     if (data->filter && !data->filter(data->conn, def))
553         goto cleanup;
554 
555     if (!virSecretObjMatch(obj, data->flags))
556         goto cleanup;
557 
558     if (!data->secrets) {
559         data->nsecrets++;
560         goto cleanup;
561     }
562 
563     if (!(secret = virGetSecret(data->conn, def->uuid,
564                                 def->usage_type,
565                                 def->usage_id))) {
566         data->error = true;
567         goto cleanup;
568     }
569 
570     data->secrets[data->nsecrets++] = secret;
571 
572  cleanup:
573     virObjectUnlock(obj);
574     return 0;
575 }
576 
577 
578 int
virSecretObjListExport(virConnectPtr conn,virSecretObjList * secretobjs,virSecretPtr ** secrets,virSecretObjListACLFilter filter,unsigned int flags)579 virSecretObjListExport(virConnectPtr conn,
580                        virSecretObjList *secretobjs,
581                        virSecretPtr **secrets,
582                        virSecretObjListACLFilter filter,
583                        unsigned int flags)
584 {
585     virSecretObjListExportData data = {
586         .conn = conn, .secrets = NULL,
587         .filter = filter, .flags = flags,
588         .nsecrets = 0, .error = false };
589 
590     virObjectRWLockRead(secretobjs);
591     if (secrets)
592         data.secrets = g_new0(virSecretPtr, virHashSize(secretobjs->objs) + 1);
593 
594     virHashForEach(secretobjs->objs, virSecretObjListExportCallback, &data);
595     virObjectRWUnlock(secretobjs);
596 
597     if (data.error)
598         goto error;
599 
600     if (data.secrets) {
601         /* trim the array to the final size */
602         VIR_REALLOC_N(data.secrets, data.nsecrets + 1);
603         *secrets = data.secrets;
604     }
605 
606     return data.nsecrets;
607 
608  error:
609     virObjectListFree(data.secrets);
610     return -1;
611 }
612 
613 
614 int
virSecretObjListGetUUIDs(virSecretObjList * secrets,char ** uuids,int maxuuids,virSecretObjListACLFilter filter,virConnectPtr conn)615 virSecretObjListGetUUIDs(virSecretObjList *secrets,
616                          char **uuids,
617                          int maxuuids,
618                          virSecretObjListACLFilter filter,
619                          virConnectPtr conn)
620 {
621     struct virSecretListData data = {
622         .conn = conn, .filter = filter, .uuids = uuids, .nuuids = 0,
623         .maxuuids = maxuuids, .error = false };
624 
625     virObjectRWLockRead(secrets);
626     virHashForEach(secrets->objs, virSecretObjListGetUUIDsCallback, &data);
627     virObjectRWUnlock(secrets);
628 
629     if (data.error)
630         goto error;
631 
632     return data.nuuids;
633 
634  error:
635     while (--data.nuuids)
636         VIR_FREE(data.uuids[data.nuuids]);
637     return -1;
638 }
639 
640 
641 int
virSecretObjDeleteConfig(virSecretObj * obj)642 virSecretObjDeleteConfig(virSecretObj *obj)
643 {
644     virSecretDef *def = obj->def;
645 
646     if (!def->isephemeral &&
647         unlink(obj->configFile) < 0 && errno != ENOENT) {
648         virReportSystemError(errno, _("cannot unlink '%s'"),
649                              obj->configFile);
650         return -1;
651     }
652 
653     return 0;
654 }
655 
656 
657 void
virSecretObjDeleteData(virSecretObj * obj)658 virSecretObjDeleteData(virSecretObj *obj)
659 {
660     /* The configFile will already be removed, so secret won't be
661      * loaded again if this fails */
662     unlink(obj->base64File);
663 }
664 
665 
666 /* Permanent secret storage */
667 
668 /* Secrets are stored in virSecretDriverState *->configDir.  Each secret
669    has virSecretDef stored as XML in "$basename.xml".  If a value of the
670    secret is defined, it is stored as base64 (with no formatting) in
671    "$basename.base64".  "$basename" is in both cases the base64-encoded UUID. */
672 int
virSecretObjSaveConfig(virSecretObj * obj)673 virSecretObjSaveConfig(virSecretObj *obj)
674 {
675     g_autofree char *xml = NULL;
676 
677     if (!(xml = virSecretDefFormat(obj->def)))
678         return -1;
679 
680     if (virFileRewriteStr(obj->configFile, S_IRUSR | S_IWUSR, xml) < 0)
681         return -1;
682 
683     return 0;
684 }
685 
686 
687 int
virSecretObjSaveData(virSecretObj * obj)688 virSecretObjSaveData(virSecretObj *obj)
689 {
690     g_autofree char *base64 = NULL;
691 
692     if (!obj->value)
693         return 0;
694 
695     base64 = g_base64_encode(obj->value, obj->value_size);
696 
697     if (virFileRewriteStr(obj->base64File, S_IRUSR | S_IWUSR, base64) < 0)
698         return -1;
699 
700     return 0;
701 }
702 
703 
704 virSecretDef *
virSecretObjGetDef(virSecretObj * obj)705 virSecretObjGetDef(virSecretObj *obj)
706 {
707     return obj->def;
708 }
709 
710 
711 void
virSecretObjSetDef(virSecretObj * obj,virSecretDef * def)712 virSecretObjSetDef(virSecretObj *obj,
713                    virSecretDef *def)
714 {
715     obj->def = def;
716 }
717 
718 
719 unsigned char *
virSecretObjGetValue(virSecretObj * obj)720 virSecretObjGetValue(virSecretObj *obj)
721 {
722     virSecretDef *def = obj->def;
723     unsigned char *ret = NULL;
724 
725     if (!obj->value) {
726         char uuidstr[VIR_UUID_STRING_BUFLEN];
727         virUUIDFormat(def->uuid, uuidstr);
728         virReportError(VIR_ERR_NO_SECRET,
729                        _("secret '%s' does not have a value"), uuidstr);
730         return NULL;
731     }
732 
733     ret = g_new0(unsigned char, obj->value_size);
734     memcpy(ret, obj->value, obj->value_size);
735 
736     return ret;
737 }
738 
739 
740 int
virSecretObjSetValue(virSecretObj * obj,const unsigned char * value,size_t value_size)741 virSecretObjSetValue(virSecretObj *obj,
742                      const unsigned char *value,
743                      size_t value_size)
744 {
745     virSecretDef *def = obj->def;
746     g_autofree unsigned char *old_value = NULL;
747     g_autofree unsigned char *new_value = NULL;
748     size_t old_value_size;
749 
750     new_value = g_new0(unsigned char, value_size);
751 
752     old_value = obj->value;
753     old_value_size = obj->value_size;
754 
755     memcpy(new_value, value, value_size);
756     obj->value = g_steal_pointer(&new_value);
757     obj->value_size = value_size;
758 
759     if (!def->isephemeral && virSecretObjSaveData(obj) < 0)
760         goto error;
761 
762     /* Saved successfully - drop old value */
763     if (old_value)
764         memset(old_value, 0, old_value_size);
765 
766     return 0;
767 
768  error:
769     /* Error - restore previous state and free new value */
770     new_value = g_steal_pointer(&obj->value);
771     obj->value = g_steal_pointer(&old_value);
772     obj->value_size = old_value_size;
773     memset(new_value, 0, value_size);
774     return -1;
775 }
776 
777 
778 size_t
virSecretObjGetValueSize(virSecretObj * obj)779 virSecretObjGetValueSize(virSecretObj *obj)
780 {
781     return obj->value_size;
782 }
783 
784 
785 void
virSecretObjSetValueSize(virSecretObj * obj,size_t value_size)786 virSecretObjSetValueSize(virSecretObj *obj,
787                          size_t value_size)
788 {
789     obj->value_size = value_size;
790 }
791 
792 
793 static int
virSecretLoadValidateUUID(virSecretDef * def,const char * file)794 virSecretLoadValidateUUID(virSecretDef *def,
795                           const char *file)
796 {
797     char uuidstr[VIR_UUID_STRING_BUFLEN];
798 
799     virUUIDFormat(def->uuid, uuidstr);
800 
801     if (!virStringMatchesNameSuffix(file, uuidstr, ".xml")) {
802         virReportError(VIR_ERR_INTERNAL_ERROR,
803                        _("<uuid> does not match secret file name '%s'"),
804                        file);
805         return -1;
806     }
807 
808     return 0;
809 }
810 
811 
812 static int
virSecretLoadValue(virSecretObj * obj)813 virSecretLoadValue(virSecretObj *obj)
814 {
815     int ret = -1, fd = -1;
816     struct stat st;
817     g_autofree char *contents = NULL;
818 
819     if ((fd = open(obj->base64File, O_RDONLY)) == -1) {
820         if (errno == ENOENT) {
821             ret = 0;
822             goto cleanup;
823         }
824         virReportSystemError(errno, _("cannot open '%s'"),
825                              obj->base64File);
826         goto cleanup;
827     }
828 
829     if (fstat(fd, &st) < 0) {
830         virReportSystemError(errno, _("cannot stat '%s'"),
831                              obj->base64File);
832         goto cleanup;
833     }
834 
835     if ((size_t)st.st_size != st.st_size) {
836         virReportError(VIR_ERR_INTERNAL_ERROR,
837                        _("'%s' file does not fit in memory"),
838                        obj->base64File);
839         goto cleanup;
840     }
841 
842     contents = g_new0(char, st.st_size + 1);
843 
844     if (saferead(fd, contents, st.st_size) != st.st_size) {
845         virReportSystemError(errno, _("cannot read '%s'"),
846                              obj->base64File);
847         goto cleanup;
848     }
849     contents[st.st_size] = '\0';
850 
851     VIR_FORCE_CLOSE(fd);
852 
853     obj->value = g_base64_decode(contents, &obj->value_size);
854 
855     ret = 0;
856 
857  cleanup:
858     if (contents != NULL)
859         memset(contents, 0, st.st_size);
860     VIR_FORCE_CLOSE(fd);
861     return ret;
862 }
863 
864 
865 static virSecretObj *
virSecretLoad(virSecretObjList * secrets,const char * file,const char * path,const char * configDir)866 virSecretLoad(virSecretObjList *secrets,
867               const char *file,
868               const char *path,
869               const char *configDir)
870 {
871     virSecretDef *def = NULL;
872     virSecretObj *obj = NULL;
873 
874     if (!(def = virSecretDefParseFile(path)))
875         goto cleanup;
876 
877     if (virSecretLoadValidateUUID(def, file) < 0)
878         goto cleanup;
879 
880     if (!(obj = virSecretObjListAdd(secrets, &def, configDir, NULL)))
881         goto cleanup;
882 
883     if (virSecretLoadValue(obj) < 0) {
884         virSecretObjListRemove(secrets, obj);
885         virObjectUnref(obj);
886         obj = NULL;
887     }
888 
889  cleanup:
890     virSecretDefFree(def);
891     return obj;
892 }
893 
894 
895 int
virSecretLoadAllConfigs(virSecretObjList * secrets,const char * configDir)896 virSecretLoadAllConfigs(virSecretObjList *secrets,
897                         const char *configDir)
898 {
899     g_autoptr(DIR) dir = NULL;
900     struct dirent *de;
901     int rc;
902 
903     if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
904         return rc;
905 
906     /* Ignore errors reported by readdir or other calls within the
907      * loop (if any).  It's better to keep the secrets we managed to find. */
908     while (virDirRead(dir, &de, NULL) > 0) {
909         char *path;
910         virSecretObj *obj;
911 
912         if (!virStringHasSuffix(de->d_name, ".xml"))
913             continue;
914 
915         if (!(path = virFileBuildPath(configDir, de->d_name, NULL)))
916             continue;
917 
918         if (!(obj = virSecretLoad(secrets, de->d_name, path, configDir))) {
919             VIR_ERROR(_("Error reading secret: %s"),
920                       virGetLastErrorMessage());
921             VIR_FREE(path);
922             continue;
923         }
924 
925         VIR_FREE(path);
926         virSecretObjEndAPI(&obj);
927     }
928 
929     return 0;
930 }
931