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