简体   繁体   中英

SSL_connect() and SSL_accept() calls

I have been struggling with this for a few days now figuring out how they work. I have read the documentation and looked at some examples but I am still in need of guidance.

Specifically, when the client calls connect() and successfully connects to the server host, should I issue the SSL_connect() right after it to Initiate the handshake ? The client then tries to write some bytes to the socket using SSL_write() .

On the other hand, the server uses pselect() to monitor any read fds ready for read and issues the accept() call successfully for the incoming connection. Should I issue the SSL_accept() call right after the accept() returns to complete the handshake ?

I have noticed that the SSL_connect() returns SSL_ERROR_WANT_READ (this is when the SSL_connect() is issued after a select() call to monitor the write fd set and returns and as per the Openssl documentation).

What is the right procedure here on issuing the calls and in what order ?

Edit to add snippet of code -

Client side :
          err = connect(fd, addr, addrlen);

          if ( err == -1 && errno == EINPROGRESS )
          {
              // check if this is a true error,
              // or wait until connect times out
              fd_set fdset;
              FD_ZERO(&fdset);
              FD_SET(sfd, &fdset);
              timeval tv =  {F_sockwaitconnect, 0};                                                   TEMP_FAILURE_RETRY(err = select(fd + 1,\
                                NULL,\
                                &fdset,\
                                NULL,\
                                &tv));

                  // what happened?
                  if ( err == 1 )
                  { 
                      connect was successful
                  }
                  else
                     return 0;

            const SSL_METHOD *method;
            SSL_CTX *cctx;
            SSL *cssl;

            FILE *fp;
            fp = stdout;

            ERR_clear_error();

            method = TLSv1_client_method(); 
            cctx = SSL_CTX_new(method);   

            if ( cctx == NULL )
            {
                ERR_print_errors_fp(stdout);
                return 0;
            }

         SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, NULL);
         SSL_CTX_set_verify_depth(cctx, 4);
         if (SSL_CTX_load_verify_locations(cctx, "mycert.pem", NULL) == 0)  
              return 0;                   

         SSL_CTX_set_options(cctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_COMPRESSION);

         ERR_clear_error();

         cssl = SSL_new(cctx);   /* create new SSL connection state */
         SSL_set_fd(cssl, fd);   * attach the socket descriptor */

         ERR_clear_error();
         int rconnect = SSL_ERROR_WANT_READ;
         while ( rconnect == SSL_ERROR_WANT_READ || rconnect == SSL_ERROR_WANT_WRITE )
         {
             char *buf = (char *) malloc(124);
             ERR_error_string(SSL_get_error(cssl, rconnect), buf);
             ERR_clear_error();
             if ( rconnect == SSL_ERROR_WANT_READ ) {    
                 int err = 0;
                 fd_set fdset;
                 FD_ZERO(&fdset);
                 FD_SET(fd, &fdset);
                 timeval tv =  {F_sockwaitconnect, 0};
                 TEMP_FAILURE_RETRY(err1 = select(fd + 1,\
                                                  &fdset,\
                                                  NULL,\
                                                    NULL,\
                                                    &tv));
                  // what happened?
                  if ( err == 1 )
                  {                   
                      rconnect = SSL_connect(cssl);   
                   }
               }
            }
            X509 *cert;
            cert = SSL_get_peer_certificate(cssl);
            char line[2000+1];
            if ( cert != NULL )
            {
              X509_NAME_oneline(X509_get_subject_name(cert), line, MAX_SIZE);
              X509_NAME_oneline(X509_get_issuer_name(cert), line1, MAX_SIZE);
              X509_free(cert);
            }

            ERR_clear_error();
            r = SSL_write(cssl, buffer, len);
            < check error >


Server side :
    int res = pselect(max_fd + 1,   // host socket file descriptor
                      &fd_setw,      // set of ds wait 4 incoming data
                      NULL,          // no write operations
                      NULL,          // no exception operations
                      &tm,           // how much time to wait
                      &sig_set);     // block all signals
     if ( event on listening socket )
     {
          client = accept(sfd, &peer, &peerl);  
     }
     else       // incoming data to receive on existing connection
     {
          SSL *ssl;
          FILE *fp = stdout;
          if ( !ctx )
          {
              return 0;
          }

           ERR_clear_error();

           ssl = SSL_new(ctx);
           SSL_set_fd(ssl, soc);

           int ret = SSL_accept(ssl);
           while (ret <= 0) {
              ERR_print_errors_fp(fp);
              char *buf = (char *) malloc(124);
              ERR_error_string(SSL_get_error(ssl, ret), buf);
              ERR_clear_error();
              ret = SSL_accept(ssl);
            }

            X509 *cert;
            cert = SSL_get_peer_certificate(ssl);
            char line[2000+1];
            if ( cert != NULL )
            {
               X509_NAME_oneline(X509_get_subject_name(cert), line, MAX_SIZE);
               X509_NAME_oneline(X509_get_issuer_name(cert), line1, MAX_SIZE);
               X509_free(cert);
             }
             // get data and analyze result
             int rc = 0;
             bool recv_called = false;           
             rc = SSL_read(ssl, buffer, len);
             < check error >
     }

Before all the above, the server opens, binds and listens on a non-blocking socket for any new incoming client connections.

When I run the above, the client does the connect() and the server does the accept(). The server is now waiting at pselect() for any fd's to be ready to receive data. The client on the other hand is at the SSL_connect() and keeps getting the SSL_ERROR_WANT_READ error. The select() returns the socket is ready to read. My guess is the client is waiting for the SSL_accept() part of the handshake ? I do not know why the server is waiting at pselect(). The code around SSL_accept() is wrong ie it loops and does not look for the WANT_READ and WANT_WRITE errors but I do not get to that point in the code.

SSL_connect can be called whenever the connect is finished. Since both connect and SSL_connect need to exchange data with the peer they might not succeed immediately when using non-blocking sockets. If SSL_connect returns with an error of SSL_WANT_READ or SSL_WANT_WRITE you have to call it again after new data got available on the socket ( SSL_WANT_READ ) or the socket is writable ( SSL_WANT_WRITE ). You can check or wait for this with select , pselect , poll , epoll, kqueue or whatever API your OS provides for this.

SSL_accept and accept are similar, ie SSL_accept can be called directly after a successful accept , might not succeed immediately since data exchange is needed with the SSL client and thus you have to call it again if it returns an error of SSL_WANT_READ or SSL_WANT_WRITE .

Note that SSL_write and SSL_read might also result in such errors. ie you need to deal with SSL_WANT_READ and SSL_WANT_WRITE also for these functions and also the same way as with SSL_connect and SSL_accept . It might even be that a SSL_read results in a SSL_WANT_WRITE since a SSL renegotiation might happen even if the SSL session was already established.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM