📄 sslsniffer.c
字号:
/* SSL Sniffer V1.21. ---------------------------------------------- Written by: Eu-Jin Goh (eujin@cs.stanford.edu) Stanford University October 2000 Copyright (C) 2000 Eu-Jin Goh This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------- See the README for program notes.*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/errno.h>#include <sys/select.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <openssl/x509.h>#include <openssl/ssl.h>#include <openssl/bio.h>#include <openssl/asn1.h>#include "sslsniffer.h"#include "general_utilities.h"#include "net_utilities.h"#ifndef INADDR_NONE#define INADDR_NONE 0xffffffff /* should be in <netinet/in.h> */#endif/* function declarations */static int PopulateArgvParams(argv_params *argv_p, int argc, char **argv);static int InitSSLSniffer(struct sockaddr_in *servAddr, argv_params *argv_p);static int WaitForConnection(int listenfd, argv_params *argv_p);static int WaitForClient(struct sockaddr_in *fromAddr, int listenfd);static int ParseCONNECT(char *buffer, struct sockaddr_in *toAddr, int client_fd);static void DoOneSSLConnection(int client_fd, int server_fd);static void Setfds(fd_set *fds, ssl_connection *ssl_conn);static int ClientHelloRead(ssl_connection *ssl_conn);static int ReadOneTLSRecord(ssl_connection *ssl_conn);static int ReadOneSSL2Record(ssl_connection *ssl_conn);static int ServerRead(ssl_connection *ssl_conn);static int ClientRead(ssl_connection *ssl_conn);/* before calling this function, the record len of the ssl_conn should be the total num of bytes to be read including the previously read bytes*/static int ReadRecordData(ssl_connection *ssl_conn, char *prev_read_buf, int num_prev_read_bytes);/* SSL3 / TLS */static int ProcessTLSPacketType(ssl_connection *ssl_conn);static void ProcessTLSAlertMessage(ssl_connection *ssl_conn);static void ProcessTLSHandshakeMessage(ssl_connection *ssl_conn);static void ProcessTLSClientHello(char *buffer);static char *ProcessTLSHello(char *buffer);static void ProcessServerHello(char *buffer, ssl_connection *ssl_conn);static void ProcessTLSCipherSuite(char byte1, char byte2);static void DetermineTLSKeyExchangeAlgorithm(char byte1, ssl_connection *ssl_conn);static void ProcessCertificateChain(char *buffer, ssl_connection *ssl_conn);static void ProcessCertificate(X509 *x);static void PrintCertificateInfo(UTL_CERT_INFO *buf, EVP_PKEY *key);static void ProcessCertificateRequest(char *buffer, ssl_connection *ssl_conn);static void ProcessTLSServerKeyExchange(char *buffer, ssl_connection *ssl_conn);static char *ExtractParams(char *params, char *type);static void ProcessTLSClientKeyExchange(char *buffer, ssl_connection *ssl_conn);/* SSL2 */static int ProcessOneSSL2Record(ssl_connection *ssl_conn);static void ProcessSSLV2ClientMasterKey(ssl_connection *ssl_conn);static void ProcessSSLV2OneCipherSuite(char byte1, char byte2, char byte3);static void ProcessSSLV2ClientHello(ssl_connection *ssl_conn);static void ProcessSSLV2CipherSuiteData(char *cipher_suite_data, unsigned short data_len);static int IsV2ClientHello(char *record_hdr);static void ProcessSSLV2ServerHello(ssl_connection *ssl_conn);static void ProcessSSLV2Error(ssl_connection *ssl_conn);#if 0static void ProcessSSLV2ClientFinished(ssl_connection *ssl_conn);static void ProcessSSLV2ClientCertificate(ssl_connection *ssl_conn);#endif/* Utility functions */void CloseSocket(int sock);short TwoBytesToInt(char *buffer);unsigned int ThreeBytesToInt(char *buffer);/* main program */intmain (int argc, char **argv){ struct sockaddr_in serv_addr; /* proxy server address */ int sock; /* our socket handler */ argv_params argv_p; /* get all the params first */ if(PopulateArgvParams(&argv_p, argc, argv) < 0) { return FAILURE; } sock = InitSSLSniffer(&serv_addr, &argv_p); /* loop till a client connects successfully */ while(1) { if(WaitForConnection(sock, &argv_p) < 0) { printf("\n--------------------------------------------------------\n\n"); } } CloseSocket(sock); return SUCCESS;}static int PopulateArgvParams(argv_params *argv_p, int argc, char **argv){ int num_args = argc - 1; char **cur_argv = &(argv[1]); int change; /* set it to the default port first */ argv_p->local_port = (short) DEFAULT_PORT; argv_p->proxy = 1; /* iterate over the arguments, setting the appropriate fields */ for(num_args = argc - 1; num_args > 0; ) { if(strcmp(cur_argv[0], SNIFFER_ARGV_PORT) == 0) { /* next argv should be the the port */ if(num_args < 2) { goto error; } argv_p->local_port = (short) atoi(cur_argv[1]); change = 2; } else if(strcmp(cur_argv[0], SNIFFER_ARGV_NO_PROXY) == 0) { /* need to specify the hostname and remote port */ if(num_args < 3) { goto error; } argv_p->remote_port = (short) atoi(cur_argv[1]); argv_p->remote_host_name_or_ip = cur_argv[2]; argv_p->proxy = 0; change = 3; } else { goto error; } num_args -= change; cur_argv += change; } return 1; error: printf("Usage: sslsniffer [-p <local port>] [-np <remote port> <remote hostname/ip>]\n"); return -1;}/* Initializes the server socket. The socket is set to be reusable. If no port number is specified, the default one is used. returns the socket number that it is listening to.*/static int InitSSLSniffer(struct sockaddr_in *serv_addr, argv_params *argv_p){ int listen_fd; /* first set up the listening socket */ listen_fd = utlnet_CreateIPV4StreamSocket(); utlnet_InitIPV4ServerSockAddrStruct(serv_addr, argv_p->local_port, INADDR_ANY); (void) utlnet_InitIPV4ServerSocket(listen_fd, serv_addr, 5, REUSABLE); /* print credits! */ printf("\nSSLV3/TLS Sniffer 1.1 written by Eu-Jin Goh\n" "Stanford University Applied Crypto Group\n"); printf("\nSSL Sniffer listening on port number %d\n", argv_p->local_port); /* print out an extra line of info for no proxy connections */ if(argv_p->proxy) { printf("\n"); } else { printf("Will connect incoming connections to %s on port %d\n\n", argv_p->remote_host_name_or_ip, argv_p->remote_port); } return listen_fd;}/* waits till a client connects to the proxy server. It first tries to read from the client socket and parse the information assuming that the first request is a CONNECT command. It then uses the information contained in the request to connect to the server and if successful, replies to the client with a 200 status code. Otherwise, it replies with a 400 if the request is not the CONNECT command in the proper format and a 404 if it cannot connect to the dest. returns 0 or 1 depending on whether the client connected successfully.*/static intWaitForConnection(int listen_fd, argv_params *argv_p){ int bytes_read=0; int client_fd; /* socket for the client */ int server_fd = -1; struct sockaddr_in from_addr; struct sockaddr_in to_addr; char buffer[BUFFER_SIZE]; /* blocks till a client tries to connect to the proxy server */ client_fd = WaitForClient(&from_addr, listen_fd); /* if the no_proxy flag was not given, we assume a CONNECT will be sent */ if(argv_p->proxy) { /* read and print from client socket */ if((bytes_read = utlnet_ReadFromSocket(client_fd, buffer, BUFFER_SIZE)) < 0) { goto error; } buffer[bytes_read] = '\0'; /* null terminate the string */ printf("\nRead %d bytes from CONNECT request:\n%s", bytes_read, buffer); /* obtain dest address and port */ if(ParseCONNECT(buffer, &to_addr, client_fd) < 0) { goto error; } } else /* otherwise, this is a direct connection */ { if(utlnet_InitIPV4ClientSockAddrStruct(&to_addr, argv_p->remote_port, argv_p->remote_host_name_or_ip) < 0) { goto error; } } /* connect to destination */ server_fd = utlnet_CreateIPV4StreamSocket(); if(utlnet_IPV4Connect(server_fd, &to_addr) < 0) { perror("WaitForConnection"); /* only send back a 404 if this is a proxied connection */ if(argv_p->proxy) { sprintf(buffer,"HTTP/1.0 404 Unable to Connect\r\n\r\n"); printf("Writing to socket: %s",buffer); (void) utlnet_WriteToSocket(client_fd, buffer, strlen(buffer)); } goto error; } /* once connected, reply to client that connection is established. only send it back if this is a proxied connection */ if(argv_p->proxy) { sprintf(buffer,"HTTP/1.0 200 Connection Established\r\n\r\n"); printf("Sending back to client: %s",buffer); if(utlnet_WriteToSocket(client_fd, buffer, strlen(buffer)) < 0) { goto error; } } /* handles this TLS Connection */ DoOneSSLConnection(client_fd, server_fd); return 1; error: CloseSocket(client_fd); if(server_fd >= 0) { CloseSocket(server_fd); } return -1;}/* accept blocks till a client tries to connect to the proxy server. it then prints out where the connection is originating from (hostname and port) returns the socket descriptor for the connection.*/static int WaitForClient(struct sockaddr_in *from_addr, int listen_fd){ int conn_fd; struct hostent *name; conn_fd = utlnet_Accept(listen_fd, from_addr); /* print out where connection originates from */ name = utlnet_GetHostName(from_addr); if(name != NULL) { printf("--------------------------------------------------------\n"); printf("Received connection from %s, port %d\n", name->h_name,ntohs(from_addr->sin_port)); } else { printf("Cannot resolve IP of incoming connection\n"); } return conn_fd;}/* Parses the first message that is sent to the proxy server. It is assumed to be in the following format: CONNECT IPADDRESS:PORT extracts the IPADDRESS and stores it in the sockaddr_in as a binary address, together with the port number. returns 1 or 0 depending on whether it was successful in parsing. */intParseCONNECT(char *buffer, struct sockaddr_in *to_addr, int client_fd){ char *start,*end; char *host_name = NULL; char *port = NULL; char temp[BUFFER_SIZE]; int return_value = 1; start = strchr(buffer, ' '); /* location of first space */ end = strchr(buffer, ':'); /* location of ':' */ /* check that the connect request conforms to the expected spec */ if(start == NULL || end == NULL) { sprintf(temp,"HTTP/1.0 400 Bad Request\r\n\r\n"); printf("Sending to client: %s", temp); utlnet_WriteToSocket(client_fd, temp, strlen(temp)); return_value = -1; goto error; } /* obtain the host_name */ host_name = (char *) utl_GetMem((end - start) / sizeof(char)); ++start; /* makes it point to the first char */ strncpy(host_name, start, end-start); host_name[end-start] = '\0'; /* obtain the port number */ start = strchr(end, ' '); port = (char *) utl_GetMem((start - end) / sizeof(char)); ++end; strncpy(port, end, start - end); port[start-end] = '\0'; printf("Destination hostname is %s, port is %s\n", host_name, port); /* initialize fields in sockaddr_in */ if(utlnet_InitIPV4ClientSockAddrStruct(to_addr, atoi(port), host_name) < 0) { return_value = -1; goto error; } error: free(host_name); free(port); return return_value;}/* Performs the proxy operations for one TLS connection. Sits in the middle the connection and transfers the data from client to server and vice versa. Processes the data that the client and server send to each other to determine at which stage of the TLS protocol the connection is in. the ssl_conn is always set to assume a client to server connection*/static void DoOneSSLConnection(int client_fd, int server_fd){ int bytes_read = 0; fd_set fds; ssl_connection ssl_conn; /* initialize the ssl_conn to default values for TLS default is doing a client to server connection */ ssl_conn.client_fd = client_fd; ssl_conn.server_fd = server_fd; ssl_conn.read_fd = ssl_conn.client_fd; ssl_conn.write_fd = ssl_conn.server_fd; ssl_conn.recv_client_hello = 0; ssl_conn.recv_server_hello = 0; ssl_conn.recv_change_cipher[SERVER_RECV_CHANGE_CIPHER] = 0; ssl_conn.recv_change_cipher[CLIENT_RECV_CHANGE_CIPHER] = 0; ssl_conn.recv_change = &(ssl_conn.recv_change_cipher[CLIENT_RECV_CHANGE_CIPHER]); /* these are the default values for SSL2 */ ssl_conn.ssl2_packets_encrypted = 0; /* sits in this while loop transferring and processing data till one of the sockets close */ while(true) { Setfds(&fds, &ssl_conn); switch(select( MAX(ssl_conn.client_fd, ssl_conn.server_fd) + 1, &fds, NULL, NULL, NULL)) { case -1: /* select error */ perror("DoOneSSLConnection"); return; default: /* figure out which socket has stuff to read and take the appropriate action */ if(FD_ISSET(ssl_conn.server_fd, &fds)) { printf("\n\nReading from SERVER socket\n");; bytes_read = ServerRead(&ssl_conn); } else if(FD_ISSET(ssl_conn.client_fd, &fds)) { printf("\n\nReading from CLIENT socket\n"); /* client hellos are handled differently, we want to pull off the protocol versions and stuff like that here */ if(!ssl_conn.recv_client_hello) { bytes_read = ClientHelloRead(&ssl_conn); } else { bytes_read = ClientRead(&ssl_conn); } } } /* either error or no bytes read */ if(bytes_read <= 0) { printf("\nClose connections\n\n"); CloseSocket(ssl_conn.server_fd); CloseSocket(ssl_conn.client_fd); break; } }}/* Sets the fd_set for use in the select function*/static void Setfds(fd_set *fds, ssl_connection *ssl_conn){ FD_ZERO(fds); FD_SET(ssl_conn->server_fd, fds); FD_SET(ssl_conn->client_fd, fds);}/* Handles client hellos. This needs to be separated out from the normal processing because of the V23 client hellos that get sent. We figure out whether it's a V2 or V3 connection here. It also writes the entire packet to the hello socket when it is done processing it. returns the number of bytes written to the server.*/static intClientHelloRead(ssl_connection *ssl_conn){ char record_hdr_buf[TLS_RECORD_HEADER_SIZE]; int bytes_read = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -