Implement usage flags in certificate, make root sets require a usage flag and a more general local trust flag, and clean up Identity return in Rust Node.

This commit is contained in:
Adam Ierymenko 2021-04-23 18:16:49 -04:00
parent 24ab618123
commit 251cc082f2
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
18 changed files with 512 additions and 169 deletions

View file

@ -40,7 +40,7 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
m_clear();
Utils::copy< sizeof(this->serialNo) >(this->serialNo, cert.serialNo);
this->flags = cert.flags;
this->usageFlags = cert.usageFlags;
this->timestamp = cert.timestamp;
this->validity[0] = cert.validity[0];
this->validity[1] = cert.validity[1];
@ -180,8 +180,8 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
* purposes the keys must be always be in order.
*/
if (this->flags != 0)
d.add("f", this->flags);
if (this->usageFlags != 0)
d.add("f", this->usageFlags);
if (this->timestamp > 0)
d.add("t", (uint64_t)this->timestamp);
if (this->validity[0] > 0)
@ -223,7 +223,7 @@ bool Certificate::decode(const void *const data, const unsigned int len)
m_clear();
this->flags = d.getUI("f");
this->usageFlags = d.getUI("f");
this->timestamp = (int64_t)d.getUI("t");
this->validity[0] = (int64_t)d.getUI("v#0");
this->validity[1] = (int64_t)d.getUI("v#1");

View file

