Greenbone Vulnerability Management Libraries  10.0.0
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
180 gvm_gpg_import_from_string (gpgme_ctx_t ctx, const char *key_str,
181  ssize_t key_len, gpgme_data_type_t key_type)
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 }
242 
251 static gpgme_key_t
252 find_email_encryption_key (gpgme_ctx_t ctx, const char *uid_email)
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 }
306 
322 static int
323 encrypt_stream_internal (FILE *plain_file, FILE *encrypted_file,
324  const char *key_str, ssize_t key_len,
325  const char *uid_email, gpgme_protocol_t protocol,
326  gpgme_data_type_t data_type)
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 }
416 
430 int
431 gvm_pgp_pubkey_encrypt_stream (FILE *plain_file, FILE *encrypted_file,
432  const char *uid_email,
433  const char *public_key_str,
434  ssize_t public_key_len)
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 }
440 
454 int
455 gvm_smime_encrypt_stream (FILE *plain_file, FILE *encrypted_file,
456  const char *uid_email, const char *certificate_str,
457  ssize_t certificate_len)
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
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_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:431
Protos and data structures for GPGME utilities.
#define G_LOG_DOMAIN
GLib log domain.
Definition: gpgmeutils.c:41
int gvm_file_remove_recurse(const gchar *pathname)
Recursively removes files and directories.
Definition: fileutils.c:77
gpgme_ctx_t gvm_init_gpgme_ctx_from_dir(const gchar *dir)
Returns a new gpgme context.
Definition: gpgmeutils.c:88
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:455
static gboolean initialized
Flag whether the config file was read.
Definition: authutils.c:47
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
Protos for file utility functions.
void log_gpgme(GLogLevelFlags level, gpg_error_t err, const char *fmt,...)
Log function with extra gpg-error style output.
Definition: gpgmeutils.c:57