int redisFreeKeepFd(redisContext *c) { int fd = c->fd; cleanupSSL( &(c->ssl) ); c->fd = -1; redisFree(c); return fd; }
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); }
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; }