Exemple #1
0
int redisFreeKeepFd(redisContext *c) {
	int fd = c->fd;
	cleanupSSL( &(c->ssl) );
	c->fd = -1;
	redisFree(c);
	return fd;
}
Exemple #2
0
void redisFree(redisContext *c) {
    cleanupSSL( &(c->ssl) );
    if (c->fd > 0)
        close(c->fd);
    if (c->obuf != NULL)
        sdsfree(c->obuf);
    if (c->reader != NULL)
        redisReaderFree(c->reader);
    free(c);
}
int main(int argc, char *argv[]) {
  
	int tap_fd, option;
	int flags = IFF_TUN;
	char if_name[IFNAMSIZ] = "";
	int header_len = IP_HDR_LEN;
	int maxfd;
	uint16_t nread, nwrite, plength;
	char buffer[BUFSIZE];
	struct sockaddr_in local, remote;
	char remote_ip[16] = "";
	unsigned short int port = PORT;
	int sock_fd, net_fd, optval = 1;
	socklen_t remotelen;
	int cliserv = -1;    /* must be specified on cmd line */
	unsigned long int tap2net = 0, net2tap = 0;

	int listen_sd;
  	int sd;
  	struct sockaddr_in sa_serv;
	struct sockaddr_in sa_cli;
	size_t client_len;
	int err;
	int yes = 1;

	struct ip *ip_header;            //ip header struct
	struct VPNDataHdr* vpn_header;

	int ptc[2];         //pipe from control process to tunnel process

	progname = argv[0];
  
  /* Check command line options */
  while((option = getopt(argc, argv, "i:sc:p:uahd")) > 0){
    switch(option) {
      case 'd':
        debug = 1;
        break;
      case 'h':
        usage();
        break;
      case 'i':
        strncpy(if_name,optarg,IFNAMSIZ-1);
        break;
      case 's':
        cliserv = SERVER;
        break;
      case 'c':
        cliserv = CLIENT;
        strncpy(remote_ip,optarg,15);
        break;
      case 'p':
        port = atoi(optarg);
        break;
      case 'u':
        flags = IFF_TUN;
        break;
      case 'a':
        flags = IFF_TAP;
        header_len = ETH_HDR_LEN;
        break;
      default:
        my_err("Unknown option %c\n", option);
        usage();
    }
  }

  argv += optind;
  argc -= optind;

  if(argc > 0){
    my_err("Too many options!\n");
    usage();
  }

  if(*if_name == '\0'){
    my_err("Must specify interface name!\n");
    usage();
  }else if(cliserv < 0){
    my_err("Must specify client or server mode!\n");
    usage();
  }else if((cliserv == CLIENT)&&(*remote_ip == '\0')){
    my_err("Must specify server address!\n");
    usage();
  }

  /* initialize tun/tap interface */
  if ( (tap_fd = tun_alloc(if_name, flags | IFF_NO_PI)) < 0 ) {
    my_err("Error connecting to tun/tap interface %s!\n", if_name);
    exit(1);
  }

  do_debug("Successfully connected to interface %s\n", if_name);
  




 
  	if ( (sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    	 perror("socket()");
   	 exit(1);
 	 }

	/////////////////////////////////////////////////////////


    	memset(&local, 0, sizeof(local));
    	local.sin_family = AF_INET;
    	local.sin_addr.s_addr = htonl(INADDR_ANY);
    	local.sin_port = htons(port);
    	if (bind(sock_fd, (struct sockaddr*) &local, sizeof(local)) < 0){
      	perror("bind()");
      	exit(1);
    	}

	//init remote
	memset(&remote, 0, sizeof(remote));
	remote.sin_family = AF_INET;
	//remote.sin_addr.s_addr = inet_addr(remote_ip);
  	remote.sin_port = htons(port);	



	net_fd = sock_fd;

    	do_debug("SERVER: UDP built\n");
	//////////////////////////////////////////////////////////////////////

///////////ssl tcp control connection////////////////
	loadcert();
	listen_sd = socket (AF_INET, SOCK_STREAM, 0);   
	if(err < 0){
		printf("err tcp listening sock!\n");
		return 0;
	}
   
	if(setsockopt(listen_sd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1){
		perror("Server-socket() error lol!");
		exit(1);
	}  


	memset (&sa_serv, '\0', sizeof(sa_serv));
	sa_serv.sin_family      = AF_INET;
	sa_serv.sin_addr.s_addr = INADDR_ANY;
	sa_serv.sin_port        = htons (TCP_LISTEN_PORT);          /* Server Port number */
  
	err = bind(listen_sd, (struct sockaddr*) &sa_serv,
	sizeof (sa_serv));
	if(err < 0){
		printf("err tcp bind!\n");
		return 0;
	}
	     
  /* Receive a TCP connection. */
	     
	err = listen (listen_sd, 5);
	do_debug("Server start Listen OK\n");                    


	if(err < 0){
		printf("err tcp listening!\n");
		return 0;
	}
  /////

////////////////////////////////////////////////////

  
  /* use select() to handle two descriptors at once */
  maxfd = (tap_fd > net_fd)?tap_fd:net_fd;
  maxfd = (maxfd > listen_sd)?maxfd:listen_sd;

  while(1) {
    int ret;
    fd_set rd_set;

	struct Session *cursess;
	cursess= slisthead;

    FD_ZERO(&rd_set);
    FD_SET(tap_fd, &rd_set); FD_SET(net_fd, &rd_set);
	FD_SET(0, &rd_set);
	FD_SET(listen_sd,&rd_set);

	while(cursess != 0){
		FD_SET(cursess->sd,&rd_set);
		maxfd = (maxfd > cursess->sd)?maxfd:cursess->sd;
		cursess = cursess->next;
	}

	cursess = slisthead;

    ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL);

    if (ret < 0 && errno == EINTR){
      continue;
    }

    if (ret < 0) {
      perror("select()");
      exit(1);
    }




	if(FD_ISSET(0, &rd_set)){
		memset(buffer,0,BUFSIZE);
		nread = read(0, buffer, BUFSIZE);
		if(0 == strncmp(buffer,"quit",strlen("quit")))
			break;

	}

	if(FD_ISSET(listen_sd, &rd_set)){
		client_len = sizeof(sa_cli);
		sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
		if(err < 0){
			printf("err accept!\n");
			return 0;
		}
		err = sslinit(sd, &slisthead, &sa_cli, "vpnauth");
		if(err)
			do_debug("Client from %s is connected\n", inet_ntoa(sa_cli.sin_addr));
		//continue;

	}


	if(FD_ISSET(tap_fd, &rd_set)){
      /* data from tun/tap: just read it and write it to the network */
		unsigned char outbuffer[VPN_BUFSIZE];
		struct VPNDataHdr *pvpnh = (struct VPNDataHdr*) outbuffer;
		unsigned long curip;
		cursess = 0;
		nread = cread(tap_fd, buffer, BUFSIZE);
		if(nread <= 0)
			continue;
		
		tap2net++;
		do_debug("TAP2NET %lu: Read %d bytes from the tap interface\n", tap2net, nread);

		ip_header = (struct ip*) buffer;
		memcpy(&curip,&(ip_header->ip_dst),sizeof(long));
		cursess = findsessByDstIP(slisthead,curip);
		//memcpy(&tpaddr,&(destsess->ip),sizeof(unsigned long));
		if(cursess == 0)
			continue;
		remote.sin_addr.s_addr = cursess->ip;
		

      /* write length + packet */

		if(32 != getHMACSHA256(buffer, pvpnh->hmac, nread,cursess->s_key,KEY_LENGTH))
			continue;
		nwrite = crypt(buffer,nread,outbuffer + sizeof(struct VPNDataHdr),cursess->s_key,cursess->iv,1);
		if(0 == nwrite)
			continue;
		pvpnh->plen = htons(nwrite);
		sendto(net_fd,outbuffer, nwrite + sizeof(struct VPNDataHdr),0,(struct sockaddr*) &remote, sizeof(remote));
	
		do_debug("TAP2NET %lu: Written %d bytes to the network %s\n", tap2net,nwrite,inet_ntoa(remote.sin_addr));
		//continue;
    }

    if(FD_ISSET(net_fd, &rd_set)){
      /* data from the network: read it, and write it to the tun/tap interface. 
       * We need to read the length first, and then the packet */
		unsigned char inbuffer[VPN_BUFSIZE];
		unsigned char hmac[32];
		int nwrite;
		struct VPNDataHdr *pvpnh = (struct VPNDataHdr*) inbuffer;
		struct sockaddr_in sa;
		socklen_t fromlen;
		fromlen =sizeof(sa);

		nread = recvfrom(net_fd,inbuffer,VPN_BUFSIZE,0,(struct sockaddr*)&sa,&fromlen);

		if(nread <= 0) {
	        continue;
	      }

		cursess = findsessByVPNHeader(slisthead,pvpnh,sa.sin_addr.s_addr);
		if(cursess == 0)
			continue;
			
		nwrite = crypt(inbuffer + sizeof(struct VPNDataHdr),htons(pvpnh->plen),buffer,cursess->s_key,cursess->iv,0);
		if(0 == nwrite)
			continue;
	
		if(32 != getHMACSHA256(buffer, hmac, nwrite,cursess->s_key,KEY_LENGTH) 
		|| !isequal(hmac,pvpnh->hmac,HMAC_LENGTH))
			continue;
		net2tap++;
		/* read packet */
		do_debug("NET2TAP %lu: Read %d bytes from the network\n", net2tap, nread);
		
		/* now buffer[] contains a full packet or frame, write it into the tun/tap interface */ 
		nwrite = cwrite(tap_fd, buffer, nwrite);
		do_debug("NET2TAP %lu: Written %d bytes to the tap interface\n", net2tap, nwrite);
		//continue;
    }

	cursess = slisthead;
	while(cursess != 0){
		if(FD_ISSET(cursess->sd, &rd_set)){
			struct CliRequest* rst;
			memset(buffer,0,BUFSIZE);
			nread = ssl_read(cursess->ssl,buffer,BUFSIZE);
			rst = (struct CliRequest*)buffer;
			if(nread != sizeof(struct CliRequest)){
				break;
			}
			if(nread == 0 || rst->act == QUIT_REQUEST){
				struct in_addr temp;
				temp.s_addr = cursess->ip;
				do_debug("Client from %s break the connection\n", inet_ntoa(temp));
				cleanupSSL(cursess);
				deletesess(&slisthead,cursess);
				//slisthead = 0;
			}

			if(rst->act == IV_REFRESH_REQUEST){
				RefreshIV(cursess);
				do_debug("IV is refreshed:");
				debugSession(cursess);
			}
			if(rst->act == IV_REFRESH_SET){
				struct ServResponse rsp;
				rsp.act = ACT_ACK;
				if(-1 != ssl_write(cursess->ssl,(char*)&rsp, sizeof(struct ServResponse))){
					memcpy(cursess->iv, rst->iv,KEY_LENGTH);
					do_debug("IV is updated:");
					debugSession(cursess);
				}
			}
			if(rst->act == KEY_REFRESH_REQUEST){
				RefreshKEY(cursess);
				do_debug("Key is refreshed:");
				debugSession(cursess);
			}
			if(rst->act == KEY_REFRESH_SET){
				struct ServResponse rsp;
				rsp.act = ACT_ACK;
				if(-1 != ssl_write(cursess->ssl,(char*)&rsp, sizeof(struct ServResponse))){
					memcpy(cursess->s_key, rst->s_key,KEY_LENGTH);
					do_debug("Key is updated:");
					debugSession(cursess);
				}
			}
			
		}
		cursess = cursess->next;
	}


	
  }

	if(slisthead != 0){
		struct Session* tsess = slisthead;
		while(tsess != 0){
			cleanupSSL(tsess);
			tsess = tsess->next;
		}
	}
	freesess(slisthead);
	close(listen_sd);
	close(net_fd);
	close(tap_fd);
	cleanupCTX();
  	
  return(0);
}
Exemple #4
0
int redisContextConnectSSL(redisContext *c, const char *addr, int port, char* certfile, char* certdir, struct timeval *timeout) {
    struct timeval  start_time;
    int             has_timeout = 0;
    int             is_nonblocking = 0;

    c->ssl.sd = -1;
    c->ssl.ctx = NULL;
    c->ssl.ssl = NULL;
    c->ssl.bio = NULL;

    // Set up a SSL_CTX object, which will tell our BIO object how to do its work
    SSL_CTX* ctx = SSL_CTX_new(TLSv1_1_client_method());
    c->ssl.ctx = ctx;

    // Create a SSL object pointer, which our BIO object will provide.
    SSL* ssl;

    // Create our BIO object for SSL connections.
    BIO* bio = BIO_new_ssl_connect(ctx);
    c->ssl.bio = bio;

    // Failure?
    if (bio == NULL) {
        char errorbuf[1024];
        __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error creating BIO!\n");

        ERR_error_string(1024,errorbuf);
        __redisSetError(c,REDIS_ERR_OTHER,errorbuf);

        // We need to free up the SSL_CTX before we leave.
        cleanupSSL( &c->ssl );
        return REDIS_ERR;
    }

    // Makes ssl point to bio's SSL object.
    BIO_get_ssl(bio, &ssl);
    c->ssl.ssl = ssl;

    // Set the SSL to automatically retry on failure.
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

    char* connect_str = (char *)calloc( 1, strlen( addr ) + 10 );
    sprintf( connect_str, "%s:%d", addr, port );
    c->ssl.conn_str = connect_str;

    // We're connection to the REDIS server at host:port
    BIO_set_conn_hostname(bio, connect_str);

    SSL_CTX_load_verify_locations(ctx, certfile, certdir);

    // Are we supposed to be blocking or non-blocking?
    if( (c->flags & REDIS_BLOCK) == 0) {
        is_nonblocking = 1;
        BIO_set_nbio(bio,1);
    }

    // What about a timeout?
    // If we're blocking and have a timeout, we need to handle that specially
    if( NULL != timeout ) {
        BIO_set_nbio(bio,1);
        has_timeout = 1;
        gettimeofday(&start_time, NULL);
    }

    while(1) {
        struct timeval	cur_time,
                    elapsed_time;

        if (has_timeout) {
            gettimeofday(&cur_time, NULL);
            elapsed_time = subtractTimeval( cur_time, start_time );

            if (compareTimeval( elapsed_time, *timeout) > 0) {
                char errorbuf[1024];

                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out.");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        }

        // Same as before, try to connect.
        if (BIO_do_connect(bio) <= 0 ) {
            if( BIO_should_retry( bio ) ) {
                // We need to retry.
            } else {
                char errorbuf[1024];
                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Failed to connect");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        } else {
            // connect is done...
            break;
        }

        if( has_timeout ) {
            // Do select and seelct on it
            int result;
            int fd = BIO_get_fd( bio, NULL );
            struct timeval time_left;

            if (has_timeout) {
                time_left = subtractTimeval( *timeout, elapsed_time );
            }

            fd_set readset, writeset;
            FD_ZERO(&readset);
            FD_SET(fd, &readset);
            FD_ZERO(&writeset);
            FD_SET(fd, &writeset);

            do {
                result = select( fd+1, &readset, &writeset, NULL, &time_left);
            } while( result == -1 && errno == EINTR );
        }

    }

    while(1) {
        struct timeval	cur_time,
                    elapsed_time;

        if (has_timeout) {
            gettimeofday(&cur_time, NULL);
            elapsed_time = subtractTimeval( cur_time, start_time );

            if (compareTimeval( elapsed_time, *timeout) > 0) {
                char errorbuf[1024];

                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out.");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        }

        // Now we need to do the SSL handshake, so we can communicate.
        if (BIO_do_handshake(bio) <= 0) {
            if( BIO_should_retry( bio ) ) {
                // We need to retry.
            } else {
                char errorbuf[1024];
                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: handshake failure");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        } else {
            // handshake is done...
            break;
        }

        if( has_timeout ) {
            // Do select and seelct on it
            int result;
            int fd = BIO_get_fd( bio, NULL );
            struct timeval time_left;

            if (has_timeout) {
                time_left = subtractTimeval( *timeout, elapsed_time );
            }

            fd_set readset, writeset;
            FD_ZERO(&readset);
            FD_SET(fd, &readset);
            FD_ZERO(&writeset);
            FD_SET(fd, &writeset);

            do {
                result = select( fd+1, &readset, &writeset, NULL, &time_left);
            } while( result == -1 && errno == EINTR );
        }

    }

    long verify_result = SSL_get_verify_result(ssl);
    if( verify_result == X509_V_OK) {
        X509* peerCertificate = SSL_get_peer_certificate(ssl);

        char commonName [512];
        X509_NAME * name = X509_get_subject_name(peerCertificate);
        X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512);

        // TODO: Add a redis config parameter for the common name to check for.
        //       Since Redis connections are generally done with an IP address directly,
        //       we can't just assume that the name we get on the addr is an actual name.
        //
        //    if(strcasecmp(commonName, "BradBroerman") != 0) {
        //      __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error validating cert common name.\n\n" );
        //      cleanupSSL( &(c->ssl) );
        //      return REDIS_ERR;

    } else {
        char errorbuf[1024];
        __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error retrieving peer certificate.\n" );
        ERR_error_string(1024,errorbuf);
        __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
        cleanupSSL( &(c->ssl) );
        return REDIS_ERR;
    }

    BIO_set_nbio(bio,is_nonblocking);

    return REDIS_OK;
}