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