Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* tls.c
3 : ** strophe XMPP client library -- generic TLS functions
4 : **
5 : ** Copyright (C) 2021 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
6 : **
7 : ** This software is provided AS-IS with no warranty, either express
8 : ** or implied.
9 : **
10 : ** This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * Generic TLS functionality.
15 : */
16 :
17 : /** @defgroup TLS SSL/TLS specific functionality
18 : * These functions provide SSL/TLS specific functionality.
19 : */
20 :
21 : #include <errno.h>
22 : #include <stdarg.h>
23 : #include <string.h>
24 :
25 : #if !defined(_WIN32)
26 : #include <unistd.h>
27 : #endif
28 :
29 : #include "strophe.h"
30 :
31 : #include "common.h"
32 :
33 : const struct conn_interface tls_intf = {
34 : tls_read,
35 : tls_write,
36 : tls_clear_pending_write,
37 : tls_pending,
38 : tls_error,
39 : tls_is_recoverable,
40 : /* init conn */
41 : NULL,
42 : };
43 :
44 : struct _dnsname_t {
45 : char **data;
46 : size_t cur, max;
47 : };
48 :
49 : const size_t tlscert_dnsnames_increment = 4;
50 :
51 : /** Get the Strophe context which is assigned to this certificate.
52 : *
53 : * @param cert a Strophe TLS certificate object
54 : *
55 : * @return the Strophe context object where this certificate originates from
56 : *
57 : * @ingroup TLS
58 : */
59 0 : xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert)
60 : {
61 0 : return cert->ctx;
62 : }
63 :
64 : /** Get the Strophe connection which is assigned to this certificate.
65 : *
66 : * @param cert a Strophe TLS certificate object
67 : *
68 : * @return the Strophe connection object where this certificate originates from
69 : *
70 : * @ingroup TLS
71 : */
72 0 : xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert)
73 : {
74 0 : return cert->conn;
75 : }
76 :
77 : /** Get the complete PEM of this certificate.
78 : *
79 : * @param cert a Strophe TLS certificate object
80 : *
81 : * @return a string containing the PEM of this certificate
82 : *
83 : * @ingroup TLS
84 : */
85 0 : const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert)
86 : {
87 0 : return cert->pem;
88 : }
89 :
90 : /** Get the dnsName entries out of the SubjectAlternativeNames.
91 : *
92 : * Note: Max. `MAX_NUM_DNSNAMES` are supported.
93 : *
94 : * @param cert a Strophe TLS certificate object
95 : * @param n which dnsName entry
96 : *
97 : * @return a string with the n'th dnsName
98 : *
99 : * @ingroup TLS
100 : */
101 0 : const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n)
102 : {
103 0 : if (n >= cert->dnsnames->cur)
104 : return NULL;
105 0 : return cert->dnsnames->data[n];
106 : }
107 :
108 : /** Get various parts of the certificate as String.
109 : *
110 : * c.f. \ref xmpp_cert_element_t for details.
111 : *
112 : * @param cert a Strophe TLS certificate object
113 : * @param elmnt which part of the certificate
114 : *
115 : * @return a string with the part of the certificate
116 : *
117 : * @ingroup TLS
118 : */
119 0 : const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
120 : xmpp_cert_element_t elmnt)
121 : {
122 0 : if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
123 : return NULL;
124 0 : return cert->elements[elmnt];
125 : }
126 :
127 : /** Get a descriptive string for each xmpp_cert_element_t.
128 : *
129 : * c.f. \ref xmpp_cert_element_t for details.
130 : *
131 : * @param elmnt which element
132 : *
133 : * @return a string with the description
134 : *
135 : * @ingroup TLS
136 : */
137 0 : const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
138 : {
139 0 : static const char *descriptions[] = {
140 : "X.509 Version",
141 : "SerialNumber",
142 : "Subject",
143 : "Issuer",
144 : "Issued On",
145 : "Expires On",
146 : "Public Key Algorithm",
147 : "Certificate Signature Algorithm",
148 : "Fingerprint SHA-1",
149 : "Fingerprint SHA-256",
150 : };
151 0 : if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
152 : return NULL;
153 0 : return descriptions[elmnt];
154 : }
155 :
156 : /** Allocate and initialize a Strophe TLS certificate object.
157 : *
158 : * @param ctx a Strophe context object
159 : *
160 : * @return a certificate object or NULL
161 : */
162 0 : xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx)
163 : {
164 0 : xmpp_tlscert_t *tlscert = strophe_alloc(ctx, sizeof(*tlscert));
165 0 : if (!tlscert)
166 : return NULL;
167 0 : memset(tlscert, 0, sizeof(*tlscert));
168 :
169 0 : tlscert->dnsnames = strophe_alloc(ctx, sizeof(*tlscert->dnsnames));
170 0 : if (!tlscert->dnsnames) {
171 0 : strophe_free(ctx, tlscert);
172 0 : return NULL;
173 : }
174 0 : memset(tlscert->dnsnames, 0, sizeof(*tlscert->dnsnames));
175 :
176 0 : tlscert->ctx = ctx;
177 :
178 0 : return tlscert;
179 : }
180 :
181 : /** Free a certificate object.
182 : *
183 : * @param cert a Strophe TLS certificate object
184 : *
185 : * @ingroup TLS
186 : */
187 0 : void xmpp_tlscert_free(xmpp_tlscert_t *cert)
188 : {
189 0 : size_t n;
190 0 : for (n = 0; n < ARRAY_SIZE(cert->elements); ++n) {
191 0 : if (cert->elements[n])
192 0 : strophe_free(cert->ctx, cert->elements[n]);
193 : }
194 0 : if (cert->dnsnames->data) {
195 0 : for (n = 0; n < cert->dnsnames->cur; ++n) {
196 0 : if (cert->dnsnames->data[n])
197 0 : strophe_free(cert->ctx, cert->dnsnames->data[n]);
198 : }
199 : }
200 0 : strophe_free(cert->ctx, cert->dnsnames->data);
201 0 : strophe_free(cert->ctx, cert->dnsnames);
202 0 : if (cert->pem)
203 0 : strophe_free(cert->ctx, cert->pem);
204 0 : strophe_free(cert->ctx, cert);
205 0 : }
206 :
207 : /** Add a dnsName to the Strophe TLS certificate object.
208 : *
209 : * @param cert a Strophe TLS certificate object
210 : * @param dnsname dnsName that shall be stored
211 : *
212 : * @return classic Unix style - 0=success, 1=error
213 : */
214 0 : int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname)
215 : {
216 0 : if ((cert->dnsnames->cur + 1) >= cert->dnsnames->max) {
217 0 : char **dnsnames =
218 0 : strophe_realloc(cert->ctx, cert->dnsnames->data,
219 0 : (cert->dnsnames->max + tlscert_dnsnames_increment) *
220 : sizeof(char **));
221 0 : if (!dnsnames)
222 : return 1;
223 0 : cert->dnsnames->data = dnsnames;
224 0 : cert->dnsnames->max += tlscert_dnsnames_increment;
225 : }
226 0 : cert->dnsnames->data[cert->dnsnames->cur++] =
227 0 : strophe_strdup(cert->ctx, dnsname);
228 0 : return 0;
229 : }
230 :
231 8 : int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn)
232 : {
233 8 : int ret;
234 8 : unsigned char hash[XMPP_SHA1_DIGEST_SIZE];
235 :
236 8 : const char *fname = conn->tls_client_cert;
237 8 : size_t fname_len = strlen(fname);
238 8 : xmpp_sha1_digest((void *)fname, fname_len, hash);
239 8 : if (fname_len && fname_len == conn->password_cache.fnamelen &&
240 6 : memcmp(hash, conn->password_cache.fname_hash, sizeof(hash)) == 0) {
241 6 : if (conn->password_cache.passlen) {
242 6 : memcpy(pw, conn->password_cache.pass,
243 : conn->password_cache.passlen + 1);
244 6 : return conn->password_cache.passlen;
245 : }
246 : }
247 2 : size_t max_len = pw_max == 256 ? pw_max : sizeof(conn->password_cache.pass);
248 2 : ret = conn->password_callback(conn->password_cache.pass, max_len, conn,
249 : conn->password_callback_userdata);
250 :
251 2 : if (ret < 0 || ret >= (ssize_t)max_len) {
252 0 : memset(conn->password_cache.pass, 0, sizeof(conn->password_cache.pass));
253 0 : return -1;
254 : }
255 2 : conn->password_cache.pass[ret] = '\0';
256 2 : memcpy(pw, conn->password_cache.pass, ret + 1);
257 2 : conn->password_cache.passlen = ret;
258 2 : conn->password_cache.fnamelen = fname_len;
259 2 : memcpy(conn->password_cache.fname_hash, hash, sizeof(hash));
260 2 : return conn->password_cache.passlen;
261 : }
262 :
263 16 : void tls_clear_password_cache(xmpp_conn_t *conn)
264 : {
265 16 : memset(&conn->password_cache, 0, sizeof(conn->password_cache));
266 16 : }
|