mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
404 lines
13 KiB
C
404 lines
13 KiB
C
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
|
│ Copyright The Mbed TLS Contributors │
|
||
|
│ │
|
||
|
│ Licensed under the Apache License, Version 2.0 (the "License"); │
|
||
|
│ you may not use this file except in compliance with the License. │
|
||
|
│ You may obtain a copy of the License at │
|
||
|
│ │
|
||
|
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||
|
│ │
|
||
|
│ Unless required by applicable law or agreed to in writing, software │
|
||
|
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||
|
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||
|
│ See the License for the specific language governing permissions and │
|
||
|
│ limitations under the License. │
|
||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
|
#include "libc/fmt/conv.h"
|
||
|
#include "libc/stdio/stdio.h"
|
||
|
#include "libc/sysv/consts/exit.h"
|
||
|
#include "third_party/mbedtls/ctr_drbg.h"
|
||
|
#include "third_party/mbedtls/debug.h"
|
||
|
#include "third_party/mbedtls/entropy.h"
|
||
|
#include "third_party/mbedtls/net_sockets.h"
|
||
|
#include "third_party/mbedtls/ssl.h"
|
||
|
#include "third_party/mbedtls/x509_crt.h"
|
||
|
|
||
|
STATIC_YOINK("ssl_root_support");
|
||
|
|
||
|
#define MODE_NONE 0
|
||
|
#define MODE_FILE 1
|
||
|
#define MODE_SSL 2
|
||
|
|
||
|
#define DFL_MODE MODE_NONE
|
||
|
#define DFL_FILENAME "cert.crt"
|
||
|
#define DFL_CA_FILE ""
|
||
|
#define DFL_CRL_FILE ""
|
||
|
#define DFL_CA_PATH "zip:usr/share/ssl/root"
|
||
|
#define DFL_SERVER_NAME "localhost"
|
||
|
#define DFL_SERVER_PORT "4433"
|
||
|
#define DFL_DEBUG_LEVEL 0
|
||
|
#define DFL_PERMISSIVE 0
|
||
|
|
||
|
#define USAGE_IO \
|
||
|
" ca_file=%%s file containing top-level CAs\n" \
|
||
|
" ca_path=%%s dir containing top-level CAs\n" \
|
||
|
" crl_file=%%s The single CRL file you want to use\n"
|
||
|
|
||
|
#define USAGE \
|
||
|
"\n usage: %s param=<>...\n" \
|
||
|
"\n acceptable parameters:\n" \
|
||
|
" mode=file|ssl default: none\n" \
|
||
|
" filename=%%s default: cert.crt\n" USAGE_IO \
|
||
|
" server_name=%%s default: localhost\n" \
|
||
|
" server_port=%%d default: 4433\n" \
|
||
|
" debug_level=%%d default: 0 (disabled)\n" \
|
||
|
" permissive=%%d default: 0 (disabled)\n" \
|
||
|
"\n"
|
||
|
|
||
|
/*
|
||
|
* global options
|
||
|
*/
|
||
|
struct options {
|
||
|
int mode; /* the mode to run the application in */
|
||
|
const char *filename; /* filename of the certificate file */
|
||
|
const char *ca_file; /* the file with the CA certificate(s) */
|
||
|
const char *crl_file; /* the file with the CRL to use */
|
||
|
const char *ca_path; /* the path with the CA certificate(s) reside */
|
||
|
const char *server_name; /* hostname of the server (client only) */
|
||
|
const char *server_port; /* port on which the ssl service runs */
|
||
|
int debug_level; /* level of debugging */
|
||
|
int permissive; /* permissive parsing */
|
||
|
} opt;
|
||
|
|
||
|
static void my_debug(void *ctx, int level, const char *file, int line,
|
||
|
const char *str) {
|
||
|
fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str);
|
||
|
fflush((FILE *)ctx);
|
||
|
}
|
||
|
|
||
|
static int my_verify(void *data, mbedtls_x509_crt *crt, int depth,
|
||
|
uint32_t *flags) {
|
||
|
char buf[1024];
|
||
|
printf("\nVerify requested for (Depth %d):\n", depth);
|
||
|
mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt);
|
||
|
printf("%s", buf);
|
||
|
if (*flags) {
|
||
|
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", *flags);
|
||
|
printf("%s\n", buf);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
mbedtls_net_context server_fd;
|
||
|
unsigned char buf[1024];
|
||
|
mbedtls_entropy_context entropy;
|
||
|
mbedtls_ctr_drbg_context ctr_drbg;
|
||
|
mbedtls_ssl_context ssl;
|
||
|
mbedtls_ssl_config conf;
|
||
|
mbedtls_x509_crt cacert;
|
||
|
mbedtls_x509_crl cacrl;
|
||
|
|
||
|
int main(int argc, char *argv[]) {
|
||
|
int ret = 1;
|
||
|
int exit_code = EXIT_FAILURE;
|
||
|
int i, j;
|
||
|
uint32_t flags;
|
||
|
int verify = 0;
|
||
|
char *p, *q;
|
||
|
const char *pers = "cert_app";
|
||
|
|
||
|
/*
|
||
|
* Set to sane values
|
||
|
*/
|
||
|
mbedtls_net_init(&server_fd);
|
||
|
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||
|
mbedtls_ssl_init(&ssl);
|
||
|
mbedtls_ssl_config_init(&conf);
|
||
|
mbedtls_x509_crt_init(&cacert);
|
||
|
#if defined(MBEDTLS_X509_CRL_PARSE_C)
|
||
|
mbedtls_x509_crl_init(&cacrl);
|
||
|
#else
|
||
|
/* Zeroize structure as CRL parsing is not supported and we have to pass
|
||
|
it to the verify function */
|
||
|
memset(&cacrl, 0, sizeof(mbedtls_x509_crl));
|
||
|
#endif
|
||
|
|
||
|
if (argc == 0) {
|
||
|
usage:
|
||
|
printf(USAGE, program_invocation_name);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
opt.mode = DFL_MODE;
|
||
|
opt.filename = DFL_FILENAME;
|
||
|
opt.ca_file = DFL_CA_FILE;
|
||
|
opt.crl_file = DFL_CRL_FILE;
|
||
|
opt.ca_path = DFL_CA_PATH;
|
||
|
opt.server_name = DFL_SERVER_NAME;
|
||
|
opt.server_port = DFL_SERVER_PORT;
|
||
|
opt.debug_level = DFL_DEBUG_LEVEL;
|
||
|
opt.permissive = DFL_PERMISSIVE;
|
||
|
|
||
|
for (i = 1; i < argc; i++) {
|
||
|
p = argv[i];
|
||
|
if ((q = strchr(p, '=')) == NULL) goto usage;
|
||
|
*q++ = '\0';
|
||
|
|
||
|
for (j = 0; p + j < q; j++) {
|
||
|
if (argv[i][j] >= 'A' && argv[i][j] <= 'Z') argv[i][j] |= 0x20;
|
||
|
}
|
||
|
|
||
|
if (strcmp(p, "mode") == 0) {
|
||
|
if (strcmp(q, "file") == 0)
|
||
|
opt.mode = MODE_FILE;
|
||
|
else if (strcmp(q, "ssl") == 0)
|
||
|
opt.mode = MODE_SSL;
|
||
|
else
|
||
|
goto usage;
|
||
|
} else if (strcmp(p, "filename") == 0)
|
||
|
opt.filename = q;
|
||
|
else if (strcmp(p, "ca_file") == 0)
|
||
|
opt.ca_file = q;
|
||
|
else if (strcmp(p, "crl_file") == 0)
|
||
|
opt.crl_file = q;
|
||
|
else if (strcmp(p, "ca_path") == 0)
|
||
|
opt.ca_path = q;
|
||
|
else if (strcmp(p, "server_name") == 0)
|
||
|
opt.server_name = q;
|
||
|
else if (strcmp(p, "server_port") == 0)
|
||
|
opt.server_port = q;
|
||
|
else if (strcmp(p, "debug_level") == 0) {
|
||
|
opt.debug_level = atoi(q);
|
||
|
if (opt.debug_level < 0 || opt.debug_level > 65535) goto usage;
|
||
|
} else if (strcmp(p, "permissive") == 0) {
|
||
|
opt.permissive = atoi(q);
|
||
|
if (opt.permissive < 0 || opt.permissive > 1) goto usage;
|
||
|
} else
|
||
|
goto usage;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 1.1. Load the trusted CA
|
||
|
*/
|
||
|
printf(" . Loading the CA root certificate ...");
|
||
|
fflush(stdout);
|
||
|
|
||
|
if (strlen(opt.ca_path)) {
|
||
|
if ((ret = mbedtls_x509_crt_parse_path(&cacert, opt.ca_path)) < 0) {
|
||
|
printf(" failed\n ! mbedtls_x509_crt_parse_path returned -0x%x\n\n",
|
||
|
(unsigned int)-ret);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
verify = 1;
|
||
|
} else if (strlen(opt.ca_file)) {
|
||
|
if ((ret = mbedtls_x509_crt_parse_file(&cacert, opt.ca_file)) < 0) {
|
||
|
printf(" failed\n ! mbedtls_x509_crt_parse_file returned -0x%x\n\n",
|
||
|
(unsigned int)-ret);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
verify = 1;
|
||
|
}
|
||
|
|
||
|
printf(" ok (%d skipped)\n", ret);
|
||
|
|
||
|
#if defined(MBEDTLS_X509_CRL_PARSE_C)
|
||
|
if (strlen(opt.crl_file)) {
|
||
|
if ((ret = mbedtls_x509_crl_parse_file(&cacrl, opt.crl_file)) != 0) {
|
||
|
printf(" failed\n ! mbedtls_x509_crl_parse returned -0x%x\n\n",
|
||
|
(unsigned int)-ret);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
verify = 1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (opt.mode == MODE_FILE) {
|
||
|
mbedtls_x509_crt crt;
|
||
|
mbedtls_x509_crt *cur = &crt;
|
||
|
mbedtls_x509_crt_init(&crt);
|
||
|
|
||
|
/*
|
||
|
* 1.1. Load the certificate(s)
|
||
|
*/
|
||
|
printf("\n . Loading the certificate(s) ...");
|
||
|
fflush(stdout);
|
||
|
|
||
|
ret = mbedtls_x509_crt_parse_file(&crt, opt.filename);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
printf(" failed\n ! mbedtls_x509_crt_parse_file returned -0x%04x\n\n",
|
||
|
-ret);
|
||
|
mbedtls_x509_crt_free(&crt);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (opt.permissive == 0 && ret > 0) {
|
||
|
printf(" failed\n ! mbedtls_x509_crt_parse failed to parse %d "
|
||
|
"certificates\n\n",
|
||
|
ret);
|
||
|
mbedtls_x509_crt_free(&crt);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
printf(" ok\n");
|
||
|
|
||
|
/*
|
||
|
* 1.2 Print the certificate(s)
|
||
|
*/
|
||
|
while (cur != NULL) {
|
||
|
printf(" . Peer certificate information ...\n");
|
||
|
ret = mbedtls_x509_crt_info((char *)buf, sizeof(buf) - 1, " ", cur);
|
||
|
if (ret == -1) {
|
||
|
printf(" failed\n ! mbedtls_x509_crt_info returned -0x%04x\n\n",
|
||
|
-ret);
|
||
|
mbedtls_x509_crt_free(&crt);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
printf("%s\n", buf);
|
||
|
|
||
|
cur = cur->next;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 1.3 Verify the certificate
|
||
|
*/
|
||
|
if (verify) {
|
||
|
printf(" . Verifying X.509 certificate...");
|
||
|
|
||
|
if ((ret = mbedtls_x509_crt_verify(&crt, &cacert, &cacrl, NULL, &flags,
|
||
|
my_verify, NULL)) != 0) {
|
||
|
char vrfy_buf[512];
|
||
|
|
||
|
printf(" failed\n");
|
||
|
|
||
|
mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags);
|
||
|
|
||
|
printf("%s\n", vrfy_buf);
|
||
|
} else
|
||
|
printf(" ok\n");
|
||
|
}
|
||
|
|
||
|
mbedtls_x509_crt_free(&crt);
|
||
|
} else if (opt.mode == MODE_SSL) {
|
||
|
/*
|
||
|
* 1. Initialize the RNG and the session data
|
||
|
*/
|
||
|
printf("\n . Seeding the random number generator...");
|
||
|
fflush(stdout);
|
||
|
|
||
|
mbedtls_entropy_init(&entropy);
|
||
|
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||
|
(const unsigned char *)pers,
|
||
|
strlen(pers))) != 0) {
|
||
|
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||
|
goto ssl_exit;
|
||
|
}
|
||
|
|
||
|
printf(" ok\n");
|
||
|
|
||
|
#if defined(MBEDTLS_DEBUG_C)
|
||
|
mbedtls_debug_set_threshold(opt.debug_level);
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* 2. Start the connection
|
||
|
*/
|
||
|
printf(" . Connecting to tcp/%s/%s...\n", opt.server_name,
|
||
|
opt.server_port);
|
||
|
|
||
|
if ((ret = mbedtls_net_connect(&server_fd, opt.server_name, opt.server_port,
|
||
|
MBEDTLS_NET_PROTO_TCP)) != 0) {
|
||
|
printf(" ! mbedtls_net_connect returned -0x%04x\n\n", -ret);
|
||
|
goto ssl_exit;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 3. Setup stuff
|
||
|
*/
|
||
|
if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
|
||
|
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||
|
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||
|
printf(" ! mbedtls_ssl_config_defaults returned -0x%04x\n\n", -ret);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (verify) {
|
||
|
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||
|
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
|
||
|
mbedtls_ssl_conf_verify(&conf, my_verify, NULL);
|
||
|
} else
|
||
|
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);
|
||
|
|
||
|
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||
|
mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
|
||
|
|
||
|
if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) {
|
||
|
printf(" ! mbedtls_ssl_setup returned -0x%04x\n\n", -ret);
|
||
|
goto ssl_exit;
|
||
|
}
|
||
|
|
||
|
if ((ret = mbedtls_ssl_set_hostname(&ssl, opt.server_name)) != 0) {
|
||
|
printf(" ! mbedtls_ssl_set_hostname returned -0x%04x\n\n", -ret);
|
||
|
goto ssl_exit;
|
||
|
}
|
||
|
|
||
|
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv,
|
||
|
NULL);
|
||
|
|
||
|
/*
|
||
|
* 4. Handshake
|
||
|
*/
|
||
|
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
|
||
|
if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
|
||
|
ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||
|
printf(" ! mbedtls_ssl_handshake returned -0x%04x\n\n", -ret);
|
||
|
goto ssl_exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 5. Print the certificate
|
||
|
*/
|
||
|
#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
|
||
|
printf(" . Peer certificate information ... skipped\n");
|
||
|
#else
|
||
|
printf(" . Peer certificate information ...\n");
|
||
|
ret = mbedtls_x509_crt_info((char *)buf, sizeof(buf) - 1, " ",
|
||
|
mbedtls_ssl_get_peer_cert(&ssl));
|
||
|
if (ret == -1) {
|
||
|
printf(" failed\n ! mbedtls_x509_crt_info returned -0x%04x\n\n", -ret);
|
||
|
goto ssl_exit;
|
||
|
}
|
||
|
|
||
|
printf("%s\n", buf);
|
||
|
#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
|
||
|
|
||
|
mbedtls_ssl_close_notify(&ssl);
|
||
|
|
||
|
ssl_exit:
|
||
|
mbedtls_ssl_free(&ssl);
|
||
|
mbedtls_ssl_config_free(&conf);
|
||
|
} else
|
||
|
goto usage;
|
||
|
|
||
|
exit_code = MBEDTLS_EXIT_SUCCESS;
|
||
|
|
||
|
exit:
|
||
|
|
||
|
mbedtls_net_free(&server_fd);
|
||
|
mbedtls_x509_crt_free(&cacert);
|
||
|
#if defined(MBEDTLS_X509_CRL_PARSE_C)
|
||
|
mbedtls_x509_crl_free(&cacrl);
|
||
|
#endif
|
||
|
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||
|
mbedtls_entropy_free(&entropy);
|
||
|
|
||
|
mbedtls_exit(exit_code);
|
||
|
}
|