summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2016-04-23 00:35:03 -0400
committerjoe <joe@jerkface.net>2016-04-23 00:35:03 -0400
commit64202f804429053058ac3efce527f77c2e12948b (patch)
treecf301c570fa9b2266abd8def3106805c970040bc
parentf11640aecb9ba8e1693bcc8fa80a53dc5feb2bac (diff)
WIP: tar command.
-rw-r--r--KeyRing.hs21
-rw-r--r--kiki.hs208
2 files changed, 209 insertions, 20 deletions
diff --git a/KeyRing.hs b/KeyRing.hs
index 7369acf..d4bb099 100644
--- a/KeyRing.hs
+++ b/KeyRing.hs
@@ -938,7 +938,7 @@ parseSpec grip spec = (topspec,subspec)
938 "fp" | top=="" -> Nothing 938 "fp" | top=="" -> Nothing
939 "" | top=="" && is40digitHex sub -> Nothing 939 "" | top=="" && is40digitHex sub -> Nothing
940 "" -> listToMaybe sub >> Just sub 940 "" -> listToMaybe sub >> Just sub
941 -- "fp" -> ??? TODO: non-ehaustive patterns in case: fp:7/fp: 941 _ -> Nothing
942 942
943 is40digitHex xs = ys == xs && length ys==40 943 is40digitHex xs = ys == xs && length ys==40
944 where 944 where
@@ -992,11 +992,14 @@ selectSecretKey (spec,mtag) db = selectKey0 False (spec,mtag) db
992selectPublicKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet 992selectPublicKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet
993selectPublicKey (spec,mtag) db = selectKey0 True (spec,mtag) db 993selectPublicKey (spec,mtag) db = selectKey0 True (spec,mtag) db
994 994
995selectPublicKeyAndSigs :: (KeySpec,Maybe String) -> KeyDB -> [(Packet,[Packet])] 995selectPublicKeyAndSigs :: (KeySpec,Maybe String) -> KeyDB -> [(KeyKey,Packet,[Packet])]
996selectPublicKeyAndSigs (spec,mtag) db = 996selectPublicKeyAndSigs (spec,mtag) db =
997 case mtag of 997 case mtag of
998 Nothing -> concat $ Map.elems $ fmap (findbyspec spec) db 998 Nothing -> do
999 Just tag -> Map.elems (Map.filter (matchSpec spec) db) >>= findsubs tag 999 (kk,r) <- Map.toList $ fmap (findbyspec spec) db
1000 (sub,sigs) <- r
1001 return (kk,sub,sigs)
1002 Just tag -> Map.toList (Map.filter (matchSpec spec) db) >>= findsubs tag
1000 where 1003 where
1001 topresult kd = (keyPacket kd, map (packet .fst) $ keySigAndTrusts kd) 1004 topresult kd = (keyPacket kd, map (packet .fst) $ keySigAndTrusts kd)
1002 1005
@@ -1009,22 +1012,22 @@ selectPublicKeyAndSigs (spec,mtag) db =
1009 ismatch (p,sigs) = matchpr g p ==g 1012 ismatch (p,sigs) = matchpr g p ==g
1010 findbyspec spec kd = if matchSpec spec kd then [topresult kd] else [] 1013 findbyspec spec kd = if matchSpec spec kd then [topresult kd] else []
1011 1014
1012 findsubs tag (KeyData topk _ _ subs) = Map.elems subs >>= gettag 1015 findsubs tag (kk, KeyData topk _ _ subs) = Map.elems subs >>= gettag
1013 where 1016 where
1014 gettag (SubKey sub sigs) = do 1017 gettag (SubKey sub sigs) = do
1015 let (_,mb,_) = findTag [mkUsage tag] (packet topk) (packet sub) sigs 1018 let (_,mb,_) = findTag [mkUsage tag] (packet topk) (packet sub) sigs
1016 (hastag,_) <- maybeToList mb 1019 (hastag,_) <- maybeToList mb
1017 guard hastag 1020 guard hastag
1018 return $ (packet sub, map (packet . fst) sigs) 1021 return $ (kk, packet sub, map (packet . fst) sigs)
1019 1022
1020selectKey0 :: Bool -> (KeySpec,Maybe String) -> KeyDB -> Maybe Packet 1023selectKey0 :: Bool -> (KeySpec,Maybe String) -> KeyDB -> Maybe Packet
1021selectKey0 wantPublic (spec,mtag) db = do 1024selectKey0 wantPublic (spec,mtag) db = do
1022 let Message ps = flattenKeys wantPublic db 1025 let Message ps = flattenKeys wantPublic db
1023 ys = snd $ seek_key spec ps 1026 ys = snd $ seek_key spec ps
1024 flip (maybe (listToMaybe ys)) mtag $ \tag -> do 1027 flip (maybe (listToMaybe ys)) mtag $ \tag -> do
1025 let (subspec,ys1) = (KeyTag y tag,ys1) where y:ys1 = ys 1028 case ys of
1026 zs = snd $ seek_key subspec ys1 1029 y:ys1 -> listToMaybe $ snd $ seek_key (KeyTag y tag) ys1
1027 listToMaybe zs 1030 [] -> Nothing
1028 1031
1029{- 1032{-
1030selectAll :: Bool -> (KeySpec,Maybe String) -> KeyDB -> [(Packet,Maybe Packet)] 1033selectAll :: Bool -> (KeySpec,Maybe String) -> KeyDB -> [(Packet,Maybe Packet)]
diff --git a/kiki.hs b/kiki.hs
index 9a2000a..d58ef2a 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -17,18 +17,24 @@ import Data.Binary
17import Data.Bits 17import Data.Bits
18import Data.Char 18import Data.Char
19import Data.IORef 19import Data.IORef
20import Data.Int
20import Data.List 21import Data.List
21import Data.Maybe 22import Data.Maybe
22import Data.OpenPGP 23import Data.OpenPGP
23import Data.Ord 24import Data.Ord
24import Data.Text.Encoding 25import Data.Text.Encoding
25import System.Posix.User 26import System.Posix.User
27import System.Posix.Files
28import System.Posix.Types
26import System.FilePath.Posix 29import System.FilePath.Posix
30import Foreign.C.Types (CTime(..))
27import System.Directory 31import System.Directory
28import System.Environment 32import System.Environment
29import System.Exit 33import System.Exit
30import System.IO (hPutStrLn,stderr) 34import System.IO (hPutStrLn,stderr)
31import qualified Codec.Binary.Base64 as Base64 35import qualified Codec.Binary.Base64 as Base64
36import qualified Codec.Archive.Tar as Tar
37import qualified Codec.Archive.Tar.Entry as Tar
32#if !defined(VERSION_cryptonite) 38#if !defined(VERSION_cryptonite)
33import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 39import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
34import qualified Crypto.Hash.SHA256 as SHA256 40import qualified Crypto.Hash.SHA256 as SHA256
@@ -42,7 +48,6 @@ import qualified Data.ByteString.Lazy as L
42import qualified Data.ByteString.Lazy.Char8 as Char8 48import qualified Data.ByteString.Lazy.Char8 as Char8
43import qualified Data.Map as Map 49import qualified Data.Map as Map
44import Control.Arrow (first,second) 50import Control.Arrow (first,second)
45import Data.Time.Clock.POSIX ( posixSecondsToUTCTime )
46import Data.Monoid ( (<>) ) 51import Data.Monoid ( (<>) )
47import Data.Binary.Put 52import Data.Binary.Put
48 53
@@ -58,6 +63,8 @@ import ProcessUtils
58import qualified SSHKey as SSH 63import qualified SSHKey as SSH
59import Text.Printf 64import Text.Printf
60import qualified DNSKey as DNS 65import qualified DNSKey as DNS
66import Data.Time.Clock.POSIX ( posixSecondsToUTCTime )
67import Debug.Trace
61 68
62-- {-# ANN module ("HLint: ignore Eta reduce"::String) #-} 69-- {-# ANN module ("HLint: ignore Eta reduce"::String) #-}
63-- {-# ANN module ("HLint: ignore Use camelCase"::String) #-} 70-- {-# ANN module ("HLint: ignore Use camelCase"::String) #-}
@@ -329,11 +336,13 @@ show_ssh' keyspec wkgrip db = do
329 let s = parseSpec wkgrip keyspec 336 let s = parseSpec wkgrip keyspec
330 flip (maybe . Left $ keyspec ++ ": not found") 337 flip (maybe . Left $ keyspec ++ ": not found")
331 (selectPublicKey s db) 338 (selectPublicKey s db)
332 $ \k -> do 339 $ return . sshblobFromPacket
333 let Just (RSAKey (MPI n) (MPI e)) = rsaKeyFromPacket k 340
334 bs = SSH.keyblob (n,e) 341sshblobFromPacket k = blob
335 blob = Char8.unpack bs 342 where
336 return blob 343 Just (RSAKey (MPI n) (MPI e)) = rsaKeyFromPacket k
344 bs = SSH.keyblob (n,e)
345 blob = Char8.unpack bs
337 346
338show_id keyspec wkgrip db = do 347show_id keyspec wkgrip db = do
339 let s = parseSpec "" keyspec 348 let s = parseSpec "" keyspec
@@ -376,7 +385,7 @@ show_cert keyspec wkgrip db = do
376 let s = parseSpec wkgrip keyspec 385 let s = parseSpec wkgrip keyspec
377 case selectPublicKeyAndSigs s db of 386 case selectPublicKeyAndSigs s db of
378 [] -> void $ warn (keyspec ++ ": not found") 387 [] -> void $ warn (keyspec ++ ": not found")
379 [(k,sigs)] -> do 388 [(_,k,sigs)] -> do
380 {- 389 {-
381 let rsa = pkcs8 . fromJust $ rsaKeyFromPacket k 390 let rsa = pkcs8 . fromJust $ rsaKeyFromPacket k
382 der = encodeASN1 DER (toASN1 rsa []) 391 der = encodeASN1 DER (toASN1 rsa [])
@@ -944,9 +953,27 @@ documentHostsOption bExport bImport bSecret =
944 ," but they are currently NOT signed and may be altered in" 953 ," but they are currently NOT signed and may be altered in"
945 ," transit." 954 ," transit."
946 ,""] 955 ,""]
947
948
949 956
957
958
959-- |
960-- Arguments:
961--
962-- * option-count pairs - List of option names paired with number of expected values to follow them.
963--
964-- * polyvariadic options - List of option names that can take any number of arguments.
965--
966-- * default polyvariadic - Implicit polyvariadic option if no other option is specified.
967--
968-- * arguments - list of arguments to be parsed.
969--
970-- Returns:
971--
972-- * (non-variadic only) options and corresponding arguemnts in list of lists form.
973--
974-- * (variadic only) map of option name to argument lists.
975--
976processArgs :: [(String,Int)] -> [String] -> String -> [String] -> ([[String]],Map.Map String [String])
950processArgs sargspec polyVariadicArgs defaultPoly args_raw = (sargs,margs) 977processArgs sargspec polyVariadicArgs defaultPoly args_raw = (sargs,margs)
951 where 978 where
952 (args,trail1) = break (=="--") args_raw 979 (args,trail1) = break (=="--") args_raw
@@ -962,7 +989,7 @@ processArgs sargspec polyVariadicArgs defaultPoly args_raw = (sargs,margs)
962 gargs) 989 gargs)
963 where (sargs,vargs) = partitionStaticArguments sargspec' args 990 where (sargs,vargs) = partitionStaticArguments sargspec' args
964 argspec = map fst sargspec' ++ polyVariadicArgs 991 argspec = map fst sargspec' ++ polyVariadicArgs
965 args' = if map (take 1) (take 1 vargs) == ["-"] 992 args' = if null defaultPoly || map (take 1) (take 1 vargs) == ["-"]
966 then vargs 993 then vargs
967 else defaultPoly:vargs 994 else defaultPoly:vargs
968 -- grouped args 995 -- grouped args
@@ -1175,7 +1202,7 @@ kiki "show" args = do
1175 , ("--torhash",1) 1202 , ("--torhash",1)
1176 ] 1203 ]
1177 polyVariadicArgs = ["--show"] 1204 polyVariadicArgs = ["--show"]
1178 let cap = parseCommonArgs margs 1205 let cap = parseCommonArgs margs
1179 homespec = cap_homespec cap 1206 homespec = cap_homespec cap
1180 passfd = cap_passfd cap 1207 passfd = cap_passfd cap
1181 pems = [] 1208 pems = []
@@ -1592,6 +1619,164 @@ kiki "delete" args = do
1592 forM_ report $ \(fname,act) -> do 1619 forM_ report $ \(fname,act) -> do
1593 putStrLn $ fname ++ ": " ++ reportString act 1620 putStrLn $ fname ++ ": " ++ reportString act
1594 1621
1622kiki "tar" args | "--help" `elem` args = do
1623 putStr . unlines $
1624 [ "kiki tar (-c|-A|-t) [--secrets SPEC] [--passphrase-fd FD] [--homedir HOMEDIR]"
1625 , ""
1626 , "Import or export a tar archive containing key files in the proper"
1627 , "format for software configuration."
1628 , ""
1629 ," -c Generate tar archive on stdout."
1630 ,""
1631 ," -A Read tar archive on stdin."
1632 ,""
1633 ," -t List filepaths that would be included in the (-c) output archive."
1634 ,""
1635 ," --secrets SPEC"
1636 ," Include secret keys for the specified identity."
1637 ," Otherwise, only public keys are included."
1638 ,""
1639 ," SPEC is matched against the following forms in order:"
1640 ,""
1641 ," -"
1642 ," (current working identity)"
1643 ,""
1644 ," fp:4A39F"
1645 ," (tail end of a fingerprint prefixed by 'fp:')"
1646 ,""
1647 ," u:joe"
1648 ," (sub-string of a user id prefixed by 'u:')"
1649 ,""
1650 ," 5E24CD442AA6965D2012E62A905C24185D5379C2"
1651 ," (fingerprint as 40 characters of hexidecimal)"
1652 ,""
1653 ," joe"
1654 ," (sub-string of a user id without 'u:' prefix)"
1655 ]
1656
1657kiki "tar" args = do
1658 let parsed_args = processArgs sargspec [] "" args
1659 sargspec = [("-t",0),("-c",0),("-A",0),("-C",1),("--secrets",1)]
1660 ismode ("-t":_) = True
1661 ismode ("-c":_) = True
1662 ismode ("-A":_) = True
1663 ismode _ = False
1664 case filter ismode (fst parsed_args) of
1665 ["-t":_] -> tarT parsed_args
1666 ["-c":_] -> tarC parsed_args
1667 ["-A":_] -> putStrLn "unimplemented."
1668 _ -> kiki "tar" ["--help"]
1669
1670tarContent rt spec pubpem knownhosts secpem = ipsecs ++ sshs ++ secrets "root"
1671 where
1672 ipsecs = do
1673 (kk,ipsec,sigs) <- selectPublicKeyAndSigs (KeyUidMatch "",Just "strongswan") (rtKeyDB rt)
1674 let kd = (rtKeyDB rt Map.! kk)
1675 k = packet $ keyMappedPacket kd
1676 (addr,(onames,ns)) = getHostnames kd
1677 oname <- onames
1678 return ("etc/ipsec.d/certs/" ++ Char8.unpack oname ++ ".pem", pubpem ns addr ipsec sigs)
1679
1680 sshs = case selectPublicKeyAndSigs (KeyUidMatch "",Just "ssh-host") (rtKeyDB rt) of
1681 [] -> []
1682 ssh_sel -> [("etc/ssh/ssh_known_hosts", knownhosts ssh_sel)]
1683
1684 secrets_kd = case fst . parseSpec "" <$> (++"/") <$> spec of
1685 _ | spec == Just "-" || spec == Just ""
1686 -> maybeToList (rtWorkingKey rt) >>= return . (Map.!) (rtKeyDB rt) . keykey
1687 Just topspec
1688 -> map snd $ filterMatches topspec $ Map.toList $ rtKeyDB rt
1689 w -> []
1690
1691 lookupSecret tag kd = maybeToList $ selectSecretKey (KeyGrip "",Just tag) m
1692 where
1693 m = Map.singleton (keykey $ keyPacket kd) kd
1694
1695 sshkeyname SecretKeyPacket { key_algorithm = RSA } = "id_rsa"
1696
1697 dir :: FilePath -> FilePath
1698 dir d = d -- TODO: prepend prefix path?
1699
1700 spem d k = (d, secpem k)
1701
1702 secrets homedir = do
1703 kd <- secrets_kd
1704 let torkey = spem (dir "var/lib/tor/samizdat/private_key") <$> lookupSecret "tor" kd
1705 sshcli = do k <- lookupSecret "ssh-client" kd
1706 return $ spem (dir $ homedir ++ "/.ssh/" ++ sshkeyname k) k
1707 sshsvr = spem (dir "etc/ssh/ssh_host_rsa_key") <$> lookupSecret "ssh-host" kd
1708 ipseckey = do
1709 k <- lookupSecret "strongswan" kd
1710 oname <- fst . snd $ getHostnames kd
1711 return $ spem (dir $ "etc/ipsec.d/private/"++Char8.unpack oname++".pem") k
1712 torkey ++ sshcli ++ sshsvr ++ ipseckey
1713
1714tarT :: ([[String]],Map.Map String [String]) -> IO ()
1715tarT (sargs,margs) = do
1716 KikiResult rt report <- runKeyRing $ minimalOp $ parseCommonArgs margs
1717 case rt of
1718 KikiSuccess rt -> do
1719 let keyspec = concat . take 1 <$> Map.lookup "--secrets" margs
1720 nil = error "internal error!"
1721 fs = map fst $ tarContent rt keyspec nil nil nil
1722 mapM_ putStrLn fs
1723 err -> putStrLn $ errorString err
1724
1725tarC :: ([[String]],Map.Map String [String]) -> IO ()
1726tarC (sargs,margs) = do
1727 KikiResult rt report <- runKeyRing $ minimalOp $ parseCommonArgs margs
1728 case rt of
1729 KikiSuccess rt -> do
1730 CTime pubtime <- modificationTime <$> getFileStatus (rtPubring rt)
1731 let keyspec = concat . take 1 <$> Map.lookup "--secrets" margs
1732 fs = tarContent rt keyspec build_ipsec (build_ssh rt pubtime) (error "todo")
1733 es = do
1734 (n,(epoch_time_int64,bs)) <- fs
1735 entry <- either (const []) (return . flip Tar.fileEntry bs) $ Tar.toTarPath False n
1736 return $ entry { Tar.entryTime = epoch_time_int64 }
1737 tarbs = Tar.write es
1738 L.putStr tarbs
1739 err -> putStrLn $ errorString err
1740 where
1741 build_ipsec ns addr ipsec sigs
1742 = ( fromIntegral $ timestamp ipsec
1743 , Char8.pack $ fromJust $ pemFromPacket ipsec)
1744 build_ssh rt timestamp sshs = (timestamp, Char8.unlines $ map knownhost sshs)
1745 where
1746 knownhost (kk,hostkey,sigs) = Char8.intercalate "," ns <> " " <> Char8.pack (sshblobFromPacket hostkey)
1747 where
1748 ns = onames ++ others
1749 (_,(onames,others)) = getHostnames $ rtKeyDB rt Map.! kk
1750
1751minimalOp :: CommonArgsParsed -> KeyRingOperation
1752minimalOp cap = op
1753 where
1754 streaminfo = StreamInfo { fill = KF_None
1755 , typ = KeyRingFile
1756 , spill = KF_All
1757 , initializer = Nothing
1758 , access = AutoAccess
1759 , transforms = []
1760 }
1761 op = KeyRingOperation
1762 { opFiles = Map.fromList $
1763 [ ( HomeSec, streaminfo { access = Sec })
1764 , ( HomePub, streaminfo { access = Pub })
1765 ]
1766 , opPassphrases = do pfile <- maybeToList (cap_passfd cap)
1767 return $ PassphraseSpec Nothing Nothing pfile
1768 , opTransforms = []
1769 , opHome = cap_homespec cap
1770 }
1771
1772-- |
1773--
1774-- no leading hyphen, returns Right (input string).
1775--
1776-- single leading hyphen, quits program with "Unrecognized option" error
1777--
1778-- Otherwise, Left (key-value pair) is returend by parsing
1779-- a string of the form --key=value.
1595splitArg :: String -> Either (String,Maybe String) String 1780splitArg :: String -> Either (String,Maybe String) String
1596splitArg arg = 1781splitArg arg =
1597 case hyphens of 1782 case hyphens of
@@ -1620,6 +1805,7 @@ commands =
1620 , ( "merge", "low level import/export operation" ) 1805 , ( "merge", "low level import/export operation" )
1621 , ( "init-key", "initialize the samizdat key ring") 1806 , ( "init-key", "initialize the samizdat key ring")
1622 , ( "delete", "Delete a subkey and its associated signatures" ) 1807 , ( "delete", "Delete a subkey and its associated signatures" )
1808 , ( "tar", "import or export system key files in tar format" )
1623 ] 1809 ]
1624 1810
1625interp vars raw = es >>= interp1 1811interp vars raw = es >>= interp1