Skip to content

Tag: <task> - Audit Users on a Device (user_show_all)

Definition

When Osirium PAM provisions a device, it runs a special task to discover all the local users present on the device. This task is called user_show_all.

This task is special in that it is the only task in Osirium PAM that can return multiple results back to Osirium PAM, an array if you like.

There is going to be multiple users on the device so the task needs to return details of these.

Example: Simple username one per line

Heres what a typical simpleuser_show_all` task looks like:

1
2
3
4
5
6
7
   <task name="user_show_all" type="dataset">
      <commands>
         <command format="textual_dataset">show running-config | include username
            <attribute name="name" word_nr="1"/>
         </command>
      </commands>
   </task>

Here`s how it works:

  • The task MUST be called user_show_all.
  • The task type MUST be dataset.
  • The command format MUST be textual_dataset.
  • The command tag MUST contain at least one attribute tag, whose name is name.

The user_show_all task is the only task in Osirium PAM that works on multiple items, known as sections. By default each section is a line of the response, i.e. the section delimiter is a new line character. Multiple line sections can also be handle as shown further below.

Consider the example above, the show running-config | include username command displays multiple user names, one per line, like this:

1
2
3
4
5
6
7
   cisco-2960-001#show running-config | include username
   username admin privilege 15 secret 7 $***********************/
   username fred privilege 15 secret 5 $***********************/
   username bob privilege 15 secret 5 $***********************/
   username alexandra privilege 15 secret 5 $***********************/
   username john privilege 15 secret 5 $***********************/
   cisco-2960-001#

The attribute tag pulls the name attribute from EACH line, but bear in mind the word_nr count starts at 0. So this task would return a dataset of the following:

1
2
3
4
5
   admin
   fred
   bob
   alexandra
   john

There are many ways that different devices show different user lists. Here are a few examples of the different ways devices might display users and examples of how the user_show_all task would be constructed to read the usernames.

Example: User table with header rows

A user list might be display on the command line in a text based table, like this::

1
2
3
4
5
    User    Pwd Hash
    --------------------
    admin   ************
    user1   ************
    user2   ************

In this example we still want to read the first word of each row, but we want to skip the first two rows as they are the headers and the row of dashes. Otherwise you will end up with a user found called User and another user found called --------------------!

To skip the first rows returned from the device, we use the preamble_line_count attribute like this:

1
2
3
4
5
6
7
8
   <task name="user_show_all" type="dataset">
      <commands>

         <command format="textual_dataset" preamble_line_count="2">show users
            <attribute name="name" word_nr="0"/>
         </command>
      </commands>
   </task>

The preamble_line_count attribute tells the task to skip the first two lines of the reply from the device. The attribute tag word_nr attribute then defines the word number to extract from the line (default word delimiter is a space or spaces).

.. note:: Again the word_nr count starts from 0.

You can also skip footer rows using the postamble_line_count attribute. Just like preamble_line_count, this simply defines how many lines to skip at the end of the output. Given the following device output::

1
2
3
4
5
6
7
    -------+---------------
    User    Pwd Hash
    -------+---------------
    admin   ************
    user1   ************
    user2   ************
    -------+---------------

The user_show_all task would look like:

1
2
3
4
5
6
7
   <task name="user_show_all" type="dataset">
      <commands>
         <command format="textual_dataset" preamble_line_count="3" postamble_line_count="1">show users
            <attribute name="name" word_nr="0"/>
         </command>
      </commands>
   </task>

Example: Multi line sections defining users and regex extraction of username

The user_show_all task breaks the devices response into sections to allow each to we worked on in turn, like a loop. The default section delimiter is a new line which means that by default sections are basically lines.

This can be overwritten using the section_end_delimiter attribute. this can be set to anything that marks the end of a section.

Consider a device output of (this is loosely based on an F5 CLI user output):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
   auth user admin {
       encrypted-password "$****************************/"
       role admin
   }
   auth user mr-jones {
       encrypted-password "$****************************/"
       role admin
   }
   auth user mr-smith {
       encrypted-password "$****************************/"
       role admin
   }

