11cb0ef41Sopenharmony_ci/*
21cb0ef41Sopenharmony_ci * Copyright 2006-2021 The OpenSSL Project Authors. All Rights Reserved.
31cb0ef41Sopenharmony_ci *
41cb0ef41Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License").  You may not use
51cb0ef41Sopenharmony_ci * this file except in compliance with the License.  You can obtain a copy
61cb0ef41Sopenharmony_ci * in the file LICENSE in the source distribution or at
71cb0ef41Sopenharmony_ci * https://www.openssl.org/source/license.html
81cb0ef41Sopenharmony_ci */
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci/*
111cb0ef41Sopenharmony_ci * Experimental ASN1 BIO. When written through the data is converted to an
121cb0ef41Sopenharmony_ci * ASN1 string type: default is OCTET STRING. Additional functions can be
131cb0ef41Sopenharmony_ci * provided to add prefix and suffix data.
141cb0ef41Sopenharmony_ci */
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci#include <string.h>
171cb0ef41Sopenharmony_ci#include "internal/bio.h"
181cb0ef41Sopenharmony_ci#include <openssl/asn1.h>
191cb0ef41Sopenharmony_ci#include "internal/cryptlib.h"
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci/* Must be large enough for biggest tag+length */
221cb0ef41Sopenharmony_ci#define DEFAULT_ASN1_BUF_SIZE 20
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_citypedef enum {
251cb0ef41Sopenharmony_ci    ASN1_STATE_START,
261cb0ef41Sopenharmony_ci    ASN1_STATE_PRE_COPY,
271cb0ef41Sopenharmony_ci    ASN1_STATE_HEADER,
281cb0ef41Sopenharmony_ci    ASN1_STATE_HEADER_COPY,
291cb0ef41Sopenharmony_ci    ASN1_STATE_DATA_COPY,
301cb0ef41Sopenharmony_ci    ASN1_STATE_POST_COPY,
311cb0ef41Sopenharmony_ci    ASN1_STATE_DONE
321cb0ef41Sopenharmony_ci} asn1_bio_state_t;
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_citypedef struct BIO_ASN1_EX_FUNCS_st {
351cb0ef41Sopenharmony_ci    asn1_ps_func *ex_func;
361cb0ef41Sopenharmony_ci    asn1_ps_func *ex_free_func;
371cb0ef41Sopenharmony_ci} BIO_ASN1_EX_FUNCS;
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_citypedef struct BIO_ASN1_BUF_CTX_t {
401cb0ef41Sopenharmony_ci    /* Internal state */
411cb0ef41Sopenharmony_ci    asn1_bio_state_t state;
421cb0ef41Sopenharmony_ci    /* Internal buffer */
431cb0ef41Sopenharmony_ci    unsigned char *buf;
441cb0ef41Sopenharmony_ci    /* Size of buffer */
451cb0ef41Sopenharmony_ci    int bufsize;
461cb0ef41Sopenharmony_ci    /* Current position in buffer */
471cb0ef41Sopenharmony_ci    int bufpos;
481cb0ef41Sopenharmony_ci    /* Current buffer length */
491cb0ef41Sopenharmony_ci    int buflen;
501cb0ef41Sopenharmony_ci    /* Amount of data to copy */
511cb0ef41Sopenharmony_ci    int copylen;
521cb0ef41Sopenharmony_ci    /* Class and tag to use */
531cb0ef41Sopenharmony_ci    int asn1_class, asn1_tag;
541cb0ef41Sopenharmony_ci    asn1_ps_func *prefix, *prefix_free, *suffix, *suffix_free;
551cb0ef41Sopenharmony_ci    /* Extra buffer for prefix and suffix data */
561cb0ef41Sopenharmony_ci    unsigned char *ex_buf;
571cb0ef41Sopenharmony_ci    int ex_len;
581cb0ef41Sopenharmony_ci    int ex_pos;
591cb0ef41Sopenharmony_ci    void *ex_arg;
601cb0ef41Sopenharmony_ci} BIO_ASN1_BUF_CTX;
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_cistatic int asn1_bio_write(BIO *h, const char *buf, int num);
631cb0ef41Sopenharmony_cistatic int asn1_bio_read(BIO *h, char *buf, int size);
641cb0ef41Sopenharmony_cistatic int asn1_bio_puts(BIO *h, const char *str);
651cb0ef41Sopenharmony_cistatic int asn1_bio_gets(BIO *h, char *str, int size);
661cb0ef41Sopenharmony_cistatic long asn1_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
671cb0ef41Sopenharmony_cistatic int asn1_bio_new(BIO *h);
681cb0ef41Sopenharmony_cistatic int asn1_bio_free(BIO *data);
691cb0ef41Sopenharmony_cistatic long asn1_bio_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_cistatic int asn1_bio_init(BIO_ASN1_BUF_CTX *ctx, int size);
721cb0ef41Sopenharmony_cistatic int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
731cb0ef41Sopenharmony_ci                             asn1_ps_func *cleanup, asn1_bio_state_t next);
741cb0ef41Sopenharmony_cistatic int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
751cb0ef41Sopenharmony_ci                             asn1_ps_func *setup,
761cb0ef41Sopenharmony_ci                             asn1_bio_state_t ex_state,
771cb0ef41Sopenharmony_ci                             asn1_bio_state_t other_state);
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_cistatic const BIO_METHOD methods_asn1 = {
801cb0ef41Sopenharmony_ci    BIO_TYPE_ASN1,
811cb0ef41Sopenharmony_ci    "asn1",
821cb0ef41Sopenharmony_ci    bwrite_conv,
831cb0ef41Sopenharmony_ci    asn1_bio_write,
841cb0ef41Sopenharmony_ci    bread_conv,
851cb0ef41Sopenharmony_ci    asn1_bio_read,
861cb0ef41Sopenharmony_ci    asn1_bio_puts,
871cb0ef41Sopenharmony_ci    asn1_bio_gets,
881cb0ef41Sopenharmony_ci    asn1_bio_ctrl,
891cb0ef41Sopenharmony_ci    asn1_bio_new,
901cb0ef41Sopenharmony_ci    asn1_bio_free,
911cb0ef41Sopenharmony_ci    asn1_bio_callback_ctrl,
921cb0ef41Sopenharmony_ci};
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ciconst BIO_METHOD *BIO_f_asn1(void)
951cb0ef41Sopenharmony_ci{
961cb0ef41Sopenharmony_ci    return &methods_asn1;
971cb0ef41Sopenharmony_ci}
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_cistatic int asn1_bio_new(BIO *b)
1001cb0ef41Sopenharmony_ci{
1011cb0ef41Sopenharmony_ci    BIO_ASN1_BUF_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci    if (ctx == NULL) {
1041cb0ef41Sopenharmony_ci        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
1051cb0ef41Sopenharmony_ci        return 0;
1061cb0ef41Sopenharmony_ci    }
1071cb0ef41Sopenharmony_ci    if (!asn1_bio_init(ctx, DEFAULT_ASN1_BUF_SIZE)) {
1081cb0ef41Sopenharmony_ci        OPENSSL_free(ctx);
1091cb0ef41Sopenharmony_ci        return 0;
1101cb0ef41Sopenharmony_ci    }
1111cb0ef41Sopenharmony_ci    BIO_set_data(b, ctx);
1121cb0ef41Sopenharmony_ci    BIO_set_init(b, 1);
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci    return 1;
1151cb0ef41Sopenharmony_ci}
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_cistatic int asn1_bio_init(BIO_ASN1_BUF_CTX *ctx, int size)
1181cb0ef41Sopenharmony_ci{
1191cb0ef41Sopenharmony_ci    if (size <= 0 || (ctx->buf = OPENSSL_malloc(size)) == NULL) {
1201cb0ef41Sopenharmony_ci        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
1211cb0ef41Sopenharmony_ci        return 0;
1221cb0ef41Sopenharmony_ci    }
1231cb0ef41Sopenharmony_ci    ctx->bufsize = size;
1241cb0ef41Sopenharmony_ci    ctx->asn1_class = V_ASN1_UNIVERSAL;
1251cb0ef41Sopenharmony_ci    ctx->asn1_tag = V_ASN1_OCTET_STRING;
1261cb0ef41Sopenharmony_ci    ctx->state = ASN1_STATE_START;
1271cb0ef41Sopenharmony_ci    return 1;
1281cb0ef41Sopenharmony_ci}
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_cistatic int asn1_bio_free(BIO *b)
1311cb0ef41Sopenharmony_ci{
1321cb0ef41Sopenharmony_ci    BIO_ASN1_BUF_CTX *ctx;
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci    if (b == NULL)
1351cb0ef41Sopenharmony_ci        return 0;
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci    ctx = BIO_get_data(b);
1381cb0ef41Sopenharmony_ci    if (ctx == NULL)
1391cb0ef41Sopenharmony_ci        return 0;
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci    if (ctx->prefix_free != NULL)
1421cb0ef41Sopenharmony_ci        ctx->prefix_free(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg);
1431cb0ef41Sopenharmony_ci    if (ctx->suffix_free != NULL)
1441cb0ef41Sopenharmony_ci        ctx->suffix_free(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg);
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci    OPENSSL_free(ctx->buf);
1471cb0ef41Sopenharmony_ci    OPENSSL_free(ctx);
1481cb0ef41Sopenharmony_ci    BIO_set_data(b, NULL);
1491cb0ef41Sopenharmony_ci    BIO_set_init(b, 0);
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci    return 1;
1521cb0ef41Sopenharmony_ci}
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_cistatic int asn1_bio_write(BIO *b, const char *in, int inl)
1551cb0ef41Sopenharmony_ci{
1561cb0ef41Sopenharmony_ci    BIO_ASN1_BUF_CTX *ctx;
1571cb0ef41Sopenharmony_ci    int wrmax, wrlen, ret;
1581cb0ef41Sopenharmony_ci    unsigned char *p;
1591cb0ef41Sopenharmony_ci    BIO *next;
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci    ctx = BIO_get_data(b);
1621cb0ef41Sopenharmony_ci    next = BIO_next(b);
1631cb0ef41Sopenharmony_ci    if (in == NULL || inl < 0 || ctx == NULL || next == NULL)
1641cb0ef41Sopenharmony_ci        return 0;
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci    wrlen = 0;
1671cb0ef41Sopenharmony_ci    ret = -1;
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci    for (;;) {
1701cb0ef41Sopenharmony_ci        switch (ctx->state) {
1711cb0ef41Sopenharmony_ci            /* Setup prefix data, call it */
1721cb0ef41Sopenharmony_ci        case ASN1_STATE_START:
1731cb0ef41Sopenharmony_ci            if (!asn1_bio_setup_ex(b, ctx, ctx->prefix,
1741cb0ef41Sopenharmony_ci                                   ASN1_STATE_PRE_COPY, ASN1_STATE_HEADER))
1751cb0ef41Sopenharmony_ci                return -1;
1761cb0ef41Sopenharmony_ci            break;
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci            /* Copy any pre data first */
1791cb0ef41Sopenharmony_ci        case ASN1_STATE_PRE_COPY:
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci            ret = asn1_bio_flush_ex(b, ctx, ctx->prefix_free,
1821cb0ef41Sopenharmony_ci                                    ASN1_STATE_HEADER);
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci            if (ret <= 0)
1851cb0ef41Sopenharmony_ci                goto done;
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci            break;
1881cb0ef41Sopenharmony_ci
1891cb0ef41Sopenharmony_ci        case ASN1_STATE_HEADER:
1901cb0ef41Sopenharmony_ci            ctx->buflen = ASN1_object_size(0, inl, ctx->asn1_tag) - inl;
1911cb0ef41Sopenharmony_ci            if (!ossl_assert(ctx->buflen <= ctx->bufsize))
1921cb0ef41Sopenharmony_ci                return -1;
1931cb0ef41Sopenharmony_ci            p = ctx->buf;
1941cb0ef41Sopenharmony_ci            ASN1_put_object(&p, 0, inl, ctx->asn1_tag, ctx->asn1_class);
1951cb0ef41Sopenharmony_ci            ctx->copylen = inl;
1961cb0ef41Sopenharmony_ci            ctx->state = ASN1_STATE_HEADER_COPY;
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci            break;
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_ci        case ASN1_STATE_HEADER_COPY:
2011cb0ef41Sopenharmony_ci            ret = BIO_write(next, ctx->buf + ctx->bufpos, ctx->buflen);
2021cb0ef41Sopenharmony_ci            if (ret <= 0)
2031cb0ef41Sopenharmony_ci                goto done;
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci            ctx->buflen -= ret;
2061cb0ef41Sopenharmony_ci            if (ctx->buflen)
2071cb0ef41Sopenharmony_ci                ctx->bufpos += ret;
2081cb0ef41Sopenharmony_ci            else {
2091cb0ef41Sopenharmony_ci                ctx->bufpos = 0;
2101cb0ef41Sopenharmony_ci                ctx->state = ASN1_STATE_DATA_COPY;
2111cb0ef41Sopenharmony_ci            }
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_ci            break;
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci        case ASN1_STATE_DATA_COPY:
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_ci            if (inl > ctx->copylen)
2181cb0ef41Sopenharmony_ci                wrmax = ctx->copylen;
2191cb0ef41Sopenharmony_ci            else
2201cb0ef41Sopenharmony_ci                wrmax = inl;
2211cb0ef41Sopenharmony_ci            ret = BIO_write(next, in, wrmax);
2221cb0ef41Sopenharmony_ci            if (ret <= 0)
2231cb0ef41Sopenharmony_ci                goto done;
2241cb0ef41Sopenharmony_ci            wrlen += ret;
2251cb0ef41Sopenharmony_ci            ctx->copylen -= ret;
2261cb0ef41Sopenharmony_ci            in += ret;
2271cb0ef41Sopenharmony_ci            inl -= ret;
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci            if (ctx->copylen == 0)
2301cb0ef41Sopenharmony_ci                ctx->state = ASN1_STATE_HEADER;
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci            if (inl == 0)
2331cb0ef41Sopenharmony_ci                goto done;
2341cb0ef41Sopenharmony_ci
2351cb0ef41Sopenharmony_ci            break;
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci        case ASN1_STATE_POST_COPY:
2381cb0ef41Sopenharmony_ci        case ASN1_STATE_DONE:
2391cb0ef41Sopenharmony_ci            BIO_clear_retry_flags(b);
2401cb0ef41Sopenharmony_ci            return 0;
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_ci        }
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci    }
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci done:
2471cb0ef41Sopenharmony_ci    BIO_clear_retry_flags(b);
2481cb0ef41Sopenharmony_ci    BIO_copy_next_retry(b);
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci    return (wrlen > 0) ? wrlen : ret;
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci}
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_cistatic int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
2551cb0ef41Sopenharmony_ci                             asn1_ps_func *cleanup, asn1_bio_state_t next)
2561cb0ef41Sopenharmony_ci{
2571cb0ef41Sopenharmony_ci    int ret;
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci    if (ctx->ex_len <= 0)
2601cb0ef41Sopenharmony_ci        return 1;
2611cb0ef41Sopenharmony_ci    for (;;) {
2621cb0ef41Sopenharmony_ci        ret = BIO_write(BIO_next(b), ctx->ex_buf + ctx->ex_pos, ctx->ex_len);
2631cb0ef41Sopenharmony_ci        if (ret <= 0)
2641cb0ef41Sopenharmony_ci            break;
2651cb0ef41Sopenharmony_ci        ctx->ex_len -= ret;
2661cb0ef41Sopenharmony_ci        if (ctx->ex_len > 0)
2671cb0ef41Sopenharmony_ci            ctx->ex_pos += ret;
2681cb0ef41Sopenharmony_ci        else {
2691cb0ef41Sopenharmony_ci            if (cleanup)
2701cb0ef41Sopenharmony_ci                cleanup(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg);
2711cb0ef41Sopenharmony_ci            ctx->state = next;
2721cb0ef41Sopenharmony_ci            ctx->ex_pos = 0;
2731cb0ef41Sopenharmony_ci            break;
2741cb0ef41Sopenharmony_ci        }
2751cb0ef41Sopenharmony_ci    }
2761cb0ef41Sopenharmony_ci    return ret;
2771cb0ef41Sopenharmony_ci}
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_cistatic int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
2801cb0ef41Sopenharmony_ci                             asn1_ps_func *setup,
2811cb0ef41Sopenharmony_ci                             asn1_bio_state_t ex_state,
2821cb0ef41Sopenharmony_ci                             asn1_bio_state_t other_state)
2831cb0ef41Sopenharmony_ci{
2841cb0ef41Sopenharmony_ci    if (setup && !setup(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg)) {
2851cb0ef41Sopenharmony_ci        BIO_clear_retry_flags(b);
2861cb0ef41Sopenharmony_ci        return 0;
2871cb0ef41Sopenharmony_ci    }
2881cb0ef41Sopenharmony_ci    if (ctx->ex_len > 0)
2891cb0ef41Sopenharmony_ci        ctx->state = ex_state;
2901cb0ef41Sopenharmony_ci    else
2911cb0ef41Sopenharmony_ci        ctx->state = other_state;
2921cb0ef41Sopenharmony_ci    return 1;
2931cb0ef41Sopenharmony_ci}
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_cistatic int asn1_bio_read(BIO *b, char *in, int inl)
2961cb0ef41Sopenharmony_ci{
2971cb0ef41Sopenharmony_ci    BIO *next = BIO_next(b);
2981cb0ef41Sopenharmony_ci    if (next == NULL)
2991cb0ef41Sopenharmony_ci        return 0;
3001cb0ef41Sopenharmony_ci    return BIO_read(next, in, inl);
3011cb0ef41Sopenharmony_ci}
3021cb0ef41Sopenharmony_ci
3031cb0ef41Sopenharmony_cistatic int asn1_bio_puts(BIO *b, const char *str)
3041cb0ef41Sopenharmony_ci{
3051cb0ef41Sopenharmony_ci    return asn1_bio_write(b, str, strlen(str));
3061cb0ef41Sopenharmony_ci}
3071cb0ef41Sopenharmony_ci
3081cb0ef41Sopenharmony_cistatic int asn1_bio_gets(BIO *b, char *str, int size)
3091cb0ef41Sopenharmony_ci{
3101cb0ef41Sopenharmony_ci    BIO *next = BIO_next(b);
3111cb0ef41Sopenharmony_ci    if (next == NULL)
3121cb0ef41Sopenharmony_ci        return 0;
3131cb0ef41Sopenharmony_ci    return BIO_gets(next, str, size);
3141cb0ef41Sopenharmony_ci}
3151cb0ef41Sopenharmony_ci
3161cb0ef41Sopenharmony_cistatic long asn1_bio_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
3171cb0ef41Sopenharmony_ci{
3181cb0ef41Sopenharmony_ci    BIO *next = BIO_next(b);
3191cb0ef41Sopenharmony_ci    if (next == NULL)
3201cb0ef41Sopenharmony_ci        return 0;
3211cb0ef41Sopenharmony_ci    return BIO_callback_ctrl(next, cmd, fp);
3221cb0ef41Sopenharmony_ci}
3231cb0ef41Sopenharmony_ci
3241cb0ef41Sopenharmony_cistatic long asn1_bio_ctrl(BIO *b, int cmd, long arg1, void *arg2)
3251cb0ef41Sopenharmony_ci{
3261cb0ef41Sopenharmony_ci    BIO_ASN1_BUF_CTX *ctx;
3271cb0ef41Sopenharmony_ci    BIO_ASN1_EX_FUNCS *ex_func;
3281cb0ef41Sopenharmony_ci    long ret = 1;
3291cb0ef41Sopenharmony_ci    BIO *next;
3301cb0ef41Sopenharmony_ci
3311cb0ef41Sopenharmony_ci    ctx = BIO_get_data(b);
3321cb0ef41Sopenharmony_ci    if (ctx == NULL)
3331cb0ef41Sopenharmony_ci        return 0;
3341cb0ef41Sopenharmony_ci    next = BIO_next(b);
3351cb0ef41Sopenharmony_ci    switch (cmd) {
3361cb0ef41Sopenharmony_ci
3371cb0ef41Sopenharmony_ci    case BIO_C_SET_PREFIX:
3381cb0ef41Sopenharmony_ci        ex_func = arg2;
3391cb0ef41Sopenharmony_ci        ctx->prefix = ex_func->ex_func;
3401cb0ef41Sopenharmony_ci        ctx->prefix_free = ex_func->ex_free_func;
3411cb0ef41Sopenharmony_ci        break;
3421cb0ef41Sopenharmony_ci
3431cb0ef41Sopenharmony_ci    case BIO_C_GET_PREFIX:
3441cb0ef41Sopenharmony_ci        ex_func = arg2;
3451cb0ef41Sopenharmony_ci        ex_func->ex_func = ctx->prefix;
3461cb0ef41Sopenharmony_ci        ex_func->ex_free_func = ctx->prefix_free;
3471cb0ef41Sopenharmony_ci        break;
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ci    case BIO_C_SET_SUFFIX:
3501cb0ef41Sopenharmony_ci        ex_func = arg2;
3511cb0ef41Sopenharmony_ci        ctx->suffix = ex_func->ex_func;
3521cb0ef41Sopenharmony_ci        ctx->suffix_free = ex_func->ex_free_func;
3531cb0ef41Sopenharmony_ci        break;
3541cb0ef41Sopenharmony_ci
3551cb0ef41Sopenharmony_ci    case BIO_C_GET_SUFFIX:
3561cb0ef41Sopenharmony_ci        ex_func = arg2;
3571cb0ef41Sopenharmony_ci        ex_func->ex_func = ctx->suffix;
3581cb0ef41Sopenharmony_ci        ex_func->ex_free_func = ctx->suffix_free;
3591cb0ef41Sopenharmony_ci        break;
3601cb0ef41Sopenharmony_ci
3611cb0ef41Sopenharmony_ci    case BIO_C_SET_EX_ARG:
3621cb0ef41Sopenharmony_ci        ctx->ex_arg = arg2;
3631cb0ef41Sopenharmony_ci        break;
3641cb0ef41Sopenharmony_ci
3651cb0ef41Sopenharmony_ci    case BIO_C_GET_EX_ARG:
3661cb0ef41Sopenharmony_ci        *(void **)arg2 = ctx->ex_arg;
3671cb0ef41Sopenharmony_ci        break;
3681cb0ef41Sopenharmony_ci
3691cb0ef41Sopenharmony_ci    case BIO_CTRL_FLUSH:
3701cb0ef41Sopenharmony_ci        if (next == NULL)
3711cb0ef41Sopenharmony_ci            return 0;
3721cb0ef41Sopenharmony_ci
3731cb0ef41Sopenharmony_ci        /* Call post function if possible */
3741cb0ef41Sopenharmony_ci        if (ctx->state == ASN1_STATE_HEADER) {
3751cb0ef41Sopenharmony_ci            if (!asn1_bio_setup_ex(b, ctx, ctx->suffix,
3761cb0ef41Sopenharmony_ci                                   ASN1_STATE_POST_COPY, ASN1_STATE_DONE))
3771cb0ef41Sopenharmony_ci                return 0;
3781cb0ef41Sopenharmony_ci        }
3791cb0ef41Sopenharmony_ci
3801cb0ef41Sopenharmony_ci        if (ctx->state == ASN1_STATE_POST_COPY) {
3811cb0ef41Sopenharmony_ci            ret = asn1_bio_flush_ex(b, ctx, ctx->suffix_free,
3821cb0ef41Sopenharmony_ci                                    ASN1_STATE_DONE);
3831cb0ef41Sopenharmony_ci            if (ret <= 0)
3841cb0ef41Sopenharmony_ci                return ret;
3851cb0ef41Sopenharmony_ci        }
3861cb0ef41Sopenharmony_ci
3871cb0ef41Sopenharmony_ci        if (ctx->state == ASN1_STATE_DONE)
3881cb0ef41Sopenharmony_ci            return BIO_ctrl(next, cmd, arg1, arg2);
3891cb0ef41Sopenharmony_ci        else {
3901cb0ef41Sopenharmony_ci            BIO_clear_retry_flags(b);
3911cb0ef41Sopenharmony_ci            return 0;
3921cb0ef41Sopenharmony_ci        }
3931cb0ef41Sopenharmony_ci
3941cb0ef41Sopenharmony_ci    default:
3951cb0ef41Sopenharmony_ci        if (next == NULL)
3961cb0ef41Sopenharmony_ci            return 0;
3971cb0ef41Sopenharmony_ci        return BIO_ctrl(next, cmd, arg1, arg2);
3981cb0ef41Sopenharmony_ci
3991cb0ef41Sopenharmony_ci    }
4001cb0ef41Sopenharmony_ci
4011cb0ef41Sopenharmony_ci    return ret;
4021cb0ef41Sopenharmony_ci}
4031cb0ef41Sopenharmony_ci
4041cb0ef41Sopenharmony_cistatic int asn1_bio_set_ex(BIO *b, int cmd,
4051cb0ef41Sopenharmony_ci                           asn1_ps_func *ex_func, asn1_ps_func *ex_free_func)
4061cb0ef41Sopenharmony_ci{
4071cb0ef41Sopenharmony_ci    BIO_ASN1_EX_FUNCS extmp;
4081cb0ef41Sopenharmony_ci    extmp.ex_func = ex_func;
4091cb0ef41Sopenharmony_ci    extmp.ex_free_func = ex_free_func;
4101cb0ef41Sopenharmony_ci    return BIO_ctrl(b, cmd, 0, &extmp);
4111cb0ef41Sopenharmony_ci}
4121cb0ef41Sopenharmony_ci
4131cb0ef41Sopenharmony_cistatic int asn1_bio_get_ex(BIO *b, int cmd,
4141cb0ef41Sopenharmony_ci                           asn1_ps_func **ex_func,
4151cb0ef41Sopenharmony_ci                           asn1_ps_func **ex_free_func)
4161cb0ef41Sopenharmony_ci{
4171cb0ef41Sopenharmony_ci    BIO_ASN1_EX_FUNCS extmp;
4181cb0ef41Sopenharmony_ci    int ret;
4191cb0ef41Sopenharmony_ci    ret = BIO_ctrl(b, cmd, 0, &extmp);
4201cb0ef41Sopenharmony_ci    if (ret > 0) {
4211cb0ef41Sopenharmony_ci        *ex_func = extmp.ex_func;
4221cb0ef41Sopenharmony_ci        *ex_free_func = extmp.ex_free_func;
4231cb0ef41Sopenharmony_ci    }
4241cb0ef41Sopenharmony_ci    return ret;
4251cb0ef41Sopenharmony_ci}
4261cb0ef41Sopenharmony_ci
4271cb0ef41Sopenharmony_ciint BIO_asn1_set_prefix(BIO *b, asn1_ps_func *prefix,
4281cb0ef41Sopenharmony_ci                        asn1_ps_func *prefix_free)
4291cb0ef41Sopenharmony_ci{
4301cb0ef41Sopenharmony_ci    return asn1_bio_set_ex(b, BIO_C_SET_PREFIX, prefix, prefix_free);
4311cb0ef41Sopenharmony_ci}
4321cb0ef41Sopenharmony_ci
4331cb0ef41Sopenharmony_ciint BIO_asn1_get_prefix(BIO *b, asn1_ps_func **pprefix,
4341cb0ef41Sopenharmony_ci                        asn1_ps_func **pprefix_free)
4351cb0ef41Sopenharmony_ci{
4361cb0ef41Sopenharmony_ci    return asn1_bio_get_ex(b, BIO_C_GET_PREFIX, pprefix, pprefix_free);
4371cb0ef41Sopenharmony_ci}
4381cb0ef41Sopenharmony_ci
4391cb0ef41Sopenharmony_ciint BIO_asn1_set_suffix(BIO *b, asn1_ps_func *suffix,
4401cb0ef41Sopenharmony_ci                        asn1_ps_func *suffix_free)
4411cb0ef41Sopenharmony_ci{
4421cb0ef41Sopenharmony_ci    return asn1_bio_set_ex(b, BIO_C_SET_SUFFIX, suffix, suffix_free);
4431cb0ef41Sopenharmony_ci}
4441cb0ef41Sopenharmony_ci
4451cb0ef41Sopenharmony_ciint BIO_asn1_get_suffix(BIO *b, asn1_ps_func **psuffix,
4461cb0ef41Sopenharmony_ci                        asn1_ps_func **psuffix_free)
4471cb0ef41Sopenharmony_ci{
4481cb0ef41Sopenharmony_ci    return asn1_bio_get_ex(b, BIO_C_GET_SUFFIX, psuffix, psuffix_free);
4491cb0ef41Sopenharmony_ci}
450