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