Here we want to tell Osirium PAM that we have multiline sections, and that each section ends with a close curly bracket, at the start of a line.

So we might have a task that looks like:

1
2
3
4
5
6
7
   <task name="user_show_all" type="dataset">
      <commands>
         <command format="textual_dataset" section_end_delimiter="^}">show auth user
            <attribute name="name" regex="^.*user\s(.*)\s{$"/>
         </command>
      </commands>
   </task>

This user_show_all task also shows how the attribute tag is used to extract the user name with a regex capture group. You could also use word_nr="3" to achieve the same result.

Example: Usernames delimited with a different character

If you parse, for example, a Linux pwd file then the delimiter in this file is a colon not a space. For example, a device might return::

1
2
3
4
5
6
   syslog:x:101:103::/home/syslog:/bin/false
   mysql:x:102:105:MySQL Server,,,:/nonexistent:/bin/false
   messagebus:x:103:106::/var/run/dbus:/bin/false
   sshd:x:106:65534::/var/run/sshd:/usr/sbin/nologin
   pearcek:x:1000:1000:Kev Pearce,,,:/home/pearcek:/bin/bash
   ntp:x:107:115::/home/ntp:/bin/false

Here, to pull out the usernames you would use a regex to pull the all the letters upto the first colon, like this:

1
2
3
4
5
6
7
   <task name="user_show_all" type="dataset">
      <commands>
         <command format="textual_dataset">sudo cat /etc/passwd
            <attribute name="name" regex="^(.*):.*$"/>
         </command>
      </commands>
   </task>

Example: Multiple usernames per line

You might get more than one username on a line.

The Osirium PAM can handle this by changing the section delimiter from the default of a new line, to a space. As the user_show_all task works on sections, we are simply redefining the section to be each word on the line and discard the delimiter (or multiples of the delimiter). This is done with the section_line_split attribute.

The Windows net user command is a good example of a user_show_all task that shows multiple users per line. This might look like::

1
2
3
4
5
6
   User accounts for \\SERVER-ABC

   -------------------------------------------------------------------------------
   Administrator            Guest                    Cliff
   SecOps                   NetOps                   JohnR
   XavierT

The task for this would then look like:

1
2
3
4
5
6
7
   <task name="user_show_all" type="dataset">
      <commands>
         <command format="textual_dataset" preamble_line_count="3" section_line_split=" ">net user
            <attribute name="name"/>
         </command>
      </commands>
   </task>

Delimiter or split characters are treated the same if there is one or more is succession. So in this case we end up with each section just containing the username entirely on its own, not additional regex or counting words is required. We simply just return each one as the name attribute.

Example: Parsing username from XML output

It the device supports specifying the output structure to be XML then this can be easily parsed. The command tag attribute format needs to be set to xml_dataset. You can then specify the outer tag that holds each user info, in this example it is called row. Then the attribute tag can have an elem attribute which names the tag in the output xml that holds the username within the various user properties (rows). Consider this XML output::

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
   <Results>
      <Row>
         <User>admin</User>
         <Role>Admin</Role>
      </Row>
      <Row>
         <User>alice</User>
         <Role>Admin</Role>
      </Row>
      <Row>
         <User>bob</User>
         <Role>Admin</Role>
      </Row>
   <Results>

The user_show_all task for this then might look like:

1
2
3
4
5
6
7
   <task name="user_show_all" type="dataset">
      <commands>
         <command entry_elem="Row" format="xml_dataset">clish -o xml -c show users
            <attribute elem="User" name="name"/>
         </command>
      </commands>
   </task>

Note the task is still of type dataset, this does not change.

Other Output Formats

There may well be many other ways devices display data back when listing users, but using the examples on this pages and ultimately the power of regex to extract the usernames, most situations can be handled.

Parent Tags

Child Tags