Exemple #1
0
static unsigned
stun_agent_find_unknowns (StunAgent *agent, const StunMessage * msg,
    uint16_t *list, unsigned max)
{
  unsigned count = 0;
  uint16_t len = stun_message_length (msg);
  size_t offset = 0;

  offset = STUN_MESSAGE_ATTRIBUTES_POS;

  while ((offset < len) && (count < max))
  {
    size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN);
    uint16_t atype = stun_getw (msg->buffer + offset);

    offset += STUN_ATTRIBUTE_VALUE_POS + stun_align (alen);

    if (!stun_optional (atype) && stun_agent_is_unknown (agent, atype))
    {
      stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)\n",
           (unsigned)atype, (unsigned)alen);
      list[count++] = htons (atype);
    }
  }

  stun_debug ("STUN unknown: %u mandatory attribute(s)!\n", count);
  return count;
}
Exemple #2
0
static void test_hash_creds (void)
{
  uint8_t md5[16];
  uint8_t real_md5[] = {
    0x84, 0x93, 0xfb, 0xc5,
    0x3b, 0xa5, 0x82, 0xfb,
    0x4c, 0x04, 0x4c, 0x45,
    0x6b, 0xdc, 0x40, 0xeb};

  puts ("Testing long term credentials hash algorithm...");


  stun_hash_creds ("realm", strlen ("realm"),
      "user",  strlen ("user"),
      "pass", strlen ("pass"), md5);

  stun_debug ("key for user:realm:pass is : ");
  stun_debug_bytes (md5, 16);
  stun_debug ("\n\n");

  stun_debug ("RFC key for user:realm:pass is : ");
  stun_debug_bytes (real_md5, 16);
  stun_debug ("\n\n");

  if(memcmp (md5, real_md5, sizeof(md5)) != 0)
    fatal ("MD5 hashes are different!");

  puts ("Done!");

}
Exemple #3
0
bool stun_agent_default_validater (StunAgent *agent,
    StunMessage *message, uint8_t *username, uint16_t username_len,
    uint8_t **password, size_t *password_len, void *user_data)
{
  StunDefaultValidaterData* val = (StunDefaultValidaterData *) user_data;
  int i;

  for (i = 0; val && val[i].username ; i++) {
#if 0
    stun_debug ("Comparing username of size %d and %" PRIuPTR ": %d",
        username_len, val[i].username_len,
        (memcmp (username, val[i].username, username_len) == 0));
#endif
    stun_debug_bytes ("  First username: "******"  Second username: "******"Found valid username, returning password : '******'", *password);
      return TRUE;
    }
  }

  return FALSE;

}
Exemple #4
0
bool stun_agent_default_validater (StunAgent *agent,
    StunMessage *message, uint8_t *username, uint16_t username_len,
    uint8_t **password, size_t *password_len, void *user_data)
{
  StunDefaultValidaterData* val = (StunDefaultValidaterData *) user_data;
  int i;

  for (i = 0; val && val[i].username ; i++) {
    stun_debug ("Comparing username '");
    stun_debug_bytes (username, username_len);
    stun_debug ("' (%d) with '", username_len);
    stun_debug_bytes (val[i].username, val[i].username_len);
    stun_debug ("' (%d) : %d\n",
        val[i].username_len, memcmp (username, val[i].username, username_len));
    if (username_len == val[i].username_len &&
        memcmp (username, val[i].username, username_len) == 0) {
      *password = (uint8_t *) val[i].password;
      *password_len = val[i].password_len;
      stun_debug ("Found valid username, returning password : '******'\n", *password);
      return TRUE;
    }
  }

  return FALSE;

}
Exemple #5
0
void stun_debug_bytes (const void *data, size_t len)
{
    size_t i;

    stun_debug ("0x");
    for (i = 0; i < len; i++)
        stun_debug ("%02x", ((const unsigned char *)data)[i]);
}
Exemple #6
0
/**
 * Print debug information for the ICE Media
 *
 * @param pf   Print function for debug output
 * @param icem ICE Media object
 *
 * @return 0 if success, otherwise errorcode
 */
int icem_debug(struct re_printf *pf, const struct icem *icem)
{
	struct le *le;
	int err = 0;

	if (!icem)
		return 0;

	err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name);

	err |= re_hprintf(pf, " Local Candidates: %H",
			  icem_cands_debug, &icem->lcandl);
	err |= re_hprintf(pf, " Remote Candidates: %H",
			  icem_cands_debug, &icem->rcandl);
	err |= re_hprintf(pf, " Check list: [%s]%H",
			  ice_checkl_state2name(icem->state),
			  icem_candpairs_debug, &icem->checkl);
	err |= re_hprintf(pf, " Valid list: %H",
			  icem_candpairs_debug, &icem->validl);

	for (le = icem->compl.head; le; le = le->next) {

		const struct icem_comp *comp = le->data;

		if (comp->cp_sel) {
			err |= re_hprintf(pf, " Selected: %H\n",
					  icem_candpair_debug, comp->cp_sel);
		}
	}

	err |= stun_debug(pf, icem->stun);

	return err;
}
Exemple #7
0
static int
stun_bind_error (StunAgent *agent, StunMessage *msg,
                 uint8_t *buf, size_t *plen, const StunMessage *req,
                 StunError code)
{
    size_t len = *plen;
    int val;
    
    *plen = 0;
    stun_debug ("STUN Error Reply (buffer size: %u)...\n", (unsigned)len);
    
    val = stun_agent_init_error (agent, msg, buf, len, req, code);
    if (!val)
        return val;
    
    len = stun_agent_finish_message (agent, msg, NULL, 0);
    if (len == 0)
        return 0;
    
    *plen = len;
    stun_debug (" Error response (%u) of %u bytes\n", (unsigned)code,
                (unsigned)*plen);
    return 1;
}
Exemple #8
0
StunUsageTurnReturn stun_usage_turn_refresh_process (StunMessage *msg,
    uint32_t *lifetime, StunUsageTurnCompatibility compatibility)
{
  int code = -1;
  StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS;

  if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 ||
      compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) {
    if (stun_message_get_method (msg) != STUN_REFRESH)
      return STUN_USAGE_TURN_RETURN_INVALID;
  } else {
    if (stun_message_get_method (msg) != STUN_ALLOCATE)
      return STUN_USAGE_TURN_RETURN_INVALID;
  }

  switch (stun_message_get_class (msg))
  {
    case STUN_REQUEST:
    case STUN_INDICATION:
      return STUN_USAGE_TURN_RETURN_INVALID;

    case STUN_RESPONSE:
      break;

    case STUN_ERROR:
      if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
        /* missing ERROR-CODE: ignore message */
        return STUN_USAGE_TURN_RETURN_INVALID;
      }

      return STUN_USAGE_TURN_RETURN_ERROR;

    default:
      /* Fall through. */
      break;
  }

  stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime);

  stun_debug ("TURN Refresh successful!\n");
  return ret;

}
Exemple #9
0
/**
 * Print debug information for the ICE Session
 *
 * @param pf  Print function for debug output
 * @param ice ICE Session
 *
 * @return 0 if success, otherwise errorcode
 */
