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 <sys/types.h> |
18 |
#include <sys/socket.h> |
19 |
|
20 |
#include <stdio.h> |
21 |
#include <stdlib.h> |
22 |
#include <string.h> |
23 |
|
24 |
#include <netdb.h> |
25 |
#include <netinet/in.h> |
26 |
#include <arpa/inet.h> |
27 |
|
28 |
#include <openssl/bio.h> |
29 |
#include <openssl/ocsp.h> |
30 |
#include <openssl/err.h> |
31 |
|
32 |
|
33 |
#include "ocsp_sock.h" |
34 |
|
35 |
/* defines with the values as seen by the asn1parse -dump openssl command */ |
36 |
#define ASN1_SEQUENCE 0x30 |
37 |
#define ASN1_OID 0x06 |
38 |
#define ASN1_STRING 0x86 |
39 |
|
40 |
|
41 |
|
42 |
/* parses the ocsp url and updates the ocsp_urls and nocsp_urls variables |
43 |
returns 0 on success, 1 on failure */ |
44 |
static int parse_ocsp_url(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls) |
45 |
{ |
46 |
char **new_ocsp_urls, *ocsp_url; |
47 |
int len, err = 0, new_nocsp_urls; |
48 |
if(*asn1 == ASN1_STRING) { |
49 |
len = *++asn1; |
50 |
asn1++; |
51 |
new_nocsp_urls = *nocsp_urls+1; |
52 |
if((new_ocsp_urls = realloc(*ocsp_urls,new_nocsp_urls)) == NULL) |
53 |
err = 1; |
54 |
if(!err) { |
55 |
*ocsp_urls = new_ocsp_urls; |
56 |
*nocsp_urls = new_nocsp_urls; |
57 |
*(*ocsp_urls + *nocsp_urls) = NULL; |
58 |
if((ocsp_url = malloc((len+1)*sizeof(char))) == NULL) |
59 |
err = 1; |
60 |
else { |
61 |
memcpy(ocsp_url, asn1, len); |
62 |
*(ocsp_url+len) = '\0'; |
63 |
*(*ocsp_urls + *nocsp_urls -1 ) = ocsp_url; |
64 |
} |
65 |
} |
66 |
} |
67 |
return err; |
68 |
|
69 |
} |
70 |
|
71 |
/* parses the ANS1 OID and if it is an OCSP OID then calls the parse_ocsp_url function */ |
72 |
static int parse_ASN1_OID(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls) |
73 |
{ |
74 |
int len, err = 0 ; |
75 |
const unsigned char OCSP_OID[] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01}; |
76 |
|
77 |
len = *++asn1; |
78 |
asn1++; |
79 |
if(memcmp(asn1, OCSP_OID, len) == 0 ) { |
80 |
asn1+=len; |
81 |
err = parse_ocsp_url(asn1, ocsp_urls, nocsp_urls); |
82 |
} |
83 |
return err; |
84 |
} |
85 |
|
86 |
|
87 |
/* Parses an ASN1 Sequence. It is a recursive function, since if it finds a sequence |
88 |
within the sequence it calls recursively itself. This function stops when it finds |
89 |
the end of the ASN1 sequence (marked by '\0'), so if there are other sequences within |
90 |
the same sequence the while loop parses the sequences */ |
91 |
|
92 |
/* This algo was developped with AIA in mind so it was tested only with this extension */ |
93 |
static int parse_ASN1_Sequence(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls) |
94 |
{ |
95 |
int len, err = 0; |
96 |
while( !err && *asn1 != '\0') { |
97 |
switch(*asn1) { |
98 |
case ASN1_SEQUENCE: |
99 |
len = *++asn1; |
100 |
asn1++; |
101 |
err = parse_ASN1_Sequence(asn1, ocsp_urls, nocsp_urls); |
102 |
break; |
103 |
case ASN1_OID: |
104 |
err = parse_ASN1_OID(asn1,ocsp_urls,nocsp_urls); |
105 |
return 0; |
106 |
break; |
107 |
default: |
108 |
err = 1; /* we shouldn't have any errors */ |
109 |
break; |
110 |
} |
111 |
asn1+=len; |
112 |
} |
113 |
return err; |
114 |
} |
115 |
|
116 |
/* the main function that gets the ASN1 encoding string and returns |
117 |
a pointer to a NULL terminated "array" of char *, that contains |
118 |
the ocsp_urls */ |
119 |
static char **decode_OCSP_url(ASN1_OCTET_STRING *os) |
120 |
{ |
121 |
char **response = NULL; |
122 |
unsigned char *ocsp_urls; |
123 |
int i, len, err = 0, numofresponses = 0 ; |
124 |
|
125 |
len = ASN1_STRING_length(os); |
126 |
|
127 |
ocsp_urls = malloc((size_t)(len+1)*sizeof(char)); |
128 |
memcpy(ocsp_urls,os->data, (size_t) len); |
129 |
ocsp_urls[len] = '\0'; |
130 |
|
131 |
if ((response = malloc(sizeof(char *))) == NULL) |
132 |
return NULL; |
133 |
*response = NULL; |
134 |
|
135 |
err = parse_ASN1_Sequence(ocsp_urls, &response, &numofresponses); |
136 |
if (err) { |
137 |
for(i = 0 ; i < numofresponses ; i++) |
138 |
free(response[i]); |
139 |
free(response); |
140 |
response = NULL; |
141 |
} |
142 |
|
143 |
free(ocsp_urls); |
144 |
return response; |
145 |
} |
146 |
|
147 |
|
148 |
|
149 |
/* stolen from openssl ocsp command */ |
150 |
static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, |
151 |
STACK_OF(OCSP_CERTID) *ids) |
152 |
{ |
153 |
OCSP_CERTID *id; |
154 |
if(!issuer) |
155 |
return 0; |
156 |
if(!*req) *req = OCSP_REQUEST_new(); |
157 |
if(!*req) goto err; |
158 |
id = OCSP_cert_to_id(NULL, cert, issuer); |
159 |
if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err; |
160 |
if(!OCSP_request_add0_id(*req, id)) goto err; |
161 |
return 1; |
162 |
|
163 |
err: |
164 |
return 0; |
165 |
} |
166 |
|
167 |
|
168 |
/* Creates a socket, connects to the OCSP host and then |
169 |
returns a BIO object than can be used from the openssl |
170 |
library |
171 |
|
172 |
TODO : Suport https connections |
173 |
*/ |
174 |
static BIO *make_socket(char *hostname, int port) |
175 |
{ |
176 |
BIO *biosock; |
177 |
struct hostent *host; |
178 |
int sock, r = 0; |
179 |
struct sockaddr_in sa_in; |
180 |
|
181 |
bzero(&sa_in, sizeof(struct sockaddr_in)); |
182 |
|
183 |
|
184 |
sa_in.sin_family = AF_INET; |
185 |
sa_in.sin_port = htons(port); |
186 |
|
187 |
sa_in.sin_addr.s_addr = inet_addr(hostname); /* if the adrress is |
188 |
already in IP notation |
189 |
then we can use it */ |
190 |
if(sa_in.sin_addr.s_addr == -1) { |
191 |
h_errno = NETDB_SUCCESS; /* Clear h_errno */ |
192 |
do { |
193 |
host = gethostbyname(hostname); /* If we have a try again error */ |
194 |
r++; |
195 |
}while(r < 10 && h_errno == TRY_AGAIN); /* we try for 10 times */ |
196 |
/* for all other errors , we just fail */ |
197 |
if(host == NULL) |
198 |
return NULL; |
199 |
|
200 |
memcpy(&(sa_in.sin_addr), host->h_addr, sizeof(sa_in.sin_addr)); |
201 |
if(sa_in.sin_addr.s_addr == -1) |
202 |
return NULL; |
203 |
} |
204 |
|
205 |
sock = socket(AF_INET,SOCK_STREAM, 0); |
206 |
if(sock == -1) |
207 |
return NULL; |
208 |
|
209 |
if ( (connect(sock,(struct sockaddr *) &sa_in, sizeof(struct sockaddr_in))) == -1) |
210 |
return NULL; |
211 |
|
212 |
biosock = BIO_new_socket(sock, BIO_CLOSE); /* when we free we want the socket to close |
213 |
BIO_CLOSE does this (????) */ |
214 |
return biosock; |
215 |
|
216 |
} |
217 |
|
218 |
/* Creates and OCSP request and returns the OCSP_RESPONSE */ |
219 |
static OCSP_RESPONSE *get_ocsp_response(X509 *cert, X509 *issuer, char *url) |
220 |
{ |
221 |
OCSP_RESPONSE *ocsp_resp; |
222 |
OCSP_REQUEST *ocsp_req; |
223 |
BIO *sock; |
224 |
char *hostname, *path, *c_port; |
225 |
int port, use_ssl; |
226 |
STACK_OF(OCSP_CERTID) *ids = NULL; |
227 |
|
228 |
ids = sk_OCSP_CERTID_new_null(); |
229 |
|
230 |
/* problem parsing the URL */ |
231 |
if(OCSP_parse_url(url,&hostname, &c_port, &path, &use_ssl) == 0 ) { |
232 |
sk_OCP_CERTID_free(ids); |
233 |
return NULL; |
234 |
} |
235 |
|
236 |
/* Create the OCSP request */ |
237 |
if(sscanf(c_port, "%d", &port) != 1) |
238 |
goto end; |
239 |
ocsp_req = OCSP_REQUEST_new(); |
240 |
if(ocsp_req == NULL) |
241 |
return NULL; |
242 |
if(add_ocsp_cert(&ocsp_req,cert,issuer,ids) == 0 ) |
243 |
goto free_req; |
244 |
|
245 |
sock = make_socket(hostname, port); |
246 |
if (sock == NULL) { |
247 |
ocsp_resp = NULL; |
248 |
goto free_req; |
249 |
} |
250 |
|
251 |
ocsp_resp = OCSP_sendreq_bio(sock,path,ocsp_req); |
252 |
|
253 |
|
254 |
BIO_free(sock); |
255 |
|
256 |
free_req: |
257 |
sk_OCSP_CERTID_free(ids); |
258 |
OCSP_REQUEST_free(ocsp_req); |
259 |
|
260 |
end: |
261 |
return ocsp_resp; |
262 |
} |
263 |
|
264 |
/* Process the OCSP_RESPONSE and returns the corresponding |
265 |
answert according to the status. |
266 |
*/ |
267 |
static int processOCSPResponse(OCSP_RESPONSE *ocsp_resp) |
268 |
{ |
269 |
int r, o = V_OCSP_CERTSTATUS_UNKNOWN, i; |
270 |
OCSP_BASICRESP *bs; |
271 |
OCSP_SINGLERESP *ss; |
272 |
|
273 |
r = OCSP_response_status(ocsp_resp); |
274 |
|
275 |
if(r != OCSP_RESPONSE_STATUS_SUCCESSFUL) { |
276 |
OCSP_RESPONSE_free(ocsp_resp); |
277 |
return OCSP_STATUS_UNKNOWN; |
278 |
} |
279 |
bs = OCSP_response_get1_basic(ocsp_resp); |
280 |
|
281 |
ss = OCSP_resp_get0(bs,0); /* we know we have only 1 request */ |
282 |
|
283 |
i = OCSP_single_get0_status(ss, NULL, NULL, NULL, NULL); |
284 |
if ( i == V_OCSP_CERTSTATUS_GOOD ) |
285 |
o = OCSP_STATUS_OK; |
286 |
else if ( i == V_OCSP_CERTSTATUS_REVOKED ) |
287 |
o = OCSP_STATUS_REVOKED; |
288 |
else if ( i == V_OCSP_CERTSTATUS_UNKNOWN) |
289 |
o = OCSP_STATUS_UNKNOWN; |
290 |
|
291 |
/* we clean up */ |
292 |
OCSP_RESPONSE_free(ocsp_resp); |
293 |
|
294 |
return o; |
295 |
} |
296 |
|
297 |
|
298 |
int ocspRequest(X509 *cert, X509 *issuer) |
299 |
{ |
300 |
char **ocsp_urls = NULL; |
301 |
int nid, i; |
302 |
X509_EXTENSION *ext; |
303 |
ASN1_OCTET_STRING *os; |
304 |
|
305 |
/* Get the proper extension */ |
306 |
nid = X509_get_ext_by_NID(cert,NID_info_access,-1); |
307 |
if(nid >= 0 ) { |
308 |
ext = X509_get_ext(cert,nid); |
309 |
os = X509_EXTENSION_get_data(ext); |
310 |
|
311 |
ocsp_urls = decode_OCSP_url(os); |
312 |
} |
313 |
|
314 |
/* if we find the extensions and we can parse it check |
315 |
the ocsp status. Otherwise, return OCSP_STATUS_UNKNOWN */ |
316 |
if(ocsp_urls != NULL) { |
317 |
OCSP_RESPONSE *resp; |
318 |
/* for the time being just check for the fist response .. a better |
319 |
approach is to iterate for all the possible ocsp urls */ |
320 |
resp = get_ocsp_response(cert, issuer, ocsp_urls[0]); |
321 |
|
322 |
/* memory clean up */ |
323 |
for(i = 0 ; ocsp_urls[i] != NULL ; i++ ) |
324 |
free(ocsp_urls[i]); |
325 |
free(ocsp_urls); |
326 |
|
327 |
return processOCSPResponse(resp); |
328 |
} |
329 |
return OCSP_STATUS_UNKNOWN; |
330 |
} |