Greenbone Vulnerability Management Libraries  11.0.1
gpgmeutils.c
Go to the documentation of this file.
1 /* Copyright (C) 2009-2019 Greenbone Networks GmbH
2  *
3  * SPDX-License-Identifier: GPL-2.0-or-later
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
25 #include "gpgmeutils.h"
26 
27 #include "fileutils.h"
28 
29 #include <errno.h> /* for ENOENT, errno */
30 #include <gpg-error.h> /* for gpg_err_source, gpg_strerror, gpg_error_from... */
31 #include <locale.h> /* for setlocale, LC_MESSAGES, LC_CTYPE */
32 #include <stdlib.h> /* for mkdtemp */
33 #include <string.h> /* for strlen */
34 #include <sys/stat.h> /* for mkdir */
35 #include <unistd.h> /* for access, F_OK */
36 
37 #undef G_LOG_DOMAIN
38 
41 #define G_LOG_DOMAIN "util gpgme"
42 
56 void
57 log_gpgme (GLogLevelFlags level, gpg_error_t err, const char *fmt, ...)
58 {
59  va_list arg_ptr;
60  char *msg;
61 
62  va_start (arg_ptr, fmt);
63  msg = g_strdup_vprintf (fmt, arg_ptr);
64  va_end (arg_ptr);
65  if (err && gpg_err_source (err) != GPG_ERR_SOURCE_ANY && gpg_err_source (err))
66  g_log (G_LOG_DOMAIN, level, "%s: %s <%s>", msg, gpg_strerror (err),
67  gpg_strsource (err));
68  else if (err)
69  g_log (G_LOG_DOMAIN, level, "%s: %s", msg, gpg_strerror (err));
70  else
71  g_log (G_LOG_DOMAIN, level, "%s", msg);
72  g_free (msg);
73 }
74 
87 gpgme_ctx_t
88 gvm_init_gpgme_ctx_from_dir (const gchar *dir)
89 {
90  static int initialized;
91  gpgme_error_t err;
92  gpgme_ctx_t ctx;
93 
94  /* Initialize GPGME the first time we are called. This is a
95  failsafe mode; it would be better to initialize GPGME early at
96  process startup instead of this on-the-fly method; however in
97  this non-threaded system; this is an easier way for a library.
98  We allow to initialize until a valid gpgme or a gpg backend has
99  been found. */
100  if (!initialized)
101  {
102  gpgme_engine_info_t info;
103 
104  if (!gpgme_check_version (NULL))
105  {
106  g_critical ("gpgme library could not be initialized.");
107  return NULL;
108  }
109  gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
110 #ifdef LC_MESSAGES
111  gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
112 #endif
113 
114 #ifndef NDEBUG
115  g_message ("Setting GnuPG dir to '%s'", dir);
116 #endif
117  err = 0;
118  if (access (dir, F_OK))
119  {
120  err = gpg_error_from_syserror ();
121 
122  if (errno == ENOENT)
123  /* directory does not exists. try to create it */
124  if (mkdir (dir, 0700) == 0)
125  {
126 #ifndef NDEBUG
127  g_message ("Created GnuPG dir '%s'", dir);
128 #endif
129  err = 0;
130  }
131  }
132 
133  if (!err)
134  err = gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, NULL, dir);
135 
136  if (err)
137  {
138  log_gpgme (G_LOG_LEVEL_WARNING, err, "Setting GnuPG dir failed");
139  return NULL;
140  }
141 
142  /* Show the OpenPGP engine version. */
143  if (!gpgme_get_engine_info (&info))
144  {
145  while (info && info->protocol != GPGME_PROTOCOL_OpenPGP)
146  info = info->next;
147  }
148  else
149  info = NULL;
150 #ifndef NDEBUG
151  g_message ("Using OpenPGP engine version '%s'",
152  info && info->version ? info->version : "[?]");
153 #endif
154 
155  /* Everything is fine. */
156  initialized = 1;
157  }
158 
159  /* Allocate the context. */
160  ctx = NULL;
161  err = gpgme_new (&ctx);
162  if (err)
163  log_gpgme (G_LOG_LEVEL_WARNING, err, "Creating GPGME context failed");
164 
165  return ctx;
166 }
167 
179 int
181  const char *key_str,
182  ssize_t key_len,
183  GArray* key_types)
184 {
185  gpgme_data_t key_data;
186  gpgme_error_t err;
187  gpgme_data_type_t given_key_type;
188  gpgme_import_result_t import_result;
189  int ret;
190 
191  gpgme_data_new_from_mem (
192  &key_data, key_str, (key_len >= 0 ? key_len : (ssize_t) strlen (key_str)),
193  0);
194 
195  given_key_type = gpgme_data_identify (key_data, 0);
196  ret = 0;
197  if (given_key_type == GPGME_DATA_TYPE_INVALID)
198  {
199  ret = 1;
200  g_warning ("%s: key_str is invalid", __FUNCTION__);
201  }
202  else
203  {
204  unsigned int index;
205  for (index = 0; index < key_types->len; index++)
206  {
207  if (g_array_index (key_types, gpgme_data_type_t, index)
208  == given_key_type)
209  break;
210  }
211 
212  if (index >= key_types->len)
213  {
214  ret = 2;
215  GString *expected_buffer = g_string_new ("");
216  for (index = 0; index < key_types->len; index++)
217  {
218  if (index)
219  g_string_append (expected_buffer, " or ");
220  g_string_append_printf (expected_buffer,
221  "%d",
222  g_array_index (key_types,
223  gpgme_data_type_t,
224  index));
225  }
226  g_warning ("%s: key_str is not the expected type: "
227  " expected: %s, got %d",
228  __FUNCTION__, expected_buffer->str, given_key_type);
229  g_string_free (expected_buffer, TRUE);
230  }
231  }
232 
233  if (ret)
234  {
235  gpgme_data_release (key_data);
236  return ret;
237  }
238 
239  err = gpgme_op_import (ctx, key_data);
240  gpgme_data_release (key_data);
241  if (err)
242  {
243  g_warning ("%s: Import failed: %s", __FUNCTION__, gpgme_strerror (err));
244  return 3;
245  }
246 
247  import_result = gpgme_op_import_result (ctx);
248  g_debug ("%s: %d imported, %d not imported", __FUNCTION__,
249  import_result->imported, import_result->not_imported);
250 
251  gpgme_import_status_t status;
252  status = import_result->imports;
253  while (status)
254  {
255  if (status->result != GPG_ERR_NO_ERROR)
256  g_warning ("%s: '%s' could not be imported: %s", __FUNCTION__,
257  status->fpr, gpgme_strerror (status->result));
258  else
259  g_debug ("%s: Imported '%s'", __FUNCTION__, status->fpr);
260 
261  status = status->next;
262  };
263 
264  if (import_result->not_imported)
265  return 3;
266 
267  return 0;
268 }
269 
281 int
282 gvm_gpg_import_from_string (gpgme_ctx_t ctx, const char *key_str,
283  ssize_t key_len, gpgme_data_type_t key_type)
284 {
285  int ret;
286  GArray *key_types = g_array_sized_new (FALSE,
287  FALSE,
288  sizeof (gpgme_data_type_t),
289  1);
290  g_array_insert_val (key_types, 0, key_type);
291  ret = gvm_gpg_import_many_types_from_string (ctx, key_str, key_len,
292  key_types);
293  g_array_free (key_types, TRUE);
294  return ret;
295 }
296 
305 static gpgme_key_t
306 find_email_encryption_key (gpgme_ctx_t ctx, const char *uid_email)
307 {
308  gchar *bracket_email;
309  gpgme_key_t key;
310  gboolean recipient_found = FALSE;
311 
312  if (uid_email == NULL)
313  return NULL;
314 
315  bracket_email = g_strdup_printf ("<%s>", uid_email);
316 
317  gpgme_op_keylist_start (ctx, NULL, 0);
318  gpgme_op_keylist_next (ctx, &key);
319  while (key && recipient_found == FALSE)
320  {
321  if (key->can_encrypt)
322  {
323  g_debug ("%s: key '%s' OK for encryption", __FUNCTION__,
324  key->subkeys->fpr);
325 
326  gpgme_user_id_t uid;
327  uid = key->uids;
328  while (uid && recipient_found == FALSE)
329  {
330  g_debug ("%s: UID email: %s", __FUNCTION__, uid->email);
331 
332  if (strcmp (uid->email, uid_email) == 0
333  || strstr (uid->email, bracket_email))
334  {
335  g_message ("%s: Found matching UID for %s", __FUNCTION__,
336  uid_email);
337  recipient_found = TRUE;
338  }
339  uid = uid->next;
340  }
341  }
342  else
343  {
344  g_debug ("%s: key '%s' cannot be used for encryption", __FUNCTION__,
345  key->subkeys->fpr);
346  }
347 
348  if (recipient_found == FALSE)
349  gpgme_op_keylist_next (ctx, &key);
350  }
351 
352  if (recipient_found)
353  return key;
354  else
355  {
356  g_warning ("%s: No suitable key found for %s", __FUNCTION__, uid_email);
357  return NULL;
358  }
359 }
360 
370 static ssize_t
371 gvm_gpgme_fread (void *handle, void *buffer, size_t size)
372 {
373  int ret;
374  FILE *file = (FILE *)handle;
375 
376  ret = fread (buffer, 1, size, file);
377  if (ferror (file))
378  return -1;
379  return ret;
380 }
381 
391 static ssize_t
392 gvm_gpgme_fwrite (void *handle, const void *buffer, size_t size)
393 {
394  int ret;
395  FILE *file = (FILE *)handle;
396 
397  ret = fwrite (buffer, 1, size, file);
398  if (ferror (file))
399  return -1;
400  return ret;
401 }
402 
416 static int
417 create_all_certificates_trustlist (gpgme_ctx_t ctx, const char *homedir)
418 {
419  gpgme_key_t key;
420  gchar *trustlist_filename;
421  GString *trustlist_content;
422  GError *g_err;
423 
424  g_err = NULL;
425  gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_CANCEL);
426 
427  trustlist_filename = g_build_filename (homedir,
428  "trustlist.txt",
429  NULL);
430 
431  trustlist_content = g_string_new ("");
432 
433  gpgme_op_keylist_start (ctx, NULL, 0);
434  gpgme_op_keylist_next (ctx, &key);
435  while (key)
436  {
437  g_string_append_printf (trustlist_content, "%s S\n", key->fpr);
438  gpgme_op_keylist_next (ctx, &key);
439  }
440 
441  if (g_file_set_contents (trustlist_filename,
442  trustlist_content->str,
443  trustlist_content->len,
444  &g_err) == FALSE)
445  {
446  g_warning ("%s: Could not write trust list: %s",
447  __func__, g_err->message);
448  g_free (trustlist_filename);
449  g_string_free (trustlist_content, TRUE);
450  return -1;
451  }
452 
453  g_free (trustlist_filename);
454  g_string_free (trustlist_content, TRUE);
455 
456  return 0;
457 }
458 
474 static int
475 encrypt_stream_internal (FILE *plain_file, FILE *encrypted_file,
476  const char *key_str, ssize_t key_len,
477  const char *uid_email, gpgme_protocol_t protocol,
478  GArray* key_types)
479 {
480  char gpg_temp_dir[] = "/tmp/gvmd-gpg-XXXXXX";
481  gpgme_ctx_t ctx;
482  gpgme_data_t plain_data, encrypted_data;
483  gpgme_key_t key;
484  gpgme_key_t keys[2] = {NULL, NULL};
485  gpgme_error_t err;
486  gpgme_encrypt_flags_t encrypt_flags;
487  const char *key_type_str;
488  struct gpgme_data_cbs callbacks;
489 
490  if (uid_email == NULL || strcmp (uid_email, "") == 0)
491  {
492  g_warning ("%s: No email address for user identification given",
493  __FUNCTION__);
494  return -1;
495  }
496 
497  if (protocol == GPGME_PROTOCOL_CMS)
498  key_type_str = "certificate";
499  else
500  key_type_str = "public key";
501 
502  // Create temporary GPG home directory, set up context and encryption flags
503  if (mkdtemp (gpg_temp_dir) == NULL)
504  {
505  g_warning ("%s: mkdtemp failed\n", __FUNCTION__);
506  return -1;
507  }
508 
509  gpgme_new (&ctx);
510 
511  if (protocol == GPGME_PROTOCOL_CMS)
512  gpgme_set_armor (ctx, 0);
513  else
514  gpgme_set_armor (ctx, 1);
515 
516  gpgme_ctx_set_engine_info (ctx, protocol, NULL, gpg_temp_dir);
517  gpgme_set_protocol (ctx, protocol);
518  encrypt_flags = GPGME_ENCRYPT_ALWAYS_TRUST | GPGME_ENCRYPT_NO_COMPRESS;
519 
520  // Import public key into context
521  if (gvm_gpg_import_many_types_from_string (ctx, key_str, key_len, key_types))
522  {
523  g_warning ("%s: Import of %s failed", __FUNCTION__, key_type_str);
524  gpgme_release (ctx);
525  gvm_file_remove_recurse (gpg_temp_dir);
526  return -1;
527  }
528 
529  // Get imported public key
530  key = find_email_encryption_key (ctx, uid_email);
531  if (key == NULL)
532  {
533  g_warning ("%s: Could not find %s for encryption", __FUNCTION__,
534  key_type_str);
535  gpgme_release (ctx);
536  gvm_file_remove_recurse (gpg_temp_dir);
537  return -1;
538  }
539  keys[0] = key;
540 
541  // Set up data objects for input and output streams
542  gpgme_data_new_from_stream (&plain_data, plain_file);
543 
544  /* Create a GPGME data buffer with custom read and write functions.
545  *
546  * This is neccessary as gpgme_data_new_from_stream may cause problems
547  * when trying to write to the stream after some operations. */
548  memset (&callbacks, 0, sizeof (callbacks));
549  callbacks.read = gvm_gpgme_fread;
550  callbacks.write = gvm_gpgme_fwrite;
551  gpgme_data_new_from_cbs (&encrypted_data, &callbacks, encrypted_file);
552 
553  if (protocol == GPGME_PROTOCOL_CMS)
554  {
555  gpgme_data_set_encoding (encrypted_data, GPGME_DATA_ENCODING_BASE64);
556 
557  if (create_all_certificates_trustlist (ctx, gpg_temp_dir))
558  {
559  gpgme_data_release (plain_data);
560  gpgme_data_release (encrypted_data);
561  gpgme_release (ctx);
562  gvm_file_remove_recurse (gpg_temp_dir);
563  return -1;
564  }
565  }
566 
567  // Encrypt data
568  err = gpgme_op_encrypt (ctx, keys, encrypt_flags, plain_data, encrypted_data);
569 
570  if (err)
571  {
572  g_warning ("%s: Encryption failed: %s", __FUNCTION__,
573  gpgme_strerror (err));
574  gpgme_data_release (plain_data);
575  gpgme_data_release (encrypted_data);
576  gpgme_release (ctx);
577  gvm_file_remove_recurse (gpg_temp_dir);
578  return -1;
579  }
580 
581  gpgme_data_release (plain_data);
582  gpgme_data_release (encrypted_data);
583  gpgme_release (ctx);
584  gvm_file_remove_recurse (gpg_temp_dir);
585 
586  return 0;
587 }
588 
602 int
603 gvm_pgp_pubkey_encrypt_stream (FILE *plain_file, FILE *encrypted_file,
604  const char *uid_email,
605  const char *public_key_str,
606  ssize_t public_key_len)
607 {
608  int ret;
609  const gpgme_data_type_t types_ptr[1] = {GPGME_DATA_TYPE_PGP_KEY};
610  GArray *key_types = g_array_new (FALSE, FALSE, sizeof (gpgme_data_type_t));
611 
612  g_array_append_vals (key_types, types_ptr, 1);
614  plain_file, encrypted_file, public_key_str, public_key_len, uid_email,
615  GPGME_PROTOCOL_OpenPGP, key_types);
616  g_array_free (key_types, TRUE);
617 
618  return ret;
619 }
620 
634 int
635 gvm_smime_encrypt_stream (FILE *plain_file, FILE *encrypted_file,
636  const char *uid_email, const char *certificate_str,
637  ssize_t certificate_len)
638 {
639  int ret;
640  const gpgme_data_type_t types_ptr[2] = {GPGME_DATA_TYPE_X509_CERT,
641  GPGME_DATA_TYPE_CMS_OTHER};
642  GArray *key_types = g_array_new (FALSE, FALSE, sizeof (gpgme_data_type_t));
643 
644  g_array_append_vals (key_types, types_ptr, 2);
646  plain_file, encrypted_file, certificate_str, certificate_len, uid_email,
647  GPGME_PROTOCOL_CMS, key_types);
648  g_array_free (key_types, TRUE);
649 
650  return ret;
651 }
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:306
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:180
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:417
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:603
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:282
gvm_file_remove_recurse
int gvm_file_remove_recurse(const gchar *pathname)
Recursively removes files and directories.
Definition: fileutils.c:77
G_LOG_DOMAIN
#define G_LOG_DOMAIN
GLib log domain.
Definition: gpgmeutils.c:41
fileutils.h
Protos for file utility functions.
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:371
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:88
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:475
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:392
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:635
gpgmeutils.h
Protos and data structures for GPGME utilities.
initialized
static gboolean initialized
Flag whether the config file was read.
Definition: authutils.c:47
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:57