1/*
2 * $Id: ossl_x509store.c 37070 2012-10-02 19:36:26Z drbrain $
3 * 'OpenSSL for Ruby' project
4 * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
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#define WrapX509Store(klass, obj, st) do { \
14    if (!(st)) { \
15	ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \
16    } \
17    (obj) = Data_Wrap_Struct((klass), 0, X509_STORE_free, (st)); \
18} while (0)
19#define GetX509Store(obj, st) do { \
20    Data_Get_Struct((obj), X509_STORE, (st)); \
21    if (!(st)) { \
22	ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \
23    } \
24} while (0)
25#define SafeGetX509Store(obj, st) do { \
26    OSSL_Check_Kind((obj), cX509Store); \
27    GetX509Store((obj), (st)); \
28} while (0)
29
30#define WrapX509StCtx(klass, obj, ctx) do { \
31    if (!(ctx)) { \
32	ossl_raise(rb_eRuntimeError, "STORE_CTX wasn't initialized!"); \
33    } \
34    (obj) = Data_Wrap_Struct((klass), 0, ossl_x509stctx_free, (ctx)); \
35} while (0)
36#define GetX509StCtx(obj, ctx) do { \
37    Data_Get_Struct((obj), X509_STORE_CTX, (ctx)); \
38    if (!(ctx)) { \
39	ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \
40    } \
41} while (0)
42#define SafeGetX509StCtx(obj, storep) do { \
43    OSSL_Check_Kind((obj), cX509StoreContext); \
44    GetX509Store((obj), (ctx)); \
45} while (0)
46
47/*
48 * Classes
49 */
50VALUE cX509Store;
51VALUE cX509StoreContext;
52VALUE eX509StoreError;
53
54/*
55 * Public functions
56 */
57VALUE
58ossl_x509store_new(X509_STORE *store)
59{
60    VALUE obj;
61
62    WrapX509Store(cX509Store, obj, store);
63
64    return obj;
65}
66
67X509_STORE *
68GetX509StorePtr(VALUE obj)
69{
70    X509_STORE *store;
71
72    SafeGetX509Store(obj, store);
73
74    return store;
75}
76
77X509_STORE *
78DupX509StorePtr(VALUE obj)
79{
80    X509_STORE *store;
81
82    SafeGetX509Store(obj, store);
83    CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE);
84
85    return store;
86}
87
88/*
89 * Private functions
90 */
91static VALUE
92ossl_x509store_alloc(VALUE klass)
93{
94    X509_STORE *store;
95    VALUE obj;
96
97    if((store = X509_STORE_new()) == NULL){
98        ossl_raise(eX509StoreError, NULL);
99    }
100    WrapX509Store(klass, obj, store);
101
102    return obj;
103}
104
105/*
106 * General callback for OpenSSL verify
107 */
108static VALUE
109ossl_x509store_set_vfy_cb(VALUE self, VALUE cb)
110{
111    X509_STORE *store;
112
113    GetX509Store(self, store);
114    X509_STORE_set_ex_data(store, ossl_verify_cb_idx, (void*)cb);
115    rb_iv_set(self, "@verify_callback", cb);
116
117    return cb;
118}
119
120
121/*
122 * call-seq:
123 *    X509::Store.new => store
124 *
125 */
126static VALUE
127ossl_x509store_initialize(int argc, VALUE *argv, VALUE self)
128{
129    X509_STORE *store;
130
131/* BUG: This method takes any number of arguments but appears to ignore them. */
132    GetX509Store(self, store);
133    store->ex_data.sk = NULL;
134    X509_STORE_set_verify_cb_func(store, ossl_verify_cb);
135    ossl_x509store_set_vfy_cb(self, Qnil);
136
137#if (OPENSSL_VERSION_NUMBER < 0x00907000L)
138    rb_iv_set(self, "@flags", INT2NUM(0));
139    rb_iv_set(self, "@purpose", INT2NUM(0));
140    rb_iv_set(self, "@trust", INT2NUM(0));
141#endif
142
143    /* last verification status */
144    rb_iv_set(self, "@error", Qnil);
145    rb_iv_set(self, "@error_string", Qnil);
146    rb_iv_set(self, "@chain", Qnil);
147    rb_iv_set(self, "@time", Qnil);
148
149    return self;
150}
151
152static VALUE
153ossl_x509store_set_flags(VALUE self, VALUE flags)
154{
155#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
156    X509_STORE *store;
157    long f = NUM2LONG(flags);
158
159    GetX509Store(self, store);
160    X509_STORE_set_flags(store, f);
161#else
162    rb_iv_set(self, "@flags", flags);
163#endif
164
165    return flags;
166}
167
168static VALUE
169ossl_x509store_set_purpose(VALUE self, VALUE purpose)
170{
171#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
172    X509_STORE *store;
173    int p = NUM2INT(purpose);
174
175    GetX509Store(self, store);
176    X509_STORE_set_purpose(store, p);
177#else
178    rb_iv_set(self, "@purpose", purpose);
179#endif
180
181    return purpose;
182}
183
184static VALUE
185ossl_x509store_set_trust(VALUE self, VALUE trust)
186{
187#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
188    X509_STORE *store;
189    int t = NUM2INT(trust);
190
191    GetX509Store(self, store);
192    X509_STORE_set_trust(store, t);
193#else
194    rb_iv_set(self, "@trust", trust);
195#endif
196
197    return trust;
198}
199
200static VALUE
201ossl_x509store_set_time(VALUE self, VALUE time)
202{
203    rb_iv_set(self, "@time", time);
204    return time;
205}
206
207/*
208 * call-seq:
209 *   store.add_file(file) -> store
210 *
211 *
212 * Adds the certificates in +file+ to the certificate store.  The +file+ can
213 * contain multiple PEM-encoded certificates.
214 */
215
216static VALUE
217ossl_x509store_add_file(VALUE self, VALUE file)
218{
219    X509_STORE *store;
220    X509_LOOKUP *lookup;
221    char *path = NULL;
222
223    if(file != Qnil){
224        SafeStringValue(file);
225	path = RSTRING_PTR(file);
226    }
227    GetX509Store(self, store);
228    lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
229    if(lookup == NULL) ossl_raise(eX509StoreError, NULL);
230    if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){
231        ossl_raise(eX509StoreError, NULL);
232    }
233
234    return self;
235}
236
237static VALUE
238ossl_x509store_add_path(VALUE self, VALUE dir)
239{
240    X509_STORE *store;
241    X509_LOOKUP *lookup;
242    char *path = NULL;
243
244    if(dir != Qnil){
245        SafeStringValue(dir);
246	path = RSTRING_PTR(dir);
247    }
248    GetX509Store(self, store);
249    lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
250    if(lookup == NULL) ossl_raise(eX509StoreError, NULL);
251    if(X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1){
252        ossl_raise(eX509StoreError, NULL);
253    }
254
255    return self;
256}
257
258/*
259 * call-seq:
260 *   store.set_default_path
261 *
262 * Adds the default certificates to the certificate store.  These certificates
263 * are loaded from the default configuration directory which can usually be
264 * determined by:
265 *
266 *   File.dirname OpenSSL::Config::DEFAULT_CONFIG_FILE
267 */
268static VALUE
269ossl_x509store_set_default_paths(VALUE self)
270{
271    X509_STORE *store;
272
273    GetX509Store(self, store);
274    if (X509_STORE_set_default_paths(store) != 1){
275        ossl_raise(eX509StoreError, NULL);
276    }
277
278    return Qnil;
279}
280
281/*
282 * call-seq:
283 *   store.add_cert(cert)
284 *
285 * Adds the OpenSSL::X509::Certificate +cert+ to the certificate store.
286 */
287
288static VALUE
289ossl_x509store_add_cert(VALUE self, VALUE arg)
290{
291    X509_STORE *store;
292    X509 *cert;
293
294    cert = GetX509CertPtr(arg); /* NO NEED TO DUP */
295    GetX509Store(self, store);
296    if (X509_STORE_add_cert(store, cert) != 1){
297        ossl_raise(eX509StoreError, NULL);
298    }
299
300    return self;
301}
302
303static VALUE
304ossl_x509store_add_crl(VALUE self, VALUE arg)
305{
306    X509_STORE *store;
307    X509_CRL *crl;
308
309    crl = GetX509CRLPtr(arg); /* NO NEED TO DUP */
310    GetX509Store(self, store);
311    if (X509_STORE_add_crl(store, crl) != 1){
312        ossl_raise(eX509StoreError, NULL);
313    }
314
315    return self;
316}
317
318static VALUE ossl_x509stctx_get_err(VALUE);
319static VALUE ossl_x509stctx_get_err_string(VALUE);
320static VALUE ossl_x509stctx_get_chain(VALUE);
321
322static VALUE
323ossl_x509store_verify(int argc, VALUE *argv, VALUE self)
324{
325    VALUE cert, chain;
326    VALUE ctx, proc, result;
327
328    rb_scan_args(argc, argv, "11", &cert, &chain);
329    ctx = rb_funcall(cX509StoreContext, rb_intern("new"), 3, self, cert, chain);
330    proc = rb_block_given_p() ?  rb_block_proc() :
331	   rb_iv_get(self, "@verify_callback");
332    rb_iv_set(ctx, "@verify_callback", proc);
333    result = rb_funcall(ctx, rb_intern("verify"), 0);
334
335    rb_iv_set(self, "@error", ossl_x509stctx_get_err(ctx));
336    rb_iv_set(self, "@error_string", ossl_x509stctx_get_err_string(ctx));
337    rb_iv_set(self, "@chain", ossl_x509stctx_get_chain(ctx));
338
339    return result;
340}
341
342/*
343 * Public Functions
344 */
345static void ossl_x509stctx_free(X509_STORE_CTX*);
346
347VALUE
348ossl_x509stctx_new(X509_STORE_CTX *ctx)
349{
350    VALUE obj;
351
352    WrapX509StCtx(cX509StoreContext, obj, ctx);
353
354    return obj;
355}
356
357VALUE
358ossl_x509stctx_clear_ptr(VALUE obj)
359{
360    OSSL_Check_Kind(obj, cX509StoreContext);
361    RDATA(obj)->data = NULL;
362
363    return obj;
364}
365
366/*
367 * Private functions
368 */
369static void
370ossl_x509stctx_free(X509_STORE_CTX *ctx)
371{
372    if(ctx->untrusted)
373	sk_X509_pop_free(ctx->untrusted, X509_free);
374    if(ctx->cert)
375	X509_free(ctx->cert);
376    X509_STORE_CTX_free(ctx);
377}
378
379static VALUE
380ossl_x509stctx_alloc(VALUE klass)
381{
382    X509_STORE_CTX *ctx;
383    VALUE obj;
384
385    if((ctx = X509_STORE_CTX_new()) == NULL){
386        ossl_raise(eX509StoreError, NULL);
387    }
388    WrapX509StCtx(klass, obj, ctx);
389
390    return obj;
391}
392
393static VALUE ossl_x509stctx_set_flags(VALUE, VALUE);
394static VALUE ossl_x509stctx_set_purpose(VALUE, VALUE);
395static VALUE ossl_x509stctx_set_trust(VALUE, VALUE);
396static VALUE ossl_x509stctx_set_time(VALUE, VALUE);
397
398static VALUE
399ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self)
400{
401    VALUE store, cert, chain, t;
402    X509_STORE_CTX *ctx;
403    X509_STORE *x509st;
404    X509 *x509 = NULL;
405    STACK_OF(X509) *x509s = NULL;
406
407    rb_scan_args(argc, argv, "12", &store, &cert, &chain);
408    GetX509StCtx(self, ctx);
409    SafeGetX509Store(store, x509st);
410    if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */
411    if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain);
412#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
413    if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
414        sk_X509_pop_free(x509s, X509_free);
415        ossl_raise(eX509StoreError, NULL);
416    }
417#else
418    X509_STORE_CTX_init(ctx, x509st, x509, x509s);
419    ossl_x509stctx_set_flags(self, rb_iv_get(store, "@flags"));
420    ossl_x509stctx_set_purpose(self, rb_iv_get(store, "@purpose"));
421    ossl_x509stctx_set_trust(self, rb_iv_get(store, "@trust"));
422#endif
423    if (!NIL_P(t = rb_iv_get(store, "@time")))
424	ossl_x509stctx_set_time(self, t);
425    rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback"));
426    rb_iv_set(self, "@cert", cert);
427
428    return self;
429}
430
431static VALUE
432ossl_x509stctx_verify(VALUE self)
433{
434    X509_STORE_CTX *ctx;
435    int result;
436
437    GetX509StCtx(self, ctx);
438    X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx,
439                               (void*)rb_iv_get(self, "@verify_callback"));
440    result = X509_verify_cert(ctx);
441
442    return result ? Qtrue : Qfalse;
443}
444
445static VALUE
446ossl_x509stctx_get_chain(VALUE self)
447{
448    X509_STORE_CTX *ctx;
449    STACK_OF(X509) *chain;
450    X509 *x509;
451    int i, num;
452    VALUE ary;
453
454    GetX509StCtx(self, ctx);
455    if((chain = X509_STORE_CTX_get_chain(ctx)) == NULL){
456        return Qnil;
457    }
458    if((num = sk_X509_num(chain)) < 0){
459	OSSL_Debug("certs in chain < 0???");
460	return rb_ary_new();
461    }
462    ary = rb_ary_new2(num);
463    for(i = 0; i < num; i++) {
464	x509 = sk_X509_value(chain, i);
465	rb_ary_push(ary, ossl_x509_new(x509));
466    }
467
468    return ary;
469}
470
471static VALUE
472ossl_x509stctx_get_err(VALUE self)
473{
474    X509_STORE_CTX *ctx;
475
476    GetX509StCtx(self, ctx);
477
478    return INT2FIX(X509_STORE_CTX_get_error(ctx));
479}
480
481static VALUE
482ossl_x509stctx_set_error(VALUE self, VALUE err)
483{
484    X509_STORE_CTX *ctx;
485
486    GetX509StCtx(self, ctx);
487    X509_STORE_CTX_set_error(ctx, NUM2INT(err));
488
489    return err;
490}
491
492static VALUE
493ossl_x509stctx_get_err_string(VALUE self)
494{
495    X509_STORE_CTX *ctx;
496    long err;
497
498    GetX509StCtx(self, ctx);
499    err = X509_STORE_CTX_get_error(ctx);
500
501    return rb_str_new2(X509_verify_cert_error_string(err));
502}
503
504static VALUE
505ossl_x509stctx_get_err_depth(VALUE self)
506{
507    X509_STORE_CTX *ctx;
508
509    GetX509StCtx(self, ctx);
510
511    return INT2FIX(X509_STORE_CTX_get_error_depth(ctx));
512}
513
514static VALUE
515ossl_x509stctx_get_curr_cert(VALUE self)
516{
517    X509_STORE_CTX *ctx;
518
519    GetX509StCtx(self, ctx);
520
521    return ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx));
522}
523
524static VALUE
525ossl_x509stctx_get_curr_crl(VALUE self)
526{
527#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
528    X509_STORE_CTX *ctx;
529
530    GetX509StCtx(self, ctx);
531    if(!ctx->current_crl) return Qnil;
532
533    return ossl_x509crl_new(ctx->current_crl);
534#else
535    return Qnil;
536#endif
537}
538
539static VALUE
540ossl_x509stctx_set_flags(VALUE self, VALUE flags)
541{
542    X509_STORE_CTX *store;
543    long f = NUM2LONG(flags);
544
545    GetX509StCtx(self, store);
546    X509_STORE_CTX_set_flags(store, f);
547
548    return flags;
549}
550
551static VALUE
552ossl_x509stctx_set_purpose(VALUE self, VALUE purpose)
553{
554    X509_STORE_CTX *store;
555    int p = NUM2INT(purpose);
556
557    GetX509StCtx(self, store);
558    X509_STORE_CTX_set_purpose(store, p);
559
560    return purpose;
561}
562
563static VALUE
564ossl_x509stctx_set_trust(VALUE self, VALUE trust)
565{
566    X509_STORE_CTX *store;
567    int t = NUM2INT(trust);
568
569    GetX509StCtx(self, store);
570    X509_STORE_CTX_set_trust(store, t);
571
572    return trust;
573}
574
575/*
576 * call-seq:
577 *    storectx.time = time => time
578 */
579static VALUE
580ossl_x509stctx_set_time(VALUE self, VALUE time)
581{
582    X509_STORE_CTX *store;
583    long t;
584
585    t = NUM2LONG(rb_Integer(time));
586    GetX509StCtx(self, store);
587    X509_STORE_CTX_set_time(store, 0, t);
588
589    return time;
590}
591
592/*
593 * INIT
594 */
595void
596Init_ossl_x509store()
597{
598    VALUE x509stctx;
599
600#if 0
601    mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
602    mX509 = rb_define_module_under(mOSSL, "X509");
603#endif
604
605    eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError);
606
607    /* Document-class: OpenSSL::X509::Store
608     *
609     * The X509 certificate store holds trusted CA certificates used to verify
610     * peer certificates.
611     *
612     * The easiest way to create a useful certificate store is:
613     *
614     *   cert_store = OpenSSL::X509::Store.new
615     *   cert_store.set_default_paths
616     *
617     * This will use your system's built-in certificates.
618     *
619     * If your system does not have a default set of certificates you can
620     * obtain a set from Mozilla here: http://curl.haxx.se/docs/caextract.html
621     * (Note that this set does not have an HTTPS download option so you may
622     * wish to use the firefox-db2pem.sh script to extract the certificates
623     * from a local install to avoid man-in-the-middle attacks.)
624     *
625     * After downloading or generating a cacert.pem from the above link you
626     * can create a certificate store from the pem file like this:
627     *
628     *   cert_store = OpenSSL::X509::Store.new
629     *   cert_store.add_file 'cacert.pem'
630     *
631     * The certificate store can be used with an SSLSocket like this:
632     *
633     *   ssl_context = OpenSSL::SSL::SSLContext.new
634     *   ssl_context.cert_store = cert_store
635     *
636     *   tcp_socket = TCPSocket.open 'example.com', 443
637     *
638     *   ssl_socket = OpenSSL::SSL::SSLSocket.new tcp_socket, ssl_context
639     */
640
641    cX509Store = rb_define_class_under(mX509, "Store", rb_cObject);
642    rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse);
643    rb_attr(cX509Store, rb_intern("error"), 1, 0, Qfalse);
644    rb_attr(cX509Store, rb_intern("error_string"), 1, 0, Qfalse);
645    rb_attr(cX509Store, rb_intern("chain"), 1, 0, Qfalse);
646    rb_define_alloc_func(cX509Store, ossl_x509store_alloc);
647    rb_define_method(cX509Store, "initialize",   ossl_x509store_initialize, -1);
648    rb_define_method(cX509Store, "verify_callback=", ossl_x509store_set_vfy_cb, 1);
649    rb_define_method(cX509Store, "flags=",       ossl_x509store_set_flags, 1);
650    rb_define_method(cX509Store, "purpose=",     ossl_x509store_set_purpose, 1);
651    rb_define_method(cX509Store, "trust=",       ossl_x509store_set_trust, 1);
652    rb_define_method(cX509Store, "time=",        ossl_x509store_set_time, 1);
653    rb_define_method(cX509Store, "add_path",     ossl_x509store_add_path, 1);
654    rb_define_method(cX509Store, "add_file",     ossl_x509store_add_file, 1);
655    rb_define_method(cX509Store, "set_default_paths", ossl_x509store_set_default_paths, 0);
656    rb_define_method(cX509Store, "add_cert",     ossl_x509store_add_cert, 1);
657    rb_define_method(cX509Store, "add_crl",      ossl_x509store_add_crl, 1);
658    rb_define_method(cX509Store, "verify",       ossl_x509store_verify, -1);
659
660    cX509StoreContext = rb_define_class_under(mX509,"StoreContext",rb_cObject);
661    x509stctx = cX509StoreContext;
662    rb_define_alloc_func(cX509StoreContext, ossl_x509stctx_alloc);
663    rb_define_method(x509stctx,"initialize",  ossl_x509stctx_initialize, -1);
664    rb_define_method(x509stctx,"verify",      ossl_x509stctx_verify, 0);
665    rb_define_method(x509stctx,"chain",       ossl_x509stctx_get_chain,0);
666    rb_define_method(x509stctx,"error",       ossl_x509stctx_get_err, 0);
667    rb_define_method(x509stctx,"error=",      ossl_x509stctx_set_error, 1);
668    rb_define_method(x509stctx,"error_string",ossl_x509stctx_get_err_string,0);
669    rb_define_method(x509stctx,"error_depth", ossl_x509stctx_get_err_depth, 0);
670    rb_define_method(x509stctx,"current_cert",ossl_x509stctx_get_curr_cert, 0);
671    rb_define_method(x509stctx,"current_crl", ossl_x509stctx_get_curr_crl, 0);
672    rb_define_method(x509stctx,"flags=",      ossl_x509stctx_set_flags, 1);
673    rb_define_method(x509stctx,"purpose=",    ossl_x509stctx_set_purpose, 1);
674    rb_define_method(x509stctx,"trust=",      ossl_x509stctx_set_trust, 1);
675    rb_define_method(x509stctx,"time=",       ossl_x509stctx_set_time, 1);
676
677}
678