Since we seem to have a security theme building here this morning, I've decided to post a subroutine I've been working on. It's part of a module, actually the root module in a hierarchy of three, that enables arbitrary actions to be taken at later dates. Subclasses expand this behavior to include execution at specific, possibly recurring dates, and a mini-'language' for constructing the more common sorts of jobs. It's also capable of contacting users for purposes of notification and approval. Before continuing, I want to stress two points:

  1. This is an in-house system and under no circumstances will this, say, end up sitting on a public Web server with a nice friendly CGI interface.
  2. Most of the time these modules will be reached via other in-house applications, thereby further limiting access.

Now, in our system we have various usertypes. Every user has at least one assigned to him/her, and it's common to have more. There are three primary groups I'm concerned with: programmers, editors, and consultants. Programmers ought to be able to do whatever they want; editors are a highly trusted bunch, but still ought to have some restrictions; consultants are at the outskirts of the organization and have a high turnover rate, and so, while allowed to have some access to this system, need to have some heavy restrictions placed upon them. Furthermore, these usertypes may be modified by the attributes 'former' or 'admin' or 'trainee' (more precisely: ('former) || ('admin' || 'trainee')). Thus, we have things like 'former consultant', 'editor admin', 'programmer trainee', etc. I for example am a former editor and a programmer. What I want to do is limit the sorts of code that a user can run by tying particular opsets to trust levels assigned by usertype. This is accomplished using the Safe module. In my case, the fact that I'm a former editor should win me no trust, however the fact that I'm a programmer should enable me to do whatever I want. So, the assignment has to be smart enough to give trust where and only where it's due, and take it away in the same manner.

That said, here's the code:

sub _permittedOps() { my ($this) = @_; # Hashes for mapping in order to avoid if-else chaining my (%attr_value, %high_type, %trust_level); # A map associating opcode sets with trust levels. %trust_level = (full => full_opset(), strong => opset(qw/:default :filesys_read :sys_db :filesys_open :filesys_write :ownproc +ess :subprocess/), normal => opset(qw/:default/), weak => opset(qw/:base_core :base_mem :base_loop :base_io :base_orig/), none => opset(qw/:base_core :base_loop/) ); # A map associating usertype attributes with positive/negative val +ues # affecting the level of trust assigned to the user. %attr_value = (admin => 1, consultant => -1, editor => 0, former = +> -2, other => -2, programmer => 2, trainee => -1); # Run through all of the assigned usertypes, adjusting the trust l +evel in %high_type # granted the user for each particular usertype. $1, if defined wi +ll be # 'former', $2 will be either 'consultant', 'editor', or 'programm +er'. # $3, if defined, will be either 'admin' or 'trainee'. foreach my $type (@usertypes) { # @usertypes is defined in the rea +l code, don't worry :) if ($type =~ /^(former)?\s?(consultant|editor|programmer)\s? (admin|trainee)?$/ix) { unless ($1) { # Look up the appropriate usertype and attribute values. $high_type{$2} += $attr_value{$2}; $high_type{$2} += $attr_value{$3} if $3; } else { $high_type{$2} += $attr_value{$1} } } else { $high_type{other} += $attr_value{other}; } } # Take the maximum trust available. my $max = pop(@{[sort values %high_type]}); # Return the appropriate opset. ($max > 1) and return $trust_level{full}; ($max == 1) and return $trust_level{strong}; ($max == 0) and return $trust_level{normal}; ($max == -1) and return $trust_level{weak}; ($max < -1) and return $trust_level{none}; }

What I'd like is some feedback on a few separate issues:

  1. Is this a sane breakdown of opsets?
  2. Are there holes in the algorithm that might enable escalation of privileges?
  3. Is this readable/maintainable?

Many thanks in advance, fever. Oh and it's my birthday so be nice :^P


In reply to Assigning Opsets by User Type by djantzen

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.