Browse Source

Version 1.0

master
Patrick Foubet 3 years ago
parent
commit
9a92bc7d0a
4 changed files with 482 additions and 0 deletions
  1. +7
    -0
      src/Makefile
  2. +323
    -0
      src/pass.c
  3. +16
    -0
      src/pass.h
  4. +136
    -0
      src/passerelle.txt

+ 7
- 0
src/Makefile View File

@@ -0,0 +1,7 @@
pass : pass.c pass.h
cc -Wall -D_REENTRANT -o pass pass.c -pthread

clean:
rm -f pass



+ 323
- 0
src/pass.c View File

@@ -0,0 +1,323 @@
/* notre passerelle TCP IPv4 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <netdb.h>
#include <pthread.h>

#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<f) {
*d++ ^= *c++; /* equiv. *d ^= *c; d++; c++; */
if (c==fc) c=(long long *)BCLE;
}
return; /* pour l'instant ne fait rien !! */
}

int initCle(void)
{
int fd;
if ((fd = open(NFCLE,O_RDONLY)) == -1) return -1;
if (read(fd,BCLE, LCLE) != LCLE) return -1;
close(fd);
return 0;
}

void fchiffre(char * b, int l, int ori) /* travaille avec CLE de 64 bits */
{
long long *d, *f;
d=(long long*)b;
f=(long long*)(b+l);
/* le chiffrement avec xor */
while (d<f) *d++ ^= CLE;
#ifdef TRACECH
if (ori==0) { /* le thread L -> 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 */
}


+ 16
- 0
src/pass.h View File

@@ -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
******************************************* */

+ 136
- 0
src/passerelle.txt View File

@@ -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





Loading…
Cancel
Save