C: чтение из OpenSSL Socket - PullRequest
       21

C: чтение из OpenSSL Socket

0 голосов
/ 02 октября 2018

Я использую библиотеку OpenSSL для защиты сетевых сообщений, но по некоторым причинам она не всегда работает.На самом деле большую часть времени это не работает.Когда я запускаю скомпилированный код и подключаюсь к сокету, большую часть времени он просто выполняет код основного процесса и в дочернем процессоре, но иногда он запускает дочерние инструкции.Очевидно, это не так, как должно работать, вместо этого ребенок должен выйти, после того как он все время обрабатывает клиента (handle_client(newfd)).Одна интересная часть заключается в том, что если я уберу строку handle_client(newfd) из дочерних инструкций и добавлю туда что-нибудь маленькое, например printf("test"), то дочерний процесс будет работать каждый раз, как следует, он печатает тест и завершает работу сразу после этого.Является ли это каким-то ограничением в fork (), или я просто не должен запускать так много кода у ребенка?Или что-то другое?Любая помощь будет действительно ценится!

main.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/wait.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include "json.h"
#include "create_socket.h"
#include "get_addr.h"
#include "handle_income.h"
#include "handle_client.h"
int main(void) {
  int newfd;
  struct sockaddr_storage their_addr;
  char s[INET6_ADDRSTRLEN];
  pid_t pid;
  unsigned int cpc = 0;
  int listenfd = create_socket("8069");
  if (listenfd < 0)
    exit(1);
  while(1) {
    socklen_t sin_size = sizeof their_addr;
    if ((newfd = accept(listenfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
      continue;
    inet_ntop(their_addr.ss_family,
      get_addr((struct sockaddr *)&their_addr), s, sizeof s);
    printf("conn %s siz %d\n", s, (int) sin_size); //REMOVE
    if ((pid = fork()) < 0) {
      exit(1);
    } else if (pid == 0) {
      close(listenfd);
      handle_client(newfd);
      exit(0);
    }
    printf("child %d\n", (int) pid); //REMOVE
    cpc++;
    while(cpc) {
      pid = waitpid((pid_t) -1, NULL, WNOHANG);
      if (pid < 0)
        exit(1);
      else if (pid == 0)
        break;
      else
        cpc--;
    }
  }
  EVP_cleanup();
  exit(0);
}

handle_client.h:

#define READ_SIZE 32
void handle_client(int newfd) {
  char *buffer = NULL;
  char *tmp_buffer = malloc(READ_SIZE);
  unsigned long buffer_size = 0;
  unsigned long received = 0;
  int status = 0;
  SSL_load_error_strings();
  OpenSSL_add_all_algorithms();
  SSL_CTX *sslctx = SSL_CTX_new(SSLv23_server_method());
  if (sslctx) {
    SSL_CTX_set_ecdh_auto(sslctx, 1);
    if ((SSL_CTX_use_certificate_file(sslctx, "/ssl-cert.pem", SSL_FILETYPE_PEM)) > 0) {
      if ((SSL_CTX_use_PrivateKey_file(sslctx, "/ssl-key.pem", SSL_FILETYPE_PEM)) > 0) {
        SSL *ssl = SSL_new(sslctx);
        SSL_set_fd(ssl, newfd);
        if (SSL_accept(ssl) > 0) {
          fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK);
          do {
            if (received >= buffer_size) {
              char *tmp;
              buffer_size += READ_SIZE;
              if ((tmp = realloc(buffer, buffer_size)) == NULL) {
                break;
              } else {
                buffer = tmp;
              }
            }
            status = SSL_read(ssl, tmp_buffer, READ_SIZE);
            if (status > 0) {
              received += status;
              strncat(buffer, tmp_buffer, status);
            } else {
              ERR_print_errors_fp(stderr);
            }
          } while (status > 0);
          free(tmp_buffer);
          buffer[received] = 0;
          if (received < buffer_size) {
            buffer = realloc(buffer, received);
          }
          printf("%s\n", buffer); //REMOVE
          char *response = handle_income(buffer);
          SSL_write(ssl, response, strlen(response));
          printf("%s\n", response); //REMOVE
        }
        SSL_free(ssl);
      }
    }
  }
  SSL_CTX_free(sslctx);
  close(newfd);
}

1 Ответ

0 голосов
/ 02 октября 2018

Могут быть и другие проблемы, но вы должны исправить следующую ошибку переполнения буфера и проверить, исправляет ли она также видимую проблему:

SSL_read может возвращать меньше READ_SIZE.Так что следующий кусок кода не работает.Проблема возникает, например, когда 1-й SSL_read() возвращает, например, 16, а следующий вызов возвращает 32 (= READ_SIZE).Выделенный размер buffer при втором вызове strncat равен 32, поэтому переполнение буфера может произойти во время strncat().

        if (received >= buffer_size) {
          char *tmp;
          buffer_size += READ_SIZE;
          if ((tmp = realloc(buffer, buffer_size)) == NULL) {
            break;
          } else {
            buffer = tmp;
          }
        }
        status = SSL_read(ssl, tmp_buffer, READ_SIZE);
        if (status > 0) {
          received += status;
          strncat(buffer, tmp_buffer, status);
        }
        ...

Возможна другая проблема с вызовом strncat.Для нулевого терминатора строки требуется один дополнительный байт.Со страницы руководства:

размер dest должен быть как минимум strlen (dest) + n + 1

...