diff options
author | Steven Fackler <sfackler@gmail.com> | 2017-01-13 19:38:12 -0800 |
---|---|---|
committer | Steven Fackler <sfackler@gmail.com> | 2017-01-14 21:09:38 -0800 |
commit | 920ab0d6fb60c17077f43d7f08ad3ff391201689 (patch) | |
tree | 2ede3415426f622fe2aff78eaa70a3d64f35a403 | |
parent | 9942643ab6fbdecb0561fcdc08565d4f154865b3 (diff) | |
download | rust-openssl-920ab0d6fb60c17077f43d7f08ad3ff391201689.zip |
OCSP functionality
-rw-r--r-- | openssl-sys/src/lib.rs | 125 | ||||
-rw-r--r-- | openssl-sys/src/libressl.rs | 107 | ||||
-rw-r--r-- | openssl-sys/src/ossl10x.rs | 150 | ||||
-rw-r--r-- | openssl-sys/src/ossl110.rs | 5 | ||||
-rw-r--r-- | openssl/src/asn1.rs | 20 | ||||
-rw-r--r-- | openssl/src/bn.rs | 14 | ||||
-rw-r--r-- | openssl/src/crypto.rs | 60 | ||||
-rw-r--r-- | openssl/src/lib.rs | 6 | ||||
-rw-r--r-- | openssl/src/ocsp.rs | 274 | ||||
-rw-r--r-- | openssl/src/ssl/mod.rs | 144 | ||||
-rw-r--r-- | openssl/src/ssl/tests/mod.rs | 45 | ||||
-rw-r--r-- | openssl/src/string.rs | 74 | ||||
-rw-r--r-- | openssl/src/x509/mod.rs | 20 | ||||
-rw-r--r-- | openssl/src/x509/store.rs | 35 | ||||
-rw-r--r-- | openssl/src/x509/tests.rs | 13 | ||||
-rw-r--r-- | systest/build.rs | 5 |
16 files changed, 1007 insertions, 90 deletions
diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index df6d1d14..0cbd0da7 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -6,6 +6,7 @@ extern crate libc; use libc::{c_void, c_int, c_char, c_ulong, c_long, c_uint, c_uchar, size_t, FILE}; use std::ptr; +use std::mem; #[cfg(any(ossl101, ossl102))] mod ossl10x; @@ -23,6 +24,7 @@ mod libressl; pub use libressl::*; pub enum ASN1_INTEGER {} +pub enum ASN1_GENERALIZEDTIME {} pub enum ASN1_STRING {} pub enum ASN1_TIME {} pub enum ASN1_TYPE {} @@ -38,7 +40,11 @@ pub enum ENGINE {} pub enum EVP_CIPHER_CTX {} pub enum EVP_MD {} pub enum EVP_PKEY_CTX {} -pub enum SSL {} +pub enum OCSP_BASICRESP {} +pub enum OCSP_CERTID {} +pub enum OCSP_RESPONSE {} +pub enum OCSP_REQUEST {} +pub enum OCSP_ONEREQ {} pub enum SSL_CIPHER {} pub enum SSL_METHOD {} pub enum X509_CRL {} @@ -51,20 +57,17 @@ pub enum X509_STORE_CTX {} pub enum bio_st {} pub enum PKCS12 {} pub enum DH_METHOD {} - -pub type bio_info_cb = Option<unsafe extern "C" fn(*mut BIO, - c_int, - *const c_char, - c_int, - c_long, - c_long)>; - pub enum RSA_METHOD {} pub enum BN_MONT_CTX {} pub enum BN_BLINDING {} pub enum DSA_METHOD {} pub enum EVP_PKEY_ASN1_METHOD {} +pub type bio_info_cb = Option<unsafe extern fn(*mut BIO, c_int, *const c_char, c_int, c_long, c_long)>; +pub type GEN_SESSION_CB = Option<unsafe extern fn(*const SSL, *mut c_uchar, *mut c_uint) -> c_int>; +pub type tls_session_ticket_ext_cb_fn = Option<unsafe extern fn(*mut SSL, *const c_uchar, c_int, *mut c_void) -> c_int>; +pub type tls_session_secret_cb_fn = Option<unsafe extern fn(*mut SSL, *mut c_void, *mut c_int, *mut stack_st_SSL_CIPHER, *mut *mut SSL_CIPHER, *mut c_void) -> c_int>; + #[repr(C)] #[derive(Copy, Clone)] pub enum point_conversion_form_t { @@ -1059,6 +1062,40 @@ pub const NID_aes_128_cbc_hmac_sha1: c_int = 916; pub const NID_aes_192_cbc_hmac_sha1: c_int = 917; pub const NID_aes_256_cbc_hmac_sha1: c_int = 918; +pub const OCSP_NOCERTS: c_ulong = 0x1; +pub const OCSP_NOINTERN: c_ulong = 0x2; +pub const OCSP_NOSIGS: c_ulong = 0x4; +pub const OCSP_NOCHAIN: c_ulong = 0x8; +pub const OCSP_NOVERIFY: c_ulong = 0x10; +pub const OCSP_NOEXPLICIT: c_ulong = 0x20; +pub const OCSP_NOCASIGN: c_ulong = 0x40; +pub const OCSP_NODELEGATED: c_ulong = 0x80; +pub const OCSP_NOCHECKS: c_ulong = 0x100; +pub const OCSP_TRUSTOTHER: c_ulong = 0x200; +pub const OCSP_RESPID_KEY: c_ulong = 0x400; +pub const OCSP_NOTIME: c_ulong = 0x800; + +pub const V_OCSP_CERTSTATUS_GOOD: c_int = 0; +pub const V_OCSP_CERTSTATUS_REVOKED: c_int = 1; +pub const V_OCSP_CERTSTATUS_UNKNOWN: c_int = 2; + +pub const OCSP_REVOKED_STATUS_NOSTATUS: c_int = -1; +pub const OCSP_REVOKED_STATUS_UNSPECIFIED: c_int = 0; +pub const OCSP_REVOKED_STATUS_KEYCOMPROMISE: c_int = 1; +pub const OCSP_REVOKED_STATUS_CACOMPROMISE: c_int = 2; +pub const OCSP_REVOKED_STATUS_AFFILIATIONCHANGED: c_int = 3; +pub const OCSP_REVOKED_STATUS_SUPERSEDED: c_int = 4; +pub const OCSP_REVOKED_STATUS_CESSATIONOFOPERATION: c_int = 5; +pub const OCSP_REVOKED_STATUS_CERTIFICATEHOLD: c_int = 6; +pub const OCSP_REVOKED_STATUS_REMOVEFROMCRL: c_int = 8; + +pub const OCSP_RESPONSE_STATUS_SUCCESSFUL: c_int = 0; +pub const OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: c_int = 1; +pub const OCSP_RESPONSE_STATUS_INTERNALERROR: c_int = 2; +pub const OCSP_RESPONSE_STATUS_TRYLATER: c_int = 3; +pub const OCSP_RESPONSE_STATUS_SIGREQUIRED: c_int = 5; +pub const OCSP_RESPONSE_STATUS_UNAUTHORIZED: c_int = 6; + pub const PKCS5_SALT_LEN: c_int = 8; pub const RSA_F4: c_long = 0x10001; @@ -1077,6 +1114,12 @@ pub const SSL_CTRL_SET_READ_AHEAD: c_int = 41; pub const SSL_CTRL_SET_TLSEXT_SERVERNAME_CB: c_int = 53; pub const SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG: c_int = 54; pub const SSL_CTRL_SET_TLSEXT_HOSTNAME: c_int = 55; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB: c_int = 63; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG: c_int = 64; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE: c_int = 65; +pub const SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP: c_int = 70; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP: c_int = 71; +pub const SSL_CTRL_GET_EXTRA_CHAIN_CERTS: c_int = 82; pub const SSL_MODE_ENABLE_PARTIAL_WRITE: c_long = 0x1; pub const SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER: c_long = 0x2; @@ -1136,6 +1179,8 @@ pub const SSL_OP_NO_SSL_MASK: c_ulong = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | pub const TLSEXT_NAMETYPE_host_name: c_int = 0; +pub const TLSEXT_STATUSTYPE_ocsp: c_int = 1; + pub const SSL_TLSEXT_ERR_OK: c_int = 0; pub const SSL_TLSEXT_ERR_ALERT_WARNING: c_int = 1; pub const SSL_TLSEXT_ERR_ALERT_FATAL: c_int = 2; @@ -1273,7 +1318,7 @@ pub unsafe fn SSL_CTX_add_extra_chain_cert(ctx: *mut SSL_CTX, x509: *mut X509) - } pub unsafe fn SSL_CTX_set_tlsext_servername_callback(ctx: *mut SSL_CTX, - cb: Option<extern "C" fn()>) + cb: Option<extern fn()>) -> c_long { SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cb) } @@ -1284,6 +1329,32 @@ pub unsafe fn SSL_set_tlsext_host_name(s: *mut SSL, name: *mut c_char) -> c_long name as *mut c_void) } +pub unsafe fn SSL_set_tlsext_status_type(s: *mut SSL, type_: c_int) -> c_long { + SSL_ctrl(s, SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, type_ as c_long, ptr::null_mut()) +} + +pub unsafe fn SSL_CTX_set_tlsext_status_cb(ctx: *mut SSL_CTX, + cb: Option<unsafe extern fn(*mut SSL, *mut c_void) -> c_int>) + -> c_long { + SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, mem::transmute(cb)) +} + +pub unsafe fn SSL_CTX_set_tlsext_status_arg(ctx: *mut SSL_CTX, arg: *mut c_void) -> c_long { + SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG, 0, arg) +} + +pub unsafe fn SSL_CTX_get_extra_chain_certs(ctx: *mut SSL_CTX, chain: *mut *mut stack_st_X509) -> c_long { + SSL_CTX_ctrl(ctx, SSL_CTRL_GET_EXTRA_CHAIN_CERTS, 0, chain as *mut c_void) +} + +pub unsafe fn SSL_get_tlsext_status_ocsp_resp(ssl: *mut SSL, resp: *mut *mut c_uchar) -> c_long { + SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, resp as *mut c_void) +} + +pub unsafe fn SSL_set_tlsext_status_ocsp_resp(ssl: *mut SSL, resp: *mut c_uchar, len: c_long) -> c_long { + SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, len, resp as *mut c_void) +} + pub fn ERR_GET_LIB(l: c_ulong) -> c_int { ((l >> 24) & 0x0FF) as c_int } @@ -1298,6 +1369,8 @@ pub fn ERR_GET_REASON(l: c_ulong) -> c_int { extern { pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int; + pub fn ASN1_GENERALIZEDTIME_free(tm: *mut ASN1_GENERALIZEDTIME); + pub fn ASN1_GENERALIZEDTIME_print(b: *mut BIO, tm: *const ASN1_GENERALIZEDTIME) -> c_int; pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING; pub fn ASN1_TIME_free(tm: *mut ASN1_TIME); pub fn ASN1_TIME_print(b: *mut BIO, tm: *const ASN1_TIME) -> c_int; @@ -1541,6 +1614,30 @@ extern { pub fn HMAC_CTX_copy(dst: *mut HMAC_CTX, src: *mut HMAC_CTX) -> c_int; + pub fn OCSP_BASICRESP_new() -> *mut OCSP_BASICRESP; + pub fn OCSP_BASICRESP_free(r: *mut OCSP_BASICRESP); + pub fn OCSP_basic_verify(bs: *mut OCSP_BASICRESP, certs: *mut stack_st_X509, st: *mut X509_STORE, flags: c_ulong) -> c_int; + pub fn OCSP_resp_find_status(bs: *mut OCSP_BASICRESP, id: *mut OCSP_CERTID, status: *mut c_int, reason: *mut c_int, revtime: *mut *mut ASN1_GENERALIZEDTIME, thisupd: *mut *mut ASN1_GENERALIZEDTIME, nextupd: *mut *mut ASN1_GENERALIZEDTIME) -> c_int; + pub fn OCSP_check_validity(thisupd: *mut ASN1_GENERALIZEDTIME, nextupd: *mut ASN1_GENERALIZEDTIME, sec: c_long, maxsec: c_long) -> c_int; + + pub fn OCSP_CERTID_free(id: *mut OCSP_CERTID); + + pub fn OCSP_RESPONSE_new() -> *mut OCSP_RESPONSE; + pub fn OCSP_RESPONSE_free(r: *mut OCSP_RESPONSE); + pub fn i2d_OCSP_RESPONSE(a: *mut OCSP_RESPONSE, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_OCSP_RESPONSE(a: *mut *mut OCSP_RESPONSE, pp: *mut *const c_uchar, length: c_long) -> *mut OCSP_RESPONSE; + pub fn OCSP_response_create(status: c_int, bs: *mut OCSP_BASICRESP) -> *mut OCSP_RESPONSE; + pub fn OCSP_response_status(resp: *mut OCSP_RESPONSE) -> c_int; + pub fn OCSP_response_get1_basic(resp: *mut OCSP_RESPONSE) -> *mut OCSP_BASICRESP; + + pub fn OCSP_REQUEST_new() -> *mut OCSP_REQUEST; + pub fn OCSP_REQUEST_free(r: *mut OCSP_REQUEST); + pub fn i2d_OCSP_REQUEST(a: *mut OCSP_REQUEST, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_OCSP_REQUEST(a: *mut *mut OCSP_REQUEST, pp: *mut *const c_uchar, length: c_long) -> *mut OCSP_REQUEST; + pub fn OCSP_request_add0_id(r: *mut OCSP_REQUEST, id: *mut OCSP_CERTID) -> *mut OCSP_ONEREQ; + + pub fn OCSP_ONEREQ_free(r: *mut OCSP_ONEREQ); + pub fn PEM_read_bio_DHparams(bio: *mut BIO, out: *mut *mut DH, callback: Option<PasswordCallback>, user_data: *mut c_void) -> *mut DH; pub fn PEM_read_bio_X509(bio: *mut BIO, out: *mut *mut X509, callback: Option<PasswordCallback>, @@ -1696,7 +1793,7 @@ extern { pub fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX; pub fn SSL_CTX_free(ctx: *mut SSL_CTX); pub fn SSL_CTX_ctrl(ctx: *mut SSL_CTX, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long; - pub fn SSL_CTX_callback_ctrl(ctx: *mut SSL_CTX, cmd: c_int, fp: Option<extern "C" fn()>) -> c_long; + pub fn SSL_CTX_callback_ctrl(ctx: *mut SSL_CTX, cmd: c_int, fp: Option<extern fn()>) -> c_long; pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int, verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>); pub fn SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int); @@ -1749,6 +1846,8 @@ extern { client: *const c_uchar, client_len: c_uint) -> c_int; pub fn SSL_get0_next_proto_negotiated(s: *const SSL, data: *mut *const c_uchar, len: *mut c_uint); pub fn SSL_get_session(s: *const SSL) -> *mut SSL_SESSION; + #[cfg(not(any(ossl101, libressl)))] + pub fn SSL_is_server(s: *mut SSL) -> c_int; pub fn SSL_SESSION_free(s: *mut SSL_SESSION); pub fn SSL_SESSION_get_id(s: *const SSL_SESSION, len: *mut c_uint) -> *const c_uchar; @@ -1785,6 +1884,8 @@ extern { pub fn X509_get_pubkey(x: *mut X509) -> *mut EVP_PKEY; pub fn X509_to_X509_REQ(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> *mut X509_REQ; pub fn X509_verify_cert_error_string(n: c_long) -> *const c_char; + pub fn X509_get1_ocsp(x: *mut X509) -> *mut stack_st_OPENSSL_STRING; + pub fn X509_check_issued(issuer: *mut X509, subject: *mut X509) -> c_int; pub fn X509_EXTENSION_free(ext: *mut X509_EXTENSION); @@ -1797,8 +1898,10 @@ extern { pub fn ASN1_STRING_free(x: *mut ASN1_STRING); pub fn ASN1_STRING_length(x: *const ASN1_STRING) -> c_int; + pub fn X509_STORE_new() -> *mut X509_STORE; pub fn X509_STORE_free(store: *mut X509_STORE); pub fn X509_STORE_add_cert(store: *mut X509_STORE, x: *mut X509) -> c_int; + pub fn X509_STORE_set_default_paths(store: *mut X509_STORE) -> c_int; pub fn X509_STORE_CTX_free(ctx: *mut X509_STORE_CTX); pub fn X509_STORE_CTX_get_current_cert(ctx: *mut X509_STORE_CTX) -> *mut X509; diff --git a/openssl-sys/src/libressl.rs b/openssl-sys/src/libressl.rs index 2c747ffe..c1411e60 100644 --- a/openssl-sys/src/libressl.rs +++ b/openssl-sys/src/libressl.rs @@ -41,6 +41,16 @@ pub struct stack_st_void { } #[repr(C)] +pub struct stack_st_SSL_CIPHER { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_OPENSSL_STRING { + pub stack: _STACK, +} + +#[repr(C)] pub struct _STACK { pub num: c_int, pub data: *mut *mut c_char, @@ -289,6 +299,100 @@ pub struct X509_VAL { } #[repr(C)] +pub struct SSL { + version: c_int, + type_: c_int, + method: *const ::SSL_METHOD, + rbio: *mut c_void, + wbio: *mut c_void, + bbio: *mut c_void, + rwstate: c_int, + in_handshake: c_int, + handshake_func: Option<unsafe extern fn(*mut SSL) -> c_int>, + pub server: c_int, + new_session: c_int, + quiet_shutdown: c_int, + shutdown: c_int, + state: c_int, + rstate: c_int, + init_buf: *mut c_void, + init_msg: *mut c_void, + init_num: c_int, + init_off: c_int, + packet: *mut c_uchar, + packet_length: c_uint, + s3: *mut c_void, + d1: *mut c_void, + read_ahead: c_int, + msg_callback: Option<unsafe extern fn(c_int, c_int, c_int, *const c_void, size_t, *mut SSL, *mut c_void)>, + msg_callback_arg: *mut c_void, + hit: c_int, + param: *mut c_void, + cipher_list: *mut stack_st_SSL_CIPHER, + cipher_list_by_id: *mut stack_st_SSL_CIPHER, + mac_flags: c_int, + aead_read_ctx: *mut c_void, + enc_read_ctx: *mut ::EVP_CIPHER_CTX, + read_hash: *mut ::EVP_MD_CTX, + aead_write_ctx: *mut c_void, + enc_write_ctx: *mut ::EVP_CIPHER_CTX, + write_hash: *mut ::EVP_MD_CTX, + cert: *mut c_void, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + session: *mut ::SSL_SESSION, + generate_session_id: ::GEN_SESSION_CB, + verify_mode: c_int, + verify_callback: Option<unsafe extern fn(c_int, *mut ::X509_STORE_CTX) -> c_int>, + info_callback: Option<unsafe extern fn(*mut SSL, c_int, c_int)>, + error: c_int, + error_code: c_int, + ctx: *mut ::SSL_CTX, + debug: c_int, + verify_result: c_long, + ex_data: ::CRYPTO_EX_DATA, + client_CA: *mut stack_st_X509_NAME, + references: c_int, + options: c_ulong, + mode: c_ulong, + max_cert_list: c_long, + first_packet: c_int, + client_version: c_int, + max_send_fragment: c_uint, + tlsext_debug_cb: Option<unsafe extern fn(*mut SSL, c_int, c_int, *mut c_uchar, c_int, *mut c_void)>, + tlsext_debug_arg: *mut c_void, + tlsext_hostname: *mut c_char, + servername_done: c_int, + tlsext_status_type: c_int, + tlsext_status_expected: c_int, + tlsext_ocsp_ids: *mut c_void, + tlsext_ocsp_exts: *mut c_void, + tlsext_ocsp_resp: *mut c_uchar, + tlsext_ocsp_resplen: c_int, + tlsext_ticket_expected: c_int, + tlsext_ecpointformatlist_length: size_t, + tlsext_ecpointformatlist: *mut c_uchar, + tlsext_ellipticcurvelist_length: size_t, + tlsext_ellipticcurvelist: *mut c_uchar, + tlsext_session_ticket: *mut c_void, + tlsext_session_ticket_ext_cb: ::tls_session_ticket_ext_cb_fn, + tls_session_ticket_ext_cb_arg: *mut c_void, + tls_session_secret_cb: ::tls_session_secret_cb_fn, + tls_session_secret_cb_arg: *mut c_void, + initial_ctx: *mut ::SSL_CTX, + next_proto_negotiated: *mut c_uchar, + next_proto_negotiated_len: c_uchar, + srtp_profiles: *mut c_void, + srtp_profile: *mut c_void, + tlsext_heartbeat: c_uint, + tlsext_hb_pending: c_uint, + tlsext_hb_seq: c_uint, + alpn_client_proto_list: *mut c_uchar, + alpn_client_proto_list_len: c_uint, + renegotiate: c_int, +} + +#[repr(C)] pub struct SSL_CTX { method: *mut c_void, cipher_list: *mut c_void, @@ -515,6 +619,7 @@ extern { pub fn get_rfc3526_prime_6144(bn: *mut BIGNUM) -> *mut BIGNUM; pub fn get_rfc3526_prime_8192(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn CRYPTO_malloc(num: c_int, file: *const c_char, line: c_int) -> *mut c_void; pub fn CRYPTO_free(buf: *mut c_void); pub fn CRYPTO_num_locks() -> c_int; pub fn CRYPTO_set_locking_callback(func: unsafe extern "C" fn(mode: c_int, @@ -530,6 +635,8 @@ extern { cb: Option<extern fn(c_int, c_int, *mut c_void)>, cbarg: *mut c_void) -> *mut RSA; + pub fn OCSP_cert_to_id(dgst: *const ::EVP_MD, subject: *mut ::X509, issuer: *mut ::X509) -> *mut ::OCSP_CERTID; + pub fn SSL_library_init() -> c_int; pub fn SSL_load_error_strings(); pub fn OPENSSL_add_all_algorithms_noconf(); diff --git a/openssl-sys/src/ossl10x.rs b/openssl-sys/src/ossl10x.rs index 037298c1..f721daaa 100644 --- a/openssl-sys/src/ossl10x.rs +++ b/openssl-sys/src/ossl10x.rs @@ -42,6 +42,16 @@ pub struct stack_st_void { } #[repr(C)] +pub struct stack_st_SSL_CIPHER { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_OPENSSL_STRING { + pub stack: _STACK, +} + +#[repr(C)] pub struct _STACK { pub num: c_int, pub data: *mut *mut c_char, @@ -296,6 +306,143 @@ pub struct X509_VAL { } #[repr(C)] +pub struct SSL { + version: c_int, + type_: c_int, + method: *const ::SSL_METHOD, + rbio: *mut c_void, + wbio: *mut c_void, + bbio: *mut c_void, + rwstate: c_int, + in_handshake: c_int, + handshake_func: Option<unsafe extern fn(*mut SSL) -> c_int>, + pub server: c_int, + new_session: c_int, + quiet_session: c_int, + shutdown: c_int, + state: c_int, + rstate: c_int, + init_buf: *mut c_void, + init_msg: *mut c_void, + init_num: c_int, + init_off: c_int, + packet: *mut c_uchar, + packet_length: c_uint, + s2: *mut c_void, + s3: *mut c_void, + d1: *mut c_void, + read_ahead: c_int, + msg_callback: Option<unsafe extern fn(c_int, c_int, c_int, *const c_void, size_t, *mut SSL, *mut c_void)>, + msg_callback_arg: *mut c_void, + hit: c_int, + param: *mut c_void, + cipher_list: *mut stack_st_SSL_CIPHER, + cipher_list_by_id: *mut stack_st_SSL_CIPHER, + mac_flags: c_int, + enc_read_ctx: *mut ::EVP_CIPHER_CTX, + read_hash: *mut ::EVP_MD_CTX, + expand: *mut c_void, + enc_write_ctx: *mut ::EVP_CIPHER_CTX, + write_hash: *mut ::EVP_MD_CTX, + compress: *mut c_void, + cert: *mut c_void, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + session: *mut ::SSL_SESSION, + generate_session_id: ::GEN_SESSION_CB, + verify_mode: c_int, + verify_callback: Option<unsafe extern fn(c_int, *mut ::X509_STORE_CTX) -> c_int>, + info_callback: Option<unsafe extern fn(*mut SSL, c_int, c_int)>, + error: c_int, + error_code: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_KRB5"))] + kssl_ctx: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + psk_client_callback: Option<unsafe extern fn(*mut SSL, *const c_char, *mut c_char, c_uint, *mut c_uchar, c_uint) -> c_uint>, + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + psk_server_callback: Option<unsafe extern fn(*mut SSL, *const c_char, *mut c_uchar, c_uint) -> c_uint>, + ctx: *mut ::SSL_CTX, + debug: c_int, + verify_result: c_long, + ex_data: ::CRYPTO_EX_DATA, + client_CA: *mut stack_st_X509_NAME, + references: c_int, + options: c_ulong, + mode: c_ulong, + max_cert_list: c_long, + first_packet: c_int, + client_version: c_int, + max_send_fragment: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_debug_cb: Option<unsafe extern fn(*mut SSL, c_int, c_int, *mut c_uchar, c_int, *mut c_void)>, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_debug_arg: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hostname: *mut c_char, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + servername_done: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_status_type: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_status_expected: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_ids: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_exts: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_resp: *mut c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_resplen: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ticket_expected: c_int, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ecpointformatlist_length: size_t, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ecpointformatlist: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ellipticcurvelist_length: size_t, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ellipticcurvelist: *mut c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_opaque_prf_input: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_opaque_prf_input_len: size_t, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_session_ticket: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_session_ticket_ext_cb: ::tls_session_ticket_ext_cb_fn, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tls_session_ticket_ext_cb_arg: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tls_session_secret_cb: ::tls_session_secret_cb_fn, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tls_session_secret_cb_arg: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + initial_ctx: *mut ::SSL_CTX, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_NEXTPROTONEG")))] + next_proto_negotiated: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_NEXTPROTONEG")))] + next_proto_negotiated_len: c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + srtp_profiles: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + srtp_profile: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_heartbeat: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hb_pending: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hb_seq: c_uint, + renegotiate: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_SRP"))] + srp_ctx: ::SRP_CTX, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), ossl102))] + alpn_client_proto_list: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), ossl102))] + alpn_client_proto_list_len: c_uint, +} + +#[repr(C)] pub struct SSL_CTX { method: *mut c_void, cipher_list: *mut c_void, @@ -617,6 +764,7 @@ extern { pub fn get_rfc3526_prime_6144(bn: *mut BIGNUM) -> *mut BIGNUM; pub fn get_rfc3526_prime_8192(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn CRYPTO_malloc(num: c_int, file: *const c_char, line: c_int) -> *mut c_void; pub fn CRYPTO_free(buf: *mut c_void); pub fn CRYPTO_num_locks() -> c_int; pub fn CRYPTO_set_locking_callback(func: unsafe extern "C" fn(mode: c_int, @@ -632,6 +780,8 @@ extern { cb: Option<extern fn(c_int, c_int, *mut c_void)>, cbarg: *mut c_void) -> *mut RSA; + pub fn OCSP_cert_to_id(dgst: *const ::EVP_MD, subject: *mut ::X509, issuer: *mut ::X509) -> *mut ::OCSP_CERTID; + pub fn SSL_library_init() -> c_int; pub fn SSL_load_error_strings(); pub fn OPENSSL_add_all_algorithms_noconf(); diff --git a/openssl-sys/src/ossl110.rs b/openssl-sys/src/ossl110.rs index 898ad4a2..925b0d8a 100644 --- a/openssl-sys/src/ossl110.rs +++ b/openssl-sys/src/ossl110.rs @@ -12,6 +12,7 @@ pub enum EVP_PKEY {} pub enum HMAC_CTX {} pub enum OPENSSL_STACK {} pub enum RSA {} +pub enum SSL {} pub enum SSL_CTX {} pub enum SSL_SESSION {} pub enum stack_st_ASN1_OBJECT {} @@ -22,6 +23,7 @@ pub enum stack_st_X509 {} pub enum stack_st_X509_NAME {} pub enum stack_st_X509_ATTRIBUTE {} pub enum stack_st_X509_EXTENSION {} +pub enum stack_st_SSL_CIPHER {} pub enum X509 {} pub enum X509_VERIFY_PARAM {} @@ -63,11 +65,14 @@ extern { pub fn BN_get_rfc3526_prime_6144(bn: *mut BIGNUM) -> *mut BIGNUM; pub fn BN_get_rfc3526_prime_8192(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn CRYPTO_malloc(num: size_t, file: *const c_char, line: c_int) -> *mut c_void; pub fn CRYPTO_free(buf: *mut c_void, file: *const c_char, line: c_int); pub fn HMAC_CTX_new() -> *mut HMAC_CTX; pub fn HMAC_CTX_free(ctx: *mut HMAC_CTX); + pub fn OCSP_cert_to_id(dgst: *const ::EVP_MD, subject: *const ::X509, issuer: *const ::X509) -> *mut ::OCSP_CERTID; + pub fn TLS_method() -> *const ::SSL_METHOD; pub fn DTLS_method() -> *const ::SSL_METHOD; pub fn SSL_CIPHER_get_version(cipher: *const ::SSL_CIPHER) -> *const c_char; diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index c0a23591..d177885e 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -1,5 +1,5 @@ use ffi; -use libc::c_long; +use libc::{c_long, c_char}; use std::fmt; use std::ptr; use std::slice; @@ -7,9 +7,21 @@ use std::str; use {cvt, cvt_p}; use bio::MemBio; -use crypto::CryptoString; use error::ErrorStack; use types::{OpenSslType, OpenSslTypeRef}; +use string::OpensslString; + +type_!(Asn1GeneralizedTime, Asn1GeneralizedTimeRef, ffi::ASN1_GENERALIZEDTIME, ffi::ASN1_GENERALIZEDTIME_free); + +impl fmt::Display for Asn1GeneralizedTimeRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let mem_bio = try!(MemBio::new()); + try!(cvt(ffi::ASN1_GENERALIZEDTIME_print(mem_bio.as_ptr(), self.as_ptr()))); + write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf())) + } + } +} type_!(Asn1Time, Asn1TimeRef, ffi::ASN1_TIME, ffi::ASN1_TIME_free); @@ -42,7 +54,7 @@ impl Asn1Time { type_!(Asn1String, Asn1StringRef, ffi::ASN1_STRING, ffi::ASN1_STRING_free); impl Asn1StringRef { - pub fn as_utf8(&self) -> Result<CryptoString, ErrorStack> { + pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> { unsafe { let mut ptr = ptr::null_mut(); let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr()); @@ -50,7 +62,7 @@ impl Asn1StringRef { return Err(ErrorStack::get()); } - Ok(CryptoString::from_raw_parts(ptr, len as usize)) + Ok(OpensslString::from_ptr(ptr as *mut c_char)) } } diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs index a8bb9619..01c0b428 100644 --- a/openssl/src/bn.rs +++ b/openssl/src/bn.rs @@ -6,8 +6,8 @@ use std::{fmt, ptr}; use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Deref}; use {cvt, cvt_p, cvt_n}; -use crypto::CryptoString; use error::ErrorStack; +use string::OpensslString; use types::{OpenSslType, OpenSslTypeRef}; #[cfg(ossl10x)] @@ -484,12 +484,12 @@ impl BigNumRef { /// # use openssl::bn::BigNum; /// let s = -BigNum::from_u32(12345).unwrap(); /// - /// assert_eq!(&*s.to_dec_str().unwrap(), "-12345"); + /// assert_eq!(&**s.to_dec_str().unwrap(), "-12345"); /// ``` - pub fn to_dec_str(&self) -> Result<CryptoString, ErrorStack> { + pub fn to_dec_str(&self) -> Result<OpensslString, ErrorStack> { unsafe { let buf = try!(cvt_p(ffi::BN_bn2dec(self.as_ptr()))); - Ok(CryptoString::from_null_terminated(buf)) + Ok(OpensslString::from_ptr(buf)) } } @@ -499,12 +499,12 @@ impl BigNumRef { /// # use openssl::bn::BigNum; /// let s = -BigNum::from_u32(0x99ff).unwrap(); /// - /// assert_eq!(&*s.to_hex_str().unwrap(), "-99FF"); + /// assert_eq!(&**s.to_hex_str().unwrap(), "-99FF"); /// ``` - pub fn to_hex_str(&self) -> Result<CryptoString, ErrorStack> { + pub fn to_hex_str(&self) -> Result<OpensslString, ErrorStack> { unsafe { let buf = try!(cvt_p(ffi::BN_bn2hex(self.as_ptr()))); - Ok(CryptoString::from_null_terminated(buf)) + Ok(OpensslString::from_ptr(buf)) } } } diff --git a/openssl/src/crypto.rs b/openssl/src/crypto.rs index ce83cbae..49029318 100644 --- a/openssl/src/crypto.rs +++ b/openssl/src/crypto.rs @@ -1,59 +1,5 @@ -use libc::{c_char, c_int, c_void}; -use std::fmt; -use std::ffi::CStr; -use std::slice; -use std::ops::Deref; -use std::str; +use string::OpensslString; -pub struct CryptoString(&'static str); +#[deprecated(note = "renamed to OpensslString", since = "0.9.7")] +pub type CryptoString = OpensslString; -impl Drop for CryptoString { - fn drop(&mut self) { - unsafe { - CRYPTO_free(self.0.as_ptr() as *mut c_void, - concat!(file!(), "\0").as_ptr() as *const c_char, - line!() as c_int); - } - } -} - -impl Deref for CryptoString { - type Target = str; - - fn deref(&self) -> &str { - self.0 - } -} - -impl CryptoString { - pub unsafe fn from_raw_parts(buf: *mut u8, len: usize) -> CryptoString { - let slice = slice::from_raw_parts(buf, len); - CryptoString(str::from_utf8_unchecked(slice)) - } - - pub unsafe fn from_null_terminated(buf: *mut c_char) -> CryptoString { - let slice = CStr::from_ptr(buf).to_bytes(); - CryptoString(str::from_utf8_unchecked(slice)) - } -} - -impl fmt::Display for CryptoString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.0, f) - } -} - -impl fmt::Debug for CryptoString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.0, f) - } -} - -#[cfg(not(ossl110))] -#[allow(non_snake_case)] -unsafe fn CRYPTO_free(buf: *mut c_void, _: *const c_char, _: c_int) { - ::ffi::CRYPTO_free(buf); -} - -#[cfg(ossl110)] -use ffi::CRYPTO_free; diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index dc1d794f..9138896b 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -35,18 +35,20 @@ pub mod error; pub mod hash; pub mod memcmp; pub mod nid; +pub mod ocsp; pub mod pkcs12; pub mod pkcs5; pub mod pkey; pub mod rand; -pub mod types; pub mod rsa; pub mod sign; pub mod ssl; +pub mod stack; +pub mod string; pub mod symm; +pub mod types; pub mod version; pub mod x509; -pub mod stack; #[cfg(any(ossl102, ossl110))] mod verify; diff --git a/openssl/src/ocsp.rs b/openssl/src/ocsp.rs new file mode 100644 index 00000000..bba5c561 --- /dev/null +++ b/openssl/src/ocsp.rs @@ -0,0 +1,274 @@ +use ffi; +use libc::{c_int, c_long, c_ulong}; +use std::ptr; +use std::mem; + +use {cvt, cvt_p}; +use asn1::Asn1GeneralizedTimeRef; +use error::ErrorStack; +use hash::MessageDigest; +use stack::StackRef; +use types::OpenSslTypeRef; +use x509::store::X509StoreRef; +use x509::{X509, X509Ref}; + +bitflags! { + pub flags Flag: c_ulong { + const FLAG_NO_CERTS = ffi::OCSP_NOCERTS, + const FLAG_NO_INTERN = ffi::OCSP_NOINTERN, + const FLAG_NO_CHAIN = ffi::OCSP_NOCHAIN, + const FLAG_NO_VERIFY = ffi::OCSP_NOVERIFY, + const FLAG_NO_EXPLICIT = ffi::OCSP_NOEXPLICIT, + const FLAG_NO_CA_SIGN = ffi::OCSP_NOCASIGN, + const FLAG_NO_DELEGATED = ffi::OCSP_NODELEGATED, + const FLAG_NO_CHECKS = ffi::OCSP_NOCHECKS, + const FLAG_TRUST_OTHER = ffi::OCSP_TRUSTOTHER, + const FLAG_RESPID_KEY = ffi::OCSP_RESPID_KEY, + const FLAG_NO_TIME = ffi::OCSP_NOTIME, + } +} + +pub const RESPONSE_STATUS_SUCCESSFUL: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SUCCESSFUL); +pub const RESPONSE_STATUS_MALFORMED_REQUEST: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST); +pub const RESPONSE_STATUS_INTERNAL_ERROR: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_INTERNALERROR); +pub const RESPONSE_STATUS_TRY_LATER: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_TRYLATER); +pub const RESPONSE_STATUS_SIG_REQUIRED: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SIGREQUIRED); +pub const RESPONSE_STATUS_UNAUTHORIZED: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_UNAUTHORIZED); + +pub const CERT_STATUS_GOOD: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_GOOD); +pub const CERT_STATUS_REVOKED: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_REVOKED); +pub const CERT_STATUS_UNKNOWN: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_UNKNOWN); + +pub const REVOKED_STATUS_NO_STATUS: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_NOSTATUS); +pub const REVOKED_STATUS_UNSPECIFIED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_UNSPECIFIED); +pub const REVOKED_STATUS_KEY_COMPROMISE: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_KEYCOMPROMISE); +pub const REVOKED_STATUS_CA_COMPROMISE: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CACOMPROMISE); +pub const REVOKED_STATUS_AFFILIATION_CHANGED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_AFFILIATIONCHANGED); +pub const REVOKED_STATUS_SUPERSEDED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_SUPERSEDED); +pub const REVOKED_STATUS_CESSATION_OF_OPERATION: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CESSATIONOFOPERATION); +pub const REVOKED_STATUS_CERTIFICATE_HOLD: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CERTIFICATEHOLD); +pub const REVOKED_STATUS_REMOVE_FROM_CRL: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_REMOVEFROMCRL); + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspResponseStatus(c_int); + +impl OcspResponseStatus { + pub fn from_raw(raw: c_int) -> OcspResponseStatus { + OcspResponseStatus(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspCertStatus(c_int); + +impl OcspCertStatus { + pub fn from_raw(raw: c_int) -> OcspCertStatus { + OcspCertStatus(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspRevokedStatus(c_int); + +impl OcspRevokedStatus { + pub fn from_raw(raw: c_int) -> OcspRevokedStatus { + OcspRevokedStatus(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +pub struct Status<'a> { + /// The overall status of the response. + pub status: OcspCertStatus, + /// If `status` is `CERT_STATUS_REVOKED`, the reason for the revocation. + pub reason: OcspRevokedStatus, + /// If `status` is `CERT_STATUS_REVOKED`, the time at which the certificate was revoked. + pub revocation_time: Option<&'a Asn1GeneralizedTimeRef>, + /// The time that this revocation check was performed. + pub this_update: &'a Asn1GeneralizedTimeRef, + /// The time at which this revocation check expires. + pub next_update: &'a Asn1GeneralizedTimeRef, +} + +impl<'a> Status<'a> { + /// Checks validity of the `this_update` and `next_update` fields. + /// + /// The `nsec` parameter specifies an amount of slack time that will be used when comparing + /// those times with the current time to account for delays and clock skew. + /// + /// The `maxsec` parameter limits the maximum age of the `this_update` parameter to prohibit + /// very old responses. + pub fn check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_check_validity(self.this_update.as_ptr(), + self.next_update.as_ptr(), + nsec as c_long, + maxsec.map(|n| n as c_long).unwrap_or(-1))) + .map(|_| ()) + } + } +} + +type_!(OcspBasicResponse, OcspBasicResponseRef, ffi::OCSP_BASICRESP, ffi::OCSP_BASICRESP_free); + +impl OcspBasicResponseRef { + /// Verifies the validity of the response. + /// + /// The `certs` parameter contains a set of certificates that will be searched when locating the + /// OCSP response signing certificate. Some responders to not include this in the response. + pub fn verify(&self, + certs: &StackRef<X509>, + store: &X509StoreRef, + flags: Flag) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_basic_verify(self.as_ptr(), certs.as_ptr(), store.as_ptr(), flags.bits())) + .map(|_| ()) + } + } + + /// Looks up the status for the specified certificate ID. + pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<Status<'a>> { + unsafe { + let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN; + let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS; + let mut revocation_time = ptr::null_mut(); + let mut this_update = ptr::null_mut(); + let mut next_update = ptr::null_mut(); + + let r = ffi::OCSP_resp_find_status(self.as_ptr(), + id.as_ptr(), + &mut status, + &mut reason, + &mut revocation_time, + &mut this_update, + &mut next_update); + if r == 1 { + let revocation_time = if revocation_time.is_null() { + None + } else { + Some(Asn1GeneralizedTimeRef::from_ptr(revocation_time)) + }; + Some(Status { + status: OcspCertStatus(status), + reason: OcspRevokedStatus(status), + revocation_time: revocation_time, + this_update: Asn1GeneralizedTimeRef::from_ptr(this_update), + next_update: Asn1GeneralizedTimeRef::from_ptr(next_update), + }) + } else { + None + } + } + } +} + +type_!(OcspCertId, OcspCertIdRef, ffi::OCSP_CERTID, ffi::OCSP_CERTID_free); + +impl OcspCertId { + /// Constructs a certificate ID for certificate `subject`. + pub fn from_cert(digest: MessageDigest, + subject: &X509Ref, + issuer: &X509Ref) + -> Result<OcspCertId, ErrorStack> { + unsafe { + cvt_p(ffi::OCSP_cert_to_id(digest.as_ptr(), subject.as_ptr(), issuer.as_ptr())) + .map(OcspCertId) + } + } +} + +type_!(OcspResponse, OcspResponseRef, ffi::OCSP_RESPONSE, ffi::OCSP_RESPONSE_free); + +impl OcspResponse { + /// Creates an OCSP response from the status and optional body. + /// + /// A body should only be provided if `status` is `RESPONSE_STATUS_SUCCESSFUL`. + pub fn create(status: OcspResponseStatus, + body: Option<&OcspBasicResponseRef>) + -> Result<OcspResponse, ErrorStack> { + unsafe { + ffi::init(); + + cvt_p(ffi::OCSP_response_create(status.as_raw(), + body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut()))) + .map(OcspResponse) + } + } + + from_der!(OcspResponse, ffi::d2i_OCSP_RESPONSE); +} + +impl OcspResponseRef { + to_der!(ffi::i2d_OCSP_RESPONSE); + + /// Returns the status of the response. + pub fn status(&self) -> OcspResponseStatus { + unsafe { + OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr())) + } + } + + /// Returns the basic response. + /// + /// This will only succeed if `status()` returns `RESPONSE_STATUS_SUCCESSFUL`. + pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> { + unsafe { + cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(OcspBasicResponse) + } + } +} + +type_!(OcspRequest, OcspRequestRef, ffi::OCSP_REQUEST, ffi::OCSP_REQUEST_free); + +impl OcspRequest { + pub fn new() -> Result<OcspRequest, ErrorStack> { + unsafe { + ffi::init(); + + cvt_p(ffi::OCSP_REQUEST_new()).map(OcspRequest) + } + } + + from_der!(OcspRequest, ffi::d2i_OCSP_REQUEST); +} + +impl OcspRequestRef { + to_der!(ffi::i2d_OCSP_REQUEST); + + pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> { + unsafe { + let ptr = try!(cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id.as_ptr()))); + mem::forget(id); + Ok(OcspOneReqRef::from_ptr_mut(ptr)) + } + } +} + +type_!(OcspOneReq, OcspOneReqRef, ffi::OCSP_ONEREQ, ffi::OCSP_ONEREQ_free); diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index f412ca93..2fc7605a 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -97,14 +97,14 @@ use ec::EcKeyRef; #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] use ec::EcKey; use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError, X509Name}; -use x509::store::X509StoreBuilderRef; +use x509::store::{X509StoreBuilderRef, X509StoreRef}; #[cfg(any(ossl102, ossl110))] use verify::X509VerifyParamRef; use pkey::PKeyRef; use error::ErrorStack; use types::{OpenSslType, OpenSslTypeRef}; use util::Opaque; -use stack::Stack; +use stack::{Stack, StackRef}; mod error; mod connector; @@ -217,6 +217,22 @@ bitflags! { } } +#[derive(Copy, Clone)] +pub struct StatusType(c_int); + +impl StatusType { + pub fn from_raw(raw: c_int) -> StatusType { + StatusType(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// An OSCP status. +pub const STATUS_TYPE_OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp); + lazy_static! { static ref INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new()); static ref SSL_INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new()); @@ -475,6 +491,37 @@ unsafe extern fn raw_tmp_ecdh_ssl<F>(ssl: *mut ffi::SSL, } } +unsafe extern fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int + where F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + Any + 'static + Sync + Send +{ + let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl as *const _); + let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::<F>()); + let callback = &*(callback as *mut F); + + let ssl = SslRef::from_ptr_mut(ssl); + let ret = callback(ssl); + + if ssl.is_server() { + match ret { + Ok(true) => ffi::SSL_TLSEXT_ERR_OK, + Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK, + Err(_) => { + // FIXME reset error stack + ffi::SSL_TLSEXT_ERR_ALERT_FATAL + } + } + } else { + match ret { + Ok(true) => 1, + Ok(false) => 0, + Err(_) => { + // FIXME reset error stack + -1 + } + } + } +} + /// The function is given as the callback to `SSL_CTX_set_next_protos_advertised_cb`. /// /// It causes the parameter `out` to point at a `*const c_uchar` instance that @@ -887,6 +934,31 @@ impl SslContextBuilder { unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } + /// Sets the callback dealing with OCSP stapling. + /// + /// On the client side, this callback is responsible for validating the OCSP status response + /// returned by the server. The status may be retrieved with the `SslRef::ocsp_status` method. + /// A response of `Ok(true)` indicates that the OCSP status is valid, and a response of + /// `Ok(false)` indicates that the OCSP status is invalid and the handshake should be + /// terminated. + /// + /// On the server side, this callback is resopnsible for setting the OCSP status response to be + /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A + /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and + /// `Ok(false)` indicates that the status should not be returned to the client. + pub fn set_status_callback<F>(&mut self, callback: F) -> Result<(), ErrorStack> + where F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + Any + 'static + Sync + Send + { + unsafe { + let callback = Box::new(callback); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), + get_callback_idx::<F>(), + Box::into_raw(callback) as *mut c_void); + let f: unsafe extern fn (_, _) -> _ = raw_tlsext_status::<F>; + cvt(ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(f)) as c_int).map(|_| ()) + } + } + pub fn build(self) -> SslContext { let ctx = SslContext(self.0); mem::forget(self); @@ -951,6 +1023,22 @@ impl SslContextRef { } } } + + /// Returns the certificate store used for verification. + pub fn cert_store(&self) -> &X509StoreRef { + unsafe { + X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) + } + } + + pub fn extra_chain_certs(&self) -> &StackRef<X509> { + unsafe { + let mut chain = ptr::null_mut(); + ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain); + assert!(!chain.is_null()); + StackRef::from_ptr(chain) + } + } } pub struct CipherBits { @@ -1378,6 +1466,49 @@ impl SslRef { } } } + + /// Sets the status response a client wishes the server to reply with. + pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) + } + } + + /// Returns the server's OCSP response, if present. + pub fn ocsp_status(&self) -> Option<&[u8]> { + unsafe { + let mut p = ptr::null_mut(); + let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p); + + if len < 0 { + None + } else { + Some(slice::from_raw_parts(p as *const u8, len as usize)) + } + } + } + + /// Sets the OCSP response to be returned to the client. + pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(response.len() <= c_int::max_value() as usize); + let p = try!(cvt_p(ffi::CRYPTO_malloc(response.len() as _, + concat!(file!(), "\0").as_ptr() as *const _, + line!() as c_int))); + ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len()); + cvt(ffi::SSL_set_tlsext_status_ocsp_resp(self.as_ptr(), + p as *mut c_uchar, + response.len() as c_long) as c_int) + .map(|_| ()) + } + } + + /// Determines if this `Ssl` is configured for server-side or client-side use. + pub fn is_server(&self) -> bool { + unsafe { + compat::SSL_is_server(self.as_ptr()) != 0 + } + } } unsafe impl Sync for Ssl {} @@ -1745,9 +1876,8 @@ mod compat { use ffi; use libc::c_int; - pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options}; - pub use ffi::{SSL_CTX_clear_options, SSL_CTX_up_ref}; - pub use ffi::SSL_SESSION_get_master_key; + pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_clear_options, SSL_CTX_up_ref, + SSL_SESSION_get_master_key, SSL_is_server}; pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL_CTX, @@ -1839,4 +1969,8 @@ mod compat { pub fn dtls_method() -> *const ffi::SSL_METHOD { unsafe { ffi::DTLSv1_method() } } + + pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int { + (*s).server + } } diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs index 14bb2f71..349c7a4d 100644 --- a/openssl/src/ssl/tests/mod.rs +++ b/openssl/src/ssl/tests/mod.rs @@ -16,9 +16,11 @@ use tempdir::TempDir; use dh::Dh; use hash::MessageDigest; +use ocsp::{OcspResponse, RESPONSE_STATUS_UNAUTHORIZED}; use ssl; use ssl::{SslMethod, HandshakeError, SslContext, SslStream, Ssl, ShutdownResult, - SslConnectorBuilder, SslAcceptorBuilder, Error, SSL_VERIFY_PEER, SSL_VERIFY_NONE}; + SslConnectorBuilder, SslAcceptorBuilder, Error, SSL_VERIFY_PEER, SSL_VERIFY_NONE, + STATUS_TYPE_OCSP}; use x509::{X509StoreContext, X509, X509Name, X509_FILETYPE_PEM}; #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] use x509::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; @@ -1393,7 +1395,48 @@ fn active_session() { let mut buf = vec![0; len + 1]; let copied = session.master_key(&mut buf); assert_eq!(copied, len); +} + +#[test] +fn status_callbacks() { + static CALLED_BACK_SERVER: AtomicBool = ATOMIC_BOOL_INIT; + static CALLED_BACK_CLIENT: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_status_callback(|ssl| { + CALLED_BACK_SERVER.store(true, Ordering::SeqCst); + let response = OcspResponse::create(RESPONSE_STATUS_UNAUTHORIZED, None).unwrap(); + let response = response.to_der().unwrap(); + ssl.set_ocsp_status(&response).unwrap(); + Ok(true) + }).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_status_callback(|ssl| { + CALLED_BACK_CLIENT.store(true, Ordering::SeqCst); + let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap(); + assert_eq!(response.status(), RESPONSE_STATUS_UNAUTHORIZED); + Ok(true) + }); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_status_type(STATUS_TYPE_OCSP).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst)); + assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst)); + + guard.join().unwrap(); } fn _check_kinds() { diff --git a/openssl/src/string.rs b/openssl/src/string.rs new file mode 100644 index 00000000..37d44d16 --- /dev/null +++ b/openssl/src/string.rs @@ -0,0 +1,74 @@ +use ffi; +use libc::{c_char, c_void}; +use std::fmt; +use std::ffi::CStr; +use std::ops::Deref; +use std::str; + +use types::{OpenSslType, OpenSslTypeRef}; +use stack::Stackable; + +type_!(OpensslString, OpensslStringRef, c_char, free); + +impl OpensslString { + #[deprecated(note = "use from_ptr", since = "0.9.7")] + pub unsafe fn from_raw_parts(buf: *mut u8, _: usize) -> OpensslString { + OpensslString::from_ptr(buf as *mut c_char) + } + + #[deprecated(note = "use from_ptr", since = "0.9.7")] + pub unsafe fn from_null_terminated(buf: *mut c_char) -> OpensslString { + OpensslString::from_ptr(buf) + } +} + +impl fmt::Display for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Stackable for OpensslString { + type StackType = ffi::stack_st_OPENSSL_STRING; +} + +impl Deref for OpensslStringRef { + type Target = str; + + fn deref(&self) -> &str { + unsafe { + let slice = CStr::from_ptr(self.as_ptr()).to_bytes(); + str::from_utf8_unchecked(slice) + } + } +} + +impl fmt::Display for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[cfg(not(ossl110))] +unsafe fn free(buf: *mut c_char) { + ::ffi::CRYPTO_free(buf as *mut c_void); +} + +#[cfg(ossl110)] +unsafe fn free(buf: *mut c_char) { + ::ffi::CRYPTO_free(buf as *mut c_void, + concat!(file!(), "\0").as_ptr() as *const c_char, + line!() as ::libc::c_int); +} diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index d90cee22..e75dcf5d 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -20,6 +20,7 @@ use error::ErrorStack; use ffi; use nid::Nid; use types::{OpenSslType, OpenSslTypeRef}; +use string::OpensslString; use stack::{Stack, StackRef, Stackable}; #[cfg(ossl10x)] @@ -415,6 +416,25 @@ impl X509Ref { } } + /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information + /// Access field. + pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> { + unsafe { + cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) + } + } + + /// Checks that this certificate issued `subject`. + pub fn issued(&self, subject: &X509Ref) -> Result<(), X509VerifyError> { + unsafe { + let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr()); + match X509VerifyError::from_raw(r as c_long) { + Some(e) => Err(e), + None => Ok(()), + } + } + } + to_pem!(ffi::PEM_write_bio_X509); to_der!(ffi::i2d_X509); } diff --git a/openssl/src/x509/store.rs b/openssl/src/x509/store.rs index 01eb0e2f..dd08a49b 100644 --- a/openssl/src/x509/store.rs +++ b/openssl/src/x509/store.rs @@ -1,13 +1,33 @@ use ffi; use std::mem; -use cvt; +use {cvt, cvt_p}; use error::ErrorStack; use types::OpenSslTypeRef; use x509::X509; type_!(X509StoreBuilder, X509StoreBuilderRef, ffi::X509_STORE, ffi::X509_STORE_free); +impl X509StoreBuilder { + /// Returns a builder for a certificate store. + /// + /// The store is initially empty. + pub fn new() -> Result<X509StoreBuilder, ErrorStack> { + unsafe { + ffi::init(); + + cvt_p(ffi::X509_STORE_new()).map(X509StoreBuilder) + } + } + + /// Constructs the `X509Store`. + pub fn build(self) -> X509Store { + let store = X509Store(self.0); + mem::forget(self); + store + } +} + impl X509StoreBuilderRef { /// Adds a certificate to the certificate store. pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { @@ -17,4 +37,17 @@ impl X509StoreBuilderRef { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), ptr)).map(|_| ()) } } + + /// Load certificates from their default locations. + /// + /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` + /// environment variables if present, or defaults specified at OpenSSL + /// build time otherwise. + pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) + } + } } + +type_!(X509Store, X509StoreRef, ffi::X509_STORE, ffi::X509_STORE_free); diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 0843b19f..f89b7267 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -132,7 +132,7 @@ fn test_nid_values() { assert_eq!(email.data().as_slice(), b"test@example.com"); let friendly = subject.entries_by_nid(nid::FRIENDLYNAME).next().unwrap(); - assert_eq!(&*friendly.data().as_utf8().unwrap(), "Example"); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); } #[test] @@ -186,3 +186,14 @@ fn test_stack_from_pem() { assert_eq!(certs[1].fingerprint(MessageDigest::sha1()).unwrap().to_hex(), "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"); } + +#[test] +fn issued() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + + ca.issued(&cert).unwrap(); + cert.issued(&cert).err().unwrap(); +} diff --git a/systest/build.rs b/systest/build.rs index 2f0fd136..b12739f0 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -46,7 +46,8 @@ fn main() { .header("openssl/err.h") .header("openssl/rand.h") .header("openssl/pkcs12.h") - .header("openssl/bn.h"); + .header("openssl/bn.h") + .header("openssl/ocsp.h"); cfg.type_name(|s, is_struct| { // Add some `*` on some callback parameters to get function pointer to // typecheck in C, especially on MSVC. @@ -90,6 +91,8 @@ fn main() { }); cfg.skip_signededness(|s| { s.ends_with("_cb") || + s.ends_with("_CB") || + s.ends_with("_cb_fn") || s.starts_with("CRYPTO_") || s == "PasswordCallback" }); |