/* * s_client command * * Copyright (C) 2019 Patrick McDermott * * This file is part of wolfssl-util. * * wolfssl-util 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. * * wolfssl-util 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 wolfssl-util. If not, see . */ #include #include #include #include #include #include #include #include #include "commands.h" #define CA_CERTS "/etc/ssl/certs" static _Bool parse_host_port(char *hostport, char **host, char **port) { *host = hostport; /* XXX: Port is required. Otherwise, this will mangle IPv6 addresses * without some more intelligent (and larger) code. */ *port = strrchr(*host, ':'); if (!*port) { fputs("Port is required\n", stderr); return false; } **port = '\0'; ++*port; return true; } static _Bool connect_socket(const char *host, const char *port) { struct addrinfo hints = {0}; struct addrinfo *result; int s; struct addrinfo *rp; int sfd; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; hints.ai_protocol = 0; s = getaddrinfo(host, port, &hints, &result); if (s != 0) { fprintf(stderr, "Failed to resolve host and port: %s\n", gai_strerror(s)); return -1; } for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) { continue; } if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) { break; } close(sfd); } if (rp == NULL) { fputs("Failed to connect\n", stderr); freeaddrinfo(result); return -1; } freeaddrinfo(result); return sfd; } int s_client(int argc, char **argv) { char *host = NULL; char *port = NULL; const char *servername = NULL; int ret = EXIT_SUCCESS; WOLFSSL_METHOD *method; WOLFSSL_CTX *ctx; WOLFSSL *ssl; int sfd; for (; argc > 0; --argc, ++argv) { if (strcmp(*argv, "-quiet") == 0) { /* No-op */ } else if (strcmp(*argv, "-connect") == 0) { --argc, ++argv; if (parse_host_port(*argv, &host, &port) == false) { return EXIT_FAILURE; } } else if (strcmp(*argv, "-servername") == 0) { --argc, ++argv; servername = *argv; } else if (**argv == '-') { fprintf(stderr, "Unsupported option \"%s\"\n", *argv); } } wolfSSL_Init(); method = wolfSSLv23_client_method(); if (method == NULL) { fputs("Out of memory\n", stderr); ret = EXIT_FAILURE; goto cleanup; } ctx = wolfSSL_CTX_new(method); if (ctx == NULL) { fputs("Out of memory\n", stderr); ret = EXIT_FAILURE; goto cleanup; } if (wolfSSL_CTX_load_verify_locations_ex(ctx, NULL, CA_CERTS, WOLFSSL_LOAD_FLAG_IGNORE_ERR) != WOLFSSL_SUCCESS) { fputs("Failed to load CA certificates\n", stderr); ret = EXIT_FAILURE; goto ctx_free; } #ifdef HAVE_OCSP if (wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_CHECKALL) != WOLFSSL_SUCCESS) { fputs("Failed to enable OCSP\n", stderr); goto ctx_free; } #endif #ifdef HAVE_SNI if (servername != NULL) { if (wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, servername, strlen(servername)) != WOLFSSL_SUCCESS){ fputs("Out of memory\n", stderr); ret = EXIT_FAILURE; goto ctx_free; } } #else (void) servername; #endif ssl = wolfSSL_new(ctx); if (ssl == NULL) { fputs("Out of memory\n", stderr); ret = EXIT_FAILURE; goto ctx_free; } sfd = connect_socket(host, port); if (sfd == -1) { ret = EXIT_FAILURE; goto ssl_free; } close(sfd); ssl_free: wolfSSL_free(ssl); ctx_free: wolfSSL_CTX_free(ctx); cleanup: wolfSSL_Cleanup(); return ret; }