Additional functionality in ocsp utility: print summary
[openssl.git] / apps / ocsp.c
1 /* ocsp.c */
2 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
3  * project 2000.
4  */
5 /* ====================================================================
6  * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58
59 #include <stdio.h>
60 #include <string.h>
61 #include <openssl/pem.h>
62 #include <openssl/ocsp.h>
63 #include <openssl/err.h>
64 #include "apps.h"
65
66 static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer,
67                                 STACK_OF(OCSP_CERTID) *ids);
68 static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer,
69                                 STACK_OF(OCSP_CERTID) *ids);
70 static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
71                                 STACK *names, STACK_OF(OCSP_CERTID) *ids);
72
73 #undef PROG
74 #define PROG ocsp_main
75
76 int MAIN(int, char **);
77
78 int MAIN(int argc, char **argv)
79         {
80         char **args;
81         char *host = NULL, *path = "/";
82         char *reqin = NULL, *respin = NULL;
83         char *reqout = NULL, *respout = NULL;
84         char *signfile = NULL, *keyfile = NULL;
85         char *outfile = NULL;
86         int add_nonce = 1, noverify = 0;
87         OCSP_REQUEST *req = NULL;
88         OCSP_RESPONSE *resp = NULL;
89         OCSP_BASICRESP *bs = NULL;
90         X509 *issuer = NULL, *cert = NULL;
91         X509 *signer = NULL;
92         EVP_PKEY *key = NULL;
93         BIO *cbio = NULL, *derbio = NULL;
94         BIO *out = NULL;
95         int req_text = 0, resp_text = 0;
96         char *CAfile = NULL, *CApath = NULL;
97         X509_STORE *store = NULL;
98         int ret = 1;
99         int badarg = 0;
100         int i;
101         STACK *reqnames = NULL;
102         STACK_OF(OCSP_CERTID) *ids = NULL;
103         if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
104         ERR_load_crypto_strings();
105         args = argv + 1;
106         reqnames = sk_new_null();
107         ids = sk_OCSP_CERTID_new_null();
108         while (!badarg && *args && *args[0] == '-')
109                 {
110                 if (!strcmp(*args, "-out"))
111                         {
112                         if (args[1])
113                                 {
114                                 args++;
115                                 outfile = *args;
116                                 }
117                         else badarg = 1;
118                         }
119                 else if (!strcmp(*args, "-host"))
120                         {
121                         if (args[1])
122                                 {
123                                 args++;
124                                 host = *args;
125                                 }
126                         else badarg = 1;
127                         }
128                 else if (!strcmp(*args, "-noverify"))
129                         noverify = 1;
130                 else if (!strcmp(*args, "-nonce"))
131                         add_nonce = 2;
132                 else if (!strcmp(*args, "-no_nonce"))
133                         add_nonce = 0;
134                 else if (!strcmp(*args, "-text"))
135                         {
136                         req_text = 1;
137                         resp_text = 1;
138                         }
139                 else if (!strcmp(*args, "-req_text"))
140                         req_text = 1;
141                 else if (!strcmp(*args, "-resp_text"))
142                         resp_text = 1;
143                 else if (!strcmp(*args, "-reqin"))
144                         {
145                         if (args[1])
146                                 {
147                                 args++;
148                                 reqin = *args;
149                                 }
150                         else badarg = 1;
151                         }
152                 else if (!strcmp(*args, "-respin"))
153                         {
154                         if (args[1])
155                                 {
156                                 args++;
157                                 respin = *args;
158                                 }
159                         else badarg = 1;
160                         }
161                 else if (!strcmp(*args, "-signer"))
162                         {
163                         if (args[1])
164                                 {
165                                 args++;
166                                 signfile = *args;
167                                 }
168                         else badarg = 1;
169                         }
170                 else if (!strcmp (*args, "-CAfile"))
171                         {
172                         if (args[1])
173                                 {
174                                 args++;
175                                 CAfile = *args;
176                                 }
177                         else badarg = 1;
178                         }
179                 else if (!strcmp (*args, "-CApath"))
180                         {
181                         if (args[1])
182                                 {
183                                 args++;
184                                 CApath = *args;
185                                 }
186                         else badarg = 1;
187                         }
188                  else if (!strcmp(*args, "-signkey"))
189                         {
190                         if (args[1])
191                                 {
192                                 args++;
193                                 keyfile = *args;
194                                 }
195                         else badarg = 1;
196                         }
197                 else if (!strcmp(*args, "-reqout"))
198                         {
199                         if (args[1])
200                                 {
201                                 args++;
202                                 reqout = *args;
203                                 }
204                         else badarg = 1;
205                         }
206                 else if (!strcmp(*args, "-respout"))
207                         {
208                         if (args[1])
209                                 {
210                                 args++;
211                                 respout = *args;
212                                 }
213                         else badarg = 1;
214                         }
215                  else if (!strcmp(*args, "-path"))
216                         {
217                         if (args[1])
218                                 {
219                                 args++;
220                                 path = *args;
221                                 }
222                         else badarg = 1;
223                         }
224                 else if (!strcmp(*args, "-issuer"))
225                         {
226                         if (args[1])
227                                 {
228                                 args++;
229                                 X509_free(issuer);
230                                 issuer = load_cert(bio_err, *args, FORMAT_PEM);
231                                 if(!issuer) goto end;
232                                 }
233                         else badarg = 1;
234                         }
235                 else if (!strcmp (*args, "-cert"))
236                         {
237                         if (args[1])
238                                 {
239                                 args++;
240                                 X509_free(cert);
241                                 cert = load_cert(bio_err, *args, FORMAT_PEM);
242                                 if(!cert) goto end;
243                                 if(!add_ocsp_cert(&req, cert, issuer, ids))
244                                         goto end;
245                                 if(!sk_push(reqnames, *args))
246                                         goto end;
247                                 }
248                         else badarg = 1;
249                         }
250                 else if (!strcmp(*args, "-serial"))
251                         {
252                         if (args[1])
253                                 {
254                                 args++;
255                                 if(!add_ocsp_serial(&req, *args, issuer, ids))
256                                         goto end;
257                                 if(!sk_push(reqnames, *args))
258                                         goto end;
259                                 }
260                         else badarg = 1;
261                         }
262                 else badarg = 1;
263                 args++;
264                 }
265
266         /* Have we anything to do? */
267         if (!req && !reqin && !respin) badarg = 1;
268
269         if (badarg)
270                 {
271                 BIO_printf (bio_err, "OCSP utility\n");
272                 BIO_printf (bio_err, "Usage ocsp [options]\n");
273                 BIO_printf (bio_err, "where options are\n");
274                 BIO_printf (bio_err, "-out file     output filename\n");
275                 BIO_printf (bio_err, "-issuer file  issuer certificate\n");
276                 BIO_printf (bio_err, "-cert file    certificate to check\n");
277                 BIO_printf (bio_err, "-serial n     serial number to check\n");
278                 BIO_printf (bio_err, "-signer file  certificate to sign OCSP request with\n");
279                 BIO_printf (bio_err, "-signkey file private key to sign OCSP request with\n");
280                 BIO_printf (bio_err, "-req_text     print text form of request\n");
281                 BIO_printf (bio_err, "-resp_text    print text form of response\n");
282                 BIO_printf (bio_err, "-text         print text form of request and response\n");
283                 BIO_printf (bio_err, "-reqout file  write DER encoded OCSP request to \"file\"\n");
284                 BIO_printf (bio_err, "-respout file write DER encoded OCSP reponse to \"file\"\n");
285                 BIO_printf (bio_err, "-reqin file   read DER encoded OCSP request from \"file\"\n");
286                 BIO_printf (bio_err, "-respin file  read DER encoded OCSP reponse from \"file\"\n");
287                 BIO_printf (bio_err, "-nonce        add OCSP nonce to request\n");
288                 BIO_printf (bio_err, "-no_nonce     don't add OCSP nonce to request\n");
289                 BIO_printf (bio_err, "-host host:n  send OCSP request to host on port n\n");
290                 BIO_printf (bio_err, "-path         path to use in OCSP request\n");
291                 BIO_printf (bio_err, "-CApath dir   trusted certificates directory\n");
292                 BIO_printf (bio_err, "-CAfile file  trusted certificates file\n");
293                 BIO_printf (bio_err, "-noverify     don't verify response\n");
294                 goto end;
295                 }
296
297         if(outfile) out = BIO_new_file(outfile, "w");
298         else out = BIO_new_fp(stdout, BIO_NOCLOSE);
299
300         if(!out)
301                 {
302                 BIO_printf(bio_err, "Error opening output file\n");
303                 goto end;
304                 }
305
306         if (!req && (add_nonce != 2)) add_nonce = 0;
307
308         if (!req && reqin)
309                 {
310                 derbio = BIO_new_file(reqin, "rb");
311                 if (!derbio)
312                         {
313                         BIO_printf(bio_err, "Error Opening OCSP request file\n");
314                         goto end;
315                         }
316                 req = d2i_OCSP_REQUEST_bio(derbio, NULL);
317                 BIO_free(derbio);
318                 if(!req)
319                         {
320                         BIO_printf(bio_err, "Error reading OCSP request\n");
321                         goto end;
322                         }
323                 }
324
325         if (!req && (signfile || reqout || host || add_nonce))
326                 {
327                 BIO_printf(bio_err, "Need an OCSP request for this operation!\n");
328                 goto end;
329                 }
330
331         if (req && add_nonce) OCSP_request_add1_nonce(req, NULL, -1);
332
333         if (signfile)
334                 {
335                 if (!keyfile) keyfile = signfile;
336                 signer = load_cert(bio_err, signfile, FORMAT_PEM);
337                 if (!signer)
338                         {
339                         BIO_printf(bio_err, "Error loading signer certificate\n");
340                         goto end;
341                         }
342                 key = load_key(bio_err, keyfile, FORMAT_PEM, NULL, NULL);
343                 if (!key)
344                         {
345                         BIO_printf(bio_err, "Error loading signer private key\n");
346                         goto end;
347                         }
348                 if (!OCSP_request_sign(req, signer, key, EVP_sha1(), NULL, 0))
349                         {
350                         BIO_printf(bio_err, "Error signing OCSP request\n");
351                         goto end;
352                         }
353                 }
354
355         if (reqout)
356                 {
357                 derbio = BIO_new_file(reqout, "wb");
358                 if (!derbio)
359                         {
360                         BIO_printf(bio_err, "Error opening file %s\n", reqout);
361                         goto end;
362                         }
363                 i2d_OCSP_REQUEST_bio(derbio, req);
364                 BIO_free(derbio);
365                 }
366
367         if (req_text && req) OCSP_REQUEST_print(out, req, 0);
368
369         if (host)
370                 {
371                 cbio = BIO_new_connect(host);
372                 if (!cbio)
373                         {
374                         BIO_printf(bio_err, "Error creating connect BIO\n");
375                         goto end;
376                         }
377                 if (BIO_do_connect(cbio) <= 0)
378                         {
379                         BIO_printf(bio_err, "Error connecting BIO\n");
380                         goto end;
381                         }
382                 resp = OCSP_sendreq_bio(cbio, path, req);
383                 BIO_free(cbio);
384                 cbio = NULL;
385                 if (!resp)
386                         {
387                         BIO_printf(bio_err, "Error querying OCSP responsder\n");
388                         goto end;
389                         }
390                 }
391         else if (respin)
392                 {
393                 derbio = BIO_new_file(respin, "rb");
394                 if (!derbio)
395                         {
396                         BIO_printf(bio_err, "Error Opening OCSP response file\n");
397                         goto end;
398                         }
399                 resp = d2i_OCSP_RESPONSE_bio(derbio, NULL);
400                 BIO_free(derbio);
401                 if(!resp)
402                         {
403                         BIO_printf(bio_err, "Error reading OCSP response\n");
404                         goto end;
405                         }
406         
407                 }
408         else
409                 {
410                 ret = 0;
411                 goto end;
412                 }
413
414         if (respout)
415                 {
416                 derbio = BIO_new_file(respout, "wb");
417                 if(!derbio)
418                         {
419                         BIO_printf(bio_err, "Error opening file %s\n", respout);
420                         goto end;
421                         }
422                 i2d_OCSP_RESPONSE_bio(derbio, resp);
423                 BIO_free(derbio);
424                 }
425
426         i = OCSP_response_status(resp);
427
428         if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL)
429                 {
430                 BIO_printf(out, "Responder Error: %s (%ld)\n",
431                                 OCSP_response_status_str(i), i);
432                 ret = 0;
433                 goto end;
434                 }
435
436         if (resp_text) OCSP_RESPONSE_print(out, resp, 0);
437
438         store = setup_verify(bio_err, CAfile, CApath);
439         if(!store) goto end;
440
441         bs = OCSP_response_get1_basic(resp);
442
443         if (!bs)
444                 {
445                 BIO_printf(bio_err, "Error parsing response\n");
446                 goto end;
447                 }
448
449         if (!noverify)
450                 {
451                 if (req && (OCSP_check_nonce(req, bs) <= 0))
452                         {
453                         BIO_printf(bio_err, "Nonce Verify error\n");
454                         goto end;
455                         }
456
457                 i = OCSP_basic_verify(bs, NULL, store, 0);
458
459                 if(i <= 0)
460                         {
461                         BIO_printf(bio_err, "Response Verify Failure\n", i);
462                         ERR_print_errors(bio_err);
463                         }
464                 else
465                         BIO_printf(bio_err, "Response verify OK\n");
466
467                 }
468
469         if (!print_ocsp_summary(out, bs, req, reqnames, ids))
470                 goto end;
471
472         ret = 0;
473
474 end:
475         ERR_print_errors(bio_err);
476         X509_free(signer);
477         X509_STORE_free(store);
478         EVP_PKEY_free(key);
479         X509_free(issuer);
480         X509_free(cert);
481         BIO_free(cbio);
482         BIO_free(out);
483         OCSP_REQUEST_free(req);
484         OCSP_RESPONSE_free(resp);
485         OCSP_BASICRESP_free(bs);
486         sk_free(reqnames);
487         sk_OCSP_CERTID_free(ids);
488
489         EXIT(ret);
490 }
491
492 static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer,
493                                 STACK_OF(OCSP_CERTID) *ids)
494         {
495         OCSP_CERTID *id;
496         if(!issuer)
497                 {
498                 BIO_printf(bio_err, "No issuer certificate specified\n");
499                 return 0;
500                 }
501         if(!*req) *req = OCSP_REQUEST_new();
502         if(!*req) goto err;
503         id = OCSP_cert_to_id(NULL, cert, issuer);
504         if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err;
505         if(!OCSP_request_add0_id(*req, id)) goto err;
506         return 1;
507
508         err:
509         BIO_printf(bio_err, "Error Creating OCSP request\n");
510         return 0;
511         }
512
513 static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer,
514                                 STACK_OF(OCSP_CERTID) *ids)
515         {
516         OCSP_CERTID *id;
517         X509_NAME *iname;
518         ASN1_BIT_STRING *ikey;
519         ASN1_INTEGER *sno;
520         if(!issuer)
521                 {
522                 BIO_printf(bio_err, "No issuer certificate specified\n");
523                 return 0;
524                 }
525         if(!*req) *req = OCSP_REQUEST_new();
526         if(!*req) goto err;
527         iname = X509_get_subject_name(issuer);
528         ikey = issuer->cert_info->key->public_key;
529         sno = s2i_ASN1_INTEGER(NULL, serial);
530         if(!sno)
531                 {
532                 BIO_printf(bio_err, "Error converting serial number %s\n", serial);
533                 return 0;
534                 }
535         id = OCSP_cert_id_new(EVP_sha1(), iname, ikey, sno);
536         ASN1_INTEGER_free(sno);
537         if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err;
538         if(!OCSP_request_add0_id(*req, id)) goto err;
539         return 1;
540
541         err:
542         BIO_printf(bio_err, "Error Creating OCSP request\n");
543         return 0;
544         }
545
546 static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
547                                         STACK *names, STACK_OF(OCSP_CERTID) *ids)
548         {
549         OCSP_CERTID *id;
550         char *name;
551         int i;
552
553         int status, reason;
554
555         ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
556
557         if (!bs || !req || !sk_num(names) || !sk_OCSP_CERTID_num(ids))
558                 return 1;
559
560         for (i = 0; i < sk_OCSP_CERTID_num(ids); i++)
561                 {
562                 id = sk_OCSP_CERTID_value(ids, i);
563                 name = sk_value(names, i);
564                 BIO_printf(out, "%s: ", name);
565
566                 if(!OCSP_resp_find_status(bs, id, &status, &reason,
567                                         &rev, &thisupd, &nextupd))
568                         {
569                         BIO_puts(out, "ERROR: No Status found.\n");
570                         continue;
571                         }
572                 BIO_printf(out, "%s\n", OCSP_cert_status_str(status));
573
574                 BIO_puts(out, "\tThis Update: ");
575                 ASN1_GENERALIZEDTIME_print(out, thisupd);
576                 BIO_puts(out, "\n");
577
578                 if(nextupd)
579                         {
580                         BIO_puts(out, "\tNext Update: ");
581                         ASN1_GENERALIZEDTIME_print(out, thisupd);
582                         BIO_puts(out, "\n");
583                         }
584
585                 if (status != V_OCSP_CERTSTATUS_REVOKED)
586                         continue;
587
588                 if (reason > 0)
589                         BIO_printf(out, "\tReason: %s\n",
590                                 OCSP_crl_reason_str(reason));
591
592                 BIO_puts(out, "\tRevocation Time: ");
593                 ASN1_GENERALIZEDTIME_print(out, rev);
594                 BIO_puts(out, "\n");
595                 }
596
597         return 1;
598         }
599