2
Copyright (C) 2003-2004 Douglas Thain and the University of Wisconsin
3
Copyright (C) 2005- The University of Notre Dame
4
This software is distributed under the GNU General Public License.
5
See the file COPYING for details.
9
#include "globus_gss_assist.h"
17
#include "stringtools.h"
32
#include <sys/types.h>
34
int ftp_lite_data_channel_authentication = 0;
36
static int ftp_lite_send_command_gss( struct ftp_lite_server *s, const char *outbuffer );
37
static int ftp_lite_get_response_gss( struct ftp_lite_server *s, char *outbuffer );
38
static int ftp_lite_data_channel_auth( struct ftp_lite_server *s, FILE *stream );
40
struct ftp_lite_server {
46
enum { PLAIN, GLOBUS_GSS } authtype;
48
int data_channel_authentication;
51
gss_ctx_id_t data_context;
52
gss_cred_id_t credential;
56
static int ftp_lite_send_command_raw( struct ftp_lite_server *s, const char *line )
58
char buf[FTP_LITE_LINE_MAX];
60
length = sprintf(buf,"%s\r\n",line);
61
return(full_fwrite(s->command,buf,length)==length);
64
static int ftp_lite_get_response_raw( struct ftp_lite_server *s, char *line )
69
result = fgets(line,FTP_LITE_LINE_MAX,s->response);
84
static int ftp_lite_send_command( struct ftp_lite_server *s, const char *fmt, ... )
86
char buffer[FTP_LITE_LINE_MAX];
90
vsprintf(buffer,fmt,args);
93
if(!strncmp(buffer,"PASS",4)) {
94
debug(D_FTP,"%s PASS ******\n",s->hostname);
96
debug(D_FTP,"%s %s\n",s->hostname,buffer);
101
return ftp_lite_send_command_raw(s,buffer);
103
return ftp_lite_send_command_gss(s,buffer);
110
static int ftp_lite_get_response( struct ftp_lite_server *s, int accept_note, char *buffer )
119
switch(s->authtype) {
121
result = ftp_lite_get_response_raw(s,buffer);
125
Depending on the server, some responses are
126
encrypted and some are not, even once the secure
127
channel has been established.
131
c = fgetc(s->response);
132
} while(c==EOF && errno==EINTR);
133
ungetc(c,s->response);
134
if(!isdigit((int)c)) {
138
result = ftp_lite_get_response_gss(s,buffer);
140
result = ftp_lite_get_response_raw(s,buffer);
148
if(!result) return 0;
150
string_chomp(buffer);
152
debug(D_FTP,"%s %s\n",s->hostname,buffer);
154
if(!isdigit((int)(buffer[0]))) continue;
156
fields = sscanf(buffer,"%d%c",&response,&dash);
161
if( (dash==' ') && (response==do_message) ) {
168
do_message = response;
173
if( (response/100)==1 ) {
187
static int ftp_lite_parse_passive( const char *buffer, char *addr, int *port )
189
int response, fields;
193
fields = sscanf(buffer,"%d %*[^(] (%d,%d,%d,%d,%d,%d)",&response,&a,&b,&c,&d,&hi,&lo);
194
if(fields!=7) return 0;
197
sprintf(addr,"%d.%d.%d.%d",a,b,c,d);
202
static int ftp_lite_send_active( struct ftp_lite_server *s, char *addr, int port )
204
char buffer[FTP_LITE_LINE_MAX];
208
fields = sscanf(addr,"%d.%d.%d.%d",&a,&b,&c,&d);
209
if(fields!=4) return 0;
211
sprintf(buffer,"PORT %d,%d,%d,%d,%d,%d",a,b,c,d,port/256,port&0xff);
212
return ftp_lite_send_command(s,buffer);
215
struct ftp_lite_server * ftp_lite_open_and_auth( const char *host, int port )
217
struct ftp_lite_server *s;
219
int gss_port = FTP_LITE_GSS_DEFAULT_PORT;
220
int normal_port = FTP_LITE_DEFAULT_PORT;
227
debug(D_FTP,"*** attempting secure connection to %s port %d\n",host,gss_port);
229
s = ftp_lite_open(host,gss_port);
231
if(ftp_lite_auth_globus(s)) {
237
debug(D_FTP,"*** attempting insecure connection to %s port %d\n",host,normal_port);
239
s = ftp_lite_open(host,normal_port);
241
char name[FTP_LITE_LINE_MAX];
242
char pass[FTP_LITE_LINE_MAX];
244
if(ftp_lite_login(host,name,FTP_LITE_LINE_MAX,pass,FTP_LITE_LINE_MAX)) {
245
if(ftp_lite_auth_userpass(s,name,pass)) {
246
memset(pass,0,strlen(pass));
249
memset(pass,0,strlen(pass));
259
struct ftp_lite_server * ftp_lite_open( const char *host, int port )
261
char buffer[FTP_LITE_LINE_MAX];
262
struct ftp_lite_server *s;
267
s = malloc(sizeof(*s));
272
s->hostname = strdup(host);
274
s->data_channel_authentication = 0;
281
#ifdef HAS_GLOBUS_GSS
282
s->context = GSS_C_NO_CONTEXT;
283
s->data_context = GSS_C_NO_CONTEXT;
284
s->credential = GSS_C_NO_CREDENTIAL;
287
net = network_connect(host,port);
296
s->command = fdopen(net,"r+");
306
s->response = fdopen(net,"r");
316
setbuf(s->command,0);
317
setbuf(s->response,0);
319
response = ftp_lite_get_response(s,0,buffer);
320
if((response/100)!=2) {
325
errno = ftp_lite_error(response);
329
/* Most servers send 220, but promiscuous servers send 230 */
337
int ftp_lite_auth_anonymous( struct ftp_lite_server *s )
339
return ftp_lite_auth_userpass(s,"anonymous","anonymous");
342
int ftp_lite_auth_userpass( struct ftp_lite_server *s, const char *user, const char *pass )
345
char buffer[FTP_LITE_LINE_MAX];
347
if(s->auth_done) return 1;
349
ftp_lite_send_command(s,"USER %s",user);
350
ftp_lite_send_command(s,"PASS %s",pass);
352
r1 = ftp_lite_get_response(s,0,buffer);
353
r2 = ftp_lite_get_response(s,0,buffer);
355
if( ((r1/100)!=3) && ((r1/100)!=2) ) {
356
errno = ftp_lite_error(r1);
361
errno = ftp_lite_error(r2);
370
static FILE * ftp_lite_xfer_setup( struct ftp_lite_server *s, char *command )
372
char buffer[FTP_LITE_LINE_MAX];
373
char host[NETWORK_ADDR_MAX];
381
if(!s->went_binary) {
382
ftp_lite_send_command(s,"TYPE I");
383
response = ftp_lite_get_response(s,0,buffer);
384
if((response/100)!=2) return 0;
388
ftp_lite_send_command(s,"PASV");
389
response = ftp_lite_get_response(s,0,buffer);
391
if((response/100)!=2) {
392
network_address caddr, paddr;
395
network_address_local(fileno(s->command),&caddr,&cport);
397
passive = network_serve(0);
398
if(passive<0) return 0;
400
network_address_local(passive,&paddr,&pport);
401
network_address_to_string(caddr,host);
403
ftp_lite_send_active(s,host,pport);
404
response = ftp_lite_get_response(s,0,buffer);
405
if((response/100)!=2) {
406
network_close(passive);
407
errno = ftp_lite_error(response);
412
if(!ftp_lite_parse_passive(buffer,host,&port)) {
418
ftp_lite_send_command(s,command);
422
if(network_sleep(passive,100000)) {
423
net = network_accept(passive);
426
network_close(passive);
430
network_close(passive);
434
if(network_sleep(fileno(s->response),0)) {
435
response = ftp_lite_get_response(s,1,buffer);
436
if((response/100)==1) {
439
network_close(passive);
440
errno = ftp_lite_error(response);
447
net = network_connect(host,port);
452
When data channel authentication is enabled,
453
if the STOR or RETR fails because of a filesystem error
454
on the server side, then the server sends an error code
455
AFTER the network connection, but before the authentication.
456
If the command will succeed, then the server sends a
457
100 code AFTER authentication. Arg! So, we sit here and
458
wait for 100ms to see if a response comes back.
459
Fortunately, we have already done a round trip to make
460
the connection, so we don't have to measure that time.
463
if(network_sleep(fileno(s->response),10000)) {
464
response = ftp_lite_get_response(s,1,buffer);
465
if((response/100)==1) {
469
errno = ftp_lite_error(response);
475
stream = fdopen(net,"r+");
481
if(ftp_lite_data_channel_auth(s,stream)) {
489
FILE * ftp_lite_get( struct ftp_lite_server *s, const char *path, ftp_lite_off_t offset )
491
char buffer[FTP_LITE_LINE_MAX];
496
ftp_lite_send_command(s,"REST %d",offset);
497
response = ftp_lite_get_response(s,0,buffer);
498
if(response/100!=3) {
499
errno = ftp_lite_error(response);
504
sprintf(buffer,"RETR %s",path);
505
data = ftp_lite_xfer_setup(s,buffer);
513
FILE * ftp_lite_put( struct ftp_lite_server *s, const char *path, ftp_lite_off_t offset, ftp_lite_size_t size )
515
char buffer[FTP_LITE_LINE_MAX];
520
if( size==FTP_LITE_WHOLE_FILE ) {
521
ftp_lite_send_command(s,"REST %lld",(long long)offset);
522
response = ftp_lite_get_response(s,0,buffer);
523
if(response/100!=3) {
524
errno = ftp_lite_error(response);
527
sprintf(buffer,"STOR %s",path);
529
sprintf(buffer,"ESTO A %lld %s",(long long)offset,path);
532
sprintf(buffer,"STOR %s",path);
535
data = ftp_lite_xfer_setup(s,buffer);
543
FILE *ftp_lite_list( struct ftp_lite_server *s, const char *dir )
545
char buffer[FTP_LITE_LINE_MAX];
548
sprintf(buffer,"NLST %s",dir);
550
data = ftp_lite_xfer_setup(s,buffer);
558
int ftp_lite_done( struct ftp_lite_server *s )
560
char buffer[FTP_LITE_LINE_MAX];
563
response = ftp_lite_get_response(s,0,buffer);
564
if(response/100!=2) {
565
errno = ftp_lite_error(response);
572
ftp_lite_size_t ftp_lite_size( struct ftp_lite_server *s, const char *path )
574
char buffer[FTP_LITE_LINE_MAX];
575
ftp_lite_size_t size;
579
ftp_lite_send_command(s,"SIZE %s",path);
580
response = ftp_lite_get_response(s,0,buffer);
581
if(response/100!=2) {
582
errno = ftp_lite_error(response);
586
fields = sscanf(buffer,"%d %lld",&response,&size);
595
int ftp_lite_delete( struct ftp_lite_server *s, const char *path )
597
char buffer[FTP_LITE_LINE_MAX];
600
ftp_lite_send_command(s,"DELE %s",path);
601
response = ftp_lite_get_response(s,0,buffer);
602
if(response/100!=2) {
603
errno = ftp_lite_error(response);
610
int ftp_lite_rename( struct ftp_lite_server *s, const char *oldname, const char *newname )
612
char buffer[FTP_LITE_LINE_MAX];
615
ftp_lite_send_command(s,"RNFR %s",oldname);
616
ftp_lite_send_command(s,"RNTO %s",newname);
618
r1 = ftp_lite_get_response(s,0,buffer);
619
r2 = ftp_lite_get_response(s,0,buffer);
622
errno = ftp_lite_error(r1);
627
errno = ftp_lite_error(r2);
634
int ftp_lite_current_dir( struct ftp_lite_server *s, char *dir )
636
char buffer[FTP_LITE_LINE_MAX];
639
ftp_lite_send_command(s,"PWD");
640
response = ftp_lite_get_response(s,0,buffer);
641
if(response/100!=2) {
642
errno = ftp_lite_error(response);
645
if(sscanf(buffer,"%d \"%[^\"]\"",&response,dir)==2) {
648
debug(D_FTP,"couldn't parse response from PWD!");
654
int ftp_lite_change_dir( struct ftp_lite_server *s, const char *dir )
656
char buffer[FTP_LITE_LINE_MAX];
659
ftp_lite_send_command(s,"CWD %s",dir);
660
response = ftp_lite_get_response(s,0,buffer);
661
if(response/100!=2) {
662
errno = ftp_lite_error(response);
669
int ftp_lite_make_dir( struct ftp_lite_server *s, const char *dir )
671
char buffer[FTP_LITE_LINE_MAX];
674
ftp_lite_send_command(s,"MKD %s",dir);
675
response = ftp_lite_get_response(s,0,buffer);
676
if(response/100!=2) {
677
errno = ftp_lite_error(response);
684
int ftp_lite_delete_dir( struct ftp_lite_server *s, const char *dir )
686
char buffer[FTP_LITE_LINE_MAX];
689
ftp_lite_send_command(s,"RMD %s",dir);
690
response = ftp_lite_get_response(s,0,buffer);
691
if(response/100!=2) {
692
errno = ftp_lite_error(response);
699
int ftp_lite_nop( struct ftp_lite_server *s )
701
char buffer[FTP_LITE_LINE_MAX];
704
ftp_lite_send_command(s,"NOOP");
705
response = ftp_lite_get_response(s,0,buffer);
706
if(response/100!=2) {
707
errno = ftp_lite_error(response);
714
static int ftp_lite_third_party_setup( struct ftp_lite_server *source, struct ftp_lite_server *target )
716
char buffer[FTP_LITE_LINE_MAX];
719
char host[NETWORK_ADDR_MAX];
721
ftp_lite_send_command(source,"PASV");
722
response = ftp_lite_get_response(source,0,buffer);
723
if(response/100!=2) {
724
errno = ftp_lite_error(response);
729
if(!ftp_lite_parse_passive(buffer,host,&port)) {
734
ftp_lite_send_active(target,host,port);
735
response = ftp_lite_get_response(target,0,buffer);
736
if(response/100!=2) {
737
errno = ftp_lite_error(response);
745
int ftp_lite_third_party_transfer( struct ftp_lite_server *source, const char *source_file, struct ftp_lite_server *target, const char *target_file )
747
char buffer[FTP_LITE_LINE_MAX];
750
if(!source->went_binary) {
751
ftp_lite_send_command(source,"TYPE I");
752
response = ftp_lite_get_response(source,0,buffer);
753
if((response/100)!=2) {
754
errno = ftp_lite_error(response);
757
source->went_binary = 1;
760
if(!target->went_binary) {
761
ftp_lite_send_command(target,"TYPE I");
762
response = ftp_lite_get_response(target,0,buffer);
763
if((response/100)!=2) {
764
errno = ftp_lite_error(response);
767
target->went_binary = 1;
770
if(!ftp_lite_third_party_setup(source,target)) {
771
if(!ftp_lite_third_party_setup(target,source)) {
776
ftp_lite_send_command(target,"STOR %s",target_file);
777
ftp_lite_send_command(source,"RETR %s",source_file);
780
if(network_sleep(fileno(target->response),10000)) {
781
response = ftp_lite_get_response(target,1,buffer);
782
if(response/100==1) {
784
} else if(response/100==2) {
785
response = ftp_lite_get_response(source,0,buffer);
786
if(response/100==2) {
789
errno = ftp_lite_error(response);
793
ftp_lite_send_command(source,"ABOR");
794
ftp_lite_get_response(source,0,buffer);
795
errno = ftp_lite_error(response);
799
if(network_sleep(fileno(source->response),10000)) {
800
response = ftp_lite_get_response(source,1,buffer);
801
if(response/100==1) {
803
} else if(response/100==2) {
804
response = ftp_lite_get_response(target,0,buffer);
805
if(response/100==2) {
808
errno = ftp_lite_error(response);
812
ftp_lite_send_command(target,"ABOR");
813
ftp_lite_get_response(target,0,buffer);
814
errno = ftp_lite_error(response);
821
void ftp_lite_close( struct ftp_lite_server *s )
823
#ifdef HAS_GLOBUS_GSS
826
if(s->context!=GSS_C_NO_CONTEXT) {
827
gss_delete_sec_context(&major,&s->context,GSS_C_NO_BUFFER);
829
if(s->credential!=GSS_C_NO_CREDENTIAL) {
830
gss_release_cred(&major,&s->credential);
834
network_close(fileno(s->command));
841
#ifdef HAS_GLOBUS_GSS
843
static int ftp_lite_send_gss( void *arg, void *buffer, size_t length )
845
struct ftp_lite_server *s = (struct ftp_lite_server *)arg;
846
char line[FTP_LITE_LINE_MAX];
847
int ilength = length;
849
sprintf(line,"MIC ");
850
if(!ftp_lite_radix_encode(buffer,(unsigned char*)&line[4],&ilength)) return -1;
853
if(ftp_lite_send_command_raw(s,line)) {
860
static int ftp_lite_send_command_gss( struct ftp_lite_server *s, const char *buffer )
865
return globus_gss_assist_wrap_send(&minor,s->context,(char *)buffer,strlen(buffer),&token,ftp_lite_send_gss,s,stderr)==GSS_S_COMPLETE;
868
static int ftp_lite_get_gss( void *arg, void **buffer, size_t *length )
870
char line[FTP_LITE_LINE_MAX];
871
struct ftp_lite_server *s = (struct ftp_lite_server *)arg;
873
if(!ftp_lite_get_response_raw(s,line)) return -1;
874
*length = strlen(line)-4;
876
*buffer = malloc(*length+1);
877
if(!*buffer) return -1;
879
int ilength = *length;
880
if(!ftp_lite_radix_decode((unsigned char*)&line[4],*buffer,&ilength)) return -1;
886
static int ftp_lite_get_response_gss( struct ftp_lite_server *s, char *outbuffer )
888
OM_uint32 major, minor;
893
major = globus_gss_assist_get_unwrap(&minor,s->context,&buffer,&length,&token,ftp_lite_get_gss,s,stderr);
894
if(major==GSS_S_COMPLETE) {
895
strcpy(outbuffer,buffer);
896
outbuffer[length] = 0;
904
static int ftp_lite_get_adat( void *arg, void **outbuf, size_t *length )
906
char buffer[FTP_LITE_LINE_MAX];
907
struct ftp_lite_server *s = (struct ftp_lite_server *)arg;
910
response = ftp_lite_get_response(s,0,buffer);
911
if(response/100!=3) return -1;
913
if(strncmp(buffer,"335 ADAT=",9)) return -1;
915
*length = strlen(buffer)-9;
916
*outbuf = malloc(*length+1);
917
if(!*outbuf) return -1;
919
int ilength = *length;
920
if(!ftp_lite_radix_decode((unsigned char*)&buffer[9],(unsigned char *)*outbuf,&ilength)) {
930
static int ftp_lite_put_adat( void *arg, void *buf, size_t size )
932
char buffer[FTP_LITE_LINE_MAX];
933
struct ftp_lite_server *s = (struct ftp_lite_server *)arg;
936
if(!ftp_lite_radix_encode((unsigned char *)buf,(unsigned char*)buffer,&ilength)) return -1;
938
ftp_lite_send_command(s,"ADAT %s",buffer);
942
int ftp_lite_data_channel_auth( struct ftp_lite_server *s, FILE *data )
944
OM_uint32 major, minor, flags=0;
947
if(!s->data_channel_authentication) return 1;
951
debug(D_FTP,"data channel authentication in progress...");
953
major = globus_gss_assist_init_sec_context( &minor, s->credential, &s->data_context, 0, 0, &flags, &token, globus_gss_assist_token_get_fd, data, globus_gss_assist_token_send_fd, data );
954
if( major!=GSS_S_COMPLETE ) {
956
globus_gss_assist_display_status_str(&reason,"",major,minor,token);
957
if(!reason) reason = strdup("unknown error");
958
debug(D_FTP,"data channel authentication failed: %s",reason);
964
gss_delete_sec_context(&major,&s->data_context,GSS_C_NO_BUFFER);
966
debug(D_FTP,"data channel authentication succeeded");
972
int ftp_lite_auth_globus( struct ftp_lite_server *s )
974
char buffer[FTP_LITE_LINE_MAX];
975
char principal_buf[FTP_LITE_LINE_MAX];
977
OM_uint32 major, minor, flags=0;
978
network_address addr;
982
if(s->auth_done) return 1;
984
major = globus_gss_assist_acquire_cred(&minor,GSS_C_INITIATE,&s->credential);
985
if(major!=GSS_S_COMPLETE) {
990
ftp_lite_send_command(s,"AUTH GSSAPI");
991
response = ftp_lite_get_response(s,0,buffer);
993
if(response/100==2) {
994
/* Promiscuous servers respond with 200 here */
998
if(response/100!=3) {
999
errno = ftp_lite_error(response);
1003
principal = getenv("FTP_LITE_PRINCIPAL");
1005
strcpy(principal_buf,"ftp@");
1006
if(!network_address_remote(fileno(s->command),&addr,&port)) return 0;
1007
if(!network_address_to_name(addr,&principal_buf[4])) return 0;
1008
principal = principal_buf;
1011
major = globus_gss_assist_init_sec_context( &minor, s->credential, &s->context, principal, GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG, &flags, &token, ftp_lite_get_adat, s, ftp_lite_put_adat, s );
1012
if( major!=GSS_S_COMPLETE ) {
1013
gss_release_cred( &major, &s->credential );
1018
debug(D_FTP,"*** secure channel established\n");
1020
s->authtype = GLOBUS_GSS;
1022
response = ftp_lite_get_response(s,0,buffer);
1023
if((response/100)!=2) {
1024
errno = ftp_lite_error(response);
1028
if(ftp_lite_auth_userpass(s,":globus-mapping:","nothing")) {
1029
if(ftp_lite_data_channel_authentication) {
1030
ftp_lite_send_command(s,"DCAU A");
1031
response = ftp_lite_get_response(s,0,buffer);
1032
s->data_channel_authentication = (response==200);
1034
ftp_lite_send_command(s,"DCAU N");
1035
response = ftp_lite_get_response(s,0,buffer);
1036
s->data_channel_authentication = 0;
1046
int ftp_lite_data_channel_auth( struct ftp_lite_server *s, FILE *data )
1051
static int ftp_lite_send_command_gss( struct ftp_lite_server *s, const char *buffer )
1057
static int ftp_lite_get_response_gss( struct ftp_lite_server *s, char *outbuffer )
1063
int ftp_lite_auth_globus( struct ftp_lite_server *s )