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