summaryrefslogtreecommitdiff
path: root/KeyRing.hs
diff options
context:
space:
mode:
Diffstat (limited to 'KeyRing.hs')
-rw-r--r--KeyRing.hs254
1 files changed, 250 insertions, 4 deletions
diff --git a/KeyRing.hs b/KeyRing.hs
index 5149da4..c6aa6a4 100644
--- a/KeyRing.hs
+++ b/KeyRing.hs
@@ -57,10 +57,13 @@ import qualified Data.Traversable as Traversable (mapM,forM,sequence)
57#if ! MIN_VERSION_base(4,6,0) 57#if ! MIN_VERSION_base(4,6,0)
58import GHC.Exts ( Down(..) ) 58import GHC.Exts ( Down(..) )
59#endif 59#endif
60import Network.Socket -- (SockAddr)
61import qualified Data.ByteString.Lazy.Char8 as Char8
60 62
61 63
62 64
63import qualified CryptoCoins as CryptoCoins 65import qualified Hosts
66import qualified CryptoCoins
64import Base58 67import Base58
65import FunctorToMaybe 68import FunctorToMaybe
66import DotLock 69import DotLock
@@ -95,6 +98,7 @@ type PassWordFile = InputFile
95data FileType = KeyRingFile (Maybe PassWordFile) 98data FileType = KeyRingFile (Maybe PassWordFile)
96 | PEMFile UsageTag 99 | PEMFile UsageTag
97 | WalletFile -- (Maybe UsageTag) 100 | WalletFile -- (Maybe UsageTag)
101 | Hosts
98 102
99-- | RefType is perhaps not a good name for this... 103-- | RefType is perhaps not a good name for this...
100-- It is sort of like a read/write flag, although 104-- It is sort of like a read/write flag, although
@@ -308,6 +312,7 @@ data KikiReportAction =
308 | ExternallyGeneratedFile 312 | ExternallyGeneratedFile
309 | UnableToExport KeyAlgorithm String 313 | UnableToExport KeyAlgorithm String
310 | FailedFileWrite 314 | FailedFileWrite
315 | HostsDiff ByteString
311 deriving Show 316 deriving Show
312 317
313data KikiResult a = KikiResult 318data KikiResult a = KikiResult
@@ -683,9 +688,114 @@ importPEMKey doDecrypt db' tup = do
683 try r $ \(db'',report) -> do 688 try r $ \(db'',report) -> do
684 return $ KikiSuccess (db'', report0 ++ report) 689 return $ KikiSuccess (db'', report0 ++ report)
685 690
691
692mergeHostFiles :: KeyRingOperation -> KeyDB
693 -> FilePath
694 -> FilePath
695 -> IO
696 (KikiCondition
697 ( ( Map.Map [Char8.ByteString] KeyData
698 , ( [Hosts.Hosts]
699 , [Hosts.Hosts]
700 , Hosts.Hosts
701 , [(SockAddr, ([Char8.ByteString], [Char8.ByteString]))]
702 , [SockAddr]))
703 , [(FilePath,KikiReportAction)]))
704mergeHostFiles krd db secring pubring = do
705 let hns = files ishosts
706 ishosts Hosts = True
707 ishosts _ = False
708 files istyp = do
709 (f,(rtyp,ftyp)) <- Map.toList (kFiles krd)
710 guard (istyp ftyp)
711 resolveInputFile secring pubring f
712
713 hostdbs0 <- mapM (fmap Hosts.decode . L.readFile) hns
714
715 let gpgnames = map getHostnames $ Map.elems db
716 os = do
717 (addr,(ns,_)) <- gpgnames
718 n <- ns
719 return (addr,n)
720 setOnions hosts = foldl' (flip $ uncurry Hosts.assignName) hosts os
721 -- we ensure .onion names are set properly
722 hostdbs = map setOnions hostdbs0
723 outgoing_names = do
724 (addr,(_,gns)) <- gpgnames
725 guard . not $ null gns
726 guard $ all (null . Hosts.namesForAddress addr) hostdbs0
727 return addr
728 -- putStrLn $ "hostdbs = " ++ show hostdbs
729
730 -- 1. let U = union all the host dbs
731 -- preserving whitespace and comments of the first
732 let u0 = foldl' Hosts.plus Hosts.empty hostdbs
733 -- we filter U to be only finger-dresses
734 u1 = Hosts.filterAddrs (hasFingerDress db) u0
735
736 -- let nf h = map Char8.unpack $ Hosts.namesForAddress (fromJust $ Hosts.inet_pton "fdf4:ed98:29c7:6226:9bde:a5b4:d564:3321") h
737 {-
738 putStrLn $ "_ = {\n" ++ show (head hostdbs) ++ "}"
739 putStrLn $ "--> " ++ show (nf (head hostdbs))
740 putStrLn $ "u0 = {\n" ++ show u0 ++ "}"
741 putStrLn $ "--> " ++ show (nf u0)
742 putStrLn $ "u1 = {\n" ++ show u1 ++ "}"
743 putStrLn $ "--> " ++ show (nf u1)
744 -}
745
746 -- 2. replace gpg annotations with those in U
747 -- forM use_db
748 db' <- Traversable.mapM (setHostnames (\a -> not $ elem a outgoing_names) u1) db
749
750 return $ KikiSuccess ((db',(hostdbs0,hostdbs,u1,gpgnames,outgoing_names)),[])
751
752writeHostsFiles
753 :: KeyRingOperation
754 -> [Char]
755 -> [Char]
756 -> ([Hosts.Hosts],
757 [Hosts.Hosts],
758 Hosts.Hosts,
759 [(SockAddr, (t1, [Char8.ByteString]))],
760 [SockAddr])
761 -> IO [(FilePath, KikiReportAction)]
762writeHostsFiles krd secring pubring (hostdbs0,hostdbs,u1,gpgnames,outgoing_names) = do
763 let hns = files ishosts
764 ishosts Hosts = True
765 ishosts _ = False
766 files istyp = do
767 (f,(rtyp,ftyp)) <- Map.toList (kFiles krd)
768 guard (istyp ftyp)
769 resolveInputFile secring pubring f
770
771 -- 3. add hostnames from gpg for addresses not in U
772 let u = foldl' f u1 ans
773 ans = reverse $ do
774 (addr,(_,ns)) <- gpgnames
775 guard $ addr `elem` outgoing_names -- . null $ Hosts.namesForAddress addr u0
776 n <- ns
777 return (addr,n)
778 f h (addr,n) = Hosts.assignNewName addr n h
779
780 -- 4. for each host db H, union H with U and write it out as H'
781 -- only if there is a non-empty diff
782 rss <- forM (zip hns $ zip hostdbs0 hostdbs) $ \(fname,(h0,h1)) -> do
783 let h = h1 `Hosts.plus` u
784 d = Hosts.diff h0 h
785 rs = map ((fname,) . HostsDiff) d
786 when (not $ null d) $ L.writeFile fname $ Hosts.encode h
787 return rs
788 return $ concat rss
789
790
686buildKeyDB :: (MappedPacket -> IO (KikiCondition Packet)) 791buildKeyDB :: (MappedPacket -> IO (KikiCondition Packet))
687 -> FilePath -> FilePath -> Maybe String -> KeyRingOperation 792 -> FilePath -> FilePath -> Maybe String -> KeyRingOperation
688 -> IO (KikiCondition ((KeyDB,Maybe String,Maybe MappedPacket) 793 -> IO (KikiCondition ((KeyDB,Maybe String,Maybe MappedPacket,
794 ([Hosts.Hosts],
795 [Hosts.Hosts],
796 Hosts.Hosts,
797 [(SockAddr, ([ByteString], [ByteString]))],
798 [SockAddr]) )
689 ,[(FilePath,KikiReportAction)])) 799 ,[(FilePath,KikiReportAction)]))
690buildKeyDB doDecrypt secring pubring grip0 keyring = do 800buildKeyDB doDecrypt secring pubring grip0 keyring = do
691 let 801 let
@@ -754,7 +864,10 @@ buildKeyDB doDecrypt secring pubring grip0 keyring = do
754 db <- foldM (importPEMKey doDecrypt) (KikiSuccess (db,[])) imports 864 db <- foldM (importPEMKey doDecrypt) (KikiSuccess (db,[])) imports
755 try db $ \(db,reportPEMs) -> do 865 try db $ \(db,reportPEMs) -> do
756 866
757 return $ KikiSuccess ( (db, grip, mwk), reportWallets ++ reportPEMs ) 867 r <- mergeHostFiles keyring db secring pubring
868 try r $ \((db,hs),reportHosts) -> do
869
870 return $ KikiSuccess ( (db, grip, mwk, hs), reportWallets ++ reportPEMs )
758 871
759torhash key = maybe "" id $ derToBase32 <$> derRSA key 872torhash key = maybe "" id $ derToBase32 <$> derRSA key
760 873
@@ -1372,7 +1485,7 @@ runKeyRing operation = do
1372 1485
1373 -- merge all keyrings, PEM files, and wallets 1486 -- merge all keyrings, PEM files, and wallets
1374 bresult <- buildKeyDB decrypt secring pubring grip0 operation 1487 bresult <- buildKeyDB decrypt secring pubring grip0 operation
1375 try' bresult $ \((db,grip,wk),report_imports) -> do 1488 try' bresult $ \((db,grip,wk,hs),report_imports) -> do
1376 1489
1377 externals_ret <- initializeMissingPEMFiles operation 1490 externals_ret <- initializeMissingPEMFiles operation
1378 secring pubring grip 1491 secring pubring grip
@@ -1402,6 +1515,8 @@ runKeyRing operation = do
1402 1515
1403 r <- writePEMKeys decrypt (rtKeyDB rt) exports 1516 r <- writePEMKeys decrypt (rtKeyDB rt) exports
1404 try' r $ \report_pems -> do 1517 try' r $ \report_pems -> do
1518
1519 import_hosts <- writeHostsFiles operation secring pubring hs
1405 1520
1406 return $ KikiResult (KikiSuccess rt) 1521 return $ KikiResult (KikiSuccess rt)
1407 $ concat [ report_imports 1522 $ concat [ report_imports
@@ -2005,6 +2120,137 @@ flattenUid :: FilePath -> Bool -> (String,([SigAndTrust],OriginMap)) -> [MappedP
2005flattenUid fname ispub (str,(sigs,om)) = 2120flattenUid fname ispub (str,(sigs,om)) =
2006 (mappedPacket "" $ UserIDPacket str) {locations=om} : concatSort fname head (unsig fname ispub) sigs 2121 (mappedPacket "" $ UserIDPacket str) {locations=om} : concatSort fname head (unsig fname ispub) sigs
2007 2122
2123getHostnames :: KeyData -> (SockAddr, ([L.ByteString],[L.ByteString]))
2124getHostnames (KeyData topmp _ uids subs) = (addr,(onames,othernames))
2125 where
2126 othernames = do
2127 mp <- flattenAllUids "" True uids
2128 let p = packet mp
2129 guard $ isSignaturePacket p
2130 uh <- unhashed_subpackets p
2131 case uh of
2132 NotationDataPacket True "hostname@" v
2133 -> return $ Char8.pack v
2134 _ -> mzero
2135
2136 addr = fingerdress topk
2137 -- name = maybe "" id $ listToMaybe onames -- TODO: more than one tor key?
2138 topk = packet topmp
2139 torkeys = do
2140 SubKey k sigs <- Map.elems subs
2141 let subk = packet k
2142 let sigs' = do
2143 torsig <- filter (has_tag "tor") $ map (packet . fst) sigs
2144 sig <- (signatures $ Message [topk,subk,torsig])
2145 let v = verify (Message [topk]) sig
2146 -- Require parent's signature
2147 guard (not . null $ signatures_over v)
2148 let unhashed = unhashed_subpackets torsig
2149 subsigs = mapMaybe backsig unhashed
2150 -- This should consist only of 0x19 values
2151 -- subtypes = map signature_type subsigs
2152 sig' <- signatures . Message $ [topk,subk]++subsigs
2153 let v' = verify (Message [subk]) sig'
2154 -- Require subkey's signature
2155 guard . not . null $ signatures_over v'
2156 return torsig
2157 guard (not $ null sigs')
2158 return $ subk
2159 has_tag tag p = isSignaturePacket p
2160 && or [ tag `elem` mapMaybe usage (hashed_subpackets p)
2161 , tag `elem` map usageString (mapMaybe keyflags (hashed_subpackets p)) ]
2162 -- subkeyPacket (SubKey k _ ) = k
2163 onames :: [L.ByteString]
2164 onames = map ( (<> ".onion")
2165 . Char8.pack
2166 . take 16
2167 . torhash )
2168 torkeys
2169
2170hasFingerDress :: KeyDB -> SockAddr -> Bool
2171hasFingerDress db addr | socketFamily addr/=AF_INET6 = False
2172hasFingerDress db addr = pre=="fd" && isJust (selectPublicKey (KeyGrip g',Nothing) db)
2173 where
2174 (pre,g) = splitAt 2 $ filter (/=':') $ Hosts.inet_ntop addr
2175 g' = map toUpper g
2176
2177-- We return into IO in case we want to make a signature here.
2178setHostnames :: (SockAddr -> Bool) -> Hosts.Hosts -> KeyData -> IO KeyData
2179setHostnames pred hosts kd@(KeyData topmp topsigs uids subs) =
2180 -- TODO: we are removing the origin from the UID OriginMap,
2181 -- when we should be removing origins from the locations
2182 -- field of the sig's MappedPacket records.
2183 -- Call getHostnames and compare to see if no-op.
2184 if not (pred addr) || names0 == names \\ onions
2185 then {- trace (unlines [ "setHostnames NO-OP: gpg: "++show (map Char8.unpack onions, map Char8.unpack names0)
2186 , " file: "++show (map Char8.unpack names)
2187 , " pred: "++show (pred addr)]) -}
2188 (return kd)
2189 else do
2190 -- We should be sure to remove origins so that the data is written
2191 -- (but only if something changed).
2192 -- Filter all hostnames present in uids
2193 -- Write notations into first uid
2194 {-
2195 trace (unlines [ "setHostnames ACTION: gpg: "++show (map Char8.unpack onions, map Char8.unpack names0)
2196 , " file: "++show (map Char8.unpack names) ]) $ do
2197 -}
2198 return $ KeyData topmp topsigs uids1 subs
2199 where
2200 topk = packet topmp
2201 addr = fingerdress topk
2202 names :: [Char8.ByteString]
2203 names = Hosts.namesForAddress addr hosts
2204 (_,(onions,names0)) = getHostnames kd
2205 notations = map (NotationDataPacket True "hostname@" . Char8.unpack) (names \\ onions)
2206 isName (NotationDataPacket True "hostname@" _) = True
2207 isName _ = False
2208 uids0 = fmap zapIfHasName uids
2209 fstuid = head $ do
2210 p <- map packet $ flattenAllUids "" True uids
2211 guard $ isUserID p
2212 return $ uidkey p
2213 uids1 = Map.adjust addnames fstuid uids0
2214 addnames (sigs,om) = (fmap f ss ++ ts, om ) -- XXX: removed om=Map.empty, preserve UserId origin
2215 where
2216 (ss,ts) = splitAt 1 sigs
2217 f (sig,tm) = if isSignaturePacket (packet sig) then (sig { packet = p', locations=Map.empty }, tm)
2218 else (sig, tm)
2219 where p' = (packet sig) { unhashed_subpackets=uh }
2220 uh = unhashed_subpackets (packet sig) ++ notations
2221 zapIfHasName (sigs,om) = if or bs then (sigs',om) -- XXX: removed om=Map.empty to preserve UserID origin
2222 else (sigs,om)
2223 where
2224 (bs, sigs') = unzip $ map unhash sigs
2225
2226 unhash (sig,tm) = ( not (null ns)
2227 , ( sig { packet = p', locations = Map.empty }
2228 , tm ) )
2229 where
2230 psig = packet sig
2231 p' = if isSignaturePacket psig then psig { unhashed_subpackets = ps }
2232 else psig
2233 uh = unhashed_subpackets $ psig
2234 (ns,ps) = partition isName uh
2235
2236fingerdress :: Packet -> SockAddr
2237fingerdress topk = maybe zero id $ Hosts.inet_pton addr_str
2238 where
2239 zero = SockAddrInet 0 0
2240 addr_str = colons $ "fd" ++ (drop 10 $ map toLower $ fingerprint topk)
2241 colons (a:b:c:d:xs@(_:_)) = [a,b,c,d,':'] ++ colons xs
2242 colons xs = xs
2243
2244backsig (EmbeddedSignaturePacket s) = Just s
2245backsig _ = Nothing
2246
2247socketFamily (SockAddrInet _ _) = AF_INET
2248socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6
2249socketFamily (SockAddrUnix _) = AF_UNIX
2250
2251
2252
2253{-----------------------------------------------}
2008 2254
2009 2255
2010{- 2256{-