1 /*
2  * Copyright (C) 2013 Nikos Mavrogiannopoulos
3  *
4  * This file is part of GnuTLS.
5  *
6  * GnuTLS is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuTLS is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GnuTLS; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 
28 #if defined(_WIN32) || !defined(ENABLE_ALPN)
29 
main(int argc,char ** argv)30 int main(int argc, char **argv)
31 {
32 	exit(77);
33 }
34 
35 #else
36 
37 #include <string.h>
38 #include <sys/types.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
41 #include <sys/wait.h>
42 #include <arpa/inet.h>
43 #include <unistd.h>
44 #include <gnutls/gnutls.h>
45 #include <gnutls/dtls.h>
46 
47 #include "utils.h"
48 
49 static void terminate(void);
50 
51 /* This program tests whether the gnutls_record_get_state() works as
52  * expected.
53  */
54 
server_log_func(int level,const char * str)55 static void server_log_func(int level, const char *str)
56 {
57 	fprintf(stderr, "server|<%d>| %s", level, str);
58 }
59 
client_log_func(int level,const char * str)60 static void client_log_func(int level, const char *str)
61 {
62 	fprintf(stderr, "client|<%d>| %s", level, str);
63 }
64 
65 /* These are global */
66 static pid_t child;
67 
68 /* A very basic DTLS client, with anonymous authentication, that negotiates SRTP
69  */
70 
dump(const char * name,uint8_t * data,unsigned data_size)71 static void dump(const char *name, uint8_t *data, unsigned data_size)
72 {
73 	unsigned i;
74 
75 	fprintf(stderr, "%s", name);
76 	for (i=0;i<data_size;i++)
77 		fprintf(stderr, "%.2x", (unsigned)data[i]);
78 	fprintf(stderr, "\n");
79 }
80 
terminate(void)81 static void terminate(void)
82 {
83 	int status = 0;
84 
85 	kill(child, SIGTERM);
86 	wait(&status);
87 	exit(1);
88 }
89 
client(int fd)90 static void client(int fd)
91 {
92 	gnutls_session_t session;
93 	int ret;
94 	gnutls_anon_client_credentials_t anoncred;
95 	gnutls_datum_t mac_key, iv, cipher_key;
96 	gnutls_datum_t read_mac_key, read_iv, read_cipher_key;
97 	unsigned char rseq_number[8];
98 	unsigned char wseq_number[8];
99 	unsigned char key_material[512], *p;
100 	unsigned i;
101 	unsigned block_size, hash_size, key_size, iv_size;
102 	const char *err;
103 	/* Need to enable anonymous KX specifically. */
104 
105 	global_init();
106 
107 	if (debug) {
108 		gnutls_global_set_log_function(client_log_func);
109 		gnutls_global_set_log_level(4711);
110 	}
111 
112 	gnutls_anon_allocate_client_credentials(&anoncred);
113 
114 	/* Initialize TLS session
115 	 */
116 	gnutls_init(&session, GNUTLS_CLIENT);
117 
118 	/* Use default priorities */
119 	ret = gnutls_priority_set_direct(session,
120 				   "NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+SIGN-ALL:+COMP-NULL:+ANON-DH:+ANON-ECDH:+CURVE-ALL",
121 				   &err);
122 	if (ret < 0) {
123 		fail("client: priority set failed (%s): %s\n",
124 		     gnutls_strerror(ret), err);
125 		exit(1);
126 	}
127 
128 	/* put the anonymous credentials to the current session
129 	 */
130 	gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
131 
132 	gnutls_transport_set_int(session, fd);
133 
134 	/* Perform the TLS handshake
135 	 */
136 	do {
137 		ret = gnutls_handshake(session);
138 	}
139 	while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
140 
141 	if (ret < 0) {
142 		fail("client: Handshake failed: %s\n", strerror(ret));
143 		terminate();
144 	} else {
145 		if (debug)
146 			success("client: Handshake was completed\n");
147 	}
148 
149 	if (debug)
150 		success("client: TLS version is: %s\n",
151 			gnutls_protocol_get_name
152 			(gnutls_protocol_get_version(session)));
153 
154 	ret = gnutls_cipher_get(session);
155 	if (ret != GNUTLS_CIPHER_AES_128_CBC) {
156 		fprintf(stderr, "negotiated unexpected cipher: %s\n", gnutls_cipher_get_name(ret));
157 		terminate();
158 	}
159 
160 	ret = gnutls_mac_get(session);
161 	if (ret != GNUTLS_MAC_SHA1) {
162 		fprintf(stderr, "negotiated unexpected mac: %s\n", gnutls_mac_get_name(ret));
163 		terminate();
164 	}
165 
166 	iv_size = 16;
167 	hash_size = 20;
168 	key_size = 16;
169 	block_size = 2*hash_size + 2*key_size + 2 *iv_size;
170 
171 	ret = gnutls_prf(session, 13, "key expansion", 1, 0, NULL, block_size,
172 			 (void*)key_material);
173 	if (ret < 0) {
174 		fprintf(stderr, "error in %d\n", __LINE__);
175 		gnutls_perror(ret);
176 		terminate();
177 	}
178 	p = key_material;
179 
180 	/* check whether the key material matches our calculations */
181 	ret = gnutls_record_get_state(session, 0, &mac_key, &iv, &cipher_key, wseq_number);
182 	if (ret < 0) {
183 		fprintf(stderr, "error in %d\n", __LINE__);
184 		gnutls_perror(ret);
185 		terminate();
186 	}
187 
188 	if (memcmp(wseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) {
189 		dump("wseq:", wseq_number, 8);
190 		fprintf(stderr, "error in %d\n", __LINE__);
191 		terminate();
192 	}
193 
194 	ret = gnutls_record_get_state(session, 1, &read_mac_key, &read_iv, &read_cipher_key, rseq_number);
195 	if (ret < 0) {
196 		fprintf(stderr, "error in %d\n", __LINE__);
197 		gnutls_perror(ret);
198 		terminate();
199 	}
200 
201 	if (memcmp(rseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) {
202 		dump("rseq:", rseq_number, 8);
203 		fprintf(stderr, "error in %d\n", __LINE__);
204 		terminate();
205 	}
206 
207 	if (hash_size != mac_key.size || memcmp(p, mac_key.data, hash_size) != 0) {
208 		dump("MAC:", mac_key.data, mac_key.size);
209 		dump("Block:", key_material, block_size);
210 		fprintf(stderr, "error in %d\n", __LINE__);
211 		terminate();
212 	}
213 	p+= hash_size;
214 
215 	if (hash_size != read_mac_key.size || memcmp(p, read_mac_key.data, hash_size) != 0) {
216 		dump("MAC:", read_mac_key.data, read_mac_key.size);
217 		dump("Block:", key_material, block_size);
218 		fprintf(stderr, "error in %d\n", __LINE__);
219 		terminate();
220 	}
221 	p+= hash_size;
222 
223 	if (key_size != cipher_key.size || memcmp(p, cipher_key.data, key_size) != 0) {
224 		fprintf(stderr, "error in %d\n", __LINE__);
225 		terminate();
226 	}
227 	p+= key_size;
228 
229 	if (key_size != read_cipher_key.size || memcmp(p, read_cipher_key.data, key_size) != 0) {
230 		fprintf(stderr, "error in %d\n", __LINE__);
231 		terminate();
232 	}
233 	p+= key_size;
234 
235 	if (iv_size != iv.size || memcmp(p, iv.data, iv_size) != 0) {
236 		fprintf(stderr, "error in %d\n", __LINE__);
237 		terminate();
238 	}
239 	p+=iv_size;
240 
241 	if (iv_size != read_iv.size || memcmp(p, read_iv.data, iv_size) != 0) {
242 		fprintf(stderr, "error in %d\n", __LINE__);
243 		terminate();
244 	}
245 
246 	/* check sequence numbers */
247 	for (i=0;i<5;i++) {
248 		ret = gnutls_record_send(session, "hello", 5);
249 		if (ret < 0) {
250 			fail("gnutls_record_send: %s\n", gnutls_strerror(ret));
251 		}
252 	}
253 
254 	ret = gnutls_record_get_state(session, 0, NULL, NULL, NULL, wseq_number);
255 	if (ret < 0) {
256 		fprintf(stderr, "error in %d\n", __LINE__);
257 		gnutls_perror(ret);
258 		terminate();
259 	}
260 
261 	if (memcmp(wseq_number, "\x00\x00\x00\x00\x00\x00\x00\x06", 8) != 0) {
262 		dump("wseq:", wseq_number, 8);
263 		fprintf(stderr, "error in %d\n", __LINE__);
264 		terminate();
265 	}
266 
267 	ret = gnutls_record_get_state(session, 1, NULL, NULL, NULL, rseq_number);
268 	if (ret < 0) {
269 		fprintf(stderr, "error in %d\n", __LINE__);
270 		gnutls_perror(ret);
271 		terminate();
272 	}
273 
274 	if (memcmp(rseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) {
275 		dump("wseq:", wseq_number, 8);
276 		fprintf(stderr, "error in %d\n", __LINE__);
277 		terminate();
278 	}
279 	gnutls_bye(session, GNUTLS_SHUT_WR);
280 
281 	close(fd);
282 
283 	gnutls_deinit(session);
284 
285 	gnutls_anon_free_client_credentials(anoncred);
286 
287 	gnutls_global_deinit();
288 }
289 
server(int fd)290 static void server(int fd)
291 {
292 	int ret;
293 	gnutls_session_t session;
294 	gnutls_anon_server_credentials_t anoncred;
295 	gnutls_dh_params_t dh_params;
296 	char buf[128];
297 	const gnutls_datum_t p3 =
298 	    { (unsigned char *) pkcs3, strlen(pkcs3) };
299 
300 	/* this must be called once in the program
301 	 */
302 	global_init();
303 
304 	if (debug) {
305 		gnutls_global_set_log_function(server_log_func);
306 		gnutls_global_set_log_level(4711);
307 	}
308 
309 	gnutls_anon_allocate_server_credentials(&anoncred);
310 	gnutls_dh_params_init(&dh_params);
311 	gnutls_dh_params_import_pkcs3(dh_params, &p3, GNUTLS_X509_FMT_PEM);
312 	gnutls_anon_set_server_dh_params(anoncred, dh_params);
313 
314 	gnutls_init(&session, GNUTLS_SERVER);
315 
316 	/* avoid calling all the priority functions, since the defaults
317 	 * are adequate.
318 	 */
319 	ret = gnutls_priority_set_direct(session,
320 				   "NORMAL:+ANON-DH:+ANON-ECDH:-VERS-ALL:+VERS-TLS1.0", NULL);
321 	if (ret < 0) {
322 		fail("server: priority set failed (%s)\n\n",
323 		     gnutls_strerror(ret));
324 		terminate();
325 	}
326 
327 	gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
328 
329 	gnutls_transport_set_int(session, fd);
330 
331 	do {
332 		ret = gnutls_handshake(session);
333 	}
334 	while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
335 	if (ret < 0) {
336 		close(fd);
337 		gnutls_deinit(session);
338 		fail("server: Handshake has failed (%s)\n\n",
339 		     gnutls_strerror(ret));
340 		terminate();
341 	}
342 	if (debug)
343 		success("server: Handshake was completed\n");
344 
345 	if (debug)
346 		success("server: TLS version is: %s\n",
347 			gnutls_protocol_get_name
348 			(gnutls_protocol_get_version(session)));
349 
350 	do {
351 		ret = gnutls_record_recv(session, buf, sizeof(buf));
352 	} while(ret > 0);
353 
354 	if (ret < 0) {
355 		fail("error: %s\n", gnutls_strerror(ret));
356 	}
357 
358 	/* do not wait for the peer to close the connection.
359 	 */
360 	gnutls_bye(session, GNUTLS_SHUT_WR);
361 
362 	close(fd);
363 	gnutls_deinit(session);
364 
365 	gnutls_anon_free_server_credentials(anoncred);
366 	gnutls_dh_params_deinit(dh_params);
367 
368 	gnutls_global_deinit();
369 
370 	if (debug)
371 		success("server: finished\n");
372 }
373 
start(void)374 static void start(void)
375 {
376 	int fd[2];
377 	int ret;
378 
379 	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
380 	if (ret < 0) {
381 		perror("socketpair");
382 		exit(1);
383 	}
384 
385 	child = fork();
386 	if (child < 0) {
387 		perror("fork");
388 		fail("fork");
389 		exit(1);
390 	}
391 
392 	if (child) {
393 		int status;
394 		/* parent */
395 
396 		server(fd[0]);
397 		wait(&status);
398 		check_wait_status(status);
399 	} else {
400 		close(fd[0]);
401 		client(fd[1]);
402 		exit(0);
403 	}
404 }
405 
doit(void)406 void doit(void)
407 {
408 	start();
409 }
410 
411 #endif				/* _WIN32 */
412