aboutsummaryrefslogtreecommitdiff
path: root/networking/ssl_client.c
blob: 50c2180b0189d4493257b38233e516179d604f0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * Copyright (C) 2017 Denys Vlasenko
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */
//config:config SSL_CLIENT
//config:	bool "ssl_client (28 kb)"
//config:	default y
//config:	select TLS
//config:	help
//config:	This tool pipes data to/from a socket, TLS-encrypting it.

//applet:IF_SSL_CLIENT(APPLET(ssl_client, BB_DIR_USR_BIN, BB_SUID_DROP))

//kbuild:lib-$(CONFIG_SSL_CLIENT) += ssl_client.o

//usage:#define ssl_client_trivial_usage
//usage:    IF_NOT_PLATFORM_MINGW32(
//usage:       "[-n SNI] { -s FD [-r FD] | HOST | -e PROG ARGS }"
//usage:    )
//usage:    IF_PLATFORM_MINGW32(
//usage:    IF_FEATURE_TLS_SCHANNEL("[-c] ")
//usage:       "[-e] -h handle [-n SNI]"
//usage:    )
//usage:#define ssl_client_full_usage ""

#include "libbb.h"

int ssl_client_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ssl_client_main(int argc UNUSED_PARAM, char **argv)
{
	int exit_if_stdin_closed;
	tls_state_t *tls;
	const char *sni = NULL;
	int opt;
#if ENABLE_PLATFORM_MINGW32
	char *hstr = NULL;
	HANDLE h;
	enum {
		/* wrong name so exit_if_stdin_closed is set later */
		OPT_s = (1 << 0),
		OPT_h = (1 << 1),
		OPT_n = (1 << 2),
# if ENABLE_FEATURE_TLS_SCHANNEL
		OPT_c = (1 << 3),
# endif
	};
#else
	enum {
		OPT_s = (1 << 0),
		OPT_r = (1 << 1),
		OPT_n = (1 << 2),
		OPT_e = (1 << 3),
	};
#endif

	// INIT_G();
	tls = new_tls_state();
#if ENABLE_PLATFORM_MINGW32
	opt = getopt32(argv, "eh:n:"IF_FEATURE_TLS_SCHANNEL("c"), &hstr, &sni);

	if (!hstr || sscanf(hstr, "%p", &h) != 1)
		bb_error_msg_and_die("invalid handle");
	init_winsock();
	tls->ifd = tls->ofd = _open_osfhandle((intptr_t)h, _O_RDWR|_O_BINARY);
# if ENABLE_FEATURE_TLS_SCHANNEL
	tls->no_check_cert = (opt & OPT_c) != 0;
# endif
#else
	/* "+": stop on first non-option */
	opt = getopt32(argv, "^+" "s:+r:+n:e" "\0"
		"e--s:e--r:s--e:r--e", &tls->ofd, &tls->ifd, &sni
	);
	argv += optind;

	if (opt & OPT_e) {
		/* -e PROG: run PROG and talk TLS to its stdin/stdout */
		// Talk to local HTTP server behind local TLS server:
		// printf "GET / HTTP/1.1\r\n\r\n" | ssl_client -e ssl_server -d PRIVKEY.der -e httpd -i
		struct fd_pair to_prog;
		struct fd_pair from_prog;

		pid_t pid;

		if (!argv[0])
			bb_show_usage();

		xpiped_pair(to_prog);
		xpiped_pair(from_prog);

		pid = xvfork();
		if (pid == 0) {
			/* Child: run the program */

			/* NB: close _first_, then move fds! */
			close(to_prog.wr);
			close(from_prog.rd);
			xmove_fd(to_prog.rd, STDIN_FILENO);
			xmove_fd(from_prog.wr, STDOUT_FILENO);

			BB_EXECVP_or_die(argv);
		}

		/* Parent: close child ends of pipes */
		close(to_prog.rd);
		close(from_prog.wr);

		tls->ofd = to_prog.wr;   /* write to program's stdin */
		tls->ifd = from_prog.rd; /* read from program's stdout */

	} else if (!(opt & (OPT_s|OPT_r))) {
		/* Not -e/-s/-r: connect to HOST */
		// Talk to kernel.org:
		// printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | ssl_client kernel.org
		if (!argv[0] || argv[1])
			bb_show_usage();
		if (!sni)
			sni = argv[0];
		tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[0], 443);

	} else {
		/* -s FD [-r FD] */
		if (!(opt & OPT_s) || argv[0])
			bb_show_usage();
		if (!(opt & OPT_r)) {
			/* -r FD defaults to -s FD */
			tls->ifd = tls->ofd;
		}
	}
#endif

	tls_handshake(tls, sni);

	exit_if_stdin_closed = (opt & OPT_s) ? TLSLOOP_EXIT_ON_LOCAL_EOF : 0;
	tls_run_copy_loop(tls, /*flags*/ exit_if_stdin_closed);

	return EXIT_SUCCESS;
}