Logo Search packages:      
Sourcecode: vde2 version File versions  Download package

blowfish.c

/*
 * Blowfish functions
 * Copyright  2006 Daniele Lacamera
 * Released under the terms of GNU GPL v.2
 * http://www.gnu.org/copyleft/gpl.html
 *
 * This program is released under the GPL with the additional exemption that
 * compiling, linking, and/or using OpenSSL is allowed.
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include "blowfish.h"
#include "crc32.h"

unsigned char *crc32(unsigned char*,int);
static unsigned long long mycounter=1;

static EVP_CIPHER_CTX ctx;
static int nfd = -1;
static struct peer *list=NULL;

static struct itimerval TIMER = {
      .it_interval={ .tv_sec=0, .tv_usec=0},
      .it_value={ .tv_sec=SESSION_TIMEOUT/2, .tv_usec=0 }
};

/*
 * Add a peer to the main list.
 * Client will have a list of one peer only,
 * server will have a peer in the list for each "connection"
 * it establishes.
 */

void addpeer(struct peer *np)
{
      np->next=list;
      list=np;

}

/*
 * Internal, recursive functions:
 */
static int _peers(struct peer *iter)
{
      if(!iter)
            return 0;
      else
            return 1+_peers(iter->next);
}

static void _populatepoll(struct pollfd *pfd, struct peer *iter,int index, struct peer *peerlist)
{
      int datafd;
      if(!iter)
            return;
      memcpy(&(peerlist[index]),iter,sizeof(struct peer));
      datafd=vde_datafd(iter->plug);
      if(datafd >= 0){
            pfd[index].fd=datafd;
            pfd[index++].events=POLLIN|POLLHUP;
      } else {
            
      }
      
      _populatepoll(pfd,iter->next,index, peerlist);

}


struct peer *_getpeer(struct sockaddr_in saddr, struct peer *sublist)
{
      if(!sublist)
            return NULL;
      if(sublist->in_a.sin_addr.s_addr==saddr.sin_addr.s_addr && sublist->in_a.sin_port==saddr.sin_port)
            return sublist;
      return _getpeer(saddr,sublist->next);
      
}

struct peer *_getpeerbna(struct sockaddr_in saddr, struct peer *sublist)
{
      if(!sublist)
            return NULL;
      if(sublist->handover_a.sin_addr.s_addr==saddr.sin_addr.s_addr && sublist->handover_a.sin_port==saddr.sin_port)
            return sublist;
      return _getpeerbna(saddr,sublist->next);
      
}

static struct peer *_getpeerbyid(struct datagram *pkt, struct peer *sublist)
{
      if(pkt->len!=FILENAMESIZE+1)
            return NULL;
      if(!sublist)
            return NULL;
      if(strncmp(pkt->data+1,sublist->id,FILENAMESIZE)==0)
            return sublist;
      return _getpeerbyid(pkt,sublist->next);
      
}


void 
set_expire(struct peer *p)
{
      gettimeofday(&(p->expire),NULL);
      p->expire.tv_sec+=SESSION_TIMEOUT;
}

/*
 * Check progressive number validity in incoming datagram
 */
int
isvalid_timestamp(unsigned char *block, int size, struct peer *p)
{
      
      
      int i;
      unsigned long long pktcounter=0;
      for(i=0;i<8;i++){
            pktcounter+=block[size-12+i]<<(i*8);
      }
      if(pktcounter>p->counter){
            p->counter=pktcounter;
            return 1;
      }else{
            //fprintf(stderr,"bad timestamp!\n");
            return 0;
      }
      
}

/*
 * Check CRC32 Checksum from incoming datagram
 */
int 
isvalid_crc32(unsigned char *block, int len)
{
      unsigned char *crc=(unsigned char *)crc32(block,len-4);
      if(strncmp((char*)block+(len-4),(char*)crc,4)==0)
            return 1;
      else{
                  
            //fprintf(stderr,"bad crc32!\n");
            return 0;
      }
}


/*
 * Returns peer list length.
 */

