diff -ruN -x .git git-annex-5.20140412ubuntu1/CHANGELOG git-annex/CHANGELOG --- git-annex-5.20140412ubuntu1/CHANGELOG 2014-04-14 02:41:33.000000000 -0700 +++ git-annex/CHANGELOG 2015-01-12 11:36:35.622398786 -0800 @@ -1,3 +1,14 @@ +git-annex (5.20140412ubuntu2) trusty; urgency=medium + + * Backport fixes for CVE-2014-6274 (Joey Hess): + - Security fix for S3 and glacier when using embedcreds=yes with + encryption=pubkey or encryption=hybrid. The creds embedded in the + git repo were *not* encrypted. git-annex enableremote will warn + when used on a remote that has this problem. For details, see: + https://git-annex.branchable.com/upgrades/insecure_embedded_creds/ + + -- Dave Pifke Mon, 12 Jan 2015 09:42:36 -0800 + git-annex (5.20140412ubuntu1) trusty; urgency=medium * Patch from git to fix building on architectures without GHCi (Joey diff -ruN -x .git git-annex-5.20140412ubuntu1/Creds.hs git-annex/Creds.hs --- git-annex-5.20140412ubuntu1/Creds.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Creds.hs 2015-01-12 11:36:22.169062064 -0800 @@ -22,7 +22,7 @@ import Utility.FileMode import Crypto import Types.Remote (RemoteConfig, RemoteConfigKey) -import Remote.Helper.Encryptable (remoteCipher, embedCreds) +import Remote.Helper.Encryptable (remoteCipher, remoteCipher', embedCreds, EncryptionIsSetup) import Utility.Env (getEnv) import qualified Data.ByteString.Lazy.Char8 as L @@ -39,12 +39,17 @@ {- Stores creds in a remote's configuration, if the remote allows - that. Otherwise, caches them locally. - - The creds are found in storage if not provided. -} -setRemoteCredPair :: RemoteConfig -> CredPairStorage -> Maybe CredPair -> Annex RemoteConfig -setRemoteCredPair c storage Nothing = - maybe (return c) (setRemoteCredPair c storage . Just) + - The creds are found in storage if not provided. + - + - The remote's configuration should have already had a cipher stored in it + - if that's going to be done, so that the creds can be encrypted using the + - cipher. The EncryptionIsSetup phantom type ensures that is the case. + -} +setRemoteCredPair :: EncryptionIsSetup -> RemoteConfig -> CredPairStorage -> Maybe CredPair -> Annex RemoteConfig +setRemoteCredPair encsetup c storage Nothing = + maybe (return c) (setRemoteCredPair encsetup c storage . Just) =<< getRemoteCredPair c storage -setRemoteCredPair c storage (Just creds) +setRemoteCredPair _ c storage (Just creds) | embedCreds c = case credPairRemoteKey storage of Nothing -> localcache Just key -> storeconfig key =<< remoteCipher c @@ -84,20 +89,32 @@ fromcache = maybe fromconfig (return . Just) =<< readCacheCredPair storage fromconfig = case credPairRemoteKey storage of Just key -> do - mcipher <- remoteCipher c + mcipher <- remoteCipher' c case (M.lookup key c, mcipher) of (Nothing, _) -> return Nothing - (Just enccreds, Just cipher) -> do - creds <- liftIO $ decrypt cipher - (feedBytes $ L.pack $ fromB64 enccreds) - (readBytes $ return . L.unpack) - fromcreds creds + (Just enccreds, Just (cipher, storablecipher)) -> + fromenccreds enccreds cipher storablecipher (Just bcreds, Nothing) -> fromcreds $ fromB64 bcreds Nothing -> return Nothing + fromenccreds enccreds cipher storablecipher = do + mcreds <- liftIO $ catchMaybeIO $ decrypt cipher + (feedBytes $ L.pack $ fromB64 enccreds) + (readBytes $ return . L.unpack) + case mcreds of + Just creds -> fromcreds creds + Nothing -> do + -- Work around un-encrypted creds storage + -- bug in old S3 and glacier remotes. + -- Not a problem for shared cipher. + case storablecipher of + SharedCipher {} -> showLongNote "gpg error above was caused by an old git-annex bug in credentials storage. Working around it.." + _ -> error "*** Insecure credentials storage detected for this remote! See https://git-annex.branchable.com/upgrades/insecure_embedded_creds/" + fromcreds $ fromB64 enccreds fromcreds creds = case decodeCredPair creds of Just credpair -> do writeCacheCredPair credpair storage + return $ Just credpair _ -> error "bad creds" diff -ruN -x .git git-annex-5.20140412ubuntu1/debian/changelog git-annex/debian/changelog --- git-annex-5.20140412ubuntu1/debian/changelog 2014-04-14 02:41:33.000000000 -0700 +++ git-annex/debian/changelog 2015-01-12 11:36:35.622398786 -0800 @@ -1,3 +1,14 @@ +git-annex (5.20140412ubuntu2) trusty; urgency=medium + + * Backport fixes for CVE-2014-6274 (Joey Hess): + - Security fix for S3 and glacier when using embedcreds=yes with + encryption=pubkey or encryption=hybrid. The creds embedded in the + git repo were *not* encrypted. git-annex enableremote will warn + when used on a remote that has this problem. For details, see: + https://git-annex.branchable.com/upgrades/insecure_embedded_creds/ + + -- Dave Pifke Mon, 12 Jan 2015 09:42:36 -0800 + git-annex (5.20140412ubuntu1) trusty; urgency=medium * Patch from git to fix building on architectures without GHCi (Joey diff -ruN -x .git git-annex-5.20140412ubuntu1/doc/upgrades/insecure_embedded_creds.mdwn git-annex/doc/upgrades/insecure_embedded_creds.mdwn --- git-annex-5.20140412ubuntu1/doc/upgrades/insecure_embedded_creds.mdwn 1969-12-31 16:00:00.000000000 -0800 +++ git-annex/doc/upgrades/insecure_embedded_creds.mdwn 2015-01-12 11:35:06.882350537 -0800 @@ -0,0 +1,42 @@ +git-annex had a bug in the S3 and Glacier remotes where if embedcreds=yes +was set, and the remote used encryption=pubkey or encryption=hybrid, +the embedded AWS credentials were stored in the git repository +in (effectively) plaintext, not encrypted as they were supposed to be. + +That means that anyone who gets a copy of the git repository can extract the +AWS credentials from it. Which would be bad.. + +A remote with this problem cannot be enabled using `git annex +enableremote`. Old versions of git-annex will fail with a gpg error; +the current version will fail with a pointer to this web page. + +If your repository has this problem, you have two courses of action to fix +it: + +1. Change your AWS credentials, so the ones stored in the clear in git + won't be used. + + After changing the credentials, make sure you have a + fixed version of git-annex, and you can then re-embed the new creds + into the repository, encrypted this time, by setting the + `AWS_SECRET_ACCESS_KEY` and `AWS_ACCESS_KEY_ID` environment variables, + and running `git annex enableremote $remotename embedcreds=yes` + +2. Fix the problem and then remove the history of the git-annex branch + of the repository. + + Make sure you have a fixed version of git-annex, and force git-annex + to rewrite the embedded creds, with encryption this time, by setting + by setting the `AWS_SECRET_ACCESS_KEY` and `AWS_ACCESS_KEY_ID` + environment variables, and running `git annex enableremote $remotename embedcreds=yes` + + Then, to get rid of old versions of the git-annex branch that still + contain the creds in cleartext, you can use `git annex forget`; + note that it will remove other historical data too. + + Keep in mind that this will not necessarily delete data from clones + you do not control. + +3. If you're sure that you're the only one who has access to the repository, + you could decide to leave it as-is. It's no more insecure than if you + had used encryption=shared in the first place when setting it up. diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/Bup.hs git-annex/Remote/Bup.hs --- git-annex-5.20140412ubuntu1/Remote/Bup.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Remote/Bup.hs 2015-01-12 10:09:18.459334522 -0800 @@ -90,7 +90,7 @@ -- verify configuration is sane let buprepo = fromMaybe (error "Specify buprepo=") $ M.lookup "buprepo" c - c' <- encryptionSetup c + (c', _encsetup) <- encryptionSetup c -- bup init will create the repository. -- (If the repository already exists, bup init again appears safe.) diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/Directory.hs git-annex/Remote/Directory.hs --- git-annex-5.20140412ubuntu1/Remote/Directory.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Remote/Directory.hs 2015-01-12 10:09:18.459334522 -0800 @@ -77,7 +77,7 @@ absdir <- liftIO $ absPath dir liftIO $ unlessM (doesDirectoryExist absdir) $ error $ "Directory does not exist: " ++ absdir - c' <- encryptionSetup c + (c', _encsetup) <- encryptionSetup c -- The directory is stored in git config, not in this remote's -- persistant state, so it can vary between hosts. diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/External.hs git-annex/Remote/External.hs --- git-annex-5.20140412ubuntu1/Remote/External.hs 2014-04-02 15:46:31.000000000 -0700 +++ git-annex/Remote/External.hs 2015-01-12 10:09:18.459334522 -0800 @@ -79,7 +79,7 @@ u <- maybe (liftIO genUUID) return mu let externaltype = fromMaybe (error "Specify externaltype=") $ M.lookup "externaltype" c - c' <- encryptionSetup c + (c', _encsetup) <- encryptionSetup c external <- newExternal externaltype u c' handleRequest external INITREMOTE Nothing $ \resp -> case resp of @@ -226,7 +226,7 @@ send $ VALUE value handleRemoteRequest (SETCREDS setting login password) = do c <- liftIO $ atomically $ readTMVar $ externalConfig external - c' <- setRemoteCredPair c (credstorage setting) $ + c' <- setRemoteCredPair encryptionAlreadySetup c (credstorage setting) $ Just (login, password) void $ liftIO $ atomically $ swapTMVar (externalConfig external) c' handleRemoteRequest (GETCREDS setting) = do diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/GCrypt.hs git-annex/Remote/GCrypt.hs --- git-annex-5.20140412ubuntu1/Remote/GCrypt.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Remote/GCrypt.hs 2015-01-12 10:09:18.459334522 -0800 @@ -156,7 +156,7 @@ remotename = fromJust (M.lookup "name" c) go Nothing = error "Specify gitrepo=" go (Just gitrepo) = do - c' <- encryptionSetup c + (c', _encsetup) <- encryptionSetup c inRepo $ Git.Command.run [ Params "remote add" , Param remotename diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/Glacier.hs git-annex/Remote/Glacier.hs --- git-annex-5.20140412ubuntu1/Remote/Glacier.hs 2014-04-02 15:46:31.000000000 -0700 +++ git-annex/Remote/Glacier.hs 2015-01-12 10:09:18.459334522 -0800 @@ -73,12 +73,12 @@ glacierSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) glacierSetup mu mcreds c = do u <- maybe (liftIO genUUID) return mu - c' <- setRemoteCredPair c (AWS.creds u) mcreds - glacierSetup' (isJust mu) u c' -glacierSetup' :: Bool -> UUID -> RemoteConfig -> Annex (RemoteConfig, UUID) -glacierSetup' enabling u c = do - c' <- encryptionSetup c - let fullconfig = c' `M.union` defaults + glacierSetup' (isJust mu) u mcreds c +glacierSetup' :: Bool -> UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) +glacierSetup' enabling u mcreds c = do + (c', encsetup) <- encryptionSetup c + c'' <- setRemoteCredPair encsetup c' (AWS.creds u) mcreds + let fullconfig = c'' `M.union` defaults unless enabling $ genVault fullconfig u gitConfigSpecialRemote u fullconfig "glacier" "true" diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/Helper/Encryptable.hs git-annex/Remote/Helper/Encryptable.hs --- git-annex-5.20140412ubuntu1/Remote/Helper/Encryptable.hs 2013-10-17 15:58:50.000000000 -0700 +++ git-annex/Remote/Helper/Encryptable.hs 2015-01-12 11:36:10.902642149 -0800 @@ -5,7 +5,19 @@ - Licensed under the GNU GPL version 3 or higher. -} -module Remote.Helper.Encryptable where +module Remote.Helper.Encryptable ( + EncryptionIsSetup, + encryptionSetup, + encryptableRemote, + noEncryptionUsed, + encryptionAlreadySetup, + remoteCipher, + remoteCipher', + embedCreds, + cipherKey, + storeCipher, + extractCipher, +) where import qualified Data.Map as M @@ -18,11 +30,26 @@ import Utility.Base64 import Utility.Metered +-- Used to ensure that encryption has been set up before trying to +-- eg, store creds in the remote config that would need to use the +-- encryption setup. +data EncryptionIsSetup = EncryptionIsSetup | NoEncryption + +-- Remotes that don't use encryption can use this instead of +-- encryptionSetup. +noEncryptionUsed :: EncryptionIsSetup +noEncryptionUsed = NoEncryption + +-- Using this avoids the type-safe check, so you'd better be sure +-- of what you're doing. +encryptionAlreadySetup :: EncryptionIsSetup +encryptionAlreadySetup = EncryptionIsSetup + {- Encryption setup for a remote. The user must specify whether to use - an encryption key, or not encrypt. An encrypted cipher is created, or is - updated to be accessible to an additional encryption key. Or the user - could opt to use a shared cipher, which is stored unencrypted. -} -encryptionSetup :: RemoteConfig -> Annex RemoteConfig +encryptionSetup :: RemoteConfig -> Annex (RemoteConfig, EncryptionIsSetup) encryptionSetup c = maybe genCipher updateCipher $ extractCipher c where -- The type of encryption @@ -30,7 +57,7 @@ -- Generate a new cipher, depending on the chosen encryption scheme genCipher = case encryption of _ | M.member "cipher" c || M.member "cipherkeys" c -> cannotchange - Just "none" -> return c + Just "none" -> return (c, NoEncryption) Just "shared" -> use "encryption setup" . genSharedCipher =<< highRandomQuality -- hybrid encryption is the default when a keyid is @@ -50,7 +77,7 @@ cannotchange = error "Cannot set encryption type of existing remotes." -- Update an existing cipher if possible. updateCipher v = case v of - SharedCipher _ | maybe True (== "shared") encryption -> return c' + SharedCipher _ | maybe True (== "shared") encryption -> return (c', EncryptionIsSetup) EncryptedCipher _ variant _ | maybe True (== if variant == Hybrid then "hybrid" else "pubkey") encryption -> use "encryption update" $ updateEncryptedCipher newkeys v @@ -59,7 +86,7 @@ showNote m cipher <- liftIO a showNote $ describeCipher cipher - return $ storeCipher c' cipher + return (storeCipher c' cipher, EncryptionIsSetup) highRandomQuality = (&&) (maybe True ( /= "false") $ M.lookup "highRandomQuality" c) <$> fmap not (Annex.getState Annex.fast) @@ -102,21 +129,24 @@ withkey a k = cip k >>= maybe (a k) (a . snd) cip = cipherKey c +remoteCipher :: RemoteConfig -> Annex (Maybe Cipher) +remoteCipher = fmap fst <$$> remoteCipher' + {- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex - state. -} -remoteCipher :: RemoteConfig -> Annex (Maybe Cipher) -remoteCipher c = go $ extractCipher c +remoteCipher' :: RemoteConfig -> Annex (Maybe (Cipher, StorableCipher)) +remoteCipher' c = go $ extractCipher c where go Nothing = return Nothing go (Just encipher) = do cache <- Annex.getState Annex.ciphers case M.lookup encipher cache of - Just cipher -> return $ Just cipher + Just cipher -> return $ Just (cipher, encipher) Nothing -> do showNote "gpg" cipher <- liftIO $ decryptCipher encipher Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache }) - return $ Just cipher + return $ Just (cipher, encipher) {- Checks if the remote's config allows storing creds in the remote's config. - diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/Hook.hs git-annex/Remote/Hook.hs --- git-annex-5.20140412ubuntu1/Remote/Hook.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Remote/Hook.hs 2015-01-12 10:09:18.463334542 -0800 @@ -71,7 +71,7 @@ u <- maybe (liftIO genUUID) return mu let hooktype = fromMaybe (error "Specify hooktype=") $ M.lookup "hooktype" c - c' <- encryptionSetup c + (c', _encsetup) <- encryptionSetup c gitConfigSpecialRemote u c' "hooktype" hooktype return (c', u) diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/Rsync.hs git-annex/Remote/Rsync.hs --- git-annex-5.20140412ubuntu1/Remote/Rsync.hs 2014-04-02 15:46:31.000000000 -0700 +++ git-annex/Remote/Rsync.hs 2015-01-12 10:09:18.463334542 -0800 @@ -132,7 +132,7 @@ -- verify configuration is sane let url = fromMaybe (error "Specify rsyncurl=") $ M.lookup "rsyncurl" c - c' <- encryptionSetup c + (c', _encsetup) <- encryptionSetup c -- The rsyncurl is stored in git config, not only in this remote's -- persistant state, so it can vary between hosts. diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/S3.hs git-annex/Remote/S3.hs --- git-annex-5.20140412ubuntu1/Remote/S3.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Remote/S3.hs 2015-01-12 10:09:18.463334542 -0800 @@ -76,10 +76,9 @@ s3Setup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) s3Setup mu mcreds c = do u <- maybe (liftIO genUUID) return mu - c' <- setRemoteCredPair c (AWS.creds u) mcreds - s3Setup' u c' -s3Setup' :: UUID -> RemoteConfig -> Annex (RemoteConfig, UUID) -s3Setup' u c = if isIA c then archiveorg else defaulthost + s3Setup' u mcreds c +s3Setup' :: UUID -> Maybe CredPair -> RemoteConfig -> Annex (RemoteConfig, UUID) +s3Setup' u mcreds c = if isIA c then archiveorg else defaulthost where remotename = fromJust (M.lookup "name" c) defbucket = remotename ++ "-" ++ fromUUID u @@ -96,13 +95,15 @@ return (fullconfig, u) defaulthost = do - c' <- encryptionSetup c - let fullconfig = c' `M.union` defaults + (c', encsetup) <- encryptionSetup c + c'' <- setRemoteCredPair encsetup c' (AWS.creds u) mcreds + let fullconfig = c'' `M.union` defaults genBucket fullconfig u use fullconfig archiveorg = do showNote "Internet Archive mode" + void $ setRemoteCredPair noEncryptionUsed c (AWS.creds u) mcreds -- Ensure user enters a valid bucket name, since -- this determines the name of the archive.org item. let bucket = replace " " "-" $ map toLower $ diff -ruN -x .git git-annex-5.20140412ubuntu1/Remote/WebDAV.hs git-annex/Remote/WebDAV.hs --- git-annex-5.20140412ubuntu1/Remote/WebDAV.hs 2014-03-12 08:53:22.000000000 -0700 +++ git-annex/Remote/WebDAV.hs 2015-01-12 10:09:18.463334542 -0800 @@ -86,11 +86,11 @@ u <- maybe (liftIO genUUID) return mu let url = fromMaybe (error "Specify url=") $ M.lookup "url" c - c' <- encryptionSetup c + (c', encsetup) <- encryptionSetup c creds <- maybe (getCreds c' u) (return . Just) mcreds testDav url creds gitConfigSpecialRemote u c' "webdav" "true" - c'' <- setRemoteCredPair c' (davCreds u) creds + c'' <- setRemoteCredPair encsetup c' (davCreds u) creds return (c'', u) store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool