|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* Contributions to this work were made on behalf of the GÉANT project, a |
|
5
|
|
|
* project that has received funding from the European Union’s Horizon 2020 |
|
6
|
|
|
* research and innovation programme under Grant Agreement No. 731122 (GN4-2). |
|
7
|
|
|
* |
|
8
|
|
|
* On behalf of the GÉANT project, GEANT Association is the sole owner of the |
|
9
|
|
|
* copyright in all material which was developed by a member of the GÉANT |
|
10
|
|
|
* project. GÉANT Vereniging (Association) is registered with the Chamber of |
|
11
|
|
|
* Commerce in Amsterdam with registration number 40535155 and operates in the |
|
12
|
|
|
* UK as a branch of GÉANT Vereniging. |
|
13
|
|
|
* |
|
14
|
|
|
* Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. |
|
15
|
|
|
* UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK |
|
16
|
|
|
* |
|
17
|
|
|
* License: see the web/copyright.inc.php file in the file structure or |
|
18
|
|
|
* <base_url>/copyright.php after deploying the software |
|
19
|
|
|
*/ |
|
20
|
|
|
?> |
|
21
|
|
|
<?php |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* Skin selection for user pages |
|
25
|
|
|
* |
|
26
|
|
|
* @author Stefan Winter <[email protected]> |
|
27
|
|
|
* @package Core |
|
28
|
|
|
*/ |
|
29
|
|
|
require_once dirname(dirname(dirname(__FILE__))) . "/config/_config.php"; |
|
30
|
|
|
|
|
31
|
|
|
$cleanToken = FALSE; |
|
32
|
|
|
$invitationObject = new core\SilverbulletInvitation("INVALID"); |
|
33
|
|
|
$profile = NULL; |
|
34
|
|
|
$idp = NULL; |
|
35
|
|
|
$fed = NULL; |
|
36
|
|
|
|
|
37
|
|
|
$validator = new \web\lib\common\InputValidation(); |
|
38
|
|
|
$Gui = new \web\lib\user\Gui(); |
|
39
|
|
|
|
|
40
|
|
|
if (isset($_REQUEST['token'])) { |
|
41
|
|
|
$recoverTokenUnfiltered = filter_input(INPUT_GET, 'token') ?? filter_input(INPUT_POST, 'token') ?? "INVALID"; |
|
42
|
|
|
$recoverToken = htmlspecialchars(strip_tags($recoverTokenUnfiltered)); |
|
43
|
|
|
$cleanToken = $validator->token($recoverToken); |
|
44
|
|
|
if ($cleanToken) { |
|
45
|
|
|
// check status of this silverbullet token according to info in DB: |
|
46
|
|
|
// it can be VALID (exists and not redeemed, EXPIRED, REDEEMED or INVALID (non existent) |
|
47
|
|
|
$invitationObject = new core\SilverbulletInvitation($cleanToken); |
|
48
|
|
|
} |
|
49
|
|
|
} elseif (isset($_SERVER['SSL_CLIENT_SAN_Email']) || isset($_SERVER['SSL_CLIENT_SAN_Email_0'])) { |
|
50
|
|
|
// maybe the user authenticated with his client cert? Then pick any of his |
|
51
|
|
|
// tokens to go on |
|
52
|
|
|
$certname = $_SERVER['SSL_CLIENT_SAN_Email'] ?? $_SERVER['SSL_CLIENT_SAN_Email_0']; |
|
53
|
|
|
if (preg_match("R$", $_SERVER['SSL_CLIENT_I_DN'])) { |
|
54
|
|
|
$certObject = new \core\SilverbulletCertificate($certname, devices\Devices::SUPPORT_EMBEDDED_RSA); |
|
55
|
|
|
} else if (preg_match("E$", $_SERVER['SSL_CLIENT_I_DN'])) { |
|
56
|
|
|
$certObject = new \core\SilverbulletCertificate($certname, devices\Devices::SUPPORT_EMBEDDED_ECDSA); |
|
57
|
|
|
} else { |
|
58
|
|
|
throw new Exception("We got an accepted certificate authentication, but can't find the certificate in the database!"); |
|
59
|
|
|
} |
|
60
|
|
|
$profile = new \core\ProfileSilverbullet($certObject->profileId); |
|
61
|
|
|
$allTokens = $profile->userStatus($certObject->userId); |
|
62
|
|
|
$invitationObject = $allTokens[0]; |
|
63
|
|
|
$cleanToken = $invitationObject->invitationTokenString; |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
if ($invitationObject->invitationTokenStatus != \core\SilverbulletInvitation::SB_TOKENSTATUS_INVALID) { // determine skin to use based on NROs preference |
|
67
|
|
|
$profile = new \core\ProfileSilverbullet($invitationObject->profile, NULL); |
|
68
|
|
|
$idp = new \core\IdP($profile->institution); |
|
69
|
|
|
$fed = $validator->existingFederation(strtoupper($idp->federation)); |
|
70
|
|
|
$fedskin = $fed->getAttributes("fed:desired_skin"); |
|
71
|
|
|
} |
|
72
|
|
|
// ... unless overwritten by direct GET/POST parameter in the request |
|
73
|
|
|
// ... with last resort being the default skin (first one in the configured skin list is the default) |
|
74
|
|
|
|
|
75
|
|
|
$statusInfo = ["token" => $cleanToken, |
|
76
|
|
|
"invitation_object" => $invitationObject, |
|
77
|
|
|
"OS" => $Gui->operatingSystem,]; |
|
78
|
|
|
|
|
79
|
|
|
if ($profile !== NULL) { |
|
80
|
|
|
$attributes = $Gui->profileAttributes($profile->identifier); |
|
81
|
|
|
$statusInfo["profile"] = $profile; |
|
82
|
|
|
$statusInfo["attributes"] = $Gui->profileAttributes($profile->identifier); |
|
83
|
|
|
$statusInfo["profile_id"] = $invitationObject->profile; |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
$action = filter_input(INPUT_GET, 'action', FILTER_VALIDATE_INT); |
|
87
|
|
|
$caAndSerialUnfiltered = filter_input(INPUT_GET, 'serial'); |
|
88
|
|
|
$caAndSerial = htmlspecialchars(strip_tags($caAndSerialUnfiltered)); |
|
89
|
|
|
|
|
90
|
|
|
if ($action !== NULL && $action !== FALSE && $action === \web\lib\common\FormElements::BUTTON_DELETE && $caAndSerial !== NULL && $caAndSerial !== FALSE) { |
|
91
|
|
|
$tuple = explode(':',$caAndSerial); |
|
92
|
|
|
$ca_type = $tuple[0]; |
|
93
|
|
|
$serial = $tuple[1]; |
|
94
|
|
|
if ($statusInfo['invitation_object']->invitationTokenStatus != \core\SilverbulletInvitation::SB_TOKENSTATUS_INVALID) { |
|
95
|
|
|
$userdata = $profile->userStatus($statusInfo['invitation_object']->userId); |
|
96
|
|
|
// if the requested serial belongs to the user, AND it is currently valid, revoke it |
|
97
|
|
|
$allcerts = []; |
|
98
|
|
|
foreach ($userdata as $content) { |
|
99
|
|
|
$allcerts = array_merge($allcerts, $content->associatedCertificates); |
|
100
|
|
|
} |
|
101
|
|
|
foreach ($allcerts as $onecert) { |
|
102
|
|
|
if ($onecert->serial == $serial && $onecert->ca_type == $ca_type && $onecert->revocationStatus == 'NOT_REVOKED') { |
|
103
|
|
|
print "//REVOKING\n"; |
|
104
|
|
|
$certObject = new \core\SilverbulletCertificate($serial, $ca_type); |
|
105
|
|
|
$certObject->revokeCertificate(); |
|
106
|
|
|
header("Location: accountstatus.php?token=" . $statusInfo['token']); |
|
|
|
|
|
|
107
|
|
|
exit; |
|
108
|
|
|
} |
|
109
|
|
|
} |
|
110
|
|
|
} |
|
111
|
|
|
header("Location: accountstatus.php?token=" . $statusInfo['token']); |
|
|
|
|
|
|
112
|
|
|
exit; |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
if ($idp !== NULL) { |
|
116
|
|
|
$logo = $idp->getAttributes('general:logo_file'); |
|
117
|
|
|
$statusInfo["idp"] = $idp; |
|
118
|
|
|
$statusInfo["idp_id"] = $idp->identifier; |
|
119
|
|
|
$statusInfo["idp_logo"] = (count($logo) == 0 ? 0 : 1); |
|
120
|
|
|
$statusInfo["idp_name"] = $idp->name; |
|
121
|
|
|
$statusInfo["fed"] = new core\Federation($idp->federation); |
|
122
|
|
|
} |
|
123
|
|
|
|
|
124
|
|
|
const KNOWN_ERRORCODES = ["GENERATOR_CONSUMED"]; |
|
125
|
|
|
$errorcode = $_REQUEST['errorcode'] ?? ""; |
|
126
|
|
|
switch ($errorcode) { |
|
127
|
|
|
case KNOWN_ERRORCODES[0]: |
|
128
|
|
|
$statusInfo['errorcode'] = KNOWN_ERRORCODES[0]; |
|
129
|
|
|
break; |
|
130
|
|
|
default: |
|
131
|
|
|
$statusInfo['errorcode'] = NULL; |
|
132
|
|
|
} |
|
133
|
|
|
$skinObject = new \web\lib\user\Skinjob($_REQUEST['skin'] ?? $_SESSION['skin'] ?? $fedskin[0] ?? \config\Master::APPEARANCE['skins'][0]); |
|
134
|
|
|
|
|
135
|
|
|
// and now, serve actual data |
|
136
|
|
|
require "../skins/" . $skinObject->skin . "/accountstatus/accountstatus.php"; |
|
137
|
|
|
|
'Location: accountstatus... . $statusInfo['token']can contain request data and is used in response header context(s) leading to a potential security vulnerability.2 paths for user data to reach this point
$_POST,and Data is passed throughstrip_tags(), and Data is passed throughhtmlspecialchars(), andhtmlspecialchars(strip_tags($_POST['device']))is assigned to$devIdin core/UserAPI.php on line 573$_POST,and Data is passed throughstrip_tags(), and Data is passed throughhtmlspecialchars(), andhtmlspecialchars(strip_tags($_POST['device']))is assigned to$devIdin core/UserAPI.php on line 573
$devIdis returnedin core/UserAPI.php on line 583
$this->deviceFromRequest()is assigned to$devIdin core/UserAPI.php on line 517
returnDevice(), and$this->returnDevice($devId, $Dev[$devId])is assigned to$retin core/UserAPI.php on line 519
$retis returnedin core/UserAPI.php on line 521
$this->detectOS()is assigned to property Gui::$operatingSystemin web/lib/user/Gui.php on line 48
in web/accountstatus/accountstatus.php on line 77
array('token' => $cleanToken, 'invitation_object' => $invitationObject, 'OS' => $Gui->operatingSystem)is assigned to$statusInfoin web/accountstatus/accountstatus.php on line 75
$_GET,and Data is passed throughstrip_tags(), and Data is passed throughhtmlspecialchars(), andhtmlspecialchars(strip_tags($_GET['device']))is assigned to$devIdin core/UserAPI.php on line 571$_GET,and Data is passed throughstrip_tags(), and Data is passed throughhtmlspecialchars(), andhtmlspecialchars(strip_tags($_GET['device']))is assigned to$devIdin core/UserAPI.php on line 571
$devIdis returnedin core/UserAPI.php on line 583
$this->deviceFromRequest()is assigned to$devIdin core/UserAPI.php on line 517
returnDevice(), and$this->returnDevice($devId, $Dev[$devId])is assigned to$retin core/UserAPI.php on line 519
$retis returnedin core/UserAPI.php on line 521
$this->detectOS()is assigned to property Gui::$operatingSystemin web/lib/user/Gui.php on line 48
in web/accountstatus/accountstatus.php on line 77
array('token' => $cleanToken, 'invitation_object' => $invitationObject, 'OS' => $Gui->operatingSystem)is assigned to$statusInfoin web/accountstatus/accountstatus.php on line 75
Response Splitting Attacks
Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.
General Strategies to prevent injection
In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) { throw new \InvalidArgumentException('This input is not allowed.'); }For numeric data, we recommend to explicitly cast the data: