• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &quotes, 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