| ["cron", node, uname] => OS.Process.exit (Main.requestCron {node = node, uname = uname})
| ["ftp", node, uname] => OS.Process.exit (Main.requestFtp {node = node, uname = uname})
| ["tpe", node, uname] => OS.Process.exit (Main.requestTrustedPath {node = node, uname = uname})
+ | ["sockperm", node, uname] => OS.Process.exit (Main.requestSocketPerm {node = node, uname = uname})
| _ => print "Invalid command-line arguments\n"
val requestCron : {node : string, uname : string} -> OS.Process.status
val requestFtp : {node : string, uname : string} -> OS.Process.status
val requestTrustedPath : {node : string, uname : string} -> OS.Process.status
+ val requestSocketPerm : {node : string, uname : string} -> OS.Process.status
end
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 regenerate context =
let
val b = basis ()
| 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)
fun describeQuery q =
case q of
| 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
fun service () =
let
| SOME 1 => SOME true
| _ => NONE
+fun sendSockPerm (bio, p) =
+ case p of
+ Any => OpenSSL.writeInt (bio, 0)
+ | Client => OpenSSL.writeInt (bio, 1)
+ | Server => OpenSSL.writeInt (bio, 2)
+ | Nada => OpenSSL.writeInt (bio, 3)
+
+fun recvSockPerm bio =
+ case OpenSSL.readInt bio of
+ SOME 0 => SOME Any
+ | SOME 1 => SOME Client
+ | SOME 2 => SOME Server
+ | SOME 3 => SOME Nada
+ | _ => NONE
+
fun sendQuery (bio, q) =
case q of
QApt s => (OpenSSL.writeInt (bio, 0);
OpenSSL.writeString (bio, s))
| QTrustedPath s => (OpenSSL.writeInt (bio, 3);
OpenSSL.writeString (bio, s))
+ | QSocket s => (OpenSSL.writeInt (bio, 4);
+ OpenSSL.writeString (bio, s))
fun recvQuery bio =
case OpenSSL.readInt bio of
| 1 => Option.map QCron (OpenSSL.readString bio)
| 2 => Option.map QFtp (OpenSSL.readString bio)
| 3 => Option.map QTrustedPath (OpenSSL.readString bio)
+ | 4 => Option.map QSocket (OpenSSL.readString bio)
| _ => NONE)
| NONE => NONE
| MsgNo => OpenSSL.writeInt (bio, 31)
| MsgQuery q => (OpenSSL.writeInt (bio, 32);
sendQuery (bio, q))
+ | MsgSocket p => (OpenSSL.writeInt (bio, 33);
+ sendSockPerm (bio, p))
fun checkIt v =
case v of
| 30 => SOME MsgYes
| 31 => SOME MsgNo
| 32 => Option.map MsgQuery (recvQuery bio)
+ | 33 => Option.map MsgSocket (recvSockPerm bio)
| _ => NONE)
end
structure MsgTypes = struct
+datatype socket_permission =
+ Any
+ | Client
+ | Server
+ | Nada
+
datatype query =
QApt of string
(* Is this apt package installed? *)
(* Is this user allowed to use FTP? *)
| QTrustedPath of string
(* Is this user restricted to trusted-path executables? *)
+ | QSocket of string
+ (* What socket permissions does this user have? *)
datatype msg =
MsgOk
(* Answers to boolean queries *)
| MsgQuery of query
(* Ask for host-specific information *)
+ | MsgSocket of socket_permission
+ (* Answer to a QSocket query *)
end
--- /dev/null
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *)
+
+(* Socket permission settings querying *)
+
+signature SOCKET_PERM = sig
+
+ val query : string -> MsgTypes.socket_permission
+ (* What socket permissions does the named user have on this host? *)
+
+end
--- /dev/null
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *)
+
+(* Trusted path settings querying *)
+
+structure SocketPerm :> SOCKET_PERM = struct
+
+open MsgTypes
+
+fun query uname =
+ if Slave.inGroup {user = uname, group = "no-sockets"} then
+ Nada
+ else if Slave.inGroup {user = uname, group = "no-cli-sockets"} then
+ if Slave.inGroup {user = uname, group = "no-serv-sockets"} then
+ Nada
+ else
+ Server
+ else if Slave.inGroup {user = uname, group = "no-serv-sockets"} then
+ Client
+ else
+ Any
+
+end
structure TrustedPath :> TRUSTED_PATH = struct
-fun query uname = List.exists (fn x => x = uname)
- (Posix.SysDB.Group.members (Posix.SysDB.getgrnam "only-tpe"))
- handle OS.SysErr _ => false
+fun query uname = Slave.inGroup {group = "only-tpe", user = uname}
end
val lineInFile : string -> string -> bool
(* Is there a line in the file (first arg) that matches that given? *)
+ val inGroup : {user : string, group : string} -> bool
+ (* Check membership in a UNIX group. *)
+
end
before TextIO.closeIn inf
end handle IO.Io _ => false
+fun inGroup {user, group} =
+ List.exists (fn x => x = user)
+ (Posix.SysDB.Group.members (Posix.SysDB.getgrnam group))
+ handle OS.SysErr _ => false
+
end
plugins/trustedPath.sig
plugins/trustedPath.sml
+plugins/socketPerm.sig
+plugins/socketPerm.sml
+
mail/vmail.sig
mail/vmail.sml