/*
* bbftpd/bbftpd.c
* Copyright (C) 1999, 2000, 2001, 2002 IN2P3, CNRS
* bbftp@in2p3.fr
* http://doc.in2p3.fr/bbftp
*
* 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 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.
*/
/****************************************************************************
bbftpd.c v 0.0.0 1999/11/24
v 1.3.0 2000/03/16 - Version in openlog
v 1.4.0 2000/03/22 - Modify the connection protocole
in order to send the crypt type
after the connection.
v 1.5.0 2000/03/24 - Change the wait timer in default
v 1.6.0 2000/03/24 - Change the main loop to make it more
modular
v 1.6.1 2000/03/24 - Portage on OSF1
- Make the control socket non blocking
v 1.8.0 2000/04/14 - Introduce RSA Cryptage
v 1.8.6 2000/05/21 - Allow to run with inetd
v 1.8.7 2000/05/24 - Introduce version.h and config.h
v 1.8.10 2000/08/11 - Portage to Linux
v 1.9.0 2000/08/18 - Use configure to help portage
- default time out set to 900 s
v 1.9.3 2000/10/12 - Add -b and -w option in order to overwrite
the fixed values
v 1.9.4 2000/10/16 - Make all sockets blocking in order
to prevent the error in case of lack
of memory
- Supress %m
v 2.0.0 2001/03/26 - Protocol V2 implementation
v 2.0.1 2001/04/23 - Correct indentation
- Port to IRIX
v 2.0.2 2001/05/07 - Add debug option for RFIO
v 2.1.0 2001/05/28 - Add private authentication
- Correct syslog level
- Add -l option
v 2.2.0 2001/10/03 - Add the certificate authentication process
*****************************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utime.h>
#include <bbftpd.h>
#include <common.h>
#include <config.h>
#include <daemon.h>
#include <daemon_proto.h>
#include <status.h>
#include <structures.h>
#include <version.h>
#ifdef CERTIFICATE_AUTH
#include <gssapi.h>
#include <gfw.h>
#endif
#ifdef HAVE_BYTESWAP_H
#include <byteswap.h>
#endif
#include <openssl/rsa.h>
#ifdef CERTIFICATE_AUTH
#define OPTIONS "bcd:fl:m:pR:suvw:"
#else
#define OPTIONS "bd:e:fl:m:R:suvw:"
#endif
/*
** Common variables for BBFTP protocole version 1 and 2
*/
/*
** be_daemon :
** That is the way the daemon is going to be run.
** 0 means run by inetd (that can be done trought
** a wrapper) (run as bbftpd)
** 1 means that the control socket are stdin and
** stdout (run as bbftpd -s)
** 2 means running in background (run as bbftpd -b)
** Default 0
*/
int be_daemon = 0 ;
/*
** daemonchar:
** String used to initialize openlog
*/
char daemonchar[50] ;
/*
** fixeddataport :
** If set to 1 then the server will use CONTROLPORT-1
** to use as local port number for the data socket.
** It is useful to set it to 1 if the server is
** running behing a firewall. (run as bbftpd)
** If set to 0 then the server will not fixe the
** local data port (run as bbftpd -f)
** Default 1
*/
int fixeddataport = 1 ;
/*
** state :
** Define the state of the server. values differ depending
** of the BBFTP protocol version
*/
int state ;
/*
** newcontrolport :
** Define the control port to listen on
*/
int newcontrolport ;
/*
** currentusername :
** Define the local username
*/
char currentusername[MAXLEN] ;
/*
** myrsa :
** Define the local location where is store the key pair
*/
RSA *myrsa ;
/*
** his_addr :
** Remote address (the client)
*/
struct sockaddr_in his_addr;
/*
** ctrl_addr :
** Local adresse (the server)
*/
struct sockaddr_in ctrl_addr;
/*
** bbftprc:
** Where to store the bbftprc file
*/
char *bbftprc = NULL ;
/*
** protocolversion :
** Set to 0 before any PROT message exchange, that also means
** that no children have been started
*/
int protocolversion ;
int protocolmin = 1 ;
int protocolmax = 3 ;
/*
** fatherpid :
** The pid of the process keepng the control connection
*/
pid_t fatherpid ;
/*
** flagsighup:
** Set to one when a SIGHUP is received
**/
int flagsighup = 0 ;
/*
** killdone:
** Set to one when the father has send a kill to all chidren
*/
int killdone= 0 ;
/*
** childendinerror:
** Set to one when one child has been detected in error in order
** to prevent father to send multiple answer
*/
int childendinerror = 0 ;
/*
** unlinkfile:
** Set to one when father has to unlink the file to store. Used
** in case of child ended incorrectly (kill -9 for example)
*/
int unlinkfile = 0 ;
/*
** debug:
** Set to one for RFIO debug. Valid only with the -b option
** set to the value needed for RFIO_TRACE
*/
int debug = 0 ;
/*
** castfd:
** CASTOR file descriptor use with RFIO and CASTOR
*/
int castfd ;
/*
** castfilename:
** CASTOR/SHIFT real file name
*/
char *castfilename = NULL ;
/*
** End Common variables for BBFTP protocole version 1 and 2 ********************
*/
/*
** Variables for BBFTP protocole version 1
*/
/*
** msgsock :
** Define control socket for the server
*/
int msgsock ;
/*
** currentfilename :
** Define the file we are working on
*/
char currentfilename[MAXLENFILE];
/*
** pid_child :
** Array to store the children pid
*/
pid_t pid_child[MAXPORT] ;
/*
** End Variables for BBFTP protocole version 1 *********************************
*/
/*
** Variables for BBFTP protocole version 2
*/
/*
** incontrolsock :
** Define the control socket for reading
** outcontrolsock :
** Define the control socket for writing
**
** For be_daemon equal to 0 or 2 incontrolsock = outcontrolsock
*/
int incontrolsock ;
int outcontrolsock ;
/*
** curfilename :
** Define the pointer to the current file
*/
char *curfilename = NULL ;
/*
** curfilenamelen :
** Define the length of the memory used by curfilename
*/
int curfilenamelen ;
/*
** realfilename :
** Define the pointer to the real file (= curfilename if TROPT_TMP not
** set)
*/
char *realfilename = NULL ;
/*
** mychildren :
** Pointer to the first pid of children
*/
int *mychildren = NULL ;
/*
** nbpidchid :
** Number of pid pointed by mychildren
*/
int nbpidchild ;
/*
** myports :
** Pointer to the first port
*/
int *myports = NULL ;
/*
** mysockets :
** Pointer to the first socket
*/
int *mysockets = NULL ;
/*
** readbuffer :
** Pointer to the readbuffer
*/
int *readbuffer = NULL ;
/*
** compbuffer :
** Pointer to the compression buffer
*/
int *compbuffer = NULL ;
/*
** myumask :
** Umask for the bbftpd process, the default will be 022 which
** means that group and other write permissions are denied
** This can be changed by the bbftp umask command
**/
int myumask = 022 ;
/*
** Parameters describing the connection
*/
int ackto = ACKTO;
int recvcontrolto = CONTROLSOCKTO;
int sendcontrolto = SENDCONTROLTO;
int datato = DATASOCKTO;
int checkstdinto = CHECKSTDINTO;
int force_encoding = 1;
#ifdef CERTIFICATE_AUTH
int accept_certs_only = 0;
int accept_pass_only = 0;
#endif
/*
** Parameters describing the transfer
*/
int transferoption ;
int filemode ;
char lastaccess[9] ;
char lastmodif[9] ;
int sendwinsize ;
int recvwinsize ;
int buffersizeperstream ;
int requestedstreamnumber ;
my64_t filesize ;
int mycos = 0 ;
/*
** maxstreams :
** That is to fix the maximum number of stream the deamon will accept.
** (run as bbftpd -m 25)
** Default 25
*/
int maxstreams = 25 ;
/*
** End Variables for BBFTP protocole version 2 *********************************
*/
/*
* Variables for protocol version 3 (PASV mode) ******************************
*/
/*
* Range for the ephemeral ports for data connections
*/
int pasvport_min ;
int pasvport_max ;
/*
** End Variables for BBFTP protocole version 3 *********************************
*/
/*
** credentials for certificate authentication
*/
#ifdef CERTIFICATE_AUTH
gss_cred_id_t server_creds;
#endif
/*
* Stats
*/
struct timeval tstart;
main (argc,argv,envp)
int argc ;
char **argv ;
char **envp ;
{
extern char *optarg;
extern int optind, opterr, optopt;
#if defined(SUNOS) || defined(_HPUX_SOURCE) || defined(IRIX)
int addrlen ;
#else
size_t addrlen ;
#endif
struct timeval wait_timer;
fd_set selectmask ;
int nfds ;
int retcode ;
int i, j, k ;
struct message *msg ;
char buffer[MINMESSLEN] ;
char logmessage[1024] ;
char rfio_trace[20] ;
struct passwd *mypasswd ;
char *bbftprcfile = NULL ;
int fd ;
char *carret ;
char *startcmd ;
struct stat statbuf ;
int alluse ;
sprintf(daemonchar,"bbftpd v%s",VERSION) ;
openlog(daemonchar, LOG_PID | LOG_NDELAY, BBFTPD_FACILITY);
/*
** Set the log mask to BBFTPD_EMERG (0)
*/
setlogmask(LOG_UPTO(BBFTPD_EMERG));
/*
** Initialize variables
*/
protocolversion = 0 ;
#ifdef PORT_RANGE
sscanf(PORT_RANGE,"%d:%d",&pasvport_min, &pasvport_max) ;
#endif
newcontrolport = CONTROLPORT ;
opterr = 0 ;
while ((j = getopt(argc, argv, OPTIONS)) != -1) {
switch (j) {
case 'v' :{
printf("bbftpd version %s\n",VERSION) ;
printf("Compiled with : default port %d\n",CONTROLPORT) ;
printf(" default maximum streams = %d \n",maxstreams) ;
#ifdef PORT_RANGE
printf(" data ports range = %s \n", PORT_RANGE) ;
#endif
#ifdef WITH_GZIP
printf(" compression with Zlib-%s\n", zlibVersion()) ;
#endif
printf(" encryption with %s \n",SSLeay_version(SSLEAY_VERSION)) ;
#ifdef WITH_RFIO
printf(" RFIO interface \n") ;
#endif
#ifdef WITH_RFIO64
printf(" RFIO64 interface \n") ;
#endif
#ifdef CASTOR
printf(" CASTOR support \n") ;
#endif
#ifdef CERTIFICATE_AUTH
printf(" certificate authentication \n") ;
#endif
#ifdef AFS
printf(" AFS authentication \n") ;
#endif
#ifdef PRIVATE_AUTH
printf(" private authentication \n") ;
#endif
exit(0) ;
}
}
}
/*
** Look at the loglevel option
*/
i = 0 ;
opterr = 0 ;
optind = 1 ;
while ((j = getopt(argc, argv, OPTIONS)) != -1) {
switch (j) {
case 'l' :{
for ( i=0 ; i< strlen(optarg) ; i++ ) {
optarg[i] = toupper(optarg[i]) ;
}
i = 0 ;
if ( !strcmp(optarg,"EMERGENCY") ) {
i = BBFTPD_EMERG;
} else if ( !strcmp(optarg,"ALERT") ) {
i = BBFTPD_ALERT;
} else if ( !strcmp(optarg,"CRITICAL") ) {
i = BBFTPD_CRIT;
} else if ( !strcmp(optarg,"ERROR") ) {
i = BBFTPD_ERR;
} else if ( !strcmp(optarg,"WARNING") ) {
i = BBFTPD_WARNING;
} else if ( !strcmp(optarg,"NOTICE") ) {
i = BBFTPD_NOTICE;
} else if ( !strcmp(optarg,"INFORMATION") ) {
i = BBFTPD_INFO;
} else if ( !strcmp(optarg,"DEBUG") ) {
i = BBFTPD_DEBUG;
}
if ( i > 0 ) {
setlogmask(LOG_UPTO(i));
}
break ;
}
}
}
syslog(BBFTPD_DEBUG,"Starting bbftpd") ;
opterr = 0 ;
optind = 1 ;
while ((j = getopt(argc, argv, OPTIONS)) != -1) {
switch (j) {
case 'b' :{
if ( be_daemon != 0 ) {
syslog(BBFTPD_ERR,"-b and -s options are incompatibles") ;
exit(1) ;
}
be_daemon = 2 ;
break ;
}
case 'd' :{
sscanf(optarg,"%d",&i) ;
if ( i > 0 ) debug = i ;
break ;
}
case 'e' :{
if ((sscanf(optarg,"%d:%d",&i, &k) == 2) && (i < k)) {
pasvport_min = i; pasvport_max = k;
} else {
syslog(BBFTPD_ERR,"Invalid port range : %s",optarg) ;
fprintf(stderr,"Invalid port range : %s\n",optarg) ;
exit(1) ;
}
break ;
}
case 'f' :{
fixeddataport = 0 ;
break ;
}
case 'm' :{
sscanf(optarg,"%d",&i) ;
if ( i > 0 ) maxstreams = i ;
break ;
}
case 'R' :{
bbftprcfile = optarg ;
break ;
}
case 's' :{
if ( be_daemon != 0 ) {
syslog(BBFTPD_ERR,"-b and -s options are incompatibles") ;
exit(1) ;
}
#ifdef PRIVATE_AUTH
syslog(BBFTPD_ERR,"-s option cannot be used with private authentication") ;
exit(1) ;
#endif
be_daemon = 1 ;
break ;
}
case 'u' :{
force_encoding = 0 ;
break ;
}
case 'w' :{
sscanf(optarg,"%d",&newcontrolport) ;
break ;
}
#ifdef CERTIFICATE_AUTH
case 'c' :{
accept_certs_only = 1 ;
break ;
}
case 'p' :{
accept_pass_only = 1 ;
break ;
}
#endif
default : {
break ;
}
}
}
/*
** Check for the local user in order to find the .bbftprc file
*/
if ( bbftprcfile == NULL ) {
/*
** look for the local user in order to find the .bbftprc file
** use /etc/bbftpd.conf if root
*/
if ( getuid() == 0) {
if ( (bbftprcfile = (char *) malloc (strlen("/etc/bbftpd.conf")+1 )) == NULL ) {
syslog(BBFTPD_ERR, "Error allocationg space for config file name.\n") ;
} else {
strcpy(bbftprcfile,"/etc/bbftpd.conf");
}
} else if ( (mypasswd = getpwuid(getuid())) == NULL ) {
syslog(BBFTPD_WARNING, "Unable to get passwd entry, .bbftprc will not be used\n") ;
} else if ( mypasswd->pw_dir == NULL ) {
syslog(BBFTPD_WARNING, "No home directory, .bbftprc will not be used\n") ;
} else if ( (bbftprcfile = (char *) malloc (strlen(mypasswd->pw_dir)+10) ) == NULL ) {
syslog(BBFTPD_ERR, "Error allocationg space for bbftprc file name, .bbftprc will not be used\n") ;
} else {
strcpy(bbftprcfile,mypasswd->pw_dir) ;
strcat(bbftprcfile,"/.bbftprc") ;
}
}
if ( bbftprcfile != NULL ) {
if ( strncmp(bbftprcfile,"none",4) != 0 ) {
/*
** Stat the file in order to get the length
*/
if ( stat(bbftprcfile,&statbuf) < 0 ) {
/*
syslog(BBFTPD_WARNING, "Error stating bbftprc file (%s)\n",bbftprcfile) ;
*/
} else if ( statbuf.st_size == 0 ) {
/*
** do nothing
*/
} else if ( (bbftprc = (char *) malloc (statbuf.st_size + 1 ) ) == NULL ) {
syslog(BBFTPD_ERR, "Error allocation memory for bbftprc, .bbftprc will not be used\n") ;
} else if ( ( fd = open (bbftprcfile,O_RDONLY) ) < 0 ) {
syslog(BBFTPD_ERR, "Error openning .bbftprc file (%s) : %s \n",bbftprcfile,strerror(errno)) ;
} else if ( ( j = read( fd, bbftprc , statbuf.st_size )) != statbuf.st_size ) {
syslog(BBFTPD_ERR, "Error reading .bbftprc file (%s)\n",bbftprcfile) ;
} else {
bbftprc[j] = '\0' ;
}
}
}
/*
** Analyse the bbftprc command in order to supress forbiden command
** Allowed commands are :
** setackto %d
** setrecvcontrolto %d
** setsendcontrolto %d
** setdatato %d
** setcheckstdinto %d
**
*/
if ( bbftprc != NULL ) {
carret = bbftprc ;
startcmd = bbftprc ;
/*
** Strip starting CR
*/
while (1) {
while ( *carret == 10 || *carret == ' ' ) carret++ ;
startcmd = carret ;
carret = (char *) strchr (carret, 10);
if ( carret == NULL ) break ;
*carret = '\0' ;
if (!strncmp(startcmd,"setackto",8)) {
retcode = sscanf(startcmd,"setackto %d",&alluse) ;
if ( retcode != 1 || alluse <= 0 ) {
syslog(BBFTPD_WARNING, "Acknowledge timeout must be numeric and > 0\n") ;
} else {
ackto = alluse ;
}
} else if (!strncmp(startcmd,"setrecvcontrolto",16)) {
retcode = sscanf(startcmd,"setrecvcontrolto %d",&alluse) ;
if ( retcode != 1 || alluse <= 0 ) {
syslog(BBFTPD_WARNING, "Input control timeout must be numeric and > 0\n") ;
} else {
recvcontrolto = alluse ;
}
} else if (!strncmp(startcmd,"setsendcontrolto",16)) {
retcode = sscanf(startcmd,"setsendcontrolto %d",&alluse) ;
if ( retcode != 1 || alluse <= 0 ) {
syslog(BBFTPD_WARNING, "Output control timeout must be numeric and > 0\n") ;
} else {
sendcontrolto = alluse ;
}
} else if (!strncmp(startcmd,"setdatato",9)) {
retcode = sscanf(startcmd,"setdatato %d",&alluse) ;
if ( retcode != 1 || alluse <= 0 ) {
syslog(BBFTPD_WARNING, "Data timeout must be numeric and > 0\n") ;
} else {
datato = alluse ;
}
} else if (!strncmp(startcmd,"setcheckstdinto",15)) {
retcode = sscanf(startcmd,"setcheckstdinto %d",&alluse) ;
if ( retcode != 1 || alluse <= 0 ) {
syslog(BBFTPD_WARNING, "Check input timeout must be numeric and > 0\n") ;
} else {
checkstdinto = alluse ;
}
} else {
syslog(BBFTPD_WARNING, "Unkown command in .bbftprc file (%s)\n",startcmd) ;
}
carret++ ;
}
}
/*
** Get 5K for castfilename in order to work with CASTOR
** software (even if we are not using it)
*/
if ( (castfilename = (char *) malloc (5000)) == NULL ) {
/*
** Starting badly if we are unable to malloc 5K
*/
syslog(BBFTPD_ERR,"No memory for CASTOR : %s",strerror(errno)) ;
fprintf(stderr,"No memory for CASTOR : %s\n",strerror(errno)) ;
exit(1) ;
}
/*
** Reset debug to zero if -b option is not present
*/
if ( (be_daemon != 2) ) debug = 0 ;
/*
** Check if be_daemon = 0 and in this case reset the
** control port to CONTROLPORT
*/
if ( be_daemon == 0 ) newcontrolport = CONTROLPORT ;
#ifdef CERTIFICATE_AUTH
if (be_daemon != 1 && !accept_pass_only) {
OM_uint32 min_stat, maj_stat;
maj_stat = gfw_acquire_cred(&min_stat, NULL, &server_creds);
if (maj_stat != GSS_S_COMPLETE) {
gfw_msgs_list *messages = NULL;
gfw_status_to_strings(maj_stat, min_stat, &messages) ;
while (messages != NULL) {
syslog(BBFTPD_ERR,"gfw_acquire_cred: %s", messages->msg) ;
if (be_daemon == 2) fprintf(stderr,"Acquire credentials: %s\n", messages->msg) ;
messages = messages->next;
}
exit(1);
}
}
#endif
if ( be_daemon == 2 ) {
/*
** Run as a daemon
*/
do_daemon(argc, argv, envp);
/*
** Check for debug
*/
if ( debug != 0 ) {
#if defined(WITH_RFIO) || defined(WITH_RFIO64)
sprintf(rfio_trace,"RFIO_TRACE=%d",debug) ;
retcode = putenv(rfio_trace) ;
#endif
if ( retcode == 0 ) {
/*
** reopen stdout to a file like /tmp/bbftpd.rfio.trace.pid
*/
close(STDOUT_FILENO) ;
sprintf(logmessage,"/tmp/bbftp.rfio.trace.level.%d.%d",debug,getpid()) ;
(void) freopen(logmessage,"w",stdout) ;
}
}
} else if ( be_daemon == 1 ) {
/*
** Run by a remote user
*/
/*
** Get the username
*/
struct passwd *result ;
if ( (result = getpwuid(getuid())) == NULL ) {
syslog(BBFTPD_WARNING,"Error getting username") ;
sprintf(currentusername,"UID %d",getuid()) ;
} else {
strcpy(currentusername,result->pw_name) ;
}
/*
** Set the control sock to stdin and stdout
*/
incontrolsock = 0 ;
outcontrolsock = 1 ;
/*
** As we cannot rely on the env variables to know the
** remote host, we are going to wait on an undefined
** port, send the MSG_LOGGED_STDIN and the port number
** and wait for a connection...
*/
checkfromwhere() ;
syslog(BBFTPD_INFO,"bbftpd started by : %s from %s",currentusername,inet_ntoa(his_addr.sin_addr)) ;
} else {
char buffrand[NBITSINKEY] ;
struct timeval tp ;
unsigned int seed ;
/*
** Load the error message from the crypto lib
*/
ERR_load_crypto_strings() ;
/*
** Initialize the buffrand buffer which is giong to be used to initialize the
** random generator
*/
/*
** Take the usec to initialize the random session
*/
gettimeofday(&tp,NULL) ;
seed = tp.tv_usec ;
srandom(seed) ;
for (i=0; i < sizeof(buffrand) ; i++) {
buffrand[i] = random() ;
}
/*
** Initialize the random generator
*/
RAND_seed(buffrand,NBITSINKEY) ;
incontrolsock = 0 ;
outcontrolsock = incontrolsock ;
msgsock = incontrolsock ;
/*
** set the state
*/
state = S_PROTWAIT ;
}
fatherpid = getpid() ;
if ( be_daemon == 2 || be_daemon == 0 ) {
/* Get the remote address */
addrlen = sizeof(his_addr);
if (getpeername(incontrolsock, (struct sockaddr *) &his_addr, &addrlen) < 0) {
syslog(BBFTPD_ERR, "getpeername (%s): %s", argv[0],strerror(errno));
exit(1);
}
addrlen = sizeof(ctrl_addr);
if (getsockname(incontrolsock, (struct sockaddr *) &ctrl_addr, &addrlen) < 0) {
syslog(BBFTPD_ERR, "getsockname (%s): %s", argv[0],strerror(errno));
exit(1);
}
syslog(BBFTPD_INFO,"Getting new bbftp connexion from : %s",inet_ntoa(his_addr.sin_addr)) ;
/*
** Send the encryption supported
*/
sendcrypt() ;
/*
** set the state
*/
login:
state = S_CONN ;
nfds = sysconf(_SC_OPEN_MAX) ;
FD_ZERO(&selectmask) ;
FD_SET(incontrolsock,&selectmask) ;
wait_timer.tv_sec = 10 ;
wait_timer.tv_usec = 0 ;
if ( (retcode = select(nfds,&selectmask,0,0,&wait_timer) ) == -1 ) {
syslog(BBFTPD_ERR,"Select error : %s",strerror(errno)) ;
exit(1) ;
} else if ( retcode == 0 ) {
syslog(BBFTPD_ERR,"Time OUT ") ;
exit(1) ;
} else {
if ( (retcode = readmessage(incontrolsock,buffer,MINMESSLEN,recvcontrolto)) < 0 ) {
syslog(BBFTPD_ERR,"Error reading MSG_LOG ") ;
exit(1) ;
}
msg = (struct message *) buffer ;
#ifdef CERTIFICATE_AUTH
if ( msg->code == MSG_CERT_LOG ) {
int retval;
if (accept_pass_only) {
sprintf(logmessage, "The server only accepts USER/PASS");
syslog(BBFTPD_ERR,"%s",logmessage) ;
reply(MSG_BAD_NO_RETRY,logmessage);
exit(1);
}
retval=bbftpd_cert_receive_connection(msg->msglen);
if ( retval < 0 ) {
/*
** The login failed, do not wait for a new one
*/
exit(1) ;
}
/*
** If retval >0, means an MSG_WARN has been sent already
*/
state = S_PROTWAIT ;
if (retval == 0) {
sprintf(logmessage,"bbftpd version %s : OK",VERSION) ;
reply(MSG_OK,logmessage) ;
}
} else if ( msg->code == MSG_LOG ) {
if (accept_certs_only) {
sprintf(logmessage, "The server only accepts certificates");
syslog(BBFTPD_ERR,"%s",logmessage) ;
reply(MSG_BAD_NO_RETRY,logmessage);
} else {
/*
** It seems that it is the message we were waiting
** so lets decrypt
*/
if ( loginsequence() < 0 ) {
/*
** The login failed, do not wait for a new one
*/
exit(1) ;
}
state = S_PROTWAIT ;
sprintf(logmessage,"bbftpd version %s : OK",VERSION) ;
reply(MSG_OK,logmessage) ;
}
} else if (msg->code == MSG_CRYPT) {
if (accept_certs_only) {
sprintf(logmessage, "The server only accepts certificates");
reply(MSG_BAD_NO_RETRY,logmessage);
} else if (force_encoding) {
/*
** The client can't encode and ask if it can send uncrypted
** login information
*/
sprintf(logmessage, "The server requires encrypted login");
reply(MSG_BAD_NO_RETRY,logmessage);
} else {
sprintf(logmessage, "Uncrypted login dialog accepted");
reply(MSG_OK,logmessage);
goto login;
}
} else {
syslog(BBFTPD_ERR,"Unkown message in connected state : %d",msg->code) ;
reply(MSG_BAD,"Unkown message in connected state") ;
exit(1) ;
}
#else
#ifndef PRIVATE_AUTH
if ( msg->code == MSG_LOG ) {
/*
** It seems that it is the message we were waiting
** so lets decrypt
*/
if ( loginsequence() < 0 ) {
/*
** The login failed, do not wait for a new one
*/
exit(1) ;
}
state = S_PROTWAIT ;
sprintf(logmessage,"bbftpd version %s : OK",VERSION) ;
reply(MSG_OK,logmessage) ;
} else if (msg->code == MSG_CRYPT) {
/*
** The client can't encode and ask if it can send uncrypted
** login information
*/
if (force_encoding) {
sprintf(logmessage, "The server requires encrypted login");
reply(MSG_BAD_NO_RETRY,logmessage);
exit(1);
}
sprintf(logmessage, "Uncrypted login dialog accepted");
reply(MSG_OK,logmessage);
goto login;
} else {
syslog(BBFTPD_ERR,"Unkown message in connected state : %d",msg->code) ;
reply(MSG_BAD,"Unkown message in connected state") ;
exit(1) ;
}
#else
if ( msg->code == MSG_PRIV_LOG ) {
if ( bbftpd_private_receive_connection(msg->msglen) < 0 ) {
/*
** The login failed, do not wait for a new one
*/
exit(1) ;
}
state = S_PROTWAIT ;
sprintf(logmessage,"bbftpd version %s : OK",VERSION) ;
reply(MSG_OK,logmessage) ;
} else {
syslog(BBFTPD_ERR,"Unkown message in connected state : %d",msg->code) ;
reply(MSG_BAD,"Unkown message in connected state") ;
exit(1) ;
}
#endif
#endif
}
}
/*
** At this stage we are in the S_PROTWAIT state
*/
nfds = sysconf(_SC_OPEN_MAX) ;
FD_ZERO(&selectmask) ;
FD_SET(incontrolsock,&selectmask) ;
wait_timer.tv_sec = 10 ;
wait_timer.tv_usec = 0 ;
if ( (retcode = select(nfds,&selectmask,0,0,&wait_timer) ) == -1 ) {
syslog(BBFTPD_ERR,"Select error in S_PROTWAIT state : %s",strerror(errno)) ;
exit(1) ;
} else if ( retcode == 0 ) {
syslog(BBFTPD_ERR,"Time OUT in S_PROTWAIT state") ;
exit(1) ;
} else {
if ( (retcode = readmessage(incontrolsock,buffer,MINMESSLEN,recvcontrolto)) < 0 ) {
syslog(BBFTPD_ERR,"Error reading in S_PROTWAIT state") ;
exit(1) ;
}
msg = (struct message *) buffer ;
if ( msg->code == MSG_PROT ) {
/*
** The client is using bbftp v2 protocol or higher
*/
if ( checkprotocol() < 0 ) {
exit_clean() ;
exit(1) ;
}
syslog(BBFTPD_INFO,"Using bbftp protocol version %d",protocolversion) ;
state = S_LOGGED ;
/*
** Initialize the variables
*/
mychildren = NULL ;
nbpidchild = 0 ;
curfilename = NULL ;
curfilenamelen = 0 ;
} else {
/*
** This is a bbftp v1 client
*/
protocolversion = 1 ;
syslog(BBFTPD_INFO,"Using bbftp protocol version 1") ;
state = S_LOGGED ;
/*
** So set up the v1 handlers
*/
if ( set_signals_v1() < 0 ) {
exit(1) ;
}
/*
** Initialize the pid array
*/
for ( i=0 ; i< MAXPORT ; i++) {
pid_child[i] = 0 ;
}
/*
** As we have already read the message
*/
if ( readcontrol(msg->code,msg->msglen) < 0 ) {
clean_child() ;
exit_clean() ;
exit(0) ;
}
}
}
if ( protocolversion == 1 ) goto loopv1 ;
if ( protocolversion == 2 || protocolversion == 3) goto loopv2 ;
syslog(BBFTPD_ERR,"Unknown protocol version %d",protocolversion) ;
exit(1) ;
/*
** Loop for the v2 protocol (also available for v3)
*/
loopv2:
/*
** Set up v2 handlers
*/
if ( bbftpd_setsignals() < 0 ) {
exit(1) ;
}
/*
** Set the umask ; first unset it and then reset to the default value
*/
umask(0) ;
umask(myumask) ;
for (;;) {
/*
** Initialize the selectmask
*/
nfds = sysconf(_SC_OPEN_MAX) ;
FD_ZERO(&selectmask) ;
FD_SET(incontrolsock,&selectmask) ;
/*
** Depending on the state set a timer or not
*/
switch (state) {
case S_WAITING_STORE_START :
case S_WAITING_FILENAME_STORE :
case S_WAITING_FILENAME_RETR : {
/*
** Timer of 10s between XX_V2 and FILENAME_XX
*/
wait_timer.tv_sec = 10 ;
wait_timer.tv_usec = 0 ;
break ;
}
case S_SENDING :
case S_RECEIVING : {
/*
** No timer while receiving or sending
*/
wait_timer.tv_sec = 0 ;
wait_timer.tv_usec = 0 ;
break ;
}
default : {
/*
** Timer of 900s between commands
*/
wait_timer.tv_sec = 900 ;
wait_timer.tv_usec = 0 ;
break ;
}
}
if ( (retcode = select(nfds,&selectmask,0,0,(wait_timer.tv_sec == 0) ? NULL : &wait_timer) ) == -1 ) {
if ( errno != EINTR ) {
syslog(BBFTPD_ERR,"Select error : %s",strerror(errno)) ;
}
} else if ( retcode == 0 ) {
syslog(BBFTPD_ERR,"Time OUT ") ;
if ( state == S_WAITING_STORE_START ) {
bbftpd_storeunlink(realfilename) ;
}
clean_child() ;
exit_clean() ;
exit(0) ;
} else {
/*
** At this stage we can only receive a command
*/
if ( (retcode = readmessage(incontrolsock,buffer,MINMESSLEN,recvcontrolto)) < 0 ) {
if ( state == S_WAITING_STORE_START || state == S_RECEIVING) {
bbftpd_storeunlink(realfilename) ;
sleep(5) ;
}
clean_child() ;
exit_clean() ;
exit(0) ;
}
if ( bbftpd_readcontrol(msg->code,msg->msglen) < 0 ) {
clean_child() ;
exit_clean() ;
exit(0) ;
} else {
}
}
}
/*
** Loop for the v1 protocol
*/
loopv1:
for (;;) {
/*
** Initialize the selectmask
*/
nfds = sysconf(_SC_OPEN_MAX) ;
FD_ZERO(&selectmask) ;
FD_SET(incontrolsock,&selectmask) ;
/*
** Depending on the state set a timer or not
*/
switch (state) {
case S_SENDING :
case S_RECEIVING : {
/*
** No timer while receiving or sending
*/
wait_timer.tv_sec = 0 ;
wait_timer.tv_usec = 0 ;
break ;
}
default : {
/*
** Timer of 900s between commands
*/
wait_timer.tv_sec = 900 ;
wait_timer.tv_usec = 0 ;
break ;
}
}
if ( (retcode = select(nfds,&selectmask,0,0,(wait_timer.tv_sec == 0) ? NULL : &wait_timer) ) == -1 ) {
if ( errno != EINTR ) {
syslog(BBFTPD_ERR,"Select error : %s",strerror(errno)) ;
}
} else if ( retcode == 0 ) {
syslog(BBFTPD_ERR,"Time OUT ") ;
clean_child() ;
exit_clean() ;
exit(0) ;
} else {
/*
** At this stage we can only receive a command
*/
if ( (retcode = readmessage(incontrolsock,buffer,MINMESSLEN,recvcontrolto)) < 0 ) {
clean_child() ;
exit_clean() ;
exit(0) ;
}
if ( readcontrol(msg->code,msg->msglen) < 0 ) {
clean_child() ;
exit_clean() ;
exit(0) ;
} else {
}
}
}
}
void clean_child()
{
int *pidfree ;
int i ;
if ( protocolversion == 0 ) return ;
if ( protocolversion == 1 ) {
if ( killdone == 0 ) {
killdone = 1 ;
for ( i=0 ; i<MAXPORT ; i++) {
if ( pid_child[i] != 0 ) {
syslog(BBFTPD_DEBUG,"Killing child %d",pid_child[i]) ;
kill(pid_child[i],SIGKILL) ;
}
}
}
return ;
}
if ( protocolversion >= 2 ) {
if ( killdone == 0 ) {
killdone = 1 ;
pidfree = mychildren ;
for ( i=0 ; i<nbpidchild ; i++) {
if ( *pidfree != 0 ) {
syslog(BBFTPD_DEBUG,"Killing child %d",*pidfree) ;
kill(*pidfree,SIGKILL) ;
}
pidfree++ ;
}
}
return ;
}
}
void exit_clean()
{
switch (state) {
case S_CONN : {
return ;
}
case S_LOGGED :
case S_PROTWAIT :
case S_WAITING_FILENAME_STORE :
case S_WAITING_STORE_START :
case S_SENDING:
case S_RECEIVING : {
syslog(BBFTPD_INFO,"User %s disconnected",currentusername) ;
return ;
}
default :{
return ;
}
}
}
my64_t convertlong(my64_t v) {
struct bb {
int fb ;
int sb ;
} ;
struct bb *bbpt ;
int tmp ;
my64_t tmp64 ;
tmp64 = v ;
bbpt = (struct bb *) &tmp64 ;
tmp = bbpt->fb ;
bbpt->fb = ntohl(bbpt->sb) ;
bbpt->sb = ntohl(tmp) ;
return tmp64 ;
}
#ifndef HAVE_NTOHLL
my64_t ntohll(my64_t v) {
#ifdef HAVE_BYTESWAP_H
return bswap_64(v);
#else
long lo = v & 0xffffffff;
long hi = v >> 32U;
lo = ntohl(lo);
hi = ntohl(hi);
return ((my64_t) lo) << 32U | hi;
#endif
}
#define htonll ntohll
#endif
syntax highlighted by Code2HTML, v. 0.9.1