File: tlsutils.c

package info (click to toggle)
gvm-libs 22.35.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,980 kB
  • sloc: ansic: 39,095; makefile: 26
file content (229 lines) | stat: -rw-r--r-- 6,417 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/* SPDX-FileCopyrightText: 2009-2023 Greenbone AG
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/**
 * @file tlsutils.c
 * @brief TLS certificate utilities.
 */

#include "tlsutils.h"

#include <string.h>

#undef G_LOG_DOMAIN
/**
 * @brief GLib logging domain.
 */
#define G_LOG_DOMAIN "libgvm util"

/**
 * @brief Try to determine the format (DER or PEM) of a x509 certificate.
 *
 * @param[in]  cert_data  The certificate data.
 * @param[in]  cert_len   Length of the certificate data.
 *
 * @return The GnuTLS x509 certificate type.
 */
gnutls_x509_crt_fmt_t
gvm_x509_format_from_data (const char *cert_data, size_t cert_len)
{
  static const gchar *begin_str = "-----BEGIN ";
  if (g_strstr_len (cert_data, cert_len, begin_str))
    return GNUTLS_X509_FMT_PEM;
  else
    return GNUTLS_X509_FMT_DER;
}

/**
 * @brief Decode a Base64 string to the contents of a gnutls_datum_t
 *
 * @param[in]     encoded         The Base64 data as a NUL-terminated string
 * @param[in,out] decoded_datum   The datum struct to decode to.
 *
 * @return The return code from gnutls_base64_decode2
 */
int
gvm_base64_to_gnutls_datum (const char *encoded, gnutls_datum_t *decoded_datum)
{
  gnutls_datum_t encoded_datum;
  decoded_datum->data = NULL;
  decoded_datum->size = 0;
  encoded_datum.data = (unsigned char *) encoded;
  encoded_datum.size = strlen (encoded);

  return gnutls_base64_decode2 (&encoded_datum, decoded_datum);
}

/**
 * @brief Frees a list of X509 certificates.
 *
 * @param[in]  certs        The cerificate list to free.
 * @param[in]  certs_count  The number of certificates in the list.
 */
void
gvm_x509_cert_list_free (gnutls_x509_crt_t *certs, unsigned int certs_count)
{
  if (certs == NULL)
    return;
  for (unsigned int i = 0; i < certs_count; i++)
    gnutls_x509_crt_deinit (certs[i]);
  gnutls_free (certs);
}

/**
 * @brief Export a GnuTLS x509 private key as a PEM formatted string.
 *
 * @param[in]  privkey  The private key to export.
 *
 * @return The private key as a PEM string, or NULL on error.
 */
gchar *
gvm_x509_privkey_to_pem (gnutls_x509_privkey_t privkey)
{
  gchar *pem_str = NULL;
  int ret;
  gnutls_datum_t export_datum = {.data = NULL, .size = 0};

  ret =
    gnutls_x509_privkey_export2 (privkey, GNUTLS_X509_FMT_PEM, &export_datum);
  if (ret)
    g_warning ("%s: Error exporting private key: %s", __func__,
               gnutls_strerror (ret));
  else
    pem_str = g_strdup ((const char *) export_datum.data);

  gnutls_free (export_datum.data);

  return pem_str;
}

/**
 * @brief Export a GnuTLS x509 cerificate list as a PEM formatted string.
 *
 * @param[in]  certs        The array of certificates to export
 * @param[in]  certs_count  The number of certificates to export
 *
 * @return The certificates as a PEM string, or NULL on error.
 */
