#include #include #include #include #include #include #include #include #include "cryptot.h" #include "crypto_buffer.h" #include "ipv4.h" #include "scan.h" #include "socket.h" #define HOSTLEN 64 /* length of hostname string in resolvip */ /* * adding ciphers, fct to update : * usage_cipher * change_cipher * allocate_buffer * do_stats */ static void connect_to_dist(int sock, struct cryptot_st *data, int verbose) { int retry = 5; char buf[2]; if(socket_local4(sock,data->local_ip,&data->local_port)==-1) fprintf(stderr,"%s : socket_local4 error.\n",PROG_NAME); if(verbose){ fprintf(stderr,"%s : trying to connect to %03d.%03d.%03d.%03d:%d " "from %03d.%03d.%03d.%03d:%d\n",PROG_NAME, NIPQUAD(data->dist_ip),data->dist_port,NIPQUAD(data->local_ip),data->local_port); } while(retry--){ if(!socket_connect4(sock,data->dist_ip,data->dist_port)){ /* TODO we may be rejected by -S option */ if(!recv(sock,buf,2,0)){ fprintf(stderr,"%s : rejected (because of -S option ?).\n",PROG_NAME); exit(1); } if(strncmp(buf,"OK",2)) { fprintf(stderr,"%s : protocol error (middle man ?).\n",PROG_NAME); exit(1); } if(verbose)fprintf(stderr,"%s : connected.\n",PROG_NAME); return; } sleep(3); fprintf(stderr,"%s : connect failed, retry\n",PROG_NAME); } fprintf(stderr,"%s socket_connect4 error :",PROG_NAME); perror(""); exit(1); } static int wait_connection(int sock, struct cryptot_st *data, int verbose) { int client; char peer_ip[4]; unsigned int peer_port; for(;;){ if(verbose) fprintf(stderr,"%s : waiting for a connection on %03d.%03d.%03d.%03d:%d.\n", PROG_NAME, NIPQUAD(data->local_ip),data->local_port); client = socket_accept4(sock,peer_ip,&peer_port); if(client<0){ fprintf(stderr,"%s socket_accept error : ",PROG_NAME); perror(""); exit(1); } if(data->check_accept){ if(peer_port != data->src_port || memcmp(data->src_ip,peer_ip,4)){ fprintf(stderr,"%s : REFUSE connection from %03d.%03d.%03d.%03d:%d.\n", PROG_NAME,NIPQUAD(peer_ip),peer_port); shutdown(client,SHUT_RDWR); close(client); continue; } } send(client,"OK",2,0); if(verbose) fprintf(stderr,"%s : accept connection from %03d.%03d.%03d.%03d:%d.\n", PROG_NAME,NIPQUAD(peer_ip),peer_port); return client; } } static void do_stats(struct cryptot_st *data, struct timespec *s0, struct timespec *s1, unsigned int count) { char *cipher=NULL; char b[6] = "bytes"; char kb[3] = "Kb"; char mb[3] = "Mb"; char gb[3] = "Gb"; char *unit = b; float time; int size =0; float nbr =(float)count; if(nbr> 1024){ nbr/=1024; unit=kb;} if(nbr> 1024){ nbr/=1024; unit=mb;} if(nbr> 1024){ nbr/=1024; unit=gb;} time = (float)(s1->tv_sec - s0->tv_sec)+((float)(s1->tv_nsec-s0->tv_nsec))/1E9; if (data->cipher & BLOWFISH) { cipher ="blowfish"; size = data->blocks*BF_BLOCK_SIZE;} else if (data->cipher & TWOFISH) { cipher ="twofish"; size = data->blocks*TF_BLOCK_SIZE; } else if (data->cipher & AES) { cipher ="aes"; size = data->blocks*AES_BLOCK_SIZE; } else if (data->cipher & DES) { cipher ="des"; size = data->blocks*DES_BLOCK_SIZE; } else if (data->cipher & DES3_EDE) { cipher ="des3_ede"; size = data->blocks*DES3_EDE_BLOCK_SIZE; } fprintf(stderr,"\n%s statistics :\n",PROG_NAME); fprintf(stderr,"\nusing %s cipher with a buffer of %d bytes.\n",cipher, size); fprintf(stderr,"\n%.3f %s read in %0.2f second(s) ",nbr,unit,time); fprintf(stderr,"=> %.3f %s/sec.\n",nbr/time,unit); } static void usage(void) { fprintf(stderr,"%s version %s, Copyright (C) 2004 Zurcher Jeremy\n",PROG_NAME,VERSION); fprintf(stderr,"\tThis program is free software; you can redistribute it and/or modify\n"); fprintf(stderr,"\tit under the terms of the GNU General Public License as published by\n"); fprintf(stderr,"\tthe Free Software Foundation; either version 2 of the License, or\n"); fprintf(stderr,"\t(at your option) any later version.\n"); fprintf(stderr,"\tThis program is distributed in the hope that it will be useful,\n"); fprintf(stderr,"\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n"); fprintf(stderr,"\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); fprintf(stderr,"\tGNU General Public License for more details.\n\n"); fprintf(stderr,"usage : %s [-v] [-x] [-c cipher] [-n nbr_blocks]\n" " [-s [ip]:[port]] [-d [ip]:[port]] [-b [ip]:[port]] [-S [ip]:[port]] key\n",PROG_NAME); fprintf(stderr,"\t\t-v : verbose mode (statistics)\n"); fprintf(stderr,"\t\t-x : decrypt input (default is encrypt input)\n"); fprintf(stderr,"\t\t-c cipher : -c h for help (default = blowfish)\n"); fprintf(stderr,"\t\t-n nbr_blocks : nbr blocks in buffer (default = %d/CIPHER_BLOCK_SIZE)\n",BUFFER_LENGTH); fprintf(stderr,"\t\t-s ip:port : local address to read from\n"); fprintf(stderr,"\t\t-d ip:port : distant address to write to\n"); fprintf(stderr,"\t\t-b ip:port : local address used for distant connection (use with -d option)\n"); fprintf(stderr,"\t\t-S ip:port : distant address to accept connection from (use with -s option)\n"); fprintf(stderr,"\t\t key : symetric secret key (see '-c h' for tips about key length)\n"); fprintf(stderr,"\t\t MUST be the last argument of the command line.\n"); fprintf(stderr,"\n"); fprintf(stderr,"\twithout -s option reads input from stdin.\n"); fprintf(stderr,"\twithout -d option writes output to stdout.\n"); fprintf(stderr,"\twithout -S option accept connection from 'the first one'.\n"); fprintf(stderr,"\n"); fprintf(stderr,"\tif no ip is provided with s, d, b or S option, hostname is resolved and used.\n"); fprintf(stderr,"\tif no port is provided with s, d, b or S option %d is used.\n",DEFAULT_PORT); fprintf(stderr,"\n"); fprintf(stderr,"\texample:\n\t\t%s -s :5000 -S 10.10.10.10:200 -d 10.10.10.10: -b : my_secret_key\n",PROG_NAME); fprintf(stderr,"\t\twill listen on hostname:5000 for incoming data from 10.10.10.10:200, \n"); fprintf(stderr,"\t\tencrypt it and send it to 10.10.10.10:%d through hostname:%d. \n",DEFAULT_PORT,DEFAULT_PORT); fprintf(stderr,"\n"); exit(1); } static void usage_cipher(void) { fprintf(stderr,"available ciphers :\n"); fprintf(stderr,"\t0 : _Blowfish : A 64-Bit Block Cipher_ by Bruce Schneier...\n"); fprintf(stderr,"\t1 : _Towfish : A 128-Bit Block Cipher_ by Bruce Schneier... (keylength : 16,24,32)\n"); fprintf(stderr,"\t2 : _aes : A 128-Bit Block Cipher_ known as Rijndael... (keylength : 16,24,32)\n"); fprintf(stderr,"\t3 : _des : A 64-Bit BLock Cipher_ improvement of Lucifer from IBM.. (keylength : 56)\n"); fprintf(stderr,"\t4 : _des3_ede : Tripple des C = des_enc(des_dec(des_enc(M)))\n"); fprintf(stderr,"\tnext to come : ??\n\n"); exit(1); } static void init_data(struct cryptot_st *data) { data->key = NULL; data->buffer = NULL; data->size = -1; data->blocks = -1; data->cipher = DEFAULT_CIPHER; data->check_accept = 0; data->in_fd = 0; data->out_fd = 1; data->src_port = DEFAULT_PORT; data->dist_port = DEFAULT_PORT; data->local_port = DEFAULT_PORT; data->src_ip[0] = data->src_ip[1] = data->src_ip[2] = data->src_ip[3] = 0; data->dist_ip[0] = data->dist_ip[1] = data->dist_ip[2] = data->dist_ip[3] = 0; data->local_ip[0] = data->local_ip[1] = data->local_ip[2] = data->local_ip[3] = 0; } /* update cipher param */ static void change_cipher(unsigned int *cipher, unsigned long param) { *cipher &= DECRYPT; if(param==0){ *cipher |= BLOWFISH; return ; } if(param==1){ *cipher |= TWOFISH; return; } if(param==2){ *cipher |= AES; return; } if(param==3){ *cipher |= DES; return; } if(param==4){ *cipher |= DES3_EDE; return; } fprintf(stderr,"%s : unknown cipher.\n",PROG_NAME); exit(1); } /* set ip to localhost if == 0:0:0:0 */ static void resolve_ip(char *ip) { char hostname[HOSTLEN]; struct hostent *hp; if(ip[0] == ip[1] && ip[1] == ip[2] && ip[2] == ip[3] && ip[3] == 0){ if(gethostname(hostname,HOSTLEN)==-1){ fprintf(stderr,"%s error ",PROG_NAME); perror("gethostname "); exit(1); } if((hp = gethostbyname(hostname))==NULL){ fprintf(stderr,"%s : can't resolve %s, ",PROG_NAME,hostname); herror("\tgethostbyname "); exit(1); } memcpy(ip,hp->h_addr,4); } } /* if ip is 0:0:0:0 use localhost, bind to ip/port call listen if required */ static int bind_to(char *ip, int port, int listen) { int socket; if((socket = socket_tcp(1))==-1) { fprintf(stderr,"%s error in ",PROG_NAME); perror("socket_tcp "); exit(1); } if(socket_bind4(socket,ip,port)==-1) { fprintf(stderr,"%s : unable to bind to %03d.%03d.%03d.%03d:%d",PROG_NAME,NIPQUAD(*ip),port); perror("\tsocket_bind "); exit(1); } if(listen) if(socket_listen(socket,20)==-1) { fprintf(stderr,"%s error in ",PROG_NAME); perror("socket_listen "); exit(1); } return socket; } /* compute blocks if == -1, allocate buffer, set size */ static char* allocate_buffer(unsigned int cipher, unsigned int *blocks, unsigned int *size) { char *buffer = NULL; int tmp = *blocks; if(tmp==-1){ if(cipher & BLOWFISH) tmp = BUFFER_LENGTH/BF_BLOCK_SIZE; else if(cipher & TWOFISH) tmp = BUFFER_LENGTH/TF_BLOCK_SIZE; else if(cipher & AES) tmp = BUFFER_LENGTH/AES_BLOCK_SIZE; else if(cipher & DES) tmp = BUFFER_LENGTH/DES_BLOCK_SIZE; else if(cipher & DES3_EDE) tmp = BUFFER_LENGTH/DES3_EDE_BLOCK_SIZE; buffer = (char*)malloc(BUFFER_LENGTH); } else { if(cipher & BLOWFISH) buffer = (char*)malloc(tmp*BF_BLOCK_SIZE); else if(cipher & TWOFISH) buffer = (char*)malloc(tmp*TF_BLOCK_SIZE); else if(cipher & AES) buffer = (char*)malloc(tmp*AES_BLOCK_SIZE); else if(cipher & DES) buffer = (char*)malloc(tmp*DES_BLOCK_SIZE); else if(cipher & DES3_EDE) buffer = (char*)malloc(tmp*DES3_EDE_BLOCK_SIZE); } if(buffer==NULL) { fprintf(stderr,"%s : not enough memory for buffer.\n",PROG_NAME); exit(1); } if(cipher & BLOWFISH) *size = tmp * BF_BLOCK_SIZE; else if(cipher & TWOFISH) *size = tmp * TF_BLOCK_SIZE; else if(cipher & AES) *size = tmp * AES_BLOCK_SIZE; else if(cipher & DES) *size = tmp * DES_BLOCK_SIZE; else if(cipher & DES3_EDE) *size = tmp * DES3_EDE_BLOCK_SIZE; *blocks = tmp; return buffer; } static void c_encrypt(struct cryptot_st *data, int verbose) { register unsigned int ret = 0; register unsigned int reg = 0; register unsigned int size = data->size; register u8 *buffer = (u8*)data->buffer; operation op; int input, output; crypto_buffer c_buffer; struct timespec s0, s1; input = data->in_fd; output = data->out_fd; if(output==1) op = &u8_unix_write; else{ op = &u8_tcp_write; connect_to_dist(output,data,verbose); } if(crypto_buffer_init(&c_buffer, output, op, data->blocks, data->cipher, (u8*)data->key, strlen(data->key))==-1){ fprintf(stderr,"%s : crypto_buffer_init_error. Check your key len. (try -c h)\n",PROG_NAME); exit(1); } /* now I'm ready to write */ if(input==0){ if(verbose){ clock_gettime(MY_CLOCK, &s0); while((ret=read(input, buffer, size))>0){ reg += ret; if(crypto_buffer_put(&c_buffer,buffer,ret)==-1){ fprintf(stderr,"%s crypto_buffer_put error ",PROG_NAME); perror(""); exit(1); } } } else{ reg = (unsigned int)&c_buffer; while((ret=read(input, buffer, size))>0) if(crypto_buffer_put((crypto_buffer*)reg,buffer,ret)==-1){ fprintf(stderr,"%s crypto_buffer_put error ",PROG_NAME); perror(""); exit(1); } } } else { reg = wait_connection(input,data,verbose); if(verbose){ clock_gettime(MY_CLOCK, &s0); while((ret=recv(reg, buffer, size, 0))>0){ reg += ret; if(crypto_buffer_put(&c_buffer,buffer,ret)==-1){ fprintf(stderr,"%s crypto_buffer_put error ",PROG_NAME); perror(""); exit(1); } } } else{ reg = (unsigned int)&c_buffer; while((ret=recv(reg, buffer, size, 0))>0) if(crypto_buffer_put((crypto_buffer*)reg,buffer,ret)==-1){ fprintf(stderr,"%s crypto_buffer_put error ",PROG_NAME); perror(""); exit(1); } } shutdown(reg,SHUT_RDWR); close(reg); /* close client socket */ } crypto_buffer_flush(&c_buffer); if(ret==-1){ fprintf(stderr,"%s read error ",PROG_NAME); perror(""); exit(1); } if(verbose) { clock_gettime(MY_CLOCK, &s1); do_stats(data, &s0, &s1, reg); } } static void c_decrypt(struct cryptot_st *data, int verbose) { register int n = 0; register unsigned int ret = 0; register unsigned int reg = 0; register unsigned int size = data->size; register u8 *buffer = (u8*)data->buffer; operation op; int input, output; crypto_buffer c_buffer; struct timespec s0, s1; input = data->in_fd; output = data->out_fd; if(input==0) op = &u8_unix_read; else{ op = &u8_tcp_read; input = wait_connection(input,data,verbose); } if(crypto_buffer_init(&c_buffer, input, op, data->blocks, data->cipher, (u8*)data->key, strlen(data->key))==-1){ fprintf(stderr,"%s : crypto_buffer_init_error. Check your key len. (try -c ?)\n",PROG_NAME); exit(1); } /* now I'm ready to read */ if(output==1){ if(verbose){ clock_gettime(MY_CLOCK, &s0); while((ret=crypto_buffer_get(&c_buffer, buffer, size))>0){ reg += ret; n = write(output, buffer, ret); } } else{ reg = (unsigned int)&c_buffer; while((ret=crypto_buffer_get(&c_buffer, buffer, size))>0) n = write(output, buffer, ret); } } else { connect_to_dist(output,data,verbose); if(verbose){ clock_gettime(MY_CLOCK, &s0); while((ret=crypto_buffer_get(&c_buffer, buffer, size))>0){ reg += ret; if(send(output, buffer, ret, MSG_CONFIRM)==-1){ fprintf(stderr,"%s send error ",PROG_NAME); perror(""); exit(1); } } } else{ reg = (unsigned int)&c_buffer; while((ret=crypto_buffer_get(&c_buffer, buffer, size))>0) if(send(output, buffer, ret, MSG_CONFIRM)==-1){ fprintf(stderr,"%s send error ",PROG_NAME); perror(""); exit(1); } } } if(input!=0) { shutdown(input,SHUT_RDWR); close(input); } /* close client socket */ if(ret==-1){ fprintf(stderr,"%s read error ",PROG_NAME); perror(""); exit(1); } if(verbose) { clock_gettime(MY_CLOCK, &s1); do_stats(data, &s0, &s1, reg); } } static void argument_error(char opt, int wrong) { if(wrong) fprintf(stderr,"%s : -%c option, wrong argument.\n",PROG_NAME,opt); else fprintf(stderr,"%s : -%c option, argument missing.\n",PROG_NAME,opt); exit(1); } int main(int argc, char **argv) { /* general */ int ret; char *tmp; unsigned long param; unsigned int verbose = 0; char bind_ip[4]; unsigned int bind_port; /* crypto */ struct cryptot_st data; init_data(&data); bind_ip[0] = bind_ip[1] = bind_ip[2] = bind_ip[3] = 0; bind_port = DEFAULT_PORT; if(!--argc) usage(); ++argv; while(argc--){ if((*argv)[0]=='-'){ if((*argv)[1]=='v') verbose = 1; else if((*argv)[1]=='x') data.cipher|=DECRYPT; else if((*argv)[1]=='n') { if(!argc--) argument_error('n',0); ret=scan_ulong(*(++argv),¶m); if(!ret || (*argv)[ret]) argument_error('n',1); data.blocks = (unsigned int)param; } else if((*argv)[1]=='c') { if(!argc--) argument_error('c',0); if(*(*(++argv))=='h') usage_cipher(); ret=scan_ulong(*argv,¶m); if(!ret || (*argv)[ret])argument_error('c',1); change_cipher(&data.cipher,param); } else if((*argv)[1]=='s') { if(!argc--) argument_error('s',0); ret=ipv4_scan(*(++argv),data.local_ip); tmp = &(*argv)[ret]; if(*tmp++!=':') argument_error('s',1); ret=scan_ulong(tmp,¶m); if(ret) data.local_port = (unsigned int)param; if(*(tmp+ret)) argument_error('s',1); data.in_fd = -1; } else if((*argv)[1]=='b') { if(!argc--) argument_error('b',0); ret=ipv4_scan(*(++argv),bind_ip); tmp = &(*argv)[ret]; if(*tmp++!=':') argument_error('b',1); ret=scan_ulong(tmp,¶m); if(ret) bind_port = (unsigned int)param; if(*(tmp+ret)) argument_error('b',1); } else if((*argv)[1]=='d') { if(!argc--) argument_error('d',0); ret=ipv4_scan(*(++argv),data.dist_ip); tmp = &(*argv)[ret]; if(*tmp++!=':') argument_error('d',1); ret=scan_ulong(tmp,¶m); if(ret) data.dist_port = (unsigned int)param; if(*(tmp+ret)) argument_error('d',1); data.out_fd = -1; } else if((*argv)[1]=='S') { if(!argc--) argument_error('S',0); ret=ipv4_scan(*(++argv),data.src_ip); tmp = &(*argv)[ret]; if(*tmp++!=':') argument_error('S',1); ret=scan_ulong(tmp,¶m); if(ret) data.src_port = (unsigned int)param; if(*(tmp+ret)) argument_error('S',1); data.check_accept = 1; } else usage(); } else { data.key = *argv; break; } argv++; } if(!data.key) usage(); /* if ip is 0:0:0:0 call gethostname and gethostbyname */ if(data.in_fd != 0) resolve_ip(data.local_ip); if(data.out_fd != 1){ resolve_ip(data.dist_ip); resolve_ip(bind_ip); } if(data.in_fd != 0 && data.check_accept) resolve_ip(data.src_ip); /* if input AND output have been changed, check that local != bind AND local != distant */ if(data.in_fd != 0 && data.out_fd != 1){ if(data.local_port == bind_port && !memcmp(data.local_ip,bind_ip,4)){ fprintf(stderr,"%s : trying to bind twice to same address; use -b option.\n",PROG_NAME); exit(1); } if(data.local_port == data.dist_port && !memcmp(data.local_ip,data.dist_ip,4)){ fprintf(stderr,"%s : trying to read from output, you crazy !\n",PROG_NAME); exit(1); } } if(data.in_fd != 0) data.in_fd = bind_to(data.local_ip, data.local_port, 1); /* bind and listen */ if(data.out_fd != 1) data.out_fd = bind_to(bind_ip, bind_port, 0); /* bind */ data.buffer = allocate_buffer(data.cipher, &data.blocks, &data.size); /* allocate buffer and set blocks & size */ #ifdef _DEBUG_ fprintf(stderr,"user defind aprameters :\n"); fprintf(stderr,"\tcipher : %d\n",data.cipher); fprintf(stderr,"\tblocks : %d\n",data.blocks); fprintf(stderr,"\tsize : %d\n",data.size); fprintf(stderr,"\tkey : %s\n",data.key); fprintf(stderr,"\tlocal %03d.%03d.%03d.%03d:%d\n", NIPQUAD(data.local_ip),data.local_port); fprintf(stderr,"\tdist %03d.%03d.%03d.%03d:%d\n", NIPQUAD(data.dist_ip),data.dist_port); fprintf(stderr,"\tbind %03d.%03d.%03d.%03d:%d\n", NIPQUAD(bind_ip),bind_port); fprintf(stderr,"\tsrc %03d.%03d.%03d.%03d:%d\n", NIPQUAD(src_ip),src_port); #endif if(verbose){ fprintf(stderr,"%s : running in verbose mode:\n",PROG_NAME); if(data.in_fd !=0){ socket_local4(data.in_fd,data.local_ip,&data.local_port); fprintf(stderr,"%s : reading from : %03d.%03d.%03d.%03d:%d\n", PROG_NAME,NIPQUAD(data.local_ip),data.local_port); if(data.check_accept) fprintf(stderr,"%s : accept connection from : %03d.%03d.%03d.%03d:%d\n", PROG_NAME,NIPQUAD(data.src_ip),data.src_port); else fprintf(stderr,"%s : accept connection from anywhere.\n",PROG_NAME); } else fprintf(stderr,"%s : reading from stdin\n",PROG_NAME); if(data.out_fd !=1){ socket_local4(data.out_fd,data.local_ip,&data.local_port); fprintf(stderr,"%s : writing through to : %03d.%03d.%03d.%03d:%d\n", PROG_NAME,NIPQUAD(data.local_ip),data.local_port); } else fprintf(stderr,"%s : writing to stdout\n",PROG_NAME); } if(data.cipher&DECRYPT){ c_decrypt(&data,verbose); } else{ c_encrypt(&data,verbose); } /* close sockets */ if(data.in_fd !=0) close(data.in_fd); if(data.out_fd !=0) close(data.out_fd); return 0; }