From 3d2ed222004f3cf2066af19faa55151ab48951cb Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sun, 25 Feb 2007 22:04:58 +0000 Subject: [PATCH] Updated security settings to handle multiple machines --- sec.mlt | 92 +++++++++++++++-------- sec.sig | 37 +++++---- sec.sml | 217 ++++++++++++++++++++++++----------------------------- tables.sql | 1 + 4 files changed, 178 insertions(+), 169 deletions(-) rewrite sec.sig (63%) rewrite sec.sml (69%) diff --git a/sec.mlt b/sec.mlt index 2983077..8885eaa 100644 --- a/sec.mlt +++ b/sec.mlt @@ -1,14 +1,19 @@ <% val you = Init.getUserId (); val yourname = Init.getUserName (); +val nodeNum = case $"node" of + "" => 2 + | node => Web.stoi node; +val nodeName = Init.nodeName nodeNum; + val uname = case $"uname" of "" => yourname | uname => uname; -val socks = Sec.socketPerms uname; -val tpe = Sec.isTpe uname; -val cron = Sec.cronAllowed uname; -val ftp = Sec.ftpAllowed uname; +val socks = Sec.socketPerms {node = nodeNum, uname = uname}; +val tpe = Sec.isTpe {node = nodeNum, uname = uname}; +val cron = Sec.cronAllowed {node = nodeNum, uname = uname}; +val ftp = Sec.ftpAllowed {node = nodeNum, uname = uname}; ref showNormal = true; @@ -17,10 +22,10 @@ ref showNormal = true; if $"cmd" = "socks" then showNormal := false; val socks = $"socks"; - %>Are you sure you want to request that socket permissions for <% Web.html uname %> be changed to <% Web.html socks %>?
- ">Yes, place the request!<% + %>Are you sure you want to request that socket permissions for <% Web.html uname %> on <% Web.html nodeName %> be changed to <% Web.html socks %>?
+ ">Yes, place the request!<% elseif $"cmd" = "socks2" then - val id = Sec.Req.add (you, String.concat [uname, ": change socket permissions to ", $"socks"], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": change socket permissions to ", $"socks"], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -29,10 +34,10 @@ elseif $"cmd" = "socks2" then elseif $"cmd" = "tpe" then showNormal := false; val tpe = iff $"tpe" = "yes" then "on" else "off"; - %>Are you sure you want to request that trusted-path-executables-only for <% Web.html uname %> be turned <% tpe %>?
- ">Yes, place the request!<% + %>Are you sure you want to request that trusted-path-executables-only for <% Web.html uname %> on <% Web.html nodeName %> be turned <% tpe %>?
+ ">Yes, place the request!<% elseif $"cmd" = "tpe2" then - val id = Sec.Req.add (you, String.concat [uname, ": turn tpe ", $"tpe"], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": turn tpe ", $"tpe"], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -41,11 +46,11 @@ elseif $"cmd" = "tpe2" then elseif $"cmd" = "cron" then showNormal := false; val cron = iff $"cron" = "yes" then "enabled" else "disabled"; - %>Are you sure you want to request that cron permissions for <% Web.html uname %> be <% cron %>?
- ">Yes, place the request!<% + %>Are you sure you want to request that cron permissions for <% Web.html uname %> on <% Web.html nodeName %> be <% cron %>?
+ ">Yes, place the request!<% elseif $"cmd" = "cron2" then val cron = iff $"cron" = "enabled" then "enable" else "disable"; - val id = Sec.Req.add (you, String.concat [uname, ": ", cron, " cron access"], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": ", cron, " cron access"], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -54,11 +59,11 @@ elseif $"cmd" = "cron2" then elseif $"cmd" = "ftp" then showNormal := false; val ftp = iff $"ftp" = "yes" then "enabled" else "disabled"; - %>Are you sure you want to request that FTP permissions for <% Web.html uname %> be <% ftp %>?
- ">Yes, place the request!<% + %>Are you sure you want to request that FTP permissions for <% Web.html uname %> on <% Web.html nodeName %> be <% ftp %>?
+ ">Yes, place the request!<% elseif $"cmd" = "ftp2" then val ftp = iff $"ftp" = "enabled" then "enable" else "disable"; - val id = Sec.Req.add (you, String.concat [uname, ": ", ftp, " FTP access"], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": ", ftp, " FTP access"], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -67,10 +72,10 @@ elseif $"cmd" = "ftp2" then elseif $"cmd" = "rule" then showNormal := false; val rule = $"rule"; - %>Are you sure you want to request the firewall rule <% Web.html uname %> <% Web.html rule %>?
- ">Yes, place the request!<% + %>Are you sure you want to request the firewall rule <% Web.html uname %> <% Web.html rule %> on <% Web.html nodeName %>?
+ ">Yes, place the request!<% elseif $"cmd" = "rule2" then - val id = Sec.Req.add (you, String.concat ["Add firewall rule \"", uname, " ", $"rule", "\""], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat ["Add firewall rule \"", uname, " ", $"rule", "\""], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -83,11 +88,11 @@ elseif $"modRule" <> "" then if oldRule = rule then %>You didn't modify the textbox for this rule before clicking the button, so there is no request to be made.<% else - %>Are you sure you want to request that firewall rule <% Web.html uname %> <% Web.html oldRule %> be replaced by <% Web.html uname %> <% Web.html rule %>?
- ">Yes, place the request!<% + %>Are you sure you want to request that firewall rule <% Web.html uname %> <% Web.html oldRule %> be replaced by <% Web.html uname %> <% Web.html rule %> on <% Web.html nodeName %>?
+ ">Yes, place the request!<% end elseif $"modRule2" <> "" then - val id = Sec.Req.add (you, String.concat ["Change firewall rule \"", uname, " ", $"modRule2", "\" to \"", uname, " ", $"rule", "\""], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat ["Change firewall rule \"", uname, " ", $"modRule2", "\" to \"", uname, " ", $"rule", "\""], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -96,10 +101,10 @@ elseif $"modRule2" <> "" then elseif $"delRule" <> "" then showNormal := false; val oldRule = $"delRule"; - %>Are you sure you want to request that firewall rule <% Web.html uname %> <% Web.html oldRule %> be deleted?
- ">Yes, place the request!<% + %>Are you sure you want to request that firewall rule <% Web.html uname %> <% Web.html oldRule %> on <% Web.html nodeName %> be deleted?
+ ">Yes, place the request!<% elseif $"delRule2" <> "" then - val id = Sec.Req.add (you, String.concat ["Delete firewall rule \"", uname, " ", $"delRule2", "\""], $"msg"); + val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat ["Delete firewall rule \"", uname, " ", $"delRule2", "\""], msg = $"msg"}; if not (Sec.Req.notifyNew id) then %>