@ -300,7 +300,7 @@ static bool ZTT_deepCompareCertificateName(const ZT_Certificate_Name &a, const Z
static bool ZTT_deepCompareCertificates(const Certificate &a, const Certificate &b)
{
if (
(a.flags != b.flags) ||
(a.usageFlags != b.usageFlags) ||
(a.timestamp != b.timestamp) ||
(a.validity[0] != b.validity[0]) ||
(a.validity[1] != b.validity[1]) ||

View file

@ -39,19 +39,22 @@ Map< Identity, SharedPtr< const Locator > > TrustStore::roots()
for (Map< Fingerprint, Vector< SharedPtr< Entry > > >::const_iterator cv(m_bySubjectIdentity.begin()); cv != m_bySubjectIdentity.end(); ++cv) {
for (Vector< SharedPtr< Entry > >::const_iterator c(cv->second.begin()); c != cv->second.end(); ++c) {
if (((*c)->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET) != 0) {
// A root set cert must be marked for this use and authorized to influence this node's config.
if ((((*c)->m_certificate.usageFlags & ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET) != 0) && (((*c)->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG) != 0)) {
// Add all identities to the root set, and for each entry in the set make sure we have the latest locator if there's more than one cert with one.
for (unsigned int j = 0; j < (*c)->certificate().subject.identityCount; ++j) {
const Identity *const id = reinterpret_cast<const Identity *>((*c)->certificate().subject.identities[j].identity);
if ((id) && (*id)) { // sanity check
SharedPtr< const Locator > &existingLoc = r[*id];
const Locator *const loc = reinterpret_cast<const Locator *>((*c)->certificate().subject.identities[j].locator);
if (loc) {
// If more than one certificate names a root, this ensures that the newest locator is used.
if ((!existingLoc) || (existingLoc->revision() < loc->revision()))
existingLoc.set(new Locator(*loc));
}
}
}
}
}
@ -198,10 +201,13 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
m_bySubjectUniqueId.clear();
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) {
if (c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) {
const unsigned int uniqueIdSize = c->second->m_certificate.subject.uniqueIdSize;
if ((uniqueIdSize > 0) && (uniqueIdSize <= ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE)) {
SharedPtr< Entry > &entry = m_bySubjectUniqueId[Blob< ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE >(c->second->m_certificate.subject.uniqueId, uniqueIdSize)];
if (entry) {
// If there's already an entry, see if there's a newer certificate for this subject.
if (c->second->m_certificate.subject.timestamp > entry->m_certificate.subject.timestamp) {
entry->m_subjectDeprecated = true;
entry = c->second;
@ -216,10 +222,12 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
c->second->m_subjectDeprecated = true;
}
}
} else {
entry = c->second;
}
}
}
}
@ -228,17 +236,17 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
m_bySubjectIdentity.clear();
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
if ((c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) && (!c->second->m_subjectDeprecated)) {
for (unsigned int i = 0; i < c->second->m_certificate.subject.identityCount; ++i) {
const Identity *const id = reinterpret_cast<const Identity *>(c->second->m_certificate.subject.identities[i].identity);
if ((id) && (*id)) // sanity check
m_bySubjectIdentity[id->fingerprint()].push_back(c->second);
}
}
}
// Purge error certificates and return them if 'purge' is non-NULL. This purges error certificates
// and deprecated certificates not on a trust path. Deprecated certificates on a trust path remain
// as they are still technically valid and other possibly wanted certificates depend on them.
// If purge is set, erase and return error and deprecated certs (that are not on a trust path).
if (purge) {
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) {
if ( (c->second->error() != ZT_CERTIFICATE_ERROR_NONE) || ((c->second->m_subjectDeprecated) && (!c->second->m_onTrustPath)) ) {
@ -259,15 +267,15 @@ Vector< uint8_t > TrustStore::save() const
int compSize;
{
Vector< uint8_t > b;
b.reserve(65536);
RWMutex::RLock l(m_lock);
Vector< uint8_t > b;
b.reserve(4096);
// A version byte.
b.push_back(0);
// <size> <certificate> <trust> tuples terminated by a 0 size.
// <size[2]> <certificate[...]> <trust[2]> tuples terminated by a 0 size.
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
const Vector< uint8_t > cdata(c->second->certificate().encode());
const unsigned long size = (uint32_t)cdata.size();
@ -276,8 +284,6 @@ Vector< uint8_t > TrustStore::save() const
b.push_back((uint8_t)size);
b.insert(b.end(), cdata.begin(), cdata.end());
const uint32_t localTrust = (uint32_t)c->second->localTrust();
b.push_back((uint8_t)(localTrust >> 24U));
b.push_back((uint8_t)(localTrust >> 16U));
b.push_back((uint8_t)(localTrust >> 8U));
b.push_back((uint8_t)localTrust);
}
@ -291,15 +297,8 @@ Vector< uint8_t > TrustStore::save() const
return Vector< uint8_t >();
const uint32_t uncompSize = (uint32_t)b.size();
const uint32_t cksum = Utils::fnv1a32(b.data(), (unsigned int)uncompSize);
comp[0] = (uint8_t)(uncompSize >> 24);
comp[1] = (uint8_t)(uncompSize >> 16);
comp[2] = (uint8_t)(uncompSize >> 8);
comp[3] = (uint8_t)uncompSize;
comp[4] = (uint8_t)(cksum >> 24);
comp[5] = (uint8_t)(cksum >> 16);
comp[6] = (uint8_t)(cksum >> 8);
comp[7] = (uint8_t)cksum;
Utils::storeBigEndian(comp.data(), uncompSize);
Utils::storeBigEndian(comp.data() + 4, Utils::fnv1a32(b.data(), (unsigned int)uncompSize));
compSize += 8;
}
@ -314,8 +313,8 @@ int TrustStore::load(const Vector< uint8_t > &data)
if (data.size() < 8)
return -1;
const unsigned long uncompSize = Utils::loadBigEndian< uint32_t >(data.data());
if ((uncompSize == 0) || (uncompSize > (data.size() * 128)))
const unsigned int uncompSize = Utils::loadBigEndian< uint32_t >(data.data());
if ((uncompSize == 0) || (uncompSize > (unsigned int)(data.size() * 128)))
return -1;
Vector< uint8_t > uncomp;
@ -336,20 +335,20 @@ int TrustStore::load(const Vector< uint8_t > &data)
for (;;) {
if ((b + 2) > eof)
break;
const uint32_t cdataSize = Utils::loadBigEndian< uint16_t >(b);
const uint32_t certDataSize = Utils::loadBigEndian< uint16_t >(b);
b += 2;
if (cdataSize == 0)
if (certDataSize == 0)
break;
if ((b + cdataSize + 4) > eof)
if ((b + certDataSize + 2) > eof) // certificate length + 2 bytes for trust flags
break;
Certificate c;
if (c.decode(b, (unsigned int)cdataSize)) {
b += cdataSize;
const uint32_t localTrust = Utils::loadBigEndian< uint32_t >(b);
b += 4;
this->add(c, (unsigned int)localTrust);
if (c.decode(b, (unsigned int)certDataSize)) {
b += certDataSize;
this->add(c, Utils::loadBigEndian< uint16_t >(b));
b += 2;
++readCount;
}
}

View file

@ -101,6 +101,7 @@ public:
Entry &operator=(const Entry &) { return *this; }
ZT_INLINE Entry(RWMutex &l, const Certificate &cert, const unsigned int lt) noexcept:
__refCount(0),
m_lock(l),
m_certificate(cert),
m_localTrust(lt),
@ -109,11 +110,12 @@ public:
m_onTrustPath(false)
{}
std::atomic< int > __refCount;
RWMutex &m_lock;
const Certificate m_certificate;
unsigned int m_localTrust;
ZT_CertificateError m_error;
std::atomic< int > __refCount;
bool m_subjectDeprecated;
bool m_onTrustPath;
};

View file

@ -328,9 +328,81 @@ typedef struct
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA 0x0001U
/**
* Certificate's subject describes a set of roots (local trust flag)
* Certificate can affect configuration of the node.
*
* An example is a ZeroTier root set certificate which adds root servers.
*/
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET 0x0002U
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG 0x0002U
/*
* Certificate usage flags
*
* Not all of these are used in ZeroTier. Some are simply reserved because
* same named usage attributes exist in X509 and we want to be future proof
* against future desires to translate over X509 functionality.
*/
/**
* Certificate can sign things (general).
*/
#define ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE 0x00000001U
/**
* Certificate can verify signatures to verify actions.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_NON_REPUDIATION 0x00000002U
/**
* Certificate's key can encipher other keys.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_KEY_ENCIPHERMENT 0x00000004U
/**
* Certificate's key can encipher data.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_DATA_ENCIPHERMENT 0x00000008U
/**
* Certificate's key can be used for Diffie-Hellman style key agreemtn.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_KEY_AGREEMENT 0x00000010U
/**
* Certificate can sign other certificates.
*/
#define ZT_CERTIFICATE_USAGE_CERTIFICATE_SIGNING 0x00000020U
/**
* Certificate can revoke signatures.
*/
#define ZT_CERTIFICATE_USAGE_CRL_SIGNING 0x00000040U
/**
* Certificate can sign executable code.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE 0x00000080U
/**
* Certificate's public key can be used for a timestamp service.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_TIMESTAMPING 0x00000100U
/**
* Certificate can enumerate a set of ZeroTier root nodes.
*/
#define ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET 0x00000200U
/**
* Errors returned by functions that verify or handle certificates.
@ -572,9 +644,9 @@ typedef struct
uint8_t serialNo[ZT_CERTIFICATE_HASH_SIZE];
/**
* Flags indicating certificate usage and any other attributes.
* Certificate usage flags.
*/
uint64_t flags;
uint64_t usageFlags;
/**
* Certificate timestamp in milliseconds since epoch.

View file

@ -11,6 +11,8 @@
*/
/****/
use std::hash::{Hash, Hasher};
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
pub struct Address(pub u64);
@ -28,14 +30,10 @@ impl Address {
}
}
impl From<&[u8]> for Address {
impl Hash for Address {
#[inline(always)]
fn from(bytes: &[u8]) -> Self {
if bytes.len() >= 5 {
Address(((bytes[0] as u64) << 32) | ((bytes[1] as u64) << 24) | ((bytes[2] as u64) << 16) | ((bytes[3] as u64) << 8) | (bytes[4] as u64))
} else {
Address(0)
}
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
@ -48,14 +46,25 @@ impl ToString for Address {
impl From<u64> for Address {
#[inline(always)]
fn from(i: u64) -> Self {
Address(i)
Address(i & 0xffffffffff)
}
}
impl From<&str> for Address {
#[inline(always)]
fn from(s: &str) -> Self {
Address(u64::from_str_radix(s, 16).unwrap_or(0))
Address::from(u64::from_str_radix(s, 16).unwrap_or(0))
}
}
impl From<&[u8]> for Address {
#[inline(always)]
fn from(bytes: &[u8]) -> Self {
if bytes.len() >= 5 {
Address(((bytes[0] as u64) << 32) | ((bytes[1] as u64) << 24) | ((bytes[2] as u64) << 16) | ((bytes[3] as u64) << 8) | (bytes[4] as u64))
} else {
Address(0)
}
}
}
@ -77,6 +86,7 @@ impl<'de> serde::de::Visitor<'de> for AddressVisitor {
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address") }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(s)) }
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
}
impl<'de> serde::Deserialize<'de> for Address {

View file

@ -13,10 +13,10 @@
use std::ffi::CString;
use std::hash::{Hash, Hasher};
use std::mem::zeroed;
use std::mem::{zeroed, MaybeUninit};
use std::os::raw::{c_char, c_uint, c_void};
use std::pin::Pin;
use std::ptr::{copy_nonoverlapping, null, null_mut};
use std::ptr::{copy_nonoverlapping, null, null_mut, read_unaligned, write_bytes};
use num_derive::{FromPrimitive, ToPrimitive};
#[allow(unused_imports)]
@ -32,13 +32,39 @@ pub const CERTIFICATE_MAX_STRING_LENGTH: isize = ztcore::ZT_CERTIFICATE_MAX_STRI
/// Certificate local trust bit field flag: this certificate self-signs a root CA.
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA;
/// Certificate local trust bit field flag: this certificate specifies a set of ZeroTier roots.
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET;
pub const CERTIFICATE_USAGE_DIGITAL_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE as u64;
pub const CERTIFICATE_USAGE_NON_REPUDIATION: u64 = ztcore::ZT_CERTIFICATE_USAGE_NON_REPUDIATION as u64;
pub const CERTIFICATE_USAGE_KEY_ENCIPHERMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_KEY_ENCIPHERMENT as u64;
pub const CERTIFICATE_USAGE_DATA_ENCIPHERMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_DATA_ENCIPHERMENT as u64;
pub const CERTIFICATE_USAGE_KEY_AGREEMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_KEY_AGREEMENT as u64;
pub const CERTIFICATE_USAGE_CERTIFICATE_SIGNING: u64 = ztcore::ZT_CERTIFICATE_USAGE_CERTIFICATE_SIGNING as u64;
pub const CERTIFICATE_USAGE_CRL_SIGNING: u64 = ztcore::ZT_CERTIFICATE_USAGE_CRL_SIGNING as u64;
pub const CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE as u64;
pub const CERTIFICATE_USAGE_TIMESTAMPING: u64 = ztcore::ZT_CERTIFICATE_USAGE_TIMESTAMPING as u64;
/// All certificate usage flags and their corresponding canonical abbreviations.
pub const ALL_CERTIFICATE_USAGE_FLAGS: [(u64, &'static str); 9] = [
(CERTIFICATE_USAGE_DIGITAL_SIGNATURE, "ds"),
(CERTIFICATE_USAGE_NON_REPUDIATION, "nr"),
(CERTIFICATE_USAGE_KEY_ENCIPHERMENT, "ke"),
(CERTIFICATE_USAGE_DATA_ENCIPHERMENT, "de"),
(CERTIFICATE_USAGE_KEY_AGREEMENT, "ka"),
(CERTIFICATE_USAGE_CERTIFICATE_SIGNING, "cs"),
(CERTIFICATE_USAGE_CRL_SIGNING, "crl"),
(CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE, "es"),
(CERTIFICATE_USAGE_TIMESTAMPING, "ts"),
];
#[inline(always)]
fn vec_to_array<const L: usize>(v: &Vec<u8>) -> [u8; L] {
let mut a = [0_u8; L];
unsafe { copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr(), v.len().min(L)) };
a
unsafe {
let mut a: MaybeUninit<[u8; L]> = MaybeUninit::uninit();
copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr().cast::<u8>(), v.len().min(L));
if v.len() < L {
write_bytes(a.as_mut_ptr().cast::<u8>(), 0, L - v.len());
}
a.assume_init()
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -62,25 +88,44 @@ impl From<i32> for CertificatePublicKeyAlgorithm {
pub struct CertificateSerialNo(pub [u8; 48]);
impl CertificateSerialNo {
#[inline(always)]
pub fn new() -> CertificateSerialNo { CertificateSerialNo([0; 48]) }
pub fn new_from_string(s: &str) -> Result<CertificateSerialNo, ResultCode> { hex::decode(s).map_or_else(|_| { Err(ResultCode::ErrorBadParameter) }, |b| { Ok(CertificateSerialNo::from(b)) }) }
}
pub const SIZE: usize = 48;
pub const SIZE_HEX: usize = 96;
impl<A: AsRef<[u8]>> From<A> for CertificateSerialNo {
fn from(a: A) -> CertificateSerialNo {
let mut sn = CertificateSerialNo::new();
let aa = a.as_ref();
for i in 0..aa.len() {
sn.0[i] = aa[i];
}
sn
#[inline(always)]
pub fn new() -> CertificateSerialNo { CertificateSerialNo([0_u8; 48]) }
/// Create a new certificate serial from a hex string, returning None if invalid.
/// The from() alternative for converting from a string returns an a nil (all zero) serial on error.
pub fn new_from_string(s: &str) -> Option<CertificateSerialNo> {
hex::decode(s).map_or(None, |b| {
if b.len() == Self::SIZE {
Some(CertificateSerialNo(vec_to_array::<48>(&b)))
} else {
None
}
})
}
/// Returns true if serial is all zeroes.
#[inline(always)]
pub fn is_nil(&self) -> bool {
is_all_zeroes(self.0)
}
}
impl Hash for CertificateSerialNo {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state); }
fn hash<H: Hasher>(&self, state: &mut H) {
// Serials are SHA384 hashes, so we can just use the first 8 bytes as-is.
state.write_u64(unsafe { read_unaligned::<u64>(self.0.as_ptr().cast()) })
}
}
impl<S: AsRef<str>> From<S> for CertificateSerialNo {
#[inline(always)]
fn from(s: S) -> CertificateSerialNo {
Self::new_from_string(s.as_ref()).unwrap_or_else(|| CertificateSerialNo::new())
}
}
impl ToString for CertificateSerialNo {
@ -95,7 +140,7 @@ struct CertificateSerialNoVisitor;
impl<'de> serde::de::Visitor<'de> for CertificateSerialNoVisitor {
type Value = CertificateSerialNo;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("object") }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Self::Value::new_from_string(s).map_or_else(|_| { Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) },|id| { Ok(id as Self::Value) }) }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Self::Value::new_from_string(s).map_or_else(|| { Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) },|serial| { Ok(serial as Self::Value) }) }
}
impl<'de> serde::Deserialize<'de> for CertificateSerialNo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(CertificateSerialNoVisitor) }
@ -117,22 +162,27 @@ pub enum CertificateError {
Revoked = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_REVOKED as isize,
}
impl CertificateError {
pub fn to_str(&self) -> &'static str {
match self {
CertificateError::None => "None",
CertificateError::InvalidFormat => "InvalidFormat",
CertificateError::InvalidIdentity => "InvalidIdentity",
CertificateError::InvalidPrimarySignature => "InvalidPrimarySignature",
CertificateError::InvalidChain => "InvalidChain",
CertificateError::InvalidComponentSignature => "InvalidComponentSignature",
CertificateError::InvalidUniqueIdProof => "InvalidUniqueIdProof",
CertificateError::MissingRequiredFields => "MissingRequiredFields",
CertificateError::OutOfValidTimeWindow => "OutOfValidTimeWindow",
CertificateError::Revoked => "Revoked",
}
}
}
impl ToString for CertificateError {
#[inline(always)]
fn to_string(&self) -> String {
String::from(
match self {
CertificateError::None => "None",
CertificateError::InvalidFormat => "InvalidFormat",
CertificateError::InvalidIdentity => "InvalidIdentity",
CertificateError::InvalidPrimarySignature => "InvalidPrimarySignature",
CertificateError::InvalidChain => "InvalidChain",
CertificateError::InvalidComponentSignature => "InvalidComponentSignature",
CertificateError::InvalidUniqueIdProof => "InvalidUniqueIdProof",
CertificateError::MissingRequiredFields => "MissingRequiredFields",
CertificateError::OutOfValidTimeWindow => "OutOfValidTimeWindow",
CertificateError::Revoked => "Revoked",
}
)
String::from(self.to_str())
}
}
@ -338,10 +388,10 @@ pub struct CertificateSubject {
#[serde(rename = "updateURLs")]
pub update_urls: Vec<String>,
pub name: CertificateName,
#[serde(with = "Base64Standard")]
#[serde(with = "Base64URLSafeNoPad")]
#[serde(rename = "uniqueId")]
pub unique_id: Vec<u8>,
#[serde(with = "Base64Standard")]
#[serde(with = "Base64URLSafeNoPad")]
#[serde(rename = "uniqueIdSignature")]
pub unique_id_signature: Vec<u8>,
}
@ -509,7 +559,8 @@ impl CertificateSubject {
pub struct Certificate {
#[serde(rename = "serialNo")]
pub serial_no: CertificateSerialNo,
pub flags: u64,
#[serde(rename = "usageFlags")]
pub usage_flags: u64,
pub timestamp: i64,
pub validity: [i64; 2],
pub subject: CertificateSubject,
@ -520,7 +571,7 @@ pub struct Certificate {
pub public_key: Vec<u8>,
#[serde(rename = "extendedAttributes")]
pub extended_attributes: Vec<u8>,
#[serde(with = "Base64Standard")]
#[serde(with = "Base64URLSafeNoPad")]
pub signature: Vec<u8>,
#[serde(rename = "maxPathLength")]
pub max_path_length: u32,
@ -557,7 +608,7 @@ impl Certificate {
pub fn new() -> Certificate {
Certificate {
serial_no: CertificateSerialNo::new(),
flags: 0,
usage_flags: 0,
timestamp: 0,
validity: [0, i64::MAX],
subject: CertificateSubject::new(),
@ -573,7 +624,7 @@ impl Certificate {
pub(crate) unsafe fn new_from_capi(c: &ztcore::ZT_Certificate) -> Certificate {
return Certificate {
serial_no: CertificateSerialNo(c.serialNo),
flags: c.flags,
usage_flags: c.usageFlags,
timestamp: c.timestamp,
validity: c.validity,
subject: CertificateSubject::new_from_capi(&c.subject),
@ -591,7 +642,7 @@ impl Certificate {
CertificateCAPIContainer {
certificate: ztcore::ZT_Certificate {
serialNo: self.serial_no.0,
flags: self.flags,
usageFlags: self.usage_flags,
timestamp: self.timestamp,
validity: self.validity,
subject: subject.subject,
@ -689,7 +740,7 @@ mod tests {
let mut cert = Certificate{
serial_no: CertificateSerialNo::new(),
flags: 1,
usage_flags: 1,
timestamp: 2,
validity: [ 1,10 ],
subject: CertificateSubject::new(),

View file

@ -11,15 +11,17 @@
*/
/****/
use std::cmp::Ordering;
use std::ffi::CString;
use std::mem::MaybeUninit;
use std::mem::{MaybeUninit, transmute};
use std::os::raw::*;
use std::hash::{Hash, Hasher};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use crate::*;
use crate::capi as ztcore;
use std::cmp::Ordering;
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
pub enum IdentityType {
@ -27,9 +29,15 @@ pub enum IdentityType {
NistP384 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_P384 as isize,
}
impl IdentityType {
fn to_str(&self) -> &'static str {
if *self == IdentityType::Curve25519 { "c25519" } else { "p384" }
}
}
impl ToString for IdentityType {
fn to_string(&self) -> String {
String::from(if *self == IdentityType::Curve25519 { "c25519" } else { "p384" })
String::from(self.to_str())
}
}
@ -43,17 +51,25 @@ pub struct Identity {
impl Identity {
pub(crate) fn new_from_capi(id: *const ztcore::ZT_Identity, requires_delete: bool) -> Identity {
unsafe {
let idt = ztcore::ZT_Identity_type(id);
let a = ztcore::ZT_Identity_address(id);
return Identity {
type_: FromPrimitive::from_i32(idt as i32).unwrap(),
address: Address(a),
Identity {
type_: FromPrimitive::from_i32(ztcore::ZT_Identity_type(id) as i32).unwrap(),
address: Address(ztcore::ZT_Identity_address(id)),
capi: id,
requires_delete,
};
}
}
}
/// Sync type and address fields with C API.
/// This isn't truly unsafe because these are primitive types, but it's marked as such
/// because it breaks the rules slightly. It's used in Node to make sure its identity
/// wrapper matches the one in C++-land even if the latter changes.
pub(crate) unsafe fn sync_type_and_address_with_capi(&self) {
let s: *mut Identity = transmute(self as *const Identity);
(*s).type_ = FromPrimitive::from_i32(ztcore::ZT_Identity_type(self.capi) as i32).unwrap();
(*s).address.0 = ztcore::ZT_Identity_address(self.capi);
}
/// Generate a new identity.
/// This is time consuming due to one time proof of work. It can take several seconds.
pub fn new_generate(id_type: IdentityType) -> Result<Identity, ResultCode> {
@ -143,6 +159,13 @@ impl Identity {
}
}
impl Hash for Identity {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
(self.address.0 | (self.type_.to_u64().unwrap_or(0) << 40)).hash(state);
}
}
impl PartialEq for Identity {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {

View file

@ -58,7 +58,7 @@ pub use virtualnetworkconfig::*;
pub use multicastgroup::MulticastGroup;
pub use dictionary::*;
base64_serde_type!(Base64Standard, base64::URL_SAFE_NO_PAD);
base64_serde_type!(Base64URLSafeNoPad, base64::URL_SAFE_NO_PAD);
/// Recommended minimum thread stack size for background threads.
pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 524288;
@ -84,6 +84,16 @@ pub const DEFAULT_UDP_MTU: u32 = ztcore::ZT_DEFAULT_UDP_MTU;
/// Maximum UDP MTU (we never actually get this high).
pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU;
/// Base64 encode using the URL-safe with no padding configuration.
pub fn base64_encode<T: AsRef<[u8]>>(t: &T) -> String {
base64::encode_config(t, base64::URL_SAFE_NO_PAD)
}
/// Base64 decode using the URL-safe with no padding configuration.
pub fn base64_decode<T: AsRef<[u8]>>(t: &T) -> Result<Vec<u8>, base64::DecodeError> {
base64::decode_config(t, base64::URL_SAFE_NO_PAD)
}
#[allow(non_snake_case,non_upper_case_globals)]
pub mod RulePacketCharacteristicFlags {
pub const Inbound: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_INBOUND as u64;

View file

@ -11,6 +11,8 @@
*/
/****/
use std::hash::{Hash, Hasher};
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct NetworkId(pub u64);
@ -21,6 +23,13 @@ impl Default for NetworkId {
}
}
impl Hash for NetworkId {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl ToString for NetworkId {
fn to_string(&self) -> String {
format!("{:0>16x}", self.0)

View file

@ -12,19 +12,20 @@
/****/
use std::collections::hash_map::HashMap;
use std::intrinsics::copy_nonoverlapping;
use std::marker::PhantomData;
use std::mem::{MaybeUninit, transmute};
use std::os::raw::{c_int, c_uint, c_ulong, c_void};
use std::pin::Pin;
use std::ptr::{null_mut, slice_from_raw_parts};
use std::ptr::{null_mut, slice_from_raw_parts, copy_nonoverlapping};
use std::sync::Mutex;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize};
use crate::*;
use crate::capi as ztcore;
use std::marker::PhantomData;
pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: i64 = 200;
@ -51,7 +52,8 @@ pub enum StateObjectType {
}
impl StateObjectType {
/// True if this state object should be protected.
/// True if this state object should be protected in a data store.
/// This could mean its file permissions should be locked down so they're only readable by the service, for example.
#[inline(always)]
pub fn is_secret(&self) -> bool {
*self == StateObjectType::IdentitySecret || *self == StateObjectType::TrustStore
@ -71,8 +73,8 @@ pub struct NodeStatus {
}
/// An event handler that receives events, frames, and packets from the core.
/// Note that these handlers can be called concurrently from any thread and
/// must be thread safe.
/// Note that if multiple threads are calling into Node these may be called by any of these threads
/// at any time and must be thread safe.
pub trait NodeEventHandler<N: Sync + Send + 'static> {
/// Called when a configuration change or update should be applied to a network.
fn virtual_network_config(&self, network_id: NetworkId, network_obj: &N, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>);
@ -99,7 +101,7 @@ pub trait NodeEventHandler<N: Sync + Send + 'static> {
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress>;
}
pub struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
pub(crate) struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
event_handler: T,
capi: *mut ztcore::ZT_Node,
networks_by_id: Mutex<HashMap<u64, Pin<Box<N>>>>,
@ -120,6 +122,7 @@ pub struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send
/// multithreaded or async.
pub struct Node<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
intl: Pin<Box<NodeIntl<T, N, H>>>,
identity_wrapper: Option<Identity>,
event_handler_placeholder: PhantomData<H>,
}
@ -323,6 +326,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
recent_clock: PortableAtomicI64::new(clock),
recent_ticks: PortableAtomicI64::new(ticks),
}),
identity_wrapper: None,
event_handler_placeholder: PhantomData::default(),
};
@ -342,6 +346,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
if rc == 0 {
assert!(!n.intl.capi.is_null());
n.identity_wrapper.replace(Identity::new_from_capi(unsafe { ztcore::ZT_Node_identity(n.intl.capi) }, false));
Ok(n)
} else {
Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal))
@ -352,6 +357,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
/// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds
/// since the node was created, and after this runs it returns the amount of time the caller
/// should wait before calling it again.
#[inline(always)]
pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 {
self.intl.recent_clock.set(clock);
self.intl.recent_ticks.set(ticks);
@ -392,7 +398,9 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
self.intl.networks_by_id.lock().unwrap().remove(&nwid.0).map_or_else(|| {
ResultCode::ErrorNetworkNotFound
}, |_| {
unsafe { ResultCode::from_i32(ztcore::ZT_Node_leave(self.intl.capi, clock, ticks, null_mut(), null_mut(), nwid.0) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
unsafe {
ResultCode::from_i32(ztcore::ZT_Node_leave(self.intl.capi, clock, ticks, null_mut(), null_mut(), nwid.0) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal)
}
})
}
@ -410,11 +418,14 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
})
}
/// Get the address of this node.
#[inline(always)]
pub fn address(&self) -> Address {
unsafe { Address(ztcore::ZT_Node_address(self.intl.capi) as u64) }
}
/// Handle a wire packet read from the physical network.
/// This is physical -> virtual.
#[inline(always)]
pub fn process_wire_packet(&self, clock: i64, ticks: i64, local_socket: i64, remote_address: &InetAddress, data: Buffer, next_task_deadline: &mut i64) -> ResultCode {
let intl = &*self.intl;
@ -423,6 +434,8 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
rc
}
/// Handle a packet sent via a virtual network interface such as a tun/tap device.
/// This is virtual -> physical.
#[inline(always)]
pub fn process_virtual_network_frame(&self, clock: i64, ticks: i64, nwid: &NetworkId, source_mac: &MAC, dest_mac: &MAC, ethertype: u16, vlan_id: u16, data: Buffer, next_task_deadline: &mut i64) -> ResultCode {
let intl = &*self.intl;
@ -431,30 +444,27 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
rc
}
/// Subscribe to a multicast group on a given network.
#[inline(always)]
pub fn multicast_subscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
unsafe { ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.intl.capi, clock, ticks, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
}
/// Unsubscribe from a multicast group on a given network.
#[inline(always)]
pub fn multicast_unsubscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
unsafe { ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.intl.capi, clock, ticks, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
}
/// Get a copy of this node's identity.
/// Get this node's identity.
#[inline(always)]
pub fn identity(&self) -> Identity {
unsafe { self.identity_fast().clone() }
}
/// Get an identity that simply holds a pointer to the underlying node's identity.
/// This is unsafe because the identity object becomes invalid if the node ceases
/// to exist the Identity becomes invalid. Use clone() on it to get a copy.
#[inline(always)]
pub(crate) unsafe fn identity_fast(&self) -> Identity {
Identity::new_from_capi(ztcore::ZT_Node_identity(self.intl.capi), false)
pub fn identity(&self) -> &Identity {
let id = self.identity_wrapper.as_ref().unwrap();
unsafe { id.sync_type_and_address_with_capi() };
id
}
/// Get status information for this node.
pub fn status(&self, clock: i64, ticks: i64) -> NodeStatus {
let mut ns: MaybeUninit<ztcore::ZT_NodeStatus> = MaybeUninit::zeroed();
unsafe {
@ -473,6 +483,8 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
}
}
/// Get a list of all peers with which this node is communicating or has very recently communicated.
/// The presence of a peer on this list doesn't mean it has actual access to any network.
pub fn peers(&self, clock: i64, ticks: i64) -> Vec<Peer> {
let mut p: Vec<Peer> = Vec::new();
unsafe {
@ -489,6 +501,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
p
}
/// Get the networks this node has joined.
pub fn networks(&self) -> Vec<VirtualNetworkConfig> {
let mut n: Vec<VirtualNetworkConfig> = Vec::new();
unsafe {
@ -505,6 +518,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
n
}
/// Get the certificates in this node's trust store and their local trust flags.
pub fn certificates(&self, clock: i64, ticks: i64) -> Vec<(Certificate, u32)> {
let mut c: Vec<(Certificate, u32)> = Vec::new();
unsafe {

View file

@ -12,6 +12,7 @@
/****/
use serde::{Deserialize, Serialize};
use crate::Endpoint;
use crate::capi as ztcore;

View file

@ -19,13 +19,163 @@ use dialoguer::Input;
use zerotier_core::*;
use crate::store::Store;
use crate::utils::{read_limit, ms_since_epoch, to_json_pretty};
use crate::GlobalFlags;
/// Dump a certificate in human-readable format to stdout.
fn dump_cert(certificate: &Certificate) {
let mut subject_identities = String::new();
let mut subject_networks = String::new();
let mut subject_update_urls = String::new();
let mut usage_flags = String::new();
fn string_or_dash(s: &str) -> &str {
if s.is_empty() {
"-"
} else {
s
}
}
if certificate.subject.identities.is_empty() {
subject_identities.push_str(": (none)");
} else {
for x in certificate.subject.identities.iter() {
subject_identities.push_str("\n ");
subject_identities.push_str(x.identity.to_string().as_str());
if x.locator.is_some() {
subject_identities.push_str(" ");
subject_identities.push_str(x.locator.as_ref().unwrap().to_string().as_str());
}
}
}
if certificate.subject.networks.is_empty() {
subject_networks.push_str(": (none)");
} else {
for x in certificate.subject.networks.iter() {
subject_networks.push_str("\n ");
subject_networks.push_str(x.id.to_string().as_str());
if x.controller.is_some() {
subject_networks.push_str(" at ");
subject_networks.push_str(x.controller.as_ref().unwrap().to_string().as_str());
}
}
}
if certificate.subject.update_urls.is_empty() {
subject_update_urls.push_str(": (none)");
} else {
for x in certificate.subject.update_urls.iter() {
subject_update_urls.push_str("\n ");
subject_update_urls.push_str(x.as_ref());
}
}
if certificate.usage_flags != 0 {
usage_flags.push_str(" (");
for f in ALL_CERTIFICATE_USAGE_FLAGS.iter() {
if (certificate.usage_flags & (*f).0) != 0 {
if !usage_flags.is_empty() {
usage_flags.push(',');
}
usage_flags.push_str((*f).1);
}
}
usage_flags.push(')');
}
println!(r###"Serial Number (SHA384): {}
Usage Flags: 0x{:0>8x}{}
Timestamp: {}
Validity: {} to {}
Subject
Timestamp: {}
Identities{}
Networks{}
Update URLs{}
Name
Serial: {}
Common Name: {}
Country: {}
Organization: {}
Unit: {}
Locality: {}
State/Province: {}
Street Address: {}
Postal Code: {}
E-Mail: {}
URL: {}
Host: {}
Unique ID: {}
Unique ID Signature: {}
Issuer: {}
Issuer Public Key: {}
Public Key: {}
Extended Attributes: {} bytes
Signature: {}
Maximum Path Length: {}{}"###,
certificate.serial_no.to_string(),
certificate.usage_flags, usage_flags,
certificate.timestamp,
certificate.validity[0], certificate.validity[1],
certificate.subject.timestamp,
subject_identities,
subject_networks,
subject_update_urls,
string_or_dash(certificate.subject.name.serial_no.as_str()),
string_or_dash(certificate.subject.name.common_name.as_str()),
string_or_dash(certificate.subject.name.country.as_str()),
string_or_dash(certificate.subject.name.organization.as_str()),
string_or_dash(certificate.subject.name.unit.as_str()),
string_or_dash(certificate.subject.name.locality.as_str()),
string_or_dash(certificate.subject.name.province.as_str()),
string_or_dash(certificate.subject.name.street_address.as_str()),
string_or_dash(certificate.subject.name.postal_code.as_str()),
string_or_dash(certificate.subject.name.email.as_str()),
string_or_dash(certificate.subject.name.url.as_str()),
string_or_dash(certificate.subject.name.host.as_str()),
string_or_dash(base64_encode(&certificate.subject.unique_id).as_str()),
string_or_dash(base64_encode(&certificate.subject.unique_id_signature).as_str()),
certificate.issuer.to_string(),
string_or_dash(base64_encode(&certificate.issuer_public_key).as_str()),
string_or_dash(base64_encode(&certificate.public_key).as_str()),
certificate.extended_attributes.len(),
string_or_dash(base64_encode(&certificate.signature).as_str()),
certificate.max_path_length, if certificate.max_path_length == 0 { " (leaf)" } else { " (CA or sub-CA)" });
}
fn list(store: &Arc<Store>) -> i32 {
0
}
fn show<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
fn show<'a>(store: &Arc<Store>, global_flags: &GlobalFlags, cli_args: &ArgMatches<'a>) -> i32 {
let serial_or_path = cli_args.value_of("serialorpath").unwrap().trim();
CertificateSerialNo::new_from_string(serial_or_path).map_or_else(|| {
read_limit(serial_or_path, 65536).map_or_else(|e| {
println!("ERROR: unable to read certificate from '{}': {}", serial_or_path, e.to_string());
1
}, |cert_json| {
serde_json::from_slice::<Certificate>(cert_json.as_ref()).map_or_else(|e| {
println!("ERROR: unable to decode certificate from '{}': {}", serial_or_path, e.to_string());
1
}, |certificate| {
if global_flags.json_output {
println!("{}", to_json_pretty(&certificate));
} else {
dump_cert(&certificate);
}
let cv = certificate.verify(ms_since_epoch());
if cv != CertificateError::None {
println!("\nWARNING: certificate validity check failed: {}", cv.to_str());
}
0
})
})
}, |serial| {
// TODO: query node
0
})
}
fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
@ -35,13 +185,13 @@ fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
1
} else {
let (_, privk) = key_pair.ok().unwrap();
let privk_hex = hex::encode(privk);
let privk_base64 = base64_encode(&privk);
let path = cli_args.map_or("", |cli_args| { cli_args.value_of("path").unwrap_or("") });
if path.is_empty() {
println!("{}", privk_hex);
println!("{}", privk_base64);
0
} else {
std::fs::write(path, privk_hex.as_bytes()).map_or_else(|e| {
std::fs::write(path, privk_base64.as_bytes()).map_or_else(|e| {
eprintln!("FATAL: error writing '{}': {}", path, e.to_string());
e.raw_os_error().unwrap_or(1)
}, |_| {
@ -251,10 +401,6 @@ fn verify<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
}
fn dump<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
}
fn import<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
}
@ -271,19 +417,18 @@ fn delete<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
}
pub(crate) fn run(store: Arc<Store>, cli_args: &ArgMatches) -> i32 {
pub(crate) fn run(store: Arc<Store>, global_flags: GlobalFlags, cli_args: &ArgMatches) -> i32 {
match cli_args.subcommand() {
("list", None) => list(&store),
("show", Some(sub_cli_args)) => show(&store, sub_cli_args),
("show", Some(sub_cli_args)) => show(&store, &global_flags, sub_cli_args),
("newsuid", sub_cli_args) => newsuid(sub_cli_args),
("newcsr", Some(sub_cli_args)) => newcsr(sub_cli_args),
("sign", Some(sub_cli_args)) => sign(&store, sub_cli_args),
("verify", Some(sub_cli_args)) => verify(&store, sub_cli_args),
("dump", Some(sub_cli_args)) => dump(&store, sub_cli_args),
("import", Some(sub_cli_args)) => import(&store, sub_cli_args),
("factoryreset", None) => factoryreset(&store),
("export", Some(sub_cli_args)) => export(&store, sub_cli_args),
("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args),
("factoryreset", None) => factoryreset(&store),
_ => {
crate::print_help();
1

View file

@ -96,7 +96,7 @@ mod tests {
fn test_getifaddrs() {
println!("starting getifaddrs...");
crate::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| {
println!(" device: {} ip: {}", dev, a.to_string())
println!(" {} {}", dev, a.to_string())
});
println!("done.")
}

View file

@ -85,12 +85,12 @@ Common Operations:
· defaultroute <boolean> Can default route be overridden?
· join [-...] <network> Join a virtual network
-c <identity | fingerprint> Controller identity / fingerprint
-c <?identity | fingerprint> Controller identity / fingerprint
· leave <network> Leave a virtual network
Advanced Operations:
service Start this node
service Start node
(usually not invoked directly)
controller <command> [option]
@ -103,39 +103,35 @@ Advanced Operations:
identity <command> [args]
new [c25519 | p384] Create identity (default: c25519)
getpublic <identity> Extract public part of identity
fingerprint <identity> Get an identity's fingerprint
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
getpublic <?identity> Extract public part of identity
fingerprint <?identity> Get an identity's fingerprint
validate <?identity> Locally validate an identity
sign <?identity> <@file> Sign a file with an identity's key
verify <?identity> <@file> <sig> Verify a signature
locator <command> [args]
new [-...] <identity> <endpoint> [...] Create new signed locator
new [-...] <?identity> <endpoint,...> Create new signed locator
-r <revision> Revision number (default: time)
verify <identity> <locator> Verify locator signature
show <locator> Show contents of a locator
verify <?identity> <?locator> Verify locator signature
show <?locator> Show contents of a locator
cert <command> [args]
· list List certificates at local node
· show <serial> Show certificate details
newsuid [suid secret out] Create a subject unique ID secret
newcsr <csr out> <secret out> Create a CSR (interactive)
sign <csr> <identity> [cert out] Sign a CSR to create a certificate
verify <cert> Verify certificate (not chain)
dump <cert> Verify and print certificate
· import <cert> [trust,trust,...] Import certificate into this node
trust flag: rootca Certificate is a root CA
trust flag: ztrootset ZeroTier root node set
· factoryreset Re-import compiled-in default certs
· export <serial> [path] Export a certificate from this node
show <@cert|·serial> Show certificate details
newsuid [@secret] Create a subject unique ID secret
newcsr <@csr> <@secret> Create a CSR (interactive)
sign <@csr> <@secret> <@cert> Sign a CSR to create a certificate
verify <@cert> Internally verify certificate
· import <@cert> [trust,trust,...] Import certificate into this node
trust flag: rootca Root (or self-signed) CA
trust flag: config Can influence node configuration
· export <serial> [@cert] Export a certificate from this node
· delete <serial|ALL> Delete certificate from this node
· factoryreset Re-import compiled-in default certs
· Command requires a running node and access to a local API token.
An <address> may be specified as a 10-digit short ZeroTier address, a
fingerprint containing both an address and a SHA384 hash, or an identity.
Identities and locators can be specified as either paths to files on the
filesystem or verbatim objects in string format. This is auto-detected.
· Command (or command with argument type) requires a running node.
@ Argument is a path to an object, not the object itself.
? Argument can be either an inline value or a path to it.
"###, ver.0, ver.1, ver.2)
}
@ -178,10 +174,11 @@ fn make_store(cli_args: &ArgMatches) -> Arc<Store> {
}
#[derive(Clone)]
pub struct GlobalFlags {
pub(crate) struct GlobalFlags {
pub json_output: bool,
}
#[inline(always)]
fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags {
GlobalFlags {
json_output: cli_args.is_present("json")
@ -276,7 +273,7 @@ fn main() {
.subcommand(App::new("cert")
.subcommand(App::new("list"))
.subcommand(App::new("show")
.arg(Arg::with_name("serial").index(1).required(true)))
.arg(Arg::with_name("serialorpath").index(1).required(true)))
.subcommand(App::new("newsuid")
.arg(Arg::with_name("path").index(1).required(false)))
.subcommand(App::new("newcsr")
@ -284,7 +281,7 @@ fn main() {
.arg(Arg::with_name("secretpath").index(2).required(true)))
.subcommand(App::new("sign")
.arg(Arg::with_name("csr").index(1).required(true))
.arg(Arg::with_name("identity").index(2).required(true))
.arg(Arg::with_name("secretpath").index(2).required(true))
.arg(Arg::with_name("output").index(3).required(false)))
.subcommand(App::new("verify")
.arg(Arg::with_name("cert").index(1).required(true)))
@ -340,7 +337,7 @@ fn main() {
("controller", Some(sub_cli_args)) => { 0 }
("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args),
("locator", Some(sub_cli_args)) => crate::commands::locator::run(sub_cli_args),
("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), sub_cli_args),
("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), get_global_flags(&cli_args), sub_cli_args),
_ => {
print_help();
1

View file

@ -219,7 +219,7 @@ impl Service {
uptime: ms_monotonic() - self.startup_time_monotonic,
config: (*self.local_config()).clone(),
online: self.online(),
public_identity: node.identity(),
public_identity: node.identity().clone(),
version: format!("{}.{}.{}", ver.0, ver.1, ver.2),
version_major: ver.0,
version_minor: ver.1,
@ -237,7 +237,7 @@ unsafe impl Send for Service {}
unsafe impl Sync for Service {}
async fn run_async(store: Arc<Store>, local_config: Arc<LocalConfig>) -> i32 {
let mut process_exit_value: i32 = 0;
let process_exit_value: i32 = 0;
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
let mut http_listeners: BTreeMap<InetAddress, HttpListener> = BTreeMap::new();

View file

@ -123,6 +123,16 @@ pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
}
}
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub(crate) fn to_json<O: serde::Serialize>(o: &O) -> String {
serde_json::to_string(o).unwrap_or("null".into())
}
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub(crate) fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
serde_json::to_string_pretty(o).unwrap_or("null".into())
}
/// Recursively patch a JSON object.
/// This is slightly different from a usual JSON merge. For objects in the target their fields
/// are updated by recursively calling json_patch if the same field is present in the source.

View file

@ -32,7 +32,7 @@ pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGro
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family as i32 == osdep::AF_LINK as i32 {
let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast());
let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast());
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && crate::osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
let mi = la.sdl_nlen as usize;
groups.insert(MulticastGroup{
mac: MAC((la.sdl_data[mi] as u64) << 40 | (la.sdl_data[mi+1] as u64) << 32 | (la.sdl_data[mi+2] as u64) << 24 | (la.sdl_data[mi+3] as u64) << 16 | (la.sdl_data[mi+4] as u64) << 8 | la.sdl_data[mi+5] as u64),