From a0c280eae479be1b0f1cd28288d59a5aba18afe5 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 5 Jan 2022 20:40:35 +0100 Subject: [PATCH 01/37] Export some more version metadata --- build.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 4 ++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/build.rs b/build.rs index a353525..733c61e 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,4 @@ +use std::process::Command; use walkdir::{WalkDir, DirEntry}; fn is_hidden(entry: &DirEntry) -> bool { @@ -8,8 +9,25 @@ fn is_hidden(entry: &DirEntry) -> bool { } fn main() { - // Tell cargo to only run this script if the schema files or this script have changed - println!("cargo:rerun-if-changed=schema"); + // Build version number using the current git commit id + let out = Command::new("git").arg("rev-list") + .args(["HEAD", "-1"]) + .output() + .expect("failed to run `git rev-list HEAD -1`"); + let owned_gitrev = String::from_utf8(out.stdout) + .expect("git rev-list output was not valid UTF8"); + let gitrev = owned_gitrev.trim(); + let abbrev = &gitrev[0..9]; + println!("cargo:rustc-env=CARGO_PKG_VERSION_GITREV={}", gitrev); + + let out = Command::new("git").arg("log") + .args(["-1", "--format=%as"]) + .output() + .expect("failed to run `git log -1 --format=\"format:%as\"`"); + let commit_date = String::from_utf8(out.stdout) + .expect("git log output was not valid UTF8"); + let commit_date = commit_date.trim(); + println!("cargo:rustc-env=BFFH_GIT_COMMIT_DATE={}", commit_date); let mut compile_command = ::capnpc::CompilerCommand::new(); @@ -17,6 +35,8 @@ fn main() { // i.e. a file "user.capnp" will result in module "schema::user" compile_command.default_parent_module(vec!["schema".into()]); + println!(">>> Collecting schemas..."); + for entry in WalkDir::new("schema") .max_depth(2) .into_iter() @@ -30,10 +50,35 @@ fn main() { .unwrap_or(false) ) { - println!("Collecting schema file {}", entry.path().display()); + println!(" Collecting schema file {}", entry.path().display()); compile_command.file(entry.path()); } - println!("Compiling schemas..."); + println!(">>> Compiling schemas..."); compile_command.run().expect("Failed to generate API code"); + + println!(">>> Building version number..."); + + let rustc = std::env::var("RUSTC").unwrap(); + let out = Command::new(rustc).arg("--version") + .output() + .expect("failed to run `rustc --version`"); + let rustc_version = String::from_utf8(out.stdout) + .expect("rustc --version returned invalid UTF-8"); + let rustc_version = rustc_version.trim(); + println!("cargo:rustc-env=CARGO_RUSTC_VERSION={}", rustc_version); + + let tagged_release = option_env!("BFFHD_BUILD_TAGGED_RELEASE") == Some("1"); + let release = if tagged_release { + format!("BFFH {version} [{rustc}]", + version = env!("CARGO_PKG_VERSION"), + rustc = rustc_version) + } else { + format!("BFFH {version} ({gitrev} {date}) [{rustc}]", + version=env!("CARGO_PKG_VERSION"), + gitrev=abbrev, + date=commit_date, + rustc=rustc_version) + }; + println!("cargo:rustc-env=BFFHD_RELEASE_STRING={}", release); } diff --git a/src/main.rs b/src/main.rs index 07afdb7..81b756e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,6 +43,10 @@ use slog::Logger; use paho_mqtt::AsyncClient; use crate::config::{ActorConn, Config, InitiatorConn}; +const RELEASE: &'static str = env!("BFFHD_RELEASE_STRING"); +const VERSION: &'static str = env!("CARGO_PKG_VERSION"); +const GITREV: &'static str = env!("CARGO_PKG_VERSION_GITREV"); + fn main() { use clap::{crate_version, crate_description, crate_name}; From 70bfdbbf4e60b945a458a5b898f26c61d0c43c65 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 5 Jan 2022 20:41:54 +0100 Subject: [PATCH 02/37] Update API files --- Cargo.toml | 2 +- schema | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ba9f301..c28774f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diflouroborane" -version = "0.3.1" +version = "0.3.2" authors = [ "dequbed " , "Kai Jan Kriegel " , "Joseph Langosch " diff --git a/schema b/schema index c855646..5b88b64 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit c855646a90958ae575d58be074d187acb9f8f4fa +Subproject commit 5b88b6446c55833a1c1340cd5b7fe9fcb992b872 From 17005c0536a37c803cad9c7147838acd7b5bd6d2 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 5 Jan 2022 21:01:16 +0100 Subject: [PATCH 03/37] Properly parse optional config elements --- examples/bffh.dhall | 7 ++++--- src/machine.rs | 12 ++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index 4a57c51..249f473 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -33,7 +33,8 @@ ] , machines = { Testmachine = - { description = Some "A test machine" + { description = "A test machine" + , wiki = "test" , disclose = "lab.test.read" , manage = "lab.test.admin" , name = "MachineA" @@ -41,7 +42,7 @@ , write = "lab.test.write" }, Another = - { description = Some "Another test machine" + { wiki = "test_another" , disclose = "lab.test.read" , manage = "lab.test.admin" , name = "Another" @@ -49,7 +50,7 @@ , write = "lab.test.write" }, Yetmore = - { description = Some "Yet more test machines" + { description = "Yet more test machines" , disclose = "lab.test.read" , manage = "lab.test.admin" , name = "Yetmore" diff --git a/src/machine.rs b/src/machine.rs index 255c669..9bd53c7 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -352,6 +352,7 @@ impl Inner { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] /// A description of a machine /// /// This is the struct that a machine is serialized to/from. @@ -359,11 +360,12 @@ impl Inner { pub struct MachineDescription { /// The name of the machine. Doesn't need to be unique but is what humans will be presented. pub name: String, + /// An optional description of the Machine. + #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] pub description: Option, - #[serde(default)] - #[serde(flatten)] + #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] pub wiki: Option, /// The permission required @@ -371,6 +373,12 @@ pub struct MachineDescription { pub privs: access::PrivilegesBuf, } +fn deser_option<'de, D, T>(d: D) -> std::result::Result, D::Error> + where D: serde::Deserializer<'de>, T: serde::Deserialize<'de>, +{ + Ok(T::deserialize(d).ok()) +} + impl MachineDescription { pub fn load_file>(path: P) -> Result> { let content = fs::read(path)?; From f524079914f70542b1a494662b44264ef8e608c7 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 5 Jan 2022 21:15:05 +0100 Subject: [PATCH 04/37] Implement categories support --- examples/bffh.dhall | 1 + src/api/machines.rs | 3 +++ src/config.rs | 1 + src/machine.rs | 3 +++ 4 files changed, 8 insertions(+) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index 249f473..a541710 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -43,6 +43,7 @@ }, Another = { wiki = "test_another" + , category = "test" , disclose = "lab.test.read" , manage = "lab.test.admin" , name = "Another" diff --git a/src/api/machines.rs b/src/api/machines.rs index 878b57d..cc2d5c5 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -194,6 +194,9 @@ async fn fill_machine_builder( if let Some(ref wiki) = machine.desc.wiki { builder.set_wiki(wiki); } + if let Some(ref category) = machine.desc.category { + builder.set_category(category); + } builder.set_urn(&format!("urn:fabaccess:resource:{}", id.as_ref())); let machineapi = Machine::new(user.clone(), perms, machine.clone()); diff --git a/src/config.rs b/src/config.rs index 79f1a0c..974fe54 100644 --- a/src/config.rs +++ b/src/config.rs @@ -97,6 +97,7 @@ impl Default for Config { name: "Testmachine".to_string(), description: Some("A test machine".to_string()), wiki: None, + category: None, privs: PrivilegesBuf { disclose: PermissionBuf::from_string("lab.test.read".to_string()), read: PermissionBuf::from_string("lab.test.read".to_string()), diff --git a/src/machine.rs b/src/machine.rs index 9bd53c7..0ff2268 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -368,6 +368,9 @@ pub struct MachineDescription { #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] pub wiki: Option, + #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] + pub category: Option, + /// The permission required #[serde(flatten)] pub privs: access::PrivilegesBuf, From 0da32133958525619904f16392dd1c804bcab416 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 6 Jan 2022 18:19:02 +0100 Subject: [PATCH 05/37] Improve documentation around example setup Fixes: #38 --- examples/bffh.dhall | 282 +++++++++++++++++++++++++++++++------------- examples/roles.toml | 19 --- examples/users.toml | 10 +- src/api/machines.rs | 7 +- src/config.rs | 10 ++ src/machine.rs | 8 +- 6 files changed, 224 insertions(+), 112 deletions(-) delete mode 100644 examples/roles.toml diff --git a/examples/bffh.dhall b/examples/bffh.dhall index a541710..2a7ae5d 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -1,81 +1,205 @@ --- { actor_connections = [] : List { _1 : Text, _2 : Text } -{ actor_connections = - -- Link up machines to actors - [ { machine = "Testmachine", actor = "Shelly1234" } - , { machine = "Another", actor = "Bash" } - -- One machine can have as many actors as it wants - , { machine = "Yetmore", actor = "Bash2" } - , { machine = "Yetmore", actor = "FailBash"} - ] -, actors = - { Shelly1234 = { module = "Shelly", params = - { topic = "Topic1234" }} - , Bash = { module = "Process", params = - { cmd = "./examples/actor.sh" - , args = "your ad could be here" - }} - , Bash2 = { module = "Process", params = - { cmd = "./examples/actor.sh" - , args = "this is a different one" - }} - , FailBash = { module = "Process", params = - { cmd = "./examples/fail-actor.sh" - }} - } - , init_connections = [] : List { machine : Text, initiator : Text } - --, init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] - , initiators = {=} - --{ Initiator = { module = "Dummy", params = { uid = "Testuser" } } } -, listens = - [ { address = "127.0.0.1", port = Some 59661 } - , { address = "::1", port = Some 59661 } - , { address = "192.168.0.114", port = Some 59661 } - ] -, machines = - { Testmachine = - { description = "A test machine" - , wiki = "test" - , disclose = "lab.test.read" - , manage = "lab.test.admin" - , name = "MachineA" - , read = "lab.test.read" - , write = "lab.test.write" +{- Main configuration file for bffh + - ================================ + - + - In this configuration file you configure almost all parts of how bffh operates, but most importantly: + - * Machines + - * Initiators and Actors + - * Which Initiators and Actors relate to which machine(s) + - * Roles and the permissions granted by them + -} + +-- The config is in the configuration format/language dhall. You can find more information about dhall over at +-- https://dhall-lang.org + +-- (Our) Dhall is somewhat similar to JSON and YAML in that it expects a top-level object containing the +-- configuration values +{ + -- Configure the addresses and ports bffh listens on + listens = [ + -- BFFH binds a port for every listen object in this array. + -- Each listen object is of the format { address = , port = } + -- If you don't specify a port bffh will use the default of `59661` + -- 'address' can be a IP address or a hostname + -- If bffh can not bind a port for the specified combination if will log an error but *continue with the remaining ports* + { address = "127.0.0.1", port = 59661 }, + { address = "::1", port = 59661 }, + { address = "192.168.0.114", port = 59661 } + ], + + -- Configure TLS. BFFH requires a PEM-encoded certificate and the associated key as two separate files + certfile = "examples/self-signed-cert.pem", + keyfile = "examples/self-signed-key.pem", + + -- BFFH right now requires a running MQTT broker. + mqtt_url = "tcp://localhost:1883", + + -- Path to the database file for bffh. bffh will in fact create two files; ${db_path} and ${db_path}.lock. + -- BFFH will *not* create any directories so ensure that the directory exists and the user running bffh has write + -- access into them. + db_path = "/tmp/bffh", + + -- In dhall you can also easily import definitions from other files, e.g. you could write + -- roles = ./roles.dhall + roles = { + -- Role definitions + -- A role definition is of the form + -- rolename = { + -- parents = [], + -- permissions = [], + -- } + -- + -- Role names are case sensitive, so RoleName != rolename. + -- + -- If you want either parents or permissions to be empty its best to completely skip it: + testrole = { + permissions = [ "lab.some.admin" ] + }, + somerole = { + parents = ["testparent"], + -- "Permissions" are formatted as Perm Rules, so you can use the wildcards '*' and '+' + permissions = [ "lab.test.*" ] + }, + -- Roles can inherit from each other. In that case a member of e.g. 'somerole' that inherits from + -- 'testparent' will have all the permissions of 'somerole' AND 'testparent' assigned to them. + -- Right now permissions are stricly additive so you can't take a permission away in a child role that a parent + -- role grants. + testparent = { + permissions = [ + "lab.some.write", + "lab.some.read", + "lab.some.disclose" + ] + } }, - Another = - { wiki = "test_another" - , category = "test" - , disclose = "lab.test.read" - , manage = "lab.test.admin" - , name = "Another" - , read = "lab.test.read" - , write = "lab.test.write" + + -- Configure machines + -- "Machines" (which in future will be more appropiately named "resources") are the main thing bffh is concerned + -- with. + -- You can define an almost limitless amount of machines (well 2^64 - 1, so 18_446_744_073_709_551_615 to be precise) + -- Each of these machines can then have several "actors" and "initiators" assigned + machines = { + Testmachine = { + -- A machine comes with two "names". The id above ("Testmachine") and the "name" ("MachineA"). + -- The id is what you'll use in the config format and is strictly limited to alphanumeric characters and '_' + -- and must begin with a letter. Most importantly you CAN NOT use '-' or spaces in an identifier + -- (dhall makes this technically possible but you can break things in subtle ways) + + -- REQUIRED. The "name" of a machine is what will be presented to humans. It can contain all unicode + -- including spaces and nonprintable characters. + -- A name SHOULD be short but unique. + name = "MachineA", + + -- OPTIONAL. A description can be assigned to machines. It will also only be shown to humans. Thus it is + -- once again limited only to unicode. If you want to provide your users with important additional + -- information other than the name this is the place to do it. + description = "A test machine", + + -- OPTIONAL. If you have a wiki going into more detail how to use a certain machine or what to keep in + -- mind when using it you can provide a URL here that will be presented to users. + wiki = "https://wiki.example.org/machineA", + + -- OPTIONAL. You can assign categories to machines to allow clients to group/filter machines by them. + category = "Testcategory", + + -- REQUIRED. + -- Each machine MUST have *all* Permission levels assigned to it. + -- Permissions aren't PermRules as used in the 'roles' definitions but must be precise without wildcards. + -- Permission levels aren't additive, so a user having 'manage' permission does not automatically get + -- 'read' or 'write' permission. + + -- (Note, disclose is not fully implemented at the moment) + -- Users lacking 'disclose' will not be informed about this machine in any way and it will be hidden from + -- them in the client. Usually the best idea is to assign 'read' and 'disclose' to the same permission. + disclose = "lab.test.read", + + -- Users lacking 'read' will be shown a machine including name, description, category and wiki but not + -- it's current state. The current user is not disclosed. + read = "lab.test.read", + + -- The 'write' permission allows to 'use' the machine. + write = "lab.test.write", + + -- Manage represents the 'superuser' permission. Users with this permission can force set any state and + -- read out the current user + manage = "lab.test.admin" + }, + Another = { + wiki = "test_another", + category = "test", + disclose = "lab.test.read", + manage = "lab.test.admin", + name = "Another", + read = "lab.test.read", + write = "lab.test.write" + }, + Yetmore = { + description = "Yet more test machines", + disclose = "lab.test.read", + manage = "lab.test.admin", + name = "Yetmore", + read = "lab.test.read", + write = "lab.test.write" + } }, - Yetmore = - { description = "Yet more test machines" - , disclose = "lab.test.read" - , manage = "lab.test.admin" - , name = "Yetmore" - , read = "lab.test.read" - , write = "lab.test.write" - } - } -, mqtt_url = "tcp://localhost:1883" -, db_path = "/tmp/bffh" -, roles = - { testrole = - { permissions = [ "lab.test.*" ] } - , somerole = - { parents = ["testparent"] - , permissions = [ "lab.some.admin" ] - } - , testparent = - { permissions = - [ "lab.some.write" - , "lab.some.read" - , "lab.some.disclose" - ] - } - } -, certfile = "examples/self-signed-cert.pem" -, keyfile = "examples/self-signed-key.pem" -} + + -- Actor configuration. Actors are how bffh affects change in the real world by e.g. switching a power socket + -- using a shelly + actors = { + -- Actors similarly to machines have an 'id'. This id (here "Shelly1234") is limited to Alphanumeric ASCII + -- and must begin with a letter. + Shelly1234 = { + -- Actors are modular pieces of code that are loaded as required. The "Shelly" module will send + -- activation signals to a shelly switched power socket over MQTT + module = "Shelly", + -- Actors can have arbitrary parameters passed to them, varying by actor module. + params = { + -- For Shelly you can configure the MQTT topic segment it uses. Shellies listen to a specific topic + -- containing their name (which is usually of the form "shelly_" but can be changed). + -- If you do not configure a topic here the actor will use it's 'id' (in this case "Shelly1234"). + topic = "Topic1234" + } + }, + + Bash = { + -- The "Process" module runs a given script or command on state change. + -- bffh invoces the given cmd as `$ ${cmd} ${args} ${id} ${state}` so e.g. as + -- `$ ./examples/actor.sh your ad could be here Bash inuse` + module = "Process", + params = { + -- which is configured by the (required) 'cmd' parameter. Paths are relative to PWD of bffh. Systemd + -- and similar process managers may change this PWD so it's usually the most future-proof to use + -- absolute paths. + cmd = "./examples/actor.sh", + -- You can pass static args in here, these will be passed to every invocation of the command by this actor. + -- args passed here are split by whitespace, so these here will be passed as 5 separate arguments + args = "your ad could be here" + } + }, + Bash2 = { module = "Process", params = { cmd = "./examples/actor.sh" , args = "this is a different one" }}, + FailBash = { module = "Process", params = { cmd = "./examples/fail-actor.sh" }} + }, + + -- Linkng up machines to actors + -- Actors need to be connected to machines to be useful. A machine can be connected to multiple actors, but one + -- actor can only be connected to one machine. + actor_connections = [ + { machine = "Testmachine", actor = "Shelly1234" }, + { machine = "Another", actor = "Bash" }, + { machine = "Yetmore", actor = "Bash2" }, + { machine = "Yetmore", actor = "FailBash"} + ], + + -- Initiators are configured almost the same way as Actors, refer to actor documentation for more details + -- The below '{=}' is what you need if you want to define *no* initiators at all and only use the API with apps + -- to let people use machines. + initiators = {=}, + -- The "Dummy" initiator will try to use and return a machine as the given user every few seconds. It's good to + -- test your system but will spam your log so is disabled by default. + --{ Initiator = { module = "Dummy", params = { uid = "Testuser" } } } + + -- Linking up machines to initiators. Similar to actors a machine can have several initiators assigned but an + -- initiator can only be assigned to one machine. + -- The below is once again how you have to define *no* initiators. + init_connections = [] : List { machine : Text, initiator : Text } + -- init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] +} \ No newline at end of file diff --git a/examples/roles.toml b/examples/roles.toml deleted file mode 100644 index 2c91a30..0000000 --- a/examples/roles.toml +++ /dev/null @@ -1,19 +0,0 @@ -[anotherrole] - -[testrole] -permissions = [ - "lab.test.*" -] - -[somerole] -parents = ["testparent/lmdb"] -permissions = [ - "lab.some.admin" -] - -[testparent] -permissions = [ - "lab.some.write", - "lab.some.read", - "lab.some.disclose", -] diff --git a/examples/users.toml b/examples/users.toml index 3c4aa37..719f2fb 100644 --- a/examples/users.toml +++ b/examples/users.toml @@ -1,13 +1,13 @@ [Testuser] -# Define them in roles.toml as well +# These roles have to be defined in 'bffh.dhall'. +# Non-existant roles will not crash the server but print a `WARN` level message in the +# server log in the form "Did not find role somerole/internal while trying to tally". roles = ["somerole/internal", "testrole/internal"] -# If two or more users want to use the same machine at once the higher prio -# wins -priority = 0 - +# The password will be hashed using argon2id on load time and is not available in plaintext afterwards. passwd = "secret" # You can add whatever random data you want. # It will get stored in the `kv` field in UserData. +# This is not used for anything at the moment noot = "noot!" diff --git a/src/api/machines.rs b/src/api/machines.rs index cc2d5c5..f754d52 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -200,8 +200,7 @@ async fn fill_machine_builder( builder.set_urn(&format!("urn:fabaccess:resource:{}", id.as_ref())); let machineapi = Machine::new(user.clone(), perms, machine.clone()); - let state = machine.get_status().await; - if perms.write && state == Status::Free { + if perms.write { builder.set_use(capnp_rpc::new_client(machineapi.clone())); } if perms.manage { @@ -229,7 +228,9 @@ async fn fill_machine_builder( Status::Reserved(_) => MachineState::Reserved, Status::ToCheck(_) => MachineState::ToCheck, }; - builder.set_state(s); + if perms.read { + builder.set_state(s); + } builder.set_info(capnp_rpc::new_client(machineapi)); } \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 974fe54..53e0a78 100644 --- a/src/config.rs +++ b/src/config.rs @@ -58,6 +58,14 @@ pub struct Config { pub keyfile: PathBuf, } + +pub(crate) fn deser_option<'de, D, T>(d: D) -> std::result::Result, D::Error> + where D: serde::Deserializer<'de>, T: serde::Deserialize<'de>, +{ + Ok(T::deserialize(d).ok()) +} + + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RoleConfig { #[serde(default = "Vec::new")] @@ -69,6 +77,8 @@ pub struct RoleConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Listen { pub address: String, + + #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] pub port: Option, } diff --git a/src/machine.rs b/src/machine.rs index 0ff2268..2308303 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -32,6 +32,8 @@ use crate::Error::Denied; use crate::network::MachineMap; use crate::space; +use crate::config::deser_option; + pub struct Machines { machines: Vec } @@ -376,12 +378,6 @@ pub struct MachineDescription { pub privs: access::PrivilegesBuf, } -fn deser_option<'de, D, T>(d: D) -> std::result::Result, D::Error> - where D: serde::Deserializer<'de>, T: serde::Deserialize<'de>, -{ - Ok(T::deserialize(d).ok()) -} - impl MachineDescription { pub fn load_file>(path: P) -> Result> { let content = fs::read(path)?; From bf9fadbf7440cdd70f035e80bf45a2c9df3e565b Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 6 Jan 2022 19:42:54 +0100 Subject: [PATCH 06/37] Implement getAPIVersion and getServerRelease methods --- src/api.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/api.rs b/src/api.rs index 8effc5d..797b692 100644 --- a/src/api.rs +++ b/src/api.rs @@ -43,7 +43,10 @@ impl Bootstrap { } } +use connection_capnp::{API_VERSION_MAJOR, API_VERSION_MINOR, API_VERSION_PATCH}; use connection_capnp::bootstrap::*; +use crate::RELEASE; + impl connection_capnp::bootstrap::Server for Bootstrap { fn authentication_system(&mut self, _: AuthenticationSystemParams, @@ -99,4 +102,28 @@ impl connection_capnp::bootstrap::Server for Bootstrap { Promise::ok(()) } + + fn get_a_p_i_version( + &mut self, + _: GetAPIVersionParams, + mut results: GetAPIVersionResults + ) -> Promise<(), capnp::Error> { + let mut builder = results.get(); + let mut builder = builder.init_version(); + builder.set_major(API_VERSION_MAJOR); + builder.set_minor(API_VERSION_MINOR); + builder.set_patch(API_VERSION_PATCH); + Promise::ok(()) + } + + fn get_server_release( + &mut self, + _: GetServerReleaseParams, + mut results: GetServerReleaseResults + ) -> Promise<(), capnp::Error> { + let mut builder = results.get(); + builder.set_name("bffh"); + builder.set_release(RELEASE); + Promise::ok(()) + } } From 19abba371e230eb63a1fe932054abed9ea37a2f4 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 6 Jan 2022 20:19:49 +0100 Subject: [PATCH 07/37] Delete outdated pass.toml --- examples/pass.toml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/pass.toml diff --git a/examples/pass.toml b/examples/pass.toml deleted file mode 100644 index 6d4855d..0000000 --- a/examples/pass.toml +++ /dev/null @@ -1 +0,0 @@ -Testuser = "secret" From 4858a6a6fb8c56327a88fd2748edd6480de6a0d9 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 6 Jan 2022 20:30:50 +0100 Subject: [PATCH 08/37] Implement a simple audit log --- Cargo.lock | 33 +++++++++++++++++++++++++++-- Cargo.toml | 3 +++ examples/bffh.dhall | 13 ++++++++---- src/audit.rs | 43 ++++++++++++++++++++++++++++++++++++++ src/config.rs | 2 ++ src/db/machine.rs | 7 +++++-- src/db/machine/internal.rs | 9 +++++--- src/machine.rs | 2 +- src/main.rs | 2 ++ 9 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 src/audit.rs diff --git a/Cargo.lock b/Cargo.lock index 03ae416..b7996e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,7 +401,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time", + "time 0.1.43", "winapi", ] @@ -532,7 +532,7 @@ dependencies = [ [[package]] name = "diflouroborane" -version = "0.3.1" +version = "0.3.2" dependencies = [ "async-channel", "async-rustls", @@ -561,12 +561,14 @@ dependencies = [ "rustls-pemfile", "serde", "serde_dhall", + "serde_json", "signal-hook", "slog", "slog-async", "slog-term", "smol", "tempfile", + "time 0.3.5", "toml", "uuid", "walkdir", @@ -1077,6 +1079,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "js-sys" version = "0.3.55" @@ -1725,6 +1733,17 @@ dependencies = [ "url", ] +[[package]] +name = "serde_json" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.8.2" @@ -1974,6 +1993,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +dependencies = [ + "libc", + "serde", +] + [[package]] name = "tinyvec" version = "1.3.1" diff --git a/Cargo.toml b/Cargo.toml index c28774f..f220ea2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,9 @@ flexbuffers = "2.0.0" bincode = "2.0.0-dev" serde_dhall = { version = "0.10.1", default-features = false } +# Audit log uses serde_json for now +serde_json = "1.0.74" +time = { version = "0.3.5", features = ["serde"] } uuid = { version = "0.8.2", features = ["serde", "v4"] } diff --git a/examples/bffh.dhall b/examples/bffh.dhall index 2a7ae5d..a7a5f88 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -38,6 +38,11 @@ -- access into them. db_path = "/tmp/bffh", + -- Audit log path. Bffh will log state changes into this file, one per line. + -- Audit log entries are for now JSON: + -- {"timestamp":1641497361,"machine":"Testmachine","state":{"state":{"InUse":{"uid":"Testuser","subuid":null,"realm":null}}}} + auditlog_path = "/tmp/bffh.audit", + -- In dhall you can also easily import definitions from other files, e.g. you could write -- roles = ./roles.dhall roles = { @@ -192,14 +197,14 @@ -- Initiators are configured almost the same way as Actors, refer to actor documentation for more details -- The below '{=}' is what you need if you want to define *no* initiators at all and only use the API with apps -- to let people use machines. - initiators = {=}, + -- initiators = {=}, -- The "Dummy" initiator will try to use and return a machine as the given user every few seconds. It's good to -- test your system but will spam your log so is disabled by default. - --{ Initiator = { module = "Dummy", params = { uid = "Testuser" } } } + initiators = { Initiator = { module = "Dummy", params = { uid = "Testuser" } } }, -- Linking up machines to initiators. Similar to actors a machine can have several initiators assigned but an -- initiator can only be assigned to one machine. -- The below is once again how you have to define *no* initiators. - init_connections = [] : List { machine : Text, initiator : Text } - -- init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] + --init_connections = [] : List { machine : Text, initiator : Text } + init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] } \ No newline at end of file diff --git a/src/audit.rs b/src/audit.rs new file mode 100644 index 0000000..833bca9 --- /dev/null +++ b/src/audit.rs @@ -0,0 +1,43 @@ +use std::fs::{File, OpenOptions}; +use std::io; +use std::io::{LineWriter, Write}; +use std::sync::Mutex; +use std::time::Instant; +use crate::Config; +use serde::{Serialize, Deserialize}; +use serde_json::Serializer; +use time::OffsetDateTime; +use crate::db::machine::{MachineIdentifier, MachineState}; + +#[derive(Debug)] +pub struct AuditLog { + writer: Mutex>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AuditLogLine { + timestamp: i64, + machine: MachineIdentifier, + state: MachineState, +} + +impl AuditLog { + pub fn new(config: &Config) -> io::Result { + let fd = OpenOptions::new().create(true).append(true).open(&config.auditlog_path)?; + let writer = Mutex::new(LineWriter::new(fd)); + Ok(Self { writer }) + } + + pub fn log(&self, machine: &MachineIdentifier, state: &MachineState) -> io::Result<()> { + let timestamp = OffsetDateTime::now_utc().unix_timestamp(); + let line = AuditLogLine { timestamp, machine: machine.clone(), state: state.clone() }; + + let mut guard = self.writer.lock().unwrap(); + let mut writer: &mut LineWriter = &mut *guard; + + let mut ser = Serializer::new(&mut writer); + line.serialize(&mut ser).expect("failed to serialize audit log line"); + writer.write("\n".as_bytes())?; + Ok(()) + } +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 53e0a78..3dbf7b9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,6 +50,7 @@ pub struct Config { pub init_connections: Box<[InitiatorConn]>, pub db_path: PathBuf, + pub auditlog_path: PathBuf, pub roles: HashMap, @@ -136,6 +137,7 @@ impl Default for Config { }, ]), + auditlog_path: PathBuf::from("/var/log/bffh/audit.log"), db_path: PathBuf::from("/run/bffh/database"), roles: HashMap::new(), diff --git a/src/db/machine.rs b/src/db/machine.rs index 7e371c4..a9353ae 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -11,6 +11,7 @@ use crate::db::user::UserId; pub mod internal; use internal::Internal; +use crate::audit::AuditLog; pub type MachineIdentifier = String; pub type Priority = u64; @@ -73,11 +74,13 @@ impl MachineState { } } -pub fn init(log: Logger, _config: &Config, env: Arc) -> Result { +pub fn init(log: Logger, config: &Config, env: Arc) -> Result { let mut flags = lmdb::DatabaseFlags::empty(); //flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); let machdb = env.create_db(Some("machines"), flags)?; debug!(&log, "Opened machine db successfully."); - Ok(Internal::new(log, env, machdb)) + let audit = AuditLog::new(config)?; + + Ok(Internal::new(log, audit, env, machdb)) } diff --git a/src/db/machine/internal.rs b/src/db/machine/internal.rs index 99f36fa..c20b062 100644 --- a/src/db/machine/internal.rs +++ b/src/db/machine/internal.rs @@ -3,20 +3,22 @@ use std::sync::Arc; use slog::Logger; use lmdb::{Environment, Transaction, RwTransaction, Cursor, RoTransaction}; +use crate::audit::AuditLog; use super::{MachineIdentifier, MachineState}; use crate::error::Result; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Internal { log: Logger, + audit: AuditLog, env: Arc, db: lmdb::Database, } impl Internal { - pub fn new(log: Logger, env: Arc, db: lmdb::Database) -> Self { - Self { log, env, db } + pub fn new(log: Logger, audit: AuditLog, env: Arc, db: lmdb::Database) -> Self { + Self { log, audit, env, db } } pub fn get_with_txn(&self, txn: &T, id: &String) @@ -47,6 +49,7 @@ impl Internal { } pub fn put(&self, id: &MachineIdentifier, status: &MachineState) -> Result<()> { + self.audit.log(id, status)?; let mut txn = self.env.begin_rw_txn()?; self.put_with_txn(&mut txn, id, status)?; txn.commit().map_err(Into::into) diff --git a/src/machine.rs b/src/machine.rs index 2308303..d644592 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -295,7 +295,7 @@ impl Inner { Box::pin(self.state.signal_cloned().dedupe_cloned()) } - fn replace_state(&mut self, new_state: MachineState) -> MachineState { + fn replace_state(&self, new_state: MachineState) -> MachineState { self.db.put(&self.id, &new_state); self.state.replace(new_state) } diff --git a/src/main.rs b/src/main.rs index 81b756e..9857ef8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ mod actor; mod initiator; mod space; +mod audit; + use clap::{App, Arg}; use std::io; From 9fbacc171ba124970d630fce33dc41ff8b1bfd2c Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 17 Jan 2022 19:54:53 +0100 Subject: [PATCH 09/37] Adds an example python process actor --- examples/actor.py | 179 ++++++++++++++++++++++++++++++++++++++++++++ examples/bffh.dhall | 26 ++++++- 2 files changed, 203 insertions(+), 2 deletions(-) create mode 100755 examples/actor.py diff --git a/examples/actor.py b/examples/actor.py new file mode 100755 index 0000000..489260c --- /dev/null +++ b/examples/actor.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +import sys +import argparse + +def on_free(args, actor_name): + """ + Function called when the state of the connected machine changes to Free + again + """ + if args.verbose > 2: + print("on_free called!") + + if actor_name == "DoorControl1": + # Do whatever you want to do in case `DoorControl1` is returned back to free. + # Keep in mind that process actors should return quickly to not miss + # updates, so if you need to do things that take a while fork a new + # process e.g. with the `subprocess` Module + print("I'm locking door 1!") + pass + elif actor_name == "DoorControl2": + print("I'm locking door 2!") + pass # Close a different door + else: + if not args.quiet: + print("process called with unknown id %s for state `Free`" % actor_name) + # It's a good idea to exit with an error code in case something + # unexpected happens. + # The process module logs everything printed to stdout by actors into + # the server log, but marks them as `Error` in case the actor process + # exits with a code != 0, making debugging somewhat easier. + exit(-1) + +def on_use(args, actor_name, user_id): + """ + Function called when an user takes control of the connected machine + + user_id contains the UID of the user now using the machine + """ + if args.verbose > 2: + print("on_use called!") + if actor_name == "DoorControl1": + print("I'm opening door 1 for 10 seconds!") + pass # Open door one + elif actor_name == "DoorControl2": + print("I'm opening door 2 for 10 seconds!") + pass # Open a different door + else: + if not args.quiet: + print("process called with unknown id %s for state `InUse`" % actor_name) + # It's a good idea to exit with an error code in case something + # unexpected happens. + # The process module logs everything printed to stdout by actors into + # the server log, but marks them as `Error` in case the actor process + # exits with a code != 0, making debugging somewhat easier. + exit(-1) + +def on_tocheck(args, actor_name, user_id): + """ + Function called when an user returns control and the connected machine is + configured to go to state `ToCheck` instead of `Free` in that case. + + user_id contains the UID of the manager expected to check the machine. + The user that used the machine beforehand has to be taken from the last + user field using the API (via e.g. the mobile app) + """ + if args.verbose > 2: + print("on_tocheck called!") + if not args.quiet: + print("process called with unexpected combo id %s and state 'ToCheck'" % actor_name) + exit(-1) + +def on_blocked(args, actor_name, user_id): + """ + Function called when an manager marks the connected machine as `Blocked` + + user_id contains the UID of the manager that blocked the machine + """ + if args.verbose > 2: + print("on_blocked called!") + if not args.quiet: + print("process called with unexpected combo id %s and state 'Blocked'" % actor_name) + exit(-1) + +def on_disabled(args, actor_name): + """ + Function called when the connected machine is marked `Disabled` + """ + if not args.quiet: + print("process called with unexpected combo id %s and state 'Disabled'" % actor_name) + exit(-1) + +def on_reserve(args, actor_name, user_id): + """ + Function called when the connected machine has been reserved by somebody. + + user_id contains the UID of the reserving user. + """ + if not args.quiet: + print("process called with unexpected combo id %s and state 'Reserved'" % actor_name) + exit(-1) + + +def main(args): + """ + Python example actor + + This is an example how to use the `process` actor type to run a Python script. + """ + + if args.verbose is not None: + if args.verbose == 1: + print("verbose output enabled") + elif args.verbose == 2: + print("loud output enabled!") + elif args.verbose == 3: + print("LOUD output enabled!!!") + elif args.verbose > 4: + print("Okay stop you're being ridiculous.") + sys.exit(-2) + else: + args.verbose = 0 + + # You could also check the actor name here and call different functions + # depending on that variable instead of passing it to the state change + # methods. + + new_state = args.state + if new_state == "free": + on_free(args, args.name) + elif new_state == "inuse": + on_use(args, args.name, args.userid) + elif new_state == "tocheck": + on_tocheck(args, args.name, args.userid) + elif new_state == "blocked": + on_blocked(args, args.name, args.userid) + elif new_state == "disabled": + on_disabled(args, args.name) + elif new_state == "reserved": + on_reserve(args, args.name, args.userid) + else: + print("Process actor called with unknown state %s" % new_state) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + # Parameters are passed to the Process actor as follows: + # 1. the contents of params.args, split by whitespace as separate args + # 2. the configured id of the actor (e.g. "DoorControl1") + # 3. the new state as one of [free|inuse|tocheck|blocked|disabled|reserved] + + parser.add_argument("-q", "--quiet", help="be less verbose", action="store_true") + parser.add_argument("-v", "--verbose", help="be more verbose", action="count") + + parser.add_argument("name", + help="name of this actor as configured in bffh.dhall" + ) + + # We parse the new state using subparsers so that we only require a userid + # in case it's a state that sets one. + subparsers = parser.add_subparsers(required=True, dest="state") + + parser_free = subparsers.add_parser("free") + + parser_inuse = subparsers.add_parser("inuse") + parser_inuse.add_argument("userid", help="The user that is now using the machine") + + parser_tocheck = subparsers.add_parser("tocheck") + parser_tocheck.add_argument("userid", help="The user that should go check the machine") + + parser_blocked = subparsers.add_parser("blocked") + parser_blocked.add_argument("userid", help="The user that marked the machine as blocked") + + parser_disabled = subparsers.add_parser("disabled") + + parser_reserved = subparsers.add_parser("reserved") + parser_reserved.add_argument("userid", help="The user that reserved the machine") + + args = parser.parse_args() + main(args) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index a7a5f88..dcf8d5c 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -180,6 +180,28 @@ args = "your ad could be here" } }, + + DoorControl1 = { + -- This actor calls the actor.py script in examples/ + -- It gets passed it's own name, so you can have several actors + -- from the same script. + -- If you need to pass more arguments to the command you can use the `args` key in + -- `params` as is done with the actor `Bash` + module = "Process", + params = { cmd = "./examples/actor.py", } + }, + DoorControl2 = { + module = "Process", + params = { cmd = "./examples/actor.py", } + }, + DoorControl3 = { + -- This is an example for how it looks like if an actor is misconfigured. + -- the actor.py doesn't know anything about DoorControl3 and, if this actor is enabled, + -- will return with an error showing up in the server logs. + module = "Process", + params = { cmd = "./examples/actor.py", } + }, + Bash2 = { module = "Process", params = { cmd = "./examples/actor.sh" , args = "this is a different one" }}, FailBash = { module = "Process", params = { cmd = "./examples/fail-actor.sh" }} }, @@ -188,7 +210,7 @@ -- Actors need to be connected to machines to be useful. A machine can be connected to multiple actors, but one -- actor can only be connected to one machine. actor_connections = [ - { machine = "Testmachine", actor = "Shelly1234" }, + { machine = "Testmachine", actor = "DoorControl1" }, { machine = "Another", actor = "Bash" }, { machine = "Yetmore", actor = "Bash2" }, { machine = "Yetmore", actor = "FailBash"} @@ -207,4 +229,4 @@ -- The below is once again how you have to define *no* initiators. --init_connections = [] : List { machine : Text, initiator : Text } init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] -} \ No newline at end of file +} From 73162d278f3ca33e6b399034970a5f37333c18c1 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 17 Jan 2022 20:00:40 +0100 Subject: [PATCH 10/37] Adds an example usecase for args --- examples/bffh.dhall | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index dcf8d5c..f1b982b 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -188,7 +188,9 @@ -- If you need to pass more arguments to the command you can use the `args` key in -- `params` as is done with the actor `Bash` module = "Process", - params = { cmd = "./examples/actor.py", } + -- the `args` are passed in front of all other parameters so they are best suited to + -- optional parameters like e.g. the verboseness + params = { cmd = "./examples/actor.py", args = "-vvv" } }, DoorControl2 = { module = "Process", From 89b292a8ac502f969798b354331a72e657456bcc Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Fri, 4 Feb 2022 14:20:15 +0100 Subject: [PATCH 11/37] Disclose machines that are used by yourself --- schema | 2 +- src/api/machines.rs | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/schema b/schema index 5b88b64..18ed9c2 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit 5b88b6446c55833a1c1340cd5b7fe9fcb992b872 +Subproject commit 18ed9c2ae6a221f57d19e255165c7ebc4508e9af diff --git a/src/api/machines.rs b/src/api/machines.rs index f754d52..97a0d87 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -52,16 +52,6 @@ impl machines::Server for Machines { let session = self.session.borrow(); if session.deref().is_some() { let v: Vec<(String, crate::machine::Machine)> = self.network.machines.iter() - .filter(|(_name, machine)| { - let required_disclose = &machine.desc.privs.disclose; - for perm_rule in session.as_ref().unwrap().perms.iter() { - if perm_rule.match_perm(required_disclose) { - return true; - } - } - - false - }) .map(|(n,m)| (n.clone(), m.clone())) .collect(); @@ -70,8 +60,34 @@ impl machines::Server for Machines { let user = &session.as_ref().unwrap().authzid; let permissions = &session.as_ref().unwrap().perms; - let mut machines = results.get().init_machine_list(v.len() as u32); - for (i, (id, machine)) in v.into_iter().enumerate() { + let mut filtered_v = Vec::with_capacity(v.len()); + for (id, machine) in v.into_iter() { + match machine.get_status().await { + // Always show a machine if they're in use by myself + Status::InUse(ref bywho) => + if bywho.is_some() && bywho.as_ref().filter(|bywho| *bywho == user).is_some() + { + filtered_v.push((id, machine)); + } + Status::Reserved(ref bywho) => if bywho == user { + filtered_v.push((id, machine)); + } + + // The rest depends on the actual priviledges below + _ => { + let required_disclose = &machine.desc.privs.disclose; + if session.as_ref().unwrap().perms.iter() + .any(|rule| rule.match_perm(required_disclose)) + { + filtered_v.push((id, machine)); + } + } + } + + } + + let mut machines = results.get().init_machine_list(filtered_v.len() as u32); + for (i, (id, machine)) in filtered_v.into_iter().enumerate() { let mut builder = machines.reborrow().get(i as u32); fill_machine_builder( &mut builder, From 2777645205c55d3a37148103e679b167aff3bbec Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 14 Feb 2022 19:56:39 +0100 Subject: [PATCH 12/37] Make MQTT client try to reconnect on connection lost or disconnect --- src/actor.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/actor.rs b/src/actor.rs index ec29c36..8dcd5d6 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -129,7 +129,19 @@ impl Actuator for Dummy { pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { let mut map = HashMap::new(); - let mqtt = AsyncClient::new(config.mqtt_url.clone())?; + let mut mqtt = AsyncClient::new(config.mqtt_url.clone())?; + let dlog = log.clone(); + mqtt.set_disconnected_callback(move |c, prop, reason| { + error!(dlog, "got Disconnect({}) message from MQTT Broker: {:?}", reason, prop); + let tok = c.reconnect(); + smol::block_on(tok); + }); + let dlog = log.clone(); + mqtt.set_connection_lost_callback(move |c| { + error!(dlog, "lost connection to MQTT Broker!"); + let tok = c.reconnect(); + smol::block_on(tok); + }); let tok = mqtt.connect(paho_mqtt::ConnectOptions::new()); smol::block_on(tok)?; From 337e8aa5634f5c73be05c7992da11b941e0efe53 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Tue, 15 Feb 2022 17:43:50 +0100 Subject: [PATCH 13/37] Sets a 20 second MQTT keepalive intervall --- src/actor.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/actor.rs b/src/actor.rs index 8dcd5d6..07f6beb 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -3,6 +3,7 @@ use std::task::{Poll, Context}; use std::sync::Mutex; use std::collections::HashMap; use std::future::Future; +use std::time::Duration; use futures::{future::BoxFuture, Stream}; use futures::channel::mpsc; @@ -142,7 +143,11 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { let tok = c.reconnect(); smol::block_on(tok); }); - let tok = mqtt.connect(paho_mqtt::ConnectOptions::new()); + let conn_opts = paho_mqtt::ConnectOptionsBuilder::new() + .keep_alive_interval(Duration::from_secs(20)) + .clean_session(false) + .finalize(); + let tok = mqtt.connect(conn_opts); smol::block_on(tok)?; let actuators = config.actors.iter() From a7d30930ba30d7c157100e09bdef1f5bb967f9c9 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Tue, 15 Feb 2022 18:28:58 +0100 Subject: [PATCH 14/37] Don't default to MQTT 3.1 either --- src/actor.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/actor.rs b/src/actor.rs index 07f6beb..34dab10 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -145,7 +145,6 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { }); let conn_opts = paho_mqtt::ConnectOptionsBuilder::new() .keep_alive_interval(Duration::from_secs(20)) - .clean_session(false) .finalize(); let tok = mqtt.connect(conn_opts); smol::block_on(tok)?; From 07e181b107e4942b35ee3473a2b3cc51ab05b0f9 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 11:38:53 +0100 Subject: [PATCH 15/37] Update dependencies --- Cargo.lock | 490 ++++++++++++++++++++++++----------------------------- Cargo.toml | 4 +- 2 files changed, 227 insertions(+), 267 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7996e5..4507936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,18 +34,18 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c96c3d1062ea7101741480185a6a1275eab01cbe8b20e378d1311bc056d2e08" +checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36" dependencies = [ "unicode-width", ] [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] @@ -62,6 +62,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "async-channel" version = "1.6.1" @@ -119,9 +125,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" dependencies = [ "event-listener", ] @@ -139,9 +145,9 @@ dependencies = [ [[package]] name = "async-process" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b21b63ab5a0db0369deb913540af2892750e42d949faacc7a61495ac418a1692" +checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6" dependencies = [ "async-io", "blocking", @@ -167,15 +173,15 @@ dependencies = [ [[package]] name = "async-task" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" +checksum = "677d306121baf53310a3fd342d88dc0824f6bbeace68347593658525565abee8" [[package]] name = "async-trait" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" dependencies = [ "proc-macro2", "quote", @@ -201,9 +207,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" @@ -252,12 +258,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake2b_simd" -version = "0.5.11" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.7.2", "constant_time_eq", ] @@ -279,7 +285,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] @@ -293,9 +299,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427" dependencies = [ "async-channel", "async-task", @@ -307,9 +313,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.8.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "byte-tools" @@ -325,15 +331,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cache-padded" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "capnp" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9b8a7119420b5279ddc2b4ee553ee15bcf4605df6135a26f03ffe153bee97c" +checksum = "16c262726f68118392269a3f7a5546baf51dcfe5cb3c3f0957b502106bf1a065" [[package]] name = "capnp-futures" @@ -342,7 +348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a15248c8facb189a3c5fee74fbf1ff3adc134261d27da663b89c7d19ebaf983" dependencies = [ "capnp", - "futures 0.3.16", + "futures 0.3.21", ] [[package]] @@ -353,23 +359,23 @@ checksum = "4c4f17f96f68f2c1168ed7105d9e5cb4a095a5bef3578aee0f9c0644b85ca95e" dependencies = [ "capnp", "capnp-futures", - "futures 0.3.16", + "futures 0.3.21", ] [[package]] name = "capnpc" -version = "0.14.4" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47bce811162518b5c38f746ed584bd2922ae7bb560ef64f230d2e4ee0d111fe" +checksum = "c7ed9b80f792ac01a8b328ccbc509c2bd756fb5dec18af0163e7963dde23c0b5" dependencies = [ "capnp", ] [[package]] name = "cc" -version = "1.0.69" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cexpr" @@ -392,24 +398,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time 0.1.43", - "winapi", -] - [[package]] name = "clang-sys" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cf2cc85830eae84823884db23c5306442a6c3d5bfd3beb2f2a2c829faa1816" +checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" dependencies = [ "glob", "libc", @@ -418,9 +411,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -433,9 +426,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.45" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" dependencies = [ "cc", ] @@ -466,9 +459,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -476,35 +469,25 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if 1.0.0", "lazy_static", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dhall" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a7d7b648a24d2edd4ba9f31fe42c831080522d1cf35bca3522887ba0d802745" +checksum = "49483fe619f146762fa89c7c840fa1fbf8b534cab0b59622106e8f434bbd4bca" dependencies = [ "abnf_to_pest", "annotate-snippets", "elsa", "hex", + "home", "itertools", "lazy_static", "once_cell", @@ -521,9 +504,9 @@ dependencies = [ [[package]] name = "dhall_proc_macros" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64ba6f41d9b223e2e1d7c97a1145a1aa03e57d65e1c9c2baa29f194caf322c9" +checksum = "df7c81d16870879ef530b07cef32bc6088f98937ab4168106cc8e382a05146bf" dependencies = [ "proc-macro2", "quote", @@ -545,7 +528,7 @@ dependencies = [ "clap", "easy-parallel", "flexbuffers", - "futures 0.3.16", + "futures 0.3.21", "futures-signals", "futures-test", "futures-util", @@ -568,7 +551,7 @@ dependencies = [ "slog-term", "smol", "tempfile", - "time 0.3.5", + "time", "toml", "uuid", "walkdir", @@ -589,7 +572,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] @@ -627,9 +610,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "easy-parallel" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd4afd79212583ff429b913ad6605242ed7eec277e950b1438f300748f948f4" +checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" [[package]] name = "either" @@ -639,9 +622,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "elsa" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848344296205756adc00ab3bec02658da0f72eaa1461474aa2d51d64311876a5" +checksum = "348b451f939db7ec8fb01068d419a2354d76386596d5e4e025ab50bdd9e5c480" dependencies = [ "stable_deref_trait", ] @@ -661,9 +644,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" [[package]] name = "fake-simd" @@ -673,9 +656,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", ] @@ -711,9 +694,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" dependencies = [ "futures-channel", "futures-core", @@ -726,9 +709,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" dependencies = [ "futures-core", "futures-sink", @@ -746,9 +729,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" [[package]] name = "futures-core-preview" @@ -758,9 +741,9 @@ checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" [[package]] name = "futures-executor" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" dependencies = [ "futures-core", "futures-task", @@ -781,9 +764,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" [[package]] name = "futures-io-preview" @@ -808,12 +791,10 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ - "autocfg", - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -835,23 +816,24 @@ dependencies = [ [[package]] name = "futures-signals" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7c31ebfcb97c87ac64db66a2d4d6ae71b487d5a8ef631c80c3d7e65a772a8c" +checksum = "439b5920b9dc57ddd4a959b4b1c0eb9a68ded001f1e331d422b0a68ef2928c5f" dependencies = [ "discard", "futures-channel", "futures-core", "futures-util", + "log", "pin-project", "serde", ] [[package]] name = "futures-sink" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" [[package]] name = "futures-sink-preview" @@ -861,15 +843,15 @@ checksum = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" [[package]] name = "futures-task" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" [[package]] name = "futures-test" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a5ac667be097531d74ff9fff9c9da7820dd63afd2312bb9c6f589211ae32080" +checksum = "8c3e9379dbbfb35dd6df79e895d73c0f75558827fe68eb853b858ff417a8ee98" dependencies = [ "futures-core", "futures-executor", @@ -894,11 +876,10 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" dependencies = [ - "autocfg", "futures 0.1.31", "futures-channel", "futures-core", @@ -909,8 +890,6 @@ dependencies = [ "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] @@ -970,9 +949,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", "version_check", @@ -980,9 +959,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if 1.0.0", "libc", @@ -997,18 +976,18 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "gsasl-sys" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89192b27c2356690cddc81ef17438cede69f0e6794a645debe579de3a6bb835" +checksum = "cd377f206007e661591b4c6a6ba9907a75ae24e2a9ef40f3d0750a1db6bd5dbd" dependencies = [ "bindgen", ] [[package]] name = "half" -version = "1.7.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" @@ -1031,6 +1010,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi", +] + [[package]] name = "humantime" version = "1.3.0" @@ -1053,9 +1041,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", "hashbrown", @@ -1063,9 +1051,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] @@ -1087,9 +1075,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" dependencies = [ "wasm-bindgen", ] @@ -1112,7 +1100,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "bitflags", "cfg-if 1.0.0", "ryu", @@ -1121,15 +1109,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.101" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libloading" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ "cfg-if 1.0.0", "winapi", @@ -1149,9 +1137,9 @@ dependencies = [ [[package]] name = "lmdb-rkv-sys" -version = "0.11.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b27470ac25167b3afdfb6af8fcd3bc1be67de50ffbdaf4073378cfded6ae24a5" +checksum = "61b9ce6b3be08acefa3003c57b7565377432a89ec24476bbe72e11d101f852fe" dependencies = [ "cc", "libc", @@ -1196,30 +1184,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", @@ -1227,19 +1196,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" +checksum = "720d3ea1055e4e4574c0c0b0f8c3fd4f24c4cdaf465948206dea090b57b526ad" dependencies = [ - "derivative", "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" +checksum = "0d992b768490d7fe0d8586d9b5745f6c49f557da6d81dc982b1d167ad4edbb21" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1248,10 +1216,19 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.8.0" +name = "num_threads" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "opaque-debug" @@ -1270,7 +1247,7 @@ name = "paho-mqtt" version = "0.8.0" source = "git+https://github.com/dequbed/paho.mqtt.rust.git?branch=master#14ec804ecf284564ee71b04345d1fdf1f75571df" dependencies = [ - "futures 0.3.16", + "futures 0.3.21", "futures-timer", "libc", "log", @@ -1372,18 +1349,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" dependencies = [ "proc-macro2", "quote", @@ -1392,9 +1369,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" [[package]] name = "pin-utils" @@ -1404,15 +1381,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] name = "polling" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1423,9 +1400,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "pretty" @@ -1438,9 +1415,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.0.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", "toml", @@ -1478,17 +1455,11 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] @@ -1501,23 +1472,22 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] @@ -1539,15 +1509,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -1610,9 +1571,9 @@ dependencies = [ [[package]] name = "rsasl" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165e25c96193ef9f5eeb7e68e868c5543b30d6a68bef5470be45ae2416466817" +checksum = "00658df565e044eeb2d0358bf370eb597c7089daf7d5021aa142b91f2524f641" dependencies = [ "discard", "gsasl-sys", @@ -1621,9 +1582,9 @@ dependencies = [ [[package]] name = "rust-argon2" -version = "0.8.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +checksum = "b50162d19404029c1ceca6f6980fe40d45c8b369f6f44446fa14bb39573b5bb9" dependencies = [ "base64", "blake2b_simd", @@ -1661,15 +1622,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "same-file" @@ -1692,9 +1653,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.130" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] @@ -1711,9 +1672,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -1722,9 +1683,9 @@ dependencies = [ [[package]] name = "serde_dhall" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1393976875f3080d8cd1ff54083129e2e6a30d7a62582b67c3eb5924789e8e75" +checksum = "5212ade489a28b0763867bb60a15169e41d855f3c04c0b26aa3bea50f9770e12" dependencies = [ "dhall", "dhall_proc_macros", @@ -1735,9 +1696,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.74" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", @@ -1758,9 +1719,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.6" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9204c41a1597a8c5af23c82d1c921cb01ec0a4c59e07a9c7306062829a3903f3" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -1777,9 +1738,9 @@ checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook" -version = "0.3.9" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" +checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d" dependencies = [ "libc", "signal-hook-registry", @@ -1796,9 +1757,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "slog" @@ -1820,15 +1781,15 @@ dependencies = [ [[package]] name = "slog-term" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c1e7e5aab61ced6006149ea772770b84a0d16ce0f7885def313e4829946d76" +checksum = "87d29185c55b7b258b4f120eab00f48557d4d9bc814f41713f449d35b0f8977c" dependencies = [ "atty", - "chrono", "slog", "term", "thread_local", + "time", ] [[package]] @@ -1851,9 +1812,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", "winapi", @@ -1885,9 +1846,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.75" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", @@ -1913,13 +1874,13 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if 1.0.0", + "fastrand", "libc", - "rand", "redox_syscall", "remove_dir_all", "winapi", @@ -1956,18 +1917,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283d5230e63df9608ac7d9691adc1dfb6e701225436eb64d0b9a7f0a5a04f6ec" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3884228611f5cd3608e2d409bf7dce832e4eb3135e3f11addbd7e41bd68e71" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -1976,38 +1937,37 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ "once_cell", ] [[package]] name = "time" -version = "0.1.43" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" dependencies = [ + "itoa", "libc", - "winapi", + "num_threads", + "serde", + "time-macros", ] [[package]] -name = "time" -version = "0.3.5" +name = "time-macros" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" -dependencies = [ - "libc", - "serde", -] +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" [[package]] name = "tinyvec" -version = "1.3.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" dependencies = [ "tinyvec_macros", ] @@ -2035,9 +1995,9 @@ checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" [[package]] name = "typenum" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" @@ -2047,9 +2007,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unicode-bidi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" [[package]] name = "unicode-normalization" @@ -2062,9 +2022,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" @@ -2108,9 +2068,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" @@ -2137,9 +2097,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -2147,9 +2107,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" dependencies = [ "bumpalo", "lazy_static", @@ -2162,9 +2122,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2172,9 +2132,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" dependencies = [ "proc-macro2", "quote", @@ -2185,15 +2145,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index f220ea2..312d48e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ toml = "0.5.8" flexbuffers = "2.0.0" bincode = "2.0.0-dev" -serde_dhall = { version = "0.10.1", default-features = false } +serde_dhall = { version = "0.11", default-features = false } # Audit log uses serde_json for now serde_json = "1.0.74" time = { version = "0.3.5", features = ["serde"] } @@ -62,7 +62,7 @@ async-trait = "0.1.51" lazy_static = "1.4.0" -rust-argon2 = "0.8.3" +rust-argon2 = "1.0.0" rand = "0.8.4" async-channel = "1.6.1" From 53cdfeda5d337c54f413bbf000071365cc82021f Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 14:00:00 +0100 Subject: [PATCH 16/37] Fix warnings --- src/actor.rs | 77 +++++++++++++++++++++++++++++++++++---------- src/api.rs | 6 ++-- src/api/auth.rs | 8 ++--- src/api/machine.rs | 12 +++---- src/api/machines.rs | 2 +- src/api/users.rs | 6 ++-- src/audit.rs | 2 +- src/connection.rs | 16 +++++----- src/db.rs | 9 +++--- src/db/machine.rs | 2 +- src/initiator.rs | 6 ++-- src/machine.rs | 16 +++++----- src/server.rs | 4 +-- 13 files changed, 105 insertions(+), 61 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 34dab10..a295f4f 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -2,20 +2,24 @@ use std::pin::Pin; use std::task::{Poll, Context}; use std::sync::Mutex; use std::collections::HashMap; +use std::convert::TryFrom; use std::future::Future; use std::time::Duration; use futures::{future::BoxFuture, Stream}; use futures::channel::mpsc; use futures_signals::signal::Signal; +use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions, OptionError, Transport}; use crate::db::machine::MachineState; use crate::config::Config; use crate::error::Result; use crate::network::ActorMap; -use paho_mqtt::AsyncClient; use slog::Logger; +use url::Url; +use crate::Error; +use crate::Error::{BadConfiguration, MQTTConnectionError}; pub trait Actuator { fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; @@ -130,24 +134,65 @@ impl Actuator for Dummy { pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { let mut map = HashMap::new(); - let mut mqtt = AsyncClient::new(config.mqtt_url.clone())?; + let mqtt_url = Url::parse(config.mqtt_url.as_str())?; + let mut mqttoptions = MqttOptions::try_from(mqtt_url) + .map_err(|opt| Error::Boxed(Box::new(opt)))?; + mqttoptions.set_keep_alive(Duration::from_secs(20)); + + let (mut mqtt, mut eventloop) = AsyncClient::new(mqttoptions, 256); let dlog = log.clone(); - mqtt.set_disconnected_callback(move |c, prop, reason| { - error!(dlog, "got Disconnect({}) message from MQTT Broker: {:?}", reason, prop); - let tok = c.reconnect(); - smol::block_on(tok); - }); + let mut eventloop = smol::block_on(async move { + match eventloop.poll().await { + Ok(Event::Incoming(Incoming::Connect(connect))) => {}, + Ok(e) => { + warn!(dlog, "Got unexpected mqtt event {:?}", e); + } + Err(connerror) => { + error!(dlog, "MQTT connection failed: {:?}", &connerror); + return Err(MQTTConnectionError(connerror)); + } + } + + Ok(eventloop) + })?; let dlog = log.clone(); - mqtt.set_connection_lost_callback(move |c| { - error!(dlog, "lost connection to MQTT Broker!"); - let tok = c.reconnect(); - smol::block_on(tok); + smol::spawn(async move { + let mut fault = false; + loop { + match eventloop.poll().await { + Ok(_) => { + fault = false; + // TODO: Handle incoming MQTT messages + } + Err(ConnectionError::Cancel) | + Err(ConnectionError::StreamDone) | + Err(ConnectionError::RequestsDone) => { + // Normal exit + info!(dlog, "MQTT request queue closed, stopping client."); + return; + } + Err(ConnectionError::Timeout(_)) => { + error!(dlog, "MQTT operation timed out!"); + warn!(dlog, "MQTT client will continue, but messages may have been lost.") + // Timeout does not close the client + } + Err(ConnectionError::Io(e)) if fault => { + error!(dlog, "MQTT recurring IO error, closing client: {}", e); + // Repeating IO errors close client. Any Ok() in between resets fault to false. + return; + } + Err(ConnectionError::Io(e)) => { + fault = true; + error!(dlog, "MQTT encountered IO error: {}", e); + // *First* IO error does not close the client. + } + Err(e) => { + error!(dlog, "MQTT client encountered unhandled error: {:?}", e); + return; + } + } + } }); - let conn_opts = paho_mqtt::ConnectOptionsBuilder::new() - .keep_alive_interval(Duration::from_secs(20)) - .finalize(); - let tok = mqtt.connect(conn_opts); - smol::block_on(tok)?; let actuators = config.actors.iter() .map(|(k,v)| (k, load_single(log, k, &v.module, &v.params, mqtt.clone()))) diff --git a/src/api.rs b/src/api.rs index 797b692..0918dde 100644 --- a/src/api.rs +++ b/src/api.rs @@ -6,13 +6,13 @@ use slog::Logger; use std::sync::Arc; -use capnp::capability::{Params, Results, Promise}; +use capnp::capability::{Promise}; use crate::schema::connection_capnp; use crate::connection::Session; use crate::db::Databases; -use crate::db::user::UserId; + use crate::network::Network; @@ -108,7 +108,7 @@ impl connection_capnp::bootstrap::Server for Bootstrap { _: GetAPIVersionParams, mut results: GetAPIVersionResults ) -> Promise<(), capnp::Error> { - let mut builder = results.get(); + let builder = results.get(); let mut builder = builder.init_version(); builder.set_major(API_VERSION_MAJOR); builder.set_minor(API_VERSION_MINOR); diff --git a/src/api/auth.rs b/src/api/auth.rs index eb43429..4269bbb 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::rc::Rc; use std::cell::RefCell; -use std::ops::Deref; + use slog::Logger; @@ -22,14 +22,14 @@ use rsasl::{ use serde::{Serialize, Deserialize}; -use capnp::capability::{Params, Results, Promise}; +use capnp::capability::{Promise}; use crate::api::Session; pub use crate::schema::authenticationsystem_capnp as auth_system; use crate::db::Databases; -use crate::db::pass::PassDB; -use crate::db::user::{Internal as UserDB, UserId, User}; + +use crate::db::user::{Internal as UserDB, User}; use crate::db::access::AccessControl as AccessDB; pub struct AppData { diff --git a/src/api/machine.rs b/src/api/machine.rs index ab30a7f..c2774b3 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -1,12 +1,12 @@ -use std::sync::Arc; + use std::time::Duration; use capnp::capability::Promise; -use capnp::Error; + use futures::FutureExt; -use crate::db::access::{PrivilegesBuf, PermRule, Perms}; +use crate::db::access::{Perms}; use crate::db::user::UserId; use crate::db::machine::{Status, MachineState}; use crate::machine::Machine as NwMachine; @@ -36,7 +36,7 @@ impl info::Server for Machine { let perms = self.perms.clone(); let f = async move { if perms.manage { - let mut builder = results.get(); + let builder = results.get(); let mut extinfo = builder.init_machine_info_extended(); let guard = machine.lock().await; @@ -83,7 +83,7 @@ impl info::Server for Machine { fn get_reservation_list( &mut self, _: info::GetReservationListParams, - mut results: info::GetReservationListResults, + _results: info::GetReservationListResults, ) -> Promise<(), capnp::Error> { Promise::err(capnp::Error::unimplemented("Reservations are unavailable".to_string())) } @@ -91,7 +91,7 @@ impl info::Server for Machine { fn get_property_list( &mut self, _: info::GetPropertyListParams, - mut results: info::GetPropertyListResults, + _results: info::GetPropertyListResults, ) -> Promise<(), capnp::Error> { Promise::err(capnp::Error::unimplemented("Extended Properties are unavailable".to_string())) } diff --git a/src/api/machines.rs b/src/api/machines.rs index 97a0d87..03c0be1 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -13,7 +13,7 @@ use crate::schema::machinesystem_capnp::machine_system; use crate::schema::machinesystem_capnp::machine_system::info as machines; use crate::network::Network; use crate::db::user::UserId; -use crate::db::access::{PermRule, admin_perm, Permission, Perms}; +use crate::db::access::{PermRule, admin_perm, Perms}; use crate::connection::Session; use crate::machine::Machine as NwMachine; diff --git a/src/api/users.rs b/src/api/users.rs index 3258edd..c617821 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -7,8 +7,8 @@ use capnp::capability::Promise; use crate::api::user::User; use crate::connection::Session; -use crate::db::access::{PermRule, Permission}; -use crate::db::user::{UserId, Internal as UserDB}; +use crate::db::access::{Permission}; +use crate::db::user::{Internal as UserDB}; use crate::schema::usersystem_capnp::user_system; use crate::schema::usersystem_capnp::user_system::{info, manage}; use crate::error; @@ -86,7 +86,7 @@ impl manage::Server for Users { match result { Ok(()) => Promise::ok(()), - Err(e) => Promise::err(capnp::Error::failed("User lookup failed: {}".to_string())), + Err(_e) => Promise::err(capnp::Error::failed("User lookup failed: {}".to_string())), } } diff --git a/src/audit.rs b/src/audit.rs index 833bca9..2cdf9ff 100644 --- a/src/audit.rs +++ b/src/audit.rs @@ -2,7 +2,7 @@ use std::fs::{File, OpenOptions}; use std::io; use std::io::{LineWriter, Write}; use std::sync::Mutex; -use std::time::Instant; + use crate::Config; use serde::{Serialize, Deserialize}; use serde_json::Serializer; diff --git a/src/connection.rs b/src/connection.rs index 6072463..769c94a 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,23 +1,23 @@ use std::fmt::Debug; -use std::ops::DerefMut; + use futures::{AsyncRead, AsyncWrite, FutureExt}; use std::future::Future; -use std::io::{IoSlice, IoSliceMut}; -use std::pin::Pin; -use std::rc::Rc; + + + use std::sync::Arc; -use std::task::{Context, Poll}; + use async_rustls::server::TlsStream; use slog::Logger; -use smol::lock::Mutex; + use crate::api::Bootstrap; use crate::error::Result; use capnp_rpc::{rpc_twoparty_capnp, twoparty}; -use futures_util::{pin_mut, ready}; + use smol::io::split; use crate::schema::connection_capnp; @@ -86,7 +86,7 @@ impl ConnectionHandler { pub fn handle(&mut self, stream: TlsStream) -> impl Future> { - let (mut reader, mut writer) = split(stream); + let (reader, writer) = split(stream); let boots = Bootstrap::new(self.log.new(o!()), self.db.clone(), self.network.clone()); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); diff --git a/src/db.rs b/src/db.rs index 5bec845..7581cb8 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use std::path::PathBuf; -use std::str::FromStr; -use std::ops::{Deref, DerefMut}; + + + use slog::Logger; @@ -74,7 +74,6 @@ use lmdb::{ WriteFlags, Cursor, RoCursor, - RwCursor, Iter, }; @@ -359,7 +358,7 @@ mod tests { let db = DB::new(e.env.clone(), ldb); - let adapter = TestAdapter; + let _adapter = TestAdapter; let testdb = TestDB::new(db.clone()); let mut val = "value"; diff --git a/src/db/machine.rs b/src/db/machine.rs index a9353ae..f9158c7 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -75,7 +75,7 @@ impl MachineState { } pub fn init(log: Logger, config: &Config, env: Arc) -> Result { - let mut flags = lmdb::DatabaseFlags::empty(); + let flags = lmdb::DatabaseFlags::empty(); //flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); let machdb = env.create_db(Some("machines"), flags)?; debug!(&log, "Opened machine db successfully."); diff --git a/src/initiator.rs b/src/initiator.rs index 39ca5d8..d4ebda1 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -7,14 +7,14 @@ use smol::Timer; use slog::Logger; -use paho_mqtt::AsyncClient; + use futures::future::BoxFuture; use futures_signals::signal::{Signal, Mutable, MutableSignalCloned}; use crate::machine::Machine; use crate::db::machine::MachineState; -use crate::db::user::{User, UserId, UserData}; +use crate::db::user::{UserId}; use crate::network::InitMap; @@ -90,7 +90,7 @@ impl Future for Initiator { debug!(this.log, "State change blocked"); return Poll::Pending; }, - Poll::Ready(Ok(rt)) => { + Poll::Ready(Ok(_rt)) => { debug!(this.log, "State change returned ok"); // Explicity drop the future let _ = this.state_change_fut.take(); diff --git a/src/machine.rs b/src/machine.rs index d644592..c2d9269 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -3,34 +3,34 @@ use std::iter::FromIterator; use std::sync::Arc; use futures_util::lock::Mutex; use std::path::Path; -use std::task::{Poll, Context}; -use std::pin::Pin; -use std::future::Future; + + + use std::collections::HashMap; use std::fs; use serde::{Serialize, Deserialize}; -use futures::Stream; + use futures::future::BoxFuture; -use futures::channel::{mpsc, oneshot}; + use futures_signals::signal::Signal; use futures_signals::signal::SignalExt; use futures_signals::signal::{Mutable, ReadOnlyMutable}; use slog::Logger; -use crate::error::{Result, Error}; +use crate::error::{Result}; use crate::db::{access, Databases, MachineDB, UserDB}; use crate::db::access::{AccessControl, Perms}; use crate::db::machine::{MachineIdentifier, MachineState, Status}; -use crate::db::user::{User, UserData, UserId}; +use crate::db::user::{UserId}; use crate::Error::Denied; use crate::network::MachineMap; -use crate::space; + use crate::config::deser_option; diff --git a/src/server.rs b/src/server.rs index 7775330..4f24991 100644 --- a/src/server.rs +++ b/src/server.rs @@ -22,7 +22,7 @@ use std::os::unix::io::AsRawFd; use std::path::Path; use async_rustls::TlsAcceptor; use rustls::{Certificate, KeyLogFile, NoClientAuth, PrivateKey, ServerConfig}; -use rustls_pemfile::Item; + use signal_hook::low_level::pipe as sigpipe; use crate::db::Databases; @@ -103,7 +103,7 @@ pub fn serve_api_connections(log: Arc, config: Config, db: Databases, nw } }).collect(); - let local_ex = LocalExecutor::new(); + let _local_ex = LocalExecutor::new(); let network = Arc::new(nw); From 68418161d7d25ef85d8af5ce8dfcdcfe796c7f8a Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 14:00:26 +0100 Subject: [PATCH 17/37] Replaces paho_mqtt with rumqttc --- Cargo.lock | 238 +++++++++++++++++++++++------------------- Cargo.toml | 4 +- src/actor.rs | 2 +- src/error.rs | 28 +++-- src/main.rs | 4 +- src/modules/shelly.rs | 21 ++-- 6 files changed, 169 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4507936..85f06e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,6 +329,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "cache-padded" version = "1.2.0" @@ -424,15 +430,6 @@ dependencies = [ "vec_map", ] -[[package]] -name = "cmake" -version = "0.1.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" -dependencies = [ - "cc", -] - [[package]] name = "concurrent-queue" version = "1.2.2" @@ -536,9 +533,9 @@ dependencies = [ "lazy_static", "libc", "lmdb-rkv", - "paho-mqtt", "rand", "rsasl", + "rumqttc", "rust-argon2", "rustls", "rustls-pemfile", @@ -553,6 +550,7 @@ dependencies = [ "tempfile", "time", "toml", + "url", "uuid", "walkdir", ] @@ -676,6 +674,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -717,28 +721,12 @@ dependencies = [ "futures-sink", ] -[[package]] -name = "futures-channel-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" -dependencies = [ - "futures-core-preview", - "futures-sink-preview", -] - [[package]] name = "futures-core" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" -[[package]] -name = "futures-core-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" - [[package]] name = "futures-executor" version = "0.3.21" @@ -751,29 +739,12 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "futures-executor-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" -dependencies = [ - "futures-core-preview", - "futures-util-preview", - "num_cpus", -] - [[package]] name = "futures-io" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" -[[package]] -name = "futures-io-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" - [[package]] name = "futures-lite" version = "1.12.0" @@ -800,20 +771,6 @@ dependencies = [ "syn", ] -[[package]] -name = "futures-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" -dependencies = [ - "futures-channel-preview", - "futures-core-preview", - "futures-executor-preview", - "futures-io-preview", - "futures-sink-preview", - "futures-util-preview", -] - [[package]] name = "futures-signals" version = "0.3.24" @@ -835,12 +792,6 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" -[[package]] -name = "futures-sink-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" - [[package]] name = "futures-task" version = "0.3.21" @@ -864,16 +815,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "futures-timer" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9eb554aa23143abc64ec4d0016f038caf53bb7cbc3d91490835c54edc96550" -dependencies = [ - "futures-preview", - "pin-utils", -] - [[package]] name = "futures-util" version = "0.3.21" @@ -893,21 +834,6 @@ dependencies = [ "slab", ] -[[package]] -name = "futures-util-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" -dependencies = [ - "futures-channel-preview", - "futures-core-preview", - "futures-io-preview", - "futures-sink-preview", - "memchr", - "pin-utils", - "slab", -] - [[package]] name = "genawaiter" version = "0.99.1" @@ -1019,6 +945,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "humantime" version = "1.3.0" @@ -1173,6 +1110,37 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "mio" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "mqttbytes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7bd39d24e28e1544d74ff5746e322a477e52353c8ba7adcaa83d2e760752853" +dependencies = [ + "bytes", +] + [[package]] name = "nom" version = "5.1.2" @@ -1184,6 +1152,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -1242,28 +1219,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "paho-mqtt" -version = "0.8.0" -source = "git+https://github.com/dequbed/paho.mqtt.rust.git?branch=master#14ec804ecf284564ee71b04345d1fdf1f75571df" -dependencies = [ - "futures 0.3.21", - "futures-timer", - "libc", - "log", - "paho-mqtt-sys", - "thiserror", -] - -[[package]] -name = "paho-mqtt-sys" -version = "0.4.1" -source = "git+https://github.com/dequbed/paho.mqtt.rust.git?branch=master#14ec804ecf284564ee71b04345d1fdf1f75571df" -dependencies = [ - "bindgen", - "cmake", -] - [[package]] name = "parking" version = "2.0.0" @@ -1398,6 +1353,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1580,6 +1541,25 @@ dependencies = [ "libc", ] +[[package]] +name = "rumqttc" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63ee9fd315db8880bf3fd3c20684dee03ca42cdd59b7d5cfdd4378f100a2aa0" +dependencies = [ + "async-channel", + "bytes", + "http", + "log", + "mqttbytes", + "pollster", + "thiserror", + "tokio", + "tokio-rustls", + "url", + "webpki", +] + [[package]] name = "rust-argon2" version = "1.0.0" @@ -1978,6 +1958,44 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + [[package]] name = "toml" version = "0.5.8" diff --git a/Cargo.toml b/Cargo.toml index 312d48e..85541ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,8 +50,8 @@ clap = "2.33.3" rsasl = "1.4.0" #rsasl = { path = "../../rsasl" } -# rumqtt needs tokio which I'm trying to get away from -paho-mqtt = { git = "https://github.com/dequbed/paho.mqtt.rust.git", branch = "master", features = ["build_bindgen"] } +rumqttc = { version = "0.10", features = ["url"] } +url = "2.2.2" #mlua = { version = "0.4", features = ["async", "luajit"] } diff --git a/src/actor.rs b/src/actor.rs index a295f4f..3a898a3 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -192,7 +192,7 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { } } } - }); + }).detach(); let actuators = config.actors.iter() .map(|(k,v)| (k, load_single(log, k, &v.module, &v.params, mqtt.clone()))) diff --git a/src/error.rs b/src/error.rs index e61cb00..8256dfa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,12 +8,11 @@ use rsasl::SaslError; // SpawnError is a somewhat ambigous name, `use as` to make it futures::SpawnError instead. use futures::task as futures_task; -use paho_mqtt::errors as mqtt; - use crate::network; #[derive(Debug)] pub enum Error { + BadConfiguration, TomlDe(toml::de::Error), TomlSer(toml::ser::Error), Dhall(serde_dhall::Error), @@ -25,11 +24,13 @@ pub enum Error { FlexbuffersDe(flexbuffers::DeserializationError), FlexbuffersSer(flexbuffers::SerializationError), FuturesSpawn(futures_task::SpawnError), - MQTT(mqtt::Error), + MQTT(rumqttc::ClientError), + MQTTConnectionError(rumqttc::ConnectionError), BadVersion((u32,u32)), Argon2(argon2::Error), EventNetwork(network::Error), RustTls(rustls::TLSError), + ParseUrl(url::ParseError), Denied, } @@ -70,7 +71,7 @@ impl fmt::Display for Error { write!(f, "Future could not be spawned: {}", e) }, Error::MQTT(e) => { - write!(f, "Paho MQTT encountered an error: {}", e) + write!(f, "MQTT client encountered an error: {:?}", e) }, Error::Argon2(e) => { write!(f, "Argon2 en/decoding failure: {}", e) @@ -87,6 +88,9 @@ impl fmt::Display for Error { Error::RustTls(e) => { write!(f, "TLS Error: {}", e) } + Error::ParseUrl(e) => write!(f, "Failed to parse URL: {}", e), + Error::BadConfiguration => write!(f, "Bad configuration provided"), + Error::MQTTConnectionError(e) => write!(f, "MQTT connection error: {:?}", e), } } } @@ -157,18 +161,30 @@ impl From for Error { } } -impl From for Error { - fn from(e: mqtt::Error) -> Error { +impl From for Error { + fn from(e: rumqttc::ClientError) -> Error { Error::MQTT(e) } } +impl From for Error { + fn from(e: rumqttc::ConnectionError) -> Error { + Error::MQTTConnectionError(e) + } +} + impl From for Error { fn from(e: network::Error) -> Error { Error::EventNetwork(e) } } +impl From for Error { + fn from(e: url::ParseError) -> Error { + Error::ParseUrl(e) + } +} + impl From for Error { fn from(e: argon2::Error) -> Error { Error::Argon2(e) diff --git a/src/main.rs b/src/main.rs index 9857ef8..4ed00a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ use error::Error; use slog::Logger; -use paho_mqtt::AsyncClient; + use crate::config::{ActorConn, Config, InitiatorConn}; const RELEASE: &'static str = env!("BFFHD_RELEASE_STRING"); @@ -101,7 +101,7 @@ fn main() { } else if matches.is_present("check config") { let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall"); match config::read(&PathBuf::from_str(configpath).unwrap()) { - Ok(cfg) => { + Ok(_cfg) => { //TODO: print a normalized version of the supplied config println!("config is valid"); std::process::exit(0); diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index 843e2a3..bd7f102 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -3,14 +3,12 @@ use slog::Logger; use crate::db::machine::Status; -use futures::prelude::*; use futures::future::BoxFuture; +use rumqttc::{AsyncClient, QoS}; use crate::actor::Actuator; use crate::db::machine::MachineState; -use paho_mqtt as mqtt; - /// An actuator for a Shellie connected listening on one MQTT broker /// /// This actuator will toggle the shellie with the given `name`. @@ -19,12 +17,12 @@ use paho_mqtt as mqtt; pub struct Shelly { log: Logger, name: String, - client: mqtt::AsyncClient, + client: AsyncClient, topic: String, } impl Shelly { - pub fn new(log: Logger, name: String, client: mqtt::AsyncClient, params: &HashMap) -> Self { + pub fn new(log: Logger, name: String, client: AsyncClient, params: &HashMap) -> Self { let topic = if let Some(topic) = params.get("topic") { format!("shellies/{}/relay/0/command", topic) } else { @@ -55,8 +53,17 @@ impl Actuator for Shelly { Status::InUse(_) => "on", _ => "off", }; - let msg = mqtt::Message::new(&self.topic.clone(), pl, 0); - let f = self.client.publish(msg).map(|_| ()); + + let elog = self.log.clone(); + let name = self.name.clone(); + let client = self.client.clone(); + let topic = self.topic.clone(); + let f = async move { + let res = client.publish(topic, QoS::AtLeastOnce, false, pl).await; + if let Err(e) = res { + error!(elog,"Shelly actor {} failed to update state: {:?}",name,e,); + } + }; return Box::pin(f); } From e6cb1a958d9d62e5c0d340ea3b64ff77fdc6aa1a Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 14:02:47 +0100 Subject: [PATCH 18/37] Fixing more warnings --- src/actor.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 3a898a3..28fc75a 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -9,7 +9,7 @@ use std::time::Duration; use futures::{future::BoxFuture, Stream}; use futures::channel::mpsc; use futures_signals::signal::Signal; -use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions, OptionError, Transport}; +use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions}; use crate::db::machine::MachineState; use crate::config::Config; @@ -19,7 +19,7 @@ use crate::network::ActorMap; use slog::Logger; use url::Url; use crate::Error; -use crate::Error::{BadConfiguration, MQTTConnectionError}; +use crate::Error::{MQTTConnectionError}; pub trait Actuator { fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; @@ -139,11 +139,11 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { .map_err(|opt| Error::Boxed(Box::new(opt)))?; mqttoptions.set_keep_alive(Duration::from_secs(20)); - let (mut mqtt, mut eventloop) = AsyncClient::new(mqttoptions, 256); + let (mqtt, mut eventloop) = AsyncClient::new(mqttoptions, 256); let dlog = log.clone(); let mut eventloop = smol::block_on(async move { match eventloop.poll().await { - Ok(Event::Incoming(Incoming::Connect(connect))) => {}, + Ok(Event::Incoming(Incoming::Connect(_connect))) => {}, Ok(e) => { warn!(dlog, "Got unexpected mqtt event {:?}", e); } From e9b1ba1f50a9d75dc0d814a521a9dfab2571cfca Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 14:16:46 +0100 Subject: [PATCH 19/37] Use our own MQTT URL dissector so existing configs don't break --- src/actor.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 28fc75a..0122512 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -2,7 +2,6 @@ use std::pin::Pin; use std::task::{Poll, Context}; use std::sync::Mutex; use std::collections::HashMap; -use std::convert::TryFrom; use std::future::Future; use std::time::Duration; @@ -18,8 +17,7 @@ use crate::network::ActorMap; use slog::Logger; use url::Url; -use crate::Error; -use crate::Error::{MQTTConnectionError}; +use crate::Error::{BadConfiguration, MQTTConnectionError}; pub trait Actuator { fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; @@ -135,9 +133,32 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { let mut map = HashMap::new(); let mqtt_url = Url::parse(config.mqtt_url.as_str())?; - let mut mqttoptions = MqttOptions::try_from(mqtt_url) - .map_err(|opt| Error::Boxed(Box::new(opt)))?; - mqttoptions.set_keep_alive(Duration::from_secs(20)); + let (transport, default_port) = match mqtt_url.scheme() { + "mqtts" | "ssl" => + (rumqttc::Transport::tls_with_config(rumqttc::ClientConfig::new().into()), 8883), + + "mqtt" | "tcp" => (rumqttc::Transport::tcp(), 1883), + + scheme => { + error!(log, "MQTT url uses invalid scheme {}", scheme); + return Err(BadConfiguration); + } + }; + let host = mqtt_url.host_str().ok_or_else(|| { + error!(log, "MQTT url must contain a hostname"); + BadConfiguration + })?; + let port = mqtt_url.port().unwrap_or(default_port); + + let mut mqttoptions = MqttOptions::new("bffh", host, port); + + mqttoptions + .set_transport(transport) + .set_keep_alive(Duration::from_secs(20)); + + if !mqtt_url.username().is_empty() { + mqttoptions.set_credentials(mqtt_url.username(), mqtt_url.password().unwrap_or_default()); + } let (mqtt, mut eventloop) = AsyncClient::new(mqttoptions, 256); let dlog = log.clone(); From ea863e71af0d50cbd86271a5f1f811ce5b3a6da8 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 14:30:06 +0100 Subject: [PATCH 20/37] Makes rumqttc futures run on the tokio runtime as required. --- Cargo.lock | 14 ++++++++++++++ Cargo.toml | 1 + src/actor.rs | 7 ++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85f06e1..891a08e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" version = "1.4.1" @@ -515,6 +528,7 @@ name = "diflouroborane" version = "0.3.2" dependencies = [ "async-channel", + "async-compat", "async-rustls", "async-trait", "bincode", diff --git a/Cargo.toml b/Cargo.toml index 85541ca..a66d9eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ rsasl = "1.4.0" #rsasl = { path = "../../rsasl" } rumqttc = { version = "0.10", features = ["url"] } +async-compat = "0.2.1" url = "2.2.2" #mlua = { version = "0.4", features = ["async", "luajit"] } diff --git a/src/actor.rs b/src/actor.rs index 0122512..76ad85e 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -9,6 +9,7 @@ use futures::{future::BoxFuture, Stream}; use futures::channel::mpsc; use futures_signals::signal::Signal; use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions}; +use async_compat::CompatExt; use crate::db::machine::MachineState; use crate::config::Config; @@ -175,12 +176,12 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { } Ok(eventloop) - })?; + }.compat())?; let dlog = log.clone(); smol::spawn(async move { let mut fault = false; loop { - match eventloop.poll().await { + match eventloop.poll().compat().await { Ok(_) => { fault = false; // TODO: Handle incoming MQTT messages @@ -213,7 +214,7 @@ pub fn load(log: &Logger, config: &Config) -> Result<(ActorMap, Vec)> { } } } - }).detach(); + }.compat()).detach(); let actuators = config.actors.iter() .map(|(k,v)| (k, load_single(log, k, &v.module, &v.params, mqtt.clone()))) From 4306b5b691200a4eecbdb36d7d9055892432f667 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 26 Feb 2022 14:45:17 +0100 Subject: [PATCH 21/37] More cleanup. Also, this MR closes #48 --- src/machine.rs | 3 ++- src/main.rs | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index c2d9269..86f95a0 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -296,7 +296,8 @@ impl Inner { } fn replace_state(&self, new_state: MachineState) -> MachineState { - self.db.put(&self.id, &new_state); + // Ignore for now, nothing we can do either way. + let _ = self.db.put(&self.id, &new_state); self.state.replace(new_state) } diff --git a/src/main.rs b/src/main.rs index 4ed00a7..f5453f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -113,8 +113,6 @@ fn main() { } } - let retval; - // Scope to drop everything before exiting. { // Initialize the logging subsystem first to be able to better document the progress from now @@ -126,10 +124,9 @@ fn main() { info!(log, "Starting"); match maybe(matches, log.clone()) { - Ok(_) => retval = 0, + Ok(_) => {}, Err(e) => { error!(log, "{}", e); - retval = -1; } } drop(guard); From e130e5965115cab0e8569d0d4588a5aa6d96f2a4 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 2 Mar 2022 17:20:28 +0100 Subject: [PATCH 22/37] Port to rsasl2 Closes #45 --- Cargo.lock | 308 +++++++++++++++++++------------------------- Cargo.toml | 2 +- examples/bffh.dhall | 10 +- src/api/auth.rs | 112 ++++++++-------- src/error.rs | 9 +- 5 files changed, 191 insertions(+), 250 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 891a08e..17a5241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,15 +23,6 @@ dependencies = [ "pretty", ] -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - [[package]] name = "annotate-snippets" version = "0.9.1" @@ -164,7 +155,7 @@ checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6" dependencies = [ "async-io", "blocking", - "cfg-if 1.0.0", + "cfg-if", "event-listener", "futures-lite", "libc", @@ -239,30 +230,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.55.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b13ce559e6433d360c26305643803cb52cfbabbc2b9c47ce04a58493dfb443" -dependencies = [ - "bitflags", - "cexpr", - "cfg-if 0.1.10", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -301,6 +268,15 @@ dependencies = [ "generic-array 0.14.5", ] +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "block-padding" version = "0.1.5" @@ -396,38 +372,12 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" -[[package]] -name = "cexpr" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clang-sys" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "2.34.0" @@ -473,7 +423,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -483,10 +433,20 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array 0.14.5", + "typenum", +] + [[package]] name = "dhall" version = "0.11.0" @@ -508,7 +468,7 @@ dependencies = [ "quote", "serde", "serde_cbor", - "sha2", + "sha2 0.9.9", "url", ] @@ -587,13 +547,24 @@ dependencies = [ "generic-array 0.14.5", ] +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", + "subtle", +] + [[package]] name = "dirs-next" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "dirs-sys-next", ] @@ -641,19 +612,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "event-listener" version = "2.5.2" @@ -903,26 +861,11 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "gsasl-sys" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd377f206007e661591b4c6a6ba9907a75ae24e2a9ef40f3d0750a1db6bd5dbd" -dependencies = [ - "bindgen", -] - [[package]] name = "half" version = "1.8.2" @@ -950,6 +893,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.3", +] + [[package]] name = "home" version = "0.5.3" @@ -970,15 +922,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - [[package]] name = "idna" version = "0.2.3" @@ -1006,7 +949,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1039,12 +982,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "lexical-core" version = "0.7.6" @@ -1053,7 +990,7 @@ checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec 0.5.2", "bitflags", - "cfg-if 1.0.0", + "cfg-if", "ryu", "static_assertions", ] @@ -1065,13 +1002,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] -name = "libloading" -version = "0.7.3" +name = "linkme" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "edd4ad156b9934dc21cad96fd17278a7cb6f30a5657a9d976cd7b71d6d49c02c" dependencies = [ - "cfg-if 1.0.0", - "winapi", + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fd9dc7072de7168cbdaba9125e8f742cd3a965aa12bde994b4611a174488d8" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1103,7 +1050,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1118,6 +1065,15 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + [[package]] name = "memchr" version = "2.4.1" @@ -1240,10 +1196,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "pbkdf2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest 0.10.3", +] [[package]] name = "percent-encoding" @@ -1313,7 +1272,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ "maplit", "pest", - "sha-1", + "sha-1 0.8.2", ] [[package]] @@ -1360,7 +1319,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "log", "wepoll-ffi", @@ -1439,12 +1398,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.15" @@ -1503,23 +1456,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -1546,13 +1482,21 @@ dependencies = [ [[package]] name = "rsasl" -version = "1.4.2" +version = "2.0.0-preview2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00658df565e044eeb2d0358bf370eb597c7089daf7d5021aa142b91f2524f641" +checksum = "2adcc7fb89ad42cf35d527905a11232c02fa030f7b6983b8c9880c385da2ae8e" dependencies = [ - "discard", - "gsasl-sys", + "base64", + "digest 0.10.3", + "hmac", "libc", + "linkme", + "md-5", + "pbkdf2", + "rand", + "sha-1 0.10.0", + "sha2 0.10.2", + "stringprep", ] [[package]] @@ -1586,12 +1530,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustls" version = "0.19.1" @@ -1711,6 +1649,17 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1718,17 +1667,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug 0.3.0", ] [[package]] -name = "shlex" -version = "0.1.1" +name = "sha2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] [[package]] name = "signal-hook" @@ -1832,12 +1786,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.86" @@ -1872,7 +1842,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", "redox_syscall", @@ -1891,15 +1861,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -2133,7 +2094,7 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -2210,15 +2171,6 @@ dependencies = [ "cc", ] -[[package]] -name = "which" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -dependencies = [ - "libc", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index a66d9eb..4a5223c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ uuid = { version = "0.8.2", features = ["serde", "v4"] } clap = "2.33.3" # TODO update this if bindgen breaks (again) -rsasl = "1.4.0" +rsasl = "2.0.0-preview2" #rsasl = { path = "../../rsasl" } rumqttc = { version = "0.10", features = ["url"] } diff --git a/examples/bffh.dhall b/examples/bffh.dhall index f1b982b..7f9a3a7 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -212,7 +212,7 @@ -- Actors need to be connected to machines to be useful. A machine can be connected to multiple actors, but one -- actor can only be connected to one machine. actor_connections = [ - { machine = "Testmachine", actor = "DoorControl1" }, + { machine = "Testmachine", actor = "Shelly1234" }, { machine = "Another", actor = "Bash" }, { machine = "Yetmore", actor = "Bash2" }, { machine = "Yetmore", actor = "FailBash"} @@ -221,14 +221,14 @@ -- Initiators are configured almost the same way as Actors, refer to actor documentation for more details -- The below '{=}' is what you need if you want to define *no* initiators at all and only use the API with apps -- to let people use machines. - -- initiators = {=}, + initiators = {=}, -- The "Dummy" initiator will try to use and return a machine as the given user every few seconds. It's good to -- test your system but will spam your log so is disabled by default. - initiators = { Initiator = { module = "Dummy", params = { uid = "Testuser" } } }, + --initiators = { Initiator = { module = "Dummy", params = { uid = "Testuser" } } }, -- Linking up machines to initiators. Similar to actors a machine can have several initiators assigned but an -- initiator can only be assigned to one machine. -- The below is once again how you have to define *no* initiators. - --init_connections = [] : List { machine : Text, initiator : Text } - init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] + init_connections = [] : List { machine : Text, initiator : Text } + --init_connections = [{ machine = "Testmachine", initiator = "Initiator" }] } diff --git a/src/api/auth.rs b/src/api/auth.rs index 4269bbb..e6c6590 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -6,23 +6,21 @@ use std::sync::Arc; use std::rc::Rc; use std::cell::RefCell; +use std::io::Cursor; use slog::Logger; -use rsasl::{ - SASL, - RSASL, - Property, - Session as SaslSession, - ReturnCode, - Callback, - Step, -}; - use serde::{Serialize, Deserialize}; use capnp::capability::{Promise}; +use rsasl::callback::Callback; +use rsasl::error::SessionError; +use rsasl::mechname::Mechname; +use rsasl::property::{AuthId, Password}; +use rsasl::SASL; +use rsasl::session::Step; +use rsasl::validate::{Validation, validations}; use crate::api::Session; @@ -39,42 +37,35 @@ pub struct SessionData { authz: Option, } -struct CB; -impl Callback for CB { - fn callback(sasl: &mut SASL, - session: &mut SaslSession, - prop: Property - ) -> Result<(), ReturnCode> - { - let ret = match prop { - Property::GSASL_VALIDATE_SIMPLE => { - // FIXME: get_property and retrieve_mut can't be used interleaved but that's - // technically safe. +struct CB { + userdb: Arc, +} +impl CB { + pub fn new(userdb: Arc) -> Self { + Self { userdb } + } +} - let authid: &str = session - .get_property(Property::GSASL_AUTHID) - .ok_or(ReturnCode::GSASL_NO_AUTHID) - .and_then(|a| match a.to_str() { - Ok(s) => Ok(s), - Err(_) => Err(ReturnCode::GSASL_SASLPREP_ERROR), - })?; +impl Callback for CB { + fn validate(&self, session: &mut rsasl::session::SessionData, validation: Validation, _mechanism: &Mechname) -> Result<(), SessionError> { + let ret = match validation { + validations::SIMPLE => { - let pass = session.get_property(Property::GSASL_PASSWORD) - .ok_or(ReturnCode::GSASL_NO_PASSWORD)?; + let authid = session + .get_property::() + .ok_or(SessionError::no_property::())?; + let pass = session.get_property::() + .ok_or(SessionError::no_property::())?; - if let Some(appdata) = sasl.retrieve_mut() { - if let Ok(Some(user)) = appdata.userdb.login(authid, pass.to_bytes()) { - session.retrieve_mut().unwrap().authz.replace(user); - return Ok(()); - } + if let Some(opt) = self.userdb.login(authid.as_ref(), pass.as_bytes()).unwrap() { + return Ok(()) } - ReturnCode::GSASL_AUTHENTICATION_ERROR + SessionError::AuthenticationFailure } - p => { - println!("Callback called with property {:?}", p); - ReturnCode::GSASL_NO_CALLBACK + _ => { + SessionError::no_validate(validation) } }; Err(ret) @@ -82,22 +73,19 @@ impl Callback for CB { } pub struct Auth { - pub ctx: RSASL, + pub ctx: SASL, session: Rc>>, + userdb: Arc, access: Arc, log: Logger, } impl Auth { pub fn new(log: Logger, dbs: Databases, session: Rc>>) -> Self { - let mut ctx = SASL::new().unwrap(); + let mut ctx = SASL::new(); + ctx.install_callback(Arc::new(CB::new(dbs.userdb.clone()))); - let appdata = Box::new(AppData { userdb: dbs.userdb.clone() }); - - ctx.store(appdata); - ctx.install_callback::(); - - Self { log, ctx, session, access: dbs.access.clone() } + Self { log, ctx, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() } } } @@ -147,6 +135,8 @@ impl authentication_system::Server for Auth { }) } + let mech = Mechname::new(mech.as_bytes()).unwrap(); + let mut session = match self.ctx.server_start(mech) { Ok(s) => s, Err(e) => @@ -156,7 +146,7 @@ impl authentication_system::Server for Auth { }), }; - session.store(Box::new(SessionData { authz: None })); + let mut out = Cursor::new(Vec::new()); // If the client has provided initial data go use that use request::initial_response::Which; @@ -169,24 +159,24 @@ impl authentication_system::Server for Auth { Ok(Which::None(_)) => { // FIXME: Actually this needs to indicate NO data instead of SOME data of 0 length - session.step(&[]) + session.step(Option::<&[u8]>::None, &mut out) } Ok(Which::Initial(data)) => { - session.step(pry!(data)) + session.step(Some(pry!(data)), &mut out) } }; // The step may either return an error, a success or the need for more data // TODO: Set the session user. Needs a lookup though <.> - use response::Result as Resres; + match step_res { Ok(Step::Done(b)) => { let user = session - .retrieve_mut() + .get_property::() .and_then(|data| { - data.authz.take() + self.userdb.get_user(data.as_str()).unwrap() }) - .expect("Authentication returned OK but didn't set user id"); + .expect("Authentication returned OK but the given AuthId is invalid"); let perms = pry!(self.access.collect_permrules(&user.data) .map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))); @@ -199,26 +189,26 @@ impl authentication_system::Server for Auth { ))); let mut outcome = pry!(res.get().get_response()).init_outcome(); - outcome.reborrow().set_result(Resres::Successful); - if b.len() != 0 { - outcome.init_additional_data().set_additional(&b); + outcome.reborrow().set_result(response::Result::Successful); + if let Some(data) = b { + outcome.init_additional_data().set_additional(&out.get_ref()); } Promise::ok(()) }, Ok(Step::NeedsMore(b)) => { - pry!(res.get().get_response()).set_challence(&b); + if b.is_some() { + pry!(res.get().get_response()).set_challence(&out.get_ref()); + } Promise::ok(()) } - // TODO: This should really be an outcome because this is failed auth just as much atm. Err(e) => { let mut outcome = pry!(res.get().get_response()).init_outcome(); - outcome.reborrow().set_result(Resres::Failed); + outcome.reborrow().set_result(response::Result::InvalidCredentials); let text = format!("{}", e); outcome.set_help_text(&text); Promise::ok(()) } } - } } diff --git a/src/error.rs b/src/error.rs index 8256dfa..df13cf3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,10 +3,9 @@ use std::fmt; use toml; use serde_dhall; -use rsasl::SaslError; - // SpawnError is a somewhat ambigous name, `use as` to make it futures::SpawnError instead. use futures::task as futures_task; +use rsasl::error::SessionError; use crate::network; @@ -16,7 +15,7 @@ pub enum Error { TomlDe(toml::de::Error), TomlSer(toml::ser::Error), Dhall(serde_dhall::Error), - SASL(SaslError), + SASL(SessionError), IO(io::Error), Boxed(Box), Capnp(capnp::Error), @@ -95,8 +94,8 @@ impl fmt::Display for Error { } } -impl From for Error { - fn from(e: SaslError) -> Error { +impl From for Error { + fn from(e: SessionError) -> Error { Error::SASL(e) } } From c8623fd62b42a3cc05c1e76ed146750d4d5e0968 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 2 Mar 2022 17:28:41 +0100 Subject: [PATCH 23/37] Fixes warnings --- src/api/auth.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/auth.rs b/src/api/auth.rs index e6c6590..662d3d5 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -58,7 +58,7 @@ impl Callback for CB { let pass = session.get_property::() .ok_or(SessionError::no_property::())?; - if let Some(opt) = self.userdb.login(authid.as_ref(), pass.as_bytes()).unwrap() { + if self.userdb.login(authid.as_ref(), pass.as_bytes()).unwrap().is_some() { return Ok(()) } @@ -190,7 +190,7 @@ impl authentication_system::Server for Auth { let mut outcome = pry!(res.get().get_response()).init_outcome(); outcome.reborrow().set_result(response::Result::Successful); - if let Some(data) = b { + if b.is_some() { outcome.init_additional_data().set_additional(&out.get_ref()); } Promise::ok(()) From 0531156b9eaa6e8d326c827fb0e2f2357c9c8733 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 9 Mar 2022 02:40:38 +0100 Subject: [PATCH 24/37] Improve TLS support --- Cargo.lock | 68 ++++++++++++++++++++++++++++++++++------------- Cargo.toml | 4 +-- src/connection.rs | 3 +-- src/server.rs | 27 ++++++++++++------- 4 files changed, 70 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17a5241..3a48440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,17 +164,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "async-rustls" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378" -dependencies = [ - "futures-lite", - "rustls", - "webpki", -] - [[package]] name = "async-task" version = "4.1.0" @@ -489,7 +478,6 @@ version = "0.3.2" dependencies = [ "async-channel", "async-compat", - "async-rustls", "async-trait", "bincode", "capnp", @@ -500,6 +488,7 @@ dependencies = [ "easy-parallel", "flexbuffers", "futures 0.3.21", + "futures-rustls", "futures-signals", "futures-test", "futures-util", @@ -511,7 +500,7 @@ dependencies = [ "rsasl", "rumqttc", "rust-argon2", - "rustls", + "rustls 0.20.4", "rustls-pemfile", "serde", "serde_dhall", @@ -743,6 +732,17 @@ dependencies = [ "syn", ] +[[package]] +name = "futures-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d383f0425d991a05e564c2f3ec150bd6dde863179c131dd60d8aa73a05434461" +dependencies = [ + "futures-io", + "rustls 0.20.4", + "webpki 0.22.0", +] + [[package]] name = "futures-signals" version = "0.3.24" @@ -1515,7 +1515,7 @@ dependencies = [ "tokio", "tokio-rustls", "url", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -1539,8 +1539,20 @@ dependencies = [ "base64", "log", "ring", - "sct", - "webpki", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", ] [[package]] @@ -1583,6 +1595,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.136" @@ -1966,9 +1988,9 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ - "rustls", + "rustls 0.19.1", "tokio", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -2162,6 +2184,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 4a5223c..535308b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,9 +71,9 @@ easy-parallel = "3.1.0" genawaiter = "0.99.1" # TLS -rustls = "0.19" +rustls = "0.20" rustls-pemfile = "0.2" -async-rustls = "0.2" +futures-rustls = "0.22.0" [build-dependencies] capnpc = "0.14.4" diff --git a/src/connection.rs b/src/connection.rs index 769c94a..239d37b 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -7,8 +7,6 @@ use std::future::Future; use std::sync::Arc; -use async_rustls::server::TlsStream; - use slog::Logger; @@ -17,6 +15,7 @@ use crate::api::Bootstrap; use crate::error::Result; use capnp_rpc::{rpc_twoparty_capnp, twoparty}; +use futures_rustls::server::TlsStream; use smol::io::split; diff --git a/src/server.rs b/src/server.rs index 4f24991..3747166 100644 --- a/src/server.rs +++ b/src/server.rs @@ -20,8 +20,8 @@ use std::sync::Arc; use std::os::unix::io::AsRawFd; use std::path::Path; -use async_rustls::TlsAcceptor; -use rustls::{Certificate, KeyLogFile, NoClientAuth, PrivateKey, ServerConfig}; +use futures_rustls::TlsAcceptor; +use rustls::{Certificate, KeyLogFile, PrivateKey, ServerConfig}; use signal_hook::low_level::pipe as sigpipe; @@ -60,23 +60,30 @@ pub fn serve_api_connections(log: Arc, config: Config, db: Databases, nw .collect(); info!(log, "Reading private key file"); let mut keyfp = BufReader::new(File::open(&config.keyfile)?); - let mut tls_config = ServerConfig::new(Arc::new(NoClientAuth)); - tls_config.key_log = Arc::new(KeyLogFile::new()); - if let Some(path) = std::env::var_os("SSLKEYLOGFILE") { - let path = Path::new(&path); - warn!(log, "TLS SECRET LOGGING ENABLED! This will write all connection secrets to file {}!", - path.display()); - } + let mut tls_builder = ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + ; + + let mut tls_config; match rustls_pemfile::read_one(&mut keyfp)? { Some(rustls_pemfile::Item::PKCS8Key(key) | rustls_pemfile::Item::RSAKey(key)) => { let key = PrivateKey(key); - tls_config.set_single_cert(certs, key)?; + tls_config = tls_builder.with_single_cert(certs, key)?; } _ => { error!(log, "private key file must contain a PEM-encoded private key"); return Ok(()); } } + + if let Some(path) = std::env::var_os("SSLKEYLOGFILE") { + let path = Path::new(&path); + warn!(log, "TLS SECRET LOGGING ENABLED! This will write all connection secrets to file {}!", + path.display()); + } + tls_config.key_log = Arc::new(KeyLogFile::new()); + let tls_acceptor: TlsAcceptor = Arc::new(tls_config).into(); // Bind to each address in config.listens. From 4611ed5b481661afa9c0539955b074becd62a3c3 Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Sat, 12 Mar 2022 00:45:59 +0100 Subject: [PATCH 25/37] initial integration of the X-FABFIRE mechnism Integrates the fabfire mechanism for use with the jorisdevice and desfire cards --- Cargo.lock | 94 +++++++- Cargo.toml | 9 +- Dockerfile.dev | 7 + src/api/auth.rs | 11 +- src/api/auth/fabfire.rs | 20 ++ src/api/auth/fabfire/server.rs | 420 +++++++++++++++++++++++++++++++++ 6 files changed, 555 insertions(+), 6 deletions(-) create mode 100644 Dockerfile.dev create mode 100644 src/api/auth/fabfire.rs create mode 100644 src/api/auth/fabfire/server.rs diff --git a/Cargo.lock b/Cargo.lock index 17a5241..909cc1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,18 @@ dependencies = [ "pretty", ] +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug 0.3.0", +] + [[package]] name = "annotate-snippets" version = "0.9.1" @@ -253,7 +265,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding", + "block-padding 0.1.5", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -277,6 +289,16 @@ dependencies = [ "generic-array 0.14.5", ] +[[package]] +name = "block-modes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +dependencies = [ + "block-padding 0.2.1", + "cipher", +] + [[package]] name = "block-padding" version = "0.1.5" @@ -286,6 +308,12 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "blocking" version = "1.1.0" @@ -378,6 +406,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "clap" version = "2.34.0" @@ -447,6 +484,32 @@ dependencies = [ "typenum", ] +[[package]] +name = "des" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d" +dependencies = [ + "byteorder", + "cipher", + "opaque-debug 0.3.0", +] + +[[package]] +name = "desfire" +version = "0.1.0" +source = "git+https://gitlab.com/fabinfra/fabaccess/nfc_rs.git?branch=main#34d1d7f3a062f007fcdc229995f9013970e460a5" +dependencies = [ + "aes", + "block-modes", + "des", + "hex", + "num-derive", + "num-traits", + "rand", + "simple-error", +] + [[package]] name = "dhall" version = "0.11.0" @@ -497,6 +560,7 @@ dependencies = [ "capnp-rpc", "capnpc", "clap", + "desfire", "easy-parallel", "flexbuffers", "futures 0.3.21", @@ -504,8 +568,10 @@ dependencies = [ "futures-test", "futures-util", "genawaiter", + "hex", "lazy_static", "libc", + "linkme", "lmdb-rkv", "rand", "rsasl", @@ -1131,6 +1197,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -1703,6 +1789,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simple-error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b" + [[package]] name = "slab" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 4a5223c..03f35d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,8 @@ uuid = { version = "0.8.2", features = ["serde", "v4"] } clap = "2.33.3" # TODO update this if bindgen breaks (again) -rsasl = "2.0.0-preview2" -#rsasl = { path = "../../rsasl" } +rsasl = { version = "2.0.0-preview2", features = ["unstable_custom_mechanism", "registry_static"] } +#rsasl = { path = "../../rsasl", features = ["unstable_custom_mechanism", "registry_static"] } rumqttc = { version = "0.10", features = ["url"] } async-compat = "0.2.1" @@ -75,6 +75,11 @@ rustls = "0.19" rustls-pemfile = "0.2" async-rustls = "0.2" +# Desfire +desfire = { git = "https://gitlab.com/fabinfra/fabaccess/nfc_rs.git", branch = "main" } +hex = "0.4.3" +linkme = "0.2" + [build-dependencies] capnpc = "0.14.4" # Used in build.rs to iterate over all files in schema/ diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..9a52f58 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,7 @@ +# Setup build image for multistage build +FROM rust:latest +# install build deps +RUN apt-get update && apt-get upgrade -y +RUN apt-get install -yqq --no-install-recommends capnproto build-essential cmake clang libclang-dev libgsasl7-dev + +COPY ../nfc_rs /nfc_rs \ No newline at end of file diff --git a/src/api/auth.rs b/src/api/auth.rs index 662d3d5..8f260ba 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -30,6 +30,9 @@ use crate::db::Databases; use crate::db::user::{Internal as UserDB, User}; use crate::db::access::AccessControl as AccessDB; +mod fabfire; +use fabfire::FABFIRE; + pub struct AppData { userdb: Arc, } @@ -83,6 +86,7 @@ pub struct Auth { impl Auth { pub fn new(log: Logger, dbs: Databases, session: Rc>>) -> Self { let mut ctx = SASL::new(); + ctx.register(&FABFIRE); ctx.install_callback(Arc::new(CB::new(dbs.userdb.clone()))); Self { log, ctx, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() } @@ -111,9 +115,10 @@ impl authentication_system::Server for Auth { for (i, m) in mechvec.into_iter().enumerate() { res_mechs.set(i as u32, m); }*/ - // For now, only PLAIN - let mut res_mechs = res.get().init_mechs(1); + // For now, only PLAIN and X-FABFIRE + let mut res_mechs = res.get().init_mechs(2); res_mechs.set(0, "PLAIN"); + res_mechs.set(1, "X-FABFIRE"); Promise::ok(()) } @@ -128,7 +133,7 @@ impl authentication_system::Server for Auth { // Extract the MECHANISM the client wants to use and start a session. // Or fail at that and thrown an exception TODO: return Outcome let mech = pry!(req.get_mechanism()); - if pry!(req.get_mechanism()) != "PLAIN" { + if mech != "PLAIN" || mech != "X-FABFIRE" { return Promise::err(capnp::Error { kind: capnp::ErrorKind::Failed, description: format!("Invalid SASL mech"), diff --git a/src/api/auth/fabfire.rs b/src/api/auth/fabfire.rs new file mode 100644 index 0000000..9c9c43d --- /dev/null +++ b/src/api/auth/fabfire.rs @@ -0,0 +1,20 @@ +mod server; +pub use server::FabFire; + +use rsasl::mechname::Mechname; +use rsasl::registry::{Mechanism, MECHANISMS}; +use rsasl::session::Side; + +const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE"); + +#[linkme::distributed_slice(MECHANISMS)] +pub static FABFIRE: Mechanism = Mechanism { + mechanism: MECHNAME, + priority: 300, + // In this situation there's one struct for both sides, however you can just as well use + // different types than then have different `impl Authentication` instead of checking a value + // in self. + client: None, + server: Some(FabFire::new_server), + first: Side::Client, +}; diff --git a/src/api/auth/fabfire/server.rs b/src/api/auth/fabfire/server.rs new file mode 100644 index 0000000..ed76e82 --- /dev/null +++ b/src/api/auth/fabfire/server.rs @@ -0,0 +1,420 @@ +use std::fmt::{Debug, Display, Formatter}; +use std::io::Write; +use rsasl::error::{MechanismError, MechanismErrorKind, SASLError, SessionError}; +use rsasl::mechanism::Authentication; +use rsasl::SASL; +use rsasl::session::{SessionData, StepResult}; +use serde::{Deserialize, Serialize}; +use desfire::desfire::Desfire; +use desfire::iso7816_4::apducommand::APDUCommand; +use desfire::iso7816_4::apduresponse::APDUResponse; +use desfire::error::{Error as DesfireError, Error}; +use std::convert::TryFrom; +use std::ops::Deref; + +enum FabFireError { + ParseError, + SerializationError, + CardError(DesfireError), +} + +impl Debug for FabFireError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + FabFireError::ParseError => write!(f, "ParseError"), + FabFireError::SerializationError => write!(f, "SerializationError"), + FabFireError::CardError(err) => write!(f, "CardError: {}", err), + } + } +} + +impl Display for FabFireError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + FabFireError::ParseError => write!(f, "ParseError"), + FabFireError::SerializationError => write!(f, "SerializationError"), + FabFireError::CardError(err) => write!(f, "CardError: {}", err), + } + } +} + +impl MechanismError for FabFireError { + fn kind(&self) -> MechanismErrorKind { + match self { + FabFireError::ParseError => MechanismErrorKind::Parse, + FabFireError::SerializationError => MechanismErrorKind::Protocol, + FabFireError::CardError(_) => MechanismErrorKind::Protocol, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +struct CardInfo { + card_uid: [u8; 7], + key_old: Option>, + key_new: Option> +} + +struct KeyInfo { + key_id: u8, + key: Box<[u8]> +} + +struct AuthInfo { + rnd_a: Vec, + rnd_b: Vec, + iv: Vec +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "Cmd")] +enum CardCommand { + message { + msg_id: Option, + clr_txt: Option, + addn_txt: Option, + }, + sendPICC { + data: String + }, + haltPICC, + Key { + data: String + }, + ConfirmUser +} + +enum Step { + New, + SelectApp, + VerifyMagic, + GetURN, + GetToken, + Authenticate1, + Authenticate2, + Authenticate3, +} + +pub struct FabFire { + step: Step, + card_info: Option, + key_info: Option, + auth_info: Option, + app_id: u32, + local_urn: String, + desfire: Desfire, +} + +const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0"; + +impl FabFire { + pub fn new_server(_sasl: &SASL) -> Result, SASLError> { + Ok(Box::new(Self { step: Step::New, card_info: None, key_info: None, auth_info: None, app_id: 1, local_urn: "urn:fabaccess:lab:innovisionlab".to_string(), desfire: Desfire { card: None, session_key: None, cbc_iv: None } })) + } +} + +impl Authentication for FabFire { + fn step(&mut self, session: &mut SessionData, input: Option<&[u8]>, writer: &mut dyn Write) -> StepResult { + match self.step { + Step::New => { + //receive card info (especially card UID) from reader + return match input { + None => { Err(SessionError::InputDataRequired) }, + Some(cardinfo) => { + self.card_info = match serde_json::from_slice(cardinfo) { + Ok(card_info) => Some(card_info), + Err(_) => { + return Err(FabFireError::ParseError.into()) + } + }; + self.step = Step::SelectApp; + Ok(rsasl::session::Step::NeedsMore(None)) + } + } + } + Step::SelectApp => { + //select application + let buf = match self.desfire.select_application_cmd(self.app_id) { + Ok(buf) => match Vec::::try_from(buf) { + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }; + let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; + return match serde_json::to_writer(writer, &cmd) { + Ok(_) => { + self.step = Step::VerifyMagic; + Ok(rsasl::session::Step::NeedsMore(None)) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + } + } + Step::VerifyMagic => { + // check that we successfully selected the application + let response = match input { + None => {return Err(SessionError::InputDataRequired)}, + Some(buf) => APDUResponse::new(buf) + }; + response.check().map_err(|e| FabFireError::CardError(e))?; + + // request the contents of the file containing the magic string + const MAGIC_FILE_ID: u8 = 0x01; + + let buf = match self.desfire.read_data_chunk_cmd(MAGIC_FILE_ID, 0, MAGIC.len()) { + Ok(buf) => match Vec::::try_from(buf) { + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }; + let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; + return match serde_json::to_writer(writer, &cmd) { + Ok(_) => { + self.step = Step::GetURN; + Ok(rsasl::session::Step::NeedsMore(None)) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + } + } + Step::GetURN => { + // verify the magic string to determine that we have a valid fabfire card + let response = match input { + None => {return Err(SessionError::InputDataRequired)}, + Some(buf) => APDUResponse::new(buf) + }; + match response.check() { + Ok(_) => { + match response.body { + Some(data) => { + if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) { + return Err(FabFireError::ParseError.into()); + } + } + None => { + return Err(FabFireError::ParseError.into()) + } + }; + } + Err(_) => { + return Err(FabFireError::ParseError.into()); + } + } + + + // request the contents of the file containing the URN + const URN_FILE_ID: u8 = 0x02; + + let buf = match self.desfire.read_data_chunk_cmd(URN_FILE_ID, 0, self.local_urn.as_bytes().len()) { // TODO: support urn longer than 47 Bytes + Ok(buf) => match Vec::::try_from(buf) { + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }; + let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; + return match serde_json::to_writer(writer, &cmd) { + Ok(_) => { + self.step = Step::GetToken; + Ok(rsasl::session::Step::NeedsMore(None)) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + } + } + Step::GetToken => { + // parse the urn and match it to our local urn + let response = match input { + None => {return Err(SessionError::InputDataRequired)}, + Some(buf) => APDUResponse::new(buf) + }; + match response.check() { + Ok(_) => { + match response.body { + Some(data) => { + if String::from_utf8(data).unwrap() != self.local_urn { + return Err(FabFireError::ParseError.into()); + } + } + None => { + return Err(FabFireError::ParseError.into()) + } + }; + } + Err(_) => { + return Err(FabFireError::ParseError.into()); + } + } + // request the contents of the file containing the URN + const TOKEN_FILE_ID: u8 = 0x03; + + let buf = match self.desfire.read_data_chunk_cmd(TOKEN_FILE_ID, 0, 47) { // TODO: support data longer than 47 Bytes + Ok(buf) => match Vec::::try_from(buf) { + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }; + let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; + return match serde_json::to_writer(writer, &cmd) { + Ok(_) => { + self.step = Step::Authenticate1; + Ok(rsasl::session::Step::NeedsMore(None)) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + } + } + Step::Authenticate1 => { + // parse the token and select the appropriate user + let response = match input { + None => {return Err(SessionError::InputDataRequired)}, + Some(buf) => APDUResponse::new(buf) + }; + match response.check() { + Ok(_) => { + match response.body { + Some(data) => { + if String::from_utf8(data).unwrap() != "LoremIpsum" { // FIXME: match against user db + return Err(FabFireError::ParseError.into()); + } + } + None => { + return Err(FabFireError::ParseError.into()) + } + }; + } + Err(_) => { + return Err(FabFireError::ParseError.into()); + } + } + + let buf = match self.desfire.authenticate_iso_aes_challenge_cmd(self.key_info.as_ref().unwrap().key_id) { + Ok(buf) => match Vec::::try_from(buf) { + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }; + let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; + return match serde_json::to_writer(writer, &cmd) { + Ok(_) => { + self.step = Step::Authenticate2; + Ok(rsasl::session::Step::NeedsMore(None)) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + } + + } + Step::Authenticate2 => { + let response = match input { + None => {return Err(SessionError::InputDataRequired)}, + Some(buf) => APDUResponse::new(buf) + }; + match response.check() { + Ok(_) => { + match response.body { + Some(data) => { + let rnd_b_enc = data.as_slice(); + + //FIXME: This is ugly, we should find a better way to make the function testable + //TODO: Check if we need a CSPRNG here + let rnd_a: [u8; 16] = rand::random(); + println!("RND_A: {:x?}", rnd_a); + + let (cmd_challenge_response, rnd_b, iv) = self.desfire.authenticate_iso_aes_response_cmd(rnd_b_enc, &*(self.key_info.as_ref().unwrap().key), &rnd_a).unwrap(); + self.auth_info = Some(AuthInfo{rnd_a: Vec::::from(rnd_a), rnd_b, iv}); + let buf = match Vec::::try_from(cmd_challenge_response) { + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()) + } + }; + let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; + return match serde_json::to_writer(writer, &cmd) { + Ok(_) => { + self.step = Step::Authenticate3; + Ok(rsasl::session::Step::NeedsMore(None)) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + } + } + None => { + return Err(FabFireError::ParseError.into()) + } + }; + } + Err(_) => { + return Err(FabFireError::ParseError.into()); + } + } + } + Step::Authenticate3 => { + let response = match input { + None => {return Err(SessionError::InputDataRequired)}, + Some(buf) => APDUResponse::new(buf) + }; + match response.check() { + Ok(_) => { + match response.body { + Some(data) => { + match self.auth_info.as_ref() { + None => {return Err(FabFireError::ParseError.into())} + Some(auth_info) => { + if self.desfire.authenticate_iso_aes_verify( + data.as_slice(), + auth_info.rnd_a.as_slice(), + auth_info.rnd_b.as_slice(), &*(self.key_info.as_ref().unwrap().key), + auth_info.iv.as_slice()).is_ok() { + // TODO: Do stuff with the info that we are authenticated + return Ok(rsasl::session::Step::Done(None)); + } + } + } + } + None => { + return Err(FabFireError::ParseError.into()) + } + }; + } + Err(_) => { + return Err(FabFireError::ParseError.into()); + } + } + } + } + + return Ok(rsasl::session::Step::Done(None)); + } + +} \ No newline at end of file From 37db05a5574e2e75c919562c9bd95d0209625ac8 Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Sat, 12 Mar 2022 00:51:42 +0100 Subject: [PATCH 26/37] fix stupid logic error --- src/api/auth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/auth.rs b/src/api/auth.rs index 8f260ba..85a7c8c 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -133,7 +133,7 @@ impl authentication_system::Server for Auth { // Extract the MECHANISM the client wants to use and start a session. // Or fail at that and thrown an exception TODO: return Outcome let mech = pry!(req.get_mechanism()); - if mech != "PLAIN" || mech != "X-FABFIRE" { + if !((mech == "PLAIN") || (mech == "X-FABFIRE")) { return Promise::err(capnp::Error { kind: capnp::ErrorKind::Failed, description: format!("Invalid SASL mech"), From 5c5c9710c5c8543fc0a5617c5fb00ca50349f6ba Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Sat, 12 Mar 2022 10:45:09 +0100 Subject: [PATCH 27/37] working Desfire auth in the api! --- Cargo.lock | 3 + Cargo.toml | 2 +- examples/users.toml | 1 + src/api/auth.rs | 104 +++++++- src/api/auth/fabfire.rs | 24 ++ src/api/auth/fabfire/server.rs | 463 +++++++++++++++++++++++---------- src/db/user.rs | 2 +- 7 files changed, 445 insertions(+), 154 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 909cc1b..33d8a7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -958,6 +958,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hmac" diff --git a/Cargo.toml b/Cargo.toml index 03f35d1..ca7a144 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ async-rustls = "0.2" # Desfire desfire = { git = "https://gitlab.com/fabinfra/fabaccess/nfc_rs.git", branch = "main" } -hex = "0.4.3" +hex = { version = "0.4.3", features = ["serde"] } linkme = "0.2" [build-dependencies] diff --git a/examples/users.toml b/examples/users.toml index 719f2fb..59b267a 100644 --- a/examples/users.toml +++ b/examples/users.toml @@ -11,3 +11,4 @@ passwd = "secret" # It will get stored in the `kv` field in UserData. # This is not used for anything at the moment noot = "noot!" +cardkey = "7ab8704a61b5317e1fe4cae9e3e1fd8d" diff --git a/src/api/auth.rs b/src/api/auth.rs index 85a7c8c..22c92af 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use std::rc::Rc; use std::cell::RefCell; +use std::convert::TryFrom; use std::io::Cursor; @@ -18,7 +19,7 @@ use rsasl::callback::Callback; use rsasl::error::SessionError; use rsasl::mechname::Mechname; use rsasl::property::{AuthId, Password}; -use rsasl::SASL; +use rsasl::{Property, SASL}; use rsasl::session::Step; use rsasl::validate::{Validation, validations}; @@ -32,6 +33,7 @@ use crate::db::access::AccessControl as AccessDB; mod fabfire; use fabfire::FABFIRE; +use crate::api::auth::fabfire::FabFireCardKey; pub struct AppData { userdb: Arc, @@ -73,10 +75,35 @@ impl Callback for CB { }; Err(ret) } + + fn provide_prop( + &self, + session: &mut rsasl::session::SessionData, + property: Property, + ) -> Result<(), SessionError> { + match property { + FABFIRECARDKEY => { + // Access the authentication id, i.e. the username to check the password for + let authcid = session.get_property_or_callback::()?; + println!("auth'ing user {:?}", authcid); + self.userdb.get_user(authcid.unwrap().as_ref()).map(|user| { + let kvs= user.unwrap().data.kv; + println!("kvs: {:?}", kvs); + kvs.get("cardkey").map(|key| { + session.set_property::(Arc::new(<[u8; 16]>::try_from(hex::decode(key).unwrap()).unwrap())); + }); + }).ok(); + + Ok(()) + } + _ => Err(SessionError::NoProperty { property }), + } + } } pub struct Auth { pub ctx: SASL, + sasl_session: Option, session: Rc>>, userdb: Arc, access: Arc, @@ -89,7 +116,7 @@ impl Auth { ctx.register(&FABFIRE); ctx.install_callback(Arc::new(CB::new(dbs.userdb.clone()))); - Self { log, ctx, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() } + Self { log, ctx, sasl_session: None, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() } } } @@ -134,6 +161,7 @@ impl authentication_system::Server for Auth { // Or fail at that and thrown an exception TODO: return Outcome let mech = pry!(req.get_mechanism()); if !((mech == "PLAIN") || (mech == "X-FABFIRE")) { + debug!(self.log, "Invalid SASL mech: {}", mech); return Promise::err(capnp::Error { kind: capnp::ErrorKind::Failed, description: format!("Invalid SASL mech"), @@ -142,8 +170,8 @@ impl authentication_system::Server for Auth { let mech = Mechname::new(mech.as_bytes()).unwrap(); - let mut session = match self.ctx.server_start(mech) { - Ok(s) => s, + self.sasl_session = match self.ctx.server_start(mech) { + Ok(s) => Some(s), Err(e) => return Promise::err(capnp::Error { kind: capnp::ErrorKind::Failed, @@ -164,10 +192,11 @@ impl authentication_system::Server for Auth { Ok(Which::None(_)) => { // FIXME: Actually this needs to indicate NO data instead of SOME data of 0 length - session.step(Option::<&[u8]>::None, &mut out) + self.sasl_session.as_mut().unwrap().step(Option::<&[u8]>::None, &mut out) } Ok(Which::Initial(data)) => { - session.step(Some(pry!(data)), &mut out) + debug!(self.log, "running step() with initial data"); + self.sasl_session.as_mut().unwrap().step(Some(pry!(data)), &mut out) } }; @@ -176,7 +205,7 @@ impl authentication_system::Server for Auth { match step_res { Ok(Step::Done(b)) => { - let user = session + let user = self.sasl_session.as_mut().unwrap() .get_property::() .and_then(|data| { self.userdb.get_user(data.as_str()).unwrap() @@ -207,8 +236,67 @@ impl authentication_system::Server for Auth { Promise::ok(()) } Err(e) => { + error!(self.log, "SASL Auth failed: {}", e); let mut outcome = pry!(res.get().get_response()).init_outcome(); - outcome.reborrow().set_result(response::Result::InvalidCredentials); + outcome.reborrow().set_result(response::Result::InvalidCredentials); //FIXME: Check if problem where invalid creds or something else + let text = format!("{}", e); + outcome.set_help_text(&text); + Promise::ok(()) + } + } + } + + fn step(&mut self, params: authentication_system::StepParams, mut res: authentication_system::StepResults) -> Promise<(), capnp::Error> { + let resp = pry!(pry!(params.get()).get_response()); + + let mut out = Cursor::new(Vec::new()); + + // Use the data the Client has provided + let step_res = self.sasl_session.as_mut().unwrap().step(Some(resp), &mut out); + + + debug!(self.log, "running step() with additional data"); + + + // The step may either return an error, a success or the need for more data + // TODO: Set the session user. Needs a lookup though <.> + + match step_res { + Ok(Step::Done(b)) => { + let user = self.sasl_session.as_mut().unwrap() + .get_property::() + .and_then(|data| { + self.userdb.get_user(data.as_str()).unwrap() + }) + .expect("Authentication returned OK but the given AuthId is invalid"); + + let perms = pry!(self.access.collect_permrules(&user.data) + .map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))); + self.session.replace(Some(Session::new( + self.log.new(o!()), + user.id, + "".to_string(), + user.data.roles.into_boxed_slice(), + perms.into_boxed_slice() + ))); + + let mut outcome = pry!(res.get().get_response()).init_outcome(); + outcome.reborrow().set_result(response::Result::Successful); + if b.is_some() { + outcome.init_additional_data().set_additional(&out.get_ref()); + } + Promise::ok(()) + }, + Ok(Step::NeedsMore(b)) => { + if b.is_some() { + pry!(res.get().get_response()).set_challence(&out.get_ref()); + } + Promise::ok(()) + } + Err(e) => { + error!(self.log, "SASL Auth failed: {}", e); + let mut outcome = pry!(res.get().get_response()).init_outcome(); + outcome.reborrow().set_result(response::Result::InvalidCredentials); //FIXME: Check if problem where invalid creds or something else let text = format!("{}", e); outcome.set_help_text(&text); Promise::ok(()) diff --git a/src/api/auth/fabfire.rs b/src/api/auth/fabfire.rs index 9c9c43d..bedc15d 100644 --- a/src/api/auth/fabfire.rs +++ b/src/api/auth/fabfire.rs @@ -18,3 +18,27 @@ pub static FABFIRE: Mechanism = Mechanism { server: Some(FabFire::new_server), first: Side::Client, }; + +use std::marker::PhantomData; +use rsasl::property::{Property, PropertyQ, PropertyDefinition}; +// All Property types must implement Debug. +#[derive(Debug)] +// The `PhantomData` in the constructor is only used so external crates can't construct this type. +pub struct FabFireCardKey(PhantomData<()>); +impl PropertyQ for FabFireCardKey { + // This is the type stored for this property. This could also be the struct itself if you + // so choose + type Item = [u8; 16]; + // You need to return the constant you define below here for things to work properly + fn property() -> Property { + FABFIRECARDKEY + } +} +// This const is used by your mechanism to query and by your users to set your property. It +// thus needs to be exported from your crate +pub const FABFIRECARDKEY: Property = Property::new(&PropertyDefinition::new( + // Short name, used in `Debug` output + "FabFireCardKey", + // A longer user-facing name used in `Display` output + "A AES128 key for a FabFire card", +)); diff --git a/src/api/auth/fabfire/server.rs b/src/api/auth/fabfire/server.rs index ed76e82..32647b0 100644 --- a/src/api/auth/fabfire/server.rs +++ b/src/api/auth/fabfire/server.rs @@ -6,16 +6,24 @@ use rsasl::SASL; use rsasl::session::{SessionData, StepResult}; use serde::{Deserialize, Serialize}; use desfire::desfire::Desfire; -use desfire::iso7816_4::apducommand::APDUCommand; use desfire::iso7816_4::apduresponse::APDUResponse; -use desfire::error::{Error as DesfireError, Error}; +use desfire::error::{Error as DesfireError}; use std::convert::TryFrom; -use std::ops::Deref; +use std::sync::Arc; +use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION; +use rsasl::property::AuthId; +use crate::api::auth::fabfire::FabFireCardKey; enum FabFireError { ParseError, SerializationError, + DeserializationError(serde_json::Error), CardError(DesfireError), + InvalidMagic(String), + InvalidToken(String), + InvalidURN(String), + InvalidCredentials(String), + Session(SessionError), } impl Debug for FabFireError { @@ -23,7 +31,13 @@ impl Debug for FabFireError { match self { FabFireError::ParseError => write!(f, "ParseError"), FabFireError::SerializationError => write!(f, "SerializationError"), + FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e), FabFireError::CardError(err) => write!(f, "CardError: {}", err), + FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic), + FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token), + FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn), + FabFireError::InvalidCredentials(credentials) => write!(f, "InvalidCredentials: {}", credentials), + FabFireError::Session(err) => write!(f, "Session: {}", err), } } } @@ -33,7 +47,13 @@ impl Display for FabFireError { match self { FabFireError::ParseError => write!(f, "ParseError"), FabFireError::SerializationError => write!(f, "SerializationError"), + FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e), FabFireError::CardError(err) => write!(f, "CardError: {}", err), + FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic), + FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token), + FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn), + FabFireError::InvalidCredentials(credentials) => write!(f, "InvalidCredentials: {}", credentials), + FabFireError::Session(err) => write!(f, "Session: {}", err), } } } @@ -43,45 +63,61 @@ impl MechanismError for FabFireError { match self { FabFireError::ParseError => MechanismErrorKind::Parse, FabFireError::SerializationError => MechanismErrorKind::Protocol, + FabFireError::DeserializationError(_) => MechanismErrorKind::Parse, FabFireError::CardError(_) => MechanismErrorKind::Protocol, + FabFireError::InvalidMagic(_) => MechanismErrorKind::Protocol, + FabFireError::InvalidToken(_) => MechanismErrorKind::Protocol, + FabFireError::InvalidURN(_) => MechanismErrorKind::Protocol, + FabFireError::InvalidCredentials(_) => MechanismErrorKind::Protocol, + FabFireError::Session(_) => MechanismErrorKind::Protocol, } } } #[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] struct CardInfo { - card_uid: [u8; 7], + #[serde(rename = "UID", with = "hex")] + uid: [u8; 7], key_old: Option>, - key_new: Option> + key_new: Option>, } struct KeyInfo { key_id: u8, - key: Box<[u8]> + key: Box<[u8]>, } struct AuthInfo { rnd_a: Vec, rnd_b: Vec, - iv: Vec + iv: Vec, } #[derive(Debug, Deserialize, Serialize)] #[serde(tag = "Cmd")] enum CardCommand { message { + #[serde(rename = "MssgID", skip_serializing_if = "Option::is_none")] msg_id: Option, + #[serde(rename = "ClrTxt", skip_serializing_if = "Option::is_none")] clr_txt: Option, + #[serde(rename = "AddnTxt", skip_serializing_if = "Option::is_none")] addn_txt: Option, }, sendPICC { - data: String + #[serde(deserialize_with = "hex::deserialize", serialize_with = "hex::serialize_upper")] + data: Vec + }, + readPICC { + #[serde(deserialize_with = "hex::deserialize", serialize_with = "hex::serialize_upper")] + data: Vec }, haltPICC, Key { data: String }, - ConfirmUser + ConfirmUser, } enum Step { @@ -92,7 +128,6 @@ enum Step { GetToken, Authenticate1, Authenticate2, - Authenticate3, } pub struct FabFire { @@ -117,52 +152,70 @@ impl Authentication for FabFire { fn step(&mut self, session: &mut SessionData, input: Option<&[u8]>, writer: &mut dyn Write) -> StepResult { match self.step { Step::New => { + // println!("Step: New"); //receive card info (especially card UID) from reader return match input { - None => { Err(SessionError::InputDataRequired) }, + None => { Err(SessionError::InputDataRequired) } Some(cardinfo) => { self.card_info = match serde_json::from_slice(cardinfo) { Ok(card_info) => Some(card_info), - Err(_) => { - return Err(FabFireError::ParseError.into()) + Err(e) => { + // eprintln!("{:?}", e); + return Err(FabFireError::DeserializationError(e).into()); + } + }; + //select application + let buf = match self.desfire.select_application_cmd(self.app_id) { + Ok(buf) => match Vec::::try_from(buf) { + Ok(data) => data, + Err(e) => { + // eprintln!("Failed to convert APDUCommand to Vec: {:?}", e); + return Err(FabFireError::SerializationError.into()); + } + }, + Err(e) => { + // eprintln!("Failed to generate APDUCommand: {:?}", e); + return Err(FabFireError::SerializationError.into()); + } + }; + let cmd = CardCommand::sendPICC { data: buf }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::SelectApp; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len()))) + } + Err(e) => { + // eprintln!("Failed to serialize APDUCommand: {:?}", e); + Err(FabFireError::SerializationError.into()) } }; - self.step = Step::SelectApp; - Ok(rsasl::session::Step::NeedsMore(None)) } - } + }; } Step::SelectApp => { - //select application - let buf = match self.desfire.select_application_cmd(self.app_id) { - Ok(buf) => match Vec::::try_from(buf) { - Ok(data) => data, - Err(_) => { - return Err(FabFireError::SerializationError.into()) - } - }, - Err(_) => { - return Err(FabFireError::SerializationError.into()) - } - }; - let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; - return match serde_json::to_writer(writer, &cmd) { - Ok(_) => { - self.step = Step::VerifyMagic; - Ok(rsasl::session::Step::NeedsMore(None)) - } - Err(_) => { - Err(FabFireError::SerializationError.into()) - } - } - } - Step::VerifyMagic => { + // println!("Step: SelectApp"); // check that we successfully selected the application - let response = match input { - None => {return Err(SessionError::InputDataRequired)}, - Some(buf) => APDUResponse::new(buf) + let response: CardCommand = match input { + None => { return Err(SessionError::InputDataRequired); } + Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) { + Ok(response) => response, + Err(e) => { + // eprintln!("{:?}", e); + return Err(e.into()); + } + } }; - response.check().map_err(|e| FabFireError::CardError(e))?; + + let apdu_response = match response { + CardCommand::readPICC { data } => { APDUResponse::new(&*data) } + _ => { + // eprintln!("Unexpected response: {:?}", response); + return Err(FabFireError::ParseError.into()); + } + }; + + apdu_response.check().map_err(|e| FabFireError::CardError(e))?; // request the contents of the file containing the magic string const MAGIC_FILE_ID: u8 = 0x01; @@ -171,40 +224,58 @@ impl Authentication for FabFire { Ok(buf) => match Vec::::try_from(buf) { Ok(data) => data, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }; - let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; - return match serde_json::to_writer(writer, &cmd) { - Ok(_) => { - self.step = Step::GetURN; - Ok(rsasl::session::Step::NeedsMore(None)) + let cmd = CardCommand::sendPICC { data: buf }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::VerifyMagic; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len()))) } Err(_) => { Err(FabFireError::SerializationError.into()) } - } - } - Step::GetURN => { - // verify the magic string to determine that we have a valid fabfire card - let response = match input { - None => {return Err(SessionError::InputDataRequired)}, - Some(buf) => APDUResponse::new(buf) }; - match response.check() { + } + Step::VerifyMagic => { + // println!("Step: VerifyMagic"); + // verify the magic string to determine that we have a valid fabfire card + let response: CardCommand = match input { + None => { return Err(SessionError::InputDataRequired); } + Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) { + Ok(response) => response, + Err(e) => { + // eprintln!("{:?}", e); + return Err(e.into()); + } + } + }; + + let apdu_response = match response { + CardCommand::readPICC { data } => { APDUResponse::new(&*data) } + _ => { + // eprintln!("Unexpected response: {:?}", response); + return Err(FabFireError::ParseError.into()); + } + }; + + + match apdu_response.check() { Ok(_) => { - match response.body { + match apdu_response.body { Some(data) => { if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) { return Err(FabFireError::ParseError.into()); } } None => { - return Err(FabFireError::ParseError.into()) + return Err(FabFireError::ParseError.into()); } }; } @@ -221,92 +292,143 @@ impl Authentication for FabFire { Ok(buf) => match Vec::::try_from(buf) { Ok(data) => data, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }; - let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; - return match serde_json::to_writer(writer, &cmd) { - Ok(_) => { - self.step = Step::GetToken; - Ok(rsasl::session::Step::NeedsMore(None)) + let cmd = CardCommand::sendPICC { data: buf }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::GetURN; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len()))) } Err(_) => { Err(FabFireError::SerializationError.into()) } - } - } - Step::GetToken => { - // parse the urn and match it to our local urn - let response = match input { - None => {return Err(SessionError::InputDataRequired)}, - Some(buf) => APDUResponse::new(buf) }; - match response.check() { + } + Step::GetURN => { + // println!("Step: GetURN"); + // parse the urn and match it to our local urn + let response: CardCommand = match input { + None => { return Err(SessionError::InputDataRequired); } + Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) { + Ok(response) => response, + Err(e) => { + // eprintln!("{:?}", e); + return Err(e.into()); + } + } + }; + + let apdu_response = match response { + CardCommand::readPICC { data } => { APDUResponse::new(&*data) } + _ => { + // eprintln!("Unexpected response: {:?}", response); + return Err(FabFireError::ParseError.into()); + } + }; + + + match apdu_response.check() { Ok(_) => { - match response.body { + match apdu_response.body { Some(data) => { - if String::from_utf8(data).unwrap() != self.local_urn { + let received_urn = String::from_utf8(data).unwrap(); + if received_urn != self.local_urn { + // eprintln!("URN mismatch: {:?} != {:?}", received_urn, self.local_urn); return Err(FabFireError::ParseError.into()); } } None => { - return Err(FabFireError::ParseError.into()) + // eprintln!("No data in response"); + return Err(FabFireError::ParseError.into()); } }; } - Err(_) => { + Err(e) => { + // eprintln!("Invalid response: {:?}", e); return Err(FabFireError::ParseError.into()); } } // request the contents of the file containing the URN const TOKEN_FILE_ID: u8 = 0x03; - let buf = match self.desfire.read_data_chunk_cmd(TOKEN_FILE_ID, 0, 47) { // TODO: support data longer than 47 Bytes + let buf = match self.desfire.read_data_chunk_cmd(TOKEN_FILE_ID, 0, MAX_BYTES_PER_TRANSACTION) { // TODO: support data longer than 47 Bytes Ok(buf) => match Vec::::try_from(buf) { Ok(data) => data, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }; - let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; - return match serde_json::to_writer(writer, &cmd) { - Ok(_) => { - self.step = Step::Authenticate1; - Ok(rsasl::session::Step::NeedsMore(None)) + let cmd = CardCommand::sendPICC { data: buf }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::GetToken; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len()))) } Err(_) => { Err(FabFireError::SerializationError.into()) } - } - } - Step::Authenticate1 => { - // parse the token and select the appropriate user - let response = match input { - None => {return Err(SessionError::InputDataRequired)}, - Some(buf) => APDUResponse::new(buf) }; - match response.check() { + } + Step::GetToken => { + // println!("Step: GetToken"); + // parse the token and select the appropriate user + let response: CardCommand = match input { + None => { return Err(SessionError::InputDataRequired); } + Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) { + Ok(response) => response, + Err(e) => { + // eprintln!("{:?}", e); + return Err(e.into()); + } + } + }; + + let apdu_response = match response { + CardCommand::readPICC { data } => { APDUResponse::new(&*data) } + _ => { + // eprintln!("Unexpected response: {:?}", response); + return Err(FabFireError::ParseError.into()); + } + }; + + + match apdu_response.check() { Ok(_) => { - match response.body { + match apdu_response.body { Some(data) => { - if String::from_utf8(data).unwrap() != "LoremIpsum" { // FIXME: match against user db - return Err(FabFireError::ParseError.into()); - } + let token = String::from_utf8(data).unwrap(); + session.set_property::(Arc::new(token.trim_matches(char::from(0)).to_string())); + let key = match session.get_property_or_callback::() { + Ok(Some(key)) => Box::from(key.as_slice()), + Ok(None) => { + return Err(FabFireError::InvalidCredentials("No keys on file for token".to_string()).into()); + } + Err(e) => { + return Err(FabFireError::Session(e).into()); + } + }; + self.key_info = Some(KeyInfo{ key_id: 0x01, key }); } None => { - return Err(FabFireError::ParseError.into()) + // eprintln!("No data in response"); + return Err(FabFireError::ParseError.into()); } }; } - Err(_) => { + Err(e) => { + // eprintln!("Invalid response: {:?}", e); return Err(FabFireError::ParseError.into()); } } @@ -315,62 +437,85 @@ impl Authentication for FabFire { Ok(buf) => match Vec::::try_from(buf) { Ok(data) => data, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }, Err(_) => { - return Err(FabFireError::SerializationError.into()) + return Err(FabFireError::SerializationError.into()); } }; - let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; - return match serde_json::to_writer(writer, &cmd) { - Ok(_) => { - self.step = Step::Authenticate2; - Ok(rsasl::session::Step::NeedsMore(None)) + let cmd = CardCommand::sendPICC { data: buf }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::Authenticate1; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len()))) } Err(_) => { Err(FabFireError::SerializationError.into()) } - } - - } - Step::Authenticate2 => { - let response = match input { - None => {return Err(SessionError::InputDataRequired)}, - Some(buf) => APDUResponse::new(buf) }; - match response.check() { + } + Step::Authenticate1 => { + // println!("Step: Authenticate1"); + let response: CardCommand = match input { + None => { return Err(SessionError::InputDataRequired); } + Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) { + Ok(response) => response, + Err(e) => { + // eprintln!("{:?}", e); + return Err(e.into()); + } + } + }; + + let apdu_response = match response { + CardCommand::readPICC { data } => { APDUResponse::new(&*data) } + _ => { + // eprintln!("Unexpected response: {:?}", response); + return Err(FabFireError::ParseError.into()); + } + }; + + + match apdu_response.check() { Ok(_) => { - match response.body { + match apdu_response.body { Some(data) => { let rnd_b_enc = data.as_slice(); //FIXME: This is ugly, we should find a better way to make the function testable //TODO: Check if we need a CSPRNG here let rnd_a: [u8; 16] = rand::random(); - println!("RND_A: {:x?}", rnd_a); + // println!("RND_A: {:x?}", rnd_a); - let (cmd_challenge_response, rnd_b, iv) = self.desfire.authenticate_iso_aes_response_cmd(rnd_b_enc, &*(self.key_info.as_ref().unwrap().key), &rnd_a).unwrap(); - self.auth_info = Some(AuthInfo{rnd_a: Vec::::from(rnd_a), rnd_b, iv}); + let (cmd_challenge_response, + rnd_b, + iv) = self.desfire.authenticate_iso_aes_response_cmd( + rnd_b_enc, + &*(self.key_info.as_ref().unwrap().key), + &rnd_a).unwrap(); + self.auth_info = Some(AuthInfo { rnd_a: Vec::::from(rnd_a), rnd_b, iv }); let buf = match Vec::::try_from(cmd_challenge_response) { - Ok(data) => data, - Err(_) => { - return Err(FabFireError::SerializationError.into()) - } + Ok(data) => data, + Err(_) => { + return Err(FabFireError::SerializationError.into()); + } }; - let cmd = CardCommand::sendPICC { data: hex::encode_upper(buf) }; - return match serde_json::to_writer(writer, &cmd) { - Ok(_) => { - self.step = Step::Authenticate3; - Ok(rsasl::session::Step::NeedsMore(None)) + let cmd = CardCommand::sendPICC { data: buf }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::Authenticate2; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len()))) } Err(_) => { Err(FabFireError::SerializationError.into()) } - } + }; } None => { - return Err(FabFireError::ParseError.into()) + return Err(FabFireError::ParseError.into()); } }; } @@ -379,36 +524,67 @@ impl Authentication for FabFire { } } } - Step::Authenticate3 => { - let response = match input { - None => {return Err(SessionError::InputDataRequired)}, - Some(buf) => APDUResponse::new(buf) + Step::Authenticate2 => { + // println!("Step: Authenticate2"); + let response: CardCommand = match input { + None => { return Err(SessionError::InputDataRequired); } + Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) { + Ok(response) => response, + Err(e) => { + // eprintln!("{:?}", e); + return Err(e.into()); + } + } }; - match response.check() { + + let apdu_response = match response { + CardCommand::readPICC { data } => { APDUResponse::new(&*data) } + _ => { + // eprintln!("Unexpected response: {:?}", response); + return Err(FabFireError::ParseError.into()); + } + }; + + + match apdu_response.check() { Ok(_) => { - match response.body { + match apdu_response.body { Some(data) => { match self.auth_info.as_ref() { - None => {return Err(FabFireError::ParseError.into())} + None => { return Err(FabFireError::ParseError.into()); } Some(auth_info) => { if self.desfire.authenticate_iso_aes_verify( data.as_slice(), auth_info.rnd_a.as_slice(), auth_info.rnd_b.as_slice(), &*(self.key_info.as_ref().unwrap().key), auth_info.iv.as_slice()).is_ok() { - // TODO: Do stuff with the info that we are authenticated - return Ok(rsasl::session::Step::Done(None)); + + let cmd = CardCommand::message{ + msg_id: Some(4), + clr_txt: None, + addn_txt: Some("".to_string()), + }; + return match serde_json::to_vec(&cmd) { + Ok(send_buf) => { + self.step = Step::Authenticate1; + writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?; + return Ok(rsasl::session::Step::Done(Some(send_buf.len()))) + } + Err(_) => { + Err(FabFireError::SerializationError.into()) + } + }; } } } } None => { - return Err(FabFireError::ParseError.into()) + return Err(FabFireError::ParseError.into()); } }; } Err(_) => { - return Err(FabFireError::ParseError.into()); + return Err(FabFireError::InvalidCredentials(format!("{}", apdu_response)).into()); } } } @@ -416,5 +592,4 @@ impl Authentication for FabFire { return Ok(rsasl::session::Step::Done(None)); } - } \ No newline at end of file diff --git a/src/db/user.rs b/src/db/user.rs index 3a200dd..6d37d35 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -95,7 +95,7 @@ pub struct UserData { /// Additional data storage #[serde(flatten, skip_serializing_if = "HashMap::is_empty")] - kv: HashMap, + pub kv: HashMap, } impl UserData { From 926d200c938cff487a7ee05d8e2ed21d1fed2001 Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Sun, 13 Mar 2022 18:05:36 +0100 Subject: [PATCH 28/37] update desfire crate to version on crates.io --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ca7a144..468e3b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ rustls-pemfile = "0.2" async-rustls = "0.2" # Desfire -desfire = { git = "https://gitlab.com/fabinfra/fabaccess/nfc_rs.git", branch = "main" } +desfire = "0.2.0-alpha1" hex = { version = "0.4.3", features = ["serde"] } linkme = "0.2" From 15f31ffd7cf731bbc7397da4544d51cd19326aaf Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 13 Mar 2022 23:31:00 +0100 Subject: [PATCH 29/37] Update API --- schema | 2 +- src/api.rs | 131 ++++++++++------------ src/api/auth.rs | 263 +++++++++++++++++++++----------------------- src/api/machine.rs | 116 ++++++++++--------- src/api/machines.rs | 4 +- 5 files changed, 244 insertions(+), 272 deletions(-) diff --git a/schema b/schema index 18ed9c2..c9283eb 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit 18ed9c2ae6a221f57d19e255165c7ebc4508e9af +Subproject commit c9283ebd696ed6dd428a7c3d24820889f7ab4bf3 diff --git a/src/api.rs b/src/api.rs index 0918dde..02bacea 100644 --- a/src/api.rs +++ b/src/api.rs @@ -7,6 +7,9 @@ use slog::Logger; use std::sync::Arc; use capnp::capability::{Promise}; +use rsasl::mechname::Mechname; +use rsasl::SASL; +use auth::State; use crate::schema::connection_capnp; use crate::connection::Session; @@ -31,99 +34,85 @@ pub struct Bootstrap { db: Databases, nw: Arc, - - session: Rc>>, + ctx: SASL, } impl Bootstrap { pub fn new(log: Logger, db: Databases, nw: Arc) -> Self { info!(log, "Created Bootstrap"); - let session = Rc::new(RefCell::new(None)); - Self { session, db, nw, log } + let mut ctx = SASL::new(); + ctx.install_callback(Arc::new(auth::CB::new(db.userdb.clone()))); + Self { db, nw, log, ctx } } } use connection_capnp::{API_VERSION_MAJOR, API_VERSION_MINOR, API_VERSION_PATCH}; use connection_capnp::bootstrap::*; +use crate::api::auth::Auth; use crate::RELEASE; impl connection_capnp::bootstrap::Server for Bootstrap { - fn authentication_system(&mut self, - _: AuthenticationSystemParams, - mut res: AuthenticationSystemResults - ) -> Promise<(), capnp::Error> { - // TODO: Forbid mutltiple authentication for now - // TODO: When should we allow multiple auth and how do me make sure that does not leak - // priviledges (e.g. due to previously issues caps)? - - // If this Rc has a strong count of 1 then there's no other cap issued yet meaning we can - // safely transform the inner session with an auth. - if Rc::strong_count(&self.session) == 1 { - let session = Rc::clone(&self.session); - let db = self.db.clone(); - res.get().set_authentication_system(capnp_rpc::new_client( - auth::Auth::new(self.log.new(o!()), db, session)) - ); - } - - Promise::ok(()) - } - - fn machine_system(&mut self, - _: MachineSystemParams, - mut res: MachineSystemResults - ) -> Promise<(), capnp::Error> { - if let Some(session) = self.session.borrow().deref() { - debug!(self.log, "Giving MachineSystem cap to user {} with perms:", session.authzid); - for r in session.perms.iter() { - debug!(session.log, " {}", r); - } - - // TODO actual permission check and stuff - // Right now we only check that the user has authenticated at all. - let c = capnp_rpc::new_client(Machines::new(Rc::clone(&self.session), self.nw.clone())); - res.get().set_machine_system(c); - } - - Promise::ok(()) - } - - fn user_system( - &mut self, - _: UserSystemParams, - mut results: UserSystemResults - ) -> Promise<(), capnp::Error> { - if self.session.borrow().is_some() { - // TODO actual permission check and stuff - // Right now we only check that the user has authenticated at all. - let c = capnp_rpc::new_client(Users::new(Rc::clone(&self.session), self.db.userdb.clone())); - results.get().set_user_system(c); - } - - Promise::ok(()) - } - fn get_a_p_i_version( &mut self, _: GetAPIVersionParams, - mut results: GetAPIVersionResults - ) -> Promise<(), capnp::Error> { - let builder = results.get(); - let mut builder = builder.init_version(); - builder.set_major(API_VERSION_MAJOR); - builder.set_minor(API_VERSION_MINOR); - builder.set_patch(API_VERSION_PATCH); + _: GetAPIVersionResults, + ) -> Promise<(), ::capnp::Error> { Promise::ok(()) } fn get_server_release( &mut self, _: GetServerReleaseParams, - mut results: GetServerReleaseResults - ) -> Promise<(), capnp::Error> { - let mut builder = results.get(); - builder.set_name("bffh"); - builder.set_release(RELEASE); + mut result: GetServerReleaseResults, + ) -> Promise<(), ::capnp::Error> { + let mut builder = result.get(); + builder.set_name("bffhd"); + builder.set_release(crate::RELEASE); + Promise::ok(()) + } + + fn mechanisms( + &mut self, + _: MechanismsParams, + mut result: MechanismsResults, + ) -> Promise<(), ::capnp::Error> { + let mut builder = result.get(); + let mechs: Vec<_> = self.ctx.server_mech_list() + .into_iter() + .map(|m| m.mechanism.as_str()) + .collect(); + let mut mechbuilder = builder.init_mechs(mechs.len() as u32); + for (i,m) in mechs.iter().enumerate() { + mechbuilder.set(i as u32, m); + } + + Promise::ok(()) + } + + fn create_session( + &mut self, + params: CreateSessionParams, + mut result: CreateSessionResults, + ) -> Promise<(), ::capnp::Error> { + let params = pry!(params.get()); + let mechanism: &str = pry!(params.get_mechanism()); + + let mechname = mechanism.as_bytes(); + let state = if let Ok(mechname) = Mechname::new(mechname) { + if let Ok(session) = self.ctx.server_start(mechname) { + State::Running(session) + } else { + State::Aborted + } + } else { + State::InvalidMechanism + }; + + let auth = Auth::new(self.log.clone(), self.db.clone(), state, self.nw.clone()); + + let mut builder = result.get(); + builder.set_authentication(capnp_rpc::new_client(auth)); + Promise::ok(()) } } diff --git a/src/api/auth.rs b/src/api/auth.rs index 662d3d5..7d518b0 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -3,32 +3,35 @@ //! Authorization is over in `access.rs` //! Authentication using SASL -use std::sync::Arc; -use std::rc::Rc; use std::cell::RefCell; use std::io::Cursor; - +use std::rc::Rc; +use std::sync::Arc; use slog::Logger; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; -use capnp::capability::{Promise}; +use crate::api::machines::Machines; +use capnp::capability::Promise; use rsasl::callback::Callback; use rsasl::error::SessionError; use rsasl::mechname::Mechname; use rsasl::property::{AuthId, Password}; -use rsasl::SASL; +use rsasl::session::Session as RsaslSession; use rsasl::session::Step; -use rsasl::validate::{Validation, validations}; +use rsasl::validate::{validations, Validation}; +use rsasl::SASL; +use crate::api::users::Users; use crate::api::Session; -pub use crate::schema::authenticationsystem_capnp as auth_system; use crate::db::Databases; +pub use crate::schema::authenticationsystem_capnp as auth_system; -use crate::db::user::{Internal as UserDB, User}; use crate::db::access::AccessControl as AccessDB; +use crate::db::user::{Internal as UserDB, User, UserId}; +use crate::network::Network; pub struct AppData { userdb: Arc, @@ -37,7 +40,7 @@ pub struct SessionData { authz: Option, } -struct CB { +pub struct CB { userdb: Arc, } impl CB { @@ -47,168 +50,151 @@ impl CB { } impl Callback for CB { - fn validate(&self, session: &mut rsasl::session::SessionData, validation: Validation, _mechanism: &Mechname) -> Result<(), SessionError> { + fn validate( + &self, + session: &mut rsasl::session::SessionData, + validation: Validation, + _mechanism: &Mechname, + ) -> Result<(), SessionError> { let ret = match validation { validations::SIMPLE => { - let authid = session .get_property::() .ok_or(SessionError::no_property::())?; - let pass = session.get_property::() - .ok_or(SessionError::no_property::())?; + let pass = session + .get_property::() + .ok_or(SessionError::no_property::())?; - if self.userdb.login(authid.as_ref(), pass.as_bytes()).unwrap().is_some() { - return Ok(()) + if self + .userdb + .login(authid.as_ref(), pass.as_bytes()) + .unwrap() + .is_some() + { + return Ok(()); } SessionError::AuthenticationFailure } - _ => { - SessionError::no_validate(validation) - } + _ => SessionError::no_validate(validation), }; Err(ret) } } +pub enum State { + InvalidMechanism, + Finished, + Aborted, + Running(RsaslSession), +} + pub struct Auth { - pub ctx: SASL, - session: Rc>>, userdb: Arc, access: Arc, + state: State, log: Logger, + network: Arc, } impl Auth { - pub fn new(log: Logger, dbs: Databases, session: Rc>>) -> Self { - let mut ctx = SASL::new(); - ctx.install_callback(Arc::new(CB::new(dbs.userdb.clone()))); + pub fn new(log: Logger, dbs: Databases, state: State, network: Arc) -> Self { + Self { + log, + userdb: dbs.userdb.clone(), + access: dbs.access.clone(), + state, + network, + } + } - Self { log, ctx, session, userdb: dbs.userdb.clone(), access: dbs.access.clone() } + fn build_error(&self, response: response::Builder) { + use crate::schema::authenticationsystem_capnp::response::Error as ErrorCode; + if let State::Running(_) = self.state { + return; + } + + let mut builder = response.init_failed(); + match self.state { + State::InvalidMechanism => builder.set_code(ErrorCode::BadMechanism), + State::Finished => builder.set_code(ErrorCode::Aborted), + State::Aborted => builder.set_code(ErrorCode::Aborted), + _ => unreachable!(), + } } } use crate::schema::authenticationsystem_capnp::*; -impl authentication_system::Server for Auth { - fn mechanisms(&mut self, - _: authentication_system::MechanismsParams, - mut res: authentication_system::MechanismsResults +impl authentication::Server for Auth { + fn step( + &mut self, + params: authentication::StepParams, + mut results: authentication::StepResults, ) -> Promise<(), capnp::Error> { - /*let mechs = match self.ctx.server_mech_list() { - Ok(m) => m, - Err(e) => { - return Promise::err(capnp::Error { - kind: capnp::ErrorKind::Failed, - description: format!("SASL Failure: {}", e), - }) - }, - }; + let mut builder = results.get(); + if let State::Running(mut session) = std::mem::replace(&mut self.state, State::Aborted) { + let data: &[u8] = pry!(pry!(params.get()).get_data()); + let mut out = Cursor::new(Vec::new()); + match session.step(Some(data), &mut out) { + Ok(Step::Done(data)) => { + self.state = State::Finished; - let mechvec: Vec<&str> = mechs.iter().collect(); + let uid = pry!(session.get_property::().ok_or(capnp::Error::failed( + "Authentication didn't provide an authid as required".to_string() + ))); + let user = self.userdb.get_user(uid.as_str()).unwrap() + .expect("Just auth'ed user was not found?!"); - let mut res_mechs = res.get().init_mechs(mechvec.len() as u32); - for (i, m) in mechvec.into_iter().enumerate() { - res_mechs.set(i as u32, m); - }*/ - // For now, only PLAIN - let mut res_mechs = res.get().init_mechs(1); - res_mechs.set(0, "PLAIN"); + let mut builder = builder.init_successful(); + if data.is_some() { + builder.set_additional_data(out.into_inner().as_slice()); + } + + let mut builder = builder.init_session(); + let perms = pry!(self.access.collect_permrules(&user.data) + .map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))); + + let session = Rc::new(RefCell::new(Some(Session::new( + self.log.clone(), + user.id, + uid.to_string(), + user.data.roles.into_boxed_slice(), + perms.into_boxed_slice(), + )))); + + builder.set_machine_system(capnp_rpc::new_client(Machines::new( + session.clone(), + self.network.clone(), + ))); + builder.set_user_system(capnp_rpc::new_client(Users::new( + session.clone(), + self.userdb.clone(), + ))); + } + Ok(Step::NeedsMore(_)) => { + self.state = State::Aborted; + self.build_error(builder); + } + Err(_) => { + self.state = State::Aborted; + self.build_error(builder); + } + } + } else { + self.build_error(builder); + } Promise::ok(()) } - // TODO: return Outcome instead of exceptions - fn start(&mut self, - params: authentication_system::StartParams, - mut res: authentication_system::StartResults + fn abort( + &mut self, + _: authentication::AbortParams, + _: authentication::AbortResults, ) -> Promise<(), capnp::Error> { - let req = pry!(pry!(params.get()).get_request()); - - // Extract the MECHANISM the client wants to use and start a session. - // Or fail at that and thrown an exception TODO: return Outcome - let mech = pry!(req.get_mechanism()); - if pry!(req.get_mechanism()) != "PLAIN" { - return Promise::err(capnp::Error { - kind: capnp::ErrorKind::Failed, - description: format!("Invalid SASL mech"), - }) - } - - let mech = Mechname::new(mech.as_bytes()).unwrap(); - - let mut session = match self.ctx.server_start(mech) { - Ok(s) => s, - Err(e) => - return Promise::err(capnp::Error { - kind: capnp::ErrorKind::Failed, - description: format!("SASL error: {}", e), - }), - }; - - let mut out = Cursor::new(Vec::new()); - - // If the client has provided initial data go use that - use request::initial_response::Which; - let step_res = match req.get_initial_response().which() { - Err(capnp::NotInSchema(_)) => - return Promise::err(capnp::Error { - kind: capnp::ErrorKind::Failed, - description: "Initial data is badly formatted".to_string(), - }), - - Ok(Which::None(_)) => { - // FIXME: Actually this needs to indicate NO data instead of SOME data of 0 length - session.step(Option::<&[u8]>::None, &mut out) - } - Ok(Which::Initial(data)) => { - session.step(Some(pry!(data)), &mut out) - } - }; - - // The step may either return an error, a success or the need for more data - // TODO: Set the session user. Needs a lookup though <.> - - match step_res { - Ok(Step::Done(b)) => { - let user = session - .get_property::() - .and_then(|data| { - self.userdb.get_user(data.as_str()).unwrap() - }) - .expect("Authentication returned OK but the given AuthId is invalid"); - - let perms = pry!(self.access.collect_permrules(&user.data) - .map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))); - self.session.replace(Some(Session::new( - self.log.new(o!()), - user.id, - "".to_string(), - user.data.roles.into_boxed_slice(), - perms.into_boxed_slice() - ))); - - let mut outcome = pry!(res.get().get_response()).init_outcome(); - outcome.reborrow().set_result(response::Result::Successful); - if b.is_some() { - outcome.init_additional_data().set_additional(&out.get_ref()); - } - Promise::ok(()) - }, - Ok(Step::NeedsMore(b)) => { - if b.is_some() { - pry!(res.get().get_response()).set_challence(&out.get_ref()); - } - Promise::ok(()) - } - Err(e) => { - let mut outcome = pry!(res.get().get_response()).init_outcome(); - outcome.reborrow().set_result(response::Result::InvalidCredentials); - let text = format!("{}", e); - outcome.set_help_text(&text); - Promise::ok(()) - } - } + self.state = State::Aborted; + Promise::ok(()) } } @@ -262,11 +248,11 @@ pub struct AuthenticationData { // Authentication has two parts: Granting the authentication itself and then performing the // authentication. -// Granting the authentication checks if +// Granting the authentication checks if // a) the given authcid fits with the given (authMethod, kvs). In general a failure here indicates // a programming failure — the authcid come from the same source as that tuple // b) the given authcid may authenticate as the given authzid. E.g. if a given client certificate -// has been configured for that user, if a GSSAPI user maps to a given user, +// has been configured for that user, if a GSSAPI user maps to a given user, #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub enum AuthError { @@ -278,5 +264,4 @@ pub enum AuthError { MalformedAuthzid, /// User may not use that authorization id NotAllowedAuthzid, - } diff --git a/src/api/machine.rs b/src/api/machine.rs index c2774b3..d2e1027 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -27,59 +27,6 @@ impl Machine { } impl info::Server for Machine { - fn get_machine_info_extended( - &mut self, - _: info::GetMachineInfoExtendedParams, - mut results: info::GetMachineInfoExtendedResults, - ) -> Promise<(), capnp::Error> { - let machine = self.machine.get_inner(); - let perms = self.perms.clone(); - let f = async move { - if perms.manage { - let builder = results.get(); - let mut extinfo = builder.init_machine_info_extended(); - let guard = machine.lock().await; - - // "previous" user - if let Some(user) = guard.get_previous() { - let mut previous = extinfo.reborrow().init_transfer_user(); - previous.set_username(&user.uid); - } - - let state = guard.read_state(); - let state_lock = state.lock_ref(); - match state_lock.state { - Status::Free => {} - Status::InUse(ref user) => if user.is_some() { - let user = user.as_ref().unwrap(); - let mut current = extinfo.init_current_user(); - current.set_username(&user.uid); - } - Status::ToCheck(ref user) => { - let mut current = extinfo.init_current_user(); - current.set_username(&user.uid); - } - Status::Blocked(ref user) => { - let mut current = extinfo.init_current_user(); - current.set_username(&user.uid); - } - Status::Disabled => {} - Status::Reserved(ref user) => { - let mut current = extinfo.init_current_user(); - current.set_username(&user.uid); - } - } - } - - Ok(()) - }; - - let g = smol::future::race(f, smol::Timer::after(Duration::from_secs(4)) - .map(|_| Err(capnp::Error::failed("Waiting for machine lock timed out!".to_string())))); - - Promise::from_future(g) - } - fn get_reservation_list( &mut self, _: info::GetReservationListParams, @@ -170,12 +117,6 @@ impl in_use::Server for Machine { } } -impl transfer::Server for Machine { -} - -impl check::Server for Machine { -} - impl manage::Server for Machine { fn force_free(&mut self, _: manage::ForceFreeParams, @@ -229,6 +170,60 @@ impl manage::Server for Machine { }; Promise::from_future(f) } + + fn get_machine_info_extended( + &mut self, + _: manage::GetMachineInfoExtendedParams, + mut results: manage::GetMachineInfoExtendedResults, + ) -> Promise<(), capnp::Error> { + let machine = self.machine.get_inner(); + let perms = self.perms.clone(); + let f = async move { + if perms.manage { + let builder = results.get(); + let mut extinfo = builder; + let guard = machine.lock().await; + + // "previous" user + if let Some(user) = guard.get_previous() { + let mut previous = extinfo.reborrow().init_last_user(); + previous.set_username(&user.uid); + } + + let state = guard.read_state(); + let state_lock = state.lock_ref(); + match state_lock.state { + Status::Free => {} + Status::InUse(ref user) => if user.is_some() { + let user = user.as_ref().unwrap(); + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + Status::ToCheck(ref user) => { + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + Status::Blocked(ref user) => { + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + Status::Disabled => {} + Status::Reserved(ref user) => { + let mut current = extinfo.init_current_user(); + current.set_username(&user.uid); + } + } + } + + Ok(()) + }; + + let g = smol::future::race(f, smol::Timer::after(Duration::from_secs(4)) + .map(|_| Err(capnp::Error::failed("Waiting for machine lock timed out!".to_string())))); + + Promise::from_future(g) + } + } impl admin::Server for Machine { @@ -244,6 +239,9 @@ impl admin::Server for Machine { APIMState::InUse => MachineState::used(Some(uid)), APIMState::Reserved => MachineState::reserved(uid), APIMState::ToCheck => MachineState::check(uid), + APIMState::Totakeover => return Promise::err(::capnp::Error::unimplemented( + "totakeover not implemented".to_string(), + )), }; let machine = self.machine.get_inner(); let f = async move { diff --git a/src/api/machines.rs b/src/api/machines.rs index 03c0be1..6635902 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -125,7 +125,7 @@ impl machines::Server for Machines { let permissions = &session.as_ref().unwrap().perms; if let Some(machine) = network.machines.get(&id) { - let mut builder = results.get().init_machine(); + let mut builder = results.get(); fill_machine_builder( &mut builder, &machine, @@ -171,7 +171,7 @@ impl machines::Server for Machines { let permissions = &session.as_ref().unwrap().perms; if let Some(machine) = network.machines.get(&id) { - let mut builder = results.get().init_machine(); + let mut builder = results.get(); fill_machine_builder( &mut builder, &machine, From 75c449c83afe678e3ff7b2f53106a4babeec2aa5 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 13 Mar 2022 23:58:03 +0100 Subject: [PATCH 30/37] Fix auth --- src/api.rs | 14 ++++++++++---- src/api/auth.rs | 5 ++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/api.rs b/src/api.rs index 02bacea..c9ae961 100644 --- a/src/api.rs +++ b/src/api.rs @@ -99,12 +99,18 @@ impl connection_capnp::bootstrap::Server for Bootstrap { let mechname = mechanism.as_bytes(); let state = if let Ok(mechname) = Mechname::new(mechname) { - if let Ok(session) = self.ctx.server_start(mechname) { - State::Running(session) - } else { - State::Aborted + match self.ctx.server_start(mechname) { + Ok(session) => { + debug!(self.log, "Starting session using {}", mechname); + State::Running(session) + }, + Err(error) => { + debug!(self.log, "Session start failed {:?}", error); + State::Aborted + } } } else { + debug!(self.log, "Invalid mechname {:?}", mechname); State::InvalidMechanism }; diff --git a/src/api/auth.rs b/src/api/auth.rs index 7d518b0..0a01038 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -138,6 +138,7 @@ impl authentication::Server for Auth { let mut out = Cursor::new(Vec::new()); match session.step(Some(data), &mut out) { Ok(Step::Done(data)) => { + trace!(self.log, "Authentication done!"); self.state = State::Finished; let uid = pry!(session.get_property::().ok_or(capnp::Error::failed( @@ -173,10 +174,12 @@ impl authentication::Server for Auth { ))); } Ok(Step::NeedsMore(_)) => { + trace!(self.log, "Authentication wants more data"); self.state = State::Aborted; self.build_error(builder); } - Err(_) => { + Err(error) => { + trace!(self.log, "Authentication errored: {}", error); self.state = State::Aborted; self.build_error(builder); } From 27539429f7225979b312153112bb1c24abbbcaa6 Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Wed, 16 Mar 2022 05:42:19 +0100 Subject: [PATCH 31/37] enable mutistage auth --- src/api/auth.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/api/auth.rs b/src/api/auth.rs index ccc2a2b..041aa8b 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -95,12 +95,9 @@ impl Callback for CB { ) -> Result<(), SessionError> { match property { fabfire::FABFIRECARDKEY => { - // Access the authentication id, i.e. the username to check the password for let authcid = session.get_property_or_callback::()?; - println!("auth'ing user {:?}", authcid); self.userdb.get_user(authcid.unwrap().as_ref()).map(|user| { let kvs= user.unwrap().data.kv; - println!("kvs: {:?}", kvs); kvs.get("cardkey").map(|key| { session.set_property::(Arc::new(<[u8; 16]>::try_from(hex::decode(key).unwrap()).unwrap())); }); @@ -205,8 +202,8 @@ impl authentication::Server for Auth { } Ok(Step::NeedsMore(_)) => { trace!(self.log, "Authentication wants more data"); - self.state = State::Aborted; - self.build_error(builder); + builder.set_challenge(&out.get_ref()); + self.state = State::Running(session); } Err(error) => { trace!(self.log, "Authentication errored: {}", error); From 4feb21e7fc4c16255d40240457267e63a3d75a16 Mon Sep 17 00:00:00 2001 From: Kai Jan Kriegel Date: Wed, 16 Mar 2022 05:42:56 +0100 Subject: [PATCH 32/37] allow bffh to build outside of a git repo --- build.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 733c61e..02b3add 100644 --- a/build.rs +++ b/build.rs @@ -17,7 +17,10 @@ fn main() { let owned_gitrev = String::from_utf8(out.stdout) .expect("git rev-list output was not valid UTF8"); let gitrev = owned_gitrev.trim(); - let abbrev = &gitrev[0..9]; + let abbrev = match gitrev.len(){ + 0 => "unknown", + _ => &gitrev[0..9], + }; println!("cargo:rustc-env=CARGO_PKG_VERSION_GITREV={}", gitrev); let out = Command::new("git").arg("log") From 069819bb9a765426ae6b6d236929bd84e1b7e4d5 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 16 Mar 2022 15:04:32 +0100 Subject: [PATCH 33/37] Only return `use` interface if machine is currently free or reserved by me --- src/api/machines.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/api/machines.rs b/src/api/machines.rs index 6635902..a57e662 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -216,9 +216,18 @@ async fn fill_machine_builder( builder.set_urn(&format!("urn:fabaccess:resource:{}", id.as_ref())); let machineapi = Machine::new(user.clone(), perms, machine.clone()); - if perms.write { + let state = machine.get_status().await; + + // Only set `use` interf if machine could be used by the user. + if perms.write && match &state { + Status::Free => true, + Status::Reserved(reserver) if reserver == user => true, + _ => false, + } + { builder.set_use(capnp_rpc::new_client(machineapi.clone())); } + if perms.manage { //builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); //builder.set_check(capnp_rpc::new_client(machineapi.clone())); @@ -228,12 +237,11 @@ async fn fill_machine_builder( builder.set_admin(capnp_rpc::new_client(machineapi.clone())); } - - let s = match machine.get_status().await { + let s = match state { Status::Free => MachineState::Free, Status::Disabled => MachineState::Disabled, Status::Blocked(_) => MachineState::Blocked, - Status::InUse(u) => { + Status::InUse(ref u) => { if let Some(owner) = u.as_ref() { if owner == user { builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); From cc2b43a9f2ee9ec9282cc31954679a3e7a105eb2 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 16 Mar 2022 15:09:25 +0100 Subject: [PATCH 34/37] Reverse visibility check to properly disclose machines Machines that you have disclose on are always shown. Machines you *don't* have `disclose` on are *also* show *iff* you are using them. --- src/api/machines.rs | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/api/machines.rs b/src/api/machines.rs index a57e662..940448d 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -62,24 +62,33 @@ impl machines::Server for Machines { let mut filtered_v = Vec::with_capacity(v.len()); for (id, machine) in v.into_iter() { - match machine.get_status().await { - // Always show a machine if they're in use by myself - Status::InUse(ref bywho) => - if bywho.is_some() && bywho.as_ref().filter(|bywho| *bywho == user).is_some() - { + // Check if the user has disclose. If yes, machines are always shown. + let required_disclose = &machine.desc.privs.disclose; + if session.as_ref().unwrap().perms.iter() + .any(|rule| rule.match_perm(required_disclose)) + { + filtered_v.push((id, machine)); + } else { + // If no, match their state. Used & reserved machines are also shown + match machine.get_status().await { + // Always show a machine if they're in use by myself + Status::InUse(ref bywho) => + if bywho.is_some() && bywho.as_ref().filter(|bywho| *bywho == user).is_some() + { + filtered_v.push((id, machine)); + } + Status::Reserved(ref bywho) => if bywho == user { filtered_v.push((id, machine)); } - Status::Reserved(ref bywho) => if bywho == user { - filtered_v.push((id, machine)); - } - // The rest depends on the actual priviledges below - _ => { - let required_disclose = &machine.desc.privs.disclose; - if session.as_ref().unwrap().perms.iter() - .any(|rule| rule.match_perm(required_disclose)) - { - filtered_v.push((id, machine)); + // The rest depends on the actual priviledges below + _ => { + let required_disclose = &machine.desc.privs.disclose; + if session.as_ref().unwrap().perms.iter() + .any(|rule| rule.match_perm(required_disclose)) + { + filtered_v.push((id, machine)); + } } } } From dae9d0c93b50a4fbda0be925b6889613edb2baa1 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 16 Mar 2022 15:13:30 +0100 Subject: [PATCH 35/37] Make build step only use git info when not building a tagged release --- build.rs | 44 +++++++++++++++++++++----------------------- src/main.rs | 1 - 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/build.rs b/build.rs index 02b3add..e94d031 100644 --- a/build.rs +++ b/build.rs @@ -9,29 +9,6 @@ fn is_hidden(entry: &DirEntry) -> bool { } fn main() { - // Build version number using the current git commit id - let out = Command::new("git").arg("rev-list") - .args(["HEAD", "-1"]) - .output() - .expect("failed to run `git rev-list HEAD -1`"); - let owned_gitrev = String::from_utf8(out.stdout) - .expect("git rev-list output was not valid UTF8"); - let gitrev = owned_gitrev.trim(); - let abbrev = match gitrev.len(){ - 0 => "unknown", - _ => &gitrev[0..9], - }; - println!("cargo:rustc-env=CARGO_PKG_VERSION_GITREV={}", gitrev); - - let out = Command::new("git").arg("log") - .args(["-1", "--format=%as"]) - .output() - .expect("failed to run `git log -1 --format=\"format:%as\"`"); - let commit_date = String::from_utf8(out.stdout) - .expect("git log output was not valid UTF8"); - let commit_date = commit_date.trim(); - println!("cargo:rustc-env=BFFH_GIT_COMMIT_DATE={}", commit_date); - let mut compile_command = ::capnpc::CompilerCommand::new(); // Set parent module of all generated schema files. @@ -77,6 +54,27 @@ fn main() { version = env!("CARGO_PKG_VERSION"), rustc = rustc_version) } else { + // Build version number using the current git commit id + let out = Command::new("git").arg("rev-list") + .args(["HEAD", "-1"]) + .output() + .expect("failed to run `git rev-list HEAD -1`"); + let owned_gitrev = String::from_utf8(out.stdout) + .expect("git rev-list output was not valid UTF8"); + let gitrev = owned_gitrev.trim(); + let abbrev = match gitrev.len(){ + 0 => "unknown", + _ => &gitrev[0..9], + }; + + let out = Command::new("git").arg("log") + .args(["-1", "--format=%as"]) + .output() + .expect("failed to run `git log -1 --format=\"format:%as\"`"); + let commit_date = String::from_utf8(out.stdout) + .expect("git log output was not valid UTF8"); + let commit_date = commit_date.trim(); + format!("BFFH {version} ({gitrev} {date}) [{rustc}]", version=env!("CARGO_PKG_VERSION"), gitrev=abbrev, diff --git a/src/main.rs b/src/main.rs index f5453f1..03ccc14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,6 @@ use crate::config::{ActorConn, Config, InitiatorConn}; const RELEASE: &'static str = env!("BFFHD_RELEASE_STRING"); const VERSION: &'static str = env!("CARGO_PKG_VERSION"); -const GITREV: &'static str = env!("CARGO_PKG_VERSION_GITREV"); fn main() { use clap::{crate_version, crate_description, crate_name}; From 80ceb4af3424dfdd1e946a6e53b0449270edaa90 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 16 Mar 2022 15:14:58 +0100 Subject: [PATCH 36/37] Log version on start --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 03ccc14..ee6bf83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -120,7 +120,7 @@ fn main() { // Log is in an Arc so we can do very cheap clones in closures. let (log, guard) = log::init(); let log = Arc::new(log); - info!(log, "Starting"); + info!(log, "Starting {}", RELEASE); match maybe(matches, log.clone()) { Ok(_) => {}, From 7f362c7ab4a1b469778c40f4d6b3f5d9a6212741 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 16 Mar 2022 15:17:09 +0100 Subject: [PATCH 37/37] Rerun on changed BFFHD_BUILD_TAGGED_RELEASE env var --- build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/build.rs b/build.rs index e94d031..1cc4f92 100644 --- a/build.rs +++ b/build.rs @@ -48,6 +48,7 @@ fn main() { let rustc_version = rustc_version.trim(); println!("cargo:rustc-env=CARGO_RUSTC_VERSION={}", rustc_version); + println!("cargo:rerun-if-env-changed=BFFHD_BUILD_TAGGED_RELEASE"); let tagged_release = option_env!("BFFHD_BUILD_TAGGED_RELEASE") == Some("1"); let release = if tagged_release { format!("BFFH {version} [{rustc}]",