/***************************************

    This is part of frox: A simple transparent FTP proxy
    Copyright (C) 2000 James Hollingshead

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
      ssl.c -- implementation of rfc2228 AUTH/SSL/TLS security extensions.
               Needs openssl libraries.

	       So far this is only tested with vsftpd. May not work with
	       anything else.

  ***************************************/

#include <openssl/ssl.h>
#include <stdlib.h>

#include "common.h"
#include "control.h"
#include "sstr.h"
#include "ssl.h"

static SSL_CTX *ctx;
/* ------------------------------------------------------------- **
** init ssl context
** ------------------------------------------------------------- */
void ssl_init(void)
{
	sstr *msg;
	int i;

	if(!config.usessl)
		return;
	if(info->greeting == FAKED || info->greeting == AWAITED) {
		get_message(&i, &msg);
		if(i != 220) {
			die(INFO, "Unable to contact server in ntp", 421,
			    "Server Unable to accept connection", 0);
		}
		info->greetingmsg = sstr_dup(msg);
		info->greeting = info->greeting == FAKED ? DONE : SUPPRESSED;
	}

	send_ccommand("AUTH", config.datassl ? "TLS-P" : "TLS");
	get_message(&i, &msg);
	if(i != 234) {
		write_log(IMPORT, "SSL connection refused. "
			  "Using unencrypted connection");
		return;
	}

	SSL_load_error_strings();
	SSL_library_init();
	ctx = SSL_CTX_new(SSLv23_client_method());
	if(!ctx)
		die(ERROR, "Unable to initialise SSL", 0, 0, -1);

	info->ssl_sc = ssl_initfd(info->server_control.fd, SSL_CTRL);
	if(info->ssl_sc)
		write_log(IMPORT, "SSL initialised on control connection");
	else
		die(ERROR, "Unable to initialise SSL", 0, 0, -1);

	if(info->greeting == SUPPRESSED) {
		send_message(220, info->greetingmsg);
		info->greeting = DONE;
		sstr_free(info->greetingmsg);
	}
}

/*Initialise SSL on a file descriptor if required*/
void *ssl_initfd(int fd, int type)
{
	SSL *ssl;

	if(!config.usessl)
		return NULL;
	if(type == SSL_DATA) {
		if(config.datassl)
			write_log(VERBOSE,
				  "Initialising ssl on data connection.");
		else
			return NULL;
	}

	ssl = SSL_new(ctx);
	if(!SSL_set_fd(ssl, fd))
		die(ERROR, "Unable to init SSL", 0, 0, -1);
	if(type != SSL_DATA)
		SSL_connect(ssl);
	return (void *) ssl;
}

void ssl_shutdown(void **ssl)
{
	SSL *s = (SSL *) * ssl;
	if(!s)
		return;

	SSL_shutdown(s);
	SSL_free(s);
	*ssl = NULL;
}

int ssl_append_read(void *ssl, sstr * buf, int len)
{
	int i;
	char *tbuf;
	SSL *s = (SSL *) ssl;

	if(!len)
		len = 4096;
	tbuf = malloc(len);
	if(!tbuf)
		die(ERROR, "Malloc failure", 0, 0, -1);
	i = SSL_read(s, tbuf, len);
	if(i <= 0) {
		free(tbuf);
		if(i == 0)
			return 0;
		write_log(ERROR, "SSL Error %d\n", SSL_get_error(s, i));
		return 0;	/*SSL often seems to give an error on closing */
	}

	sstr_ncat2(buf, tbuf, i);
	free(tbuf);
	return i;
}

int ssl_write(void *ssl, sstr * buf)
{
	int i;
	SSL *s = (SSL *) ssl;
	i = SSL_write(s, sstr_buf(buf), sstr_len(buf));
	return i;
}

/* We need to intercept 150 messages so that we can initialise ssl on the
 * data connection if required. We can't do it when the initial tcp data
 * connection is made as this frequently happens before the 150 reply, the
 * ftp server isn't ready to negotiate the ssl, and SSL_connect() blocks.
 *
 * We can't just call SSL_set_connect_state() because if we are downloading
 * then frox select()s for a read on the data line, but it is the ftp client's
 * responsibility to play the part of the SSL client and initialise the
 * connection.
 *
 * We always return 0 as we never actually deal with the reply ourselves, but
 * just need notification. We should be called first in the && list in
 * control.c to ensure we always get it.*/
int ssl_parsed_reply(int code, sstr * msg)
{
	if(code != 150)
		return 0;
	if(!config.usessl || !config.datassl)
		return 0;
	if(SSL_connect(info->ssl_sd) == -1)
		die(ERROR, "Unable to initialise ssl connection", 0, 0, -1);
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1