static int numberofpeers(){
      struct peer *iter=list;
      return _peers(iter);
}



struct peer *clean_peerlist(struct peer *sublist)
{
      if(!sublist)
            return NULL;

      if(sublist->state == ST_AUTH && vde_datafd(sublist->plug) < 0){
            struct peer *nxt=sublist->next;
            free(sublist);
            return nxt;
      }

      if(sublist->state == ST_AUTH){
            struct timeval now;
            gettimeofday(&now,NULL);
            
            if(after(now,sublist->expire)){
                  struct peer *nxt=sublist->next;
                  vde_close(sublist->plug);
                  free(sublist);
                  return nxt;
            }
      }

      sublist->next=clean_peerlist(sublist->next);
      return sublist;
}


void
autocleaner(int signo)
{
      struct itimerval *old=NULL;
      list=clean_peerlist(list);
      setitimer(ITIMER_REAL, &TIMER, old);
}

/*
 * Returns a list of all the peer in the peer list, adding their 
 * network socket to pollfd.
 * This is called in blowfish_select, to populate the pollfd structure.
 */
static struct peer *populate_peerlist(struct pollfd *pfd)
{
      struct peer *iter, *peerlist;
      iter=list; //=clean_peerlist(list);

      peerlist=(struct peer *) malloc( (numberofpeers()+1)*sizeof(struct peer) );
      _populatepoll(pfd,iter,1,peerlist);

      return peerlist;
}

/*
 * Get a pointer to the peer in the list which has the given udp address.
 */
struct peer *getpeer(struct sockaddr_in saddr)
{
      struct peer *iter=list;
      return (_getpeer(saddr,iter));
                  
}

struct peer *getpeerbynewaddr(struct sockaddr_in saddr)
{
      struct peer *iter=list;
      return (_getpeerbna(saddr,iter));
                  
}


/*
 * Get a pointer to the peer in the list which key filename is the same of that in the login datagram. 
 */
struct peer *getpeerbyid(struct datagram *pkt)
{
      struct peer *iter=list;
      return (_getpeerbyid(pkt,iter));
                  
}

/*
 * Send a plain "access denied" message to the specified peer.
 */
void
deny_access(struct peer *p)
{
      send_udp("Access Denied.\0",15,p,CMD_DENY);
}

/*
 * Main select routine.
 * A poll will wake up whenever a new packet is available to read, either from one 
 * of the vde_plug attached, or from udp socket.
 * Returns a struct datagram aware of its own source.
 * Also discriminate commands from data, by first byte.
 */
struct datagram *blowfish_select (int timeout)
{
   unsigned peerlen;
   int pollret;
   struct pollfd *pfd;
   static struct datagram *ret = NULL;
   struct peer *peerlist;
   static int i=1;
   
   pfd=malloc((1+numberofpeers())*sizeof(struct pollfd));
   
   pfd[0].fd=nfd;
   pfd[0].events=POLLIN|POLLHUP;
   peerlist = populate_peerlist(pfd);

