Linux.Sys.LastUserLogin

Find and parse system wtmp files. This indicate when the user last logged in.


name: Linux.Sys.LastUserLogin
description: |
  Find and parse system wtmp files. This indicate when the user last
  logged in.

parameters:
  - name: wtmpGlobs
    default: /var/log/wtmp*

  - name: MaxCount
    default: 10000
    type: int64

  - name: LoginType
    type: choices
    default: Interactive Sessions
    choices:
        - Interactive Sessions
        - All Sessions
    description: |
      Per default, we are only interested in interactive sessions, if
      you want to see more, choose the second option


  - name: recent_x_days
    default: 100000
    type: int
    description: |
      show all logs within the last X days (default 14 days)

  - name: excluded_users
    type: regex
    default: "ansible|LOGIN"
    description: |
      List of Users (regex), you are not interested in

export: |
  LET FilterLookup = dict(
     `Interactive Sessions`="USER_PROCESS|LOGIN_PROCESS",
     `All Sessions`="RUN_LVL|BOOT_TIME|INIT_PROCESS|LOGIN_PROCESS|USER_PROCESS")

  LET wtmpProfile <= '''
  [
    ["Header", 0, [

    ["records", 0, "Array", {
        "type": "utmp",
        "count": "x=>MaxCount",
        "max_count": 100000,
    }],
    ]],
    ["utmp", 384, [
        ["ut_type", 0, "Enumeration", {
            "type": "short int",
            "choices": {
               "0": "EMPTY",
               "1": "RUN_LVL",
               "2": "BOOT_TIME",
               "5": "INIT_PROCESS",
               "6": "LOGIN_PROCESS",
               "7": "USER_PROCESS",
               "8": "DEAD_PROCESS"
             }
          }],
        ["ut_pid", 4, "int"],
        ["ut_terminal", 8, "String", {"length": 32}],
        ["ut_terminal_identifier", 40, "String", {"length": 4}],
        ["ut_user", 44, "String", {"length": 32}],
        ["ut_hostname", 76, "String", {"length": 256}],
        ["ut_termination_status", 332, "int"],
        ["ut_exit_status", 334, "int"],
        ["ut_session", 336, "int"],
        ["ut_timestamp", 340, "Timestamp", {
            "type": "uint32",
        }],
        ["ut_ip_address", 348, "int64"],
    ]
    ]
    ]]
    ]'''

sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'

    query: |
      LET LoginType <= get(item=FilterLookup, field=LoginType) || LoginType
      LET start_time <= timestamp(epoch=now() - recent_x_days * 3600 * 24)

      LET _ <= log(message="Start time %v", args=start_time)

      LET parsed = SELECT OSPath, parse_binary(
                   filename=OSPath,
                   profile=wtmpProfile,
                   struct="Header"
                 ) AS Parsed
      FROM glob(globs=split(string=wtmpGlobs, sep=","))

      // In Order to combine Login/Logout into one Table, we create a
      // logout table first
      LET logout_table <= SELECT * FROM foreach(row=parsed,
      query={
         SELECT * FROM foreach(row=Parsed.records,
         query={
           SELECT ut_type AS logout_Type,
              ut_pid as logout_PID,
              ut_terminal as logout_Terminal,
              ut_timestamp as logout_time
           FROM scope()
           WHERE logout_Type = "DEAD_PROCESS"
             AND logout_time > start_time
        })
      })
      Order by logout_time DESC

      SELECT * FROM foreach(row=parsed,
      query={
         SELECT * FROM foreach(row=Parsed.records,
         query={
           SELECT OSPath,
              ut_type AS login_Type,
              ut_id AS login_ID,
              ut_pid as login_PID,
              ut_hostname as login_Host,
              ut_user as login_User,
              ip(netaddr4_le=ut_ip_address) AS login_IpAddr,
              ut_terminal as login_Terminal,
              ut_timestamp as login_time, {
                SELECT logout_time
                FROM logout_table
                WHERE ut_pid = logout_PID
                  AND ut_terminal = logout_Terminal
                  AND ut_timestamp < logout_time
                LIMIT 1
              } AS logout_time
          FROM scope()
          WHERE login_Type =~ LoginType
            AND NOT login_User =~ excluded_users
            AND login_time > start_time
        })
      })