Greenbone Vulnerability Management Libraries  10.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_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, gpgme_data_type_t data_type)
 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,
gpgme_data_type_t  data_type 
)
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]data_typeThe expected GPGME buffered data type.
Returns
0 success, -1 error.

Definition at line 323 of file gpgmeutils.c.

327 {
328  char gpg_temp_dir[] = "/tmp/gvmd-gpg-XXXXXX";
329  gpgme_ctx_t ctx;
330  gpgme_data_t plain_data, encrypted_data;
331  gpgme_key_t key;
332  gpgme_key_t keys[2] = {NULL, NULL};
333  gpgme_error_t err;
334  gpgme_encrypt_flags_t encrypt_flags;
335  const char *key_type_str;
336 
337  if (uid_email == NULL || strcmp (uid_email, "") == 0)
338  {
339  g_warning ("%s: No email address for user identification given",
340  __FUNCTION__);
341  return -1;
342  }
343 
344  if (protocol == GPGME_PROTOCOL_CMS)
345  key_type_str = "certificate";
346  else
347  key_type_str = "public key";
348 
349  // Create temporary GPG home directory, set up context and encryption flags
350  if (mkdtemp (gpg_temp_dir) == NULL)
351  {
352  g_warning ("%s: mkdtemp failed\n", __FUNCTION__);
353  return -1;
354  }
355 
356  gpgme_new (&ctx);
357 
358  if (protocol == GPGME_PROTOCOL_CMS)
359  gpgme_set_armor (ctx, 0);
360  else
361  gpgme_set_armor (ctx, 1);
362 
363  gpgme_ctx_set_engine_info (ctx, protocol, NULL, gpg_temp_dir);
364  gpgme_set_protocol (ctx, protocol);
365  encrypt_flags = GPGME_ENCRYPT_ALWAYS_TRUST | GPGME_ENCRYPT_NO_COMPRESS;
366 
367  // Import public key into context
368  if (gvm_gpg_import_from_string (ctx, key_str, key_len, data_type))
369  {
370  g_warning ("%s: Import of %s failed", __FUNCTION__, key_type_str);
371  gpgme_release (ctx);
372  gvm_file_remove_recurse (gpg_temp_dir);
373  return -1;
374  }
375 
376  // Get imported public key
377  key = find_email_encryption_key (ctx, uid_email);
378  if (key == NULL)
379  {
380  g_warning ("%s: Could not find %s for encryption", __FUNCTION__,
381  key_type_str);
382  gpgme_release (ctx);
383  gvm_file_remove_recurse (gpg_temp_dir);
384  return -1;
385  }
386  keys[0] = key;
387 
388  // Set up data objects for input and output streams
389  gpgme_data_new_from_stream (&plain_data, plain_file);
390  gpgme_data_new_from_stream (&encrypted_data, encrypted_file);
391 
392  if (protocol == GPGME_PROTOCOL_CMS)
393  gpgme_data_set_encoding (encrypted_data, GPGME_DATA_ENCODING_BASE64);
394 
395  // Encrypt data
396  err = gpgme_op_encrypt (ctx, keys, encrypt_flags, plain_data, encrypted_data);
397 
398  if (err)
399  {
400  g_warning ("%s: Encryption failed: %s", __FUNCTION__,
401  gpgme_strerror (err));
402  gpgme_data_release (plain_data);
403  gpgme_data_release (encrypted_data);
404  gpgme_release (ctx);
405  gvm_file_remove_recurse (gpg_temp_dir);
406  return -1;
407  }
408 
409  gpgme_data_release (plain_data);
410  gpgme_data_release (encrypted_data);
411  gpgme_release (ctx);
412  gvm_file_remove_recurse (gpg_temp_dir);
413 
414  return 0;
415 }
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:180
int gvm_file_remove_recurse(const gchar *pathname)
Recursively removes files and directories.
Definition: fileutils.c:77
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:252

References find_email_encryption_key(), gvm_file_remove_recurse(), and gvm_gpg_import_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 252 of file gpgmeutils.c.

