Greenbone Vulnerability Management Libraries  11.0.0
gpgmeutils.c File Reference

GPGME utilities. More...

#include "gpgmeutils.h"
#include "fileutils.h"
#include <errno.h>
#include <gpg-error.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
Include dependency graph for gpgmeutils.c:

Go to the source code of this file.

Macros

#define G_LOG_DOMAIN   "util gpgme"
 GLib log domain. More...
 

Functions

void log_gpgme (GLogLevelFlags level, gpg_error_t err, const char *fmt,...)
 Log function with extra gpg-error style output. More...
 
gpgme_ctx_t gvm_init_gpgme_ctx_from_dir (const gchar *dir)
 Returns a new gpgme context. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 

Detailed Description

GPGME utilities.

Definition in file gpgmeutils.c.

Macro Definition Documentation

◆ G_LOG_DOMAIN

#define G_LOG_DOMAIN   "util gpgme"

GLib log domain.

Definition at line 41 of file gpgmeutils.c.

Function Documentation

◆ 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 
)
static

Encrypt a stream for a PGP public key, writing to another stream.

The output will use ASCII armor mode and no compression.

Parameters
[in]plain_fileStream / FILE* providing the plain text.
[in]encrypted_fileStream to write the encrypted text to.
[in]key_strString containing the public key or certificate.
[in]key_lenLength of key / certificate, -1 to use strlen.
[in]uid_emailEmail address of key / certificate to use.
[in]protocolThe protocol to use, e.g. OpenPGP or CMS.
[in]key_typesThe expected GPGME buffered data types.
Returns
0 success, -1 error.

Definition at line 377 of file gpgmeutils.c.

381 {
382  char gpg_temp_dir[] = "/tmp/gvmd-gpg-XXXXXX";
383  gpgme_ctx_t ctx;
384  gpgme_data_t plain_data, encrypted_data;
385  gpgme_key_t key;
386  gpgme_key_t keys[2] = {NULL, NULL};
387  gpgme_error_t err;
388  gpgme_encrypt_flags_t encrypt_flags;
389  const char *key_type_str;
390 
391  if (uid_email == NULL || strcmp (uid_email, "") == 0)
392  {
393  g_warning ("%s: No email address for user identification given",
394  __FUNCTION__);
395  return -1;
396  }
397 
398  if (protocol == GPGME_PROTOCOL_CMS)
399  key_type_str = "certificate";
400  else
401  key_type_str = "public key";
402 
403  // Create temporary GPG home directory, set up context and encryption flags
404  if (mkdtemp (gpg_temp_dir) == NULL)
405  {
406  g_warning ("%s: mkdtemp failed\n", __FUNCTION__);
407  return -1;
408  }
409 
410  gpgme_new (&ctx);
411 
412  if (protocol == GPGME_PROTOCOL_CMS)
413  gpgme_set_armor (ctx, 0);
414  else
415  gpgme_set_armor (ctx, 1);
416 
417  gpgme_ctx_set_engine_info (ctx, protocol, NULL, gpg_temp_dir);
418  gpgme_set_protocol (ctx, protocol);
419  encrypt_flags = GPGME_ENCRYPT_ALWAYS_TRUST | GPGME_ENCRYPT_NO_COMPRESS;
420 
421  // Import public key into context
422  if (gvm_gpg_import_many_types_from_string (ctx, key_str, key_len, key_types))
423  {
424  g_warning ("%s: Import of %s failed", __FUNCTION__, key_type_str);
425  gpgme_release (ctx);
426  gvm_file_remove_recurse (gpg_temp_dir);
427  return -1;
428  }
429 
430  // Get imported public key
431  key = find_email_encryption_key (ctx, uid_email);
432  if (key == NULL)
433  {
434  g_warning ("%s: Could not find %s for encryption", __FUNCTION__,
435  key_type_str);
436  gpgme_release (ctx);
437  gvm_file_remove_recurse (gpg_temp_dir);
438  return -1;
439  }
440  keys[0] = key;
441 
442  // Set up data objects for input and output streams
443  gpgme_data_new_from_stream (&plain_data, plain_file);
444  gpgme_data_new_from_stream (&encrypted_data, encrypted_file);
445 
446  if (protocol == GPGME_PROTOCOL_CMS)
447  gpgme_data_set_encoding (encrypted_data, GPGME_DATA_ENCODING_BASE64);
448 
449  // Encrypt data
450  err = gpgme_op_encrypt (ctx, keys, encrypt_flags, plain_data, encrypted_data);
451 
452  if (err)
453  {
454  g_warning ("%s: Encryption failed: %s", __FUNCTION__,
455  gpgme_strerror (err));
456  gpgme_data_release (plain_data);
457  gpgme_data_release (encrypted_data);
458  gpgme_release (ctx);
459  gvm_file_remove_recurse (gpg_temp_dir);
460  return -1;
461  }
462 
463  gpgme_data_release (plain_data);
464  gpgme_data_release (encrypted_data);
465  gpgme_release (ctx);
466  gvm_file_remove_recurse (gpg_temp_dir);
467 
468  return 0;
469 }

