mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-28 00:40:01 +02:00 
			
		
		
		
	
							
								
								
									
										8
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								.env
									
									
									
									
									
								
							| @@ -40,6 +40,14 @@ | ||||
| ## For U2F to work, the server must use HTTPS, you can use Let's Encrypt for free certs | ||||
| # DOMAIN=https://bw.domain.tld:8443 | ||||
|  | ||||
| ## Yubico (Yubikey) Settings | ||||
| ## Set your Client ID and Secret Key for Yubikey OTP | ||||
| ## You can generate it here: https://upgrade.yubico.com/getapikey/ | ||||
| ## You can optionally specify a custom OTP server | ||||
| # YUBICO_CLIENT_ID=11111 | ||||
| # YUBICO_SECRET_KEY=AAAAAAAAAAAAAAAAAAAAAAAA | ||||
| # YUBICO_SERVER=http://yourdomain.com/wsapi/2.0/verify | ||||
|  | ||||
| ## Rocket specific settings, check Rocket documentation to learn more | ||||
| # ROCKET_ENV=staging | ||||
| # ROCKET_ADDRESS=0.0.0.0 # Enable this to test mobile app | ||||
|   | ||||
							
								
								
									
										124
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										124
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -81,6 +81,14 @@ dependencies = [ | ||||
|  "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "base64" | ||||
| version = "0.10.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "1.0.4" | ||||
| @@ -119,6 +127,7 @@ dependencies = [ | ||||
|  "u2f 0.1.2 (git+https://github.com/wisespace-io/u2f-rs?rev=193de35093a44)", | ||||
|  "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "ws 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "yubico 0.4.0 (git+https://github.com/dani-garcia/yubico-rs)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -1348,6 +1357,16 @@ dependencies = [ | ||||
|  "scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand" | ||||
| version = "0.3.22" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand" | ||||
| version = "0.4.3" | ||||
| @@ -1370,6 +1389,33 @@ dependencies = [ | ||||
|  "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand" | ||||
| version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand_isaac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_chacha" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_core" | ||||
| version = "0.2.2" | ||||
| @@ -1383,6 +1429,39 @@ name = "rand_core" | ||||
| version = "0.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_hc" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_isaac" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_pcg" | ||||
| version = "0.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_xorshift" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rayon" | ||||
| version = "0.7.1" | ||||
| @@ -1559,6 +1638,18 @@ dependencies = [ | ||||
|  "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rust-crypto" | ||||
| version = "0.2.36" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rustc-demangle" | ||||
| version = "0.1.9" | ||||
| @@ -1872,6 +1963,14 @@ dependencies = [ | ||||
|  "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "threadpool" | ||||
| version = "1.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "time" | ||||
| version = "0.1.40" | ||||
| @@ -2319,6 +2418,20 @@ name = "yansi" | ||||
| version = "0.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
|  | ||||
| [[package]] | ||||
| name = "yubico" | ||||
| version = "0.4.0" | ||||
| source = "git+https://github.com/dani-garcia/yubico-rs#f93a88834e4ebaaed0169850ff1a5e32a7d01719" | ||||
| dependencies = [ | ||||
|  "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rand 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "reqwest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
|  | ||||
| [metadata] | ||||
| "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" | ||||
| "checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" | ||||
| @@ -2328,6 +2441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" | ||||
| "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" | ||||
| "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" | ||||
| "checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" | ||||
| "checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557" | ||||
| "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" | ||||
| "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" | ||||
| @@ -2470,10 +2584,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" | ||||
| "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" | ||||
| "checksum r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9078ca6a8a5568ed142083bb2f7dc9295b69d16f867ddcc9849e51b17d8db46" | ||||
| "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" | ||||
| "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" | ||||
| "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" | ||||
| "checksum rand 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de3f08319b5395bd19b70e73c4c465329495db02dafeb8ca711a20f1c2bd058c" | ||||
| "checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" | ||||
| "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" | ||||
| "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" | ||||
| "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" | ||||
| "checksum rand_isaac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6ecfe9ebf36acd47a49d150990b047a5f7db0a7236ee2414b7ff5cc1097c7b" | ||||
| "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" | ||||
| "checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" | ||||
| "checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" | ||||
| "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" | ||||
| "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" | ||||
| @@ -2489,6 +2610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum rocket 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "a61d746c68f1d357f6e011985570474c4af368aa81900320074098d34ed0c64e" | ||||
| "checksum rocket_codegen 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7873d65adfa3e440ac373a28240341853da170913aad7e4207c0198389e5d0e9" | ||||
| "checksum rocket_contrib 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2348e3b2458e173203f1e51f3b2e00495a092b70bd9506d2ce2ac64e129d14" | ||||
| "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" | ||||
| "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" | ||||
| "checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" | ||||
| "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" | ||||
| @@ -2528,6 +2650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "55c1195ef8513f3273d55ff59fe5da6940287a0d7a98331254397f464833675b" | ||||
| "checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" | ||||
| "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" | ||||
| "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" | ||||
| "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" | ||||
| "checksum tiny_http 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a442681f9f72e440be192700eeb2861e4174b9983f16f4877c93a134cb5e5f63" | ||||
| "checksum tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6e93c78d23cc61aa245a8acd2c4a79c4d7fa7fb5c3ca90d5737029f043a84895" | ||||
| @@ -2579,3 +2702,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum ws 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "d2c221321dca56e6a80aa179d562e1fbe6ae116aeaa9205c76fa64e9e3c49dfc" | ||||
| "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" | ||||
| "checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48" | ||||
| "checksum yubico 0.4.0 (git+https://github.com/dani-garcia/yubico-rs)" = "<none>" | ||||
|   | ||||
| @@ -57,6 +57,9 @@ jsonwebtoken = "= 4.0.1" | ||||
| # U2F library | ||||
| u2f = "0.1.2" | ||||
|  | ||||
| # Yubico Library | ||||
| yubico= { version = "0.4.0", default-features = false } | ||||
|  | ||||
| # A `dotenv` implementation for Rust | ||||
| dotenv = { version = "0.13.0", default-features = false } | ||||
|  | ||||
| @@ -84,3 +87,6 @@ lettre_email = { git = 'https://github.com/lettre/lettre', rev = 'c988b1760ad81' | ||||
|  | ||||
| # Version 0.1.2 from crates.io lacks a commit that fixes a certificate error | ||||
| u2f = { git = 'https://github.com/wisespace-io/u2f-rs', rev = '193de35093a44' } | ||||
|  | ||||
| # Allows optional libusb support | ||||
| yubico = { git = 'https://github.com/dani-garcia/yubico-rs' } | ||||
|   | ||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							| @@ -28,6 +28,7 @@ _*Note, that this project is not associated with the [Bitwarden](https://bitward | ||||
|   - [Enabling HTTPS](#enabling-https) | ||||
|   - [Enabling WebSocket notifications](#enabling-websocket-notifications) | ||||
|   - [Enabling U2F authentication](#enabling-u2f-authentication) | ||||
|   - [Enabling YubiKey OTP authentication](#enabling-yubikey-otp-authentication) | ||||
|   - [Changing persistent data location](#changing-persistent-data-location) | ||||
|     - [/data prefix:](#data-prefix) | ||||
|     - [database name and location](#database-name-and-location) | ||||
| @@ -68,11 +69,11 @@ Basically full implementation of Bitwarden API is provided including: | ||||
|  * Serving the static files for Vault interface | ||||
|  * Website icons API | ||||
|  * Authenticator and U2F support | ||||
|  * YubiKey OTP | ||||
|   | ||||
| ## Missing features | ||||
| * Email confirmation | ||||
| * Other two-factor systems: | ||||
|   * YubiKey OTP (if your key supports U2F, you can use that) | ||||
|   * Duo | ||||
|   * Email codes | ||||
|  | ||||
| @@ -252,6 +253,22 @@ docker run -d --name bitwarden \ | ||||
|  | ||||
| Note that the value has to include the `https://` and it may include a port at the end (in the format of `https://bw.domain.tld:port`) when not using `443`. | ||||
|  | ||||
| ### Enabling YubiKey OTP authentication | ||||
| To enable YubiKey authentication, you must set the `YUBICO_CLIENT_ID` and `YUBICO_SECRET_KEY` env variables. | ||||
|  | ||||
| If `YUBICO_SERVER` is not specified, it will use the default YubiCloud servers. You can generate `YUBICO_CLIENT_ID` and `YUBICO_SECRET_KEY` for the default YubiCloud [here](https://upgrade.yubico.com/getapikey/). | ||||
|  | ||||
| Note: In order to generate API keys or use a YubiKey with an OTP server, it must be registered. After configuring your key in the [YubiKey Personalization Tool](https://www.yubico.com/products/services-software/personalization-tools/use/), you can register it with the default servers [here](https://upload.yubico.com/). | ||||
|  | ||||
| ```sh | ||||
| docker run -d --name bitwarden \ | ||||
|   -e YUBICO_CLIENT_ID=12345 \ | ||||
|   -e YUBICO_SECRET_KEY=ABCDEABCDEABCDEABCDE= \ | ||||
|   -v /bw-data/:/data/ \ | ||||
|   -p 80:80 \ | ||||
|   mprasil/bitwarden:latest | ||||
| ``` | ||||
|  | ||||
| ### Changing persistent data location | ||||
|  | ||||
| #### /data prefix: | ||||
|   | ||||
| @@ -83,6 +83,9 @@ pub fn routes() -> Vec<Route> { | ||||
|         generate_u2f_challenge, | ||||
|         activate_u2f, | ||||
|         activate_u2f_put, | ||||
|         generate_yubikey, | ||||
|         activate_yubikey, | ||||
|         activate_yubikey_put, | ||||
|  | ||||
|         get_organization, | ||||
|         create_organization, | ||||
|   | ||||
| @@ -491,3 +491,218 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api | ||||
|     } | ||||
|     err!("error verifying response") | ||||
| } | ||||
|  | ||||
|  | ||||
| #[derive(Deserialize, Debug)] | ||||
| #[allow(non_snake_case)] | ||||
| struct EnableYubikeyData { | ||||
|     MasterPasswordHash: String, | ||||
|     Key1: Option<String>, | ||||
|     Key2: Option<String>, | ||||
|     Key3: Option<String>, | ||||
|     Key4: Option<String>, | ||||
|     Key5: Option<String>, | ||||
|     Nfc: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug)] | ||||
| #[allow(non_snake_case)] | ||||
| struct YubikeyMetadata { | ||||
|     Keys: Vec<String>, | ||||
|     Nfc: bool, | ||||
| } | ||||
|  | ||||
| use yubico::Yubico; | ||||
| use yubico::config::Config; | ||||
|  | ||||
| fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> { | ||||
|     let mut yubikeys: Vec<String> = Vec::new(); | ||||
|  | ||||
|     if data.Key1.is_some() { | ||||
|         yubikeys.push(data.Key1.as_ref().unwrap().to_owned()); | ||||
|     } | ||||
|  | ||||
|     if data.Key2.is_some() { | ||||
|         yubikeys.push(data.Key2.as_ref().unwrap().to_owned()); | ||||
|     } | ||||
|  | ||||
|     if data.Key3.is_some() { | ||||
|         yubikeys.push(data.Key3.as_ref().unwrap().to_owned()); | ||||
|     } | ||||
|  | ||||
|     if data.Key4.is_some() { | ||||
|         yubikeys.push(data.Key4.as_ref().unwrap().to_owned()); | ||||
|     } | ||||
|  | ||||
|     if data.Key5.is_some() { | ||||
|         yubikeys.push(data.Key5.as_ref().unwrap().to_owned()); | ||||
|     } | ||||
|  | ||||
|     yubikeys | ||||
| } | ||||
|  | ||||
| fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value { | ||||
|     let mut result = json!({}); | ||||
|  | ||||
|     for i in 0..yubikeys.len() { | ||||
|         let ref key = &yubikeys[i]; | ||||
|         result[format!("Key{}", i+1)] = Value::String(key.to_string()); | ||||
|     } | ||||
|  | ||||
|     result | ||||
| } | ||||
|  | ||||
| fn verify_yubikey_otp(otp: String) -> JsonResult { | ||||
|     if !CONFIG.yubico_cred_set { | ||||
|         err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. \ | ||||
|                Yubikey OTP Disabled") | ||||
|     } | ||||
|  | ||||
|     let yubico = Yubico::new(); | ||||
|     let config = Config::default().set_client_id(CONFIG.yubico_client_id.to_owned()).set_key(CONFIG.yubico_secret_key.to_owned()); | ||||
|  | ||||
|     let result = match CONFIG.yubico_server { | ||||
|         Some(ref server) => yubico.verify(otp, config.set_api_hosts(vec![server.to_owned()])), | ||||
|         None => yubico.verify(otp, config) | ||||
|     }; | ||||
|  | ||||
|     match result { | ||||
|         Ok(_answer) => Ok(Json(json!({}))), | ||||
|         Err(_e) => err!("Failed to verify OTP"), | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[post("/two-factor/get-yubikey", data = "<data>")] | ||||
| fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     if !CONFIG.yubico_cred_set { | ||||
|         err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. \ | ||||
|                Yubikey OTP Disabled") | ||||
|     } | ||||
|  | ||||
|     let data: PasswordData = data.into_inner().data; | ||||
|  | ||||
|     if !headers.user.check_valid_password(&data.MasterPasswordHash) { | ||||
|         err!("Invalid password"); | ||||
|     } | ||||
|  | ||||
|     let user_uuid = &headers.user.uuid; | ||||
|     let yubikey_type = TwoFactorType::YubiKey as i32; | ||||
|  | ||||
|     let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn); | ||||
|  | ||||
|     if let Some(r) = r { | ||||
|         let yubikey_metadata: YubikeyMetadata = | ||||
|             serde_json::from_str(&r.data).expect("Can't parse YubikeyMetadata data"); | ||||
|  | ||||
|         let mut result = jsonify_yubikeys(yubikey_metadata.Keys); | ||||
|  | ||||
|         result["Enabled"] = Value::Bool(true); | ||||
|         result["Nfc"] = Value::Bool(yubikey_metadata.Nfc); | ||||
|         result["Object"] = Value::String("twoFactorU2f".to_owned()); | ||||
|  | ||||
|         Ok(Json(result)) | ||||
|     } else { | ||||
|         Ok(Json(json!({ | ||||
|             "Enabled": false, | ||||
|             "Object": "twoFactorU2f", | ||||
|         }))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[post("/two-factor/yubikey", data = "<data>")] | ||||
| fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let data: EnableYubikeyData = data.into_inner().data; | ||||
|  | ||||
|     if !headers.user.check_valid_password(&data.MasterPasswordHash) { | ||||
|         err!("Invalid password"); | ||||
|     } | ||||
|  | ||||
|     // Check if we already have some data | ||||
|     let yubikey_data = TwoFactor::find_by_user_and_type( | ||||
|         &headers.user.uuid, | ||||
|         TwoFactorType::YubiKey as i32, | ||||
|         &conn, | ||||
|     ); | ||||
|  | ||||
|     if let Some(yubikey_data) = yubikey_data { | ||||
|         yubikey_data.delete(&conn).expect("Error deleting current Yubikeys"); | ||||
|     } | ||||
|  | ||||
|     let yubikeys = parse_yubikeys(&data); | ||||
|  | ||||
|     if yubikeys.len() == 0 { | ||||
|         return Ok(Json(json!({ | ||||
|             "Enabled": false, | ||||
|             "Object": "twoFactorU2f", | ||||
|         }))); | ||||
|     } | ||||
|  | ||||
|     // Ensure they are valid OTPs | ||||
|     for yubikey in &yubikeys { | ||||
|         if yubikey.len() == 12 { | ||||
|             // YubiKey ID | ||||
|             continue | ||||
|         } | ||||
|  | ||||
|         let result = verify_yubikey_otp(yubikey.to_owned()); | ||||
|  | ||||
|         if let Err(_e) = result { | ||||
|             err!("Invalid Yubikey OTP provided"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let yubikey_ids: Vec<String> = yubikeys.into_iter().map(|x| (&x[..12]).to_owned()).collect(); | ||||
|  | ||||
|     let yubikey_metadata = YubikeyMetadata { | ||||
|         Keys: yubikey_ids, | ||||
|         Nfc: data.Nfc, | ||||
|     }; | ||||
|  | ||||
|     let yubikey_registration = TwoFactor::new( | ||||
|         headers.user.uuid.clone(), | ||||
|         TwoFactorType::YubiKey, | ||||
|         serde_json::to_string(&yubikey_metadata).unwrap(), | ||||
|     ); | ||||
|     yubikey_registration | ||||
|         .save(&conn).expect("Failed to save Yubikey info"); | ||||
|  | ||||
|     let mut result = jsonify_yubikeys(yubikey_metadata.Keys); | ||||
|  | ||||
|     result["Enabled"] = Value::Bool(true); | ||||
|     result["Nfc"] = Value::Bool(yubikey_metadata.Nfc); | ||||
|     result["Object"] = Value::String("twoFactorU2f".to_owned()); | ||||
|  | ||||
|     Ok(Json(result)) | ||||
| } | ||||
|  | ||||
| #[put("/two-factor/yubikey", data = "<data>")] | ||||
| fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     activate_yubikey(data, headers, conn) | ||||
| } | ||||
|  | ||||
| pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) -> ApiResult<()> { | ||||
|     if response.len() != 44 { | ||||
|         err!("Invalid Yubikey OTP length"); | ||||
|     } | ||||
|  | ||||
|     let yubikey_type = TwoFactorType::YubiKey as i32; | ||||
|  | ||||
|     let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn) { | ||||
|         Some(tf) => tf, | ||||
|         None => err!("No YubiKey devices registered"), | ||||
|     }; | ||||
|  | ||||
|     let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&twofactor.data).expect("Can't parse Yubikey Metadata"); | ||||
|     let response_id = &response[..12]; | ||||
|  | ||||
|     if !yubikey_metadata.Keys.contains(&response_id.to_owned()) { | ||||
|         err!("Given Yubikey is not registered"); | ||||
|     } | ||||
|  | ||||
|     let result = verify_yubikey_otp(response.to_owned()); | ||||
|  | ||||
|     match result { | ||||
|         Ok(_answer) => Ok(()), | ||||
|         Err(_e) => err!("Failed to verify Yubikey against OTP server"), | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -209,6 +209,12 @@ fn twofactor_auth( | ||||
|             two_factor::validate_u2f_login(user_uuid, twofactor_code, conn)?; | ||||
|         } | ||||
|  | ||||
|         Some(TwoFactorType::YubiKey) => { | ||||
|             use api::core::two_factor; | ||||
|  | ||||
|             two_factor::validate_yubikey_login(user_uuid, twofactor_code, conn)?; | ||||
|         } | ||||
|  | ||||
|         _ => err!("Invalid two factor provider"), | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -25,6 +25,7 @@ extern crate oath; | ||||
| extern crate data_encoding; | ||||
| extern crate jsonwebtoken as jwt; | ||||
| extern crate u2f; | ||||
| extern crate yubico; | ||||
| extern crate dotenv; | ||||
| #[macro_use] | ||||
| extern crate lazy_static; | ||||
| @@ -245,6 +246,11 @@ pub struct Config { | ||||
|     domain: String, | ||||
|     domain_set: bool, | ||||
|  | ||||
|     yubico_cred_set: bool, | ||||
|     yubico_client_id: String, | ||||
|     yubico_secret_key: String, | ||||
|     yubico_server: Option<String>, | ||||
|  | ||||
|     mail: Option<MailConfig>, | ||||
| } | ||||
|  | ||||
| @@ -258,6 +264,9 @@ impl Config { | ||||
|  | ||||
|         let domain = get_env("DOMAIN"); | ||||
|  | ||||
|         let yubico_client_id = get_env("YUBICO_CLIENT_ID"); | ||||
|         let yubico_secret_key = get_env("YUBICO_SECRET_KEY"); | ||||
|  | ||||
|         Config { | ||||
|             database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")), | ||||
|             icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")), | ||||
| @@ -283,6 +292,11 @@ impl Config { | ||||
|             domain_set: domain.is_some(), | ||||
|             domain: domain.unwrap_or("http://localhost".into()), | ||||
|  | ||||
|             yubico_cred_set: yubico_client_id.is_some() && yubico_secret_key.is_some(), | ||||
|             yubico_client_id: yubico_client_id.unwrap_or("00000".into()), | ||||
|             yubico_secret_key: yubico_secret_key.unwrap_or("AAAAAAA".into()), | ||||
|             yubico_server: get_env("YUBICO_SERVER"), | ||||
|  | ||||
|             mail: MailConfig::load(), | ||||
|         } | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user