dani-garcia / vaultwarden

Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs
GNU Affero General Public License v3.0
38.64k stars 1.87k forks source link

Emergency Access Takeover not storing password #3196

Closed rsoftnl closed 1 year ago

rsoftnl commented 1 year ago

Hi,

I'm trying to access a users vault by means of the Emergency Access feature. Everything appears to be setup correctly, but when I'm given the opportunity to takeover the Master Password (after 7 days wait period, E-mail from Vaultwarden received it has granted me rights) I can't change the Master Password. It will allow me to enter the new password, but Save seems to not do anything at all. The window does not close and the LOG shows a 200 message

Your environment (Generated via diagnostics page)

Config (Generated via diagnostics page)

Show Running Config **Environment settings which are overridden:** ADMIN_TOKEN ```json { "_duo_akey": null, "_enable_duo": false, "_enable_email_2fa": true, "_enable_smtp": true, "_enable_yubico": true, "_icon_service_csp": "", "_icon_service_url": "", "_ip_header_enabled": true, "_smtp_img_src": "cid:", "admin_ratelimit_max_burst": 3, "admin_ratelimit_seconds": 300, "admin_token": "***", "allowed_iframe_ancestors": "", "attachments_folder": "data/attachments", "authenticator_disable_time_drift": false, "data_folder": "data", "database_conn_init": "", "database_max_conns": 10, "database_timeout": 30, "database_url": "***************", "db_connection_retries": 15, "disable_2fa_remember": false, "disable_admin_token": false, "disable_icon_download": false, "domain": "*****://*************************", "domain_origin": "*****://*************************", "domain_path": "", "domain_set": true, "duo_host": null, "duo_ikey": null, "duo_skey": null, "email_attempts_limit": 3, "email_expiration_time": 600, "email_token_size": 6, "emergency_access_allowed": true, "emergency_notification_reminder_schedule": "0 3 * * * *", "emergency_request_timeout_schedule": "0 7 * * * *", "enable_db_wal": true, "event_cleanup_schedule": "0 10 0 * * *", "events_days_retain": null, "extended_logging": true, "helo_name": "********", "hibp_api_key": null, "icon_blacklist_non_global_ips": true, "icon_blacklist_regex": null, "icon_cache_folder": "data/icon_cache", "icon_cache_negttl": 259200, "icon_cache_ttl": 2592000, "icon_download_timeout": 10, "icon_redirect_code": 302, "icon_service": "internal", "incomplete_2fa_schedule": "30 * * * * *", "incomplete_2fa_time_limit": 3, "invitation_expiration_hours": 120, "invitation_org_name": "Vaultwarden", "invitations_allowed": true, "ip_header": "X-Real-IP", "job_poll_interval_ms": 30000, "log_file": "/data/vaultwarden.log", "log_level": "INFO", "log_timestamp_format": "%Y-%m-%d %H:%M:%S.%3f", "login_ratelimit_max_burst": 10, "login_ratelimit_seconds": 60, "org_attachment_limit": null, "org_creation_users": "***************************", "org_events_enabled": false, "org_groups_enabled": false, "password_hints_allowed": false, "password_iterations": 100000, "reload_templates": false, "require_device_email": false, "rsa_key_filename": "data/rsa_key", "send_purge_schedule": "0 5 * * * *", "sends_allowed": true, "sends_folder": "data/sends", "show_password_hint": false, "signups_allowed": false, "signups_domains_whitelist": "********", "signups_verify": true, "signups_verify_resend_limit": 6, "signups_verify_resend_time": 3600, "smtp_accept_invalid_certs": false, "smtp_accept_invalid_hostnames": false, "smtp_auth_mechanism": "LOGIN", "smtp_debug": false, "smtp_embed_images": true, "smtp_explicit_tls": null, "smtp_from": "**********************", "smtp_from_name": "Vaultwarden", "smtp_host": "*****************", "smtp_password": "***", "smtp_port": 587, "smtp_security": "starttls", "smtp_ssl": null, "smtp_timeout": 15, "smtp_username": "**********************", "templates_folder": "data/templates", "tmp_folder": "data/tmp", "trash_auto_delete_days": 30, "trash_purge_schedule": "0 5 0 * * *", "use_syslog": false, "user_attachment_limit": null, "web_vault_enabled": true, "web_vault_folder": "web-vault/", "websocket_address": "0.0.0.0", "websocket_enabled": true, "websocket_port": 3012, "yubico_client_id": null, "yubico_secret_key": null, "yubico_server": null } ```

