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