Greenbone Vulnerability Management Libraries 22.32.0
gpgmeutils.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2023 Greenbone AG
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
10
11#include "gpgmeutils.h"
12
13#include "fileutils.h"
14
15#include <errno.h> /* for ENOENT, errno */
16#include <gpg-error.h> /* for gpg_err_source, gpg_strerror, gpg_error_from... */
17#include <locale.h> /* for setlocale, LC_MESSAGES, LC_CTYPE */
18#include <stdlib.h> /* for mkdtemp */
19#include <string.h> /* for strlen */
20#include <sys/stat.h> /* for mkdir */
21#include <unistd.h> /* for access, F_OK */
22
23#undef G_LOG_DOMAIN
27#define G_LOG_DOMAIN "libgvm util"
28
42void
43log_gpgme (GLogLevelFlags level, gpg_error_t err, const char *fmt, ...)
44{
45 va_list arg_ptr;
46 char *msg;
47
48 va_start (arg_ptr, fmt);
49 msg = g_strdup_vprintf (fmt, arg_ptr);
50 va_end (arg_ptr);
51 if (err && gpg_err_source (err) != GPG_ERR_SOURCE_ANY && gpg_err_source (err))
52 g_log (G_LOG_DOMAIN, level, "%s: %s <%s>", msg, gpg_strerror (err),
53 gpg_strsource (err));
54 else if (err)
55 g_log (G_LOG_DOMAIN, level, "%s: %s", msg, gpg_strerror (err));
56 else
57 g_log (G_LOG_DOMAIN, level, "%s", msg);
58 g_free (msg);
59}
60
73gpgme_ctx_t
75{
76 static int initialized;
77 gpgme_error_t err;
78 gpgme_ctx_t ctx;
79
80 /* Initialize GPGME the first time we are called. This is a
81 failsafe mode; it would be better to initialize GPGME early at
82 process startup instead of this on-the-fly method; however in
83 this non-threaded system; this is an easier way for a library.
84 We allow to initialize until a valid gpgme or a gpg backend has
85 been found. */
86 if (!initialized)
87 {
88 gpgme_engine_info_t info;
89
90 if (!gpgme_check_version (NULL))
91 {
92 g_critical ("gpgme library could not be initialized.");
93 return NULL;
94 }
95 gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
96#ifdef LC_MESSAGES
97 gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
98#endif
99
100#ifndef NDEBUG
101 g_message ("Setting GnuPG dir to '%s'", dir);
102#endif
103 err = 0;
104 if (access (dir, F_OK))
105 {
106 err = gpg_error_from_syserror ();
107
108 if (errno == ENOENT)
109 /* directory does not exists. try to create it */
110 if (mkdir (dir, 0700) == 0)
111 {
112#ifndef NDEBUG
113 g_message ("Created GnuPG dir '%s'", dir);
114#endif
115 err = 0;
116 }
117 }
118
119 if (!err)
120 err = gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, NULL, dir);
121
122 if (err)
123 {
124 log_gpgme (G_LOG_LEVEL_WARNING, err, "Setting GnuPG dir failed");
125 return NULL;
126 }
127
128 /* Show the OpenPGP engine version. */
129 if (!gpgme_get_engine_info (&info))
130 {
131 while (info && info->protocol != GPGME_PROTOCOL_OpenPGP)
132 info = info->next;
133 }
134 else
135 info = NULL;
136#ifndef NDEBUG
137 g_message ("Using OpenPGP engine version '%s'",
138 info && info->version ? info->version : "[?]");
139#endif
140
141 /* Everything is fine. */
142 initialized = 1;
143 }
144
145 /* Allocate the context. */
146 ctx = NULL;
147 err = gpgme_new (&ctx);
148 if (err)
149 log_gpgme (G_LOG_LEVEL_WARNING, err, "Creating GPGME context failed");
150
151 return ctx;
152}
153
165int
166gvm_gpg_import_many_types_from_string (gpgme_ctx_t ctx, const char *key_str,
167 ssize_t key_len, GArray *key_types)
168{
169 gpgme_data_t key_data;
170 gpgme_error_t err;
171 gpgme_data_type_t given_key_type;
172 gpgme_import_result_t import_result;
173 int ret;
174
175 gpgme_data_new_from_mem (
176 &key_data, key_str, (key_len >= 0 ? key_len : (ssize_t) strlen (key_str)),
177 0);
178
179 given_key_type = gpgme_data_identify (key_data, 0);
180 ret = 0;
181 if (given_key_type == GPGME_DATA_TYPE_INVALID)
182 {
183 ret = 1;
184 g_warning ("%s: key_str is invalid", __func__);
185 }
186 else
187 {
188 unsigned int index;
189 for (index = 0; index < key_types->len; index++)
190 {
191 if (g_array_index (key_types, gpgme_data_type_t, index)
192 == given_key_type)
193 break;
194 }
195
196 if (index >= key_types->len)
197 {
198 ret = 2;
199 GString *expected_buffer = g_string_new ("");
200 for (index = 0; index < key_types->len; index++)
201 {
202 if (index)
203 g_string_append (expected_buffer, " or ");
204 g_string_append_printf (
205 expected_buffer, "%d",
206 g_array_index (key_types, gpgme_data_type_t, index));
207 }
208 g_warning ("%s: key_str is not the expected type: "
209 " expected: %s, got %d",
210 __func__, expected_buffer->str, given_key_type);
211 g_string_free (expected_buffer, TRUE);
212 }
213 }
214
215 if (ret)
216 {
217 gpgme_data_release (key_data);
218 return ret;
219 }
220
221 err = gpgme_op_import (ctx, key_data);
222 gpgme_data_release (key_data);
223 if (err)
224 {
225 g_warning ("%s: Import failed: %s", __func__, gpgme_strerror (err));
226 return 3;
227 }
228
229 import_result = gpgme_op_import_result (ctx);
230 g_debug ("%s: %d imported, %d not imported", __func__,
231 import_result->imported, import_result->not_imported);
232
233 gpgme_import_status_t status;
234 status = import_result->imports;
235 while (status)
236 {
237 if (status->result != GPG_ERR_NO_ERROR)
238 g_warning ("%s: '%s' could not be imported: %s", __func__, status->fpr,
239 gpgme_strerror (status->result));
240 else
241 g_debug ("%s: Imported '%s'", __func__, status->fpr);
242
243 status = status->next;
244 };
245
246 if (import_result->not_imported)
247 return 3;
248
249 return 0;
250}
251
263int
264gvm_gpg_import_from_string (gpgme_ctx_t ctx, const char *key_str,
265 ssize_t key_len, gpgme_data_type_t key_type)
266{
267 int ret;
268 GArray *key_types =
269 g_array_sized_new (FALSE, FALSE, sizeof (gpgme_data_type_t), 1);
270 g_array_insert_val (key_types, 0, key_type);
271 ret =
272 gvm_gpg_import_many_types_from_string (ctx, key_str, key_len, key_types);
273 g_array_free (key_types, TRUE);
274 return ret;
275}
276
285static gpgme_key_t
286find_email_encryption_key (gpgme_ctx_t ctx, const char *uid_email)
287{
288 gchar *bracket_email;
289 gpgme_key_t key, found_key;
290 gpgme_error_t err;
291
292 if (uid_email == NULL)
293 return NULL;
294
295 bracket_email = g_strdup_printf ("<%s>", uid_email);
296
297 err = gpgme_op_keylist_start (ctx, NULL, 0);
298 if (err)
299 {
300 g_free (bracket_email);
301 g_warning ("gpgme_op_keylist_start failed: %s", gpgme_strerror (err));
302 return NULL;
303 }
304
305 gpgme_op_keylist_next (ctx, &key);
306 if (err)
307 {
308 g_free (bracket_email);
309 g_warning ("gpgme_op_keylist_next failed: %s", gpgme_strerror (err));
310 return NULL;
311 }
312
313 found_key = NULL;
314 while (key)
315 {
316 if (key->can_encrypt)
317 {
318 g_debug ("%s: key '%s' OK for encryption", __func__,
319 key->subkeys->fpr);
320
321 gpgme_user_id_t uid;
322 uid = key->uids;
323 while (uid && found_key == NULL)
324 {
325 g_debug ("%s: UID email: %s", __func__, uid->email);
326
327 if (strcmp (uid->email, uid_email) == 0
328 || strstr (uid->email, bracket_email))
329 {
330 g_message ("%s: Found matching UID for %s", __func__,
331 uid_email);
332 found_key = key;
333 }
334 uid = uid->next;
335 }
336 }
337 else
338 {
339 g_debug ("%s: key '%s' cannot be used for encryption", __func__,
340 key->subkeys->fpr);
341 }
342
343 err = gpgme_op_keylist_next (ctx, &key);
344 if (err & GPG_ERR_EOF)
345 break;
346 else if (err)
347 {
348 g_free (bracket_email);
349 g_warning ("gpgme_op_keylist_next failed: %s", gpgme_strerror (err));
350 return NULL;
351 }
352 }
353
354 if (found_key == NULL)
355 g_warning ("%s: No suitable key found for %s", __func__, uid_email);
356
357 return found_key;
358}
359
369static ssize_t
370gvm_gpgme_fread (void *handle, void *buffer, size_t size)
371{
372 int ret;
373 FILE *file = (FILE *) handle;
374
375 ret = fread (buffer, 1, size, file);
376 if (ferror (file))
377 return -1;
378 return ret;
379}
380
390static ssize_t
391gvm_gpgme_fwrite (void *handle, const void *buffer, size_t size)
392{
393 int ret;
394 FILE *file = (FILE *) handle;
395
396 ret = fwrite (buffer, 1, size, file);
397 if (ferror (file))
398 return -1;
399 return ret;
400}
401
402#define CHECK_ERR(func) \
403 if (err) \
404 { \
405 printf ("%s: %s failed: %s\n", __func__, func, gpgme_strerror (err)); \
406 return -1; \
407 }
408
422static int
423create_all_certificates_trustlist (gpgme_ctx_t ctx, const char *homedir)
424{
425 gpgme_key_t key;
426 gchar *trustlist_filename;
427 GString *trustlist_content;
428 GError *error;
429 gpgme_error_t err;
430
431 error = NULL;
432 gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_CANCEL);
433
434 trustlist_filename = g_build_filename (homedir, "trustlist.txt", NULL);
435
436 trustlist_content = g_string_new ("");
437
438 err = gpgme_op_keylist_start (ctx, NULL, 0);
439 CHECK_ERR ("gpgme_op_keylist_start")
440 gpgme_op_keylist_next (ctx, &key);
441 CHECK_ERR ("gpgme_op_keylist_next")
442 while (key)
443 {
444 g_string_append_printf (trustlist_content, "%s S\n", key->fpr);
445 err = gpgme_op_keylist_next (ctx, &key);
446 if (err & GPG_ERR_EOF)
447 break;
448 else
449 CHECK_ERR ("gpgme_op_keylist_next")
450 }
451
452 if (g_file_set_contents (trustlist_filename, trustlist_content->str,
453 trustlist_content->len, &error)
454 == FALSE)
455 {
456 g_warning ("%s: Could not write trust list: %s", __func__,
457 error->message);
458 g_free (trustlist_filename);
459 g_string_free (trustlist_content, TRUE);
460 return -1;
461 }
462
463 g_free (trustlist_filename);
464 g_string_free (trustlist_content, TRUE);
465
466 return 0;
467}
468
469#undef CHECK_ERR
470#define CHECK_ERR(func) \
471 if (err) \
472 { \
473 printf ("%s: %s failed: %s\n", __func__, func, gpgme_strerror (err)); \
474 if (plain_data) \
475 gpgme_data_release (plain_data); \
476 if (encrypted_data) \
477 gpgme_data_release (encrypted_data); \
478 if (ctx) \
479 gpgme_release (ctx); \
480 gvm_file_remove_recurse (gpg_temp_dir); \
481 return -1; \
482 }
483
499static int
500encrypt_stream_internal (FILE *plain_file, FILE *encrypted_file,
501 const char *key_str, ssize_t key_len,
502 const char *uid_email, gpgme_protocol_t protocol,
503 GArray *key_types)
504{
505 char gpg_temp_dir[] = "/tmp/gvmd-gpg-XXXXXX";
506 gpgme_ctx_t ctx;
507 gpgme_data_t plain_data, encrypted_data;
508 gpgme_key_t key;
509 gpgme_key_t keys[2] = {NULL, NULL};
510 gpgme_error_t err;
511 gpgme_encrypt_flags_t encrypt_flags;
512 const char *key_type_str;
513 struct gpgme_data_cbs callbacks;
514
515 ctx = NULL;
516 plain_data = NULL;
517 encrypted_data = NULL;
518
519 if (uid_email == NULL || strcmp (uid_email, "") == 0)
520 {
521 g_warning ("%s: No email address for user identification given",
522 __func__);
523 return -1;
524 }
525
526 if (gpgme_check_version (NULL) == NULL)
527 {
528 g_warning ("%s: gpgme_check_version failed", __func__);
529 return -1;
530 }
531
532 if (protocol == GPGME_PROTOCOL_CMS)
533 key_type_str = "certificate";
534 else
535 key_type_str = "public key";
536
537 // Create temporary GPG home directory, set up context and encryption flags
538 if (mkdtemp (gpg_temp_dir) == NULL)
539 {
540 g_warning ("%s: mkdtemp failed\n", __func__);
541 return -1;
542 }
543
544 err = gpgme_new (&ctx);
545 CHECK_ERR ("gpgme_new")
546
547 if (protocol == GPGME_PROTOCOL_CMS)
548 gpgme_set_armor (ctx, 0);
549 else
550 gpgme_set_armor (ctx, 1);
551
552 err = gpgme_ctx_set_engine_info (ctx, protocol, NULL, gpg_temp_dir);
553 CHECK_ERR ("gpgme_ctx_set_engine_info")
554
555 err = gpgme_set_protocol (ctx, protocol);
556 CHECK_ERR ("gpgme_set_protocol")
557
558 err = gpgme_set_keylist_mode (ctx, GPGME_KEYLIST_MODE_LOCAL);
559 CHECK_ERR ("gpgme_set_keylist_mode")
560
561 gpgme_set_offline (ctx, 1);
562
563 encrypt_flags = GPGME_ENCRYPT_ALWAYS_TRUST | GPGME_ENCRYPT_NO_COMPRESS;
564
565 // Import public key into context
566 if (gvm_gpg_import_many_types_from_string (ctx, key_str, key_len, key_types))
567 {
568 g_warning ("%s: Import of %s failed", __func__, key_type_str);
569 gpgme_release (ctx);
570 gvm_file_remove_recurse (gpg_temp_dir);
571 return -1;
572 }
573
574 // Get imported public key
575 key = find_email_encryption_key (ctx, uid_email);
576 if (key == NULL)
577 {
578 g_warning ("%s: Could not find %s for encryption", __func__,
579 key_type_str);
580 gpgme_release (ctx);
581 gvm_file_remove_recurse (gpg_temp_dir);
582 return -1;
583 }
584 keys[0] = key;
585
586 // Set up data objects for input and output streams
587 err = gpgme_data_new_from_stream (&plain_data, plain_file);
588 CHECK_ERR ("gpgme_data_new_from_stream for plain text")
589
590 /* Create a GPGME data buffer with custom read and write functions.
591 *
592 * This is necessary as gpgme_data_new_from_stream may cause problems
593 * when trying to write to the stream after some operations. */
594 memset (&callbacks, 0, sizeof (callbacks));
595 callbacks.read = gvm_gpgme_fread;
596 callbacks.write = gvm_gpgme_fwrite;
597 err = gpgme_data_new_from_cbs (&encrypted_data, &callbacks, encrypted_file);
598 CHECK_ERR ("gpgme_data_new_from_stream for encrypted text")
599
600 if (protocol == GPGME_PROTOCOL_CMS)
601 {
602 gpgme_data_set_encoding (encrypted_data, GPGME_DATA_ENCODING_BASE64);
603
604 if (create_all_certificates_trustlist (ctx, gpg_temp_dir))
605 {
606 gpgme_data_release (plain_data);
607 gpgme_data_release (encrypted_data);
608 gpgme_release (ctx);
609 gvm_file_remove_recurse (gpg_temp_dir);
610 return -1;
611 }
612 }
613
614 // Encrypt data
615 err = gpgme_op_encrypt (ctx, keys, encrypt_flags, plain_data, encrypted_data);
616 CHECK_ERR ("gpgme_op_encrypt")
617
618 gpgme_data_release (plain_data);
619 gpgme_data_release (encrypted_data);
620 gpgme_release (ctx);
621 gvm_file_remove_recurse (gpg_temp_dir);
622
623 return 0;
624}
625
639int
640gvm_pgp_pubkey_encrypt_stream (FILE *plain_file, FILE *encrypted_file,
641 const char *uid_email,
642 const char *public_key_str,
643 ssize_t public_key_len)
644{
645 int ret;
646 const gpgme_data_type_t types_ptr[1] = {GPGME_DATA_TYPE_PGP_KEY};
647 GArray *key_types = g_array_new (FALSE, FALSE, sizeof (gpgme_data_type_t));
648
649 g_array_append_vals (key_types, types_ptr, 1);
650 ret = encrypt_stream_internal (plain_file, encrypted_file, public_key_str,
651 public_key_len, uid_email,
652 GPGME_PROTOCOL_OpenPGP, key_types);
653 g_array_free (key_types, TRUE);
654
655 return ret;
656}
657
671int
672gvm_smime_encrypt_stream (FILE *plain_file, FILE *encrypted_file,
673 const char *uid_email, const char *certificate_str,
674 ssize_t certificate_len)
675{
676 int ret;
677 const gpgme_data_type_t types_ptr[2] = {GPGME_DATA_TYPE_X509_CERT,
678 GPGME_DATA_TYPE_CMS_OTHER};
679 GArray *key_types = g_array_new (FALSE, FALSE, sizeof (gpgme_data_type_t));
680
681 g_array_append_vals (key_types, types_ptr, 2);
682 ret = encrypt_stream_internal (plain_file, encrypted_file, certificate_str,
683 certificate_len, uid_email, GPGME_PROTOCOL_CMS,
684 key_types);
685 g_array_free (key_types, TRUE);
686
687 return ret;
688}
#define G_LOG_DOMAIN
GLib log domain.
Definition array.c:17
static gboolean initialized
Flag whether the config file was read.
Definition authutils.c:33
int gvm_file_remove_recurse(const gchar *pathname)
Recursively removes files and directories.
Definition fileutils.c:123
Protos for file utility functions.
static int create_all_certificates_trustlist(gpgme_ctx_t ctx, const char *homedir)
Adds a trust list of all current certificates to a GPG homedir.
Definition gpgmeutils.c:423
int gvm_pgp_pubkey_encrypt_stream(FILE *plain_file, FILE *encrypted_file, const char *uid_email, const char *public_key_str, ssize_t public_key_len)
Encrypt a stream for a PGP public key, writing to another stream.
Definition gpgmeutils.c:640
static ssize_t gvm_gpgme_fread(void *handle, void *buffer, size_t size)
Wrapper for fread for use as a GPGME callback.
Definition gpgmeutils.c:370
int gvm_smime_encrypt_stream(FILE *plain_file, FILE *encrypted_file, const char *uid_email, const char *certificate_str, ssize_t certificate_len)
Encrypt a stream for a S/MIME certificate, writing to another stream.
Definition gpgmeutils.c:672
int gvm_gpg_import_many_types_from_string(gpgme_ctx_t ctx, const char *key_str, ssize_t key_len, GArray *key_types)
Import a key or certificate given by a string.
Definition gpgmeutils.c:166
gpgme_ctx_t gvm_init_gpgme_ctx_from_dir(const gchar *dir)
Returns a new gpgme context.
Definition gpgmeutils.c:74
int gvm_gpg_import_from_string(gpgme_ctx_t ctx, const char *key_str, ssize_t key_len, gpgme_data_type_t key_type)
Import a key or certificate given by a string.
Definition gpgmeutils.c:264
static ssize_t gvm_gpgme_fwrite(void *handle, const void *buffer, size_t size)
Wrapper for fread for use as a GPGME callback.
Definition gpgmeutils.c:391
static gpgme_key_t find_email_encryption_key(gpgme_ctx_t ctx, const char *uid_email)
Find a key that can be used to encrypt for an email recipient.
Definition gpgmeutils.c:286
#define CHECK_ERR(func)
Definition gpgmeutils.c:402
static int encrypt_stream_internal(FILE *plain_file, FILE *encrypted_file, const char *key_str, ssize_t key_len, const char *uid_email, gpgme_protocol_t protocol, GArray *key_types)
Encrypt a stream for a PGP public key, writing to another stream.
Definition gpgmeutils.c:500
void log_gpgme(GLogLevelFlags level, gpg_error_t err, const char *fmt,...)
Log function with extra gpg-error style output.
Definition gpgmeutils.c:43
Protos and data structures for GPGME utilities.