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