1/* $OpenBSD: ssl_asn1.c,v 1.67 2023/07/08 16:40:13 beck Exp $ */
2/*
3 * Copyright (c) 2016 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <limits.h>
19
20#include <openssl/ssl.h>
21#include <openssl/x509.h>
22
23#include "bytestring.h"
24#include "ssl_local.h"
25
26#define SSLASN1_TAG	(CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)
27#define SSLASN1_TIME_TAG		(SSLASN1_TAG | 1)
28#define SSLASN1_TIMEOUT_TAG		(SSLASN1_TAG | 2)
29#define SSLASN1_PEER_CERT_TAG		(SSLASN1_TAG | 3)
30#define SSLASN1_SESSION_ID_CTX_TAG	(SSLASN1_TAG | 4)
31#define SSLASN1_VERIFY_RESULT_TAG	(SSLASN1_TAG | 5)
32#define SSLASN1_HOSTNAME_TAG		(SSLASN1_TAG | 6)
33#define SSLASN1_LIFETIME_TAG		(SSLASN1_TAG | 9)
34#define SSLASN1_TICKET_TAG		(SSLASN1_TAG | 10)
35
36static uint64_t
37time_max(void)
38{
39	if (sizeof(time_t) == sizeof(int32_t))
40		return INT32_MAX;
41	if (sizeof(time_t) == sizeof(int64_t))
42		return INT64_MAX;
43	return 0;
44}
45
46static int
47SSL_SESSION_encode(SSL_SESSION *s, unsigned char **out, size_t *out_len,
48    int ticket_encoding)
49{
50	CBB cbb, session, cipher_suite, session_id, master_key, time, timeout;
51	CBB peer_cert, sidctx, verify_result, hostname, lifetime, ticket, value;
52	unsigned char *peer_cert_bytes = NULL;
53	int len, rv = 0;
54	uint16_t cid;
55
56	if (!CBB_init(&cbb, 0))
57		goto err;
58
59	if (!CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE))
60		goto err;
61
62	/* Session ASN1 version. */
63	if (!CBB_add_asn1_uint64(&session, SSL_SESSION_ASN1_VERSION))
64		goto err;
65
66	/* TLS/SSL protocol version. */
67	if (s->ssl_version < 0)
68		goto err;
69	if (!CBB_add_asn1_uint64(&session, s->ssl_version))
70		goto err;
71
72	/* Cipher suite ID. */
73	/* XXX - require cipher to be non-NULL or always/only use cipher_id. */
74	cid = (uint16_t)(s->cipher_id & SSL3_CK_VALUE_MASK);
75	if (s->cipher != NULL)
76		cid = ssl3_cipher_get_value(s->cipher);
77	if (!CBB_add_asn1(&session, &cipher_suite, CBS_ASN1_OCTETSTRING))
78		goto err;
79	if (!CBB_add_u16(&cipher_suite, cid))
80		goto err;
81
82	/* Session ID - zero length for a ticket. */
83	if (!CBB_add_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING))
84		goto err;
85	if (!CBB_add_bytes(&session_id, s->session_id,
86	    ticket_encoding ? 0 : s->session_id_length))
87		goto err;
88
89	/* Master key. */
90	if (!CBB_add_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING))
91		goto err;
92	if (!CBB_add_bytes(&master_key, s->master_key, s->master_key_length))
93		goto err;
94
95	/* Time [1]. */
96	if (s->time != 0) {
97		if (s->time < 0)
98			goto err;
99		if (!CBB_add_asn1(&session, &time, SSLASN1_TIME_TAG))
100			goto err;
101		if (!CBB_add_asn1_uint64(&time, s->time))
102			goto err;
103	}
104
105	/* Timeout [2]. */
106	if (s->timeout != 0) {
107		if (s->timeout < 0)
108			goto err;
109		if (!CBB_add_asn1(&session, &timeout, SSLASN1_TIMEOUT_TAG))
110			goto err;
111		if (!CBB_add_asn1_uint64(&timeout, s->timeout))
112			goto err;
113	}
114
115	/* Peer certificate [3]. */
116	if (s->peer_cert != NULL) {
117		if ((len = i2d_X509(s->peer_cert, &peer_cert_bytes)) <= 0)
118			goto err;
119		if (!CBB_add_asn1(&session, &peer_cert, SSLASN1_PEER_CERT_TAG))
120			goto err;
121		if (!CBB_add_bytes(&peer_cert, peer_cert_bytes, len))
122			goto err;
123	}
124
125	/* Session ID context [4]. */
126	/* XXX - Actually handle this as optional? */
127	if (!CBB_add_asn1(&session, &sidctx, SSLASN1_SESSION_ID_CTX_TAG))
128		goto err;
129	if (!CBB_add_asn1(&sidctx, &value, CBS_ASN1_OCTETSTRING))
130		goto err;
131	if (!CBB_add_bytes(&value, s->sid_ctx, s->sid_ctx_length))
132		goto err;
133
134	/* Verify result [5]. */
135	if (s->verify_result != X509_V_OK) {
136		if (s->verify_result < 0)
137			goto err;
138		if (!CBB_add_asn1(&session, &verify_result,
139		    SSLASN1_VERIFY_RESULT_TAG))
140			goto err;
141		if (!CBB_add_asn1_uint64(&verify_result, s->verify_result))
142			goto err;
143	}
144
145	/* Hostname [6]. */
146	if (s->tlsext_hostname != NULL) {
147		if (!CBB_add_asn1(&session, &hostname, SSLASN1_HOSTNAME_TAG))
148			goto err;
149		if (!CBB_add_asn1(&hostname, &value, CBS_ASN1_OCTETSTRING))
150			goto err;
151		if (!CBB_add_bytes(&value, (const uint8_t *)s->tlsext_hostname,
152		    strlen(s->tlsext_hostname)))
153			goto err;
154	}
155
156	/* PSK identity hint [7]. */
157	/* PSK identity [8]. */
158
159	/* Ticket lifetime hint [9]. */
160	if (s->tlsext_tick_lifetime_hint > 0) {
161		if (!CBB_add_asn1(&session, &lifetime, SSLASN1_LIFETIME_TAG))
162			goto err;
163		if (!CBB_add_asn1_uint64(&lifetime,
164		    s->tlsext_tick_lifetime_hint))
165			goto err;
166	}
167
168	/* Ticket [10]. */
169	if (s->tlsext_tick != NULL) {
170		if (!CBB_add_asn1(&session, &ticket, SSLASN1_TICKET_TAG))
171			goto err;
172		if (!CBB_add_asn1(&ticket, &value, CBS_ASN1_OCTETSTRING))
173			goto err;
174		if (!CBB_add_bytes(&value, s->tlsext_tick, s->tlsext_ticklen))
175			goto err;
176	}
177
178	/* Compression method [11]. */
179	/* SRP username [12]. */
180
181	if (!CBB_finish(&cbb, out, out_len))
182		goto err;
183
184	rv = 1;
185
186 err:
187	CBB_cleanup(&cbb);
188	free(peer_cert_bytes);
189
190	return rv;
191}
192
193int
194SSL_SESSION_ticket(SSL_SESSION *ss, unsigned char **out, size_t *out_len)
195{
196	if (ss == NULL)
197		return 0;
198
199	if (ss->cipher == NULL && ss->cipher_id == 0)
200		return 0;
201
202	return SSL_SESSION_encode(ss, out, out_len, 1);
203}
204
205int
206i2d_SSL_SESSION(SSL_SESSION *ss, unsigned char **pp)
207{
208	unsigned char *data = NULL;
209	size_t data_len = 0;
210	int rv = -1;
211
212	if (ss == NULL)
213		return 0;
214
215	if (ss->cipher == NULL && ss->cipher_id == 0)
216		return 0;
217
218	if (!SSL_SESSION_encode(ss, &data, &data_len, 0))
219		goto err;
220
221	if (data_len > INT_MAX)
222		goto err;
223
224	if (pp != NULL) {
225		if (*pp == NULL) {
226			*pp = data;
227			data = NULL;
228		} else {
229			memcpy(*pp, data, data_len);
230			*pp += data_len;
231		}
232	}
233
234	rv = (int)data_len;
235
236 err:
237	freezero(data, data_len);
238
239	return rv;
240}
241LSSL_ALIAS(i2d_SSL_SESSION);
242
243SSL_SESSION *
244d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length)
245{
246	CBS cbs, session, cipher_suite, session_id, master_key, peer_cert;
247	CBS hostname, ticket;
248	uint64_t version, tls_version, stime, timeout, verify_result, lifetime;
249	const unsigned char *peer_cert_bytes;
250	uint16_t cipher_value;
251	SSL_SESSION *s = NULL;
252	size_t data_len;
253	int present;
254
255	if (a != NULL)
256		s = *a;
257
258	if (s == NULL) {
259		if ((s = SSL_SESSION_new()) == NULL) {
260			SSLerrorx(ERR_R_MALLOC_FAILURE);
261			return (NULL);
262		}
263	}
264
265	CBS_init(&cbs, *pp, length);
266
267	if (!CBS_get_asn1(&cbs, &session, CBS_ASN1_SEQUENCE))
268		goto err;
269
270	/* Session ASN1 version. */
271	if (!CBS_get_asn1_uint64(&session, &version))
272		goto err;
273	if (version != SSL_SESSION_ASN1_VERSION)
274		goto err;
275
276	/* TLS/SSL Protocol Version. */
277	if (!CBS_get_asn1_uint64(&session, &tls_version))
278		goto err;
279	if (tls_version > INT_MAX)
280		goto err;
281	s->ssl_version = (int)tls_version;
282
283	/* Cipher suite. */
284	if (!CBS_get_asn1(&session, &cipher_suite, CBS_ASN1_OCTETSTRING))
285		goto err;
286	if (!CBS_get_u16(&cipher_suite, &cipher_value))
287		goto err;
288	if (CBS_len(&cipher_suite) != 0)
289		goto err;
290
291	/* XXX - populate cipher instead? */
292	s->cipher = NULL;
293	s->cipher_id = SSL3_CK_ID | cipher_value;
294
295	/* Session ID. */
296	if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING))
297		goto err;
298	if (!CBS_write_bytes(&session_id, s->session_id, sizeof(s->session_id),
299	    &s->session_id_length))
300		goto err;
301
302	/* Master key. */
303	if (!CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING))
304		goto err;
305	if (!CBS_write_bytes(&master_key, s->master_key, sizeof(s->master_key),
306	    &s->master_key_length))
307		goto err;
308
309	/* Time [1]. */
310	s->time = time(NULL);
311	if (!CBS_get_optional_asn1_uint64(&session, &stime, SSLASN1_TIME_TAG,
312	    0))
313		goto err;
314	if (stime > time_max())
315		goto err;
316	if (stime != 0)
317		s->time = (time_t)stime;
318
319	/* Timeout [2]. */
320	s->timeout = 3;
321	if (!CBS_get_optional_asn1_uint64(&session, &timeout,
322	    SSLASN1_TIMEOUT_TAG, 0))
323		goto err;
324	if (timeout > LONG_MAX)
325		goto err;
326	if (timeout != 0)
327		s->timeout = (long)timeout;
328
329	/* Peer certificate [3]. */
330	X509_free(s->peer_cert);
331	s->peer_cert = NULL;
332	if (!CBS_get_optional_asn1(&session, &peer_cert, &present,
333	    SSLASN1_PEER_CERT_TAG))
334		goto err;
335	if (present) {
336		data_len = CBS_len(&peer_cert);
337		if (data_len > LONG_MAX)
338			goto err;
339		peer_cert_bytes = CBS_data(&peer_cert);
340		if (d2i_X509(&s->peer_cert, &peer_cert_bytes,
341		    (long)data_len) == NULL)
342			goto err;
343	}
344
345	/* Session ID context [4]. */
346	s->sid_ctx_length = 0;
347	if (!CBS_get_optional_asn1_octet_string(&session, &session_id, &present,
348	    SSLASN1_SESSION_ID_CTX_TAG))
349		goto err;
350	if (present) {
351		if (!CBS_write_bytes(&session_id, (uint8_t *)&s->sid_ctx,
352		    sizeof(s->sid_ctx), &s->sid_ctx_length))
353			goto err;
354	}
355
356	/* Verify result [5]. */
357	s->verify_result = X509_V_OK;
358	if (!CBS_get_optional_asn1_uint64(&session, &verify_result,
359	    SSLASN1_VERIFY_RESULT_TAG, X509_V_OK))
360		goto err;
361	if (verify_result > LONG_MAX)
362		goto err;
363	s->verify_result = (long)verify_result;
364
365	/* Hostname [6]. */
366	free(s->tlsext_hostname);
367	s->tlsext_hostname = NULL;
368	if (!CBS_get_optional_asn1_octet_string(&session, &hostname, &present,
369	    SSLASN1_HOSTNAME_TAG))
370		goto err;
371	if (present) {
372		if (CBS_contains_zero_byte(&hostname))
373			goto err;
374		if (!CBS_strdup(&hostname, &s->tlsext_hostname))
375			goto err;
376	}
377
378	/* PSK identity hint [7]. */
379	/* PSK identity [8]. */
380
381	/* Ticket lifetime [9]. */
382	s->tlsext_tick_lifetime_hint = 0;
383	if (!CBS_get_optional_asn1_uint64(&session, &lifetime,
384	    SSLASN1_LIFETIME_TAG, 0))
385		goto err;
386	if (lifetime > UINT32_MAX)
387		goto err;
388	if (lifetime > 0)
389		s->tlsext_tick_lifetime_hint = (uint32_t)lifetime;
390
391	/* Ticket [10]. */
392	free(s->tlsext_tick);
393	s->tlsext_tick = NULL;
394	if (!CBS_get_optional_asn1_octet_string(&session, &ticket, &present,
395	    SSLASN1_TICKET_TAG))
396		goto err;
397	if (present) {
398		if (!CBS_stow(&ticket, &s->tlsext_tick, &s->tlsext_ticklen))
399			goto err;
400	}
401
402	/* Compression method [11]. */
403	/* SRP username [12]. */
404
405	*pp = CBS_data(&cbs);
406
407	if (a != NULL)
408		*a = s;
409
410	return (s);
411
412 err:
413	ERR_asprintf_error_data("offset=%d", (int)(CBS_data(&cbs) - *pp));
414
415	if (s != NULL && (a == NULL || *a != s))
416		SSL_SESSION_free(s);
417
418	return (NULL);
419}
420LSSL_ALIAS(d2i_SSL_SESSION);
421