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