#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
+#include <signal.h>
#include <sys/socket.h>
+#include <netinet/in.h>
#include <arpa/inet.h>
#include <err.h>
X509 *cert;
};
+static int verbose;
+static const char *cipher_list;
+
/* How much K to transfer between client and server. */
#define KTRANSFER (1 * 1024)
* Simple TLS Server code is based on
* https://wiki.openssl.org/index.php/Simple_TLS_Server
*/
-static int s_server(EVP_PKEY *pkey, X509 *cert, int pipewr)
+static int s_server(EVP_PKEY *pkey, X509 *cert, int client)
{
SSL_CTX *ctx;
T(ctx = SSL_CTX_new(TLS_server_method()));
T(SSL_CTX_use_PrivateKey(ctx, pkey));
T(SSL_CTX_check_private_key(ctx));
- struct sockaddr_in addr = { .sin_family = AF_INET };
- socklen_t len;
- int sock;
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock < 0)
- err(1, "socket");
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
- err(1, "setsockopt");
- if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
- err(1, "bind");
- len = sizeof(addr);
- if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0)
- err(1, "getsockname");
- int port = ntohs(addr.sin_port);
- if (listen(sock, 1) < 0)
- err(1, "listen");
- /* Signal to client that server is ready. */
- if (write(pipewr, &port, sizeof(port)) != sizeof(port))
- err(1, "write pipe");
- len = sizeof(addr);
- alarm(1);
- int client = accept(sock, (struct sockaddr *)&addr, &len);
- if (client < 0)
- err(1, "accept");
- alarm(0);
SSL *ssl;
- ssl = SSL_new(ctx);
- SSL_set_fd(ssl, client);
+ T(ssl = SSL_new(ctx));
+ T(SSL_set_fd(ssl, client));
+ if (cipher_list)
+ T(SSL_set_cipher_list(ssl, cipher_list));
T(SSL_accept(ssl) == 1);
/* Receive data from client */
SSL_free(ssl);
close(client);
- close(sock);
SSL_CTX_free(ctx);
return 0;
}
* Simple TLC Client code is based on man BIO_f_ssl and
* https://wiki.openssl.org/index.php/SSL/TLS_Client
*/
-static int s_client(int piperd)
+static int s_client(int server)
{
SSL_CTX *ctx;
T(ctx = SSL_CTX_new(TLS_client_method()));
SSL *ssl;
T(BIO_get_ssl(sbio, &ssl));
T(SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY));
+ if (cipher_list)
+ T(SSL_set_cipher_list(ssl, cipher_list));
#if 0
/* Does not work with reneg. */
BIO_set_ssl_renegotiate_bytes(sbio, 100 * 1024);
#endif
- int port;
- alarm(1);
- /* Wait for server to be ready. */
- if (read(piperd, &port, sizeof(port)) != sizeof(port))
- err(1, "read pipe");
- char tport[8];
- snprintf(tport, sizeof(tport), "%d", port);
- T(BIO_set_conn_port(sbio, tport));
- T(BIO_do_connect(sbio) == 1);
+ T(SSL_set_fd(ssl, server));
T(BIO_do_handshake(sbio) == 1);
- alarm(0);
printf("Protocol: %s\n", SSL_get_version(ssl));
printf("Cipher: %s\n", SSL_get_cipher_name(ssl));
-#if 0
- SSL_SESSION *sess = SSL_get0_session(ssl);
- SSL_SESSION_print_fp(stdout, sess);
-#endif
+ if (verbose) {
+ SSL_SESSION *sess = SSL_get0_session(ssl);
+ SSL_SESSION_print_fp(stdout, sess);
+ }
X509 *cert;
T(cert = SSL_get_peer_certificate(ssl));
struct certkey ck;
ck = certgen(algname, paramset);
- int pipefd[2];
- if (pipe(pipefd))
- err(1, "pipe");
+ int sockfd[2];
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) == -1)
+ err(1, "socketpair");
- pid_t pid = fork();
- if (pid < 0)
- err(1, "fork");
+ setpgid(0, 0);
- if (pid > 0) {
- int status;
-
- ret = s_client(pipefd[0]);
- wait(&status);
- ret |= WIFEXITED(status) && WEXITSTATUS(status);
+ /* Run server in separate process. */
+ pid_t server_pid = fork();
+ if (server_pid < 0)
+ err(1, "fork server");
+ if (server_pid == 0) {
+ ret = s_server(ck.pkey, ck.cert, sockfd[1]);
X509_free(ck.cert);
EVP_PKEY_free(ck.pkey);
- } else if (pid == 0) {
- ret = s_server(ck.pkey, ck.cert, pipefd[1]);
+ exit(ret);
+ }
+
+ /* Run client in separate process. */
+ pid_t client_pid = fork();
+ if (client_pid < 0)
+ err(1, "fork client");
+ if (client_pid == 0) {
+ ret = s_client(sockfd[0]);
X509_free(ck.cert);
EVP_PKEY_free(ck.pkey);
exit(ret);
}
+ /* Wait for first child to exit. */
+ int status;
+ pid_t exited_pid = wait(&status);
+ ret = (WIFEXITED(status) && WEXITSTATUS(status)) ||
+ (WIFSIGNALED(status) && WTERMSIG(status));
+ if (ret) {
+ warnx(cRED "%s child %s with %d %s" cNORM,
+ exited_pid == server_pid? "server" : "client",
+ WIFSIGNALED(status)? "killed" : "exited",
+ WIFSIGNALED(status)? WTERMSIG(status) : WEXITSTATUS(status),
+ WIFSIGNALED(status)? strsignal(WTERMSIG(status)) : "");
+
+ /* If first child exited with error, kill other. */
+ warnx("terminating %s by force",
+ exited_pid == server_pid? "client" : "server");
+ kill(exited_pid == server_pid? client_pid : server_pid, SIGTERM);
+ }
+
+ exited_pid = wait(&status);
+ /* Report error unless we killed it. */
+ if (!ret && (!WIFEXITED(status) || WEXITSTATUS(status)))
+ warnx(cRED "%s child %s with %d %s" cNORM,
+ exited_pid == server_pid? "server" : "client",
+ WIFSIGNALED(status)? "killed" : "exited",
+ WIFSIGNALED(status)? WTERMSIG(status) : WEXITSTATUS(status),
+ WIFSIGNALED(status)? strsignal(WTERMSIG(status)) : "");
+ ret |= (WIFEXITED(status) && WEXITSTATUS(status)) ||
+ (WIFSIGNALED(status) && WTERMSIG(status));
+
+ /* Every responsible process should free this. */
+ X509_free(ck.cert);
+ EVP_PKEY_free(ck.pkey);
+#ifdef __SANITIZE_ADDRESS__
+ /* Abort on the first (hopefully) ASan error. */
+ if (ret)
+ _exit(ret);
+#endif
return ret;
}
T(ENGINE_init(eng));
T(ENGINE_set_default(eng, ENGINE_METHOD_ALL));
- ret |= test("rsa", NULL);
+ char *p;
+ if ((p = getenv("VERBOSE")))
+ verbose = atoi(p);
+
+ /* ret |= test("rsa", NULL); */
+ cipher_list = "LEGACY-GOST2012-GOST8912-GOST8912";
ret |= test("gost2012_256", "A");
ret |= test("gost2012_256", "B");
ret |= test("gost2012_256", "C");