add P2PAnnex constructor
authorJoey Hess <joeyh@joeyh.name>
Wed, 30 Jul 2025 16:02:33 +0000 (12:02 -0400)
committerJoey Hess <joeyh@joeyh.name>
Wed, 30 Jul 2025 16:09:17 +0000 (12:09 -0400)
This is for p2p-annex:: urls that will use the new generic P2P
transport.

In addressCredsFile, threw in an url encoding of any non-alphanumeric
characters that are in the address. This is to avoid any possible path
traversal attacks via a p2p-annex:: url, since the address part of it
could contain any characters. And, went ahead and did the same url
encoding of tor-annex:: urls, even though tor onion addresses are all
alphanumerics, on the off chance that might avoid a similar problem.
(It does not seem likely enough to treat it as a security hole.)

Command/EnableTor.hs
P2P/Address.hs
P2P/Auth.hs
doc/design/generic_p2p_transport.mdwn

index 03293d2af44960954f71c028010cdd7d352fdba8..b36136553a0e1c189c3212e396ddb1b9edac3648 100644 (file)
@@ -102,6 +102,7 @@ checkHiddenService = bracket setup cleanup go
        go _ = check (150 :: Int) =<< filter istoraddr <$> loadP2PAddresses
        
        istoraddr (TorAnnex _ _) = True
+       istoraddr _ = False
 
        check 0 _ = giveup "Still unable to connect to hidden service. It might not yet be usable by others. Please check Tor's logs for details."
        check _ [] = giveup "Somehow didn't get an onion address."
index 1a3186aca93b96d1894705b37f74f2999cf26edd..052f0af6ce765f09c290f83ddbcfebad6b7aa955 100644 (file)
@@ -1,6 +1,6 @@
 {- P2P protocol addresses
  -
- - Copyright 2016 Joey Hess <id@joeyh.name>
+ - Copyright 2016-2025 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -25,7 +25,15 @@ import System.PosixCompat.Files (fileOwner, fileGroup)
 --
 -- This is enough information to connect to the peer,
 -- but not enough to authenticate with it.
-data P2PAddress = TorAnnex OnionAddress OnionPort
+data P2PAddress
+       = TorAnnex OnionAddress OnionPort
+       | P2PAnnex P2PNetName UnderlyingP2PAddress
+       deriving (Eq, Show)
+
+newtype P2PNetName = P2PNetName String
+       deriving (Eq, Show)
+
+newtype UnderlyingP2PAddress = UnderlyingP2PAddress String
        deriving (Eq, Show)
 
 -- | A P2P address, with an AuthToken.
@@ -42,17 +50,26 @@ class FormatP2PAddress a where
 instance FormatP2PAddress P2PAddress where
        formatP2PAddress (TorAnnex (OnionAddress onionaddr) onionport) =
                torAnnexScheme ++ ":" ++ onionaddr ++ ":" ++ show onionport
+       formatP2PAddress (P2PAnnex (P2PNetName netname) (UnderlyingP2PAddress address)) =
+               p2pAnnexScheme ++ ":" ++ netname ++ ":" ++ address
        unformatP2PAddress s
-               | (torAnnexScheme ++ ":") `isPrefixOf` s = do
-                       let s' = dropWhile (== ':') $ dropWhile (/= ':') s
-                       let (onionaddr, ps) = separate (== ':') s'
-                       onionport <- readish ps
-                       return (TorAnnex (OnionAddress onionaddr) onionport)
+               | schemeprefixed torAnnexScheme = do
+                       onionport <- readish bs
+                       return (TorAnnex (OnionAddress as) onionport)
+               | schemeprefixed p2pAnnexScheme =
+                       return (P2PAnnex (P2PNetName as) (UnderlyingP2PAddress bs))
                | otherwise = Nothing
+         where
+               schemeprefixed scheme = (scheme ++ ":") `isPrefixOf` s
+               (as, bs) = separate (== ':') $
+                       dropWhile (== ':') $ dropWhile (/= ':') s
 
 torAnnexScheme :: String
 torAnnexScheme = "tor-annex:"
 
+p2pAnnexScheme :: String
+p2pAnnexScheme = "p2p-annex:"
+
 instance FormatP2PAddress P2PAddressAuth where
        formatP2PAddress (P2PAddressAuth addr authtoken) =
                formatP2PAddress addr ++ ":" ++ T.unpack (fromAuthToken authtoken)
index 8de3eda39c299bff274eb0043de840fffcf22e33..f7b79678e6bc911f2a60e46e6ecd7748703831ac 100644 (file)
@@ -16,6 +16,8 @@ import Utility.AuthToken
 import Utility.Tor
 import Utility.Env
 
+import Network.URI
+import Data.Char
 import qualified Data.Text as T
 
 -- | Load authtokens that are accepted by this repository for tor.
@@ -55,7 +57,7 @@ storeP2PAuthToken addr t = do
   where
        v = case addr of
                TorAnnex _ _ -> (t, Nothing)
-               -- _ -> (t, Just addr)
+               _ -> (t, Just addr)
        
        fmt (tok, Nothing) = T.unpack (fromAuthToken tok)
        fmt (tok, Just addr') = T.unpack (fromAuthToken tok) 
@@ -86,9 +88,13 @@ storeP2PRemoteAuthToken addr t = writeCreds
        (T.unpack $ fromAuthToken t)
        (addressCredsFile addr)
 
+-- | Unusual characters in the address are url encoded.
 addressCredsFile :: P2PAddress -> OsPath
--- We can omit the port and just use the onion address for the creds file,
--- because any given tor hidden service runs on a single port and has a
--- unique onion address.
-addressCredsFile (TorAnnex (OnionAddress onionaddr) _port) =
-       toOsPath onionaddr
+addressCredsFile addr = toOsPath $ escapeURIString isAlphaNum $ case addr of
+       -- We can omit the port and just use the onion address for the
+       -- creds file, because any given tor hidden service runs on a
+       -- single port and has a unique onion address.
+       TorAnnex (OnionAddress onionaddr) _port ->
+               onionaddr
+       P2PAnnex (P2PNetName netname) (UnderlyingP2PAddress address) ->
+               netname ++ ":" ++ address
index 20860504e9f05d057c2efaa8f85dfb6693d1b233..2e06da195bd3e86dbd277119e4b23535574352e3 100644 (file)
@@ -7,7 +7,7 @@ Such a P2P network has some form of address, which can be
 used to connect to a given peer by address across the network.
 
 A git remote using the P2P network has an url of the form
-`p2p-annex::<netname>+<address>`
+`p2p-annex::<netname>:<address>`
 
 To connect to that remote, git-annex runs the command
 `git-annex-p2p-<netname>`, giving it the P2P network address as its only