]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/CyaSSL/src/ocsp.c
3b3dea968185f4bc4b4acaf833a1a8504dae29b6
[freertos] / FreeRTOS-Plus / Source / CyaSSL / src / ocsp.c
1 /* ocsp.c
2  *
3  * Copyright (C) 2006-2012 Sawtooth Consulting Ltd.
4  *
5  * This file is part of CyaSSL.
6  *
7  * CyaSSL is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * CyaSSL is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #ifdef HAVE_CONFIG_H
23     #include <config.h>
24 #endif
25
26 #ifdef HAVE_OCSP
27
28 #include <cyassl/error.h>
29 #include <cyassl/ocsp.h>
30 #include <cyassl/internal.h>
31 #include <ctype.h>
32
33 #include <string.h>
34 #include <unistd.h>
35 #include <netdb.h>
36 #include <netinet/in.h>
37 #include <netinet/tcp.h>
38 #include <arpa/inet.h>
39 #include <sys/ioctl.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43
44
45 CYASSL_API int ocsp_test(unsigned char* buf, int sz);
46 #define CYASSL_OCSP_ENABLE       0x0001 /* Enable OCSP lookups */
47 #define CYASSL_OCSP_URL_OVERRIDE 0x0002 /* Use the override URL instead of URL
48                                          * in certificate */
49
50 typedef struct sockaddr_in  SOCKADDR_IN_T;
51 #define AF_INET_V    AF_INET
52 #define SOCKET_T unsigned int
53    
54
55 int CyaSSL_OCSP_Init(CYASSL_OCSP* ocsp)
56 {
57     if (ocsp != NULL) {
58         XMEMSET(ocsp, 0, sizeof(*ocsp));
59         return 0;
60     }
61
62     return -1;
63 }
64
65
66 static void FreeOCSP_Entry(OCSP_Entry* ocspe)
67 {
68     CertStatus* tmp = ocspe->status;
69
70     CYASSL_ENTER("FreeOCSP_Entry");
71
72     while (tmp) {
73         CertStatus* next = tmp->next;
74         XFREE(tmp, NULL, DYNAMIC_TYPE_OCSP_STATUS);
75         tmp = next;
76     }
77 }
78
79
80 void CyaSSL_OCSP_Cleanup(CYASSL_OCSP* ocsp)
81 {
82     OCSP_Entry* tmp = ocsp->ocspList;
83
84     ocsp->enabled = 0;
85     while (tmp) {
86         OCSP_Entry* next = tmp->next;
87         FreeOCSP_Entry(tmp);
88         XFREE(tmp, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
89         tmp = next;
90     }
91 }
92
93
94 static int decode_url(const char* url, int urlSz,
95     char* outName, char* outPath, int* outPort)
96 {
97     if (outName != NULL && outPath != NULL && outPort != NULL)
98     {
99         if (url == NULL || urlSz == 0)
100         {
101             *outName = 0;
102             *outPath = 0;
103             *outPort = 0;
104         }
105         else
106         {
107             int i, cur;
108     
109             /* need to break the url down into scheme, address, and port */
110             /* "http://example.com:8080/" */
111             if (XSTRNCMP(url, "http://", 7) == 0) {
112                 cur = 7;
113             } else cur = 0;
114     
115             i = 0;
116             while (url[cur] != 0 && url[cur] != ':' && url[cur] != '/') {
117                 outName[i++] = url[cur++];
118             }
119             outName[i] = 0;
120             /* Need to pick out the path after the domain name */
121     
122             if (cur < urlSz && url[cur] == ':') {
123                 char port[6];
124                 int j;
125                 i = 0;
126                 cur++;
127                 while (cur < urlSz && url[cur] != 0 && url[cur] != '/' &&
128                         i < 6) {
129                     port[i++] = url[cur++];
130                 }
131     
132                 *outPort = 0;
133                 for (j = 0; j < i; j++) {
134                     if (port[j] < '0' || port[j] > '9') return -1;
135                     *outPort = (*outPort * 10) + (port[j] - '0');
136                 }
137             }
138             else
139                 *outPort = 80;
140     
141             if (cur < urlSz && url[cur] == '/') {
142                 i = 0;
143                 while (cur < urlSz && url[cur] != 0 && i < 80) {
144                     outPath[i++] = url[cur++];
145                 }
146                 outPath[i] = 0;
147             }
148             else {
149                 outPath[0] = '/';
150                 outPath[1] = 0;
151             }
152         }
153     }
154
155     return 0;
156 }
157
158
159 int CyaSSL_OCSP_set_override_url(CYASSL_OCSP* ocsp, const char* url)
160 {
161     if (ocsp != NULL) {
162         int urlSz = strlen(url);
163         decode_url(url, urlSz,
164             ocsp->overrideName, ocsp->overridePath, &ocsp->overridePort);
165         return 1;
166     }
167
168     return 0;
169 }
170
171
172 static INLINE void tcp_socket(SOCKET_T* sockfd, SOCKADDR_IN_T* addr,
173                               const char* peer, word16 port)
174 {
175     const char* host = peer;
176
177     /* peer could be in human readable form */
178     if (peer != INADDR_ANY && isalpha(peer[0])) {
179         struct hostent* entry = gethostbyname(peer);
180
181         if (entry) {
182             struct sockaddr_in tmp;
183             memset(&tmp, 0, sizeof(struct sockaddr_in));
184             memcpy(&tmp.sin_addr.s_addr, entry->h_addr_list[0],
185                    entry->h_length);
186             host = inet_ntoa(tmp.sin_addr);
187         }
188         else {
189             CYASSL_MSG("no entry for host");
190         }
191     }
192
193     *sockfd = socket(AF_INET_V, SOCK_STREAM, 0);
194     memset(addr, 0, sizeof(SOCKADDR_IN_T));
195
196     addr->sin_family = AF_INET_V;
197     addr->sin_port = htons(port);
198     if (host == INADDR_ANY)
199         addr->sin_addr.s_addr = INADDR_ANY;
200     else
201         addr->sin_addr.s_addr = inet_addr(host);
202 }
203
204
205 static INLINE void tcp_connect(SOCKET_T* sockfd, const char* ip, word16 port)
206 {
207     SOCKADDR_IN_T addr;
208     tcp_socket(sockfd, &addr, ip, port);
209
210     if (connect(*sockfd, (const struct sockaddr*)&addr, sizeof(addr)) != 0) {
211         CYASSL_MSG("tcp connect failed");
212     }
213 }
214
215
216 static int build_http_request(const char* domainName, const char* path,
217                                     int ocspReqSz, byte* buf, int bufSize)
218 {
219     return snprintf((char*)buf, bufSize,
220         "POST %s HTTP/1.1\r\n"
221         "Host: %s\r\n"
222         "Content-Length: %d\r\n"
223         "Content-Type: application/ocsp-request\r\n"
224         "\r\n", 
225         path, domainName, ocspReqSz);
226 }
227
228
229 static int decode_http_response(byte* httpBuf, int httpBufSz, byte** dst)
230 {
231     int idx = 0;
232     int stop = 0;
233     int len = 0;
234     byte* contentType = NULL;
235     byte* contentLength = NULL;
236     char* buf = (char*)httpBuf; /* kludge so I'm not constantly casting */
237
238     if (strncasecmp(buf, "HTTP/1", 6) != 0)
239         return 0;
240     
241     idx = 9; /* sets to the first byte after "HTTP/1.X ", which should be the
242               * HTTP result code */
243
244      if (strncasecmp(&buf[idx], "200 OK", 6) != 0)
245         return 0;
246     
247     idx += 8;
248
249     while (idx < httpBufSz && !stop) {
250         if (buf[idx] == '\r' && buf[idx+1] == '\n') {
251             stop = 1;
252             idx += 2;
253         }
254         else {
255             if (contentType == NULL &&
256                 strncasecmp(&buf[idx], "Content-Type:", 13) == 0) {
257                 idx += 13;
258                 if (buf[idx] == ' ') idx++;
259                 if (strncasecmp(&buf[idx], "application/ocsp-response", 25) != 0)
260                     return 0;
261                 idx += 27;
262             } else if (contentLength == NULL &&
263                 strncasecmp(&buf[idx], "Content-Length:", 15) == 0) {
264                 idx += 15;
265                 if (buf[idx] == ' ') idx++;
266                 while (buf[idx] >= '0' && buf[idx] <= '9' && idx < httpBufSz) {
267                     len = (len * 10) + (buf[idx] - '0');
268                     idx++;
269                 }
270                 idx += 2; /* skip the crlf */
271             } else {
272                 /* Advance idx past the next \r\n */
273                 char* end = strstr(&buf[idx], "\r\n");
274                 idx = end - buf + 2;
275                 stop = 1;
276             }
277         }
278     }
279     
280     if (len > 0) {
281         *dst = (byte*)XMALLOC(len, NULL, DYNAMIC_TYPE_IN_BUFFER);
282         XMEMCPY(*dst, httpBuf + idx, len);
283     }
284
285     return len;
286 }
287
288
289 static int InitOCSP_Entry(OCSP_Entry* ocspe, DecodedCert* cert)
290 {
291     CYASSL_ENTER("InitOCSP_Entry");
292
293     ocspe->next = NULL;
294     XMEMCPY(ocspe->issuerHash, cert->issuerHash, SHA_DIGEST_SIZE);
295     XMEMCPY(ocspe->issuerKeyHash, cert->issuerKeyHash, SHA_DIGEST_SIZE);
296     ocspe->status = NULL;
297     ocspe->totalStatus = 0;
298
299     return 0;
300 }
301
302
303 static OCSP_Entry* find_ocsp_entry(CYASSL_OCSP* ocsp, DecodedCert* cert)
304 {
305     OCSP_Entry* entry = ocsp->ocspList;
306
307     while (entry)
308     {
309         if (XMEMCMP(entry->issuerHash, cert->issuerHash, SHA_DIGEST_SIZE) == 0
310             && XMEMCMP(entry->issuerKeyHash, cert->issuerKeyHash,
311                                                         SHA_DIGEST_SIZE) == 0)
312         {
313             CYASSL_MSG("Found OCSP responder");
314             break;
315         }
316         else
317         {
318             entry = entry->next;
319         }
320     }
321
322     if (entry == NULL)
323     {
324         CYASSL_MSG("Add a new OCSP entry");
325         entry = (OCSP_Entry*)XMALLOC(sizeof(OCSP_Entry),
326                                                 NULL, DYNAMIC_TYPE_OCSP_ENTRY);
327         if (entry != NULL)
328         {
329             InitOCSP_Entry(entry, cert);
330             entry->next = ocsp->ocspList;
331             ocsp->ocspList = entry;
332         }
333     }
334
335     return entry;
336 }
337
338
339 static CertStatus* find_cert_status(OCSP_Entry* ocspe, DecodedCert* cert)
340 {
341     CertStatus* stat = ocspe->status;
342
343     while (stat)
344     {
345         if(stat->serialSz == cert->serialSz &&
346             (XMEMCMP(stat->serial, cert->serial, cert->serialSz) == 0))
347         {
348             break;
349         }
350         else
351         {
352             stat = stat->next;
353         }
354     }
355     if (stat == NULL)
356     {
357         stat = (CertStatus*)XMALLOC(sizeof(CertStatus),
358                                             NULL, DYNAMIC_TYPE_OCSP_STATUS);
359         if (stat != NULL)
360         {
361             XMEMCPY(stat->serial, cert->serial, cert->serialSz);
362             stat->serialSz = cert->serialSz;
363             stat->status = -1;
364             stat->nextDate[0] = 0;
365             ocspe->totalStatus++;
366
367             stat->next = ocspe->status;
368             ocspe->status = stat;
369         }
370     }
371
372     return stat;
373 }
374
375
376 #define SCRATCH_BUFFER_SIZE 2048
377
378 static int http_ocsp_transaction(CYASSL_OCSP* ocsp, DecodedCert* cert,
379                         byte* ocspReqBuf, int ocspReqSz, byte** ocspRespBuf)
380 {
381     SOCKET_T sfd = -1;
382     byte httpBuf[SCRATCH_BUFFER_SIZE];
383     int httpBufSz = SCRATCH_BUFFER_SIZE;
384     char domainName[80], path[80];
385     int port, ocspRespSz;
386
387     if (ocsp->useOverrideUrl || cert->extAuthInfo == NULL) {
388         if (ocsp->overrideName != NULL) {
389             XMEMCPY(domainName, ocsp->overrideName, 80);
390             XMEMCPY(path, ocsp->overridePath, 80);
391             port = ocsp->overridePort;
392         } else
393             return OCSP_NEED_URL;
394     } else {
395         if (!decode_url((const char*)cert->extAuthInfo, cert->extAuthInfoSz,
396                                                     domainName, path, &port))
397             return OCSP_NEED_URL;
398     }
399
400     httpBufSz = build_http_request(domainName, path, ocspReqSz,
401                                                         httpBuf, httpBufSz);
402
403     tcp_connect(&sfd, domainName, port);
404     if (sfd > 0) {
405         int written;
406         written = write(sfd, httpBuf, httpBufSz);
407         if (written == httpBufSz) {
408             written = write(sfd, ocspReqBuf, ocspReqSz);
409             if (written == ocspReqSz) {
410                 httpBufSz = read(sfd, httpBuf, SCRATCH_BUFFER_SIZE);
411                 if (httpBufSz > 0) {
412                     ocspRespSz = decode_http_response(httpBuf, httpBufSz,
413                         ocspRespBuf);
414                 }
415             }
416         }
417         close(sfd);
418         if (ocspRespSz == 0) {
419             CYASSL_MSG("HTTP response was not OK, no OCSP response");
420             return OCSP_LOOKUP_FAIL;
421         }
422     } else {
423         CYASSL_MSG("OCSP Responder connection failed");
424         return OCSP_LOOKUP_FAIL;
425     }
426
427     return ocspRespSz;
428 }
429
430
431 static int xstat2err(int stat)
432 {
433     switch (stat) {
434         case CERT_GOOD:
435             return 0;
436             break;
437         case CERT_REVOKED:
438             return OCSP_CERT_REVOKED;
439             break;
440         default:
441             return OCSP_CERT_UNKNOWN;
442             break;
443     }
444 }
445
446
447 int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert)
448 {
449     byte ocspReqBuf[SCRATCH_BUFFER_SIZE];
450     int ocspReqSz = SCRATCH_BUFFER_SIZE;
451     byte* ocspRespBuf = NULL;
452     int ocspRespSz;
453     OcspRequest ocspRequest;
454     OcspResponse ocspResponse;
455     int result = 0;
456     OCSP_Entry* ocspe;
457     CertStatus* certStatus;
458
459     /* If OCSP lookups are disabled, return success. */
460     if (!ocsp->enabled) {
461         CYASSL_MSG("OCSP lookup disabled, assuming CERT_GOOD");
462         return 0;
463     }
464
465     ocspe = find_ocsp_entry(ocsp, cert);
466     if (ocspe == NULL) {
467         CYASSL_MSG("alloc OCSP entry failed");
468         return MEMORY_ERROR;
469     }
470
471     certStatus = find_cert_status(ocspe, cert);
472     if (certStatus == NULL)
473     {
474         CYASSL_MSG("alloc OCSP cert status failed");
475         return MEMORY_ERROR;
476     }
477
478     if (certStatus->status != -1)
479     {
480         if (!ValidateDate(certStatus->thisDate,
481                                         certStatus->thisDateFormat, BEFORE) ||
482             (certStatus->nextDate[0] == 0) ||
483             !ValidateDate(certStatus->nextDate,
484                                         certStatus->nextDateFormat, AFTER))
485         {
486             CYASSL_MSG("\tinvalid status date, looking up cert");
487             certStatus->status = -1;
488         }
489         else
490         {
491             CYASSL_MSG("\tusing cached status");
492             result = xstat2err(certStatus->status);
493             return result;
494         }
495     }
496     
497     InitOcspRequest(&ocspRequest, cert, ocspReqBuf, ocspReqSz);
498     ocspReqSz = EncodeOcspRequest(&ocspRequest);
499     result = http_ocsp_transaction(ocsp, cert,
500                                         ocspReqBuf, ocspReqSz, &ocspRespBuf);
501     if (result < 0) return result;
502         /* If the transaction failed, return that result. */
503
504     InitOcspResponse(&ocspResponse, certStatus, ocspRespBuf, ocspRespSz);
505     OcspResponseDecode(&ocspResponse);
506
507     if (ocspResponse.responseStatus != OCSP_SUCCESSFUL) {
508         CYASSL_MSG("OCSP Responder failure");
509         result = OCSP_LOOKUP_FAIL;
510     } else {
511         if (CompareOcspReqResp(&ocspRequest, &ocspResponse) == 0)
512         {
513             result = xstat2err(ocspResponse.status->status);
514         }
515         else
516         {
517             CYASSL_MSG("OCSP Response incorrect for Request");
518             result = OCSP_LOOKUP_FAIL;
519         }
520     }
521     if (ocspReqBuf != NULL) {
522         XFREE(ocspRespBuf, NULL, DYNAMIC_TYPE_IN_BUFFER);
523     }
524
525     return result;
526 }
527
528
529 #endif /* HAVE_OCSP */
530