/* Ce programme permet à différents serveur d'archiver des documents sans avoir de privilèges particulier autre qu'un secret. C'est un espèce de trou noir (unidirectionnel). Le but original était de permettre à des serveurs de se débarrasser de certains documents vers un serveur archive sans ouvrir trop d'accès au serveur archive. Le serveur archive tourne le service archiveur sur un certain port et la commande archive permet d'envoyer des documents vers l'archiveur. */ #include #include #include #include #include #include #include #include #include #include #include #include "multiserveur.m" #include "multiserveur.h" /* Create the challenge used by firelisten to encrypt its secret Return -1 if we can't create the challenge (resources ?) */ static int fire_formatkey (char challenge[30]) { int ret = -1; // We create the challenge by combining the current time // and a random number. This create a unique solution // We are keeping the file opened for all the session static int fd = -1; if (fd == -1){ fd = open ("/dev/urandom",O_RDONLY); } if (fd == -1){ syslog (LOG_ERR,"Can't open /dev/urandom (%s)",strerror(errno)); }else{ char tmp[8]; if (read (fd,tmp,8)!=8){ syslog (LOG_ERR,"Can't read 8 bytes from /dev/urandom"); }else{ for (int i=0; i<8; i++){ sprintf (challenge+i*2,"%02x",tmp[i]); } time_t ti = time(NULL); sprintf (challenge+16,"%lu",ti); ret = 0; } } return ret; } /* Check the secret of the provider Return != -1 if the secret was valid */ static int fire_auth( const char *secretfile, const char *challenge, const char *buf) { int ret = -1; SSTRING client,digest; const char *pt = str_copyword (client,buf); str_copyword (digest,pt); if (digest.is_empty()){ tlmp_error ("Invalid authentication"); }else{ FILE *fin = fopen (secretfile,"r"); if (fin == NULL){ tlmp_error ("No %s secret file",secretfile); }else{ char line[100]; bool client_found = false; while (fgets(line,sizeof(line)-1,fin)!=NULL){ if (line[0] == '#') continue; char *pt = strchr(line,':'); if (pt == NULL){ tlmp_error ("Invalid line in suppliers.conf: %s",line); break; }else{ *pt++ = '\0'; if (client.cmp(line)==0){ client_found = true; strip_end(pt); SSTRING tmp; tmp.setfromf ("%s%s",challenge,pt); misc_sha (tmp.get(),tmp); if (tmp.cmp(digest)!=0){ tlmp_error ("Invalid challenge response from supplier %s",client.get()); }else{ // Ok, the supplier is authentified // we accept its requests ret = 0; } break; } } } fclose (fin); if (!client_found){ tlmp_error ("Unknown supplier %s",client.get()); } } } return ret; } class ARCH_INFO: public ARRAY_OBJ{ public: SSTRING challenge; int state; SSTRING file; // Nom du fichier transmis SSTRING user; // code usager SSTRING path; // Fichier produit (path absolue) FILE *fout; SSTRING clientip; ARCH_INFO(){ state = 0; fout = NULL; } ~ARCH_INFO(){ if (fout != NULL) fclose (fout); } }; /* On s'assure que le nom est seulement un nom sans caractère bizarre. On filtre certains caractères même s'ils ne sont pas dangeureux pour cet application mais peuvent créer de la confusion plus tard, dans certains script. */ static bool validfname (const char *name) { bool ret = true; if (strchr(name,';')!=NULL || strchr(name,'#')!=NULL || strchr(name,'?')!=NULL || strchr(name,'*')!=NULL || strchr(name,'/')!=NULL || strchr(name,'"')!=NULL || strchr(name,'\\')!=NULL || strchr(name,'\'')!=NULL){ ret = false; } return ret; } int main (int argc, char *argv[]) { glocal const char *port = NULL; glocal const char *dir = NULL; glocal const char *sfile = NULL; glocal const char *run = NULL; int ret = -1; openlog ("archiveur",LOG_PID,LOG_DAEMON); ret = (argc,argv,"multiserveur"); extern const char *version; setproginfo ("archiveur",version ,MSG_U(I_ARCHIVEUR,"Recoit et archive des fichiers")); setarg ('d',"directory","Répertoire d'archivage",glocal.dir,true); setarg ('p',"port","Ecoute sur le port TCP (ou __inetd__)",glocal.port,true); setarg ('r',"run","Exécute une commande lors de la réception du fichier" ,glocal.run,false); setarg ('s',"secretfile","Fichier contenant les secrets",glocal.sfile,true); if (strcmp(glocal.port,"__inetd__")!=0){ fprintf (stderr,"%s\n",msg); } syslog (LOG_ERR,"%s",msg); if (file_type(glocal.dir)!=1){ tlmp_error ("Répertoire d'archivage %s n'existe pas",glocal.dir); exit (-1); } (glocal.port,100); ARCH_INFO *data = new ARCH_INFO; info.data = data; char challenge[30]; if (fire_formatkey(challenge)==-1){ endclient = true; }else{ sendf ("%s\n",challenge); // Record the challenge for reuse data->challenge = challenge; data->clientip.setfromf ("%u.%u.%u.%u" ,(from>>24)&0xff ,(from>>16)&0xff ,(from>>8)&0xff ,from&0xff); } ARCH_INFO *data = (ARCH_INFO*)info.data; if (data->fout != NULL){ fclose (data->fout); data->fout = NULL; if (glocal.run != NULL){ SSTRING cmd; cmd.setfromf ("%s --ip %s --user %s --file %s --path %s" ,glocal.run,data->clientip.get() ,data->user.get(),data->file.get(),data->path.get()); if (system (cmd.get()) != 0){ tlmp_error ("Echec execution de la commande: %s",cmd.get()); } } } ARCH_INFO *data = (ARCH_INFO*)info.data; ERROR_PREFIX prefix ("From %s: ",data->clientip.get()); if (data->state == 0){ // We receive the answer to the challenge if (fire_auth(glocal.sfile,data->challenge.get(),line)==-1){ endclient = true; }else{ data->state = 1; } }else if (data->state == 1){ SSTRING cmd,user; const char *pt = str_copyword(cmd,line); pt = str_copyword(data->user,pt); data->file = str_skip(pt); if (cmd.cmp("archive")!=0 || data->file.is_empty()){ endclient = true; tlmp_error ("Connexion rejeté, commande invalide: %s",line); }else if (!validfname(data->user.get())){ endclient = true; tlmp_error ("Connexion rejeté, nom d'usager/clé invalide: %s",line); }else if (!validfname(data->file.get())){ endclient = true; tlmp_error ("Connexion rejeté, nom de fichier invalide: %s",line); }else{ SSTRING path; path.setfromf ("%s/%s",glocal.dir,data->user.get()); mkdir (path.get(),0700); path.appendf ("/%08lx-%s",time(NULL),data->file.get()); data->fout = fopen (path.get(),"w"); if (data->fout == NULL){ tlmp_error ("Ne peut ouvrir le fichier archive %s" ,path.get()); endclient = true; }else{ data->path = path; fchmod (fileno(data->fout),0600); data->state = 2; setrawmode (true); send ("go\n"); } } }else{ fwrite (line,1,info.linelen,data->fout); // printf ("Recoit %d bytes\n",info.linelen); } srv.loop(); return 0; return ret; }