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