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$devId
in 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$devId
in core/UserAPI.php on line 573
$devId
is returnedin core/UserAPI.php on line 583
$this->deviceFromRequest()
is assigned to$devId
in core/UserAPI.php on line 517
returnDevice()
, and$this->returnDevice($devId, $Dev[$devId])
is assigned to$ret
in core/UserAPI.php on line 519
$ret
is 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$statusInfo
in 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$devId
in 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$devId
in core/UserAPI.php on line 571
$devId
is returnedin core/UserAPI.php on line 583
$this->deviceFromRequest()
is assigned to$devId
in core/UserAPI.php on line 517
returnDevice()
, and$this->returnDevice($devId, $Dev[$devId])
is assigned to$ret
in core/UserAPI.php on line 519
$ret
is 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$statusInfo
in 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:
For numeric data, we recommend to explicitly cast the data: