Greenbone Vulnerability Management Libraries  22.8.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 
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
24 
27 #define G_LOG_DOMAIN "libgvm util"
28 
42 void
43 log_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 
73 gpgme_ctx_t
74 gvm_init_gpgme_ctx_from_dir (const gchar *dir)
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 
165 int
166 gvm_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 
263 int
264 gvm_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 
285 static gpgme_key_t
286 find_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 
369 static ssize_t
370 gvm_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 
390 static ssize_t
391 gvm_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 
422 static int
423 create_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 *g_err;
429  gpgme_error_t err;
430 
431  g_err = 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, &g_err)
454  == FALSE)
455  {
456  g_warning ("%s: Could not write trust list: %s", __func__,
457  g_err->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 
499 static int
500 encrypt_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 
639 int
640 gvm_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 
671 int
672 gvm_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 }
find_email_encryption_key
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
gvm_gpg_import_many_types_from_string
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
create_all_certificates_trustlist
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
gvm_pgp_pubkey_encrypt_stream
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
gvm_gpg_import_from_string
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
gvm_file_remove_recurse
int gvm_file_remove_recurse(const gchar *pathname)
Recursively removes files and directories.
Definition: fileutils.c:123
G_LOG_DOMAIN
#define G_LOG_DOMAIN
GLib logging domain.
Definition: gpgmeutils.c:27
fileutils.h
Protos for file utility functions.
CHECK_ERR
#define CHECK_ERR(func)
Definition: gpgmeutils.c:470
gvm_gpgme_fread
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
gvm_init_gpgme_ctx_from_dir
gpgme_ctx_t gvm_init_gpgme_ctx_from_dir(const gchar *dir)
Returns a new gpgme context.
Definition: gpgmeutils.c:74
encrypt_stream_internal
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
gvm_gpgme_fwrite
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
gvm_smime_encrypt_stream
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
gpgmeutils.h
Protos and data structures for GPGME utilities.
initialized
static gboolean initialized
Flag whether the config file was read.
Definition: authutils.c:33
log_gpgme
void log_gpgme(GLogLevelFlags level, gpg_error_t err, const char *fmt,...)
Log function with extra gpg-error style output.
Definition: gpgmeutils.c:43