On Fri, Aug 15, 2014 at 12:20:41PM -0700, [email protected] wrote:
> 
> 

I'm slowly working my way through this. A couple of code comments follow:

> --- /dev/null
> +++ 2.9-test/parser/af_rule.cc
> @@ -0,0 +1,171 @@
> +/*
> + *   Copyright (c) 2014
> + *   Canonical, Ltd. (All rights reserved)
> + *
> + *   This program is free software; you can redistribute it and/or
> + *   modify it under the terms of version 2 of the GNU General Public
> + *   License published by the Free Software Foundation.
> + *
> + *   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, contact Novell, Inc. or Canonical
> + *   Ltd.
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/apparmor.h>
> +
> +#include <iomanip>
> +#include <string>
> +#include <iostream>
> +#include <sstream>
> +
> +#include "network.h"
> +#include "parser.h"
> +#include "profile.h"
> +#include "parser_yacc.h"
> +#include "af_rule.h"
> +
> +
> +/* need to move this table to stl */
> +static struct supported_cond supported_conds[] = {
> +     { "type", true, false, false, local_cond },
> +     { "protocol", false, false, false, local_cond },
> +     { "label", true, false, false, peer_cond },
> +     { NULL, false, false, false, local_cond },      /* eol sentinal */
> +};
> +
> +int af_rule::cond_check(struct supported_cond *conds, struct cond_entry *ent,
> +                     bool peer, const char *rname)
> +{
> +     struct supported_cond *i;
> +     for (i = conds; i->name; i++) {
> +             if (strcmp(ent->name, i->name) != 0)
> +                     continue;
> +             if (!i->supported)
> +                     yyerror("%s rule: '%s' conditional is not currently 
> supported\n", rname, ent->name);
> +             if (!peer && (i->side == peer_cond))
> +                     yyerror("%s rule: '%s' conditional is only valid in the 
> peer expression\n", rname, ent->name);
> +             if (peer && (i->side == local_cond))
> +                     yyerror("%s rule: '%s' conditional is not allowed in 
> the peer expression\n", rname, ent->name);
> +             if (!ent->eq && !i->in)
> +                     yyerror("%s rule: keyword 'in' is not allowed in '%s' 
> socket conditional\n", rname, ent->name);
> +             if (list_len(ent->vals) > 1 && !i->multivalue)
> +                     yyerror("%s rule: conditional '%s' only supports a 
> single value\n", rname, ent->name);
> +             return true;
> +     }
> +
> +     /* not in support table */
> +     return false;
> +}
> +
> +/* generic af supported conds.
> + * returns: true if processed, else false
> + */
> +int af_rule::move_base_cond(struct cond_entry *ent, bool peer)
> +{
> +     if (!cond_check(supported_conds, ent, peer, "unknown"))
> +             return false;
> +
> +     if (strcmp(ent->name, "type") == 0) {
> +             move_conditional_value("socket rule", &sock_type, ent);
> +             sock_type_n = net_find_type_val(sock_type);
> +             if (sock_type_n == -1)
> +                     yyerror("socket rule: invalid socket type '%s'", 
> sock_type);
> +     } else if (strcmp(ent->name, "protocol") == 0) {
> +             yyerror("socket rule: 'protocol' conditional is not currently 
> supported\n");
> +     } else if (strcmp(ent->name, "label") == 0) {
> +             if (peer)
> +                     move_conditional_value("unix", &label, ent);
> +             else
> +                     move_conditional_value("unix", &peer_label, ent);

Is the logic above here correct? Or am I really confused about what
'peer' is supposed to represent?

> +     } else
> +             return false;
> +
> +     return true;
> +}
> +
> +ostream &af_rule::dump_prefix(ostream &os)
> +{
> +     if (audit)
> +             os << "audit ";
> +     if (deny)
> +             os << "deny ";
> +     return os;
> +}
[SNIP]

> --- /dev/null
> +++ 2.9-test/parser/af_unix.cc
> @@ -0,0 +1,386 @@
> +/*
> + *   Copyright (c) 2014
> + *   Canonical, Ltd. (All rights reserved)
> + *
> + *   This program is free software; you can redistribute it and/or
> + *   modify it under the terms of version 2 of the GNU General Public
> + *   License published by the Free Software Foundation.
> + *
> + *   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, contact Novell, Inc. or Canonical
> + *   Ltd.
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/apparmor.h>
> +
> +#include <iomanip>
> +#include <string>
> +#include <iostream>
> +#include <sstream>
> +
> +#include "network.h"
> +#include "parser.h"
> +#include "profile.h"
> +#include "af_unix.h"
> +
> +int parse_unix_mode(const char *str_mode, int *mode, int fail)
> +{
> +     return parse_X_mode("unix", AA_VALID_NET_PERMS, str_mode, mode, fail);
> +}
> +
> +
> +static struct supported_cond supported_conds[] = {
> +     { "path", true, false, false, either_cond },
> +     { NULL, false, false, false, local_cond },      /* sentinal */
> +};
> +
> +void unix_rule::move_conditionals(struct cond_entry *conds)
> +{
> +     struct cond_entry *ent;
> +
> +     list_for_each(conds, ent) {
> +
> +             if (!cond_check(supported_conds, ent, false, "unix") &&
> +                 !move_base_cond(ent, false)) {
> +                     yyerror("unix rule: invalid conditional '%s'\n",
> +                             ent->name);
> +                     continue;
> +             }
> +             if (strcmp(ent->name, "path") == 0) {
> +                     move_conditional_value("unix socket", &path, ent);
> +                     if (path[0] != '@' && strcmp(path, "none") != 0)
> +                             yyerror("unix rule: invalid value for 
> path='%s'\n", path);
> +             }
> +
> +             /* TODO: add conditionals for
> +              *   listen queue length
> +              *   attrs that can be read/set
> +              *   ops that can be read/set
> +              * allow in on
> +              *   type, protocol
> +              * local label match, and set
> +              */
> +     }
> +}
> +
> +void unix_rule::move_peer_conditionals(struct cond_entry *conds)
> +{
> +     struct cond_entry *ent;
> +
> +     list_for_each(conds, ent) {
> +             if (!cond_check(supported_conds, ent, true, "unix") &&
> +                 !move_base_cond(ent, true)) {
> +                     yyerror("unix rule: invalid peer conditional '%s'\n",
> +                             ent->name);
> +                     continue;
> +             }
> +             if (strcmp(ent->name, "path") == 0) {
> +                     move_conditional_value("unix", &peer_path, ent);
> +                     if (peer_path[0] != '@' && strcmp(path, "none") != 0)
> +                             yyerror("unix rule: invalid value for 
> path='%s'\n", peer_path);
> +             }
> +     }
> +}
> +
> +unix_rule::unix_rule(unsigned int type_p, bool audit_p, bool denied):
> +     af_rule("unix"), path(NULL), peer_path(NULL)
> +{
> +     if (type_p != 0xffffffff) {
> +             sock_type_n = type_p;
> +             sock_type = strdup(net_find_type_name(type_p));
> +             if (!sock_type)
> +                     yyerror("socket rule: invalid socket type '%d'", 
> type_p);
> +     }
> +     mode = AA_VALID_NET_PERMS;
> +     audit = audit_p ? AA_VALID_NET_PERMS : 0;
> +     deny = denied;
> +}
> +
> +unix_rule::unix_rule(int mode_p, struct cond_entry *conds,
> +                  struct cond_entry *peer_conds):
> +     af_rule("unix"), path(NULL), peer_path(NULL)
> +{
> +     move_conditionals(conds);
> +     move_peer_conditionals(peer_conds);
> +
> +     if (mode_p) {
> +             mode = mode_p;
> +             if (mode & ~AA_VALID_NET_PERMS)
> +                     yyerror("mode contains invalid permissions for unix 
> socket rules\n");
> +             else if ((mode & AA_NET_BIND) &&
> +                      ((mode & AA_PEER_NET_PERMS) || has_peer_conds()))
> +                     /* Do we want to loosen this? */
> +                     yyerror("unix socket 'bind' access cannot be used with 
> message rule conditionals\n");
> +             else if ((mode & AA_NET_LISTEN) &&
> +                      ((mode & AA_PEER_NET_PERMS) || has_peer_conds()))
> +                     /* Do we want to loosen this? */
> +                     yyerror("unix socket 'listen' access cannot be used 
> with message rule conditionals\n");
> +             else if ((mode & AA_NET_ACCEPT) &&
> +                      ((mode & AA_PEER_NET_PERMS) || has_peer_conds()))
> +                     /* Do we want to loosen this? */
> +                     yyerror("unix socket 'accept' access cannot be used 
> with message rule conditionals\n");
> +     } else {
> +             mode = AA_VALID_NET_PERMS;
> +     }
> +
> +     free_cond_list(conds);
> +     free_cond_list(peer_conds);
> +
> +}
> +
> +ostream &unix_rule::dump_local(ostream &os)
> +{
> +     af_rule::dump_local(os);
> +     if (path)
> +             os << "path='" << path << "'";
> +     return os;
> +}
> +
> +ostream &unix_rule::dump_peer(ostream &os)
> +{
> +     af_rule::dump_peer(os);
> +     if (peer_path)
> +             os << "path='" << peer_path << "'";
> +     return os;
> +}
> +
> +
> +int unix_rule::expand_variables(void)
> +{
> +     int error = af_rule::expand_variables();
> +     if (error)
> +             return error;
> +     error = expand_entry_variables(&path);
> +     if (error)
> +             return error;
> +     error = expand_entry_variables(&peer_path);
> +     if (error)
> +             return error;
> +
> +     return 0;
> +}
> +
> +/* do we want to warn once/profile or just once per compile?? */
> +static void warn_once(const char *name, const char *msg)
> +{
> +     static const char *warned_name = NULL;
> +
> +     if (warned_name != name) {
> +             cerr << "Warning from profile " << name << " (";
> +             if (current_filename)
> +                     cerr << current_filename;
> +             else
> +                     cerr << "stdin";
> +             cerr << "): " << msg << "\n";
> +             warned_name = name;
> +     }
> +}
> +
> +static void warn_once(const char *name)
> +{
> +     warn_once(name, "extended network unix socket rules not enforced");
> +}
> +
> +std::ostringstream &writeu16(std::ostringstream &o, int v)
> +{
> +     u16 tmp = htobe16((u16) v);
> +     char *c = (char *) &tmp;
> +     o << "\\x" << std::setfill('0') << std::setw(2) << std::hex << *c++;
> +     o << "\\x" << std::setfill('0') << std::setw(2) << std::hex << *c;
> +     return o;
> +}
> +
> +#define CMD_ADDR     1
> +#define CMD_LISTEN   2
> +#define CMD_ACCEPT   3
> +#define CMD_OPT              4
> +
> +void unix_rule::downgrade_rule(Profile &prof) {
> +     if (!prof.net.allow && !prof.alloc_net_table())
> +             yyerror(_("Memory allocation error."));
> +     if (deny) {
> +             prof.net.deny[AF_UNIX] |= 1 << sock_type_n;
> +             if (!audit)
> +                     prof.net.quiet[AF_UNIX] |= 1 << sock_type_n;
> +     } else {
> +             prof.net.allow[AF_UNIX] |= 1 << sock_type_n;
> +             if (audit)
> +                     prof.net.audit[AF_UNIX] |= 1 << sock_type_n;
> +     }
> +}
> +
> +int unix_rule::gen_policy_re(Profile &prof)
> +{
> +     std::ostringstream buffer, tmp;
> +     std::string buf;
> +
> +     pattern_t ptype;
> +     int pos;
> +     int mask = mode;
> +
> +     /* always generate a downgraded rule. This doesn't change generated
> +      * policy size and allows the binary policy to be loaded against
> +      * older kernels and be enforced to the best of the old network
> +      * rules ability
> +      */
> +     downgrade_rule(prof);
> +     if (!kernel_supports_unix) {
> +             if (kernel_supports_network) {
> +                     /* only warn if we are building against a kernel
> +                      * that requires downgrading */
> +                     warn_once(prof.name, "downgrading extended network unix 
> socket rule to generic network rule\n");
> +                     /* TODO: add ability to abort instead of downgrade */
> +                     return RULE_OK;
> +             }
> +             warn_once(prof.name);
> +             return RULE_NOT_SUPPORTED;
> +     }
> +
> +
> +     buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << 
> AA_CLASS_NET;
> +     buffer << writeu16(buffer, AF_UNIX);
> +     if (sock_type)
> +             buffer << writeu16(buffer, sock_type_n);
> +     else
> +             buffer << "..";
> +     if (proto)
> +             buffer << writeu16(buffer, proto_n);
> +     else
> +             buffer << "..";
> +
> +     if (mask & AA_NET_CREATE) {
> +             buf = buffer.str();
> +             if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +                                              AA_NET_CREATE,
> +                                              audit & AA_NET_CREATE,
> +                                              dfaflags))
> +                     goto fail;
> +             mask &= ~AA_NET_CREATE;
> +     }
> +
> +     /* local addr */
> +     if (path) {
> +             if (strcmp(path, "none") == 0) {
> +                     buffer << "\\x01";
> +             } else {
> +                     /* skip leading @ */
> +                     ptype = convert_aaregex_to_pcre(path + 1, 0, buf, &pos);
> +                     if (ptype == ePatternInvalid)
> +                             goto fail;
> +                     /* kernel starts abstract with \0 */
> +                     buffer << "\\x00";
> +                     buffer << buf;
> +             }
> +     } else
> +             buffer << ".*";
> +
> +     /* change to out of band separator */
> +     buffer << "\\x00";
> +
> +     if (mask & AA_LOCAL_NET_PERMS) {
> +             /* local label option */
> +             if (label) {
> +                     ptype = convert_aaregex_to_pcre(label, 0, buf, &pos);
> +                     if (ptype == ePatternInvalid)
> +                             goto fail;
> +                     /* kernel starts abstract with \0 */
> +                     buffer << buf;
> +             } else
> +                     tmp << anyone_match_pattern;
> +             buffer << "\\x00";
> +
> +             /* create already masked off */
> +             if (mask & AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD) {
> +                     buf = buffer.str();
> +                     if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +                                                      mask & 
> AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD,
> +                                                      audit & 
> AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD,
> +                                                      dfaflags))
> +                             goto fail;
> +             }
> +
> +             /* cmd selector - drop accept??? */
> +             if (mask & AA_NET_ACCEPT) {
> +                     tmp.str(buffer.str());
> +                     tmp << "\\x" << std::setfill('0') << std::setw(2) << 
> std::hex << CMD_ACCEPT;
> +                     buf = tmp.str();
> +                     if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +                                                      AA_NET_ACCEPT,
> +                                                      audit & AA_NET_ACCEPT,
> +                                                      dfaflags))
> +                             goto fail;
> +             }
> +             if (mask & AA_NET_LISTEN) {
> +                     tmp.str(buffer.str());
> +                     tmp << "\\x" << std::setfill('0') << std::setw(2) << 
> std::hex << CMD_LISTEN;
> +                     /* TODO: backlog conditional */
> +                     tmp << "..";
> +                     buf = tmp.str();
> +                     if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +                                                      AA_NET_LISTEN,
> +                                                      audit & AA_NET_LISTEN,
> +                                                      dfaflags))
> +                             goto fail;
> +             }
> +             if (mask & AA_NET_OPT) {
> +                     tmp.str(buffer.str());
> +                     tmp << "\\x" << std::setfill('0') << std::setw(2) << 
> std::hex << CMD_OPT;
> +                     /* TODO: sockopt conditional */
> +                     tmp << "..";
> +                     buf = tmp.str();
> +                     if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +                                                      AA_NET_OPT,
> +                                                      audit & AA_NET_OPT,
> +                                                      dfaflags))
> +                             goto fail;
> +             }
> +             mask &= ~AA_LOCAL_NET_PERMS;
> +     }
> +
> +     if (mask & AA_PEER_NET_PERMS) {
> +             /* cmd selector */
> +             buffer << "\\x" << std::setfill('0') << std::setw(2) << 
> std::hex << CMD_ADDR;
> +
> +             /* peer addr */
> +             if (peer_path) {
> +                     if (strcmp(path, "none") == 0) {
> +                             buffer << "\\x01";

The argument to strcmp should be peer_path, no? (Thank you, clang's
scan-build.)

> +                     } else {
> +                             /* skip leading @ */
> +                             ptype = convert_aaregex_to_pcre(peer_path + 1, 
> 0, buf, &pos);
> +                             if (ptype == ePatternInvalid)
> +                                     goto fail;
> +                             /* kernel starts abstract with \0 */
> +                             buffer << "\\x00";
> +                             buffer << buf;
> +                     }
> +             }
> +             /* change to out of band separator */
> +             buffer << "\\x00";
> +
> +             if (peer_label) {
> +                     ptype = convert_aaregex_to_pcre(peer_label, 0, buf, 
> &pos);
> +                     if (ptype == ePatternInvalid)
> +                             goto fail;
> +                     buffer << buf;
> +             } else {
> +                     buffer << anyone_match_pattern;
> +             }
> +
> +             buf = buffer.str();
> +             if (!prof.policy.rules->add_rule(buf.c_str(), deny, mode & 
> AA_PEER_NET_PERMS, audit, dfaflags))
> +                     goto fail;
> +     }
> +
> +     return RULE_OK;
> +
> +fail:
> +     return RULE_ERROR;
> +}

-- 
Steve Beattie
<[email protected]>
http://NxNW.org/~steve/

Attachment: signature.asc
Description: Digital signature

-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to