int ice_debug(struct re_printf *pf, const struct ice *ice)
{
	struct le *le;
	int err = 0;

	if (!ice)
		return 0;

	err |= re_hprintf(pf, " local_mode=%s, remote_mode=%s",
			  ice_mode2name(ice->lmode),
			  ice_mode2name(ice->rmode));
	err |= re_hprintf(pf, ", local_role=%s\n", ice_role2name(ice->lrole));
	err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n",
			  ice->lufrag, ice->lpwd);

	for (le = ice->ml.head; le; le = le->next)
		err |= icem_debug(pf, le->data);

	err |= stun_debug(pf, ice->stun);

	return err;
}
/**
 * Print debug information for the ICE Media
 *
 * @param pf   Print function for debug output
 * @param icem ICE Media object
 *
 * @return 0 if success, otherwise errorcode
 */
int icem_debug(struct re_printf *pf, const struct icem *icem)
{
	struct le *le;
	int err = 0;

	if (!icem)
		return 0;

	err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name);

	err |= re_hprintf(pf, " local_mode=%s, remote_mode=%s",
			  ice_mode2name(icem->lmode),
			  ice_mode2name(icem->rmode));
	err |= re_hprintf(pf, ", local_role=%s\n", ice_role2name(icem->lrole));
	err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n",
			  icem->lufrag, icem->lpwd);

	err |= re_hprintf(pf, " Components: (%u)\n", list_count(&icem->compl));
	for (le = icem->compl.head; le; le = le->next) {
		struct icem_comp *comp = le->data;

		err |= re_hprintf(pf, "  %H\n", icecomp_debug, comp);
	}

	err |= re_hprintf(pf, " Local Candidates: %H",
			  icem_cands_debug, &icem->lcandl);
	err |= re_hprintf(pf, " Remote Candidates: %H",
			  icem_cands_debug, &icem->rcandl);
	err |= re_hprintf(pf, " Check list: [state=%s]%H",
			  ice_checkl_state2name(icem->state),
			  icem_candpairs_debug, &icem->checkl);
	err |= re_hprintf(pf, " Valid list: %H",
			  icem_candpairs_debug, &icem->validl);

	err |= stun_debug(pf, icem->stun);

	return err;
}
Exemple #11
0
size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
    const uint8_t *key, size_t key_len)
{
  uint8_t *ptr;
  uint32_t fpr;
  int saved_id_idx = 0;
  uint8_t md5[16];

  if (stun_message_get_class (msg) == STUN_REQUEST) {
    for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) {
      if (agent->sent_ids[saved_id_idx].valid == FALSE) {
        break;
      }
    }
  }
  if (saved_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
    stun_debug ("Saved ids full");
    return 0;
  }

  if (msg->key != NULL) {
    key = msg->key;
    key_len = msg->key_len;
  }

  if (key != NULL) {
    bool skip = FALSE;

    if (msg->long_term_valid) {
      memcpy (md5, msg->long_term_key, sizeof(msg->long_term_key));
    } else if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
      uint8_t *realm = NULL;
      uint8_t *username = NULL;
      uint16_t realm_len;
      uint16_t username_len;

      realm = (uint8_t *) stun_message_find (msg,
          STUN_ATTRIBUTE_REALM, &realm_len);
      username = (uint8_t *) stun_message_find (msg,
          STUN_ATTRIBUTE_USERNAME, &username_len);
      if (username == NULL || realm == NULL) {
        skip = TRUE;
      } else {
        stun_hash_creds (realm, realm_len,
            username,  username_len,
            key, key_len, md5);
      }
      memcpy (msg->long_term_key, md5, sizeof(msg->long_term_key));
      msg->long_term_valid = TRUE;
    }

    /* If no realm/username and long term credentials,
       then don't send the message integrity */
    if (skip == FALSE) {
      ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20);
      if (ptr == NULL) {
        return 0;
      }
      if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
        if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, md5, sizeof(md5), TRUE);
        } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) {
          size_t minus = 20;
          if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
            minus -= 8;

          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - minus, ptr, md5, sizeof(md5), TRUE);
        } else {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, md5, sizeof(md5), FALSE);
        }
      } else {
        if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, key, key_len, TRUE);
        } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) {
          size_t minus = 20;
          if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
            minus -= 8;

          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - minus, ptr, key, key_len, TRUE);
        } else {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, key, key_len, FALSE);
        }
      }

      stun_debug (" Message HMAC-SHA1 message integrity:"
          "\n  key     : ");
      stun_debug_bytes (key, key_len);
      stun_debug ("\n  sent    : ");
      stun_debug_bytes (ptr, 20);
      stun_debug ("\n");
    }
  }

  if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
          agent->compatibility == STUN_COMPATIBILITY_WLM2009) &&
      agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
    ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4);
    if (ptr == NULL) {
      return 0;
    }

    fpr = stun_fingerprint (msg->buffer, stun_message_length (msg),
        agent->compatibility == STUN_COMPATIBILITY_WLM2009);
    memcpy (ptr, &fpr, sizeof (fpr));

    stun_debug (" Message HMAC-SHA1 fingerprint: ");
    stun_debug_bytes (ptr, 4);
    stun_debug ("\n");
  }


  if (stun_message_get_class (msg) == STUN_REQUEST) {
    stun_message_id (msg, agent->sent_ids[saved_id_idx].id);
    agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg);
    agent->sent_ids[saved_id_idx].key = (uint8_t *) key;
    agent->sent_ids[saved_id_idx].key_len = key_len;
    memcpy (agent->sent_ids[saved_id_idx].long_term_key, msg->long_term_key,
        sizeof(msg->long_term_key));
    agent->sent_ids[saved_id_idx].long_term_valid = msg->long_term_valid;
    agent->sent_ids[saved_id_idx].valid = TRUE;
  }

  msg->key = (uint8_t *) key;
  msg->key_len = key_len;
  return stun_message_length (msg);

}
Exemple #12
0
StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg,
    struct sockaddr *relay_addr, socklen_t *relay_addrlen,
    struct sockaddr *addr, socklen_t *addrlen,
    struct sockaddr *alternate_server, socklen_t *alternate_server_len,
    uint32_t *bandwidth, uint32_t *lifetime,
    StunUsageTurnCompatibility compatibility)
{
  int val, code = -1;
  StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS;

  if (stun_message_get_method (msg) != STUN_ALLOCATE)
    return STUN_USAGE_TURN_RETURN_INVALID;

  switch (stun_message_get_class (msg))
  {
    case STUN_REQUEST:
    case STUN_INDICATION:
      return STUN_USAGE_TURN_RETURN_INVALID;

    case STUN_RESPONSE:
      break;

    case STUN_ERROR:
      if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
        /* missing ERROR-CODE: ignore message */
        return STUN_USAGE_TURN_RETURN_INVALID;
      }

      /* NOTE: currently we ignore unauthenticated messages if the context
       * is authenticated, for security reasons. */
      stun_debug (" STUN error message received (code: %d)\n", code);

      /* ALTERNATE-SERVER mechanism */
      if ((code / 100) == 3) {
        if (alternate_server && alternate_server_len) {
          if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
                  alternate_server, alternate_server_len) !=
              STUN_MESSAGE_RETURN_SUCCESS) {
            stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
            return STUN_USAGE_TURN_RETURN_ERROR;
          }
        } else {
          if (!stun_message_has_attribute (msg,
                  STUN_ATTRIBUTE_ALTERNATE_SERVER)) {
            stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
            return STUN_USAGE_TURN_RETURN_ERROR;
          }
        }

        stun_debug ("Found alternate server\n");
        return STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER;

      }
      return STUN_USAGE_TURN_RETURN_ERROR;
  }

  stun_debug ("Received %u-bytes STUN message\n", stun_message_length (msg));

  if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9) {
    val = stun_message_find_xor_addr (msg,
        STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen);

    if (val == STUN_MESSAGE_RETURN_SUCCESS)
      ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;
    val = stun_message_find_xor_addr (msg,
        STUN_ATTRIBUTE_RELAY_ADDRESS, relay_addr, relay_addrlen);
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
      stun_debug (" No RELAYED-ADDRESS: %d\n", val);
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
  } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_GOOGLE) {
    val = stun_message_find_addr (msg,
        STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
      stun_debug (" No MAPPED-ADDRESS: %d\n", val);
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
  } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_MSN) {
    val = stun_message_find_addr (msg,
        STUN_ATTRIBUTE_MSN_MAPPED_ADDRESS, addr, addrlen);

    if (val == STUN_MESSAGE_RETURN_SUCCESS)
      ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS;

    val = stun_message_find_addr (msg,
        STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen);
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
      stun_debug (" No MAPPED-ADDRESS: %d\n", val);
      return STUN_USAGE_TURN_RETURN_ERROR;
    }
  }

  stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime);
  stun_message_find32 (msg, STUN_ATTRIBUTE_BANDWIDTH, bandwidth);

  stun_debug (" Mapped address found!\n");
  return ret;

}
Exemple #13
0
size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
    const uint8_t *key, size_t key_len)
{
  uint8_t *ptr;
  uint32_t fpr;
  int saved_id_idx = 0;
  uint8_t md5[16];
  bool remember_transaction;

  remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST);

  if (agent->compatibility == STUN_COMPATIBILITY_OC2007 &&
      stun_message_get_method (msg) == STUN_SEND) {
    /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to
     * STUN_SEND requests, so don't bother waiting for them. More details at
     * https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx.
     */
    remember_transaction = FALSE;
  }

  if (remember_transaction) {
    for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) {
      if (agent->sent_ids[saved_id_idx].valid == FALSE) {
        break;
      }
    }
  }
  if (saved_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
    stun_debug ("WARNING: Saved IDs full. STUN message dropped.");
    return 0;
  }

  if (msg->key != NULL) {
    key = msg->key;
    key_len = msg->key_len;
  }

  if (key != NULL) {
    bool skip = FALSE;

    if (msg->long_term_valid) {
      memcpy (md5, msg->long_term_key, sizeof(msg->long_term_key));
    } else if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
      uint8_t *realm = NULL;
      uint8_t *username = NULL;
      uint16_t realm_len;
      uint16_t username_len;

      realm = (uint8_t *) stun_message_find (msg,
          STUN_ATTRIBUTE_REALM, &realm_len);
      username = (uint8_t *) stun_message_find (msg,
          STUN_ATTRIBUTE_USERNAME, &username_len);
      if (username == NULL || realm == NULL) {
        skip = TRUE;
      } else {
        stun_hash_creds (realm, realm_len,
            username,  username_len,
            key, key_len, md5);
        memcpy (msg->long_term_key, md5, sizeof(msg->long_term_key));
        msg->long_term_valid = TRUE;
      }
    }

    /* If no realm/username and long term credentials,
       then don't send the message integrity */
    if (skip == FALSE) {
      ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20);
      if (ptr == NULL) {
        return 0;
      }
      if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
        if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
            agent->compatibility == STUN_COMPATIBILITY_OC2007) {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, md5, sizeof(md5), TRUE);
        } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) {
          size_t minus = 20;
          if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
            minus -= 8;

          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - minus, ptr, md5, sizeof(md5), TRUE);
        } else {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, md5, sizeof(md5), FALSE);
        }
      } else {
        if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
            agent->compatibility == STUN_COMPATIBILITY_OC2007) {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, key, key_len, TRUE);
        } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) {
          size_t minus = 20;
          if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
            minus -= 8;

          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - minus, ptr, key, key_len, TRUE);
        } else {
          stun_sha1 (msg->buffer, stun_message_length (msg),
              stun_message_length (msg) - 20, ptr, key, key_len, FALSE);
        }
      }

      stun_debug (" Message HMAC-SHA1 message integrity:");
      stun_debug_bytes ("  key     : ", key, key_len);
      stun_debug_bytes ("  sent    : ", ptr, 20);
    }
  }

  if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
          agent->compatibility == STUN_COMPATIBILITY_WLM2009) &&
      agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
    ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4);
    if (ptr == NULL) {
      return 0;
    }

    fpr = stun_fingerprint (msg->buffer, stun_message_length (msg),
        agent->compatibility == STUN_COMPATIBILITY_WLM2009);
    memcpy (ptr, &fpr, sizeof (fpr));

    stun_debug_bytes (" Message HMAC-SHA1 fingerprint: ", ptr, 4);
  }


  if (remember_transaction) {
    stun_message_id (msg, agent->sent_ids[saved_id_idx].id);
    agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg);
    agent->sent_ids[saved_id_idx].key = (uint8_t *) key;
    agent->sent_ids[saved_id_idx].key_len = key_len;
    memcpy (agent->sent_ids[saved_id_idx].long_term_key, msg->long_term_key,
        sizeof(msg->long_term_key));
    agent->sent_ids[saved_id_idx].long_term_valid = msg->long_term_valid;
    agent->sent_ids[saved_id_idx].valid = TRUE;
  }

  msg->key = (uint8_t *) key;
  msg->key_len = key_len;
  return stun_message_length (msg);

}
Exemple #14
0
StunUsageIceReturn
stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req,
                                       StunMessage *msg, uint8_t *buf, size_t *plen,
                                       const struct sockaddr *src, socklen_t srclen,
                                       bool *control, uint64_t tie,
                                       StunUsageIceCompatibility compatibility)
{
    const char *username = NULL;
    uint16_t username_len;
    size_t len = *plen;
    uint64_t q;
    StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS;
    StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS;
    
    
#define err( code ) \
stun_bind_error (agent, msg, buf, &len, req, code); \
*plen = len
    
    *plen = 0;
    stun_debug ("STUN Reply (buffer size = %u)...\n", (unsigned)len);
    
    if (stun_message_get_class (req) != STUN_REQUEST)
    {
        stun_debug (" Unhandled non-request (class %u) message.\n",
                    stun_message_get_class (req));
        return STUN_USAGE_ICE_RETURN_INVALID_REQUEST;
    }
    
    if (stun_message_get_method (req) != STUN_BINDING)
    {
        stun_debug (" Bad request (method %u) message.\n",
                    stun_message_get_method (req));
        err (STUN_ERROR_BAD_REQUEST);
        return STUN_USAGE_ICE_RETURN_INVALID_METHOD;
    }
    
    /* Role conflict handling */
    assert (control != NULL);
    if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING
                             : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS)
    {
        stun_debug ("STUN Role Conflict detected:\n");
        
        if (tie < q)
        {
            stun_debug (" switching role from \"controll%s\" to \"controll%s\"\n",
                        *control ? "ing" : "ed", *control ? "ed" : "ing");
            *control = !*control;
            ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
        }
        else
        {
            stun_debug (" staying \"controll%s\" (sending error)\n",
                        *control ? "ing" : "ed");
            err (STUN_ERROR_ROLE_CONFLICT);
            return STUN_USAGE_ICE_RETURN_SUCCESS;
        }
    } else {
        stun_debug ("STUN Role not specified by peer!\n");
    }
    
    if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) {
        stun_debug ("Unable to create response\n");
        goto failure;
    }
    if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) {
        StunTransactionId transid;
        uint32_t magic_cookie;
        stun_message_id (msg, transid);
        magic_cookie = *((uint32_t *) transid);
        
        val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
                                                 src, srclen, htonl (magic_cookie));
    } else if (stun_message_has_cookie (msg)) {
        val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
                                            src, srclen);
    } else {
        val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS,
                                        src, srclen);
    }
    
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
        stun_debug (" Mapped address problem: %d\n", val);
        goto failure;
    }
    
    username = (const char *)stun_message_find (req,
                                                STUN_ATTRIBUTE_USERNAME, &username_len);
    if (username) {
        val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
                                         username, username_len);
    }
    
    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
        stun_debug ("Error appending username: %d\n", val);
        goto failure;
    }
    
    
    
    /* the stun agent will automatically use the password of the request */
    len = stun_agent_finish_message (agent, msg, NULL, 0);
    if (len == 0)
        goto failure;
    
    *plen = len;
    stun_debug (" All done (response size: %u)\n", (unsigned)len);
    return ret;
    
