diff options
-rw-r--r-- | KeyRing.hs | 81 | ||||
-rw-r--r-- | ProcessUtils.hs | 44 | ||||
-rw-r--r-- | kiki.hs | 28 |
3 files changed, 99 insertions, 54 deletions
@@ -51,6 +51,7 @@ import qualified CryptoCoins as CryptoCoins | |||
51 | import Base58 | 51 | import Base58 |
52 | import FunctorToMaybe | 52 | import FunctorToMaybe |
53 | import DotLock | 53 | import DotLock |
54 | import ProcessUtils (systemEnv, ExitCode(ExitFailure, ExitSuccess) ) | ||
54 | 55 | ||
55 | -- DER-encoded elliptic curve ids | 56 | -- DER-encoded elliptic curve ids |
56 | nistp256_id = 0x2a8648ce3d030107 | 57 | nistp256_id = 0x2a8648ce3d030107 |
@@ -68,19 +69,27 @@ home = HomeDir | |||
68 | , optfile_alts = ["keys.conf","gpg.conf-2","gpg.conf"] | 69 | , optfile_alts = ["keys.conf","gpg.conf-2","gpg.conf"] |
69 | } | 70 | } |
70 | 71 | ||
71 | data InputFile = HomeSec | HomePub | ArgFile FilePath | FileDesc Int | 72 | data InputFile = HomeSec |
73 | | HomePub | ||
74 | | ArgFile FilePath | ||
75 | | FileDesc Int | ||
72 | 76 | ||
73 | type UsageTag = String | 77 | type UsageTag = String |
74 | type Initializer = String | 78 | type Initializer = String |
75 | type PassWordFile = InputFile | 79 | type PassWordFile = InputFile |
76 | 80 | ||
77 | data FileType = KeyRingFile PassWordFile | PEMFile UsageTag | WalletFile | 81 | data FileType = KeyRingFile PassWordFile |
82 | | PEMFile UsageTag | ||
83 | | WalletFile -- (Maybe UsageTag) | ||
78 | 84 | ||
79 | data RefType = ConstRef | MutableRef (Maybe Initializer) | 85 | data RefType = ConstRef | MutableRef (Maybe Initializer) |
80 | 86 | ||
81 | initializer (MutableRef x) = x | 87 | initializer (MutableRef x) = x |
82 | initializer _ = Nothing | 88 | initializer _ = Nothing |
83 | 89 | ||
90 | getUsage (PEMFile usage) = return usage | ||
91 | getUsage _ = mzero | ||
92 | |||
84 | 93 | ||
85 | data KeyRingRuntime = KeyRingRuntime | 94 | data KeyRingRuntime = KeyRingRuntime |
86 | { rtPubring :: FilePath | 95 | { rtPubring :: FilePath |
@@ -247,6 +256,8 @@ data KikiReportAction = | |||
247 | | YieldSecretKeyPacket String | 256 | | YieldSecretKeyPacket String |
248 | | UnableToUpdateExpiredSignature | 257 | | UnableToUpdateExpiredSignature |
249 | | WarnFailedToMakeSignature | 258 | | WarnFailedToMakeSignature |
259 | | FailedExternal Int | ||
260 | | ExternallyGeneratedFile | ||
250 | 261 | ||
251 | data KikiResult a = KikiResult | 262 | data KikiResult a = KikiResult |
252 | { kikiCondition :: KikiCondition a | 263 | { kikiCondition :: KikiCondition a |
@@ -480,6 +491,8 @@ data KeySpec = | |||
480 | | KeyUidMatch String | 491 | | KeyUidMatch String |
481 | deriving Show | 492 | deriving Show |
482 | 493 | ||
494 | -- | Parse a key specification. | ||
495 | -- The first argument is a grip for the default working key. | ||
483 | parseSpec :: String -> String -> (KeySpec,Maybe String) | 496 | parseSpec :: String -> String -> (KeySpec,Maybe String) |
484 | parseSpec grip spec = (topspec,subspec) | 497 | parseSpec grip spec = (topspec,subspec) |
485 | where | 498 | where |
@@ -525,11 +538,8 @@ buildKeyDB secring pubring grip0 keyring = do | |||
525 | let isring (KeyRingFile {}) = True | 538 | let isring (KeyRingFile {}) = True |
526 | isring _ = False | 539 | isring _ = False |
527 | 540 | ||
528 | getUsage (PEMFile usage) = return usage | 541 | iswallet (WalletFile {}) = True |
529 | getUsage _ = mzero | 542 | iswallet _ = False |
530 | |||
531 | iswallet WalletFile = True | ||
532 | iswallet _ = False | ||
533 | 543 | ||
534 | files isring = do | 544 | files isring = do |
535 | (f,(rtyp,ftyp)) <- Map.toList (kFiles keyring) | 545 | (f,(rtyp,ftyp)) <- Map.toList (kFiles keyring) |
@@ -812,7 +822,7 @@ writeWalletKeys :: KeyRingData -> KeyDB -> Maybe Packet -> IO (KikiCondition [(F | |||
812 | writeWalletKeys krd db wk = do | 822 | writeWalletKeys krd db wk = do |
813 | let cs = db `coinKeysOwnedBy` wk | 823 | let cs = db `coinKeysOwnedBy` wk |
814 | -- export wallet keys | 824 | -- export wallet keys |
815 | isMutableWallet (MutableRef {}) WalletFile = True | 825 | isMutableWallet (MutableRef {}) (WalletFile {}) = True |
816 | isMutableWallet _ _ = False | 826 | isMutableWallet _ _ = False |
817 | files pred = do | 827 | files pred = do |
818 | (f,(rtyp,ftyp)) <- Map.toList (kFiles krd) | 828 | (f,(rtyp,ftyp)) <- Map.toList (kFiles krd) |
@@ -853,11 +863,6 @@ writeRingKeys :: KeyRingData -> KeyDB -> Maybe Packet | |||
853 | -> IO (KikiCondition [(FilePath,KikiReportAction)]) | 863 | -> IO (KikiCondition [(FilePath,KikiReportAction)]) |
854 | writeRingKeys krd db wk secring pubring = do | 864 | writeRingKeys krd db wk secring pubring = do |
855 | let ks = Map.elems db | 865 | let ks = Map.elems db |
856 | {- | ||
857 | fs = Map.keys (foldr unionfiles Map.empty ks) | ||
858 | where unionfiles (KeyData p _ _ _) m = | ||
859 | Map.union m (locations p) | ||
860 | -} | ||
861 | isring (KeyRingFile {}) = True | 866 | isring (KeyRingFile {}) = True |
862 | isring _ = False | 867 | isring _ = False |
863 | isMutable (MutableRef {}) = True | 868 | isMutable (MutableRef {}) = True |
@@ -869,9 +874,6 @@ writeRingKeys krd db wk secring pubring = do | |||
869 | return (n,isMutable rtyp) | 874 | return (n,isMutable rtyp) |
870 | fromfile f kd@(KeyData p _ _ _) = | 875 | fromfile f kd@(KeyData p _ _ _) = |
871 | Map.member f $ locations p | 876 | Map.member f $ locations p |
872 | {- maybe (Map.member f $ locations p) | ||
873 | (\pred -> pred kd) | ||
874 | (Map.lookup f $ kImports krd) -} | ||
875 | let s = do | 877 | let s = do |
876 | (f,mutable) <- fs | 878 | (f,mutable) <- fs |
877 | let x = do | 879 | let x = do |
@@ -921,12 +923,31 @@ runKeyRing keyring op = do | |||
921 | if e==0 then return $ Just lk | 923 | if e==0 then return $ Just lk |
922 | else dotlock_destroy lk >> return Nothing | 924 | else dotlock_destroy lk >> return Nothing |
923 | return (v,f) | 925 | return (v,f) |
924 | let (lked, map snd -> failed) = partition (isJust . fst) lks | 926 | let (lked, map snd -> failed_locks) = partition (isJust . fst) lks |
925 | ret = if null failed then KikiSuccess () else FailedToLock failed | 927 | ret <- |
926 | ret <- case functorToEither ret of | 928 | if not $ null failed_locks |
927 | Right {} -> do | 929 | then return $ KikiResult (FailedToLock failed_locks) [] |
928 | bresult <- buildKeyDB secring pubring grip0 keyring -- build db | 930 | else do |
929 | try' bresult $ \((db,grip,wk),report1) -> do | 931 | -- create nonexistent files via external commands |
932 | report_externals <- do | ||
933 | let cmds = do | ||
934 | (f,(rtyp,ftyp)) <- Map.toList (kFiles keyring) | ||
935 | cmd <- maybeToList (initializer rtyp) | ||
936 | (_,subspec) <- fmap (parseSpec "") $ getUsage ftyp | ||
937 | fname <- resolveInputFile secring pubring f | ||
938 | return (fname,maybe "" id subspec,cmd) | ||
939 | forM cmds $ \(fname,usage,cmd) -> do | ||
940 | e <- systemEnv [ ("file",fname) | ||
941 | , ("usage",usage) ] | ||
942 | cmd | ||
943 | case e of | ||
944 | ExitFailure num -> return (fname,FailedExternal num) | ||
945 | ExitSuccess -> return (fname,ExternallyGeneratedFile) | ||
946 | |||
947 | -- merge all keyrings, PEM files, and wallets | ||
948 | bresult <- buildKeyDB secring pubring grip0 keyring | ||
949 | |||
950 | try' bresult $ \((db,grip,wk),report_imports) -> do | ||
930 | a <- return $ op KeyRingRuntime | 951 | a <- return $ op KeyRingRuntime |
931 | { rtPubring = pubring | 952 | { rtPubring = pubring |
932 | , rtSecring = secring | 953 | , rtSecring = secring |
@@ -935,12 +956,20 @@ runKeyRing keyring op = do | |||
935 | , rtGrip = grip | 956 | , rtGrip = grip |
936 | , rtKeyDB = db | 957 | , rtKeyDB = db |
937 | } | 958 | } |
959 | |||
938 | r <- writeWalletKeys keyring db wk | 960 | r <- writeWalletKeys keyring db wk |
939 | try' r $ \report2 -> do | 961 | try' r $ \report_wallets -> do |
962 | |||
940 | r <- writeRingKeys keyring db wk secring pubring | 963 | r <- writeRingKeys keyring db wk secring pubring |
941 | try' r $ \report3 -> do | 964 | try' r $ \report_rings -> do |
942 | return $ KikiResult (KikiSuccess a) (report1 ++ report3) | 965 | |
943 | Left err -> return $ KikiResult err [] | 966 | -- writePEMKeys |
967 | |||
968 | return $ KikiResult (KikiSuccess a) | ||
969 | $ concat [ report_externals | ||
970 | , report_imports | ||
971 | , report_wallets | ||
972 | , report_rings ] | ||
944 | 973 | ||
945 | forM_ lked $ \(Just lk, fname) -> do dotlock_release lk | 974 | forM_ lked $ \(Just lk, fname) -> do dotlock_release lk |
946 | dotlock_destroy lk -- todo: verify we want this | 975 | dotlock_destroy lk -- todo: verify we want this |
diff --git a/ProcessUtils.hs b/ProcessUtils.hs new file mode 100644 index 0000000..7f7928c --- /dev/null +++ b/ProcessUtils.hs | |||
@@ -0,0 +1,44 @@ | |||
1 | module ProcessUtils | ||
2 | ( ExitCode(ExitFailure,ExitSuccess) | ||
3 | , systemEnv | ||
4 | ) where | ||
5 | |||
6 | import GHC.IO.Exception ( ioException, IOErrorType(..) ) | ||
7 | import System.Process | ||
8 | import System.Posix.Signals | ||
9 | import System.Process.Internals (runGenProcess_,defaultSignal) | ||
10 | import System.Environment | ||
11 | import Data.Maybe ( isNothing ) | ||
12 | import System.IO.Error ( mkIOError, ioeSetErrorString ) | ||
13 | import System.Exit ( ExitCode(..) ) | ||
14 | |||
15 | |||
16 | -- | systemEnv | ||
17 | -- This is like System.Process.system except that it lets you set | ||
18 | -- some environment variables. | ||
19 | systemEnv _ "" = | ||
20 | ioException (ioeSetErrorString (mkIOError InvalidArgument "system" Nothing Nothing) "null command") | ||
21 | systemEnv vars cmd = do | ||
22 | env0 <- getEnvironment | ||
23 | let env1 = filter (isNothing . flip lookup vars . fst) env0 | ||
24 | env = vars ++ env1 | ||
25 | syncProcess "system" $ (shell cmd) {env=Just env} | ||
26 | where | ||
27 | -- This is a non-exported function from System.Process | ||
28 | syncProcess fun c = do | ||
29 | -- The POSIX version of system needs to do some manipulation of signal | ||
30 | -- handlers. Since we're going to be synchronously waiting for the child, | ||
31 | -- we want to ignore ^C in the parent, but handle it the default way | ||
32 | -- in the child (using SIG_DFL isn't really correct, it should be the | ||
33 | -- original signal handler, but the GHC RTS will have already set up | ||
34 | -- its own handler and we don't want to use that). | ||
35 | old_int <- installHandler sigINT Ignore Nothing | ||
36 | old_quit <- installHandler sigQUIT Ignore Nothing | ||
37 | (_,_,_,p) <- runGenProcess_ fun c | ||
38 | (Just defaultSignal) (Just defaultSignal) | ||
39 | r <- waitForProcess p | ||
40 | _ <- installHandler sigINT old_int Nothing | ||
41 | _ <- installHandler sigQUIT old_quit Nothing | ||
42 | return r | ||
43 | |||
44 | |||
@@ -853,34 +853,6 @@ show_wip keyspec wkgrip db = do | |||
853 | let nwb = maybe 0x80 CryptoCoins.secretByteFromName $ snd s | 853 | let nwb = maybe 0x80 CryptoCoins.secretByteFromName $ snd s |
854 | putStrLn $ walletImportFormat nwb k | 854 | putStrLn $ walletImportFormat nwb k |
855 | 855 | ||
856 | -- | systemEnv | ||
857 | -- This is like System.Process.system except that it lets you set | ||
858 | -- some environment variables. | ||
859 | systemEnv _ "" = | ||
860 | ioException (ioeSetErrorString (mkIOError InvalidArgument "system" Nothing Nothing) "null command") | ||
861 | systemEnv vars cmd = do | ||
862 | env0 <- getEnvironment | ||
863 | let env1 = filter (isNothing . flip lookup vars . fst) env0 | ||
864 | env = vars ++ env1 | ||
865 | syncProcess "system" $ (shell cmd) {env=Just env} | ||
866 | where | ||
867 | -- This is a non-exported function from System.Process | ||
868 | syncProcess fun c = do | ||
869 | -- The POSIX version of system needs to do some manipulation of signal | ||
870 | -- handlers. Since we're going to be synchronously waiting for the child, | ||
871 | -- we want to ignore ^C in the parent, but handle it the default way | ||
872 | -- in the child (using SIG_DFL isn't really correct, it should be the | ||
873 | -- original signal handler, but the GHC RTS will have already set up | ||
874 | -- its own handler and we don't want to use that). | ||
875 | old_int <- installHandler sigINT Ignore Nothing | ||
876 | old_quit <- installHandler sigQUIT Ignore Nothing | ||
877 | (_,_,_,p) <- runGenProcess_ fun c | ||
878 | (Just defaultSignal) (Just defaultSignal) | ||
879 | r <- waitForProcess p | ||
880 | _ <- installHandler sigINT old_int Nothing | ||
881 | _ <- installHandler sigQUIT old_quit Nothing | ||
882 | return r | ||
883 | |||
884 | doExport doDecrypt (db,use_db) (fname,subspec,ms,cmd) = | 856 | doExport doDecrypt (db,use_db) (fname,subspec,ms,cmd) = |
885 | case ms of | 857 | case ms of |
886 | [_] -> export | 858 | [_] -> export |