1 /* tls.c
2 ** strophe XMPP client library -- generic TLS functions
3 **
4 ** Copyright (C) 2021 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
5 **
6 ** This software is provided AS-IS with no warranty, either express
7 ** or implied.
8 **
9 ** This program is dual licensed under the MIT and GPLv3 licenses.
10 */
11
12 /** @file
13 * Generic TLS functionality.
14 */
15
16 /** @defgroup TLS SSL/TLS specific functionality
17 * These functions provide SSL/TLS specific functionality.
18 */
19
20 #include <errno.h>
21 #include <stdarg.h>
22 #include <string.h>
23
24 #include "strophe.h"
25
26 #include "common.h"
27
28 struct _dnsname_t {
29 char **data;
30 size_t cur, max;
31 };
32
33 const size_t tlscert_dnsnames_increment = 4;
34
35 /** Get the Strophe context which is assigned to this certificate.
36 *
37 * @param cert a Strophe TLS certificate object
38 *
39 * @return the Strophe context object where this certificate originates from
40 *
41 * @ingroup TLS
42 */
xmpp_tlscert_get_ctx(const xmpp_tlscert_t * cert)43 xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert)
44 {
45 return cert->ctx;
46 }
47
48 /** Get the Strophe connection which is assigned to this certificate.
49 *
50 * @param cert a Strophe TLS certificate object
51 *
52 * @return the Strophe connection object where this certificate originates from
53 *
54 * @ingroup TLS
55 */
xmpp_tlscert_get_conn(const xmpp_tlscert_t * cert)56 xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert)
57 {
58 return cert->conn;
59 }
60
61 /** Get the complete PEM of this certificate.
62 *
63 * @param cert a Strophe TLS certificate object
64 *
65 * @return a string containing the PEM of this certificate
66 *
67 * @ingroup TLS
68 */
xmpp_tlscert_get_pem(const xmpp_tlscert_t * cert)69 const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert)
70 {
71 return cert->pem;
72 }
73
74 /** Get the dnsName entries out of the SubjectAlternativeNames.
75 *
76 * Note: Max. `MAX_NUM_DNSNAMES` are supported.
77 *
78 * @param cert a Strophe TLS certificate object
79 * @param n which dnsName entry
80 *
81 * @return a string with the n'th dnsName
82 *
83 * @ingroup TLS
84 */
xmpp_tlscert_get_dnsname(const xmpp_tlscert_t * cert,size_t n)85 const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n)
86 {
87 if (n >= cert->dnsnames->cur)
88 return NULL;
89 return cert->dnsnames->data[n];
90 }
91
92 /** Get various parts of the certificate as String.
93 *
94 * c.f. \ref xmpp_cert_element_t for details.
95 *
96 * @param cert a Strophe TLS certificate object
97 * @param elmnt which part of the certificate
98 *
99 * @return a string with the part of the certificate
100 *
101 * @ingroup TLS
102 */
xmpp_tlscert_get_string(const xmpp_tlscert_t * cert,xmpp_cert_element_t elmnt)103 const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
104 xmpp_cert_element_t elmnt)
105 {
106 if (elmnt >= XMPP_CERT_ELEMENT_MAX)
107 return NULL;
108 return cert->elements[elmnt];
109 }
110
111 /** Get a descriptive string for each xmpp_cert_element_t.
112 *
113 * c.f. \ref xmpp_cert_element_t for details.
114 *
115 * @param cert a Strophe TLS certificate object
116 * @param elmnt which element
117 *
118 * @return a string with the description
119 *
120 * @ingroup TLS
121 */
xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)122 const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
123 {
124 static const char *descriptions[] = {
125 "X.509 Version",
126 "SerialNumber",
127 "Subject",
128 "Issuer",
129 "Issued On",
130 "Expires On",
131 "Public Key Algorithm",
132 "Certificate Signature Algorithm",
133 "Fingerprint SHA-1",
134 "Fingerprint SHA-256",
135 };
136 if (elmnt >= XMPP_CERT_ELEMENT_MAX)
137 return NULL;
138 return descriptions[elmnt];
139 }
140
141 /** Allocate and initialize a Strophe TLS certificate object.
142 *
143 * @param ctx a Strophe context object
144 *
145 * @return a certificate object or NULL
146 */
tlscert_new(xmpp_ctx_t * ctx)147 xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx)
148 {
149 xmpp_tlscert_t *tlscert = xmpp_alloc(ctx, sizeof(*tlscert));
150 if (!tlscert)
151 return NULL;
152 memset(tlscert, 0, sizeof(*tlscert));
153
154 tlscert->dnsnames = xmpp_alloc(ctx, sizeof(*tlscert->dnsnames));
155 if (!tlscert->dnsnames) {
156 xmpp_free(ctx, tlscert);
157 return NULL;
158 }
159 memset(tlscert->dnsnames, 0, sizeof(*tlscert->dnsnames));
160
161 tlscert->ctx = ctx;
162
163 return tlscert;
164 }
165
166 /** Free a certificate object.
167 *
168 * @param cert a Strophe TLS certificate object
169 *
170 * @ingroup TLS
171 */
xmpp_tlscert_free(xmpp_tlscert_t * cert)172 void xmpp_tlscert_free(xmpp_tlscert_t *cert)
173 {
174 size_t n;
175 for (n = 0; n < ARRAY_SIZE(cert->elements); ++n) {
176 if (cert->elements[n])
177 xmpp_free(cert->ctx, cert->elements[n]);
178 }
179 if (cert->dnsnames->data) {
180 for (n = 0; n < cert->dnsnames->cur; ++n) {
181 if (cert->dnsnames->data[n])
182 xmpp_free(cert->ctx, cert->dnsnames->data[n]);
183 }
184 }
185 xmpp_free(cert->ctx, cert->dnsnames->data);
186 xmpp_free(cert->ctx, cert->dnsnames);
187 if (cert->pem)
188 xmpp_free(cert->ctx, cert->pem);
189 xmpp_free(cert->ctx, cert);
190 }
191
192 /** Add a dnsName to the Strophe TLS certificate object.
193 *
194 * @param cert a Strophe TLS certificate object
195 * @param dnsname dnsName that shall be stored
196 *
197 * @return classic Unix style - 0=success, 1=error
198 */
tlscert_add_dnsname(xmpp_tlscert_t * cert,const char * dnsname)199 int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname)
200 {
201 if ((cert->dnsnames->cur + 1) >= cert->dnsnames->max) {
202 char **dnsnames =
203 xmpp_realloc(cert->ctx, cert->dnsnames->data,
204 (cert->dnsnames->max + tlscert_dnsnames_increment) *
205 sizeof(char **));
206 if (!dnsnames)
207 return 1;
208 cert->dnsnames->data = dnsnames;
209 cert->dnsnames->max += tlscert_dnsnames_increment;
210 }
211 cert->dnsnames->data[cert->dnsnames->cur++] =
212 xmpp_strdup(cert->ctx, dnsname);
213 return 0;
214 }
215