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