From 8d12ac9a66eb948fd48dc33d7010f13d40625a2b Mon Sep 17 00:00:00 2001 From: grcgrc Date: Wed, 8 May 2019 17:20:03 +0100 Subject: [PATCH] Requested changes to PR 2965 * Less technical error messages, reduced code duplication, changed makeproject to generate keys, added op php file for generating keypairs, changed the button styling, changed input to textarea. * Split the ops scripts into separate check and generate files, reduced code duplication by creating a new inc file to define key variables. --- html/inc/account_ownership.inc | 35 ++++ html/inc/user.inc | 14 +- html/ops/check_account_ownership_keys.php | 39 +++++ html/ops/generate_account_ownership_keys.php | 73 ++++++++ html/ops/index.php | 19 +- html/user/account_ownership.php | 173 +++++++++++++++++++ html/user/account_ownership_action.php | 130 -------------- html/user/account_ownership_form.php | 73 -------- html/user/get_project_config.php | 10 +- tools/make_project | 5 + 10 files changed, 350 insertions(+), 221 deletions(-) create mode 100644 html/inc/account_ownership.inc create mode 100644 html/ops/check_account_ownership_keys.php create mode 100644 html/ops/generate_account_ownership_keys.php create mode 100644 html/user/account_ownership.php delete mode 100644 html/user/account_ownership_action.php delete mode 100644 html/user/account_ownership_form.php diff --git a/html/inc/account_ownership.inc b/html/inc/account_ownership.inc new file mode 100644 index 0000000000..cba6c8fe14 --- /dev/null +++ b/html/inc/account_ownership.inc @@ -0,0 +1,35 @@ +. + +require_once("../inc/boinc_db.inc"); +require_once("../inc/user.inc"); +require_once("../inc/util.inc"); + +$temp_config = get_config(); +$temp_keydir = parse_config($temp_config, ""); // key directory can be customized + +$account_ownership_private_key_file_name = "account_ownership_private.pem"; // private key file name +$account_ownership_private_key = "$temp_keydir/$account_ownership_private_key_file_name"; // for overwriting key +$account_ownership_private_key_file_path = "file://$account_ownership_private_key"; // for checking file existence + +$account_ownership_public_key_file_name = "account_ownership_public.pem"; // public key file name +$account_ownership_public_key = "$temp_keydir/$account_ownership_public_key_file_name"; // for overwriting key +$account_ownership_public_key_file_path = "file://$account_ownership_public_key"; // for checking file existence + +?> diff --git a/html/inc/user.inc b/html/inc/user.inc index 668484e6f9..da5c5af40e 100644 --- a/html/inc/user.inc +++ b/html/inc/user.inc @@ -283,10 +283,16 @@ function show_user_info_private($user) { tra("Account keys"), "".tra("View")."" ); - row2( - tra("Account Ownership"), - "Generate ownership proof" - ); + + require_once("../inc/account_ownership.inc"); + if (file_exists($account_ownership_private_key_file_path)) { + // If the server has keys configured show the account ownership form + row2( + tra("Account Ownership"), + "Generate ownership proof" + ); + } + } } diff --git a/html/ops/check_account_ownership_keys.php b/html/ops/check_account_ownership_keys.php new file mode 100644 index 0000000000..4c02e93e21 --- /dev/null +++ b/html/ops/check_account_ownership_keys.php @@ -0,0 +1,39 @@ +. + +require_once("../inc/boinc_db.inc"); +require_once("../inc/user.inc"); +require_once("../inc/util.inc"); +require_once("../inc/account_ownership.inc"); + +if (!file_exists($account_ownership_private_key_file_path)) { + echo "

The '$account_ownership_private_key_file_name' key doesn't exist. Please run the 'generate_account_ownership_keys.php' script from the BOINC web server command line."; +} else { + echo "

The '$account_ownership_private_key_file_name' key exists."; +} + +if (!file_exists($account_ownership_public_key_file_path)) { + echo "

The '$account_ownership_public_key_file_name' key doesn't exist. Please run the 'generate_account_ownership_keys.php' script from the BOINC web server command line."; +} else { + echo "

The '$account_ownership_public_key_file_name' key exists."; +} + +echo "

For more info see the related wiki page: ProofOfOwnership

