From 96ef1b0a733aba39b8965a8fc48898f4b1cf4329 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Fri, 6 Oct 2023 11:11:40 -0400 Subject: [PATCH] Add server version compatibility check for Fido2Credentials on sharing with org (#3328) * Added compatibility checks. * Refactored into separate methods for easier removal. * Added check on ShareMany * Updated method order to be consistent. * Linting --- .../Vault/Controllers/CiphersController.cs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs index 8e4c24dd5792..6a7de61f9d8f 100644 --- a/src/Api/Vault/Controllers/CiphersController.cs +++ b/src/Api/Vault/Controllers/CiphersController.cs @@ -180,7 +180,8 @@ public async Task Put(Guid id, [FromBody] CipherRequestMode throw new NotFoundException(); } - ValidateItemLevelEncryptionIsAvailable(cipher); + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + ValidateClientVersionForFido2CredentialSupport(cipher); var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList(); var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ? @@ -191,14 +192,6 @@ public async Task Put(Guid id, [FromBody] CipherRequestMode "then try again."); } - // Temporary protection against old clients overwriting and deleting Fido2Keys - // Response model used to re-use logic for parsing 'data' property - var cipherModel = new CipherResponseModel(cipher, _globalSettings); - if (cipherModel.Login?.Fido2Credentials != null && _currentContext.ClientVersion < _fido2KeyCipherMinimumVersion) - { - throw new BadRequestException("Please update your client to edit this item."); - } - await _cipherService.SaveDetailsAsync(model.ToCipherDetails(cipher), userId, model.LastKnownRevisionDate, collectionIds); var response = new CipherResponseModel(cipher, _globalSettings); @@ -212,7 +205,8 @@ public async Task PutAdmin(Guid id, [FromBody] CipherRe var userId = _userService.GetProperUserId(User).Value; var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(id); - ValidateItemLevelEncryptionIsAvailable(cipher); + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + ValidateClientVersionForFido2CredentialSupport(cipher); if (cipher == null || !cipher.OrganizationId.HasValue || !await _currentContext.EditAnyCollection(cipher.OrganizationId.Value)) @@ -277,6 +271,9 @@ public async Task PutShare(string id, [FromBody] CipherShar throw new NotFoundException(); } + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + ValidateClientVersionForFido2CredentialSupport(cipher); + var original = cipher.Clone(); await _cipherService.ShareAsync(original, model.Cipher.ToCipher(cipher), new Guid(model.Cipher.OrganizationId), model.CollectionIds.Select(c => new Guid(c)), userId, model.Cipher.LastKnownRevisionDate); @@ -539,7 +536,12 @@ public async Task PutShareMany([FromBody] CipherBulkShareRequestModel model) throw new BadRequestException("Trying to move ciphers that you do not own."); } - shareCiphers.Add((cipher.ToCipher(ciphersDict[cipher.Id.Value]), cipher.LastKnownRevisionDate)); + var existingCipher = ciphersDict[cipher.Id.Value]; + + ValidateClientVersionForItemLevelEncryptionSupport(existingCipher); + ValidateClientVersionForFido2CredentialSupport(existingCipher); + + shareCiphers.Add((cipher.ToCipher(existingCipher), cipher.LastKnownRevisionDate)); } await _cipherService.ShareManyAsync(shareCiphers, organizationId, @@ -592,7 +594,7 @@ await _cipherRepository.GetOrganizationDetailsByIdAsync(idGuid) : throw new NotFoundException(); } - ValidateItemLevelEncryptionIsAvailable(cipher); + ValidateClientVersionForItemLevelEncryptionSupport(cipher); if (request.FileSize > CipherService.MAX_FILE_SIZE) { @@ -814,11 +816,23 @@ private void ValidateAttachment() } } - private void ValidateItemLevelEncryptionIsAvailable(Cipher cipher) + private void ValidateClientVersionForItemLevelEncryptionSupport(Cipher cipher) { if (cipher.Key != null && _currentContext.ClientVersion < _cipherKeyEncryptionMinimumVersion) { throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again."); } } + + private void ValidateClientVersionForFido2CredentialSupport(Cipher cipher) + { + if (cipher.Type == Core.Vault.Enums.CipherType.Login) + { + var loginData = JsonSerializer.Deserialize(cipher.Data); + if (loginData?.Fido2Credentials != null && _currentContext.ClientVersion < _fido2KeyCipherMinimumVersion) + { + throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again."); + } + } + } }