1 /*
2 Copyright (c) 2007, Adobe Systems, Incorporated
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 * Neither the name of Adobe Systems, Network Resonance nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <errno.h>
34 #include <csi_platform.h>
35
36 #ifdef WIN32
37 #include <winsock2.h>
38 #include <stdlib.h>
39 #include <io.h>
40 #include <time.h>
41 #else /* UNIX */
42 #include <string.h>
43 #endif /* end UNIX */
44 #include <assert.h>
45
46 #include "stun.h"
47 #include "stun_reg.h"
48 #include "registry.h"
49
50 static int
51 nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res);
52
53 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */
54 int
nr_stun_receive_message(nr_stun_message * req,nr_stun_message * msg)55 nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg)
56 {
57 int _status;
58 nr_stun_message_attribute *attr;
59
60 #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
61 /* if this message was generated by an RFC 3489 impementation,
62 * the call to nr_is_stun_message will fail, so skip that
63 * check and puke elsewhere if the message can't be decoded */
64 if (msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE
65 || msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE2) {
66 #endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
67 if (!nr_is_stun_message(msg->buffer, msg->length)) {
68 r_log(NR_LOG_STUN, LOG_WARNING, "Not a STUN message");
69 ABORT(R_REJECTED);
70 }
71 #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
72 }
73 #endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
74
75 if (req == 0) {
76 if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_REQUEST) {
77 r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message type: %03x", msg->header.type);
78 ABORT(R_REJECTED);
79 }
80 }
81 else {
82 if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_RESPONSE
83 && NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_ERROR_RESPONSE) {
84 r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message class: %03x", msg->header.type);
85 ABORT(R_REJECTED);
86 }
87
88 if (NR_STUN_GET_TYPE_METHOD(req->header.type) != NR_STUN_GET_TYPE_METHOD(msg->header.type)) {
89 r_log(NR_LOG_STUN,LOG_WARNING,"Inconsistent message method: %03x expected %03x", msg->header.type, req->header.type);
90 ABORT(R_REJECTED);
91 }
92
93 if (nr_stun_different_transaction(msg->buffer, msg->length, req)) {
94 r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized STUN transaction");
95 ABORT(R_REJECTED);
96 }
97 }
98
99 switch (msg->header.magic_cookie) {
100 case NR_STUN_MAGIC_COOKIE:
101 /* basically draft-ietf-behave-rfc3489bis-10.txt S 6 rules */
102
103 if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &attr)
104 && !attr->u.fingerprint.valid) {
105 r_log(NR_LOG_STUN, LOG_WARNING, "Invalid fingerprint");
106 ABORT(R_REJECTED);
107 }
108
109 break;
110
111 #ifdef USE_STUND_0_96
112 case NR_STUN_MAGIC_COOKIE2:
113 /* nothing to check in this case */
114 break;
115 #endif /* USE_STUND_0_96 */
116
117 default:
118 #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
119 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
120 #else
121 #ifdef NDEBUG
122 /* in deployment builds we should always see a recognized magic cookie */
123 r_log(NR_LOG_STUN, LOG_WARNING, "Missing Magic Cookie");
124 ABORT(R_REJECTED);
125 #else
126 /* ignore this condition because sometimes we like to pretend we're
127 * a server talking to old clients and their messages don't contain
128 * a magic cookie at all but rather the magic cookie field is part
129 * of their ID and therefore random */
130 #endif /* NDEBUG */
131 #endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
132 break;
133 }
134
135 _status=0;
136 abort:
137 return _status;
138 }
139
140 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1 */
141 int
nr_stun_process_request(nr_stun_message * req,nr_stun_message * res)142 nr_stun_process_request(nr_stun_message *req, nr_stun_message *res)
143 {
144 int _status;
145 #ifdef USE_STUN_PEDANTIC
146 int r;
147 nr_stun_attr_unknown_attributes unknown_attributes = { { 0 } };
148 nr_stun_message_attribute *attr;
149
150 if (req->comprehension_required_unknown_attributes > 0) {
151 nr_stun_form_error_response(req, res, 420, "Unknown Attributes");
152 r_log(NR_LOG_STUN, LOG_WARNING, "Request contains comprehension required but unknown attributes");
153
154 TAILQ_FOREACH(attr, &req->attributes, entry) {
155 if (attr->name == 0) {
156 /* unrecognized attribute */
157
158 /* should never happen, but truncate if it ever were to occur */
159 if (unknown_attributes.num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES)
160 break;
161
162 unknown_attributes.attribute[unknown_attributes.num_attributes++] = attr->type;
163 }
164 }
165
166 assert(req->comprehension_required_unknown_attributes + req->comprehension_optional_unknown_attributes == unknown_attributes.num_attributes);
167
168 if ((r=nr_stun_message_add_unknown_attributes_attribute(res, &unknown_attributes)))
169 ABORT(R_ALREADY);
170
171 ABORT(R_ALREADY);
172 }
173 #endif /* USE_STUN_PEDANTIC */
174
175 _status=0;
176 #ifdef USE_STUN_PEDANTIC
177 abort:
178 #endif /* USE_STUN_PEDANTIC */
179 return _status;
180 }
181
182 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3.2 */
183 int
nr_stun_process_indication(nr_stun_message * ind)184 nr_stun_process_indication(nr_stun_message *ind)
185 {
186 int _status;
187 #ifdef USE_STUN_PEDANTIC
188
189 if (ind->comprehension_required_unknown_attributes > 0) {
190 r_log(NR_LOG_STUN, LOG_WARNING, "Indication contains comprehension required but unknown attributes");
191 ABORT(R_REJECTED);
192 }
193 #endif /* USE_STUN_PEDANTIC */
194
195 _status=0;
196 #ifdef USE_STUN_PEDANTIC
197 abort:
198 #endif /* USE_STUN_PEDANTIC */
199 return _status;
200 }
201
202 /* RFC5389 S 7.3.3, except that we *also* allow a MAPPED_ADDRESS
203 to compensate for a bug in Google's STUN server where it
204 always returns MAPPED_ADDRESS.
205
206 Mozilla bug: 888274.
207 */
208 int
nr_stun_process_success_response(nr_stun_message * res)209 nr_stun_process_success_response(nr_stun_message *res)
210 {
211 int _status;
212
213 #ifdef USE_STUN_PEDANTIC
214 if (res->comprehension_required_unknown_attributes > 0) {
215 r_log(NR_LOG_STUN, LOG_WARNING, "Response contains comprehension required but unknown attributes");
216 ABORT(R_REJECTED);
217 }
218 #endif /* USE_STUN_PEDANTIC */
219
220 if (NR_STUN_GET_TYPE_METHOD(res->header.type) == NR_METHOD_BINDING) {
221 if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0) &&
222 ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) {
223 r_log(NR_LOG_STUN, LOG_WARNING, "Missing XOR-MAPPED-ADDRESS and MAPPED_ADDRESS");
224 ABORT(R_REJECTED);
225 }
226 }
227
228 _status=0;
229 abort:
230 return _status;
231 }
232
233 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3.4 */
234 int
nr_stun_process_error_response(nr_stun_message * res,UINT2 * error_code)235 nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code)
236 {
237 int _status;
238 nr_stun_message_attribute *attr;
239
240 if (res->comprehension_required_unknown_attributes > 0) {
241 r_log(NR_LOG_STUN, LOG_WARNING, "Error response contains comprehension required but unknown attributes");
242 ABORT(R_REJECTED);
243 }
244
245 if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, &attr)) {
246 r_log(NR_LOG_STUN, LOG_WARNING, "Missing ERROR-CODE");
247 ABORT(R_REJECTED);
248 }
249
250 *error_code = attr->u.error_code.number;
251
252 switch (attr->u.error_code.number / 100) {
253 case 3:
254 /* We do not treat STUN/300 as retryable. The TURN Allocate handling
255 * code will reset the ctx if appropriate. */
256 ABORT(R_REJECTED);
257 break;
258
259 case 4:
260 /* If the error code is 400 through 499, the client declares the
261 * transaction failed; in the case of 420 (Unknown Attribute), the
262 * response should contain a UNKNOWN-ATTRIBUTES attribute that gives
263 * additional information. */
264 if (attr->u.error_code.number == 420)
265 ABORT(R_REJECTED);
266
267 /* it may be possible to restart given the info that was received in
268 * this response, so retry */
269 ABORT(R_RETRY);
270 break;
271
272 case 5:
273 /* If the error code is 500 through 599, the client MAY resend the
274 * request; clients that do so MUST limit the number of times they do
275 * this. */
276 /* let the retransmit mechanism handle resending the request */
277 break;
278
279 default:
280 ABORT(R_REJECTED);
281 break;
282 }
283
284 /* the spec says: "The client then does any processing specified by the authentication
285 * mechanism (see Section 10). This may result in a new transaction
286 * attempt." -- but this is handled already elsewhere, so needn't be repeated
287 * in this function */
288
289 _status=0;
290 abort:
291 return _status;
292 }
293
294 /* draft-ietf-behave-rfc3489bis-10.txt S 10.1.2 */
295 int
nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message * msg,nr_stun_message * res)296 nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg,
297 nr_stun_message *res)
298 {
299 int _status;
300 nr_stun_message_attribute *attr;
301
302 switch (msg->header.magic_cookie) {
303 default:
304 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
305 /* drop thru */
306 case NR_STUN_MAGIC_COOKIE:
307 if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
308 nr_stun_form_error_response(msg, res, 400, "Missing MESSAGE-INTEGRITY");
309 ABORT(R_ALREADY);
310 }
311
312 if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, 0)) {
313 nr_stun_form_error_response(msg, res, 400, "Missing USERNAME");
314 ABORT(R_ALREADY);
315 }
316
317 if (attr->u.message_integrity.unknown_user) {
318 nr_stun_form_error_response(msg, res, 401, "Unrecognized USERNAME");
319 ABORT(R_ALREADY);
320 }
321
322 if (!attr->u.message_integrity.valid) {
323 nr_stun_form_error_response(msg, res, 401, "Bad MESSAGE-INTEGRITY");
324 ABORT(R_ALREADY);
325 }
326
327 break;
328
329 #ifdef USE_STUND_0_96
330 case NR_STUN_MAGIC_COOKIE2:
331 /* nothing to check in this case */
332 break;
333 #endif /* USE_STUND_0_96 */
334 }
335
336 _status=0;
337 abort:
338 return _status;
339 }
340
341 /* draft-ietf-behave-rfc3489bis-10.txt S 10.1.3 */
342 int
nr_stun_receive_response_short_term_auth(nr_stun_message * res)343 nr_stun_receive_response_short_term_auth(nr_stun_message *res)
344 {
345 int _status;
346 nr_stun_message_attribute *attr;
347
348 switch (res->header.magic_cookie) {
349 default:
350 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
351 /* drop thru */
352 case NR_STUN_MAGIC_COOKIE:
353 if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
354 r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY");
355 ABORT(R_REJECTED);
356 }
357
358 if (!attr->u.message_integrity.valid) {
359 r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY");
360 ABORT(R_REJECTED);
361 }
362
363 break;
364
365 #ifdef USE_STUND_0_96
366 case NR_STUN_MAGIC_COOKIE2:
367 /* nothing to check in this case */
368 break;
369 #endif /* USE_STUND_0_96 */
370 }
371
372 _status=0;
373 abort:
374 return _status;
375 }
376
377 static int
nr_stun_add_realm_and_nonce(int new_nonce,nr_stun_server_client * clnt,nr_stun_message * res)378 nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res)
379 {
380 int r,_status;
381 char *realm = 0;
382 char *nonce;
383 UINT2 size;
384
385 if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm)))
386 ABORT(r);
387
388 if ((r=nr_stun_message_add_realm_attribute(res, realm)))
389 ABORT(r);
390
391 if (clnt) {
392 if (strlen(clnt->nonce) < 1)
393 new_nonce = 1;
394
395 if (new_nonce) {
396 if (NR_reg_get_uint2(NR_STUN_REG_PREF_SERVER_NONCE_SIZE, &size))
397 size = 48;
398
399 if (size > (sizeof(clnt->nonce) - 1))
400 size = sizeof(clnt->nonce) - 1;
401
402 nr_random_alphanum(clnt->nonce, size);
403 clnt->nonce[size] = '\0';
404 }
405
406 nonce = clnt->nonce;
407 }
408 else {
409 /* user is not known, so use a bogus nonce since there's no way to
410 * store a good nonce with the client-specific data -- this nonce
411 * will be recognized as stale if the client attempts another
412 * request */
413 nonce = "STALE";
414 }
415
416 if ((r=nr_stun_message_add_nonce_attribute(res, nonce)))
417 ABORT(r);
418
419 _status=0;
420 abort:
421 #ifdef USE_TURN
422 assert(_status == 0); /* TODO: !nn! cleanup after I reimplmement TURN */
423 #endif
424 RFREE(realm);
425 return _status;
426 }
427
428 /* draft-ietf-behave-rfc3489bis-10.txt S 10.2.1 - 10.2.2 */
429 int
nr_stun_receive_request_long_term_auth(nr_stun_message * req,nr_stun_server_ctx * ctx,nr_stun_message * res)430 nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res)
431 {
432 int r,_status;
433 nr_stun_message_attribute *mi;
434 nr_stun_message_attribute *n;
435 nr_stun_server_client *clnt = 0;
436
437 switch (req->header.magic_cookie) {
438 default:
439 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
440 /* drop thru */
441 case NR_STUN_MAGIC_COOKIE:
442 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)) {
443 nr_stun_form_error_response(req, res, 400, "Missing USERNAME");
444 nr_stun_add_realm_and_nonce(0, 0, res);
445 ABORT(R_ALREADY);
446 }
447
448 if ((r=nr_stun_get_message_client(ctx, req, &clnt))) {
449 nr_stun_form_error_response(req, res, 401, "Unrecognized USERNAME");
450 nr_stun_add_realm_and_nonce(0, 0, res);
451 ABORT(R_ALREADY);
452 }
453
454 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, &mi)) {
455 nr_stun_form_error_response(req, res, 401, "Missing MESSAGE-INTEGRITY");
456 nr_stun_add_realm_and_nonce(0, clnt, res);
457 ABORT(R_ALREADY);
458 }
459
460 assert(!mi->u.message_integrity.unknown_user);
461
462 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_REALM, 0)) {
463 nr_stun_form_error_response(req, res, 400, "Missing REALM");
464 ABORT(R_ALREADY);
465 }
466
467 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_NONCE, &n)) {
468 nr_stun_form_error_response(req, res, 400, "Missing NONCE");
469 ABORT(R_ALREADY);
470 }
471
472 assert(sizeof(clnt->nonce) == sizeof(n->u.nonce));
473 if (strncmp(clnt->nonce, n->u.nonce, sizeof(n->u.nonce))) {
474 nr_stun_form_error_response(req, res, 438, "Stale NONCE");
475 nr_stun_add_realm_and_nonce(1, clnt, res);
476 ABORT(R_ALREADY);
477 }
478
479 if (!mi->u.message_integrity.valid) {
480 nr_stun_form_error_response(req, res, 401, "Bad MESSAGE-INTEGRITY");
481 nr_stun_add_realm_and_nonce(0, clnt, res);
482 ABORT(R_ALREADY);
483 }
484
485 break;
486
487 #ifdef USE_STUND_0_96
488 case NR_STUN_MAGIC_COOKIE2:
489 /* nothing to do in this case */
490 break;
491 #endif /* USE_STUND_0_96 */
492 }
493
494 _status=0;
495 abort:
496
497 return _status;
498 }
499
500 /* draft-ietf-behave-rfc3489bis-10.txt S 10.2.3 */
501 int
nr_stun_receive_response_long_term_auth(nr_stun_message * res,nr_stun_client_ctx * ctx)502 nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx)
503 {
504 int _status;
505 nr_stun_message_attribute *attr;
506
507 switch (res->header.magic_cookie) {
508 default:
509 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
510 /* drop thru */
511 case NR_STUN_MAGIC_COOKIE:
512 if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_REALM, &attr)) {
513 RFREE(ctx->realm);
514 ctx->realm = r_strdup(attr->u.realm);
515 if (!ctx->realm)
516 ABORT(R_NO_MEMORY);
517 }
518 else {
519 r_log(NR_LOG_STUN, LOG_WARNING, "Missing REALM");
520 ABORT(R_REJECTED);
521 }
522
523 if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_NONCE, &attr)) {
524 RFREE(ctx->nonce);
525 ctx->nonce = r_strdup(attr->u.nonce);
526 if (!ctx->nonce)
527 ABORT(R_NO_MEMORY);
528 }
529 else {
530 r_log(NR_LOG_STUN, LOG_WARNING, "Missing NONCE");
531 ABORT(R_REJECTED);
532 }
533
534 if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
535 if (!attr->u.message_integrity.valid) {
536 r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY");
537 ABORT(R_REJECTED);
538 }
539 }
540
541 break;
542
543 #ifdef USE_STUND_0_96
544 case NR_STUN_MAGIC_COOKIE2:
545 /* nothing to check in this case */
546 break;
547 #endif /* USE_STUND_0_96 */
548 }
549
550 _status=0;
551 abort:
552 return _status;
553 }
554
555