diff options
Diffstat (limited to 'kiki.hs')
-rw-r--r-- | kiki.hs | 262 |
1 files changed, 137 insertions, 125 deletions
@@ -53,7 +53,7 @@ import Control.Arrow (first,second) | |||
53 | import Data.Monoid ( (<>) ) | 53 | import Data.Monoid ( (<>) ) |
54 | import Data.Binary.Put | 54 | import Data.Binary.Put |
55 | 55 | ||
56 | import Data.OpenPGP.Util (verify,fingerprint) | 56 | import Data.OpenPGP.Util (verify,fingerprint,generateKey, GenerateKeyParams(..)) |
57 | import ScanningParser | 57 | import ScanningParser |
58 | import PEM | 58 | import PEM |
59 | import DotLock | 59 | import DotLock |
@@ -697,18 +697,8 @@ kiki_usage bExport bImport bSecret cmd = putStr $ | |||
697 | [" --help" | 697 | [" --help" |
698 | ," Gives usage information" | 698 | ," Gives usage information" |
699 | ,"" | 699 | ,"" |
700 | ," --homedir DIR" | 700 | ] ++ documentHomeDir ++ [""] |
701 | ," Where to find the files secring.gpg and pubring.gpg. The" | 701 | ++ documentPassphraseFDFlag bExport bImport bSecret |
702 | ," default location is taken from the environment variable" | ||
703 | ," GNUPGHOME. If this environment variable is not set and no" | ||
704 | ," directory is specified using this option then a hardcoded" | ||
705 | ," default of ~/.gnupg is assumed. " | ||
706 | ,"" | ||
707 | ," WARNING: Confusingly, this is *not* your home directory as" | ||
708 | ," given by the HOME environment variable. The option is named" | ||
709 | ," or rather misnamed in a fashion similar to the gpg option with" | ||
710 | ," exactly the same functionality." | ||
711 | ,""] ++ documentPassphraseFDFlag bExport bImport bSecret | ||
712 | showwk :: [String] | 702 | showwk :: [String] |
713 | showwk = | 703 | showwk = |
714 | [" --show-wk" | 704 | [" --show-wk" |
@@ -769,6 +759,21 @@ kiki_usage bExport bImport bSecret cmd = putStr $ | |||
769 | ," 5E24CD442AA6965D2012E62A905C24185D5379C2" | 759 | ," 5E24CD442AA6965D2012E62A905C24185D5379C2" |
770 | ] | 760 | ] |
771 | 761 | ||
762 | documentHomeDir :: [String] | ||
763 | documentHomeDir = | ||
764 | [" --homedir DIR" | ||
765 | ," Where to find the files secring.gpg and pubring.gpg. The" | ||
766 | ," default location is taken from the environment variable" | ||
767 | ," GNUPGHOME. If this environment variable is not set and no" | ||
768 | ," directory is specified using this option then a hardcoded" | ||
769 | ," default of ~/.gnupg is assumed. " | ||
770 | ,"" | ||
771 | ," WARNING: Confusingly, this is *not* your home directory as" | ||
772 | ," given by the HOME environment variable. The option is named" | ||
773 | ," or rather misnamed in a fashion similar to the gpg option with" | ||
774 | ," exactly the same functionality." | ||
775 | ] | ||
776 | |||
772 | documentPassphraseFDFlag bExport bImport bSecret = | 777 | documentPassphraseFDFlag bExport bImport bSecret = |
773 | if bSecret then | 778 | if bSecret then |
774 | [" --passphrase-fd FD" | 779 | [" --passphrase-fd FD" |
@@ -1043,7 +1048,7 @@ buildStreamInfo rtyp ftyp = StreamInfo { typ = ftyp | |||
1043 | , fill = rtyp | 1048 | , fill = rtyp |
1044 | , spill = KF_All | 1049 | , spill = KF_All |
1045 | , access = AutoAccess | 1050 | , access = AutoAccess |
1046 | , initializer = Nothing | 1051 | , initializer =NoCreate |
1047 | , transforms = [] } | 1052 | , transforms = [] } |
1048 | 1053 | ||
1049 | 1054 | ||
@@ -1100,7 +1105,7 @@ sync bExport bImport bSecret cmdarg args_raw = do | |||
1100 | then DNSPresentation | 1105 | then DNSPresentation |
1101 | else PEMFile | 1106 | else PEMFile |
1102 | , access = if bSecret then Sec else Pub | 1107 | , access = if bSecret then Sec else Pub |
1103 | , initializer = cmd' | 1108 | , initializer = maybe NoCreate External cmd' |
1104 | , transforms = [] | 1109 | , transforms = [] |
1105 | } ) | 1110 | } ) |
1106 | else if isNothing cmd' | 1111 | else if isNothing cmd' |
@@ -1228,7 +1233,7 @@ kiki "show" args = do | |||
1228 | streaminfo = StreamInfo { fill = KF_None | 1233 | streaminfo = StreamInfo { fill = KF_None |
1229 | , typ = KeyRingFile | 1234 | , typ = KeyRingFile |
1230 | , spill = KF_All | 1235 | , spill = KF_All |
1231 | , initializer = Nothing | 1236 | , initializer = NoCreate |
1232 | , access = AutoAccess | 1237 | , access = AutoAccess |
1233 | , transforms = [] | 1238 | , transforms = [] |
1234 | } | 1239 | } |
@@ -1312,7 +1317,7 @@ kiki "merge" args = do | |||
1312 | , typ = KeyRingFile | 1317 | , typ = KeyRingFile |
1313 | , spill = KF_None | 1318 | , spill = KF_None |
1314 | , fill = KF_None | 1319 | , fill = KF_None |
1315 | , initializer = Nothing | 1320 | , initializer = NoCreate |
1316 | , transforms = [] | 1321 | , transforms = [] |
1317 | } | 1322 | } |
1318 | updateFlow fil spil mtch flow = spill' $ fill' $ flow | 1323 | updateFlow fil spil mtch flow = spill' $ fill' $ flow |
@@ -1397,7 +1402,7 @@ kiki "merge" args = do | |||
1397 | Left ("autosign",Just "false")-> doAutosign False flow op | 1402 | Left ("autosign",Just "false")-> doAutosign False flow op |
1398 | Left ("passphrase-fd",Just pass) -> doPassphrase flow op pass | 1403 | Left ("passphrase-fd",Just pass) -> doPassphrase flow op pass |
1399 | Left ("create",Just cmd) -> | 1404 | Left ("create",Just cmd) -> |
1400 | ( flow { initializer = if null cmd then Nothing else Just cmd } | 1405 | ( flow { initializer = if null cmd then NoCreate else External cmd } |
1401 | , op ) | 1406 | , op ) |
1402 | Left ("type",Just "keyring") -> ( flow { typ = KeyRingFile } , op ) | 1407 | Left ("type",Just "keyring") -> ( flow { typ = KeyRingFile } , op ) |
1403 | Left ("type",Just "pem" ) -> ( flow { typ = PEMFile } , op ) | 1408 | Left ("type",Just "pem" ) -> ( flow { typ = PEMFile } , op ) |
@@ -1425,13 +1430,28 @@ kiki "merge" args = do | |||
1425 | Nothing -> error "Valid flow words are: spill,fill,sync,subkeys or match=KEYSPEC" | 1430 | Nothing -> error "Valid flow words are: spill,fill,sync,subkeys or match=KEYSPEC" |
1426 | Left (option,_) -> error $ "Unrecognized option: " ++ option | 1431 | Left (option,_) -> error $ "Unrecognized option: " ++ option |
1427 | 1432 | ||
1428 | kiki "init-key" args | "--help" `elem` args = do | 1433 | kiki "init" args | "--help" `elem` args = do |
1429 | putStr . unlines $ | 1434 | putStr . unlines $ |
1430 | [ "kiki init-key [ --passphrase-fd=FD" | 1435 | [ "kiki init [ --passphrase-fd=FD" |
1431 | , " | --home[=HOMEDIR]" | 1436 | , " | --home[=HOMEDIR]" |
1432 | , " | --chroot=ROOTDIR ] ..."] | 1437 | , " | --chroot=ROOTDIR ] ..." |
1433 | return () | 1438 | , "" |
1434 | kiki "init-key" args = do | 1439 | , "Initialize a GnuPG keyring for use with kiki. After completion, you" |
1440 | , "willl have a GnuPG master key with following specialized subkeys:" | ||
1441 | , "" | ||
1442 | , " tor - freshly generated tor hidden service key" | ||
1443 | , " ipsec - freshly generated VPN key" | ||
1444 | , " ssh-server - possibly read from /etc/ssh/*" | ||
1445 | , " ssh-client - possibly read from /root/.ssh/id_rsa" | ||
1446 | , "" | ||
1447 | , "OPTIONS" | ||
1448 | , "" | ||
1449 | , " --chroot=ROOTDIR" | ||
1450 | , " Use ROOTDIR for input of ssh keys and export files to" | ||
1451 | , " ROOTDIR/var/cache/kiki instead of the current system path." | ||
1452 | , "" | ||
1453 | ] ++ documentHomeDir ++ [""] ++ documentPassphraseFDFlag True True True | ||
1454 | kiki "init" args = do | ||
1435 | me <- getEffectiveUserID | 1455 | me <- getEffectiveUserID |
1436 | {- | 1456 | {- |
1437 | if me/=0 then error "This command requires root." else do | 1457 | if me/=0 then error "This command requires root." else do |
@@ -1460,8 +1480,11 @@ kiki "init-key" args = do | |||
1460 | (home,secring,pubring,mbwk) <- unconditionally $ getHomeDir homespec | 1480 | (home,secring,pubring,mbwk) <- unconditionally $ getHomeDir homespec |
1461 | osHomeDir <- if bUnprivileged then getHomeDirectory else return "/root" | 1481 | osHomeDir <- if bUnprivileged then getHomeDirectory else return "/root" |
1462 | -- putStrLn $ "home = " ++ show (home,secring,pubring,mbwk) | 1482 | -- putStrLn $ "home = " ++ show (home,secring,pubring,mbwk) |
1483 | |||
1484 | -- Generate secring.gpg if it does not exist... | ||
1463 | gotsec <- doesFileExist secring | 1485 | gotsec <- doesFileExist secring |
1464 | when (not gotsec) $ do | 1486 | when (not gotsec) $ do |
1487 | {- ssh-keygen to create master key... | ||
1465 | let mkpath = home ++ "/master-key" | 1488 | let mkpath = home ++ "/master-key" |
1466 | mkdirFor mkpath | 1489 | mkdirFor mkpath |
1467 | e <- systemEnv [ ("file",mkpath) ] (fromJust $ sshkeygen 4096) | 1490 | e <- systemEnv [ ("file",mkpath) ] (fromJust $ sshkeygen 4096) |
@@ -1472,59 +1495,78 @@ kiki "init-key" args = do | |||
1472 | writeInputFileL (InputFileContext secring pubring) | 1495 | writeInputFileL (InputFileContext secring pubring) |
1473 | HomeSec | 1496 | HomeSec |
1474 | ( encode $ Message [mk { is_subkey = False }] ) | 1497 | ( encode $ Message [mk { is_subkey = False }] ) |
1498 | -} | ||
1499 | master <- (\k -> k { is_subkey = False }) <$> generateKey (GenRSA $ 4096 `div` 8 ) | ||
1500 | writeInputFileL (InputFileContext secring pubring) | ||
1501 | HomeSec | ||
1502 | $ encode $ Message [master { is_subkey = False}] | ||
1503 | |||
1475 | gotpub <- doesFileExist pubring | 1504 | gotpub <- doesFileExist pubring |
1476 | when (not gotpub) $ do | 1505 | when (not gotpub) $ do |
1477 | writeInputFileL (InputFileContext secring pubring) | 1506 | writeInputFileL (InputFileContext secring pubring) |
1478 | HomePub | 1507 | HomePub |
1479 | ( encode $ Message [] ) | 1508 | ( encode $ Message [] ) |
1480 | 1509 | ||
1481 | -- TODO: These should be read from a configuration file. | 1510 | -- Old paths.. |
1482 | -- (use SimpleConfig) | 1511 | -- |
1483 | let torpath = fromMaybe "" rootdir ++ "/var/lib/tor/samizdat/private_key" | 1512 | -- Private |
1484 | sshcpath0 = fromMaybe "" rootdir ++ osHomeDir </> ".ssh" </>"id_rsa" | 1513 | -- pem tor /var/lib/tor/samizdat/private_key |
1485 | sshspath0 = fromMaybe "" rootdir ++ "/etc/ssh/ssh_host_rsa_key" | 1514 | -- pem ssh-client %(home)/.ssh/id_rsa |
1486 | ipsecpath0 = fromMaybe "" rootdir ++ "/etc/ipsec.d/private/%(onion).pem" | 1515 | -- pem ssh-server /etc/ssh/ssh_host_rsa_key |
1487 | sshcpathpub0 = fromMaybe "" rootdir ++ osHomeDir </> ".ssh" </> "id_rsa.pub" | 1516 | -- pem ipsec /etc/ipsec.d/private/%(onion).pem |
1488 | sshspathpub0 = fromMaybe "" rootdir ++ "/etc/ssh/ssh_host_rsa_key.pub" | ||
1489 | ipsecpathpub0 = fromMaybe "" rootdir ++ "/etc/ipsec.d/certs/%(onion).pem" | ||
1490 | contactipsec0 = fromMaybe "" rootdir ++ "/etc/ipsec.d/certs/%(onion).pem" | ||
1491 | 1517 | ||
1518 | -- Public | ||
1519 | -- ssh-client %(home)/.ssh/id_rsa.pub | ||
1520 | -- ssh-server /etc/ssh/ssh_host_rsa_key.pub | ||
1521 | -- ipsec /etc/ipsec.d/certs/%(onion).pem | ||
1492 | 1522 | ||
1493 | -- First, we ensure that the tor key exists and is imported | 1523 | -- First, we ensure that the tor key exists and is imported |
1494 | -- so that we know where to put the strongswan key. | 1524 | -- so that we know where to put the strongswan key. |
1495 | let passfd = fmap (FileDesc . read) $ lookup "passphrase-fd" args | 1525 | let passfd = fmap (FileDesc . read) $ lookup "passphrase-fd" args |
1526 | strm = StreamInfo { typ = KeyRingFile | ||
1527 | , fill = KF_None | ||
1528 | , spill = KF_All | ||
1529 | , access = AutoAccess | ||
1530 | , initializer = NoCreate | ||
1531 | , transforms = [] } | ||
1496 | buildStreamInfo rtyp ftyp = StreamInfo { typ = ftyp | 1532 | buildStreamInfo rtyp ftyp = StreamInfo { typ = ftyp |
1497 | , fill = rtyp | 1533 | , fill = rtyp |
1498 | , spill = KF_All | 1534 | , spill = KF_All |
1499 | , access = AutoAccess | 1535 | , access = AutoAccess |
1500 | , initializer = Nothing | 1536 | , initializer = NoCreate |
1501 | , transforms = [] } | 1537 | , transforms = [] } |
1502 | peminfo bits usage = | 1538 | peminfo bits usage = |
1503 | StreamInfo { typ = PEMFile | 1539 | StreamInfo { typ = PEMFile |
1504 | , fill = KF_Match usage | 1540 | , fill = KF_None -- KF_Match usage |
1505 | , spill = KF_Match usage | 1541 | , spill = KF_Match usage |
1506 | , access = Sec | 1542 | , access = Sec |
1507 | , initializer = sshkeygen bits | 1543 | , initializer = Internal (GenRSA $ bits `div` 8) |
1508 | , transforms = [] | 1544 | , transforms = [] |
1509 | } | 1545 | } |
1546 | sshcpath = fromMaybe "" rootdir ++ osHomeDir ++ ".ssh/id_rsa" | ||
1547 | sshspath = fromMaybe "" rootdir ++ "/etc/ssh/ssh_host_rsa_key" | ||
1510 | op = KeyRingOperation | 1548 | op = KeyRingOperation |
1511 | { opFiles = Map.fromList $ | 1549 | { opFiles = Map.fromList $ |
1512 | [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) | 1550 | [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) |
1513 | , ( HomePub, (buildStreamInfo KF_All KeyRingFile) { access = Pub } ) | 1551 | , ( HomePub, (buildStreamInfo KF_All KeyRingFile) { access = Pub } ) |
1514 | , ( ArgFile torpath, peminfo 1024 "tor" ) ] | 1552 | , ( Generate 0 (GenRSA (1024 `div` 8)), strm { spill = KF_Match "tor" }) |
1553 | , ( Generate 1 (GenRSA (1024 `div` 8)), strm { spill = KF_Match "ipsec" }) | ||
1554 | , ( ArgFile sshcpath, (peminfo 2048 "ssh-client") ) | ||
1555 | , ( ArgFile sshspath, (peminfo 2048 "ssh-server") ) | ||
1556 | ] | ||
1515 | , opPassphrases = do pfd <- maybeToList passfd | 1557 | , opPassphrases = do pfd <- maybeToList passfd |
1516 | return $ PassphraseSpec Nothing Nothing pfd | 1558 | return $ PassphraseSpec Nothing Nothing pfd |
1517 | , opHome = homespec | 1559 | , opHome = homespec |
1518 | , opTransforms = [] | 1560 | , opTransforms = [] |
1519 | } | 1561 | } |
1520 | doNothing = return () | 1562 | -- doNothing = return () |
1521 | nop = KeyRingOperation | 1563 | nop = KeyRingOperation |
1522 | { opFiles = Map.empty | 1564 | { opFiles = Map.empty |
1523 | , opPassphrases = do pfd <- maybeToList passfd | 1565 | , opPassphrases = do pfd <- maybeToList passfd |
1524 | return $ PassphraseSpec Nothing Nothing pfd | 1566 | return $ PassphraseSpec Nothing Nothing pfd |
1525 | , opHome=homespec, opTransforms = [] | 1567 | , opHome=homespec, opTransforms = [] |
1526 | } | 1568 | } |
1527 | if bUnprivileged then doNothing else mkdirFor torpath | 1569 | -- if bUnprivileged then doNothing else mkdirFor torpath |
1528 | KikiResult rt report <- runKeyRing (if bUnprivileged then nop else op) | 1570 | KikiResult rt report <- runKeyRing (if bUnprivileged then nop else op) |
1529 | forM_ report $ \(fname,act) -> do | 1571 | forM_ report $ \(fname,act) -> do |
1530 | putStrLn $ fname ++ ": " ++ reportString act | 1572 | putStrLn $ fname ++ ": " ++ reportString act |
@@ -1533,87 +1575,7 @@ kiki "init-key" args = do | |||
1533 | error "Operation requires correct passphrase. (Hint: Use --passphrase-fd=0 to input it on stdin.)" | 1575 | error "Operation requires correct passphrase. (Hint: Use --passphrase-fd=0 to input it on stdin.)" |
1534 | _ -> unconditionally $ return rt | 1576 | _ -> unconditionally $ return rt |
1535 | 1577 | ||
1536 | -- Now import, export, or generate the remaining secret keys. | 1578 | when (not bUnprivileged) $ refreshCache rt rootdir |
1537 | let oname' = do wk <- rtWorkingKey rt | ||
1538 | onionNameForContact (keykey wk) (rtKeyDB rt) | ||
1539 | if (oname' == Nothing) && (not bUnprivileged) then error "Missing tor key" else do | ||
1540 | let oname = fromMaybe "" oname' | ||
1541 | let [ sshcpath, sshcpathpub ] = {- map (interp (Map.fromList [("onion",oname)]))-} [ sshcpath0, sshcpathpub0 ] | ||
1542 | [ sshspath , ipsecpath ] = map (interp (Map.fromList [("onion",oname)])) [ sshspath0, ipsecpath0 ] | ||
1543 | [ sshspathpub, ipsecpathpub ] | ||
1544 | = map (interp (Map.fromList [("onion",oname)])) | ||
1545 | [ sshspathpub0, ipsecpathpub0 ] | ||
1546 | let opPriv = op | ||
1547 | { opFiles = Map.fromList $ | ||
1548 | [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) | ||
1549 | , ( HomePub, (buildStreamInfo KF_All KeyRingFile) { access = Pub } ) | ||
1550 | , ( ArgFile ipsecpath, peminfo 1024 "strongswan" ) | ||
1551 | , ( ArgFile sshcpath, peminfo 2048 "ssh-client" ) | ||
1552 | , ( ArgFile sshspath, peminfo 2048 "ssh-server" ) ] | ||
1553 | , opPassphrases = [ PassphraseMemoizer (rtPassphrases rt) ] | ||
1554 | } | ||
1555 | opUnPriv = op | ||
1556 | { opFiles = Map.fromList $ | ||
1557 | [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) | ||
1558 | , ( HomePub, (buildStreamInfo KF_All KeyRingFile) { access = Pub } ) | ||
1559 | , ( ArgFile sshcpath, peminfo 2048 "ssh-client" ) | ||
1560 | ] | ||
1561 | , opPassphrases = [ PassphraseMemoizer (rtPassphrases rt) ] | ||
1562 | } | ||
1563 | mapM_ mkdirFor $ [sshcpath,sshcpathpub] ++ if not bUnprivileged then [sshspath,ipsecpath,sshspathpub,ipsecpathpub] else [] | ||
1564 | KikiResult rt report <- runKeyRing (if bUnprivileged then opUnPriv else opPriv) | ||
1565 | forM_ report $ \(fname,act) -> do | ||
1566 | putStrLn $ fname ++ ": " ++ reportString act | ||
1567 | rt <- case rt of | ||
1568 | BadPassphrase -> | ||
1569 | error "Operation requires correct passphrase. (Hint: Use --passphrase-fd=0 to input it on stdin.)" | ||
1570 | _ -> unconditionally $ return rt | ||
1571 | |||
1572 | -- Finally, export public keys if they do not exist. | ||
1573 | let writeFileWARNING fname bs = do | ||
1574 | --TODO | ||
1575 | hPutStrLn stderr $ fname ++ ": DID NOT CHECK TRUST (TODO)" | ||
1576 | writeFile fname bs | ||
1577 | flip (maybe $ warn "missing working key?") (rtGrip rt) $ \grip -> do | ||
1578 | gotc <- doesFileExist (sshcpathpub) | ||
1579 | when (not gotc) $ do | ||
1580 | either warn (writeFile sshcpathpub) | ||
1581 | $ show_ssh' "ssh-client" grip (rtKeyDB rt) | ||
1582 | if (not bUnprivileged) | ||
1583 | then do | ||
1584 | goth <- doesFileExist (sshspathpub) | ||
1585 | when (not goth) $ do | ||
1586 | either warn (writeFile $ sshspathpub) | ||
1587 | $ show_ssh' "ssh-host" grip (rtKeyDB rt) | ||
1588 | goti <- doesFileExist (ipsecpathpub) | ||
1589 | when (not goti) $ do | ||
1590 | either warn (writeFile $ ipsecpathpub) | ||
1591 | $ show_pem' "strongswan" grip (rtKeyDB rt) pemFromPacket | ||
1592 | else return () | ||
1593 | |||
1594 | |||
1595 | let cs = filter notme (Map.elems $ rtKeyDB rt) | ||
1596 | kk = keykey (fromJust $ rtWorkingKey rt) | ||
1597 | notme kd = keykey (keyPacket kd) /= kk | ||
1598 | |||
1599 | installConctact kd = do | ||
1600 | -- The getHostnames command requires a valid cross-signed tor key | ||
1601 | -- for each onion name returned in (_,(ns,_)). | ||
1602 | let (_,(ns,_)) = getHostnames kd | ||
1603 | contactname = fmap Char8.unpack $ listToMaybe ns -- only first onion name. | ||
1604 | flip (maybe $ return ()) contactname $ \contactname -> do | ||
1605 | |||
1606 | let cpath = interp (Map.singleton "onion" contactname) contactipsec0 | ||
1607 | their_master = packet $ keyMappedPacket kd | ||
1608 | -- We find all cross-certified ipsec keys for the given cross-certified onion name. | ||
1609 | ipsecs = sortOn (Down . timestamp) | ||
1610 | $ getCrossSignedSubkeys their_master (keySubKeys kd) "strongswan" | ||
1611 | forM_ (take 1 ipsecs) $ \k -> do | ||
1612 | goti <- doesFileExist (cpath) | ||
1613 | when (not goti) $ do | ||
1614 | either warn (writeFile cpath) $ pemFromPacket k | ||
1615 | |||
1616 | mapM_ installConctact cs | ||
1617 | 1579 | ||
1618 | kiki "delete" args | "--help" `elem` args = do | 1580 | kiki "delete" args | "--help" `elem` args = do |
1619 | putStr . unlines $ | 1581 | putStr . unlines $ |
@@ -1690,10 +1652,59 @@ kiki "tar" args = do | |||
1690 | ["-A":_] -> putStrLn "unimplemented." -- import tar file? | 1652 | ["-A":_] -> putStrLn "unimplemented." -- import tar file? |
1691 | _ -> kiki "tar" ["--help"] | 1653 | _ -> kiki "tar" ["--help"] |
1692 | 1654 | ||
1655 | refreshCache rt rootdir = do | ||
1656 | |||
1657 | let mkpath pth = fromMaybe "" rootdir ++ "/var/cache/kiki/"++pth | ||
1658 | |||
1659 | write f bs = do | ||
1660 | createDirectoryIfMissing True $ takeDirectory f | ||
1661 | writeFile f bs | ||
1662 | |||
1663 | let oname' = do wk <- rtWorkingKey rt | ||
1664 | -- XXX unnecessary signature check | ||
1665 | onionNameForContact (keykey wk) (rtKeyDB rt) | ||
1666 | bUnprivileged = False -- TODO | ||
1667 | if (oname' == Nothing) && (not bUnprivileged) then error "Missing tor key" else do | ||
1668 | let oname = fromMaybe "" oname' | ||
1669 | -- sshcpathpub0 = fromMaybe "" rootdir ++ osHomeDir </> ".ssh" </> "id_rsa.pub" | ||
1670 | -- sshspathpub0 = fromMaybe "" rootdir ++ "/etc/ssh/ssh_host_rsa_key.pub" | ||
1671 | -- contactipsec0 = fromMaybe "" rootdir ++ "/etc/ipsec.d/certs/%(onion).pem" | ||
1672 | |||
1673 | -- Finally, export public keys if they do not exist. | ||
1674 | flip (maybe $ warn "missing working key?") (rtGrip rt) $ \grip -> do | ||
1675 | either warn (write $ mkpath "root/.ssh/id_rsa.pub") | ||
1676 | $ show_ssh' "ssh-client" grip (rtKeyDB rt) | ||
1677 | either warn (write $ mkpath "ssh_host_rsa_key.pub") | ||
1678 | $ show_ssh' "ssh-server" grip (rtKeyDB rt) | ||
1679 | either warn (write $ mkpath "ipsec.d/certs/" ++ oname++".pem") | ||
1680 | $ show_pem' "ipsec" grip (rtKeyDB rt) pemFromPacket | ||
1681 | |||
1682 | let cs = filter notme (Map.elems $ rtKeyDB rt) | ||
1683 | kk = keykey (fromJust $ rtWorkingKey rt) | ||
1684 | notme kd = keykey (keyPacket kd) /= kk | ||
1685 | |||
1686 | installConctact kd = do | ||
1687 | -- The getHostnames command requires a valid cross-signed tor key | ||
1688 | -- for each onion name returned in (_,(ns,_)). | ||
1689 | let (_,(ns,_)) = getHostnames kd | ||
1690 | contactname = fmap Char8.unpack $ listToMaybe ns -- only first onion name. | ||
1691 | flip (maybe $ return ()) contactname $ \contactname -> do | ||
1692 | |||
1693 | let cpath = interp (Map.singleton "onion" contactname) "ipsec.d/certs/%(onion).pem" | ||
1694 | their_master = packet $ keyMappedPacket kd | ||
1695 | -- We find all cross-certified ipsec keys for the given cross-certified onion name. | ||
1696 | ipsecs = sortOn (Down . timestamp) | ||
1697 | $ getCrossSignedSubkeys their_master (keySubKeys kd) "ipsec" | ||
1698 | forM_ (take 1 ipsecs) $ \k -> do | ||
1699 | either warn (write $ mkpath cpath) $ pemFromPacket k | ||
1700 | |||
1701 | mapM_ installConctact cs | ||
1702 | |||
1703 | |||
1693 | tarContent rt spec pubpem knownhosts secpem = ipsecs ++ sshs ++ secrets "root" | 1704 | tarContent rt spec pubpem knownhosts secpem = ipsecs ++ sshs ++ secrets "root" |
1694 | where | 1705 | where |
1695 | ipsecs = do | 1706 | ipsecs = do |
1696 | (kk,ipsec,sigs) <- selectPublicKeyAndSigs (KeyUidMatch "",Just "strongswan") (rtKeyDB rt) | 1707 | (kk,ipsec,sigs) <- selectPublicKeyAndSigs (KeyUidMatch "",Just "ipsec") (rtKeyDB rt) |
1697 | let kd = (rtKeyDB rt Map.! kk) | 1708 | let kd = (rtKeyDB rt Map.! kk) |
1698 | k = packet $ keyMappedPacket kd | 1709 | k = packet $ keyMappedPacket kd |
1699 | (addr,(onames,ns)) = getHostnames kd | 1710 | (addr,(onames,ns)) = getHostnames kd |
@@ -1729,7 +1740,7 @@ tarContent rt spec pubpem knownhosts secpem = ipsecs ++ sshs ++ secrets "root" | |||
1729 | return $ spem (dir $ homedir ++ "/.ssh/" ++ sshkeyname k) k | 1740 | return $ spem (dir $ homedir ++ "/.ssh/" ++ sshkeyname k) k |
1730 | sshsvr = spem (dir "etc/ssh/ssh_host_rsa_key") <$> lookupSecret "ssh-host" kd | 1741 | sshsvr = spem (dir "etc/ssh/ssh_host_rsa_key") <$> lookupSecret "ssh-host" kd |
1731 | ipseckey = do | 1742 | ipseckey = do |
1732 | k <- lookupSecret "strongswan" kd | 1743 | k <- lookupSecret "ipsec" kd |
1733 | oname <- fst . snd $ getHostnames kd | 1744 | oname <- fst . snd $ getHostnames kd |
1734 | return $ spem (dir $ "etc/ipsec.d/private/"++Char8.unpack oname++".pem") k | 1745 | return $ spem (dir $ "etc/ipsec.d/private/"++Char8.unpack oname++".pem") k |
1735 | torkey ++ sshcli ++ sshsvr ++ ipseckey | 1746 | torkey ++ sshcli ++ sshsvr ++ ipseckey |
@@ -1800,7 +1811,7 @@ minimalOp cap = op | |||
1800 | streaminfo = StreamInfo { fill = KF_None | 1811 | streaminfo = StreamInfo { fill = KF_None |
1801 | , typ = KeyRingFile | 1812 | , typ = KeyRingFile |
1802 | , spill = KF_All | 1813 | , spill = KF_All |
1803 | , initializer = Nothing | 1814 | , initializer = NoCreate |
1804 | , access = AutoAccess | 1815 | , access = AutoAccess |
1805 | , transforms = [] | 1816 | , transforms = [] |
1806 | } | 1817 | } |
@@ -1849,7 +1860,8 @@ commands = | |||
1849 | , ( "export-secret", "export (both public and secret) information into your keyring" ) | 1860 | , ( "export-secret", "export (both public and secret) information into your keyring" ) |
1850 | , ( "export-public", "import (public) information into your keyring" ) | 1861 | , ( "export-public", "import (public) information into your keyring" ) |
1851 | , ( "merge", "low level import/export operation" ) | 1862 | , ( "merge", "low level import/export operation" ) |
1852 | , ( "init-key", "initialize the samizdat key ring") | 1863 | -- , ( "init-key", "initialize the samizdat key ring") |
1864 | , ( "init", "Initialize kiki") | ||
1853 | , ( "delete", "Delete a subkey and its associated signatures" ) | 1865 | , ( "delete", "Delete a subkey and its associated signatures" ) |
1854 | , ( "tar", "import or export system key files in tar format" ) | 1866 | , ( "tar", "import or export system key files in tar format" ) |
1855 | ] | 1867 | ] |