media/mtransport/third_party/nICEr/src/stun/stun_proc.c

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:418b80605745
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
34 static char *RCSSTRING __UNUSED__="$Id: stun_proc.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
35
36 #include <errno.h>
37 #include <csi_platform.h>
38
39 #ifdef WIN32
40 #include <winsock2.h>
41 #include <stdlib.h>
42 #include <io.h>
43 #include <time.h>
44 #else /* UNIX */
45 #include <string.h>
46 #endif /* end UNIX */
47 #include <assert.h>
48
49 #include "stun.h"
50 #include "stun_reg.h"
51 #include "registry.h"
52
53 static int
54 nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res);
55
56 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */
57 int
58 nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg)
59 {
60 int _status;
61 nr_stun_message_attribute *attr;
62
63 #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
64 /* if this message was generated by an RFC 3489 impementation,
65 * the call to nr_is_stun_message will fail, so skip that
66 * check and puke elsewhere if the message can't be decoded */
67 if (msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE
68 || msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE2) {
69 #endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
70 if (!nr_is_stun_message(msg->buffer, msg->length)) {
71 r_log(NR_LOG_STUN, LOG_WARNING, "Not a STUN message");
72 ABORT(R_REJECTED);
73 }
74 #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
75 }
76 #endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
77
78 if (req == 0) {
79 if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_REQUEST) {
80 r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message type: %03x", msg->header.type);
81 ABORT(R_REJECTED);
82 }
83 }
84 else {
85 if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_RESPONSE
86 && NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_ERROR_RESPONSE) {
87 r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message class: %03x", msg->header.type);
88 ABORT(R_REJECTED);
89 }
90
91 if (NR_STUN_GET_TYPE_METHOD(req->header.type) != NR_STUN_GET_TYPE_METHOD(msg->header.type)) {
92 r_log(NR_LOG_STUN,LOG_WARNING,"Inconsistent message method: %03x expected %03x", msg->header.type, req->header.type);
93 ABORT(R_REJECTED);
94 }
95
96 if (nr_stun_different_transaction(msg->buffer, msg->length, req)) {
97 r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized STUN transaction");
98 ABORT(R_REJECTED);
99 }
100 }
101
102 switch (msg->header.magic_cookie) {
103 case NR_STUN_MAGIC_COOKIE:
104 /* basically draft-ietf-behave-rfc3489bis-10.txt S 6 rules */
105
106 if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &attr)
107 && !attr->u.fingerprint.valid) {
108 r_log(NR_LOG_STUN, LOG_WARNING, "Invalid fingerprint");
109 ABORT(R_REJECTED);
110 }
111
112 break;
113
114 #ifdef USE_STUND_0_96
115 case NR_STUN_MAGIC_COOKIE2:
116 /* nothing to check in this case */
117 break;
118 #endif /* USE_STUND_0_96 */
119
120 default:
121 #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
122 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
123 #else
124 #ifdef NDEBUG
125 /* in deployment builds we should always see a recognized magic cookie */
126 r_log(NR_LOG_STUN, LOG_WARNING, "Missing Magic Cookie");
127 ABORT(R_REJECTED);
128 #else
129 /* ignore this condition because sometimes we like to pretend we're
130 * a server talking to old clients and their messages don't contain
131 * a magic cookie at all but rather the magic cookie field is part
132 * of their ID and therefore random */
133 #endif /* NDEBUG */
134 #endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
135 break;
136 }
137
138 _status=0;
139 abort:
140 return _status;
141 }
142
143 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1 */
144 int
145 nr_stun_process_request(nr_stun_message *req, nr_stun_message *res)
146 {
147 int _status;
148 #ifdef USE_STUN_PEDANTIC
149 int r;
150 nr_stun_attr_unknown_attributes unknown_attributes = { { 0 } };
151 nr_stun_message_attribute *attr;
152
153 if (req->comprehension_required_unknown_attributes > 0) {
154 nr_stun_form_error_response(req, res, 420, "Unknown Attributes");
155
156 TAILQ_FOREACH(attr, &req->attributes, entry) {
157 if (attr->name == 0) {
158 /* unrecognized attribute */
159
160 /* should never happen, but truncate if it ever were to occur */
161 if (unknown_attributes.num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES)
162 break;
163
164 unknown_attributes.attribute[unknown_attributes.num_attributes++] = attr->type;
165 }
166 }
167
168 assert(req->comprehension_required_unknown_attributes + req->comprehension_optional_unknown_attributes == unknown_attributes.num_attributes);
169
170 if ((r=nr_stun_message_add_unknown_attributes_attribute(res, &unknown_attributes)))
171 ABORT(R_ALREADY);
172
173 ABORT(R_ALREADY);
174 }
175 #endif /* USE_STUN_PEDANTIC */
176
177 _status=0;
178 #ifdef USE_STUN_PEDANTIC
179 abort:
180 #endif /* USE_STUN_PEDANTIC */
181 return _status;
182 }
183
184 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3.2 */
185 int
186 nr_stun_process_indication(nr_stun_message *ind)
187 {
188 int _status;
189 #ifdef USE_STUN_PEDANTIC
190
191 if (ind->comprehension_required_unknown_attributes > 0)
192 ABORT(R_REJECTED);
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
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 ABORT(R_REJECTED);
216 #endif /* USE_STUN_PEDANTIC */
217
218 if (NR_STUN_GET_TYPE_METHOD(res->header.type) == NR_METHOD_BINDING) {
219 if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0) &&
220 ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) {
221 r_log(NR_LOG_STUN, LOG_WARNING, "Missing XOR-MAPPED-ADDRESS and MAPPED_ADDRESS");
222 ABORT(R_REJECTED);
223 }
224 }
225
226 _status=0;
227 abort:
228 return _status;
229 }
230
231 /* draft-ietf-behave-rfc3489bis-10.txt S 7.3.4 */
232 int
233 nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code)
234 {
235 int _status;
236 nr_stun_message_attribute *attr;
237
238 if (res->comprehension_required_unknown_attributes > 0) {
239 ABORT(R_REJECTED);
240 }
241
242 if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, &attr)) {
243 r_log(NR_LOG_STUN, LOG_WARNING, "Missing ERROR-CODE");
244 ABORT(R_REJECTED);
245 }
246
247 *error_code = attr->u.error_code.number;
248
249 switch (attr->u.error_code.number / 100) {
250 case 3:
251 /* If the error code is 300 through 399, the client SHOULD consider
252 * the transaction as failed unless the ALTERNATE-SERVER extension is
253 * being used. See Section 11. */
254
255 if (attr->u.error_code.number == 300) {
256 if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ALTERNATE_SERVER, 0)) {
257 r_log(NR_LOG_STUN, LOG_WARNING, "Missing ALTERNATE-SERVER");
258 ABORT(R_REJECTED);
259 }
260
261 /* draft-ietf-behave-rfc3489bis-10.txt S 11 */
262 if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) {
263 r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY");
264 ABORT(R_REJECTED);
265 }
266
267 ABORT(R_RETRY);
268 }
269
270 ABORT(R_REJECTED);
271 break;
272
273 case 4:
274 /* If the error code is 400 through 499, the client declares the
275 * transaction failed; in the case of 420 (Unknown Attribute), the
276 * response should contain a UNKNOWN-ATTRIBUTES attribute that gives
277 * additional information. */
278 if (attr->u.error_code.number == 420)
279 ABORT(R_REJECTED);
280
281 /* it may be possible to restart given the info that was received in
282 * this response, so retry */
283 ABORT(R_RETRY);
284 break;
285
286 case 5:
287 /* If the error code is 500 through 599, the client MAY resend the
288 * request; clients that do so MUST limit the number of times they do
289 * this. */
290 /* let the retransmit mechanism handle resending the request */
291 break;
292
293 default:
294 ABORT(R_REJECTED);
295 break;
296 }
297
298 /* the spec says: "The client then does any processing specified by the authentication
299 * mechanism (see Section 10). This may result in a new transaction
300 * attempt." -- but this is handled already elsewhere, so needn't be repeated
301 * in this function */
302
303 _status=0;
304 abort:
305 return _status;
306 }
307
308 /* draft-ietf-behave-rfc3489bis-10.txt S 10.1.2 */
309 int
310 nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg,
311 nr_stun_message *res)
312 {
313 int _status;
314 nr_stun_message_attribute *attr;
315
316 switch (msg->header.magic_cookie) {
317 default:
318 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
319 /* drop thru */
320 case NR_STUN_MAGIC_COOKIE:
321 if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
322 nr_stun_form_error_response(msg, res, 400, "Missing MESSAGE-INTEGRITY");
323 ABORT(R_ALREADY);
324 }
325
326 if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, 0)) {
327 nr_stun_form_error_response(msg, res, 400, "Missing USERNAME");
328 ABORT(R_ALREADY);
329 }
330
331 if (attr->u.message_integrity.unknown_user) {
332 nr_stun_form_error_response(msg, res, 401, "Unrecognized USERNAME");
333 ABORT(R_ALREADY);
334 }
335
336 if (!attr->u.message_integrity.valid) {
337 nr_stun_form_error_response(msg, res, 401, "Bad MESSAGE-INTEGRITY");
338 ABORT(R_ALREADY);
339 }
340
341 break;
342
343 #ifdef USE_STUND_0_96
344 case NR_STUN_MAGIC_COOKIE2:
345 /* nothing to check in this case */
346 break;
347 #endif /* USE_STUND_0_96 */
348 }
349
350 _status=0;
351 abort:
352 return _status;
353 }
354
355 /* draft-ietf-behave-rfc3489bis-10.txt S 10.1.3 */
356 int
357 nr_stun_receive_response_short_term_auth(nr_stun_message *res)
358 {
359 int _status;
360 nr_stun_message_attribute *attr;
361
362 switch (res->header.magic_cookie) {
363 default:
364 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
365 /* drop thru */
366 case NR_STUN_MAGIC_COOKIE:
367 if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
368 r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY");
369 ABORT(R_REJECTED);
370 }
371
372 if (!attr->u.message_integrity.valid) {
373 r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY");
374 ABORT(R_REJECTED);
375 }
376
377 break;
378
379 #ifdef USE_STUND_0_96
380 case NR_STUN_MAGIC_COOKIE2:
381 /* nothing to check in this case */
382 break;
383 #endif /* USE_STUND_0_96 */
384 }
385
386 _status=0;
387 abort:
388 return _status;
389 }
390
391 static int
392 nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res)
393 {
394 int r,_status;
395 char *realm = 0;
396 char *nonce;
397 UINT2 size;
398
399 if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm)))
400 ABORT(r);
401
402 if ((r=nr_stun_message_add_realm_attribute(res, realm)))
403 ABORT(r);
404
405 if (clnt) {
406 if (strlen(clnt->nonce) < 1)
407 new_nonce = 1;
408
409 if (new_nonce) {
410 if (NR_reg_get_uint2(NR_STUN_REG_PREF_SERVER_NONCE_SIZE, &size))
411 size = 48;
412
413 if (size > (sizeof(clnt->nonce) - 1))
414 size = sizeof(clnt->nonce) - 1;
415
416 nr_random_alphanum(clnt->nonce, size);
417 clnt->nonce[size] = '\0';
418 }
419
420 nonce = clnt->nonce;
421 }
422 else {
423 /* user is not known, so use a bogus nonce since there's no way to
424 * store a good nonce with the client-specific data -- this nonce
425 * will be recognized as stale if the client attempts another
426 * request */
427 nonce = "STALE";
428 }
429
430 if ((r=nr_stun_message_add_nonce_attribute(res, nonce)))
431 ABORT(r);
432
433 _status=0;
434 abort:
435 #ifdef USE_TURN
436 assert(_status == 0); /* TODO: !nn! cleanup after I reimplmement TURN */
437 #endif
438 RFREE(realm);
439 return _status;
440 }
441
442 /* draft-ietf-behave-rfc3489bis-10.txt S 10.2.1 - 10.2.2 */
443 int
444 nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res)
445 {
446 int r,_status;
447 nr_stun_message_attribute *mi;
448 nr_stun_message_attribute *n;
449 nr_stun_server_client *clnt = 0;
450
451 switch (req->header.magic_cookie) {
452 default:
453 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
454 /* drop thru */
455 case NR_STUN_MAGIC_COOKIE:
456 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)) {
457 nr_stun_form_error_response(req, res, 400, "Missing USERNAME");
458 nr_stun_add_realm_and_nonce(0, 0, res);
459 ABORT(R_ALREADY);
460 }
461
462 if ((r=nr_stun_get_message_client(ctx, req, &clnt))) {
463 nr_stun_form_error_response(req, res, 401, "Unrecognized USERNAME");
464 nr_stun_add_realm_and_nonce(0, 0, res);
465 ABORT(R_ALREADY);
466 }
467
468 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, &mi)) {
469 nr_stun_form_error_response(req, res, 401, "Missing MESSAGE-INTEGRITY");
470 nr_stun_add_realm_and_nonce(0, clnt, res);
471 ABORT(R_ALREADY);
472 }
473
474 assert(!mi->u.message_integrity.unknown_user);
475
476 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_REALM, 0)) {
477 nr_stun_form_error_response(req, res, 400, "Missing REALM");
478 ABORT(R_ALREADY);
479 }
480
481 if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_NONCE, &n)) {
482 nr_stun_form_error_response(req, res, 400, "Missing NONCE");
483 ABORT(R_ALREADY);
484 }
485
486 assert(sizeof(clnt->nonce) == sizeof(n->u.nonce));
487 if (strncmp(clnt->nonce, n->u.nonce, sizeof(n->u.nonce))) {
488 nr_stun_form_error_response(req, res, 438, "Stale NONCE");
489 nr_stun_add_realm_and_nonce(1, clnt, res);
490 ABORT(R_ALREADY);
491 }
492
493 if (!mi->u.message_integrity.valid) {
494 nr_stun_form_error_response(req, res, 401, "Bad MESSAGE-INTEGRITY");
495 nr_stun_add_realm_and_nonce(0, clnt, res);
496 ABORT(R_ALREADY);
497 }
498
499 break;
500
501 #ifdef USE_STUND_0_96
502 case NR_STUN_MAGIC_COOKIE2:
503 /* nothing to do in this case */
504 break;
505 #endif /* USE_STUND_0_96 */
506 }
507
508 _status=0;
509 abort:
510
511 return _status;
512 }
513
514 /* draft-ietf-behave-rfc3489bis-10.txt S 10.2.3 */
515 int
516 nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx)
517 {
518 int _status;
519 nr_stun_message_attribute *attr;
520
521 switch (res->header.magic_cookie) {
522 default:
523 /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
524 /* drop thru */
525 case NR_STUN_MAGIC_COOKIE:
526 if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_REALM, &attr)) {
527 RFREE(ctx->realm);
528 ctx->realm = r_strdup(attr->u.realm);
529 if (!ctx->realm)
530 ABORT(R_NO_MEMORY);
531 }
532 else {
533 r_log(NR_LOG_STUN, LOG_WARNING, "Missing REALM");
534 ABORT(R_REJECTED);
535 }
536
537 if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_NONCE, &attr)) {
538 RFREE(ctx->nonce);
539 ctx->nonce = r_strdup(attr->u.nonce);
540 if (!ctx->nonce)
541 ABORT(R_NO_MEMORY);
542 }
543 else {
544 r_log(NR_LOG_STUN, LOG_WARNING, "Missing NONCE");
545 ABORT(R_REJECTED);
546 }
547
548 if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
549 if (!attr->u.message_integrity.valid) {
550 r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY");
551 ABORT(R_REJECTED);
552 }
553 }
554
555 break;
556
557 #ifdef USE_STUND_0_96
558 case NR_STUN_MAGIC_COOKIE2:
559 /* nothing to check in this case */
560 break;
561 #endif /* USE_STUND_0_96 */
562 }
563
564 _status=0;
565 abort:
566 return _status;
567 }
568

mercurial