<?php /** * File: setup.php * Author: Ryan Prather <ryan.prather@cyberperspectives.com> * Purpose: Allow setup process for new installations * Created: Nov 28, 2017 * * Copyright 2017: Cyber Perspective, LLC, All rights reserved * Released under the Apache v2.0 License * * See license.txt for details * * Change Log: * - Nov 28, 2017 - File created * - Dec 27, 2017 - Added check for local mysql server and empty root password, updated include path to display root path * - Apr 29, 2018 - Updated 3rd party libraries * - May 10, 2018 - Added root confirmation password validation (bug #412) */ set_time_limit(0); include_once 'helper.inc'; /** * check for PHP settings * 1. is openssl installed and running? * 2. is mysqli installed and running? * 3. is ZipArchive class installed? * 4. is the request_order set correctly? GPCS? * 5. is root, inc, and classes folders in include_path */ $fail = false; $config = file_get_contents(dirname(__FILE__) . "/config.inc"); if (version_compare(PHP_VERSION, '7.1') < 0) { print "The minimum version of PHP necessary is 7.1, please upgrade to continue<br />"; $fail = true; } if (!is_writable(dirname(__FILE__))) { print <<<EOO The Apache user requires write access to all directories in the document root, please make sure that all permissions are as follows: Directories = rwx Files = rw On *nix: find ./ -type d -exec chmod 775 {} \; && find ./ -type f -exec chmod 665 {} \; EOO; die(); } if (!is_writable(dirname(__FILE__) . "/config.inc")) { die("Sagacity needs write access to the config.inc file in the document root"); } if(!is_writable(dirname(__FILE__) . "/inc")) { die("Sagacity needs write access to the /inc directory to create the encrypted password file"); } if(!file_exists(dirname(__FILE__) . "/logs")) { mkdir(dirname(__FILE__) . "/logs"); } elseif(!is_writable(dirname(__FILE__) . "/logs")) { die("Sagacity needs write access to the /logs directory to create system and scanner log files"); } if (!function_exists('openssl_encrypt')) { print <<<EOO The PHP OpenSSL module is not install or enabled.<br /> Visit <a href='/?phpinfo=1'>PHPInfo</a> to double-check this. If you know it's installed, restart Apache and see if that works.<br /><br /> EOO; $fail = true; } else { $algorithms = ["aes-256-cbc-hmac-sha256", "aec-256-cbc-hmac-sha1", "aes-256-cbc"]; $ciphers = array_map('strtolower', openssl_get_cipher_methods()); if (in_array($algorithms[0], $ciphers)) { $idx = 0; } elseif (in_array($algorithms[1], $ciphers)) { $idx = 1; } elseif (in_array($algorithms[2], $ciphers)) { $idx = 2; } else { print <<<EOO The needed encryption algorithm is not available please install one of the following: EOO; print implode("<br />", $algorithms); $fail = true; } if (!$fail) { my_str_replace("{ALGORITHM}", $algorithms[$idx], $config); $salt = base64_encode(openssl_random_pseudo_bytes(32)); my_str_replace("{SALT}", $salt, $config); file_put_contents(dirname(__FILE__) . "/config.inc", $config); } } if (!class_exists('mysqli')) { print <<<EOO The PHP mysqli module is not installed or enabled.<br /> Visit <a href='/?phpinfo=1'>PHPInfo</a> to double-check this. If you know it's installed, retstart Apache and try again<br /><br /> EOO; $fail = true; } if (!class_exists('ZipArchive')) { print <<<EOO The PHP ZipArchive module is not installed or enabled.<br /> Visit <a href='/?phpinfo=1'>PHPInfo</a> to double-check this.<br /><br /> EOO; $fail = true; } if (strtolower(substr(PHP_OS, 0, 3)) == "win" && !class_exists("COM")) { print <<<EOO The Component Object Model (COM) class is not available. Please make sure it is installed and enabled<br /> Visit <a href='http://php.net/manual/en/book.com.php'>http://php.net/manual/en/book.com.php</a> for more info EOO; $fail = true; } elseif (strtolower(substr(PHP_OS, 0, 3)) == 'win') { try { new COM("WScript.Shell"); } catch (Exception $e) { print <<<EOO The Component Object Model (COM) class does not seem to be available EOO; $fail = true; } } $ro = ini_get('request_order'); if ($ro != 'GPCS' && $ro != 'GPC') { print <<<EOO The request_order directive in php.ini is not set correctly. It needs to be either GPC or GPCS, it is currently $ro.<br /> Open the php.ini file, search for request_order, and change it to either GPC or GPCS. After it's saved, you'll need to restart Apache<br /><br /> EOO; $fail = true; } if (strtolower(substr(PHP_OS, 0, 3)) == 'win') { $delim = ';'; } else { $delim = ':'; } $inc_path = explode($delim, ini_get('include_path')); $doc_root = realpath(dirname(__FILE__)); $classes = realpath("{$doc_root}/classes"); $inc = realpath("{$doc_root}/inc"); $root = realpath(dirname(__FILE__)); if (!in_array($inc, $inc_path) || !in_array($classes, $inc_path) || !in_array($root, $inc_path)) { print <<<EOO The include_path directive in php.ini does not include the required paths.<br /> Open the php.ini file, search for include_path (and make sure that the one for your OS) includes $root, $inc, & $classes<br /> Current include_path: EOO; print ini_get('include_path') . "<br />"; $fail = true; } $mem_limit = return_bytes(ini_get("memory_limit")); $gig = return_bytes('1G'); if ($mem_limit < $gig) { print <<<EOO Sagacity does many data intensive actions, so we recommend a memory_limit of 1G. $mem_limit bytes is the current setting.<br /> To change this, open the php.ini file and look for 'memory_limit' and set to at least 1G<br /> EOO; } if (!ini_get("file_uploads")) { print "File uploads are currently turned off by the file_uploads directive in php.ini. Please turn them back on if you wish to upload files through the user interfaces<br />"; } else { $upload_file_max = return_bytes(ini_get('upload_max_filesize')); $post_max_size = return_bytes(ini_get('post_max_size')); if ($upload_file_max != $post_max_size) { print <<<EOO Upload file max size ($upload_file_max bytes) and post max size ($post_max_size bytes) do not match. The smaller will be used to limit uploaded file sizes.<br /> To change this, open the php.ini file and change 'upload_max_filesize' and 'post_max_size'<br /> EOO; } } if ($fail) { die; } $is_online = ping("cyberperspectives.com"); $step = filter_input(INPUT_GET, 'step', FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE); $blank_root = false; $mysql_host = ''; mysqli_report(MYSQLI_REPORT_STRICT); try { $db = new mysqli("localhost", "root", ""); if (!$db->connect_errno) { $blank_root = true; $mysql_host = 'localhost'; } } catch (Exception $e) { } if (strtolower(substr(PHP_OS, 0, 3)) == 'win') { $mysql_host = 'localhost'; } ?> <!DOCTYPE HTML> <html> <head> <title>Sagacity Setup</title> <link href='/style/fonts/fonts.css' rel='stylesheet' type='text/css' /> <!--[if IE 9]><link rel="stylesheet" href="style/style-ie9.css" /><![endif]--> <link href='/script/jquery-ui/jquery-ui.min.css' rel='stylesheet' type='text/css' /> <script src="/script/jquery-3.2.1.min.js"></script> <script src="/style/5grid/jquery.browser.min.js"></script> <script type="text/javascript" src="/script/jquery-ui/jquery-ui.min.js"></script> <script src="/style/5grid/init.js?use=mobile,desktop,1000px&mobileUI=1&mobileUI.theme=none"></script> <script type="text/javascript" src="/script/default.js"></script> <script type="text/javascript" src="/script/spin/spin.min.js"></script> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png"> <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png"> <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png"> <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png"> <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png"> <link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png"> <link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png"> <link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png"> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png"> <link rel="icon" type="image/png" href="/favicon-192x192.png" sizes="192x192"> <link rel="icon" type="image/png" href="/favicon-160x160.png" sizes="160x160"> <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96"> <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16"> <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32"> <meta name="msapplication-TileColor" content="#da532c"> <meta name="msapplication-TileImage" content="/mstile-144x144.png"> <style type='text/css'> #msg { display: none; } .err { color: white; background-color: red; } .msg { color: white; background-color: green; } #pwd-msg, #root-pwd-msg { display: none; width: 25px; } #root-conf { display: none; } .buttons { text-align: right; } .label { width: 250px; display: inline-block; } .left { width: 49%; display: inline-block; vertical-align: top; } .right { width: 49%; display: inline-block; } .setup-header, .buttons { width: 49%; display: inline-block; } </style> <script type="text/javascript"> var current_step = 0; $(function () { $('#tabs').tabs({ disabled: [1, 2] }); $('#derived-on,#declassify-on').datepicker({dateFormat: "yy-mm-dd"}); $('.next').click(next_step); $('.back').click(prev_step); <?php if ($blank_root) { print "alert('System has detected your root MySQL password is blank. Please enter the password you want in the blank and the confirmation and we can set the password');"; print "$('#root-conf').show();"; } if ($step !== null && $step > 0) { switch ($step) { case 2: print <<<EOL $('#tabs').tabs('enable', 2) .tabs('option', 'active', 2); current_step = 2; EOL; break; case 1: print <<<EOL $('#tabs').tabs('enable', 1) .tabs('option', 'active', 1); current_step = 1; EOL; break; } switch ($step) { case 2: print "$('#tabs').tabs('disable', 1);" . PHP_EOL; case 1: print "$('#tabs').tabs('disable', 0);" . PHP_EOL; print "setTimeout(function(){enable_next(current_step);}, 3000);" . PHP_EOL; } } ?> }); function next_step() { var params; if (current_step == 0) { if ($('#web-pwd').val() != $('#conf').val()) { display_msg("Web passwords don't match"); $('#web-pwd').focus(); return; } if ($('#root-conf').is(":visible") && $('#root-pwd').val() != $('#root-conf').val()) { display_msg("Root passwords don't match"); $('#root-pwd').focus(); return; } var action = null; if ($('#do').is(":checked")) { action = 'do'; } else if ($('#po').is(":checked")) { action = 'po'; } params = { 'step': current_step, 'doc-root': $('#doc-root').val(), 'pwd-file': $('#pwd-file').val(), 'tmp-path': $('#tmp-path').val(), 'log-path': $('#log-path').val(), 'log-level': $('#log-level').val(), 'db-server': $('#db-server').val(), 'root-uname': $('#root-uname').val(), 'root-pwd': $('#root-pwd').val(), 'conf-root-pwd': $('#root-conf').val(), 'web-pwd': $('#web-pwd').val(), 'sample-data': ($('#sample-data').is(':checked') ? '1' : '0'), 'cpe': ($('#cpe').is(":checked") ? '1' : '0'), 'cve': ($('#cve').is(":checked") ? '1' : '0'), 'stig': ($('#stig').is(":checked") ? '1' : '0'), 'update-freq': $('#update-freq').val(), 'action': action }; } else if (current_step == 1) { params = { 'step': current_step, 'company': $('#comp-name').val(), 'comp-add': $('#comp-add').val(), 'last-modified': $('#last-modified-by').val(), 'creator': $('#creator').val(), 'system-class': $('#sys-class').val(), 'classified-by': $('#classified-by').val(), 'scg': $('#derived-by').val(), 'derived-on': $('#derived-on').val(), 'declassify-on': $('#declassify-on').val() }; } else if (current_step == 2) { params = { 'step': current_step, 'flatten': ($('#flatten').is(":checked") ? "1" : '0'), 'wrap-text': ($('#wrap-text').is(":checked") ? "1" : "0"), 'notifications': ($('#notifications').is(":checked") ? '1' : '0'), 'port-limit': $('#port-limit').val(), 'max-results': $('#max-results').val(), 'output-format': $('#output-format').val() }; } $.ajax('/exec/installer.php', { data: params, beforeSend: function () { display_msg('Processing', 'msg'); }, success: function (data) { if (data.error) { display_msg(data.error + "<br />Go back to the previous step and fix the error", 'err'); } else if (data.success) { if (current_step > 2) { location.href = '/ste/'; } if (data.msg) { display_msg(data.msg, 'msg', 10000); } else { display_msg('Step Completed', 'msg'); } } }, error: function (xhr, status, error) { console.error(error); display_msg(error, 'err'); }, dataType: 'json', method: 'post' }); current_step++; $('#tabs').tabs('enable', current_step) .tabs('option', 'active', current_step) .tabs('disable', current_step - 1); setTimeout(function () { enable_next(current_step); }, 3000); } function display_msg(msg, err_class, delay = 3000) { $('#msg').removeClass('msg err') .addClass(err_class) .html(msg) .slideDown(); setTimeout(function () { $('#msg').slideUp(); }, delay); } function enable_next(step) { if (step == 1) { $('#company').find('.next') .removeClass('button-delete') .addClass('button') .prop('disabled', false); } else if (step == 2) { $('#options').find('.next') .removeClass('button-delete') .addClass('button') .prop('disabled', false); } } function prev_step() { current_step--; $('#tabs').tabs('enable', current_step); $('#tabs').tabs('option', 'active', current_step); $('#tabs').tabs('disable', current_step + 1); } function chk_pwd() { if ($('#web-pwd').val() != $('#conf').val()) { $('#pwd-msg').attr('src', "/img/X.png"); } else { $('#pwd-msg').attr('src', "/img/ok.png"); } $('#pwd-msg').show(); } function chk_root_pwd() { if ($('#root-conf').is(":visible")) { if ($('#root-pwd').val() != $('#root-conf').val()) { $('#root-pwd-msg').attr('src', '/img/X.png'); } else { $('#root-pwd-msg').attr('src', '/img/ok.png'); } $('#root-pwd-msg').show(); } } </script> </head> <body> <div id="header-wrapper" style="height:125px;"> <header id="header" class="5grid-layout"> <div class="row"> <div class="12u" style="text-align:center;"> <!-- Logo --> <span class="mobileUI-site-name"> <img src='/img/Sagacity-Logo.png' style='width:365px;' /> </span> </div> </div> </header> </div> <div style="width:1200px;margin:auto;"> <?php print "Maximum file upload size is currently set to " . ini_get("upload_max_filesize") . "B<br />"; print "Your current timezone is set to " . ini_get("date.timezone") . "<br />"; ?> <div id='msg'></div> <div id='tabs' style='height:450px;'> <ul> <li><a href='#database'>Database</a></li> <li><a href='#company'>Company</a></li> <li><a href='#options'>Options</a></li> </ul> <div id='database'> <div class='setup-header'> <h2>Database Configuration</h2> </div> <div class="buttons"> <input type='button' class='button' value='Adv Web Settings' onclick="$('#advanced').slideToggle();" /> <input type='button' class='button next' value='Next' /> </div> <div class='left'> <label class='label' for='db_server'>Database Server:</label> <input type='text' id="db-server" placeholder="Hostname or IP" value='<?php print $mysql_host; ?>' title='Database server DNS name or IP' /><br /> <span class='label'> <input type='text' id='root-uname' placeholder='Root username' value='root' /> </span> <input type='password' id='root-pwd' placeholder='Root password' /><br /> <label class='label'> </label> <input type='password' id='root-conf' onkeyup='javascript:chk_root_pwd();' placeholder='Confirm root password' /> <img id='root-pwd-msg' /><br /> <label class='label' for='pwd'>Web user password:</label> <input type='password' id='web-pwd' /><br /> <label class='label' for='conf'>Confirm password:</label> <input type='password' id='conf' onkeyup='javascript:chk_pwd();' /> <img id='pwd-msg'/><br /> <label class='label' for='sample-data'>Add Sample Data:</label> <input type='checkbox' id='sample-data' title='Add sample data to database' /><br /> </div> <div class='right'> <label for='cpe' class='label'>Load CPE's:</label> <input type='checkbox' id='cpe' checked title="Do you want to load CPE's upon completion?" /><br /> <label for='cve' class='label'>Load CVE's:</label> <input type='checkbox' id='cve' checked title="Do you want to laod CVE's upon completion?" /><br /> <label for='stig' class='label'>Load STIG's:</label> <input type='checkbox' id='stig' checked title="Do you want to load STIG's upon completion?" /><br /> <?php if ($is_online) { ?> <label for='dp' class='label'>Online:</label> <input type='radio' id='dp' name='action' value='dp' checked /><br /> <label for='do' class='label'>Download only:</label> <input type='radio' id='do' name='action' value='do' /><br /> <?php } ?> <label for='po' class='label'>Offline:</label> <input type='radio' id='po' name='action' value='po' /> </div> <div id='advanced' style='display:none;margin-top:15px;'> <div class="left"> <label class='label'>Web Root:</label> <input type='text' id='doc-root' value='<?php print realpath(getcwd()); ?>' title='Absolute path of the document root' /><br /> <label class='label'>Password File:</label> <input type='text' id='pwd-file' value='inc/passwd' title='Relative path to the encrypted password file' /><br/> <label class='label'>AJAX Refresh Freq:</label> <input type='number' id='update-freq' value='3' title='Frequency that the AJAX calls refresh methods (in seconds)' /> </div> <div class='right'> <label class='label'>TMP Path:</label> <input type='text' id='tmp-path' value='<?php print realpath(getcwd()) . DIRECTORY_SEPARATOR . "tmp"; ?>' title='Absolute path to the temporary storage folder' /><br /> <?php $log_path = null; if (strtolower(substr(PHP_OS, 0, 3)) == 'lin') { $log_path = "/var/log/sagacity"; } else { $log_path = realpath(getcwd()) . DIRECTORY_SEPARATOR . "logs"; } ?> <label class='label'>Log Path:</label> <input type='text' id='log-path' value='<?php print $log_path; ?>' title='Absolute path to the log path' /><br /> <label class='label'>Log Level:</label> <select id='log-level' title='The default log level'> <option>ERROR</option> <option>WARNING</option> <option>NOTICE</option> <option>DEBUG</option> </select> </div> </div> </div> <div id='company'> <div class='setup-header'> <h2>Company Information</h2> </div> <div class='buttons'> <input type='button' class='button back' value='Previous' /> <input type='button' class='button-delete next' value='Next' disabled="true" /> </div> <div class='left'> <label class='label'>Name:</label> <input type='text' id='comp-name' placeholder='Company Name' title='The name of your company' /><br /> <label class='label'>Address:</label> <input type='text' id='comp-add' placeholder='Company Address' title='The company address' /><br /> <label class='label'>Last Modified By:</label> <input type='text' id='last-modified-by' placeholder='Last modified by?' title='The name of the person that last modified the eChecklist' /><br /> <label class='label'>Creator:</label> <input type='text' id='creator' placeholder='Creator' title='Person who created the eChecklist' /> </div> <div class='right'> <label class='label'>System Classification:</label> <select id='sys-class'> <option>UNCLASSIFIED</option> <option>U//FOUO</option> <option>SECRET</option> </select> <br /> <label class='label'>Classified By:</label> <input type='text' id='classified-by' /><br /> <label class='label'>Derived From:</label> <input type='text' id='derived-by' /><br /> <label class='label'>SCG Date:</label> <input type='text' id='derived-on' /><br /> <label class='label'>Declassify On:</label> <input type='text' id='declassify-on' /> </div> </div> <div id='options'> <div class='setup-header'> <h2>System Options</h2> </div> <div class='buttons'> <input type='button' class='button back' value='Previous' /> <input type='button' class='button-delete next' value='Done' disabled="true" /> </div> <div class="left"> <label for='flatten' class='label'>Flatten eChecklist:</label> <input type='checkbox' id='flatten' checked title='Do you want a high-water mark with the eChecklist exports by default (shows worst case/check)?' /><br /> <label for='wrap-text' class='label'>Wrap Text in eChecklist:</label> <input type='checkbox' id='wrap-text' title='Do you want exported eChecklist files to have wrapped text for the check contents field?' /><br /> <label for='notifications' class='label'>Scan Notifications:</label> <input type='checkbox' id='notifications' title='Do you want to hear audible notifications when result scans complete' /><br /> <label for='port-limit' class='label'>Port Ingestion Limit:</label> <input type="number" id='port-limit' value="100" min="0" max="10000" title="The maximum number of open ports to import from a target (limit 10000)" /><br /> <label for='max-results' class='label'>Max # of Result Threads:</label> <input type="number" id='max-results' value="5" min="1" max="20" title="The maximum number of scans to import at a given time (recommended limit of 20)" /><br /> <label for='output-format' class='label'>Output format</label> <select id='output-format'> <option value="xlsx">Microsoft Excel 2007+ (.xlsx)</option> <option value="xls">Microsoft Excel 95-2003 (.xls)</option> <option value="ods">OpenDocument Format (.ods)</option> <?php /* <option value="html">HTML (.html)</option> <option value="pdf">Post-script Document (.pdf)</option> <option value="csv">Comma-separated files (.csv)</option> */ ?> </select> </div> </div> </div> </div> </body> </html>