1 /**
2  * @file stun/msg.c  STUN message encoding
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <string.h>
7 #include <re_types.h>
8 #include <re_mem.h>
9 #include <re_mbuf.h>
10 #include <re_sa.h>
11 #include <re_list.h>
12 #include <re_fmt.h>
13 #include <re_md5.h>
14 #include <re_sha.h>
15 #include <re_hmac.h>
16 #include <re_crc32.h>
17 #include <re_stun.h>
18 #include "stun.h"
19 
20 
21 enum {
22 	MI_SIZE = 24,
23 	FP_SIZE = 8
24 };
25 
26 
27 /**
28    Defines a STUN Message object
29 
30    <pre>
31 
32    .---------------------.      /|\           /|\
33    | STUN Header         |       |             |
34    |---------------------|       |             |
35    |         ....        |       |--------.    |
36    |      N Attributes   |       |        |    |----.
37    |         ....        |      \|/       |    |    |
38    |---------------------|                |    |    |
39    |  MESSAGE-INTEGRITY  | <-(HMAC-SHA1)--'   \|/   |
40    |---------------------|                          |
41    |     FINGERPRINT     | <-(CRC-32)---------------'
42    '---------------------'
43    </pre>
44 */
45 struct stun_msg {
46 	struct stun_hdr hdr;
47 	struct list attrl;
48 	struct mbuf *mb;
49 	size_t start;
50 };
51 
52 
fingerprint(const uint8_t * buf,size_t len)53 static uint32_t fingerprint(const uint8_t *buf, size_t len)
54 {
55 	return (uint32_t)crc32(0, buf, (unsigned int)len) ^ 0x5354554e;
56 }
57 
58 
destructor(void * arg)59 static void destructor(void *arg)
60 {
61 	struct stun_msg *msg = arg;
62 
63 	list_flush(&msg->attrl);
64 	mem_deref(msg->mb);
65 }
66 
67 
68 /**
69  * Decode a buffer to a STUN Message
70  *
71  * @param msgpp Pointer to allocation STUN message
72  * @param mb    Buffer containing the raw STUN packet
73  * @param ua    Unknown attributes (optional)
74  *
75  * @return 0 if success, otherwise errorcode
76  *
77  * @note `mb' will be referenced
78  */
stun_msg_decode(struct stun_msg ** msgpp,struct mbuf * mb,struct stun_unknown_attr * ua)79 int stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb,
80 		    struct stun_unknown_attr *ua)
81 {
82 	struct stun_msg *msg;
83 	struct stun_hdr hdr;
84 	size_t start, extra;
85 	int err;
86 
87 	if (!msgpp || !mb)
88 		return EINVAL;
89 
90 	start = mb->pos;
91 
92 	err = stun_hdr_decode(mb, &hdr);
93 	if (err) {
94 		mb->pos = start;
95 		return err;
96 	}
97 
98 	msg = mem_zalloc(sizeof(*msg), destructor);
99 	if (!msg) {
100 		mb->pos = start;
101 		return ENOMEM;
102 	}
103 
104 	msg->hdr = hdr;
105 	msg->mb = mem_ref(mb);
106 	msg->start = start;
107 
108 	if (ua)
109 		ua->typec = 0;
110 
111 	/* mbuf_get_left(mb) >= hdr.len checked in stun_hdr_decode() above */
112 	extra = mbuf_get_left(mb) - hdr.len;
113 
114 	while (mbuf_get_left(mb) - extra >= 4) {
115 
116 		struct stun_attr *attr;
117 
118 		err = stun_attr_decode(&attr, mb, hdr.tid, ua);
119 		if (err)
120 			break;
121 
122 		list_append(&msg->attrl, &attr->le, attr);
123 	}
124 
125 	if (err)
126 		mem_deref(msg);
127 	else
128 		*msgpp = msg;
129 
130 	mb->pos = start;
131 
132 	return err;
133 }
134 
135 
136 /**
137  * Get the STUN message type
138  *
139  * @param msg STUN Message
140  *
141  * @return STUN Message type
142  */
stun_msg_type(const struct stun_msg * msg)143 uint16_t stun_msg_type(const struct stun_msg *msg)
144 {
145 	return msg ? msg->hdr.type : 0;
146 }
147 
148 
149 /**
150  * Get the STUN message class
151  *
152  * @param msg STUN Message
153  *
154  * @return STUN Message class
155  */
stun_msg_class(const struct stun_msg * msg)156 uint16_t stun_msg_class(const struct stun_msg *msg)
157 {
158 	return STUN_CLASS(stun_msg_type(msg));
159 }
160 
161 
162 /**
163  * Get the STUN message method
164  *
165  * @param msg STUN Message
166  *
167  * @return STUN Message method
168  */
stun_msg_method(const struct stun_msg * msg)169 uint16_t stun_msg_method(const struct stun_msg *msg)
170 {
171 	return STUN_METHOD(stun_msg_type(msg));
172 }
173 
174 
175 /**
176  * Get the STUN message Transaction-ID
177  *
178  * @param msg STUN Message
179  *
180  * @return STUN Message Transaction-ID
181  */
stun_msg_tid(const struct stun_msg * msg)182 const uint8_t *stun_msg_tid(const struct stun_msg *msg)
183 {
184 	return msg ? msg->hdr.tid : NULL;
185 }
186 
187 
188 /**
189  * Check if a STUN Message has the magic cookie
190  *
191  * @param msg STUN Message
192  *
193  * @return true if Magic Cookie, otherwise false
194  */
stun_msg_mcookie(const struct stun_msg * msg)195 bool stun_msg_mcookie(const struct stun_msg *msg)
196 {
197 	return msg && (STUN_MAGIC_COOKIE == msg->hdr.cookie);
198 }
199 
200 
201 /**
202  * Lookup a STUN attribute in a STUN message
203  *
204  * @param msg  STUN Message
205  * @param type STUN Attribute type
206  *
207  * @return STUN Attribute if found, otherwise NULL
208  */
stun_msg_attr(const struct stun_msg * msg,uint16_t type)209 struct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type)
210 {
211 	struct le *le = msg ? list_head(&msg->attrl) : NULL;
212 
213 	while (le) {
214 		struct stun_attr *attr = le->data;
215 
216 		le = le->next;
217 
218 		if (attr->type == type)
219 			return attr;
220 	}
221 
222 	return NULL;
223 }
224 
225 
226 /**
227  * Apply a function handler to all STUN attribute
228  *
229  * @param msg  STUN Message
230  * @param h    Attribute handler
231  * @param arg  Handler argument
232  *
233  * @return STUN attribute if handler returned true, otherwise NULL
234  */
stun_msg_attr_apply(const struct stun_msg * msg,stun_attr_h * h,void * arg)235 struct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg,
236 				      stun_attr_h *h, void *arg)
237 {
238 	struct le *le = msg ? list_head(&msg->attrl) : NULL;
239 
240 	while (le) {
241 		struct stun_attr *attr = le->data;
242 
243 		le = le->next;
244 
245 		if (h && h(attr, arg))
246 			return (attr);
247 	}
248 
249 	return NULL;
250 }
251 
252 
253 /**
254  * Encode a STUN message
255  *
256  * @param mb      Buffer to encode message into
257  * @param method  STUN Method
258  * @param class   STUN Method class
259  * @param tid     Transaction ID
260  * @param ec      STUN error code (optional)
261  * @param key     Authentication key (optional)
262  * @param keylen  Number of bytes in authentication key
263  * @param fp      Use STUN Fingerprint attribute
264  * @param padding Padding byte
265  * @param attrc   Number of attributes to encode (variable arguments)
266  * @param ap      Variable list of attribute-tuples
267  *                Each attribute has 2 arguments, attribute type and value
268  *
269  * @return 0 if success, otherwise errorcode
270  */
stun_msg_vencode(struct mbuf * mb,uint16_t method,uint8_t class,const uint8_t * tid,const struct stun_errcode * ec,const uint8_t * key,size_t keylen,bool fp,uint8_t padding,uint32_t attrc,va_list ap)271 int stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t class,
272 		     const uint8_t *tid, const struct stun_errcode *ec,
273 		     const uint8_t *key, size_t keylen, bool fp,
274 		     uint8_t padding, uint32_t attrc, va_list ap)
275 {
276 	struct stun_hdr hdr;
277 	size_t start;
278 	int err = 0;
279 	uint32_t i;
280 
281 	if (!mb || !tid)
282 		return EINVAL;
283 
284 	start = mb->pos;
285 	mb->pos += STUN_HEADER_SIZE;
286 
287 	hdr.type   = STUN_TYPE(method, class);
288 	hdr.cookie = STUN_MAGIC_COOKIE;
289 	memcpy(hdr.tid, tid, STUN_TID_SIZE);
290 
291 	if (ec)
292 		err |= stun_attr_encode(mb, STUN_ATTR_ERR_CODE, ec,
293 					NULL, padding);
294 
295 	for (i=0; i<attrc; i++) {
296 
297 		uint16_t type = va_arg(ap, int);
298 		const void *v = va_arg(ap, const void *);
299 
300 		if (!v)
301 			continue;
302 
303 		err |= stun_attr_encode(mb, type, v, hdr.tid, padding);
304 	}
305 
306 	/* header */
307 	hdr.len = mb->pos - start - STUN_HEADER_SIZE + (key ? MI_SIZE : 0);
308 	mb->pos = start;
309 	err |= stun_hdr_encode(mb, &hdr);
310 	mb->pos += hdr.len - (key ? MI_SIZE : 0);
311 
312 	if (key) {
313 		uint8_t mi[20];
314 
315 		mb->pos = start;
316 		hmac_sha1(key, keylen, mbuf_buf(mb), mbuf_get_left(mb),
317 			  mi, sizeof(mi));
318 
319 		mb->pos += STUN_HEADER_SIZE + hdr.len - MI_SIZE;
320 		err |= stun_attr_encode(mb, STUN_ATTR_MSG_INTEGRITY, mi,
321 					NULL, padding);
322 	}
323 
324 	if (fp) {
325 		uint32_t fprnt;
326 
327 		/* header */
328 		hdr.len = mb->pos - start - STUN_HEADER_SIZE + FP_SIZE;
329 		mb->pos = start;
330 		err |= stun_hdr_encode(mb, &hdr);
331 
332 		mb->pos = start;
333 		fprnt = fingerprint(mbuf_buf(mb), mbuf_get_left(mb));
334 
335 		mb->pos += STUN_HEADER_SIZE + hdr.len - FP_SIZE;
336 		err |= stun_attr_encode(mb, STUN_ATTR_FINGERPRINT, &fprnt,
337 					NULL, padding);
338 	}
339 
340 	return err;
341 }
342 
343 
344 /**
345  * Encode a STUN message
346  *
347  * @param mb      Buffer to encode message into
348  * @param method  STUN Method
349  * @param class   STUN Method class
350  * @param tid     Transaction ID
351  * @param ec      STUN error code (optional)
352  * @param key     Authentication key (optional)
353  * @param keylen  Number of bytes in authentication key
354  * @param fp      Use STUN Fingerprint attribute
355  * @param padding Padding byte
356  * @param attrc   Number of attributes to encode (variable arguments)
357  * @param ...     Variable list of attribute-tuples
358  *                Each attribute has 2 arguments, attribute type and value
359  *
360  * @return 0 if success, otherwise errorcode
361  */
stun_msg_encode(struct mbuf * mb,uint16_t method,uint8_t class,const uint8_t * tid,const struct stun_errcode * ec,const uint8_t * key,size_t keylen,bool fp,uint8_t padding,uint32_t attrc,...)362 int stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t class,
363 		    const uint8_t *tid, const struct stun_errcode *ec,
364 		    const uint8_t *key, size_t keylen, bool fp,
365 		    uint8_t padding, uint32_t attrc, ...)
366 {
367 	va_list ap;
368 	int err;
369 
370 	va_start(ap, attrc);
371 	err = stun_msg_vencode(mb, method, class, tid, ec, key, keylen, fp,
372 			       padding, attrc, ap);
373 	va_end(ap);
374 
375 	return err;
376 }
377 
378 
379 /**
380  * Verify the Message-Integrity of a STUN message
381  *
382  * @param msg    STUN Message
383  * @param key    Authentication key
384  * @param keylen Number of bytes in authentication key
385  *
386  * @return 0 if verified, otherwise errorcode
387  */
stun_msg_chk_mi(const struct stun_msg * msg,const uint8_t * key,size_t keylen)388 int stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key,
389 		    size_t keylen)
390 {
391 	uint8_t hmac[SHA_DIGEST_LENGTH];
392 	struct stun_attr *mi, *fp;
393 
394 	if (!msg)
395 		return EINVAL;
396 
397 	mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY);
398 	if (!mi)
399 		return EPROTO;
400 
401 	msg->mb->pos = msg->start;
402 
403 	fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);
404 	if (fp) {
405 		((struct stun_msg *)msg)->hdr.len -= FP_SIZE;
406 		(void)stun_hdr_encode(msg->mb, &msg->hdr);
407 		msg->mb->pos -= STUN_HEADER_SIZE;
408 	}
409 
410 	hmac_sha1(key, keylen, mbuf_buf(msg->mb),
411 		  STUN_HEADER_SIZE + msg->hdr.len - MI_SIZE,
412 		  hmac, sizeof(hmac));
413 
414 	if (fp) {
415 		((struct stun_msg *)msg)->hdr.len += FP_SIZE;
416 		(void)stun_hdr_encode(msg->mb, &msg->hdr);
417 		msg->mb->pos -= STUN_HEADER_SIZE;
418 	}
419 
420 	if (memcmp(mi->v.msg_integrity, hmac, SHA_DIGEST_LENGTH))
421 		return EBADMSG;
422 
423 	return 0;
424 }
425 
426 
427 /**
428  * Check the Fingerprint of a STUN message
429  *
430  * @param msg STUN Message
431  *
432  * @return 0 if fingerprint matches, otherwise errorcode
433  */
stun_msg_chk_fingerprint(const struct stun_msg * msg)434 int stun_msg_chk_fingerprint(const struct stun_msg *msg)
435 {
436 	struct stun_attr *fp;
437 	uint32_t fprnt;
438 
439 	if (!msg)
440 		return EINVAL;
441 
442 	fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);
443 	if (!fp)
444 		return EPROTO;
445 
446 	msg->mb->pos = msg->start;
447 
448 	fprnt = fingerprint(mbuf_buf(msg->mb),
449 			    STUN_HEADER_SIZE + msg->hdr.len - FP_SIZE);
450 
451 	if (fprnt != fp->v.fingerprint)
452 		return EBADMSG;
453 
454 	return 0;
455 }
456 
457 
attr_print(const struct stun_attr * attr,void * arg)458 static bool attr_print(const struct stun_attr *attr, void *arg)
459 {
460 	(void)arg;
461 
462 	stun_attr_dump(attr);
463 
464 	return false;
465 }
466 
467 
468 /**
469  * Print a STUN message to STDOUT
470  *
471  * @param msg STUN Message
472  */
stun_msg_dump(const struct stun_msg * msg)473 void stun_msg_dump(const struct stun_msg *msg)
474 {
475 	if (!msg)
476 		return;
477 
478 	(void)re_printf("%s %s (len=%u cookie=%08x tid=%w)\n",
479 			stun_method_name(stun_msg_method(msg)),
480 			stun_class_name(stun_msg_class(msg)),
481 			msg->hdr.len, msg->hdr.cookie,
482 			msg->hdr.tid, sizeof(msg->hdr.tid));
483 
484 	stun_msg_attr_apply(msg, attr_print, NULL);
485 }
486