Thanks in advance for any suggestions

rsoftnl commented 1 year ago

The database record in the Emergency Access table is: Database dump of the record in question INSERT INTO emergency_access VALUES('','','',NULL,NULL,1,4,7,'2023-01-22 12:20:38.749272006','2023-01-29 13:03:24.861587170','2023-01-29 13:07:24.872023194','2022-07-20 18:00:44.595490964');

rsoftnl commented 1 year ago

[2023-01-29 16:30:13.214][request][INFO] POST /identity/accounts/prelogin [2023-01-29 16:30:13.217][response][INFO] (prelogin) POST /identity/accounts/prelogin => 200 OK [2023-01-29 16:30:13.299][request][INFO] POST /identity/connect/token [2023-01-29 16:30:13.484][vaultwarden::api::identity][INFO] User logged in successfully. IP: [2023-01-29 16:30:13.484][response][INFO] (login) POST /identity/connect/token => 200 OK [2023-01-29 16:30:13.604][request][INFO] POST /identity/connect/token [2023-01-29 16:30:13.613][response][INFO] (login) POST /identity/connect/token => 200 OK [2023-01-29 16:30:13.645][request][INFO] GET /api/sync?excludeDomains=true [2023-01-29 16:30:13.694][vaultwarden::api::notifications][INFO] Accepting WS connection from :41792 [2023-01-29 16:30:13.696][tungstenite::handshake::server][DEBUG] Server handshake done. [2023-01-29 16:30:13.743][response][INFO] (sync) GET /api/sync? => 200 OK [2023-01-29 16:30:18.028][request][INFO] GET /api/accounts/profile [2023-01-29 16:30:18.030][response][INFO] (profile) GET /api/accounts/profile => 200 OK [2023-01-29 16:30:18.036][request][INFO] GET /api/two-factor [2023-01-29 16:30:18.038][response][INFO] (get_twofactor) GET /api/two-factor => 200 OK [2023-01-29 16:30:19.726][request][INFO] GET /api/emergency-access/trusted [2023-01-29 16:30:19.730][response][INFO] (get_contacts) GET /api/emergency-access/trusted => 200 OK [2023-01-29 16:30:19.754][request][INFO] GET /api/emergency-access/granted [2023-01-29 16:30:19.759][response][INFO] (get_grantees) GET /api/emergency-access/granted => 200 OK [2023-01-29 16:30:22.396][request][INFO] GET /api/emergency-access/f4861a66-08c5-4d59-8725-a54339e91a1d/policies [2023-01-29 16:30:22.400][response][INFO] (policies_emergency_access) GET /api/emergency-access//policies => 200 OK [2023-01-29 16:30:34.513][request][INFO] POST /api/emergency-access/f4861a66-08c5-4d59-8725-a54339e91a1d/takeover [2023-01-29 16:30:34.516][response][INFO] (takeover_emergency_access) POST /api/emergency-access//takeover => 200 OK [2023-01-29 16:30:59.998][vaultwarden::api::core::two_factor][DEBUG] Sending notifications for incomplete 2FA logins [2023-01-29 16:31:00.202][request][INFO] GET /alive [2023-01-29 16:31:00.203][response][INFO] (alive) GET /alive => 200 OK [2023-01-29 16:31:19.936][request][INFO] POST /identity/accounts/prelogin [2023-01-29 16:31:19.937][response][INFO] (prelogin) POST /identity/accounts/prelogin => 200 OK [2023-01-29 16:31:20.036][request][INFO] POST /identity/connect/token [2023-01-29 16:31:20.217][vaultwarden::api::identity][ERROR] Username or password is incorrect. Try again. IP: . Username: <vault I'm trying to take over>. [2023-01-29 16:31:20.217][response][INFO] (login) POST /identity/connect/token => 400 Bad Request