  do{
        pollret=poll(pfd,1+numberofpeers(),1000);
        if(pollret<0){
              if(errno==EINTR)
                    return NULL;
              perror("poll");
              exit(1);
        }
        if (!ret){
              ret = malloc(sizeof(struct datagram));
              bzero(ret,sizeof(struct datagram));
              ret->orig = malloc(sizeof (struct peer));
              bzero(ret->orig,sizeof(struct peer));
        }
  } while (pollret==0);

  
for(;;){
  if (pfd[0].revents&POLLIN) {
      unsigned char inpkt[MAXPKT];
      unsigned char *inbuff=inpkt+1;
      int ilen,tlen;
      struct sockaddr_in ipaddress;
      peerlen = sizeof(struct sockaddr_in);
      ilen = recvfrom(nfd, inpkt, MAXPKT, 0,
            (struct sockaddr *) &ipaddress, &peerlen);
  
        ret->orig=getpeer(ipaddress);
      if(!ret->orig){
            ret->orig=malloc(sizeof(struct peer));
            bzero(ret->orig,sizeof(struct peer));
            ret->orig->in_a.sin_family = AF_INET;
            ret->orig->in_a.sin_port = ipaddress.sin_port;
            ret->orig->in_a.sin_addr.s_addr= ipaddress.sin_addr.s_addr;
            ret->orig->state=ST_CLOSED;
      }
      
            
      if((inpkt[0]==PKT_DATA)&&       
             (ret->orig->state==ST_AUTH || ret->orig->state==ST_SERVER))
      {
            
            ret->src = SRC_BF;
            ilen--;
            
            EVP_DecryptInit (&ctx, EVP_bf_cbc (), ret->orig->key, ret->orig->iv);
             
            if (EVP_DecryptUpdate (&ctx, ret->data, &ret->len, inbuff, ilen) != 1)
              {
                  fprintf (stderr,"error in decrypt update\n");
                  return NULL;
              }
            if (EVP_DecryptFinal (&ctx, ret->data + ret->len, &tlen) != 1)
              {
                  fprintf (stderr,"error in decrypt final\n");
                  return NULL;
              }
              ret->len += tlen;
              if( isvalid_crc32(ret->data,ret->len) && isvalid_timestamp(ret->data,ret->len,ret->orig) ){
                  ret->len-=12;
                  if(ret->orig->state==ST_AUTH)
                        set_expire(ret->orig);
                  return ret;
            }else{            
//                deny_access(ret->orig);
                  return NULL;
            }
      }else if((inpkt[0]==CMD_HANDOVER)){
            ret->src = SRC_CTL;
            ilen--;
            
            //fprintf (stderr,"Recived Handover datagram.:   ");
            EVP_DecryptInit (&ctx, EVP_bf_cbc (), ret->orig->key, ret->orig->iv);
             
            if (EVP_DecryptUpdate (&ctx, ret->data+1, &ret->len, inbuff, ilen) != 1)
              {
                  fprintf (stderr,"error in decrypt update\n");
                  return NULL;
              }
            if (EVP_DecryptFinal (&ctx, ret->data + 1 + ret->len, &tlen) != 1)
              {
                  fprintf (stderr,"error in decrypt final\n");
                  return NULL;
              }
              ret->len += tlen;
              ret->len +=1;
              if( isvalid_crc32(ret->data+1,ret->len-1) && isvalid_timestamp(ret->data,ret->len,ret->orig) ){
                  ret->len-=12;
                  ret->data[0]=CMD_HANDOVER;
                  //fprintf (stderr,"Valid Handover. Resending key.\n");
                  return ret;
            }else{            
                  fprintf (stderr,"Invalid Handover packet. crc32?%d, timestamp?%d \n",isvalid_crc32(ret->data+1,ret->len-1), isvalid_timestamp(ret->data,ret->len,ret->orig));
                  deny_access(ret->orig);
                  return NULL;
            }
            
      }else if(inpkt[0]&PKT_CTL){
            ret->src = SRC_CTL;
            memcpy(ret->data,inpkt,ilen);
            ret->len=ilen;
            return ret;
      }else{      
      deny_access(ret->orig);
      return NULL;
                  
      
      }
      
  }

  // This increment comes with "static int i" def, to ensure fairness among peers.
  i++;        
  if(i>numberofpeers())
        i=1;
  
  if (pfd[i].revents&POLLIN) {
/*      c=read(pfd[i].fd,ret->data,2);
        if(c<2) 
              return NULL;
        vde_len=0;
        vde_len+=((unsigned char)(ret->data[0]))<<8;
        vde_len+=(unsigned char)(ret->data[1]);
        
        ret->len=2;
        while(ret->len < (vde_len + 2)){
            ret->len += read(pfd[i].fd, ret->data+ret->len, ((vde_len+2) - ret->len));
        }
      // fprintf(stderr,"Read %d.\n",vde_len);
        
*/ 
      ret->len = vde_recv(peerlist[i].plug, ret->data, MAXPKT,0);
      
      if(ret->len<1)
            return NULL;
      
      ret->src = SRC_VDE;
      ret->orig = &(peerlist[i]);

      //set_expire(&(peerlist[i]));
        
        
      return ret;
            
  }
// list=clean_peerlist(list); 
}
  return NULL;
}