failure:
    assert (*plen == 0);
    stun_debug (" Fatal error formatting Response: %d\n", val);
    
    switch (val)
    {
        case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE:
            return STUN_USAGE_ICE_RETURN_MEMORY_ERROR;
        case STUN_MESSAGE_RETURN_INVALID:
        case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS:
            return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS;
        default:
            return STUN_USAGE_ICE_RETURN_ERROR;
    }
}
Exemple #15
0
StunUsageBindReturn stun_usage_bind_process (StunMessage *msg,
    struct sockaddr *addr, socklen_t *addrlen,
    struct sockaddr *alternate_server, socklen_t *alternate_server_len)
{
  int code = -1;
  StunMessageReturn val;

  if (stun_message_get_method (msg) != STUN_BINDING)
    return STUN_USAGE_BIND_RETURN_INVALID;

  switch (stun_message_get_class (msg))
  {
    case STUN_REQUEST:
    case STUN_INDICATION:
      return STUN_USAGE_BIND_RETURN_INVALID;

    case STUN_RESPONSE:
      break;

    case STUN_ERROR:
      if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
        /* missing ERROR-CODE: ignore message */
        return STUN_USAGE_BIND_RETURN_INVALID;
      }

      /* NOTE: currently we ignore unauthenticated messages if the context
       * is authenticated, for security reasons. */
      stun_debug (" STUN error message received (code: %d)", code);

