3 * Web service library with GLib integration
5 * Copyright (C) 2009-2012 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <gnutls/gnutls.h>
32 #include <tpkp_gnutls.h>
34 #include "giognutls.h"
36 //#define DBG(fmt, arg...) printf("%s: " fmt "\n" , __func__ , ## arg)
37 #define DBG(fmt, arg...)
39 typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel;
40 typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch;
42 struct _GIOGnuTLSChannel {
45 gnutls_certificate_credentials_t cred;
46 gnutls_session_t session;
51 struct _GIOGnuTLSWatch {
55 GIOCondition condition;
58 static volatile int global_init_done = 0;
60 static inline void g_io_gnutls_global_init(void)
62 if (__sync_bool_compare_and_swap(&global_init_done, 0, 1))
66 static GIOStatus check_handshake(GIOChannel *channel, GError **err)
68 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
71 DBG("channel %p", channel);
73 if (gnutls_channel->established)
74 return G_IO_STATUS_NORMAL;
77 result = gnutls_handshake(gnutls_channel->session);
79 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
80 GIOFlags flags = g_io_channel_get_flags(channel);
82 if (gnutls_channel->again)
83 return G_IO_STATUS_AGAIN;
85 if (flags & G_IO_FLAG_NONBLOCK)
86 return G_IO_STATUS_AGAIN;
92 g_set_error(err, G_IO_CHANNEL_ERROR,
93 G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
94 return G_IO_STATUS_ERROR;
97 gnutls_channel->established = true;
99 DBG("handshake done");
101 return G_IO_STATUS_NORMAL;
104 static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
105 gsize count, gsize *bytes_read, GError **err)
107 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
111 DBG("channel %p count %zu", channel, count);
116 status = check_handshake(channel, err);
117 if (status != G_IO_STATUS_NORMAL)
120 result = gnutls_record_recv(gnutls_channel->session, buf, count);
122 DBG("result %zd", result);
124 if (result == GNUTLS_E_REHANDSHAKE) {
125 gnutls_channel->established = false;
129 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
130 GIOFlags flags = g_io_channel_get_flags(channel);
132 if (gnutls_channel->again)
133 return G_IO_STATUS_AGAIN;
135 if (flags & G_IO_FLAG_NONBLOCK)
136 return G_IO_STATUS_AGAIN;
141 if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
142 return G_IO_STATUS_EOF;
145 g_set_error(err, G_IO_CHANNEL_ERROR,
146 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
147 return G_IO_STATUS_ERROR;
150 *bytes_read = result;
152 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
155 static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
156 gsize count, gsize *bytes_written, GError **err)
158 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
162 DBG("channel %p count %zu", channel, count);
167 status = check_handshake(channel, err);
168 if (status != G_IO_STATUS_NORMAL)
171 result = gnutls_record_send(gnutls_channel->session, buf, count);
173 DBG("result %zd", result);
175 if (result == GNUTLS_E_REHANDSHAKE) {
176 gnutls_channel->established = false;
180 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
181 GIOFlags flags = g_io_channel_get_flags(channel);
183 if (gnutls_channel->again)
184 return G_IO_STATUS_AGAIN;
186 if (flags & G_IO_FLAG_NONBLOCK)
187 return G_IO_STATUS_AGAIN;
193 g_set_error(err, G_IO_CHANNEL_ERROR,
194 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
195 return G_IO_STATUS_ERROR;
198 *bytes_written = result;
200 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
203 static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
204 GSeekType type, GError **err)
206 DBG("channel %p", channel);
208 g_set_error_literal(err, G_IO_CHANNEL_ERROR,
209 G_IO_CHANNEL_ERROR_FAILED, "Not supported");
210 return G_IO_STATUS_ERROR;
213 static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
215 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
217 DBG("channel %p", channel);
219 if (gnutls_channel->established)
220 gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
222 if (close(gnutls_channel->fd) < 0) {
223 g_set_error_literal(err, G_IO_CHANNEL_ERROR,
224 G_IO_CHANNEL_ERROR_FAILED, "Closing failed");
225 return G_IO_STATUS_ERROR;
228 return G_IO_STATUS_NORMAL;
231 static void g_io_gnutls_free(GIOChannel *channel)
233 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
235 DBG("channel %p", channel);
237 gnutls_deinit(gnutls_channel->session);
239 tpkp_gnutls_cleanup();
241 gnutls_certificate_free_credentials(gnutls_channel->cred);
243 g_free(gnutls_channel);
246 static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
247 GIOFlags flags, GError **err)
249 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
250 glong fcntl_flags = 0;
252 DBG("channel %p flags %u", channel, flags);
254 if (flags & G_IO_FLAG_NONBLOCK)
255 fcntl_flags |= O_NONBLOCK;
257 if (fcntl(gnutls_channel->fd, F_SETFL, fcntl_flags) < 0) {
258 g_set_error_literal(err, G_IO_CHANNEL_ERROR,
259 G_IO_CHANNEL_ERROR_FAILED, "Setting flags failed");
260 return G_IO_STATUS_ERROR;
263 return G_IO_STATUS_NORMAL;
266 static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
268 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
272 DBG("channel %p", channel);
274 fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL);
278 if (fcntl_flags & O_NONBLOCK)
279 flags |= G_IO_FLAG_NONBLOCK;
284 static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
286 DBG("source %p", source);
293 static gboolean g_io_gnutls_check(GSource *source)
295 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
296 GIOCondition condition = watch->pollfd.revents;
298 DBG("source %p condition %u", source, condition);
300 if (condition & watch->condition)
306 static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
309 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
310 GIOFunc func = (GIOFunc) callback;
311 GIOCondition condition = watch->pollfd.revents;
313 DBG("source %p condition %u", source, condition);
318 return func(watch->channel, condition & watch->condition, user_data);
321 static void g_io_gnutls_finalize(GSource *source)
323 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
325 DBG("source %p", source);
327 g_io_channel_unref(watch->channel);
330 static GSourceFuncs gnutls_watch_funcs = {
333 g_io_gnutls_dispatch,
334 g_io_gnutls_finalize,
337 static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
338 GIOCondition condition)
340 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
341 GIOGnuTLSWatch *watch;
344 DBG("channel %p condition %u", channel, condition);
346 source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
348 watch = (GIOGnuTLSWatch *) source;
350 watch->channel = channel;
351 g_io_channel_ref(channel);
353 watch->condition = condition;
355 watch->pollfd.fd = gnutls_channel->fd;
356 watch->pollfd.events = condition;
358 g_source_add_poll(source, &watch->pollfd);
363 static GIOFuncs gnutls_channel_funcs = {
368 g_io_gnutls_create_watch,
370 g_io_gnutls_set_flags,
371 g_io_gnutls_get_flags,
374 static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
375 const void *buf, size_t count)
377 GIOGnuTLSChannel *gnutls_channel = transport_data;
380 DBG("count %zu", count);
382 result = write(gnutls_channel->fd, buf, count);
384 if (result < 0 && errno == EAGAIN)
385 gnutls_channel->again = true;
387 gnutls_channel->again = false;
389 DBG("result %zd", result);
394 static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
395 void *buf, size_t count)
397 GIOGnuTLSChannel *gnutls_channel = transport_data;
400 DBG("count %zu", count);
402 result = read(gnutls_channel->fd, buf, count);
404 if (result < 0 && errno == EAGAIN)
405 gnutls_channel->again = true;
407 gnutls_channel->again = false;
409 DBG("result %zd", result);
414 bool g_io_channel_supports_tls(void)
419 GIOChannel *g_io_channel_gnutls_new(int fd)
421 GIOGnuTLSChannel *gnutls_channel;
427 gnutls_channel = g_new(GIOGnuTLSChannel, 1);
429 channel = (GIOChannel *) gnutls_channel;
431 g_io_channel_init(channel);
432 channel->funcs = &gnutls_channel_funcs;
434 gnutls_channel->fd = fd;
436 channel->is_seekable = FALSE;
437 channel->is_readable = TRUE;
438 channel->is_writeable = TRUE;
440 channel->do_encode = FALSE;
442 g_io_gnutls_global_init();
444 err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
446 g_free(gnutls_channel);
450 gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel);
451 gnutls_transport_set_push_function(gnutls_channel->session,
452 g_io_gnutls_push_func);
453 gnutls_transport_set_pull_function(gnutls_channel->session,
454 g_io_gnutls_pull_func);
455 #if GNUTLS_VERSION_NUMBER < 0x020c00
456 gnutls_transport_set_lowat(gnutls_channel->session, 0);
458 gnutls_priority_set_direct(gnutls_channel->session,
459 "NORMAL:%COMPAT", NULL);
461 gnutls_priority_set_direct(gnutls_channel->session,
462 "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL);
465 gnutls_certificate_set_verify_function(gnutls_channel->cred, &tpkp_gnutls_verify_callback);
467 * TODO: get ca-bundle path build-time configuration unless gnutls set it as a default
469 gnutls_certificate_set_x509_trust_file(gnutls_channel->cred, "/etc/ssl/ca-bundle.pem", GNUTLS_X509_FMT_PEM);
471 gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
472 gnutls_credentials_set(gnutls_channel->session,
473 GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
475 DBG("channel %p", channel);