1

I've develepted some windows server side socket in c and want to add sni server name indication to support sni

but servername callback never called and cant get servername is there any suggestion?

I've defined some functions for initializing ssl before main function:

void init_openssl()
{
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

SSL_CTX *create_context()
{
    const SSL_METHOD *method;
    SSL_CTX *ctx;

    //method = SSLv23_server_method();
    method = TLSv1_1_server_method();

    ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    return ctx;
}

void configure_context(SSL_CTX *ctx)
{

    SSL_CTX_set_ecdh_auto(ctx, 1);

    /* Set the key and cert */
    if (SSL_CTX_use_certificate_file(ctx, "somesite.cer", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);

        exit(EXIT_FAILURE);
    }

    if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);

        exit(EXIT_FAILURE);
    }
}

static int ssl_servername_cb(SSL *ssl, int *ad, void *arg)
{
    if (ssl == NULL)
        return SSL_TLSEXT_ERR_NOACK;

    const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
    printf("ServerName: %s\n", servername);
}

and here is my main function:

int main()
{

SSL_CTX *ctx;

    init_openssl();
    ctx = create_context();

    configure_context(ctx);

    SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);



    WSADATA wsaData;
    int iResult;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET ClientSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    //iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    iResult = getaddrinfo("192.168.200.1", DEFAULT_PORT, &hints, &result);
    //iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);

    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Create a SOCKET for connecting to server 
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    // Setup the TCP listening socket   
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Accept a client socket       
    ClientSocket = accept(ListenSocket, NULL, NULL);    
    SSL *ssl;

    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // No longer need server socket
    closesocket(ListenSocket);

    // Receive until the peer shuts down the connection
    do {

        ssl = SSL_new(ctx);
    SSL_set_fd(ssl, ClientSocket);
    //added this line for handling server name
    SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
    int rr = SSL_accept(ssl);


    iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);

    //int type=SSL_get_servername_type(ssl);
    //const char[] x=SSL_get_servername(ssl, type);
    const char *zx= SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);

        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            // Echo the buffer back to the sender
            iSendResult = send(ClientSocket, recvbuf, iResult, 0);
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else {
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }

    } while (iResult > 0);

return 0;
}

In do while function I've tried to call

const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);

but didn't find anything. I know that clienthello handshake is in first bytes that is received from socket.

iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);

And I receive 219 bytes of data. I need to handle servername and find it, and load certificate file and do continuous job, and maybe after that keep going with higher level in c# sslstream class. Any suggestion?

Reza Akraminejad
  • 1,412
  • 3
  • 24
  • 38
  • 1
    `// No longer need server socket closesocket(ListenSocket);` and then `SSL_set_fd(ssl, ListenSocket);`?!? How well do you think the SSL negotiation is going to work if you tell it to use the *closed* socket used for `listen()`? – Andrew Henle Jun 19 '19 at 12:34
  • @AndrewHenle Thanks, and it received, but added some code and edited but could not get servername yet – Reza Akraminejad Jun 19 '19 at 12:41
  • @AndrewHenle *zx still null. have any idea? – Reza Akraminejad Jun 19 '19 at 12:45
  • Why are you calling `recv()` and `send()` directly? That just gets you the raw bytes sent across the network and doesn't use SSL at all. – Andrew Henle Jun 19 '19 at 13:07
  • @AndrewHenle sorry for late answer. Do you have any suggestion for using ssl library for receiving ssl content and diagnose servername (SNI) and to other staff like other webservers and response to requests? – Reza Akraminejad Jun 19 '19 at 19:18
  • @AndrewHenle I've finally changed my code and callback worked, I've edit it above. and doing other staff to see if I could handle multiple domains in same IP and respond to them – Reza Akraminejad Jun 20 '19 at 05:56
  • @AndrewHenle I've edited my code. 2 questions: **1)** Is it ok in every loop call `ssl = SSL_new(ctx); SSL_set_fd(ssl, ClientSocket); //added this line for handling server name SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);` or out of loop? **2)** does `iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);` needed for rest of job or just process request and socket write? – Reza Akraminejad Jun 20 '19 at 06:02
  • @AndrewHenle Do you have any suggestion for continuous handshake and doing other staff? – Reza Akraminejad Jun 22 '19 at 05:38
  • 1
    You can find one example some a simple server here: https://wiki.openssl.org/index.php/Simple_TLS_Server You can also look in the OpenSSL source for the `s_server` application. – Andrew Henle Jun 22 '19 at 12:14
  • @AndrewHenle `do { ssl = SSL_new(ctx); SSL_set_fd(ssl, ClientSocket); int rr = SSL_accept(ssl);const char *senddata = "HTTP/1.1 200 Ok\r\nServer: BasisCore 1.0.1\r\nContent-Type: text/html\r\n" "Content-Length : 20\r\n" "Vary: Accept-Encoding\r\n" "Hello from server ;)\r\n\r\n"; iSendResult=SSL_write(ssl, senddata, strlen(senddata)); //iSendResult = send(ClientSocket, senddata, (int)strlen(senddata), 0); iResult = 0; SSL_free(ssl); closesocket(ClientSocket); WSACleanup();} while (iResult > 0);` – Reza Akraminejad Jun 23 '19 at 10:04
  • @AndrewHenle I've change the while loop here for sending http response to client. but setting breakpoint on line `*senddata = "HTTP/1.1 200 Ok\r\nServer: ...` I see that in firefox, client socket is closed and firefox is not waiting for response. and I see this error: SSL_ERROR_NO_CYPHER_OVERLAP authenticity of data received data could not be verified – Reza Akraminejad Jun 23 '19 at 10:08
  • @AndrewHenle My question is should I do all handshake process in server side by my own code like explained here [link]http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session[link] and there is no higher level methods in Openssl to do that? – Reza Akraminejad Jun 23 '19 at 12:22

0 Answers0