      /* ALTERNATE-SERVER mechanism */
      if ((code / 100) == 3) {
        if (alternate_server && alternate_server_len) {
          if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
                  (struct sockaddr_storage *) alternate_server,
                  alternate_server_len) != STUN_MESSAGE_RETURN_SUCCESS) {
            stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
            return STUN_USAGE_BIND_RETURN_ERROR;
          }
        } else {
          if (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER)) {
            stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
            return STUN_USAGE_BIND_RETURN_ERROR;
          }
        }

        stun_debug ("Found alternate server");
        return STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER;

      }
      return STUN_USAGE_BIND_RETURN_ERROR;

    default:
      /* Fall through. */
      break;
  }

  stun_debug ("Received %u-bytes STUN message", stun_message_length (msg));

  val = stun_message_find_xor_addr (msg,
      STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, (struct sockaddr_storage *)addr,
      addrlen);
  if (val != STUN_MESSAGE_RETURN_SUCCESS)
  {
    stun_debug (" No XOR-MAPPED-ADDRESS: %d", val);
    val = stun_message_find_addr (msg,
        STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr_storage *)addr,
        addrlen);
    if (val != STUN_MESSAGE_RETURN_SUCCESS)
    {
      stun_debug (" No MAPPED-ADDRESS: %d", val);
      return STUN_USAGE_BIND_RETURN_ERROR;
    }
  }

  stun_debug (" Mapped address found!");
  return STUN_USAGE_BIND_RETURN_SUCCESS;

}
Exemple #16
0
StunUsageIceReturn
stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req,
    StunMessage *msg, uint8_t *buf, size_t *plen,
    const struct sockaddr_storage *src, socklen_t srclen,
    bool *control, uint64_t tie,
    StunUsageIceCompatibility compatibility)
{
  const char *username = NULL;
  uint16_t username_len;
  size_t len = *plen;
  uint64_t q;
  StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS;
  StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS;


#define err( code ) \
  stun_bind_error (agent, msg, buf, &len, req, code); \
  *plen = len

  *plen = 0;
  stun_debug ("STUN Reply (buffer size = %u)...", (unsigned)len);

  if (stun_message_get_class (req) != STUN_REQUEST)
  {
    stun_debug (" Unhandled non-request (class %u) message.",
         stun_message_get_class (req));
    return STUN_USAGE_ICE_RETURN_INVALID_REQUEST;
  }

  if (stun_message_get_method (req) != STUN_BINDING)
  {
    stun_debug (" Bad request (method %u) message.",
         stun_message_get_method (req));
    err (STUN_ERROR_BAD_REQUEST);
    return STUN_USAGE_ICE_RETURN_INVALID_METHOD;
  }

  /* Role conflict handling */
  assert (control != NULL);
  if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING
          : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS)
  {
    /* we have the ice-controlling/controlled attribute,
     * and there's a role conflict
     */
    stun_debug ("STUN Role Conflict detected:");

    /* According to ICE RFC 5245, section 7.2.1.1, we consider the four
     * possible cases when a role conflict is detected: two cases are
     * resolved by switching role locally, and the two other cases are
     * handled by responding with a STUN error.
     */
    if ((tie < q && *control) || (tie >= q && !*control))
    {
      stun_debug (" switching role from \"controll%s\" to \"controll%s\"",
           *control ? "ing" : "ed", *control ? "ed" : "ing");
      *control = !*control;
      ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
    }
    else
    {
      stun_debug (" staying \"controll%s\" (sending error)",
           *control ? "ing" : "ed");
      err (STUN_ERROR_ROLE_CONFLICT);
      return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
    }
  } else {
    if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED
            : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS)
    {
      /* we don't have the expected ice-controlling/controlled
       * attribute
       */
      if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 ||
          compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2)
      {
        stun_debug ("STUN Role not specified by peer!");
      }
    }
  }

  if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) {
    stun_debug ("Unable to create response");
    goto failure;
  }
  if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) {
    union {
      StunTransactionId transid;
      uint32_t magic_cookie;
    } conv;

    stun_message_id (msg, conv.transid);

    val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
        src, srclen, htonl (conv.magic_cookie));
  } else if (stun_message_has_cookie (msg) &&
      compatibility != STUN_USAGE_ICE_COMPATIBILITY_GOOGLE) {
    val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
        src, srclen);
  } else {
    val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS,
        (struct sockaddr *) src, srclen);
  }

  if (val != STUN_MESSAGE_RETURN_SUCCESS) {
    stun_debug (" Mapped address problem: %d", val);
    goto failure;
  }

  username = (const char *)stun_message_find (req,
      STUN_ATTRIBUTE_USERNAME, &username_len);
  if (username) {
    val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
        username, username_len);
  }

  if (val != STUN_MESSAGE_RETURN_SUCCESS) {
    stun_debug ("Error appending username: %d", val);
    goto failure;
  }

  if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) {
    val = stun_message_append32 (msg,
        STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION, 2);

    if (val != STUN_MESSAGE_RETURN_SUCCESS) {
      stun_debug ("Error appending implementation version: %d", val);
      goto failure;
    }
  }

  /* the stun agent will automatically use the password of the request */
  len = stun_agent_finish_message (agent, msg, NULL, 0);
  if (len == 0)
    goto failure;

  *plen = len;
  stun_debug (" All done (response size: %u)", (unsigned)len);
  return ret;