Error sending e-mail notification

<% end @@ -116,6 +121,7 @@ elseif $"cmd" = "open" then +
By: <% name %>
Time: <% #stamp req %>
Node: <% Web.html (Init.nodeName (#node req)) %>
Request: <% #data req %>
Msg: <% Web.html (#msg req) %>
@@ -136,6 +142,7 @@ elseif $"cmd" = "list" then +
By: <% name %>
Time: <% #stamp req %>
Node: <% Web.html (Init.nodeName (#node req)) %>
Request: <% #data req %>
Reason: <% Web.html (#msg req) %>
@@ -164,6 +171,10 @@ elseif $"mod" <> "" then + Node: Request: Message: @@ -177,9 +188,9 @@ elseif $"mod" <> "" then val req = Sec.Req.lookup id; val oldStatus = #status req; val newStatus = Sec.Req.statusFromInt (Web.stoi ($"status")); - Sec.Req.modify {req with data = $"req", msg = $"msg", status = newStatus}; + Sec.Req.modify {req with node = nodeNum, data = $"req", msg = $"msg", status = newStatus}; if oldStatus <> newStatus then - if not (Sec.Req.notifyMod (oldStatus, newStatus, Init.getUserName(), id)) then + if not (Sec.Req.notifyMod {old = oldStatus, new = newStatus, changer = Init.getUserName(), req = id}) then %>

Error sending e-mail notification

<% end end @@ -192,7 +203,7 @@ elseif $"mod" <> "" then val id = Web.stoi ($"del"); val req = Sec.Req.lookup id; val user = Init.lookupUser (#usr req) - %>

Are you sure you want to delete request by <% #name user %> for "<% #data req %>"?

+ %>

Are you sure you want to delete request by <% #name user %> for "<% #data req %>" on <% Web.html (Init.nodeName (#node req)) %>?

Yes, I'm sure! <% elseif $"del2" <> "" then @@ -207,15 +218,29 @@ elseif $"mod" <> "" then if showNormal then %> + + + + + + -Your users: + + + +
Machines:
Your users: +<% end %>

Request socket permissions change

+ @@ -233,6 +258,7 @@ if showNormal then %>

Request change to your execute permissions

+
@@ -248,6 +274,7 @@ if showNormal then %>

Request change to your cron permissions

+
@@ -265,6 +292,7 @@ if showNormal then %>

Please read our wiki instructions on file transfer before requesting FTP access. Almost everyone should use alternative protocols to FTP that provide superior security benefits.

+
@@ -277,13 +305,14 @@ if showNormal then %>
-<% val rules = Sec.findFirewallRules uname; +<% val rules = Sec.findFirewallRules {node = nodeNum, uname = uname}; switch rules of _::_ => %>

Your firewall rules

<% foreach rule in rules do %>
+ @@ -300,6 +329,7 @@ end%>

Please note that your firewall rule will be useless if you don't first request the corresponding socket privileges at the top of this page.

+ diff --git a/sec.sig b/sec.sig dissimilarity index 63% index 29882ad..a282b37 100644 --- a/sec.sig +++ b/sec.sig @@ -1,19 +1,18 @@ -signature SEC = sig - structure Req : REQUEST_OUT - - val findSubusers : string -> string list - - datatype socket_perms = - ANY - | CLIENT_ONLY - | SERVER_ONLY - | NADA - - val inGroup : string * string -> bool - val socketPerms : string -> socket_perms - val isTpe : string -> bool - val cronAllowed : string -> bool - val ftpAllowed : string -> bool - - val findFirewallRules : string -> string list -end +signature SEC = sig + structure Req : REQUESTH_OUT + + val findSubusers : string -> string list + + datatype socket_perms = + ANY + | CLIENT_ONLY + | SERVER_ONLY + | NADA + + val socketPerms : {node : int, uname : string} -> socket_perms + val isTpe : {node : int, uname : string} -> bool + val cronAllowed : {node : int, uname : string} -> bool + val ftpAllowed : {node : int, uname : string} -> bool + + val findFirewallRules : {node : int, uname : string} -> string list +end diff --git a/sec.sml b/sec.sml dissimilarity index 69% index af3f813..782fdf7 100644 --- a/sec.sml +++ b/sec.sml @@ -1,119 +1,98 @@ -structure Sec :> SEC = struct - -open Init Util Sql - -structure Req = Request(struct - val table = "Sec" - val adminGroup = "server" - fun subject _ = "Security permissions change request" - val template = "sec" - val descr = "change" - fun body (mail, req) = - (Mail.mwrite (mail, req); - Mail.mwrite (mail, "\n")) - end) - -fun findSubusers uname = - let - val uname_under = uname ^ "_" - val inf = TextIO.openIn "/etc/passwd" - - fun loop subs = - case TextIO.inputLine inf of - NONE => ListMergeSort.sort (fn (x, y) => String.compare (x, y) = GREATER) subs - | SOME line => - case String.fields (fn ch => ch = #":") line of - uname'::_ => - if size uname' >= size uname_under - andalso String.substring (uname', 0, size uname_under) = uname_under then - loop (uname' :: subs) - else - loop subs - | _ => loop subs - in - loop [] - before TextIO.closeIn inf - end - -datatype socket_perms = - ANY - | CLIENT_ONLY - | SERVER_ONLY - | NADA - -fun inGroup (uname, grp) = - let - val uname_under = uname ^ "_" - val inf = TextIO.openIn "/etc/group" - - fun loop () = - case TextIO.inputLine inf of - NONE => false - | SOME line => - case String.fields (fn ch => ch = #":") line of - [gname, _, _, members] => - if gname = grp then - mem (uname, String.fields (fn ch => ch = #",") members) - else - loop () - | _ => loop () - in - loop () - before TextIO.closeIn inf - end - -fun socketPerms uname = - if inGroup (uname, "no-sockets") then - NADA - else if inGroup (uname, "no-cli-sockets") then - if inGroup (uname, "no-serv-sockets") then - NADA - else - SERVER_ONLY - else if inGroup (uname, "no-serv-sockets") then - CLIENT_ONLY - else - ANY - -fun isTpe uname = inGroup (uname, "only-tpe") - -fun findFirewallRules uname = - let - val inf = TextIO.openIn "/etc/firewall/users.rules" - - fun loop rules = - case TextIO.inputLine inf of - NONE => List.rev rules - | SOME line => - if String.sub (line, 0) = #"#" then - loop rules - else case String.tokens Char.isSpace line of - uname'::rest => - if uname = uname' then - loop (String.concatWith " " rest :: rules) - else - loop rules - | _ => loop rules - in - loop [] - before TextIO.closeIn inf - end - -fun somethingAllowed fname uname = - let - val inf = TextIO.openIn fname - val uname' = uname ^ "\n" - - fun loop () = - case TextIO.inputLine inf of - NONE => false - | SOME line => line = uname' orelse loop () - in - loop () - before TextIO.closeIn inf - end - -val cronAllowed = somethingAllowed "/etc/cron.allow" -val ftpAllowed = somethingAllowed "/etc/ftpusers" - -end +structure Sec :> SEC = struct + +open Init Util Sql + +structure Req = RequestH(struct + val table = "Sec" + val adminGroup = "server" + fun subject _ = "Security permissions change request" + val template = "sec" + val descr = "change" + fun body {node, mail, data = req} = + (Mail.mwrite (mail, req); + Mail.mwrite (mail, "\n")) + end) + +fun findSubusers uname = + let + val uname_under = uname ^ "_" + val inf = TextIO.openIn "/etc/passwd" + + fun loop subs = + case TextIO.inputLine inf of + NONE => ListMergeSort.sort (fn (x, y) => String.compare (x, y) = GREATER) subs + | SOME line => + case String.fields (fn ch => ch = #":") line of + uname'::_ => + if size uname' >= size uname_under + andalso String.substring (uname', 0, size uname_under) = uname_under then + loop (uname' :: subs) + else + loop subs + | _ => loop subs + in + loop [] + before TextIO.closeIn inf + end + +datatype socket_perms = + ANY + | CLIENT_ONLY + | SERVER_ONLY + | NADA + +fun socketPerms {node, uname} = + let + val proc = Unix.execute ("/bin/sh", + ["-c", + "DOMTOOL_USER=apache2.deleuze.hcoop.net /usr/local/bin/domtool-admin sockperm " + ^ Init.nodeName node ^ " " ^ uname]) + + val inf = Unix.textInstreamOf proc + + val p = case TextIO.inputLine inf of + SOME "Any\n" => ANY + | SOME "Client\n" => CLIENT_ONLY + | SOME "Server\n" => SERVER_ONLY + | _ => NADA + in + TextIO.closeIn inf; + if OS.Process.isSuccess (Unix.reap proc) then + p + else + NADA + end + +fun checkIt cmd {node, uname} = + OS.Process.isSuccess (OS.Process.system + ("DOMTOOL_USER=apache2.deleuze.hcoop.net /usr/local/bin/domtool-admin " + ^ cmd ^ " " ^ Init.nodeName node ^ " " ^ uname ^ " >/dev/null 2>/dev/null")) + +val isTpe = checkIt "tpe" +val cronAllowed = checkIt "cron" +val ftpAllowed = checkIt "ftp" + +fun findFirewallRules {node, uname} = + let + val proc = Unix.execute ("/bin/sh", + ["-c", + "DOMTOOL_USER=apache2.deleuze.hcoop.net /usr/local/bin/domtool-admin firewall " + ^ Init.nodeName node ^ " " ^ uname]) + + val inf = Unix.textInstreamOf proc + + fun readEm lines = + case TextIO.inputLine inf of + SOME line => readEm (String.substring (line, 0, size line - 1) :: lines) + | NONE => rev lines + + val lines = readEm [] + in + TextIO.closeIn inf; + if OS.Process.isSuccess (Unix.reap proc) then + lines + else + [] + end + +end diff --git a/tables.sql b/tables.sql index 35cfb29..cc2ff98 100644 --- a/tables.sql +++ b/tables.sql @@ -298,6 +298,7 @@ CREATE TABLE RollCallEntry( CREATE TABLE Sec( id INTEGER PRIMARY KEY, usr INTEGER NOT NULL, + node INTEGER NOT NULL, data TEXT NOT NULL, msg TEXT NOT NULL, status INTEGER NOT NULL, -- 2.20.1