/*
 * Send a virtual frame to the vde_plug process associated 
 * with the peer
 */
void
send_vdeplug(const char *data, size_t len, struct peer *p)
{
      static unsigned int outbuf[MAXPKT];
      static int outp=0;
      static u_int16_t outlen;
      if(len<=0)
            return;
      
      if(outp==0 && (len >=2) ){
            outlen=2;
            outlen+=(unsigned char)data[1];
            outlen+=((unsigned char)(data[0]))<<8;
      }
      
      if(len>=outlen){
            vde_send(p->plug,data,outlen,0);
            send_vdeplug(data+outlen,len-outlen, p);
            return;
      }
            
      memcpy(outbuf+outp,data,len);
      outp+=len;
      if(outp>=outlen){
            vde_send(p->plug,(char *)outbuf,outlen,0);
      }                 
}

/*
 * Include a progressive number into outgoing datagram,
 * to prevent packet replication/injection attack.
 * 
 */
void
set_timestamp(unsigned char *block)
{
      int i;
      for(i=0;i<8;i++){
            block[i]=(unsigned char)(mycounter>>(i*8))&(0x00000000000000FF);
      }
      mycounter++;
      
            
}


/*
 * Send an udp datagram to specified peer.
 */
void
send_udp (char *data, size_t len, struct peer *p, unsigned char flags)
{
              
      unsigned char outpkt[MAXPKT];
      unsigned char *outbuf=outpkt+1;
      int olen,tlen;
      struct sockaddr_in *destination=&(p->in_a);
      if(flags==CMD_CHALLENGE || flags==CMD_LOGIN || flags==CMD_DENY || flags==CMD_AUTH_OK || flags==CMD_IDENTIFY){
            memcpy(outbuf,data,len);
            olen=len;
      }else{
            if(flags==PKT_DATA||flags==CMD_HANDOVER){
                  set_timestamp(data+len);
                  len+=8;
                  
                  memcpy(data+len,crc32(data,len),4);
                  len+=4;
                  
            }
            if(flags==CMD_HANDOVER){

                  destination=&(p->handover_a);
            }
            
            EVP_EncryptInit (&ctx, EVP_bf_cbc (), p->key, p->iv);
              if (EVP_EncryptUpdate (&ctx, outbuf, &olen, data, len) != 1)
                {
                      fprintf (stderr,"error in encrypt update\n");
                      return;
                }
      
             
              if (EVP_EncryptFinal (&ctx, outbuf + olen, &tlen) != 1)
                {
                      fprintf (stderr,"error in encrypt final\n");
                      return;
                }
            olen += tlen;
      }
      
      outpkt[0]=flags;
      sendto(nfd, outpkt, olen+1, 0, (struct sockaddr *) destination,
            sizeof(struct sockaddr_in));
}

/*
 * Generate a new blowfish key, store it in a local file and fill the fields
 * of peer structure.
 * Client only.
 */
struct peer
*generate_key (struct peer *ret)
{
      int i, fd=-1, od=-1, createnow=0;
      unsigned char key[16];
      unsigned char iv[8];
      unsigned char c;
      
      if(!ret){
            ret=malloc(sizeof(struct peer));
            bzero(ret,sizeof(struct peer));
            createnow=1;
      }
      if ( ((fd = open ("/dev/random", O_RDONLY)) == -1)||
                   ((read (fd, key, 16)) == -1) ||
                   ((read (fd, iv, 8)) == -1) )
      {

            perror ("Error Creating key.\n");
            goto failure;
      }
      
      //fprintf(stderr,"128 bit key stored.\n");

      //fprintf(stderr,"64 bit Initialization vector stored.\n");
      
      for(i=0; i<FILENAMESIZE-1;i++){
            read(fd,&c,1);
            c=(c%25);
            //fprintf(stderr,"c=%u\n",c);
            ret->id[i]=(char)('a' + c);
      }
      ret->id[FILENAMESIZE-1]='\0';
      
      close (fd);
      
      if ((od = creat ("/tmp/.blowfish.key",0600)) == -1){
            perror ("blowfish.key creat error");
            goto failure;
      }
      memcpy(ret->key,key,16);
      memcpy(ret->iv,iv,8);
      write(od,key,16);
      write(od,iv,8);
      close (od);
      return ret;
      
failure:
      if (createnow)
            free(ret);
      if (fd != -1)
            close(fd);
      if (od != -1)
            close(od);
      return NULL;
}


