ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-ssl.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* gnome-vfs-ssl.h
4  *
5  * Copyright (C) 2001 Ian McKellar
6  * Copyright (C) 2002 Andrew McDonald
7  *
8  * The Gnome Library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * The Gnome Library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
20  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA. 
22  */
23 /*
24  * Authors: Ian McKellar <yakk@yakk.net>
25  *   My knowledge of SSL programming is due to reading Jeffrey Stedfast's
26  *   excellent SSL implementation in Evolution.
27  *
28  *          Andrew McDonald <andrew@mcdonald.org.uk>
29  *   Basic SSL/TLS support using the LGPL'ed GNUTLS Library
30  */
31
32 #include <config.h>
33 #include "gnome-vfs-ssl.h"
34
35 #include "gnome-vfs-ssl-private.h"
36 #include "gnome-vfs-private-utils.h"
37 #include <glib/gmem.h>
38 #include <string.h>
39
40 #ifdef HAVE_OPENSSL
41 #include <openssl/ssl.h>
42 #include <openssl/x509.h>
43 #include <openssl/err.h>
44 #elif defined HAVE_GNUTLS
45 #include <gnutls/gnutls.h>
46 #endif
47 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
48 #include <sys/time.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <netinet/in.h>
55 #include <netdb.h>
56 #include <sys/socket.h>
57 #endif
58
59 typedef struct {
60 #ifdef HAVE_OPENSSL
61         int sockfd;
62         SSL *ssl;
63 #elif defined HAVE_GNUTLS
64         int sockfd;
65         GNUTLS_STATE tlsstate;
66         GNUTLS_CERTIFICATE_CLIENT_CREDENTIALS xcred;
67 #elif defined HAVE_NSS
68         PRFileDesc *sockfd;
69 #else
70         char    dummy;
71 #endif
72 } GnomeVFSSSLPrivate;
73
74 struct GnomeVFSSSL {
75         GnomeVFSSSLPrivate *private;
76 };
77
78 void 
79 _gnome_vfs_ssl_init () {
80 #ifdef HAVE_OPENSSL
81         SSL_library_init ();
82 #elif defined HAVE_GNUTLS
83         gnutls_global_init();
84 #endif
85 }
86
87 /**
88  * gnome_vfs_ssl_enabled:
89  *
90  * Checks whether GnomeVFS was compiled with SSL support.
91  *
92  * Return value: %TRUE if GnomeVFS was compiled with SSL support,
93  * otherwise %FALSE.
94  **/
95 gboolean
96 gnome_vfs_ssl_enabled ()
97 {
98 #if defined HAVE_OPENSSL || defined HAVE_GNUTLS
99         return TRUE;
100 #else
101         return FALSE;
102 #endif
103 }
104
105 /**
106  * gnome_vfs_ssl_create:
107  * @handle_return: pointer to a GnmoeVFSSSL struct, which will
108  * contain an allocated GnomeVFSSSL object on return.
109  * @host: string indicating the host to establish an SSL connection with
110  * @port: the port number to connect to
111  *
112  * Creates an SSL socket connection at @handle_return to @host using
113  * port @port.
114  *
115  * Return value: GnomeVFSResult indicating the success of the operation
116  **/
117 GnomeVFSResult
118 gnome_vfs_ssl_create (GnomeVFSSSL **handle_return, 
119                       const char *host, 
120                       unsigned int port)
121 {
122 /* FIXME: add *some* kind of cert verification! */
123 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
124         int fd;
125         int ret;
126 #ifdef ENABLE_IPV6      
127         struct addrinfo hints, *result, *res;
128 #endif
129         struct hostent *h;
130         struct sockaddr_in sin;
131
132 #ifdef ENABLE_IPV6
133         if (_gnome_vfs_have_ipv6 ()) {
134                 fd = 0;
135                 ret = 0;
136                 result = NULL;
137
138                 memset (&hints, 0, sizeof (hints));
139                 hints.ai_socktype = SOCK_STREAM;
140
141                 if (getaddrinfo (host, NULL, &hints, &result) != 0) {
142                         /* host lookup failed */
143                         return GNOME_VFS_ERROR_HOST_NOT_FOUND;
144                 }
145
146                 for (res = result; res; res = res->ai_next) {
147
148                         if (res->ai_family != AF_INET && res->ai_family != AF_INET6) {
149                                 continue;
150                         }
151
152                         fd = socket (res->ai_family, SOCK_STREAM, 0);
153                         if (fd < 0) {
154                                 continue;
155                         }
156
157                         if (res->ai_family == AF_INET) {
158                                 ((struct sockaddr_in *)res->ai_addr)->sin_port = htons (port);
159                         }
160
161                         if (res->ai_family == AF_INET6) {
162                                 ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons (port);
163                         }
164
165                         ret = connect (fd, res->ai_addr, res->ai_addrlen);
166                         if (ret != -1) {
167                                 break;
168                         }
169                         
170                         close (fd);
171                 }
172
173                 freeaddrinfo (result);
174                 if (!res) {
175                         if (fd < 0 || ret < 0) {
176                                 /*Error in socket creation.*/
177                                 return gnome_vfs_result_from_errno ();
178                         } else {
179                                 /*Error: No IPv4 or IPv6 address.*/
180                                 return GNOME_VFS_ERROR_HOST_NOT_FOUND;
181                         }
182                 }
183         }
184         else
185 #endif
186         {
187                 sin.sin_port = htons (port);
188                 h = gethostbyname (host);
189
190                 if (h == NULL) {
191                         /* host lookup failed */
192                         return gnome_vfs_result_from_h_errno ();
193                 }
194
195                 sin.sin_family = h->h_addrtype;
196                 memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr));
197
198                 fd = socket (h->h_addrtype, SOCK_STREAM, 0);
199                 if (fd < 0) {
200                         return gnome_vfs_result_from_errno ();
201                 }
202
203                 ret = connect (fd, (struct sockaddr *)&sin, sizeof (sin));
204         }
205
206         if (ret == -1) {
207                 /* connect failed */
208                 return gnome_vfs_result_from_errno ();
209         }
210
211         return gnome_vfs_ssl_create_from_fd (handle_return, fd);
212 #else
213         return GNOME_VFS_ERROR_NOT_SUPPORTED;
214 #endif
215 }
216
217 #ifdef HAVE_GNUTLS
218 static const int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};
219 static const int cipher_priority[] = 
220         {GNUTLS_CIPHER_RIJNDAEL_128_CBC, GNUTLS_CIPHER_3DES_CBC,
221          GNUTLS_CIPHER_RIJNDAEL_256_CBC, GNUTLS_CIPHER_TWOFISH_128_CBC,
222          GNUTLS_CIPHER_ARCFOUR, 0};
223 static const int comp_priority[] =
224         {GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0};
225 static const int kx_priority[] =
226         {GNUTLS_KX_DHE_RSA, GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, 0};
227 static const int mac_priority[] =
228         {GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0};
229 #endif
230
231 /**
232  * gnome_vfs_ssl_create_from_fd:
233  * @handle_return: pointer to a GnmoeVFSSSL struct, which will
234  * contain an allocated GnomeVFSSSL object on return.
235  * @fd: file descriptior to try and establish an SSL connection over
236  *
237  * Try to establish an SSL connection over the file descriptor @fd.
238  *
239  * Return value: a GnomeVFSResult indicating the success of the operation
240  **/
241 GnomeVFSResult
242 gnome_vfs_ssl_create_from_fd (GnomeVFSSSL **handle_return, 
243                               gint fd)
244 {
245 #ifdef HAVE_OPENSSL
246         GnomeVFSSSL *ssl;
247         SSL_CTX *ssl_ctx = NULL;
248         int ret;
249
250         ssl = g_new0 (GnomeVFSSSL, 1);
251         ssl->private = g_new0 (GnomeVFSSSLPrivate, 1);
252         ssl->private->sockfd = fd;
253
254         /* SSLv23_client_method will negotiate with SSL v2, v3, or TLS v1 */
255         ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
256
257         if (ssl_ctx == NULL) {
258                 return GNOME_VFS_ERROR_INTERNAL;
259         }
260
261         /* FIXME: SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_PEER, &ssl_verify);*/
262         ssl->private->ssl = SSL_new (ssl_ctx);
263
264         if (ssl->private->ssl == NULL) {
265                 return GNOME_VFS_ERROR_IO;
266         }
267
268         SSL_set_fd (ssl->private->ssl, fd);
269
270         ret = SSL_connect (ssl->private->ssl);
271         if (ret != 1) {
272                 SSL_shutdown (ssl->private->ssl);
273
274                 if (ssl->private->ssl->ctx)
275                         SSL_CTX_free (ssl->private->ssl->ctx);
276
277                 SSL_free (ssl->private->ssl);
278                 g_free (ssl->private);
279                 g_free (ssl);
280                 return GNOME_VFS_ERROR_IO;
281         }
282
283         *handle_return = ssl;
284
285         return GNOME_VFS_OK;
286
287 #elif defined HAVE_GNUTLS
288         GnomeVFSSSL *ssl;
289         int err;
290
291         ssl = g_new0 (GnomeVFSSSL, 1);
292         ssl->private = g_new0 (GnomeVFSSSLPrivate, 1);
293         ssl->private->sockfd = fd;
294
295         err = gnutls_certificate_allocate_sc (&ssl->private->xcred);
296         if (err < 0) {
297                 g_free (ssl->private);
298                 g_free (ssl);
299                 return GNOME_VFS_ERROR_INTERNAL;
300         }
301
302         gnutls_init (&ssl->private->tlsstate, GNUTLS_CLIENT);
303
304         /* set socket */
305         gnutls_transport_set_ptr (ssl->private->tlsstate, fd);
306
307         gnutls_protocol_set_priority (ssl->private->tlsstate, protocol_priority);
308         gnutls_cipher_set_priority (ssl->private->tlsstate, cipher_priority);
309         gnutls_compression_set_priority (ssl->private->tlsstate, comp_priority);
310         gnutls_kx_set_priority (ssl->private->tlsstate, kx_priority);
311         gnutls_mac_set_priority (ssl->private->tlsstate, mac_priority);
312
313         gnutls_cred_set (ssl->private->tlsstate, GNUTLS_CRD_CERTIFICATE,
314                          ssl->private->xcred);
315
316         err = gnutls_handshake (ssl->private->tlsstate);
317
318         while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
319                 err = gnutls_handshake (ssl->private->tlsstate);
320         }
321
322         if (err < 0) {
323                 gnutls_certificate_free_sc (ssl->private->xcred);
324                 gnutls_deinit (ssl->private->tlsstate);
325                 g_free (ssl->private);
326                 g_free (ssl);
327                 return GNOME_VFS_ERROR_IO;
328         }
329
330         *handle_return = ssl;
331
332         return GNOME_VFS_OK;
333 #else
334         return GNOME_VFS_ERROR_NOT_SUPPORTED;
335 #endif
336 }
337
338 /**
339  * gnome_vfs_ssl_read:
340  * @ssl: SSL socket to read data from
341  * @buffer: allocated buffer of at least @bytes bytes to be read into
342  * @bytes: number of bytes to read from @ssl into @buffer
343  * @bytes_read: pointer to a GnomeVFSFileSize, will contain
344  * the number of bytes actually read from the socket on return.
345  *
346  * Read @bytes bytes of data from the SSL socket @ssl into @buffer.
347  *
348  * Return value: GnomeVFSResult indicating the success of the operation
349  **/
350 GnomeVFSResult 
351 gnome_vfs_ssl_read (GnomeVFSSSL *ssl,
352                     gpointer buffer,
353                     GnomeVFSFileSize bytes,
354                     GnomeVFSFileSize *bytes_read)
355 {
356 #if HAVE_OPENSSL
357         if (bytes == 0) {
358                 *bytes_read = 0;
359                 return GNOME_VFS_OK;
360         }
361
362         *bytes_read = SSL_read (ssl->private->ssl, buffer, bytes);
363
364         if (*bytes_read <= 0) {
365                 *bytes_read = 0;
366                 return GNOME_VFS_ERROR_GENERIC;
367         }
368         return GNOME_VFS_OK;
369 #elif defined HAVE_GNUTLS
370         if (bytes == 0) {
371                 *bytes_read = 0;
372                 return GNOME_VFS_OK;
373         }
374
375         *bytes_read = gnutls_record_recv (ssl->private->tlsstate, buffer, bytes);
376
377         if (*bytes_read <= 0) {
378                 *bytes_read = 0;
379                 return GNOME_VFS_ERROR_GENERIC;
380         }
381         return GNOME_VFS_OK;
382 #else
383         return GNOME_VFS_ERROR_NOT_SUPPORTED;
384 #endif
385 }
386
387 /**
388  * gnome_vfs_ssl_write:
389  * @ssl: SSL socket to write data to
390  * @buffer: data to write to the socket
391  * @bytes: number of bytes from @buffer to write to @ssl
392  * @bytes_written: pointer to a GnomeVFSFileSize, will contain
393  * the number of bytes actually written to the socket on return.
394  *
395  * Write @bytes bytes of data from @buffer to @ssl.
396  *
397  * Return value: GnomeVFSResult indicating the success of the operation
398  **/
399 GnomeVFSResult 
400 gnome_vfs_ssl_write (GnomeVFSSSL *ssl,
401                      gconstpointer buffer,
402                      GnomeVFSFileSize bytes,
403                      GnomeVFSFileSize *bytes_written)
404 {
405 #if HAVE_OPENSSL
406         if (bytes == 0) {
407                 *bytes_written = 0;
408                 return GNOME_VFS_OK;
409         }
410
411         *bytes_written = SSL_write (ssl->private->ssl, buffer, bytes);
412
413         if (*bytes_written <= 0) {
414                 *bytes_written = 0;
415                 return GNOME_VFS_ERROR_GENERIC;
416         }
417         return GNOME_VFS_OK;
418 #elif defined HAVE_GNUTLS
419         if (bytes == 0) {
420                 *bytes_written = 0;
421                 return GNOME_VFS_OK;
422         }
423
424         *bytes_written = gnutls_record_send (ssl->private->tlsstate, buffer, bytes);
425
426         if (*bytes_written <= 0) {
427                 *bytes_written = 0;
428                 return GNOME_VFS_ERROR_GENERIC;
429         }
430         return GNOME_VFS_OK;
431 #else
432         return GNOME_VFS_ERROR_NOT_SUPPORTED;
433 #endif
434 }
435
436 /**
437  * gnome_vfs_ssl_destroy:
438  * @ssl: SSL socket to be closed and destroyed
439  *
440  * Free resources used by @ssl and close the connection.
441  */
442 void
443 gnome_vfs_ssl_destroy (GnomeVFSSSL *ssl) 
444 {
445 #if HAVE_OPENSSL
446         SSL_shutdown (ssl->private->ssl);
447         SSL_CTX_free (ssl->private->ssl->ctx);
448         SSL_free (ssl->private->ssl);
449         close (ssl->private->sockfd);
450 #elif defined HAVE_GNUTLS
451         gnutls_bye (ssl->private->tlsstate, GNUTLS_SHUT_RDWR);
452         gnutls_certificate_free_sc (ssl->private->xcred);
453         gnutls_deinit (ssl->private->tlsstate);
454         close (ssl->private->sockfd);
455 #else
456 #endif
457         g_free (ssl->private);
458         g_free (ssl);
459 }
460
461 static GnomeVFSSocketImpl ssl_socket_impl = {
462         (GnomeVFSSocketReadFunc)gnome_vfs_ssl_read,
463         (GnomeVFSSocketWriteFunc)gnome_vfs_ssl_write,
464         (GnomeVFSSocketCloseFunc)gnome_vfs_ssl_destroy
465 };
466
467 /**
468  * gnome_vfs_ssl_to_socket:
469  * @ssl: SSL socket to convert into a standard socket
470  *
471  * Wrapper an SSL socket inside a standard GnomeVFSSocket.
472  *
473  * Return value: a newly allocated GnomeVFSSocket corresponding to @ssl.
474  **/
475 GnomeVFSSocket *
476 gnome_vfs_ssl_to_socket (GnomeVFSSSL *ssl)
477 {
478         return gnome_vfs_socket_new (&ssl_socket_impl, ssl);
479 }