253 {
254  gchar *bracket_email;
255  gpgme_key_t key;
256  gboolean recipient_found = FALSE;
257 
258  if (uid_email == NULL)
259  return NULL;
260 
261  bracket_email = g_strdup_printf ("<%s>", uid_email);
262 
263  gpgme_op_keylist_start (ctx, NULL, 0);
264  gpgme_op_keylist_next (ctx, &key);
265  while (key && recipient_found == FALSE)
266  {
267  if (key->can_encrypt)
268  {
269  g_debug ("%s: key '%s' OK for encryption", __FUNCTION__,
270  key->subkeys->fpr);
271 
272  gpgme_user_id_t uid;
273  uid = key->uids;
274  while (uid && recipient_found == FALSE)
275  {
276  g_debug ("%s: UID email: %s", __FUNCTION__, uid->email);
277 
278  if (strcmp (uid->email, uid_email) == 0
279  || strstr (uid->email, bracket_email))
280  {
281  g_message ("%s: Found matching UID for %s", __FUNCTION__,
282  uid_email);
283  recipient_found = TRUE;
284  }
285  uid = uid->next;
286  }
287  }
288  else
289  {
290  g_debug ("%s: key '%s' cannot be used for encryption", __FUNCTION__,
291  key->subkeys->fpr);
292  }
293 
294  if (recipient_found == FALSE)
295  gpgme_op_keylist_next (ctx, &key);
296  }
297 
298  if (recipient_found)
299  return key;
300  else
301  {
302  g_warning ("%s: No suitable key found for %s", __FUNCTION__, uid_email);
303  return NULL;
304  }
305 }

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 180 of file gpgmeutils.c.

182 {
183  gpgme_data_t key_data;
184  gpgme_error_t err;
185  gpgme_data_type_t given_key_type;
186  gpgme_import_result_t import_result;
187 
188  gpgme_data_new_from_mem (
189  &key_data, key_str, (key_len >= 0 ? key_len : (ssize_t) strlen (key_str)),
190  0);
191 
192  given_key_type = gpgme_data_identify (key_data, 0);
193  if (given_key_type != key_type)
194  {
195  int ret;
196  if (given_key_type == GPGME_DATA_TYPE_INVALID)
197  {
198  ret = 1;
199  g_warning ("%s: key_str is invalid", __FUNCTION__);
200  }
201  else
202  {
203  ret = 2;
204  g_warning ("%s: key_str is not the expected type: "
205  " expected: %d, got %d",
206  __FUNCTION__, key_type, given_key_type);
207  }
208  gpgme_data_release (key_data);
209  return ret;
210  }
211 
212  err = gpgme_op_import (ctx, key_data);
213  gpgme_data_release (key_data);
214  if (err)
215  {
216  g_warning ("%s: Import failed: %s", __FUNCTION__, gpgme_strerror (err));
217  return 3;
218  }
219 
220  import_result = gpgme_op_import_result (ctx);
221  g_debug ("%s: %d imported, %d not imported", __FUNCTION__,
222  import_result->imported, import_result->not_imported);
223 
224  gpgme_import_status_t status;
225  status = import_result->imports;
226  while (status)
227  {
228  if (status->result != GPG_ERR_NO_ERROR)
229  g_warning ("%s: '%s' could not be imported: %s", __FUNCTION__,
230  status->fpr, gpgme_strerror (status->result));
231  else
232  g_debug ("%s: Imported '%s'", __FUNCTION__, status->fpr);
233 
234  status = status->next;
235  };
236 
237  if (import_result->not_imported)
238  return 3;
239 
240  return 0;
241 }

Referenced by encrypt_stream_internal().

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 }
static gboolean initialized
Flag whether the config file was read.
Definition: authutils.c:47
void log_gpgme(GLogLevelFlags level, gpg_error_t err, const char *fmt,...)
Log function with extra gpg-error style output.
Definition: gpgmeutils.c:57

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 431 of file gpgmeutils.c.

435 {
436  return encrypt_stream_internal (
437  plain_file, encrypted_file, public_key_str, public_key_len, uid_email,
438  GPGME_PROTOCOL_OpenPGP, GPGME_DATA_TYPE_PGP_KEY);
439 }
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, gpgme_data_type_t data_type)
Encrypt a stream for a PGP public key, writing to another stream.
Definition: gpgmeutils.c:323

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 455 of file gpgmeutils.c.

458 {
459  return encrypt_stream_internal (
460  plain_file, encrypted_file, certificate_str, certificate_len, uid_email,
461  GPGME_PROTOCOL_CMS, GPGME_DATA_TYPE_CMS_OTHER);
462 }
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, gpgme_data_type_t data_type)
Encrypt a stream for a PGP public key, writing to another stream.
Definition: gpgmeutils.c:323

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 }
#define G_LOG_DOMAIN
GLib log domain.
Definition: gpgmeutils.c:41

References G_LOG_DOMAIN.

Referenced by gvm_init_gpgme_ctx_from_dir().

Here is the caller graph for this function: