2013-09-05 20:02:39 +00:00
|
|
|
{- git-remote-gcrypt support
|
|
|
|
-
|
2016-07-05 15:30:58 +00:00
|
|
|
- https://spwhitton.name/tech/code/git-remote-gcrypt/
|
2013-09-05 20:02:39 +00:00
|
|
|
-
|
2015-01-21 16:50:09 +00:00
|
|
|
- Copyright 2013 Joey Hess <id@joeyh.name>
|
2013-09-05 20:02:39 +00:00
|
|
|
-
|
2019-03-13 19:48:14 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2013-09-05 20:02:39 +00:00
|
|
|
-}
|
|
|
|
|
2019-11-27 20:54:11 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
2013-09-05 20:02:39 +00:00
|
|
|
module Git.GCrypt where
|
|
|
|
|
|
|
|
import Common
|
|
|
|
import Git.Types
|
|
|
|
import Git.Construct
|
2013-09-05 20:34:13 +00:00
|
|
|
import qualified Git.Config as Config
|
2013-09-18 19:30:53 +00:00
|
|
|
import qualified Git.Command as Command
|
2013-09-05 20:34:13 +00:00
|
|
|
import Utility.Gpg
|
2013-09-05 20:02:39 +00:00
|
|
|
|
2019-11-27 20:54:11 +00:00
|
|
|
import qualified Data.ByteString as S
|
2021-03-09 16:30:13 +00:00
|
|
|
import qualified Network.URI
|
2019-11-27 20:54:11 +00:00
|
|
|
|
2014-04-08 20:16:46 +00:00
|
|
|
urlScheme :: String
|
|
|
|
urlScheme = "gcrypt:"
|
|
|
|
|
2013-09-05 20:02:39 +00:00
|
|
|
urlPrefix :: String
|
2014-04-08 20:16:46 +00:00
|
|
|
urlPrefix = urlScheme ++ ":"
|
2013-09-05 20:02:39 +00:00
|
|
|
|
|
|
|
isEncrypted :: Repo -> Bool
|
|
|
|
isEncrypted Repo { location = Url url } = urlPrefix `isPrefixOf` show url
|
2021-01-18 18:52:56 +00:00
|
|
|
isEncrypted Repo { location = UnparseableUrl url } = urlPrefix `isPrefixOf` url
|
2013-09-05 20:02:39 +00:00
|
|
|
isEncrypted _ = False
|
|
|
|
|
|
|
|
{- The first Repo is the git repository that has the second Repo
|
|
|
|
- as one of its remotes.
|
|
|
|
-
|
|
|
|
- When the remote Repo uses gcrypt, returns the actual underlying
|
|
|
|
- git repository that gcrypt is using to store its data.
|
|
|
|
-
|
2021-01-18 18:52:56 +00:00
|
|
|
- Throws an exception if the repo does not use gcrypt.
|
2013-09-05 20:02:39 +00:00
|
|
|
-}
|
2013-09-19 16:53:24 +00:00
|
|
|
encryptedRemote :: Repo -> Repo -> IO Repo
|
|
|
|
encryptedRemote baserepo = go
|
2013-09-05 20:02:39 +00:00
|
|
|
where
|
2021-01-18 18:52:56 +00:00
|
|
|
go Repo { location = Url url } = go' (show url)
|
|
|
|
go Repo { location = UnparseableUrl url } = go' url
|
|
|
|
go _ = notencrypted
|
|
|
|
|
|
|
|
go' u
|
2013-09-05 20:02:39 +00:00
|
|
|
| urlPrefix `isPrefixOf` u =
|
2021-03-09 16:30:13 +00:00
|
|
|
let l = drop plen u
|
|
|
|
-- Git.Construct.fromUrl escapes characters
|
|
|
|
-- that are not allowed in URIs (though git
|
|
|
|
-- allows them); need to de-escape any such
|
|
|
|
-- to get back the path to the repository.
|
|
|
|
l' = Network.URI.unEscapeString l
|
2023-03-23 19:19:04 +00:00
|
|
|
-- gcrypt supports relative urls for rsync
|
|
|
|
-- like "rsync://host:relative/path"
|
|
|
|
-- but that does not parse as a valid url
|
|
|
|
-- (while the absolute urls it supports are
|
|
|
|
-- valid).
|
|
|
|
-- In order to support it, force treating it as
|
|
|
|
-- an url.
|
|
|
|
knownurl = "rsync://" `isPrefixOf` l'
|
|
|
|
in fromRemoteLocation l' knownurl baserepo
|
2013-09-05 20:02:39 +00:00
|
|
|
| otherwise = notencrypted
|
2021-01-18 18:52:56 +00:00
|
|
|
|
2016-11-16 01:29:54 +00:00
|
|
|
notencrypted = giveup "not a gcrypt encrypted repository"
|
2013-09-05 20:02:39 +00:00
|
|
|
|
2021-01-18 18:52:56 +00:00
|
|
|
plen = length urlPrefix
|
|
|
|
|
2013-09-19 16:53:24 +00:00
|
|
|
data ProbeResult = Decryptable | NotDecryptable | NotEncrypted
|
|
|
|
|
|
|
|
{- Checks if the git repo at a location uses gcrypt.
|
|
|
|
-
|
|
|
|
- Rather expensive -- many need to fetch the entire repo contents.
|
|
|
|
- (Which is fine if the repo is going to be added as a remote..)
|
2013-09-18 19:30:53 +00:00
|
|
|
-}
|
2013-09-19 16:53:24 +00:00
|
|
|
probeRepo :: String -> Repo -> IO ProbeResult
|
|
|
|
probeRepo loc baserepo = do
|
|
|
|
let p = proc "git" $ toCommand $ Command.gitCommandLine
|
|
|
|
[ Param "remote-gcrypt"
|
|
|
|
, Param "--check"
|
|
|
|
, Param loc
|
|
|
|
] baserepo
|
2020-06-03 19:48:09 +00:00
|
|
|
withCreateProcess p $ \_ _ _ pid -> do
|
|
|
|
code <- waitForProcess pid
|
|
|
|
return $ case code of
|
|
|
|
ExitSuccess -> Decryptable
|
|
|
|
ExitFailure 1 -> NotDecryptable
|
|
|
|
ExitFailure _ -> NotEncrypted
|
2013-09-18 19:30:53 +00:00
|
|
|
|
|
|
|
type GCryptId = String
|
2013-09-07 22:38:00 +00:00
|
|
|
|
2023-03-14 02:39:16 +00:00
|
|
|
{- gcrypt gives each encrypted repository a unique gcrypt-id,
|
2013-09-05 20:02:39 +00:00
|
|
|
- which is stored in the repository (in encrypted form)
|
|
|
|
- and cached in a per-remote gcrypt-id configuration setting. -}
|
2013-09-18 19:30:53 +00:00
|
|
|
remoteRepoId :: Repo -> Maybe RemoteName -> Maybe GCryptId
|
2019-12-05 18:36:43 +00:00
|
|
|
remoteRepoId r n = fromConfigValue <$> getRemoteConfig "gcrypt-id" r n
|
2013-09-05 20:34:13 +00:00
|
|
|
|
2019-12-05 18:36:43 +00:00
|
|
|
getRemoteConfig :: S.ByteString -> Repo -> Maybe RemoteName -> Maybe ConfigValue
|
2013-09-07 22:38:00 +00:00
|
|
|
getRemoteConfig field repo remotename = do
|
|
|
|
n <- remotename
|
|
|
|
Config.getMaybe (remoteConfigKey field n) repo
|
2013-09-05 20:34:13 +00:00
|
|
|
|
|
|
|
{- Gpg keys that the remote is encrypted for.
|
|
|
|
- If empty, gcrypt uses --default-recipient-self -}
|
2013-09-07 22:38:00 +00:00
|
|
|
getParticiantList :: Maybe Repo -> Repo -> Maybe RemoteName -> KeyIds
|
|
|
|
getParticiantList globalconfigrepo repo remotename = KeyIds $ parse $ firstJust
|
|
|
|
[ getRemoteConfig "gcrypt-participants" repo remotename
|
|
|
|
, Config.getMaybe defaultkey repo
|
2013-09-05 20:34:13 +00:00
|
|
|
, Config.getMaybe defaultkey =<< globalconfigrepo
|
|
|
|
]
|
|
|
|
where
|
|
|
|
defaultkey = "gcrypt.participants"
|
2019-12-05 18:36:43 +00:00
|
|
|
parse (Just (ConfigValue "simple")) = []
|
2021-08-11 00:45:02 +00:00
|
|
|
parse (Just (ConfigValue b)) = words (decodeBS b)
|
2020-04-13 17:35:22 +00:00
|
|
|
parse (Just NoConfigValue) = []
|
2013-09-05 20:34:13 +00:00
|
|
|
parse Nothing = []
|
|
|
|
|
2019-12-02 14:57:09 +00:00
|
|
|
remoteParticipantConfigKey :: RemoteName -> ConfigKey
|
2013-09-07 22:38:00 +00:00
|
|
|
remoteParticipantConfigKey = remoteConfigKey "gcrypt-participants"
|
|
|
|
|
2019-12-02 14:57:09 +00:00
|
|
|
remotePublishParticipantConfigKey :: RemoteName -> ConfigKey
|
2014-07-15 21:33:14 +00:00
|
|
|
remotePublishParticipantConfigKey = remoteConfigKey "gcrypt-publish-participants"
|
|
|
|
|
2019-12-02 14:57:09 +00:00
|
|
|
remoteSigningKey :: RemoteName -> ConfigKey
|
2013-09-17 20:06:29 +00:00
|
|
|
remoteSigningKey = remoteConfigKey "gcrypt-signingkey"
|
|
|
|
|
2019-12-02 14:57:09 +00:00
|
|
|
remoteConfigKey :: S.ByteString -> RemoteName -> ConfigKey
|
|
|
|
remoteConfigKey key remotename = ConfigKey $
|
2021-08-11 00:45:02 +00:00
|
|
|
"remote." <> encodeBS remotename <> "." <> key
|