"Fossies" - the Fresh Open Source Software Archive

Member "gvm-libs-11.0.1/util/gpgmeutils.c" (12 May 2020, 19890 Bytes) of package /linux/misc/openvas/gvm-libs-11.0.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gpgmeutils.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 11.0.0_vs_11.0.1.

    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 
   20 /**
   21  * @file
   22  * @brief GPGME utilities.
   23  */
   24 
   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 /**
   39  * @brief GLib log domain.
   40  */
   41 #define G_LOG_DOMAIN "util gpgme"
   42 
   43 /**
   44  * @brief Log function with extra gpg-error style output
   45  *
   46  * If \p err is not 0, the appropriate error string is appended to
   47  * the output.  It takes care to only add the error source string if
   48  * it makes sense.
   49  *
   50  * @param level  The GLib style log level
   51  * @param err    An gpg-error value or 0
   52  * @param fmt    The printf style format string, followed by its
   53  *                arguments.
   54  *
   55  */
   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 
   75 /**
   76  * @brief Returns a new gpgme context.
   77  *
   78  * Inits a gpgme context with the custom gpg directory, protocol
   79  * version etc. Returns the context or NULL if an error occurred.
   80  * This function also does an gpgme initialization the first time it
   81  * is called.
   82  *
   83  * @param dir  Directory to use for gpg
   84  *
   85  * @return The gpgme_ctx_t to the context or NULL if an error occurred.
   86  */
   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 
  168 /**
  169  * @brief Import a key or certificate given by a string.
  170  *
  171  * @param[in]  ctx      The GPGME context to import the key / certificate into.
  172  * @param[in]  key_str  Key or certificate string.
  173  * @param[in]  key_len  Length of key/certificate string or -1 to use strlen.
  174  * @param[in]  key_types   GArray of expected key types.
  175  *
  176  * @return 0 success, 1 invalid key data, 2 unexpected key data,
  177  *  3 error importing key/certificate, -1 error.
  178  */
  179 int
  180 gvm_gpg_import_many_types_from_string (gpgme_ctx_t ctx,
  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 
  270 /**
  271  * @brief Import a key or certificate given by a string.
  272  *
  273  * @param[in]  ctx      The GPGME context to import the key / certificate into.
  274  * @param[in]  key_str  Key or certificate string.
  275  * @param[in]  key_len  Length of key/certificate string or -1 to use strlen.
  276  * @param[in]  key_type The expected key type.
  277  *
  278  * @return 0 success, 1 invalid key data, 2 unexpected key data,
  279  *  3 error importing key/certificate, -1 error.
  280  */
  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 
  297 /**
  298  * @brief Find a key that can be used to encrypt for an email recipient.
  299  *
  300  * @param[in]  ctx        The GPGME context.
  301  * @param[in]  uid_email  The recipient email address to look for.
  302  *
  303  * @return  The key as a gpgme_key_t.
  304  */
  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 
  361 /**
  362  * @brief Wrapper for fread for use as a GPGME callback.
  363  *
  364  * @param[in]  handle   The file handle.
  365  * @param[out] buffer   The data buffer to read data into.
  366  * @param[in]  size     The size of the buffer.
  367  *
  368  * @return The number of bytes read or -1 on error.
  369  */
  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 
  382 /**
  383  * @brief Wrapper for fread for use as a GPGME callback.
  384  *
  385  * @param[in]  handle   The file handle.
  386  * @param[in]  buffer   The data buffer to read data into.
  387  * @param[in]  size     The amount of buffered data.
  388  *
  389  * @return The number of bytes written or -1 on error.
  390  */
  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 
  403 /**
  404  * @brief  Adds a trust list of all current certificates to a GPG homedir.
  405  *
  406  * This will overwrite the existing trustlist, so it should only be used for
  407  *  temporary, automatically generated GPG home directories.
  408  *
  409  * TODO: This should use or be replaced by a trust model inside GVM.
  410  *
  411  * @param[in]  ctx      The GPGME context to get the keys from.
  412  * @param[in]  homedir  The directory to write the trust list file to.
  413  *
  414  * @return 0 success, -1 error.
  415  */
  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 
  459 /**
  460  * @brief Encrypt a stream for a PGP public key, writing to another stream.
  461  *
  462  * The output will use ASCII armor mode and no compression.
  463  *
  464  * @param[in]  plain_file     Stream / FILE* providing the plain text.
  465  * @param[in]  encrypted_file Stream to write the encrypted text to.
  466  * @param[in]  key_str        String containing the public key or certificate.
  467  * @param[in]  key_len        Length of key / certificate, -1 to use strlen.
  468  * @param[in]  uid_email      Email address of key / certificate to use.
  469  * @param[in]  protocol       The protocol to use, e.g. OpenPGP or CMS.
  470  * @param[in]  key_types      The expected GPGME buffered data types.
  471  *
  472  * @return 0 success, -1 error.
  473  */
  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 
  589 /**
  590  * @brief Encrypt a stream for a PGP public key, writing to another stream.
  591  *
  592  * The output will use ASCII armor mode and no compression.
  593  *
  594  * @param[in]  plain_file       Stream / FILE* providing the plain text.
  595  * @param[in]  encrypted_file   Stream to write the encrypted text to.
  596  * @param[in]  uid_email        Email address of public key to use.
  597  * @param[in]  public_key_str   String containing the public key.
  598  * @param[in]  public_key_len   Length of public key or -1 to use strlen.
  599  *
  600  * @return 0 success, -1 error.
  601  */
  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);
  613   ret = encrypt_stream_internal (
  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 
  621 /**
  622  * @brief Encrypt a stream for a S/MIME certificate, writing to another stream.
  623  *
  624  * The output will use ASCII armor mode and no compression.
  625  *
  626  * @param[in]  plain_file       Stream / FILE* providing the plain text.
  627  * @param[in]  encrypted_file   Stream to write the encrypted text to.
  628  * @param[in]  uid_email        Email address of certificate to use.
  629  * @param[in]  certificate_str  String containing the public key.
  630  * @param[in]  certificate_len  Length of public key or -1 to use strlen.
  631  *
  632  * @return 0 success, -1 error.
  633  */
  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);
  645   ret = encrypt_stream_internal (
  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 }