summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kiki.hs12
-rw-r--r--lib/GnuPGAgent.hs41
-rw-r--r--lib/KeyRing.hs157
-rw-r--r--lib/Kiki.hs17
4 files changed, 171 insertions, 56 deletions
diff --git a/kiki.hs b/kiki.hs
index a0eff1a..71222ce 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -1146,8 +1146,8 @@ sync bExport bImport bSecret cmdarg args_raw = do
1146 ++ pems 1146 ++ pems
1147 ++ if bSecret then walts else [] 1147 ++ if bSecret then walts else []
1148 ++ hosts 1148 ++ hosts
1149 , opPassphrases = do pfile <- maybeToList passfd 1149 , opPassphrases = withAgent $ do pfile <- maybeToList passfd
1150 return $ PassphraseSpec Nothing Nothing pfile 1150 return $ PassphraseSpec Nothing Nothing pfile
1151 , opTransforms = maybe [] (const [Autosign]) $ Map.lookup "--autosign" margs 1151 , opTransforms = maybe [] (const [Autosign]) $ Map.lookup "--autosign" margs
1152 , opHome = homespec 1152 , opHome = homespec
1153 } 1153 }
@@ -1252,8 +1252,8 @@ kiki "show" args = do
1252 ++ pems 1252 ++ pems
1253 ++ walts 1253 ++ walts
1254 ++ hosts 1254 ++ hosts
1255 , opPassphrases = do pfile <- maybeToList passfd 1255 , opPassphrases = withAgent $ do pfile <- maybeToList passfd
1256 return $ PassphraseSpec Nothing Nothing pfile 1256 return $ PassphraseSpec Nothing Nothing pfile
1257 , opTransforms = [] 1257 , opTransforms = []
1258 , opHome = homespec 1258 , opHome = homespec
1259 } 1259 }
@@ -1537,8 +1537,8 @@ kiki "delete" args = do
1537 [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) 1537 [ ( HomeSec, buildStreamInfo KF_All KeyRingFile )
1538 , ( HomePub, buildStreamInfo KF_All KeyRingFile ) 1538 , ( HomePub, buildStreamInfo KF_All KeyRingFile )
1539 ] 1539 ]
1540 , opPassphrases = do pfile <- maybeToList passfd 1540 , opPassphrases = withAgent $ do pfile <- maybeToList passfd
1541 return $ PassphraseSpec Nothing Nothing pfile 1541 return $ PassphraseSpec Nothing Nothing pfile
1542 , opTransforms = map DeleteSubkeyByFingerprint fps 1542 , opTransforms = map DeleteSubkeyByFingerprint fps
1543 , opHome = homespec 1543 , opHome = homespec
1544 } 1544 }
diff --git a/lib/GnuPGAgent.hs b/lib/GnuPGAgent.hs
index 8bffd1b..165fdf2 100644
--- a/lib/GnuPGAgent.hs
+++ b/lib/GnuPGAgent.hs
@@ -3,6 +3,8 @@
3{-# LANGUAGE PatternGuards #-} 3{-# LANGUAGE PatternGuards #-}
4module GnuPGAgent 4module GnuPGAgent
5 ( session 5 ( session
6 , Query(..)
7 , QueryMode(..)
6 , getPassphrase 8 , getPassphrase
7 , clearPassphrase 9 , clearPassphrase
8 , quit ) where 10 , quit ) where
@@ -44,6 +46,12 @@ session = do
44 connect sock (SockAddrUnix (gpghome ++ "/S.gpg-agent")) 46 connect sock (SockAddrUnix (gpghome ++ "/S.gpg-agent"))
45 agent <- socketToHandle sock ReadWriteMode 47 agent <- socketToHandle sock ReadWriteMode
46 hSetBuffering agent LineBuffering 48 hSetBuffering agent LineBuffering
49 lookupEnv "DISPLAY" >>= \case
50 Just display -> do hPutStrLn agent ("option putenv DISPLAY="++display)
51 _ <- hGetLine agent
52 return ()
53 Nothing -> return ()
54 -- TODO: GPG_TTY
47 return $ Just $ GnuPGAgent agent 55 return $ Just $ GnuPGAgent agent
48 Nothing -> do 56 Nothing -> do
49 hPutStrLn stderr "Unable to find home directory." 57 hPutStrLn stderr "Unable to find home directory."
@@ -64,23 +72,42 @@ clearPassphrase agent key = do
64 let cmd = "clear_passphrase "++fingerprint key 72 let cmd = "clear_passphrase "++fingerprint key
65 hPutStrLn (agentHandle agent) cmd 73 hPutStrLn (agentHandle agent) cmd
66 74
67getPassphrase :: GnuPGAgent -> Bool -> Packet -> String -> Maybe Packet -> IO (Maybe String) 75data Query = Query
68getPassphrase agent ask key uid masterkey = do 76 { queryPacket :: Packet
69 let askopt = if ask then "" else "--no-ask " 77 , queryUID :: String
70 (er,pr,desc) = prompts key uid masterkey 78 , queryMainKey :: Maybe Packet
79 }
80 deriving Show
81
82data QueryMode = AskNot | AskAgain String | Ask
83 deriving (Show,Eq,Ord)
84
85getPassphrase :: GnuPGAgent -> QueryMode -> Query -> IO (Maybe String)
86getPassphrase agent ask (Query key uid masterkey) = do
87 let (er0,pr,desc) = prompts key uid masterkey
88 (er,askopt) = case ask of
89 AskNot -> (er0,"--no-ask")
90 AskAgain ermsg -> (ermsg,"")
91 Ask -> (er0,"")
71 cmd = "get_passphrase "++askopt++fingerprint key++" "++unwords (map percentPlusEscape [er,pr,desc]) 92 cmd = "get_passphrase "++askopt++fingerprint key++" "++unwords (map percentPlusEscape [er,pr,desc])
72 putStrLn cmd 93 -- putStrLn cmd
73 hPutStrLn (agentHandle agent) cmd 94 hPutStrLn (agentHandle agent) cmd
74 r0 <- hGetLine (agentHandle agent) 95 r0 <- hGetLine (agentHandle agent)
96 -- putStrLn $ "agent says: " ++ r0
75 case takeWhile (/=' ') r0 of 97 case takeWhile (/=' ') r0 of
76 "OK" -> hGetLine (agentHandle agent) >>= unhex . drop 3 98 "OK" -> hGetLine (agentHandle agent) >>= unhex . drop 3
77 where 99 where
78#if defined(VERSION_memory) 100#if defined(VERSION_memory)
79 unhex hx = case convertFromBase Base16 (S8.pack hx) of 101 unhex hx = case convertFromBase Base16 (S8.pack hx) of
80 Left e -> return Nothing 102 Left e -> do
103 -- Useful for debugging but insecure generally ;)
104 -- putStrLn $ "convertFromBase error for input "++show hx++": "++show e
105 return Nothing
81 Right bs -> return $ Just $ S8.unpack bs 106 Right bs -> return $ Just $ S8.unpack bs
82#elif defined(VERSION_dataenc) 107#elif defined(VERSION_dataenc)
83 unhex hx = return $ fmap (map $ chr . fromIntegral) $ Base16.decode hx 108 unhex hx = maybe (return () {- putStrLn $ "dataenc error for input "++show hx -})
109 return
110 $ fmap (map $ chr . fromIntegral) $ Base16.decode hx
84#endif 111#endif
85 "ERR" -> return Nothing 112 "ERR" -> return Nothing
86 113
diff --git a/lib/KeyRing.hs b/lib/KeyRing.hs
index ae2d14d..a055dad 100644
--- a/lib/KeyRing.hs
+++ b/lib/KeyRing.hs
@@ -47,7 +47,8 @@ module KeyRing
47 , KeyFilter(..) 47 , KeyFilter(..)
48 -- * Results of a KeyRing Operation 48 -- * Results of a KeyRing Operation
49 , KeyRingRuntime(..) 49 , KeyRingRuntime(..)
50 , MappedPacket(..) 50 , OriginMapped(..)
51 , MappedPacket
51 , KeyDB 52 , KeyDB
52 , KeyData(..) 53 , KeyData(..)
53 , SubKey(..) 54 , SubKey(..)
@@ -209,6 +210,7 @@ import Base58
209import FunctorToMaybe 210import FunctorToMaybe
210import DotLock 211import DotLock
211import ProcessUtils (systemEnv, ExitCode(ExitFailure, ExitSuccess) ) 212import ProcessUtils (systemEnv, ExitCode(ExitFailure, ExitSuccess) )
213import GnuPGAgent as Agent
212 214
213-- DER-encoded elliptic curve ids 215-- DER-encoded elliptic curve ids
214-- nistp256_id = 0x2a8648ce3d030107 216-- nistp256_id = 0x2a8648ce3d030107
@@ -429,6 +431,7 @@ data PassphraseSpec = PassphraseSpec
429 } 431 }
430 -- | Use this to carry pasphrases from a previous run. 432 -- | Use this to carry pasphrases from a previous run.
431 | PassphraseMemoizer PacketTranscoder 433 | PassphraseMemoizer PacketTranscoder
434 | PassphraseAgent
432 435
433instance Show PassphraseSpec where 436instance Show PassphraseSpec where
434 show (PassphraseSpec a b c) = "PassphraseSpec "++show (a,b,c) 437 show (PassphraseSpec a b c) = "PassphraseSpec "++show (a,b,c)
@@ -1433,6 +1436,9 @@ doesInputFileExist ctx f = do
1433-} 1436-}
1434 1437
1435 1438
1439-- | Reads contents of an 'InputFile' or returns the cached content from a prior call.
1440-- An optional prompt is provided and will be printed on stdout only in the case that
1441-- the provided 'InputFile' is 'FileDesc' 0 (i.e. stdin).
1436cachedContents :: Maybe S.ByteString -> InputFileContext -> InputFile -> IO (IO S.ByteString) 1442cachedContents :: Maybe S.ByteString -> InputFileContext -> InputFile -> IO (IO S.ByteString)
1437cachedContents maybePrompt ctx fd = do 1443cachedContents maybePrompt ctx fd = do
1438 ref <- newIORef Nothing 1444 ref <- newIORef Nothing
@@ -1637,7 +1643,7 @@ buildKeyDB ctx grip0 keyring = do
1637 readw wk n = fmap (n,) (readPacketsFromWallet wk (ArgFile n)) 1643 readw wk n = fmap (n,) (readPacketsFromWallet wk (ArgFile n))
1638 1644
1639 -- KeyRings (todo: KikiCondition reporting?) 1645 -- KeyRings (todo: KikiCondition reporting?)
1640 (spilled,mwk,grip,accs,keys,unspilled) <- do 1646 (spilled,mwk,grip,accs,keyqs,unspilled) <- do
1641#if MIN_VERSION_containers(0,5,0) 1647#if MIN_VERSION_containers(0,5,0)
1642 ringPackets <- Map.traverseWithKey readp ringMap 1648 ringPackets <- Map.traverseWithKey readp ringMap
1643#else 1649#else
@@ -1660,32 +1666,69 @@ buildKeyDB ctx grip0 keyring = do
1660 -- | keys 1666 -- | keys
1661 -- process ringPackets, and get a map of fingerprint info to 1667 -- process ringPackets, and get a map of fingerprint info to
1662 -- to a packet, remembering it's original file, access. 1668 -- to a packet, remembering it's original file, access.
1663 keys :: Map.Map KeyKey MappedPacket 1669 keys :: Map.Map KeyKey (MappedPacket,Map.Map String [Packet])
1664 keys = Map.foldl slurpkeys Map.empty 1670 keys = Map.foldl slurpkeys Map.empty
1665 $ Map.mapWithKey filterSecrets ringPackets 1671 $ Map.mapWithKey filterSecrets ringPackets
1666 where 1672 where
1667 filterSecrets f (_,Message ps) = 1673 filterSecrets :: InputFile -> (a,Message) -> [[MappedPacket]]
1668 filter (isSecretKey . packet) 1674 filterSecrets f (_,Message ps) = keygroups
1669 $ zipWith (mappedPacketWithHint fname) ps [1..] 1675 -- filter (isSecretKey . packet) mps
1670 where fname = resolveForReport (Just ctx) f 1676 where
1671 slurpkeys m ps = m `Map.union` Map.fromList ps' 1677 mps = zipWith (mappedPacketWithHint fname) ps [1..]
1672 where ps' = zip (map (keykey . packet) ps) ps 1678 fname = resolveForReport (Just ctx) f
1679 keygroups = dropWhile (not . isSecretKey . packet . head)
1680 $ groupBy (const $ not . isSecretKey . packet) mps
1681 slurpkeys :: (Map.Map KeyKey (MappedPacket,Map.Map String [Packet]))
1682 -> [[MappedPacket]]
1683 -> (Map.Map KeyKey (MappedPacket,Map.Map String [Packet]))
1684 slurpkeys m pss = Map.unionWith combineKeyKey m m2
1685 where
1686 m2 :: Map.Map KeyKey (MappedPacket, (Map.Map String [Packet]))
1687 m2 = Map.fromList $ map build pss
1688 where
1689 build ps = (kk,(kp,uidmap ps'))
1690 where
1691 (kpkt,ps') = splitAt 1 ps
1692 kp = head kpkt
1693 kk = keykey . packet $ kp
1694 combineKeyKey (mp,um) (mp2,um2) = (mp,Map.unionWith (++) um um2)
1695 uidmap ps = um2
1696 where
1697 ugs = dropWhile (not . isUserID . packet .head) $ groupBy (const $ not . isUserID . packet) ps
1698 um2 = Map.fromList
1699 $ map (\(MappedPacket (UserIDPacket s) _:sigs)->(s,takeWhile isSignaturePacket $ map packet sigs)) ugs
1673 -- | mwk 1700 -- | mwk
1674 -- first master key matching the provided grip 1701 -- first master key matching the provided grip
1675 -- (the m is for "MappedPacket", wk for working key) 1702 -- (the m is for "MappedPacket", wk for working key)
1676 mwk :: Maybe MappedPacket 1703 mwk = fst <$> mwkq
1677 mwk = listToMaybe $ do 1704
1705 main_query = fromMaybe (Query MarkerPacket "anonymous1" Nothing) $ snd <$> mwkq
1706
1707 keyqs :: Map.Map KeyKey (OriginMapped Query)
1708 keyqs = fmap (\(mp,us) -> mp { packet = main_query { queryPacket = packet mp} }) keys
1709
1710 mwkq :: Maybe (MappedPacket,Query)
1711 mwkq = listToMaybe $ do
1678 fp <- maybeToList grip 1712 fp <- maybeToList grip
1679 let matchfp mp = not (is_subkey p) && matchpr fp p == fp 1713 let matchfp (mp,us)
1714 | not (is_subkey p) && matchpr fp p == fp = Just (mp,query p us)
1715 | otherwise = Nothing
1680 where p = packet mp 1716 where p = packet mp
1681 Map.elems $ Map.filter matchfp keys 1717 -- TODO: check signature on UID packet?
1718 -- TODO: custom queries for subkeys?
1719 query p us = Query p
1720 (fromMaybe "" $ listToMaybe $ Map.keys us)
1721 Nothing -- No subkey queries for now.
1722 Map.elems $ Map.mapMaybe matchfp keys
1723
1682 -- | accs 1724 -- | accs
1683 -- file access(Sec | Pub) lookup table 1725 -- file access(Sec | Pub) lookup table
1684 accs :: Map.Map InputFile Access 1726 accs :: Map.Map InputFile Access
1685 accs = fmap (access . fst) ringPackets 1727 accs = fmap (access . fst) ringPackets
1686 return (spilled,mwk,grip,accs,keys,fmap snd unspilled) 1728 return (spilled,mwk,grip,accs,keyqs,fmap snd unspilled)
1687 1729
1688 transcode <- makeMemoizingDecrypter keyring ctx keys 1730 putStrLn $ ppShow keyqs
1731 transcode <- makeMemoizingDecrypter keyring ctx keyqs
1689 let doDecrypt = transcode (Unencrypted,S2K 100 "") 1732 let doDecrypt = transcode (Unencrypted,S2K 100 "")
1690 1733
1691 let wk = fmap packet mwk 1734 let wk = fmap packet mwk
@@ -1707,6 +1750,9 @@ buildKeyDB ctx grip0 keyring = do
1707 r <- performManipulations doDecrypt rt1 mwk manip 1750 r <- performManipulations doDecrypt rt1 mwk manip
1708 try r $ \(rt2,report) -> do 1751 try r $ \(rt2,report) -> do
1709 return $ KikiSuccess (report,rtKeyDB rt2) 1752 return $ KikiSuccess (report,rtKeyDB rt2)
1753 -- XXX: Unspilled keys are not obtainable from rtKeyDB.
1754 -- If the working key is marked non spillable, then how
1755 -- would we look up it's UID and such?
1710#if MIN_VERSION_containers(0,5,0) 1756#if MIN_VERSION_containers(0,5,0)
1711 in fmap sequenceA $ Map.traverseWithKey trans spilled 1757 in fmap sequenceA $ Map.traverseWithKey trans spilled
1712#else 1758#else
@@ -2544,7 +2590,7 @@ writePEMKeys doDecrypt db exports = do
2544 return $ KikiSuccess (fname,stream,pun) 2590 return $ KikiSuccess (fname,stream,pun)
2545 2591
2546makeMemoizingDecrypter :: KeyRingOperation -> InputFileContext 2592makeMemoizingDecrypter :: KeyRingOperation -> InputFileContext
2547 -> Map.Map KeyKey MappedPacket 2593 -> Map.Map KeyKey (OriginMapped Query)
2548 -> IO PacketTranscoder 2594 -> IO PacketTranscoder
2549makeMemoizingDecrypter operation ctx keys = do 2595makeMemoizingDecrypter operation ctx keys = do
2550 if null chains then do 2596 if null chains then do
@@ -2555,12 +2601,14 @@ makeMemoizingDecrypter operation ctx keys = do
2555 -- FilePath? 2601 -- FilePath?
2556 -- pws :: Map.Map FilePath (IO S.ByteString) 2602 -- pws :: Map.Map FilePath (IO S.ByteString)
2557 {- 2603 {-
2604 -- This disabled code obtained password sources from StreamInfo records.
2558 pws <- 2605 pws <-
2559 Traversable.mapM (cachedContents ctx . fromJust . pwfile . typ) 2606 Traversable.mapM (cachedContents ctx . fromJust . pwfile . typ)
2560 (Map.mapKeys (resolveForReport Nothing) -- see note (*) note above 2607 (Map.mapKeys (resolveForReport Nothing) -- see note (*) note above
2561 $ Map.filter (isJust . pwfile . typ) $ opFiles operation) 2608 $ Map.filter (isJust . pwfile . typ) $ opFiles operation)
2562 -} 2609 -}
2563 let prompt = Just "Enter possibly multi-line passphrase (Press CTRL-D when finished):\n" 2610 let prompt = Just "Enter possibly multi-line passphrase (Press CTRL-D when finished):\n"
2611 -- List of file-specific password sources.
2564 pws2 <- 2612 pws2 <-
2565 Traversable.mapM (cachedContents prompt ctx) 2613 Traversable.mapM (cachedContents prompt ctx)
2566 $ Map.fromList $ mapMaybe 2614 $ Map.fromList $ mapMaybe
@@ -2568,55 +2616,80 @@ makeMemoizingDecrypter operation ctx keys = do
2568 guard $ isNothing $ passSpecKeySpec spec 2616 guard $ isNothing $ passSpecKeySpec spec
2569 passSpecRingFile spec) 2617 passSpecRingFile spec)
2570 passspecs 2618 passspecs
2619 -- List of general password sources.
2571 defpw <- do 2620 defpw <- do
2572 Traversable.mapM (cachedContents prompt ctx . passSpecPassFile) 2621 Traversable.mapM (cachedContents prompt ctx . passSpecPassFile)
2573 $ listToMaybe $ filter (\sp -> isNothing (passSpecRingFile sp) 2622 $ listToMaybe $ filter (\sp -> isNothing (passSpecRingFile sp)
2574 && isNothing (passSpecKeySpec sp)) 2623 && isNothing (passSpecKeySpec sp))
2575 $ opPassphrases operation 2624 $ passspecs
2576 unkeysRef <- newIORef (Map.empty :: Map.Map KeyKey Packet) 2625 unkeysRef <- newIORef (Map.empty :: Map.Map KeyKey Packet)
2577 return $ doDecrypt unkeysRef ({- pws `Map.union` -} pws2) defpw 2626 return $ doDecrypt unkeysRef ({- pws `Map.union` -} pws2) defpw (not $ null agentspec)
2578 else let PassphraseMemoizer f = head chains 2627 else let PassphraseMemoizer f = head chains
2579 in return f 2628 in return f
2580 where 2629 where
2581 (chains,passspecs) = partition isChain $ opPassphrases operation 2630 (chains,passspecs0) = partition isChain $ opPassphrases operation
2582 where isChain (PassphraseMemoizer {}) = True 2631 where isChain (PassphraseMemoizer {}) = True
2583 isChain _ = False 2632 isChain _ = False
2633 (agentspec,passspecs) = partition isAgent $ opPassphrases operation
2634 where isAgent PassphraseAgent = True
2635 isAgent _ = False
2584 doDecrypt :: IORef (Map.Map KeyKey Packet) 2636 doDecrypt :: IORef (Map.Map KeyKey Packet)
2585 -> Map.Map FilePath (IO S.ByteString) 2637 -> Map.Map FilePath (IO S.ByteString)
2586 -> Maybe (IO S.ByteString) 2638 -> Maybe (IO S.ByteString)
2639 -> Bool
2587 -> (SymmetricAlgorithm,S2K) 2640 -> (SymmetricAlgorithm,S2K)
2588 -> MappedPacket 2641 -> MappedPacket
2589 -> IO (KikiCondition Packet) 2642 -> IO (KikiCondition Packet)
2590 doDecrypt unkeysRef pws defpw (dest_alg,dest_s2k) mp0 = do 2643 doDecrypt unkeysRef pws defpw agent_requested (dest_alg,dest_s2k) mp0 = do
2591 unkeys <- readIORef unkeysRef 2644 unkeys <- readIORef unkeysRef
2592 let mp = fromMaybe mp0 $ do 2645 let (mp,qry) = fromMaybe (mp0,Query (packet mp0) "anonymous2" Nothing) $ do
2593 k <- Map.lookup kk keys 2646 k <- Map.lookup kk keys
2594 return $ mergeKeyPacket "decrypt" mp0 k 2647 return (mergeKeyPacket "decrypt" mp0 (fmap queryPacket k), packet k)
2595 wk = packet mp0 2648 wk = packet mp0
2596 kk = keykey wk 2649 kk = keykey wk
2597 fs = Map.keys $ locations mp 2650 fs = Map.keys $ locations mp
2598 2651
2599 decryptIt [] = return BadPassphrase 2652 decryptIt [] = return BadPassphrase
2600 decryptIt (getpw:getpws) = do 2653 decryptIt (getpw:getpws) = tries 1 getpw (decryptIt getpws)
2601 -- TODO: This function should use mergeKeyPacket to 2654 where
2655 tries count getpw recurse = do
2656 -- TODO: This function should use mergeKeyPacket to
2602 -- combine the packet with it's unspilled version before 2657 -- combine the packet with it's unspilled version before
2603 -- attempting to decrypt it. 2658 -- attempting to decrypt it. Note: We are uninterested
2604 pw <- getpw 2659 -- in the 'locations' field, so this would effectively
2660 -- allow you to run 'decryptIt' on an unencrypted public key
2661 -- to obtain it's secret key.
2662 (pw,wants_retry) <- getpw (if count>1 then AskAgain "Bad pasphrase." else Ask,qry)
2605 let wkun = fromMaybe wk $ do 2663 let wkun = fromMaybe wk $ do
2606 guard $ symmetric_algorithm (packet mp) /= Unencrypted 2664 guard $ symmetric_algorithm (packet mp) /= Unencrypted
2607 decryptSecretKey pw (packet mp) 2665 decryptSecretKey pw (packet mp)
2666
2608 case symmetric_algorithm wkun of 2667 case symmetric_algorithm wkun of
2668
2609 Unencrypted -> do 2669 Unencrypted -> do
2610 writeIORef unkeysRef (Map.insert kk wkun unkeys) 2670 writeIORef unkeysRef (Map.insert kk wkun unkeys)
2611 ek <- if dest_alg==Unencrypted 2671 ek <- if dest_alg==Unencrypted
2612 then return $ Just wkun 2672 then return $ Just wkun
2613 else encryptSecretKey pw dest_s2k dest_alg wkun 2673 else encryptSecretKey pw dest_s2k dest_alg wkun
2614 case ek of 2674 case ek of
2615 Nothing -> return $ BadPassphrase 2675 Nothing | wants_retry && count<3 -> tries (count+1) getpw recurse
2616 Just wken -> return $ KikiSuccess wken 2676 Nothing -> recurse
2617 _ -> decryptIt getpws 2677 Just wken -> return $ KikiSuccess wken
2678
2679 _ -> recurse
2618 2680
2619 getpws = mapMaybe (`Map.lookup` pws) fs ++ maybeToList defpw 2681 getpws = (map (const . fmap (,False)) $ mapMaybe (`Map.lookup` pws) fs ++ maybeToList defpw) ++ [ agentpw | agent_requested ]
2682
2683 agentpw (ask,qry) = do
2684 s <- session
2685 fromMaybe (return ("",False)) $ do
2686 s <- s
2687 Just $ do
2688 case ask of AskAgain _ -> clearPassphrase s (queryPacket qry)
2689 _ -> return ()
2690 mbpw <- getPassphrase s ask qry
2691 quit s
2692 return ( maybe "" S8.pack mbpw, True)
2620 2693
2621 if symmetric_algorithm wk == dest_alg 2694 if symmetric_algorithm wk == dest_alg
2622 && ( symmetric_algorithm wk == Unencrypted || s2k wk == dest_s2k ) 2695 && ( symmetric_algorithm wk == Unencrypted || s2k wk == dest_s2k )
@@ -3434,10 +3507,13 @@ data OriginFlags = OriginFlags
3434 3507
3435type OriginMap = Map.Map FilePath OriginFlags 3508type OriginMap = Map.Map FilePath OriginFlags
3436 3509
3437data MappedPacket = MappedPacket 3510type MappedPacket = OriginMapped Packet
3438 { packet :: Packet 3511data OriginMapped a = MappedPacket
3512 { packet :: a
3439 , locations :: OriginMap 3513 , locations :: OriginMap
3440 } deriving Show 3514 } deriving Show
3515instance Functor OriginMapped where
3516 fmap f (MappedPacket x ls) = MappedPacket (f x) ls
3441 3517
3442type TrustMap = Map.Map FilePath Packet 3518type TrustMap = Map.Map FilePath Packet
3443type SigAndTrust = ( MappedPacket 3519type SigAndTrust = ( MappedPacket
@@ -3521,19 +3597,26 @@ onionName kd = (addr,name)
3521 where 3597 where
3522 (addr,(name:_,_)) = getHostnames kd 3598 (addr,(name:_,_)) = getHostnames kd
3523-} 3599-}
3600
3601-- | Compare different versions if the same key pair. Public versions
3602-- are considered greater. If the two packets do not represent the same
3603-- key or the packets are not keys at all, an error will result that
3604-- includes the context provided as the first argument.
3524keyCompare :: String -> Packet -> Packet -> Ordering 3605keyCompare :: String -> Packet -> Packet -> Ordering
3525keyCompare what (SecretKeyPacket {}) (PublicKeyPacket {}) = LT 3606keyCompare what (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
3526keyCompare what (PublicKeyPacket {}) (SecretKeyPacket {}) = GT 3607keyCompare what (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
3527keyCompare what a b | keykey a==keykey b = EQ 3608keyCompare what a b | keykey a==keykey b = EQ
3528keyCompare what a b = error $ unlines ["Unable to merge "++what++":" 3609keyCompare what a b = error $ unlines ["Unable to merge "++what++":"
3529 , fingerprint a 3610 , if isKey a then fingerprint a else ""
3530 , PP.ppShow a 3611 , PP.ppShow a
3531 , fingerprint b 3612 , if isKey b then fingerprint b else ""
3532 , PP.ppShow b 3613 , PP.ppShow b
3533 ] 3614 ]
3534 3615
3616-- | Merge two representations of the same key, prefering secret version
3617-- because they have more information.
3535mergeKeyPacket :: String -> MappedPacket -> MappedPacket -> MappedPacket 3618mergeKeyPacket :: String -> MappedPacket -> MappedPacket -> MappedPacket
3536mergeKeyPacket what key p = 3619mergeKeyPacket what key p =
3537 key { packet = minimumBy (keyCompare what) [packet key,packet p] 3620 key { packet = minimumBy (keyCompare what) [packet key,packet p]
3538 , locations = Map.union (locations key) (locations p) 3621 , locations = Map.union (locations key) (locations p)
3539 } 3622 }
diff --git a/lib/Kiki.hs b/lib/Kiki.hs
index a134680..25c98e2 100644
--- a/lib/Kiki.hs
+++ b/lib/Kiki.hs
@@ -41,6 +41,10 @@ import CommandLine
41import KeyRing 41import KeyRing
42import DotLock 42import DotLock
43 43
44withAgent :: [PassphraseSpec] -> [PassphraseSpec]
45withAgent [] = [PassphraseAgent]
46withAgent ps = ps
47
44ciphername Unencrypted = "-" 48ciphername Unencrypted = "-"
45ciphername TripleDES = "3des" 49ciphername TripleDES = "3des"
46ciphername (SymmetricAlgorithm w8) = "cipher-"++show w8 50ciphername (SymmetricAlgorithm w8) = "cipher-"++show w8
@@ -87,8 +91,8 @@ minimalOp cap = op
87 [ ( HomeSec, streaminfo { access = Sec }) 91 [ ( HomeSec, streaminfo { access = Sec })
88 , ( HomePub, streaminfo { access = Pub }) 92 , ( HomePub, streaminfo { access = Pub })
89 ] 93 ]
90 , opPassphrases = do pfile <- maybeToList (cap_passfd cap) 94 , opPassphrases = withAgent $ do pfile <- maybeToList (cap_passfd cap)
91 return $ PassphraseSpec Nothing Nothing pfile 95 return $ PassphraseSpec Nothing Nothing pfile
92 , opTransforms = [] 96 , opTransforms = []
93 , opHome = cap_homespec cap 97 , opHome = cap_homespec cap
94 } 98 }
@@ -155,6 +159,7 @@ importAndRefresh root cmn = do
155 ctx = InputFileContext secring pubring 159 ctx = InputFileContext secring pubring
156 passwordop = KeyRingOperation 160 passwordop = KeyRingOperation
157 { opFiles = Map.empty 161 { opFiles = Map.empty
162 -- TODO: ask agent for new passphrase
158 , opPassphrases = do pfd <- maybeToList passfd 163 , opPassphrases = do pfd <- maybeToList passfd
159 return $ PassphraseSpec Nothing Nothing pfd 164 return $ PassphraseSpec Nothing Nothing pfd
160 , opHome = homespec 165 , opHome = homespec
@@ -233,16 +238,16 @@ importAndRefresh root cmn = do
233 , ( ArgFile sshcpath, (peminfo 2048 "ssh-client") ) 238 , ( ArgFile sshcpath, (peminfo 2048 "ssh-client") )
234 , ( ArgFile sshspath, (peminfo 2048 "ssh-server") ) 239 , ( ArgFile sshspath, (peminfo 2048 "ssh-server") )
235 ] 240 ]
236 , opPassphrases = pwds ++ do pfd <- maybeToList passfd 241 , opPassphrases = withAgent $ pwds ++ do pfd <- maybeToList passfd
237 return $ PassphraseSpec Nothing Nothing pfd 242 return $ PassphraseSpec Nothing Nothing pfd
238 , opHome = homespec 243 , opHome = homespec
239 , opTransforms = [] 244 , opTransforms = []
240 } 245 }
241 -- doNothing = return () 246 -- doNothing = return ()
242 nop = KeyRingOperation 247 nop = KeyRingOperation
243 { opFiles = Map.empty 248 { opFiles = Map.empty
244 , opPassphrases = do pfd <- maybeToList passfd 249 , opPassphrases = withAgent $ do pfd <- maybeToList passfd
245 return $ PassphraseSpec Nothing Nothing pfd 250 return $ PassphraseSpec Nothing Nothing pfd
246 , opHome=homespec, opTransforms = [] 251 , opHome=homespec, opTransforms = []
247 } 252 }
248 -- if bUnprivileged then doNothing else mkdirFor torpath 253 -- if bUnprivileged then doNothing else mkdirFor torpath