1 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2 * All rights reserved.
3 *
4 * This package is an SSL implementation written
5 * by Eric Young (eay@cryptsoft.com).
6 * The implementation was written so as to conform with Netscapes SSL.
7 *
8 * This library is free for commercial and non-commercial use as long as
9 * the following conditions are aheared to. The following conditions
10 * apply to all code found in this distribution, be it the RC4, RSA,
11 * lhash, DES, etc., code; not just the SSL code. The SSL documentation
12 * included with this distribution is covered by the same copyright terms
13 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
14 *
15 * Copyright remains Eric Young's, and as such any Copyright notices in
16 * the code are not to be removed.
17 * If this package is used in a product, Eric Young should be given attribution
18 * as the author of the parts of the library used.
19 * This can be in the form of a textual message at program startup or
20 * in documentation (online or textual) provided with the package.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the copyright
26 * notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in the
29 * documentation and/or other materials provided with the distribution.
30 * 3. All advertising materials mentioning features or use of this software
31 * must display the following acknowledgement:
32 * "This product includes cryptographic software written by
33 * Eric Young (eay@cryptsoft.com)"
34 * The word 'cryptographic' can be left out if the rouines from the library
35 * being used are not cryptographic related :-).
36 * 4. If you include any Windows specific code (or a derivative thereof) from
37 * the apps directory (application code) you must include an acknowledgement:
38 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
39 *
40 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 *
52 * The licence and distribution terms for any publically available version or
53 * derivative of this code cannot be changed. i.e. this code cannot simply be
54 * copied and put under another distribution licence
55 * [including the GNU Public Licence.] */
56
57 #include <openssl/asn1.h>
58
59 #include <ctype.h>
60 #include <inttypes.h>
61 #include <string.h>
62
63 #include <openssl/bio.h>
64 #include <openssl/mem.h>
65
66 #include "charmap.h"
67 #include "internal.h"
68
69
70 // These flags must be distinct from |ESC_FLAGS| and fit in a byte.
71
72 // Character is a valid PrintableString character
73 #define CHARTYPE_PRINTABLESTRING 0x10
74 // Character needs escaping if it is the first character
75 #define CHARTYPE_FIRST_ESC_2253 0x20
76 // Character needs escaping if it is the last character
77 #define CHARTYPE_LAST_ESC_2253 0x40
78
79 #define CHARTYPE_BS_ESC (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
80
81 #define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
82 ASN1_STRFLGS_ESC_QUOTE | \
83 ASN1_STRFLGS_ESC_CTRL | \
84 ASN1_STRFLGS_ESC_MSB)
85
maybe_write(BIO * out,const void * buf,int len)86 static int maybe_write(BIO *out, const void *buf, int len)
87 {
88 /* If |out| is NULL, ignore the output but report the length. */
89 return out == NULL || BIO_write(out, buf, len) == len;
90 }
91
92 /*
93 * This function handles display of strings, one character at a time. It is
94 * passed an unsigned long for each character because it could come from 2 or
95 * even 4 byte forms.
96 */
97
98 #define HEX_SIZE(type) (sizeof(type)*2)
99
do_esc_char(uint32_t c,unsigned char flags,char * do_quotes,BIO * out)100 static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes,
101 BIO *out)
102 {
103 unsigned char chflgs, chtmp;
104 char tmphex[HEX_SIZE(uint32_t) + 3];
105
106 if (c > 0xffff) {
107 BIO_snprintf(tmphex, sizeof tmphex, "\\W%08" PRIX32, c);
108 if (!maybe_write(out, tmphex, 10))
109 return -1;
110 return 10;
111 }
112 if (c > 0xff) {
113 BIO_snprintf(tmphex, sizeof tmphex, "\\U%04" PRIX32, c);
114 if (!maybe_write(out, tmphex, 6))
115 return -1;
116 return 6;
117 }
118 chtmp = (unsigned char)c;
119 if (chtmp > 0x7f)
120 chflgs = flags & ASN1_STRFLGS_ESC_MSB;
121 else
122 chflgs = char_type[chtmp] & flags;
123 if (chflgs & CHARTYPE_BS_ESC) {
124 /* If we don't escape with quotes, signal we need quotes */
125 if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
126 if (do_quotes)
127 *do_quotes = 1;
128 if (!maybe_write(out, &chtmp, 1))
129 return -1;
130 return 1;
131 }
132 if (!maybe_write(out, "\\", 1))
133 return -1;
134 if (!maybe_write(out, &chtmp, 1))
135 return -1;
136 return 2;
137 }
138 if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB)) {
139 BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
140 if (!maybe_write(out, tmphex, 3))
141 return -1;
142 return 3;
143 }
144 /*
145 * If we get this far and do any escaping at all must escape the escape
146 * character itself: backslash.
147 */
148 if (chtmp == '\\' && flags & ESC_FLAGS) {
149 if (!maybe_write(out, "\\\\", 2))
150 return -1;
151 return 2;
152 }
153 if (!maybe_write(out, &chtmp, 1))
154 return -1;
155 return 1;
156 }
157
158 #define BUF_TYPE_WIDTH_MASK 0x7
159 #define BUF_TYPE_CONVUTF8 0x8
160
161 /*
162 * This function sends each character in a buffer to do_esc_char(). It
163 * interprets the content formats and converts to or from UTF8 as
164 * appropriate.
165 */
166
do_buf(unsigned char * buf,int buflen,int type,unsigned char flags,char * quotes,BIO * out)167 static int do_buf(unsigned char *buf, int buflen,
168 int type, unsigned char flags, char *quotes, BIO *out)
169 {
170 int i, outlen, len, charwidth;
171 unsigned char orflags, *p, *q;
172 uint32_t c;
173 p = buf;
174 q = buf + buflen;
175 outlen = 0;
176 charwidth = type & BUF_TYPE_WIDTH_MASK;
177
178 switch (charwidth) {
179 case 4:
180 if (buflen & 3) {
181 OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UNIVERSALSTRING);
182 return -1;
183 }
184 break;
185 case 2:
186 if (buflen & 1) {
187 OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BMPSTRING);
188 return -1;
189 }
190 break;
191 default:
192 break;
193 }
194
195 while (p != q) {
196 if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
197 orflags = CHARTYPE_FIRST_ESC_2253;
198 else
199 orflags = 0;
200 /* TODO(davidben): Replace this with |cbs_get_ucs2_be|, etc., to check
201 * for invalid codepoints. */
202 switch (charwidth) {
203 case 4:
204 c = ((uint32_t)*p++) << 24;
205 c |= ((uint32_t)*p++) << 16;
206 c |= ((uint32_t)*p++) << 8;
207 c |= *p++;
208 break;
209
210 case 2:
211 c = ((uint32_t)*p++) << 8;
212 c |= *p++;
213 break;
214
215 case 1:
216 c = *p++;
217 break;
218
219 case 0:
220 i = UTF8_getc(p, buflen, &c);
221 if (i < 0)
222 return -1; /* Invalid UTF8String */
223 buflen -= i;
224 p += i;
225 break;
226 default:
227 return -1; /* invalid width */
228 }
229 if (p == q && flags & ASN1_STRFLGS_ESC_2253)
230 orflags = CHARTYPE_LAST_ESC_2253;
231 if (type & BUF_TYPE_CONVUTF8) {
232 unsigned char utfbuf[6];
233 int utflen;
234 utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
235 for (i = 0; i < utflen; i++) {
236 /*
237 * We don't need to worry about setting orflags correctly
238 * because if utflen==1 its value will be correct anyway
239 * otherwise each character will be > 0x7f and so the
240 * character will never be escaped on first and last.
241 */
242 len = do_esc_char(utfbuf[i], (unsigned char)(flags | orflags),
243 quotes, out);
244 if (len < 0)
245 return -1;
246 outlen += len;
247 }
248 } else {
249 len = do_esc_char(c, (unsigned char)(flags | orflags), quotes, out);
250 if (len < 0)
251 return -1;
252 outlen += len;
253 }
254 }
255 return outlen;
256 }
257
258 /* This function hex dumps a buffer of characters */
259
do_hex_dump(BIO * out,unsigned char * buf,int buflen)260 static int do_hex_dump(BIO *out, unsigned char *buf, int buflen)
261 {
262 static const char hexdig[] = "0123456789ABCDEF";
263 unsigned char *p, *q;
264 char hextmp[2];
265 if (out) {
266 p = buf;
267 q = buf + buflen;
268 while (p != q) {
269 hextmp[0] = hexdig[*p >> 4];
270 hextmp[1] = hexdig[*p & 0xf];
271 if (!maybe_write(out, hextmp, 2))
272 return -1;
273 p++;
274 }
275 }
276 return buflen << 1;
277 }
278
279 /*
280 * "dump" a string. This is done when the type is unknown, or the flags
281 * request it. We can either dump the content octets or the entire DER
282 * encoding. This uses the RFC 2253 #01234 format.
283 */
284
do_dump(unsigned long lflags,BIO * out,const ASN1_STRING * str)285 static int do_dump(unsigned long lflags, BIO *out, const ASN1_STRING *str)
286 {
287 if (!maybe_write(out, "#", 1)) {
288 return -1;
289 }
290
291 /* If we don't dump DER encoding just dump content octets */
292 if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
293 int outlen = do_hex_dump(out, str->data, str->length);
294 if (outlen < 0) {
295 return -1;
296 }
297 return outlen + 1;
298 }
299
300 /*
301 * Placing the ASN1_STRING in a temporary ASN1_TYPE allows the DER encoding
302 * to readily obtained.
303 */
304 ASN1_TYPE t;
305 t.type = str->type;
306 /* Negative INTEGER and ENUMERATED values are the only case where
307 * |ASN1_STRING| and |ASN1_TYPE| types do not match.
308 *
309 * TODO(davidben): There are also some type fields which, in |ASN1_TYPE|, do
310 * not correspond to |ASN1_STRING|. It is unclear whether those are allowed
311 * in |ASN1_STRING| at all, or what the space of allowed types is.
312 * |ASN1_item_ex_d2i| will never produce such a value so, for now, we say
313 * this is an invalid input. But this corner of the library in general
314 * should be more robust. */
315 if (t.type == V_ASN1_NEG_INTEGER) {
316 t.type = V_ASN1_INTEGER;
317 } else if (t.type == V_ASN1_NEG_ENUMERATED) {
318 t.type = V_ASN1_ENUMERATED;
319 }
320 t.value.asn1_string = (ASN1_STRING *)str;
321 unsigned char *der_buf = NULL;
322 int der_len = i2d_ASN1_TYPE(&t, &der_buf);
323 if (der_len < 0) {
324 return -1;
325 }
326 int outlen = do_hex_dump(out, der_buf, der_len);
327 OPENSSL_free(der_buf);
328 if (outlen < 0) {
329 return -1;
330 }
331 return outlen + 1;
332 }
333
334 /*
335 * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
336 * used for non string types otherwise it is the number of bytes per
337 * character
338 */
339
340 static const signed char tag2nbyte[] = {
341 -1, -1, -1, -1, -1, /* 0-4 */
342 -1, -1, -1, -1, -1, /* 5-9 */
343 -1, -1, 0, -1, /* 10-13 */
344 -1, -1, -1, -1, /* 15-17 */
345 1, 1, 1, /* 18-20 */
346 -1, 1, 1, 1, /* 21-24 */
347 -1, 1, -1, /* 25-27 */
348 4, -1, 2 /* 28-30 */
349 };
350
351 /*
352 * This is the main function, print out an ASN1_STRING taking note of various
353 * escape and display options. Returns number of characters written or -1 if
354 * an error occurred.
355 */
356
ASN1_STRING_print_ex(BIO * out,const ASN1_STRING * str,unsigned long lflags)357 int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long lflags)
358 {
359 int outlen, len;
360 int type;
361 char quotes;
362 unsigned char flags;
363 quotes = 0;
364 /* Keep a copy of escape flags */
365 flags = (unsigned char)(lflags & ESC_FLAGS);
366
367 type = str->type;
368
369 outlen = 0;
370
371 if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
372 const char *tagname;
373 tagname = ASN1_tag2str(type);
374 outlen += strlen(tagname);
375 if (!maybe_write(out, tagname, outlen) || !maybe_write(out, ":", 1))
376 return -1;
377 outlen++;
378 }
379
380 /* Decide what to do with type, either dump content or display it */
381
382 /* Dump everything */
383 if (lflags & ASN1_STRFLGS_DUMP_ALL)
384 type = -1;
385 /* Ignore the string type */
386 else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
387 type = 1;
388 else {
389 /* Else determine width based on type */
390 if ((type > 0) && (type < 31))
391 type = tag2nbyte[type];
392 else
393 type = -1;
394 if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
395 type = 1;
396 }
397
398 if (type == -1) {
399 len = do_dump(lflags, out, str);
400 if (len < 0)
401 return -1;
402 outlen += len;
403 return outlen;
404 }
405
406 if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
407 /*
408 * Note: if string is UTF8 and we want to convert to UTF8 then we
409 * just interpret it as 1 byte per character to avoid converting
410 * twice.
411 */
412 if (!type)
413 type = 1;
414 else
415 type |= BUF_TYPE_CONVUTF8;
416 }
417
418 len = do_buf(str->data, str->length, type, flags, "es, NULL);
419 if (len < 0)
420 return -1;
421 outlen += len;
422 if (quotes)
423 outlen += 2;
424 if (!out)
425 return outlen;
426 if (quotes && !maybe_write(out, "\"", 1))
427 return -1;
428 if (do_buf(str->data, str->length, type, flags, NULL, out) < 0)
429 return -1;
430 if (quotes && !maybe_write(out, "\"", 1))
431 return -1;
432 return outlen;
433 }
434
ASN1_STRING_print_ex_fp(FILE * fp,const ASN1_STRING * str,unsigned long flags)435 int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
436 unsigned long flags)
437 {
438 BIO *bio = NULL;
439 if (fp != NULL) {
440 /* If |fp| is NULL, this function returns the number of bytes without
441 * writing. */
442 bio = BIO_new_fp(fp, BIO_NOCLOSE);
443 if (bio == NULL) {
444 return -1;
445 }
446 }
447 int ret = ASN1_STRING_print_ex(bio, str, flags);
448 BIO_free(bio);
449 return ret;
450 }
451
ASN1_STRING_to_UTF8(unsigned char ** out,const ASN1_STRING * in)452 int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
453 {
454 ASN1_STRING stmp, *str = &stmp;
455 int mbflag, type, ret;
456 if (!in)
457 return -1;
458 type = in->type;
459 if ((type < 0) || (type > 30))
460 return -1;
461 mbflag = tag2nbyte[type];
462 if (mbflag == -1)
463 return -1;
464 mbflag |= MBSTRING_FLAG;
465 stmp.data = NULL;
466 stmp.length = 0;
467 stmp.flags = 0;
468 ret = ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
469 B_ASN1_UTF8STRING);
470 if (ret < 0)
471 return ret;
472 *out = stmp.data;
473 return stmp.length;
474 }
475
ASN1_STRING_print(BIO * bp,const ASN1_STRING * v)476 int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
477 {
478 int i, n;
479 char buf[80];
480 const char *p;
481
482 if (v == NULL)
483 return (0);
484 n = 0;
485 p = (const char *)v->data;
486 for (i = 0; i < v->length; i++) {
487 if ((p[i] > '~') || ((p[i] < ' ') &&
488 (p[i] != '\n') && (p[i] != '\r')))
489 buf[n] = '.';
490 else
491 buf[n] = p[i];
492 n++;
493 if (n >= 80) {
494 if (BIO_write(bp, buf, n) <= 0)
495 return (0);
496 n = 0;
497 }
498 }
499 if (n > 0)
500 if (BIO_write(bp, buf, n) <= 0)
501 return (0);
502 return (1);
503 }
504
ASN1_TIME_print(BIO * bp,const ASN1_TIME * tm)505 int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
506 {
507 if (tm->type == V_ASN1_UTCTIME)
508 return ASN1_UTCTIME_print(bp, tm);
509 if (tm->type == V_ASN1_GENERALIZEDTIME)
510 return ASN1_GENERALIZEDTIME_print(bp, tm);
511 BIO_write(bp, "Bad time value", 14);
512 return (0);
513 }
514
515 static const char *const mon[12] = {
516 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
517 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
518 };
519
ASN1_GENERALIZEDTIME_print(BIO * bp,const ASN1_GENERALIZEDTIME * tm)520 int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
521 {
522 char *v;
523 int gmt = 0;
524 int i;
525 int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
526 char *f = NULL;
527 int f_len = 0;
528
529 i = tm->length;
530 v = (char *)tm->data;
531
532 if (i < 12)
533 goto err;
534 if (v[i - 1] == 'Z')
535 gmt = 1;
536 for (i = 0; i < 12; i++)
537 if ((v[i] > '9') || (v[i] < '0'))
538 goto err;
539 y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] -
540 '0');
541 M = (v[4] - '0') * 10 + (v[5] - '0');
542 if ((M > 12) || (M < 1))
543 goto err;
544 d = (v[6] - '0') * 10 + (v[7] - '0');
545 h = (v[8] - '0') * 10 + (v[9] - '0');
546 m = (v[10] - '0') * 10 + (v[11] - '0');
547 if (tm->length >= 14 &&
548 (v[12] >= '0') && (v[12] <= '9') &&
549 (v[13] >= '0') && (v[13] <= '9')) {
550 s = (v[12] - '0') * 10 + (v[13] - '0');
551 /* Check for fractions of seconds. */
552 if (tm->length >= 15 && v[14] == '.') {
553 int l = tm->length;
554 f = &v[14]; /* The decimal point. */
555 f_len = 1;
556 while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
557 ++f_len;
558 }
559 }
560
561 if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
562 mon[M - 1], d, h, m, s, f_len, f, y,
563 (gmt) ? " GMT" : "") <= 0)
564 return (0);
565 else
566 return (1);
567 err:
568 BIO_write(bp, "Bad time value", 14);
569 return (0);
570 }
571
572 // consume_two_digits is a helper function for ASN1_UTCTIME_print. If |*v|,
573 // assumed to be |*len| bytes long, has two leading digits, updates |*out| with
574 // their value, updates |v| and |len|, and returns one. Otherwise, returns
575 // zero.
consume_two_digits(int * out,const char ** v,int * len)576 static int consume_two_digits(int* out, const char **v, int *len) {
577 if (*len < 2|| !isdigit((*v)[0]) || !isdigit((*v)[1])) {
578 return 0;
579 }
580 *out = ((*v)[0] - '0') * 10 + ((*v)[1] - '0');
581 *len -= 2;
582 *v += 2;
583 return 1;
584 }
585
586 // consume_zulu_timezone is a helper function for ASN1_UTCTIME_print. If |*v|,
587 // assumed to be |*len| bytes long, starts with "Z" then it updates |*v| and
588 // |*len| and returns one. Otherwise returns zero.
consume_zulu_timezone(const char ** v,int * len)589 static int consume_zulu_timezone(const char **v, int *len) {
590 if (*len == 0 || (*v)[0] != 'Z') {
591 return 0;
592 }
593
594 *len -= 1;
595 *v += 1;
596 return 1;
597 }
598
ASN1_UTCTIME_print(BIO * bp,const ASN1_UTCTIME * tm)599 int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm) {
600 const char *v = (const char *)tm->data;
601 int len = tm->length;
602 int Y = 0, M = 0, D = 0, h = 0, m = 0, s = 0;
603
604 // YYMMDDhhmm are required to be present.
605 if (!consume_two_digits(&Y, &v, &len) ||
606 !consume_two_digits(&M, &v, &len) ||
607 !consume_two_digits(&D, &v, &len) ||
608 !consume_two_digits(&h, &v, &len) ||
609 !consume_two_digits(&m, &v, &len)) {
610 goto err;
611 }
612 // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires seconds
613 // to be present, but historically this code has forgiven its absence.
614 consume_two_digits(&s, &v, &len);
615
616 // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, specifies this
617 // interpretation of the year.
618 if (Y < 50) {
619 Y += 2000;
620 } else {
621 Y += 1900;
622 }
623 if (M > 12 || M == 0) {
624 goto err;
625 }
626 if (D > 31 || D == 0) {
627 goto err;
628 }
629 if (h > 23 || m > 59 || s > 60) {
630 goto err;
631 }
632
633 // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires the "Z"
634 // to be present, but historically this code has forgiven its absence.
635 const int is_gmt = consume_zulu_timezone(&v, &len);
636
637 // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, does not permit
638 // the specification of timezones using the +hhmm / -hhmm syntax, which is
639 // the only other thing that might legitimately be found at the end.
640 if (len) {
641 goto err;
642 }
643
644 return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", mon[M - 1], D, h, m, s, Y,
645 is_gmt ? " GMT" : "") > 0;
646
647 err:
648 BIO_write(bp, "Bad time value", 14);
649 return 0;
650 }
651