/*
 * Send a "Challenge" 4WHS packet.
 */
static void
send_challenge(struct peer *p)
{
      int fd;
      if ( ((fd = open ("/dev/random", O_RDONLY)) == -1)||
                   ((read (fd, p->challenge, 128)) != -1))
      {     
            send_udp(p->challenge,128,p,PKT_CTL|CMD_CHALLENGE);
      }           
      p->state=ST_CHALLENGE;
      close(fd);
}

/*
 * Send a "Auth OK" 4WHS packet.
 */
static void
send_auth_ok(struct peer *p, void (*callback)(struct peer*))
{
      send_udp(NULL,0,p,CMD_AUTH_OK);
      p->state=ST_AUTH;
      callback(p);
      set_expire(p);
}

/*
 * Receive a challenge. Try to send response encrypted with local blowfish key.
 */
void 
rcv_challenge(struct datagram *pkt, struct peer *p)
{
      send_udp(pkt->data+1,pkt->len-1,p,CMD_RESPONSE);
      p->state=ST_WAIT_AUTH;
}


/*
 * Receive a login request. Send challenge.
 */
void
rcv_login(struct datagram *pkt, struct peer *p)
{
      int fd;
      char filename[128];
      snprintf(filename,127,"/tmp/.%s.key\0",pkt->data+1);
//    fprintf(stderr,"Filename:%s\n",filename);
      if (((fd = open (filename, O_RDONLY)) == -1)||
                  ((read (fd, p->key, 16)) == -1) ||
                  ((read (fd, p->iv, 8)) == -1) ){
            perror ("blowfish.key open error");
            deny_access(p);
            return;
      }
      close(fd);
      memcpy(p->id,pkt->data+1,FILENAMESIZE);
      send_challenge(p);

}

/*
 * Receive a response from challenge. Validate encryption and send "ok auth"
 * or "access denied"
 */
void
rcv_response(struct datagram *pkt, struct peer *p, void (*callback)(struct peer*))
{
      unsigned char response[MAXPKT];
      int rlen, tlen;
      
      EVP_DecryptInit (&ctx, EVP_bf_cbc (), p->key, p->iv);
       
      if (EVP_DecryptUpdate (&ctx, response, &rlen, pkt->data+1, pkt->len-1) != 1)
        {
            fprintf (stderr,"error in decrypt update\n");
            return;
        }

        if (EVP_DecryptFinal (&ctx, response + rlen, &tlen) != 1)
        {
            fprintf (stderr,"error in decrypt final\n");
            return;
        }

        if (strncmp(response,p->challenge,128)==0){
              p->state=ST_AUTH;
              send_auth_ok(p, callback);
        }
              
        else{
              p->state=ST_CLOSED;
              deny_access(p);
        }

}

/*
 * Send a login packet. This is the first phase of 4WHS
 */
void
blowfish_login(struct peer *p)
{
      send_udp(p->id,FILENAMESIZE,p,CMD_LOGIN);
}

/*
 * Initialize blowfish module.
 * Set udp socket and initialize crypto engine & CRC32.
 */
void
blowfish_init(int socketfd)
{
      nfd=socketfd;
      EVP_CIPHER_CTX_init (&ctx);   
      chksum_crc32gentab ();
}



Generated by  Doxygen 1.6.0   Back to index