commit 1687273e65e829e55167db16dfcd92342969e35e Author: Cuga Rajal Date: Thu Aug 25 16:41:04 2022 -0700 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc5350e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +development/* +old/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..f98fa50 --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +HGAuth + +An Opensim authentication module that can enforce a Web Form submission before allowing +inbound HG teleport + +Version 1.0, August 25, 2022 + +*** Summary + +This is a re-write of Project Sasha which has not been developed since 2018. + +It is a set of PHP scripts that provide a way to enforce inbound HG teleporting +avatars (from other grids) to agree to terms presented on a web page, before they +are allowed to enter. + +Although I am not aware of any issues, please use it at your own risk. + + +*** How it works + +Avatars attempting to HG teleport to a grid with this package installed, will +receive a rejection dialog in the Viewer with a customizable message and an +external link. Clicking that link will take them to an external web page with an +on-page message and a form. The web form pre-fills their avatar address so they +can not enter it manually. They are asked to confirm or reject the agreement. +Clicking the Confirm/Yes button on the form will authorize them for future +inbound HG teleports. + +The package prevents the avatar name from being altered and prevents submitting +avatar names other than the one actually used in the viewer. + +The verbiage on the web page can be changed or adapted to suit your needs. +Project Sasha was originally developed to enforce legal requirements of GDPR for +residents of the EU. However the form can be used to enforce TOS or other needs. + + +*** Changes from Project Sasha + +- Eliminated transmitting the username and UUID in the clear in the URL namespace +- Eliminated vulnerability of the form processor that could result in database misuse +- Properly handle form submission through jQuery and AJAX +- Fix PHP code not compatible with PHP 8.x +- Consolidation of PHP templates to simplify UI changes +- Added developer information, to report bugs and feature requests + + +*** How to install + +The file hgauth.sql will give you the table structure you need +import it into your mysql server, into the database you choose. + +The file authconfig.php contains database credentials and configurations for the +other scripts. It is used only as an include file so it could be placed outside +the document root. + +The file hgauth.php is expected to be used to receive HG teleport authorization +requests from an Opensim Authorization Service Connector via HTTP, and to send +responses back to that service. This file also includes a message that appears +in the viewer's dialog box when the initial inbound teleport request is +rejected. You may wish to update the message. + +The file index.php is expected to be used to present an authorization form on a +web browser to a user who has clicked the link in the viewer's HG TP rejection +dialog. You may wish to change the on-screen message to suit your needs. + +After the files are configured and placed in appropriate web server directories, +you need to make some changes to your Opensim configuration files for these to +take effect. + +in file: +config-include/GridCommon.ini (Grid Mode) +or +config-include/StandaloneCommon.ini (Standalone Mode) + +1) Add the following in the [Modules] section: + + AuthorizationServices = "RemoteAuthorizationServicesConnector" + +2) Add the following in the [AuthorizationService] section: + + AuthorizationServerURI = "http://yourwebserver/path/to/hgauth.php" + + +For security it is recommended to restrict web access to these files. A sample +htaccess.txt file for Apache is included (apache 2.4 syntax). This should be +renamed .htaccess and placed in the directory containing the PHP scripts. Apache +may need to be configured to read the .htaccess file. + + + authconfig.php - should not be accessed directly, it is meant to be an include file only + hgauth.php - access should be restricted to the IP of the Opensim server's inbound HTTP connection + index.php - unrestricted web page + +Though not critical, make sure your date.timezone is set in your php.ini. +Failure to do so may result in database records containing a mix of local and +GMT times. Creation time is written by the internal clock of mySQL while +confirmation time is written by PHP. + + +*** Requirements + + * Webserver, tested on Apache + * PHP, tested and developed on PHP 8.x + * mySQL server, should work on all flavors of mySQL >= 7.x or MariaDB >= 10.x. + + +*** Credits + +Portions of the code are taken from Project Sasha. The following people (avatars) +are credited from Project Sasha: Foto50, Hack13, FreakyTech and Leighton Marjoram. + + + +---- + +This is a work in progress. Please notify me of bugs or feature requests. + +Cuga Rajal (Second Life and OSGrid) +cuga@rajal.org \ No newline at end of file diff --git a/authconfig.php b/authconfig.php new file mode 100644 index 0000000..7d54f98 --- /dev/null +++ b/authconfig.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/hgauth.php b/hgauth.php new file mode 100644 index 0000000..1234379 --- /dev/null +++ b/hgauth.php @@ -0,0 +1,84 @@ +m_isAuthorized = $isAuthorized; + $this->m_message = $message; + } + + public function toXML() { + return '' . + '' . + ''. $this->m_isAuthorized .'' . + 'm_message .']]>'; + } +} + +$request = @file_get_contents('php://input'); +$xml3 = simplexml_load_string($request); +$uuid = $xml3->ID; +$firstname = $xml3->FirstName; +$lastname = $xml3->SurName; +$nametest = substr($lastname,0,1); +$avatarname = $firstname.$lastname; + +$query = "SELECT * FROM $tablename WHERE avatarname LIKE '" . $avatarname . "'"; +$data = mysqli_query($dbc, $query); + + +if(($nametest == "@") && (mysqli_num_rows($data)>0)) { + $row = mysqli_fetch_array($data); + if($row['uuid']=='') { + $query2 = "UPDATE $tablename SET uuid='$uuid' WHERE id=" . $row['id']; + $data2 = mysqli_query($dbc, $query2); + } + $authResp = new AuthorizationResponse("true","Authorized"); + echo $authResp->toXML(); + mysqli_close($dbc); + exit(); +} + +if(($nametest == "@") && (mysqli_num_rows($data)==0)) { + $token = substr(hash("sha1", $avatarname, false),0,7); + $query = "INSERT INTO $tablename (`token`) VALUES ('". $token . "')"; + $data = mysqli_query($dbc, $query); + + $getstring = base64_url_encode("fn=" . $firstname . "&ln=" . $lastname); + $authlink2 = $authlink . "?token=" . $getstring; + + $newmsg = sprintf($msgformat,$firstname,$authlink2); + $authResp = new AuthorizationResponse("false",$newmsg); + echo $authResp->toXML(); + mysqli_close($dbc); + exit(); +} + +if($nametest != "@") { + $authResp = new AuthorizationResponse("true","Authorized"); + echo $authResp->toXML(); + mysqli_close($dbc); + exit(); +} + +if($nametest != "" ) { + $authResp = new AuthorizationResponse("false",$failmsg); + echo $authResp->toXML(); + mysqli_close($dbc); + exit(); +} + + +?> \ No newline at end of file diff --git a/hgauth.sql b/hgauth.sql new file mode 100644 index 0000000..ca3eec5 --- /dev/null +++ b/hgauth.sql @@ -0,0 +1,21 @@ + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET AUTOCOMMIT = 0; +START TRANSACTION; +SET time_zone = "+00:00"; + + + +CREATE TABLE IF NOT EXISTS `hgauth` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `token` varchar(8) CHARACTER SET utf8 NOT NULL, + `uuid` char(36) CHARACTER SET utf8, + `avatarname` varchar(64) CHARACTER SET utf8, + `createtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `confirmtime` datetime, + PRIMARY KEY (`id`), + UNIQUE KEY uuid (`uuid`), + UNIQUE KEY avatarname (`avatarname`) +) DEFAULT CHARSET=utf8; +COMMIT; + diff --git a/htaccess.txt b/htaccess.txt new file mode 100644 index 0000000..08893f5 --- /dev/null +++ b/htaccess.txt @@ -0,0 +1,10 @@ +Options All -Indexes + + +Require all denied + + + +Require all denied +Require forward-dns mydomain.com + \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..024cb5d --- /dev/null +++ b/index.php @@ -0,0 +1,174 @@ +1"; } // If query successful report success + else { $xml = "0$query"; } // If malformed query, report it as a problem + if(mysqli_affected_rows($dbc)==0) { $xml = "0$query"; } // But if no rows were updated, report it as a problem + header('Content-type: text/xml'); + echo "\n$xml\n"; + } + exit(0); +} +// END of AJAX section + +$skip = FALSE; + +// Make sure URL has a "token" in the query string. This is an encoded string of name/value pairs +if((! isset($_REQUEST['token'])) || ((isset($_REQUEST['token'])) && ($_REQUEST['token']==''))) { + $message = $tokenerror; + $skip = TRUE; + goto postcheck; +} + +$t = base64_url_decode($_REQUEST['token']); + +// $t is the decoded query string with a list of name/value pairs +// Check that $t has the expected name/value pairs +if((strpos($t, "@")===false) || (strpos($t, "fn=")===false) || (strpos($t, "ln=")===false)) { + $message = $tokenerror; + $skip = TRUE; + goto postcheck; +} + +// extract and sanitize the name/value pairs +list($sf, $sl) = explode("&", base64_url_decode($_REQUEST['token'])); +$firstname = mysqli_real_escape_string($dbc,explode("=", $sf)[1]); +$lastname = mysqli_real_escape_string($dbc,explode("=", $sl)[1]); +$avatarname = $firstname."".$lastname; +$nametest = substr($lastname,0,1); +$expected = substr(hash("sha1", $avatarname, false),0,7); + +// It's possible for someone to alter the query string and still have readable name/value pairs. +// Check that avatar name and stored hash match to make sure there they didn't alter the query string +$query = "SELECT * FROM $tablename WHERE token='$expected'"; +$data = mysqli_query($dbc, $query); +if(mysqli_num_rows($data)==0) { + $message = $tokenerror; + $skip = TRUE; + goto postcheck; +} + +// Now that we know the sent avatar name hasn't been altered, check if it's already registered +$query = "SELECT * FROM $tablename WHERE avatarname LIKE '$avatarname'"; +$data = mysqli_query($dbc, $query); + +if(mysqli_num_rows($data)>0) { // Avatar name exists, they already registered + $message = $avatarname . ", " . $alreadyreg; + $skip = TRUE; +} else { // Avatar name doesn't exist, proceed with registration + $message = $regmsg; +} + +postcheck: + +// Prevent hand-crafted forms from being able to submit +$_SESSION['id'] = session_id(); + +// Adjust the following HTML text as needed +?> + + + +Authorize HG Avatar + + + +
+

Authorize HG Avatar

+
+ + +

+ +

+Our grid requires all people entering the grid to agree to the Terms of Service and to be at least 18 years of age. +
+
+If you are a member of the European Union, GDP Regulations +require you to give us permission to store and use your data, including: +avatar first and last name, avatar UUID, and your IP address, for you and the avatars you may interact with. +
+Other activity in Opensim, such as Friendships, +Friendship Requests, Instant Messages, Profiles, and inventory exchanges, may also expose this information, +regardless if you actually travel to a foreign grid. +
+Your data will only be used for the purpose of your visits here and your interactions with other users. We will not share it with any 3rd party +unless required to do so by law. +
+
+Accepting the agreement below indicates acceptance of the Terms of Service and, if applicable, the GDPR. +
+
+
+
+ + +
    +
  • I agree to the Terms of Service
  • +
  • I confirm I am 18 years of age or older
  • +
  • If I live in the EU, I give you my permission to collect and use my data as indicated above
  • +
+     + +
+
+ + +
+ + + + + + +