rsoftnl commented 1 year ago

image

image

BlackDex commented 1 year ago

Seems to work fine for me. It doesn't log you out from your own account. It just updates/changes the password and keys for the takeover account.

rsoftnl commented 1 year ago

I know and I did try to login with the username of the grantor with the new password, but failed. The thing is: if I click on save as grantee, nothing seems to happen. Window doesn't close, no message saying that the new password is save/applied, ... (I do see a message if the 2nd password doesn't match the first. So that works...)

BlackDex commented 1 year ago

Again, it does work for me. Just tested it without any issues.

Maybe something else on your side is blocking the request? Try to set it via a different browser, or use a Private/incognito window. Some browser extensions (AdGuard), or Firewall/Anti-Virus software tend to block some headers.

Also, on the reverse proxy side, i suggest to check the logs there. because i'm missing the POST that tells it to change the password.

It should look like this, but i only see the POST to the /takeover in your example.

[2023-01-29 18:15:44.798][request][INFO] POST /api/emergency-access/f3891612-1c27-49f8-8641-1ab4b7601941/takeover
[2023-01-29 18:15:44.799][response][INFO] (takeover_emergency_access) POST /api/emergency-access/<emer_id>/takeover => 200 OK
[2023-01-29 18:15:45.114][request][INFO] POST /api/emergency-access/f3891612-1c27-49f8-8641-1ab4b7601941/password
[2023-01-29 18:15:46.698][response][INFO] (password_emergency_access) POST /api/emergency-access/<emer_id>/password => 200 OK
stefan0xC commented 1 year ago

I think I can reproduce this by adding (and confirming) someone as an emergency contact with only view access and then later changing their role to takeover. Afterwards I can attempt an takeover but even if I approve it, it will not work (i.e. do nothing as described above) because the field emergency_access.key_encrypted will be empty (or NULL as above).

On the other hand, if I add and confirm someone with takeover permissions from the beginning, this field will not be empty.

BlackDex commented 1 year ago

@stefan0xC But, as far as i know, you can't change it from takeover to view without removing/rejecting the previous approved one. So, that is probably not the issue here.

Update: Ah, wait, you can indeed. That does cause issues. Not sure if that is also the same with Bitwarden Self-Hosted.

BlackDex commented 1 year ago

We probably need to prevent updating the key if it is not set. It looks like that it doesn't send the key on a PUT.

rsoftnl commented 1 year ago

As far as I can remember, this was a straight TakeOver config, not first a View Only. Is there any digging I can do on my side to verify this situation? I'm also trying to rule out my nginx reverse proxy, but Vaultwarden (understandably) does not allow me to log in over HTTP. I need some more time to figure out the best way to access Vaultwarden directly in my network. I do get an additional POST logline if I click on Save, but that was a /takeover/ URL and not a /password/ URL if I recall correctly (not at home right now and therefor not able to debug at the moment).

Is there a way to verify the user has no items in it's personal vault? I have this disabled in the policy, but I'm unsure whether this was prior to this user being created. Currently this is the view in the Admin Panel for this specific user: image (being very curious at the "Never" for Last Active as I'm quite sure I had this active on the users smartphone and I'm 100% sure I have logged into the users account via web to help create the emergency contact)

BlackDex commented 1 year ago

That value is derived from the device table. If for some reason all sessions are deauthorized for that user, that table will have no records for that user, and thus will show up as never.

