xref: /qemu/crypto/secret.c (revision 7a4e543d)
1 /*
2  * QEMU crypto secret support
3  *
4  * Copyright (c) 2015 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 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 <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "qemu/osdep.h"
22 #include "crypto/secret.h"
23 #include "crypto/cipher.h"
24 #include "qom/object_interfaces.h"
25 #include "qemu/base64.h"
26 #include "trace.h"
27 
28 
29 static void
30 qcrypto_secret_load_data(QCryptoSecret *secret,
31                          uint8_t **output,
32                          size_t *outputlen,
33                          Error **errp)
34 {
35     char *data = NULL;
36     size_t length = 0;
37     GError *gerr = NULL;
38 
39     *output = NULL;
40     *outputlen = 0;
41 
42     if (secret->file) {
43         if (secret->data) {
44             error_setg(errp,
45                        "'file' and 'data' are mutually exclusive");
46             return;
47         }
48         if (!g_file_get_contents(secret->file, &data, &length, &gerr)) {
49             error_setg(errp,
50                        "Unable to read %s: %s",
51                        secret->file, gerr->message);
52             g_error_free(gerr);
53             return;
54         }
55         *output = (uint8_t *)data;
56         *outputlen = length;
57     } else if (secret->data) {
58         *outputlen = strlen(secret->data);
59         *output = (uint8_t *)g_strdup(secret->data);
60     } else {
61         error_setg(errp, "Either 'file' or 'data' must be provided");
62     }
63 }
64 
65 
66 static void qcrypto_secret_decrypt(QCryptoSecret *secret,
67                                    const uint8_t *input,
68                                    size_t inputlen,
69                                    uint8_t **output,
70                                    size_t *outputlen,
71                                    Error **errp)
72 {
73     uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
74     size_t keylen, ciphertextlen, ivlen;
75     QCryptoCipher *aes = NULL;
76     uint8_t *plaintext = NULL;
77 
78     *output = NULL;
79     *outputlen = 0;
80 
81     if (qcrypto_secret_lookup(secret->keyid,
82                               &key, &keylen,
83                               errp) < 0) {
84         goto cleanup;
85     }
86 
87     if (keylen != 32) {
88         error_setg(errp, "Key should be 32 bytes in length");
89         goto cleanup;
90     }
91 
92     if (!secret->iv) {
93         error_setg(errp, "IV is required to decrypt secret");
94         goto cleanup;
95     }
96 
97     iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
98     if (!iv) {
99         goto cleanup;
100     }
101     if (ivlen != 16) {
102         error_setg(errp, "IV should be 16 bytes in length not %zu",
103                    ivlen);
104         goto cleanup;
105     }
106 
107     aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
108                              QCRYPTO_CIPHER_MODE_CBC,
109                              key, keylen,
110                              errp);
111     if (!aes) {
112         goto cleanup;
113     }
114 
115     if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
116         goto cleanup;
117     }
118 
119     if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
120         ciphertext = qbase64_decode((const gchar*)input,
121                                     inputlen,
122                                     &ciphertextlen,
123                                     errp);
124         if (!ciphertext) {
125             goto cleanup;
126         }
127         plaintext = g_new0(uint8_t, ciphertextlen + 1);
128     } else {
129         ciphertextlen = inputlen;
130         plaintext = g_new0(uint8_t, inputlen + 1);
131     }
132     if (qcrypto_cipher_decrypt(aes,
133                                ciphertext ? ciphertext : input,
134                                plaintext,
135                                ciphertextlen,
136                                errp) < 0) {
137         plaintext = NULL;
138         goto cleanup;
139     }
140 
141     if (plaintext[ciphertextlen - 1] > 16 ||
142         plaintext[ciphertextlen - 1] > ciphertextlen) {
143         error_setg(errp, "Incorrect number of padding bytes (%d) "
144                    "found on decrypted data",
145                    (int)plaintext[ciphertextlen - 1]);
146         g_free(plaintext);
147         plaintext = NULL;
148         goto cleanup;
149     }
150 
151     /* Even though plaintext may contain arbitrary NUL
152      * ensure it is explicitly NUL terminated.
153      */
154     ciphertextlen -= plaintext[ciphertextlen - 1];
155     plaintext[ciphertextlen] = '\0';
156 
157     *output = plaintext;
158     *outputlen = ciphertextlen;
159 
160  cleanup:
161     g_free(ciphertext);
162     g_free(iv);
163     g_free(key);
164     qcrypto_cipher_free(aes);
165 }
166 
167 
168 static void qcrypto_secret_decode(const uint8_t *input,
169                                   size_t inputlen,
170                                   uint8_t **output,
171                                   size_t *outputlen,
172                                   Error **errp)
173 {
174     *output = qbase64_decode((const gchar*)input,
175                              inputlen,
176                              outputlen,
177                              errp);
178 }
179 
180 
181 static void
182 qcrypto_secret_prop_set_loaded(Object *obj,
183                                bool value,
184                                Error **errp)
185 {
186     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
187 
188     if (value) {
189         Error *local_err = NULL;
190         uint8_t *input = NULL;
191         size_t inputlen = 0;
192         uint8_t *output = NULL;
193         size_t outputlen = 0;
194 
195         qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
196         if (local_err) {
197             error_propagate(errp, local_err);
198             return;
199         }
200 
201         if (secret->keyid) {
202             qcrypto_secret_decrypt(secret, input, inputlen,
203                                    &output, &outputlen, &local_err);
204             g_free(input);
205             if (local_err) {
206                 error_propagate(errp, local_err);
207                 return;
208             }
209             input = output;
210             inputlen = outputlen;
211         } else {
212             if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
213                 qcrypto_secret_decode(input, inputlen,
214                                       &output, &outputlen, &local_err);
215                 g_free(input);
216                 if (local_err) {
217                     error_propagate(errp, local_err);
218                     return;
219                 }
220                 input = output;
221                 inputlen = outputlen;
222             }
223         }
224 
225         secret->rawdata = input;
226         secret->rawlen = inputlen;
227     } else {
228         g_free(secret->rawdata);
229         secret->rawlen = 0;
230     }
231 }
232 
233 
234 static bool
235 qcrypto_secret_prop_get_loaded(Object *obj,
236                                Error **errp G_GNUC_UNUSED)
237 {
238     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
239     return secret->data != NULL;
240 }
241 
242 
243 static void
244 qcrypto_secret_prop_set_format(Object *obj,
245                                int value,
246                                Error **errp G_GNUC_UNUSED)
247 {
248     QCryptoSecret *creds = QCRYPTO_SECRET(obj);
249 
250     creds->format = value;
251 }
252 
253 
254 static int
255 qcrypto_secret_prop_get_format(Object *obj,
256                                Error **errp G_GNUC_UNUSED)
257 {
258     QCryptoSecret *creds = QCRYPTO_SECRET(obj);
259 
260     return creds->format;
261 }
262 
263 
264 static void
265 qcrypto_secret_prop_set_data(Object *obj,
266                              const char *value,
267                              Error **errp)
268 {
269     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
270 
271     g_free(secret->data);
272     secret->data = g_strdup(value);
273 }
274 
275 
276 static char *
277 qcrypto_secret_prop_get_data(Object *obj,
278                              Error **errp)
279 {
280     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
281     return g_strdup(secret->data);
282 }
283 
284 
285 static void
286 qcrypto_secret_prop_set_file(Object *obj,
287                              const char *value,
288                              Error **errp)
289 {
290     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
291 
292     g_free(secret->file);
293     secret->file = g_strdup(value);
294 }
295 
296 
297 static char *
298 qcrypto_secret_prop_get_file(Object *obj,
299                              Error **errp)
300 {
301     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
302     return g_strdup(secret->file);
303 }
304 
305 
306 static void
307 qcrypto_secret_prop_set_iv(Object *obj,
308                            const char *value,
309                            Error **errp)
310 {
311     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
312 
313     g_free(secret->iv);
314     secret->iv = g_strdup(value);
315 }
316 
317 
318 static char *
319 qcrypto_secret_prop_get_iv(Object *obj,
320                            Error **errp)
321 {
322     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
323     return g_strdup(secret->iv);
324 }
325 
326 
327 static void
328 qcrypto_secret_prop_set_keyid(Object *obj,
329                               const char *value,
330                               Error **errp)
331 {
332     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
333 
334     g_free(secret->keyid);
335     secret->keyid = g_strdup(value);
336 }
337 
338 
339 static char *
340 qcrypto_secret_prop_get_keyid(Object *obj,
341                               Error **errp)
342 {
343     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
344     return g_strdup(secret->keyid);
345 }
346 
347 
348 static void
349 qcrypto_secret_complete(UserCreatable *uc, Error **errp)
350 {
351     object_property_set_bool(OBJECT(uc), true, "loaded", errp);
352 }
353 
354 
355 static void
356 qcrypto_secret_finalize(Object *obj)
357 {
358     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
359 
360     g_free(secret->iv);
361     g_free(secret->file);
362     g_free(secret->keyid);
363     g_free(secret->rawdata);
364     g_free(secret->data);
365 }
366 
367 static void
368 qcrypto_secret_class_init(ObjectClass *oc, void *data)
369 {
370     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
371 
372     ucc->complete = qcrypto_secret_complete;
373 
374     object_class_property_add_bool(oc, "loaded",
375                                    qcrypto_secret_prop_get_loaded,
376                                    qcrypto_secret_prop_set_loaded,
377                                    NULL);
378     object_class_property_add_enum(oc, "format",
379                                    "QCryptoSecretFormat",
380                                    QCryptoSecretFormat_lookup,
381                                    qcrypto_secret_prop_get_format,
382                                    qcrypto_secret_prop_set_format,
383                                    NULL);
384     object_class_property_add_str(oc, "data",
385                                   qcrypto_secret_prop_get_data,
386                                   qcrypto_secret_prop_set_data,
387                                   NULL);
388     object_class_property_add_str(oc, "file",
389                                   qcrypto_secret_prop_get_file,
390                                   qcrypto_secret_prop_set_file,
391                                   NULL);
392     object_class_property_add_str(oc, "keyid",
393                                   qcrypto_secret_prop_get_keyid,
394                                   qcrypto_secret_prop_set_keyid,
395                                   NULL);
396     object_class_property_add_str(oc, "iv",
397                                   qcrypto_secret_prop_get_iv,
398                                   qcrypto_secret_prop_set_iv,
399                                   NULL);
400 }
401 
402 
403 int qcrypto_secret_lookup(const char *secretid,
404                           uint8_t **data,
405                           size_t *datalen,
406                           Error **errp)
407 {
408     Object *obj;
409     QCryptoSecret *secret;
410 
411     obj = object_resolve_path_component(
412         object_get_objects_root(), secretid);
413     if (!obj) {
414         error_setg(errp, "No secret with id '%s'", secretid);
415         return -1;
416     }
417 
418     secret = (QCryptoSecret *)
419         object_dynamic_cast(obj,
420                             TYPE_QCRYPTO_SECRET);
421     if (!secret) {
422         error_setg(errp, "Object with id '%s' is not a secret",
423                    secretid);
424         return -1;
425     }
426 
427     if (!secret->rawdata) {
428         error_setg(errp, "Secret with id '%s' has no data",
429                    secretid);
430         return -1;
431     }
432 
433     *data = g_new0(uint8_t, secret->rawlen + 1);
434     memcpy(*data, secret->rawdata, secret->rawlen);
435     (*data)[secret->rawlen] = '\0';
436     *datalen = secret->rawlen;
437 
438     return 0;
439 }
440 
441 
442 char *qcrypto_secret_lookup_as_utf8(const char *secretid,
443                                     Error **errp)
444 {
445     uint8_t *data;
446     size_t datalen;
447 
448     if (qcrypto_secret_lookup(secretid,
449                               &data,
450                               &datalen,
451                               errp) < 0) {
452         return NULL;
453     }
454 
455     if (!g_utf8_validate((const gchar*)data, datalen, NULL)) {
456         error_setg(errp,
457                    "Data from secret %s is not valid UTF-8",
458                    secretid);
459         g_free(data);
460         return NULL;
461     }
462 
463     return (char *)data;
464 }
465 
466 
467 char *qcrypto_secret_lookup_as_base64(const char *secretid,
468                                       Error **errp)
469 {
470     uint8_t *data;
471     size_t datalen;
472     char *ret;
473 
474     if (qcrypto_secret_lookup(secretid,
475                               &data,
476                               &datalen,
477                               errp) < 0) {
478         return NULL;
479     }
480 
481     ret = g_base64_encode(data, datalen);
482     g_free(data);
483     return ret;
484 }
485 
486 
487 static const TypeInfo qcrypto_secret_info = {
488     .parent = TYPE_OBJECT,
489     .name = TYPE_QCRYPTO_SECRET,
490     .instance_size = sizeof(QCryptoSecret),
491     .instance_finalize = qcrypto_secret_finalize,
492     .class_size = sizeof(QCryptoSecretClass),
493     .class_init = qcrypto_secret_class_init,
494     .interfaces = (InterfaceInfo[]) {
495         { TYPE_USER_CREATABLE },
496         { }
497     }
498 };
499 
500 
501 static void
502 qcrypto_secret_register_types(void)
503 {
504     type_register_static(&qcrypto_secret_info);
505 }
506 
507 
508 type_init(qcrypto_secret_register_types);
509