two_factor.rs 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. use data_encoding::{BASE32, BASE64};
  2. use rocket_contrib::json::Json;
  3. use serde_json;
  4. use serde_json::Value;
  5. use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
  6. use crate::auth::Headers;
  7. use crate::crypto;
  8. use crate::db::{
  9. models::{TwoFactor, TwoFactorType, User},
  10. DbConn,
  11. };
  12. use crate::error::{Error, MapResult};
  13. use rocket::Route;
  14. pub fn routes() -> Vec<Route> {
  15. routes![
  16. get_twofactor,
  17. get_recover,
  18. recover,
  19. disable_twofactor,
  20. disable_twofactor_put,
  21. generate_authenticator,
  22. activate_authenticator,
  23. activate_authenticator_put,
  24. generate_u2f,
  25. generate_u2f_challenge,
  26. activate_u2f,
  27. activate_u2f_put,
  28. generate_yubikey,
  29. activate_yubikey,
  30. activate_yubikey_put,
  31. get_duo,
  32. activate_duo,
  33. activate_duo_put,
  34. ]
  35. }
  36. #[get("/two-factor")]
  37. fn get_twofactor(headers: Headers, conn: DbConn) -> JsonResult {
  38. let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn);
  39. let twofactors_json: Vec<Value> = twofactors.iter().map(TwoFactor::to_json_list).collect();
  40. Ok(Json(json!({
  41. "Data": twofactors_json,
  42. "Object": "list",
  43. "ContinuationToken": null,
  44. })))
  45. }
  46. #[post("/two-factor/get-recover", data = "<data>")]
  47. fn get_recover(data: JsonUpcase<PasswordData>, headers: Headers) -> JsonResult {
  48. let data: PasswordData = data.into_inner().data;
  49. let user = headers.user;
  50. if !user.check_valid_password(&data.MasterPasswordHash) {
  51. err!("Invalid password");
  52. }
  53. Ok(Json(json!({
  54. "Code": user.totp_recover,
  55. "Object": "twoFactorRecover"
  56. })))
  57. }
  58. #[derive(Deserialize)]
  59. #[allow(non_snake_case)]
  60. struct RecoverTwoFactor {
  61. MasterPasswordHash: String,
  62. Email: String,
  63. RecoveryCode: String,
  64. }
  65. #[post("/two-factor/recover", data = "<data>")]
  66. fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
  67. let data: RecoverTwoFactor = data.into_inner().data;
  68. use crate::db::models::User;
  69. // Get the user
  70. let mut user = match User::find_by_mail(&data.Email, &conn) {
  71. Some(user) => user,
  72. None => err!("Username or password is incorrect. Try again."),
  73. };
  74. // Check password
  75. if !user.check_valid_password(&data.MasterPasswordHash) {
  76. err!("Username or password is incorrect. Try again.")
  77. }
  78. // Check if recovery code is correct
  79. if !user.check_valid_recovery_code(&data.RecoveryCode) {
  80. err!("Recovery code is incorrect. Try again.")
  81. }
  82. // Remove all twofactors from the user
  83. TwoFactor::delete_all_by_user(&user.uuid, &conn)?;
  84. // Remove the recovery code, not needed without twofactors
  85. user.totp_recover = None;
  86. user.save(&conn)?;
  87. Ok(Json(json!({})))
  88. }
  89. fn _generate_recover_code(user: &mut User, conn: &DbConn) {
  90. if user.totp_recover.is_none() {
  91. let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
  92. user.totp_recover = Some(totp_recover);
  93. user.save(conn).ok();
  94. }
  95. }
  96. #[derive(Deserialize)]
  97. #[allow(non_snake_case)]
  98. struct DisableTwoFactorData {
  99. MasterPasswordHash: String,
  100. Type: NumberOrString,
  101. }
  102. #[post("/two-factor/disable", data = "<data>")]
  103. fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
  104. let data: DisableTwoFactorData = data.into_inner().data;
  105. let password_hash = data.MasterPasswordHash;
  106. let user = headers.user;
  107. if !user.check_valid_password(&password_hash) {
  108. err!("Invalid password");
  109. }
  110. let type_ = data.Type.into_i32()?;
  111. if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn) {
  112. twofactor.delete(&conn)?;
  113. }
  114. Ok(Json(json!({
  115. "Enabled": false,
  116. "Type": type_,
  117. "Object": "twoFactorProvider"
  118. })))
  119. }
  120. #[put("/two-factor/disable", data = "<data>")]
  121. fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
  122. disable_twofactor(data, headers, conn)
  123. }
  124. #[post("/two-factor/get-authenticator", data = "<data>")]
  125. fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  126. let data: PasswordData = data.into_inner().data;
  127. let user = headers.user;
  128. if !user.check_valid_password(&data.MasterPasswordHash) {
  129. err!("Invalid password");
  130. }
  131. let type_ = TwoFactorType::Authenticator as i32;
  132. let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn);
  133. let (enabled, key) = match twofactor {
  134. Some(tf) => (true, tf.data),
  135. _ => (false, BASE32.encode(&crypto::get_random(vec![0u8; 20]))),
  136. };
  137. Ok(Json(json!({
  138. "Enabled": enabled,
  139. "Key": key,
  140. "Object": "twoFactorAuthenticator"
  141. })))
  142. }
  143. #[derive(Deserialize, Debug)]
  144. #[allow(non_snake_case)]
  145. struct EnableAuthenticatorData {
  146. MasterPasswordHash: String,
  147. Key: String,
  148. Token: NumberOrString,
  149. }
  150. #[post("/two-factor/authenticator", data = "<data>")]
  151. fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
  152. let data: EnableAuthenticatorData = data.into_inner().data;
  153. let password_hash = data.MasterPasswordHash;
  154. let key = data.Key;
  155. let token = data.Token.into_i32()? as u64;
  156. let mut user = headers.user;
  157. if !user.check_valid_password(&password_hash) {
  158. err!("Invalid password");
  159. }
  160. // Validate key as base32 and 20 bytes length
  161. let decoded_key: Vec<u8> = match BASE32.decode(key.as_bytes()) {
  162. Ok(decoded) => decoded,
  163. _ => err!("Invalid totp secret"),
  164. };
  165. if decoded_key.len() != 20 {
  166. err!("Invalid key length")
  167. }
  168. let type_ = TwoFactorType::Authenticator;
  169. let twofactor = TwoFactor::new(user.uuid.clone(), type_, key.to_uppercase());
  170. // Validate the token provided with the key
  171. validate_totp_code(token, &twofactor.data)?;
  172. _generate_recover_code(&mut user, &conn);
  173. twofactor.save(&conn)?;
  174. Ok(Json(json!({
  175. "Enabled": true,
  176. "Key": key,
  177. "Object": "twoFactorAuthenticator"
  178. })))
  179. }
  180. #[put("/two-factor/authenticator", data = "<data>")]
  181. fn activate_authenticator_put(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
  182. activate_authenticator(data, headers, conn)
  183. }
  184. pub fn validate_totp_code_str(totp_code: &str, secret: &str) -> EmptyResult {
  185. let totp_code: u64 = match totp_code.parse() {
  186. Ok(code) => code,
  187. _ => err!("TOTP code is not a number"),
  188. };
  189. validate_totp_code(totp_code, secret)
  190. }
  191. pub fn validate_totp_code(totp_code: u64, secret: &str) -> EmptyResult {
  192. use oath::{totp_raw_now, HashType};
  193. let decoded_secret = match BASE32.decode(secret.as_bytes()) {
  194. Ok(s) => s,
  195. Err(_) => err!("Invalid TOTP secret"),
  196. };
  197. let generated = totp_raw_now(&decoded_secret, 6, 0, 30, &HashType::SHA1);
  198. if generated != totp_code {
  199. err!("Invalid TOTP code");
  200. }
  201. Ok(())
  202. }
  203. use u2f::messages::{RegisterResponse, SignResponse, U2fSignRequest};
  204. use u2f::protocol::{Challenge, U2f};
  205. use u2f::register::Registration;
  206. use crate::CONFIG;
  207. const U2F_VERSION: &str = "U2F_V2";
  208. lazy_static! {
  209. static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain());
  210. static ref U2F: U2f = U2f::new(APP_ID.clone());
  211. }
  212. #[post("/two-factor/get-u2f", data = "<data>")]
  213. fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  214. if !CONFIG.domain_set() {
  215. err!("`DOMAIN` environment variable is not set. U2F disabled")
  216. }
  217. let data: PasswordData = data.into_inner().data;
  218. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  219. err!("Invalid password");
  220. }
  221. let (enabled, keys) = get_u2f_registrations(&headers.user.uuid, &conn)?;
  222. let keys_json: Vec<Value> = keys.iter().map(U2FRegistration::to_json).collect();
  223. Ok(Json(json!({
  224. "Enabled": enabled,
  225. "Keys": keys_json,
  226. "Object": "twoFactorU2f"
  227. })))
  228. }
  229. #[post("/two-factor/get-u2f-challenge", data = "<data>")]
  230. fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  231. let data: PasswordData = data.into_inner().data;
  232. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  233. err!("Invalid password");
  234. }
  235. let _type = TwoFactorType::U2fRegisterChallenge;
  236. let challenge = _create_u2f_challenge(&headers.user.uuid, _type, &conn).challenge;
  237. Ok(Json(json!({
  238. "UserId": headers.user.uuid,
  239. "AppId": APP_ID.to_string(),
  240. "Challenge": challenge,
  241. "Version": U2F_VERSION,
  242. })))
  243. }
  244. #[derive(Deserialize, Debug)]
  245. #[allow(non_snake_case)]
  246. struct EnableU2FData {
  247. Id: NumberOrString, // 1..5
  248. Name: String,
  249. MasterPasswordHash: String,
  250. DeviceResponse: String,
  251. }
  252. // This struct is referenced from the U2F lib
  253. // because it doesn't implement Deserialize
  254. #[derive(Serialize, Deserialize)]
  255. #[serde(rename_all = "camelCase")]
  256. #[serde(remote = "Registration")]
  257. struct RegistrationDef {
  258. key_handle: Vec<u8>,
  259. pub_key: Vec<u8>,
  260. attestation_cert: Option<Vec<u8>>,
  261. }
  262. #[derive(Serialize, Deserialize)]
  263. struct U2FRegistration {
  264. id: i32,
  265. name: String,
  266. #[serde(with = "RegistrationDef")]
  267. reg: Registration,
  268. counter: u32,
  269. compromised: bool,
  270. }
  271. impl U2FRegistration {
  272. fn to_json(&self) -> Value {
  273. json!({
  274. "Id": self.id,
  275. "Name": self.name,
  276. "Compromised": self.compromised,
  277. })
  278. }
  279. }
  280. // This struct is copied from the U2F lib
  281. // to add an optional error code
  282. #[derive(Deserialize)]
  283. #[serde(rename_all = "camelCase")]
  284. struct RegisterResponseCopy {
  285. pub registration_data: String,
  286. pub version: String,
  287. pub client_data: String,
  288. pub error_code: Option<NumberOrString>,
  289. }
  290. impl Into<RegisterResponse> for RegisterResponseCopy {
  291. fn into(self) -> RegisterResponse {
  292. RegisterResponse {
  293. registration_data: self.registration_data,
  294. version: self.version,
  295. client_data: self.client_data,
  296. }
  297. }
  298. }
  299. #[post("/two-factor/u2f", data = "<data>")]
  300. fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
  301. let data: EnableU2FData = data.into_inner().data;
  302. let mut user = headers.user;
  303. if !user.check_valid_password(&data.MasterPasswordHash) {
  304. err!("Invalid password");
  305. }
  306. let tf_type = TwoFactorType::U2fRegisterChallenge as i32;
  307. let tf_challenge = match TwoFactor::find_by_user_and_type(&user.uuid, tf_type, &conn) {
  308. Some(c) => c,
  309. None => err!("Can't recover challenge"),
  310. };
  311. let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
  312. tf_challenge.delete(&conn)?;
  313. let response: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
  314. let error_code = response
  315. .error_code
  316. .clone()
  317. .map_or("0".into(), NumberOrString::into_string);
  318. if error_code != "0" {
  319. err!("Error registering U2F token")
  320. }
  321. let registration = U2F.register_response(challenge.clone(), response.into())?;
  322. let full_registration = U2FRegistration {
  323. id: data.Id.into_i32()?,
  324. name: data.Name,
  325. reg: registration,
  326. compromised: false,
  327. counter: 0,
  328. };
  329. let mut regs = get_u2f_registrations(&user.uuid, &conn)?.1;
  330. // TODO: Check that there is no repeat Id
  331. regs.push(full_registration);
  332. save_u2f_registrations(&user.uuid, &regs, &conn)?;
  333. _generate_recover_code(&mut user, &conn);
  334. let keys_json: Vec<Value> = regs.iter().map(U2FRegistration::to_json).collect();
  335. Ok(Json(json!({
  336. "Enabled": true,
  337. "Keys": keys_json,
  338. "Object": "twoFactorU2f"
  339. })))
  340. }
  341. #[put("/two-factor/u2f", data = "<data>")]
  342. fn activate_u2f_put(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
  343. activate_u2f(data, headers, conn)
  344. }
  345. fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge {
  346. let challenge = U2F.generate_challenge().unwrap();
  347. TwoFactor::new(user_uuid.into(), type_, serde_json::to_string(&challenge).unwrap())
  348. .save(conn)
  349. .expect("Error saving challenge");
  350. challenge
  351. }
  352. fn save_u2f_registrations(user_uuid: &str, regs: &[U2FRegistration], conn: &DbConn) -> EmptyResult {
  353. TwoFactor::new(user_uuid.into(), TwoFactorType::U2f, serde_json::to_string(regs)?).save(&conn)
  354. }
  355. fn get_u2f_registrations(user_uuid: &str, conn: &DbConn) -> Result<(bool, Vec<U2FRegistration>), Error> {
  356. let type_ = TwoFactorType::U2f as i32;
  357. let (enabled, regs) = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn) {
  358. Some(tf) => (tf.enabled, tf.data),
  359. None => return Ok((false, Vec::new())), // If no data, return empty list
  360. };
  361. let data = match serde_json::from_str(&regs) {
  362. Ok(d) => d,
  363. Err(_) => {
  364. // If error, try old format
  365. let mut old_regs = _old_parse_registrations(&regs);
  366. if old_regs.len() != 1 {
  367. err!("The old U2F format only allows one device")
  368. }
  369. // Convert to new format
  370. let new_regs = vec![U2FRegistration {
  371. id: 1,
  372. name: "Unnamed U2F key".into(),
  373. reg: old_regs.remove(0),
  374. compromised: false,
  375. counter: 0,
  376. }];
  377. // Save new format
  378. save_u2f_registrations(user_uuid, &new_regs, &conn)?;
  379. new_regs
  380. }
  381. };
  382. Ok((enabled, data))
  383. }
  384. fn _old_parse_registrations(registations: &str) -> Vec<Registration> {
  385. #[derive(Deserialize)]
  386. struct Helper(#[serde(with = "RegistrationDef")] Registration);
  387. let regs: Vec<Value> = serde_json::from_str(registations).expect("Can't parse Registration data");
  388. regs.into_iter()
  389. .map(|r| serde_json::from_value(r).unwrap())
  390. .map(|Helper(r)| r)
  391. .collect()
  392. }
  393. pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRequest> {
  394. let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn);
  395. let registrations: Vec<_> = get_u2f_registrations(user_uuid, conn)?
  396. .1
  397. .into_iter()
  398. .map(|r| r.reg)
  399. .collect();
  400. if registrations.is_empty() {
  401. err!("No U2F devices registered")
  402. }
  403. Ok(U2F.sign_request(challenge, registrations))
  404. }
  405. pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
  406. let challenge_type = TwoFactorType::U2fLoginChallenge as i32;
  407. let tf_challenge = TwoFactor::find_by_user_and_type(user_uuid, challenge_type, &conn);
  408. let challenge = match tf_challenge {
  409. Some(tf_challenge) => {
  410. let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
  411. tf_challenge.delete(&conn)?;
  412. challenge
  413. }
  414. None => err!("Can't recover login challenge"),
  415. };
  416. let response: SignResponse = serde_json::from_str(response)?;
  417. let mut registrations = get_u2f_registrations(user_uuid, conn)?.1;
  418. if registrations.is_empty() {
  419. err!("No U2F devices registered")
  420. }
  421. for reg in &mut registrations {
  422. let response = U2F.sign_response(challenge.clone(), reg.reg.clone(), response.clone(), reg.counter);
  423. match response {
  424. Ok(new_counter) => {
  425. reg.counter = new_counter;
  426. save_u2f_registrations(user_uuid, &registrations, &conn)?;
  427. return Ok(());
  428. }
  429. Err(u2f::u2ferror::U2fError::CounterTooLow) => {
  430. reg.compromised = true;
  431. save_u2f_registrations(user_uuid, &registrations, &conn)?;
  432. err!("This device might be compromised!");
  433. }
  434. Err(e) => {
  435. warn!("E {:#}", e);
  436. // break;
  437. }
  438. }
  439. }
  440. err!("error verifying response")
  441. }
  442. #[derive(Deserialize, Debug)]
  443. #[allow(non_snake_case)]
  444. struct EnableYubikeyData {
  445. MasterPasswordHash: String,
  446. Key1: Option<String>,
  447. Key2: Option<String>,
  448. Key3: Option<String>,
  449. Key4: Option<String>,
  450. Key5: Option<String>,
  451. Nfc: bool,
  452. }
  453. #[derive(Deserialize, Serialize, Debug)]
  454. #[allow(non_snake_case)]
  455. pub struct YubikeyMetadata {
  456. Keys: Vec<String>,
  457. pub Nfc: bool,
  458. }
  459. use yubico::config::Config;
  460. use yubico::verify;
  461. fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
  462. let data_keys = [&data.Key1, &data.Key2, &data.Key3, &data.Key4, &data.Key5];
  463. data_keys.iter().filter_map(|e| e.as_ref().cloned()).collect()
  464. }
  465. fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
  466. let mut result = json!({});
  467. for (i, key) in yubikeys.into_iter().enumerate() {
  468. result[format!("Key{}", i + 1)] = Value::String(key);
  469. }
  470. result
  471. }
  472. fn get_yubico_credentials() -> Result<(String, String), Error> {
  473. match (CONFIG.yubico_client_id(), CONFIG.yubico_secret_key()) {
  474. (Some(id), Some(secret)) => Ok((id, secret)),
  475. _ => err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled"),
  476. }
  477. }
  478. fn verify_yubikey_otp(otp: String) -> EmptyResult {
  479. let (yubico_id, yubico_secret) = get_yubico_credentials()?;
  480. let config = Config::default().set_client_id(yubico_id).set_key(yubico_secret);
  481. match CONFIG.yubico_server() {
  482. Some(server) => verify(otp, config.set_api_hosts(vec![server])),
  483. None => verify(otp, config),
  484. }
  485. .map_res("Failed to verify OTP")
  486. .and(Ok(()))
  487. }
  488. #[post("/two-factor/get-yubikey", data = "<data>")]
  489. fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  490. // Make sure the credentials are set
  491. get_yubico_credentials()?;
  492. let data: PasswordData = data.into_inner().data;
  493. let user = headers.user;
  494. if !user.check_valid_password(&data.MasterPasswordHash) {
  495. err!("Invalid password");
  496. }
  497. let user_uuid = &user.uuid;
  498. let yubikey_type = TwoFactorType::YubiKey as i32;
  499. let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn);
  500. if let Some(r) = r {
  501. let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
  502. let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
  503. result["Enabled"] = Value::Bool(true);
  504. result["Nfc"] = Value::Bool(yubikey_metadata.Nfc);
  505. result["Object"] = Value::String("twoFactorU2f".to_owned());
  506. Ok(Json(result))
  507. } else {
  508. Ok(Json(json!({
  509. "Enabled": false,
  510. "Object": "twoFactorU2f",
  511. })))
  512. }
  513. }
  514. #[post("/two-factor/yubikey", data = "<data>")]
  515. fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
  516. let data: EnableYubikeyData = data.into_inner().data;
  517. let mut user = headers.user;
  518. if !user.check_valid_password(&data.MasterPasswordHash) {
  519. err!("Invalid password");
  520. }
  521. // Check if we already have some data
  522. let mut yubikey_data = match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &conn) {
  523. Some(data) => data,
  524. None => TwoFactor::new(user.uuid.clone(), TwoFactorType::YubiKey, String::new()),
  525. };
  526. let yubikeys = parse_yubikeys(&data);
  527. if yubikeys.is_empty() {
  528. return Ok(Json(json!({
  529. "Enabled": false,
  530. "Object": "twoFactorU2f",
  531. })));
  532. }
  533. // Ensure they are valid OTPs
  534. for yubikey in &yubikeys {
  535. if yubikey.len() == 12 {
  536. // YubiKey ID
  537. continue;
  538. }
  539. verify_yubikey_otp(yubikey.to_owned()).map_res("Invalid Yubikey OTP provided")?;
  540. }
  541. let yubikey_ids: Vec<String> = yubikeys.into_iter().map(|x| (&x[..12]).to_owned()).collect();
  542. let yubikey_metadata = YubikeyMetadata {
  543. Keys: yubikey_ids,
  544. Nfc: data.Nfc,
  545. };
  546. yubikey_data.data = serde_json::to_string(&yubikey_metadata).unwrap();
  547. yubikey_data.save(&conn)?;
  548. _generate_recover_code(&mut user, &conn);
  549. let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
  550. result["Enabled"] = Value::Bool(true);
  551. result["Nfc"] = Value::Bool(yubikey_metadata.Nfc);
  552. result["Object"] = Value::String("twoFactorU2f".to_owned());
  553. Ok(Json(result))
  554. }
  555. #[put("/two-factor/yubikey", data = "<data>")]
  556. fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
  557. activate_yubikey(data, headers, conn)
  558. }
  559. pub fn validate_yubikey_login(response: &str, twofactor_data: &str) -> EmptyResult {
  560. if response.len() != 44 {
  561. err!("Invalid Yubikey OTP length");
  562. }
  563. let yubikey_metadata: YubikeyMetadata = serde_json::from_str(twofactor_data).expect("Can't parse Yubikey Metadata");
  564. let response_id = &response[..12];
  565. if !yubikey_metadata.Keys.contains(&response_id.to_owned()) {
  566. err!("Given Yubikey is not registered");
  567. }
  568. let result = verify_yubikey_otp(response.to_owned());
  569. match result {
  570. Ok(_answer) => Ok(()),
  571. Err(_e) => err!("Failed to verify Yubikey against OTP server"),
  572. }
  573. }
  574. #[derive(Serialize, Deserialize)]
  575. struct DuoData {
  576. host: String,
  577. ik: String,
  578. sk: String,
  579. }
  580. impl DuoData {
  581. fn global() -> Option<Self> {
  582. match CONFIG.duo_host() {
  583. Some(host) => Some(Self {
  584. host,
  585. ik: CONFIG.duo_ikey().unwrap(),
  586. sk: CONFIG.duo_skey().unwrap(),
  587. }),
  588. None => None,
  589. }
  590. }
  591. fn msg(s: &str) -> Self {
  592. Self {
  593. host: s.into(),
  594. ik: s.into(),
  595. sk: s.into(),
  596. }
  597. }
  598. fn secret() -> Self {
  599. Self::msg("<global_secret>")
  600. }
  601. fn obscure(self) -> Self {
  602. let mut host = self.host;
  603. let mut ik = self.ik;
  604. let mut sk = self.sk;
  605. let digits = 4;
  606. let replaced = "************";
  607. host.replace_range(digits.., replaced);
  608. ik.replace_range(digits.., replaced);
  609. sk.replace_range(digits.., replaced);
  610. Self { host, ik, sk }
  611. }
  612. }
  613. enum DuoStatus {
  614. Global(DuoData), // Using the global duo config
  615. User(DuoData), // Using the user's config
  616. Disabled(bool), // True if there is a global setting
  617. }
  618. impl DuoStatus {
  619. fn data(self) -> Option<DuoData> {
  620. match self {
  621. DuoStatus::Global(data) => Some(data),
  622. DuoStatus::User(data) => Some(data),
  623. DuoStatus::Disabled(_) => None,
  624. }
  625. }
  626. }
  627. const DISABLED_MESSAGE_DEFAULT: &str = "<To use the global Duo keys, please leave these fields untouched>";
  628. #[post("/two-factor/get-duo", data = "<data>")]
  629. fn get_duo(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  630. let data: PasswordData = data.into_inner().data;
  631. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  632. err!("Invalid password");
  633. }
  634. let data = get_user_duo_data(&headers.user.uuid, &conn);
  635. let (enabled, data) = match data {
  636. DuoStatus::Global(_) => (true, Some(DuoData::secret())),
  637. DuoStatus::User(data) => (true, Some(data.obscure())),
  638. DuoStatus::Disabled(true) => (false, Some(DuoData::msg(DISABLED_MESSAGE_DEFAULT))),
  639. DuoStatus::Disabled(false) => (false, None),
  640. };
  641. let json = if let Some(data) = data {
  642. json!({
  643. "Enabled": enabled,
  644. "Host": data.host,
  645. "SecretKey": data.sk,
  646. "IntegrationKey": data.ik,
  647. "Object": "twoFactorDuo"
  648. })
  649. } else {
  650. json!({
  651. "Enabled": enabled,
  652. "Object": "twoFactorDuo"
  653. })
  654. };
  655. Ok(Json(json))
  656. }
  657. #[derive(Deserialize)]
  658. #[allow(non_snake_case, dead_code)]
  659. struct EnableDuoData {
  660. MasterPasswordHash: String,
  661. Host: String,
  662. SecretKey: String,
  663. IntegrationKey: String,
  664. }
  665. impl From<EnableDuoData> for DuoData {
  666. fn from(d: EnableDuoData) -> Self {
  667. Self {
  668. host: d.Host,
  669. ik: d.IntegrationKey,
  670. sk: d.SecretKey,
  671. }
  672. }
  673. }
  674. fn check_duo_fields_custom(data: &EnableDuoData) -> bool {
  675. fn empty_or_default(s: &str) -> bool {
  676. let st = s.trim();
  677. st.is_empty() || s == DISABLED_MESSAGE_DEFAULT
  678. }
  679. !empty_or_default(&data.Host) && !empty_or_default(&data.SecretKey) && !empty_or_default(&data.IntegrationKey)
  680. }
  681. #[post("/two-factor/duo", data = "<data>")]
  682. fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn) -> JsonResult {
  683. let data: EnableDuoData = data.into_inner().data;
  684. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  685. err!("Invalid password");
  686. }
  687. let (data, data_str) = if check_duo_fields_custom(&data) {
  688. let data_req: DuoData = data.into();
  689. let data_str = serde_json::to_string(&data_req)?;
  690. duo_api_request("GET", "/auth/v2/check", "", &data_req).map_res("Failed to validate Duo credentials")?;
  691. (data_req.obscure(), data_str)
  692. } else {
  693. (DuoData::secret(), String::new())
  694. };
  695. let type_ = TwoFactorType::Duo;
  696. let twofactor = TwoFactor::new(headers.user.uuid.clone(), type_, data_str);
  697. twofactor.save(&conn)?;
  698. Ok(Json(json!({
  699. "Enabled": true,
  700. "Host": data.host,
  701. "SecretKey": data.sk,
  702. "IntegrationKey": data.ik,
  703. "Object": "twoFactorDuo"
  704. })))
  705. }
  706. #[put("/two-factor/duo", data = "<data>")]
  707. fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn) -> JsonResult {
  708. activate_duo(data, headers, conn)
  709. }
  710. fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult {
  711. const AGENT: &str = "bitwarden_rs:Duo/1.0 (Rust)";
  712. use reqwest::{header::*, Client, Method};
  713. use std::str::FromStr;
  714. let url = format!("https://{}{}", &data.host, path);
  715. let date = Utc::now().to_rfc2822();
  716. let username = &data.ik;
  717. let fields = [&date, method, &data.host, path, params];
  718. let password = crypto::hmac_sign(&data.sk, &fields.join("\n"));
  719. let m = Method::from_str(method).unwrap_or_default();
  720. Client::new()
  721. .request(m, &url)
  722. .basic_auth(username, Some(password))
  723. .header(USER_AGENT, AGENT)
  724. .header(DATE, date)
  725. .send()?
  726. .error_for_status()?;
  727. Ok(())
  728. }
  729. const DUO_EXPIRE: i64 = 300;
  730. const APP_EXPIRE: i64 = 3600;
  731. const AUTH_PREFIX: &str = "AUTH";
  732. const DUO_PREFIX: &str = "TX";
  733. const APP_PREFIX: &str = "APP";
  734. use chrono::Utc;
  735. fn get_user_duo_data(uuid: &str, conn: &DbConn) -> DuoStatus {
  736. let type_ = TwoFactorType::Duo as i32;
  737. // If the user doesn't have an entry, disabled
  738. let twofactor = match TwoFactor::find_by_user_and_type(uuid, type_, &conn) {
  739. Some(t) => t,
  740. None => return DuoStatus::Disabled(DuoData::global().is_some()),
  741. };
  742. // If the user has the required values, we use those
  743. if let Ok(data) = serde_json::from_str(&twofactor.data) {
  744. return DuoStatus::User(data);
  745. }
  746. // Otherwise, we try to use the globals
  747. if let Some(global) = DuoData::global() {
  748. return DuoStatus::Global(global);
  749. }
  750. // If there are no globals configured, just disable it
  751. DuoStatus::Disabled(false)
  752. }
  753. // let (ik, sk, ak, host) = get_duo_keys();
  754. fn get_duo_keys_email(email: &str, conn: &DbConn) -> ApiResult<(String, String, String, String)> {
  755. let data = User::find_by_mail(email, &conn)
  756. .and_then(|u| get_user_duo_data(&u.uuid, &conn).data())
  757. .or_else(DuoData::global)
  758. .map_res("Can't fetch Duo keys")?;
  759. Ok((data.ik, data.sk, CONFIG.get_duo_akey(), data.host))
  760. }
  761. pub fn generate_duo_signature(email: &str, conn: &DbConn) -> ApiResult<(String, String)> {
  762. let now = Utc::now().timestamp();
  763. let (ik, sk, ak, host) = get_duo_keys_email(email, conn)?;
  764. let duo_sign = sign_duo_values(&sk, email, &ik, DUO_PREFIX, now + DUO_EXPIRE);
  765. let app_sign = sign_duo_values(&ak, email, &ik, APP_PREFIX, now + APP_EXPIRE);
  766. Ok((format!("{}:{}", duo_sign, app_sign), host))
  767. }
  768. fn sign_duo_values(key: &str, email: &str, ikey: &str, prefix: &str, expire: i64) -> String {
  769. let val = format!("{}|{}|{}", email, ikey, expire);
  770. let cookie = format!("{}|{}", prefix, BASE64.encode(val.as_bytes()));
  771. format!("{}|{}", cookie, crypto::hmac_sign(key, &cookie))
  772. }
  773. pub fn validate_duo_login(email: &str, response: &str, conn: &DbConn) -> EmptyResult {
  774. let split: Vec<&str> = response.split(':').collect();
  775. if split.len() != 2 {
  776. err!("Invalid response length");
  777. }
  778. let auth_sig = split[0];
  779. let app_sig = split[1];
  780. let now = Utc::now().timestamp();
  781. let (ik, sk, ak, _host) = get_duo_keys_email(email, conn)?;
  782. let auth_user = parse_duo_values(&sk, auth_sig, &ik, AUTH_PREFIX, now)?;
  783. let app_user = parse_duo_values(&ak, app_sig, &ik, APP_PREFIX, now)?;
  784. if !crypto::ct_eq(&auth_user, app_user) || !crypto::ct_eq(&auth_user, email) {
  785. err!("Error validating duo authentication")
  786. }
  787. Ok(())
  788. }
  789. fn parse_duo_values(key: &str, val: &str, ikey: &str, prefix: &str, time: i64) -> ApiResult<String> {
  790. let split: Vec<&str> = val.split('|').collect();
  791. if split.len() != 3 {
  792. err!("Invalid value length")
  793. }
  794. let u_prefix = split[0];
  795. let u_b64 = split[1];
  796. let u_sig = split[2];
  797. let sig = crypto::hmac_sign(key, &format!("{}|{}", u_prefix, u_b64));
  798. if !crypto::ct_eq(crypto::hmac_sign(key, &sig), crypto::hmac_sign(key, u_sig)) {
  799. err!("Duo signatures don't match")
  800. }
  801. if u_prefix != prefix {
  802. err!("Prefixes don't match")
  803. }
  804. let cookie_vec = match BASE64.decode(u_b64.as_bytes()) {
  805. Ok(c) => c,
  806. Err(_) => err!("Invalid Duo cookie encoding"),
  807. };
  808. let cookie = match String::from_utf8(cookie_vec) {
  809. Ok(c) => c,
  810. Err(_) => err!("Invalid Duo cookie encoding"),
  811. };
  812. let cookie_split: Vec<&str> = cookie.split('|').collect();
  813. if cookie_split.len() != 3 {
  814. err!("Invalid cookie length")
  815. }
  816. let username = cookie_split[0];
  817. let u_ikey = cookie_split[1];
  818. let expire = cookie_split[2];
  819. if !crypto::ct_eq(ikey, u_ikey) {
  820. err!("Invalid ikey")
  821. }
  822. let expire = match expire.parse() {
  823. Ok(e) => e,
  824. Err(_) => err!("Invalid expire time"),
  825. };
  826. if time >= expire {
  827. err!("Expired authorization")
  828. }
  829. Ok(username.into())
  830. }