1 /*	$OpenBSD: client.cpp,v 1.1 2020/09/15 01:45:16 bluhm Exp $	*/
2 /*
3  * Copyright (c) 2019-2020 Alexander Bluhm <bluhm@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 
21 #include <err.h>
22 #include <netdb.h>
23 #include <unistd.h>
24 
25 #include <botan/tls_client.h>
26 #include <botan/tls_callbacks.h>
27 #include <botan/tls_session_manager.h>
28 #include <botan/tls_policy.h>
29 #include <botan/auto_rng.h>
30 #include <botan/certstor.h>
31 
32 #include <iostream>
33 #include <string>
34 using namespace std;
35 
36 class Callbacks : public Botan::TLS::Callbacks {
37 public:
Callbacks(int socket)38 	Callbacks(int socket) :
39 		m_socket(socket)
40 	{}
41 
print_sockname()42 	void print_sockname()
43 	{
44 		struct sockaddr_storage ss;
45 		char host[NI_MAXHOST], port[NI_MAXSERV];
46 		socklen_t slen;
47 
48 		slen = sizeof(ss);
49 		if (getsockname(m_socket, (struct sockaddr *)&ss, &slen) == -1)
50 			err(1, "getsockname");
51 		if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host,
52 		    sizeof(host), port, sizeof(port),
53 		    NI_NUMERICHOST | NI_NUMERICSERV))
54 			errx(1, "getnameinfo");
55 		cout <<"sock: " <<host <<" " <<port <<endl <<flush;
56 	}
57 
print_peername()58 	void print_peername()
59 	{
60 		struct sockaddr_storage ss;
61 		char host[NI_MAXHOST], port[NI_MAXSERV];
62 		socklen_t slen;
63 
64 		slen = sizeof(ss);
65 		if (getpeername(m_socket, (struct sockaddr *)&ss, &slen) == -1)
66 			err(1, "getpeername");
67 		if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host,
68 		    sizeof(host), port, sizeof(port),
69 		    NI_NUMERICHOST | NI_NUMERICSERV))
70 			errx(1, "getnameinfo");
71 		cout <<"peer: " <<host <<" " <<port <<endl <<flush;
72 	}
73 
tls_emit_data(const uint8_t data[],size_t size)74 	void tls_emit_data(const uint8_t data[], size_t size) override
75 	{
76 		size_t off = 0, len = size;
77 
78 		while (len > 0) {
79 			ssize_t n;
80 
81 			n = send(m_socket, data + off, len, 0);
82 			if (n < 0)
83 				err(1, "send");
84 			off += n;
85 			len -= n;
86 		}
87 	}
88 
tls_record_received(uint64_t seq_no,const uint8_t data[],size_t size)89 	void tls_record_received(uint64_t seq_no, const uint8_t data[],
90 	    size_t size) override
91 	{
92 		cout <<"<<< " <<string((const char *)data, size) <<flush;
93 
94 		string str("hello\n");
95 		cout <<">>> " <<str <<flush;
96 		m_channel->send(str);
97 		m_channel->close();
98 	}
99 
tls_alert(Botan::TLS::Alert alert)100 	void tls_alert(Botan::TLS::Alert alert) override
101 	{
102 		errx(1, "alert: %s", alert.type_string().c_str());
103 	}
104 
tls_session_established(const Botan::TLS::Session & session)105 	bool tls_session_established(const Botan::TLS::Session& session)
106 	    override
107 	{
108 		cout <<"established" <<endl <<flush;
109 		return false;
110 	}
111 
set_channel(Botan::TLS::Channel & channel)112 	void set_channel(Botan::TLS::Channel &channel) {
113 		m_channel = &channel;
114 	}
115 
116 protected:
117 	int m_socket = -1;
118 	Botan::TLS::Channel *m_channel = nullptr;
119 };
120 
121 class Credentials : public Botan::Credentials_Manager {
122 public:
trusted_certificate_authorities(const std::string & type,const std::string & context)123 	std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
124 	    const std::string  &type, const std::string  &context)
125 	    override
126 	{
127 		std::vector<Botan::Certificate_Store*> cs { &m_ca };
128 		return cs;
129 	}
130 
add_certificate_file(const std::string & file)131 	void add_certificate_file(const std::string &file) {
132 		Botan::X509_Certificate cert(file);
133 		m_ca.add_certificate(cert);
134 	}
135 private:
136 	Botan::Certificate_Store_In_Memory m_ca;
137 };
138 
139 class Policy : public Botan::TLS::Strict_Policy {
140 public:
require_cert_revocation_info() const141 	bool require_cert_revocation_info() const override {
142 		return false;
143 	}
144 };
145 
146 void __dead
usage(void)147 usage(void)
148 {
149 	fprintf(stderr, "usage: client [-C CA] host port\n");
150 	exit(2);
151 }
152 
153 int
main(int argc,char * argv[])154 main(int argc, char *argv[])
155 {
156 	struct addrinfo hints, *res;
157 	int ch, s, error;
158 	char buf[256];
159 	char *cafile = NULL;
160 	char *host, *port;
161 
162 	while ((ch = getopt(argc, argv, "C:")) != -1) {
163 		switch (ch) {
164 		case 'C':
165 			cafile = optarg;
166 			break;
167 		default:
168 			usage();
169 		}
170 	}
171 	argc -= optind;
172 	argv += optind;
173 	if (argc == 2) {
174 		host = argv[0];
175 		port = argv[1];
176 	} else {
177 		usage();
178 	}
179 
180 	memset(&hints, 0, sizeof(hints));
181 	hints.ai_family = AF_INET;
182 	hints.ai_socktype = SOCK_STREAM;
183 	error = getaddrinfo(host, port, &hints, &res);
184 	if (error)
185 		errx(1, "getaddrinfo: %s", gai_strerror(error));
186 	if (res == NULL)
187 		errx(1, "getaddrinfo empty");
188 	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
189 	if (s == -1)
190 		err(1, "socket");
191 	if (connect(s, res->ai_addr, res->ai_addrlen) == -1)
192 		err(1, "connect");
193 	freeaddrinfo(res);
194 
195 	{
196 		Callbacks callbacks(s);
197 		Botan::AutoSeeded_RNG rng;
198 		Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
199 		Credentials creds;
200 		if (cafile != NULL)
201 			creds.add_certificate_file(cafile);
202 		Policy policy;
203 
204 		callbacks.print_sockname();
205 		callbacks.print_peername();
206 		Botan::TLS::Client client(callbacks, session_mgr, creds,
207 		    policy, rng);
208 		callbacks.set_channel(client);
209 
210 		while (!client.is_closed()) {
211 			ssize_t n;
212 
213 			n = recv(s, buf, sizeof(buf), 0);
214 			if (n < 0)
215 				err(1, "recv");
216 			if (n == 0)
217 				errx(1, "eof");
218 			client.received_data((uint8_t *)&buf, n);
219 		}
220 	}
221 
222 	if (close(s) == -1)
223 		err(1, "close");
224 
225 	cout <<"success" <<endl;
226 
227 	return 0;
228 }
229