diff --git a/lib/agent/index.js b/lib/agent/index.js index 810aab333..2f4c24e17 100644 --- a/lib/agent/index.js +++ b/lib/agent/index.js @@ -25,7 +25,7 @@ var config = common.config, helpers = common.helpers, exceptions = common.exceptions, plugins = common.plugins, - watch_list = ['connection', 'control-zones', 'hostname', 'location', 'network', 'power'], + watch_list = ['auto-connect', 'connection', 'control-zones', 'hostname', 'location', 'network', 'power', 'system'], running = false, started_at = null, running_as = null; diff --git a/lib/agent/providers/network/index.js b/lib/agent/providers/network/index.js index 0058a61d6..2d998cb59 100644 --- a/lib/agent/providers/network/index.js +++ b/lib/agent/providers/network/index.js @@ -169,6 +169,25 @@ exp.get_open_access_points_list = function(callback) { }; +exp.get_categorized_access_points_list = function(callback) { + + exp.get_access_points_list(function(err, list) { + if (err || !list) + return callback(err || new Error('No access points detected.')); + + var open_aps = list.filter(function(ap) { + return ap.security == false || ap.security == null; + }); + + var secured_aps = list.filter(function(ap) { + return ap.security; + }); + + callback(null, [open_aps, secured_aps]); + }); + +}; + exp.get_connection_status = function(cb) { var proxy = config.get('try_proxy'), protocol = config.get('control-panel').protocol, diff --git a/lib/agent/providers/network/linux.js b/lib/agent/providers/network/linux.js index f468b2be6..6b4e8972d 100644 --- a/lib/agent/providers/network/linux.js +++ b/lib/agent/providers/network/linux.js @@ -129,3 +129,5 @@ exports.parse_access_points_list = function(output) { }).filter(function(el) { return !!el }) }; + +exports.get_first_wireless_interface = get_first_wireless_interface; \ No newline at end of file diff --git a/lib/agent/triggers/auto-connect/bin/linux_auth.sh b/lib/agent/triggers/auto-connect/bin/linux_auth.sh new file mode 100644 index 000000000..8b3715798 --- /dev/null +++ b/lib/agent/triggers/auto-connect/bin/linux_auth.sh @@ -0,0 +1,124 @@ +#!/bin/bash + +if [ "$EUID" -ne 0 ] + then echo 'Please run as root' + exit +fi + +file="/var/lib/polkit-1/localauthority/50-local.d/10-network-manager.pkla" + +askAutorization () { + read -p "Prey needs autorization to modify network parameters for the autoconnect feature (this is no mandatory for prey to work) (y/n)" answer > $file; fi +} + +setResults() { + key=`cut -d "=" -f 1 <<< "$1"` + value=`cut -d "=" -f 2 <<< "$1"` + + if [[ "$value" != "yes" ]]; then + if [ $authorized == false ]; then askAutorization; fi + + if [ $key == "ResultAny" ]; then ready[1]=2; else ready[2]=2; fi + value="yes" + fi + + echo $key=$value >> $file; +} + +setIdentity() { + key=`cut -d "=" -f 1 <<< "$1"` + value=`cut -d "=" -f 2 <<< "$1"` + + if [[ $value != *":prey"* ]] && [[ $value != *":*"* ]]; then + if [ $authorized == false ]; then askAutorization; fi + ready[0]=2 + value="$value;unix-user:prey" + fi + + echo $key=$value >> $file; +} + +check () { + # Covered all fields and nothing changed + if [ ${ready[0]} == 1 ] && [ ${ready[1]} == 1 ] && [ ${ready[2]} == 1 ]; then + rm "$file.old" + exit + fi + + # if any of the fields wasn't in the original file + if [ ${ready[0]} == 0 ]; then setIdentity "Identity" "unix-user:prey"; fi + if [ ${ready[1]} == 0 ]; then setResults "ResultAny" "yes"; fi + if [ ${ready[2]} == 0 ]; then setResults "ResultActive" "yes"; fi +} + +grant_permissions () { +# Verify if the files exists + if [ -f $file ]; then + authorized=false + ready=(0 0 0) + + # Back up information and clear original file + cp $file "$file.old" + > $file + + while read -r line; do + processLine $line + done < "$file.old" + + check + exit + else + # Create the file + su -c `printf "[Grant network permissions to Prey user]\nIdentity=unix-user:prey\nAction=org.freedesktop.NetworkManager.*\nResultAny=yes\nResultInactive=no\nResultActive=yes\n" > ${file}` + exit + fi +} + +restore_permissions () { + if [ -f $file ] && [ -f "$file.old" ]; then + cp "$file.old" $file + rm "$file.old" + exit + elif [ -f $file ]; then + rm $file + exit + else + exit + fi +} + +if [ -z "$1" ]; then + echo 'Grant (--grant) or Restore (--restore) arguments necessary' + exit +else + if [[ $1 == "--grant" ]] || [[ $1 == "-g" ]]; then + grant_permissions + elif [[ $1 == "--restore" ]] || [[ $1 == "-r" ]]; then + restore_permissions + else + echo 'Invalid argument' + exit + fi +fi \ No newline at end of file diff --git a/lib/agent/triggers/auto-connect/index.js b/lib/agent/triggers/auto-connect/index.js index ab0bb8f06..d9ada6693 100644 --- a/lib/agent/triggers/auto-connect/index.js +++ b/lib/agent/triggers/auto-connect/index.js @@ -4,85 +4,112 @@ var join = require('path').join, network = require(join(base_path,'providers', 'network')), reconnect = require('./reconnect'), common = require('./../../common'), - os_name = process.platform.replace('darwin', 'mac').replace('win32', 'windows'), config = common.config, logger = common.logger.prefix('auto-connect'), Emitter = require('events').EventEmitter; var emitter, reconnect_time = 30000, - was_disconnected = false; + running = false, + sleeping = false, + was_disconnected = false, timer = null; var connected = false; -var restart_reconnection = function() { +var restart_reconnection = () => { stop_waiting(); - reconnect_time = 1000 * 60 * 3; // Three minutes + reconnect_time = 1000 * 60 * 2; // Two minutes wait_normal_reconnection(); } -var check_err = function(err) { - logger.info(err); - if (err.message.includes('Already connected')) { - logger.info(err.message + ". Autoconnect disengaged for now :)") +var check_err = (err) => { + logger.debug(err); + if (err.message.includes('Already connected') || + err.message.includes('Device on sleeping state') || + err.message.includes('Unable to get wifi interface')) { + + logger.info(err.message + ". Autoconnect disengaged for now") stop_waiting(); } else restart_reconnection(); } -var wait_normal_reconnection = function() { +var wait_normal_reconnection = () => { + running = true; + + if (connected) return stop_waiting(); logger.info("Device disconnected! Waiting for reconnection..."); - reconnect.enable_wifi(function() { - timer = setTimeout(function() { - logger.info("Nothing happened, let's try connecting to the nearest access points..."); - reconnect.get_open_ap_list(function(err, list) { - if (err) return check_err(err); - reconnect.try_connecting_to(list, function(err, stdout) { + + setTimeout(() => { + // Make sure Wi-Fi is on + reconnect.enable_wifi((err) => { + if (err) return check_err(err); + timer = setTimeout(() => { + logger.info("Nothing happened, let's try connecting to the nearest access points..."); + reconnect.get_ap_lists((err, list) => { if (err) return check_err(err); - return wait_normal_reconnection(); + reconnect.try_connecting_to(list, (err, stdout) => { + if (err) return check_err(err); + return wait_normal_reconnection(); + }); }); - }); - }, reconnect_time) - }) + }, reconnect_time) + }) + }, 10000); } -var stop_waiting = function() { +var stop_waiting = () => { + running = false; if (timer) { clearTimeout(timer); timer = null; } } -exports.start = function(opts, cb) { - hooks.on('connected', function() { - network.get_active_access_point(function(err, ap) { +exports.start = (opts, cb) => { + hooks.on('connected', () => { + connected = true; + reconnect.is_connected(true); + network.get_active_access_point((err, ap) => { if (was_disconnected) { - logger.info("Connection achieved! " + (ap ? ap.ssid || "" : "")); + logger.info("Connection achieved! " + ((ap && ap.ssid) ? (ap.ssid || "") : "")); was_disconnected = false; } - connected = ap; - reconnect.connected(ap); + reconnect.is_connected_to(ap); }) stop_waiting(); }); - hooks.on('disconnected', function() { + hooks.on('disconnected', () => { + reconnect.is_connected(false); was_disconnected = true; connected = false; - if (config.get('auto_connect') && os_name != 'linux') { - reconnect.connected(null); + if (config.get('auto_connect') && !sleeping) { wait_normal_reconnection(); } }); + hooks.on('sleeping', (value) => { + if (value) sleeping = true; + else sleeping = false; + + reconnect.is_sleeping(value); + + // if just woke up and it's disconnected, start reconnection + if (!sleeping && !connected && !running) { + wait_normal_reconnection(); + } + }) + emitter = new Emitter(); cb(null, emitter); } -exports.stop = function() { +exports.stop = () => { hooks.remove('connected'); hooks.remove('disconnected'); + hooks.remove('sleeping'); if (emitter) { emitter.removeAllListeners(); diff --git a/lib/agent/triggers/auto-connect/linux.js b/lib/agent/triggers/auto-connect/linux.js index 99b04a737..5f73e133c 100644 --- a/lib/agent/triggers/auto-connect/linux.js +++ b/lib/agent/triggers/auto-connect/linux.js @@ -1,59 +1,88 @@ -var join = require('path').join, - exec = require('child_process').exec; +var exec = require('child_process').exec; network = require('./../../providers/network/linux'); -var net_interface = null; +var net_interface = null, + interface_err = 'Unable to get wifi interface'; + +var get_interface = (cb) => { + network.get_first_wireless_interface((err, data) => { + if (err) return cb(err); -var get_interface = function(cb) { - network.get_first_wireless_interface(function(err, data) { net_interface = data; + return cb(null); }); } -exports.enable_wifi = function(cb) { - var turn_on_wifi = function() { - var cmd = 'nmcli networking on; nmcli radio wifi on'; - exec(cmd, function(err, data) { +exports.is_wifi_on = (cb) => { + var check_wifi_status = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + + var cmd = `LC_ALL=C nmcli networking` + exec(cmd, (err, stdout) => { + let status = stdout.trim(), + enabled = status == 'enabled' ? true : false; + + if (!enabled) return exports.enable_wifi(cb); return cb(); }) } - net_interface ? turn_on_wifi() : get_interface(turn_on_wifi); + net_interface ? check_wifi_status() : get_interface(check_wifi_status); } -exports.get_existing_profiles = function(cb) { - var get_profiles = function() { +exports.enable_wifi = (cb) => { + var restart_wifi = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + + var cmd = 'nmcli networking off; nmcli networking on; nmcli radio wifi on'; + exec(cmd, (err, data) => { + return cb(); + }) + } + net_interface ? restart_wifi() : get_interface(restart_wifi); +} + +exports.get_existing_profiles = (cb) => { + var get_profiles = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = 'ls /etc/NetworkManager/system-connections' - exec(cmd, function(err, stdout) { + exec(cmd, (err, stdout) => { cb(null, stdout.split("\n").slice(0, -1)); }) } net_interface ? get_profiles() : get_interface(get_profiles); } -exports.create_profile = function(ssid, cb) { - var create = function() { +exports.create_profile = (ssid, cb) => { + var create = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = `nmcli connection add type wifi ifname ${net_interface} con-name "${ssid}" ssid "${ssid}"`; - exec(cmd, function(err, out) { + exec(cmd, (err, out) => { return cb && cb(err); }) } net_interface ? create() : get_interface(create); } -exports.delete_profile = function(ssid, cb) { - var discard = function() { +exports.delete_profile = (ssid, cb) => { + var discard = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = `nmcli connection delete "${ssid}"`; - exec('nmcli connection delete ' + '"' + ssid + '"', function(err, out) { + exec(cmd, (err, out) => { return cb && cb(err); }) } net_interface ? discard() : get_interface(discard); } -exports.connect_to_ap = function(ssid, cb) { - var connect = function() { +exports.connect_to_ap = (ssid, cb) => { + var connect = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = `nmcli connection up "${ssid}"`; - exec(cmd, function(err, out) { + exec(cmd, (err, out) => { return cb(err, out); }); } diff --git a/lib/agent/triggers/auto-connect/mac.js b/lib/agent/triggers/auto-connect/mac.js index 240cc1f32..e986665ba 100644 --- a/lib/agent/triggers/auto-connect/mac.js +++ b/lib/agent/triggers/auto-connect/mac.js @@ -1,64 +1,85 @@ -var join = require('path').join, - exec = require('child_process').exec, +var exec = require('child_process').exec, system = require('./../../../system'), run_as_user = system.run_as_logged_user; -var net_interface = null; +var net_interface = null, + interface_err = 'Unable to get wifi interface'; -var get_interface = function(cb) { +var get_interface = (cb) => { var cmd = 'networksetup -listallhardwareports | awk "/Hardware Port: Wi-Fi/,/Ethernet/" | awk "NR==2" | cut -d " " -f 2'; - exec(cmd, function(err, data) { + exec(cmd, (err, data) => { if (err) return cb(err); net_interface = data.split("\n").slice(0, -1); - return cb(null, net_interface); + return cb(null); }) } -exports.enable_wifi = function(cb) { - var turn_on_wifi = function() { - var cmd = `networksetup -setairportpower ${net_interface} on`; - exec(cmd, function(err, data) { - return cb(); +exports.is_wifi_on = (cb) => { + var cmd = `networksetup -getairportpower ${net_interface}` + exec(cmd, (err, stdout) => { + var status = stdout.split('Wi-Fi Power (en0): ').pop().trim(), + enabled = status == 'On' ? true : false; + + if (!enabled) return exports.enable_wifi(cb); + return cb(); + }) +} + +exports.enable_wifi = (cb) => { + var restart_wifi = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + + var cmd = `networksetup -setairportpower ${net_interface} off; networksetup -setairportpower ${net_interface} on`; + exec(cmd, (err) => { + return cb(null); }) } - net_interface ? turn_on_wifi() : get_interface(turn_on_wifi); + net_interface ? restart_wifi() : get_interface(restart_wifi); } -exports.get_existing_profiles = function(cb) { - var get_profiles = function() { - var cmd = `networksetup -listpreferredwirelessnetworks ${net_interface} | awk 'NR>1' | awk '{$1=$1;print}'` - exec(cmd, function(err, stdout) { +exports.get_existing_profiles = (cb) => { + var get_profiles = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + + var cmd = `networksetup -listpreferredwirelessnetworks ${net_interface} | awk 'NR>1' | awk -F '\t' '{print $2}'` + exec(cmd, (err, stdout) => { cb(null, stdout.split("\n").slice(0, -1)); }) } net_interface ? get_profiles() : get_interface(get_profiles); } -exports.create_profile = function(ssid, cb) { - var create = function() { +exports.create_profile = (ssid, cb) => { + var create = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = `networksetup -addpreferredwirelessnetworkatindex ${net_interface} "${ssid}" 0 open`; - run_as_user(cmd, [], function(err) { + run_as_user(cmd, [], (err) => { return cb && cb(err); }) } net_interface ? create() : get_interface(create); } -exports.delete_profile = function(ssid, cb) { - var discard = function() { +exports.delete_profile = (ssid, cb) => { + var discard = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = `networksetup -removepreferredwirelessnetwork ${net_interface} "${ssid}"`; - run_as_user(cmd, [], function(err) { + run_as_user(cmd, [], (err) => { return cb && cb(err); }) } net_interface ? discard() : get_interface(discard); } -exports.connect_to_ap = function(ssid, cb) { // revisar cb si va - var connect = function() { +exports.connect_to_ap = (ssid, cb) => { // revisar cb si va + var connect = (err) => { + if (err || !net_interface) return cb(new Error(interface_err)); + var cmd = `networksetup -setairportnetwork ${net_interface} "${ssid}"`; - run_as_user(cmd, [], function(err, out) { + run_as_user(cmd, [], (err, out) => { return cb(err, out); }); } diff --git a/lib/agent/triggers/auto-connect/reconnect.js b/lib/agent/triggers/auto-connect/reconnect.js index f3be85d92..7b2d43fd7 100644 --- a/lib/agent/triggers/auto-connect/reconnect.js +++ b/lib/agent/triggers/auto-connect/reconnect.js @@ -6,27 +6,42 @@ var async = require('async'), logger = common.logger.prefix('auto-connect'); var previous = null, + sleeping = false, + connected = true, connected_to = false; exports.attempted_wifi = {}, -exports.time_between = 20000; +exports.time_between = 25000; exports.init_profiles = []; -var get_existing_profiles = function(cb) { - os_functions.get_existing_profiles(function(err, data) { +var validate_procedure = () => { + var error = null; + if (connected) error = new Error("Already connected" + (connected_to ? " to: " + connected_to.ssid : "")); + if (sleeping) error = new Error("Device on sleeping state, stopping autoconnect"); + + return error; +} + +var get_existing_profiles = (cb) => { + os_functions.get_existing_profiles((err, data) => { return cb(null, data); }); } -var delete_profile = function(ssid, cb) { - get_existing_profiles(function(err, current_profiles) { +var delete_profile = (ap, cb) => { + get_existing_profiles((err, current_profiles) => { + if (!ap || !ap.ssid) return cb(new Error("Nothing to delete")); + // Check if the profile isn't original and exists in the current list also if the device is connected to that ap - if (!ssid || exports.init_profiles.indexOf(ssid) != -1 || current_profiles.indexOf(ssid) == -1 || connected_to.ssid == ssid) { - return cb(new Error("Nothing to delete " + ssid)); - } - os_functions.delete_profile(ssid, function(err) { + if (exports.init_profiles.indexOf(ap.ssid) != -1 + || current_profiles.indexOf(ap.ssid) == -1 + || (connected_to && connected_to.ssid == ap.ssid) + || ap.security) + return cb(new Error("Nothing to delete " + ap.ssid)); + + os_functions.delete_profile(ap.ssid, (err) => { if (!err) { - logger.debug("Profile " + ssid + " succesfuly deleted"); + logger.debug("Profile " + ap.ssid + " successfully deleted"); previous = null; } return cb && cb(err); @@ -34,71 +49,71 @@ var delete_profile = function(ssid, cb) { }) } -var create_profile = function(ssid, cb) { - get_existing_profiles(function(err, current_profiles) { - if (current_profiles.indexOf(ssid) != -1) - return cb(new Error("Profile " + ssid + " already exists")); +var create_profile = (ap, cb) => { + get_existing_profiles((err, current_profiles) => { + if (current_profiles.indexOf(ap.ssid) != -1) + return cb(new Error("Profile " + ap.ssid + " already exists")); - os_functions.create_profile(ssid, function(err) { - if (!err) logger.debug("Profile " + ssid + " succesfuly created"); + os_functions.create_profile(ap.ssid, (err) => { + if (!err) logger.debug("Profile " + ap.ssid + " successfully created"); cb(null); }); }) } -var connect_to_access_point = function(ap, cb) { - var connect = function() { +var connect_to_access_point = (ap, cb) => { + var connect = () => { logger.info("Trying to connect to: " + ap.ssid); - previous = ap.ssid; + previous = ap; os_functions.connect_to_ap(ap.ssid, cb); } - // Before attempt to connect it checks if it's already connected - if (connected_to) { - delete exports.attempted_wifi[connected_to.mac_address]; - if (previous != connected_to.ssid) { - delete_profile(previous, function(err) { - if (err) logger.debug(err.message) - }); - } - return cb(new Error("Already connected to: " + connected_to.ssid)) - } + if (err = validate_procedure()) return cb(err); - exports.attempted_wifi[ap.mac_address] ? exports.attempted_wifi[ap.mac_address]++ : exports.attempted_wifi[ap.mac_address] = 1; - - delete_profile(previous, function(err) { - if (err) logger.debug(err.message) - create_profile(ap.ssid, function(err) { - if (err) logger.debug(err.message) - connect(); + if (ap.security) { + delete_profile(previous, (err) => { + if (err) logger.debug(err.message); + return connect(); }) - }) + } else { + exports.attempted_wifi[ap.ssid] ? exports.attempted_wifi[ap.ssid]++ : exports.attempted_wifi[ap.ssid] = 1; + + delete_profile(previous, (err) => { + if (err) logger.debug(err.message); + create_profile(ap, (err) => { + if (err) logger.debug(err.message); + return connect(); + }) + }) + } } -exports.try_connecting_to = function(list, cb) { +exports.try_connecting_to = (list, cb) => { var array = []; - list.forEach(function(ap) { + list.forEach((ap) => { array.push( - function(callback) { - setTimeout(function() { - connect_to_access_point(ap, function(err) { - if (err && err.message.includes('Already connected')) return callback(err); - callback(); + (callback) => { + setTimeout(() => { + os_functions.is_wifi_on(() => { + connect_to_access_point(ap, (err) => { + if (err && err.message.includes('Already connected')) return callback(err); + callback(); + }) }) }, exports.time_between) } ) }) - array.push(function(callback) { - setTimeout(function() { + array.push((callback) => { + setTimeout(() => { callback(new Error("Connection attempted with all the open access points, retrying in 3 minutes...")); }, exports.time_between) }) - async.series(array, function(err) { + async.series(array, (err) => { if (err.message.includes("Connection attempted with all the open access points")) { - delete_profile(previous, function(err) { + delete_profile(previous, (err) => { if (err) logger.info(err.message); }); } @@ -109,43 +124,103 @@ exports.try_connecting_to = function(list, cb) { }); } -exports.get_open_ap_list = function(cb) { +exports.get_ap_lists = (cb) => { previuos = null; - if (connected_to) return cb(new Error("Already connected to: " + connected_to.ssid)); - // Make sure Wi-Fi is on - os_functions.enable_wifi(function() { - // Scan the open access points - providers.get('open_access_points_list', function(err, list) { - if (err || !list || list.length == 0) { + if (err = validate_procedure()) return cb(err); + + var filter_wifi_list = (list) => { + var filtered_list = []; + + if (list.length == 0) return filtered_list; + filtered_list = list.filter((obj, pos, arr) => { + return list.map(mapObj => mapObj.ssid).indexOf(obj.ssid) === pos; + }); + return filtered_list; + } + + var proceed_with_open_aps = (open, secured, current_profiles) => { + if (!open || open.length == 0) return cb(new Error('No open access points found. Retrying in 10')) + + var final_list = []; + + // Filter the 3 times attempted access points + open.forEach((ap) => { + if (!(ap.ssid in exports.attempted_wifi) || exports.attempted_wifi[ap.ssid] < 3) { + // Put open ap at the beginning of the list if the device was previously connected to it + current_profiles.indexOf(ap.ssid) > -1 ? final_list.unshift(ap) : final_list.push(ap); + } else { + logger.debug("3 attempts reached for: " + ap.ssid); + } + }) + + secured.forEach((ap) => { + final_list.unshift(ap); + }) + + cb(null, final_list); + } + + os_functions.is_wifi_on(() => { + providers.get('categorized_access_points_list', (err, list) => { + if (err || !list) { return cb(new Error('No open access points found. Retrying in 10')) } - var final_list = []; + var open = list[0], + secured = list[1]; - // Filter the 3 times attempted access points - list.forEach(function(ap) { - if (!(ap.mac_address in exports.attempted_wifi) || exports.attempted_wifi[ap.mac_address] < 3) { - final_list.push(ap); - } else { - logger.debug("3 attempts reached for: " + ap.ssid); - } - }) - cb(null, final_list); + exports.get_existing_profiles((err, current_profiles) => { + if (err || !secured || secured.length == 0) return proceed_with_open_aps(open, [], current_profiles); + + // Check if the device previously connected to at least one of the secured ap's + var known_secured_wifi = []; + secured.forEach((ap) => { + current_profiles.forEach((ssid) => { + if (ssid.trim() == ap.ssid.trim()) + known_secured_wifi.push(ap); + }) + }) + + secured = filter_wifi_list(known_secured_wifi); + open = filter_wifi_list(open); + + proceed_with_open_aps(open, secured, current_profiles); + + }); }) }) } -exports.connected = function(ap) { - connected_to = false; - if (ap) connected_to = ap; +exports.is_connected = (value) => { + if (value) connected = true; + else { + connected = false; + connected_to = null; + } +} + +exports.is_sleeping = (value) => { + if (value) sleeping = true; + else sleeping = false; +} + +exports.is_connected_to = (ap) => { + connected_to = ap; + delete exports.attempted_wifi[connected_to.ssid]; + + if (previous && previous.ssid && previous.ssid != connected_to.ssid) { + delete_profile(previous, (err) => { + if (err) logger.debug(err.message) + }); + } } -exports.enable_wifi = function(cb) { +exports.enable_wifi = (cb) => { os_functions.enable_wifi(cb); } -get_existing_profiles(function(err, profiles) { +get_existing_profiles((err, profiles) => { exports.init_profiles = profiles; }) diff --git a/lib/agent/triggers/auto-connect/windows.js b/lib/agent/triggers/auto-connect/windows.js index 8e4749cab..5d3ac47d6 100644 --- a/lib/agent/triggers/auto-connect/windows.js +++ b/lib/agent/triggers/auto-connect/windows.js @@ -38,7 +38,7 @@ var profile_json = { } } -var generate_xml = function(ssid, cb) { +var generate_xml = (ssid, cb) => { var profile = profile_json; profile.WLANProfile.name = [ssid]; profile.WLANProfile.SSIDConfig[0].SSID[0].hex = [Buffer.from(ssid, 'utf8').toString('hex').toUpperCase()]; @@ -47,30 +47,35 @@ var generate_xml = function(ssid, cb) { var builder = new xml2js.Builder(), xml = builder.buildObject(profile); - fs.writeFile(join(tmpdir, `PreyWiFi-${ssid}.xml`), xml, function(err) { + fs.writeFile(join(tmpdir, `PreyWiFi-${ssid}.xml`), xml, (err) => { return cb && cb(err); }) } -exports.enable_wifi = function(cb) { - exec(wifion, function(err) { +exports.is_wifi_on = (cb) => { + return exports.enable_wifi(cb); +} + +exports.enable_wifi = (cb) => { + exec(wifion, (err) => { return cb(); }); } -var delete_xml = function(ssid, cb) { +var delete_xml = (ssid, cb) => { var file = join(tmpdir, `PreyWiFi-${ssid}.xml`); - fs.unlink(file, function(err) { + fs.unlink(file, (err) => { return cb && cb(err); }) } -exports.get_existing_profiles = function(cb) { - exec('netsh wlan show profiles', function(err, data) { +exports.get_existing_profiles = (cb) => { + exec('netsh wlan show profiles', (err, data) => { var lines = data.toString().split('\n'); var profiles = []; - lines.forEach(function(line) { + lines.forEach((line) => { + if (line.includes('All User Profile :')) { var profile = line.split(': ').pop(); profiles.push(profile.trim()); @@ -81,27 +86,27 @@ exports.get_existing_profiles = function(cb) { }) } -exports.create_profile = function(ssid, cb) { +exports.create_profile = (ssid, cb) => { var file = join(tmpdir, `PreyWiFi-${ssid}.xml`); - generate_xml(ssid, function(err) { + generate_xml(ssid, (err) => { if (err) return cb(err); - exec(`netsh wlan add profile filename="${file}" user=all`, function(err, stdout) { + exec(`netsh wlan add profile filename="${file}" user=all`, (err, stdout) => { return cb && cb(err); }) }) } -exports.delete_profile = function(ssid, cb) { - delete_xml(ssid, function(err) { - exec(`netsh wlan delete profile "${ssid}"`, function(err, stdout) { +exports.delete_profile = (ssid, cb) => { + delete_xml(ssid, (err) => { + exec(`netsh wlan delete profile "${ssid}"`, (err, stdout) => { return cb && cb(err); }) }); } -exports.connect_to_ap = function(ssid, cb) { // revisar cb si va - exec(`netsh wlan connect name="${ssid}"`, function(err, out) { +exports.connect_to_ap = (ssid, cb) => { // revisar cb si va + exec(`netsh wlan connect name="${ssid}"`, (err, out) => { return cb(err, out); }); } \ No newline at end of file diff --git a/lib/agent/triggers/system/index.js b/lib/agent/triggers/system/index.js new file mode 100644 index 000000000..c271c1d71 --- /dev/null +++ b/lib/agent/triggers/system/index.js @@ -0,0 +1,98 @@ +"use strict" + +var triggers = require('os-triggers'), + join = require('path').join, + base_path = join(__dirname, '..', '..'), + hooks = require(join(base_path, 'hooks')), + exec = require('child_process').exec, + os_name = process.platform.replace('win32', 'windows').replace('darwin', 'mac'), + is_mac = os_name == 'mac', + Emitter = require('events').EventEmitter; + +var emitter, + previous = null, + checking = false; + +var common = require('./../../common'), + logger = common.logger.prefix('system'); + +var get_current_state = (cb) => { + if (is_mac) { + let cmd = `pmset -g log|grep -e " Sleep " -e " Wake "|tail -n 1 |awk '{print $4}'`; + exec(cmd, (err, state) => { + if (err) return cb(err); + + state = state.trim(); + let current = false; + + if (state == "Sleep") + current = true; + + return cb(null, current); + }); + + } else return cb(new Error('No info available')); +} + +var check_and_emit = (info) => { + if (checking) return; + checking = true; + + var done = (err, current) => { + if (err) logger.warn("ERROR!", err.message) + + if (previous != current) { + logger.info("SLEEPING!!! " + current) + hooks.trigger('sleeping', current); + } + + previous = current; + checking = false; + } + + if (info) { + let current = (info == 'true'); + return done(null, current); + } + + get_current_state((err, current) => { + if (err) return done(err); + return done(null, current); + }) +}; + +exports.start = function(opts, cb){ + + triggers.watch('system', { respawn: true }) + .catch(error => { return cb(error); }) + .then((system) => { + + // For mac and linux + system.on('state_changed', (info) => { + check_and_emit(info); + }); + + // For windows + system.on('suspended', (info) => { + check_and_emit('true'); + }); + system.on('unsuspended', (info) => { + check_and_emit(info); + }); + system.on('resumed', (info) => { + check_and_emit('false'); + }); + + emitter = new Emitter(); + cb(null, emitter); + }); +}; + +exports.stop = () => { + triggers.unwatch('system'); + if (emitter) { + emitter.removeAllListeners(); + emitter = null; + } +}; + diff --git a/test/lib/agent/triggers/auto-connect.js b/test/lib/agent/triggers/auto-connect.js index 54d7d268b..786542b22 100644 --- a/test/lib/agent/triggers/auto-connect.js +++ b/test/lib/agent/triggers/auto-connect.js @@ -11,12 +11,12 @@ var ap_list = [ { ssid: 'Prey-Guest', signal_strength: -51, channel: 1, security: false }, - { ssid: 'Unsecured Wifi', + { ssid: 'Unsecured Wifi', mac_address: 'ab:cd:ef:gh:ij:kl', signal_strength: -66, channel: 1, security: false }, - { ssid: 'Secured wifi', + { ssid: 'Secured wifi', mac_address: '12:34:56:78:ab:cd', signal_strength: -66, channel: 1, @@ -75,10 +75,21 @@ describe('auto connect', function() { enable_wifi.restore(); }) + describe('get existing profiles', function() { + it('not callsback error', function(done) { + reconnect.get_existing_profiles(function(err, profiles) { + should.not.exist(err); + profiles.should.be.an.Array; + done(); + }) + }) + }) + describe('get open ap list', function() { describe('on empty list', function() { before(function() { + reconnect.is_connected(false); ap_list_stub = sinon.stub(network, 'get_access_points_list', function(cb) { cb(null, close_ap_list); }) @@ -89,7 +100,7 @@ describe('auto connect', function() { }) it('callback an error', function(done) { - reconnect.get_open_ap_list(function(err, list) { + reconnect.get_ap_lists(function(err, list) { should.exist(err); err.message.should.containEql('No open access points found'); done(); @@ -111,49 +122,73 @@ describe('auto connect', function() { }) }) - it('not callback error', function(done) { - reconnect.get_open_ap_list(function(err, list) { - should.not.exist(err); - should.exist(list) - done(); + describe('when in the secured list there a profile in the current list', () => { + before(() => { + profiles_stub = sinon.stub(reconnect, 'get_existing_profiles', function(cb) { + cb(null, ['Pery', 'Secured wifi']); + }) }) - }) - it('returns a list only of open access points', function(done) { - reconnect.get_open_ap_list(function(err, list) { - list.length.should.be.equal(2); - list[0].ssid.should.be.equal('Prey-Guest'); - list[0].security.should.be.equal(false); - list[1].ssid.should.be.equal('Unsecured Wifi'); - list[1].security.should.be.equal(false); - done(); + after(() => { + profiles_stub.restore(); + }) + + it('gonna try to connect to the secured wifi first', (done) => { + reconnect.get_ap_lists(function(err, list) { + should.not.exist(err); + should.exist(list); + list[0].ssid.should.be.equal('Secured wifi'); + list[1].ssid.should.be.equal('Prey-Guest'); + done(); + }) }) }) - describe('when an access has been attempted 3 times', function() { - before(function() { - reconnect.attempted_wifi = {'ab:cd:ef:gh:ij:kl': 3} + describe('when in the secured list there a profile in the current list', () => { + before(() => { + profiles_stub = sinon.stub(reconnect, 'get_existing_profiles', function(cb) { + cb(null, []); + }) + }) + + after(() => { + profiles_stub.restore(); }) - it('shouldnt return that ap in the final list', function(done) { - reconnect.get_open_ap_list(function(err, list) { + it('not callback error', function(done) { + reconnect.get_ap_lists(function(err, list) { should.not.exist(err); - should.exist(list); - list.length.should.be.equal(1); + should.exist(list) + done(); + }) + }) + + it('returns a list only of open access points', function(done) { + reconnect.get_ap_lists(function(err, list) { + list.length.should.be.equal(2); list[0].ssid.should.be.equal('Prey-Guest'); + list[0].security.should.be.equal(false); + list[1].ssid.should.be.equal('Unsecured Wifi'); + list[1].security.should.be.equal(false); done(); }) }) - }) - }) - }) - describe('get existing profiles', function() { - it('not callsback error', function(done) { - reconnect.get_existing_profiles(function(err, profiles) { - should.not.exist(err); - profiles.should.be.an.Array; - done(); + describe('when an open access has been attempted 3 times', function() { + before(function() { + reconnect.attempted_wifi = { 'Unsecured Wifi': 3 } + }) + + it('shouldnt return that ap in the final list', function(done) { + reconnect.get_ap_lists(function(err, list) { + should.not.exist(err); + should.exist(list); + list.length.should.be.equal(1); + list[0].ssid.should.be.equal('Prey-Guest'); + done(); + }) + }) + }) }) }) }) @@ -162,19 +197,19 @@ describe('auto connect', function() { before(function(done) { reconnect.init_profiles = []; - reconnect.delete_profile('Prey-test', function() { + reconnect.delete_profile(open_ap_list[0], function() { done(); }) }) after(function(done) { - reconnect.delete_profile('Prey-test', function(err) { + reconnect.delete_profile(open_ap_list[0], function(err) { done(); }) }) it('not callsback error', function(done) { - reconnect.create_profile('Prey-test', function(err) { + reconnect.create_profile(open_ap_list[0], function(err) { should.not.exist(err); done(); }) @@ -189,7 +224,7 @@ describe('auto connect', function() { }) it('returns errors when profile already exists', function(done){ - reconnect.create_profile('Prey-test', function(err) { + reconnect.create_profile(open_ap_list[0], function(err) { should.exist(err); err.message.should.containEql('already exists'); done(); @@ -201,7 +236,7 @@ describe('auto connect', function() { describe('when profile does not exists', function() { it('callbacks an error', function(done) { - reconnect.delete_profile('Prey-test', function(err) { + reconnect.delete_profile(open_ap_list[0], function(err) { should.exist(err); err.message.should.containEql('Nothing to delete'); done(); @@ -211,13 +246,13 @@ describe('auto connect', function() { describe('when profile exists', function() { before(function(done) { - reconnect.create_profile('Prey-test', function() { + reconnect.create_profile(open_ap_list[0], function() { done(); }) }) it('not callsback an error', function(done) { - reconnect.delete_profile('Prey-test', function(err) { + reconnect.delete_profile(open_ap_list[0], function(err) { should.not.exist(err); done(); }) @@ -258,7 +293,6 @@ describe('auto connect', function() { describe('when theres an attempt to connect to an ap', function() { before(function() { reconnect.attempted_wifi = {}; - var ssid = 'Prey-test'; ap_list_stub = sinon.stub(os_functions, 'connect_to_ap', function(ssid, cb) { cb(null, 'not connected!'); }) @@ -266,7 +300,7 @@ describe('auto connect', function() { after(function(done) { ap_list_stub.restore(); - reconnect.delete_profile('Prey-test', function() { + reconnect.delete_profile(open_ap_list[0], function() { done(); }) }) @@ -274,7 +308,7 @@ describe('auto connect', function() { it('will add the ap to the attempts list', function(done) { reconnect.connect_to_access_point(open_ap_list[0], function() { Object.keys(reconnect.attempted_wifi).length.should.be.equal(1); - reconnect.attempted_wifi[open_ap_list[0].mac_address].should.be.equal(1); + reconnect.attempted_wifi[open_ap_list[0].ssid].should.be.equal(1); done(); }) }) @@ -282,14 +316,14 @@ describe('auto connect', function() { it('will increment the attempt number for that ap', function(done) { reconnect.connect_to_access_point(open_ap_list[0], function() { Object.keys(reconnect.attempted_wifi).length.should.be.equal(1); - reconnect.attempted_wifi[open_ap_list[0].mac_address].should.be.equal(2); + reconnect.attempted_wifi[open_ap_list[0].ssid].should.be.equal(2); done(); }) }) describe('when tryes to connect to another ap', function() { after(function(done) { - reconnect.delete_profile('Unsecured Wifi', function() { + reconnect.delete_profile(ap_list[1], function() { done(); }) }) @@ -297,8 +331,8 @@ describe('auto connect', function() { it('should keep saved the previous ap attempts', function(done) { reconnect.connect_to_access_point(ap_list[1], function() { Object.keys(reconnect.attempted_wifi).length.should.be.equal(2); - reconnect.attempted_wifi[open_ap_list[0].mac_address].should.be.equal(2); - reconnect.attempted_wifi[ap_list[1].mac_address].should.be.equal(1); + reconnect.attempted_wifi[open_ap_list[0].ssid].should.be.equal(2); + reconnect.attempted_wifi[ap_list[1].ssid].should.be.equal(1); done(); }) }) @@ -331,8 +365,9 @@ describe('auto connect', function() { describe('when device connects', function() { before(function() { - reconnect.attempted_wifi = {'oa:oa:oa:oa:oa:oa': 1}; - reconnect.connected({ ssid: 'Pery', mac_address: 'oa:oa:oa:oa:oa:oa', signal_strength: -51, channel: 1,security: 'WP2' }); + reconnect.attempted_wifi = { 'Pery': 1 }; + reconnect.is_connected(true); + reconnect.is_connected_to({ ssid: 'Pery', mac_address: 'oa:oa:oa:oa:oa:oa', signal_strength: -51, channel: 1,security: 'WP2' }); }) after(function() {