Skip to content

Commit

Permalink
Security ssh detection update (#943)
Browse files Browse the repository at this point in the history
* Updated ssh user detection and added nested group detection.

* Script returns string instead of array.
Added separate run for ssh group detection.

* Add SSH groups to client details summary

* Migration attempt

* Growing pains on syntax

* More syntax learning

* Fixed migration.
Removed user verbiage for ssh_group output

* Remove debug code.

* Commit correct migration code

* Tightened up code, removed extra for loop.
  • Loading branch information
poundbangbash authored and bochoven committed Feb 8, 2018
1 parent 891029d commit 9c05815
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 25 deletions.
2 changes: 1 addition & 1 deletion app/controllers/clients.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function get_data($serial_number = '')

$sql = "SELECT m.*, r.console_user, r.long_username, r.remote_ip,
r.uptime, r.reg_timestamp, r.machine_group, r.timestamp,
s.gatekeeper, s.sip, s.ssh_users, s.ard_users, s.firmwarepw, s.firewall_state, s.skel_state,
s.gatekeeper, s.sip, s.ssh_groups, s.ssh_users, s.ard_users, s.firmwarepw, s.firewall_state, s.skel_state,
w.purchase_date, w.end_date, w.status, l.users, d.totalsize, d.freespace,
d.smartstatus, d.encrypted
FROM machine m
Expand Down
1 change: 1 addition & 0 deletions app/modules/security/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"sip_active": "SIP Active",
"sip_disabled": "SIP Disabled",
"sip_status": "SIP Status",
"ssh_groups": "SSH Groups",
"ssh_users": "SSH Users",
"skel": {
"kext-loading": "KEXT Loading",
Expand Down
32 changes: 32 additions & 0 deletions app/modules/security/migrations/2018_01_19_234938_security.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Capsule\Manager as Capsule;

class Security extends Migration
{
private $tableName = 'security';

public function up()
{

$capsule = new Capsule();

$capsule::schema()->table($this->tableName, function (Blueprint $table) {
$table->string('ssh_groups')->after('sip');

});
}

public function down()
{

$capsule = new Capsule();

$capsule::schema()->table($this->tableName, function (Blueprint $table) {
$table->dropColumn('ssh_groups');
});
}
}
85 changes: 63 additions & 22 deletions app/modules/security/scripts/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import subprocess
import plistlib
import grp

def gatekeeper_check():
""" Gatekeeper checks. Simply calls the spctl and parses status. Requires 10.7+"""
Expand Down Expand Up @@ -36,9 +37,10 @@ def sip_check():
return "Not Supported"


def ssh_access_check():
"""Check for users who can log in via SSH using the built-in group. This will not
cover any users who are granted SSH via an AD/OD group."""
def ssh_user_access_check():
"""Check for users who can log in via SSH
using the built-in group reporting.
Checks for explicitly added users , both local and directory based."""

#Check first that SSH is enabled!
sp = subprocess.Popen(['systemsetup', '-getremotelogin'], stdout=subprocess.PIPE)
Expand All @@ -64,29 +66,67 @@ def ssh_access_check():

elif "com.apple.access_ssh" in out:
# if this group exists, SSH is enabled but only some users are permitted
sp = subprocess.Popen(['dscl', '.', 'list', '/Users'], stdout=subprocess.PIPE)
out, err = sp.communicate()

user_list = out.split()
ssh_users = ''

for user in user_list:
if user[0] in '_': # filter out system users that start with _
continue
else:
sp = subprocess.Popen(['dsmemberutil', 'checkmembership', '-U', user, '-G', 'com.apple.access_ssh'], stdout=subprocess.PIPE)
out, err = sp.communicate()
if 'is a member' in out:
ssh_users += user + ' '
else:
pass
return ssh_users.strip()
# Get a list of users in the com.apple.access_ssh GroupMembership
user_sp = subprocess.Popen(['dscl', '.', 'read', '/Groups/com.apple.access_ssh', 'GroupMembership'], stdout=subprocess.PIPE)
user_out, user_err = user_sp.communicate()
user_list = user_out.split()

return ', '.join(item for item in user_list[1:])

else:
# if neither SSH group exists but SSH is enabled, it was turned on with
# systemsetup and all users are enabled.
return "All users permitted"

def ssh_group_access_check():
"""Check for groups that have members who can log in via SSH
using the built-in group reporting.
Checks for explicitly added groups, both local and directory based."""

#Check first that SSH is enabled!
sp = subprocess.Popen(['systemsetup', '-getremotelogin'], stdout=subprocess.PIPE)
out, err = sp.communicate()

if "Off" in out:
return "SSH Disabled"

else:
# First we need to check if SSH is open to all users. A few ways to tell:
# -on 10.8 and older, systemsetup will show as on but the access_ssh groups are not present
# -on 10.9, systemsetup will show as on and list all users in access_ssh
# -on 10.10 and newer, systemsetup will show as on and access_ssh-disabled will be present
# Note for 10.10 and newer - root will show up as authorized if systemsetup was used to turn
# on SSH, and not if pref pane was used.

sp = subprocess.Popen(['dscl', '.', 'list', '/Groups'], stdout=subprocess.PIPE)
out, err = sp.communicate()

if "com.apple.access_ssh-disabled" in out:
# if this group exists, all users are permitted to access SSH.
# Nothing group specific
pass

elif "com.apple.access_ssh" in out:
# Get a list of UUIDs of Nested Groups
group_sp = subprocess.Popen(['dscl', '.', 'read', '/Groups/com.apple.access_ssh', 'NestedGroups'], stdout=subprocess.PIPE)
group_out, group_err = group_sp.communicate()
group_list_uuid = group_out.split()

# Translate group UUIDs to gids
group_list = []
for group_uuid in group_list_uuid[1:]:
group_id_sp = subprocess.Popen(['dsmemberutil', 'getid', '-x', group_uuid], stdout=subprocess.PIPE)
group_id_out, group_id_err = group_id_sp.communicate()
group_list.append(grp.getgrgid(group_id_out.split()[1]).gr_name)

return ', '.join(item for item in group_list)

else:
# If neither SSH group exists but SSH is enabled, it was turned on with
# systemsetup and all users are enabled.
# Nothing group specific
pass

def ard_access_check():
"""Check for local users who have ARD permissions
First we need to check if all users are allowed. If not, we look for granular permissions
Expand Down Expand Up @@ -199,7 +239,8 @@ def main():
result = {}
result.update({'gatekeeper': gatekeeper_check()})
result.update({'sip': sip_check()})
result.update({'ssh_users': ssh_access_check()})
result.update({'ssh_users': ssh_user_access_check()})
result.update({'ssh_groups': ssh_group_access_check()})
result.update({'ard_users': ard_access_check()})
result.update({'firmwarepw': firmware_pw_check()})
result.update({'firewall_state':firewall_enable_check()})
Expand All @@ -208,6 +249,6 @@ def main():
# Write results of checks to cache file
output_plist = os.path.join(cachedir, 'security.plist')
plistlib.writePlist(result, output_plist)

if __name__ == "__main__":
main()
6 changes: 4 additions & 2 deletions app/modules/security/security_model.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public function __construct($serial = '')
$this->rs['serial_number'] = $serial; //$this->rt['serial_number'] = 'VARCHAR(255) UNIQUE';
$this->rs['gatekeeper'] = '';
$this->rs['sip'] = '';
$this->rs['ssh_groups'] = '';
$this->rs['ssh_users'] = '';
$this->rs['ard_users'] = '';
$this->rs['firmwarepw'] = '';
Expand All @@ -21,14 +22,15 @@ public function __construct($serial = '')
$this->idx[] = array('serial_number');
$this->idx[] = array('gatekeeper');
$this->idx[] = array('sip');
$this->idx[] = array('ssh_groups');
$this->idx[] = array('ssh_users');
$this->idx[] = array('ard_users');
$this->idx[] = array('firmwarepw');
$this->idx[] = array('firewall_state');
$this->idx[] = array('skel_state');

// Schema version, increment when creating a db migration
$this->schema_version = 6;
$this->schema_version = 7;

// Create table if it does not exist
//$this->create_table();
Expand Down Expand Up @@ -61,7 +63,7 @@ public function process($data)

$plist = $parser->toArray();

foreach (array('sip', 'gatekeeper', 'ssh_users', 'ard_users', 'firmwarepw', 'firewall_state', 'skel_state') as $item) {
foreach (array('sip', 'gatekeeper', 'ssh_groups', 'ssh_users', 'ard_users', 'firmwarepw', 'firewall_state', 'skel_state') as $item) {
if (isset($plist[$item])) {
$this->$item = $plist[$item];
} else {
Expand Down
1 change: 1 addition & 0 deletions app/modules/security/views/security_listing.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<th data-i18n="security.firmwarepw" data-colname='security.firmwarepw'></th>
<th data-i18n="security.firewall_state" data-colname='security.firewall_state'></th>
<th data-i18n="security.skel.kext-loading" data-colname='security.skel_state'></th>
<th data-i18n="security.ssh_groups" data-colname='security.ssh_groups'></th>
<th data-i18n="security.ssh_users" data-colname='security.ssh_users'></th>
<th data-i18n="security.ard_users" data-colname='security.ard_users'></th>
</tr>
Expand Down
3 changes: 3 additions & 0 deletions app/views/client/summary_tab.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@
<tr>
<th data-i18n="security.sip"></th><td class="mr-sip"></td>
</tr>
<tr>
<th data-i18n="security.ssh_groups"></th><td class="mr-ssh_groups"></td>
</tr>
<tr>
<th data-i18n="security.ssh_users"></th><td class="mr-ssh_users"></td>
</tr>
Expand Down

0 comments on commit 9c05815

Please sign in to comment.