diff options
Diffstat (limited to 'KeyRing.hs')
-rw-r--r-- | KeyRing.hs | 254 |
1 files changed, 250 insertions, 4 deletions
@@ -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) |
58 | import GHC.Exts ( Down(..) ) | 58 | import GHC.Exts ( Down(..) ) |
59 | #endif | 59 | #endif |
60 | import Network.Socket -- (SockAddr) | ||
61 | import qualified Data.ByteString.Lazy.Char8 as Char8 | ||
60 | 62 | ||
61 | 63 | ||
62 | 64 | ||
63 | import qualified CryptoCoins as CryptoCoins | 65 | import qualified Hosts |
66 | import qualified CryptoCoins | ||
64 | import Base58 | 67 | import Base58 |
65 | import FunctorToMaybe | 68 | import FunctorToMaybe |
66 | import DotLock | 69 | import DotLock |
@@ -95,6 +98,7 @@ type PassWordFile = InputFile | |||
95 | data FileType = KeyRingFile (Maybe PassWordFile) | 98 | data 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 | ||
313 | data KikiResult a = KikiResult | 318 | data 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 | |||
692 | mergeHostFiles :: 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)])) | ||
704 | mergeHostFiles 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 | |||
752 | writeHostsFiles | ||
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)] | ||
762 | writeHostsFiles 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 | |||
686 | buildKeyDB :: (MappedPacket -> IO (KikiCondition Packet)) | 791 | buildKeyDB :: (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)])) |
690 | buildKeyDB doDecrypt secring pubring grip0 keyring = do | 800 | buildKeyDB 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 | ||
759 | torhash key = maybe "" id $ derToBase32 <$> derRSA key | 872 | torhash 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 | |||
2005 | flattenUid fname ispub (str,(sigs,om)) = | 2120 | flattenUid 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 | ||
2123 | getHostnames :: KeyData -> (SockAddr, ([L.ByteString],[L.ByteString])) | ||
2124 | getHostnames (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 | |||
2170 | hasFingerDress :: KeyDB -> SockAddr -> Bool | ||
2171 | hasFingerDress db addr | socketFamily addr/=AF_INET6 = False | ||
2172 | hasFingerDress 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. | ||
2178 | setHostnames :: (SockAddr -> Bool) -> Hosts.Hosts -> KeyData -> IO KeyData | ||
2179 | setHostnames 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 | |||
2236 | fingerdress :: Packet -> SockAddr | ||
2237 | fingerdress 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 | |||
2244 | backsig (EmbeddedSignaturePacket s) = Just s | ||
2245 | backsig _ = Nothing | ||
2246 | |||
2247 | socketFamily (SockAddrInet _ _) = AF_INET | ||
2248 | socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6 | ||
2249 | socketFamily (SockAddrUnix _) = AF_UNIX | ||
2250 | |||
2251 | |||
2252 | |||
2253 | {-----------------------------------------------} | ||
2008 | 2254 | ||
2009 | 2255 | ||
2010 | {- | 2256 | {- |