5 import Control.Monad.Trans
10 import qualified Database.HDBC as RAW
11 import qualified Database.HDBC.Sqlite3 as RAW
12 import Database.HaskellDB.DBSpec
13 import Database.HaskellDB.HDBC.SQLite3
16 import OpenSSL.EVP.PKey
17 import OpenSSL.EVP.Verify
20 import OpenSSL.X509 as X509
21 import OpenSSL.X509.Request as Req
22 import System.Console.Haskeline
23 import System.Console.ParseArgs
24 import System.Directory
25 import System.FilePath
27 import System.Posix.Files
28 import System.Posix.Uname
37 deriving (Show, Eq, Ord)
39 arginfo :: [Arg Options]
43 , argName = Just "help"
45 , argDesc = "print this help."
50 , argName = Just "localstatedir"
51 -- LOCALSTATEDIR will be replaced by CPP
52 , argData = argDataDefaulted "DIR" ArgtypeString LOCALSTATEDIR
53 , argDesc = "specify the local state directory (def: " ++ LOCALSTATEDIR ++ ")."
58 , argName = Just "dns-port"
59 , argData = argDataDefaulted "PORT" ArgtypeString "53"
60 , argDesc = "port to listen for DNS packets (default: 53)."
65 , argName = Just "https-port"
66 , argData = argDataDefaulted "PORT" ArgtypeString "443"
67 , argDesc = "port to listen for HTTPS connections (def: 443)."
70 argIndex = AllowTransfer
72 , argName = Just "allow-transfer"
74 , argDesc = "accept AXFR requests."
80 , argData = argDataDefaulted "COMMAND" ArgtypeString "daemon"
81 , argDesc = "command to the server (default: \"daemon\")."
87 do m <- parseArgsIO ArgsComplete arginfo
91 let lsdir = getRequiredArg m LSDir
92 (key, cert) <- ensureWeHaveKeypair lsdir
94 case getRequiredArg m Command of
99 ensureWeHaveKeypair :: FilePath -> IO (SomeKeyPair, X509)
100 ensureWeHaveKeypair lsdir
101 = do let keyFile = lsdir </> "server.key"
102 certFile = lsdir </> "server.cert"
103 keyExi <- doesFileExist keyFile
104 cerExi <- doesFileExist certFile
105 if keyExi && cerExi then
106 do key <- flip readPrivateKey PwNone =<< readFile keyFile
107 cert <- readX509 =<< readFile certFile
110 do fqdn <- runInputT defaultSettings $
111 do defaultFqdn <- liftM uNodeName $ liftIO uname
112 fqdn <- getInputLine ("What's your FQDN? (default: " ++ defaultFqdn ++ "): ")
114 Just s@(_:_) -> return s
115 _ -> return defaultFqdn
117 putStrLn "Generating RSA keypair..."
118 key <- generateRSAKey 1024 3 Nothing
119 withFile keyFile WriteMode $ \ h ->
120 writePKCS8PrivateKey key Nothing >>= hPutStr h
121 setFileMode keyFile ownerReadMode
122 putStrLn ("Wrote " ++ keyFile)
125 X509.setVersion cert 2
126 X509.setSerialNumber cert 1
127 X509.setIssuerName cert [("CN", fqdn)]
128 X509.setSubjectName cert [("CN", fqdn)]
129 X509.setNotBefore cert =<< liftM (addUTCTime (-1)) getCurrentTime
130 X509.setNotAfter cert =<< liftM (addUTCTime (20 * 365 * 24 * 60 * 60)) getCurrentTime
131 X509.setPublicKey cert key
132 X509.signX509 cert key Nothing
133 withFile certFile WriteMode $ \ h ->
134 writeX509 cert >>= hPutStr h
135 putStrLn ("Wrote " ++ certFile)
137 return (fromKeyPair key, cert)
139 signReq :: KeyPair k => k -> X509 -> IO ()
141 = runInputT (setComplete completeFilename defaultSettings) $
142 do file <- getInputLine "Which X.509 request do you want to sign?: "
146 do req <- readX509Req =<< readFile file'
147 key <- Req.getPublicKey req
148 vst <- verifyX509Req req key
149 unless (vst == VerifySuccess)
150 $ fail "Invalid X.509 request"
152 cert <- makeX509FromReq req sCert
153 setSerialNumber cert 1 -- FIXME: This nasty behavior makes revocation-list unusable.
154 setNotBefore cert =<< liftM (addUTCTime (-1)) getCurrentTime
155 setNotAfter cert =<< liftM (addUTCTime (20 * 365 * 24 * 60 * 60)) getCurrentTime
156 signX509 cert sKey Nothing
158 let certFile = file' `replaceExtension` ".cert"
159 withFile certFile WriteMode $ \ h ->
160 writeX509 cert >>= hPutStr h
161 putStrLn ("Wrote " ++ certFile)
163 Just (_, fqdn) <- liftM (find ((== "CN") . fst)) $ X509.getSubjectName cert False
164 putStrLn ("Now send " ++ certFile ++ " back to " ++ fqdn ++ ".")
168 main = do sqliteConnect "ddns.db" $ \ db ->
169 dbSpecToDatabase db dbinfo
171 rawCon <- RAW.connectSqlite3 "ddns.db"
172 mapM_ (\ sql -> RAW.run rawCon sql []) indexDeclarations
174 RAW.disconnect rawCon