" + +?> diff --git a/html/ops/generate_account_ownership_keys.php b/html/ops/generate_account_ownership_keys.php new file mode 100644 index 0000000000..1dcf288856 --- /dev/null +++ b/html/ops/generate_account_ownership_keys.php @@ -0,0 +1,73 @@ +#!/usr/bin/env php +. + +require_once("../inc/boinc_db.inc"); +require_once("../inc/user.inc"); +require_once("../inc/util.inc"); +require_once("../inc/account_ownership.inc"); + +if (php_sapi_name() == "cli") { + if (!empty($argv[1])) { + if ($argv[1] == "overwrite") { + if (file_exists($account_ownership_private_key_file_path)) { + // If the private key exists, delete it. + unlink($account_ownership_private_key); + echo "erased '$account_ownership_private_key_file_name' \n"; + } + if (file_exists($account_ownership_public_key_file_path)) { + // If the public key exists, delete it. + unlink($account_ownership_public_key); + echo "erased '$account_ownership_public_key_file_name' \n"; + } + } + } + + if ((!file_exists($account_ownership_private_key_file_path)) && (!file_exists($account_ownership_public_key_file_path))) { + + try { + $generated_pkey = openssl_pkey_new(array( + 'digest_alg' => 'sha512', + 'private_key_bits' => 4096, + 'private_key_type' => OPENSSL_KEYTYPE_RSA + )); + + $pubkey = openssl_pkey_get_details($generated_pkey); // Get the public key from the generated pkey pair + file_put_contents($account_ownership_public_key, $pubkey['key']); // Save the public key to disk + openssl_pkey_export_to_file($generated_pkey, $account_ownership_private_key); // Save the private key to disk + openssl_pkey_free($generated_pkey); // Free key data securely from memory + + if ((file_exists($account_ownership_private_key_file_path)) && (file_exists($account_ownership_public_key_file_path))) { + echo "Successfully generated a new account ownership keypair."; + } else { + throw new Exception('Failed to generate account ownership keypair.'); + } + + } catch (Exception $e) { + echo 'Caught exception during account ownership key generation: ', $e->getMessage(), "\n"; + } + + } else { + echo "The private and public keys already exist. Repeat the command with the 'overwrite' parameter to replace the existing ownership keys."; + } +} else { + echo "This script must be run from the CLI"; +} + +?> diff --git a/html/ops/index.php b/html/ops/index.php index 6dcb0f7127..6647446d22 100644 --- a/html/ops/index.php +++ b/html/ops/index.php @@ -87,7 +87,7 @@ echo "

