Merge "Fix a svace 20384" into tizen
[platform/upstream/connman.git] / gweb / giognutls.c
1 /*
2  *
3  *  Web service library with GLib integration
4  *
5  *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30
31 #include <gnutls/gnutls.h>
32 #include <tpkp_gnutls.h>
33
34 #include "giognutls.h"
35
36 //#define DBG(fmt, arg...)  printf("%s: " fmt "\n" , __func__ , ## arg)
37 #define DBG(fmt, arg...)
38
39 typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel;
40 typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch;
41
42 struct _GIOGnuTLSChannel {
43         GIOChannel channel;
44         gint fd;
45         gnutls_certificate_credentials_t cred;
46         gnutls_session_t session;
47         bool established;
48         bool again;
49 };
50
51 struct _GIOGnuTLSWatch {
52         GSource source;
53         GPollFD pollfd;
54         GIOChannel *channel;
55         GIOCondition condition;
56 };
57
58 static volatile int global_init_done = 0;
59
60 static inline void g_io_gnutls_global_init(void)
61 {
62         if (__sync_bool_compare_and_swap(&global_init_done, 0, 1))
63                 gnutls_global_init();
64 }
65
66 static GIOStatus check_handshake(GIOChannel *channel, GError **err)
67 {
68         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
69         int result;
70
71         DBG("channel %p", channel);
72
73         if (gnutls_channel->established)
74                 return G_IO_STATUS_NORMAL;
75
76 again:
77         result = gnutls_handshake(gnutls_channel->session);
78
79         if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
80                 GIOFlags flags = g_io_channel_get_flags(channel);
81
82                 if (gnutls_channel->again)
83                         return G_IO_STATUS_AGAIN;
84
85                 if (flags & G_IO_FLAG_NONBLOCK)
86                         return G_IO_STATUS_AGAIN;
87
88                 goto again;
89         }
90
91         if (result < 0) {
92                 g_set_error(err, G_IO_CHANNEL_ERROR,
93                                 G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
94                 return G_IO_STATUS_ERROR;
95         }
96
97         gnutls_channel->established = true;
98
99         DBG("handshake done");
100
101         return G_IO_STATUS_NORMAL;
102 }
103
104 static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
105                                 gsize count, gsize *bytes_read, GError **err)
106 {
107         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
108         GIOStatus status;
109         ssize_t result;
110
111         DBG("channel %p count %zu", channel, count);
112
113         *bytes_read = 0;
114
115 again:
116         status = check_handshake(channel, err);
117         if (status != G_IO_STATUS_NORMAL)
118                 return status;
119
120         result = gnutls_record_recv(gnutls_channel->session, buf, count);
121
122         DBG("result %zd", result);
123
124         if (result == GNUTLS_E_REHANDSHAKE) {
125                 gnutls_channel->established = false;
126                 goto again;
127         }
128
129         if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
130                 GIOFlags flags = g_io_channel_get_flags(channel);
131
132                 if (gnutls_channel->again)
133                         return G_IO_STATUS_AGAIN;
134
135                 if (flags & G_IO_FLAG_NONBLOCK)
136                         return G_IO_STATUS_AGAIN;
137
138                 goto again;
139         }
140
141         if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
142                 return G_IO_STATUS_EOF;
143
144         if (result < 0) {
145                 g_set_error(err, G_IO_CHANNEL_ERROR,
146                                 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
147                 return G_IO_STATUS_ERROR;
148         }
149
150         *bytes_read = result;
151
152         return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
153 }
154
155 static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
156                                 gsize count, gsize *bytes_written, GError **err)
157 {
158         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
159         GIOStatus status;
160         ssize_t result;
161
162         DBG("channel %p count %zu", channel, count);
163
164         *bytes_written = 0;
165
166 again:
167         status = check_handshake(channel, err);
168         if (status != G_IO_STATUS_NORMAL)
169                 return status;
170
171         result = gnutls_record_send(gnutls_channel->session, buf, count);
172
173         DBG("result %zd", result);
174
175         if (result == GNUTLS_E_REHANDSHAKE) {
176                 gnutls_channel->established = false;
177                 goto again;
178         }
179
180         if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
181                 GIOFlags flags = g_io_channel_get_flags(channel);
182
183                 if (gnutls_channel->again)
184                         return G_IO_STATUS_AGAIN;
185
186                 if (flags & G_IO_FLAG_NONBLOCK)
187                         return G_IO_STATUS_AGAIN;
188
189                 goto again;
190         }
191
192         if (result < 0) {
193                 g_set_error(err, G_IO_CHANNEL_ERROR,
194                                 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
195                 return G_IO_STATUS_ERROR;
196         }
197
198         *bytes_written = result;
199
200         return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
201 }
202
203 static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
204                                                 GSeekType type, GError **err)
205 {
206         DBG("channel %p", channel);
207
208         g_set_error_literal(err, G_IO_CHANNEL_ERROR,
209                                 G_IO_CHANNEL_ERROR_FAILED, "Not supported");
210         return G_IO_STATUS_ERROR;
211 }
212
213 static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
214 {
215         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
216
217         DBG("channel %p", channel);
218
219         if (gnutls_channel->established)
220                 gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
221
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;
226         }
227
228         return G_IO_STATUS_NORMAL;
229 }
230
231 static void g_io_gnutls_free(GIOChannel *channel)
232 {
233         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
234
235         DBG("channel %p", channel);
236
237         gnutls_deinit(gnutls_channel->session);
238
239         tpkp_gnutls_cleanup();
240
241         gnutls_certificate_free_credentials(gnutls_channel->cred);
242
243         g_free(gnutls_channel);
244 }
245
246 static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
247                                                 GIOFlags flags, GError **err)
248 {
249         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
250         glong fcntl_flags = 0;
251
252         DBG("channel %p flags %u", channel, flags);
253
254         if (flags & G_IO_FLAG_NONBLOCK)
255                 fcntl_flags |= O_NONBLOCK;
256
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;
261         }
262
263         return G_IO_STATUS_NORMAL;
264 }
265
266 static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
267 {
268         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
269         GIOFlags flags = 0;
270         glong fcntl_flags;
271
272         DBG("channel %p", channel);
273
274         fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL);
275         if (fcntl_flags < 0)
276                 return 0;
277
278         if (fcntl_flags & O_NONBLOCK)
279                 flags |= G_IO_FLAG_NONBLOCK;
280
281         return flags;
282 }
283
284 static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
285 {
286         DBG("source %p", source);
287
288         *timeout = -1;
289
290         return FALSE;
291 }
292
293 static gboolean g_io_gnutls_check(GSource *source)
294 {
295         GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
296         GIOCondition condition = watch->pollfd.revents;
297
298         DBG("source %p condition %u", source, condition);
299
300         if (condition & watch->condition)
301                 return TRUE;
302
303         return FALSE;
304 }
305
306 static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
307                                                         gpointer user_data)
308 {
309         GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
310         GIOFunc func = (GIOFunc) callback;
311         GIOCondition condition = watch->pollfd.revents;
312
313         DBG("source %p condition %u", source, condition);
314
315         if (!func)
316                 return FALSE;
317
318         return func(watch->channel, condition & watch->condition, user_data);
319 }
320
321 static void g_io_gnutls_finalize(GSource *source)
322 {
323         GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
324
325         DBG("source %p", source);
326
327         g_io_channel_unref(watch->channel);
328 }
329
330 static GSourceFuncs gnutls_watch_funcs = {
331         g_io_gnutls_prepare,
332         g_io_gnutls_check,
333         g_io_gnutls_dispatch,
334         g_io_gnutls_finalize,
335 };
336
337 static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
338                                                 GIOCondition condition)
339 {
340         GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
341         GIOGnuTLSWatch *watch;
342         GSource *source;
343
344         DBG("channel %p condition %u", channel, condition);
345
346         source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
347
348         watch = (GIOGnuTLSWatch *) source;
349
350         watch->channel = channel;
351         g_io_channel_ref(channel);
352
353         watch->condition = condition;
354
355         watch->pollfd.fd = gnutls_channel->fd;
356         watch->pollfd.events = condition;
357
358         g_source_add_poll(source, &watch->pollfd);
359
360         return source;
361 }
362
363 static GIOFuncs gnutls_channel_funcs = {
364         g_io_gnutls_read,
365         g_io_gnutls_write,
366         g_io_gnutls_seek,
367         g_io_gnutls_close,
368         g_io_gnutls_create_watch,
369         g_io_gnutls_free,
370         g_io_gnutls_set_flags,
371         g_io_gnutls_get_flags,
372 };
373
374 static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
375                                                 const void *buf, size_t count)
376 {
377         GIOGnuTLSChannel *gnutls_channel = transport_data;
378         ssize_t result;
379
380         DBG("count %zu", count);
381
382         result = write(gnutls_channel->fd, buf, count);
383
384         if (result < 0 && errno == EAGAIN)
385                 gnutls_channel->again = true;
386         else
387                 gnutls_channel->again = false;
388
389         DBG("result %zd", result);
390
391         return result;
392 }
393
394 static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
395                                                 void *buf, size_t count)
396 {
397         GIOGnuTLSChannel *gnutls_channel = transport_data;
398         ssize_t result;
399
400         DBG("count %zu", count);
401
402         result = read(gnutls_channel->fd, buf, count);
403
404         if (result < 0 && errno == EAGAIN)
405                 gnutls_channel->again = true;
406         else
407                 gnutls_channel->again = false;
408
409         DBG("result %zd", result);
410
411         return result;
412 }
413
414 bool g_io_channel_supports_tls(void)
415 {
416         return true;
417 }
418
419 GIOChannel *g_io_channel_gnutls_new(int fd)
420 {
421         GIOGnuTLSChannel *gnutls_channel;
422         GIOChannel *channel;
423         int err;
424
425         DBG("");
426
427         gnutls_channel = g_new(GIOGnuTLSChannel, 1);
428
429         channel = (GIOChannel *) gnutls_channel;
430
431         g_io_channel_init(channel);
432         channel->funcs = &gnutls_channel_funcs;
433
434         gnutls_channel->fd = fd;
435
436         channel->is_seekable = FALSE;
437         channel->is_readable = TRUE;
438         channel->is_writeable = TRUE;
439
440         channel->do_encode = FALSE;
441
442         g_io_gnutls_global_init();
443
444         err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
445         if (err < 0) {
446                 g_free(gnutls_channel);
447                 return NULL;
448         }
449
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);
457
458         gnutls_priority_set_direct(gnutls_channel->session,
459                                                 "NORMAL:%COMPAT", NULL);
460 #else
461         gnutls_priority_set_direct(gnutls_channel->session,
462                 "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL);
463 #endif
464
465         gnutls_certificate_set_verify_function(gnutls_channel->cred, &tpkp_gnutls_verify_callback);
466         /*
467         *       TODO: get ca-bundle path build-time configuration unless gnutls set it as a default
468         */
469         gnutls_certificate_set_x509_trust_file(gnutls_channel->cred, "/etc/ssl/ca-bundle.pem", GNUTLS_X509_FMT_PEM);
470
471         gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
472         gnutls_credentials_set(gnutls_channel->session,
473                                 GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
474
475         DBG("channel %p", channel);
476
477         return channel;
478 }