1 #include "tinydtls.h"
2 
3 /* This is needed for apple */
4 #define __APPLE_USE_RFC_3542
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <ctype.h>
11 #include <netinet/in.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/time.h>
15 #include <arpa/inet.h>
16 #include <netdb.h>
17 #include <signal.h>
18 
19 #include "global.h"
20 #include "debug.h"
21 #include "dtls.h"
22 
23 #define DEFAULT_PORT 20220
24 
25 #define PSK_DEFAULT_IDENTITY "Client_identity"
26 #define PSK_DEFAULT_KEY      "secretPSK"
27 #define PSK_OPTIONS          "i:k:"
28 
29 #ifdef __GNUC__
30 #define UNUSED_PARAM __attribute__((unused))
31 #else
32 #define UNUSED_PARAM
33 #endif /* __GNUC__ */
34 
35 static char buf[200];
36 static size_t len = 0;
37 
38 typedef struct {
39   size_t length;               /* length of string */
40   unsigned char *s;            /* string data */
41 } dtls_str;
42 
43 static dtls_str output_file = { 0, NULL }; /* output file name */
44 
45 static dtls_context_t *dtls_context = NULL;
46 
47 
48 static const unsigned char ecdsa_priv_key[] = {
49 			0x41, 0xC1, 0xCB, 0x6B, 0x51, 0x24, 0x7A, 0x14,
50 			0x43, 0x21, 0x43, 0x5B, 0x7A, 0x80, 0xE7, 0x14,
51 			0x89, 0x6A, 0x33, 0xBB, 0xAD, 0x72, 0x94, 0xCA,
52 			0x40, 0x14, 0x55, 0xA1, 0x94, 0xA9, 0x49, 0xFA};
53 
54 static const unsigned char ecdsa_pub_key_x[] = {
55 			0x36, 0xDF, 0xE2, 0xC6, 0xF9, 0xF2, 0xED, 0x29,
56 			0xDA, 0x0A, 0x9A, 0x8F, 0x62, 0x68, 0x4E, 0x91,
57 			0x63, 0x75, 0xBA, 0x10, 0x30, 0x0C, 0x28, 0xC5,
58 			0xE4, 0x7C, 0xFB, 0xF2, 0x5F, 0xA5, 0x8F, 0x52};
59 
60 static const unsigned char ecdsa_pub_key_y[] = {
61 			0x71, 0xA0, 0xD4, 0xFC, 0xDE, 0x1A, 0xB8, 0x78,
62 			0x5A, 0x3C, 0x78, 0x69, 0x35, 0xA7, 0xCF, 0xAB,
63 			0xE9, 0x3F, 0x98, 0x72, 0x09, 0xDA, 0xED, 0x0B,
64 			0x4F, 0xAB, 0xC3, 0x6F, 0xC7, 0x72, 0xF8, 0x29};
65 
66 #ifdef DTLS_PSK
67 ssize_t
read_from_file(char * arg,unsigned char * buf,size_t max_buf_len)68 read_from_file(char *arg, unsigned char *buf, size_t max_buf_len) {
69   FILE *f;
70   ssize_t result = 0;
71 
72   f = fopen(arg, "r");
73   if (f == NULL)
74     return -1;
75 
76   while (!feof(f)) {
77     size_t bytes_read;
78     bytes_read = fread(buf, 1, max_buf_len, f);
79     if (ferror(f)) {
80       result = -1;
81       break;
82     }
83 
84     buf += bytes_read;
85     result += bytes_read;
86     max_buf_len -= bytes_read;
87   }
88 
89   fclose(f);
90   return result;
91 }
92 
93 /* The PSK information for DTLS */
94 #define PSK_ID_MAXLEN 256
95 #define PSK_MAXLEN 256
96 static unsigned char psk_id[PSK_ID_MAXLEN];
97 static size_t psk_id_length = 0;
98 static unsigned char psk_key[PSK_MAXLEN];
99 static size_t psk_key_length = 0;
100 
101 /* This function is the "key store" for tinyDTLS. It is called to
102  * retrieve a key for the given identity within this particular
103  * session. */
104 static int
get_psk_info(struct dtls_context_t * ctx UNUSED_PARAM,const session_t * session UNUSED_PARAM,dtls_credentials_type_t type,const unsigned char * id,size_t id_len,unsigned char * result,size_t result_length)105 get_psk_info(struct dtls_context_t *ctx UNUSED_PARAM,
106 	    const session_t *session UNUSED_PARAM,
107 	    dtls_credentials_type_t type,
108 	    const unsigned char *id, size_t id_len,
109 	    unsigned char *result, size_t result_length) {
110 
111   switch (type) {
112   case DTLS_PSK_IDENTITY:
113     if (id_len) {
114       dtls_debug("got psk_identity_hint: '%.*s'\n", id_len, id);
115     }
116 
117     if (result_length < psk_id_length) {
118       dtls_warn("cannot set psk_identity -- buffer too small\n");
119       return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
120     }
121 
122     memcpy(result, psk_id, psk_id_length);
123     return psk_id_length;
124   case DTLS_PSK_KEY:
125     if (id_len != psk_id_length || memcmp(psk_id, id, id_len) != 0) {
126       dtls_warn("PSK for unknown id requested, exiting\n");
127       return dtls_alert_fatal_create(DTLS_ALERT_ILLEGAL_PARAMETER);
128     } else if (result_length < psk_key_length) {
129       dtls_warn("cannot set psk -- buffer too small\n");
130       return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
131     }
132 
133     memcpy(result, psk_key, psk_key_length);
134     return psk_key_length;
135   default:
136     dtls_warn("unsupported request type: %d\n", type);
137   }
138 
139   return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
140 }
141 #endif /* DTLS_PSK */
142 
143 #ifdef DTLS_ECC
144 static int
get_ecdsa_key(struct dtls_context_t * ctx,const session_t * session,const dtls_ecdsa_key_t ** result)145 get_ecdsa_key(struct dtls_context_t *ctx,
146 	      const session_t *session,
147 	      const dtls_ecdsa_key_t **result) {
148   static const dtls_ecdsa_key_t ecdsa_key = {
149     .curve = DTLS_ECDH_CURVE_SECP256R1,
150     .priv_key = ecdsa_priv_key,
151     .pub_key_x = ecdsa_pub_key_x,
152     .pub_key_y = ecdsa_pub_key_y
153   };
154 
155   *result = &ecdsa_key;
156   return 0;
157 }
158 
159 static int
verify_ecdsa_key(struct dtls_context_t * ctx,const session_t * session,const unsigned char * other_pub_x,const unsigned char * other_pub_y,size_t key_size)160 verify_ecdsa_key(struct dtls_context_t *ctx,
161 		 const session_t *session,
162 		 const unsigned char *other_pub_x,
163 		 const unsigned char *other_pub_y,
164 		 size_t key_size) {
165   return 0;
166 }
167 #endif /* DTLS_ECC */
168 
169 static void
try_send(struct dtls_context_t * ctx,session_t * dst)170 try_send(struct dtls_context_t *ctx, session_t *dst) {
171   int res;
172   res = dtls_write(ctx, dst, (uint8 *)buf, len);
173   if (res >= 0) {
174     memmove(buf, buf + res, len - res);
175     len -= res;
176   }
177 }
178 
179 static void
handle_stdin()180 handle_stdin() {
181   if (fgets(buf + len, sizeof(buf) - len, stdin))
182     len += strlen(buf + len);
183 }
184 
185 static int
read_from_peer(struct dtls_context_t * ctx,session_t * session,uint8 * data,size_t len)186 read_from_peer(struct dtls_context_t *ctx,
187 	       session_t *session, uint8 *data, size_t len) {
188   size_t i;
189   for (i = 0; i < len; i++)
190     printf("%c", data[i]);
191   return 0;
192 }
193 
194 static int
send_to_peer(struct dtls_context_t * ctx,session_t * session,uint8 * data,size_t len)195 send_to_peer(struct dtls_context_t *ctx,
196 	     session_t *session, uint8 *data, size_t len) {
197 
198   int fd = *(int *)dtls_get_app_data(ctx);
199   return sendto(fd, data, len, MSG_DONTWAIT,
200 		&session->addr.sa, session->size);
201 }
202 
203 static int
dtls_handle_read(struct dtls_context_t * ctx)204 dtls_handle_read(struct dtls_context_t *ctx) {
205   int fd;
206   session_t session;
207 #define MAX_READ_BUF 2000
208   static uint8 buf[MAX_READ_BUF];
209   int len;
210 
211   fd = *(int *)dtls_get_app_data(ctx);
212 
213   if (!fd)
214     return -1;
215 
216   memset(&session, 0, sizeof(session_t));
217   session.size = sizeof(session.addr);
218   len = recvfrom(fd, buf, MAX_READ_BUF, 0,
219 		 &session.addr.sa, &session.size);
220 
221   if (len < 0) {
222     perror("recvfrom");
223     return -1;
224   } else {
225     dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "peer", &session);
226     dtls_debug_dump("bytes from peer", buf, len);
227   }
228 
229   return dtls_handle_message(ctx, &session, buf, len);
230 }
231 
dtls_handle_signal(int sig)232 static void dtls_handle_signal(int sig)
233 {
234   dtls_free_context(dtls_context);
235   signal(sig, SIG_DFL);
236   kill(getpid(), sig);
237 }
238 
239 /* stolen from libcoap: */
240 static int
resolve_address(const char * server,struct sockaddr * dst)241 resolve_address(const char *server, struct sockaddr *dst) {
242 
243   struct addrinfo *res, *ainfo;
244   struct addrinfo hints;
245   static char addrstr[256];
246   int error;
247 
248   memset(addrstr, 0, sizeof(addrstr));
249   if (server && strlen(server) > 0)
250     memcpy(addrstr, server, strlen(server));
251   else
252     memcpy(addrstr, "localhost", 9);
253 
254   memset ((char *)&hints, 0, sizeof(hints));
255   hints.ai_socktype = SOCK_DGRAM;
256   hints.ai_family = AF_UNSPEC;
257 
258   error = getaddrinfo(addrstr, "", &hints, &res);
259 
260   if (error != 0) {
261     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
262     return error;
263   }
264 
265   for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
266 
267     switch (ainfo->ai_family) {
268     case AF_INET6:
269     case AF_INET:
270 
271       memcpy(dst, ainfo->ai_addr, ainfo->ai_addrlen);
272       return ainfo->ai_addrlen;
273     default:
274       ;
275     }
276   }
277 
278   freeaddrinfo(res);
279   return -1;
280 }
281 
282 /*---------------------------------------------------------------------------*/
283 static void
usage(const char * program,const char * version)284 usage( const char *program, const char *version) {
285   const char *p;
286 
287   p = strrchr( program, '/' );
288   if ( p )
289     program = ++p;
290 
291   fprintf(stderr, "%s v%s -- DTLS client implementation\n"
292 	  "(c) 2011-2014 Olaf Bergmann <bergmann@tzi.org>\n\n"
293 #ifdef DTLS_PSK
294 	  "usage: %s [-i file] [-k file] [-o file] [-p port] [-v num] addr [port]\n"
295 #else /*  DTLS_PSK */
296 	  "usage: %s [-o file] [-p port] [-v num] addr [port]\n"
297 #endif /* DTLS_PSK */
298 #ifdef DTLS_PSK
299 	  "\t-i file\t\tread PSK identity from file\n"
300 	  "\t-k file\t\tread pre-shared key from file\n"
301 #endif /* DTLS_PSK */
302 	  "\t-o file\t\toutput received data to this file (use '-' for STDOUT)\n"
303 	  "\t-p port\t\tlisten on specified port (default is %d)\n"
304 	  "\t-v num\t\tverbosity level (default: 3)\n",
305 	   program, version, program, DEFAULT_PORT);
306 }
307 
handle_event(struct dtls_context_t * ctx,session_t * session,dtls_alert_level_t level,unsigned short code)308 static int handle_event(struct dtls_context_t *ctx, session_t *session,
309 			dtls_alert_level_t level, unsigned short code)
310 {
311 	printf("event: level %d code %d\n", level, code);
312 	if (level > 0) {
313 		/* alert code, quit */
314 	} else if (level == 0) {
315 		/* internal event */
316 		if (code == DTLS_EVENT_CONNECTED) {
317 			/* We can send data now */
318 		}
319 	}
320 
321 	return 0;
322 }
323 
324 static dtls_handler_t cb = {
325   .write = send_to_peer,
326   .read  = read_from_peer,
327   .event = handle_event,
328 #ifdef DTLS_PSK
329   .get_psk_info = get_psk_info,
330 #endif /* DTLS_PSK */
331 #ifdef DTLS_ECC
332   .get_ecdsa_key = get_ecdsa_key,
333   .verify_ecdsa_key = verify_ecdsa_key
334 #endif /* DTLS_ECC */
335 };
336 
337 #define DTLS_CLIENT_CMD_CLOSE "client:close"
338 #define DTLS_CLIENT_CMD_RENEGOTIATE "client:renegotiate"
339 
340 int
main(int argc,char ** argv)341 main(int argc, char **argv) {
342   fd_set rfds, wfds;
343   struct timeval timeout;
344   unsigned short port = DEFAULT_PORT;
345   char port_str[NI_MAXSERV] = "0";
346   log_t log_level = DTLS_LOG_WARN;
347   int fd, result;
348   int on = 1;
349   int opt, res;
350   session_t dst;
351 
352   dtls_init();
353   snprintf(port_str, sizeof(port_str), "%d", port);
354 
355 #ifdef DTLS_PSK
356   psk_id_length = strlen(PSK_DEFAULT_IDENTITY);
357   psk_key_length = strlen(PSK_DEFAULT_KEY);
358   memcpy(psk_id, PSK_DEFAULT_IDENTITY, psk_id_length);
359   memcpy(psk_key, PSK_DEFAULT_KEY, psk_key_length);
360 #endif /* DTLS_PSK */
361 
362   while ((opt = getopt(argc, argv, "p:o:v:" PSK_OPTIONS)) != -1) {
363     switch (opt) {
364 #ifdef DTLS_PSK
365     case 'i' : {
366       ssize_t result = read_from_file(optarg, psk_id, PSK_ID_MAXLEN);
367       if (result < 0) {
368 	dtls_warn("cannot read PSK identity\n");
369       } else {
370 	psk_id_length = result;
371       }
372       break;
373     }
374     case 'k' : {
375       ssize_t result = read_from_file(optarg, psk_key, PSK_MAXLEN);
376       if (result < 0) {
377 	dtls_warn("cannot read PSK\n");
378       } else {
379 	psk_key_length = result;
380       }
381       break;
382     }
383 #endif /* DTLS_PSK */
384     case 'p' :
385       strncpy(port_str, optarg, NI_MAXSERV-1);
386       port_str[NI_MAXSERV - 1] = '\0';
387       break;
388     case 'o' :
389       output_file.length = strlen(optarg);
390       output_file.s = (unsigned char *)malloc(output_file.length + 1);
391 
392       if (!output_file.s) {
393 	dtls_crit("cannot set output file: insufficient memory\n");
394 	exit(-1);
395       } else {
396 	/* copy filename including trailing zero */
397 	memcpy(output_file.s, optarg, output_file.length + 1);
398       }
399       break;
400     case 'v' :
401       log_level = strtol(optarg, NULL, 10);
402       break;
403     default:
404       usage(argv[0], dtls_package_version());
405       exit(1);
406     }
407   }
408 
409   dtls_set_log_level(log_level);
410 
411   if (argc <= optind) {
412     usage(argv[0], dtls_package_version());
413     exit(1);
414   }
415 
416   memset(&dst, 0, sizeof(session_t));
417   /* resolve destination address where server should be sent */
418   res = resolve_address(argv[optind++], &dst.addr.sa);
419   if (res < 0) {
420     dtls_emerg("failed to resolve address\n");
421     exit(-1);
422   }
423   dst.size = res;
424 
425   /* use port number from command line when specified or the listen
426      port, otherwise */
427   dst.addr.sin.sin_port = htons(atoi(optind < argc ? argv[optind++] : port_str));
428 
429 
430   /* init socket and set it to non-blocking */
431   fd = socket(dst.addr.sa.sa_family, SOCK_DGRAM, 0);
432 
433   if (fd < 0) {
434     dtls_alert("socket: %s\n", strerror(errno));
435     return 0;
436   }
437 
438   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
439     dtls_alert("setsockopt SO_REUSEADDR: %s\n", strerror(errno));
440   }
441 #if 0
442   flags = fcntl(fd, F_GETFL, 0);
443   if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
444     dtls_alert("fcntl: %s\n", strerror(errno));
445     goto error;
446   }
447 #endif
448   on = 1;
449 #ifdef IPV6_RECVPKTINFO
450   if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on) ) < 0) {
451 #else /* IPV6_RECVPKTINFO */
452   if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on) ) < 0) {
453 #endif /* IPV6_RECVPKTINFO */
454     dtls_alert("setsockopt IPV6_PKTINFO: %s\n", strerror(errno));
455   }
456 
457   if (signal(SIGINT, dtls_handle_signal) == SIG_ERR) {
458     dtls_alert("An error occurred while setting a signal handler.\n");
459     return EXIT_FAILURE;
460   }
461 
462   dtls_context = dtls_new_context(&fd);
463   if (!dtls_context) {
464     dtls_emerg("cannot create context\n");
465     exit(-1);
466   }
467 
468   dtls_set_handler(dtls_context, &cb);
469 
470   dtls_connect(dtls_context, &dst);
471 
472   while (1) {
473     FD_ZERO(&rfds);
474     FD_ZERO(&wfds);
475 
476     FD_SET(fileno(stdin), &rfds);
477     FD_SET(fd, &rfds);
478     /* FD_SET(fd, &wfds); */
479 
480     timeout.tv_sec = 5;
481     timeout.tv_usec = 0;
482 
483     result = select(fd+1, &rfds, &wfds, 0, &timeout);
484 
485     if (result < 0) {		/* error */
486       if (errno != EINTR)
487 	perror("select");
488     } else if (result == 0) {	/* timeout */
489     } else {			/* ok */
490       if (FD_ISSET(fd, &wfds))
491 	/* FIXME */;
492       else if (FD_ISSET(fd, &rfds))
493 	dtls_handle_read(dtls_context);
494       else if (FD_ISSET(fileno(stdin), &rfds))
495 	handle_stdin();
496     }
497 
498     if (len) {
499       if (len >= strlen(DTLS_CLIENT_CMD_CLOSE) &&
500 	  !memcmp(buf, DTLS_CLIENT_CMD_CLOSE, strlen(DTLS_CLIENT_CMD_CLOSE))) {
501 	printf("client: closing connection\n");
502 	dtls_close(dtls_context, &dst);
503 	len = 0;
504       } else if (len >= strlen(DTLS_CLIENT_CMD_RENEGOTIATE) &&
505 	         !memcmp(buf, DTLS_CLIENT_CMD_RENEGOTIATE, strlen(DTLS_CLIENT_CMD_RENEGOTIATE))) {
506 	printf("client: renegotiate connection\n");
507 	dtls_renegotiate(dtls_context, &dst);
508 	len = 0;
509       } else {
510 	try_send(dtls_context, &dst);
511       }
512     }
513   }
514 
515   dtls_free_context(dtls_context);
516   exit(0);
517 }
518 
519