home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / modules / PortRanges.ycp < prev    next >
Text File  |  2006-11-29  |  17KB  |  498 lines

  1. /**
  2.  * Copyright 2004, Novell, Inc.  All rights reserved.
  3.  *
  4.  * File:    modules/PortRanges.ycp
  5.  * Package:    SuSEFirewall configuration
  6.  * Summary:    Checking and manipulation with port ranges (iptables).
  7.  * Authors:    Lukas Ocilka <locilka@suse.cz>
  8.  *
  9.  * $id$
  10.  *
  11.  * Module for handling port ranges.
  12.  */
  13.  
  14. {
  15.     module "PortRanges";
  16.     textdomain "base";
  17.  
  18.     import "PortAliases";
  19.  
  20.     // Local Helper Functions -->
  21.  
  22.     /**
  23.      * Variable for ReportOnlyOnce() function
  24.      */
  25.     list <string> report_only_once = [];
  26.  
  27.     /**
  28.      * Report the error, warning, message only once.
  29.      * Stores the error, warning, message in memory.
  30.      * This is just a helper function that could avoid from filling y2log up with
  31.      * a lot of the very same messages - 'foreach()' is a very powerful builtin.
  32.      *
  33.      * @param string error, warning or message
  34.      * @return boolean whether the message should be reported or not
  35.      *
  36.      * @example
  37.      *    string error = sformat("Port number %1 is invalid.", port_nr);
  38.      *    if (ReportOnlyOnce(error)) y2error(error);
  39.      */
  40.     boolean ReportOnlyOnce (string what_to_report) {
  41.     if (contains(report_only_once, what_to_report)) {
  42.         return false;
  43.     } else {
  44.         report_only_once = add (report_only_once, what_to_report);
  45.         return true;
  46.     }
  47.     }
  48.  
  49.     // <-- Local Helper Functions
  50.  
  51.     /**
  52.      * Maximal number of port number, they are in the interval 1-65535 included.
  53.      * The very same value should appear in SuSEFirewall::max_port_number.
  54.      */
  55.     global integer max_port_number = 65535;
  56.  
  57.     // Port Ranges -->
  58.  
  59.     /**
  60.      * Function returns where the string parameter is a port range.
  61.      * Port ranges are defined by the syntax "min_port_number:max_port_number".
  62.      * Port range means that these maximum and minimum ports define the range
  63.      * of currency in Firewall. Ports defining the range are included in it.
  64.      * This function doesn't check whether the port range is valid or not.
  65.      *
  66.      * @param string to be checked
  67.      * @return boolean whether the checked string is a port range or not
  68.      *
  69.      * @see IsValidPortRange()
  70.      *
  71.      * @example
  72.      *     IsPortRange("34:38")      -> true
  73.      *     IsPortRange("0:38")       -> true
  74.      *     IsPortRange("port-range") -> false
  75.      *     IsPortRange("19-22")      -> false
  76.      */
  77.     global boolean IsPortRange (string check_this) {
  78.     if (regexpmatch(check_this,"^[0123456789]+:[0123456789]+$")) {
  79.         return true;
  80.     }
  81.     return false;
  82.     }
  83.  
  84.     /**
  85.      * Checks whether the port range is valid.
  86.      *
  87.      * @param string port_range
  88.      * @return boolean if it is valid
  89.      *
  90.      * @see IsPortRange()
  91.      *
  92.      * @example
  93.      *     IsValidPortRange("54:135") -> true  // valid
  94.      *     IsValidPortRange("135:54") -> false // reverse order
  95.      *     IsValidPortRange("0:135")  -> false // cannot be from 0
  96.      *     IsValidPortRange("135")    -> false // cannot be one number
  97.      *     IsValidPortRange("54-135") -> false // wrong separator
  98.      */
  99.     global boolean IsValidPortRange (string port_range) {
  100.     // not a port range
  101.     if (! IsPortRange(port_range)) return false;
  102.     
  103.     integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
  104.     integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
  105.     
  106.     // couldn't extract two integers
  107.     if (min_pr == nil && max_pr == nil) return false;
  108.     
  109.     // Checking the minimal port number in the port-range
  110.     // wrong range
  111.     if (min_pr < 1 || min_pr > max_port_number) {
  112.         string warning = sformat ("Wrong port-range definition %1", port_range);
  113.         if (ReportOnlyOnce(warning)) y2warning(warning);
  114.  
  115.         return false;
  116.     }
  117.     
  118.     // Checking the maximal port number in the port-range
  119.     // wrong range
  120.     if (max_pr < 1 || max_pr > max_port_number) {
  121.         string warning = sformat ("Wrong port-range definition %1", port_range);
  122.         if (ReportOnlyOnce(warning)) y2warning(warning);
  123.  
  124.         return false;
  125.     }
  126.     
  127.     // wrong range
  128.     if (min_pr >= max_pr) {
  129.         string warning = sformat ("Wrong port-range definition %1", port_range);
  130.         if (ReportOnlyOnce(warning)) y2warning(warning);
  131.  
  132.         return false;
  133.     }
  134.  
  135.     return true;
  136.     }
  137.  
  138.     /**
  139.      * Function returns where the port name or port number is included in the
  140.      * list of port ranges. Port ranges must be defined as a string with format
  141.      * "min_port_number:max_port_number".
  142.      * 
  143.      * @param string port_number_or_port_name
  144.      * @param list <string> port_ranges
  145.      *
  146.      * @example
  147.      *     PortIsInPortranges ("130",  ["100:150","10:30"]) -> true
  148.      *     PortIsInPortranges ("30",   ["100:150","10:20"]) -> false
  149.      *     PortIsInPortranges ("pop3", ["100:150","10:30"]) -> true
  150.      *     PortIsInPortranges ("http", ["100:150","10:20"]) -> false
  151.      */
  152.     global boolean PortIsInPortranges (string port, list <string> port_ranges) {
  153.     if (size(port_ranges) == 0) return false;
  154.  
  155.     boolean ret = false;
  156.  
  157.     integer port_number = PortAliases::GetPortNumber(port);
  158.  
  159.     if (port_number != nil) {
  160.         foreach (string port_range, port_ranges, {
  161.         // is portrange really a port range?
  162.         if (IsValidPortRange(port_range)) {
  163.             integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
  164.             integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
  165.  
  166.             // is the port inside?
  167.             if (min_pr <= max_pr && min_pr <= port_number && port_number <= max_pr) {
  168.             ret = true;
  169.  
  170.             break;  // break the loop, match found
  171.             }
  172.         }
  173.         });
  174.     }
  175.  
  176.     return ret;
  177.     }
  178.  
  179.     /**
  180.      * Function divides list of ports to the map of ports and port ranges.
  181.      * If with_aliases is 'true' it also returns ports wit their port aliases.
  182.      * Port ranges are not affected with it.
  183.      *
  184.      * @struct Returns $[
  185.      *    "ports" : [ list of ports ],
  186.      *    "port_ranges" : [ list of port ranges ],
  187.      * ]
  188.      *
  189.      * @param list <string> unsorted_ports
  190.      * @param boolean with port aliases
  191.      * @return <map <string, list <string> > > of divided ports
  192.      */
  193.     global map <string, list <string> > DividePortsAndPortRanges (list <string> unsorted_ports, boolean with_aliases) {
  194.     map <string, list <string> > ret = $[];
  195.  
  196.     foreach (string port, unsorted_ports, {
  197.         // port range
  198.         if (IsPortRange(port)) {
  199.         ret["port_ranges"] = add (ret["port_ranges"]:[], port);
  200.         // is a normal port
  201.         } else {
  202.         // find also aliases
  203.         if (with_aliases) {
  204.             ret["ports"] = (list<string>) union (
  205.             ret["ports"]:[], PortAliases::GetListOfServiceAliases(port)
  206.             );
  207.         // only add the port itself
  208.         } else {
  209.             ret["ports"] = add (ret["ports"]:[], port);
  210.         }
  211.         }
  212.     });
  213.  
  214.     return ret;
  215.     }
  216.  
  217.     /**
  218.      * Function creates a port range from min and max params. Max must be bigger than min.
  219.      * If something is wrong, it returns an empty string.
  220.      *
  221.      * @param integer min_port
  222.      * @param integer max_port
  223.      * @return string new port range
  224.      */
  225.     global string CreateNewPortRange (integer min_pr, integer max_pr) {
  226.     if (min_pr == nil || min_pr == 0) {
  227.         y2error("Wrong definition of the starting port '%1', it must be between 1 and 65535", min_pr);
  228.         return "";
  229.     } else if (max_pr == nil || max_pr == 0 || max_pr > 65535) {
  230.         y2error("Wrong definition of the ending port '%1', it must be between 1 and 65535", max_pr);
  231.         return "";
  232.     }
  233.  
  234.     // max and min are the same, this is not a port range
  235.     if (min_pr == max_pr) {
  236.         return tostring(min_pr);
  237.     // right port range
  238.     } else if (min_pr < max_pr) {
  239.         return tostring(min_pr) + ":" + tostring(max_pr);
  240.     // min is bigger than max
  241.     } else {
  242.         y2error("Starting port '%1' cannot be bigger than ending port '%2'", min_pr, max_pr);
  243.         return "";
  244.     }
  245.     }
  246.  
  247.     /**
  248.      * Function removes port number from all port ranges. Port must be in its numeric
  249.      * form.
  250.      *
  251.      * @see PortAliases::GetPortNumber()
  252.      * @param integer port_number to be removed
  253.      * @param list <string> of all current port_ranges
  254.      * @return list <string> of filtered port_ranges
  255.      *
  256.      * @example
  257.      *     RemovePortFromPortRanges(25, ["19-88", "152-160"]) -> ["19-24", "26-88", "152-160"]
  258.      */
  259.     global list <string> RemovePortFromPortRanges (integer port_number, list <string> port_ranges) {
  260.     // Checking necessarity of filtering and params
  261.     if (port_ranges == nil || port_ranges == []) return port_ranges;
  262.     if (port_number == nil || port_number == 0) return port_ranges;
  263.  
  264.     y2milestone("Removing port %1 from port ranges %2", port_number, port_ranges);
  265.     
  266.     list <string> ret = [];
  267.     // Checking every port range alone
  268.     foreach (string port_range, port_ranges, {
  269.         // Port range might be now only "port"
  270.         if (!IsPortRange(port_range)) {
  271.         // If the port doesn't match the ~port_range...
  272.         if (tostring(port_number) != port_range)
  273.             ret = add (ret, port_range);
  274.         // If matches, it isn't added (it is filtered)
  275.         // Modify the port range when the port is included
  276.         } else if (PortIsInPortranges(tostring(port_number), [port_range])) {
  277.         integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
  278.         integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
  279.         
  280.         // Port matches the min. value of port range
  281.         if (port_number == min_pr) {
  282.             ret = add (ret, CreateNewPortRange(port_number + 1, max_pr));
  283.         // Port matches the max. value of port range
  284.         } else if (port_number == max_pr) {
  285.             ret = add (ret, CreateNewPortRange(min_pr, port_number - 1));
  286.         // Port is inside the port range, split it up
  287.         } else {
  288.             ret = add (ret, CreateNewPortRange(port_number + 1, max_pr));
  289.             ret = add (ret, CreateNewPortRange(min_pr, port_number - 1));
  290.         }
  291.         // Port isn't in the port range, adding the current port range
  292.         } else {
  293.         ret = add (ret, port_range);
  294.         }
  295.     });
  296.  
  297.     y2milestone("Result: %1", ret);
  298.     
  299.     return ret;
  300.     }
  301.  
  302.     /**
  303.      * Function tries to flatten services into the minimal list.
  304.      * If ports are already mentioned inside port ranges, they are dropped.
  305.      *
  306.      * @param list <string> of services and port ranges
  307.      * @return list <string> of flattened services and port ranges
  308.      */
  309.     global list <string> FlattenServices (list <string> old_list, string protocol) {
  310.     if (! contains(["TCP", "UDP"], protocol)) {
  311.         string message = sformat("Protocol %1 doesn't support port ranges, skipping...", protocol);
  312.         if (ReportOnlyOnce(message)) y2milestone(message);
  313.         return old_list;
  314.     }
  315.     
  316.     list <string> new_list = [];
  317.     list <string> list_of_ports = [];
  318.     list <string> list_of_ranges = [];
  319.     // Using port number, we can remove ports mentioned in port ranges
  320.     map <string, integer> ports_to_port_numbers = $[];
  321.     // Using this we can remove ports mentioned more than once
  322.     map <integer, list <string> > port_numbers_to_port_names = $[];
  323.     
  324.     foreach (string one_item, old_list, {
  325.         // Port range
  326.         if (IsPortRange(one_item)) list_of_ranges = add (list_of_ranges, one_item);
  327.         // Port number or name
  328.         else {
  329.         integer port_number = PortAliases::GetPortNumber(one_item);
  330.         // Cannot find port number for this port, it is en error of the configuration
  331.         if (port_number == nil) {
  332.             y2warning("Unknown port %1 but leaving it in the configuration.", one_item);
  333.             new_list = add (new_list, one_item);
  334.             // skip the 'nil' port number
  335.             return;
  336.         }
  337.         ports_to_port_numbers[one_item] = port_number;
  338.         port_numbers_to_port_names[port_number] = add (port_numbers_to_port_names[port_number]:[], one_item);
  339.         }
  340.     });
  341.     
  342.     foreach (integer port_number, list <string> port_names, port_numbers_to_port_names, {
  343.         // Port is not in any defined port range
  344.         if (!PortIsInPortranges(tostring(port_number), list_of_ranges)) {
  345.         // Port - 1 IS in some port range
  346.         if (PortIsInPortranges(tostring(port_number - 1), list_of_ranges)) {
  347.             // Creating fake port range, to be joined with another one
  348.             list_of_ranges = add (list_of_ranges, CreateNewPortRange(port_number - 1, port_number));
  349.         // Port + 1 IS in some port range
  350.         } else if (PortIsInPortranges(tostring(port_number + 1), list_of_ranges)) {
  351.             // Creating fake port range, to be joined with another one
  352.             list_of_ranges = add (list_of_ranges, CreateNewPortRange(port_number, port_number + 1));
  353.         // Port is not in any port range and also it cannot be joined with any one
  354.         } else {
  355.             // Port names of this port
  356.             list <string> used_port_names = port_numbers_to_port_names[port_number]:[];
  357.             if (size(used_port_names)>0) {
  358.             new_list = add (new_list, used_port_names[0]:"");
  359.             } else {
  360.             y2milestone("No port name for port number %1. Adding %1...", port_number);
  361.             // There are no port names (hmm?), adding port number
  362.             new_list = add (new_list, tostring(port_number));
  363.             }
  364.         }
  365.         // Port is in a port range
  366.         } else {
  367.         y2milestone("Removing port %1 mentioned in port ranges %2", port_number, list_of_ranges);
  368.         }
  369.     });
  370.  
  371.     list_of_ranges = toset(list_of_ranges);
  372.     // maximal count of steps
  373.     integer max_loops = 5000;
  374.  
  375.     // Joining port ranges together
  376.     // this is a bit dangerous!
  377.     y2milestone("Joining list of ranges %1", list_of_ranges);
  378.     while (true && max_loops>0) {
  379.         // if something goes wrong
  380.         max_loops = max_loops - 1;
  381.         
  382.         boolean any_change_during_this_loop = false;
  383.  
  384.         list <string> try_all_these_ranges = list_of_ranges;
  385.         foreach (string port_range, try_all_these_ranges, {
  386.         if (! IsValidPortRange(port_range)) {
  387.             string warning = sformat("Wrong port-range definition %1, cannot join", port_range);
  388.             if (ReportOnlyOnce(warning)) y2warning(warning);
  389.             return;
  390.         }
  391.  
  392.         integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
  393.         integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
  394.         
  395.         if (min_pr == nil || max_pr == nil) {
  396.             y2error("Not a port range %1", port_range);
  397.             return;
  398.         }
  399.  
  400.         // try to join it with another port ranges
  401.         // -->
  402.             foreach (string try_this_pr, try_all_these_ranges, {
  403.             // Exact match means the same port range
  404.             if (try_this_pr == port_range) return;
  405.             
  406.             string this_min = regexpsub(try_this_pr,"^([0123456789]+):.*$", "\\1");
  407.             string this_max = regexpsub(try_this_pr,"^.*:([0123456789]+)$", "\\1");
  408.             
  409.             if (this_min == nil || this_max == nil) {
  410.                 y2error("Wrong port range %1, %2 > %3", port_range, this_min, this_max);
  411.                 // skip it
  412.                 return;
  413.             }
  414.             
  415.             integer this_min_pr = tointeger(this_min);
  416.             integer this_max_pr = tointeger(this_max);
  417.             
  418.             // // wrong definition of the port range
  419.             if (this_min_pr < 1 || this_max_pr > max_port_number) {
  420.                 string warning = sformat("Wrong port-range definition %1, cannot join", port_range);
  421.                 if (ReportOnlyOnce(warning)) y2warning(warning);
  422.                 // skip it
  423.                 return;
  424.             }
  425.             
  426.             // If new port range should be created
  427.             integer new_min = nil;
  428.             integer new_max = nil;
  429.  
  430.             // the second one is inside the first one
  431.             if (min_pr <= this_min_pr && max_pr >= this_max_pr) {
  432.                 // take min_pr & max_pr
  433.                 any_change_during_this_loop = true;
  434.                 new_min = min_pr;
  435.                 new_max = max_pr;
  436.             // the fist one is inside the second one
  437.             } else if (min_pr >= this_min_pr && max_pr <= this_max_pr) {
  438.                 // take this_min_pr & this_max_pr
  439.                 any_change_during_this_loop = true;
  440.                 new_min = this_min_pr;
  441.                 new_max = this_max_pr;
  442.             // the fist one partly covers the second one (by its right side)
  443.             } else if (min_pr <= this_min_pr && max_pr >= this_min_pr) {
  444.                 // take min_pr & this_max_pr
  445.                 any_change_during_this_loop = true;
  446.                 new_min = min_pr;
  447.                 new_max = this_max_pr;
  448.             // the second one partly covers the first one (by its left side)
  449.             } else if (min_pr >= this_min_pr && max_pr <= this_max_pr) {
  450.                 // take this_min_pr & max_pr
  451.                 any_change_during_this_loop = true;
  452.                 new_min = this_min_pr;
  453.                 new_max = max_pr;
  454.             // the first one has the second one just next on the right
  455.             } else if ((max_pr + 1) == this_min_pr) {
  456.                 // take min_pr & this_max_pr
  457.                 any_change_during_this_loop = true;
  458.                 new_min = min_pr;
  459.                 new_max = this_max_pr;
  460.             // the first one has the second one just next on the left side
  461.             } else if ((min_pr - 1) == this_max_pr) {
  462.                 // take this_min_pr & max_pr
  463.                 any_change_during_this_loop = true;
  464.                 new_min = this_min_pr;
  465.                 new_max = max_pr;
  466.             }
  467.             
  468.             if (any_change_during_this_loop && new_min != nil && new_max != nil) {
  469.                 string new_port_range = CreateNewPortRange(new_min, new_max);
  470.                 y2milestone("Joining %1 and %2 into %3", port_range, try_this_pr, new_port_range);
  471.                 // Remove old port ranges
  472.                 list_of_ranges = filter (string filter_pr, list_of_ranges, {
  473.                 return (filter_pr != port_range && filter_pr != try_this_pr);
  474.                 });
  475.                 // Create a new one
  476.                 list_of_ranges = add (list_of_ranges, new_port_range);
  477.             }
  478.             });
  479.         // <--
  480.         
  481.         // renew list of current port ranges, they have changed
  482.         if (any_change_during_this_loop) break;
  483.         });
  484.         
  485.         if (!any_change_during_this_loop) break;
  486.     }
  487.     y2milestone("Result of joining: %1", list_of_ranges);
  488.  
  489.     new_list = (list <string>) union (new_list, list_of_ranges);
  490.  
  491.     return new_list;
  492.     }
  493.  
  494.     // <-- Port Ranges
  495.  
  496. /* EOF */
  497. }
  498.