code: /*
   * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
   *
   * Licensed under the OpenSSL license (the "License").  You may not use
   * this file except in compliance with the License.  You can obtain a copy
   * in the file LICENSE in the source distribution or at
   * https://www.openssl.org/source/license.html
   */
  
  /*
   * Here is an STORE loader for ENGINE backed keys.  It relies on deprecated
   * functions, and therefore need to have deprecation warnings suppressed.
   * This file is not compiled at all in a '--api=3 no-deprecated' configuration.
   */
  #define OPENSSL_SUPPRESS_DEPRECATED
  
  #include "apps.h"
  
  #ifndef OPENSSL_NO_ENGINE
  
  # include <stdarg.h>
  # include <string.h>
  # include <openssl/engine.h>
  # include <openssl/store.h>
  
  /*
   * Support for legacy private engine keys via the 'org.openssl.engine:' scheme
   *
   * org.openssl.engine:{engineid}:{keyid}
   *
   * Note: we ONLY support ENGINE_load_private_key() and ENGINE_load_public_key()
   * Note 2: This scheme has a precedent in code in PKIX-SSH. for exactly
   * this sort of purpose.
   */
  
  /* Local definition of OSSL_STORE_LOADER_CTX */
  struct ossl_store_loader_ctx_st {
      ENGINE *e;                   /* Structural reference */
      char *keyid;
      int expected;
      int loaded;                  /* 0 = key not loaded yet, 1 = key loaded */
  };
  
  static OSSL_STORE_LOADER_CTX *OSSL_STORE_LOADER_CTX_new(ENGINE *e, char *keyid)
  {
      OSSL_STORE_LOADER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
  
      if (ctx != NULL) {
          ctx->e = e;
          ctx->keyid = keyid;
      }
      return ctx;
  }
  
  static void OSSL_STORE_LOADER_CTX_free(OSSL_STORE_LOADER_CTX *ctx)
  {
      if (ctx != NULL) {
          ENGINE_free(ctx->e);
          OPENSSL_free(ctx->keyid);
          OPENSSL_free(ctx);
      }
  }
  
  static OSSL_STORE_LOADER_CTX *engine_open(const OSSL_STORE_LOADER *loader,
                                            const char *uri,
                                            const UI_METHOD *ui_method,
                                            void *ui_data)
  {
      const char *p = uri, *q;
      ENGINE *e = NULL;
      char *keyid = NULL;
      OSSL_STORE_LOADER_CTX *ctx = NULL;
  
      if (strncasecmp(p, ENGINE_SCHEME_COLON, sizeof(ENGINE_SCHEME_COLON) - 1)
          != 0)
          return NULL;
      p += sizeof(ENGINE_SCHEME_COLON) - 1;
  
      /* Look for engine ID */
      q = strchr(p, ':');
      if (q != NULL                /* There is both an engine ID and a key ID */
          && p[0] != ':'           /* The engine ID is at least one character */
          && q[1] != '\0') {       /* The key ID is at least one character */
          char engineid[256];
          size_t engineid_l = q - p;
  
          strncpy(engineid, p, engineid_l);
          engineid[engineid_l] = '\0';
          e = ENGINE_by_id(engineid);
  
          keyid = OPENSSL_strdup(q + 1);
      }
  
      if (e != NULL)
          ctx = OSSL_STORE_LOADER_CTX_new(e, keyid);
  
      if (ctx == NULL) {
          OPENSSL_free(keyid);
          ENGINE_free(e);
      }
  
      return ctx;
  }
  
  static int engine_expect(OSSL_STORE_LOADER_CTX *ctx, int expected)
  {
      if (expected == 0
          || expected == OSSL_STORE_INFO_PUBKEY
          || expected == OSSL_STORE_INFO_PKEY) {
          ctx->expected = expected;
          return 1;
      }
      return 0;
  }
  
  static OSSL_STORE_INFO *engine_load(OSSL_STORE_LOADER_CTX *ctx,
                                      const UI_METHOD *ui_method, void *ui_data)
  {
      EVP_PKEY *pkey = NULL, *pubkey = NULL;
      OSSL_STORE_INFO *info = NULL;
  
      if (ctx->loaded == 0) {
          if (ENGINE_init(ctx->e)) {
              if (ctx->expected == 0
                  || ctx->expected == OSSL_STORE_INFO_PKEY)
                  pkey =
                      ENGINE_load_private_key(ctx->e, ctx->keyid,
                                              (UI_METHOD *)ui_method, ui_data);
              if ((pkey == NULL && ctx->expected == 0)
                  || ctx->expected == OSSL_STORE_INFO_PUBKEY)
                  pubkey =
                      ENGINE_load_public_key(ctx->e, ctx->keyid,
                                             (UI_METHOD *)ui_method, ui_data);
              ENGINE_finish(ctx->e);
          }
      }
  
      ctx->loaded = 1;
  
      if (pubkey != NULL)
          info = OSSL_STORE_INFO_new_PUBKEY(pubkey);
      else if (pkey != NULL)
          info = OSSL_STORE_INFO_new_PKEY(pkey);
      if (info == NULL) {
          EVP_PKEY_free(pkey);
          EVP_PKEY_free(pubkey);
      }
      return info;
  }
  
  static int engine_eof(OSSL_STORE_LOADER_CTX *ctx)
  {
      return ctx->loaded != 0;
  }
  
  static int engine_error(OSSL_STORE_LOADER_CTX *ctx)
  {
      return 0;
  }
  
  static int engine_close(OSSL_STORE_LOADER_CTX *ctx)
  {
      OSSL_STORE_LOADER_CTX_free(ctx);
      return 1;
  }
  
  int setup_engine_loader(void)
  {
      OSSL_STORE_LOADER *loader = NULL;
  
      if ((loader = OSSL_STORE_LOADER_new(NULL, ENGINE_SCHEME)) == NULL
          || !OSSL_STORE_LOADER_set_open(loader, engine_open)
          || !OSSL_STORE_LOADER_set_expect(loader, engine_expect)
          || !OSSL_STORE_LOADER_set_load(loader, engine_load)
          || !OSSL_STORE_LOADER_set_eof(loader, engine_eof)
          || !OSSL_STORE_LOADER_set_error(loader, engine_error)
          || !OSSL_STORE_LOADER_set_close(loader, engine_close)
          || !OSSL_STORE_register_loader(loader)) {
          OSSL_STORE_LOADER_free(loader);
          loader = NULL;
      }
  
      return loader != NULL;
  }
  
  void destroy_engine_loader(void)
  {
      OSSL_STORE_LOADER *loader = OSSL_STORE_unregister_loader(ENGINE_SCHEME);
      OSSL_STORE_LOADER_free(loader);
  }
  
  #else  /* !OPENSSL_NO_ENGINE */
  
  int setup_engine_loader(void)
  {
      return 0;
  }
  
  void destroy_engine_loader(void)
  {
  }
  
  #endif
  