+ + + +
Browse database: - Computing - - User management
  • Post news item
  • @@ -136,6 +136,7 @@ echo "
  • User privileges
  • User job submission privileges
  • Send mass email to a selected set of users
  • +
  • Check generated account ownership keys
  • ID: @@ -153,11 +154,11 @@ $show_deprecated = get_str("show_deprecated", true); $show_only = array("all"); // Add all appids you want to display, or "all" $apps = BoincApp::enum(""); foreach ($apps as $app) { - if (in_array($app->id, $show_only) + if (in_array($app->id, $show_only) || ( in_array("all", $show_only) && (!$app->deprecated || $show_deprecated) )) { - + echo " Results for $app->name:
      @@ -177,7 +178,7 @@ foreach ($apps as $app) { | id&nsecs=$secs\"> summary per app version - | + | id&nsecs=$secs\"> failures broken down by (app version, host) | diff --git a/html/user/account_ownership.php b/html/user/account_ownership.php new file mode 100644 index 0000000000..fd9ba75b0a --- /dev/null +++ b/html/user/account_ownership.php @@ -0,0 +1,173 @@ +. + +require_once("../inc/boinc_db.inc"); +require_once("../inc/user.inc"); +require_once("../inc/util.inc"); +require_once("../inc/countries.inc"); +require_once('../inc/recaptchalib.php'); + +check_get_args(array("tnow", "ttok")); + +$user = get_logged_in_user(); +check_tokens($user->authenticator); + +function account_ownership_action($user) { + // POST request - the user has submitted the form. + page_head(tra("Proof of account ownership results"), null, null, null, boinc_recaptcha_get_head_extra()); + + global $recaptcha_private_key; + if ($recaptcha_private_key) { + // Recaptcha is enabled on the BOINC server + if (!boinc_recaptcha_isValidated($recaptcha_private_key)) { + // The user failed to solve the recaptcha prompt - redirect them to an error message! + error_page( + tra("Your reCAPTCHA response was not correct. Please try again.") + ); + } + } + + // Input is passed in from the openssl_sign_form + $user_data = htmlentities(post_str("user_data", true), ENT_QUOTES, "UTF-8"); // Convert special characters to html equivelant + + if ((strlen($user_data) > 0) && (strlen($user_data) <= 4096)) { + require_once("../inc/account_ownership.inc"); + // Check that the private key file exists where specified. If not, redirect to error page. + if (!file_exists($account_ownership_private_key_file_path)) { + error_page(tra("The proof of account ownership feature is not set up properly. Contact the project administrator to resolve the issue.")); + } + + // Check that the public key file exists where specified. If not, redirect to error page. + if (!file_exists($account_ownership_public_key_file_path)) { + error_page(tra("The proof of account ownership feature is not set up properly. Contact the project administrator to resolve the issue.")); + } + + $privkey = fopen($account_ownership_private_key_file_path, "r"); // Opening private key file + if (!isset($privkey) || empty($privkey)) { + error_page(tra("The proof of account ownership feature is not set up properly. Contact the project administrator to resolve the issue.")); + } + $privkey_contents = fread($privkey, 8192); // Reading contents of private key into var + fclose($privkey); // Closing private key file + + $userid = $user->id; // Retrieving the user's UserId + $message_data = "$userid $user_data"; // Create the message which will be signed. + + $private_key_pem = openssl_pkey_get_private($privkey_contents); // Loading the private key into memory + openssl_sign($message_data, $signature, $private_key_pem, OPENSSL_ALGO_SHA512); // Compute signature using SHA512 + openssl_free_key($private_key_pem); // Free the private key from memory for additional security + + $pubkey = fopen($account_ownership_public_key_file_path, "r"); // Open public key file + if ((!isset($pubkey)) || empty($pubkey)) { + error_page(tra("The proof of account ownership feature is not set up properly. Contact the project administrator to resolve the issue.")); + } + $pubkey_contents = fread($pubkey, 8192); // Read contents to var + fclose($pubkey); // Close pub key file + + $base64_sig = base64_encode($signature); // Base64 encode the generated signature to enable safe output to text file. + $decoded_sig = base64_decode($base64_sig); // Decode base64 sig for use in sig_verification + $pubkeyid = openssl_pkey_get_public($pubkey_contents); // fetch public key into memory + $sig_verification = openssl_verify($message_data, $decoded_sig, $pubkeyid, OPENSSL_ALGO_SHA512); // Verify that the generated signature against the original data, using the public key. + openssl_free_key($pubkeyid); // Free the public key from memory + + // Check if signature was successfully validated + if ($sig_verification == 1) { + $url_tokens = url_tokens($user->authenticator); + // The generated signature has been successfully verified using the public key. + global $master_url; // Define global master_url variable for use in output + echo "

      Do not share this information with anyone other than the external system which has requested this proof of account ownership.

      "; + echo ""; + echo "

      "; + echo ""; + echo ''; + page_tail(); + + } elseif ($sig_verification == 0) { + // The generated signature has not been verified. The private/public keys do not match. + error_page(tra("Signature verification failed. Contact the project administrator to resolve the issue.")); + } else { + // Something has gone wrong & an error has occurred. + error_page(tra("An error occured during the signature verification. Contact the project administrator to resolve the issue.")); + } + } else { + // User data input invalid + error_page(tra("Invalid input. User input must have a length > 0 and < 4096. ")); + } +} + +function account_ownership_form($user) { + // GET request - the user has navigated to the page. + page_head(tra("Generate proof of account ownership"), null, null, null, boinc_recaptcha_get_head_extra()); + + if ($user) { // Verify the user is logged in + require_once("../inc/account_ownership.inc"); + + if (!file_exists($account_ownership_private_key_file_path)) { + // Check that the private key file exists where specified. If not, redirect to error page. + error_page(tra("The proof of account ownership feature is not set up properly. Contact the project administrator to resolve the issue.")); + } + + if (!file_exists($account_ownership_public_key_file_path)) { + // Check that the public key file exists where specified. If not, redirect to error page. + error_page(tra("The proof of account ownership feature is not set up properly. Contact the project administrator to resolve the issue.")); + } + + echo "

      This tool is designed to create a proof of account ownership for external systems.

      "; + + global $recaptcha_public_key; + if ($recaptcha_public_key) { + // Recaptcha configured + echo "

      Enter a message with length less than 4096 characters into the input textbox below, solve the captcha then click the 'Generate' button.

      "; + } else { + // Recaptcha not configured + echo "

      Enter a message with length less than 4096 characters into the input textbox below then click the 'Generate' button.

      "; + } + echo "

      A textbox will then appear which contains your proof of account ownership."; + echo "

      "; + + echo form_tokens($user->authenticator); + echo "

      "; + + if ($recaptcha_public_key) { + // Trigger recaptcha! + form_general("", boinc_recaptcha_get_html($recaptcha_public_key)); + } + + echo ""; + echo "


      "; + } else { + // The user is not logged in! + echo "

      You need to be logged in to use this functionality.

      "; + } + + page_tail(); +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + account_ownership_action($user); +} else { + account_ownership_form($user); +} + +?> diff --git a/html/user/account_ownership_action.php b/html/user/account_ownership_action.php deleted file mode 100644 index 92850bad93..0000000000 --- a/html/user/account_ownership_action.php +++ /dev/null @@ -1,130 +0,0 @@ -. - -require_once("../inc/boinc_db.inc"); -require_once("../inc/user.inc"); -require_once("../inc/util.inc"); -require_once('../inc/recaptchalib.php'); - -check_get_args(array("tnow", "ttok")); - -// Check the user is online -$user = get_logged_in_user(); -check_tokens($user->authenticator); - -page_head(tra("Proof of account ownership results"), null, null, null, boinc_recaptcha_get_head_extra()); - -global $recaptcha_private_key; -if ($recaptcha_private_key) { - // Recaptcha is enabled on the BOINC server - if (!boinc_recaptcha_isValidated($recaptcha_private_key)) { - // The user failed to solve the recaptcha prompt - redirect them to an error message! - error_page( - tra("Your reCAPTCHA response was not correct. Please try again.") - ); - } -} - -// Input is passed in from the openssl_sign_form -$user_data = htmlentities(post_str("user_data", true), ENT_QUOTES, "UTF-8"); // Convert special characters to html equivelant - -if ((strlen($user_data) > 0) && (strlen($user_data) <= 4096)) { - // The user data input is valid - $config = get_config(); - $keydir = parse_config($config, ""); - - /* - How to generate required keys in /project/keys/ folder: - openssl genpkey -algorithm RSA -out ownership_sign_private.pem -pkeyopt rsa_keygen_bits:2048 - openssl rsa -pubout -in ownership_sign_private.pem -out ownership_sign_public.pem - chown -R boincadm:boincadm ext* - chmod --reference upload_private ownership_sign_public.pem - chmod --reference upload_private ownership_sign_private.pem - */ - - // If the following keys do not exist, then the users will be shown an error message. - $private_key_path = "file://$keydir/ownership_sign_private.pem"; - $public_key_path = "file://$keydir/ownership_sign_public.pem"; - - // Check that the private key file exists where specified. If not, redirect to error page. - if (!file_exists($private_key_path)) { - error_page(tra("The required private key doesn't exist. Contact the project administrator to resolve this issue.")); - } - - // Check that the public key file exists where specified. If not, redirect to error page. - if (!file_exists($public_key_path)) { - error_page(tra("The required public key doesn't exist. Contact the project administrator to resolve this issue.")); - } - - $privkey = fopen($private_key_path, "r"); // Opening private key file - if (!isset($privkey) || empty($privkey)) { - error_page(tra("Unable to access the required private key. Contact the project administrator to resolve this issue.")); - } - $privkey_contents = fread($privkey, 8192); // Reading contents of private key into var - fclose($privkey); // Closing private key file - - $userid = $user->id; // Retrieving the user's UserId - $message_data = "$userid $user_data"; // Create the message which will be signed. - - $private_key_pem = openssl_pkey_get_private($privkey_contents); // Loading the private key into memory - openssl_sign($message_data, $signature, $private_key_pem, OPENSSL_ALGO_SHA512); // Compute signature using SHA512 - openssl_free_key($private_key_pem); // Free the private key from memory for additional security - - $pubkey = fopen($public_key_path, "r"); // Open public key file - if ((!isset($pubkey)) || empty($pubkey)) { - error_page(tra("Unable to access the required public key. Contact the project administrator to resolve this issue.")); - } - $pubkey_contents = fread($pubkey, 8192); // Read contents to var - fclose($pubkey); // Close pub key file - - $base64_sig = base64_encode($signature); // Base64 encode the generated signature to enable safe output to text file. - $decoded_sig = base64_decode($base64_sig); // Decode base64 sig for use in sig_verification - $pubkeyid = openssl_pkey_get_public($pubkey_contents); // fetch public key into memory - $sig_verification = openssl_verify($message_data, $decoded_sig, $pubkeyid, OPENSSL_ALGO_SHA512); // Verify that the generated signature against the original data, using the public key. - openssl_free_key($pubkeyid); // Free the public key from memory - - // Check if signature was successfully validated - if ($sig_verification == 1) { - // The generated signature has been successfully verified using the public key. - global $master_url; // Define global master_url variable for use in output - echo "

      Do not share this information with anyone other than the external system which has requested this proof of account ownership.

      "; - echo ""; - echo "
      "; - echo ''; - page_tail(); - - } elseif ($sig_verification == 0) { - // The generated signature has not been verified. The private/public keys do not match. - error_page(tra("Signature verification failed. Try again at a later time.")); - } else { - // Something has gone wrong & an error has occurred. - error_page(tra("An error occured during the signature verification. Try again at a later time.")); - } -} else { - // User data input invalid - error_page(tra("Invalid input. User input must have a length > 0 and < 4096.
      ")); -} - -?> diff --git a/html/user/account_ownership_form.php b/html/user/account_ownership_form.php deleted file mode 100644 index ff63acadf4..0000000000 --- a/html/user/account_ownership_form.php +++ /dev/null @@ -1,73 +0,0 @@ -. - -require_once("../inc/boinc_db.inc"); -require_once("../inc/user.inc"); -require_once("../inc/util.inc"); -require_once("../inc/countries.inc"); -require_once('../inc/recaptchalib.php'); - -check_get_args(array("tnow", "ttok")); - -$user = get_logged_in_user(); -check_tokens($user->authenticator); - -page_head(tra("Generate proof of account ownership"), null, null, null, boinc_recaptcha_get_head_extra()); - -// Verify the user is logged in -if ($user) { - // If the following keys do not exist, then the users will be shown an error message. - $config = get_config(); - $keydir = parse_config($config, ""); - $private_key_path = "file://$keydir/ownership_sign_private.pem"; - $public_key_path = "file://$keydir/ownership_sign_public.pem"; - - // Check that the private key file exists where specified. If not, redirect to error page. - if (!file_exists($private_key_path)) { - error_page(tra("The private key doesn't exist. Contact the project administrator to resolve this issue.")); - } - - // Check that the public key file exists where specified. If not, redirect to error page. - if (!file_exists($public_key_path)) { - error_page(tra("The public key doesn't exist. Contact the project administrator to resolve this issue.")); - } - - echo "

      This tool is designed to create a proof of account ownership for external systems.

      "; - echo "

      Enter a message with length less than 4096 characters into the input textbox below, solve the captcha then click the 'Generate' button.

      "; - echo "

      A textbox will then appear which contains your proof of account ownership."; - echo "

      "; - - echo form_tokens($user->authenticator); - echo "

      "; - - // Trigger recaptcha! - global $recaptcha_public_key; - if ($recaptcha_public_key) { - form_general("", boinc_recaptcha_get_html($recaptcha_public_key)); - } - - echo "
      "; - echo "


      "; -} else { - // The user is not logged in! - echo "

      You need to be logged in to use this functionality.

      "; -} - -page_tail(); - -?> diff --git a/html/user/get_project_config.php b/html/user/get_project_config.php index 1461a7dac7..ca7eddd42e 100644 --- a/html/user/get_project_config.php +++ b/html/user/get_project_config.php @@ -19,6 +19,7 @@ require_once("../inc/consent.inc"); require_once("../inc/util.inc"); require_once("../inc/xml.inc"); +require_once("../inc/account_ownership.inc"); if(file_exists('../inc/release.inc')) include '../inc/release.inc'; @@ -138,11 +139,10 @@ if (file_exists("../../project_keywords.xml")) { readfile("../../project_keywords.xml"); } -$keydir = parse_config($config, ""); -if (is_readable($keydir."/ownership_sign_public.pem")) { - echo " "; - echo base64_encode(file_get_contents($keydir."/ownership_sign_public.pem")); - echo "\n"; +if (is_readable($account_ownership_public_key)) { + echo " "; + echo base64_encode(file_get_contents($account_ownership_public_key)); + echo "\n"; } echo ""; diff --git a/tools/make_project b/tools/make_project index 0606dd674f..98015824c7 100755 --- a/tools/make_project +++ b/tools/make_project @@ -324,6 +324,11 @@ try: except: print '''Couldn't install translation files''' +try: + os.system('cd '+proot+'/html/ops; ./generate_account_ownership_keys.php') +except: + print '''Couldn't generate account ownership keypair''' + print '''Done installing default daemons.''' # copy the test app if needed