gchar *
gvm_x509_cert_list_to_pem (gnutls_x509_crt_t *certs, unsigned int certs_count)
{
  int ret;
  GString *certs_string = g_string_new ("");
  for (unsigned int i = 0; i < certs_count; i++)
    {
      gnutls_x509_crt_t cert;
      gnutls_datum_t export_datum = {.data = NULL, .size = 0};

      cert = certs[i];
      ret = gnutls_x509_crt_export2 (cert, GNUTLS_X509_FMT_PEM, &export_datum);
      if (ret)
        {
          g_warning ("%s: Error exporting certificate: %s", __func__,
                     gnutls_strerror (ret));
        }
      else
        g_string_append_printf (certs_string, "%s\n",
                                (char *) export_datum.data);
      gnutls_free (export_datum.data);
    }
  return g_string_free (certs_string, FALSE);
}

/**
 * @brief Export a GnuTLS x509 CRL as a PEM formatted string.
 *
 * @param[in]  crl        The certificate revocation list CRL
 *
 * @return The certificates as a PEM string, or NULL on error.
 */
gchar *
gvm_x509_crl_to_pem (gnutls_x509_crl_t crl)
{
  gchar *crl_str = NULL;
  int ret;
  gnutls_datum_t export_datum = {.data = NULL, .size = 0};

  ret = gnutls_x509_crl_export2 (crl, GNUTLS_X509_FMT_PEM, &export_datum);
  if (ret)
    {
      g_warning ("%s: Error exporting CRL: %s", __func__,
                 gnutls_strerror (ret));
    }
  else
    crl_str = g_strdup ((char *) export_datum.data);

  gnutls_free (export_datum.data);
  return crl_str;
}

/**
 * @brief Convert GnuTLS PKCS12 data to a PEM formatted string.
 *
 * @param[in]  pkcs12           PKCS12 data to get data from
 * @param[in]  passphrase       Passphrase to decrypt PKCS12 data
 * @param[out] privkey_out      Optional private key output
 * @param[out] cert_chain_out   Optional certificate chain output
 * @param[out] extra_certs_out  Optional extra certificates output
 * @param[out] crl_out          Optional CRL output
 *
 * @return 0 success or a GnuTLS error code if decryption or parsing fails.
 */
int
gvm_pkcs12_to_pem (gnutls_pkcs12_t pkcs12, const char *passphrase,
                   gchar **privkey_out, gchar **cert_chain_out,
                   gchar **extra_certs_out, gchar **crl_out)
{
  gnutls_x509_privkey_t privkey;
  gnutls_x509_crt_t *chain_certs, *extra_certs;
  gnutls_x509_crl_t crl;
  unsigned int chain_certs_count, extra_certs_count;
  int ret;

  if (privkey_out)
    *privkey_out = NULL;
  if (cert_chain_out)
    *cert_chain_out = NULL;
  if (extra_certs_out)
    *extra_certs_out = NULL;
  if (crl_out)
    *crl_out = NULL;

  chain_certs = extra_certs = NULL;

  gnutls_x509_privkey_init (&privkey);
  gnutls_x509_crl_init (&crl);
  ret = gnutls_pkcs12_simple_parse (pkcs12, passphrase, &privkey, &chain_certs,
                                    &chain_certs_count, &extra_certs,
                                    &extra_certs_count, &crl, 0);
  if (ret != GNUTLS_E_SUCCESS)
    {
      gnutls_x509_privkey_deinit (privkey);
      gnutls_x509_crl_deinit (crl);
      return ret;
    }

  if (privkey_out && privkey)
    *privkey_out = gvm_x509_privkey_to_pem (privkey);

  gnutls_x509_privkey_deinit (privkey);

  if (cert_chain_out && chain_certs_count)
    *cert_chain_out =
      gvm_x509_cert_list_to_pem (chain_certs, chain_certs_count);

  if (extra_certs_out && extra_certs_count)
    *extra_certs_out =
      gvm_x509_cert_list_to_pem (extra_certs, extra_certs_count);

  if (crl_out && crl)
    *crl_out = gvm_x509_crl_to_pem (crl);

  gvm_x509_cert_list_free (chain_certs, chain_certs_count);
  gvm_x509_cert_list_free (extra_certs, extra_certs_count);
  gnutls_x509_crl_deinit (crl);

  return GNUTLS_E_SUCCESS;
}