1/* 2 * $Id: ossl_engine.c 44659 2014-01-19 16:28:53Z nagachika $ 3 * 'OpenSSL for Ruby' project 4 * Copyright (C) 2003 GOTOU Yuuzou <gotoyuzo@notwork.org> 5 * All rights reserved. 6 */ 7/* 8 * This program is licenced under the same licence as Ruby. 9 * (See the file 'LICENCE'.) 10 */ 11#include "ossl.h" 12 13#if defined(OSSL_ENGINE_ENABLED) 14 15#define WrapEngine(klass, obj, engine) do { \ 16 if (!(engine)) { \ 17 ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ 18 } \ 19 (obj) = Data_Wrap_Struct((klass), 0, ENGINE_free, (engine)); \ 20} while(0) 21#define GetEngine(obj, engine) do { \ 22 Data_Get_Struct((obj), ENGINE, (engine)); \ 23 if (!(engine)) { \ 24 ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ 25 } \ 26} while (0) 27#define SafeGetEngine(obj, engine) do { \ 28 OSSL_Check_Kind((obj), cEngine); \ 29 GetPKCS7((obj), (engine)); \ 30} while (0) 31 32/* 33 * Classes 34 */ 35VALUE cEngine; 36VALUE eEngineError; 37 38/* 39 * Private 40 */ 41#define OSSL_ENGINE_LOAD_IF_MATCH(x) \ 42do{\ 43 if(!strcmp(#x, RSTRING_PTR(name))){\ 44 ENGINE_load_##x();\ 45 return Qtrue;\ 46 }\ 47}while(0) 48 49static VALUE 50ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) 51{ 52#if !defined(HAVE_ENGINE_LOAD_BUILTIN_ENGINES) 53 return Qnil; 54#else 55 VALUE name; 56 57 rb_scan_args(argc, argv, "01", &name); 58 if(NIL_P(name)){ 59 ENGINE_load_builtin_engines(); 60 return Qtrue; 61 } 62 StringValue(name); 63#ifndef OPENSSL_NO_STATIC_ENGINE 64#if HAVE_ENGINE_LOAD_DYNAMIC 65 OSSL_ENGINE_LOAD_IF_MATCH(dynamic); 66#endif 67#if HAVE_ENGINE_LOAD_4758CCA 68 OSSL_ENGINE_LOAD_IF_MATCH(4758cca); 69#endif 70#if HAVE_ENGINE_LOAD_AEP 71 OSSL_ENGINE_LOAD_IF_MATCH(aep); 72#endif 73#if HAVE_ENGINE_LOAD_ATALLA 74 OSSL_ENGINE_LOAD_IF_MATCH(atalla); 75#endif 76#if HAVE_ENGINE_LOAD_CHIL 77 OSSL_ENGINE_LOAD_IF_MATCH(chil); 78#endif 79#if HAVE_ENGINE_LOAD_CSWIFT 80 OSSL_ENGINE_LOAD_IF_MATCH(cswift); 81#endif 82#if HAVE_ENGINE_LOAD_NURON 83 OSSL_ENGINE_LOAD_IF_MATCH(nuron); 84#endif 85#if HAVE_ENGINE_LOAD_SUREWARE 86 OSSL_ENGINE_LOAD_IF_MATCH(sureware); 87#endif 88#if HAVE_ENGINE_LOAD_UBSEC 89 OSSL_ENGINE_LOAD_IF_MATCH(ubsec); 90#endif 91#if HAVE_ENGINE_LOAD_PADLOCK 92 OSSL_ENGINE_LOAD_IF_MATCH(padlock); 93#endif 94#if HAVE_ENGINE_LOAD_CAPI 95 OSSL_ENGINE_LOAD_IF_MATCH(capi); 96#endif 97#if HAVE_ENGINE_LOAD_GMP 98 OSSL_ENGINE_LOAD_IF_MATCH(gmp); 99#endif 100#if HAVE_ENGINE_LOAD_GOST 101 OSSL_ENGINE_LOAD_IF_MATCH(gost); 102#endif 103#if HAVE_ENGINE_LOAD_CRYPTODEV 104 OSSL_ENGINE_LOAD_IF_MATCH(cryptodev); 105#endif 106#if HAVE_ENGINE_LOAD_AESNI 107 OSSL_ENGINE_LOAD_IF_MATCH(aesni); 108#endif 109#endif 110#ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO 111 OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto); 112#endif 113 OSSL_ENGINE_LOAD_IF_MATCH(openssl); 114 rb_warning("no such builtin loader for `%s'", RSTRING_PTR(name)); 115 return Qnil; 116#endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */ 117} 118 119static VALUE 120ossl_engine_s_cleanup(VALUE self) 121{ 122#if defined(HAVE_ENGINE_CLEANUP) 123 ENGINE_cleanup(); 124#endif 125 return Qnil; 126} 127 128static VALUE 129ossl_engine_s_engines(VALUE klass) 130{ 131 ENGINE *e; 132 VALUE ary, obj; 133 134 ary = rb_ary_new(); 135 for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)){ 136 /* Need a ref count of two here because of ENGINE_free being 137 * called internally by OpenSSL when moving to the next ENGINE 138 * and by us when releasing the ENGINE reference */ 139 ENGINE_up_ref(e); 140 WrapEngine(klass, obj, e); 141 rb_ary_push(ary, obj); 142 } 143 144 return ary; 145} 146 147static VALUE 148ossl_engine_s_by_id(VALUE klass, VALUE id) 149{ 150 ENGINE *e; 151 VALUE obj; 152 153 StringValue(id); 154 ossl_engine_s_load(1, &id, klass); 155 if(!(e = ENGINE_by_id(RSTRING_PTR(id)))) 156 ossl_raise(eEngineError, NULL); 157 WrapEngine(klass, obj, e); 158 if(rb_block_given_p()) rb_yield(obj); 159 if(!ENGINE_init(e)) 160 ossl_raise(eEngineError, NULL); 161 ENGINE_ctrl(e, ENGINE_CTRL_SET_PASSWORD_CALLBACK, 162 0, NULL, (void(*)(void))ossl_pem_passwd_cb); 163 ERR_clear_error(); 164 165 return obj; 166} 167 168static VALUE 169ossl_engine_s_alloc(VALUE klass) 170{ 171 ENGINE *e; 172 VALUE obj; 173 174 if (!(e = ENGINE_new())) { 175 ossl_raise(eEngineError, NULL); 176 } 177 WrapEngine(klass, obj, e); 178 179 return obj; 180} 181 182static VALUE 183ossl_engine_get_id(VALUE self) 184{ 185 ENGINE *e; 186 GetEngine(self, e); 187 return rb_str_new2(ENGINE_get_id(e)); 188} 189 190static VALUE 191ossl_engine_get_name(VALUE self) 192{ 193 ENGINE *e; 194 GetEngine(self, e); 195 return rb_str_new2(ENGINE_get_name(e)); 196} 197 198static VALUE 199ossl_engine_finish(VALUE self) 200{ 201 ENGINE *e; 202 203 GetEngine(self, e); 204 if(!ENGINE_finish(e)) ossl_raise(eEngineError, NULL); 205 206 return Qnil; 207} 208 209#if defined(HAVE_ENGINE_GET_CIPHER) 210static VALUE 211ossl_engine_get_cipher(VALUE self, VALUE name) 212{ 213 ENGINE *e; 214 const EVP_CIPHER *ciph, *tmp; 215 char *s; 216 int nid; 217 218 s = StringValuePtr(name); 219 tmp = EVP_get_cipherbyname(s); 220 if(!tmp) ossl_raise(eEngineError, "no such cipher `%s'", s); 221 nid = EVP_CIPHER_nid(tmp); 222 GetEngine(self, e); 223 ciph = ENGINE_get_cipher(e, nid); 224 if(!ciph) ossl_raise(eEngineError, NULL); 225 226 return ossl_cipher_new(ciph); 227} 228#else 229#define ossl_engine_get_cipher rb_f_notimplement 230#endif 231 232#if defined(HAVE_ENGINE_GET_DIGEST) 233static VALUE 234ossl_engine_get_digest(VALUE self, VALUE name) 235{ 236 ENGINE *e; 237 const EVP_MD *md, *tmp; 238 char *s; 239 int nid; 240 241 s = StringValuePtr(name); 242 tmp = EVP_get_digestbyname(s); 243 if(!tmp) ossl_raise(eEngineError, "no such digest `%s'", s); 244 nid = EVP_MD_nid(tmp); 245 GetEngine(self, e); 246 md = ENGINE_get_digest(e, nid); 247 if(!md) ossl_raise(eEngineError, NULL); 248 249 return ossl_digest_new(md); 250} 251#else 252#define ossl_engine_get_digest rb_f_notimplement 253#endif 254 255static VALUE 256ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self) 257{ 258 ENGINE *e; 259 EVP_PKEY *pkey; 260 VALUE id, data, obj; 261 char *sid, *sdata; 262 263 rb_scan_args(argc, argv, "02", &id, &data); 264 sid = NIL_P(id) ? NULL : StringValuePtr(id); 265 sdata = NIL_P(data) ? NULL : StringValuePtr(data); 266 GetEngine(self, e); 267#if OPENSSL_VERSION_NUMBER < 0x00907000L 268 pkey = ENGINE_load_private_key(e, sid, sdata); 269#else 270 pkey = ENGINE_load_private_key(e, sid, NULL, sdata); 271#endif 272 if (!pkey) ossl_raise(eEngineError, NULL); 273 obj = ossl_pkey_new(pkey); 274 OSSL_PKEY_SET_PRIVATE(obj); 275 276 return obj; 277} 278 279static VALUE 280ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) 281{ 282 ENGINE *e; 283 EVP_PKEY *pkey; 284 VALUE id, data; 285 char *sid, *sdata; 286 287 rb_scan_args(argc, argv, "02", &id, &data); 288 sid = NIL_P(id) ? NULL : StringValuePtr(id); 289 sdata = NIL_P(data) ? NULL : StringValuePtr(data); 290 GetEngine(self, e); 291#if OPENSSL_VERSION_NUMBER < 0x00907000L 292 pkey = ENGINE_load_public_key(e, sid, sdata); 293#else 294 pkey = ENGINE_load_public_key(e, sid, NULL, sdata); 295#endif 296 if (!pkey) ossl_raise(eEngineError, NULL); 297 298 return ossl_pkey_new(pkey); 299} 300 301static VALUE 302ossl_engine_set_default(VALUE self, VALUE flag) 303{ 304 ENGINE *e; 305 int f = NUM2INT(flag); 306 307 GetEngine(self, e); 308 ENGINE_set_default(e, f); 309 310 return Qtrue; 311} 312 313static VALUE 314ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self) 315{ 316 ENGINE *e; 317 VALUE cmd, val; 318 int ret; 319 320 GetEngine(self, e); 321 rb_scan_args(argc, argv, "11", &cmd, &val); 322 StringValue(cmd); 323 if (!NIL_P(val)) StringValue(val); 324 ret = ENGINE_ctrl_cmd_string(e, RSTRING_PTR(cmd), 325 NIL_P(val) ? NULL : RSTRING_PTR(val), 0); 326 if (!ret) ossl_raise(eEngineError, NULL); 327 328 return self; 329} 330 331static VALUE 332ossl_engine_cmd_flag_to_name(int flag) 333{ 334 switch(flag){ 335 case ENGINE_CMD_FLAG_NUMERIC: return rb_str_new2("NUMERIC"); 336 case ENGINE_CMD_FLAG_STRING: return rb_str_new2("STRING"); 337 case ENGINE_CMD_FLAG_NO_INPUT: return rb_str_new2("NO_INPUT"); 338 case ENGINE_CMD_FLAG_INTERNAL: return rb_str_new2("INTERNAL"); 339 default: return rb_str_new2("UNKNOWN"); 340 } 341} 342 343static VALUE 344ossl_engine_get_cmds(VALUE self) 345{ 346 ENGINE *e; 347 const ENGINE_CMD_DEFN *defn, *p; 348 VALUE ary, tmp; 349 350 GetEngine(self, e); 351 ary = rb_ary_new(); 352 if ((defn = ENGINE_get_cmd_defns(e)) != NULL){ 353 for (p = defn; p->cmd_num > 0; p++){ 354 tmp = rb_ary_new(); 355 rb_ary_push(tmp, rb_str_new2(p->cmd_name)); 356 rb_ary_push(tmp, rb_str_new2(p->cmd_desc)); 357 rb_ary_push(tmp, ossl_engine_cmd_flag_to_name(p->cmd_flags)); 358 rb_ary_push(ary, tmp); 359 } 360 } 361 362 return ary; 363} 364 365static VALUE 366ossl_engine_inspect(VALUE self) 367{ 368 ENGINE *e; 369 370 GetEngine(self, e); 371 return rb_sprintf("#<%"PRIsVALUE" id=\"%s\" name=\"%s\">", 372 rb_obj_class(self), ENGINE_get_id(e), ENGINE_get_name(e)); 373} 374 375#define DefEngineConst(x) rb_define_const(cEngine, #x, INT2NUM(ENGINE_##x)) 376 377void 378Init_ossl_engine() 379{ 380 cEngine = rb_define_class_under(mOSSL, "Engine", rb_cObject); 381 eEngineError = rb_define_class_under(cEngine, "EngineError", eOSSLError); 382 383 rb_define_alloc_func(cEngine, ossl_engine_s_alloc); 384 rb_define_singleton_method(cEngine, "load", ossl_engine_s_load, -1); 385 rb_define_singleton_method(cEngine, "cleanup", ossl_engine_s_cleanup, 0); 386 rb_define_singleton_method(cEngine, "engines", ossl_engine_s_engines, 0); 387 rb_define_singleton_method(cEngine, "by_id", ossl_engine_s_by_id, 1); 388 rb_undef_method(CLASS_OF(cEngine), "new"); 389 390 rb_define_method(cEngine, "id", ossl_engine_get_id, 0); 391 rb_define_method(cEngine, "name", ossl_engine_get_name, 0); 392 rb_define_method(cEngine, "finish", ossl_engine_finish, 0); 393 rb_define_method(cEngine, "cipher", ossl_engine_get_cipher, 1); 394 rb_define_method(cEngine, "digest", ossl_engine_get_digest, 1); 395 rb_define_method(cEngine, "load_private_key", ossl_engine_load_privkey, -1); 396 rb_define_method(cEngine, "load_public_key", ossl_engine_load_pubkey, -1); 397 rb_define_method(cEngine, "set_default", ossl_engine_set_default, 1); 398 rb_define_method(cEngine, "ctrl_cmd", ossl_engine_ctrl_cmd, -1); 399 rb_define_method(cEngine, "cmds", ossl_engine_get_cmds, 0); 400 rb_define_method(cEngine, "inspect", ossl_engine_inspect, 0); 401 402 DefEngineConst(METHOD_RSA); 403 DefEngineConst(METHOD_DSA); 404 DefEngineConst(METHOD_DH); 405 DefEngineConst(METHOD_RAND); 406#ifdef ENGINE_METHOD_BN_MOD_EXP 407 DefEngineConst(METHOD_BN_MOD_EXP); 408#endif 409#ifdef ENGINE_METHOD_BN_MOD_EXP_CRT 410 DefEngineConst(METHOD_BN_MOD_EXP_CRT); 411#endif 412#ifdef ENGINE_METHOD_CIPHERS 413 DefEngineConst(METHOD_CIPHERS); 414#endif 415#ifdef ENGINE_METHOD_DIGESTS 416 DefEngineConst(METHOD_DIGESTS); 417#endif 418 DefEngineConst(METHOD_ALL); 419 DefEngineConst(METHOD_NONE); 420} 421#else 422void 423Init_ossl_engine() 424{ 425} 426#endif 427