GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: ca.c,v 1.25 2017/05/08 21:12:36 beck Exp $ */ |
||
2 |
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
||
3 |
* All rights reserved. |
||
4 |
* |
||
5 |
* This package is an SSL implementation written |
||
6 |
* by Eric Young (eay@cryptsoft.com). |
||
7 |
* The implementation was written so as to conform with Netscapes SSL. |
||
8 |
* |
||
9 |
* This library is free for commercial and non-commercial use as long as |
||
10 |
* the following conditions are aheared to. The following conditions |
||
11 |
* apply to all code found in this distribution, be it the RC4, RSA, |
||
12 |
* lhash, DES, etc., code; not just the SSL code. The SSL documentation |
||
13 |
* included with this distribution is covered by the same copyright terms |
||
14 |
* except that the holder is Tim Hudson (tjh@cryptsoft.com). |
||
15 |
* |
||
16 |
* Copyright remains Eric Young's, and as such any Copyright notices in |
||
17 |
* the code are not to be removed. |
||
18 |
* If this package is used in a product, Eric Young should be given attribution |
||
19 |
* as the author of the parts of the library used. |
||
20 |
* This can be in the form of a textual message at program startup or |
||
21 |
* in documentation (online or textual) provided with the package. |
||
22 |
* |
||
23 |
* Redistribution and use in source and binary forms, with or without |
||
24 |
* modification, are permitted provided that the following conditions |
||
25 |
* are met: |
||
26 |
* 1. Redistributions of source code must retain the copyright |
||
27 |
* notice, this list of conditions and the following disclaimer. |
||
28 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
29 |
* notice, this list of conditions and the following disclaimer in the |
||
30 |
* documentation and/or other materials provided with the distribution. |
||
31 |
* 3. All advertising materials mentioning features or use of this software |
||
32 |
* must display the following acknowledgement: |
||
33 |
* "This product includes cryptographic software written by |
||
34 |
* Eric Young (eay@cryptsoft.com)" |
||
35 |
* The word 'cryptographic' can be left out if the rouines from the library |
||
36 |
* being used are not cryptographic related :-). |
||
37 |
* 4. If you include any Windows specific code (or a derivative thereof) from |
||
38 |
* the apps directory (application code) you must include an acknowledgement: |
||
39 |
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" |
||
40 |
* |
||
41 |
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND |
||
42 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
43 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
44 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
||
45 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
46 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
47 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
48 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
49 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
50 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
51 |
* SUCH DAMAGE. |
||
52 |
* |
||
53 |
* The licence and distribution terms for any publically available version or |
||
54 |
* derivative of this code cannot be changed. i.e. this code cannot simply be |
||
55 |
* copied and put under another distribution licence |
||
56 |
* [including the GNU Public Licence.] |
||
57 |
*/ |
||
58 |
|||
59 |
/* The PPKI stuff has been donated by Jeff Barber <jeffb@issl.atl.hp.com> */ |
||
60 |
|||
61 |
#include <sys/types.h> |
||
62 |
|||
63 |
#include <ctype.h> |
||
64 |
#include <stdio.h> |
||
65 |
#include <stdlib.h> |
||
66 |
#include <limits.h> |
||
67 |
#include <string.h> |
||
68 |
#include <unistd.h> |
||
69 |
|||
70 |
#include "apps.h" |
||
71 |
|||
72 |
#include <openssl/bio.h> |
||
73 |
#include <openssl/bn.h> |
||
74 |
#include <openssl/conf.h> |
||
75 |
#include <openssl/err.h> |
||
76 |
#include <openssl/evp.h> |
||
77 |
#include <openssl/objects.h> |
||
78 |
#include <openssl/ocsp.h> |
||
79 |
#include <openssl/pem.h> |
||
80 |
#include <openssl/txt_db.h> |
||
81 |
#include <openssl/x509.h> |
||
82 |
#include <openssl/x509v3.h> |
||
83 |
|||
84 |
#define BASE_SECTION "ca" |
||
85 |
|||
86 |
#define ENV_DEFAULT_CA "default_ca" |
||
87 |
|||
88 |
#define STRING_MASK "string_mask" |
||
89 |
#define UTF8_IN "utf8" |
||
90 |
|||
91 |
#define ENV_NEW_CERTS_DIR "new_certs_dir" |
||
92 |
#define ENV_CERTIFICATE "certificate" |
||
93 |
#define ENV_SERIAL "serial" |
||
94 |
#define ENV_CRLNUMBER "crlnumber" |
||
95 |
#define ENV_PRIVATE_KEY "private_key" |
||
96 |
#define ENV_DEFAULT_DAYS "default_days" |
||
97 |
#define ENV_DEFAULT_STARTDATE "default_startdate" |
||
98 |
#define ENV_DEFAULT_ENDDATE "default_enddate" |
||
99 |
#define ENV_DEFAULT_CRL_DAYS "default_crl_days" |
||
100 |
#define ENV_DEFAULT_CRL_HOURS "default_crl_hours" |
||
101 |
#define ENV_DEFAULT_MD "default_md" |
||
102 |
#define ENV_DEFAULT_EMAIL_DN "email_in_dn" |
||
103 |
#define ENV_PRESERVE "preserve" |
||
104 |
#define ENV_POLICY "policy" |
||
105 |
#define ENV_EXTENSIONS "x509_extensions" |
||
106 |
#define ENV_CRLEXT "crl_extensions" |
||
107 |
#define ENV_MSIE_HACK "msie_hack" |
||
108 |
#define ENV_NAMEOPT "name_opt" |
||
109 |
#define ENV_CERTOPT "cert_opt" |
||
110 |
#define ENV_EXTCOPY "copy_extensions" |
||
111 |
#define ENV_UNIQUE_SUBJECT "unique_subject" |
||
112 |
|||
113 |
#define ENV_DATABASE "database" |
||
114 |
|||
115 |
/* Additional revocation information types */ |
||
116 |
|||
117 |
#define REV_NONE 0 /* No addditional information */ |
||
118 |
#define REV_CRL_REASON 1 /* Value is CRL reason code */ |
||
119 |
#define REV_HOLD 2 /* Value is hold instruction */ |
||
120 |
#define REV_KEY_COMPROMISE 3 /* Value is cert key compromise time */ |
||
121 |
#define REV_CA_COMPROMISE 4 /* Value is CA key compromise time */ |
||
122 |
|||
123 |
static const char *ca_usage[] = { |
||
124 |
"usage: ca args\n", |
||
125 |
"\n", |
||
126 |
" -verbose - Talk a lot while doing things\n", |
||
127 |
" -config file - A config file\n", |
||
128 |
" -name arg - The particular CA definition to use\n", |
||
129 |
" -gencrl - Generate a new CRL\n", |
||
130 |
" -crldays days - Days is when the next CRL is due\n", |
||
131 |
" -crlhours hours - Hours is when the next CRL is due\n", |
||
132 |
" -startdate YYMMDDHHMMSSZ - certificate validity notBefore\n", |
||
133 |
" -enddate YYMMDDHHMMSSZ - certificate validity notAfter (overrides -days)\n", |
||
134 |
" -days arg - number of days to certify the certificate for\n", |
||
135 |
" -md arg - md to use, one of md5 or sha1\n", |
||
136 |
" -policy arg - The CA 'policy' to support\n", |
||
137 |
" -keyfile arg - private key file\n", |
||
138 |
" -keyform arg - private key file format (PEM)\n", |
||
139 |
" -key arg - key to decode the private key if it is encrypted\n", |
||
140 |
" -cert file - The CA certificate\n", |
||
141 |
" -selfsign - sign a certificate with the key associated with it\n", |
||
142 |
" -in file - The input PEM encoded certificate request(s)\n", |
||
143 |
" -out file - Where to put the output file(s)\n", |
||
144 |
" -outdir dir - Where to put output certificates\n", |
||
145 |
" -infiles .... - The last argument, requests to process\n", |
||
146 |
" -spkac file - File contains DN and signed public key and challenge\n", |
||
147 |
" -ss_cert file - File contains a self signed cert to sign\n", |
||
148 |
" -preserveDN - Don't re-order the DN\n", |
||
149 |
" -noemailDN - Don't add the EMAIL field into certificate' subject\n", |
||
150 |
" -batch - Don't ask questions\n", |
||
151 |
" -msie_hack - msie modifications to handle all those universal strings\n", |
||
152 |
" -revoke file - Revoke a certificate (given in file)\n", |
||
153 |
" -subj arg - Use arg instead of request's subject\n", |
||
154 |
" -utf8 - input characters are UTF8 (default ASCII)\n", |
||
155 |
" -multivalue-rdn - enable support for multivalued RDNs\n", |
||
156 |
" -extensions .. - Extension section (override value in config file)\n", |
||
157 |
" -extfile file - Configuration file with X509v3 extentions to add\n", |
||
158 |
" -crlexts .. - CRL extension section (override value in config file)\n", |
||
159 |
" -status serial - Shows certificate status given the serial number\n", |
||
160 |
" -updatedb - Updates db for expired certificates\n", |
||
161 |
NULL |
||
162 |
}; |
||
163 |
|||
164 |
static void lookup_fail(const char *name, const char *tag); |
||
165 |
static int certify(X509 ** xret, char *infile, EVP_PKEY * pkey, X509 * x509, |
||
166 |
const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
167 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
168 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
169 |
char *enddate, long days, int batch, char *ext_sect, CONF * conf, |
||
170 |
int verbose, unsigned long certopt, unsigned long nameopt, |
||
171 |
int default_op, int ext_copy, int selfsign); |
||
172 |
static int certify_cert(X509 ** xret, char *infile, EVP_PKEY * pkey, |
||
173 |
X509 * x509, const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
174 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
175 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
176 |
char *enddate, long days, int batch, char *ext_sect, CONF * conf, |
||
177 |
int verbose, unsigned long certopt, unsigned long nameopt, int default_op, |
||
178 |
int ext_copy); |
||
179 |
static int certify_spkac(X509 ** xret, char *infile, EVP_PKEY * pkey, |
||
180 |
X509 * x509, const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
181 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
182 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
183 |
char *enddate, long days, char *ext_sect, CONF * conf, int verbose, |
||
184 |
unsigned long certopt, unsigned long nameopt, int default_op, int ext_copy); |
||
185 |
static void write_new_certificate(BIO * bp, X509 * x, int output_der, |
||
186 |
int notext); |
||
187 |
static int do_body(X509 ** xret, EVP_PKEY * pkey, X509 * x509, |
||
188 |
const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
189 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
190 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
191 |
char *enddate, long days, int batch, int verbose, X509_REQ * req, |
||
192 |
char *ext_sect, CONF * conf, unsigned long certopt, unsigned long nameopt, |
||
193 |
int default_op, int ext_copy, int selfsign); |
||
194 |
static int do_revoke(X509 * x509, CA_DB * db, int ext, char *extval); |
||
195 |
static int get_certificate_status(const char *ser_status, CA_DB * db); |
||
196 |
static int do_updatedb(CA_DB * db); |
||
197 |
static int check_time_format(const char *str); |
||
198 |
static char * bin2hex(unsigned char *, size_t); |
||
199 |
char *make_revocation_str(int rev_type, char *rev_arg); |
||
200 |
int make_revoked(X509_REVOKED * rev, const char *str); |
||
201 |
int old_entry_print(BIO * bp, ASN1_OBJECT * obj, ASN1_STRING * str); |
||
202 |
static CONF *conf = NULL; |
||
203 |
static CONF *extconf = NULL; |
||
204 |
static char *section = NULL; |
||
205 |
|||
206 |
static int preserve = 0; |
||
207 |
static int msie_hack = 0; |
||
208 |
|||
209 |
|||
210 |
/* |
||
211 |
* Set a certificate time based on user provided input. Make sure |
||
212 |
* what we put in the certificate is legit for RFC 5280. Returns |
||
213 |
* 0 on success, -1 on an invalid time string. Strings must be |
||
214 |
* YYYYMMDDHHMMSSZ for post 2050 dates. YYYYMMDDHHMMSSZ or |
||
215 |
* YYMMDDHHMMSSZ is accepted for pre 2050 dates, and fixed up to |
||
216 |
* be the correct format in the certificate. |
||
217 |
*/ |
||
218 |
static int |
||
219 |
setCertificateTime(ASN1_TIME *x509time, char *timestring) |
||
220 |
{ |
||
221 |
struct tm tm1; |
||
222 |
memset(&tm1, 0, sizeof(tm1)); |
||
223 |
if (ASN1_time_parse(timestring, strlen(timestring), &tm1, 0) == -1) |
||
224 |
return (-1); |
||
225 |
if (!ASN1_TIME_set_tm(x509time, &tm1)) |
||
226 |
return (-1); |
||
227 |
return 0; |
||
228 |
} |
||
229 |
|||
230 |
int |
||
231 |
ca_main(int argc, char **argv) |
||
232 |
{ |
||
233 |
56 |
char *key = NULL, *passargin = NULL; |
|
234 |
int create_ser = 0; |
||
235 |
int free_key = 0; |
||
236 |
int total = 0; |
||
237 |
int total_done = 0; |
||
238 |
int badops = 0; |
||
239 |
int ret = 1; |
||
240 |
int email_dn = 1; |
||
241 |
int req = 0; |
||
242 |
int verbose = 0; |
||
243 |
int gencrl = 0; |
||
244 |
int dorevoke = 0; |
||
245 |
int doupdatedb = 0; |
||
246 |
28 |
long crldays = 0; |
|
247 |
28 |
long crlhours = 0; |
|
248 |
long crlsec = 0; |
||
249 |
28 |
long errorline = -1; |
|
250 |
char *configfile = NULL; |
||
251 |
char *md = NULL; |
||
252 |
char *policy = NULL; |
||
253 |
char *keyfile = NULL; |
||
254 |
char *certfile = NULL; |
||
255 |
int keyform = FORMAT_PEM; |
||
256 |
char *infile = NULL; |
||
257 |
char *spkac_file = NULL; |
||
258 |
char *ss_cert_file = NULL; |
||
259 |
char *ser_status = NULL; |
||
260 |
EVP_PKEY *pkey = NULL; |
||
261 |
int output_der = 0; |
||
262 |
char *outfile = NULL; |
||
263 |
char *outdir = NULL; |
||
264 |
char *serialfile = NULL; |
||
265 |
char *crlnumberfile = NULL; |
||
266 |
char *extensions = NULL; |
||
267 |
char *extfile = NULL; |
||
268 |
char *subj = NULL; |
||
269 |
unsigned long chtype = MBSTRING_ASC; |
||
270 |
int multirdn = 0; |
||
271 |
char *tmp_email_dn = NULL; |
||
272 |
char *crl_ext = NULL; |
||
273 |
int rev_type = REV_NONE; |
||
274 |
char *rev_arg = NULL; |
||
275 |
28 |
BIGNUM *serial = NULL; |
|
276 |
BIGNUM *crlnumber = NULL; |
||
277 |
char *startdate = NULL; |
||
278 |
char *enddate = NULL; |
||
279 |
28 |
long days = 0; |
|
280 |
int batch = 0; |
||
281 |
int notext = 0; |
||
282 |
28 |
unsigned long nameopt = 0, certopt = 0; |
|
283 |
int default_op = 1; |
||
284 |
28 |
int ext_copy = EXT_COPY_NONE; |
|
285 |
int selfsign = 0; |
||
286 |
X509 *x509 = NULL, *x509p = NULL; |
||
287 |
28 |
X509 *x = NULL; |
|
288 |
BIO *in = NULL, *out = NULL, *Sout = NULL, *Cout = NULL; |
||
289 |
char *dbfile = NULL; |
||
290 |
CA_DB *db = NULL; |
||
291 |
X509_CRL *crl = NULL; |
||
292 |
X509_REVOKED *r = NULL; |
||
293 |
ASN1_TIME *tmptm; |
||
294 |
ASN1_INTEGER *tmpser; |
||
295 |
char *f; |
||
296 |
const char *p; |
||
297 |
char *const * pp; |
||
298 |
int i, j; |
||
299 |
const EVP_MD *dgst = NULL; |
||
300 |
STACK_OF(CONF_VALUE) * attribs = NULL; |
||
301 |
STACK_OF(X509) * cert_sk = NULL; |
||
302 |
STACK_OF(OPENSSL_STRING) * sigopts = NULL; |
||
303 |
char *tofree = NULL; |
||
304 |
28 |
const char *errstr = NULL; |
|
305 |
28 |
DB_ATTR db_attr; |
|
306 |
|||
307 |
✓✗ | 28 |
if (single_execution) { |
308 |
✗✓ | 28 |
if (pledge("stdio cpath wpath rpath tty flock", NULL) == -1) { |
309 |
perror("pledge"); |
||
310 |
exit(1); |
||
311 |
} |
||
312 |
} |
||
313 |
|||
314 |
28 |
conf = NULL; |
|
315 |
28 |
key = NULL; |
|
316 |
28 |
section = NULL; |
|
317 |
|||
318 |
28 |
preserve = 0; |
|
319 |
28 |
msie_hack = 0; |
|
320 |
|||
321 |
28 |
argc--; |
|
322 |
28 |
argv++; |
|
323 |
✓✓ | 368 |
while (argc >= 1) { |
324 |
✗✓ | 160 |
if (strcmp(*argv, "-verbose") == 0) |
325 |
verbose = 1; |
||
326 |
✗✓ | 160 |
else if (strcmp(*argv, "-config") == 0) { |
327 |
if (--argc < 1) |
||
328 |
goto bad; |
||
329 |
configfile = *(++argv); |
||
330 |
✗✓ | 160 |
} else if (strcmp(*argv, "-name") == 0) { |
331 |
if (--argc < 1) |
||
332 |
goto bad; |
||
333 |
section = *(++argv); |
||
334 |
✗✓ | 160 |
} else if (strcmp(*argv, "-subj") == 0) { |
335 |
if (--argc < 1) |
||
336 |
goto bad; |
||
337 |
subj = *(++argv); |
||
338 |
/* preserve=1; */ |
||
339 |
✗✓ | 160 |
} else if (strcmp(*argv, "-utf8") == 0) |
340 |
chtype = MBSTRING_UTF8; |
||
341 |
✗✓ | 160 |
else if (strcmp(*argv, "-create_serial") == 0) |
342 |
create_ser = 1; |
||
343 |
✗✓ | 160 |
else if (strcmp(*argv, "-multivalue-rdn") == 0) |
344 |
multirdn = 1; |
||
345 |
✗✓ | 160 |
else if (strcmp(*argv, "-startdate") == 0) { |
346 |
if (--argc < 1) |
||
347 |
goto bad; |
||
348 |
startdate = *(++argv); |
||
349 |
✗✓ | 160 |
} else if (strcmp(*argv, "-enddate") == 0) { |
350 |
if (--argc < 1) |
||
351 |
goto bad; |
||
352 |
enddate = *(++argv); |
||
353 |
✗✓ | 160 |
} else if (strcmp(*argv, "-days") == 0) { |
354 |
if (--argc < 1) |
||
355 |
goto bad; |
||
356 |
days = strtonum(*(++argv), 0, LONG_MAX, &errstr); |
||
357 |
if (errstr) |
||
358 |
goto bad; |
||
359 |
✗✓ | 160 |
} else if (strcmp(*argv, "-md") == 0) { |
360 |
if (--argc < 1) |
||
361 |
goto bad; |
||
362 |
md = *(++argv); |
||
363 |
✗✓ | 160 |
} else if (strcmp(*argv, "-policy") == 0) { |
364 |
if (--argc < 1) |
||
365 |
goto bad; |
||
366 |
policy = *(++argv); |
||
367 |
✓✓ | 160 |
} else if (strcmp(*argv, "-keyfile") == 0) { |
368 |
✓✗ | 24 |
if (--argc < 1) |
369 |
goto bad; |
||
370 |
24 |
keyfile = *(++argv); |
|
371 |
✗✓ | 160 |
} else if (strcmp(*argv, "-keyform") == 0) { |
372 |
if (--argc < 1) |
||
373 |
goto bad; |
||
374 |
keyform = str2fmt(*(++argv)); |
||
375 |
✓✓ | 136 |
} else if (strcmp(*argv, "-passin") == 0) { |
376 |
✓✗ | 4 |
if (--argc < 1) |
377 |
goto bad; |
||
378 |
4 |
passargin = *(++argv); |
|
379 |
✓✓ | 136 |
} else if (strcmp(*argv, "-key") == 0) { |
380 |
✓✗ | 20 |
if (--argc < 1) |
381 |
goto bad; |
||
382 |
20 |
key = *(++argv); |
|
383 |
✓✓ | 132 |
} else if (strcmp(*argv, "-cert") == 0) { |
384 |
✓✗ | 24 |
if (--argc < 1) |
385 |
goto bad; |
||
386 |
24 |
certfile = *(++argv); |
|
387 |
✗✓ | 112 |
} else if (strcmp(*argv, "-selfsign") == 0) |
388 |
selfsign = 1; |
||
389 |
✓✓ | 88 |
else if (strcmp(*argv, "-in") == 0) { |
390 |
✓✗ | 16 |
if (--argc < 1) |
391 |
goto bad; |
||
392 |
16 |
infile = *(++argv); |
|
393 |
req = 1; |
||
394 |
✓✓ | 88 |
} else if (strcmp(*argv, "-out") == 0) { |
395 |
✓✗ | 24 |
if (--argc < 1) |
396 |
goto bad; |
||
397 |
24 |
outfile = *(++argv); |
|
398 |
✗✓ | 72 |
} else if (strcmp(*argv, "-outdir") == 0) { |
399 |
if (--argc < 1) |
||
400 |
goto bad; |
||
401 |
outdir = *(++argv); |
||
402 |
✗✓ | 48 |
} else if (strcmp(*argv, "-sigopt") == 0) { |
403 |
if (--argc < 1) |
||
404 |
goto bad; |
||
405 |
if (!sigopts) |
||
406 |
sigopts = sk_OPENSSL_STRING_new_null(); |
||
407 |
if (!sigopts || |
||
408 |
!sk_OPENSSL_STRING_push(sigopts, *(++argv))) |
||
409 |
goto bad; |
||
410 |
✗✓ | 48 |
} else if (strcmp(*argv, "-notext") == 0) |
411 |
notext = 1; |
||
412 |
✓✓ | 48 |
else if (strcmp(*argv, "-batch") == 0) |
413 |
20 |
batch = 1; |
|
414 |
✗✓ | 28 |
else if (strcmp(*argv, "-preserveDN") == 0) |
415 |
preserve = 1; |
||
416 |
✗✓ | 28 |
else if (strcmp(*argv, "-noemailDN") == 0) |
417 |
email_dn = 0; |
||
418 |
✓✓ | 28 |
else if (strcmp(*argv, "-gencrl") == 0) |
419 |
4 |
gencrl = 1; |
|
420 |
✗✓ | 24 |
else if (strcmp(*argv, "-msie_hack") == 0) |
421 |
msie_hack = 1; |
||
422 |
✓✓ | 24 |
else if (strcmp(*argv, "-crldays") == 0) { |
423 |
✓✗ | 4 |
if (--argc < 1) |
424 |
goto bad; |
||
425 |
4 |
crldays = strtonum(*(++argv), 0, LONG_MAX, &errstr); |
|
426 |
✗✓ | 4 |
if (errstr) |
427 |
goto bad; |
||
428 |
✗✓ | 20 |
} else if (strcmp(*argv, "-crlhours") == 0) { |
429 |
if (--argc < 1) |
||
430 |
goto bad; |
||
431 |
crlhours = strtonum(*(++argv), 0, LONG_MAX, &errstr); |
||
432 |
if (errstr) |
||
433 |
goto bad; |
||
434 |
✗✓ | 20 |
} else if (strcmp(*argv, "-crlsec") == 0) { |
435 |
if (--argc < 1) |
||
436 |
goto bad; |
||
437 |
crlsec = strtonum(*(++argv), 0, LONG_MAX, &errstr); |
||
438 |
if (errstr) |
||
439 |
goto bad; |
||
440 |
✗✓ | 20 |
} else if (strcmp(*argv, "-infiles") == 0) { |
441 |
argc--; |
||
442 |
argv++; |
||
443 |
req = 1; |
||
444 |
break; |
||
445 |
✗✓ | 20 |
} else if (strcmp(*argv, "-ss_cert") == 0) { |
446 |
if (--argc < 1) |
||
447 |
goto bad; |
||
448 |
ss_cert_file = *(++argv); |
||
449 |
req = 1; |
||
450 |
✓✓ | 20 |
} else if (strcmp(*argv, "-spkac") == 0) { |
451 |
✓✗ | 4 |
if (--argc < 1) |
452 |
goto bad; |
||
453 |
4 |
spkac_file = *(++argv); |
|
454 |
req = 1; |
||
455 |
✓✓ | 20 |
} else if (strcmp(*argv, "-revoke") == 0) { |
456 |
✓✗ | 4 |
if (--argc < 1) |
457 |
goto bad; |
||
458 |
4 |
infile = *(++argv); |
|
459 |
dorevoke = 1; |
||
460 |
✓✓ | 16 |
} else if (strcmp(*argv, "-extensions") == 0) { |
461 |
✓✗ | 8 |
if (--argc < 1) |
462 |
goto bad; |
||
463 |
8 |
extensions = *(++argv); |
|
464 |
✗✓ | 12 |
} else if (strcmp(*argv, "-extfile") == 0) { |
465 |
if (--argc < 1) |
||
466 |
goto bad; |
||
467 |
extfile = *(++argv); |
||
468 |
✗✓ | 4 |
} else if (strcmp(*argv, "-status") == 0) { |
469 |
if (--argc < 1) |
||
470 |
goto bad; |
||
471 |
ser_status = *(++argv); |
||
472 |
✗✓ | 4 |
} else if (strcmp(*argv, "-updatedb") == 0) { |
473 |
doupdatedb = 1; |
||
474 |
✗✓ | 4 |
} else if (strcmp(*argv, "-crlexts") == 0) { |
475 |
if (--argc < 1) |
||
476 |
goto bad; |
||
477 |
crl_ext = *(++argv); |
||
478 |
✗✓ | 4 |
} else if (strcmp(*argv, "-crl_reason") == 0) { |
479 |
if (--argc < 1) |
||
480 |
goto bad; |
||
481 |
rev_arg = *(++argv); |
||
482 |
rev_type = REV_CRL_REASON; |
||
483 |
✗✓ | 4 |
} else if (strcmp(*argv, "-crl_hold") == 0) { |
484 |
if (--argc < 1) |
||
485 |
goto bad; |
||
486 |
rev_arg = *(++argv); |
||
487 |
rev_type = REV_HOLD; |
||
488 |
✗✓ | 4 |
} else if (strcmp(*argv, "-crl_compromise") == 0) { |
489 |
if (--argc < 1) |
||
490 |
goto bad; |
||
491 |
rev_arg = *(++argv); |
||
492 |
rev_type = REV_KEY_COMPROMISE; |
||
493 |
✗✓ | 4 |
} else if (strcmp(*argv, "-crl_CA_compromise") == 0) { |
494 |
if (--argc < 1) |
||
495 |
goto bad; |
||
496 |
rev_arg = *(++argv); |
||
497 |
rev_type = REV_CA_COMPROMISE; |
||
498 |
} |
||
499 |
else { |
||
500 |
bad: |
||
501 |
✗✓ | 4 |
if (errstr) |
502 |
BIO_printf(bio_err, "invalid argument %s: %s\n", |
||
503 |
*argv, errstr); |
||
504 |
else |
||
505 |
4 |
BIO_printf(bio_err, "unknown option %s\n", *argv); |
|
506 |
badops = 1; |
||
507 |
4 |
break; |
|
508 |
} |
||
509 |
156 |
argc--; |
|
510 |
156 |
argv++; |
|
511 |
} |
||
512 |
|||
513 |
✓✓ | 28 |
if (badops) { |
514 |
const char **pp2; |
||
515 |
|||
516 |
✓✓✓✗ |
304 |
for (pp2 = ca_usage; (*pp2 != NULL); pp2++) |
517 |
148 |
BIO_printf(bio_err, "%s", *pp2); |
|
518 |
goto err; |
||
519 |
} |
||
520 |
|||
521 |
/*****************************************************************/ |
||
522 |
tofree = NULL; |
||
523 |
✓✗ | 24 |
if (configfile == NULL) |
524 |
24 |
configfile = getenv("OPENSSL_CONF"); |
|
525 |
✗✓ | 24 |
if (configfile == NULL) { |
526 |
if ((tofree = make_config_name()) == NULL) { |
||
527 |
BIO_printf(bio_err, "error making config file name\n"); |
||
528 |
goto err; |
||
529 |
} |
||
530 |
configfile = tofree; |
||
531 |
} |
||
532 |
24 |
BIO_printf(bio_err, "Using configuration from %s\n", configfile); |
|
533 |
24 |
conf = NCONF_new(NULL); |
|
534 |
✗✓ | 24 |
if (NCONF_load(conf, configfile, &errorline) <= 0) { |
535 |
if (errorline <= 0) |
||
536 |
BIO_printf(bio_err, |
||
537 |
"error loading the config file '%s'\n", |
||
538 |
configfile); |
||
539 |
else |
||
540 |
BIO_printf(bio_err, |
||
541 |
"error on line %ld of config file '%s'\n", |
||
542 |
errorline, configfile); |
||
543 |
goto err; |
||
544 |
} |
||
545 |
24 |
free(tofree); |
|
546 |
tofree = NULL; |
||
547 |
|||
548 |
/* Lets get the config section we are using */ |
||
549 |
✓✗ | 24 |
if (section == NULL) { |
550 |
24 |
section = NCONF_get_string(conf, BASE_SECTION, ENV_DEFAULT_CA); |
|
551 |
✗✓ | 24 |
if (section == NULL) { |
552 |
lookup_fail(BASE_SECTION, ENV_DEFAULT_CA); |
||
553 |
goto err; |
||
554 |
} |
||
555 |
} |
||
556 |
✓✗ | 24 |
if (conf != NULL) { |
557 |
24 |
p = NCONF_get_string(conf, NULL, "oid_file"); |
|
558 |
✓✗ | 24 |
if (p == NULL) |
559 |
24 |
ERR_clear_error(); |
|
560 |
✗✓ | 24 |
if (p != NULL) { |
561 |
BIO *oid_bio; |
||
562 |
|||
563 |
oid_bio = BIO_new_file(p, "r"); |
||
564 |
if (oid_bio == NULL) { |
||
565 |
/* |
||
566 |
BIO_printf(bio_err, |
||
567 |
"problems opening %s for extra oid's\n", p); |
||
568 |
ERR_print_errors(bio_err); |
||
569 |
*/ |
||
570 |
ERR_clear_error(); |
||
571 |
} else { |
||
572 |
OBJ_create_objects(oid_bio); |
||
573 |
BIO_free(oid_bio); |
||
574 |
} |
||
575 |
} |
||
576 |
✗✓ | 24 |
if (!add_oid_section(bio_err, conf)) { |
577 |
ERR_print_errors(bio_err); |
||
578 |
goto err; |
||
579 |
} |
||
580 |
} |
||
581 |
24 |
f = NCONF_get_string(conf, section, STRING_MASK); |
|
582 |
✓✗ | 24 |
if (!f) |
583 |
24 |
ERR_clear_error(); |
|
584 |
|||
585 |
✗✓✗✗ |
24 |
if (f && !ASN1_STRING_set_default_mask_asc(f)) { |
586 |
BIO_printf(bio_err, |
||
587 |
"Invalid global string mask setting %s\n", f); |
||
588 |
goto err; |
||
589 |
} |
||
590 |
✓✗ | 24 |
if (chtype != MBSTRING_UTF8) { |
591 |
24 |
f = NCONF_get_string(conf, section, UTF8_IN); |
|
592 |
✓✗ | 24 |
if (!f) |
593 |
24 |
ERR_clear_error(); |
|
594 |
else if (!strcmp(f, "yes")) |
||
595 |
chtype = MBSTRING_UTF8; |
||
596 |
} |
||
597 |
24 |
db_attr.unique_subject = 1; |
|
598 |
24 |
p = NCONF_get_string(conf, section, ENV_UNIQUE_SUBJECT); |
|
599 |
✗✓ | 24 |
if (p) { |
600 |
db_attr.unique_subject = parse_yesno(p, 1); |
||
601 |
} else |
||
602 |
24 |
ERR_clear_error(); |
|
603 |
|||
604 |
24 |
in = BIO_new(BIO_s_file()); |
|
605 |
24 |
out = BIO_new(BIO_s_file()); |
|
606 |
24 |
Sout = BIO_new(BIO_s_file()); |
|
607 |
24 |
Cout = BIO_new(BIO_s_file()); |
|
608 |
✗✓ | 24 |
if ((in == NULL) || (out == NULL) || (Sout == NULL) || (Cout == NULL)) { |
609 |
ERR_print_errors(bio_err); |
||
610 |
goto err; |
||
611 |
} |
||
612 |
/*****************************************************************/ |
||
613 |
/* report status of cert with serial number given on command line */ |
||
614 |
✗✓ | 24 |
if (ser_status) { |
615 |
if ((dbfile = NCONF_get_string(conf, section, |
||
616 |
ENV_DATABASE)) == NULL) { |
||
617 |
lookup_fail(section, ENV_DATABASE); |
||
618 |
goto err; |
||
619 |
} |
||
620 |
db = load_index(dbfile, &db_attr); |
||
621 |
if (db == NULL) |
||
622 |
goto err; |
||
623 |
|||
624 |
if (!index_index(db)) |
||
625 |
goto err; |
||
626 |
|||
627 |
if (get_certificate_status(ser_status, db) != 1) |
||
628 |
BIO_printf(bio_err, "Error verifying serial %s!\n", |
||
629 |
ser_status); |
||
630 |
goto err; |
||
631 |
} |
||
632 |
/*****************************************************************/ |
||
633 |
/* we definitely need a private key, so let's get it */ |
||
634 |
|||
635 |
✗✓✗✗ |
24 |
if ((keyfile == NULL) && ((keyfile = NCONF_get_string(conf, |
636 |
section, ENV_PRIVATE_KEY)) == NULL)) { |
||
637 |
lookup_fail(section, ENV_PRIVATE_KEY); |
||
638 |
goto err; |
||
639 |
} |
||
640 |
✓✓ | 24 |
if (!key) { |
641 |
free_key = 1; |
||
642 |
✗✓ | 4 |
if (!app_passwd(bio_err, passargin, NULL, &key, NULL)) { |
643 |
BIO_printf(bio_err, "Error getting password\n"); |
||
644 |
goto err; |
||
645 |
} |
||
646 |
} |
||
647 |
24 |
pkey = load_key(bio_err, keyfile, keyform, 0, key, "CA private key"); |
|
648 |
✓✗ | 24 |
if (key) |
649 |
24 |
explicit_bzero(key, strlen(key)); |
|
650 |
✓✗ | 24 |
if (pkey == NULL) { |
651 |
/* load_key() has already printed an appropriate message */ |
||
652 |
goto err; |
||
653 |
} |
||
654 |
/*****************************************************************/ |
||
655 |
/* we need a certificate */ |
||
656 |
✓✗ | 24 |
if (!selfsign || spkac_file || ss_cert_file || gencrl) { |
657 |
✗✓✗✗ |
24 |
if ((certfile == NULL) && |
658 |
((certfile = NCONF_get_string(conf, |
||
659 |
section, ENV_CERTIFICATE)) == NULL)) { |
||
660 |
lookup_fail(section, ENV_CERTIFICATE); |
||
661 |
goto err; |
||
662 |
} |
||
663 |
24 |
x509 = load_cert(bio_err, certfile, FORMAT_PEM, NULL, |
|
664 |
"CA certificate"); |
||
665 |
✓✗ | 24 |
if (x509 == NULL) |
666 |
goto err; |
||
667 |
|||
668 |
✗✓ | 24 |
if (!X509_check_private_key(x509, pkey)) { |
669 |
BIO_printf(bio_err, |
||
670 |
"CA certificate and CA private key do not match\n"); |
||
671 |
goto err; |
||
672 |
} |
||
673 |
} |
||
674 |
✓✗ | 24 |
if (!selfsign) |
675 |
24 |
x509p = x509; |
|
676 |
|||
677 |
24 |
f = NCONF_get_string(conf, BASE_SECTION, ENV_PRESERVE); |
|
678 |
✓✗ | 24 |
if (f == NULL) |
679 |
24 |
ERR_clear_error(); |
|
680 |
✗✓✗✗ ✗✗ |
24 |
if ((f != NULL) && ((*f == 'y') || (*f == 'Y'))) |
681 |
preserve = 1; |
||
682 |
24 |
f = NCONF_get_string(conf, BASE_SECTION, ENV_MSIE_HACK); |
|
683 |
✓✗ | 24 |
if (f == NULL) |
684 |
24 |
ERR_clear_error(); |
|
685 |
✗✓✗✗ ✗✗ |
24 |
if ((f != NULL) && ((*f == 'y') || (*f == 'Y'))) |
686 |
msie_hack = 1; |
||
687 |
|||
688 |
24 |
f = NCONF_get_string(conf, section, ENV_NAMEOPT); |
|
689 |
|||
690 |
✗✓ | 24 |
if (f) { |
691 |
if (!set_name_ex(&nameopt, f)) { |
||
692 |
BIO_printf(bio_err, |
||
693 |
"Invalid name options: \"%s\"\n", f); |
||
694 |
goto err; |
||
695 |
} |
||
696 |
default_op = 0; |
||
697 |
} else |
||
698 |
24 |
ERR_clear_error(); |
|
699 |
|||
700 |
24 |
f = NCONF_get_string(conf, section, ENV_CERTOPT); |
|
701 |
|||
702 |
✗✓ | 24 |
if (f) { |
703 |
if (!set_cert_ex(&certopt, f)) { |
||
704 |
BIO_printf(bio_err, |
||
705 |
"Invalid certificate options: \"%s\"\n", f); |
||
706 |
goto err; |
||
707 |
} |
||
708 |
default_op = 0; |
||
709 |
} else |
||
710 |
24 |
ERR_clear_error(); |
|
711 |
|||
712 |
24 |
f = NCONF_get_string(conf, section, ENV_EXTCOPY); |
|
713 |
|||
714 |
✗✓ | 24 |
if (f) { |
715 |
if (!set_ext_copy(&ext_copy, f)) { |
||
716 |
BIO_printf(bio_err, |
||
717 |
"Invalid extension copy option: \"%s\"\n", f); |
||
718 |
goto err; |
||
719 |
} |
||
720 |
} else |
||
721 |
24 |
ERR_clear_error(); |
|
722 |
|||
723 |
/*****************************************************************/ |
||
724 |
/* lookup where to write new certificates */ |
||
725 |
✓✓ | 24 |
if (outdir == NULL && req) { |
726 |
✗✓ | 40 |
if ((outdir = NCONF_get_string(conf, section, |
727 |
20 |
ENV_NEW_CERTS_DIR)) == NULL) { |
|
728 |
BIO_printf(bio_err, "output directory %s not defined\n", |
||
729 |
ENV_NEW_CERTS_DIR); |
||
730 |
goto err; |
||
731 |
} |
||
732 |
} |
||
733 |
/*****************************************************************/ |
||
734 |
/* we need to load the database file */ |
||
735 |
✗✓ | 24 |
if ((dbfile = NCONF_get_string(conf, section, ENV_DATABASE)) == NULL) { |
736 |
lookup_fail(section, ENV_DATABASE); |
||
737 |
goto err; |
||
738 |
} |
||
739 |
24 |
db = load_index(dbfile, &db_attr); |
|
740 |
✓✗ | 24 |
if (db == NULL) |
741 |
goto err; |
||
742 |
|||
743 |
/* Lets check some fields */ |
||
744 |
✓✓ | 168 |
for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { |
745 |
60 |
pp = sk_OPENSSL_PSTRING_value(db->db->data, i); |
|
746 |
✓✓✗✓ |
112 |
if ((pp[DB_type][0] != DB_TYPE_REV) && |
747 |
52 |
(pp[DB_rev_date][0] != '\0')) { |
|
748 |
BIO_printf(bio_err, "entry %d: not revoked yet, but has a revocation date\n", i + 1); |
||
749 |
goto err; |
||
750 |
} |
||
751 |
✓✓✗✓ |
68 |
if ((pp[DB_type][0] == DB_TYPE_REV) && |
752 |
8 |
!make_revoked(NULL, pp[DB_rev_date])) { |
|
753 |
BIO_printf(bio_err, " in entry %d\n", i + 1); |
||
754 |
goto err; |
||
755 |
} |
||
756 |
✗✓ | 60 |
if (!check_time_format((char *) pp[DB_exp_date])) { |
757 |
BIO_printf(bio_err, "entry %d: invalid expiry date\n", |
||
758 |
i + 1); |
||
759 |
goto err; |
||
760 |
} |
||
761 |
60 |
p = pp[DB_serial]; |
|
762 |
60 |
j = strlen(p); |
|
763 |
✗✓ | 60 |
if (*p == '-') { |
764 |
p++; |
||
765 |
j--; |
||
766 |
} |
||
767 |
✗✓ | 60 |
if ((j & 1) || (j < 2)) { |
768 |
BIO_printf(bio_err, |
||
769 |
"entry %d: bad serial number length (%d)\n", |
||
770 |
i + 1, j); |
||
771 |
goto err; |
||
772 |
} |
||
773 |
✓✓ | 584 |
while (*p) { |
774 |
✓✗✓✓ ✗✗ |
464 |
if (!(((*p >= '0') && (*p <= '9')) || |
775 |
✓✗✗✓ |
88 |
((*p >= 'A') && (*p <= 'F')) || |
776 |
((*p >= 'a') && (*p <= 'f')))) { |
||
777 |
BIO_printf(bio_err, "entry %d: bad serial number characters, char pos %ld, char is '%c'\n", i + 1, (long) (p - pp[DB_serial]), *p); |
||
778 |
goto err; |
||
779 |
} |
||
780 |
232 |
p++; |
|
781 |
} |
||
782 |
} |
||
783 |
✗✓ | 24 |
if (verbose) { |
784 |
BIO_set_fp(out, stdout, BIO_NOCLOSE | BIO_FP_TEXT); /* cannot fail */ |
||
785 |
TXT_DB_write(out, db->db); |
||
786 |
BIO_printf(bio_err, "%d entries loaded from the database\n", |
||
787 |
sk_OPENSSL_PSTRING_num(db->db->data)); |
||
788 |
BIO_printf(bio_err, "generating index\n"); |
||
789 |
} |
||
790 |
✓✗ | 24 |
if (!index_index(db)) |
791 |
goto err; |
||
792 |
|||
793 |
/*****************************************************************/ |
||
794 |
/* Update the db file for expired certificates */ |
||
795 |
✗✓ | 24 |
if (doupdatedb) { |
796 |
if (verbose) |
||
797 |
BIO_printf(bio_err, "Updating %s ...\n", dbfile); |
||
798 |
|||
799 |
i = do_updatedb(db); |
||
800 |
if (i == -1) { |
||
801 |
BIO_printf(bio_err, "Malloc failure\n"); |
||
802 |
goto err; |
||
803 |
} else if (i == 0) { |
||
804 |
if (verbose) |
||
805 |
BIO_printf(bio_err, |
||
806 |
"No entries found to mark expired\n"); |
||
807 |
} else { |
||
808 |
if (!save_index(dbfile, "new", db)) |
||
809 |
goto err; |
||
810 |
|||
811 |
if (!rotate_index(dbfile, "new", "old")) |
||
812 |
goto err; |
||
813 |
|||
814 |
if (verbose) |
||
815 |
BIO_printf(bio_err, |
||
816 |
"Done. %d entries marked as expired\n", i); |
||
817 |
} |
||
818 |
} |
||
819 |
/*****************************************************************/ |
||
820 |
/* Read extentions config file */ |
||
821 |
✗✓ | 24 |
if (extfile) { |
822 |
extconf = NCONF_new(NULL); |
||
823 |
if (NCONF_load(extconf, extfile, &errorline) <= 0) { |
||
824 |
if (errorline <= 0) |
||
825 |
BIO_printf(bio_err, |
||
826 |
"ERROR: loading the config file '%s'\n", |
||
827 |
extfile); |
||
828 |
else |
||
829 |
BIO_printf(bio_err, |
||
830 |
"ERROR: on line %ld of config file '%s'\n", |
||
831 |
errorline, extfile); |
||
832 |
ret = 1; |
||
833 |
goto err; |
||
834 |
} |
||
835 |
if (verbose) |
||
836 |
BIO_printf(bio_err, |
||
837 |
"Successfully loaded extensions file %s\n", |
||
838 |
extfile); |
||
839 |
|||
840 |
/* We can have sections in the ext file */ |
||
841 |
if (!extensions && !(extensions = NCONF_get_string(extconf, |
||
842 |
"default", "extensions"))) |
||
843 |
extensions = "default"; |
||
844 |
} |
||
845 |
/*****************************************************************/ |
||
846 |
✓✗ | 24 |
if (req || gencrl) { |
847 |
✓✗ | 24 |
if (outfile != NULL) { |
848 |
✗✓ | 24 |
if (BIO_write_filename(Sout, outfile) <= 0) { |
849 |
perror(outfile); |
||
850 |
goto err; |
||
851 |
} |
||
852 |
} else { |
||
853 |
BIO_set_fp(Sout, stdout, BIO_NOCLOSE | BIO_FP_TEXT); |
||
854 |
} |
||
855 |
} |
||
856 |
✓✗✗✓ |
72 |
if ((md == NULL) && ((md = NCONF_get_string(conf, section, |
857 |
24 |
ENV_DEFAULT_MD)) == NULL)) { |
|
858 |
lookup_fail(section, ENV_DEFAULT_MD); |
||
859 |
goto err; |
||
860 |
} |
||
861 |
✓✗ | 24 |
if (!strcmp(md, "default")) { |
862 |
24 |
int def_nid; |
|
863 |
✗✓ | 24 |
if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) <= 0) { |
864 |
BIO_puts(bio_err, "no default digest\n"); |
||
865 |
goto err; |
||
866 |
} |
||
867 |
24 |
md = (char *) OBJ_nid2sn(def_nid); |
|
868 |
✓✗✗ | 48 |
} |
869 |
✗✓ | 24 |
if ((dgst = EVP_get_digestbyname(md)) == NULL) { |
870 |
BIO_printf(bio_err, |
||
871 |
"%s is an unsupported message digest type\n", md); |
||
872 |
goto err; |
||
873 |
} |
||
874 |
✓✓ | 24 |
if (req) { |
875 |
✓✗✗✓ |
80 |
if ((email_dn == 1) && ((tmp_email_dn = NCONF_get_string(conf, |
876 |
40 |
section, ENV_DEFAULT_EMAIL_DN)) != NULL)) { |
|
877 |
if (strcmp(tmp_email_dn, "no") == 0) |
||
878 |
email_dn = 0; |
||
879 |
} |
||
880 |
✗✓ | 20 |
if (verbose) |
881 |
BIO_printf(bio_err, "message digest is %s\n", |
||
882 |
OBJ_nid2ln(dgst->type)); |
||
883 |
✓✗✗✓ |
80 |
if ((policy == NULL) && ((policy = NCONF_get_string(conf, |
884 |
40 |
section, ENV_POLICY)) == NULL)) { |
|
885 |
lookup_fail(section, ENV_POLICY); |
||
886 |
goto err; |
||
887 |
} |
||
888 |
✗✓ | 20 |
if (verbose) |
889 |
BIO_printf(bio_err, "policy is %s\n", policy); |
||
890 |
|||
891 |
✗✓ | 40 |
if ((serialfile = NCONF_get_string(conf, section, |
892 |
20 |
ENV_SERIAL)) == NULL) { |
|
893 |
lookup_fail(section, ENV_SERIAL); |
||
894 |
goto err; |
||
895 |
} |
||
896 |
✓✗ | 20 |
if (!extconf) { |
897 |
/* |
||
898 |
* no '-extfile' option, so we look for extensions in |
||
899 |
* the main configuration file |
||
900 |
*/ |
||
901 |
✓✓ | 20 |
if (!extensions) { |
902 |
12 |
extensions = NCONF_get_string(conf, section, |
|
903 |
ENV_EXTENSIONS); |
||
904 |
✓✗ | 12 |
if (!extensions) |
905 |
12 |
ERR_clear_error(); |
|
906 |
} |
||
907 |
✓✓ | 20 |
if (extensions) { |
908 |
/* Check syntax of file */ |
||
909 |
8 |
X509V3_CTX ctx; |
|
910 |
8 |
X509V3_set_ctx_test(&ctx); |
|
911 |
8 |
X509V3_set_nconf(&ctx, conf); |
|
912 |
✗✓ | 8 |
if (!X509V3_EXT_add_nconf(conf, &ctx, |
913 |
extensions, NULL)) { |
||
914 |
BIO_printf(bio_err, |
||
915 |
"Error Loading extension section %s\n", |
||
916 |
extensions); |
||
917 |
ret = 1; |
||
918 |
goto err; |
||
919 |
} |
||
920 |
✓✗✗ | 16 |
} |
921 |
} |
||
922 |
✓✗ | 20 |
if (startdate == NULL) { |
923 |
20 |
startdate = NCONF_get_string(conf, section, |
|
924 |
ENV_DEFAULT_STARTDATE); |
||
925 |
✓✗ | 20 |
if (startdate == NULL) |
926 |
20 |
ERR_clear_error(); |
|
927 |
} |
||
928 |
✓✗ | 20 |
if (startdate == NULL) |
929 |
20 |
startdate = "today"; |
|
930 |
|||
931 |
✓✗ | 20 |
if (enddate == NULL) { |
932 |
20 |
enddate = NCONF_get_string(conf, section, |
|
933 |
ENV_DEFAULT_ENDDATE); |
||
934 |
✓✗ | 20 |
if (enddate == NULL) |
935 |
20 |
ERR_clear_error(); |
|
936 |
} |
||
937 |
✓✗ | 20 |
if (days == 0 && enddate == NULL) { |
938 |
✗✓ | 20 |
if (!NCONF_get_number(conf, section, |
939 |
ENV_DEFAULT_DAYS, &days)) |
||
940 |
days = 0; |
||
941 |
} |
||
942 |
✗✓ | 20 |
if (enddate == NULL && days == 0) { |
943 |
BIO_printf(bio_err, |
||
944 |
"cannot lookup how many days to certify for\n"); |
||
945 |
goto err; |
||
946 |
} |
||
947 |
✗✓ | 20 |
if ((serial = load_serial(serialfile, create_ser, NULL)) == |
948 |
NULL) { |
||
949 |
BIO_printf(bio_err, |
||
950 |
"error while loading serial number\n"); |
||
951 |
goto err; |
||
952 |
} |
||
953 |
✗✓ | 20 |
if (verbose) { |
954 |
if (BN_is_zero(serial)) |
||
955 |
BIO_printf(bio_err, |
||
956 |
"next serial number is 00\n"); |
||
957 |
else { |
||
958 |
if ((f = BN_bn2hex(serial)) == NULL) |
||
959 |
goto err; |
||
960 |
BIO_printf(bio_err, |
||
961 |
"next serial number is %s\n", f); |
||
962 |
free(f); |
||
963 |
} |
||
964 |
} |
||
965 |
✗✓ | 20 |
if ((attribs = NCONF_get_section(conf, policy)) == NULL) { |
966 |
BIO_printf(bio_err, |
||
967 |
"unable to find 'section' for %s\n", policy); |
||
968 |
goto err; |
||
969 |
} |
||
970 |
✗✓ | 20 |
if ((cert_sk = sk_X509_new_null()) == NULL) { |
971 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
972 |
goto err; |
||
973 |
} |
||
974 |
✓✓ | 20 |
if (spkac_file != NULL) { |
975 |
total++; |
||
976 |
4 |
j = certify_spkac(&x, spkac_file, pkey, x509, dgst, |
|
977 |
4 |
sigopts, attribs, db, serial, subj, chtype, |
|
978 |
4 |
multirdn, email_dn, startdate, enddate, days, |
|
979 |
4 |
extensions, conf, verbose, certopt, nameopt, |
|
980 |
4 |
default_op, ext_copy); |
|
981 |
✓✗ | 4 |
if (j < 0) |
982 |
goto err; |
||
983 |
✓✗ | 4 |
if (j > 0) { |
984 |
total_done++; |
||
985 |
4 |
BIO_printf(bio_err, "\n"); |
|
986 |
✓✗ | 4 |
if (!BN_add_word(serial, 1)) |
987 |
goto err; |
||
988 |
✗✓ | 4 |
if (!sk_X509_push(cert_sk, x)) { |
989 |
BIO_printf(bio_err, |
||
990 |
"Memory allocation failure\n"); |
||
991 |
goto err; |
||
992 |
} |
||
993 |
✓✗ | 4 |
if (outfile) { |
994 |
output_der = 1; |
||
995 |
batch = 1; |
||
996 |
4 |
} |
|
997 |
} |
||
998 |
} |
||
999 |
✗✓ | 20 |
if (ss_cert_file != NULL) { |
1000 |
total++; |
||
1001 |
j = certify_cert(&x, ss_cert_file, pkey, x509, dgst, |
||
1002 |
sigopts, attribs, db, serial, subj, chtype, |
||
1003 |
multirdn, email_dn, startdate, enddate, days, batch, |
||
1004 |
extensions, conf, verbose, certopt, nameopt, |
||
1005 |
default_op, ext_copy); |
||
1006 |
if (j < 0) |
||
1007 |
goto err; |
||
1008 |
if (j > 0) { |
||
1009 |
total_done++; |
||
1010 |
BIO_printf(bio_err, "\n"); |
||
1011 |
if (!BN_add_word(serial, 1)) |
||
1012 |
goto err; |
||
1013 |
if (!sk_X509_push(cert_sk, x)) { |
||
1014 |
BIO_printf(bio_err, |
||
1015 |
"Memory allocation failure\n"); |
||
1016 |
goto err; |
||
1017 |
} |
||
1018 |
} |
||
1019 |
} |
||
1020 |
✓✓ | 20 |
if (infile != NULL) { |
1021 |
16 |
total++; |
|
1022 |
16 |
j = certify(&x, infile, pkey, x509p, dgst, sigopts, |
|
1023 |
16 |
attribs, db, serial, subj, chtype, multirdn, |
|
1024 |
16 |
email_dn, startdate, enddate, days, batch, |
|
1025 |
16 |
extensions, conf, verbose, certopt, nameopt, |
|
1026 |
16 |
default_op, ext_copy, selfsign); |
|
1027 |
✓✗ | 16 |
if (j < 0) |
1028 |
goto err; |
||
1029 |
✓✗ | 16 |
if (j > 0) { |
1030 |
16 |
total_done++; |
|
1031 |
16 |
BIO_printf(bio_err, "\n"); |
|
1032 |
✓✗ | 16 |
if (!BN_add_word(serial, 1)) |
1033 |
goto err; |
||
1034 |
✗✓ | 16 |
if (!sk_X509_push(cert_sk, x)) { |
1035 |
BIO_printf(bio_err, |
||
1036 |
"Memory allocation failure\n"); |
||
1037 |
goto err; |
||
1038 |
} |
||
1039 |
} |
||
1040 |
} |
||
1041 |
✗✓ | 40 |
for (i = 0; i < argc; i++) { |
1042 |
total++; |
||
1043 |
j = certify(&x, argv[i], pkey, x509p, dgst, sigopts, |
||
1044 |
attribs, db, serial, subj, chtype, multirdn, |
||
1045 |
email_dn, startdate, enddate, days, batch, |
||
1046 |
extensions, conf, verbose, certopt, nameopt, |
||
1047 |
default_op, ext_copy, selfsign); |
||
1048 |
if (j < 0) |
||
1049 |
goto err; |
||
1050 |
if (j > 0) { |
||
1051 |
total_done++; |
||
1052 |
BIO_printf(bio_err, "\n"); |
||
1053 |
if (!BN_add_word(serial, 1)) |
||
1054 |
goto err; |
||
1055 |
if (!sk_X509_push(cert_sk, x)) { |
||
1056 |
BIO_printf(bio_err, |
||
1057 |
"Memory allocation failure\n"); |
||
1058 |
goto err; |
||
1059 |
} |
||
1060 |
} |
||
1061 |
} |
||
1062 |
/* |
||
1063 |
* we have a stack of newly certified certificates and a data |
||
1064 |
* base and serial number that need updating |
||
1065 |
*/ |
||
1066 |
|||
1067 |
✓✗ | 20 |
if (sk_X509_num(cert_sk) > 0) { |
1068 |
✗✓ | 20 |
if (!batch) { |
1069 |
char answer[10]; |
||
1070 |
|||
1071 |
BIO_printf(bio_err, "\n%d out of %d certificate requests certified, commit? [y/n]", total_done, total); |
||
1072 |
(void) BIO_flush(bio_err); |
||
1073 |
if (!fgets(answer, sizeof answer - 1, stdin)) { |
||
1074 |
BIO_printf(bio_err, "CERTIFICATION CANCELED: I/O error\n"); |
||
1075 |
ret = 0; |
||
1076 |
goto err; |
||
1077 |
} |
||
1078 |
if ((answer[0] != 'y') && (answer[0] != 'Y')) { |
||
1079 |
BIO_printf(bio_err, "CERTIFICATION CANCELED\n"); |
||
1080 |
ret = 0; |
||
1081 |
goto err; |
||
1082 |
} |
||
1083 |
} |
||
1084 |
20 |
BIO_printf(bio_err, "Write out database with %d new entries\n", sk_X509_num(cert_sk)); |
|
1085 |
|||
1086 |
✓✗ | 20 |
if (!save_serial(serialfile, "new", serial, NULL)) |
1087 |
goto err; |
||
1088 |
|||
1089 |
✓✗ | 20 |
if (!save_index(dbfile, "new", db)) |
1090 |
goto err; |
||
1091 |
} |
||
1092 |
✗✓ | 20 |
if (verbose) |
1093 |
BIO_printf(bio_err, "writing new certificates\n"); |
||
1094 |
✓✓ | 80 |
for (i = 0; i < sk_X509_num(cert_sk); i++) { |
1095 |
int k; |
||
1096 |
char *serialstr; |
||
1097 |
unsigned char *data; |
||
1098 |
20 |
char pempath[PATH_MAX]; |
|
1099 |
|||
1100 |
20 |
x = sk_X509_value(cert_sk, i); |
|
1101 |
|||
1102 |
20 |
j = x->cert_info->serialNumber->length; |
|
1103 |
20 |
data = (unsigned char *)x->cert_info->serialNumber->data; |
|
1104 |
✓✗ | 20 |
if (j > 0) |
1105 |
20 |
serialstr = bin2hex(data, j); |
|
1106 |
else |
||
1107 |
serialstr = strdup("00"); |
||
1108 |
✓✗ | 20 |
if (serialstr) { |
1109 |
20 |
k = snprintf(pempath, sizeof(pempath), |
|
1110 |
"%s/%s.pem", outdir, serialstr); |
||
1111 |
20 |
free(serialstr); |
|
1112 |
✓✗✗✓ |
40 |
if (k == -1 || k >= sizeof(pempath)) { |
1113 |
BIO_printf(bio_err, |
||
1114 |
"certificate file name too long\n"); |
||
1115 |
goto err; |
||
1116 |
} |
||
1117 |
} else { |
||
1118 |
BIO_printf(bio_err, |
||
1119 |
"memory allocation failed\n"); |
||
1120 |
goto err; |
||
1121 |
} |
||
1122 |
✗✓ | 20 |
if (verbose) |
1123 |
BIO_printf(bio_err, "writing %s\n", pempath); |
||
1124 |
|||
1125 |
✗✓ | 20 |
if (BIO_write_filename(Cout, pempath) <= 0) { |
1126 |
perror(pempath); |
||
1127 |
goto err; |
||
1128 |
} |
||
1129 |
20 |
write_new_certificate(Cout, x, 0, notext); |
|
1130 |
20 |
write_new_certificate(Sout, x, output_der, notext); |
|
1131 |
✓✓✓ | 80 |
} |
1132 |
|||
1133 |
✓✗ | 20 |
if (sk_X509_num(cert_sk)) { |
1134 |
/* Rename the database and the serial file */ |
||
1135 |
✓✗ | 20 |
if (!rotate_serial(serialfile, "new", "old")) |
1136 |
goto err; |
||
1137 |
|||
1138 |
✓✗ | 20 |
if (!rotate_index(dbfile, "new", "old")) |
1139 |
goto err; |
||
1140 |
|||
1141 |
20 |
BIO_printf(bio_err, "Data Base Updated\n"); |
|
1142 |
20 |
} |
|
1143 |
} |
||
1144 |
/*****************************************************************/ |
||
1145 |
✓✓ | 24 |
if (gencrl) { |
1146 |
int crl_v2 = 0; |
||
1147 |
✓✗ | 4 |
if (!crl_ext) { |
1148 |
4 |
crl_ext = NCONF_get_string(conf, section, ENV_CRLEXT); |
|
1149 |
✓✗ | 4 |
if (!crl_ext) |
1150 |
4 |
ERR_clear_error(); |
|
1151 |
} |
||
1152 |
✗✓ | 4 |
if (crl_ext) { |
1153 |
/* Check syntax of file */ |
||
1154 |
X509V3_CTX ctx; |
||
1155 |
X509V3_set_ctx_test(&ctx); |
||
1156 |
X509V3_set_nconf(&ctx, conf); |
||
1157 |
if (!X509V3_EXT_add_nconf(conf, &ctx, crl_ext, NULL)) { |
||
1158 |
BIO_printf(bio_err, |
||
1159 |
"Error Loading CRL extension section %s\n", |
||
1160 |
crl_ext); |
||
1161 |
ret = 1; |
||
1162 |
goto err; |
||
1163 |
} |
||
1164 |
} |
||
1165 |
✓✗ | 8 |
if ((crlnumberfile = NCONF_get_string(conf, section, |
1166 |
4 |
ENV_CRLNUMBER)) != NULL) |
|
1167 |
✗✓ | 8 |
if ((crlnumber = load_serial(crlnumberfile, 0, |
1168 |
4 |
NULL)) == NULL) { |
|
1169 |
BIO_printf(bio_err, |
||
1170 |
"error while loading CRL number\n"); |
||
1171 |
goto err; |
||
1172 |
} |
||
1173 |
✗✓ | 4 |
if (!crldays && !crlhours && !crlsec) { |
1174 |
if (!NCONF_get_number(conf, section, |
||
1175 |
ENV_DEFAULT_CRL_DAYS, &crldays)) |
||
1176 |
crldays = 0; |
||
1177 |
if (!NCONF_get_number(conf, section, |
||
1178 |
ENV_DEFAULT_CRL_HOURS, &crlhours)) |
||
1179 |
crlhours = 0; |
||
1180 |
ERR_clear_error(); |
||
1181 |
} |
||
1182 |
✗✓ | 4 |
if ((crldays == 0) && (crlhours == 0) && (crlsec == 0)) { |
1183 |
BIO_printf(bio_err, "cannot lookup how long until the next CRL is issued\n"); |
||
1184 |
goto err; |
||
1185 |
} |
||
1186 |
✗✓ | 4 |
if (verbose) |
1187 |
BIO_printf(bio_err, "making CRL\n"); |
||
1188 |
✗✓ | 4 |
if ((crl = X509_CRL_new()) == NULL) |
1189 |
goto err; |
||
1190 |
✗✓ | 4 |
if (!X509_CRL_set_issuer_name(crl, X509_get_subject_name(x509))) |
1191 |
goto err; |
||
1192 |
|||
1193 |
4 |
tmptm = ASN1_TIME_new(); |
|
1194 |
✗✓ | 4 |
if (!tmptm) |
1195 |
goto err; |
||
1196 |
4 |
X509_gmtime_adj(tmptm, 0); |
|
1197 |
4 |
X509_CRL_set_lastUpdate(crl, tmptm); |
|
1198 |
✗✓ | 8 |
if (!X509_time_adj_ex(tmptm, crldays, |
1199 |
4 |
crlhours * 60 * 60 + crlsec, NULL)) { |
|
1200 |
BIO_puts(bio_err, "error setting CRL nextUpdate\n"); |
||
1201 |
goto err; |
||
1202 |
} |
||
1203 |
4 |
X509_CRL_set_nextUpdate(crl, tmptm); |
|
1204 |
|||
1205 |
4 |
ASN1_TIME_free(tmptm); |
|
1206 |
|||
1207 |
✓✓ | 32 |
for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { |
1208 |
12 |
pp = sk_OPENSSL_PSTRING_value(db->db->data, i); |
|
1209 |
✗✓ | 12 |
if (pp[DB_type][0] == DB_TYPE_REV) { |
1210 |
if ((r = X509_REVOKED_new()) == NULL) |
||
1211 |
goto err; |
||
1212 |
j = make_revoked(r, pp[DB_rev_date]); |
||
1213 |
if (!j) |
||
1214 |
goto err; |
||
1215 |
if (j == 2) |
||
1216 |
crl_v2 = 1; |
||
1217 |
if (!BN_hex2bn(&serial, pp[DB_serial])) |
||
1218 |
goto err; |
||
1219 |
tmpser = BN_to_ASN1_INTEGER(serial, NULL); |
||
1220 |
BN_free(serial); |
||
1221 |
serial = NULL; |
||
1222 |
if (!tmpser) |
||
1223 |
goto err; |
||
1224 |
X509_REVOKED_set_serialNumber(r, tmpser); |
||
1225 |
ASN1_INTEGER_free(tmpser); |
||
1226 |
X509_CRL_add0_revoked(crl, r); |
||
1227 |
} |
||
1228 |
} |
||
1229 |
|||
1230 |
/* |
||
1231 |
* sort the data so it will be written in serial number order |
||
1232 |
*/ |
||
1233 |
4 |
X509_CRL_sort(crl); |
|
1234 |
|||
1235 |
/* we now have a CRL */ |
||
1236 |
✗✓ | 4 |
if (verbose) |
1237 |
BIO_printf(bio_err, "signing CRL\n"); |
||
1238 |
|||
1239 |
/* Add any extensions asked for */ |
||
1240 |
|||
1241 |
✓✗ | 4 |
if (crl_ext || crlnumberfile != NULL) { |
1242 |
4 |
X509V3_CTX crlctx; |
|
1243 |
4 |
X509V3_set_ctx(&crlctx, x509, NULL, NULL, crl, 0); |
|
1244 |
4 |
X509V3_set_nconf(&crlctx, conf); |
|
1245 |
|||
1246 |
✗✓ | 4 |
if (crl_ext) |
1247 |
if (!X509V3_EXT_CRL_add_nconf(conf, &crlctx, |
||
1248 |
crl_ext, crl)) |
||
1249 |
goto err; |
||
1250 |
✓✗ | 4 |
if (crlnumberfile != NULL) { |
1251 |
4 |
tmpser = BN_to_ASN1_INTEGER(crlnumber, NULL); |
|
1252 |
✗✓ | 4 |
if (!tmpser) |
1253 |
goto err; |
||
1254 |
4 |
X509_CRL_add1_ext_i2d(crl, NID_crl_number, |
|
1255 |
4 |
tmpser, 0, 0); |
|
1256 |
4 |
ASN1_INTEGER_free(tmpser); |
|
1257 |
crl_v2 = 1; |
||
1258 |
✗✓ | 4 |
if (!BN_add_word(crlnumber, 1)) |
1259 |
goto err; |
||
1260 |
} |
||
1261 |
✓✗ | 8 |
} |
1262 |
✓✗ | 4 |
if (crl_ext || crl_v2) { |
1263 |
✗✓ | 4 |
if (!X509_CRL_set_version(crl, 1)) |
1264 |
goto err; /* version 2 CRL */ |
||
1265 |
} |
||
1266 |
✓✗ | 4 |
if (crlnumberfile != NULL) /* we have a CRL number that |
1267 |
* need updating */ |
||
1268 |
✗✓ | 4 |
if (!save_serial(crlnumberfile, "new", crlnumber, NULL)) |
1269 |
goto err; |
||
1270 |
|||
1271 |
✓✗ | 4 |
if (crlnumber) { |
1272 |
4 |
BN_free(crlnumber); |
|
1273 |
crlnumber = NULL; |
||
1274 |
4 |
} |
|
1275 |
✗✓ | 4 |
if (!do_X509_CRL_sign(bio_err, crl, pkey, dgst, sigopts)) |
1276 |
goto err; |
||
1277 |
|||
1278 |
4 |
PEM_write_bio_X509_CRL(Sout, crl); |
|
1279 |
|||
1280 |
✓✗ | 4 |
if (crlnumberfile != NULL) /* Rename the crlnumber file */ |
1281 |
✗✓ | 4 |
if (!rotate_serial(crlnumberfile, "new", "old")) |
1282 |
goto err; |
||
1283 |
|||
1284 |
✓✗✗ | 4 |
} |
1285 |
/*****************************************************************/ |
||
1286 |
✓✓ | 24 |
if (dorevoke) { |
1287 |
✗✓ | 4 |
if (infile == NULL) { |
1288 |
BIO_printf(bio_err, "no input files\n"); |
||
1289 |
goto err; |
||
1290 |
} else { |
||
1291 |
X509 *revcert; |
||
1292 |
4 |
revcert = load_cert(bio_err, infile, FORMAT_PEM, |
|
1293 |
NULL, infile); |
||
1294 |
✗✓ | 4 |
if (revcert == NULL) |
1295 |
goto err; |
||
1296 |
4 |
j = do_revoke(revcert, db, rev_type, rev_arg); |
|
1297 |
✗✓ | 4 |
if (j <= 0) |
1298 |
goto err; |
||
1299 |
4 |
X509_free(revcert); |
|
1300 |
|||
1301 |
✗✓ | 4 |
if (!save_index(dbfile, "new", db)) |
1302 |
goto err; |
||
1303 |
|||
1304 |
✗✓ | 4 |
if (!rotate_index(dbfile, "new", "old")) |
1305 |
goto err; |
||
1306 |
|||
1307 |
4 |
BIO_printf(bio_err, "Data Base Updated\n"); |
|
1308 |
✓✓✓ | 4 |
} |
1309 |
} |
||
1310 |
/*****************************************************************/ |
||
1311 |
24 |
ret = 0; |
|
1312 |
|||
1313 |
err: |
||
1314 |
28 |
free(tofree); |
|
1315 |
|||
1316 |
28 |
BIO_free_all(Cout); |
|
1317 |
28 |
BIO_free_all(Sout); |
|
1318 |
28 |
BIO_free_all(out); |
|
1319 |
28 |
BIO_free_all(in); |
|
1320 |
|||
1321 |
✓✓ | 28 |
if (cert_sk) |
1322 |
20 |
sk_X509_pop_free(cert_sk, X509_free); |
|
1323 |
|||
1324 |
✓✓ | 28 |
if (ret) |
1325 |
4 |
ERR_print_errors(bio_err); |
|
1326 |
✓✓ | 28 |
if (free_key && key) |
1327 |
4 |
free(key); |
|
1328 |
28 |
BN_free(serial); |
|
1329 |
28 |
BN_free(crlnumber); |
|
1330 |
28 |
free_index(db); |
|
1331 |
✗✓ | 28 |
if (sigopts) |
1332 |
sk_OPENSSL_STRING_free(sigopts); |
||
1333 |
28 |
EVP_PKEY_free(pkey); |
|
1334 |
✓✓ | 28 |
if (x509) |
1335 |
24 |
X509_free(x509); |
|
1336 |
28 |
X509_CRL_free(crl); |
|
1337 |
28 |
NCONF_free(conf); |
|
1338 |
28 |
NCONF_free(extconf); |
|
1339 |
28 |
OBJ_cleanup(); |
|
1340 |
|||
1341 |
28 |
return (ret); |
|
1342 |
28 |
} |
|
1343 |
|||
1344 |
static void |
||
1345 |
lookup_fail(const char *name, const char *tag) |
||
1346 |
{ |
||
1347 |
BIO_printf(bio_err, "variable lookup failed for %s::%s\n", name, tag); |
||
1348 |
} |
||
1349 |
|||
1350 |
static int |
||
1351 |
certify(X509 ** xret, char *infile, EVP_PKEY * pkey, X509 * x509, |
||
1352 |
const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
1353 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
1354 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
1355 |
char *enddate, long days, int batch, char *ext_sect, CONF * lconf, |
||
1356 |
int verbose, unsigned long certopt, unsigned long nameopt, int default_op, |
||
1357 |
int ext_copy, int selfsign) |
||
1358 |
{ |
||
1359 |
X509_REQ *req = NULL; |
||
1360 |
BIO *in = NULL; |
||
1361 |
EVP_PKEY *pktmp = NULL; |
||
1362 |
int ok = -1, i; |
||
1363 |
|||
1364 |
32 |
in = BIO_new(BIO_s_file()); |
|
1365 |
|||
1366 |
✗✓ | 16 |
if (BIO_read_filename(in, infile) <= 0) { |
1367 |
perror(infile); |
||
1368 |
goto err; |
||
1369 |
} |
||
1370 |
✗✓ | 16 |
if ((req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL)) == NULL) { |
1371 |
BIO_printf(bio_err, "Error reading certificate request in %s\n", |
||
1372 |
infile); |
||
1373 |
goto err; |
||
1374 |
} |
||
1375 |
✗✓ | 16 |
if (verbose) |
1376 |
X509_REQ_print(bio_err, req); |
||
1377 |
|||
1378 |
16 |
BIO_printf(bio_err, "Check that the request matches the signature\n"); |
|
1379 |
|||
1380 |
✗✓✗✗ |
16 |
if (selfsign && !X509_REQ_check_private_key(req, pkey)) { |
1381 |
BIO_printf(bio_err, |
||
1382 |
"Certificate request and CA private key do not match\n"); |
||
1383 |
ok = 0; |
||
1384 |
goto err; |
||
1385 |
} |
||
1386 |
✗✓ | 16 |
if ((pktmp = X509_REQ_get_pubkey(req)) == NULL) { |
1387 |
BIO_printf(bio_err, "error unpacking public key\n"); |
||
1388 |
goto err; |
||
1389 |
} |
||
1390 |
16 |
i = X509_REQ_verify(req, pktmp); |
|
1391 |
16 |
EVP_PKEY_free(pktmp); |
|
1392 |
✗✓ | 16 |
if (i < 0) { |
1393 |
ok = 0; |
||
1394 |
BIO_printf(bio_err, "Signature verification problems....\n"); |
||
1395 |
goto err; |
||
1396 |
} |
||
1397 |
✗✓ | 16 |
if (i == 0) { |
1398 |
ok = 0; |
||
1399 |
BIO_printf(bio_err, |
||
1400 |
"Signature did not match the certificate request\n"); |
||
1401 |
goto err; |
||
1402 |
} else |
||
1403 |
16 |
BIO_printf(bio_err, "Signature ok\n"); |
|
1404 |
|||
1405 |
16 |
ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, |
|
1406 |
subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, |
||
1407 |
verbose, req, ext_sect, lconf, certopt, nameopt, default_op, |
||
1408 |
ext_copy, selfsign); |
||
1409 |
|||
1410 |
err: |
||
1411 |
✓✗ | 16 |
if (req != NULL) |
1412 |
16 |
X509_REQ_free(req); |
|
1413 |
✓✗ | 16 |
if (in != NULL) |
1414 |
16 |
BIO_free(in); |
|
1415 |
16 |
return (ok); |
|
1416 |
} |
||
1417 |
|||
1418 |
static int |
||
1419 |
certify_cert(X509 ** xret, char *infile, EVP_PKEY * pkey, X509 * x509, |
||
1420 |
const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
1421 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
1422 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
1423 |
char *enddate, long days, int batch, char *ext_sect, CONF * lconf, |
||
1424 |
int verbose, unsigned long certopt, unsigned long nameopt, int default_op, |
||
1425 |
int ext_copy) |
||
1426 |
{ |
||
1427 |
X509 *req = NULL; |
||
1428 |
X509_REQ *rreq = NULL; |
||
1429 |
EVP_PKEY *pktmp = NULL; |
||
1430 |
int ok = -1, i; |
||
1431 |
|||
1432 |
if ((req = load_cert(bio_err, infile, FORMAT_PEM, NULL, |
||
1433 |
infile)) == NULL) |
||
1434 |
goto err; |
||
1435 |
if (verbose) |
||
1436 |
X509_print(bio_err, req); |
||
1437 |
|||
1438 |
BIO_printf(bio_err, "Check that the request matches the signature\n"); |
||
1439 |
|||
1440 |
if ((pktmp = X509_get_pubkey(req)) == NULL) { |
||
1441 |
BIO_printf(bio_err, "error unpacking public key\n"); |
||
1442 |
goto err; |
||
1443 |
} |
||
1444 |
i = X509_verify(req, pktmp); |
||
1445 |
EVP_PKEY_free(pktmp); |
||
1446 |
if (i < 0) { |
||
1447 |
ok = 0; |
||
1448 |
BIO_printf(bio_err, "Signature verification problems....\n"); |
||
1449 |
goto err; |
||
1450 |
} |
||
1451 |
if (i == 0) { |
||
1452 |
ok = 0; |
||
1453 |
BIO_printf(bio_err, |
||
1454 |
"Signature did not match the certificate\n"); |
||
1455 |
goto err; |
||
1456 |
} else |
||
1457 |
BIO_printf(bio_err, "Signature ok\n"); |
||
1458 |
|||
1459 |
if ((rreq = X509_to_X509_REQ(req, NULL, EVP_md5())) == NULL) |
||
1460 |
goto err; |
||
1461 |
|||
1462 |
ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, |
||
1463 |
subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, |
||
1464 |
verbose, rreq, ext_sect, lconf, certopt, nameopt, default_op, |
||
1465 |
ext_copy, 0); |
||
1466 |
|||
1467 |
err: |
||
1468 |
if (rreq != NULL) |
||
1469 |
X509_REQ_free(rreq); |
||
1470 |
if (req != NULL) |
||
1471 |
X509_free(req); |
||
1472 |
return (ok); |
||
1473 |
} |
||
1474 |
|||
1475 |
static int |
||
1476 |
do_body(X509 ** xret, EVP_PKEY * pkey, X509 * x509, const EVP_MD * dgst, |
||
1477 |
STACK_OF(OPENSSL_STRING) * sigopts, STACK_OF(CONF_VALUE) * policy, |
||
1478 |
CA_DB * db, BIGNUM * serial, char *subj, unsigned long chtype, int multirdn, |
||
1479 |
int email_dn, char *startdate, char *enddate, long days, int batch, |
||
1480 |
int verbose, X509_REQ * req, char *ext_sect, CONF * lconf, |
||
1481 |
unsigned long certopt, unsigned long nameopt, int default_op, |
||
1482 |
int ext_copy, int selfsign) |
||
1483 |
{ |
||
1484 |
X509_NAME *name = NULL, *CAname = NULL, *subject = NULL, *dn_subject = NULL; |
||
1485 |
ASN1_UTCTIME *tm, *tmptm; |
||
1486 |
ASN1_STRING *str, *str2; |
||
1487 |
ASN1_OBJECT *obj; |
||
1488 |
X509 *ret = NULL; |
||
1489 |
X509_CINF *ci; |
||
1490 |
X509_NAME_ENTRY *ne; |
||
1491 |
X509_NAME_ENTRY *tne, *push; |
||
1492 |
EVP_PKEY *pktmp; |
||
1493 |
int ok = -1, i, j, last, nid; |
||
1494 |
const char *p; |
||
1495 |
CONF_VALUE *cv; |
||
1496 |
40 |
OPENSSL_STRING row[DB_NUMBER]; |
|
1497 |
OPENSSL_STRING *irow = NULL; |
||
1498 |
OPENSSL_STRING *rrow = NULL; |
||
1499 |
|||
1500 |
20 |
tmptm = ASN1_UTCTIME_new(); |
|
1501 |
✗✓ | 20 |
if (tmptm == NULL) { |
1502 |
BIO_printf(bio_err, "malloc error\n"); |
||
1503 |
return (0); |
||
1504 |
} |
||
1505 |
✓✓ | 280 |
for (i = 0; i < DB_NUMBER; i++) |
1506 |
120 |
row[i] = NULL; |
|
1507 |
|||
1508 |
✗✓ | 20 |
if (subj) { |
1509 |
X509_NAME *n = parse_name(subj, chtype, multirdn); |
||
1510 |
|||
1511 |
if (!n) { |
||
1512 |
ERR_print_errors(bio_err); |
||
1513 |
goto err; |
||
1514 |
} |
||
1515 |
X509_REQ_set_subject_name(req, n); |
||
1516 |
req->req_info->enc.modified = 1; |
||
1517 |
X509_NAME_free(n); |
||
1518 |
} |
||
1519 |
✓✗ | 20 |
if (default_op) |
1520 |
20 |
BIO_printf(bio_err, |
|
1521 |
"The Subject's Distinguished Name is as follows\n"); |
||
1522 |
|||
1523 |
20 |
name = X509_REQ_get_subject_name(req); |
|
1524 |
✓✓ | 200 |
for (i = 0; i < X509_NAME_entry_count(name); i++) { |
1525 |
80 |
ne = X509_NAME_get_entry(name, i); |
|
1526 |
80 |
str = X509_NAME_ENTRY_get_data(ne); |
|
1527 |
80 |
obj = X509_NAME_ENTRY_get_object(ne); |
|
1528 |
|||
1529 |
✗✓ | 80 |
if (msie_hack) { |
1530 |
/* assume all type should be strings */ |
||
1531 |
nid = OBJ_obj2nid(ne->object); |
||
1532 |
|||
1533 |
if (str->type == V_ASN1_UNIVERSALSTRING) |
||
1534 |
ASN1_UNIVERSALSTRING_to_string(str); |
||
1535 |
|||
1536 |
if ((str->type == V_ASN1_IA5STRING) && |
||
1537 |
(nid != NID_pkcs9_emailAddress)) |
||
1538 |
str->type = V_ASN1_T61STRING; |
||
1539 |
|||
1540 |
if ((nid == NID_pkcs9_emailAddress) && |
||
1541 |
(str->type == V_ASN1_PRINTABLESTRING)) |
||
1542 |
str->type = V_ASN1_IA5STRING; |
||
1543 |
} |
||
1544 |
/* If no EMAIL is wanted in the subject */ |
||
1545 |
✓✗ | 80 |
if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && (!email_dn)) |
1546 |
continue; |
||
1547 |
|||
1548 |
/* check some things */ |
||
1549 |
✗✓✗✗ |
80 |
if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && |
1550 |
(str->type != V_ASN1_IA5STRING)) { |
||
1551 |
BIO_printf(bio_err, "\nemailAddress type needs to be of type IA5STRING\n"); |
||
1552 |
goto err; |
||
1553 |
} |
||
1554 |
✓✗✓✓ |
160 |
if ((str->type != V_ASN1_BMPSTRING) && |
1555 |
80 |
(str->type != V_ASN1_UTF8STRING)) { |
|
1556 |
20 |
j = ASN1_PRINTABLE_type(str->data, str->length); |
|
1557 |
✗✓✗✗ |
20 |
if (((j == V_ASN1_T61STRING) && |
1558 |
(str->type != V_ASN1_T61STRING)) || |
||
1559 |
✗✓ | 20 |
((j == V_ASN1_IA5STRING) && |
1560 |
(str->type == V_ASN1_PRINTABLESTRING))) { |
||
1561 |
BIO_printf(bio_err, "\nThe string contains characters that are illegal for the ASN.1 type\n"); |
||
1562 |
goto err; |
||
1563 |
} |
||
1564 |
} |
||
1565 |
✓✗ | 80 |
if (default_op) |
1566 |
80 |
old_entry_print(bio_err, obj, str); |
|
1567 |
} |
||
1568 |
|||
1569 |
/* Ok, now we check the 'policy' stuff. */ |
||
1570 |
✗✓ | 20 |
if ((subject = X509_NAME_new()) == NULL) { |
1571 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
1572 |
goto err; |
||
1573 |
} |
||
1574 |
/* take a copy of the issuer name before we mess with it. */ |
||
1575 |
✗✓ | 20 |
if (selfsign) |
1576 |
CAname = X509_NAME_dup(name); |
||
1577 |
else |
||
1578 |
20 |
CAname = X509_NAME_dup(x509->cert_info->subject); |
|
1579 |
✓✗ | 20 |
if (CAname == NULL) |
1580 |
goto err; |
||
1581 |
str = str2 = NULL; |
||
1582 |
|||
1583 |
✓✓ | 280 |
for (i = 0; i < sk_CONF_VALUE_num(policy); i++) { |
1584 |
120 |
cv = sk_CONF_VALUE_value(policy, i); /* get the object id */ |
|
1585 |
✗✓ | 120 |
if ((j = OBJ_txt2nid(cv->name)) == NID_undef) { |
1586 |
BIO_printf(bio_err, "%s:unknown object type in 'policy' configuration\n", cv->name); |
||
1587 |
goto err; |
||
1588 |
} |
||
1589 |
120 |
obj = OBJ_nid2obj(j); |
|
1590 |
|||
1591 |
last = -1; |
||
1592 |
200 |
for (;;) { |
|
1593 |
/* lookup the object in the supplied name list */ |
||
1594 |
200 |
j = X509_NAME_get_index_by_OBJ(name, obj, last); |
|
1595 |
✓✓ | 200 |
if (j < 0) { |
1596 |
✓✓ | 120 |
if (last != -1) |
1597 |
break; |
||
1598 |
tne = NULL; |
||
1599 |
40 |
} else { |
|
1600 |
80 |
tne = X509_NAME_get_entry(name, j); |
|
1601 |
} |
||
1602 |
last = j; |
||
1603 |
|||
1604 |
/* depending on the 'policy', decide what to do. */ |
||
1605 |
push = NULL; |
||
1606 |
✓✓ | 120 |
if (strcmp(cv->value, "optional") == 0) { |
1607 |
✗✓ | 40 |
if (tne != NULL) |
1608 |
push = tne; |
||
1609 |
✓✓ | 80 |
} else if (strcmp(cv->value, "supplied") == 0) { |
1610 |
✗✓ | 20 |
if (tne == NULL) { |
1611 |
BIO_printf(bio_err, "The %s field needed to be supplied and was missing\n", cv->name); |
||
1612 |
goto err; |
||
1613 |
} else |
||
1614 |
push = tne; |
||
1615 |
✓✗ | 80 |
} else if (strcmp(cv->value, "match") == 0) { |
1616 |
int last2; |
||
1617 |
|||
1618 |
✗✓ | 60 |
if (tne == NULL) { |
1619 |
BIO_printf(bio_err, "The mandatory %s field was missing\n", cv->name); |
||
1620 |
goto err; |
||
1621 |
} |
||
1622 |
60 |
last2 = -1; |
|
1623 |
|||
1624 |
again2: |
||
1625 |
60 |
j = X509_NAME_get_index_by_OBJ(CAname, obj, last2); |
|
1626 |
✗✓ | 60 |
if ((j < 0) && (last2 == -1)) { |
1627 |
BIO_printf(bio_err, "The %s field does not exist in the CA certificate,\nthe 'policy' is misconfigured\n", cv->name); |
||
1628 |
goto err; |
||
1629 |
} |
||
1630 |
✓✗ | 60 |
if (j >= 0) { |
1631 |
60 |
push = X509_NAME_get_entry(CAname, j); |
|
1632 |
60 |
str = X509_NAME_ENTRY_get_data(tne); |
|
1633 |
60 |
str2 = X509_NAME_ENTRY_get_data(push); |
|
1634 |
last2 = j; |
||
1635 |
✗✓ | 60 |
if (ASN1_STRING_cmp(str, str2) != 0) |
1636 |
goto again2; |
||
1637 |
} |
||
1638 |
✗✓ | 60 |
if (j < 0) { |
1639 |
BIO_printf(bio_err, "The %s field needed to be the same in the\nCA certificate (%s) and the request (%s)\n", cv->name, ((str2 == NULL) ? "NULL" : (char *) str2->data), ((str == NULL) ? "NULL" : (char *) str->data)); |
||
1640 |
goto err; |
||
1641 |
} |
||
1642 |
✓✓✓ | 60 |
} else { |
1643 |
BIO_printf(bio_err, "%s:invalid type in 'policy' configuration\n", cv->value); |
||
1644 |
goto err; |
||
1645 |
} |
||
1646 |
|||
1647 |
✓✓ | 120 |
if (push != NULL) { |
1648 |
✗✓ | 80 |
if (!X509_NAME_add_entry(subject, push, |
1649 |
-1, 0)) { |
||
1650 |
if (push != NULL) |
||
1651 |
X509_NAME_ENTRY_free(push); |
||
1652 |
BIO_printf(bio_err, |
||
1653 |
"Memory allocation failure\n"); |
||
1654 |
goto err; |
||
1655 |
} |
||
1656 |
} |
||
1657 |
✓✓ | 120 |
if (j < 0) |
1658 |
break; |
||
1659 |
} |
||
1660 |
} |
||
1661 |
|||
1662 |
✗✓ | 20 |
if (preserve) { |
1663 |
X509_NAME_free(subject); |
||
1664 |
/* subject=X509_NAME_dup(X509_REQ_get_subject_name(req)); */ |
||
1665 |
subject = X509_NAME_dup(name); |
||
1666 |
if (subject == NULL) |
||
1667 |
goto err; |
||
1668 |
} |
||
1669 |
✗✓ | 20 |
if (verbose) |
1670 |
BIO_printf(bio_err, "The subject name appears to be ok, checking data base for clashes\n"); |
||
1671 |
|||
1672 |
/* Build the correct Subject if no email is wanted in the subject */ |
||
1673 |
/* |
||
1674 |
* and add it later on because of the method extensions are added |
||
1675 |
* (altName) |
||
1676 |
*/ |
||
1677 |
|||
1678 |
✓✗ | 20 |
if (email_dn) |
1679 |
20 |
dn_subject = subject; |
|
1680 |
else { |
||
1681 |
X509_NAME_ENTRY *tmpne; |
||
1682 |
/* |
||
1683 |
* Its best to dup the subject DN and then delete any email |
||
1684 |
* addresses because this retains its structure. |
||
1685 |
*/ |
||
1686 |
if (!(dn_subject = X509_NAME_dup(subject))) { |
||
1687 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
1688 |
goto err; |
||
1689 |
} |
||
1690 |
while ((i = X509_NAME_get_index_by_NID(dn_subject, |
||
1691 |
NID_pkcs9_emailAddress, -1)) >= 0) { |
||
1692 |
tmpne = X509_NAME_get_entry(dn_subject, i); |
||
1693 |
X509_NAME_delete_entry(dn_subject, i); |
||
1694 |
X509_NAME_ENTRY_free(tmpne); |
||
1695 |
} |
||
1696 |
} |
||
1697 |
|||
1698 |
✗✓ | 20 |
if (BN_is_zero(serial)) |
1699 |
row[DB_serial] = strdup("00"); |
||
1700 |
else |
||
1701 |
20 |
row[DB_serial] = BN_bn2hex(serial); |
|
1702 |
✗✓ | 20 |
if (row[DB_serial] == NULL) { |
1703 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
1704 |
goto err; |
||
1705 |
} |
||
1706 |
✓✗ | 20 |
if (db->attributes.unique_subject) { |
1707 |
20 |
OPENSSL_STRING *crow = row; |
|
1708 |
|||
1709 |
20 |
rrow = TXT_DB_get_by_index(db->db, DB_name, crow); |
|
1710 |
✗✓ | 20 |
if (rrow != NULL) { |
1711 |
BIO_printf(bio_err, |
||
1712 |
"ERROR:There is already a certificate for %s\n", |
||
1713 |
row[DB_name]); |
||
1714 |
} |
||
1715 |
20 |
} |
|
1716 |
✓✗ | 20 |
if (rrow == NULL) { |
1717 |
20 |
rrow = TXT_DB_get_by_index(db->db, DB_serial, row); |
|
1718 |
✗✓ | 20 |
if (rrow != NULL) { |
1719 |
BIO_printf(bio_err, |
||
1720 |
"ERROR:Serial number %s has already been issued,\n", |
||
1721 |
row[DB_serial]); |
||
1722 |
BIO_printf(bio_err, " check the database/serial_file for corruption\n"); |
||
1723 |
} |
||
1724 |
} |
||
1725 |
✗✓ | 20 |
if (rrow != NULL) { |
1726 |
BIO_printf(bio_err, |
||
1727 |
"The matching entry has the following details\n"); |
||
1728 |
if (rrow[DB_type][0] == 'E') |
||
1729 |
p = "Expired"; |
||
1730 |
else if (rrow[DB_type][0] == 'R') |
||
1731 |
p = "Revoked"; |
||
1732 |
else if (rrow[DB_type][0] == 'V') |
||
1733 |
p = "Valid"; |
||
1734 |
else |
||
1735 |
p = "\ninvalid type, Data base error\n"; |
||
1736 |
BIO_printf(bio_err, "Type :%s\n", p); |
||
1737 |
if (rrow[DB_type][0] == 'R') { |
||
1738 |
p = rrow[DB_exp_date]; |
||
1739 |
if (p == NULL) |
||
1740 |
p = "undef"; |
||
1741 |
BIO_printf(bio_err, "Was revoked on:%s\n", p); |
||
1742 |
} |
||
1743 |
p = rrow[DB_exp_date]; |
||
1744 |
if (p == NULL) |
||
1745 |
p = "undef"; |
||
1746 |
BIO_printf(bio_err, "Expires on :%s\n", p); |
||
1747 |
p = rrow[DB_serial]; |
||
1748 |
if (p == NULL) |
||
1749 |
p = "undef"; |
||
1750 |
BIO_printf(bio_err, "Serial Number :%s\n", p); |
||
1751 |
p = rrow[DB_file]; |
||
1752 |
if (p == NULL) |
||
1753 |
p = "undef"; |
||
1754 |
BIO_printf(bio_err, "File name :%s\n", p); |
||
1755 |
p = rrow[DB_name]; |
||
1756 |
if (p == NULL) |
||
1757 |
p = "undef"; |
||
1758 |
BIO_printf(bio_err, "Subject Name :%s\n", p); |
||
1759 |
ok = -1; /* This is now a 'bad' error. */ |
||
1760 |
goto err; |
||
1761 |
} |
||
1762 |
/* We are now totally happy, lets make and sign the certificate */ |
||
1763 |
✗✓ | 20 |
if (verbose) |
1764 |
BIO_printf(bio_err, "Everything appears to be ok, creating and signing the certificate\n"); |
||
1765 |
|||
1766 |
✓✗ | 20 |
if ((ret = X509_new()) == NULL) |
1767 |
goto err; |
||
1768 |
20 |
ci = ret->cert_info; |
|
1769 |
|||
1770 |
#ifdef X509_V3 |
||
1771 |
/* Make it an X509 v3 certificate. */ |
||
1772 |
if (!X509_set_version(ret, 2)) |
||
1773 |
goto err; |
||
1774 |
#endif |
||
1775 |
✓✗ | 20 |
if (ci->serialNumber == NULL) |
1776 |
goto err; |
||
1777 |
✓✗ | 20 |
if (BN_to_ASN1_INTEGER(serial, ci->serialNumber) == NULL) |
1778 |
goto err; |
||
1779 |
✗✓ | 20 |
if (selfsign) { |
1780 |
if (!X509_set_issuer_name(ret, subject)) |
||
1781 |
goto err; |
||
1782 |
} else { |
||
1783 |
✓✗ | 20 |
if (!X509_set_issuer_name(ret, X509_get_subject_name(x509))) |
1784 |
goto err; |
||
1785 |
} |
||
1786 |
|||
1787 |
✓✗ | 20 |
if (strcmp(startdate, "today") == 0) |
1788 |
20 |
X509_gmtime_adj(X509_get_notBefore(ret), 0); |
|
1789 |
else if (setCertificateTime(X509_get_notBefore(ret), startdate) == -1) { |
||
1790 |
BIO_printf(bio_err, "Invalid start date %s\n", |
||
1791 |
startdate); |
||
1792 |
goto err; |
||
1793 |
} |
||
1794 |
|||
1795 |
✓✗ | 20 |
if (enddate == NULL) |
1796 |
20 |
X509_time_adj_ex(X509_get_notAfter(ret), days, 0, NULL); |
|
1797 |
else if (setCertificateTime(X509_get_notAfter(ret), enddate) == -1) { |
||
1798 |
BIO_printf(bio_err, "Invalid end date %s\n", |
||
1799 |
enddate); |
||
1800 |
goto err; |
||
1801 |
} |
||
1802 |
|||
1803 |
✓✗ | 20 |
if (!X509_set_subject_name(ret, subject)) |
1804 |
goto err; |
||
1805 |
|||
1806 |
20 |
pktmp = X509_REQ_get_pubkey(req); |
|
1807 |
20 |
i = X509_set_pubkey(ret, pktmp); |
|
1808 |
20 |
EVP_PKEY_free(pktmp); |
|
1809 |
✓✗ | 20 |
if (!i) |
1810 |
goto err; |
||
1811 |
|||
1812 |
/* Lets add the extensions, if there are any */ |
||
1813 |
✓✓ | 20 |
if (ext_sect) { |
1814 |
8 |
X509V3_CTX ctx; |
|
1815 |
✓✗ | 8 |
if (ci->version == NULL) |
1816 |
✗✓ | 8 |
if ((ci->version = ASN1_INTEGER_new()) == NULL) |
1817 |
goto err; |
||
1818 |
8 |
ASN1_INTEGER_set(ci->version, 2); /* version 3 certificate */ |
|
1819 |
|||
1820 |
/* |
||
1821 |
* Free the current entries if any, there should not be any I |
||
1822 |
* believe |
||
1823 |
*/ |
||
1824 |
✗✓ | 8 |
if (ci->extensions != NULL) |
1825 |
sk_X509_EXTENSION_pop_free(ci->extensions, |
||
1826 |
X509_EXTENSION_free); |
||
1827 |
|||
1828 |
8 |
ci->extensions = NULL; |
|
1829 |
|||
1830 |
/* Initialize the context structure */ |
||
1831 |
✗✓ | 8 |
if (selfsign) |
1832 |
X509V3_set_ctx(&ctx, ret, ret, req, NULL, 0); |
||
1833 |
else |
||
1834 |
8 |
X509V3_set_ctx(&ctx, x509, ret, req, NULL, 0); |
|
1835 |
|||
1836 |
✗✓ | 8 |
if (extconf) { |
1837 |
if (verbose) |
||
1838 |
BIO_printf(bio_err, |
||
1839 |
"Extra configuration file found\n"); |
||
1840 |
|||
1841 |
/* Use the extconf configuration db LHASH */ |
||
1842 |
X509V3_set_nconf(&ctx, extconf); |
||
1843 |
|||
1844 |
/* Test the structure (needed?) */ |
||
1845 |
/* X509V3_set_ctx_test(&ctx); */ |
||
1846 |
|||
1847 |
/* Adds exts contained in the configuration file */ |
||
1848 |
if (!X509V3_EXT_add_nconf(extconf, &ctx, |
||
1849 |
ext_sect, ret)) { |
||
1850 |
BIO_printf(bio_err, |
||
1851 |
"ERROR: adding extensions in section %s\n", |
||
1852 |
ext_sect); |
||
1853 |
ERR_print_errors(bio_err); |
||
1854 |
goto err; |
||
1855 |
} |
||
1856 |
if (verbose) |
||
1857 |
BIO_printf(bio_err, "Successfully added extensions from file.\n"); |
||
1858 |
✓✗ | 8 |
} else if (ext_sect) { |
1859 |
/* We found extensions to be set from config file */ |
||
1860 |
8 |
X509V3_set_nconf(&ctx, lconf); |
|
1861 |
|||
1862 |
✗✓ | 8 |
if (!X509V3_EXT_add_nconf(lconf, &ctx, ext_sect, ret)) { |
1863 |
BIO_printf(bio_err, |
||
1864 |
"ERROR: adding extensions in section %s\n", |
||
1865 |
ext_sect); |
||
1866 |
ERR_print_errors(bio_err); |
||
1867 |
goto err; |
||
1868 |
} |
||
1869 |
✗✓ | 8 |
if (verbose) |
1870 |
BIO_printf(bio_err, "Successfully added extensions from config\n"); |
||
1871 |
} |
||
1872 |
✓✓✓ | 32 |
} |
1873 |
/* Copy extensions from request (if any) */ |
||
1874 |
|||
1875 |
✗✓ | 20 |
if (!copy_extensions(ret, req, ext_copy)) { |
1876 |
BIO_printf(bio_err, "ERROR: adding extensions from request\n"); |
||
1877 |
ERR_print_errors(bio_err); |
||
1878 |
goto err; |
||
1879 |
} |
||
1880 |
/* Set the right value for the noemailDN option */ |
||
1881 |
✗✓ | 20 |
if (email_dn == 0) { |
1882 |
if (!X509_set_subject_name(ret, dn_subject)) |
||
1883 |
goto err; |
||
1884 |
} |
||
1885 |
✗✓ | 20 |
if (!default_op) { |
1886 |
BIO_printf(bio_err, "Certificate Details:\n"); |
||
1887 |
/* |
||
1888 |
* Never print signature details because signature not |
||
1889 |
* present |
||
1890 |
*/ |
||
1891 |
certopt |= X509_FLAG_NO_SIGDUMP | X509_FLAG_NO_SIGNAME; |
||
1892 |
X509_print_ex(bio_err, ret, nameopt, certopt); |
||
1893 |
} |
||
1894 |
20 |
BIO_printf(bio_err, "Certificate is to be certified until "); |
|
1895 |
20 |
ASN1_TIME_print(bio_err, X509_get_notAfter(ret)); |
|
1896 |
✓✗ | 20 |
if (days) |
1897 |
20 |
BIO_printf(bio_err, " (%ld days)", days); |
|
1898 |
20 |
BIO_printf(bio_err, "\n"); |
|
1899 |
|||
1900 |
✗✓ | 20 |
if (!batch) { |
1901 |
char answer[25]; |
||
1902 |
|||
1903 |
BIO_printf(bio_err, "Sign the certificate? [y/n]:"); |
||
1904 |
(void) BIO_flush(bio_err); |
||
1905 |
if (!fgets(answer, sizeof(answer) - 1, stdin)) { |
||
1906 |
BIO_printf(bio_err, |
||
1907 |
"CERTIFICATE WILL NOT BE CERTIFIED: I/O error\n"); |
||
1908 |
ok = 0; |
||
1909 |
goto err; |
||
1910 |
} |
||
1911 |
if (!((answer[0] == 'y') || (answer[0] == 'Y'))) { |
||
1912 |
BIO_printf(bio_err, |
||
1913 |
"CERTIFICATE WILL NOT BE CERTIFIED\n"); |
||
1914 |
ok = 0; |
||
1915 |
goto err; |
||
1916 |
} |
||
1917 |
} |
||
1918 |
20 |
pktmp = X509_get_pubkey(ret); |
|
1919 |
✗✓✗✗ |
20 |
if (EVP_PKEY_missing_parameters(pktmp) && |
1920 |
!EVP_PKEY_missing_parameters(pkey)) |
||
1921 |
EVP_PKEY_copy_parameters(pktmp, pkey); |
||
1922 |
20 |
EVP_PKEY_free(pktmp); |
|
1923 |
|||
1924 |
✓✗ | 20 |
if (!do_X509_sign(bio_err, ret, pkey, dgst, sigopts)) |
1925 |
goto err; |
||
1926 |
|||
1927 |
/* We now just add it to the database */ |
||
1928 |
20 |
row[DB_type] = malloc(2); |
|
1929 |
|||
1930 |
20 |
tm = X509_get_notAfter(ret); |
|
1931 |
20 |
row[DB_exp_date] = malloc(tm->length + 1); |
|
1932 |
✓✗✗✓ |
40 |
if (row[DB_type] == NULL || row[DB_exp_date] == NULL) { |
1933 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
1934 |
goto err; |
||
1935 |
} |
||
1936 |
|||
1937 |
20 |
memcpy(row[DB_exp_date], tm->data, tm->length); |
|
1938 |
20 |
row[DB_exp_date][tm->length] = '\0'; |
|
1939 |
|||
1940 |
20 |
row[DB_rev_date] = NULL; |
|
1941 |
|||
1942 |
/* row[DB_serial] done already */ |
||
1943 |
20 |
row[DB_file] = malloc(8); |
|
1944 |
20 |
row[DB_name] = X509_NAME_oneline(X509_get_subject_name(ret), NULL, 0); |
|
1945 |
|||
1946 |
✓✗✓✗ ✗✓ |
60 |
if ((row[DB_type] == NULL) || (row[DB_file] == NULL) || |
1947 |
20 |
(row[DB_name] == NULL)) { |
|
1948 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
1949 |
goto err; |
||
1950 |
} |
||
1951 |
20 |
(void) strlcpy(row[DB_file], "unknown", 8); |
|
1952 |
20 |
row[DB_type][0] = 'V'; |
|
1953 |
20 |
row[DB_type][1] = '\0'; |
|
1954 |
|||
1955 |
✗✓ | 20 |
if ((irow = reallocarray(NULL, DB_NUMBER + 1, sizeof(char *))) == |
1956 |
NULL) { |
||
1957 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
1958 |
goto err; |
||
1959 |
} |
||
1960 |
✓✓ | 280 |
for (i = 0; i < DB_NUMBER; i++) { |
1961 |
120 |
irow[i] = row[i]; |
|
1962 |
120 |
row[i] = NULL; |
|
1963 |
} |
||
1964 |
20 |
irow[DB_NUMBER] = NULL; |
|
1965 |
|||
1966 |
✗✓ | 20 |
if (!TXT_DB_insert(db->db, irow)) { |
1967 |
BIO_printf(bio_err, "failed to update database\n"); |
||
1968 |
BIO_printf(bio_err, "TXT_DB error number %ld\n", db->db->error); |
||
1969 |
goto err; |
||
1970 |
} |
||
1971 |
20 |
ok = 1; |
|
1972 |
err: |
||
1973 |
✓✓ | 280 |
for (i = 0; i < DB_NUMBER; i++) |
1974 |
120 |
free(row[i]); |
|
1975 |
|||
1976 |
✓✗ | 20 |
if (CAname != NULL) |
1977 |
20 |
X509_NAME_free(CAname); |
|
1978 |
✓✗ | 20 |
if (subject != NULL) |
1979 |
20 |
X509_NAME_free(subject); |
|
1980 |
✗✓ | 20 |
if ((dn_subject != NULL) && !email_dn) |
1981 |
X509_NAME_free(dn_subject); |
||
1982 |
✓✗ | 20 |
if (tmptm != NULL) |
1983 |
20 |
ASN1_UTCTIME_free(tmptm); |
|
1984 |
✗✓ | 20 |
if (ok <= 0) { |
1985 |
if (ret != NULL) |
||
1986 |
X509_free(ret); |
||
1987 |
ret = NULL; |
||
1988 |
} else |
||
1989 |
20 |
*xret = ret; |
|
1990 |
20 |
return (ok); |
|
1991 |
20 |
} |
|
1992 |
|||
1993 |
static void |
||
1994 |
write_new_certificate(BIO * bp, X509 * x, int output_der, int notext) |
||
1995 |
{ |
||
1996 |
✓✓ | 80 |
if (output_der) { |
1997 |
4 |
(void) i2d_X509_bio(bp, x); |
|
1998 |
4 |
return; |
|
1999 |
} |
||
2000 |
✓✗ | 36 |
if (!notext) |
2001 |
36 |
X509_print(bp, x); |
|
2002 |
36 |
PEM_write_bio_X509(bp, x); |
|
2003 |
76 |
} |
|
2004 |
|||
2005 |
static int |
||
2006 |
certify_spkac(X509 ** xret, char *infile, EVP_PKEY * pkey, X509 * x509, |
||
2007 |
const EVP_MD * dgst, STACK_OF(OPENSSL_STRING) * sigopts, |
||
2008 |
STACK_OF(CONF_VALUE) * policy, CA_DB * db, BIGNUM * serial, char *subj, |
||
2009 |
unsigned long chtype, int multirdn, int email_dn, char *startdate, |
||
2010 |
char *enddate, long days, char *ext_sect, CONF * lconf, int verbose, |
||
2011 |
unsigned long certopt, unsigned long nameopt, int default_op, int ext_copy) |
||
2012 |
{ |
||
2013 |
STACK_OF(CONF_VALUE) * sk = NULL; |
||
2014 |
LHASH_OF(CONF_VALUE) * parms = NULL; |
||
2015 |
X509_REQ *req = NULL; |
||
2016 |
CONF_VALUE *cv = NULL; |
||
2017 |
NETSCAPE_SPKI *spki = NULL; |
||
2018 |
X509_REQ_INFO *ri; |
||
2019 |
char *type, *buf; |
||
2020 |
EVP_PKEY *pktmp = NULL; |
||
2021 |
X509_NAME *n = NULL; |
||
2022 |
int ok = -1, i, j; |
||
2023 |
8 |
long errline; |
|
2024 |
int nid; |
||
2025 |
|||
2026 |
/* |
||
2027 |
* Load input file into a hash table. (This is just an easy |
||
2028 |
* way to read and parse the file, then put it into a convenient |
||
2029 |
* STACK format). |
||
2030 |
*/ |
||
2031 |
4 |
parms = CONF_load(NULL, infile, &errline); |
|
2032 |
✗✓ | 4 |
if (parms == NULL) { |
2033 |
BIO_printf(bio_err, "error on line %ld of %s\n", |
||
2034 |
errline, infile); |
||
2035 |
ERR_print_errors(bio_err); |
||
2036 |
goto err; |
||
2037 |
} |
||
2038 |
4 |
sk = CONF_get_section(parms, "default"); |
|
2039 |
✗✓ | 4 |
if (sk_CONF_VALUE_num(sk) == 0) { |
2040 |
BIO_printf(bio_err, "no name/value pairs found in %s\n", |
||
2041 |
infile); |
||
2042 |
CONF_free(parms); |
||
2043 |
goto err; |
||
2044 |
} |
||
2045 |
/* |
||
2046 |
* Now create a dummy X509 request structure. We don't actually |
||
2047 |
* have an X509 request, but we have many of the components |
||
2048 |
* (a public key, various DN components). The idea is that we |
||
2049 |
* put these components into the right X509 request structure |
||
2050 |
* and we can use the same code as if you had a real X509 request. |
||
2051 |
*/ |
||
2052 |
4 |
req = X509_REQ_new(); |
|
2053 |
✗✓ | 4 |
if (req == NULL) { |
2054 |
ERR_print_errors(bio_err); |
||
2055 |
goto err; |
||
2056 |
} |
||
2057 |
/* |
||
2058 |
* Build up the subject name set. |
||
2059 |
*/ |
||
2060 |
4 |
ri = req->req_info; |
|
2061 |
4 |
n = ri->subject; |
|
2062 |
|||
2063 |
24 |
for (i = 0;; i++) { |
|
2064 |
✓✓ | 24 |
if (sk_CONF_VALUE_num(sk) <= i) |
2065 |
break; |
||
2066 |
|||
2067 |
20 |
cv = sk_CONF_VALUE_value(sk, i); |
|
2068 |
20 |
type = cv->name; |
|
2069 |
/* |
||
2070 |
* Skip past any leading X. X: X, etc to allow for multiple |
||
2071 |
* instances |
||
2072 |
*/ |
||
2073 |
✓✓ | 528 |
for (buf = cv->name; *buf; buf++) { |
2074 |
✓✗✓✗ ✗✓ |
732 |
if ((*buf == ':') || (*buf == ',') || (*buf == '.')) { |
2075 |
buf++; |
||
2076 |
if (*buf) |
||
2077 |
type = buf; |
||
2078 |
break; |
||
2079 |
} |
||
2080 |
} |
||
2081 |
|||
2082 |
20 |
buf = cv->value; |
|
2083 |
✓✓ | 20 |
if ((nid = OBJ_txt2nid(type)) == NID_undef) { |
2084 |
✓✗ | 4 |
if (strcmp(type, "SPKAC") == 0) { |
2085 |
4 |
spki = NETSCAPE_SPKI_b64_decode(cv->value, -1); |
|
2086 |
✗✓ | 4 |
if (spki == NULL) { |
2087 |
BIO_printf(bio_err, "unable to load Netscape SPKAC structure\n"); |
||
2088 |
ERR_print_errors(bio_err); |
||
2089 |
goto err; |
||
2090 |
} |
||
2091 |
} |
||
2092 |
continue; |
||
2093 |
} |
||
2094 |
✓✗ | 16 |
if (!X509_NAME_add_entry_by_NID(n, nid, chtype, |
2095 |
(unsigned char *)buf, -1, -1, 0)) |
||
2096 |
goto err; |
||
2097 |
} |
||
2098 |
✗✓ | 4 |
if (spki == NULL) { |
2099 |
BIO_printf(bio_err, |
||
2100 |
"Netscape SPKAC structure not found in %s\n", infile); |
||
2101 |
goto err; |
||
2102 |
} |
||
2103 |
/* |
||
2104 |
* Now extract the key from the SPKI structure. |
||
2105 |
*/ |
||
2106 |
|||
2107 |
4 |
BIO_printf(bio_err, |
|
2108 |
"Check that the SPKAC request matches the signature\n"); |
||
2109 |
|||
2110 |
✗✓ | 4 |
if ((pktmp = NETSCAPE_SPKI_get_pubkey(spki)) == NULL) { |
2111 |
BIO_printf(bio_err, "error unpacking SPKAC public key\n"); |
||
2112 |
goto err; |
||
2113 |
} |
||
2114 |
4 |
j = NETSCAPE_SPKI_verify(spki, pktmp); |
|
2115 |
✗✓ | 4 |
if (j <= 0) { |
2116 |
BIO_printf(bio_err, |
||
2117 |
"signature verification failed on SPKAC public key\n"); |
||
2118 |
goto err; |
||
2119 |
} |
||
2120 |
4 |
BIO_printf(bio_err, "Signature ok\n"); |
|
2121 |
|||
2122 |
4 |
X509_REQ_set_pubkey(req, pktmp); |
|
2123 |
4 |
EVP_PKEY_free(pktmp); |
|
2124 |
4 |
ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, |
|
2125 |
subj, chtype, multirdn, email_dn, startdate, enddate, days, 1, |
||
2126 |
verbose, req, ext_sect, lconf, certopt, nameopt, default_op, |
||
2127 |
ext_copy, 0); |
||
2128 |
|||
2129 |
err: |
||
2130 |
✓✗ | 4 |
if (req != NULL) |
2131 |
4 |
X509_REQ_free(req); |
|
2132 |
✓✗ | 4 |
if (parms != NULL) |
2133 |
4 |
CONF_free(parms); |
|
2134 |
✓✗ | 4 |
if (spki != NULL) |
2135 |
4 |
NETSCAPE_SPKI_free(spki); |
|
2136 |
|||
2137 |
4 |
return (ok); |
|
2138 |
4 |
} |
|
2139 |
|||
2140 |
static int |
||
2141 |
check_time_format(const char *str) |
||
2142 |
{ |
||
2143 |
120 |
return ASN1_TIME_set_string(NULL, str); |
|
2144 |
} |
||
2145 |
|||
2146 |
static int |
||
2147 |
do_revoke(X509 * x509, CA_DB * db, int type, char *value) |
||
2148 |
{ |
||
2149 |
ASN1_UTCTIME *tm = NULL; |
||
2150 |
16 |
char *row[DB_NUMBER], **rrow, **irow; |
|
2151 |
char *rev_str = NULL; |
||
2152 |
BIGNUM *bn = NULL; |
||
2153 |
int ok = -1, i; |
||
2154 |
|||
2155 |
✓✓ | 112 |
for (i = 0; i < DB_NUMBER; i++) |
2156 |
48 |
row[i] = NULL; |
|
2157 |
8 |
row[DB_name] = X509_NAME_oneline(X509_get_subject_name(x509), NULL, 0); |
|
2158 |
8 |
bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(x509), NULL); |
|
2159 |
✓✗ | 8 |
if (!bn) |
2160 |
goto err; |
||
2161 |
✗✓ | 8 |
if (BN_is_zero(bn)) |
2162 |
row[DB_serial] = strdup("00"); |
||
2163 |
else |
||
2164 |
8 |
row[DB_serial] = BN_bn2hex(bn); |
|
2165 |
8 |
BN_free(bn); |
|
2166 |
✓✗✗✓ |
16 |
if ((row[DB_name] == NULL) || (row[DB_serial] == NULL)) { |
2167 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
2168 |
goto err; |
||
2169 |
} |
||
2170 |
/* |
||
2171 |
* We have to lookup by serial number because name lookup skips |
||
2172 |
* revoked certs |
||
2173 |
*/ |
||
2174 |
8 |
rrow = TXT_DB_get_by_index(db->db, DB_serial, row); |
|
2175 |
✓✓ | 8 |
if (rrow == NULL) { |
2176 |
8 |
BIO_printf(bio_err, |
|
2177 |
"Adding Entry with serial number %s to DB for %s\n", |
||
2178 |
4 |
row[DB_serial], row[DB_name]); |
|
2179 |
|||
2180 |
/* We now just add it to the database */ |
||
2181 |
4 |
row[DB_type] = malloc(2); |
|
2182 |
|||
2183 |
4 |
tm = X509_get_notAfter(x509); |
|
2184 |
4 |
row[DB_exp_date] = malloc(tm->length + 1); |
|
2185 |
✓✗✗✓ |
8 |
if (row[DB_type] == NULL || row[DB_exp_date] == NULL) { |
2186 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
2187 |
goto err; |
||
2188 |
} |
||
2189 |
4 |
memcpy(row[DB_exp_date], tm->data, tm->length); |
|
2190 |
4 |
row[DB_exp_date][tm->length] = '\0'; |
|
2191 |
|||
2192 |
4 |
row[DB_rev_date] = NULL; |
|
2193 |
|||
2194 |
/* row[DB_serial] done already */ |
||
2195 |
4 |
row[DB_file] = malloc(8); |
|
2196 |
|||
2197 |
/* row[DB_name] done already */ |
||
2198 |
|||
2199 |
✓✗✗✓ |
8 |
if ((row[DB_type] == NULL) || (row[DB_file] == NULL)) { |
2200 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
2201 |
goto err; |
||
2202 |
} |
||
2203 |
4 |
(void) strlcpy(row[DB_file], "unknown", 8); |
|
2204 |
4 |
row[DB_type][0] = 'V'; |
|
2205 |
4 |
row[DB_type][1] = '\0'; |
|
2206 |
|||
2207 |
✗✓ | 8 |
if ((irow = reallocarray(NULL, sizeof(char *), |
2208 |
4 |
(DB_NUMBER + 1))) == NULL) { |
|
2209 |
BIO_printf(bio_err, "Memory allocation failure\n"); |
||
2210 |
goto err; |
||
2211 |
} |
||
2212 |
✓✓ | 56 |
for (i = 0; i < DB_NUMBER; i++) { |
2213 |
24 |
irow[i] = row[i]; |
|
2214 |
24 |
row[i] = NULL; |
|
2215 |
} |
||
2216 |
4 |
irow[DB_NUMBER] = NULL; |
|
2217 |
|||
2218 |
✗✓ | 4 |
if (!TXT_DB_insert(db->db, irow)) { |
2219 |
BIO_printf(bio_err, "failed to update database\n"); |
||
2220 |
BIO_printf(bio_err, "TXT_DB error number %ld\n", |
||
2221 |
db->db->error); |
||
2222 |
goto err; |
||
2223 |
} |
||
2224 |
/* Revoke Certificate */ |
||
2225 |
4 |
ok = do_revoke(x509, db, type, value); |
|
2226 |
|||
2227 |
4 |
goto err; |
|
2228 |
|||
2229 |
✗✓ | 4 |
} else if (index_name_cmp_noconst(row, rrow)) { |
2230 |
BIO_printf(bio_err, "ERROR:name does not match %s\n", |
||
2231 |
row[DB_name]); |
||
2232 |
goto err; |
||
2233 |
✗✓ | 4 |
} else if (rrow[DB_type][0] == 'R') { |
2234 |
BIO_printf(bio_err, "ERROR:Already revoked, serial number %s\n", |
||
2235 |
row[DB_serial]); |
||
2236 |
goto err; |
||
2237 |
} else { |
||
2238 |
4 |
BIO_printf(bio_err, "Revoking Certificate %s.\n", |
|
2239 |
4 |
rrow[DB_serial]); |
|
2240 |
4 |
rev_str = make_revocation_str(type, value); |
|
2241 |
✗✓ | 4 |
if (!rev_str) { |
2242 |
BIO_printf(bio_err, "Error in revocation arguments\n"); |
||
2243 |
goto err; |
||
2244 |
} |
||
2245 |
4 |
rrow[DB_type][0] = 'R'; |
|
2246 |
4 |
rrow[DB_type][1] = '\0'; |
|
2247 |
4 |
rrow[DB_rev_date] = rev_str; |
|
2248 |
} |
||
2249 |
4 |
ok = 1; |
|
2250 |
|||
2251 |
err: |
||
2252 |
✓✓ | 112 |
for (i = 0; i < DB_NUMBER; i++) |
2253 |
48 |
free(row[i]); |
|
2254 |
|||
2255 |
8 |
return (ok); |
|
2256 |
8 |
} |
|
2257 |
|||
2258 |
static int |
||
2259 |
get_certificate_status(const char *serial, CA_DB * db) |
||
2260 |
{ |
||
2261 |
char *row[DB_NUMBER], **rrow; |
||
2262 |
int ok = -1, i; |
||
2263 |
|||
2264 |
/* Free Resources */ |
||
2265 |
for (i = 0; i < DB_NUMBER; i++) |
||
2266 |
row[i] = NULL; |
||
2267 |
|||
2268 |
/* Malloc needed char spaces */ |
||
2269 |
row[DB_serial] = malloc(strlen(serial) + 2); |
||
2270 |
if (row[DB_serial] == NULL) { |
||
2271 |
BIO_printf(bio_err, "Malloc failure\n"); |
||
2272 |
goto err; |
||
2273 |
} |
||
2274 |
if (strlen(serial) % 2) { |
||
2275 |
/* Set the first char to 0 */ ; |
||
2276 |
row[DB_serial][0] = '0'; |
||
2277 |
|||
2278 |
/* Copy String from serial to row[DB_serial] */ |
||
2279 |
memcpy(row[DB_serial] + 1, serial, strlen(serial)); |
||
2280 |
row[DB_serial][strlen(serial) + 1] = '\0'; |
||
2281 |
} else { |
||
2282 |
/* Copy String from serial to row[DB_serial] */ |
||
2283 |
memcpy(row[DB_serial], serial, strlen(serial)); |
||
2284 |
row[DB_serial][strlen(serial)] = '\0'; |
||
2285 |
} |
||
2286 |
|||
2287 |
/* Make it Upper Case */ |
||
2288 |
for (i = 0; row[DB_serial][i] != '\0'; i++) |
||
2289 |
row[DB_serial][i] = toupper((unsigned char) row[DB_serial][i]); |
||
2290 |
|||
2291 |
|||
2292 |
ok = 1; |
||
2293 |
|||
2294 |
/* Search for the certificate */ |
||
2295 |
rrow = TXT_DB_get_by_index(db->db, DB_serial, row); |
||
2296 |
if (rrow == NULL) { |
||
2297 |
BIO_printf(bio_err, "Serial %s not present in db.\n", |
||
2298 |
row[DB_serial]); |
||
2299 |
ok = -1; |
||
2300 |
goto err; |
||
2301 |
} else if (rrow[DB_type][0] == 'V') { |
||
2302 |
BIO_printf(bio_err, "%s=Valid (%c)\n", |
||
2303 |
row[DB_serial], rrow[DB_type][0]); |
||
2304 |
goto err; |
||
2305 |
} else if (rrow[DB_type][0] == 'R') { |
||
2306 |
BIO_printf(bio_err, "%s=Revoked (%c)\n", |
||
2307 |
row[DB_serial], rrow[DB_type][0]); |
||
2308 |
goto err; |
||
2309 |
} else if (rrow[DB_type][0] == 'E') { |
||
2310 |
BIO_printf(bio_err, "%s=Expired (%c)\n", |
||
2311 |
row[DB_serial], rrow[DB_type][0]); |
||
2312 |
goto err; |
||
2313 |
} else if (rrow[DB_type][0] == 'S') { |
||
2314 |
BIO_printf(bio_err, "%s=Suspended (%c)\n", |
||
2315 |
row[DB_serial], rrow[DB_type][0]); |
||
2316 |
goto err; |
||
2317 |
} else { |
||
2318 |
BIO_printf(bio_err, "%s=Unknown (%c).\n", |
||
2319 |
row[DB_serial], rrow[DB_type][0]); |
||
2320 |
ok = -1; |
||
2321 |
} |
||
2322 |
|||
2323 |
err: |
||
2324 |
for (i = 0; i < DB_NUMBER; i++) |
||
2325 |
free(row[i]); |
||
2326 |
|||
2327 |
return (ok); |
||
2328 |
} |
||
2329 |
|||
2330 |
static int |
||
2331 |
do_updatedb(CA_DB * db) |
||
2332 |
{ |
||
2333 |
ASN1_UTCTIME *a_tm = NULL; |
||
2334 |
int i, cnt = 0; |
||
2335 |
int db_y2k, a_y2k; /* flags = 1 if y >= 2000 */ |
||
2336 |
char **rrow, *a_tm_s; |
||
2337 |
|||
2338 |
a_tm = ASN1_UTCTIME_new(); |
||
2339 |
|||
2340 |
/* get actual time and make a string */ |
||
2341 |
a_tm = X509_gmtime_adj(a_tm, 0); |
||
2342 |
a_tm_s = malloc(a_tm->length + 1); |
||
2343 |
if (a_tm_s == NULL) { |
||
2344 |
cnt = -1; |
||
2345 |
goto err; |
||
2346 |
} |
||
2347 |
memcpy(a_tm_s, a_tm->data, a_tm->length); |
||
2348 |
a_tm_s[a_tm->length] = '\0'; |
||
2349 |
|||
2350 |
if (strncmp(a_tm_s, "49", 2) <= 0) |
||
2351 |
a_y2k = 1; |
||
2352 |
else |
||
2353 |
a_y2k = 0; |
||
2354 |
|||
2355 |
for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { |
||
2356 |
rrow = sk_OPENSSL_PSTRING_value(db->db->data, i); |
||
2357 |
|||
2358 |
if (rrow[DB_type][0] == 'V') { |
||
2359 |
/* ignore entries that are not valid */ |
||
2360 |
if (strncmp(rrow[DB_exp_date], "49", 2) <= 0) |
||
2361 |
db_y2k = 1; |
||
2362 |
else |
||
2363 |
db_y2k = 0; |
||
2364 |
|||
2365 |
if (db_y2k == a_y2k) { |
||
2366 |
/* all on the same y2k side */ |
||
2367 |
if (strcmp(rrow[DB_exp_date], a_tm_s) <= 0) { |
||
2368 |
rrow[DB_type][0] = 'E'; |
||
2369 |
rrow[DB_type][1] = '\0'; |
||
2370 |
cnt++; |
||
2371 |
|||
2372 |
BIO_printf(bio_err, "%s=Expired\n", |
||
2373 |
rrow[DB_serial]); |
||
2374 |
} |
||
2375 |
} else if (db_y2k < a_y2k) { |
||
2376 |
rrow[DB_type][0] = 'E'; |
||
2377 |
rrow[DB_type][1] = '\0'; |
||
2378 |
cnt++; |
||
2379 |
|||
2380 |
BIO_printf(bio_err, "%s=Expired\n", |
||
2381 |
rrow[DB_serial]); |
||
2382 |
} |
||
2383 |
} |
||
2384 |
} |
||
2385 |
|||
2386 |
err: |
||
2387 |
ASN1_UTCTIME_free(a_tm); |
||
2388 |
free(a_tm_s); |
||
2389 |
|||
2390 |
return (cnt); |
||
2391 |
} |
||
2392 |
|||
2393 |
static const char *crl_reasons[] = { |
||
2394 |
/* CRL reason strings */ |
||
2395 |
"unspecified", |
||
2396 |
"keyCompromise", |
||
2397 |
"CACompromise", |
||
2398 |
"affiliationChanged", |
||
2399 |
"superseded", |
||
2400 |
"cessationOfOperation", |
||
2401 |
"certificateHold", |
||
2402 |
"removeFromCRL", |
||
2403 |
/* Additional pseudo reasons */ |
||
2404 |
"holdInstruction", |
||
2405 |
"keyTime", |
||
2406 |
"CAkeyTime" |
||
2407 |
}; |
||
2408 |
|||
2409 |
#define NUM_REASONS (sizeof(crl_reasons) / sizeof(char *)) |
||
2410 |
|||
2411 |
/* Given revocation information convert to a DB string. |
||
2412 |
* The format of the string is: |
||
2413 |
* revtime[,reason,extra]. Where 'revtime' is the |
||
2414 |
* revocation time (the current time). 'reason' is the |
||
2415 |
* optional CRL reason and 'extra' is any additional |
||
2416 |
* argument |
||
2417 |
*/ |
||
2418 |
|||
2419 |
char * |
||
2420 |
make_revocation_str(int rev_type, char *rev_arg) |
||
2421 |
{ |
||
2422 |
8 |
char *other = NULL, *str; |
|
2423 |
const char *reason = NULL; |
||
2424 |
ASN1_OBJECT *otmp; |
||
2425 |
ASN1_UTCTIME *revtm = NULL; |
||
2426 |
int i; |
||
2427 |
✗✗✗✗ ✓ |
4 |
switch (rev_type) { |
2428 |
case REV_NONE: |
||
2429 |
break; |
||
2430 |
|||
2431 |
case REV_CRL_REASON: |
||
2432 |
for (i = 0; i < 8; i++) { |
||
2433 |
if (!strcasecmp(rev_arg, crl_reasons[i])) { |
||
2434 |
reason = crl_reasons[i]; |
||
2435 |
break; |
||
2436 |
} |
||
2437 |
} |
||
2438 |
if (reason == NULL) { |
||
2439 |
BIO_printf(bio_err, "Unknown CRL reason %s\n", rev_arg); |
||
2440 |
return NULL; |
||
2441 |
} |
||
2442 |
break; |
||
2443 |
|||
2444 |
case REV_HOLD: |
||
2445 |
/* Argument is an OID */ |
||
2446 |
|||
2447 |
otmp = OBJ_txt2obj(rev_arg, 0); |
||
2448 |
ASN1_OBJECT_free(otmp); |
||
2449 |
|||
2450 |
if (otmp == NULL) { |
||
2451 |
BIO_printf(bio_err, |
||
2452 |
"Invalid object identifier %s\n", rev_arg); |
||
2453 |
return NULL; |
||
2454 |
} |
||
2455 |
reason = "holdInstruction"; |
||
2456 |
other = rev_arg; |
||
2457 |
break; |
||
2458 |
|||
2459 |
case REV_KEY_COMPROMISE: |
||
2460 |
case REV_CA_COMPROMISE: |
||
2461 |
|||
2462 |
/* Argument is the key compromise time */ |
||
2463 |
if (!ASN1_GENERALIZEDTIME_set_string(NULL, rev_arg)) { |
||
2464 |
BIO_printf(bio_err, |
||
2465 |
"Invalid time format %s. Need YYYYMMDDHHMMSSZ\n", |
||
2466 |
rev_arg); |
||
2467 |
return NULL; |
||
2468 |
} |
||
2469 |
other = rev_arg; |
||
2470 |
if (rev_type == REV_KEY_COMPROMISE) |
||
2471 |
reason = "keyTime"; |
||
2472 |
else |
||
2473 |
reason = "CAkeyTime"; |
||
2474 |
|||
2475 |
break; |
||
2476 |
|||
2477 |
} |
||
2478 |
|||
2479 |
4 |
revtm = X509_gmtime_adj(NULL, 0); |
|
2480 |
✗✓ | 12 |
if (asprintf(&str, "%s%s%s%s%s", revtm->data, |
2481 |
4 |
reason ? "," : "", reason ? reason : "", |
|
2482 |
8 |
other ? "," : "", other ? other : "") == -1) |
|
2483 |
str = NULL; |
||
2484 |
4 |
ASN1_UTCTIME_free(revtm); |
|
2485 |
4 |
return str; |
|
2486 |
4 |
} |
|
2487 |
|||
2488 |
/* Convert revocation field to X509_REVOKED entry |
||
2489 |
* return code: |
||
2490 |
* 0 error |
||
2491 |
* 1 OK |
||
2492 |
* 2 OK and some extensions added (i.e. V2 CRL) |
||
2493 |
*/ |
||
2494 |
|||
2495 |
int |
||
2496 |
make_revoked(X509_REVOKED * rev, const char *str) |
||
2497 |
{ |
||
2498 |
char *tmp = NULL; |
||
2499 |
16 |
int reason_code = -1; |
|
2500 |
int i, ret = 0; |
||
2501 |
8 |
ASN1_OBJECT *hold = NULL; |
|
2502 |
8 |
ASN1_GENERALIZEDTIME *comp_time = NULL; |
|
2503 |
ASN1_ENUMERATED *rtmp = NULL; |
||
2504 |
|||
2505 |
8 |
ASN1_TIME *revDate = NULL; |
|
2506 |
|||
2507 |
8 |
i = unpack_revinfo(&revDate, &reason_code, &hold, &comp_time, str); |
|
2508 |
|||
2509 |
✓✗ | 8 |
if (i == 0) |
2510 |
goto err; |
||
2511 |
|||
2512 |
✗✓✗✗ |
8 |
if (rev && !X509_REVOKED_set_revocationDate(rev, revDate)) |
2513 |
goto err; |
||
2514 |
|||
2515 |
✗✓ | 8 |
if (rev && (reason_code != OCSP_REVOKED_STATUS_NOSTATUS)) { |
2516 |
rtmp = ASN1_ENUMERATED_new(); |
||
2517 |
if (!rtmp || !ASN1_ENUMERATED_set(rtmp, reason_code)) |
||
2518 |
goto err; |
||
2519 |
if (!X509_REVOKED_add1_ext_i2d(rev, NID_crl_reason, rtmp, 0, 0)) |
||
2520 |
goto err; |
||
2521 |
} |
||
2522 |
✗✓ | 8 |
if (rev && comp_time) { |
2523 |
if (!X509_REVOKED_add1_ext_i2d(rev, NID_invalidity_date, |
||
2524 |
comp_time, 0, 0)) |
||
2525 |
goto err; |
||
2526 |
} |
||
2527 |
✗✓ | 8 |
if (rev && hold) { |
2528 |
if (!X509_REVOKED_add1_ext_i2d(rev, NID_hold_instruction_code, |
||
2529 |
hold, 0, 0)) |
||
2530 |
goto err; |
||
2531 |
} |
||
2532 |
✗✓ | 8 |
if (reason_code != OCSP_REVOKED_STATUS_NOSTATUS) |
2533 |
ret = 2; |
||
2534 |
else |
||
2535 |
ret = 1; |
||
2536 |
|||
2537 |
err: |
||
2538 |
free(tmp); |
||
2539 |
|||
2540 |
8 |
ASN1_OBJECT_free(hold); |
|
2541 |
8 |
ASN1_GENERALIZEDTIME_free(comp_time); |
|
2542 |
8 |
ASN1_ENUMERATED_free(rtmp); |
|
2543 |
8 |
ASN1_TIME_free(revDate); |
|
2544 |
|||
2545 |
8 |
return ret; |
|
2546 |
8 |
} |
|
2547 |
|||
2548 |
int |
||
2549 |
old_entry_print(BIO * bp, ASN1_OBJECT * obj, ASN1_STRING * str) |
||
2550 |
{ |
||
2551 |
160 |
char buf[25], *pbuf, *p; |
|
2552 |
int j; |
||
2553 |
|||
2554 |
80 |
j = i2a_ASN1_OBJECT(bp, obj); |
|
2555 |
80 |
pbuf = buf; |
|
2556 |
✓✓ | 1440 |
for (j = 22 - j; j > 0; j--) |
2557 |
640 |
*(pbuf++) = ' '; |
|
2558 |
80 |
*(pbuf++) = ':'; |
|
2559 |
80 |
*(pbuf++) = '\0'; |
|
2560 |
80 |
BIO_puts(bp, buf); |
|
2561 |
|||
2562 |
✓✓ | 80 |
if (str->type == V_ASN1_PRINTABLESTRING) |
2563 |
20 |
BIO_printf(bp, "PRINTABLE:'"); |
|
2564 |
✗✓ | 60 |
else if (str->type == V_ASN1_T61STRING) |
2565 |
BIO_printf(bp, "T61STRING:'"); |
||
2566 |
✗✓ | 60 |
else if (str->type == V_ASN1_IA5STRING) |
2567 |
BIO_printf(bp, "IA5STRING:'"); |
||
2568 |
✗✓ | 60 |
else if (str->type == V_ASN1_UNIVERSALSTRING) |
2569 |
BIO_printf(bp, "UNIVERSALSTRING:'"); |
||
2570 |
else |
||
2571 |
60 |
BIO_printf(bp, "ASN.1 %2d:'", str->type); |
|
2572 |
|||
2573 |
80 |
p = (char *) str->data; |
|
2574 |
✓✓ | 2032 |
for (j = str->length; j > 0; j--) { |
2575 |
✓✗✓✗ |
1872 |
if ((*p >= ' ') && (*p <= '~')) |
2576 |
936 |
BIO_printf(bp, "%c", *p); |
|
2577 |
else if (*p & 0x80) |
||
2578 |
BIO_printf(bp, "\\0x%02X", *p); |
||
2579 |
else if ((unsigned char) *p == 0xf7) |
||
2580 |
BIO_printf(bp, "^?"); |
||
2581 |
else |
||
2582 |
BIO_printf(bp, "^%c", *p + '@'); |
||
2583 |
936 |
p++; |
|
2584 |
} |
||
2585 |
80 |
BIO_printf(bp, "'\n"); |
|
2586 |
80 |
return 1; |
|
2587 |
80 |
} |
|
2588 |
|||
2589 |
int |
||
2590 |
unpack_revinfo(ASN1_TIME ** prevtm, int *preason, ASN1_OBJECT ** phold, |
||
2591 |
ASN1_GENERALIZEDTIME ** pinvtm, const char *str) |
||
2592 |
{ |
||
2593 |
char *tmp = NULL; |
||
2594 |
char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; |
||
2595 |
int reason_code = -1; |
||
2596 |
int ret = 0; |
||
2597 |
unsigned int i; |
||
2598 |
ASN1_OBJECT *hold = NULL; |
||
2599 |
ASN1_GENERALIZEDTIME *comp_time = NULL; |
||
2600 |
|||
2601 |
✗✓ | 32 |
if ((tmp = strdup(str)) == NULL) { |
2602 |
BIO_printf(bio_err, "malloc failed\n"); |
||
2603 |
goto err; |
||
2604 |
} |
||
2605 |
16 |
p = strchr(tmp, ','); |
|
2606 |
rtime_str = tmp; |
||
2607 |
|||
2608 |
✗✓ | 16 |
if (p) { |
2609 |
*p = '\0'; |
||
2610 |
p++; |
||
2611 |
reason_str = p; |
||
2612 |
p = strchr(p, ','); |
||
2613 |
if (p) { |
||
2614 |
*p = '\0'; |
||
2615 |
arg_str = p + 1; |
||
2616 |
} |
||
2617 |
} |
||
2618 |
✓✗ | 16 |
if (prevtm) { |
2619 |
16 |
*prevtm = ASN1_UTCTIME_new(); |
|
2620 |
✗✓ | 16 |
if (!ASN1_UTCTIME_set_string(*prevtm, rtime_str)) { |
2621 |
BIO_printf(bio_err, "invalid revocation date %s\n", |
||
2622 |
rtime_str); |
||
2623 |
goto err; |
||
2624 |
} |
||
2625 |
} |
||
2626 |
✗✓ | 16 |
if (reason_str) { |
2627 |
for (i = 0; i < NUM_REASONS; i++) { |
||
2628 |
if (!strcasecmp(reason_str, crl_reasons[i])) { |
||
2629 |
reason_code = i; |
||
2630 |
break; |
||
2631 |
} |
||
2632 |
} |
||
2633 |
if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { |
||
2634 |
BIO_printf(bio_err, "invalid reason code %s\n", |
||
2635 |
reason_str); |
||
2636 |
goto err; |
||
2637 |
} |
||
2638 |
if (reason_code == 7) |
||
2639 |
reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; |
||
2640 |
else if (reason_code == 8) { /* Hold instruction */ |
||
2641 |
if (!arg_str) { |
||
2642 |
BIO_printf(bio_err, |
||
2643 |
"missing hold instruction\n"); |
||
2644 |
goto err; |
||
2645 |
} |
||
2646 |
reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; |
||
2647 |
hold = OBJ_txt2obj(arg_str, 0); |
||
2648 |
|||
2649 |
if (!hold) { |
||
2650 |
BIO_printf(bio_err, |
||
2651 |
"invalid object identifier %s\n", arg_str); |
||
2652 |
goto err; |
||
2653 |
} |
||
2654 |
if (phold) |
||
2655 |
*phold = hold; |
||
2656 |
} else if ((reason_code == 9) || (reason_code == 10)) { |
||
2657 |
if (!arg_str) { |
||
2658 |
BIO_printf(bio_err, |
||
2659 |
"missing compromised time\n"); |
||
2660 |
goto err; |
||
2661 |
} |
||
2662 |
comp_time = ASN1_GENERALIZEDTIME_new(); |
||
2663 |
if (!ASN1_GENERALIZEDTIME_set_string(comp_time, |
||
2664 |
arg_str)) { |
||
2665 |
BIO_printf(bio_err, |
||
2666 |
"invalid compromised time %s\n", arg_str); |
||
2667 |
goto err; |
||
2668 |
} |
||
2669 |
if (reason_code == 9) |
||
2670 |
reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; |
||
2671 |
else |
||
2672 |
reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; |
||
2673 |
} |
||
2674 |
} |
||
2675 |
✓✗ | 16 |
if (preason) |
2676 |
16 |
*preason = reason_code; |
|
2677 |
✓✗ | 16 |
if (pinvtm) |
2678 |
16 |
*pinvtm = comp_time; |
|
2679 |
else |
||
2680 |
ASN1_GENERALIZEDTIME_free(comp_time); |
||
2681 |
|||
2682 |
16 |
ret = 1; |
|
2683 |
|||
2684 |
err: |
||
2685 |
16 |
free(tmp); |
|
2686 |
|||
2687 |
✗✓ | 16 |
if (!phold) |
2688 |
ASN1_OBJECT_free(hold); |
||
2689 |
✗✓ | 16 |
if (!pinvtm) |
2690 |
ASN1_GENERALIZEDTIME_free(comp_time); |
||
2691 |
|||
2692 |
16 |
return ret; |
|
2693 |
} |
||
2694 |
|||
2695 |
static char * |
||
2696 |
bin2hex(unsigned char * data, size_t len) |
||
2697 |
{ |
||
2698 |
char *ret = NULL; |
||
2699 |
40 |
char hex[] = "0123456789ABCDEF"; |
|
2700 |
int i; |
||
2701 |
|||
2702 |
✓✗ | 20 |
if ((ret = malloc(len * 2 + 1))) { |
2703 |
✓✓ | 80 |
for (i = 0; i < len; i++) { |
2704 |
20 |
ret[i * 2 + 0] = hex[data[i] >> 4]; |
|
2705 |
20 |
ret[i * 2 + 1] = hex[data[i] & 0x0F]; |
|
2706 |
} |
||
2707 |
20 |
ret[len * 2] = '\0'; |
|
2708 |
20 |
} |
|
2709 |
20 |
return ret; |
|
2710 |
20 |
} |
Generated by: GCOVR (Version 3.3) |