diff options
-rw-r--r-- | KeyRing.hs | 29 | ||||
-rw-r--r-- | kiki.cabal | 3 | ||||
-rw-r--r-- | kiki.hs | 41 |
3 files changed, 53 insertions, 20 deletions
@@ -45,6 +45,7 @@ module KeyRing | |||
45 | , KeyFilter(..) | 45 | , KeyFilter(..) |
46 | -- * Results of a KeyRing Operation | 46 | -- * Results of a KeyRing Operation |
47 | , KeyRingRuntime(..) | 47 | , KeyRingRuntime(..) |
48 | , MappedPacket(..) | ||
48 | , KeyDB | 49 | , KeyDB |
49 | , KeyData(..) | 50 | , KeyData(..) |
50 | , SubKey(..) | 51 | , SubKey(..) |
@@ -97,6 +98,7 @@ module KeyRing | |||
97 | , keyPacket | 98 | , keyPacket |
98 | , KeySpec(..) | 99 | , KeySpec(..) |
99 | , getHostnames | 100 | , getHostnames |
101 | , secretPemFromPacket | ||
100 | ) where | 102 | ) where |
101 | 103 | ||
102 | import System.Environment | 104 | import System.Environment |
@@ -132,7 +134,7 @@ import qualified Data.Map as Map | |||
132 | import qualified Data.ByteString.Lazy as L ( unpack, null, readFile, writeFile | 134 | import qualified Data.ByteString.Lazy as L ( unpack, null, readFile, writeFile |
133 | , ByteString, toChunks, hGetContents, hPut, concat, fromChunks, splitAt | 135 | , ByteString, toChunks, hGetContents, hPut, concat, fromChunks, splitAt |
134 | , index, break, pack ) | 136 | , index, break, pack ) |
135 | import qualified Data.ByteString as S ( ByteString, unpack, splitAt, concat, cons, spanEnd, hGetContents, readFile, breakSubstring, drop, length, null, putStr, singleton, unfoldr, reverse ) | 137 | import qualified Data.ByteString as S ( ByteString, unpack, splitAt, concat, cons, spanEnd, hGetContents, readFile, breakSubstring, drop, length, null, hPutStr, singleton, unfoldr, reverse ) |
136 | import qualified Codec.Binary.Base32 as Base32 | 138 | import qualified Codec.Binary.Base32 as Base32 |
137 | import qualified Codec.Binary.Base64 as Base64 | 139 | import qualified Codec.Binary.Base64 as Base64 |
138 | #if !defined(VERSION_cryptonite) | 140 | #if !defined(VERSION_cryptonite) |
@@ -166,7 +168,7 @@ import Foreign.C.Error ( throwErrnoIfMinus1_ ) | |||
166 | import Foreign.Storable | 168 | import Foreign.Storable |
167 | #endif | 169 | #endif |
168 | import System.FilePath ( takeDirectory ) | 170 | import System.FilePath ( takeDirectory ) |
169 | import System.IO (hPutStrLn,withFile,IOMode(..), Handle, hPutStr) | 171 | import System.IO (hPutStrLn,withFile,IOMode(..), Handle, hPutStr, stderr) |
170 | import Data.IORef | 172 | import Data.IORef |
171 | import System.Posix.IO ( fdToHandle ) | 173 | import System.Posix.IO ( fdToHandle ) |
172 | import qualified Data.Traversable as Traversable | 174 | import qualified Data.Traversable as Traversable |
@@ -1181,7 +1183,7 @@ cachedContents maybePrompt ctx fd = do | |||
1181 | pw <- readIORef ref | 1183 | pw <- readIORef ref |
1182 | flip (flip maybe return) pw $ do | 1184 | flip (flip maybe return) pw $ do |
1183 | if fd == FileDesc 0 then case maybePrompt of | 1185 | if fd == FileDesc 0 then case maybePrompt of |
1184 | Just prompt -> S.putStr prompt | 1186 | Just prompt -> S.hPutStr stderr prompt |
1185 | Nothing -> return () | 1187 | Nothing -> return () |
1186 | else return () | 1188 | else return () |
1187 | pw <- fmap trimCR $ readInputFileS ctx fd | 1189 | pw <- fmap trimCR $ readInputFileS ctx fd |
@@ -2059,20 +2061,23 @@ rsaPrivateKeyFromPacket pkt@(SecretKeyPacket {}) = do | |||
2059 | , rsaCoefficient = coefficient } | 2061 | , rsaCoefficient = coefficient } |
2060 | rsaPrivateKeyFromPacket _ = Nothing | 2062 | rsaPrivateKeyFromPacket _ = Nothing |
2061 | 2063 | ||
2062 | 2064 | secretPemFromPacket packet = | |
2063 | writeKeyToFile :: | ||
2064 | Bool -> FileType -> InputFile -> Packet -> IO [(InputFile, KikiReportAction)] | ||
2065 | writeKeyToFile False PEMFile fname packet = do | ||
2066 | case key_algorithm packet of | 2065 | case key_algorithm packet of |
2067 | RSA -> do | 2066 | RSA -> do |
2068 | flip (maybe (return [])) | 2067 | rsa <- rsaPrivateKeyFromPacket packet -- RSAPrivateKey |
2069 | (rsaPrivateKeyFromPacket packet) -- RSAPrivateKey | ||
2070 | $ \rsa -> do | ||
2071 | let asn1 = toASN1 rsa [] | 2068 | let asn1 = toASN1 rsa [] |
2072 | bs = encodeASN1 DER asn1 | 2069 | bs = encodeASN1 DER asn1 |
2073 | dta = Base64.encode (L.unpack bs) | 2070 | dta = Base64.encode (L.unpack bs) |
2074 | output = writePEM "RSA PRIVATE KEY" dta | 2071 | output = writePEM "RSA PRIVATE KEY" dta |
2075 | stamp = toEnum . fromEnum $ timestamp packet | 2072 | Just output |
2073 | algo -> Nothing | ||
2074 | |||
2075 | writeKeyToFile :: | ||
2076 | Bool -> FileType -> InputFile -> Packet -> IO [(InputFile, KikiReportAction)] | ||
2077 | writeKeyToFile False PEMFile fname packet = do | ||
2078 | case secretPemFromPacket packet of | ||
2079 | Just output -> do | ||
2080 | let stamp = toEnum . fromEnum $ timestamp packet | ||
2076 | handleIO_ (return [(fname, FailedFileWrite)]) $ do | 2081 | handleIO_ (return [(fname, FailedFileWrite)]) $ do |
2077 | saved_mask <- setFileCreationMask 0o077 | 2082 | saved_mask <- setFileCreationMask 0o077 |
2078 | -- Note: The key's timestamp is included in it's fingerprint. | 2083 | -- Note: The key's timestamp is included in it's fingerprint. |
@@ -2080,7 +2085,7 @@ writeKeyToFile False PEMFile fname packet = do | |||
2080 | writeStamped (InputFileContext "" "") fname stamp output | 2085 | writeStamped (InputFileContext "" "") fname stamp output |
2081 | setFileCreationMask saved_mask | 2086 | setFileCreationMask saved_mask |
2082 | return [(fname, ExportedSubkey)] | 2087 | return [(fname, ExportedSubkey)] |
2083 | algo -> return [(fname, UnableToExport algo $ fingerprint packet)] | 2088 | Nothing -> return [(fname, UnableToExport (key_algorithm packet) $ fingerprint packet)] |
2084 | 2089 | ||
2085 | writeKeyToFile False DNSPresentation fname packet = do | 2090 | writeKeyToFile False DNSPresentation fname packet = do |
2086 | case key_algorithm packet of | 2091 | case key_algorithm packet of |
@@ -30,7 +30,8 @@ Executable kiki | |||
30 | bytestring -any, binary -any, | 30 | bytestring -any, binary -any, |
31 | unix, time, | 31 | unix, time, |
32 | containers -any, process -any, filepath -any, | 32 | containers -any, process -any, filepath -any, |
33 | network -any, old-locale -any, zlib -any | 33 | network -any, old-locale -any, zlib -any, |
34 | tar | ||
34 | if !flag(cryptonite) | 35 | if !flag(cryptonite) |
35 | Build-Depends: crypto-pubkey >=0.2.3, cryptohash -any, | 36 | Build-Depends: crypto-pubkey >=0.2.3, cryptohash -any, |
36 | crypto-pubkey-types -any | 37 | crypto-pubkey-types -any |
@@ -10,6 +10,7 @@ module Main ( main ) where | |||
10 | 10 | ||
11 | import Control.Applicative | 11 | import Control.Applicative |
12 | import Control.Monad | 12 | import Control.Monad |
13 | import Control.Monad.Fix | ||
13 | import Data.ASN1.BinaryEncoding | 14 | import Data.ASN1.BinaryEncoding |
14 | import Data.ASN1.Encoding | 15 | import Data.ASN1.Encoding |
15 | import Data.ASN1.Types | 16 | import Data.ASN1.Types |
@@ -268,6 +269,9 @@ partitionStaticArguments specs args = psa args | |||
268 | psa [] = ([],[]) | 269 | psa [] = ([],[]) |
269 | psa (a:as) = | 270 | psa (a:as) = |
270 | case Map.lookup a smap of | 271 | case Map.lookup a smap of |
272 | Nothing | (k,'=':v) <- break (=='=') a | ||
273 | , Just 1 <- Map.lookup k smap | ||
274 | -> first ([k,v]:) $ psa as | ||
271 | Nothing -> second (a:) $ psa as | 275 | Nothing -> second (a:) $ psa as |
272 | Just n -> first ((a:take n as):) $ psa (drop n as) | 276 | Just n -> first ((a:take n as):) $ psa (drop n as) |
273 | 277 | ||
@@ -1729,25 +1733,48 @@ tarC (sargs,margs) = do | |||
1729 | KikiSuccess rt -> do | 1733 | KikiSuccess rt -> do |
1730 | CTime pubtime <- modificationTime <$> getFileStatus (rtPubring rt) | 1734 | CTime pubtime <- modificationTime <$> getFileStatus (rtPubring rt) |
1731 | let keyspec = concat . take 1 <$> Map.lookup "--secrets" margs | 1735 | let keyspec = concat . take 1 <$> Map.lookup "--secrets" margs |
1732 | fs = tarContent rt keyspec build_ipsec (build_ssh rt pubtime) (error "todo") | 1736 | fs = tarContent rt keyspec build_ipsec (build_ssh rt pubtime) (build_secret rt) |
1733 | es = do | 1737 | es = do |
1734 | (n,(epoch_time_int64,bs)) <- fs | 1738 | (n,(epoch_time_int64,ebs)) <- fs |
1735 | entry <- either (const []) (return . flip Tar.fileEntry bs) $ Tar.toTarPath False n | 1739 | let mktar' = mktar n epoch_time_int64 |
1736 | return $ entry { Tar.entryTime = epoch_time_int64 } | 1740 | return $ case ebs of |
1737 | tarbs = Tar.write es | 1741 | Right bs -> return $ either (const Nothing) Just $ mktar' bs |
1742 | Left iombs -> do | ||
1743 | mbs <- iombs | ||
1744 | case mbs of | ||
1745 | Nothing -> return Nothing | ||
1746 | Just bs -> return $ either (const Nothing) Just $ mktar' bs | ||
1747 | tarbs <- Tar.write . mapMaybe id <$> sequence es | ||
1738 | L.putStr tarbs | 1748 | L.putStr tarbs |
1739 | err -> putStrLn $ errorString err | 1749 | err -> putStrLn $ errorString err |
1740 | where | 1750 | where |
1741 | build_ipsec ns addr ipsec sigs | 1751 | build_ipsec ns addr ipsec sigs |
1742 | = ( fromIntegral $ timestamp ipsec | 1752 | = ( fromIntegral $ timestamp ipsec |
1743 | , Char8.pack $ fromJust $ pemFromPacket ipsec) | 1753 | , Right $ Char8.pack $ fromJust $ pemFromPacket ipsec) |
1744 | build_ssh rt timestamp sshs = (timestamp, Char8.unlines $ map knownhost sshs) | 1754 | build_ssh rt timestamp sshs = (timestamp, Right $ Char8.unlines $ map knownhost sshs) |
1745 | where | 1755 | where |
1746 | knownhost (kk,hostkey,sigs) = Char8.intercalate "," ns <> " " <> Char8.pack (sshblobFromPacket hostkey) | 1756 | knownhost (kk,hostkey,sigs) = Char8.intercalate "," ns <> " " <> Char8.pack (sshblobFromPacket hostkey) |
1747 | where | 1757 | where |
1748 | ns = onames ++ others | 1758 | ns = onames ++ others |
1749 | (_,(onames,others)) = getHostnames $ rtKeyDB rt Map.! kk | 1759 | (_,(onames,others)) = getHostnames $ rtKeyDB rt Map.! kk |
1750 | 1760 | ||
1761 | build_secret rt k = ( fromIntegral $ timestamp k | ||
1762 | , Left $ fmap Char8.pack . (>>= secretPemFromPacket) <$> decrypt rt k ) | ||
1763 | |||
1764 | mktar n epoch_time_int64 bs = do | ||
1765 | torpath <- Tar.toTarPath False n | ||
1766 | Right $ (Tar.fileEntry torpath bs) { Tar.entryTime = epoch_time_int64 } | ||
1767 | |||
1768 | decrypt :: KeyRingRuntime -> Packet -> IO (Maybe Packet) | ||
1769 | decrypt rt k@SecretKeyPacket { symmetric_algorithm = Unencrypted } = return $ Just k | ||
1770 | decrypt rt k = do | ||
1771 | r <- rtPassphrases rt (MappedPacket k Map.empty) | ||
1772 | case r of | ||
1773 | KikiSuccess p -> return $ Just p | ||
1774 | _ -> do | ||
1775 | hPutStrLn stderr $ "Failed to decrypt "++fingerprint k++"." | ||
1776 | return Nothing | ||
1777 | |||
1751 | minimalOp :: CommonArgsParsed -> KeyRingOperation | 1778 | minimalOp :: CommonArgsParsed -> KeyRingOperation |
1752 | minimalOp cap = op | 1779 | minimalOp cap = op |
1753 | where | 1780 | where |