Annotation of embedaddon/axTLS/ssl/asn1.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2007, Cameron Rich
        !             3:  * 
        !             4:  * All rights reserved.
        !             5:  * 
        !             6:  * Redistribution and use in source and binary forms, with or without 
        !             7:  * modification, are permitted provided that the following conditions are met:
        !             8:  *
        !             9:  * * Redistributions of source code must retain the above copyright notice, 
        !            10:  *   this list of conditions and the following disclaimer.
        !            11:  * * Redistributions in binary form must reproduce the above copyright notice, 
        !            12:  *   this list of conditions and the following disclaimer in the documentation 
        !            13:  *   and/or other materials provided with the distribution.
        !            14:  * * Neither the name of the axTLS project nor the names of its contributors 
        !            15:  *   may be used to endorse or promote products derived from this software 
        !            16:  *   without specific prior written permission.
        !            17:  *
        !            18:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
        !            19:  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
        !            20:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
        !            21:  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
        !            22:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
        !            23:  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
        !            24:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
        !            25:  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
        !            26:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
        !            27:  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
        !            28:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            29:  */
        !            30: 
        !            31: /**
        !            32:  * Some primitive asn methods for extraction ASN.1 data.
        !            33:  */
        !            34: 
        !            35: #include <stdio.h>
        !            36: #include <stdlib.h>
        !            37: #include <string.h>
        !            38: #include <time.h>
        !            39: #include "os_port.h"
        !            40: #include "crypto.h"
        !            41: #include "crypto_misc.h"
        !            42: 
        !            43: #define SIG_OID_PREFIX_SIZE 8
        !            44: #define SIG_IIS6_OID_SIZE   5
        !            45: #define SIG_SUBJECT_ALT_NAME_SIZE 3
        !            46: 
        !            47: /* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */
        !            48: static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] = 
        !            49: {
        !            50:     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
        !            51: };
        !            52: 
        !            53: static const uint8_t sig_sha1WithRSAEncrypt[SIG_IIS6_OID_SIZE] =
        !            54: {
        !            55:     0x2b, 0x0e, 0x03, 0x02, 0x1d
        !            56: };
        !            57: 
        !            58: static const uint8_t sig_subject_alt_name[SIG_SUBJECT_ALT_NAME_SIZE] =
        !            59: {
        !            60:     0x55, 0x1d, 0x11
        !            61: };
        !            62: 
        !            63: /* CN, O, OU */
        !            64: static const uint8_t g_dn_types[] = { 3, 10, 11 };
        !            65: 
        !            66: int get_asn1_length(const uint8_t *buf, int *offset)
        !            67: {
        !            68:     int len, i;
        !            69: 
        !            70:     if (!(buf[*offset] & 0x80)) /* short form */
        !            71:     {
        !            72:         len = buf[(*offset)++];
        !            73:     }
        !            74:     else  /* long form */
        !            75:     {
        !            76:         int length_bytes = buf[(*offset)++]&0x7f;
        !            77:         len = 0;
        !            78:         for (i = 0; i < length_bytes; i++)
        !            79:         {
        !            80:             len <<= 8;
        !            81:             len += buf[(*offset)++];
        !            82:         }
        !            83:     }
        !            84: 
        !            85:     return len;
        !            86: }
        !            87: 
        !            88: /**
        !            89:  * Skip the ASN1.1 object type and its length. Get ready to read the object's
        !            90:  * data.
        !            91:  */
        !            92: int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type)
        !            93: {
        !            94:     if (buf[*offset] != obj_type)
        !            95:         return X509_NOT_OK;
        !            96:     (*offset)++;
        !            97:     return get_asn1_length(buf, offset);
        !            98: }
        !            99: 
        !           100: /**
        !           101:  * Skip over an ASN.1 object type completely. Get ready to read the next
        !           102:  * object.
        !           103:  */
        !           104: int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type)
        !           105: {
        !           106:     int len;
        !           107: 
        !           108:     if (buf[*offset] != obj_type)
        !           109:         return X509_NOT_OK;
        !           110:     (*offset)++;
        !           111:     len = get_asn1_length(buf, offset);
        !           112:     *offset += len;
        !           113:     return 0;
        !           114: }
        !           115: 
        !           116: /**
        !           117:  * Read an integer value for ASN.1 data
        !           118:  * Note: This function allocates memory which must be freed by the user.
        !           119:  */
        !           120: int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object)
        !           121: {
        !           122:     int len;
        !           123: 
        !           124:     if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0)
        !           125:         goto end_int_array;
        !           126: 
        !           127:     if (len > 1 && buf[*offset] == 0x00)    /* ignore the negative byte */
        !           128:     {
        !           129:         len--;
        !           130:         (*offset)++;
        !           131:     }
        !           132: 
        !           133:     *object = (uint8_t *)malloc(len);
        !           134:     memcpy(*object, &buf[*offset], len);
        !           135:     *offset += len;
        !           136: 
        !           137: end_int_array:
        !           138:     return len;
        !           139: }
        !           140: 
        !           141: /**
        !           142:  * Get all the RSA private key specifics from an ASN.1 encoded file 
        !           143:  */
        !           144: int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx)
        !           145: {
        !           146:     int offset = 7;
        !           147:     uint8_t *modulus = NULL, *priv_exp = NULL, *pub_exp = NULL;
        !           148:     int mod_len, priv_len, pub_len;
        !           149: #ifdef CONFIG_BIGINT_CRT
        !           150:     uint8_t *p = NULL, *q = NULL, *dP = NULL, *dQ = NULL, *qInv = NULL;
        !           151:     int p_len, q_len, dP_len, dQ_len, qInv_len;
        !           152: #endif
        !           153: 
        !           154:     /* not in der format */
        !           155:     if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */
        !           156:     {
        !           157: #ifdef CONFIG_SSL_FULL_MODE
        !           158:         printf("Error: This is not a valid ASN.1 file\n");
        !           159: #endif
        !           160:         return X509_INVALID_PRIV_KEY;
        !           161:     }
        !           162: 
        !           163:     /* Use the private key to mix up the RNG if possible. */
        !           164:     RNG_custom_init(buf, len);
        !           165: 
        !           166:     mod_len = asn1_get_int(buf, &offset, &modulus);
        !           167:     pub_len = asn1_get_int(buf, &offset, &pub_exp);
        !           168:     priv_len = asn1_get_int(buf, &offset, &priv_exp);
        !           169: 
        !           170:     if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0)
        !           171:         return X509_INVALID_PRIV_KEY;
        !           172: 
        !           173: #ifdef CONFIG_BIGINT_CRT
        !           174:     p_len = asn1_get_int(buf, &offset, &p);
        !           175:     q_len = asn1_get_int(buf, &offset, &q);
        !           176:     dP_len = asn1_get_int(buf, &offset, &dP);
        !           177:     dQ_len = asn1_get_int(buf, &offset, &dQ);
        !           178:     qInv_len = asn1_get_int(buf, &offset, &qInv);
        !           179: 
        !           180:     if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0)
        !           181:         return X509_INVALID_PRIV_KEY;
        !           182: 
        !           183:     RSA_priv_key_new(rsa_ctx, 
        !           184:             modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len,
        !           185:             p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len);
        !           186: 
        !           187:     free(p);
        !           188:     free(q);
        !           189:     free(dP);
        !           190:     free(dQ);
        !           191:     free(qInv);
        !           192: #else
        !           193:     RSA_priv_key_new(rsa_ctx, 
        !           194:             modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len);
        !           195: #endif
        !           196: 
        !           197:     free(modulus);
        !           198:     free(priv_exp);
        !           199:     free(pub_exp);
        !           200:     return X509_OK;
        !           201: }
        !           202: 
        !           203: /**
        !           204:  * Get the time of a certificate. Ignore hours/minutes/seconds.
        !           205:  */
        !           206: static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t)
        !           207: {
        !           208:     int ret = X509_NOT_OK, len, t_offset;
        !           209:     struct tm tm;
        !           210: 
        !           211:     if (buf[(*offset)++] != ASN1_UTC_TIME)
        !           212:         goto end_utc_time;
        !           213: 
        !           214:     len = get_asn1_length(buf, offset);
        !           215:     t_offset = *offset;
        !           216: 
        !           217:     memset(&tm, 0, sizeof(struct tm));
        !           218:     tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0');
        !           219: 
        !           220:     if (tm.tm_year <= 50)    /* 1951-2050 thing */
        !           221:     {
        !           222:         tm.tm_year += 100;
        !           223:     }
        !           224: 
        !           225:     tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1;
        !           226:     tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0');
        !           227:     *t = mktime(&tm);
        !           228:     *offset += len;
        !           229:     ret = X509_OK;
        !           230: 
        !           231: end_utc_time:
        !           232:     return ret;
        !           233: }
        !           234: 
        !           235: /**
        !           236:  * Get the version type of a certificate (which we don't actually care about)
        !           237:  */
        !           238: int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
        !           239: {
        !           240:     int ret = X509_NOT_OK;
        !           241: 
        !           242:     (*offset) += 2;        /* get past explicit tag */
        !           243:     if (asn1_skip_obj(cert, offset, ASN1_INTEGER))
        !           244:         goto end_version;
        !           245: 
        !           246:     ret = X509_OK;
        !           247: end_version:
        !           248:     return ret;
        !           249: }
        !           250: 
        !           251: /**
        !           252:  * Retrieve the notbefore and notafter certificate times.
        !           253:  */
        !           254: int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
        !           255: {
        !           256:     return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
        !           257:               asn1_get_utc_time(cert, offset, &x509_ctx->not_before) ||
        !           258:               asn1_get_utc_time(cert, offset, &x509_ctx->not_after));
        !           259: }
        !           260: 
        !           261: /**
        !           262:  * Get the components of a distinguished name 
        !           263:  */
        !           264: static int asn1_get_oid_x520(const uint8_t *buf, int *offset)
        !           265: {
        !           266:     int dn_type = 0;
        !           267:     int len;
        !           268: 
        !           269:     if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
        !           270:         goto end_oid;
        !           271: 
        !           272:     /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name 
        !           273:        components we are interested in. */
        !           274:     if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04)
        !           275:         dn_type = buf[(*offset)++];
        !           276:     else
        !           277:     {
        !           278:         *offset += len;     /* skip over it */
        !           279:     }
        !           280: 
        !           281: end_oid:
        !           282:     return dn_type;
        !           283: }
        !           284: 
        !           285: /**
        !           286:  * Obtain an ASN.1 printable string type.
        !           287:  */
        !           288: static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str)
        !           289: {
        !           290:     int len = X509_NOT_OK;
        !           291:     int asn1_type = buf[*offset];
        !           292: 
        !           293:     /* some certs have this awful crud in them for some reason */
        !           294:     if (asn1_type != ASN1_PRINTABLE_STR &&  
        !           295:             asn1_type != ASN1_PRINTABLE_STR2 &&  
        !           296:             asn1_type != ASN1_TELETEX_STR &&  
        !           297:             asn1_type != ASN1_IA5_STR &&  
        !           298:             asn1_type != ASN1_UNICODE_STR)
        !           299:         goto end_pnt_str;
        !           300: 
        !           301:     (*offset)++;
        !           302:     len = get_asn1_length(buf, offset);
        !           303: 
        !           304:     if (asn1_type == ASN1_UNICODE_STR)
        !           305:     {
        !           306:         int i;
        !           307:         *str = (char *)malloc(len/2+1);     /* allow for null */
        !           308: 
        !           309:         for (i = 0; i < len; i += 2)
        !           310:             (*str)[i/2] = buf[*offset + i + 1];
        !           311: 
        !           312:         (*str)[len/2] = 0;                  /* null terminate */
        !           313:     }
        !           314:     else
        !           315:     {
        !           316:         *str = (char *)malloc(len+1);       /* allow for null */
        !           317:         memcpy(*str, &buf[*offset], len);
        !           318:         (*str)[len] = 0;                    /* null terminate */
        !           319:     }
        !           320: 
        !           321:     *offset += len;
        !           322: 
        !           323: end_pnt_str:
        !           324:     return len;
        !           325: }
        !           326: 
        !           327: /**
        !           328:  * Get the subject name (or the issuer) of a certificate.
        !           329:  */
        !           330: int asn1_name(const uint8_t *cert, int *offset, char *dn[])
        !           331: {
        !           332:     int ret = X509_NOT_OK;
        !           333:     int dn_type;
        !           334:     char *tmp;
        !           335: 
        !           336:     if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
        !           337:         goto end_name;
        !           338: 
        !           339:     while (asn1_next_obj(cert, offset, ASN1_SET) >= 0)
        !           340:     {
        !           341:         int i, found = 0;
        !           342: 
        !           343:         if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
        !           344:                (dn_type = asn1_get_oid_x520(cert, offset)) < 0)
        !           345:             goto end_name;
        !           346: 
        !           347:         tmp = NULL;
        !           348: 
        !           349:         if (asn1_get_printable_str(cert, offset, &tmp) < 0)
        !           350:         {
        !           351:             free(tmp);
        !           352:             goto end_name;
        !           353:         }
        !           354: 
        !           355:         /* find the distinguished named type */
        !           356:         for (i = 0; i < X509_NUM_DN_TYPES; i++)
        !           357:         {
        !           358:             if (dn_type == g_dn_types[i])
        !           359:             {
        !           360:                 if (dn[i] == NULL)
        !           361:                 {
        !           362:                     dn[i] = tmp;
        !           363:                     found = 1;
        !           364:                     break;
        !           365:                 }
        !           366:             }
        !           367:         }
        !           368: 
        !           369:         if (found == 0) /* not found so get rid of it */
        !           370:         {
        !           371:             free(tmp);
        !           372:         }
        !           373:     }
        !           374: 
        !           375:     ret = X509_OK;
        !           376: end_name:
        !           377:     return ret;
        !           378: }
        !           379: 
        !           380: /**
        !           381:  * Read the modulus and public exponent of a certificate.
        !           382:  */
        !           383: int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
        !           384: {
        !           385:     int ret = X509_NOT_OK, mod_len, pub_len;
        !           386:     uint8_t *modulus = NULL, *pub_exp = NULL;
        !           387: 
        !           388:     if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
        !           389:             asn1_skip_obj(cert, offset, ASN1_SEQUENCE) ||
        !           390:             asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0)
        !           391:         goto end_pub_key;
        !           392: 
        !           393:     (*offset)++;        /* ignore the padding bit field */
        !           394: 
        !           395:     if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
        !           396:         goto end_pub_key;
        !           397: 
        !           398:     mod_len = asn1_get_int(cert, offset, &modulus);
        !           399:     pub_len = asn1_get_int(cert, offset, &pub_exp);
        !           400: 
        !           401:     RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len);
        !           402: 
        !           403:     free(modulus);
        !           404:     free(pub_exp);
        !           405:     ret = X509_OK;
        !           406: 
        !           407: end_pub_key:
        !           408:     return ret;
        !           409: }
        !           410: 
        !           411: #ifdef CONFIG_SSL_CERT_VERIFICATION
        !           412: /**
        !           413:  * Read the signature of the certificate.
        !           414:  */
        !           415: int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
        !           416: {
        !           417:     int ret = X509_NOT_OK;
        !           418: 
        !           419:     if (cert[(*offset)++] != ASN1_BIT_STRING)
        !           420:         goto end_sig;
        !           421: 
        !           422:     x509_ctx->sig_len = get_asn1_length(cert, offset)-1;
        !           423:     (*offset)++;            /* ignore bit string padding bits */
        !           424:     x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len);
        !           425:     memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len);
        !           426:     *offset += x509_ctx->sig_len;
        !           427:     ret = X509_OK;
        !           428: 
        !           429: end_sig:
        !           430:     return ret;
        !           431: }
        !           432: 
        !           433: /*
        !           434:  * Compare 2 distinguished name components for equality 
        !           435:  * @return 0 if a match
        !           436:  */
        !           437: static int asn1_compare_dn_comp(const char *dn1, const char *dn2)
        !           438: {
        !           439:     int ret;
        !           440: 
        !           441:     if (dn1 == NULL && dn2 == NULL)
        !           442:         ret = 0;
        !           443:     else
        !           444:         ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 1;
        !           445: 
        !           446:     return ret;
        !           447: }
        !           448: 
        !           449: /**
        !           450:  * Clean up all of the CA certificates.
        !           451:  */
        !           452: void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx)
        !           453: {
        !           454:     int i = 0;
        !           455: 
        !           456:     if (ca_cert_ctx == NULL)
        !           457:         return;
        !           458: 
        !           459:     while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
        !           460:     {
        !           461:         x509_free(ca_cert_ctx->cert[i]);
        !           462:         ca_cert_ctx->cert[i++] = NULL;
        !           463:     }
        !           464: 
        !           465:     free(ca_cert_ctx);
        !           466: }
        !           467: 
        !           468: /*
        !           469:  * Compare 2 distinguished names for equality 
        !           470:  * @return 0 if a match
        !           471:  */
        !           472: int asn1_compare_dn(char * const dn1[], char * const dn2[])
        !           473: {
        !           474:     int i;
        !           475: 
        !           476:     for (i = 0; i < X509_NUM_DN_TYPES; i++)
        !           477:     {
        !           478:         if (asn1_compare_dn_comp(dn1[i], dn2[i]))
        !           479:             return 1;
        !           480:     }
        !           481: 
        !           482:     return 0;       /* all good */
        !           483: }
        !           484: 
        !           485: int asn1_find_oid(const uint8_t* cert, int* offset, 
        !           486:                     const uint8_t* oid, int oid_length)
        !           487: {
        !           488:     int seqlen;
        !           489:     if ((seqlen = asn1_next_obj(cert, offset, ASN1_SEQUENCE))> 0)
        !           490:     {
        !           491:         int end = *offset + seqlen;
        !           492: 
        !           493:         while (*offset < end)
        !           494:         {
        !           495:             int type = cert[(*offset)++];
        !           496:             int length = get_asn1_length(cert, offset);
        !           497:             int noffset = *offset + length;
        !           498: 
        !           499:             if (type == ASN1_SEQUENCE)
        !           500:             {
        !           501:                 type = cert[(*offset)++];
        !           502:                 length = get_asn1_length(cert, offset);
        !           503: 
        !           504:                 if (type == ASN1_OID && length == oid_length && 
        !           505:                               memcmp(cert + *offset, oid, oid_length) == 0)
        !           506:                 {
        !           507:                     *offset += oid_length;
        !           508:                     return 1;
        !           509:                 }
        !           510:             }
        !           511: 
        !           512:             *offset = noffset;
        !           513:         }
        !           514:     }
        !           515: 
        !           516:     return 0;
        !           517: }
        !           518: 
        !           519: int asn1_find_subjectaltname(const uint8_t* cert, int offset)
        !           520: {
        !           521:     if (asn1_find_oid(cert, &offset, sig_subject_alt_name, 
        !           522:                                 SIG_SUBJECT_ALT_NAME_SIZE))
        !           523:     {
        !           524:         return offset;
        !           525:     }
        !           526: 
        !           527:     return 0;
        !           528: }
        !           529: 
        !           530: #endif /* CONFIG_SSL_CERT_VERIFICATION */
        !           531: 
        !           532: /**
        !           533:  * Read the signature type of the certificate. We only support RSA-MD5 and
        !           534:  * RSA-SHA1 signature types.
        !           535:  */
        !           536: int asn1_signature_type(const uint8_t *cert, 
        !           537:                                 int *offset, X509_CTX *x509_ctx)
        !           538: {
        !           539:     int ret = X509_NOT_OK, len;
        !           540: 
        !           541:     if (cert[(*offset)++] != ASN1_OID)
        !           542:         goto end_check_sig;
        !           543: 
        !           544:     len = get_asn1_length(cert, offset);
        !           545: 
        !           546:     if (len == 5 && memcmp(sig_sha1WithRSAEncrypt, &cert[*offset], 
        !           547:                                     SIG_IIS6_OID_SIZE) == 0)
        !           548:     {
        !           549:         x509_ctx->sig_type = SIG_TYPE_SHA1;
        !           550:     }
        !           551:     else
        !           552:     {
        !           553:         if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE))
        !           554:             goto end_check_sig;     /* unrecognised cert type */
        !           555: 
        !           556:         x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE];
        !           557:     }
        !           558: 
        !           559:     *offset += len;
        !           560:     asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */
        !           561:     ret = X509_OK;
        !           562: 
        !           563: end_check_sig:
        !           564:     return ret;
        !           565: }
        !           566: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>