/* $Id: eft_server.c,v 1.3 1999/10/06 18:16:22 he Exp $ */ /* Copyright 1998 by Henner Eisen This code is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This code 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include /*NEW libc2: realpath*/ #include #include #include #include "sbv.h" #include "tdu.h" #include #include "eft_private.h" #include "fileheader.h" /* * global variable for eft flat dir. Is tried as default directory when * server thinks that the client does not support navigation. */ const char * eft_flat_dir_name = EFT_METAFILE_PREFIX "_flat"; /* * map unknown users to this if not NULL */ char * eft_map_to_user = NULL; static int assoc_ind( struct tdu_user *usr, struct tdu_param *par) { struct eft * eft = usr->priv; unsigned char calling[TDU_PLEN_ADDR+1]="", udata[TDU_PLEN_UDATA+1]="", *user="", *pass=""; struct tdu_assoc_param *respar=par->res.assoc, *inpar=par->par.assoc; par->other_reason[0] = 0; par->other_reason[1] = 0; /* * the tdu layer checks that the maximum udata len is not exceeded, * which should prevent overflow attacks here. */ tdu_mk_printable(udata, inpar->udata, inpar->udata_len); tdu_mk_printable(calling, inpar->calling_addr, inpar->calling_len); tdu_printf(TDU_LOG_TRC," assoc_ind, ident_len=%d\n",inpar->ident_len); eft_set_flags(eft,eft_get_flags(eft)|EFT_FLAG_STRICT_TREE); if( inpar->ident_len > 0 ){ user = inpar->ident; /* Prevent possible buffer overflows by string functions * depending on zero termination. * The struct tdu_assoc_param contains space for adding * an extra delimiting \0 charater. * Be also careful with missing pass here */ user[inpar->ident_len]=0; pass = user; while( pass-user < inpar->ident_len ){ if(*pass == '/'){ *pass = 0; pass++; break; } pass++; }; tdu_printf(TDU_LOG_AP1,"IND: ASSOC id=%s calling=%s udata=%s\n",user,calling,udata ); /* * A leading '+' is used to indicate that the server shall * apply the case-insensitiveness or other compatibility * hacks for transfer names. * The '+' is not part of the user id, thus stripping it. */ if( user[0] == '+' ){ long flags; user++; flags = eft_get_flags(eft); eft_set_flags(eft,flags|EFT_FLAG_CASEFIX_TN| EFT_FLAG_CASEFIX_FS|EFT_FLAG_SLASHFIX); } /* 20.11.98, ms@msdatec.de: mapping to user eft_map_to_user, if failed * * Slightly modified and moved here (1999-01-07) -- HE: * * Now mapping to default user only if user is not in * passwd database. * * FIXME: * This check might be inappropriate if other authentication * methods (not based on /etc/passwd) are used. */ if (eft_map_to_user) { struct passwd *pw; /* */ setpwent(); while( (pw=getpwent()) ){ if( strcmp(user,pw->pw_name) == 0) break; }; endpwent(); if( ! pw ){ /* user is unknown (not in /etc/passwd).*/ tdu_printf(TDU_LOG_LOG, "user %s not known, trying alternate authentication as user \"%s\" instead\n", user, eft_map_to_user); user = eft_map_to_user; pass = ""; } } strcpy(eft->user_name,user); par->reason = eft->check_user(eft,user,pass,eft_peer_phone(eft)); } else { strcpy(eft->user_name,"(empty)"); tdu_printf(TDU_LOG_AP1,"IND: ASSOC with empty id" " calling=%s udata=%s\n", calling, udata ); par->reason = eft->check_user(eft,"","",eft_peer_phone(eft)); } respar->resp_timeout = 15; respar->called_len = LIMIT(strlen(eft->address), TDU_PLEN_ADDR); memcpy( respar->called_addr, eft->address, respar->called_len); if(! eft_signature ){ respar->udata_len = -1; } else { respar->udata_len = LIMIT(strlen(eft_signature), TDU_PLEN_UDATA); memcpy( respar->udata, eft_signature, respar->udata_len); } if( par->reason ) { tdu_printf(TDU_LOG_AP1,"REJ: ASSOC user=%s from=%s\n", user, eft_peer_phone(eft)); } else { tdu_printf(TDU_LOG_AP1,"ACP: ASSOC user=%s from=%s\n", user, eft_peer_phone(eft)); } tdu_printf(TDU_LOG_LOG,"user \"%s\" from %s %s\n", user, eft_peer_phone(eft), par->reason ? "rejected" : "accepted"); if( par->reason == TDU_RE_OTHER_REASON ) par->other_reason[0] = EFT_RE_ID_REJECTED; return par->reason; } /* * called from the tdu layer when an access indication is received. */ static int access_ind( struct tdu_user *usr, struct tdu_param *par) { struct eft * eft = usr->priv; par->other_reason[0] = 0; par->other_reason[1] = 0; tdu_printf(TDU_LOG_TRC,"eft_server access_ind()\n"); par->res.access->udata_len = 3; par->res.access->window = 8; #if 1 /* t_dir applies to group A, application name subset */ par->res.access->udata[0] = 0x89; #else par->res.access->udata[0] = 0x99; #endif par->res.access->udata[1] = 0x24; par->res.access->udata[2] = 0x4f; /* navigation supported */ par->reason = eft->setup_user(eft); eft_set_home(eft); tdu_printf(TDU_LOG_LOG,"access_ind: user set up.\n"); tdu_printf(TDU_LOG_AP2,"IND: ACCESS features=XXX_log_peer_supported_feastures_here\n"); return( par->reason ); } /* * Write a log record about transferred file into to a log file. * * The format is the same as used by wu-ftpd 2.4.2 although slightly * different semantics (because eft != ftp) apply to certain fields. * Thus, you should be able to use or modify existing tools for * parsing your log file. */ static void eft_log_xfer(struct eft * eft){ #define MAX_LOGREC_LEN MAXPATHLEN+400 char msg[MAX_LOGREC_LEN]; struct tdu_fsm * fsm = eft->fsm; struct transfer_regime *tr = & fsm->transfer; int tr_sec = tr->tv_end.tv_sec - tr->tv_begin.tv_sec; int bytes = tr->byte_cnt - fsm->stream->hdr.len, g; time_t t = time(NULL); if( eft->xferlog <= 0 ) return; g='r'; if( eft->flags & EFT_FLAG_ANONYMOUS ){ g = (eft->flags & EFT_FLAG_GUEST) ? 'g' : 'a'; }; snprintf(msg, MAX_LOGREC_LEN, "%.24s %d %s %d %s %c %s %c %c %s eft %d %s\n", ctime(&t), tr_sec, eft_peer_phone(eft), /* phone number instead of domain name*/ bytes, eft->fn, 'b', /* we currently support binary transfers only */ "_", /* for transfer options like on-the-fly tar/gzip, which * is currently not supported */ tr->outbound ? 'o' : 'i', g, eft->user_name, 0, /* authenticated e-mail address does not make sense with eurofile as we are probably outside internet domain */ "*" /* dto */ ); write(eft->xferlog,msg,strlen(msg)); } static void eft_write_xfer_log(struct tdu_user * usr, int abnormal) { usr->transfer_end = NULL; eft_log_xfer(usr->priv); if(abnormal){ tdu_printf(TDU_LOG_AP2, "TRF: ABORT \n"); } else { tdu_printf(TDU_LOG_AP3, "TRF: FINISH\n"); } } /* * This is called from the tdu protocol layer when a request for a download * is indicated. Basically, it provides us the name of the file to be * downlowded. This callback functions needs to do the following: * - map the transfer name of the file to a POSIX file name * - decide whether we want to grant the peer read access permission to * file * - open the file in question * - return a tdu_stream object back to the tdu layer. The tdu layer then * will use the tdu_stream object and its methods for reading the file */ int eft_load_ind( struct tdu_user * usr, struct tdu_param * par ) { unsigned char fnbuf[MAXPATHLEN], *fn="", *tn = par->par.transfer->designation; /* struct tdu_stream * ts = tdu_user_get_stream(usr); */ int fd, len=par->par.transfer->designation_len, stat_d=0,size; struct stat s[1]; struct eft * eft = usr->priv; usr->fsm->transfer.outbound = 1; par->other_reason[0] = 0; par->other_reason[1] = 0; /* FIXME: is len limited? */ /* len has at least been checked to be >=0 by lower layer */ tn[len]=0; #if 0 /* special hack to support certain rare non-conformaning application */ { int i=0; while( tn[i] ){ if( tn[i] == '\\') tn[i] = '/'; i++; } } #endif if( strcmp(tn, "EUROSFT92/NAVIGATION/S-LIST") == 0 ) { tdu_printf(TDU_LOG_AP3,"IND: SLIST \n" ); fd = eft_store_slist(eft,".",NULL); eft_stream_set_names(eft, tn, fn, TDU_FH_CLASS_EFT_LIST); stat_d = 1; } else if( strcmp(tn, "EUROSFT92/NAVIGATION/S-FILESTORE") == 0 ) { tdu_printf(TDU_LOG_AP3,"IND: PWD \n" ); fd = eft_store_cwd(eft); eft_stream_set_names(eft, tn, fn, TDU_FH_CLASS_EFT_STRING); } else if( strcmp(tn, "EUROSFT92/NAVIGATION/LIST") == 0 ) { tdu_printf(TDU_LOG_AP3,"IND: LIST \n" ); par->reason = TDU_RE_UNKNOWN_FILE; tdu_printf(TDU_LOG_AP3,"REJ: LIST reason=%s\n", eft_str_reason(par->reason) ); return( par->reason ); } else if( eft_acceptable_tname(tn) ) { fn = eft_fn_by_tn(fnbuf,tn,eft_get_flags(eft)); tdu_printf(TDU_LOG_AP2,"IND: LOAD t_name=%s f_name=%s\n",tn,fn); if( realpath(fn,eft->fn) && eft_file_is_tabu(eft->fn) ) { par->other_reason[0] = EFT_RE_FILE_ACCESS_IMPOSSIBLE; tdu_printf(TDU_LOG_AP2,"REJ: LOAD reason=%s[tabu]\n", eft_str_other_reason(par->other_reason[0])); return( par->reason = TDU_RE_OTHER_REASON); } fd = open(fn, O_RDONLY, 0640 ); if( fd < 0 ){ switch ( errno ) { case ENOENT: par->reason = TDU_RE_UNKNOWN_FILE; break; default: par->reason = TDU_RE_OTHER_REASON; par->other_reason[0] = EFT_RE_FILE_ACCESS_IMPOSSIBLE; } tdu_printf(TDU_LOG_AP2,"REJ: LOAD reason=%s[%s]\n", par->reason == TDU_RE_OTHER_REASON ? eft_str_other_reason(par->other_reason[0]) : eft_str_reason(par->reason), strerror(errno)); return( par->reason ); } eft_stream_set_names(eft, par->par.transfer->designation, fn, TDU_FH_CLASS_EFT_DATA); usr->transfer_end = eft_write_xfer_log; } else { tdu_printf(TDU_LOG_AP2,"IND: LOAD t_name=%s\n",tn); par->reason = TDU_RE_ERRON_DESIGN; tdu_printf(TDU_LOG_AP2,"REJ: LOAD reason=%s\n", eft_str_reason(par->reason)); return( par->reason); } if( fd < 0 ){ perror("cannot open local file to be transferred"); par->reason = TDU_RE_UNKNOWN_FILE; tdu_printf(TDU_LOG_AP2,"REJ: LOAD reason=%s\n", eft_str_reason(par->reason)); return( par->reason ); } tdu_printf(TDU_LOG_LOG,"File \"%s\" opened for reading, fd=%d\n", fn,fd); eft_stream_init_fd(eft,fd,1); fstat(fd,s); if( stat_d ){ size = s->st_size; stat(".",s); s->st_size = size; } eft_stream_set_stat(eft, s); return 0; } /* * This is called from the tdu protocol layer when a t_directory indication.' * is received. Basically, pattern to selcet the requested filenames is * provided. This callback function needs to do the following: * - Check designatation parameter for validity and decide whether we * want to grant the peer read access permission to the directory contents * in question. * - build a diretory contents file. * - return a tdu_stream object attached to the directory contents * back to the tdu layer. The tdu layer then will use the tdu_stream * object and its methods for reading diretory contents. */ int eft_dir_ind( struct tdu_user * usr, struct tdu_param * par ) { unsigned char fn[TDU_PLEN_DESIGNATION+1]; /* struct tdu_stream * ts = tdu_user_get_stream(usr); */ int i, fd, len=par->par.transfer->designation_len, size, extended , class; struct eft * eft = usr->priv; struct stat s[1]; usr->fsm->transfer.outbound = 1; par->other_reason[0] = 0; par->other_reason[1] = 0; extended = (par->par.transfer->udata_len >= 2) && (par->par.transfer->udata[1] == 0x40); strncpy(fn,par->par.transfer->designation,len); tdu_printf(TDU_LOG_AP3,"IND: DIR extended=%d\n",extended ); for( i=0; i< len; i++ ){ if( fn[i] == '/' ) fn[i] = 0; } fn[len] = 0; /* * we currently just show the contents of the working directory */ if( (fd = eft_store_dir(eft,".",NULL,extended)) < 0 ){ /* FIXME: wrong error code ??? */ *par->other_reason = EFT_RE_FILE_ACCESS_IMPOSSIBLE; par->reason_len = 1; return ( par->reason = TDU_RE_OTHER_REASON ); } eft_stream_init_fd(eft,fd,1); class = extended ? TDU_FH_CLASS_EFT_XDIR : TDU_FH_CLASS_EFT_DIR; eft_stream_set_names(eft, "", "", class); /* * size from generated directory content file, other attributes from * directory's stat entries */ fstat(fd,s); size = s->st_size; stat(".",s); s->st_size = size; eft_stream_set_stat(eft, s); return 0; } /* * This is called from the tdu protocol layer when the peer indicates that * it wants to upload a file to us. * Basically, it provides us the name of the file to be uploaded. This * callback function needs to do the following: * - decide whether we want to grant the peer write access permission to * the file and current directory * - open/create the file in question * - return a tdu_stream object back to the tdu layer. The tdu layer then * will use the tdu_stream object and its methods for writing to the file. */ int eft_save_ind( struct tdu_user * usr, struct tdu_param * par ) { unsigned char fnbuf[MAXPATHLEN+1], *tn = par->par.transfer->designation, *fn = fnbuf; struct tdu_stream * ts = tdu_user_get_stream(usr); int fd, cd=0, len=par->par.transfer->designation_len; struct eft * eft = usr->priv; usr->fsm->transfer.outbound = 0; par->reason = 0; par->other_reason[0] = 0; par->other_reason[1] = 0; tn[len]=0; fn[0]=0; if( strcmp(tn, "EUROSFT92/NAVIGATION/SELECT") == 0 ) { tdu_printf(TDU_LOG_AP2,"IND: CHDIR \n" ); fd = eft_get_tmp(eft); cd = 1; } else if( eft_acceptable_tname(tn) && (strncmp("EUROSFT92/",tn,10)!=0) ){ fn = eft_fn_by_tn(fnbuf,tn,eft_get_flags(eft)); tdu_printf(TDU_LOG_AP2,"IND: SAVE t_name=%s f_name=%s\n",tn,fn); if( realpath(fn,eft->fn) && eft_file_is_tabu(eft->fn) ) { par->other_reason[0] = EFT_RE_FILE_ACCESS_IMPOSSIBLE; tdu_printf(TDU_LOG_AP2,"REJ: SAVE reason=%s[tabu]\n", eft_str_other_reason(par->other_reason[0]) ); return( par->reason = TDU_RE_OTHER_REASON); } fd = open(fn, O_WRONLY | O_CREAT, 0640 ); } else { tdu_printf(TDU_LOG_AP2,"IND: SAVE t_name=%s\n",tn); par->reason = TDU_RE_ERRON_DESIGN; tdu_printf(TDU_LOG_AP2,"REJ: SAVE reason=%s\n", eft_str_reason(par->reason) ); return( par->reason ); } if( fd < 0 ){ tdu_printf(TDU_LOG_DBG,"cannot create local file to be transferred\n"); switch ( errno ) { case ENOENT: par->reason = TDU_RE_UNKNOWN_FILE; break; case EEXIST: par->reason = TDU_RE_FILE_EXISTS; break; case ENOSPC: par->reason = TDU_RE_OTHER_REASON; par->other_reason[0] = EFT_RE_DISK_FULL; break; default: par->reason = TDU_RE_OTHER_REASON; par->other_reason[0] = EFT_RE_FILE_ACCESS_IMPOSSIBLE; } tdu_printf(TDU_LOG_AP2,"REJ: SAVE reason=%s[%s]\n", par->reason == TDU_RE_OTHER_REASON ? eft_str_other_reason(par->other_reason[0]) : eft_str_reason(par->reason), strerror(errno) ); return( par->reason ); } tdu_printf(TDU_LOG_DBG,"File \"%s\" opened for writing, fd=%d\n", fn,fd); tdu_stream_init_fd(ts,fd,1); if( cd ){ ts->close = eft_cd_close; } else { usr->transfer_end = eft_write_xfer_log; } return 0; } int eft_accept_user(struct eft * eft) { int ret, sk = eft_get_socket(eft); struct tdu_user * user = eft_get_user(eft); unsigned char isdn_no[21]; time_t t; eft_get_peer_phone(isdn_no,sk); eft_add_peer_phone(eft,isdn_no); t = time(NULL); tdu_printf(TDU_LOG_LOG,"awaiting SBV connection from %s\n", isdn_no); ret = tdu_sbv_accept(sk); if( ret ){ tdu_printf(TDU_LOG_ERR,"SBV connection failed, ret=%d\n",ret); return ret; } tdu_printf(TDU_LOG_LOG,"SBV connection accepted\n"); user->t_associate = assoc_ind; user->t_access = access_ind; user->t_typed_data = eft_msg2stdout; user->t_load = eft_load_ind; user->t_save = eft_save_ind; user->t_dir = eft_dir_ind; user->t_rename = NULL; user->t_delete = NULL; user->transfer_end = NULL; tdu_init_slave(eft->fsm); eft->fsm->idle.initiator = 0; tdu_initial_set_idle(eft->fsm); tdu_assoc_allow_listen(eft->fsm); if (eft_is_up(eft)){ tdu_printf(TDU_LOG_DBG, "eft_accept_user(): waiting for associate request\n"); ret = tdu_dispatch_packet(eft->fsm); } tdu_printf(TDU_LOG_DBG,"eft_accept_user: ret=%d\n",ret); return ret; } int eft_server_mainloop( struct eft * eft ) { while(eft_is_up(eft)){ tdu_dispatch_packet(eft->fsm); } return 0; } void eft_set_auth(struct eft * eft, int (*check_user)(struct eft *, char *, char *,char *), int (*setup_user)(struct eft *), int (*cleanup_user)(struct eft *) ) { eft->check_user = check_user ? *check_user : NULL; eft->setup_user = setup_user ? *setup_user : NULL; eft->cleanup_user = cleanup_user ? *cleanup_user : NULL; }