diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..2816c0f --- /dev/null +++ b/src/Makefile @@ -0,0 +1,7 @@ +pass : pass.c pass.h + cc -Wall -D_REENTRANT -o pass pass.c -pthread + +clean: + rm -f pass + + diff --git a/src/pass.c b/src/pass.c new file mode 100644 index 0000000..25895c9 --- /dev/null +++ b/src/pass.c @@ -0,0 +1,323 @@ +/* notre passerelle TCP IPv4 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pass.h" + +/* les variables globales */ +int RUN=1; /* Indice qui permet de stopper en le mettant a zero */ +int Chiff=0; /* =0 rien; =1 chiff. distant =-1 chiff. local */ +in_port_t PORTL, PORTD; /* au format du reseau !! */ +uint32_t ADDRD; /* adressse distante au format du reseau */ +long long CLE = 0xABCD1276FA8745EC; + + +void interrupt(int S) +{ + switch(S) { + case SIGCHLD : + while (waitpid(-1,NULL,WNOHANG) != -1); + break; + case SIGINT: + case SIGTERM: + fprintf(stderr,"Arret de la passerelle !\n"); + exit(0); + default: + fprintf(stderr,"Recu signal %d !?!?\n",S); + } + return; +} + + +/* la structure sockaddr_in */ +struct sockaddr_in Sin = { AF_INET }; /* Le reste de egal à 0 !! */ + +/* fct qui fabrique un IP v4 au format reseau + exemple : makip4(127,0,0,1); */ +uint32_t makip4(int a, int b, int c, int d) +{ +uint32_t A; +char *p; + p = (char *)&A; + *p++ = (unsigned char)a; + *p++ = (unsigned char)b; + *p++ = (unsigned char)c; + *p = (unsigned char)d; + //return htonl(A); + return A; +} + +/* fct qui traduit une adresse IP V4 en chaine de caractere xxx.xxx.xxx.xxx */ +char * adip(uint32_t A) +{ +static char b[16]; + sprintf(b,"%d.%d.%d.%d", (int)((A>>24)& 0xFF), (int)((A>>16)& 0xFF), (int)((A>>8)& 0xFF), (int)(A & 0xFF)); + return b; +} + +int readNbc(int fd, char*b, int t) +{ +char * d; +int n, T=t; +#ifdef TRACESP + printf(" readNbc %d octets !\n",t); +#endif + d=b; + while(T>0) { + if ((n=read(fd,d,T)) == -1) return -1; + d+=n; + T-=n; + } + return 0; +} + +int SidLoc, SidDist; /* variables globales des 2 taches de transfert */ + +/* gestion des cles a partir d'un fichier externe */ +char *NFCLE="/tmp/clepass"; +/* ATTENTION choisir LCLE tel que = 8*n !!! */ +#define LCLE 8192 /* 8 ko => cle de 65536 bits !! */ +char BCLE[LCLE]; /* buffer contenant la cle */ + +/* on suppose dans tout le code que la fct de chiffrement est telle que +fchiffre(fchiffre(buf)) = buf !! autrement dit qu'elle est involutive ! */ +void fchiffre2(char * b, int l, int ori) /* travaille avec BCLE */ +{ +long long *d, *f, *c, *fc; + d=(long long*)b; + f=(long long*)(b+l); + c=(long long *)BCLE; + fc=(long long *)(BCLE+LCLE); + /* le chiffrement avec xor */ + while (d D */ + if (Chiff==1) printf("Chiffrement l=%d vers %d !\n",l,ntohs(PORTD)); + else printf("Dechiffrement l=%d depuis %d !\n",l,ntohs(PORTL)); + } else { /* le fils D -> L */ + if (Chiff==1) printf("Dechiffrement l=%d depuis %d !\n",l,ntohs(PORTD)); + else printf("Chiffrement l=%d vers %d !\n",l,ntohs(PORTL)); + } +#endif + return; /* pour l'instant ne fait rien !! */ +} + +/* la fonction du thread qui lit le port local et ecrit vers le port distant */ +void * fct_th(void * p) +{ +int n; +uint16_t lb; +char buf[LBUF]; + /* on s'occupe du transfert L -> D */ + while (1) { + /* lecture cote LOCAL */ + if (Chiff == -1) { /* chiffrement cote local */ + if (read(SidLoc,&lb,2)!=2) n=-1; + else { + n = (int)ntohs(lb); + if (readNbc(SidLoc,buf,n) == -1) n=-1; + fchiffre2(buf,n,0); + } + } else + n=read(SidLoc,buf,LBUF); + if (n > 0) { + /* ecriture cote DISTANT */ + if (Chiff == 1) { +#ifdef TRACESP + printf(" writeNbc %d octets !\n",n); +#endif + fchiffre2(buf,n,0); + lb = htons((uint16_t)n); + write(SidDist,&lb,2); /* on envoie le nb d'octets du paquet */ + } + write(SidDist,buf,n); + } + if (n==-1) break; + } + return NULL; +} + + +int liaison(int fd) /* il faut creer la passerelle pour le client */ +{ +int sid,n ; +uint16_t lb; +struct sockaddr_in Sin = { AF_INET }; +pthread_t thid; +char buf[LBUF]; + /* on est dans le cas du client qui veut se connecter au serveur */ + /* fabrication de l'interface reseau en IP V4 */ + SidLoc = fd; /* pour que le thread l'utilise */ + if ((sid = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) { + perror("socket"); return 1; + } + Sin.sin_port = PORTD; + Sin.sin_addr.s_addr = ADDRD; + /* on fait la connexion */ + if (connect(sid, (struct sockaddr*)&Sin, sizeof(Sin)) < 0) { + perror("connect"); return 2; + } + /* on masque le signal SIGPIPE */ + signal(SIGPIPE,SIG_IGN); + /* la connexion est faite ! */ + SidDist = sid; /* pour que le thread l'utilise */ + /* creation du thread qui va se charger du transfert L -> D */ + if (pthread_create(&thid,NULL,fct_th,NULL) !=0) { + fprintf(stderr,"Erreur creation thread !\n"); + close(fd); + close(sid); + return 3; + } + /* on s'occupe du transfert D -> L */ + while (1) { + if (Chiff == 1) { /* chiffrement cote distant */ + if (read(sid,&lb,2)!=2) n=-1; + else { + n = (int)ntohs(lb); + if (readNbc(sid,buf,n) == -1) n=-1; + fchiffre2(buf,n,0); + } + } else n=read(sid,buf,LBUF); + if (n > 0) { + if (Chiff==-1) { +#ifdef TRACESP + printf(" writeNbc %d octets !\n",n); +#endif + fchiffre2(buf,n,1); + lb = htons((uint16_t)n); + write(fd,&lb,2); /* on envoie le nb d'octets du paquet */ + } + write(fd,buf,n); + } + if (n==-1) break; + } + close(fd); + return 0; +} + +void finerr(char * N, int err) +{ + fprintf(stderr,"%s : version %s\n",N,Version); + fprintf(stderr,"Utilisation : %s -d|-l|-s port_local nom_ou_ip_serveur port_distant !\n",N); + exit(err); +} + +int main(int N, char *P[]) +{ +int sid, newsid, err; /* socket id = file descriptor */ +struct sockaddr_in Srec; +struct hostent *h; +pid_t pid; +long lg; + + signal(SIGCHLD,interrupt); + signal(SIGINT,interrupt); + signal(SIGTERM,interrupt); + if (initCle()) { + fprintf(stderr,"Erreur lecture ficher cle %s !!\n", NFCLE); + return 1; + } + /* verification que l'on a bien 4 parametres et seult 4 ! */ + if (N != 5) finerr(P[0],1); + /* verification du 1er parametres */ + if (P[1][0] != '-') finerr(P[0],2); + if (strlen(P[1]) != 2) finerr(P[0],2); + switch(P[1][1]) { + case 'l': + Chiff = -1; + break; + case 'd': + Chiff = 1; + break; + case 's': + Chiff = 0; + break; + default : + finerr(P[0],2); + } + PORTL = htons(atoi(P[2])); + PORTD = htons(atoi(P[4])); + if ((h=gethostbyname(P[3])) == NULL) { + fprintf(stderr,"Erreur gethostbyname no %d !\n",h_errno); + return 2; + } + bcopy((void*)(h->h_addr),(void*)(&ADDRD),h->h_length); + + /* fabrication de l'interface reseau en IP V4 */ + if ((sid = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) { + perror("socket"); return 5; + } + /* initialiser la structure sockaddr_in pour dire quel port on veut */ + Sin.sin_port = PORTL; + /* Sin.sin_addr.s_addr = makip4(127,0,0,1); */ + Sin.sin_addr.s_addr = makip4(0,0,0,0); + /* puis faire un bind() pour s'attacher a ce port */ + if (bind(sid,(struct sockaddr *)&Sin,sizeof(Sin)) == -1) { + perror("bind"); return 3; + } + + /* on definit le nombre d'ecoutes simultannees */ + if (listen(sid, NBCLI) == -1) { + perror("listen"); return 4; + } + while(RUN) { /* on attend le clients locaux pour transmettre les donnees */ + lg = sizeof(Sin); + if ((newsid=accept(sid,(struct sockaddr *)&Srec, (socklen_t*)&lg))<0){ + perror("accept"); + } else { + /* message pour informer qui se connecte */ +#ifdef TRACE_1 + printf("Connexion de %s !\n",adip(ntohl(Srec.sin_addr.s_addr))); +#endif + /* creation du processus dedie au client */ + if ((pid = fork()) == -1) { + perror("fork"); + write(newsid,"Erreur passerelle !\n",18); + } else { + if (pid==0) { /* code du fils */ + err=liaison(newsid);/* creation passerelle privee pour client */ + if (err) printf("Erreur liaison %d !\n",err); + return err; + } + /* suite du pere : ie passerelle principale */ + } + close(newsid); + } + } + return 0; /* fin OK */ +} + diff --git a/src/pass.h b/src/pass.h new file mode 100644 index 0000000..2311708 --- /dev/null +++ b/src/pass.h @@ -0,0 +1,16 @@ +/* pass.h : parametres du programme pass.c */ + +#define Version "1.00" + +#define EOT '\04' /* caractere fin de transmission */ + +#define NBCLI 10 /* nb de client pouvant utiliser la passerelle */ + +#define LBUF 512 /* ATTENTION doit etre un multiple de la longueur de la cle !*/ + +/* les differents niveaux de traces pour debugger le code */ +/* les options de compilation conditionnelle : +#define TRACE_1 suivit de la connexion des clients +#define TRACECH pour suivre les operations de chiffrement +#define TRACESP pour suivre les operations de structure de paquets +******************************************* */ diff --git a/src/passerelle.txt b/src/passerelle.txt new file mode 100644 index 0000000..f0ad27e --- /dev/null +++ b/src/passerelle.txt @@ -0,0 +1,136 @@ +Description du programme "Passerelle chiffree" que l'on se propose de faire. + +1°) La passerelle va permettre de faire l'intermédiare entre deux points du réseau. + ================================= + -> chiffré avec C1 -> + A <- chiffré avec C2 <- B + ================================= +2°) Elle va chiffrer tous les échanges : +- chiffrement avec la cle C1 a l'entree A, +- dechiffrement avec la cle C1 a la sortie B, +- chiffrement avec la cle C2 a l'entree B, +- dechiffrement avec la cle C2 a la sortie A, +Pour simplifier C1 peut etre identique a C2 + +Un exemple de passerelle : +- la commande ssh avec l'option -L ou -R. + +supposons que nous ayons notre serveur a l'adresse 192.168.56.102. +Quand on execute la commande suivante : +$ ssh -f -N -L 2000:localhost:9999 invite@192.168.56.102 +on cree un tunnel entre localhost de notre machine sur le port 2000 +et le localhost de la machine a l'adresse 192.168.56.102 sur le port 9999. + +pass port_local nom_ou_IP_destination port_destination +exemple dans le cas present +$ pass 2000 192.168.56.102 9999 + +3°) Dans le code de la version 1 (attente de 5 sec et coupure) on a montre que la tache liaison est connectée au serveur distant et au client local. +Elle sert donc de passerelle entre les deux !! + +Pour que notre passerelle soit universelle il ne faut pas a avoir d'à priori ! +Les donnees peuvent venir des deux cotes a n'importe quel moment ! + +Chaque client local va provoquer la creation d'un processus fils de la + passerelle qui lui est dedié. +Et ce processus va créer deux threads : +- un qui va faire suivre les donnees du CL vers SD, +- l'autre qui va faire la meme chose dans l'autre sens (SD -> CL). + +S'appuyer sur les exemples de code de multithreading ecrits en fevrier 2019. + +4°) Notre passerelle va s'installer en 2 etapes. +Supposons que nous voulions acceder au serveur qui tourne sur la machine d'IP +192.168.56.102 MAIS qui attend sur le port 9999 de l'interface localhost +interne. +Il va donc falloir, un supposant que mon client est sur la machine d'IP +192.168.56.1 : +a) établir une passerelle comme vu ci-dessus entre le port 2000 sur l'interface +localhost de la machine 192.168.56.1 : +Sur cette machine on fait : +$ pass 2000 192.168.56.102 3000 +b) Sur la machine d'IP 192.168.56.102 on fait : +$ pass 3000 localhost 9999 +c) Et evidemment le serveur est lance dans la machine 192.168.56.102 sur le port +9999 de l'interface localhost. +d) et le client echange sur son interface localhost (port 2000). + +5°) Comment va se faire le chiffrement ? + +Si on represente le montage que l'on a effectue avec ces 2 etapes : + Sur 192.168.56.1 Sur 192.168.56.102 + ========================= ========================== + 0.0.0.0 192.168.56.102 0.0.0.0 localhost +Client <-> port=2000 port=3000 <-> port 3000 port 9999 <-> Serveur + ========================= ========================== + Le Client se connecte Le serveur n'echange + sur son localhost QUE sur son localhost + +La partie gauche de la passerelle (sur la machine 56.1) va chiffrer ce qui sort +sur l'interface distante et dechiffrer de qui rentre par cette insterface. +Alors que la partie droite (sur la machine 56.102) le fait sur l'interface +locale (c'est a dire le port 3000 !!). +Il va donc falloir specifier dans commande si le chiffrement se fait sur +l'interface locale ou distante !!! + +Donc la commande pass va s'enrichir d'un nouveau parametre : -l pour local +ou -d pour distant. +On aura donc dans notre exemple : +a) établir une passerelle comme vu ci-dessus entre le port 2000 sur l'interface +localhost de la machine 192.168.56.1 avec chiffrement distant : +$ pass -d 2000 192.168.56.102 3000 +b) Sur la machine d'IP 192.168.56.102 avec chiffrement local : +$ pass -l 3000 localhost 9999 + +On pourra même avoir l'option -s : sans chiffrement ! + +6°) Exemple pratique : +On veut accéder à la fois au serveur qui est sur le port 9999 et au serveur ssh qui est sur le port 22. + a) du cote du client : +$ pass -d 2000 192.168.56.102 3000 +$ pass -d 2001 192.168.56.102 3001 + b) du cote du serveur : +$ pass -l 3000 localhost 9999 +$ pass -l 3001 localhost 22 + +Pour se connecter au serveur 9999 : +$ ./client localhost 2000 +$ ssh -p 2001 invite@localhost + +7°) Methodes pour le chiffrement + +a) simple avec cle symetrique +a.1) avec la fonction XOR +Table de verite + A B A xor B Axor0=A Axor1=Comp(A) A xor A + 0 0 0 0 1 0 + 0 1 1 0 1 0 + 1 0 1 1 0 0 + 1 1 0 1 0 0 + +A xor K = DC +DC xor K = A xor K xor K = A xor 0 = A + +D'ou l'interet d'utiliser xor car c'est une fonction involutive. + +8°) Principe détaillé avec une cle de longueur fixe : + +Cle : abcd +Buf : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Xor : abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd + +Ce qui veut dire que le chiffrement dépend de la manière dont sont découpées les +données !! + +Exemple : +Data: azertyuiopqsdfghjklm +1er cas : on les envoie en 1 seul morceau : +Data: azertyuiopqsdfghjklm +Xor : abcdabcdabcdabcdabcd +2em cas : on les envoie en 2 morceaux : +Data: azerty uiopqsdfghjklm +Xor : abcdabcd abcdabcdabcdabcd + + + +