The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/security/mac_portacl/mac_portacl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2003-2004 Networks Associates Technology, Inc.
    3  * Copyright (c) 2006 SPARTA, Inc.
    4  * All rights reserved.
    5  *
    6  * This software was developed for the FreeBSD Project by Network
    7  * Associates Laboratories, the Security Research Division of Network
    8  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
    9  * as part of the DARPA CHATS research program.
   10  *
   11  * This software was enhanced by SPARTA ISSO under SPAWAR contract
   12  * N66001-04-C-6019 ("SEFOS").
   13  *
   14  * Redistribution and use in source and binary forms, with or without
   15  * modification, are permitted provided that the following conditions
   16  * are met:
   17  * 1. Redistributions of source code must retain the above copyright
   18  *    notice, this list of conditions and the following disclaimer.
   19  * 2. Redistributions in binary form must reproduce the above copyright
   20  *    notice, this list of conditions and the following disclaimer in the
   21  *    documentation and/or other materials provided with the distribution.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  *
   35  * $FreeBSD$
   36  */
   37 
   38 /*
   39  * Developed by the TrustedBSD Project.
   40  *
   41  * Administratively limit access to local UDP/TCP ports for binding purposes.
   42  * Intended to be combined with net.inet.ip.portrange.reservedhigh to allow
   43  * specific uids and gids to bind specific ports for specific purposes,
   44  * while not opening the door to any user replacing an "official" service
   45  * while you're restarting it.  This only affects ports explicitly bound by
   46  * the user process (either for listen/outgoing socket for TCP, or send/
   47  * receive for UDP).  This module will not limit ports bound implicitly for
   48  * out-going connections where the process hasn't explicitly selected a port:
   49  * these are automatically selected by the IP stack.
   50  *
   51  * To use this module, security.mac.enforce_socket must be enabled, and you
   52  * will probably want to twiddle the net.inet sysctl listed above.  Then use
   53  * sysctl(8) to modify the rules string:
   54  *
   55  * # sysctl security.mac.portacl.rules="uid:425:tcp:80,uid:425:tcp:79"
   56  *
   57  * This ruleset, for example, permits uid 425 to bind TCP ports 80 (http) and
   58  * 79 (finger).  User names and group names can't be used directly because
   59  * the kernel only knows about uids and gids.
   60  */
   61 
   62 #include <sys/param.h>
   63 #include <sys/domain.h>
   64 #include <sys/kernel.h>
   65 #include <sys/lock.h>
   66 #include <sys/malloc.h>
   67 #include <sys/module.h>
   68 #include <sys/mutex.h>
   69 #include <sys/priv.h>
   70 #include <sys/proc.h>
   71 #include <sys/protosw.h>
   72 #include <sys/queue.h>
   73 #include <sys/systm.h>
   74 #include <sys/sbuf.h>
   75 #include <sys/socket.h>
   76 #include <sys/socketvar.h>
   77 #include <sys/sysctl.h>
   78 
   79 #include <netinet/in.h>
   80 #include <netinet/in_pcb.h>
   81 
   82 #include <security/mac/mac_policy.h>
   83 
   84 SYSCTL_DECL(_security_mac);
   85 
   86 static SYSCTL_NODE(_security_mac, OID_AUTO, portacl,
   87     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
   88     "TrustedBSD mac_portacl policy controls");
   89 
   90 static int      portacl_enabled = 1;
   91 SYSCTL_INT(_security_mac_portacl, OID_AUTO, enabled, CTLFLAG_RWTUN,
   92     &portacl_enabled, 0, "Enforce portacl policy");
   93 
   94 static int      portacl_suser_exempt = 1;
   95 SYSCTL_INT(_security_mac_portacl, OID_AUTO, suser_exempt, CTLFLAG_RWTUN,
   96     &portacl_suser_exempt, 0, "Privilege permits binding of any port");
   97 
   98 static int      portacl_autoport_exempt = 1;
   99 SYSCTL_INT(_security_mac_portacl, OID_AUTO, autoport_exempt, CTLFLAG_RWTUN,
  100     &portacl_autoport_exempt, 0, "Allow automatic allocation through "
  101     "binding port 0 if not IP_PORTRANGELOW");
  102 
  103 static int      portacl_port_high = 1023;
  104 SYSCTL_INT(_security_mac_portacl, OID_AUTO, port_high, CTLFLAG_RWTUN,
  105     &portacl_port_high, 0, "Highest port to enforce for");
  106 
  107 static MALLOC_DEFINE(M_PORTACL, "portacl_rule", "Rules for mac_portacl");
  108 
  109 #define MAC_RULE_STRING_LEN     1024
  110 
  111 #define RULE_GID        1
  112 #define RULE_UID        2
  113 #define RULE_PROTO_TCP  1
  114 #define RULE_PROTO_UDP  2
  115 struct rule {
  116         id_t                    r_id;
  117         int                     r_idtype;
  118         u_int16_t               r_port;
  119         int                     r_protocol;
  120 
  121         TAILQ_ENTRY(rule)       r_entries;
  122 };
  123 
  124 #define GID_STRING      "gid"
  125 #define TCP_STRING      "tcp"
  126 #define UID_STRING      "uid"
  127 #define UDP_STRING      "udp"
  128 
  129 /*
  130  * Text format for the rule string is that a rule consists of a
  131  * comma-separated list of elements.  Each element is in the form
  132  * idtype:id:protocol:portnumber, and constitutes granting of permission
  133  * for the specified binding.
  134  */
  135 
  136 static struct mtx                       rule_mtx;
  137 static TAILQ_HEAD(rulehead, rule)       rule_head;
  138 static char                             rule_string[MAC_RULE_STRING_LEN];
  139 
  140 static void
  141 toast_rules(struct rulehead *head)
  142 {
  143         struct rule *rule;
  144 
  145         while ((rule = TAILQ_FIRST(head)) != NULL) {
  146                 TAILQ_REMOVE(head, rule, r_entries);
  147                 free(rule, M_PORTACL);
  148         }
  149 }
  150 
  151 /*
  152  * Note that there is an inherent race condition in the unload of modules
  153  * and access via sysctl.
  154  */
  155 static void
  156 destroy(struct mac_policy_conf *mpc)
  157 {
  158 
  159         mtx_destroy(&rule_mtx);
  160         toast_rules(&rule_head);
  161 }
  162 
  163 static void
  164 init(struct mac_policy_conf *mpc)
  165 {
  166 
  167         mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF);
  168         TAILQ_INIT(&rule_head);
  169 }
  170 
  171 /*
  172  * Note: parsing routines are destructive on the passed string.
  173  */
  174 static int
  175 parse_rule_element(char *element, struct rule **rule)
  176 {
  177         char *idtype, *id, *protocol, *portnumber, *p;
  178         struct rule *new;
  179         int error;
  180 
  181         error = 0;
  182         new = malloc(sizeof(*new), M_PORTACL, M_ZERO | M_WAITOK);
  183 
  184         idtype = strsep(&element, ":");
  185         if (idtype == NULL) {
  186                 error = EINVAL;
  187                 goto out;
  188         }
  189         id = strsep(&element, ":");
  190         if (id == NULL) {
  191                 error = EINVAL;
  192                 goto out;
  193         }
  194         new->r_id = strtol(id, &p, 10);
  195         if (*p != '\0') {
  196                 error = EINVAL;
  197                 goto out;
  198         }
  199         if (strcmp(idtype, UID_STRING) == 0)
  200                 new->r_idtype = RULE_UID;
  201         else if (strcmp(idtype, GID_STRING) == 0)
  202                 new->r_idtype = RULE_GID;
  203         else {
  204                 error = EINVAL;
  205                 goto out;
  206         }
  207         protocol = strsep(&element, ":");
  208         if (protocol == NULL) {
  209                 error = EINVAL;
  210                 goto out;
  211         }
  212         if (strcmp(protocol, TCP_STRING) == 0)
  213                 new->r_protocol = RULE_PROTO_TCP;
  214         else if (strcmp(protocol, UDP_STRING) == 0)
  215                 new->r_protocol = RULE_PROTO_UDP;
  216         else {
  217                 error = EINVAL;
  218                 goto out;
  219         }
  220         portnumber = element;
  221         if (portnumber == NULL) {
  222                 error = EINVAL;
  223                 goto out;
  224         }
  225         new->r_port = strtol(portnumber, &p, 10);
  226         if (*p != '\0') {
  227                 error = EINVAL;
  228                 goto out;
  229         }
  230 
  231 out:
  232         if (error != 0) {
  233                 free(new, M_PORTACL);
  234                 *rule = NULL;
  235         } else
  236                 *rule = new;
  237         return (error);
  238 }
  239 
  240 static int
  241 parse_rules(char *string, struct rulehead *head)
  242 {
  243         struct rule *new;
  244         char *element;
  245         int error;
  246 
  247         error = 0;
  248         while ((element = strsep(&string, ",")) != NULL) {
  249                 if (strlen(element) == 0)
  250                         continue;
  251                 error = parse_rule_element(element, &new);
  252                 if (error)
  253                         goto out;
  254                 TAILQ_INSERT_TAIL(head, new, r_entries);
  255         }
  256 out:
  257         if (error != 0)
  258                 toast_rules(head);
  259         return (error);
  260 }
  261 
  262 /*
  263  * rule_printf() and rules_to_string() are unused currently because they rely
  264  * on sbufs with auto-extension, which may sleep while holding a mutex.
  265  * Instead, the non-canonical user-generated rule string is returned to the
  266  * user when the rules are queried, which is faster anyway.
  267  */
  268 #if 0
  269 static void
  270 rule_printf(struct sbuf *sb, struct rule *rule)
  271 {
  272         const char *idtype, *protocol;
  273 
  274         switch(rule->r_idtype) {
  275         case RULE_GID:
  276                 idtype = GID_STRING;
  277                 break;
  278         case RULE_UID:
  279                 idtype = UID_STRING;
  280                 break;
  281         default:
  282                 panic("rule_printf: unknown idtype (%d)\n", rule->r_idtype);
  283         }
  284 
  285         switch (rule->r_protocol) {
  286         case RULE_PROTO_TCP:
  287                 protocol = TCP_STRING;
  288                 break;
  289         case RULE_PROTO_UDP:
  290                 protocol = UDP_STRING;
  291                 break;
  292         default:
  293                 panic("rule_printf: unknown protocol (%d)\n",
  294                     rule->r_protocol);
  295         }
  296         sbuf_printf(sb, "%s:%jd:%s:%d", idtype, (intmax_t)rule->r_id,
  297             protocol, rule->r_port);
  298 }
  299 
  300 static char *
  301 rules_to_string(void)
  302 {
  303         struct rule *rule;
  304         struct sbuf *sb;
  305         int needcomma;
  306         char *temp;
  307 
  308         sb = sbuf_new_auto();
  309         needcomma = 0;
  310         mtx_lock(&rule_mtx);
  311         for (rule = TAILQ_FIRST(&rule_head); rule != NULL;
  312             rule = TAILQ_NEXT(rule, r_entries)) {
  313                 if (!needcomma)
  314                         needcomma = 1;
  315                 else
  316                         sbuf_printf(sb, ",");
  317                 rule_printf(sb, rule);
  318         }
  319         mtx_unlock(&rule_mtx);
  320         sbuf_finish(sb);
  321         temp = strdup(sbuf_data(sb), M_PORTACL);
  322         sbuf_delete(sb);
  323         return (temp);
  324 }
  325 #endif
  326 
  327 /*
  328  * Note: due to races, there is not a single serializable order
  329  * between parallel calls to the sysctl.
  330  */
  331 static int
  332 sysctl_rules(SYSCTL_HANDLER_ARGS)
  333 {
  334         char *string, *copy_string, *new_string;
  335         struct rulehead head, save_head;
  336         int error;
  337 
  338         new_string = NULL;
  339         if (req->newptr != NULL) {
  340                 new_string = malloc(MAC_RULE_STRING_LEN, M_PORTACL,
  341                     M_WAITOK | M_ZERO);
  342                 mtx_lock(&rule_mtx);
  343                 strcpy(new_string, rule_string);
  344                 mtx_unlock(&rule_mtx);
  345                 string = new_string;
  346         } else
  347                 string = rule_string;
  348 
  349         error = sysctl_handle_string(oidp, string, MAC_RULE_STRING_LEN, req);
  350         if (error)
  351                 goto out;
  352 
  353         if (req->newptr != NULL) {
  354                 copy_string = strdup(string, M_PORTACL);
  355                 TAILQ_INIT(&head);
  356                 error = parse_rules(copy_string, &head);
  357                 free(copy_string, M_PORTACL);
  358                 if (error)
  359                         goto out;
  360 
  361                 TAILQ_INIT(&save_head);
  362                 mtx_lock(&rule_mtx);
  363                 TAILQ_CONCAT(&save_head, &rule_head, r_entries);
  364                 TAILQ_CONCAT(&rule_head, &head, r_entries);
  365                 strcpy(rule_string, string);
  366                 mtx_unlock(&rule_mtx);
  367                 toast_rules(&save_head);
  368         }
  369 out:
  370         if (new_string != NULL)
  371                 free(new_string, M_PORTACL);
  372         return (error);
  373 }
  374 
  375 SYSCTL_PROC(_security_mac_portacl, OID_AUTO, rules,
  376     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
  377     0, 0, sysctl_rules, "A",
  378     "Rules");
  379 
  380 static int
  381 rules_check(struct ucred *cred, int family, int type, u_int16_t port)
  382 {
  383         struct rule *rule;
  384         int error;
  385 
  386 #if 0
  387         printf("Check requested for euid %d, family %d, type %d, port %d\n",
  388             cred->cr_uid, family, type, port);
  389 #endif
  390 
  391         if (port > portacl_port_high)
  392                 return (0);
  393 
  394         error = EPERM;
  395         mtx_lock(&rule_mtx);
  396         for (rule = TAILQ_FIRST(&rule_head);
  397             rule != NULL;
  398             rule = TAILQ_NEXT(rule, r_entries)) {
  399                 if (type == SOCK_DGRAM && rule->r_protocol != RULE_PROTO_UDP)
  400                         continue;
  401                 if (type == SOCK_STREAM && rule->r_protocol != RULE_PROTO_TCP)
  402                         continue;
  403                 if (port != rule->r_port)
  404                         continue;
  405                 if (rule->r_idtype == RULE_UID) {
  406                         if (cred->cr_uid == rule->r_id) {
  407                                 error = 0;
  408                                 break;
  409                         }
  410                 } else if (rule->r_idtype == RULE_GID) {
  411                         if (cred->cr_gid == rule->r_id) {
  412                                 error = 0;
  413                                 break;
  414                         } else if (groupmember(rule->r_id, cred)) {
  415                                 error = 0;
  416                                 break;
  417                         }
  418                 } else
  419                         panic("rules_check: unknown rule type %d",
  420                             rule->r_idtype);
  421         }
  422         mtx_unlock(&rule_mtx);
  423 
  424         if (error != 0 && portacl_suser_exempt != 0)
  425                 error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT);
  426 
  427         return (error);
  428 }
  429 
  430 /*
  431  * Note, this only limits the ability to explicitly bind a port, it
  432  * doesn't limit implicitly bound ports for outgoing connections where
  433  * the source port is left up to the IP stack to determine automatically.
  434  */
  435 static int
  436 socket_check_bind(struct ucred *cred, struct socket *so,
  437     struct label *solabel, struct sockaddr *sa)
  438 {
  439         struct sockaddr_in *sin;
  440         struct inpcb *inp;
  441         int family, type;
  442         u_int16_t port;
  443 
  444         /* Only run if we are enabled. */
  445         if (portacl_enabled == 0)
  446                 return (0);
  447 
  448         /* Only interested in IPv4 and IPv6 sockets. */
  449         if (so->so_proto->pr_domain->dom_family != PF_INET &&
  450             so->so_proto->pr_domain->dom_family != PF_INET6)
  451                 return (0);
  452 
  453         /* Currently, we don't attempt to deal with SOCK_RAW, etc. */
  454         if (so->so_type != SOCK_DGRAM &&
  455             so->so_type != SOCK_STREAM)
  456                 return (0);
  457 
  458         /* Reject addresses we don't understand; fail closed. */
  459         if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
  460                 return (EINVAL);
  461 
  462         family = so->so_proto->pr_domain->dom_family;
  463         type = so->so_type;
  464         sin = (struct sockaddr_in *) sa;
  465         port = ntohs(sin->sin_port);
  466 
  467         /*
  468          * Sockets are frequently bound with a specific IP address but a port
  469          * number of '' to request automatic port allocation.  This is often
  470          * desirable as long as IP_PORTRANGELOW isn't set, which might permit
  471          * automatic allocation of a "privileged" port.  The autoport exempt
  472          * flag exempts port 0 allocation from rule checking as long as a low
  473          * port isn't required.
  474          */
  475         if (portacl_autoport_exempt && port == 0) {
  476                 inp = sotoinpcb(so);
  477                 if ((inp->inp_flags & INP_LOWPORT) == 0)
  478                         return (0);
  479         }
  480 
  481         return (rules_check(cred, family, type, port));
  482 }
  483 
  484 static struct mac_policy_ops portacl_ops =
  485 {
  486         .mpo_destroy = destroy,
  487         .mpo_init = init,
  488         .mpo_socket_check_bind = socket_check_bind,
  489 };
  490 
  491 MAC_POLICY_SET(&portacl_ops, mac_portacl, "TrustedBSD MAC/portacl",
  492     MPC_LOADTIME_FLAG_UNLOADOK, NULL);
  493 MODULE_VERSION(mac_portacl, 1);

Cache object: 86079b7d01f1c36a213b68284a1f6d77


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.