mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-25 08:27:39 +02:00
cleanup
This commit is contained in:
parent
7522282c2e
commit
42178d1716
7 changed files with 80 additions and 53 deletions
|
@ -123,6 +123,22 @@ impl Controller {
|
|||
}));
|
||||
}
|
||||
|
||||
/// Launched as a task when the DB informs us of a change.
|
||||
async fn handle_change_notification(self: Arc<Self>, change: Change) {
|
||||
match change {
|
||||
Change::NetworkCreated(_) => {}
|
||||
Change::NetworkChanged(_, _) => {}
|
||||
Change::NetworkDeleted(_, _) => {} // TODO: somehow poison whole network
|
||||
Change::MemberCreated(_) => {}
|
||||
Change::MemberChanged(old_member, new_member) => {
|
||||
if !new_member.authorized() && old_member.authorized() {
|
||||
self.deauthorize_member(&new_member).await;
|
||||
}
|
||||
}
|
||||
Change::MemberDeleted(member) => self.deauthorize_member(&member).await,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compose and send network configuration packet (either V1 or V2)
|
||||
fn send_network_config(
|
||||
&self,
|
||||
|
@ -182,8 +198,8 @@ impl Controller {
|
|||
}
|
||||
}
|
||||
|
||||
/// Send one or more revocation object(s) to a peer.
|
||||
fn send_revocations(&self, peer: &Peer, mut revocations: Vec<Revocation>) {
|
||||
/// Send one or more revocation object(s) to a peer. The provided vector is drained.
|
||||
fn v1_proto_send_revocations(&self, peer: &Peer, revocations: &mut Vec<Revocation>) {
|
||||
if let Some(host_system) = self.service.read().unwrap().upgrade() {
|
||||
let time_ticks = ms_monotonic();
|
||||
while !revocations.is_empty() {
|
||||
|
@ -218,8 +234,26 @@ impl Controller {
|
|||
}
|
||||
}
|
||||
|
||||
/// Called when the DB informs us of a change.
|
||||
async fn handle_change_notification(self: Arc<Self>, change: Change) {}
|
||||
async fn deauthorize_member(&self, member: &Member) {
|
||||
let time_clock = ms_since_epoch();
|
||||
let mut revocations = Vec::with_capacity(1);
|
||||
if let Ok(all_network_members) = self.database.list_members(member.network_id).await {
|
||||
for m in all_network_members.iter() {
|
||||
if member.node_id != *m {
|
||||
if let Some(peer) = self.service.read().unwrap().upgrade().and_then(|s| s.node().peer(*m)) {
|
||||
if peer.is_v2() {
|
||||
todo!();
|
||||
} else {
|
||||
revocations.clear();
|
||||
Revocation::new(member.network_id, time_clock, member.node_id, *m, &self.local_identity, false)
|
||||
.map(|r| revocations.push(r));
|
||||
self.v1_proto_send_revocations(&peer, &mut revocations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to create a network configuration and return the result.
|
||||
///
|
||||
|
@ -231,11 +265,11 @@ impl Controller {
|
|||
///
|
||||
/// An error is only returned if a database or other unusual error occurs. Otherwise a rejection
|
||||
/// reason is returned with None or an acceptance reason with a network configuration is returned.
|
||||
async fn get_network_config(
|
||||
async fn authorize(
|
||||
self: &Arc<Self>,
|
||||
source_identity: &Verified<Identity>,
|
||||
network_id: NetworkId,
|
||||
now: i64,
|
||||
time_clock: i64,
|
||||
) -> Result<(AuthorizationResult, Option<NetworkConfig>, Option<Vec<vl2::v1::Revocation>>), Box<dyn Error + Send + Sync>> {
|
||||
let network = self.database.get_network(network_id).await?;
|
||||
if network.is_none() {
|
||||
|
@ -246,8 +280,8 @@ impl Controller {
|
|||
let mut member = self.database.get_member(network_id, source_identity.address).await?;
|
||||
let mut member_changed = false;
|
||||
|
||||
// WARNING: this is where members are verified before they get admitted to a network. Read and edit
|
||||
// very carefully!
|
||||
// SECURITY WARNING: this is a critical code path where members of networks are authorized.
|
||||
// Read and modify with extreme care.
|
||||
|
||||
// If we have a member object and a pinned identity, check to make sure it matches. Also accept
|
||||
// upgraded identities to replace old versions if they are properly formed and inherit.
|
||||
|
@ -264,7 +298,9 @@ impl Controller {
|
|||
}
|
||||
} else if let Some(pinned_fingerprint) = member.identity_fingerprint.as_ref() {
|
||||
if pinned_fingerprint.as_bytes().eq(&source_identity.fingerprint) {
|
||||
// Learn the FULL identity if the fingerprint is pinned and they match.
|
||||
// Learn the FULL identity if the fingerprint is pinned and they match. This
|
||||
// lets us add membrers by address/fingerprint with full SHA384 identity
|
||||
// verification instead of just the address.
|
||||
let _ = member.identity.replace(source_identity.clone_without_secret());
|
||||
member_changed = true;
|
||||
} else {
|
||||
|
@ -273,7 +309,6 @@ impl Controller {
|
|||
}
|
||||
}
|
||||
|
||||
// This will be the final verdict after everything has been checked.
|
||||
let mut authorization_result = AuthorizationResult::Rejected;
|
||||
|
||||
// This is the main "authorized" flag on the member record. If it is true then
|
||||
|
@ -297,7 +332,7 @@ impl Controller {
|
|||
// TODO: check token authorization
|
||||
} else {
|
||||
authorization_result = AuthorizationResult::ApprovedOnPublicNetwork;
|
||||
member.as_mut().unwrap().last_authorized_time = Some(now);
|
||||
member.as_mut().unwrap().last_authorized_time = Some(time_clock);
|
||||
member_authorized = true;
|
||||
member_changed = true;
|
||||
}
|
||||
|
@ -334,7 +369,7 @@ impl Controller {
|
|||
|
||||
nc.name = network.name.clone();
|
||||
nc.private = network.private;
|
||||
nc.timestamp = now;
|
||||
nc.timestamp = time_clock;
|
||||
nc.multicast_limit = network.multicast_limit.unwrap_or(DEFAULT_MULTICAST_LIMIT as u32);
|
||||
nc.multicast_like_expire = Some(protocol::VL2_DEFAULT_MULTICAST_LIKE_EXPIRE as u32);
|
||||
nc.mtu = network.mtu.unwrap_or(ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU as u16);
|
||||
|
@ -348,10 +383,10 @@ impl Controller {
|
|||
// the overhead (bandwidth and CPU) of generating these.
|
||||
|
||||
if let Some(com) =
|
||||
vl2::v1::CertificateOfMembership::new(&self.local_identity, network_id, &source_identity, now, credential_ttl)
|
||||
vl2::v1::CertificateOfMembership::new(&self.local_identity, network_id, &source_identity, time_clock, credential_ttl)
|
||||
{
|
||||
let mut v1cred = V1Credentials {
|
||||
revision: now as u64,
|
||||
revision: time_clock as u64,
|
||||
max_delta: credential_ttl,
|
||||
certificate_of_membership: com,
|
||||
certificates_of_ownership: Vec::new(),
|
||||
|
@ -359,7 +394,7 @@ impl Controller {
|
|||
};
|
||||
|
||||
if !nc.static_ips.is_empty() {
|
||||
let mut coo = vl2::v1::CertificateOfOwnership::new(network_id, now, source_identity.address);
|
||||
let mut coo = vl2::v1::CertificateOfOwnership::new(network_id, time_clock, source_identity.address);
|
||||
for ip in nc.static_ips.iter() {
|
||||
coo.add_ip(ip);
|
||||
}
|
||||
|
@ -370,39 +405,33 @@ impl Controller {
|
|||
}
|
||||
|
||||
for (id, value) in member.tags.iter() {
|
||||
let tag = vl2::v1::Tag::new(*id, *value, &self.local_identity, network_id, &source_identity, now);
|
||||
let tag = vl2::v1::Tag::new(*id, *value, &self.local_identity, network_id, &source_identity, time_clock);
|
||||
if tag.is_none() {
|
||||
return Ok((AuthorizationResult::RejectedDueToError, None, None));
|
||||
}
|
||||
let _ = v1cred.tags.insert(*id, tag.unwrap());
|
||||
}
|
||||
|
||||
nc.v1_credentials = Some(v1cred);
|
||||
|
||||
// For anyone who has been deauthorized but is still in the window, send revocations.
|
||||
if let Ok(deauthed_members_still_in_window) = self
|
||||
.database
|
||||
.list_members_deauthorized_after(network.id, now - (credential_ttl as i64))
|
||||
.list_members_deauthorized_after(network.id, time_clock - (credential_ttl as i64))
|
||||
.await
|
||||
{
|
||||
if !deauthed_members_still_in_window.is_empty() {
|
||||
let mut revs = Vec::with_capacity(deauthed_members_still_in_window.len());
|
||||
for dm in deauthed_members_still_in_window.iter() {
|
||||
if let Some(rev) = Revocation::new(
|
||||
network_id,
|
||||
now,
|
||||
*dm,
|
||||
source_identity.address,
|
||||
&self.local_identity,
|
||||
vl2::v1::CredentialType::CertificateOfMembership,
|
||||
false,
|
||||
) {
|
||||
if let Some(rev) =
|
||||
Revocation::new(network_id, time_clock, *dm, source_identity.address, &self.local_identity, false)
|
||||
{
|
||||
revs.push(rev);
|
||||
}
|
||||
}
|
||||
revocations = Some(revs);
|
||||
}
|
||||
}
|
||||
|
||||
nc.v1_credentials = Some(v1cred);
|
||||
} else {
|
||||
return Ok((AuthorizationResult::RejectedDueToError, None, None));
|
||||
}
|
||||
|
@ -493,12 +522,12 @@ impl InnerProtocol for Controller {
|
|||
let node_fingerprint = Blob::from(source.identity.fingerprint);
|
||||
let now = ms_since_epoch();
|
||||
|
||||
let (result, config) = match self2.get_network_config(&source.identity, network_id, now).await {
|
||||
let (result, config) = match self2.authorize(&source.identity, network_id, now).await {
|
||||
Result::Ok((result, Some(config), revocations)) => {
|
||||
//println!("{}", serde_yaml::to_string(&config).unwrap());
|
||||
self2.send_network_config(source.as_ref(), &config, Some(message_id));
|
||||
if let Some(revocations) = revocations {
|
||||
self2.send_revocations(source.as_ref(), revocations);
|
||||
if let Some(mut revocations) = revocations {
|
||||
self2.v1_proto_send_revocations(source.as_ref(), &mut revocations);
|
||||
}
|
||||
(result, Some(config))
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::model::*;
|
|||
pub enum Change {
|
||||
NetworkCreated(Network),
|
||||
NetworkChanged(Network, Network),
|
||||
NetworkDeleted(Network),
|
||||
NetworkDeleted(Network, Vec<Member>),
|
||||
MemberCreated(Member),
|
||||
MemberChanged(Member, Member),
|
||||
MemberDeleted(Member),
|
||||
|
|
|
@ -119,13 +119,11 @@ impl FileDatabase {
|
|||
if deleted.is_some() {
|
||||
match record_type {
|
||||
RecordType::Network => {
|
||||
if let Some((network, mut members)) =
|
||||
if let Some((network, members)) =
|
||||
db.cache.on_network_deleted(network_id)
|
||||
{
|
||||
for m in members.drain(..) {
|
||||
let _ = db.change_sender.send(Change::MemberDeleted(m));
|
||||
}
|
||||
let _ = db.change_sender.send(Change::NetworkDeleted(network));
|
||||
let _ =
|
||||
db.change_sender.send(Change::NetworkDeleted(network, members));
|
||||
}
|
||||
}
|
||||
RecordType::Member => {
|
||||
|
|
|
@ -81,7 +81,6 @@ impl AuthorizationResult {
|
|||
}
|
||||
|
||||
impl ToString for AuthorizationResult {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
self.as_str().to_string()
|
||||
}
|
||||
|
|
|
@ -158,15 +158,15 @@ pub enum Error {
|
|||
/// Data object is too large to send, even fragmented.
|
||||
DataTooLarge,
|
||||
|
||||
/// An unexpected error occurred elsewhere in the code (may indicate a bug).
|
||||
OtherError(Box<dyn std::error::Error>),
|
||||
/// An unexpected I/O error such as a buffer overrun occurred.
|
||||
IoError(std::io::Error),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
Self::OtherError(Box::new(e))
|
||||
Self::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ impl std::fmt::Display for Error {
|
|||
Self::UnknownProtocolVersion => f.write_str("UnknownProtocolVersion"),
|
||||
Self::DataBufferTooSmall => f.write_str("DataBufferTooSmall"),
|
||||
Self::DataTooLarge => f.write_str("DataTooLarge"),
|
||||
Self::OtherError(e) => f.write_str(format!("OtherError({})", e.to_string()).as_str()),
|
||||
Self::IoError(e) => f.write_str(format!("OtherError({})", e.to_string()).as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ impl From<SessionId> for u64 {
|
|||
///
|
||||
/// This holds the data structures used to defragment incoming packets that are not associated with an
|
||||
/// existing session, which would be new attempts to create sessions. Typically one of these is associated
|
||||
/// with a single listen socket or other inbound endpoint.
|
||||
/// with a single listen socket, local bound port, or other inbound endpoint.
|
||||
pub struct ReceiveContext<H: Host> {
|
||||
initial_offer_defrag: Mutex<RingBufferMap<u32, GatherArray<H::IncomingPacketBuffer, KEY_EXCHANGE_MAX_FRAGMENTS>, 1024, 128>>,
|
||||
incoming_init_header_check_cipher: Aes,
|
||||
|
@ -348,6 +348,7 @@ pub struct Session<H: Host> {
|
|||
state: RwLock<SessionMutableState>, // Mutable parts of state (other than defrag buffers)
|
||||
remote_s_public_hash: [u8; 48], // SHA384(remote static public key blob)
|
||||
remote_s_public_p384: [u8; P384_PUBLIC_KEY_SIZE], // Remote NIST P-384 static public key
|
||||
|
||||
defrag: Mutex<RingBufferMap<u32, GatherArray<H::IncomingPacketBuffer, MAX_FRAGMENTS>, 16, 4>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -328,6 +328,8 @@ impl Identity {
|
|||
}
|
||||
|
||||
/// Returns true if this identity was upgraded from another older version.
|
||||
///
|
||||
/// This does NOT validate either identity. Ensure that validation has been performed.
|
||||
pub fn is_upgraded_from(&self, other: &Identity) -> bool {
|
||||
self.address == other.address
|
||||
&& self.x25519 == other.x25519
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::io::Write;
|
||||
|
||||
use zerotier_crypto::random;
|
||||
use zerotier_crypto::verified::Verified;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::blob::Blob;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -17,8 +17,7 @@ pub struct Revocation {
|
|||
pub threshold: i64,
|
||||
pub target: Address,
|
||||
pub issued_to: Address,
|
||||
pub signature: Blob<96>,
|
||||
pub type_being_revoked: u8,
|
||||
pub signature: ArrayVec<u8, 96>,
|
||||
pub fast_propagate: bool,
|
||||
}
|
||||
|
||||
|
@ -28,8 +27,7 @@ impl Revocation {
|
|||
threshold: i64,
|
||||
target: Address,
|
||||
issued_to: Address,
|
||||
signer: &Identity,
|
||||
type_being_revoked: CredentialType,
|
||||
signer: &Verified<Identity>,
|
||||
fast_propagate: bool,
|
||||
) -> Option<Self> {
|
||||
let mut r = Self {
|
||||
|
@ -38,8 +36,7 @@ impl Revocation {
|
|||
threshold,
|
||||
target,
|
||||
issued_to,
|
||||
signature: Blob::default(),
|
||||
type_being_revoked: type_being_revoked as u8,
|
||||
signature: ArrayVec::new(),
|
||||
fast_propagate,
|
||||
};
|
||||
if let Some(sig) = signer.sign(r.internal_to_bytes(true, signer.address).as_bytes(), true) {
|
||||
|
@ -64,14 +61,15 @@ impl Revocation {
|
|||
let _ = v.write_all(&(self.fast_propagate as u64).to_be_bytes()); // 0x1 is the flag for this
|
||||
let _ = v.write_all(&self.target.to_bytes());
|
||||
let _ = v.write_all(&signed_by.to_bytes());
|
||||
v.push(self.type_being_revoked);
|
||||
v.push(CredentialType::CertificateOfMembership as u8);
|
||||
|
||||
if for_sign {
|
||||
let _ = v.write_all(&[0x7f; 8]);
|
||||
} else {
|
||||
v.push(1); // ed25519 signature
|
||||
let _ = v.write_all(&[0u8, 96u8]);
|
||||
let _ = v.write_all(self.signature.as_bytes());
|
||||
assert!(self.signature.len() <= 255);
|
||||
let _ = v.write_all(&[0u8, self.signature.len() as u8]);
|
||||
let _ = v.write_all(self.signature.as_ref());
|
||||
}
|
||||
|
||||
v
|
||||
|
|
Loading…
Add table
Reference in a new issue