OCSP check the whole cert chain
[gnutls:gnutls.git] / src / ocsptool-common.c
1 /*
2  * Copyright (C) 2012-2014 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuTLS.
5  *
6  * GnuTLS is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuTLS is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27
28 #include <gnutls/gnutls.h>
29 #include <gnutls/ocsp.h>
30 #include <gnutls/x509.h>
31 #include <gnutls/crypto.h>
32
33 /* Gnulib portability files. */
34 #include <read-file.h>
35 #include <socket.h>
36
37 #include <ocsptool-common.h>
38
39 #define MAX_BUF 4*1024
40 #define HEADER_PATTERN "POST /%s HTTP/1.1\r\n" \
41   "Host: %s\r\n" \
42   "Accept: */*\r\n" \
43   "Content-Type: application/ocsp-request\r\n" \
44   "Content-Length: %u\r\n" \
45   "Connection: close\r\n\r\n"
46 static char buffer[MAX_BUF + 1];
47
48 /* returns the host part of a URL */
49 static const char *host_from_url(const char *url, unsigned int *port, const char **path)
50 {
51         static char hostname[512];
52         char *p;
53
54         *port = 0;
55         *path = "";
56
57         if ((p = strstr(url, "http://")) != NULL) {
58                 snprintf(hostname, sizeof(hostname), "%s", p + 7);
59                 p = strchr(hostname, '/');
60                 if (p != NULL) {
61                         *p = 0;
62                         *path = p+1;
63                 }
64
65                 p = strchr(hostname, ':');
66                 if (p != NULL) {
67                         *p = 0;
68                         *port = atoi(p + 1);
69                 }
70
71                 return hostname;
72         } else {
73                 return url;
74         }
75 }
76
77 void
78 _generate_request(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
79                   gnutls_datum_t * rdata, gnutls_datum_t *nonce)
80 {
81         gnutls_ocsp_req_t req;
82         int ret;
83
84         ret = gnutls_ocsp_req_init(&req);
85         if (ret < 0) {
86                 fprintf(stderr, "ocsp_req_init: %s", gnutls_strerror(ret));
87                 exit(1);
88         }
89
90         ret = gnutls_ocsp_req_add_cert(req, GNUTLS_DIG_SHA1, issuer, cert);
91         if (ret < 0) {
92                 fprintf(stderr, "ocsp_req_add_cert: %s",
93                         gnutls_strerror(ret));
94                 exit(1);
95         }
96
97         if (nonce) {
98                 ret = gnutls_ocsp_req_set_nonce(req, 0, nonce);
99                 if (ret < 0) {
100                         fprintf(stderr, "ocsp_req_set_nonce: %s",
101                                 gnutls_strerror(ret));
102                         exit(1);
103                 }
104         }
105
106         ret = gnutls_ocsp_req_export(req, rdata);
107         if (ret != 0) {
108                 fprintf(stderr, "ocsp_req_export: %s",
109                         gnutls_strerror(ret));
110                 exit(1);
111         }
112
113         gnutls_ocsp_req_deinit(req);
114         return;
115 }
116
117 static size_t get_data(void *buf, size_t size, size_t nmemb,
118                        void *userp)
119 {
120         gnutls_datum_t *ud = userp;
121
122         size *= nmemb;
123
124         ud->data = realloc(ud->data, size + ud->size);
125         if (ud->data == NULL) {
126                 fprintf(stderr, "Not enough memory for the request\n");
127                 exit(1);
128         }
129
130         memcpy(&ud->data[ud->size], buf, size);
131         ud->size += size;
132
133         return size;
134 }
135
136 /* Returns 0 on ok, and -1 on error */
137 int send_ocsp_request(const char *server,
138                       gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
139                       gnutls_datum_t * resp_data, gnutls_datum_t *nonce)
140 {
141         gnutls_datum_t ud;
142         int ret;
143         gnutls_datum_t req;
144         char *url = (void *) server;
145         char headers[1024];
146         char service[16];
147         unsigned char *p;
148         const char *hostname;
149         const char *path = "";
150         unsigned i;
151         unsigned int headers_size = 0, port;
152         socket_st hd;
153
154         sockets_init();
155
156         if (url == NULL) {
157                 /* try to read URL from issuer certificate */
158                 gnutls_datum_t data;
159
160                 i = 0;
161                 do {
162                         ret = gnutls_x509_crt_get_authority_info_access(cert, i++,
163                                                                         GNUTLS_IA_OCSP_URI,
164                                                                         &data,
165                                                                         NULL);
166                 } while(ret == GNUTLS_E_UNKNOWN_ALGORITHM);
167
168                 if (ret < 0) {
169                         i = 0;
170                         do {
171                                 ret =
172                                     gnutls_x509_crt_get_authority_info_access
173                                     (issuer, i++, GNUTLS_IA_OCSP_URI, &data, NULL);
174                         } while(ret == GNUTLS_E_UNKNOWN_ALGORITHM);
175                 }
176
177                 if (ret < 0) {
178                         fprintf(stderr,
179                                 "*** Cannot find OCSP server URI in certificate: %s\n",
180                                 gnutls_strerror(ret));
181                         return ret;
182                 }
183
184                 url = malloc(data.size + 1);
185                 memcpy(url, data.data, data.size);
186                 url[data.size] = 0;
187
188                 gnutls_free(data.data);
189         }
190
191         hostname = host_from_url(url, &port, &path);
192         if (port != 0)
193                 snprintf(service, sizeof(service), "%u", port);
194         else
195                 strcpy(service, "80");
196
197         fprintf(stderr, "Connecting to OCSP server: %s...\n", hostname);
198
199         memset(&ud, 0, sizeof(ud));
200
201         _generate_request(cert, issuer, &req, nonce);
202
203         snprintf(headers, sizeof(headers), HEADER_PATTERN, path, hostname,
204                  (unsigned int) req.size);
205         headers_size = strlen(headers);
206
207         socket_open(&hd, hostname, service, 0, CONNECT_MSG);
208
209         socket_send(&hd, headers, headers_size);
210         socket_send(&hd, req.data, req.size);
211
212         do {
213                 ret = socket_recv(&hd, buffer, sizeof(buffer));
214                 if (ret > 0)
215                         get_data(buffer, ret, 1, &ud);
216         } while (ret > 0);
217
218         if (ret < 0 || ud.size == 0) {
219                 perror("recv");
220                 return -1;
221         }
222
223         socket_bye(&hd);
224
225         p = memmem(ud.data, ud.size, "\r\n\r\n", 4);
226         if (p == NULL) {
227                 fprintf(stderr, "Cannot interpret HTTP response\n");
228                 return -1;
229         }
230
231         p += 4;
232         resp_data->size = ud.size - (p - ud.data);
233         resp_data->data = malloc(resp_data->size);
234         if (resp_data->data == NULL)
235                 return -1;
236
237         memcpy(resp_data->data, p, resp_data->size);
238
239         free(ud.data);
240
241         return 0;
242 }
243
244 void print_ocsp_verify_res(unsigned int output)
245 {
246         int comma = 0;
247
248         if (output) {
249                 printf("Failure");
250                 comma = 1;
251         } else {
252                 printf("Success");
253                 comma = 1;
254         }
255
256         if (output & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND) {
257                 if (comma)
258                         printf(", ");
259                 printf("Signer cert not found");
260                 comma = 1;
261         }
262
263         if (output & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR) {
264                 if (comma)
265                         printf(", ");
266                 printf("Signer cert keyusage error");
267                 comma = 1;
268         }
269
270         if (output & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) {
271                 if (comma)
272                         printf(", ");
273                 printf("Signer cert is not trusted");
274                 comma = 1;
275         }
276
277         if (output & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) {
278                 if (comma)
279                         printf(", ");
280                 printf("Insecure algorithm");
281                 comma = 1;
282         }
283
284         if (output & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE) {
285                 if (comma)
286                         printf(", ");
287                 printf("Signature failure");
288                 comma = 1;
289         }
290
291         if (output & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED) {
292                 if (comma)
293                         printf(", ");
294                 printf("Signer cert not yet activated");
295                 comma = 1;
296         }
297
298         if (output & GNUTLS_OCSP_VERIFY_CERT_EXPIRED) {
299                 if (comma)
300                         printf(", ");
301                 printf("Signer cert expired");
302                 comma = 1;
303         }
304 }
305
306 /* three days */
307 #define OCSP_VALIDITY_SECS (3*60*60*24)
308
309 /* Returns:
310  *  0: certificate is revoked
311  *  1: certificate is ok
312  *  -1: dunno
313  */
314 int
315 check_ocsp_response(gnutls_x509_crt_t cert,
316                     gnutls_x509_crt_t issuer, gnutls_datum_t * data,
317                     gnutls_datum_t * nonce)
318 {
319         gnutls_ocsp_resp_t resp;
320         int ret;
321         unsigned int status, cert_status;
322         time_t rtime, vtime, ntime, now;
323
324         now = time(0);
325
326         ret = gnutls_ocsp_resp_init(&resp);
327         if (ret < 0) {
328                 fprintf(stderr, "ocsp_resp_init: %s",
329                         gnutls_strerror(ret));
330                 exit(1);
331         }
332
333         ret = gnutls_ocsp_resp_import(resp, data);
334         if (ret < 0) {
335                 fprintf(stderr, "importing response: %s",
336                         gnutls_strerror(ret));
337                 exit(1);
338         }
339
340         ret = gnutls_ocsp_resp_check_crt(resp, 0, cert);
341         if (ret < 0) {
342                 if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
343                         printf
344                             ("*** Got OCSP response with no data (ignoring)\n");
345                 } else {
346                         printf
347                             ("*** Got OCSP response on an unrelated certificate (ignoring)\n");
348                 }
349                 ret = -1;
350                 goto cleanup;
351         }
352
353         ret = gnutls_ocsp_resp_verify_direct(resp, issuer, &status, 0);
354         if (ret < 0) {
355                 fprintf(stderr, "gnutls_ocsp_resp_verify_direct: %s",
356                         gnutls_strerror(ret));
357                 exit(1);
358         }
359
360         if (status != 0) {
361                 printf("*** Verifying OCSP Response: ");
362                 print_ocsp_verify_res(status);
363                 printf(".\n");
364         }
365
366         /* do not print revocation data if response was not verified */
367         if (status != 0) {
368                 ret = -1;
369                 goto cleanup;
370         }
371
372
373         ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
374                                           &cert_status, &vtime, &ntime,
375                                           &rtime, NULL);
376         if (ret < 0) {
377                 fprintf(stderr, "reading response: %s",
378                         gnutls_strerror(ret));
379                 exit(1);
380         }
381
382         if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
383                 printf("*** Certificate was revoked at %s", ctime(&rtime));
384                 ret = 0;
385                 goto cleanup;
386         }
387
388         if (ntime == -1) {
389                 if (now - vtime > OCSP_VALIDITY_SECS) {
390                         printf
391                             ("*** The OCSP response is old (was issued at: %s) ignoring",
392                              ctime(&vtime));
393                         ret = -1;
394                         goto cleanup;
395                 }
396         } else {
397                 /* there is a newer OCSP answer, don't trust this one */
398                 if (ntime < now) {
399                         printf
400                             ("*** The OCSP response was issued at: %s, but there is a newer issue at %s",
401                              ctime(&vtime), ctime(&ntime));
402                         ret = -1;
403                         goto cleanup;
404                 }
405         }
406
407         if (nonce) {
408                 gnutls_datum_t rnonce;
409
410                 ret = gnutls_ocsp_resp_get_nonce(resp, NULL, &rnonce);
411                 if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
412                         fprintf(stderr, "*** The OCSP reply did not include the requested nonce.\n");
413                         goto finish_ok;
414                 }
415
416                 if (ret < 0) {
417                         fprintf(stderr, "could not read response's nonce: %s\n",
418                                 gnutls_strerror(ret));
419                         exit(1);
420                 }
421
422                 if (rnonce.size != nonce->size || memcmp(nonce->data, rnonce.data,
423                         nonce->size) != 0) {
424                         fprintf(stderr, "nonce in the response doesn't match\n");
425                         exit(1);
426                 }
427
428                 gnutls_free(rnonce.data);
429         }
430
431  finish_ok:
432         printf("- OCSP server flags certificate not revoked as of %s",
433                ctime(&vtime));
434         ret = 1;
435  cleanup:
436         gnutls_ocsp_resp_deinit(resp);
437
438         return ret;
439 }