References find_email_encryption_key(), gvm_file_remove_recurse(), and gvm_gpg_import_many_types_from_string().

Referenced by gvm_pgp_pubkey_encrypt_stream(), and gvm_smime_encrypt_stream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ find_email_encryption_key()

static gpgme_key_t find_email_encryption_key ( gpgme_ctx_t  ctx,
const char *  uid_email 
)
static

Find a key that can be used to encrypt for an email recipient.

Parameters
[in]ctxThe GPGME context.
[in]uid_emailThe recipient email address to look for.
Returns
The key as a gpgme_key_t.

Definition at line 306 of file gpgmeutils.c.

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 }

Referenced by encrypt_stream_internal().

Here is the caller graph for this function:

◆ 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.

Parameters
[in]ctxThe GPGME context to import the key / certificate into.
[in]key_strKey or certificate string.
[in]key_lenLength of key/certificate string or -1 to use strlen.
[in]key_typeThe expected key type.
Returns
0 success, 1 invalid key data, 2 unexpected key data, 3 error importing key/certificate, -1 error.

Definition at line 282 of file gpgmeutils.c.

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 }

References gvm_gpg_import_many_types_from_string().

Here is the call graph for this function:

◆ 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.

Parameters
[in]ctxThe GPGME context to import the key / certificate into.
[in]key_strKey or certificate string.
[in]key_lenLength of key/certificate string or -1 to use strlen.
[in]key_typesGArray of expected key types.
Returns
0 success, 1 invalid key data, 2 unexpected key data, 3 error importing key/certificate, -1 error.

Definition at line 180 of file gpgmeutils.c.

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 }

Referenced by encrypt_stream_internal(), and gvm_gpg_import_from_string().

Here is the caller graph for this function:

◆ gvm_init_gpgme_ctx_from_dir()

gpgme_ctx_t gvm_init_gpgme_ctx_from_dir ( const gchar *  dir)

Returns a new gpgme context.

Inits a gpgme context with the custom gpg directory, protocol version etc. Returns the context or NULL if an error occurred. This function also does an gpgme initialization the first time it is called.

Parameters
dirDirectory to use for gpg
Returns
The gpgme_ctx_t to the context or NULL if an error occurred.

Definition at line 88 of file gpgmeutils.c.

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 }

References initialized, and log_gpgme().

Here is the call graph for this function:

◆ 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.

The output will use ASCII armor mode and no compression.

Parameters
[in]plain_fileStream / FILE* providing the plain text.
[in]encrypted_fileStream to write the encrypted text to.
[in]uid_emailEmail address of public key to use.
[in]public_key_strString containing the public key.
[in]public_key_lenLength of public key or -1 to use strlen.
Returns
0 success, -1 error.

Definition at line 485 of file gpgmeutils.c.

489 {
490  int ret;
491  const gpgme_data_type_t types_ptr[1] = {GPGME_DATA_TYPE_PGP_KEY};
492  GArray *key_types = g_array_new (FALSE, FALSE, sizeof (gpgme_data_type_t));
493 
494  g_array_append_vals (key_types, types_ptr, 1);
496  plain_file, encrypted_file, public_key_str, public_key_len, uid_email,
497  GPGME_PROTOCOL_OpenPGP, key_types);
498  g_array_free (key_types, TRUE);
499 
500  return ret;
501 }

References encrypt_stream_internal().

Here is the call graph for this function:

◆ 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.

The output will use ASCII armor mode and no compression.

Parameters
[in]plain_fileStream / FILE* providing the plain text.
[in]encrypted_fileStream to write the encrypted text to.
[in]uid_emailEmail address of certificate to use.
[in]certificate_strString containing the public key.
[in]certificate_lenLength of public key or -1 to use strlen.
Returns
0 success, -1 error.

Definition at line 517 of file gpgmeutils.c.

520 {
521  int ret;
522  const gpgme_data_type_t types_ptr[2] = {GPGME_DATA_TYPE_X509_CERT,
523  GPGME_DATA_TYPE_CMS_OTHER};
524  GArray *key_types = g_array_new (FALSE, FALSE, sizeof (gpgme_data_type_t));
525 
526  g_array_append_vals (key_types, types_ptr, 2);
528  plain_file, encrypted_file, certificate_str, certificate_len, uid_email,
529  GPGME_PROTOCOL_CMS, key_types);
530  g_array_free (key_types, TRUE);
531 
532  return ret;
533 }

References encrypt_stream_internal().

Here is the call graph for this function:

◆ log_gpgme()

void log_gpgme ( GLogLevelFlags  level,
gpg_error_t  err,
const char *  fmt,
  ... 
)

Log function with extra gpg-error style output.

If err is not 0, the appropriate error string is appended to the output. It takes care to only add the error source string if it makes sense.

Parameters
levelThe GLib style log level
errAn gpg-error value or 0
fmtThe printf style format string, followed by its arguments.

Definition at line 57 of file gpgmeutils.c.

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 }

References G_LOG_DOMAIN.

Referenced by gvm_init_gpgme_ctx_from_dir().

Here is the caller graph for this function:
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
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
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:377
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