1e1051a39Sopenharmony_ci/* 2e1051a39Sopenharmony_ci * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. 3e1051a39Sopenharmony_ci * 4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License"). You may not use 5e1051a39Sopenharmony_ci * this file except in compliance with the License. You can obtain a copy 6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at 7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html 8e1051a39Sopenharmony_ci */ 9e1051a39Sopenharmony_ci 10e1051a39Sopenharmony_ci/* We need to use some engine deprecated APIs */ 11e1051a39Sopenharmony_ci#define OPENSSL_SUPPRESS_DEPRECATED 12e1051a39Sopenharmony_ci 13e1051a39Sopenharmony_ci#include "eng_local.h" 14e1051a39Sopenharmony_ci 15e1051a39Sopenharmony_ci/* 16e1051a39Sopenharmony_ci * When querying a ENGINE-specific control command's 'description', this 17e1051a39Sopenharmony_ci * string is used if the ENGINE_CMD_DEFN has cmd_desc set to NULL. 18e1051a39Sopenharmony_ci */ 19e1051a39Sopenharmony_cistatic const char *int_no_description = ""; 20e1051a39Sopenharmony_ci 21e1051a39Sopenharmony_ci/* 22e1051a39Sopenharmony_ci * These internal functions handle 'CMD'-related control commands when the 23e1051a39Sopenharmony_ci * ENGINE in question has asked us to take care of it (ie. the ENGINE did not 24e1051a39Sopenharmony_ci * set the ENGINE_FLAGS_MANUAL_CMD_CTRL flag. 25e1051a39Sopenharmony_ci */ 26e1051a39Sopenharmony_ci 27e1051a39Sopenharmony_cistatic int int_ctrl_cmd_is_null(const ENGINE_CMD_DEFN *defn) 28e1051a39Sopenharmony_ci{ 29e1051a39Sopenharmony_ci if ((defn->cmd_num == 0) || (defn->cmd_name == NULL)) 30e1051a39Sopenharmony_ci return 1; 31e1051a39Sopenharmony_ci return 0; 32e1051a39Sopenharmony_ci} 33e1051a39Sopenharmony_ci 34e1051a39Sopenharmony_cistatic int int_ctrl_cmd_by_name(const ENGINE_CMD_DEFN *defn, const char *s) 35e1051a39Sopenharmony_ci{ 36e1051a39Sopenharmony_ci int idx = 0; 37e1051a39Sopenharmony_ci while (!int_ctrl_cmd_is_null(defn) && (strcmp(defn->cmd_name, s) != 0)) { 38e1051a39Sopenharmony_ci idx++; 39e1051a39Sopenharmony_ci defn++; 40e1051a39Sopenharmony_ci } 41e1051a39Sopenharmony_ci if (int_ctrl_cmd_is_null(defn)) 42e1051a39Sopenharmony_ci /* The given name wasn't found */ 43e1051a39Sopenharmony_ci return -1; 44e1051a39Sopenharmony_ci return idx; 45e1051a39Sopenharmony_ci} 46e1051a39Sopenharmony_ci 47e1051a39Sopenharmony_cistatic int int_ctrl_cmd_by_num(const ENGINE_CMD_DEFN *defn, unsigned int num) 48e1051a39Sopenharmony_ci{ 49e1051a39Sopenharmony_ci int idx = 0; 50e1051a39Sopenharmony_ci /* 51e1051a39Sopenharmony_ci * NB: It is stipulated that 'cmd_defn' lists are ordered by cmd_num. So 52e1051a39Sopenharmony_ci * our searches don't need to take any longer than necessary. 53e1051a39Sopenharmony_ci */ 54e1051a39Sopenharmony_ci while (!int_ctrl_cmd_is_null(defn) && (defn->cmd_num < num)) { 55e1051a39Sopenharmony_ci idx++; 56e1051a39Sopenharmony_ci defn++; 57e1051a39Sopenharmony_ci } 58e1051a39Sopenharmony_ci if (defn->cmd_num == num) 59e1051a39Sopenharmony_ci return idx; 60e1051a39Sopenharmony_ci /* The given cmd_num wasn't found */ 61e1051a39Sopenharmony_ci return -1; 62e1051a39Sopenharmony_ci} 63e1051a39Sopenharmony_ci 64e1051a39Sopenharmony_cistatic int int_ctrl_helper(ENGINE *e, int cmd, long i, void *p, 65e1051a39Sopenharmony_ci void (*f) (void)) 66e1051a39Sopenharmony_ci{ 67e1051a39Sopenharmony_ci int idx; 68e1051a39Sopenharmony_ci char *s = (char *)p; 69e1051a39Sopenharmony_ci const ENGINE_CMD_DEFN *cdp; 70e1051a39Sopenharmony_ci 71e1051a39Sopenharmony_ci /* Take care of the easy one first (eg. it requires no searches) */ 72e1051a39Sopenharmony_ci if (cmd == ENGINE_CTRL_GET_FIRST_CMD_TYPE) { 73e1051a39Sopenharmony_ci if ((e->cmd_defns == NULL) || int_ctrl_cmd_is_null(e->cmd_defns)) 74e1051a39Sopenharmony_ci return 0; 75e1051a39Sopenharmony_ci return e->cmd_defns->cmd_num; 76e1051a39Sopenharmony_ci } 77e1051a39Sopenharmony_ci /* One or two commands require that "p" be a valid string buffer */ 78e1051a39Sopenharmony_ci if ((cmd == ENGINE_CTRL_GET_CMD_FROM_NAME) || 79e1051a39Sopenharmony_ci (cmd == ENGINE_CTRL_GET_NAME_FROM_CMD) || 80e1051a39Sopenharmony_ci (cmd == ENGINE_CTRL_GET_DESC_FROM_CMD)) { 81e1051a39Sopenharmony_ci if (s == NULL) { 82e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER); 83e1051a39Sopenharmony_ci return -1; 84e1051a39Sopenharmony_ci } 85e1051a39Sopenharmony_ci } 86e1051a39Sopenharmony_ci /* Now handle cmd_name -> cmd_num conversion */ 87e1051a39Sopenharmony_ci if (cmd == ENGINE_CTRL_GET_CMD_FROM_NAME) { 88e1051a39Sopenharmony_ci if ((e->cmd_defns == NULL) 89e1051a39Sopenharmony_ci || ((idx = int_ctrl_cmd_by_name(e->cmd_defns, s)) < 0)) { 90e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME); 91e1051a39Sopenharmony_ci return -1; 92e1051a39Sopenharmony_ci } 93e1051a39Sopenharmony_ci return e->cmd_defns[idx].cmd_num; 94e1051a39Sopenharmony_ci } 95e1051a39Sopenharmony_ci /* 96e1051a39Sopenharmony_ci * For the rest of the commands, the 'long' argument must specify a valid 97e1051a39Sopenharmony_ci * command number - so we need to conduct a search. 98e1051a39Sopenharmony_ci */ 99e1051a39Sopenharmony_ci if ((e->cmd_defns == NULL) 100e1051a39Sopenharmony_ci || ((idx = int_ctrl_cmd_by_num(e->cmd_defns, (unsigned int)i)) < 0)) { 101e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NUMBER); 102e1051a39Sopenharmony_ci return -1; 103e1051a39Sopenharmony_ci } 104e1051a39Sopenharmony_ci /* Now the logic splits depending on command type */ 105e1051a39Sopenharmony_ci cdp = &e->cmd_defns[idx]; 106e1051a39Sopenharmony_ci switch (cmd) { 107e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_NEXT_CMD_TYPE: 108e1051a39Sopenharmony_ci cdp++; 109e1051a39Sopenharmony_ci return int_ctrl_cmd_is_null(cdp) ? 0 : cdp->cmd_num; 110e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD: 111e1051a39Sopenharmony_ci return strlen(cdp->cmd_name); 112e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_NAME_FROM_CMD: 113e1051a39Sopenharmony_ci return strlen(strcpy(s, cdp->cmd_name)); 114e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD: 115e1051a39Sopenharmony_ci return strlen(cdp->cmd_desc == NULL ? int_no_description 116e1051a39Sopenharmony_ci : cdp->cmd_desc); 117e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_DESC_FROM_CMD: 118e1051a39Sopenharmony_ci return strlen(strcpy(s, cdp->cmd_desc == NULL ? int_no_description 119e1051a39Sopenharmony_ci : cdp->cmd_desc)); 120e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_CMD_FLAGS: 121e1051a39Sopenharmony_ci return cdp->cmd_flags; 122e1051a39Sopenharmony_ci } 123e1051a39Sopenharmony_ci /* Shouldn't really be here ... */ 124e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR); 125e1051a39Sopenharmony_ci return -1; 126e1051a39Sopenharmony_ci} 127e1051a39Sopenharmony_ci 128e1051a39Sopenharmony_ciint ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)) 129e1051a39Sopenharmony_ci{ 130e1051a39Sopenharmony_ci int ctrl_exists, ref_exists; 131e1051a39Sopenharmony_ci if (e == NULL) { 132e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER); 133e1051a39Sopenharmony_ci return 0; 134e1051a39Sopenharmony_ci } 135e1051a39Sopenharmony_ci if (!CRYPTO_THREAD_write_lock(global_engine_lock)) 136e1051a39Sopenharmony_ci return 0; 137e1051a39Sopenharmony_ci ref_exists = ((e->struct_ref > 0) ? 1 : 0); 138e1051a39Sopenharmony_ci CRYPTO_THREAD_unlock(global_engine_lock); 139e1051a39Sopenharmony_ci ctrl_exists = ((e->ctrl == NULL) ? 0 : 1); 140e1051a39Sopenharmony_ci if (!ref_exists) { 141e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NO_REFERENCE); 142e1051a39Sopenharmony_ci return 0; 143e1051a39Sopenharmony_ci } 144e1051a39Sopenharmony_ci /* 145e1051a39Sopenharmony_ci * Intercept any "root-level" commands before trying to hand them on to 146e1051a39Sopenharmony_ci * ctrl() handlers. 147e1051a39Sopenharmony_ci */ 148e1051a39Sopenharmony_ci switch (cmd) { 149e1051a39Sopenharmony_ci case ENGINE_CTRL_HAS_CTRL_FUNCTION: 150e1051a39Sopenharmony_ci return ctrl_exists; 151e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_FIRST_CMD_TYPE: 152e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_NEXT_CMD_TYPE: 153e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_CMD_FROM_NAME: 154e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD: 155e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_NAME_FROM_CMD: 156e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD: 157e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_DESC_FROM_CMD: 158e1051a39Sopenharmony_ci case ENGINE_CTRL_GET_CMD_FLAGS: 159e1051a39Sopenharmony_ci if (ctrl_exists && !(e->flags & ENGINE_FLAGS_MANUAL_CMD_CTRL)) 160e1051a39Sopenharmony_ci return int_ctrl_helper(e, cmd, i, p, f); 161e1051a39Sopenharmony_ci if (!ctrl_exists) { 162e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NO_CONTROL_FUNCTION); 163e1051a39Sopenharmony_ci /* 164e1051a39Sopenharmony_ci * For these cmd-related functions, failure is indicated by a -1 165e1051a39Sopenharmony_ci * return value (because 0 is used as a valid return in some 166e1051a39Sopenharmony_ci * places). 167e1051a39Sopenharmony_ci */ 168e1051a39Sopenharmony_ci return -1; 169e1051a39Sopenharmony_ci } 170e1051a39Sopenharmony_ci default: 171e1051a39Sopenharmony_ci break; 172e1051a39Sopenharmony_ci } 173e1051a39Sopenharmony_ci /* Anything else requires a ctrl() handler to exist. */ 174e1051a39Sopenharmony_ci if (!ctrl_exists) { 175e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NO_CONTROL_FUNCTION); 176e1051a39Sopenharmony_ci return 0; 177e1051a39Sopenharmony_ci } 178e1051a39Sopenharmony_ci return e->ctrl(e, cmd, i, p, f); 179e1051a39Sopenharmony_ci} 180e1051a39Sopenharmony_ci 181e1051a39Sopenharmony_ciint ENGINE_cmd_is_executable(ENGINE *e, int cmd) 182e1051a39Sopenharmony_ci{ 183e1051a39Sopenharmony_ci int flags; 184e1051a39Sopenharmony_ci if ((flags = 185e1051a39Sopenharmony_ci ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, cmd, NULL, NULL)) < 0) { 186e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NUMBER); 187e1051a39Sopenharmony_ci return 0; 188e1051a39Sopenharmony_ci } 189e1051a39Sopenharmony_ci if (!(flags & ENGINE_CMD_FLAG_NO_INPUT) && 190e1051a39Sopenharmony_ci !(flags & ENGINE_CMD_FLAG_NUMERIC) && 191e1051a39Sopenharmony_ci !(flags & ENGINE_CMD_FLAG_STRING)) 192e1051a39Sopenharmony_ci return 0; 193e1051a39Sopenharmony_ci return 1; 194e1051a39Sopenharmony_ci} 195e1051a39Sopenharmony_ci 196e1051a39Sopenharmony_ciint ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name, 197e1051a39Sopenharmony_ci long i, void *p, void (*f) (void), int cmd_optional) 198e1051a39Sopenharmony_ci{ 199e1051a39Sopenharmony_ci int num; 200e1051a39Sopenharmony_ci 201e1051a39Sopenharmony_ci if (e == NULL || cmd_name == NULL) { 202e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER); 203e1051a39Sopenharmony_ci return 0; 204e1051a39Sopenharmony_ci } 205e1051a39Sopenharmony_ci if (e->ctrl == NULL 206e1051a39Sopenharmony_ci || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME, 207e1051a39Sopenharmony_ci 0, (void *)cmd_name, NULL)) <= 0) { 208e1051a39Sopenharmony_ci /* 209e1051a39Sopenharmony_ci * If the command didn't *have* to be supported, we fake success. 210e1051a39Sopenharmony_ci * This allows certain settings to be specified for multiple ENGINEs 211e1051a39Sopenharmony_ci * and only require a change of ENGINE id (without having to 212e1051a39Sopenharmony_ci * selectively apply settings). Eg. changing from a hardware device 213e1051a39Sopenharmony_ci * back to the regular software ENGINE without editing the config 214e1051a39Sopenharmony_ci * file, etc. 215e1051a39Sopenharmony_ci */ 216e1051a39Sopenharmony_ci if (cmd_optional) { 217e1051a39Sopenharmony_ci ERR_clear_error(); 218e1051a39Sopenharmony_ci return 1; 219e1051a39Sopenharmony_ci } 220e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME); 221e1051a39Sopenharmony_ci return 0; 222e1051a39Sopenharmony_ci } 223e1051a39Sopenharmony_ci /* 224e1051a39Sopenharmony_ci * Force the result of the control command to 0 or 1, for the reasons 225e1051a39Sopenharmony_ci * mentioned before. 226e1051a39Sopenharmony_ci */ 227e1051a39Sopenharmony_ci if (ENGINE_ctrl(e, num, i, p, f) > 0) 228e1051a39Sopenharmony_ci return 1; 229e1051a39Sopenharmony_ci return 0; 230e1051a39Sopenharmony_ci} 231e1051a39Sopenharmony_ci 232e1051a39Sopenharmony_ciint ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg, 233e1051a39Sopenharmony_ci int cmd_optional) 234e1051a39Sopenharmony_ci{ 235e1051a39Sopenharmony_ci int num, flags; 236e1051a39Sopenharmony_ci long l; 237e1051a39Sopenharmony_ci char *ptr; 238e1051a39Sopenharmony_ci 239e1051a39Sopenharmony_ci if (e == NULL || cmd_name == NULL) { 240e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER); 241e1051a39Sopenharmony_ci return 0; 242e1051a39Sopenharmony_ci } 243e1051a39Sopenharmony_ci if (e->ctrl == NULL 244e1051a39Sopenharmony_ci || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME, 245e1051a39Sopenharmony_ci 0, (void *)cmd_name, NULL)) <= 0) { 246e1051a39Sopenharmony_ci /* 247e1051a39Sopenharmony_ci * If the command didn't *have* to be supported, we fake success. 248e1051a39Sopenharmony_ci * This allows certain settings to be specified for multiple ENGINEs 249e1051a39Sopenharmony_ci * and only require a change of ENGINE id (without having to 250e1051a39Sopenharmony_ci * selectively apply settings). Eg. changing from a hardware device 251e1051a39Sopenharmony_ci * back to the regular software ENGINE without editing the config 252e1051a39Sopenharmony_ci * file, etc. 253e1051a39Sopenharmony_ci */ 254e1051a39Sopenharmony_ci if (cmd_optional) { 255e1051a39Sopenharmony_ci ERR_clear_error(); 256e1051a39Sopenharmony_ci return 1; 257e1051a39Sopenharmony_ci } 258e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME); 259e1051a39Sopenharmony_ci return 0; 260e1051a39Sopenharmony_ci } 261e1051a39Sopenharmony_ci if (!ENGINE_cmd_is_executable(e, num)) { 262e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_CMD_NOT_EXECUTABLE); 263e1051a39Sopenharmony_ci return 0; 264e1051a39Sopenharmony_ci } 265e1051a39Sopenharmony_ci 266e1051a39Sopenharmony_ci flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, num, NULL, NULL); 267e1051a39Sopenharmony_ci if (flags < 0) { 268e1051a39Sopenharmony_ci /* 269e1051a39Sopenharmony_ci * Shouldn't happen, given that ENGINE_cmd_is_executable() returned 270e1051a39Sopenharmony_ci * success. 271e1051a39Sopenharmony_ci */ 272e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR); 273e1051a39Sopenharmony_ci return 0; 274e1051a39Sopenharmony_ci } 275e1051a39Sopenharmony_ci /* 276e1051a39Sopenharmony_ci * If the command takes no input, there must be no input. And vice versa. 277e1051a39Sopenharmony_ci */ 278e1051a39Sopenharmony_ci if (flags & ENGINE_CMD_FLAG_NO_INPUT) { 279e1051a39Sopenharmony_ci if (arg != NULL) { 280e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_NO_INPUT); 281e1051a39Sopenharmony_ci return 0; 282e1051a39Sopenharmony_ci } 283e1051a39Sopenharmony_ci /* 284e1051a39Sopenharmony_ci * We deliberately force the result of ENGINE_ctrl() to 0 or 1 rather 285e1051a39Sopenharmony_ci * than returning it as "return data". This is to ensure usage of 286e1051a39Sopenharmony_ci * these commands is consistent across applications and that certain 287e1051a39Sopenharmony_ci * applications don't understand it one way, and others another. 288e1051a39Sopenharmony_ci */ 289e1051a39Sopenharmony_ci if (ENGINE_ctrl(e, num, 0, (void *)arg, NULL) > 0) 290e1051a39Sopenharmony_ci return 1; 291e1051a39Sopenharmony_ci return 0; 292e1051a39Sopenharmony_ci } 293e1051a39Sopenharmony_ci /* So, we require input */ 294e1051a39Sopenharmony_ci if (arg == NULL) { 295e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_INPUT); 296e1051a39Sopenharmony_ci return 0; 297e1051a39Sopenharmony_ci } 298e1051a39Sopenharmony_ci /* If it takes string input, that's easy */ 299e1051a39Sopenharmony_ci if (flags & ENGINE_CMD_FLAG_STRING) { 300e1051a39Sopenharmony_ci /* Same explanation as above */ 301e1051a39Sopenharmony_ci if (ENGINE_ctrl(e, num, 0, (void *)arg, NULL) > 0) 302e1051a39Sopenharmony_ci return 1; 303e1051a39Sopenharmony_ci return 0; 304e1051a39Sopenharmony_ci } 305e1051a39Sopenharmony_ci /* 306e1051a39Sopenharmony_ci * If it doesn't take numeric either, then it is unsupported for use in a 307e1051a39Sopenharmony_ci * config-setting situation, which is what this function is for. This 308e1051a39Sopenharmony_ci * should never happen though, because ENGINE_cmd_is_executable() was 309e1051a39Sopenharmony_ci * used. 310e1051a39Sopenharmony_ci */ 311e1051a39Sopenharmony_ci if (!(flags & ENGINE_CMD_FLAG_NUMERIC)) { 312e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR); 313e1051a39Sopenharmony_ci return 0; 314e1051a39Sopenharmony_ci } 315e1051a39Sopenharmony_ci l = strtol(arg, &ptr, 10); 316e1051a39Sopenharmony_ci if ((arg == ptr) || (*ptr != '\0')) { 317e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ENGINE, ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER); 318e1051a39Sopenharmony_ci return 0; 319e1051a39Sopenharmony_ci } 320e1051a39Sopenharmony_ci /* 321e1051a39Sopenharmony_ci * Force the result of the control command to 0 or 1, for the reasons 322e1051a39Sopenharmony_ci * mentioned before. 323e1051a39Sopenharmony_ci */ 324e1051a39Sopenharmony_ci if (ENGINE_ctrl(e, num, l, NULL, NULL) > 0) 325e1051a39Sopenharmony_ci return 1; 326e1051a39Sopenharmony_ci return 0; 327e1051a39Sopenharmony_ci} 328