If you are sure that you did not switched anything, not even the amount of days for example, then i suggest to check (as mentioned before) your reverse proxy log and anything else infront of Vaultwarden which could have blocked calls. Because i'm missing the POST call to update the password as mentioned earlier.

rsoftnl commented 1 year ago

OK, the deauthorized sessions will indeed be the cause of Never. I did try that to force a full login for the user. I will have a look at the proxy logs tonight.

stefan0xC commented 1 year ago

We probably need to prevent updating the key if it is not set. It looks like that it doesn't send the key on a PUT.

Can confirm that this is the case.

If I replace https://github.com/dani-garcia/vaultwarden/blob/9366e3145231034040d7d2e0216070163d6d889f/src/api/core/emergency_access.rs#L126 with

    if data.KeyEncrypted.is_some() {
        emergency_access.key_encrypted = data.KeyEncrypted;
     }

the field will not be emptied when changing the user level from view to takeover (or the number of days).

rsoftnl commented 1 year ago

I tried my best to get a self signed certificate setup using https://github.com/dani-garcia/vaultwarden/wiki/Private-CA-and-self-signed-certs-that-work-with-Chrome but could not get it to work. As it explicitely states no support will be provided, I will not ask any ;-) I did however establish that the user has no personal passwords in their vault, just in collections (by checking in the ciphers table in the database, no records have a user_uuid). So I'll create a new account and attach it to the right collections.

The logging from my nginx is below: [30/Jan/2023:21:33:11 +0000] - 200 200 - GET https "/api/emergency-access/f4861a66-08c5-4d59-8725-a54339e91a1d/policies" [Client 192.168.1.175] [Length 1047] [Gzip -] [Sent-to 192.168.5.42] "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61" "https:///" [30/Jan/2023:21:33:29 +0000] - 200 200 - POST https "/api/emergency-access/f4861a66-08c5-4d59-8725-a54339e91a1d/takeover" [Client 192.168.1.175] [Length 87] [Gzip -] [Sent-to 192.168.5.42] "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61" "https:///"

This seems to correspond to the following lines in the VaultWarden log: [2023-01-30 21:33:11.554][request][INFO] GET /api/emergency-access/f4861a66-08c5-4d59-8725-a54339e91a1d/policies [2023-01-30 21:33:11.559][response][INFO] (policies_emergency_access) GET /api/emergency-access//policies => 200 OK [2023-01-30 21:33:29.833][request][INFO] POST /api/emergency-access/f4861a66-08c5-4d59-8725-a54339e91a1d/takeover [2023-01-30 21:33:29.836][response][INFO] (takeover_emergency_access) POST /api/emergency-access//takeover => 200 OK

I've tried this in both Chrome and Edge (although I'm not sure there's too much difference as both are Chromium based), in private modes and with UBlock Origin disabled for the VaultWarden site. I'm not sure where things are going wrong and why the /password POST is not even reaching my nginx, but for now I'm not sure I can do any additional debugging.

For me it's fine to close this issue, but I'm not sure you already want it closed as you may have identified a bug (although it may be unrelated to this issue). So I'll leave the honours to close it to you :-D Thanks for your help!

stefan0xC commented 1 year ago

@rsoftnl given that the table did not have the encrypted_key field set, I don't think this was a problem with your setup.

If you did check the developer console of your browser while trying to save a new password for the grantor and nothing happens you would have seen that what actually happens is that the web-vault will throw an error Error: Uncaught (in promise): TypeError: e is null as it is getting the following JSON as a response to the POST:

{"Kdf":0,"KdfIterations":100000,"KeyEncrypted":null,"Object":"emergencyAccessTakeover"}

Since this is happening on the client side it's not something you can check (or even notice) in the server logs aside from not seeing the call you expect. And because the database entry has been corrupted somehow (maybe you just opened and saved the grantee again without changing anything?) there's not really anything you could do except removing the grantee and adding them again.