1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
5 * Copyright (C) 2001 Ian McKellar
6 * Copyright (C) 2002 Andrew McDonald
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.
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.
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.
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.
28 * Andrew McDonald <andrew@mcdonald.org.uk>
29 * Basic SSL/TLS support using the LGPL'ed GNUTLS Library
33 #include "gnome-vfs-ssl.h"
35 #include "gnome-vfs-ssl-private.h"
36 #include "gnome-vfs-private-utils.h"
37 #include <glib/gmem.h>
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>
47 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
49 #include <sys/types.h>
54 #include <netinet/in.h>
56 #include <sys/socket.h>
63 #elif defined HAVE_GNUTLS
65 GNUTLS_STATE tlsstate;
66 GNUTLS_CERTIFICATE_CLIENT_CREDENTIALS xcred;
67 #elif defined HAVE_NSS
75 GnomeVFSSSLPrivate *private;
79 _gnome_vfs_ssl_init () {
82 #elif defined HAVE_GNUTLS
88 * gnome_vfs_ssl_enabled:
90 * Checks whether GnomeVFS was compiled with SSL support.
92 * Return value: %TRUE if GnomeVFS was compiled with SSL support,
96 gnome_vfs_ssl_enabled ()
98 #if defined HAVE_OPENSSL || defined HAVE_GNUTLS
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
112 * Creates an SSL socket connection at @handle_return to @host using
115 * Return value: GnomeVFSResult indicating the success of the operation
118 gnome_vfs_ssl_create (GnomeVFSSSL **handle_return,
122 /* FIXME: add *some* kind of cert verification! */
123 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
127 struct addrinfo hints, *result, *res;
130 struct sockaddr_in sin;
133 if (_gnome_vfs_have_ipv6 ()) {
138 memset (&hints, 0, sizeof (hints));
139 hints.ai_socktype = SOCK_STREAM;
141 if (getaddrinfo (host, NULL, &hints, &result) != 0) {
142 /* host lookup failed */
143 return GNOME_VFS_ERROR_HOST_NOT_FOUND;
146 for (res = result; res; res = res->ai_next) {
148 if (res->ai_family != AF_INET && res->ai_family != AF_INET6) {
152 fd = socket (res->ai_family, SOCK_STREAM, 0);
157 if (res->ai_family == AF_INET) {
158 ((struct sockaddr_in *)res->ai_addr)->sin_port = htons (port);
161 if (res->ai_family == AF_INET6) {
162 ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons (port);
165 ret = connect (fd, res->ai_addr, res->ai_addrlen);
173 freeaddrinfo (result);
175 if (fd < 0 || ret < 0) {
176 /*Error in socket creation.*/
177 return gnome_vfs_result_from_errno ();
179 /*Error: No IPv4 or IPv6 address.*/
180 return GNOME_VFS_ERROR_HOST_NOT_FOUND;
187 sin.sin_port = htons (port);
188 h = gethostbyname (host);
191 /* host lookup failed */
192 return gnome_vfs_result_from_h_errno ();
195 sin.sin_family = h->h_addrtype;
196 memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr));
198 fd = socket (h->h_addrtype, SOCK_STREAM, 0);
200 return gnome_vfs_result_from_errno ();
203 ret = connect (fd, (struct sockaddr *)&sin, sizeof (sin));
208 return gnome_vfs_result_from_errno ();
211 return gnome_vfs_ssl_create_from_fd (handle_return, fd);
213 return GNOME_VFS_ERROR_NOT_SUPPORTED;
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};
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
237 * Try to establish an SSL connection over the file descriptor @fd.
239 * Return value: a GnomeVFSResult indicating the success of the operation
242 gnome_vfs_ssl_create_from_fd (GnomeVFSSSL **handle_return,
247 SSL_CTX *ssl_ctx = NULL;
250 ssl = g_new0 (GnomeVFSSSL, 1);
251 ssl->private = g_new0 (GnomeVFSSSLPrivate, 1);
252 ssl->private->sockfd = fd;
254 /* SSLv23_client_method will negotiate with SSL v2, v3, or TLS v1 */
255 ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
257 if (ssl_ctx == NULL) {
258 return GNOME_VFS_ERROR_INTERNAL;
261 /* FIXME: SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_PEER, &ssl_verify);*/
262 ssl->private->ssl = SSL_new (ssl_ctx);
264 if (ssl->private->ssl == NULL) {
265 return GNOME_VFS_ERROR_IO;
268 SSL_set_fd (ssl->private->ssl, fd);
270 ret = SSL_connect (ssl->private->ssl);
272 SSL_shutdown (ssl->private->ssl);
274 if (ssl->private->ssl->ctx)
275 SSL_CTX_free (ssl->private->ssl->ctx);
277 SSL_free (ssl->private->ssl);
278 g_free (ssl->private);
280 return GNOME_VFS_ERROR_IO;
283 *handle_return = ssl;
287 #elif defined HAVE_GNUTLS
291 ssl = g_new0 (GnomeVFSSSL, 1);
292 ssl->private = g_new0 (GnomeVFSSSLPrivate, 1);
293 ssl->private->sockfd = fd;
295 err = gnutls_certificate_allocate_sc (&ssl->private->xcred);
297 g_free (ssl->private);
299 return GNOME_VFS_ERROR_INTERNAL;
302 gnutls_init (&ssl->private->tlsstate, GNUTLS_CLIENT);
305 gnutls_transport_set_ptr (ssl->private->tlsstate, fd);
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);
313 gnutls_cred_set (ssl->private->tlsstate, GNUTLS_CRD_CERTIFICATE,
314 ssl->private->xcred);
316 err = gnutls_handshake (ssl->private->tlsstate);
318 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
319 err = gnutls_handshake (ssl->private->tlsstate);
323 gnutls_certificate_free_sc (ssl->private->xcred);
324 gnutls_deinit (ssl->private->tlsstate);
325 g_free (ssl->private);
327 return GNOME_VFS_ERROR_IO;
330 *handle_return = ssl;
334 return GNOME_VFS_ERROR_NOT_SUPPORTED;
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.
346 * Read @bytes bytes of data from the SSL socket @ssl into @buffer.
348 * Return value: GnomeVFSResult indicating the success of the operation
351 gnome_vfs_ssl_read (GnomeVFSSSL *ssl,
353 GnomeVFSFileSize bytes,
354 GnomeVFSFileSize *bytes_read)
362 *bytes_read = SSL_read (ssl->private->ssl, buffer, bytes);
364 if (*bytes_read <= 0) {
366 return GNOME_VFS_ERROR_GENERIC;
369 #elif defined HAVE_GNUTLS
375 *bytes_read = gnutls_record_recv (ssl->private->tlsstate, buffer, bytes);
377 if (*bytes_read <= 0) {
379 return GNOME_VFS_ERROR_GENERIC;
383 return GNOME_VFS_ERROR_NOT_SUPPORTED;
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.
395 * Write @bytes bytes of data from @buffer to @ssl.
397 * Return value: GnomeVFSResult indicating the success of the operation
400 gnome_vfs_ssl_write (GnomeVFSSSL *ssl,
401 gconstpointer buffer,
402 GnomeVFSFileSize bytes,
403 GnomeVFSFileSize *bytes_written)
411 *bytes_written = SSL_write (ssl->private->ssl, buffer, bytes);
413 if (*bytes_written <= 0) {
415 return GNOME_VFS_ERROR_GENERIC;
418 #elif defined HAVE_GNUTLS
424 *bytes_written = gnutls_record_send (ssl->private->tlsstate, buffer, bytes);
426 if (*bytes_written <= 0) {
428 return GNOME_VFS_ERROR_GENERIC;
432 return GNOME_VFS_ERROR_NOT_SUPPORTED;
437 * gnome_vfs_ssl_destroy:
438 * @ssl: SSL socket to be closed and destroyed
440 * Free resources used by @ssl and close the connection.
443 gnome_vfs_ssl_destroy (GnomeVFSSSL *ssl)
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);
457 g_free (ssl->private);
461 static GnomeVFSSocketImpl ssl_socket_impl = {
462 (GnomeVFSSocketReadFunc)gnome_vfs_ssl_read,
463 (GnomeVFSSocketWriteFunc)gnome_vfs_ssl_write,
464 (GnomeVFSSocketCloseFunc)gnome_vfs_ssl_destroy
468 * gnome_vfs_ssl_to_socket:
469 * @ssl: SSL socket to convert into a standard socket
471 * Wrapper an SSL socket inside a standard GnomeVFSSocket.
473 * Return value: a newly allocated GnomeVFSSocket corresponding to @ssl.
476 gnome_vfs_ssl_to_socket (GnomeVFSSSL *ssl)
478 return gnome_vfs_socket_new (&ssl_socket_impl, ssl);