[BACK]Return to ts.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / openssl

Annotation of src/usr.bin/openssl/ts.c, Revision 1.1

1.1     ! jsing       1: /* $OpenBSD: ts.c,v 1.18 2014/07/12 17:54:31 jsing Exp $ */
        !             2: /* Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL
        !             3:  * project 2002.
        !             4:  */
        !             5: /* ====================================================================
        !             6:  * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  *
        !            12:  * 1. Redistributions of source code must retain the above copyright
        !            13:  *    notice, this list of conditions and the following disclaimer.
        !            14:  *
        !            15:  * 2. Redistributions in binary form must reproduce the above copyright
        !            16:  *    notice, this list of conditions and the following disclaimer in
        !            17:  *    the documentation and/or other materials provided with the
        !            18:  *    distribution.
        !            19:  *
        !            20:  * 3. All advertising materials mentioning features or use of this
        !            21:  *    software must display the following acknowledgment:
        !            22:  *    "This product includes software developed by the OpenSSL Project
        !            23:  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
        !            24:  *
        !            25:  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
        !            26:  *    endorse or promote products derived from this software without
        !            27:  *    prior written permission. For written permission, please contact
        !            28:  *    licensing@OpenSSL.org.
        !            29:  *
        !            30:  * 5. Products derived from this software may not be called "OpenSSL"
        !            31:  *    nor may "OpenSSL" appear in their names without prior written
        !            32:  *    permission of the OpenSSL Project.
        !            33:  *
        !            34:  * 6. Redistributions of any form whatsoever must retain the following
        !            35:  *    acknowledgment:
        !            36:  *    "This product includes software developed by the OpenSSL Project
        !            37:  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
        !            38:  *
        !            39:  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
        !            40:  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            41:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        !            42:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
        !            43:  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
        !            44:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
        !            45:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
        !            46:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            47:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
        !            48:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        !            49:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
        !            50:  * OF THE POSSIBILITY OF SUCH DAMAGE.
        !            51:  * ====================================================================
        !            52:  *
        !            53:  * This product includes cryptographic software written by Eric Young
        !            54:  * (eay@cryptsoft.com).  This product includes software written by Tim
        !            55:  * Hudson (tjh@cryptsoft.com).
        !            56:  *
        !            57:  */
        !            58:
        !            59: #include <stdio.h>
        !            60: #include <stdlib.h>
        !            61: #include <string.h>
        !            62:
        !            63: #include "apps.h"
        !            64:
        !            65: #include <openssl/bio.h>
        !            66: #include <openssl/bn.h>
        !            67: #include <openssl/err.h>
        !            68: #include <openssl/pem.h>
        !            69: #include <openssl/rand.h>
        !            70: #include <openssl/ts.h>
        !            71:
        !            72: /* Length of the nonce of the request in bits (must be a multiple of 8). */
        !            73: #define        NONCE_LENGTH            64
        !            74:
        !            75: /* Macro definitions for the configuration file. */
        !            76: #define        ENV_OID_FILE            "oid_file"
        !            77:
        !            78: /* Local function declarations. */
        !            79:
        !            80: static ASN1_OBJECT *txt2obj(const char *oid);
        !            81: static CONF *load_config_file(const char *configfile);
        !            82:
        !            83: /* Query related functions. */
        !            84: static int query_command(const char *data, char *digest,
        !            85:     const EVP_MD * md, const char *policy, int no_nonce,
        !            86:     int cert, const char *in, const char *out, int text);
        !            87: static BIO *BIO_open_with_default(const char *file, const char *mode,
        !            88:     FILE * default_fp);
        !            89: static TS_REQ *create_query(BIO * data_bio, char *digest, const EVP_MD * md,
        !            90:     const char *policy, int no_nonce, int cert);
        !            91: static int create_digest(BIO * input, char *digest,
        !            92:     const EVP_MD * md, unsigned char **md_value);
        !            93: static ASN1_INTEGER *create_nonce(int bits);
        !            94:
        !            95: /* Reply related functions. */
        !            96: static int reply_command(CONF * conf, char *section, char *engine,
        !            97:     char *queryfile, char *passin, char *inkey,
        !            98:     char *signer, char *chain, const char *policy,
        !            99:     char *in, int token_in, char *out, int token_out,
        !           100:     int text);
        !           101: static TS_RESP *read_PKCS7(BIO * in_bio);
        !           102: static TS_RESP *create_response(CONF * conf, const char *section, char *engine,
        !           103:     char *queryfile, char *passin, char *inkey,
        !           104:     char *signer, char *chain, const char *policy);
        !           105: static ASN1_INTEGER *serial_cb(TS_RESP_CTX * ctx, void *data);
        !           106: static ASN1_INTEGER *next_serial(const char *serialfile);
        !           107: static int save_ts_serial(const char *serialfile, ASN1_INTEGER * serial);
        !           108:
        !           109: /* Verify related functions. */
        !           110: static int verify_command(char *data, char *digest, char *queryfile,
        !           111:     char *in, int token_in,
        !           112:     char *ca_path, char *ca_file, char *untrusted);
        !           113: static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest,
        !           114:     char *queryfile,
        !           115:     char *ca_path, char *ca_file,
        !           116:     char *untrusted);
        !           117: static X509_STORE *create_cert_store(char *ca_path, char *ca_file);
        !           118: static int verify_cb(int ok, X509_STORE_CTX * ctx);
        !           119:
        !           120: /* Main function definition. */
        !           121: int ts_main(int, char **);
        !           122:
        !           123: int
        !           124: ts_main(int argc, char **argv)
        !           125: {
        !           126:        int ret = 1;
        !           127:        char *configfile = NULL;
        !           128:        char *section = NULL;
        !           129:        CONF *conf = NULL;
        !           130:        enum mode {
        !           131:                CMD_NONE, CMD_QUERY, CMD_REPLY, CMD_VERIFY
        !           132:        } mode = CMD_NONE;
        !           133:        char *data = NULL;
        !           134:        char *digest = NULL;
        !           135:        const EVP_MD *md = NULL;
        !           136:        char *policy = NULL;
        !           137:        int no_nonce = 0;
        !           138:        int cert = 0;
        !           139:        char *in = NULL;
        !           140:        char *out = NULL;
        !           141:        int text = 0;
        !           142:        char *queryfile = NULL;
        !           143:        char *passin = NULL;    /* Password source. */
        !           144:        char *password = NULL;  /* Password itself. */
        !           145:        char *inkey = NULL;
        !           146:        char *signer = NULL;
        !           147:        char *chain = NULL;
        !           148:        char *ca_path = NULL;
        !           149:        char *ca_file = NULL;
        !           150:        char *untrusted = NULL;
        !           151:        char *engine = NULL;
        !           152:        /* Input is ContentInfo instead of TimeStampResp. */
        !           153:        int token_in = 0;
        !           154:        /* Output is ContentInfo instead of TimeStampResp. */
        !           155:        int token_out = 0;
        !           156:
        !           157:        ERR_load_crypto_strings();
        !           158:
        !           159:        for (argc--, argv++; argc > 0; argc--, argv++) {
        !           160:                if (strcmp(*argv, "-config") == 0) {
        !           161:                        if (argc-- < 1)
        !           162:                                goto usage;
        !           163:                        configfile = *++argv;
        !           164:                } else if (strcmp(*argv, "-section") == 0) {
        !           165:                        if (argc-- < 1)
        !           166:                                goto usage;
        !           167:                        section = *++argv;
        !           168:                } else if (strcmp(*argv, "-query") == 0) {
        !           169:                        if (mode != CMD_NONE)
        !           170:                                goto usage;
        !           171:                        mode = CMD_QUERY;
        !           172:                } else if (strcmp(*argv, "-data") == 0) {
        !           173:                        if (argc-- < 1)
        !           174:                                goto usage;
        !           175:                        data = *++argv;
        !           176:                } else if (strcmp(*argv, "-digest") == 0) {
        !           177:                        if (argc-- < 1)
        !           178:                                goto usage;
        !           179:                        digest = *++argv;
        !           180:                } else if (strcmp(*argv, "-policy") == 0) {
        !           181:                        if (argc-- < 1)
        !           182:                                goto usage;
        !           183:                        policy = *++argv;
        !           184:                } else if (strcmp(*argv, "-no_nonce") == 0) {
        !           185:                        no_nonce = 1;
        !           186:                } else if (strcmp(*argv, "-cert") == 0) {
        !           187:                        cert = 1;
        !           188:                } else if (strcmp(*argv, "-in") == 0) {
        !           189:                        if (argc-- < 1)
        !           190:                                goto usage;
        !           191:                        in = *++argv;
        !           192:                } else if (strcmp(*argv, "-token_in") == 0) {
        !           193:                        token_in = 1;
        !           194:                } else if (strcmp(*argv, "-out") == 0) {
        !           195:                        if (argc-- < 1)
        !           196:                                goto usage;
        !           197:                        out = *++argv;
        !           198:                } else if (strcmp(*argv, "-token_out") == 0) {
        !           199:                        token_out = 1;
        !           200:                } else if (strcmp(*argv, "-text") == 0) {
        !           201:                        text = 1;
        !           202:                } else if (strcmp(*argv, "-reply") == 0) {
        !           203:                        if (mode != CMD_NONE)
        !           204:                                goto usage;
        !           205:                        mode = CMD_REPLY;
        !           206:                } else if (strcmp(*argv, "-queryfile") == 0) {
        !           207:                        if (argc-- < 1)
        !           208:                                goto usage;
        !           209:                        queryfile = *++argv;
        !           210:                } else if (strcmp(*argv, "-passin") == 0) {
        !           211:                        if (argc-- < 1)
        !           212:                                goto usage;
        !           213:                        passin = *++argv;
        !           214:                } else if (strcmp(*argv, "-inkey") == 0) {
        !           215:                        if (argc-- < 1)
        !           216:                                goto usage;
        !           217:                        inkey = *++argv;
        !           218:                } else if (strcmp(*argv, "-signer") == 0) {
        !           219:                        if (argc-- < 1)
        !           220:                                goto usage;
        !           221:                        signer = *++argv;
        !           222:                } else if (strcmp(*argv, "-chain") == 0) {
        !           223:                        if (argc-- < 1)
        !           224:                                goto usage;
        !           225:                        chain = *++argv;
        !           226:                } else if (strcmp(*argv, "-verify") == 0) {
        !           227:                        if (mode != CMD_NONE)
        !           228:                                goto usage;
        !           229:                        mode = CMD_VERIFY;
        !           230:                } else if (strcmp(*argv, "-CApath") == 0) {
        !           231:                        if (argc-- < 1)
        !           232:                                goto usage;
        !           233:                        ca_path = *++argv;
        !           234:                } else if (strcmp(*argv, "-CAfile") == 0) {
        !           235:                        if (argc-- < 1)
        !           236:                                goto usage;
        !           237:                        ca_file = *++argv;
        !           238:                } else if (strcmp(*argv, "-untrusted") == 0) {
        !           239:                        if (argc-- < 1)
        !           240:                                goto usage;
        !           241:                        untrusted = *++argv;
        !           242:                } else if (strcmp(*argv, "-engine") == 0) {
        !           243:                        if (argc-- < 1)
        !           244:                                goto usage;
        !           245:                        engine = *++argv;
        !           246:                } else if ((md = EVP_get_digestbyname(*argv + 1)) != NULL) {
        !           247:                        /* empty. */
        !           248:                } else
        !           249:                        goto usage;
        !           250:        }
        !           251:
        !           252:        /* Get the password if required. */
        !           253:        if (mode == CMD_REPLY && passin &&
        !           254:            !app_passwd(bio_err, passin, NULL, &password, NULL)) {
        !           255:                BIO_printf(bio_err, "Error getting password.\n");
        !           256:                goto cleanup;
        !           257:        }
        !           258:        /*
        !           259:         * Check consistency of parameters and execute the appropriate
        !           260:         * function.
        !           261:         */
        !           262:        switch (mode) {
        !           263:        case CMD_NONE:
        !           264:                goto usage;
        !           265:        case CMD_QUERY:
        !           266:                /*
        !           267:                 * Data file and message imprint cannot be specified at the
        !           268:                 * same time.
        !           269:                 */
        !           270:                ret = data != NULL && digest != NULL;
        !           271:                if (ret)
        !           272:                        goto usage;
        !           273:                /* Load the config file for possible policy OIDs. */
        !           274:                conf = load_config_file(configfile);
        !           275:                ret = !query_command(data, digest, md, policy, no_nonce, cert,
        !           276:                    in, out, text);
        !           277:                break;
        !           278:        case CMD_REPLY:
        !           279:                conf = load_config_file(configfile);
        !           280:                if (in == NULL) {
        !           281:                        ret = !(queryfile != NULL && conf != NULL && !token_in);
        !           282:                        if (ret)
        !           283:                                goto usage;
        !           284:                } else {
        !           285:                        /* 'in' and 'queryfile' are exclusive. */
        !           286:                        ret = !(queryfile == NULL);
        !           287:                        if (ret)
        !           288:                                goto usage;
        !           289:                }
        !           290:
        !           291:                ret = !reply_command(conf, section, engine, queryfile,
        !           292:                    password, inkey, signer, chain, policy,
        !           293:                    in, token_in, out, token_out, text);
        !           294:                break;
        !           295:        case CMD_VERIFY:
        !           296:                ret = !(((queryfile && !data && !digest) ||
        !           297:                    (!queryfile && data && !digest) ||
        !           298:                    (!queryfile && !data && digest)) && in != NULL);
        !           299:                if (ret)
        !           300:                        goto usage;
        !           301:
        !           302:                ret = !verify_command(data, digest, queryfile, in, token_in,
        !           303:                    ca_path, ca_file, untrusted);
        !           304:        }
        !           305:
        !           306:        goto cleanup;
        !           307:
        !           308: usage:
        !           309:        BIO_printf(bio_err, "usage:\n"
        !           310:            "ts -query [-config configfile] "
        !           311:            "[-data file_to_hash] [-digest digest_bytes]"
        !           312:            "[-md2|-md4|-md5|-sha|-sha1|-mdc2|-ripemd160] "
        !           313:            "[-policy object_id] [-no_nonce] [-cert] "
        !           314:            "[-in request.tsq] [-out request.tsq] [-text]\n");
        !           315:        BIO_printf(bio_err, "or\n"
        !           316:            "ts -reply [-config configfile] [-section tsa_section] "
        !           317:            "[-queryfile request.tsq] [-passin password] "
        !           318:            "[-signer tsa_cert.pem] [-inkey private_key.pem] "
        !           319:            "[-chain certs_file.pem] [-policy object_id] "
        !           320:            "[-in response.tsr] [-token_in] "
        !           321:            "[-out response.tsr] [-token_out] [-text] [-engine id]\n");
        !           322:        BIO_printf(bio_err, "or\n"
        !           323:            "ts -verify [-data file_to_hash] [-digest digest_bytes] "
        !           324:            "[-queryfile request.tsq] "
        !           325:            "-in response.tsr [-token_in] "
        !           326:            "-CApath ca_path -CAfile ca_file.pem "
        !           327:            "-untrusted cert_file.pem\n");
        !           328:
        !           329: cleanup:
        !           330:        /* Clean up. */
        !           331:        NCONF_free(conf);
        !           332:        free(password);
        !           333:        OBJ_cleanup();
        !           334:
        !           335:        return (ret);
        !           336: }
        !           337:
        !           338: /*
        !           339:  * Configuration file-related function definitions.
        !           340:  */
        !           341:
        !           342: static ASN1_OBJECT *
        !           343: txt2obj(const char *oid)
        !           344: {
        !           345:        ASN1_OBJECT *oid_obj = NULL;
        !           346:
        !           347:        if (!(oid_obj = OBJ_txt2obj(oid, 0)))
        !           348:                BIO_printf(bio_err, "cannot convert %s to OID\n", oid);
        !           349:
        !           350:        return oid_obj;
        !           351: }
        !           352:
        !           353: static CONF *
        !           354: load_config_file(const char *configfile)
        !           355: {
        !           356:        CONF *conf = NULL;
        !           357:        long errorline = -1;
        !           358:
        !           359:        if (!configfile)
        !           360:                configfile = getenv("OPENSSL_CONF");
        !           361:        if (!configfile)
        !           362:                configfile = getenv("SSLEAY_CONF");
        !           363:
        !           364:        if (configfile &&
        !           365:            (!(conf = NCONF_new(NULL)) ||
        !           366:            NCONF_load(conf, configfile, &errorline) <= 0)) {
        !           367:                if (errorline <= 0)
        !           368:                        BIO_printf(bio_err, "error loading the config file "
        !           369:                            "'%s'\n", configfile);
        !           370:                else
        !           371:                        BIO_printf(bio_err, "error on line %ld of config file "
        !           372:                            "'%s'\n", errorline, configfile);
        !           373:        }
        !           374:        if (conf != NULL) {
        !           375:                const char *p;
        !           376:
        !           377:                BIO_printf(bio_err, "Using configuration from %s\n",
        !           378:                    configfile);
        !           379:                p = NCONF_get_string(conf, NULL, ENV_OID_FILE);
        !           380:                if (p != NULL) {
        !           381:                        BIO *oid_bio = BIO_new_file(p, "r");
        !           382:                        if (!oid_bio)
        !           383:                                ERR_print_errors(bio_err);
        !           384:                        else {
        !           385:                                OBJ_create_objects(oid_bio);
        !           386:                                BIO_free_all(oid_bio);
        !           387:                        }
        !           388:                } else
        !           389:                        ERR_clear_error();
        !           390:                if (!add_oid_section(bio_err, conf))
        !           391:                        ERR_print_errors(bio_err);
        !           392:        }
        !           393:        return conf;
        !           394: }
        !           395:
        !           396: /*
        !           397:  * Query-related method definitions.
        !           398:  */
        !           399:
        !           400: static int
        !           401: query_command(const char *data, char *digest, const EVP_MD * md,
        !           402:     const char *policy, int no_nonce, int cert, const char *in,
        !           403:     const char *out, int text)
        !           404: {
        !           405:        int ret = 0;
        !           406:        TS_REQ *query = NULL;
        !           407:        BIO *in_bio = NULL;
        !           408:        BIO *data_bio = NULL;
        !           409:        BIO *out_bio = NULL;
        !           410:
        !           411:        /* Build query object either from file or from scratch. */
        !           412:        if (in != NULL) {
        !           413:                if ((in_bio = BIO_new_file(in, "rb")) == NULL)
        !           414:                        goto end;
        !           415:                query = d2i_TS_REQ_bio(in_bio, NULL);
        !           416:        } else {
        !           417:                /* Open the file if no explicit digest bytes were specified. */
        !           418:                if (!digest &&
        !           419:                    !(data_bio = BIO_open_with_default(data, "rb", stdin)))
        !           420:                        goto end;
        !           421:                /* Creating the query object. */
        !           422:                query = create_query(data_bio, digest, md,
        !           423:                    policy, no_nonce, cert);
        !           424:                /* Saving the random number generator state. */
        !           425:        }
        !           426:        if (query == NULL)
        !           427:                goto end;
        !           428:
        !           429:        /* Write query either in ASN.1 or in text format. */
        !           430:        if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL)
        !           431:                goto end;
        !           432:        if (text) {
        !           433:                /* Text output. */
        !           434:                if (!TS_REQ_print_bio(out_bio, query))
        !           435:                        goto end;
        !           436:        } else {
        !           437:                /* ASN.1 output. */
        !           438:                if (!i2d_TS_REQ_bio(out_bio, query))
        !           439:                        goto end;
        !           440:        }
        !           441:
        !           442:        ret = 1;
        !           443:
        !           444: end:
        !           445:        ERR_print_errors(bio_err);
        !           446:
        !           447:        /* Clean up. */
        !           448:        BIO_free_all(in_bio);
        !           449:        BIO_free_all(data_bio);
        !           450:        BIO_free_all(out_bio);
        !           451:        TS_REQ_free(query);
        !           452:
        !           453:        return ret;
        !           454: }
        !           455:
        !           456: static BIO *
        !           457: BIO_open_with_default(const char *file, const char *mode, FILE * default_fp)
        !           458: {
        !           459:        return file == NULL ? BIO_new_fp(default_fp, BIO_NOCLOSE) :
        !           460:            BIO_new_file(file, mode);
        !           461: }
        !           462:
        !           463: static TS_REQ *
        !           464: create_query(BIO * data_bio, char *digest, const EVP_MD * md,
        !           465:     const char *policy, int no_nonce, int cert)
        !           466: {
        !           467:        int ret = 0;
        !           468:        TS_REQ *ts_req = NULL;
        !           469:        int len;
        !           470:        TS_MSG_IMPRINT *msg_imprint = NULL;
        !           471:        X509_ALGOR *algo = NULL;
        !           472:        unsigned char *data = NULL;
        !           473:        ASN1_OBJECT *policy_obj = NULL;
        !           474:        ASN1_INTEGER *nonce_asn1 = NULL;
        !           475:
        !           476:        /* Setting default message digest. */
        !           477:        if (!md && !(md = EVP_get_digestbyname("sha1")))
        !           478:                goto err;
        !           479:
        !           480:        /* Creating request object. */
        !           481:        if (!(ts_req = TS_REQ_new()))
        !           482:                goto err;
        !           483:
        !           484:        /* Setting version. */
        !           485:        if (!TS_REQ_set_version(ts_req, 1))
        !           486:                goto err;
        !           487:
        !           488:        /* Creating and adding MSG_IMPRINT object. */
        !           489:        if (!(msg_imprint = TS_MSG_IMPRINT_new()))
        !           490:                goto err;
        !           491:
        !           492:        /* Adding algorithm. */
        !           493:        if (!(algo = X509_ALGOR_new()))
        !           494:                goto err;
        !           495:        if (!(algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))))
        !           496:                goto err;
        !           497:        if (!(algo->parameter = ASN1_TYPE_new()))
        !           498:                goto err;
        !           499:        algo->parameter->type = V_ASN1_NULL;
        !           500:        if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
        !           501:                goto err;
        !           502:
        !           503:        /* Adding message digest. */
        !           504:        if ((len = create_digest(data_bio, digest, md, &data)) == 0)
        !           505:                goto err;
        !           506:        if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len))
        !           507:                goto err;
        !           508:
        !           509:        if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
        !           510:                goto err;
        !           511:
        !           512:        /* Setting policy if requested. */
        !           513:        if (policy && !(policy_obj = txt2obj(policy)))
        !           514:                goto err;
        !           515:        if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj))
        !           516:                goto err;
        !           517:
        !           518:        /* Setting nonce if requested. */
        !           519:        if (!no_nonce && !(nonce_asn1 = create_nonce(NONCE_LENGTH)))
        !           520:                goto err;
        !           521:        if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
        !           522:                goto err;
        !           523:
        !           524:        /* Setting certificate request flag if requested. */
        !           525:        if (!TS_REQ_set_cert_req(ts_req, cert))
        !           526:                goto err;
        !           527:
        !           528:        ret = 1;
        !           529:
        !           530: err:
        !           531:        if (!ret) {
        !           532:                TS_REQ_free(ts_req);
        !           533:                ts_req = NULL;
        !           534:                BIO_printf(bio_err, "could not create query\n");
        !           535:        }
        !           536:        TS_MSG_IMPRINT_free(msg_imprint);
        !           537:        X509_ALGOR_free(algo);
        !           538:        free(data);
        !           539:        ASN1_OBJECT_free(policy_obj);
        !           540:        ASN1_INTEGER_free(nonce_asn1);
        !           541:
        !           542:        return ts_req;
        !           543: }
        !           544:
        !           545: static int
        !           546: create_digest(BIO * input, char *digest, const EVP_MD * md,
        !           547:     unsigned char **md_value)
        !           548: {
        !           549:        int md_value_len;
        !           550:
        !           551:        md_value_len = EVP_MD_size(md);
        !           552:        if (md_value_len < 0)
        !           553:                goto err;
        !           554:        if (input) {
        !           555:                /* Digest must be computed from an input file. */
        !           556:                EVP_MD_CTX md_ctx;
        !           557:                unsigned char buffer[4096];
        !           558:                int length;
        !           559:
        !           560:                *md_value = malloc(md_value_len);
        !           561:                if (*md_value == 0)
        !           562:                        goto err;
        !           563:
        !           564:                EVP_DigestInit(&md_ctx, md);
        !           565:                while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) {
        !           566:                        EVP_DigestUpdate(&md_ctx, buffer, length);
        !           567:                }
        !           568:                EVP_DigestFinal(&md_ctx, *md_value, NULL);
        !           569:        } else {
        !           570:                /* Digest bytes are specified with digest. */
        !           571:                long digest_len;
        !           572:                *md_value = string_to_hex(digest, &digest_len);
        !           573:                if (!*md_value || md_value_len != digest_len) {
        !           574:                        free(*md_value);
        !           575:                        *md_value = NULL;
        !           576:                        BIO_printf(bio_err, "bad digest, %d bytes "
        !           577:                            "must be specified\n", md_value_len);
        !           578:                        goto err;
        !           579:                }
        !           580:        }
        !           581:
        !           582:        return md_value_len;
        !           583: err:
        !           584:        return 0;
        !           585: }
        !           586:
        !           587: static ASN1_INTEGER *
        !           588: create_nonce(int bits)
        !           589: {
        !           590:        unsigned char buf[20];
        !           591:        ASN1_INTEGER *nonce = NULL;
        !           592:        int len = (bits - 1) / 8 + 1;
        !           593:        int i;
        !           594:
        !           595:        /* Generating random byte sequence. */
        !           596:        if (len > (int) sizeof(buf))
        !           597:                goto err;
        !           598:        if (RAND_bytes(buf, len) <= 0)
        !           599:                goto err;
        !           600:
        !           601:        /* Find the first non-zero byte and creating ASN1_INTEGER object. */
        !           602:        for (i = 0; i < len && !buf[i]; ++i)
        !           603:                ;
        !           604:        if (!(nonce = ASN1_INTEGER_new()))
        !           605:                goto err;
        !           606:        free(nonce->data);
        !           607:        /* Allocate at least one byte. */
        !           608:        nonce->length = len - i;
        !           609:        if (!(nonce->data = malloc(nonce->length + 1)))
        !           610:                goto err;
        !           611:        memcpy(nonce->data, buf + i, nonce->length);
        !           612:
        !           613:        return nonce;
        !           614:
        !           615: err:
        !           616:        BIO_printf(bio_err, "could not create nonce\n");
        !           617:        ASN1_INTEGER_free(nonce);
        !           618:        return NULL;
        !           619: }
        !           620: /*
        !           621:  * Reply-related method definitions.
        !           622:  */
        !           623:
        !           624: static int
        !           625: reply_command(CONF * conf, char *section, char *engine, char *queryfile,
        !           626:     char *passin, char *inkey, char *signer, char *chain, const char *policy,
        !           627:     char *in, int token_in, char *out, int token_out, int text)
        !           628: {
        !           629:        int ret = 0;
        !           630:        TS_RESP *response = NULL;
        !           631:        BIO *in_bio = NULL;
        !           632:        BIO *query_bio = NULL;
        !           633:        BIO *inkey_bio = NULL;
        !           634:        BIO *signer_bio = NULL;
        !           635:        BIO *out_bio = NULL;
        !           636:
        !           637:        /* Build response object either from response or query. */
        !           638:        if (in != NULL) {
        !           639:                if ((in_bio = BIO_new_file(in, "rb")) == NULL)
        !           640:                        goto end;
        !           641:                if (token_in) {
        !           642:                        /*
        !           643:                         * We have a ContentInfo (PKCS7) object, add
        !           644:                         * 'granted' status info around it.
        !           645:                         */
        !           646:                        response = read_PKCS7(in_bio);
        !           647:                } else {
        !           648:                        /* We have a ready-made TS_RESP object. */
        !           649:                        response = d2i_TS_RESP_bio(in_bio, NULL);
        !           650:                }
        !           651:        } else {
        !           652:                response = create_response(conf, section, engine, queryfile,
        !           653:                    passin, inkey, signer, chain,
        !           654:                    policy);
        !           655:                if (response)
        !           656:                        BIO_printf(bio_err, "Response has been generated.\n");
        !           657:                else
        !           658:                        BIO_printf(bio_err, "Response is not generated.\n");
        !           659:        }
        !           660:        if (response == NULL)
        !           661:                goto end;
        !           662:
        !           663:        /* Write response either in ASN.1 or text format. */
        !           664:        if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL)
        !           665:                goto end;
        !           666:        if (text) {
        !           667:                /* Text output. */
        !           668:                if (token_out) {
        !           669:                        TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response);
        !           670:                        if (!TS_TST_INFO_print_bio(out_bio, tst_info))
        !           671:                                goto end;
        !           672:                } else {
        !           673:                        if (!TS_RESP_print_bio(out_bio, response))
        !           674:                                goto end;
        !           675:                }
        !           676:        } else {
        !           677:                /* ASN.1 DER output. */
        !           678:                if (token_out) {
        !           679:                        PKCS7 *token = TS_RESP_get_token(response);
        !           680:                        if (!i2d_PKCS7_bio(out_bio, token))
        !           681:                                goto end;
        !           682:                } else {
        !           683:                        if (!i2d_TS_RESP_bio(out_bio, response))
        !           684:                                goto end;
        !           685:                }
        !           686:        }
        !           687:
        !           688:        ret = 1;
        !           689:
        !           690: end:
        !           691:        ERR_print_errors(bio_err);
        !           692:
        !           693:        /* Clean up. */
        !           694:        BIO_free_all(in_bio);
        !           695:        BIO_free_all(query_bio);
        !           696:        BIO_free_all(inkey_bio);
        !           697:        BIO_free_all(signer_bio);
        !           698:        BIO_free_all(out_bio);
        !           699:        TS_RESP_free(response);
        !           700:
        !           701:        return ret;
        !           702: }
        !           703:
        !           704: /* Reads a PKCS7 token and adds default 'granted' status info to it. */
        !           705: static TS_RESP *
        !           706: read_PKCS7(BIO * in_bio)
        !           707: {
        !           708:        int ret = 0;
        !           709:        PKCS7 *token = NULL;
        !           710:        TS_TST_INFO *tst_info = NULL;
        !           711:        TS_RESP *resp = NULL;
        !           712:        TS_STATUS_INFO *si = NULL;
        !           713:
        !           714:        /* Read PKCS7 object and extract the signed time stamp info. */
        !           715:        if (!(token = d2i_PKCS7_bio(in_bio, NULL)))
        !           716:                goto end;
        !           717:        if (!(tst_info = PKCS7_to_TS_TST_INFO(token)))
        !           718:                goto end;
        !           719:
        !           720:        /* Creating response object. */
        !           721:        if (!(resp = TS_RESP_new()))
        !           722:                goto end;
        !           723:
        !           724:        /* Create granted status info. */
        !           725:        if (!(si = TS_STATUS_INFO_new()))
        !           726:                goto end;
        !           727:        if (!(ASN1_INTEGER_set(si->status, TS_STATUS_GRANTED)))
        !           728:                goto end;
        !           729:        if (!TS_RESP_set_status_info(resp, si))
        !           730:                goto end;
        !           731:
        !           732:        /* Setting encapsulated token. */
        !           733:        TS_RESP_set_tst_info(resp, token, tst_info);
        !           734:        token = NULL;           /* Ownership is lost. */
        !           735:        tst_info = NULL;        /* Ownership is lost. */
        !           736:
        !           737:        ret = 1;
        !           738: end:
        !           739:        PKCS7_free(token);
        !           740:        TS_TST_INFO_free(tst_info);
        !           741:        if (!ret) {
        !           742:                TS_RESP_free(resp);
        !           743:                resp = NULL;
        !           744:        }
        !           745:        TS_STATUS_INFO_free(si);
        !           746:        return resp;
        !           747: }
        !           748:
        !           749: static TS_RESP *
        !           750: create_response(CONF * conf, const char *section, char *engine,
        !           751:     char *queryfile, char *passin, char *inkey,
        !           752:     char *signer, char *chain, const char *policy)
        !           753: {
        !           754:        int ret = 0;
        !           755:        TS_RESP *response = NULL;
        !           756:        BIO *query_bio = NULL;
        !           757:        TS_RESP_CTX *resp_ctx = NULL;
        !           758:
        !           759:        if (!(query_bio = BIO_new_file(queryfile, "rb")))
        !           760:                goto end;
        !           761:
        !           762:        /* Getting TSA configuration section. */
        !           763:        if (!(section = TS_CONF_get_tsa_section(conf, section)))
        !           764:                goto end;
        !           765:
        !           766:        /* Setting up response generation context. */
        !           767:        if (!(resp_ctx = TS_RESP_CTX_new()))
        !           768:                goto end;
        !           769:
        !           770:        /* Setting serial number provider callback. */
        !           771:        if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx))
        !           772:                goto end;
        !           773: #ifndef OPENSSL_NO_ENGINE
        !           774:        /* Setting default OpenSSL engine. */
        !           775:        if (!TS_CONF_set_crypto_device(conf, section, engine))
        !           776:                goto end;
        !           777: #endif
        !           778:
        !           779:        /* Setting TSA signer certificate. */
        !           780:        if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx))
        !           781:                goto end;
        !           782:
        !           783:        /* Setting TSA signer certificate chain. */
        !           784:        if (!TS_CONF_set_certs(conf, section, chain, resp_ctx))
        !           785:                goto end;
        !           786:
        !           787:        /* Setting TSA signer private key. */
        !           788:        if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx))
        !           789:                goto end;
        !           790:
        !           791:        /* Setting default policy OID. */
        !           792:        if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx))
        !           793:                goto end;
        !           794:
        !           795:        /* Setting acceptable policy OIDs. */
        !           796:        if (!TS_CONF_set_policies(conf, section, resp_ctx))
        !           797:                goto end;
        !           798:
        !           799:        /* Setting the acceptable one-way hash algorithms. */
        !           800:        if (!TS_CONF_set_digests(conf, section, resp_ctx))
        !           801:                goto end;
        !           802:
        !           803:        /* Setting guaranteed time stamp accuracy. */
        !           804:        if (!TS_CONF_set_accuracy(conf, section, resp_ctx))
        !           805:                goto end;
        !           806:
        !           807:        /* Setting the precision of the time. */
        !           808:        if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx))
        !           809:                goto end;
        !           810:
        !           811:        /* Setting the ordering flaf if requested. */
        !           812:        if (!TS_CONF_set_ordering(conf, section, resp_ctx))
        !           813:                goto end;
        !           814:
        !           815:        /* Setting the TSA name required flag if requested. */
        !           816:        if (!TS_CONF_set_tsa_name(conf, section, resp_ctx))
        !           817:                goto end;
        !           818:
        !           819:        /* Setting the ESS cert id chain flag if requested. */
        !           820:        if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx))
        !           821:                goto end;
        !           822:
        !           823:        /* Creating the response. */
        !           824:        if (!(response = TS_RESP_create_response(resp_ctx, query_bio)))
        !           825:                goto end;
        !           826:
        !           827:        ret = 1;
        !           828: end:
        !           829:        if (!ret) {
        !           830:                TS_RESP_free(response);
        !           831:                response = NULL;
        !           832:        }
        !           833:        TS_RESP_CTX_free(resp_ctx);
        !           834:        BIO_free_all(query_bio);
        !           835:
        !           836:        return response;
        !           837: }
        !           838:
        !           839: static ASN1_INTEGER *
        !           840: serial_cb(TS_RESP_CTX * ctx, void *data)
        !           841: {
        !           842:        const char *serial_file = (const char *) data;
        !           843:        ASN1_INTEGER *serial = next_serial(serial_file);
        !           844:
        !           845:        if (!serial) {
        !           846:                TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
        !           847:                    "Error during serial number "
        !           848:                    "generation.");
        !           849:                TS_RESP_CTX_add_failure_info(ctx,
        !           850:                    TS_INFO_ADD_INFO_NOT_AVAILABLE);
        !           851:        } else
        !           852:                save_ts_serial(serial_file, serial);
        !           853:
        !           854:        return serial;
        !           855: }
        !           856:
        !           857: static ASN1_INTEGER *
        !           858: next_serial(const char *serialfile)
        !           859: {
        !           860:        int ret = 0;
        !           861:        BIO *in = NULL;
        !           862:        ASN1_INTEGER *serial = NULL;
        !           863:        BIGNUM *bn = NULL;
        !           864:
        !           865:        if (!(serial = ASN1_INTEGER_new()))
        !           866:                goto err;
        !           867:
        !           868:        if (!(in = BIO_new_file(serialfile, "r"))) {
        !           869:                ERR_clear_error();
        !           870:                BIO_printf(bio_err, "Warning: could not open file %s for "
        !           871:                    "reading, using serial number: 1\n", serialfile);
        !           872:                if (!ASN1_INTEGER_set(serial, 1))
        !           873:                        goto err;
        !           874:        } else {
        !           875:                char buf[1024];
        !           876:                if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) {
        !           877:                        BIO_printf(bio_err, "unable to load number from %s\n",
        !           878:                            serialfile);
        !           879:                        goto err;
        !           880:                }
        !           881:                if (!(bn = ASN1_INTEGER_to_BN(serial, NULL)))
        !           882:                        goto err;
        !           883:                ASN1_INTEGER_free(serial);
        !           884:                serial = NULL;
        !           885:                if (!BN_add_word(bn, 1))
        !           886:                        goto err;
        !           887:                if (!(serial = BN_to_ASN1_INTEGER(bn, NULL)))
        !           888:                        goto err;
        !           889:        }
        !           890:        ret = 1;
        !           891: err:
        !           892:        if (!ret) {
        !           893:                ASN1_INTEGER_free(serial);
        !           894:                serial = NULL;
        !           895:        }
        !           896:        BIO_free_all(in);
        !           897:        BN_free(bn);
        !           898:        return serial;
        !           899: }
        !           900:
        !           901: static int
        !           902: save_ts_serial(const char *serialfile, ASN1_INTEGER * serial)
        !           903: {
        !           904:        int ret = 0;
        !           905:        BIO *out = NULL;
        !           906:
        !           907:        if (!(out = BIO_new_file(serialfile, "w")))
        !           908:                goto err;
        !           909:        if (i2a_ASN1_INTEGER(out, serial) <= 0)
        !           910:                goto err;
        !           911:        if (BIO_puts(out, "\n") <= 0)
        !           912:                goto err;
        !           913:        ret = 1;
        !           914: err:
        !           915:        if (!ret)
        !           916:                BIO_printf(bio_err, "could not save serial number to %s\n",
        !           917:                    serialfile);
        !           918:        BIO_free_all(out);
        !           919:        return ret;
        !           920: }
        !           921:
        !           922: /*
        !           923:  * Verify-related method definitions.
        !           924:  */
        !           925:
        !           926: static int
        !           927: verify_command(char *data, char *digest, char *queryfile, char *in,
        !           928:     int token_in, char *ca_path, char *ca_file, char *untrusted)
        !           929: {
        !           930:        BIO *in_bio = NULL;
        !           931:        PKCS7 *token = NULL;
        !           932:        TS_RESP *response = NULL;
        !           933:        TS_VERIFY_CTX *verify_ctx = NULL;
        !           934:        int ret = 0;
        !           935:
        !           936:        /* Decode the token (PKCS7) or response (TS_RESP) files. */
        !           937:        if (!(in_bio = BIO_new_file(in, "rb")))
        !           938:                goto end;
        !           939:        if (token_in) {
        !           940:                if (!(token = d2i_PKCS7_bio(in_bio, NULL)))
        !           941:                        goto end;
        !           942:        } else {
        !           943:                if (!(response = d2i_TS_RESP_bio(in_bio, NULL)))
        !           944:                        goto end;
        !           945:        }
        !           946:
        !           947:        if (!(verify_ctx = create_verify_ctx(data, digest, queryfile,
        !           948:            ca_path, ca_file, untrusted)))
        !           949:                goto end;
        !           950:
        !           951:        /* Checking the token or response against the request. */
        !           952:        ret = token_in ?
        !           953:            TS_RESP_verify_token(verify_ctx, token) :
        !           954:            TS_RESP_verify_response(verify_ctx, response);
        !           955:
        !           956: end:
        !           957:        printf("Verification: ");
        !           958:        if (ret)
        !           959:                printf("OK\n");
        !           960:        else {
        !           961:                printf("FAILED\n");
        !           962:                /* Print errors, if there are any. */
        !           963:                ERR_print_errors(bio_err);
        !           964:        }
        !           965:
        !           966:        /* Clean up. */
        !           967:        BIO_free_all(in_bio);
        !           968:        PKCS7_free(token);
        !           969:        TS_RESP_free(response);
        !           970:        TS_VERIFY_CTX_free(verify_ctx);
        !           971:        return ret;
        !           972: }
        !           973:
        !           974: static TS_VERIFY_CTX *
        !           975: create_verify_ctx(char *data, char *digest, char *queryfile, char *ca_path,
        !           976:     char *ca_file, char *untrusted)
        !           977: {
        !           978:        TS_VERIFY_CTX *ctx = NULL;
        !           979:        BIO *input = NULL;
        !           980:        TS_REQ *request = NULL;
        !           981:        int ret = 0;
        !           982:
        !           983:        if (data != NULL || digest != NULL) {
        !           984:                if (!(ctx = TS_VERIFY_CTX_new()))
        !           985:                        goto err;
        !           986:                ctx->flags = TS_VFY_VERSION | TS_VFY_SIGNER;
        !           987:                if (data != NULL) {
        !           988:                        ctx->flags |= TS_VFY_DATA;
        !           989:                        if (!(ctx->data = BIO_new_file(data, "rb")))
        !           990:                                goto err;
        !           991:                } else if (digest != NULL) {
        !           992:                        long imprint_len;
        !           993:                        ctx->flags |= TS_VFY_IMPRINT;
        !           994:                        if (!(ctx->imprint = string_to_hex(digest,
        !           995:                                    &imprint_len))) {
        !           996:                                BIO_printf(bio_err, "invalid digest string\n");
        !           997:                                goto err;
        !           998:                        }
        !           999:                        ctx->imprint_len = imprint_len;
        !          1000:                }
        !          1001:        } else if (queryfile != NULL) {
        !          1002:                /*
        !          1003:                 * The request has just to be read, decoded and converted to
        !          1004:                 * a verify context object.
        !          1005:                 */
        !          1006:                if (!(input = BIO_new_file(queryfile, "rb")))
        !          1007:                        goto err;
        !          1008:                if (!(request = d2i_TS_REQ_bio(input, NULL)))
        !          1009:                        goto err;
        !          1010:                if (!(ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)))
        !          1011:                        goto err;
        !          1012:        } else
        !          1013:                return NULL;
        !          1014:
        !          1015:        /* Add the signature verification flag and arguments. */
        !          1016:        ctx->flags |= TS_VFY_SIGNATURE;
        !          1017:
        !          1018:        /* Initialising the X509_STORE object. */
        !          1019:        if (!(ctx->store = create_cert_store(ca_path, ca_file)))
        !          1020:                goto err;
        !          1021:
        !          1022:        /* Loading untrusted certificates. */
        !          1023:        if (untrusted && !(ctx->certs = TS_CONF_load_certs(untrusted)))
        !          1024:                goto err;
        !          1025:
        !          1026:        ret = 1;
        !          1027: err:
        !          1028:        if (!ret) {
        !          1029:                TS_VERIFY_CTX_free(ctx);
        !          1030:                ctx = NULL;
        !          1031:        }
        !          1032:        BIO_free_all(input);
        !          1033:        TS_REQ_free(request);
        !          1034:        return ctx;
        !          1035: }
        !          1036:
        !          1037: static X509_STORE *
        !          1038: create_cert_store(char *ca_path, char *ca_file)
        !          1039: {
        !          1040:        X509_STORE *cert_ctx = NULL;
        !          1041:        X509_LOOKUP *lookup = NULL;
        !          1042:        int i;
        !          1043:
        !          1044:        /* Creating the X509_STORE object. */
        !          1045:        cert_ctx = X509_STORE_new();
        !          1046:
        !          1047:        /* Setting the callback for certificate chain verification. */
        !          1048:        X509_STORE_set_verify_cb(cert_ctx, verify_cb);
        !          1049:
        !          1050:        /* Adding a trusted certificate directory source. */
        !          1051:        if (ca_path) {
        !          1052:                lookup = X509_STORE_add_lookup(cert_ctx,
        !          1053:                    X509_LOOKUP_hash_dir());
        !          1054:                if (lookup == NULL) {
        !          1055:                        BIO_printf(bio_err, "memory allocation failure\n");
        !          1056:                        goto err;
        !          1057:                }
        !          1058:                i = X509_LOOKUP_add_dir(lookup, ca_path, X509_FILETYPE_PEM);
        !          1059:                if (!i) {
        !          1060:                        BIO_printf(bio_err, "Error loading directory %s\n",
        !          1061:                            ca_path);
        !          1062:                        goto err;
        !          1063:                }
        !          1064:        }
        !          1065:        /* Adding a trusted certificate file source. */
        !          1066:        if (ca_file) {
        !          1067:                lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
        !          1068:                if (lookup == NULL) {
        !          1069:                        BIO_printf(bio_err, "memory allocation failure\n");
        !          1070:                        goto err;
        !          1071:                }
        !          1072:                i = X509_LOOKUP_load_file(lookup, ca_file, X509_FILETYPE_PEM);
        !          1073:                if (!i) {
        !          1074:                        BIO_printf(bio_err, "Error loading file %s\n", ca_file);
        !          1075:                        goto err;
        !          1076:                }
        !          1077:        }
        !          1078:        return cert_ctx;
        !          1079: err:
        !          1080:        X509_STORE_free(cert_ctx);
        !          1081:        return NULL;
        !          1082: }
        !          1083:
        !          1084: static int
        !          1085: verify_cb(int ok, X509_STORE_CTX * ctx)
        !          1086: {
        !          1087:        /*
        !          1088:        char buf[256];
        !          1089:
        !          1090:        if (!ok)
        !          1091:                {
        !          1092:                X509_NAME_oneline(X509_get_subject_name(ctx->current_cert),
        !          1093:                                  buf, sizeof(buf));
        !          1094:                printf("%s\n", buf);
        !          1095:                printf("error %d at %d depth lookup: %s\n",
        !          1096:                       ctx->error, ctx->error_depth,
        !          1097:                        X509_verify_cert_error_string(ctx->error));
        !          1098:                }
        !          1099:        */
        !          1100:
        !          1101:        return ok;
        !          1102: }