blob: 10b71d565294008f74c37ea4b6da7114fc686cb7 [file] [log] [blame]
William Lallemand6a66a5e2020-05-15 12:01:17 +02001/*
2 * Utility functions for SSL:
3 * Mostly generic functions that retrieve information from certificates
4 *
5 * Copyright (C) 2012 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
6 * Copyright (C) 2020 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14
15#include <common/buffer.h>
Willy Tarreau6019fab2020-05-27 16:26:00 +020016#include <haproxy/openssl-compat.h>
William Lallemand6a66a5e2020-05-15 12:01:17 +020017
18#include <proto/ssl_sock.h>
19
20#include <types/ssl_sock.h>
21
22/* fill a buffer with the algorithm and size of a public key */
23int cert_get_pkey_algo(X509 *crt, struct buffer *out)
24{
25 int bits = 0;
26 int sig = TLSEXT_signature_anonymous;
27 int len = -1;
28 EVP_PKEY *pkey;
29
30 pkey = X509_get_pubkey(crt);
31 if (pkey) {
32 bits = EVP_PKEY_bits(pkey);
33 switch(EVP_PKEY_base_id(pkey)) {
34 case EVP_PKEY_RSA:
35 sig = TLSEXT_signature_rsa;
36 break;
37 case EVP_PKEY_EC:
38 sig = TLSEXT_signature_ecdsa;
39 break;
40 case EVP_PKEY_DSA:
41 sig = TLSEXT_signature_dsa;
42 break;
43 }
44 EVP_PKEY_free(pkey);
45 }
46
47 switch(sig) {
48 case TLSEXT_signature_rsa:
49 len = chunk_printf(out, "RSA%d", bits);
50 break;
51 case TLSEXT_signature_ecdsa:
52 len = chunk_printf(out, "EC%d", bits);
53 break;
54 case TLSEXT_signature_dsa:
55 len = chunk_printf(out, "DSA%d", bits);
56 break;
57 default:
58 return 0;
59 }
60 if (len < 0)
61 return 0;
62 return 1;
63}
64
65/* Extract a serial from a cert, and copy it to a chunk.
66 * Returns 1 if serial is found and copied, 0 if no serial found and
67 * -1 if output is not large enough.
68 */
69int ssl_sock_get_serial(X509 *crt, struct buffer *out)
70{
71 ASN1_INTEGER *serial;
72
73 serial = X509_get_serialNumber(crt);
74 if (!serial)
75 return 0;
76
77 if (out->size < serial->length)
78 return -1;
79
80 memcpy(out->area, serial->data, serial->length);
81 out->data = serial->length;
82 return 1;
83}
84
85/* Extract a cert to der, and copy it to a chunk.
86 * Returns 1 if the cert is found and copied, 0 on der conversion failure
87 * and -1 if the output is not large enough.
88 */
89int ssl_sock_crt2der(X509 *crt, struct buffer *out)
90{
91 int len;
92 unsigned char *p = (unsigned char *) out->area;;
93
94 len =i2d_X509(crt, NULL);
95 if (len <= 0)
96 return 1;
97
98 if (out->size < len)
99 return -1;
100
101 i2d_X509(crt,&p);
102 out->data = len;
103 return 1;
104}
105
106
107/* Copy Date in ASN1_UTCTIME format in struct buffer out.
108 * Returns 1 if serial is found and copied, 0 if no valid time found
109 * and -1 if output is not large enough.
110 */
111int ssl_sock_get_time(ASN1_TIME *tm, struct buffer *out)
112{
113 if (tm->type == V_ASN1_GENERALIZEDTIME) {
114 ASN1_GENERALIZEDTIME *gentm = (ASN1_GENERALIZEDTIME *)tm;
115
116 if (gentm->length < 12)
117 return 0;
118 if (gentm->data[0] != 0x32 || gentm->data[1] != 0x30)
119 return 0;
120 if (out->size < gentm->length-2)
121 return -1;
122
123 memcpy(out->area, gentm->data+2, gentm->length-2);
124 out->data = gentm->length-2;
125 return 1;
126 }
127 else if (tm->type == V_ASN1_UTCTIME) {
128 ASN1_UTCTIME *utctm = (ASN1_UTCTIME *)tm;
129
130 if (utctm->length < 10)
131 return 0;
132 if (utctm->data[0] >= 0x35)
133 return 0;
134 if (out->size < utctm->length)
135 return -1;
136
137 memcpy(out->area, utctm->data, utctm->length);
138 out->data = utctm->length;
139 return 1;
140 }
141
142 return 0;
143}
144
145/* Extract an entry from a X509_NAME and copy its value to an output chunk.
146 * Returns 1 if entry found, 0 if entry not found, or -1 if output not large enough.
147 */
148int ssl_sock_get_dn_entry(X509_NAME *a, const struct buffer *entry, int pos,
149 struct buffer *out)
150{
151 X509_NAME_ENTRY *ne;
152 ASN1_OBJECT *obj;
153 ASN1_STRING *data;
154 const unsigned char *data_ptr;
155 int data_len;
156 int i, j, n;
157 int cur = 0;
158 const char *s;
159 char tmp[128];
160 int name_count;
161
162 name_count = X509_NAME_entry_count(a);
163
164 out->data = 0;
165 for (i = 0; i < name_count; i++) {
166 if (pos < 0)
167 j = (name_count-1) - i;
168 else
169 j = i;
170
171 ne = X509_NAME_get_entry(a, j);
172 obj = X509_NAME_ENTRY_get_object(ne);
173 data = X509_NAME_ENTRY_get_data(ne);
174 data_ptr = ASN1_STRING_get0_data(data);
175 data_len = ASN1_STRING_length(data);
176 n = OBJ_obj2nid(obj);
177 if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
178 i2t_ASN1_OBJECT(tmp, sizeof(tmp), obj);
179 s = tmp;
180 }
181
182 if (chunk_strcasecmp(entry, s) != 0)
183 continue;
184
185 if (pos < 0)
186 cur--;
187 else
188 cur++;
189
190 if (cur != pos)
191 continue;
192
193 if (data_len > out->size)
194 return -1;
195
196 memcpy(out->area, data_ptr, data_len);
197 out->data = data_len;
198 return 1;
199 }
200
201 return 0;
202
203}
204
205/*
206 * Extract the DN in the specified format from the X509_NAME and copy result to a chunk.
207 * Currently supports rfc2253 for returning LDAP V3 DNs.
208 * Returns 1 if dn entries exist, 0 if no dn entry was found.
209 */
210int ssl_sock_get_dn_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out)
211{
212 BIO *bio = NULL;
213 int ret = 0;
214 int data_len = 0;
215
216 if (chunk_strcmp(format, "rfc2253") == 0) {
217 bio = BIO_new(BIO_s_mem());
218 if (bio == NULL)
219 goto out;
220
221 if (X509_NAME_print_ex(bio, a, 0, XN_FLAG_RFC2253) < 0)
222 goto out;
223
224 if ((data_len = BIO_read(bio, out->area, out->size)) <= 0)
225 goto out;
226
227 out->data = data_len;
228
229 ret = 1;
230 }
231out:
232 if (bio)
233 BIO_free(bio);
234 return ret;
235}
236
237/* Extract and format full DN from a X509_NAME and copy result into a chunk
238 * Returns 1 if dn entries exits, 0 if no dn entry found or -1 if output is not large enough.
239 */
240int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out)
241{
242 X509_NAME_ENTRY *ne;
243 ASN1_OBJECT *obj;
244 ASN1_STRING *data;
245 const unsigned char *data_ptr;
246 int data_len;
247 int i, n, ln;
248 int l = 0;
249 const char *s;
250 char *p;
251 char tmp[128];
252 int name_count;
253
254
255 name_count = X509_NAME_entry_count(a);
256
257 out->data = 0;
258 p = out->area;
259 for (i = 0; i < name_count; i++) {
260 ne = X509_NAME_get_entry(a, i);
261 obj = X509_NAME_ENTRY_get_object(ne);
262 data = X509_NAME_ENTRY_get_data(ne);
263 data_ptr = ASN1_STRING_get0_data(data);
264 data_len = ASN1_STRING_length(data);
265 n = OBJ_obj2nid(obj);
266 if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
267 i2t_ASN1_OBJECT(tmp, sizeof(tmp), obj);
268 s = tmp;
269 }
270 ln = strlen(s);
271
272 l += 1 + ln + 1 + data_len;
273 if (l > out->size)
274 return -1;
275 out->data = l;
276
277 *(p++)='/';
278 memcpy(p, s, ln);
279 p += ln;
280 *(p++)='=';
281 memcpy(p, data_ptr, data_len);
282 p += data_len;
283 }
284
285 if (!out->data)
286 return 0;
287
288 return 1;
289}
290