failure:
  assert (*plen == 0);
  stun_debug (" Fatal error formatting Response: %d", val);

  switch (val)
  {
    case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE:
      return STUN_USAGE_ICE_RETURN_MEMORY_ERROR;
    case STUN_MESSAGE_RETURN_INVALID:
    case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS:
      return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS;
    case STUN_MESSAGE_RETURN_SUCCESS:
      assert (0);  /* shouldn’t be reached */
    case STUN_MESSAGE_RETURN_NOT_FOUND:
    default:
      return STUN_USAGE_ICE_RETURN_ERROR;
  }
}
Exemple #17
0
/* 
 * Timer callback that handles scheduling new candidate discovery
 * processes (paced by the Ta timer), and handles running of the 
 * existing discovery processes.
 *
 * This function is designed for the g_timeout_add() interface.
 *
 * @return will return FALSE when no more pending timers.
 */
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
  size_t buffer_len = 0;

  {
    static int tick_counter = 0;
    if (tick_counter++ % 50 == 0)
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
  }

  for (i = agent->discovery_list; i ; i = i->next) {
    cand = i->data;

    if (cand->pending != TRUE) {
      cand->pending = TRUE;

      if (agent->discovery_unsched_items)
	--agent->discovery_unsched_items;

      if (nice_debug_is_enabled ()) {
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.",
            agent, cand->type, tmpbuf);
      }
      if (nice_address_is_valid (&cand->server) &&
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {

        if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED ||
            cand->component->state == NICE_COMPONENT_STATE_FAILED)
          agent_signal_component_state_change (agent,
					       cand->stream->id,
					       cand->component->id,
					       NICE_COMPONENT_STATE_GATHERING);

        if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
          buffer_len = stun_usage_bind_create (&cand->stun_agent,
              &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
        } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
          uint8_t *username = (uint8_t *)cand->turn->username;
          gsize username_len = strlen (cand->turn->username);
          uint8_t *password = (uint8_t *)cand->turn->password;
          gsize password_len = strlen (cand->turn->password);
          StunUsageTurnCompatibility turn_compat =
              agent_to_turn_compatibility (agent);

          if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
              turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
            username = g_base64_decode ((gchar *)username, &username_len);
            password = g_base64_decode ((gchar *)password, &password_len);
          }

          buffer_len = stun_usage_turn_create (&cand->stun_agent,
              &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
              cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
              STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
              -1, -1,
              username, username_len,
              password, password_len,
              turn_compat);

          if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
              turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
            g_free (username);
            g_free (password);
          }
        }

	if (buffer_len > 0) {
          if (nice_socket_is_reliable (cand->nicesock)) {
            stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout);
          } else {
            stun_timer_start (&cand->timer,
                agent->stun_initial_timeout,
                agent->stun_max_retransmissions);
          }

          /* send the conncheck */
          agent_socket_send (cand->nicesock, &cand->server,
              buffer_len, (gchar *)cand->stun_buffer);

	  /* case: success, start waiting for the result */
	  g_get_current_time (&cand->next_tick);

	} else {
	  /* case: error in starting discovery, start the next discovery */
	  cand->done = TRUE;
	  cand->stun_message.buffer = NULL;
	  cand->stun_message.buffer_len = 0;
	  continue;
	}
      }
      else
	/* allocate relayed candidates */
	g_assert_not_reached ();

      ++not_done; /* note: new discovery scheduled */
    }

    if (cand->done != TRUE) {
      GTimeVal now;

      g_get_current_time (&now);

      if (cand->stun_message.buffer == NULL) {
	nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
	cand->done = TRUE;
      }
      else if (priv_timer_expired (&cand->next_tick, &now)) {
        switch (stun_timer_refresh (&cand->timer)) {
          case STUN_USAGE_TIMER_RETURN_TIMEOUT:
            {
              /* Time out */
              /* case: error, abort processing */
              StunTransactionId id;

              stun_message_id (&cand->stun_message, id);
              stun_agent_forget_transaction (&cand->stun_agent, id);

              cand->done = TRUE;
              cand->stun_message.buffer = NULL;
              cand->stun_message.buffer_len = 0;
              nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
              break;
            }
          case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
            {
              /* case: not ready complete, so schedule next timeout */
              unsigned int timeout = stun_timer_remainder (&cand->timer);

              stun_debug ("STUN transaction retransmitted (timeout %dms).",
                  timeout);

              /* retransmit */
              agent_socket_send (cand->nicesock, &cand->server,
                  stun_message_length (&cand->stun_message),
                  (gchar *)cand->stun_buffer);

              /* note: convert from milli to microseconds for g_time_val_add() */
              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

              ++not_done; /* note: retry later */
              break;
            }
          case STUN_USAGE_TIMER_RETURN_SUCCESS:
            {
              unsigned int timeout = stun_timer_remainder (&cand->timer);

              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

              ++not_done; /* note: retry later */
              break;
            }
          default:
            /* Nothing to do. */
            break;
	}

      } else {
	++not_done; /* note: discovery not expired yet */
      }
    }
  }

  if (not_done == 0) {
    nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);

    discovery_free (agent);

    agent_gathering_done (agent);

    /* note: no pending timers, return FALSE to stop timer */
    return FALSE;
  }

  return TRUE;
}
Exemple #18
0
StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg,
    const uint8_t *buffer, size_t buffer_len,
    StunMessageIntegrityValidate validater, void * validater_data)
{
  StunTransactionId msg_id;
  uint32_t fpr;
  uint32_t crc32;
  int len;
  uint8_t *username = NULL;
  uint16_t username_len;
  uint8_t *key = NULL;
  size_t key_len;
  uint8_t *hash;
  uint8_t sha[20];
  uint16_t hlen;
  int sent_id_idx = -1;
  uint16_t unknown;
  int error_code;
  int ignore_credentials = 0;
  uint8_t long_term_key[16];
  bool long_term_key_valid = FALSE;

  len = stun_message_validate_buffer_length (buffer, buffer_len);
  if (len == STUN_MESSAGE_BUFFER_INVALID) {
    return STUN_VALIDATION_NOT_STUN;
  } else if (len == STUN_MESSAGE_BUFFER_INCOMPLETE) {
    return STUN_VALIDATION_INCOMPLETE_STUN;
  } else if (len != (int) buffer_len) {
    return STUN_VALIDATION_NOT_STUN;
  }

  msg->buffer = (uint8_t *) buffer;
  msg->buffer_len = buffer_len;
  msg->agent = agent;
  msg->key = NULL;
  msg->key_len = 0;
  msg->long_term_valid = FALSE;

  /* TODO: reject it or not ? */
  if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
          agent->compatibility == STUN_COMPATIBILITY_WLM2009) &&
      !stun_message_has_cookie (msg)) {
      stun_debug ("STUN demux error: no cookie!\n");
      return STUN_VALIDATION_BAD_REQUEST;
  }

  if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
          agent->compatibility == STUN_COMPATIBILITY_WLM2009) &&
      agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
    /* Looks for FINGERPRINT */
    if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) !=
        STUN_MESSAGE_RETURN_SUCCESS) {
      stun_debug ("STUN demux error: no FINGERPRINT attribute!\n");
      return STUN_VALIDATION_BAD_REQUEST;
    }
    /* Checks FINGERPRINT */
    crc32 = stun_fingerprint (msg->buffer, stun_message_length (msg),
        agent->compatibility == STUN_COMPATIBILITY_WLM2009);
    fpr = ntohl (fpr);
    if (fpr != crc32) {
      stun_debug ("STUN demux error: bad fingerprint: 0x%08x,"
          " expected: 0x%08x!\n", fpr, crc32);
      return STUN_VALIDATION_BAD_REQUEST;
    }

    stun_debug ("STUN demux: OK!\n");
  }

  if (stun_message_get_class (msg) == STUN_RESPONSE ||
      stun_message_get_class (msg) == STUN_ERROR) {
    stun_message_id (msg, msg_id);
    for (sent_id_idx = 0; sent_id_idx < STUN_AGENT_MAX_SAVED_IDS; sent_id_idx++) {
      if (agent->sent_ids[sent_id_idx].valid == TRUE &&
          agent->sent_ids[sent_id_idx].method == stun_message_get_method (msg) &&
          memcmp (msg_id, agent->sent_ids[sent_id_idx].id,
              sizeof(StunTransactionId)) == 0) {

        key = agent->sent_ids[sent_id_idx].key;
        key_len = agent->sent_ids[sent_id_idx].key_len;
        memcpy (long_term_key, agent->sent_ids[sent_id_idx].long_term_key,
            sizeof(long_term_key));
        long_term_key_valid = agent->sent_ids[sent_id_idx].long_term_valid;
        break;
      }
    }
    if (sent_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
      return STUN_VALIDATION_UNMATCHED_RESPONSE;
    }
  }

  ignore_credentials =
      (agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) ||
      (stun_message_get_class (msg) == STUN_ERROR &&
       stun_message_find_error (msg, &error_code) ==
          STUN_MESSAGE_RETURN_SUCCESS &&
       (error_code == 400 || error_code == 401)) ||
      (stun_message_get_class (msg) == STUN_INDICATION &&
       (agent->usage_flags & STUN_AGENT_USAGE_NO_INDICATION_AUTH));

  if (key == NULL &&
      ignore_credentials == 0 &&
      (stun_message_get_class (msg) == STUN_REQUEST ||
       stun_message_get_class (msg) == STUN_INDICATION) &&
      (((agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS) &&
       (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
        !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) ||
      ((agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) &&
        stun_message_get_class (msg) == STUN_REQUEST &&
        (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
         !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) ||
         !stun_message_has_attribute (msg, STUN_ATTRIBUTE_NONCE) ||
         !stun_message_has_attribute (msg, STUN_ATTRIBUTE_REALM))) ||
       ((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 &&
         stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) &&
         !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY)))) {
        return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST;
  }

  if (stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) &&
      ((key == NULL && ignore_credentials == 0) ||
          (agent->usage_flags & STUN_AGENT_USAGE_FORCE_VALIDATER))) {
    username_len = 0;
    username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME,
        &username_len);
    if (validater == NULL ||
        validater (agent, msg, username, username_len,
            &key, &key_len, validater_data) == FALSE) {
      return STUN_VALIDATION_UNAUTHORIZED;
    }
  }

  if (ignore_credentials == 0 && key != NULL && key_len > 0) {
    hash = (uint8_t *) stun_message_find (msg,
        STUN_ATTRIBUTE_MESSAGE_INTEGRITY, &hlen);

    if (hash) {
      /* We must give the size from start to the end of the attribute
         because you might have a FINGERPRINT attribute after it... */
      if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
        uint8_t *realm = NULL;
        uint8_t *username = NULL;
        uint16_t realm_len;
        uint16_t username_len;
        uint8_t md5[16];

        if (long_term_key_valid) {
          memcpy (md5, long_term_key, sizeof (md5));
        } else {
          realm = (uint8_t *) stun_message_find (msg,  STUN_ATTRIBUTE_REALM, &realm_len);
          username = (uint8_t *) stun_message_find (msg,
              STUN_ATTRIBUTE_USERNAME, &username_len);
          if (username == NULL || realm == NULL) {
            return STUN_VALIDATION_UNAUTHORIZED;
          }
          stun_hash_creds (realm, realm_len,
              username,  username_len,
              key, key_len, md5);
        }

        memcpy (msg->long_term_key, md5, sizeof(md5));
        msg->long_term_valid = TRUE;

        if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) {
          stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
              sha, md5, sizeof(md5), TRUE);
        } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) {
          stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
              stun_message_length (msg) - 20, sha, md5, sizeof(md5), TRUE);
        } else {
          stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
              hash - msg->buffer, sha, md5, sizeof(md5), FALSE);
        }
      } else {
        if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) {
          stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
              sha, key, key_len, TRUE);
        } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) {
          stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
              stun_message_length (msg) - 20, sha, key, key_len, TRUE);
        } else {
          stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
              hash - msg->buffer, sha, key, key_len, FALSE);
        }
      }

      stun_debug (" Message HMAC-SHA1 fingerprint:");
      stun_debug ("\nkey     : ");
      stun_debug_bytes (key, key_len);
      stun_debug ("\n  expected: ");
      stun_debug_bytes (sha, sizeof (sha));
      stun_debug ("\n  received: ");
      stun_debug_bytes (hash, sizeof (sha));
      stun_debug ("\n");

      if (memcmp (sha, hash, sizeof (sha)))  {
        stun_debug ("STUN auth error: SHA1 fingerprint mismatch!\n");
        return STUN_VALIDATION_UNAUTHORIZED;
      }

      stun_debug ("STUN auth: OK!\n");
      msg->key = key;
      msg->key_len = key_len;
    } else if (!(stun_message_get_class (msg) == STUN_ERROR &&
        stun_message_find_error (msg, &error_code) ==
            STUN_MESSAGE_RETURN_SUCCESS &&
        (error_code == 400 || error_code == 401))) {
      stun_debug ("STUN auth error: No message integrity attribute!\n");
      return STUN_VALIDATION_UNAUTHORIZED;
    }
  }


  if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) {
    agent->sent_ids[sent_id_idx].valid = FALSE;
  }

  if (stun_agent_find_unknowns (agent, msg, &unknown, 1) > 0) {
    if (stun_message_get_class (msg) == STUN_REQUEST)
      return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE;
    else
      return STUN_VALIDATION_UNKNOWN_ATTRIBUTE;
  }
  return STUN_VALIDATION_SUCCESS;

}
Exemple #19
0
StunUsageIceReturn stun_usage_ice_conncheck_process (StunMessage *msg,
                                                     struct sockaddr *addr, socklen_t *addrlen,
                                                     StunUsageIceCompatibility compatibility)
{
    int code = -1;
    StunMessageReturn val;
    
    if (stun_message_get_method (msg) != STUN_BINDING)
        return STUN_USAGE_ICE_RETURN_INVALID;
    
    switch (stun_message_get_class (msg))
    {
        case STUN_REQUEST:
        case STUN_INDICATION:
            return STUN_USAGE_ICE_RETURN_INVALID;
            
        case STUN_RESPONSE:
            break;
            
        case STUN_ERROR:
            if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
                /* missing ERROR-CODE: ignore message */
                return STUN_USAGE_ICE_RETURN_INVALID;
            }
            
            if (code  == STUN_ERROR_ROLE_CONFLICT)
                return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
            
            /* NOTE: currently we ignore unauthenticated messages if the context
             * is authenticated, for security reasons. */
            stun_debug (" STUN error message received (code: %d)\n", code);
            
            return STUN_USAGE_ICE_RETURN_ERROR;
    }
    
    stun_debug ("Received %u-bytes STUN message\n", stun_message_length (msg));
    
    if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) {
        StunTransactionId transid;
        uint32_t magic_cookie;
        stun_message_id (msg, transid);
        magic_cookie = *((uint32_t *) transid);
        
        val = stun_message_find_xor_addr_full (msg,
                                               STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen, htonl (magic_cookie));
    } else {
        val = stun_message_find_xor_addr (msg,
                                          STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen);
    }
    if (val != STUN_MESSAGE_RETURN_SUCCESS)
    {
        stun_debug (" No XOR-MAPPED-ADDRESS: %d\n", val);
        val = stun_message_find_addr (msg,
                                      STUN_ATTRIBUTE_MAPPED_ADDRESS, addr, addrlen);
        if (val != STUN_MESSAGE_RETURN_SUCCESS)
        {
            stun_debug (" No MAPPED-ADDRESS: %d\n", val);
            return STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS;
        }
    }
    
    stun_debug ("Mapped address found!\n");
    return STUN_USAGE_ICE_RETURN_SUCCESS;
}