Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / stun / stunagent.c
1 /*
2  * This file is part of the Nice GLib ICE library.
3  *
4  * (C) 2008-2009 Collabora Ltd.
5  *  Contact: Youness Alaoui
6  * (C) 2008-2009 Nokia Corporation. All rights reserved.
7  *
8  * The contents of this file are subject to the Mozilla Public License Version
9  * 1.1 (the "License"); you may not use this file except in compliance with
10  * the License. You may obtain a copy of the License at
11  * http://www.mozilla.org/MPL/
12  *
13  * Software distributed under the License is distributed on an "AS IS" basis,
14  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15  * for the specific language governing rights and limitations under the
16  * License.
17  *
18  * The Original Code is the Nice GLib ICE library.
19  *
20  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21  * Corporation. All Rights Reserved.
22  *
23  * Contributors:
24  *   Youness Alaoui, Collabora Ltd.
25  *
26  * Alternatively, the contents of this file may be used under the terms of the
27  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28  * case the provisions of LGPL are applicable instead of those above. If you
29  * wish to allow use of your version of this file only under the terms of the
30  * LGPL and not to allow others to use your version of this file under the
31  * MPL, indicate your decision by deleting the provisions above and replace
32  * them with the notice and other provisions required by the LGPL. If you do
33  * not delete the provisions above, a recipient may use your version of this
34  * file under either the MPL or the LGPL.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 # include <config.h>
39 #endif
40
41 #include "stunmessage.h"
42 #include "stunagent.h"
43 #include "stunhmac.h"
44 #include "stun5389.h"
45 #include "utils.h"
46
47 #include <string.h>
48 #include <stdlib.h>
49 #include <inttypes.h>
50
51
52 static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type);
53 static unsigned stun_agent_find_unknowns (StunAgent *agent,
54     const StunMessage * msg, uint16_t *list, unsigned max);
55
56 void stun_agent_init (StunAgent *agent, const uint16_t *known_attributes,
57     StunCompatibility compatibility, StunAgentUsageFlags usage_flags)
58 {
59   int i;
60
61   agent->known_attributes = (uint16_t *) known_attributes;
62   agent->compatibility = compatibility;
63   agent->usage_flags = usage_flags;
64   agent->software_attribute = NULL;
65   agent->ms_ice2_send_legacy_connchecks =
66       compatibility == STUN_COMPATIBILITY_MSICE2;
67
68   for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
69     agent->sent_ids[i].valid = FALSE;
70   }
71 }
72
73
74 bool stun_agent_default_validater (StunAgent *agent,
75     StunMessage *message, uint8_t *username, uint16_t username_len,
76     uint8_t **password, size_t *password_len, void *user_data)
77 {
78   StunDefaultValidaterData* val = (StunDefaultValidaterData *) user_data;
79   int i;
80
81   for (i = 0; val && val[i].username ; i++) {
82 #if 0
83     stun_debug ("Comparing username of size %d and %" PRIuPTR ": %d",
84         username_len, val[i].username_len,
85         (memcmp (username, val[i].username, username_len) == 0));
86 #endif
87     stun_debug_bytes ("  First username: ", username, username_len);
88     stun_debug_bytes ("  Second username: ", val[i].username,
89         val[i].username_len);
90     if (username_len == val[i].username_len &&
91         memcmp (username, val[i].username, username_len) == 0) {
92       *password = (uint8_t *) val[i].password;
93       *password_len = val[i].password_len;
94       stun_debug ("Found valid username, returning password : '%s'", *password);
95       return TRUE;
96     }
97   }
98
99   return FALSE;
100
101 }
102
103 static bool stun_agent_check_fingerprint(StunAgent *agent, StunMessage *msg)
104 {
105   uint32_t fpr;
106   uint32_t crc32;
107   uint16_t msg_len;
108
109   /* Looks for FINGERPRINT */
110   if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) !=
111       STUN_MESSAGE_RETURN_SUCCESS) {
112     stun_debug ("STUN demux error: no FINGERPRINT attribute!");
113     return FALSE;
114   }
115
116   msg_len = stun_message_length (msg);
117
118   /* Checks FINGERPRINT */
119   crc32 = stun_fingerprint (msg->buffer, msg_len, FALSE);
120   fpr = ntohl (fpr);
121   if (fpr != crc32) {
122     uint16_t palen;
123
124     /* [MS-ICE2] 3.1.4.8.2 Connectivity Checks Phase - legacy compatibility */
125     if (agent->compatibility == STUN_COMPATIBILITY_MSICE2 &&
126         stun_message_find (msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION,
127             &palen) == NULL &&
128         fpr == stun_fingerprint (msg->buffer, msg_len, TRUE)) {
129       return TRUE;
130     }
131
132     stun_debug ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!",
133         fpr, crc32);
134     return FALSE;
135   }
136
137   return TRUE;
138 }
139
140 StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg,
141     const uint8_t *buffer, size_t buffer_len,
142     StunMessageIntegrityValidate validater, void * validater_data)
143 {
144   StunTransactionId msg_id;
145   int len;
146   uint8_t *username = NULL;
147   uint16_t username_len;
148   uint8_t *key = NULL;
149   size_t key_len;
150   uint8_t *hash;
151   uint8_t sha[20];
152   uint16_t hlen;
153   uint32_t implementation_version;
154   int sent_id_idx = -1;
155   uint16_t unknown;
156   int error_code;
157   int ignore_credentials = 0;
158   uint8_t long_term_key[16] = { 0 };
159   bool long_term_key_valid = FALSE;
160
161   len = stun_message_validate_buffer_length (buffer, buffer_len,
162        !(agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES));
163   if (len == STUN_MESSAGE_BUFFER_INVALID) {
164     return STUN_VALIDATION_NOT_STUN;
165   } else if (len == STUN_MESSAGE_BUFFER_INCOMPLETE) {
166     return STUN_VALIDATION_INCOMPLETE_STUN;
167   } else if (len != (int) buffer_len) {
168     return STUN_VALIDATION_NOT_STUN;
169   }
170
171   msg->buffer = (uint8_t *) buffer;
172   msg->buffer_len = buffer_len;
173   msg->agent = agent;
174   msg->key = NULL;
175   msg->key_len = 0;
176   msg->long_term_valid = FALSE;
177
178   /* TODO: reject it or not ? */
179   if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
180        agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
181       !stun_message_has_cookie (msg)) {
182       stun_debug ("STUN demux error: no cookie!");
183       return STUN_VALIDATION_BAD_REQUEST;
184   }
185
186   if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
187        agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
188       agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
189     if (stun_agent_check_fingerprint(agent, msg) == FALSE) {
190       return STUN_VALIDATION_BAD_REQUEST;
191     }
192
193     stun_debug ("STUN demux: OK!");
194   }
195
196   if (stun_message_get_class (msg) == STUN_RESPONSE ||
197       stun_message_get_class (msg) == STUN_ERROR) {
198     stun_message_id (msg, msg_id);
199     for (sent_id_idx = 0; sent_id_idx < STUN_AGENT_MAX_SAVED_IDS; sent_id_idx++) {
200       if (agent->sent_ids[sent_id_idx].valid == TRUE &&
201           agent->sent_ids[sent_id_idx].method == stun_message_get_method (msg) &&
202           memcmp (msg_id, agent->sent_ids[sent_id_idx].id,
203               sizeof(StunTransactionId)) == 0) {
204
205         key = agent->sent_ids[sent_id_idx].key;
206         key_len = agent->sent_ids[sent_id_idx].key_len;
207         memcpy (long_term_key, agent->sent_ids[sent_id_idx].long_term_key,
208             sizeof(long_term_key));
209         long_term_key_valid = agent->sent_ids[sent_id_idx].long_term_valid;
210         break;
211       }
212     }
213     if (sent_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
214       return STUN_VALIDATION_UNMATCHED_RESPONSE;
215     }
216   }
217
218   ignore_credentials =
219       (agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) ||
220       (stun_message_get_class (msg) == STUN_ERROR &&
221        stun_message_find_error (msg, &error_code) ==
222           STUN_MESSAGE_RETURN_SUCCESS &&
223        (error_code == STUN_ERROR_BAD_REQUEST ||
224            error_code == STUN_ERROR_UNAUTHORIZED ||
225            error_code == STUN_ERROR_STALE_NONCE ||
226            error_code == STUN_ERROR_TRY_ALTERNATE)) ||
227       (stun_message_get_class (msg) == STUN_INDICATION &&
228           (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS ||
229               agent->usage_flags & STUN_AGENT_USAGE_NO_INDICATION_AUTH));
230
231   if (key == NULL &&
232       ignore_credentials == 0 &&
233       (stun_message_get_class (msg) == STUN_REQUEST ||
234        stun_message_get_class (msg) == STUN_INDICATION) &&
235       (((agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS) &&
236        (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
237         !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) ||
238       ((agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) &&
239         stun_message_get_class (msg) == STUN_REQUEST &&
240         (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
241          !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) ||
242          !stun_message_has_attribute (msg, STUN_ATTRIBUTE_NONCE) ||
243          !stun_message_has_attribute (msg, STUN_ATTRIBUTE_REALM))) ||
244        ((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 &&
245          stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) &&
246          !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY)))) {
247         return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST;
248   }
249
250   if (stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) &&
251       ((key == NULL && ignore_credentials == 0) ||
252           (agent->usage_flags & STUN_AGENT_USAGE_FORCE_VALIDATER))) {
253     username_len = 0;
254     username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME,
255         &username_len);
256     if (validater == NULL ||
257         validater (agent, msg, username, username_len,
258             &key, &key_len, validater_data) == FALSE) {
259       return STUN_VALIDATION_UNAUTHORIZED;
260     }
261   }
262
263   if (ignore_credentials == 0 && key != NULL && key_len > 0) {
264     hash = (uint8_t *) stun_message_find (msg,
265         STUN_ATTRIBUTE_MESSAGE_INTEGRITY, &hlen);
266
267     if (hash) {
268       /* We must give the size from start to the end of the attribute
269          because you might have a FINGERPRINT attribute after it... */
270       if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
271         uint8_t *realm = NULL;
272         uint16_t realm_len;
273         uint8_t md5[16];
274
275         if (long_term_key_valid) {
276           memcpy (md5, long_term_key, sizeof (md5));
277         } else {
278           realm = (uint8_t *) stun_message_find (msg,  STUN_ATTRIBUTE_REALM, &realm_len);
279           username = (uint8_t *) stun_message_find (msg,
280               STUN_ATTRIBUTE_USERNAME, &username_len);
281           if (username == NULL || realm == NULL) {
282             return STUN_VALIDATION_UNAUTHORIZED;
283           }
284           stun_hash_creds (realm, realm_len,
285               username,  username_len,
286               key, key_len, md5);
287         }
288
289         memcpy (msg->long_term_key, md5, sizeof(md5));
290         msg->long_term_valid = TRUE;
291
292         if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
293             agent->compatibility == STUN_COMPATIBILITY_OC2007) {
294           stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
295               sha, md5, sizeof(md5), TRUE);
296         } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
297           stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
298               stun_message_length (msg) - 20, sha, md5, sizeof(md5), TRUE);
299         } else {
300           stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
301               hash - msg->buffer, sha, md5, sizeof(md5), FALSE);
302         }
303       } else {
304         if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
305             agent->compatibility == STUN_COMPATIBILITY_OC2007) {
306           stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
307               sha, key, key_len, TRUE);
308         } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
309           stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
310               stun_message_length (msg) - 20, sha, key, key_len, TRUE);
311         } else {
312           stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
313               hash - msg->buffer, sha, key, key_len, FALSE);
314         }
315       }
316
317       stun_debug (" Message HMAC-SHA1 fingerprint:");
318       stun_debug_bytes ("  key     : ", key, key_len);
319       stun_debug_bytes ("  expected: ", sha, sizeof (sha));
320       stun_debug_bytes ("  received: ", hash, sizeof (sha));
321
322       if (memcmp (sha, hash, sizeof (sha)))  {
323         stun_debug ("STUN auth error: SHA1 fingerprint mismatch!");
324         return STUN_VALIDATION_UNAUTHORIZED;
325       }
326
327       stun_debug ("STUN auth: OK!");
328       msg->key = key;
329       msg->key_len = key_len;
330     } else if (!(stun_message_get_class (msg) == STUN_ERROR &&
331         stun_message_find_error (msg, &error_code) ==
332             STUN_MESSAGE_RETURN_SUCCESS &&
333         (error_code == STUN_ERROR_BAD_REQUEST ||
334             error_code == STUN_ERROR_UNAUTHORIZED))) {
335       stun_debug ("STUN auth error: No message integrity attribute!");
336       return STUN_VALIDATION_UNAUTHORIZED;
337     }
338   }
339
340
341   if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) {
342     agent->sent_ids[sent_id_idx].valid = FALSE;
343   }
344
345   /* [MS-ICE2] 3.1.4.8.2 stop sending additional connectivity checks */
346   if (stun_message_find32(msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION,
347       &implementation_version) == STUN_MESSAGE_RETURN_SUCCESS) {
348     msg->agent->ms_ice2_send_legacy_connchecks = FALSE;
349   }
350
351   if (stun_agent_find_unknowns (agent, msg, &unknown, 1) > 0) {
352     if (stun_message_get_class (msg) == STUN_REQUEST)
353       return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE;
354     else
355       return STUN_VALIDATION_UNKNOWN_ATTRIBUTE;
356   }
357   return STUN_VALIDATION_SUCCESS;
358
359 }
360
361 bool stun_agent_forget_transaction (StunAgent *agent, StunTransactionId id)
362 {
363   int i;
364
365   for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
366     if (agent->sent_ids[i].valid == TRUE &&
367         memcmp (id, agent->sent_ids[i].id,
368             sizeof(StunTransactionId)) == 0) {
369       agent->sent_ids[i].valid = FALSE;
370       return TRUE;
371     }
372   }
373
374   return FALSE;
375 }
376
377 bool stun_agent_init_request (StunAgent *agent, StunMessage *msg,
378     uint8_t *buffer, size_t buffer_len, StunMethod m)
379 {
380   bool ret;
381   StunTransactionId id;
382
383   msg->buffer = buffer;
384   msg->buffer_len = buffer_len;
385   msg->agent = agent;
386   msg->key = NULL;
387   msg->key_len = 0;
388   msg->long_term_valid = FALSE;
389
390   stun_make_transid (id);
391
392   ret = stun_message_init (msg, STUN_REQUEST, m, id);
393
394   if (ret) {
395     if (agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
396         agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
397       uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
398       memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
399     }
400     if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
401          agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
402         (agent->software_attribute != NULL ||
403             agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
404       stun_message_append_software (msg, agent->software_attribute);
405     }
406   }
407
408   return ret;
409 }
410
411
412 bool stun_agent_init_indication (StunAgent *agent, StunMessage *msg,
413     uint8_t *buffer, size_t buffer_len, StunMethod m)
414 {
415   bool ret;
416   StunTransactionId id;
417
418   msg->buffer = buffer;
419   msg->buffer_len = buffer_len;
420   msg->agent = agent;
421   msg->key = NULL;
422   msg->key_len = 0;
423   msg->long_term_valid = FALSE;
424
425   stun_make_transid (id);
426   ret = stun_message_init (msg, STUN_INDICATION, m, id);
427
428   if (ret) {
429     if (agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
430         agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
431       uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
432       memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
433     }
434   }
435
436   return ret;
437 }
438
439
440 bool stun_agent_init_response (StunAgent *agent, StunMessage *msg,
441     uint8_t *buffer, size_t buffer_len, const StunMessage *request)
442 {
443
444   StunTransactionId id;
445
446   if (stun_message_get_class (request) != STUN_REQUEST) {
447     return FALSE;
448   }
449
450   msg->buffer = buffer;
451   msg->buffer_len = buffer_len;
452   msg->agent = agent;
453   msg->key = request->key;
454   msg->key_len = request->key_len;
455   memmove (msg->long_term_key, request->long_term_key,
456       sizeof(msg->long_term_key));
457   msg->long_term_valid = request->long_term_valid;
458
459   stun_message_id (request, id);
460
461   if (stun_message_init (msg, STUN_RESPONSE,
462           stun_message_get_method (request), id)) {
463
464     if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
465          agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
466         (agent->software_attribute != NULL ||
467             agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
468       stun_message_append_software (msg, agent->software_attribute);
469     }
470     return TRUE;
471   }
472   return FALSE;
473 }
474
475
476 bool stun_agent_init_error (StunAgent *agent, StunMessage *msg,
477     uint8_t *buffer, size_t buffer_len, const StunMessage *request,
478     StunError err)
479 {
480   StunTransactionId id;
481
482   if (stun_message_get_class (request) != STUN_REQUEST) {
483     return FALSE;
484   }
485
486   msg->buffer = buffer;
487   msg->buffer_len = buffer_len;
488   msg->agent = agent;
489   msg->key = request->key;
490   msg->key_len = request->key_len;
491   memmove (msg->long_term_key, request->long_term_key,
492       sizeof(msg->long_term_key));
493   msg->long_term_valid = request->long_term_valid;
494
495   stun_message_id (request, id);
496
497
498   if (stun_message_init (msg, STUN_ERROR,
499           stun_message_get_method (request), id)) {
500
501     if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
502          agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
503         (agent->software_attribute != NULL ||
504             agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
505       stun_message_append_software (msg, agent->software_attribute);
506     }
507     if (stun_message_append_error (msg, err) == STUN_MESSAGE_RETURN_SUCCESS) {
508       return TRUE;
509     }
510   }
511   return FALSE;
512 }
513
514
515 size_t stun_agent_build_unknown_attributes_error (StunAgent *agent,
516     StunMessage *msg, uint8_t *buffer, size_t buffer_len,
517     const StunMessage *request)
518 {
519
520   unsigned counter;
521   uint16_t ids[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES];
522
523   counter = stun_agent_find_unknowns (agent, request,
524       ids, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES);
525
526   if (stun_agent_init_error (agent, msg, buffer, buffer_len,
527           request, STUN_ERROR_UNKNOWN_ATTRIBUTE) == FALSE) {
528     return 0;
529   }
530
531   /* NOTE: Old RFC3489 compatibility:
532    * When counter is odd, duplicate one value for 32-bits padding. */
533   if (!stun_message_has_cookie (request) && (counter & 1))
534     ids[counter++] = ids[0];
535
536   if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
537           ids, counter * 2) == STUN_MESSAGE_RETURN_SUCCESS) {
538     return stun_agent_finish_message (agent, msg, request->key, request->key_len);
539   }
540
541   return 0;
542 }
543
544
545 size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
546     const uint8_t *key, size_t key_len)
547 {
548   uint8_t *ptr;
549   uint32_t fpr;
550   int saved_id_idx = 0;
551   uint8_t md5[16];
552   bool remember_transaction;
553
554   remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST);
555
556   if (agent->compatibility == STUN_COMPATIBILITY_OC2007 &&
557       stun_message_get_method (msg) == STUN_SEND) {
558     /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to
559      * STUN_SEND requests, so don't bother waiting for them. More details at
560      * https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx.
561      */
562     remember_transaction = FALSE;
563   }
564
565   if (remember_transaction) {
566     for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) {
567       if (agent->sent_ids[saved_id_idx].valid == FALSE) {
568         break;
569       }
570     }
571   }
572   if (saved_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
573     stun_debug ("WARNING: Saved IDs full. STUN message dropped.");
574     return 0;
575   }
576
577   if (msg->key != NULL) {
578     key = msg->key;
579     key_len = msg->key_len;
580   }
581
582   if (key != NULL) {
583     bool skip = FALSE;
584
585     if (msg->long_term_valid) {
586       memcpy (md5, msg->long_term_key, sizeof(msg->long_term_key));
587     } else if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
588       uint8_t *realm = NULL;
589       uint8_t *username = NULL;
590       uint16_t realm_len;
591       uint16_t username_len;
592
593       realm = (uint8_t *) stun_message_find (msg,
594           STUN_ATTRIBUTE_REALM, &realm_len);
595       username = (uint8_t *) stun_message_find (msg,
596           STUN_ATTRIBUTE_USERNAME, &username_len);
597       if (username == NULL || realm == NULL) {
598         skip = TRUE;
599       } else {
600         stun_hash_creds (realm, realm_len,
601             username,  username_len,
602             key, key_len, md5);
603         memcpy (msg->long_term_key, md5, sizeof(msg->long_term_key));
604         msg->long_term_valid = TRUE;
605       }
606     }
607
608     /* If no realm/username and long term credentials,
609        then don't send the message integrity */
610     if (skip == FALSE) {
611       ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20);
612       if (ptr == NULL) {
613         return 0;
614       }
615       if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
616         if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
617             agent->compatibility == STUN_COMPATIBILITY_OC2007) {
618           stun_sha1 (msg->buffer, stun_message_length (msg),
619               stun_message_length (msg) - 20, ptr, md5, sizeof(md5), TRUE);
620         } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
621           size_t minus = 20;
622           if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
623             minus -= 8;
624
625           stun_sha1 (msg->buffer, stun_message_length (msg),
626               stun_message_length (msg) - minus, ptr, md5, sizeof(md5), TRUE);
627         } else {
628           stun_sha1 (msg->buffer, stun_message_length (msg),
629               stun_message_length (msg) - 20, ptr, md5, sizeof(md5), FALSE);
630         }
631       } else {
632         if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
633             agent->compatibility == STUN_COMPATIBILITY_OC2007) {
634           stun_sha1 (msg->buffer, stun_message_length (msg),
635               stun_message_length (msg) - 20, ptr, key, key_len, TRUE);
636         } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
637           size_t minus = 20;
638           if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
639             minus -= 8;
640
641           stun_sha1 (msg->buffer, stun_message_length (msg),
642               stun_message_length (msg) - minus, ptr, key, key_len, TRUE);
643         } else {
644           stun_sha1 (msg->buffer, stun_message_length (msg),
645               stun_message_length (msg) - 20, ptr, key, key_len, FALSE);
646         }
647       }
648
649       stun_debug (" Message HMAC-SHA1 message integrity:");
650       stun_debug_bytes ("  key     : ", key, key_len);
651       stun_debug_bytes ("  sent    : ", ptr, 20);
652     }
653   }
654
655   if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
656        agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
657       agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
658     ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4);
659     if (ptr == NULL) {
660       return 0;
661     }
662
663     fpr = stun_fingerprint (msg->buffer, stun_message_length (msg), FALSE);
664     memcpy (ptr, &fpr, sizeof (fpr));
665
666     stun_debug_bytes (" Message HMAC-SHA1 fingerprint: ", ptr, 4);
667   }
668
669
670   if (remember_transaction) {
671     stun_message_id (msg, agent->sent_ids[saved_id_idx].id);
672     agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg);
673     agent->sent_ids[saved_id_idx].key = (uint8_t *) key;
674     agent->sent_ids[saved_id_idx].key_len = key_len;
675     memcpy (agent->sent_ids[saved_id_idx].long_term_key, msg->long_term_key,
676         sizeof(msg->long_term_key));
677     agent->sent_ids[saved_id_idx].long_term_valid = msg->long_term_valid;
678     agent->sent_ids[saved_id_idx].valid = TRUE;
679   }
680
681   msg->key = (uint8_t *) key;
682   msg->key_len = key_len;
683   return stun_message_length (msg);
684
685 }
686
687 static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type)
688 {
689
690   uint16_t *known_attr = agent->known_attributes;
691
692   while(*known_attr != 0) {
693     if (*known_attr == type) {
694       return FALSE;
695     }
696     known_attr++;
697   }
698
699   return TRUE;
700
701 }
702
703
704 static unsigned
705 stun_agent_find_unknowns (StunAgent *agent, const StunMessage * msg,
706     uint16_t *list, unsigned max)
707 {
708   unsigned count = 0;
709   uint16_t len = stun_message_length (msg);
710   size_t offset = 0;
711
712   offset = STUN_MESSAGE_ATTRIBUTES_POS;
713
714   while ((offset < len) && (count < max))
715   {
716     size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN);
717     uint16_t atype = stun_getw (msg->buffer + offset);
718
719     if (!stun_optional (atype) && stun_agent_is_unknown (agent, atype))
720     {
721       stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)",
722            (unsigned)atype, (unsigned)alen);
723       list[count++] = htons (atype);
724     }
725
726     if (!(agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES))
727       alen = stun_align (alen);
728
729     offset += STUN_ATTRIBUTE_VALUE_POS + alen;
730   }
731
732   stun_debug ("STUN unknown: %u mandatory attribute(s)!", count);
733   return count;
734 }
735
736 void stun_agent_set_software (StunAgent *agent, const char *software)
737 {
738   agent->software_attribute = software;
739 }