1/* 2 * Copyright (C) 2004-2007 Technorama Ltd. <oss-ruby@technorama.net> 3 */ 4 5#include "ossl.h" 6 7#define GetSSLSession(obj, sess) do { \ 8 Data_Get_Struct((obj), SSL_SESSION, (sess)); \ 9 if (!(sess)) { \ 10 ossl_raise(rb_eRuntimeError, "SSL Session wasn't initialized."); \ 11 } \ 12} while (0) 13 14#define SafeGetSSLSession(obj, sess) do { \ 15 OSSL_Check_Kind((obj), cSSLSession); \ 16 GetSSLSession((obj), (sess)); \ 17} while (0) 18 19 20VALUE cSSLSession; 21static VALUE eSSLSession; 22 23static VALUE ossl_ssl_session_alloc(VALUE klass) 24{ 25 return Data_Wrap_Struct(klass, 0, SSL_SESSION_free, NULL); 26} 27 28/* 29 * call-seq: 30 * Session.new(SSLSocket | string) => session 31 * 32 * === Parameters 33 * +SSLSocket+ is an OpenSSL::SSL::SSLSocket 34 * +string+ must be a DER or PEM encoded Session. 35*/ 36static VALUE ossl_ssl_session_initialize(VALUE self, VALUE arg1) 37{ 38 SSL_SESSION *ctx = NULL; 39 40 if (RDATA(self)->data) 41 ossl_raise(eSSLSession, "SSL Session already initialized"); 42 43 if (rb_obj_is_instance_of(arg1, cSSLSocket)) { 44 SSL *ssl; 45 46 Data_Get_Struct(arg1, SSL, ssl); 47 48 if (!ssl || (ctx = SSL_get1_session(ssl)) == NULL) 49 ossl_raise(eSSLSession, "no session available"); 50 } else { 51 BIO *in = ossl_obj2bio(arg1); 52 53 ctx = PEM_read_bio_SSL_SESSION(in, NULL, NULL, NULL); 54 55 if (!ctx) { 56 OSSL_BIO_reset(in); 57 ctx = d2i_SSL_SESSION_bio(in, NULL); 58 } 59 60 BIO_free(in); 61 62 if (!ctx) 63 ossl_raise(rb_eArgError, "unknown type"); 64 } 65 66 /* should not happen */ 67 if (ctx == NULL) 68 ossl_raise(eSSLSession, "ctx not set - internal error"); 69 70 RDATA(self)->data = ctx; 71 72 return self; 73} 74 75#if HAVE_SSL_SESSION_CMP == 0 76int SSL_SESSION_cmp(const SSL_SESSION *a,const SSL_SESSION *b) 77{ 78 if (a->ssl_version != b->ssl_version || 79 a->session_id_length != b->session_id_length) 80 return 1; 81 return memcmp(a->session_id,b-> session_id, a->session_id_length); 82} 83#endif 84 85/* 86 * call-seq: 87 * session1 == session2 -> boolean 88 * 89*/ 90static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2) 91{ 92 SSL_SESSION *ctx1, *ctx2; 93 94 GetSSLSession(val1, ctx1); 95 SafeGetSSLSession(val2, ctx2); 96 97 switch (SSL_SESSION_cmp(ctx1, ctx2)) { 98 case 0: return Qtrue; 99 default: return Qfalse; 100 } 101} 102 103/* 104 * call-seq: 105 * session.time -> Time 106 * 107 * Gets start time of the session. 108 * 109*/ 110static VALUE ossl_ssl_session_get_time(VALUE self) 111{ 112 SSL_SESSION *ctx; 113 time_t t; 114 115 GetSSLSession(self, ctx); 116 117 t = SSL_SESSION_get_time(ctx); 118 119 if (t == 0) 120 return Qnil; 121 122 return rb_funcall(rb_cTime, rb_intern("at"), 1, TIMET2NUM(t)); 123} 124 125/* 126 * call-seq: 127 * session.timeout -> integer 128 * 129 * Gets how long until the session expires in seconds. 130 * 131*/ 132static VALUE ossl_ssl_session_get_timeout(VALUE self) 133{ 134 SSL_SESSION *ctx; 135 time_t t; 136 137 GetSSLSession(self, ctx); 138 139 t = SSL_SESSION_get_timeout(ctx); 140 141 return TIMET2NUM(t); 142} 143 144/* 145 * call-seq: 146 * session.time=(Time) -> Time 147 * session.time=(integer) -> Time 148 * 149 * Sets start time of the session. Time resolution is in seconds. 150 * 151*/ 152static VALUE ossl_ssl_session_set_time(VALUE self, VALUE time_v) 153{ 154 SSL_SESSION *ctx; 155 long t; 156 157 GetSSLSession(self, ctx); 158 if (rb_obj_is_instance_of(time_v, rb_cTime)) { 159 time_v = rb_funcall(time_v, rb_intern("to_i"), 0); 160 } 161 t = NUM2LONG(time_v); 162 SSL_SESSION_set_time(ctx, t); 163 return ossl_ssl_session_get_time(self); 164} 165 166/* 167 * call-seq: 168 * session.timeout=(integer) -> integer 169 * 170 * Sets how long until the session expires in seconds. 171 * 172*/ 173static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) 174{ 175 SSL_SESSION *ctx; 176 long t; 177 178 GetSSLSession(self, ctx); 179 t = NUM2LONG(time_v); 180 SSL_SESSION_set_timeout(ctx, t); 181 return ossl_ssl_session_get_timeout(self); 182} 183 184#ifdef HAVE_SSL_SESSION_GET_ID 185/* 186 * call-seq: 187 * session.id -> aString 188 * 189 * Returns the Session ID. 190*/ 191static VALUE ossl_ssl_session_get_id(VALUE self) 192{ 193 SSL_SESSION *ctx; 194 const unsigned char *p = NULL; 195 unsigned int i = 0; 196 197 GetSSLSession(self, ctx); 198 199 p = SSL_SESSION_get_id(ctx, &i); 200 201 return rb_str_new((const char *) p, i); 202} 203#endif 204 205/* 206 * call-seq: 207 * session.to_der -> aString 208 * 209 * Returns an ASN1 encoded String that contains the Session object. 210*/ 211static VALUE ossl_ssl_session_to_der(VALUE self) 212{ 213 SSL_SESSION *ctx; 214 unsigned char *p; 215 int len; 216 VALUE str; 217 218 GetSSLSession(self, ctx); 219 len = i2d_SSL_SESSION(ctx, NULL); 220 if (len <= 0) { 221 ossl_raise(eSSLSession, "i2d_SSL_SESSION"); 222 } 223 224 str = rb_str_new(0, len); 225 p = (unsigned char *)RSTRING_PTR(str); 226 i2d_SSL_SESSION(ctx, &p); 227 ossl_str_adjust(str, p); 228 return str; 229} 230 231/* 232 * call-seq: 233 * session.to_pem -> String 234 * 235 * Returns a PEM encoded String that contains the Session object. 236*/ 237static VALUE ossl_ssl_session_to_pem(VALUE self) 238{ 239 SSL_SESSION *ctx; 240 BIO *out; 241 BUF_MEM *buf; 242 VALUE str; 243 int i; 244 245 GetSSLSession(self, ctx); 246 247 if (!(out = BIO_new(BIO_s_mem()))) { 248 ossl_raise(eSSLSession, "BIO_s_mem()"); 249 } 250 251 if (!(i=PEM_write_bio_SSL_SESSION(out, ctx))) { 252 BIO_free(out); 253 ossl_raise(eSSLSession, "SSL_SESSION_print()"); 254 } 255 256 BIO_get_mem_ptr(out, &buf); 257 str = rb_str_new(buf->data, buf->length); 258 BIO_free(out); 259 260 return str; 261} 262 263 264/* 265 * call-seq: 266 * session.to_text -> String 267 * 268 * Shows everything in the Session object. 269*/ 270static VALUE ossl_ssl_session_to_text(VALUE self) 271{ 272 SSL_SESSION *ctx; 273 BIO *out; 274 BUF_MEM *buf; 275 VALUE str; 276 277 GetSSLSession(self, ctx); 278 279 if (!(out = BIO_new(BIO_s_mem()))) { 280 ossl_raise(eSSLSession, "BIO_s_mem()"); 281 } 282 283 if (!SSL_SESSION_print(out, ctx)) { 284 BIO_free(out); 285 ossl_raise(eSSLSession, "SSL_SESSION_print()"); 286 } 287 288 BIO_get_mem_ptr(out, &buf); 289 str = rb_str_new(buf->data, buf->length); 290 BIO_free(out); 291 292 return str; 293} 294 295 296void Init_ossl_ssl_session(void) 297{ 298#if 0 299 mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ 300 mSSL = rb_define_module_under(mOSSL, "SSL"); 301#endif 302 cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); 303 eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); 304 305 rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); 306 rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); 307 308 rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); 309 310 rb_define_method(cSSLSession, "time", ossl_ssl_session_get_time, 0); 311 rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); 312 rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); 313 rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); 314 315#ifdef HAVE_SSL_SESSION_GET_ID 316 rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); 317#else 318 rb_undef_method(cSSLSession, "id"); 319#endif 320 rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); 321 rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); 322 rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); 323} 324