X-Git-Url: https://git.hcoop.net/hcoop/domtool2.git/blobdiff_plain/d351d679283a797c98f5f65d18aa757c18e56305..e1b99e23f8d30efc7842ee006e0ff3ef0347b7df:/src/main.sml diff --git a/src/main.sml b/src/main.sml index 05bce83..a8cf180 100644 --- a/src/main.sml +++ b/src/main.sml @@ -1,5 +1,5 @@ (* HCoop Domtool (http://hcoop.sourceforge.net/) - * Copyright (c) 2006, Adam Chlipala + * Copyright (c) 2006-2007, Adam Chlipala * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -170,19 +170,38 @@ val dispatcher = val self = "localhost:" ^ Int.toString Config.slavePort +fun context x = + (OpenSSL.context false x) + handle e as OpenSSL.OpenSSL _ => + (print "Couldn't find your certificate.\nYou probably haven't been given any Domtool privileges.\n"; + raise e) + +fun setupUser () = + let + val user = + case Posix.ProcEnv.getenv "DOMTOOL_USER" of + NONE => + let + val uid = Posix.ProcEnv.getuid () + in + Posix.SysDB.Passwd.name (Posix.SysDB.getpwuid uid) + end + | SOME user => user + in + Acl.read Config.aclFile; + Domain.setUser user; + user + end + fun requestContext f = let - val uid = Posix.ProcEnv.getuid () - val user = Posix.SysDB.Passwd.name (Posix.SysDB.getpwuid uid) - - val () = Acl.read Config.aclFile - val () = Domain.setUser user + val user = setupUser () val () = f () - val context = OpenSSL.context (Config.certDir ^ "/" ^ user ^ ".pem", - Config.keyDir ^ "/" ^ user ^ "/key.pem", - Config.trustStore) + val context = context (Config.certDir ^ "/" ^ user ^ ".pem", + Config.keyDir ^ "/" ^ user ^ "/key.pem", + Config.trustStore) in (user, context) end @@ -229,6 +248,14 @@ fun request fname = fun requestDir dname = let + val _ = if Posix.FileSys.access (dname, []) then + () + else + (print ("Can't access " ^ dname ^ ".\n"); + print "Did you mean to run domtool on a specific file, instead of asking for all\n"; + print "files in your ~/domtool directory?\n"; + OS.Process.exit OS.Process.failure) + val _ = ErrorMsg.reset () val (user, bio) = requestBio (fn () => checkDir dname) @@ -691,8 +718,97 @@ fun requestFtp {node, uname} = before OpenSSL.close bio end +fun requestTrustedPath {node, uname} = + let + val (user, context) = requestContext (fn () => ()) + val bio = OpenSSL.connect (context, if node = Config.masterNode then + dispatcher + else + Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort) + + val _ = Msg.send (bio, MsgQuery (QTrustedPath uname)) + + fun loop () = + case Msg.recv bio of + NONE => (print "Server closed connection unexpectedly.\n"; + OS.Process.failure) + | SOME m => + case m of + MsgYes => (print "User has trusted path restriction.\n"; + OS.Process.success) + | MsgNo => (print "User does not have trusted path restriction.\n"; + OS.Process.failure) + | MsgError s => (print ("Trusted path query failed: " ^ s ^ "\n"); + OS.Process.failure) + | _ => (print "Unexpected server reply.\n"; + OS.Process.failure) + in + loop () + before OpenSSL.close bio + end + +fun requestSocketPerm {node, uname} = + let + val (user, context) = requestContext (fn () => ()) + val bio = OpenSSL.connect (context, if node = Config.masterNode then + dispatcher + else + Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort) + + val _ = Msg.send (bio, MsgQuery (QSocket uname)) + + fun loop () = + case Msg.recv bio of + NONE => (print "Server closed connection unexpectedly.\n"; + OS.Process.failure) + | SOME m => + case m of + MsgSocket p => (case p of + Any => print "Any\n" + | Client => print "Client\n" + | Server => print "Server\n" + | Nada => print "Nada\n"; + OS.Process.success) + | MsgError s => (print ("Socket permission query failed: " ^ s ^ "\n"); + OS.Process.failure) + | _ => (print "Unexpected server reply.\n"; + OS.Process.failure) + in + loop () + before OpenSSL.close bio + end + +fun requestFirewall {node, uname} = + let + val (user, context) = requestContext (fn () => ()) + val bio = OpenSSL.connect (context, if node = Config.masterNode then + dispatcher + else + Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort) + + val _ = Msg.send (bio, MsgQuery (QFirewall uname)) + + fun loop () = + case Msg.recv bio of + NONE => (print "Server closed connection unexpectedly.\n"; + OS.Process.failure) + | SOME m => + case m of + MsgFirewall ls => (app (fn s => (print s; print "\n")) ls; + OS.Process.success) + | MsgError s => (print ("Firewall query failed: " ^ s ^ "\n"); + OS.Process.failure) + | _ => (print "Unexpected server reply.\n"; + OS.Process.failure) + in + loop () + before OpenSSL.close bio + end + fun regenerate context = let + val _ = ErrorMsg.reset () + val b = basis () val () = Tycheck.disallowExterns () @@ -746,13 +862,16 @@ fun regenerate context = val (_, files) = Order.order (SOME b) files in if !ErrorMsg.anyErrors then - print ("User " ^ user ^ "'s configuration has errors!\n") + (ErrorMsg.reset (); + print ("User " ^ user ^ "'s configuration has errors!\n")) else app eval' files end handle IO.Io _ => () | OS.SysErr (s, _) => print ("System error processing user " ^ user ^ ": " ^ s ^ "\n") - | ErrorMsg.Error => print ("User " ^ user ^ " had a compilation error.\n") + | ErrorMsg.Error => (ErrorMsg.reset (); + print ("User " ^ user ^ " had a compilation error.\n")) + | _ => print "Unknown exception during regeneration!\n" in app contactNode Config.nodeIps; Env.pre (); @@ -779,20 +898,26 @@ fun answerQuery q = QApt pkg => if Apt.installed pkg then MsgYes else MsgNo | QCron user => if Cron.allowed user then MsgYes else MsgNo | QFtp user => if Ftp.allowed user then MsgYes else MsgNo + | QTrustedPath user => if TrustedPath.query user then MsgYes else MsgNo + | QSocket user => MsgSocket (SocketPerm.query user) + | QFirewall user => MsgFirewall (Firewall.query user) fun describeQuery q = case q of QApt pkg => "Requested installation status of package " ^ pkg | QCron user => "Asked about cron permissions for user " ^ user | QFtp user => "Asked about FTP permissions for user " ^ user + | QTrustedPath user => "Asked about trusted path settings for user " ^ user + | QSocket user => "Asked about socket permissions for user " ^ user + | QFirewall user => "Asked about firewall rules for user " ^ user fun service () = let val () = Acl.read Config.aclFile - val context = OpenSSL.context (Config.serverCert, - Config.serverKey, - Config.trustStore) + val context = context (Config.serverCert, + Config.serverKey, + Config.trustStore) val _ = Domain.set_context context val sock = OpenSSL.listen (context, Config.dispatcherPort) @@ -1152,6 +1277,18 @@ fun service () = OpenSSL.close bio handle OpenSSL.OpenSSL _ => (); loop ()) + | IO.Io {name, function, cause} => + (print ("IO error: " ^ function ^ " for " ^ name ^ "\n"); + app (fn x => print (x ^ "\n")) (SMLofNJ.exnHistory cause); + OpenSSL.close bio + handle OpenSSL.OpenSSL _ => (); + loop ()) + | e => + (print "Unknown exception in main loop!\n"; + app (fn x => print (x ^ "\n")) (SMLofNJ.exnHistory e); + OpenSSL.close bio + handle OpenSSL.OpenSSL _ => (); + loop ()) in print ("Domtool dispatcher starting up at " ^ now () ^ "\n"); print "Listening for connections....\n"; @@ -1163,9 +1300,9 @@ fun slave () = let val host = Slave.hostname () - val context = OpenSSL.context (Config.certDir ^ "/" ^ host ^ ".pem", - Config.keyDir ^ "/" ^ host ^ "/key.pem", - Config.trustStore) + val context = context (Config.certDir ^ "/" ^ host ^ ".pem", + Config.keyDir ^ "/" ^ host ^ "/key.pem", + Config.trustStore) val sock = OpenSSL.listen (context, Config.slavePort) @@ -1218,8 +1355,9 @@ fun slave () = OpenSSL.close bio handle OpenSSL.OpenSSL _ => (); loop ()) - | OS.SysErr (s, _) => - (print ("System error: "^ s ^ "\n"); + | e as OS.SysErr (s, _) => + (app (fn s => print (s ^ "\n")) (SMLofNJ.exnHistory e); + print ("System error: "^ s ^ "\n"); OpenSSL.close bio handle OpenSSL.OpenSSL _ => (); loop ())