X-Git-Url: http://git.hcoop.net/bpt/portal.git/blobdiff_plain/f432bce2e8a41a84b0cea40ac7b7ae27af2d5958..b67184d145404b8f263d081eb9954fe47ea37bd2:/sec.sml diff --git a/sec.sml b/sec.sml dissimilarity index 68% index af3f813..5712940 100644 --- a/sec.sml +++ b/sec.sml @@ -1,119 +1,142 @@ -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=hcoop /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=hcoop /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=hcoop /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 + +fun intFromString s = + if CharVector.all Char.isDigit s andalso size s > 0 then + Int.fromString s + else + NONE + +fun validPort port = + case intFromString port of + NONE => false + | SOME n => n > 0 + +fun validPortPiece pp = + case String.fields (fn ch => ch = #":") pp of + [port] => validPort port + | [port1, port2] => validPort port1 andalso validPort port2 + +fun validPorts ports = + List.all validPortPiece (String.fields (fn ch => ch = #",") ports) + +fun validIp s = + case map intFromString (String.fields (fn ch => ch = #".") s) of + [SOME n1, SOME n2, SOME n3, SOME n4] => + n1 >= 0 andalso n1 < 256 andalso n2 >= 0 andalso n2 < 256 andalso n3 >= 0 andalso n3 < 256 andalso n4 >= 0 andalso n4 < 256 + | _ => false + +fun isIdent ch = Char.isLower ch orelse Char.isDigit ch + +fun validHost s = + size s > 0 andalso size s < 20 + andalso CharVector.all (fn ch => isIdent ch orelse ch = #"-") s + +fun validDomain s = + size s > 0 andalso size s < 100 + andalso List.all validHost (String.fields (fn ch => ch = #".") s) + +val validHosts = List.all (fn x => validIp x orelse validDomain x) + +fun validRule rule = + case String.tokens Char.isSpace rule of + "Client" :: ports :: hosts => validPorts ports andalso validHosts hosts + | "Server" :: ports :: hosts => validPorts ports andalso validHosts hosts + | ["LocalServer", ports] => validPorts ports + | _ => false + +end