Line data Source code
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 :
35 : static char *RCSSTRING __UNUSED__="$Id: stun_server_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
36 :
37 : #include <string.h>
38 : #include <assert.h>
39 :
40 : #include "nr_api.h"
41 : #include "stun.h"
42 :
43 : static int nr_stun_server_destroy_client(nr_stun_server_client *clnt);
44 : static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt);
45 : static int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res);
46 :
47 :
48 0 : int nr_stun_server_ctx_create(char *label, nr_socket *sock, nr_stun_server_ctx **ctxp)
49 : {
50 : int r,_status;
51 0 : nr_stun_server_ctx *ctx=0;
52 :
53 0 : if ((r=nr_stun_startup()))
54 0 : ABORT(r);
55 :
56 0 : if(!(ctx=RCALLOC(sizeof(nr_stun_server_ctx))))
57 0 : ABORT(R_NO_MEMORY);
58 :
59 0 : if(!(ctx->label=r_strdup(label)))
60 0 : ABORT(R_NO_MEMORY);
61 0 : ctx->sock=sock;
62 0 : nr_socket_getaddr(sock,&ctx->my_addr);
63 :
64 0 : STAILQ_INIT(&ctx->clients);
65 :
66 0 : *ctxp=ctx;
67 :
68 0 : _status=0;
69 : abort:
70 0 : return(_status);
71 : }
72 :
73 0 : int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp)
74 : {
75 : nr_stun_server_ctx *ctx;
76 : nr_stun_server_client *clnt1,*clnt2;
77 :
78 0 : if(!ctxp || !*ctxp)
79 0 : return(0);
80 :
81 0 : ctx=*ctxp;
82 :
83 0 : STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) {
84 0 : nr_stun_server_destroy_client(clnt1);
85 : }
86 :
87 0 : nr_stun_server_destroy_client(ctx->default_client);
88 :
89 0 : RFREE(ctx->label);
90 0 : RFREE(ctx);
91 :
92 0 : return(0);
93 : }
94 :
95 0 : static int nr_stun_server_client_create(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg, nr_stun_server_client **clntp)
96 : {
97 0 : nr_stun_server_client *clnt=0;
98 : int r,_status;
99 :
100 0 : if(!(clnt=RCALLOC(sizeof(nr_stun_server_client))))
101 0 : ABORT(R_NO_MEMORY);
102 :
103 0 : if(!(clnt->label=r_strdup(client_label)))
104 0 : ABORT(R_NO_MEMORY);
105 :
106 0 : if(!(clnt->username=r_strdup(user)))
107 0 : ABORT(R_NO_MEMORY);
108 :
109 0 : if(r=r_data_copy(&clnt->password,pass))
110 0 : ABORT(r);
111 :
112 0 : r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user);
113 0 : clnt->stun_server_cb=cb;
114 0 : clnt->cb_arg=cb_arg;
115 :
116 0 : *clntp = clnt;
117 0 : _status=0;
118 : abort:
119 0 : if(_status){
120 0 : nr_stun_server_destroy_client(clnt);
121 : }
122 0 : return(_status);
123 : }
124 :
125 0 : int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg)
126 : {
127 : int r,_status;
128 : nr_stun_server_client *clnt;
129 :
130 0 : if (r=nr_stun_server_client_create(ctx, client_label, user, pass, cb, cb_arg, &clnt))
131 0 : ABORT(r);
132 :
133 0 : STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry);
134 :
135 0 : _status=0;
136 : abort:
137 0 : return(_status);
138 : }
139 :
140 0 : int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg)
141 : {
142 : int r,_status;
143 : nr_stun_server_client *clnt;
144 :
145 0 : assert(!ctx->default_client);
146 0 : if (ctx->default_client)
147 0 : ABORT(R_INTERNAL);
148 :
149 0 : if (r=nr_stun_server_client_create(ctx, "default_client", ufrag, pass, cb, cb_arg, &clnt))
150 0 : ABORT(r);
151 :
152 0 : ctx->default_client = clnt;
153 :
154 0 : _status=0;
155 : abort:
156 0 : return(_status);
157 : }
158 :
159 0 : int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg)
160 : {
161 : nr_stun_server_client *clnt1,*clnt2;
162 0 : int found = 0;
163 :
164 0 : STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) {
165 0 : if(clnt1->cb_arg == cb_arg) {
166 0 : STAILQ_REMOVE(&ctx->clients, clnt1, nr_stun_server_client_, entry);
167 0 : nr_stun_server_destroy_client(clnt1);
168 0 : found++;
169 : }
170 : }
171 :
172 0 : if (!found)
173 0 : ERETURN(R_NOT_FOUND);
174 :
175 0 : return 0;
176 : }
177 :
178 0 : static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password)
179 : {
180 : int _status;
181 0 : nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg;
182 0 : nr_stun_server_client *clnt = 0;
183 : nr_stun_message_attribute *username_attribute;
184 :
185 0 : if ((nr_stun_get_message_client(ctx, msg, &clnt))) {
186 0 : if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) {
187 0 : r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label);
188 0 : ABORT(R_NOT_FOUND);
189 : }
190 :
191 : /* Although this is an exceptional condition, we'll already have seen a
192 : * NOTICE-level log message about the unknown user, so additional log
193 : * messages at any level higher than DEBUG are unnecessary. */
194 :
195 0 : r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username);
196 0 : ABORT(R_NOT_FOUND);
197 : }
198 :
199 0 : *password = &clnt->password;
200 :
201 0 : _status=0;
202 : abort:
203 0 : return(_status);
204 : }
205 :
206 0 : int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res)
207 : {
208 : int r,_status;
209 :
210 0 : if (nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)
211 0 : || !(auth_rule & NR_STUN_AUTH_RULE_OPTIONAL)) {
212 : /* favor long term credentials over short term, if both are supported */
213 :
214 0 : if (auth_rule & NR_STUN_AUTH_RULE_LONG_TERM) {
215 0 : if ((r=nr_stun_receive_request_long_term_auth(req, ctx, res)))
216 0 : ABORT(r);
217 : }
218 0 : else if (auth_rule & NR_STUN_AUTH_RULE_SHORT_TERM) {
219 0 : if ((r=nr_stun_receive_request_or_indication_short_term_auth(req, res)))
220 0 : ABORT(r);
221 : }
222 : }
223 :
224 0 : _status=0;
225 : abort:
226 0 : return(_status);
227 : }
228 :
229 0 : int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule)
230 : {
231 : int r,_status;
232 : char string[256];
233 0 : nr_stun_message *req = 0;
234 0 : nr_stun_message *res = 0;
235 0 : nr_stun_server_client *clnt = 0;
236 : nr_stun_server_request info;
237 : int error;
238 0 : int dont_free = 0;
239 :
240 0 : r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
241 :
242 0 : snprintf(string, sizeof(string)-1, "STUN-SERVER(%s): Received ", ctx->label);
243 0 : r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len);
244 :
245 0 : memset(&info,0,sizeof(info));
246 :
247 0 : if ((r=nr_stun_message_create2(&req, (UCHAR*)msg, len)))
248 0 : ABORT(r);
249 :
250 0 : if ((r=nr_stun_message_create(&res)))
251 0 : ABORT(r);
252 :
253 0 : if ((r=nr_stun_decode_message(req, nr_stun_server_get_password, ctx))) {
254 : /* RFC5389 S 7.3 says "If any errors are detected, the message is
255 : * silently discarded." */
256 : #ifndef USE_STUN_PEDANTIC
257 : /* ... but that seems like a bad idea, at least return a 400 so
258 : * that the server isn't a black hole to the client */
259 : nr_stun_form_error_response(req, res, 400, "Bad Request - Failed to decode request");
260 : ABORT(R_ALREADY);
261 : #endif /* USE_STUN_PEDANTIC */
262 0 : ABORT(R_REJECTED);
263 : }
264 :
265 0 : if ((r=nr_stun_receive_message(0, req))) {
266 : /* RFC5389 S 7.3 says "If any errors are detected, the message is
267 : * silently discarded." */
268 : #ifndef USE_STUN_PEDANTIC
269 : /* ... but that seems like a bad idea, at least return a 400 so
270 : * that the server isn't a black hole to the client */
271 : nr_stun_form_error_response(req, res, 400, "Bad Request - Section 7.3 check failed");
272 : ABORT(R_ALREADY);
273 : #endif /* USE_STUN_PEDANTIC */
274 0 : ABORT(R_REJECTED);
275 : }
276 :
277 0 : if (NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_REQUEST
278 0 : && NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_INDICATION) {
279 0 : r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Illegal message type: %04x",ctx->label,req->header.type);
280 : /* RFC5389 S 7.3 says "If any errors are detected, the message is
281 : * silently discarded." */
282 : #ifndef USE_STUN_PEDANTIC
283 : /* ... but that seems like a bad idea, at least return a 400 so
284 : * that the server isn't a black hole to the client */
285 : nr_stun_form_error_response(req, res, 400, "Bad Request - Unsupported message type");
286 : ABORT(R_ALREADY);
287 : #endif /* USE_STUN_PEDANTIC */
288 0 : ABORT(R_REJECTED);
289 : }
290 :
291 : /* "The STUN agent then does any checks that are required by a
292 : * authentication mechanism that the usage has specified" */
293 0 : if ((r=nr_stun_server_process_request_auth_checks(ctx, req, auth_rule, res)))
294 0 : ABORT(r);
295 :
296 0 : if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) {
297 0 : if ((r=nr_stun_process_indication(req)))
298 0 : ABORT(r);
299 : }
300 : else {
301 0 : if ((r=nr_stun_process_request(req, res)))
302 0 : ABORT(r);
303 : }
304 :
305 0 : assert(res->header.type == 0);
306 :
307 0 : clnt = 0;
308 0 : if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_REQUEST) {
309 0 : if ((nr_stun_get_message_client(ctx, req, &clnt))) {
310 0 : if ((r=nr_stun_form_success_response(req, peer_addr, 0, res)))
311 0 : ABORT(r);
312 : }
313 : else {
314 0 : if ((r=nr_stun_form_success_response(req, peer_addr, &clnt->password, res)))
315 0 : ABORT(r);
316 : }
317 : }
318 :
319 0 : if(clnt && clnt->stun_server_cb){
320 0 : r_log(NR_LOG_STUN,LOG_DEBUG,"Entering STUN server callback");
321 :
322 : /* Set up the info */
323 0 : if(r=nr_transport_addr_copy(&info.src_addr,peer_addr))
324 0 : ABORT(r);
325 :
326 0 : info.request = req;
327 0 : info.response = res;
328 :
329 0 : error = 0;
330 0 : dont_free = 0;
331 0 : if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&dont_free,&error)) {
332 0 : if (error == 0)
333 0 : error = 500;
334 :
335 0 : nr_stun_form_error_response(req, res, error, "ICE Failure");
336 0 : ABORT(R_ALREADY);
337 : }
338 : }
339 :
340 0 : _status=0;
341 : abort:
342 0 : if (!res)
343 0 : goto skip_response;
344 :
345 0 : if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION)
346 0 : goto skip_response;
347 :
348 : /* Now respond */
349 :
350 0 : if (_status != 0 && ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0))
351 0 : nr_stun_form_error_response(req, res, 500, "Failed to specify error");
352 :
353 0 : if ((r=nr_stun_server_send_response(ctx, sock, peer_addr, res, clnt))) {
354 0 : r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Failed sending response (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
355 0 : _status = R_FAILED;
356 : }
357 :
358 : #if 0
359 : /* EKR: suppressed these checks because if you have an error when
360 : you are sending an error, things go wonky */
361 : #ifdef SANITY_CHECKS
362 : if (_status == R_ALREADY) {
363 : assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_ERROR_RESPONSE);
364 : assert(nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0));
365 : }
366 : else {
367 : assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_RESPONSE);
368 : assert(!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0));
369 : }
370 : #endif /* SANITY_CHECKS */
371 : #endif
372 :
373 : if (0) {
374 : skip_response:
375 0 : _status = 0;
376 : }
377 :
378 0 : if (!dont_free) {
379 0 : nr_stun_message_destroy(&res);
380 0 : nr_stun_message_destroy(&req);
381 : }
382 :
383 0 : return(_status);
384 : }
385 :
386 0 : static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt)
387 : {
388 : int r,_status;
389 : Data *hmacPassword;
390 : char string[256];
391 :
392 0 : r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(label=%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
393 :
394 0 : if (clnt) {
395 0 : hmacPassword = &clnt->password;
396 : }
397 : else {
398 0 : hmacPassword = 0;
399 : }
400 :
401 0 : if ((r=nr_stun_encode_message(res))) {
402 : /* should never happen */
403 0 : r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Unable to encode message", ctx->label);
404 : }
405 : else {
406 0 : snprintf(string, sizeof(string)-1, "STUN(%s): Sending to %s ", ctx->label, peer_addr->as_string);
407 0 : r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)res->buffer, res->length);
408 :
409 0 : if(r=nr_socket_sendto(sock?sock:ctx->sock,res->buffer,res->length,0,peer_addr))
410 0 : ABORT(r);
411 : }
412 :
413 0 : _status=0;
414 : abort:
415 0 : return(_status);
416 : }
417 :
418 0 : static int nr_stun_server_destroy_client(nr_stun_server_client *clnt)
419 : {
420 0 : if (!clnt)
421 0 : return 0;
422 :
423 0 : RFREE(clnt->label);
424 0 : RFREE(clnt->username);
425 0 : r_data_zfree(&clnt->password);
426 :
427 0 : RFREE(clnt);
428 0 : return(0);
429 : }
430 :
431 0 : int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **out)
432 : {
433 : int _status;
434 : nr_stun_message_attribute *attr;
435 0 : nr_stun_server_client *clnt=0;
436 :
437 0 : if (! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, &attr)) {
438 0 : r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label);
439 0 : ABORT(R_NOT_FOUND);
440 : }
441 :
442 0 : STAILQ_FOREACH(clnt, &ctx->clients, entry) {
443 0 : if (!strncmp(clnt->username, attr->u.username,
444 : sizeof(attr->u.username)))
445 0 : break;
446 : }
447 :
448 0 : if (!clnt && ctx->default_client) {
449 : /* If we can't find a specific client see if this matches the default,
450 : which means that the username starts with our ufrag.
451 : */
452 0 : char *colon = strchr(attr->u.username, ':');
453 0 : if (colon && !strncmp(ctx->default_client->username,
454 0 : attr->u.username,
455 0 : colon - attr->u.username)) {
456 0 : clnt = ctx->default_client;
457 0 : r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Falling back to default client, username=: %s",ctx->label,attr->u.username);
458 : }
459 : }
460 :
461 0 : if (!clnt) {
462 0 : r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Request from unknown user: %s",ctx->label,attr->u.username);
463 0 : ABORT(R_NOT_FOUND);
464 : }
465 :
466 0 : *out = clnt;
467 :
468 0 : _status=0;
469 : abort:
470 0 : return(_status);
471 : }
472 :
|