Added
Link Here
|
1 |
/* Licensed to the Apache Software Foundation (ASF) under one or more |
2 |
* contributor license agreements. See the NOTICE file distributed with |
3 |
* this work for additional information regarding copyright ownership. |
4 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
5 |
* (the "License"); you may not use this file except in compliance with |
6 |
* the License. You may obtain a copy of the License at |
7 |
* |
8 |
* http://www.apache.org/licenses/LICENSE-2.0 |
9 |
* |
10 |
* Unless required by applicable law or agreed to in writing, software |
11 |
* distributed under the License is distributed on an "AS IS" BASIS, |
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
* See the License for the specific language governing permissions and |
14 |
* limitations under the License. |
15 |
*/ |
16 |
|
17 |
#include <stdio.h> |
18 |
#include <stdlib.h> |
19 |
#include <string.h> |
20 |
|
21 |
#include <openssl/bio.h> |
22 |
#include <openssl/ocsp.h> |
23 |
#include <openssl/err.h> |
24 |
|
25 |
#include "apr_network_io.h" |
26 |
|
27 |
#include "ocsp_sock.h" |
28 |
|
29 |
/* defines with the values as seen by the asn1parse -dump openssl command */ |
30 |
#define ASN1_SEQUENCE 0x30 |
31 |
#define ASN1_OID 0x06 |
32 |
#define ASN1_STRING 0x86 |
33 |
|
34 |
|
35 |
|
36 |
/* Helps with error handling or realloc */ |
37 |
static void *xrealloc(void *buf, size_t len) |
38 |
{ |
39 |
void *newp; |
40 |
if((newp = realloc(buf, len)) == NULL) { |
41 |
free(buf); |
42 |
return NULL; |
43 |
} |
44 |
return newp; |
45 |
} |
46 |
|
47 |
/* parses the ocsp url and updates the ocsp_urls and nocsp_urls variables |
48 |
returns 0 on success, 1 on failure */ |
49 |
static int parse_ocsp_url(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls) |
50 |
{ |
51 |
char **new_ocsp_urls, *ocsp_url; |
52 |
int len, err = 0, new_nocsp_urls; |
53 |
if(*asn1 == ASN1_STRING) { |
54 |
len = *++asn1; |
55 |
asn1++; |
56 |
new_nocsp_urls = *nocsp_urls+1; |
57 |
if((new_ocsp_urls = realloc(*ocsp_urls,new_nocsp_urls)) == NULL) |
58 |
err = 1; |
59 |
if(!err) { |
60 |
*ocsp_urls = new_ocsp_urls; |
61 |
*nocsp_urls = new_nocsp_urls; |
62 |
*(*ocsp_urls + *nocsp_urls) = NULL; |
63 |
if((ocsp_url = malloc((len+1)*sizeof(char))) == NULL) |
64 |
err = 1; |
65 |
else { |
66 |
memcpy(ocsp_url, asn1, len); |
67 |
*(ocsp_url+len) = '\0'; |
68 |
*(*ocsp_urls + *nocsp_urls -1 ) = ocsp_url; |
69 |
} |
70 |
} |
71 |
} |
72 |
return err; |
73 |
|
74 |
} |
75 |
|
76 |
/* parses the ANS1 OID and if it is an OCSP OID then calls the parse_ocsp_url function */ |
77 |
static int parse_ASN1_OID(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls) |
78 |
{ |
79 |
int len, err = 0 ; |
80 |
const unsigned char OCSP_OID[] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01}; |
81 |
|
82 |
len = *++asn1; |
83 |
asn1++; |
84 |
if(memcmp(asn1, OCSP_OID, len) == 0 ) { |
85 |
asn1+=len; |
86 |
err = parse_ocsp_url(asn1, ocsp_urls, nocsp_urls); |
87 |
} |
88 |
return err; |
89 |
} |
90 |
|
91 |
|
92 |
/* Parses an ASN1 Sequence. It is a recursive function, since if it finds a sequence |
93 |
within the sequence it calls recursively itself. This function stops when it finds |
94 |
the end of the ASN1 sequence (marked by '\0'), so if there are other sequences within |
95 |
the same sequence the while loop parses the sequences */ |
96 |
|
97 |
/* This algo was developped with AIA in mind so it was tested only with this extension */ |
98 |
static int parse_ASN1_Sequence(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls) |
99 |
{ |
100 |
int len, err = 0; |
101 |
while( !err && *asn1 != '\0') { |
102 |
switch(*asn1) { |
103 |
case ASN1_SEQUENCE: |
104 |
len = *++asn1; |
105 |
asn1++; |
106 |
err = parse_ASN1_Sequence(asn1, ocsp_urls, nocsp_urls); |
107 |
break; |
108 |
case ASN1_OID: |
109 |
err = parse_ASN1_OID(asn1,ocsp_urls,nocsp_urls); |
110 |
return 0; |
111 |
break; |
112 |
default: |
113 |
err = 1; /* we shouldn't have any errors */ |
114 |
break; |
115 |
} |
116 |
asn1+=len; |
117 |
} |
118 |
return err; |
119 |
} |
120 |
|
121 |
/* the main function that gets the ASN1 encoding string and returns |
122 |
a pointer to a NULL terminated "array" of char *, that contains |
123 |
the ocsp_urls */ |
124 |
static char **decode_OCSP_url(ASN1_OCTET_STRING *os) |
125 |
{ |
126 |
char **response = NULL; |
127 |
unsigned char *ocsp_urls; |
128 |
int i, len, err = 0, numofresponses = 0 ; |
129 |
|
130 |
len = ASN1_STRING_length(os); |
131 |
|
132 |
ocsp_urls = malloc((size_t)(len+1)*sizeof(char)); |
133 |
memcpy(ocsp_urls,os->data, (size_t) len); |
134 |
ocsp_urls[len] = '\0'; |
135 |
|
136 |
if ((response = malloc(sizeof(char *))) == NULL) |
137 |
return NULL; |
138 |
*response = NULL; |
139 |
|
140 |
err = parse_ASN1_Sequence(ocsp_urls, &response, &numofresponses); |
141 |
if (err) { |
142 |
for(i = 0 ; i < numofresponses ; i++) |
143 |
free(response[i]); |
144 |
free(response); |
145 |
response = NULL; |
146 |
} |
147 |
|
148 |
free(ocsp_urls); |
149 |
return response; |
150 |
} |
151 |
|
152 |
|
153 |
|
154 |
/* stolen from openssl ocsp command */ |
155 |
static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, |
156 |
STACK_OF(OCSP_CERTID) *ids) |
157 |
{ |
158 |
OCSP_CERTID *id; |
159 |
if(!issuer) |
160 |
return 0; |
161 |
if(!*req) *req = OCSP_REQUEST_new(); |
162 |
if(!*req) goto err; |
163 |
id = OCSP_cert_to_id(NULL, cert, issuer); |
164 |
if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err; |
165 |
if(!OCSP_request_add0_id(*req, id)) goto err; |
166 |
return 1; |
167 |
|
168 |
err: |
169 |
return 0; |
170 |
} |
171 |
|
172 |
|
173 |
/* Creates the APR socket and connect to the hostname. Returns the |
174 |
socket or NULL if there is an error. |
175 |
*/ |
176 |
static apr_socket_t *make_socket(char *hostname, int port, apr_pool_t *mp) |
177 |
{ |
178 |
int r = 0; |
179 |
apr_sockaddr_t *sa_in; |
180 |
apr_status_t status; |
181 |
apr_socket_t *sock = NULL; |
182 |
|
183 |
|
184 |
status = apr_sockaddr_info_get(&sa_in, hostname, APR_INET, port, 0, mp); |
185 |
|
186 |
if(status == APR_SUCCESS) |
187 |
status = apr_socket_create(&sock, sa_in->family, SOCK_STREAM, APR_PROTO_TCP, mp); |
188 |
if(status == APR_SUCCESS) |
189 |
status = apr_socket_connect(sock, sa_in); |
190 |
|
191 |
if(status == APR_SUCCESS) |
192 |
return sock; |
193 |
return NULL; |
194 |
|
195 |
} |
196 |
|
197 |
|
198 |
/* Creates the request in a memory BIO in order to send it to the OCSP server. |
199 |
Most parts of this function are taken from mod_ssl support for OCSP (with some |
200 |
minor modifications |
201 |
*/ |
202 |
static BIO *serialize_request(OCSP_REQUEST *req, char *host, int port, char *path) |
203 |
{ |
204 |
BIO *bio; |
205 |
int len; |
206 |
|
207 |
len = i2d_OCSP_REQUEST(req, NULL); |
208 |
|
209 |
bio = BIO_new(BIO_s_mem()); |
210 |
|
211 |
BIO_printf(bio, "POST %s HTTP/1.0\r\n" |
212 |
"Host: %s:%d\r\n" |
213 |
"Content-Type: application/ocsp-request\r\n" |
214 |
"Content-Length: %d\r\n" |
215 |
"\r\n", |
216 |
path, host, port, len); |
217 |
|
218 |
if (i2d_OCSP_REQUEST_bio(bio, req) != 1) { |
219 |
BIO_free(bio); |
220 |
return NULL; |
221 |
} |
222 |
|
223 |
return bio; |
224 |
} |
225 |
|
226 |
|
227 |
/* Send the OCSP request to the OCSP server. Taken from mod_ssl OCSP support */ |
228 |
#define HUGE_STRING_LENGTH 8192 |
229 |
static int ocsp_send_req(apr_socket_t *sock, BIO *req) |
230 |
{ |
231 |
int len; |
232 |
char buf[HUGE_STRING_LENGTH]; |
233 |
apr_status_t rv; |
234 |
int ok = 1; |
235 |
|
236 |
while ((len = BIO_read(req, buf, sizeof buf)) > 0) { |
237 |
char *wbuf = buf; |
238 |
apr_size_t remain = len; |
239 |
|
240 |
do { |
241 |
apr_size_t wlen = remain; |
242 |
rv = apr_socket_send(sock, wbuf, &wlen); |
243 |
wbuf += remain; |
244 |
remain -= wlen; |
245 |
} while (rv == APR_SUCCESS && remain > 0); |
246 |
|
247 |
if (rv != APR_SUCCESS) { |
248 |
apr_socket_close(sock); |
249 |
ok = 0; |
250 |
} |
251 |
} |
252 |
|
253 |
return ok; |
254 |
} |
255 |
|
256 |
|
257 |
|
258 |
/* Parses the buffer from the response and extracts the OCSP response. |
259 |
Taken from openssl library */ |
260 |
static OCSP_RESPONSE *parse_ocsp_resp(char *buf, int len) |
261 |
{ |
262 |
BIO *mem = NULL; |
263 |
char tmpbuf[1024]; |
264 |
OCSP_RESPONSE *resp = NULL; |
265 |
char *p, *q, *r; |
266 |
int retcode; |
267 |
|
268 |
mem = BIO_new(BIO_s_mem()); |
269 |
if(mem == NULL) |
270 |
return NULL; |
271 |
|
272 |
BIO_write(mem, buf, len); /* write the buffer to the bio */ |
273 |
if(BIO_gets(mem, tmpbuf, 512) <= 0) { |
274 |
OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); |
275 |
goto err; |
276 |
} |
277 |
/* Parse the HTTP response. This will look like this: |
278 |
* "HTTP/1.0 200 OK". We need to obtain the numeric code and |
279 |
* (optional) informational message. |
280 |
*/ |
281 |
|
282 |
/* Skip to first white space (passed protocol info) */ |
283 |
for(p = tmpbuf; *p && !isspace((unsigned char)*p); p++) continue; |
284 |
if(!*p) { |
285 |
goto err; |
286 |
} |
287 |
/* Skip past white space to start of response code */ |
288 |
while(isspace((unsigned char)*p)) p++; |
289 |
if(!*p) { |
290 |
goto err; |
291 |
} |
292 |
/* Find end of response code: first whitespace after start of code */ |
293 |
for(q = p; *q && !isspace((unsigned char)*q); q++) continue; |
294 |
if(!*q) { |
295 |
goto err; |
296 |
} |
297 |
/* Set end of response code and start of message */ |
298 |
*q++ = 0; |
299 |
/* Attempt to parse numeric code */ |
300 |
retcode = strtoul(p, &r, 10); |
301 |
if(*r) goto err; |
302 |
/* Skip over any leading white space in message */ |
303 |
while(isspace((unsigned char)*q)) q++; |
304 |
if(*q) { |
305 |
/* Finally zap any trailing white space in message (include CRLF) */ |
306 |
/* We know q has a non white space character so this is OK */ |
307 |
for(r = q + strlen(q) - 1; isspace((unsigned char)*r); r--) *r = 0; |
308 |
} |
309 |
if(retcode != 200) { |
310 |
goto err; |
311 |
} |
312 |
/* Find blank line marking beginning of content */ |
313 |
while(BIO_gets(mem, tmpbuf, 512) > 0) |
314 |
{ |
315 |
for(p = tmpbuf; isspace((unsigned char)*p); p++) continue; |
316 |
if(!*p) break; |
317 |
} |
318 |
if(*p) { |
319 |
goto err; |
320 |
} |
321 |
if(!(resp = d2i_OCSP_RESPONSE_bio(mem, NULL))) { |
322 |
goto err; |
323 |
} |
324 |
err: |
325 |
BIO_free(mem); |
326 |
return resp; |
327 |
|
328 |
|
329 |
} |
330 |
|
331 |
|
332 |
/* Reads the respnse from the APR socket to a buffer, and parses the buffer to |
333 |
return the OCSP response */ |
334 |
#define ADDLEN 512 |
335 |
static OCSP_RESPONSE *ocsp_get_resp(apr_socket_t *sock) |
336 |
{ |
337 |
int buflen = 0, totalread = 0, readlen; |
338 |
char *buf, tmpbuf[ADDLEN]; |
339 |
apr_status_t rv = APR_SUCCESS; |
340 |
|
341 |
OCSP_RESPONSE *resp; |
342 |
|
343 |
buflen = ADDLEN; |
344 |
buf = malloc(buflen); |
345 |
if(buf == NULL) |
346 |
return NULL; |
347 |
|
348 |
while(rv == APR_SUCCESS ) { |
349 |
readlen = sizeof(tmpbuf); |
350 |
rv = apr_socket_recv(sock, tmpbuf, &readlen); |
351 |
if(rv == APR_SUCCESS) { /* if we have read something .. we can put it in the buffer*/ |
352 |
if((totalread + readlen) >= buflen) { |
353 |
buflen+=ADDLEN; /* if needed we enlarge the buffer */ |
354 |
buf = xrealloc(buf,buflen); |
355 |
if (buf == NULL) { |
356 |
return NULL; |
357 |
} |
358 |
} |
359 |
memcpy(buf+totalread, tmpbuf, readlen); /* the copy to the buffer */ |
360 |
totalread+=readlen; /* update the total bytes read */ |
361 |
} |
362 |
else { |
363 |
if (rv == APR_EOF && readlen == 0 ) |
364 |
; /* EOF, normal situation */ |
365 |
else if ( readlen == 0 ) { |
366 |
/* Not success, and readlen == 0 .. some error */ |
367 |
free(buf); |
368 |
return NULL; |
369 |
} |
370 |
} |
371 |
} |
372 |
|
373 |
|
374 |
resp = parse_ocsp_resp(buf, buflen); |
375 |
|
376 |
free(buf); |
377 |
|
378 |
return resp; |
379 |
} |
380 |
|
381 |
/* Creates and OCSP request and returns the OCSP_RESPONSE */ |
382 |
static OCSP_RESPONSE *get_ocsp_response(X509 *cert, X509 *issuer, char *url) |
383 |
{ |
384 |
OCSP_RESPONSE *ocsp_resp; |
385 |
OCSP_REQUEST *ocsp_req; |
386 |
BIO *bio_req; |
387 |
char *hostname, *path, *c_port; |
388 |
int port, use_ssl; |
389 |
STACK_OF(OCSP_CERTID) *ids = NULL; |
390 |
int ok = 0; |
391 |
|
392 |
apr_socket_t *apr_sock=NULL; |
393 |
|
394 |
apr_pool_t *mp; |
395 |
|
396 |
apr_pool_create(&mp, NULL); |
397 |
|
398 |
ids = sk_OCSP_CERTID_new_null(); |
399 |
|
400 |
/* problem parsing the URL */ |
401 |
if(OCSP_parse_url(url,&hostname, &c_port, &path, &use_ssl) == 0 ) { |
402 |
sk_OCP_CERTID_free(ids); |
403 |
return NULL; |
404 |
} |
405 |
|
406 |
/* Create the OCSP request */ |
407 |
if(sscanf(c_port, "%d", &port) != 1) |
408 |
goto end; |
409 |
ocsp_req = OCSP_REQUEST_new(); |
410 |
if(ocsp_req == NULL) |
411 |
return NULL; |
412 |
if(add_ocsp_cert(&ocsp_req,cert,issuer,ids) == 0 ) |
413 |
goto free_req; |
414 |
|
415 |
/* create the BIO with the request to send */ |
416 |
bio_req = serialize_request(ocsp_req, hostname, port, path); |
417 |
if(bio_req == NULL) { |
418 |
goto free_req; |
419 |
} |
420 |
|
421 |
apr_sock = make_socket(hostname, port, mp); |
422 |
if (apr_sock == NULL) { |
423 |
ocsp_resp = NULL; |
424 |
goto free_bio; |
425 |
} |
426 |
|
427 |
ok = ocsp_send_req(apr_sock, bio_req); |
428 |
if(ok) |
429 |
ocsp_resp = ocsp_get_resp(apr_sock); |
430 |
|
431 |
free_bio: |
432 |
BIO_free(bio_req); |
433 |
|
434 |
free_req: |
435 |
if(apr_sock) |
436 |
apr_socket_close(apr_sock); |
437 |
|
438 |
apr_pool_destroy(mp); |
439 |
|
440 |
sk_OCSP_CERTID_free(ids); |
441 |
OCSP_REQUEST_free(ocsp_req); |
442 |
|
443 |
end: |
444 |
return ocsp_resp; |
445 |
} |
446 |
|
447 |
/* Process the OCSP_RESPONSE and returns the corresponding |
448 |
answert according to the status. |
449 |
*/ |
450 |
static int processOCSPResponse(OCSP_RESPONSE *ocsp_resp) |
451 |
{ |
452 |
int r, o = V_OCSP_CERTSTATUS_UNKNOWN, i; |
453 |
OCSP_BASICRESP *bs; |
454 |
OCSP_SINGLERESP *ss; |
455 |
|
456 |
r = OCSP_response_status(ocsp_resp); |
457 |
|
458 |
if(r != OCSP_RESPONSE_STATUS_SUCCESSFUL) { |
459 |
OCSP_RESPONSE_free(ocsp_resp); |
460 |
return OCSP_STATUS_UNKNOWN; |
461 |
} |
462 |
bs = OCSP_response_get1_basic(ocsp_resp); |
463 |
|
464 |
ss = OCSP_resp_get0(bs,0); /* we know we have only 1 request */ |
465 |
|
466 |
i = OCSP_single_get0_status(ss, NULL, NULL, NULL, NULL); |
467 |
if ( i == V_OCSP_CERTSTATUS_GOOD ) |
468 |
o = OCSP_STATUS_OK; |
469 |
else if ( i == V_OCSP_CERTSTATUS_REVOKED ) |
470 |
o = OCSP_STATUS_REVOKED; |
471 |
else if ( i == V_OCSP_CERTSTATUS_UNKNOWN) |
472 |
o = OCSP_STATUS_UNKNOWN; |
473 |
|
474 |
/* we clean up */ |
475 |
OCSP_RESPONSE_free(ocsp_resp); |
476 |
|
477 |
return o; |
478 |
} |
479 |
|
480 |
|
481 |
int ocspRequest(X509 *cert, X509 *issuer) |
482 |
{ |
483 |
char **ocsp_urls = NULL; |
484 |
int nid, i; |
485 |
X509_EXTENSION *ext; |
486 |
ASN1_OCTET_STRING *os; |
487 |
|
488 |
/* Get the proper extension */ |
489 |
nid = X509_get_ext_by_NID(cert,NID_info_access,-1); |
490 |
if(nid >= 0 ) { |
491 |
ext = X509_get_ext(cert,nid); |
492 |
os = X509_EXTENSION_get_data(ext); |
493 |
|
494 |
ocsp_urls = decode_OCSP_url(os); |
495 |
} |
496 |
|
497 |
/* if we find the extensions and we can parse it check |
498 |
the ocsp status. Otherwise, return OCSP_STATUS_UNKNOWN */ |
499 |
if(ocsp_urls != NULL) { |
500 |
OCSP_RESPONSE *resp; |
501 |
/* for the time being just check for the fist response .. a better |
502 |
approach is to iterate for all the possible ocsp urls */ |
503 |
resp = get_ocsp_response(cert, issuer, ocsp_urls[0]); |
504 |
|
505 |
/* memory clean up */ |
506 |
for(i = 0 ; ocsp_urls[i] != NULL ; i++ ) |
507 |
free(ocsp_urls[i]); |
508 |
free(ocsp_urls); |
509 |
if(resp != NULL) |
510 |
return processOCSPResponse(resp); |
511 |
} |
512 |
return OCSP_STATUS_UNKNOWN; |
513 |
} |