Issues (51)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

authorize/index-functions.php (7 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This file contains functions called by index-site.php. The index-site.php
5
 * file should include this file with the following statement at the top:
6
 *
7
 * require_once __DIR__ . '/index-functions.php';
8
 */
9
10
use CILogon\Service\Util;
11
use CILogon\Service\Content;
12
use CILogon\Service\DBService;
13
use CILogon\Service\Loggit;
14
15
/**
16
 * printLogonPage
17
 *
18
 * This function prints out the HTML for the main cilogon.org page.
19
 * Explanatory text is shown as well as a button to log in to an IdP
20
 * and get rerouted to the Shibboleth protected getuser script.
21
 */
22
function printLogonPage()
23
{
24
    $log = new Loggit();
25
    $log->info('Welcome page hit.');
26
27
    Content::printHeader(
28
        'Welcome To The CILogon OpenID Connect Authorization Service'
29
    );
30
31
    $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
32
33
    // If the <hideportalinfo> option is set, do not show the portal info
34
    // if the OIDC redirect_uri or client_id is in the portal list.
35
    $showportalinfo = true;
36
    $skin = Util::getSkin();
37
    if (
38
        ((int)$skin->getConfigOption('portallistaction', 'hideportalinfo') == 1) &&
39
        (
40
            ($skin->inPortalList($clientparams['redirect_uri'])) ||
41
            ($skin->inPortalList($clientparams['client_id']))
42
        )
43
    ) {
44
        $showportalinfo = false;
45
    }
46
47
    if ($showportalinfo) {
48
        Content::printOIDCConsent();
0 ignored issues
show
The method printOIDCConsent() does not seem to exist on object<CILogon\Service\Content>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
49
    }
50
    Content::printWAYF();
51
    Content::printFooter();
52
}
53
54
/**
55
 * printOIDCErrorPage
56
 *
57
 * This function prints out the HTML for the page when the the various
58
 * OIDC parameters sent by the client are missing or bad.
59
 */
60
function printOIDCErrorPage()
61
{
62
    $log = new Loggit();
63
    $log->warn('Missing or invalid OIDC parameters.');
64
65
    Content::printHeader('CILogon Authorization Endpoint');
66
    Content::printCollapseBegin('oidcdefault', 'CILogon OIDC Authorization Endpoint', false);
67
68
    echo '
69
        <div class="card-body px-5">
70
          <div class="card-text my-2">
71
            You have reached the CILogon OAuth2/OpenID Connect (OIDC)
72
            Authorization Endpoint. This service is for use by OAuth2/OIDC
73
            Relying Parties (RPs) to authorize users of the CILogon Service.
74
            End users should not normally see this page.
75
          </div> <!-- end row -->
76
    ';
77
78
    $client_error_msg = Util::getSessionVar('client_error_msg');
79
    Util::unsetSessionVar('client_error_msg');
80
    if (strlen($client_error_msg) > 0) {
81
        echo '<div class="alert alert-danger" role="alert">', $client_error_msg, '</div>';
82
    } else {
83
        echo '
84
          <div class="card-text my-2">
85
            Possible reasons for seeing this page include:
86
          </div> <!-- end row -->
87
          <div class="card-text my-2">
88
            <ul>
89
              <li>You navigated directly to this page.</li>
90
              <li>You clicked your browser\'s "Back" button.</li>
91
              <li>There was a problem with the OpenID Connect client.</li>
92
            </ul>
93
          </div> <!-- end row -->
94
        ';
95
    }
96
97
    echo '
98
          <div class="card-text my-2">
99
            For assistance, please contact us at the email address at the
100
            bottom of the page.
101
          </div>
102
          <div class="card-text my-2">
103
            <strong>Note:</strong> You must enable cookies in your web
104
            browser to use this site.
105
          </div>
106
        </div> <!-- end card-body -->
107
    ';
108
109
    Content::printCollapseEnd();
110
    Content::printFooter();
111
}
112
113
/**
114
 * printMainPage
115
 *
116
 * This function is poorly named for the OIDC case, but is called by
117
 * gotUserSuccess, so the name stays. This function is called once the
118
 * user has successfully logged on at the selected IdP. In the OIDC
119
 * case, the user's UID is then paired with the OIDC 'code' and
120
 * 'authntime' in the datastore so that it can be fetched later when
121
 * the OIDC client wants to get userinfo or a certificate. There
122
 * really isn't anything 'printed' to the user here. Control is
123
 * simply redirected to the OIDC client with appropriate success or
124
 * error response.
125
 */
126
function printMainPage()
127
{
128
    $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
129
    $redirect = 'Location: ' . $clientparams['redirect_url'];
130
131
    $log = new Loggit();
132
    $log->info('Calling setTransactionState dbService method...');
133
134
    $dbs = new DBService();
135
    if (
136
        ($dbs->setTransactionState(
137
            $clientparams['code'],
138
            Util::getSessionVar('user_uid'),
139
            Util::getSessionVar('authntime'),
140
            Util::getLOA(),
141
            Util::getSessionVar('myproxyinfo')
142
        )) && (!($dbs->status & 1))
143
    ) { // STATUS_OK codes are even
144
        // CIL-360 - Check for Response Mode
145
        // http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
146
        if (isset($clientparams['response_mode'])) {
147
            $responsemode = $clientparams['response_mode'];
148
            if ($responsemode == 'query') {
149
                // This is the default mode for 'code' response
150
            } elseif ($responsemode == 'fragment') {
151
                // Replace '?' with '#'
152
                $redirect = str_replace('?', '#', $redirect);
153
            } elseif ($responsemode == 'form_post') {
154
                // https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
155
                // At this point, $clientparams['redirect_url'] contains
156
                // both the callback uri and all query string parameters
157
                // that should be passed to the callback uri. We need
158
                // to separate the two so we can put the query parameters
159
                // into hidden <input> fields in the output form.
160
                $orig_redirect_uri = $clientparams['redirect_uri'];
161
                $full_redirect_url = $clientparams['redirect_url'];
162
                $queryparams = str_replace(
163
                    $orig_redirect_uri . '?',
164
                    '',
165
                    $full_redirect_url
166
                );
167
                Util::unsetClientSessionVars();
168
                // Util::unsetAllUserSessionVars();
169
                // Get the components of the response (split by '&')
170
                $comps = explode('&', $queryparams);
171
                $outform = '<html>
172
  <head><title>Submit This Form</title></head>
173
  <body onload="javascript:document.forms[0].submit()">
174
    <form method="post" action="' . $orig_redirect_uri . '">
175
    ';
176
                foreach ($comps as $value) {
177
                    $params = explode('=', $value);
178
                    $outform .= '<input type="hidden" name="' . $params[0] .
179
                         '" value="' . html_entity_decode($params[1]) . '"/>';
180
                }
181
                $outform .= '
182
    </form>
183
  </body>
184
</html>';
185
                $log->info(
186
                    'response_mode=form_post; outputting form' . "\n" .
187
                    $outform
188
                );
189
                echo $outform;
190
                exit; // No further processing necessary
191
            }
192
        }
193
        $log->info('setTransactionState succeeded, redirect to ' . $redirect);
194
        // CIL-507 Special log message for XSEDE
195
        $email = Util::getSessionVar('email');
196
        $clientname = $clientparams['client_name'];
197
        $log->info("USAGE email=\"$email\" client=\"$clientname\"");
198
        Util::logXSEDEUsage($clientname, $email);
0 ignored issues
show
The method logXSEDEUsage() does not seem to exist on object<CILogon\Service\Util>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
199
    } else { // dbservice error
200
        $errstr = '';
201
        if (!is_null($dbs->status)) {
202
            $errstr = array_search($dbs->status, DBService::$STATUS);
203
        }
204
        $redirect = 'Location: ' . $clientparams['redirect_uri'] .
205
            (preg_match('/\?/', $clientparams['redirect_uri']) ? '&' : '?') .
206
            'error=server_error&error_description=' .
207
            'Unable%20to%20associate%20user%20UID%20with%20OIDC%20code' .
208
            ((isset($clientparams['state'])) ?
209
                '&state=' . $clientparams['state'] : '');
210
        $log->info("setTransactionState failed $errstr, redirect to $redirect");
211
        Util::sendErrorAlert(
212
            'dbService Error',
213
            'Error calling dbservice action "setTransactionState" in ' .
214
            'OIDC authorization endpoint\'s printMainPage() method. ' .
215
            $errstr . ' Redirected to ' . $redirect
216
        );
217
        Util::unsetUserSessionVars();
218
    }
219
220
    Util::unsetClientSessionVars();
221
    // Util::unsetAllUserSessionVars();
222
    header($redirect);
223
    exit; // No further processing necessary
224
}
225
226
/**
227
 * verifyOIDCParams
228
 *
229
 * This function verifies that all of the various OIDC parameters are
230
 * set in the PHP session. First, the function checks if an OIDC
231
 * client has passed appropriate parameters to the authorization
232
 * endpoint. If so, we call the 'real' OA4MP OIDC authorization
233
 * endpoint and let it verify the client parameters. Upon successful
234
 * return, we read the database to get the OIDC client information
235
 * to display to the user. All client parameters (including the ones
236
 * passed in) are saved to the 'clientparams' PHP session variable,
237
 * which is encoded as a JSON token to preserve arrays. If there are
238
 * any errors, false is returned and an email is sent. In some cases
239
 * the session variable 'client_error_msg' is set so it can be
240
 * displayed by the printOIDCErrorPage() function.
241
 *
242
 * @return bool True if the various parameters related to the OIDC
243
 *         session are present. False otherwise.
244
 */
245
function verifyOIDCParams()
246
{
247
    $retval = false; // Assume OIDC session info is not valid
248
249
    // Combine the $_GET and $_POST arrays into a single array which can be
250
    // stored in the 'clientparams' session variable as a JSON object.
251
    $clientparams = array();
252
    foreach ($_GET as $key => $value) {
253
        $clientparams[$key] = $value;
254
    }
255
    foreach ($_POST as $key => $value) {
256
        $clientparams[$key] = $value;
257
    }
258
259
    // CIL-624 If X509 certs are disabled, check for 'getcert' scope.
260
    // If found, show an error message.
261
    $scope = Util::getGetVar('scope');
262
    if (
263
        (defined('DISABLE_X509')) &&
264
        (DISABLE_X509 === true) &&
265
        (preg_match('/edu.uiuc.ncsa.myproxy.getcert/', $scope))
266
    ) {
267
        Util::sendErrorAlert(
268
            'CILogon OIDC authz endpoint error',
269
            'The CILogon OIDC authorization endpoint received a request ' .
270
            'including the "edu.ncsa.uiuc.myproxy.getcert" scope, ' .
271
            'but the server is configured with DISABLE_X509 to prevent ' .
272
            'downloading certificates. ' .
273
            "\n\n" .
274
            'clientparams = ' . print_r($clientparams, true) .
275
            "\n"
276
        );
277
        Util::setSessionVar(
278
            'client_error_msg',
279
            'The CILogon Service is currently configured to prevent ' .
280
            'downloading X.509 certificates, but the incoming request ' .
281
            'included the "edu.ncsa.uiuc.myproxy.getcert" scope. ' .
282
            'CILogon system administrators have been notified.'
283
        );
284
        $clientparams = array();
285
286
    // If the 'redirect_uri' parameter was passed in then let the 'real'
287
    // OA4MP OIDC authz endpoint handle parse the request since it might be
288
    // possible to return an error code to the client.
289
    } elseif (isset($clientparams['redirect_uri'])) {
290
        $ch = curl_init();
291
        if ($ch !== false) {
292
            $url = OAUTH2_CREATE_TRANSACTION_URL;
293
            if (count($_GET) > 0) {
294
                // CIL-658 Look for double-encoded spaces in 'scope'
295
                if (strlen($scope) > 0) {
296
                    $_GET['scope'] = preg_replace('/(\+|%2B)/', ' ', $scope);
297
                }
298
                $url .= (preg_match('/\?/', $url) ? '&' : '?') .
299
                    http_build_query($_GET);
300
            }
301
            if (count($_POST) > 0) {
302
                curl_setopt($ch, CURLOPT_POST, true);
303
                curl_setopt($ch, CUROPT_POSTFIELDS, http_build_query($_POST));
304
            }
305
            curl_setopt($ch, CURLOPT_URL, $url);
306
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
307
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
308
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // Catch redirects
309
            $output = curl_exec($ch);
310
            if (curl_errno($ch)) { // Send alert on curl errors
311
                Util::sendErrorAlert(
312
                    'cUrl Error',
313
                    'cUrl Error    = ' . curl_error($ch) . "\n" .
314
                    "URL Accessed  = $url" .
315
                    "\n\n" .
316
                    'clientparams = ' . print_r($clientparams, true)
317
                );
318
                $clientparams = array();
319
            } else {
320
                $info = curl_getinfo($ch);
321
                if ($info !== false) {
322
                    if (
323
                        (isset($info['http_code'])) &&
324
                        ($info['http_code'] == 200)
325
                    ) {
326
                        // The OA4MP OIDC authz endpoint responded with 200
327
                        // (success). The body of the message should be a
328
                        // JSON token containing the appropriate parameters
329
                        // such as the 'code'.
330
                        $json = json_decode($output, true);
331
                        if (isset($json['code'])) {
332
                            // Got 'code' - save to session and read OIDC
333
                            // client info from the database to display
334
                            // to the user
335
                            $clientparams['redirect_url'] =
336
                                $clientparams['redirect_uri'] .
337
                                (preg_match('/\?/', $clientparams['redirect_uri']) ? '&' : '?') .
338
                                http_build_query($json);
339
                            $clientparams['code'] = $json['code'];
340
                            // CIL-618 Read OIDC client info from database
341
                            if (!Util::getOIDCClientParams($clientparams)) {
342
                                Util::sendErrorAlert(
343
                                    'getOIDCClientParams Error',
344
                                    'Error getting OIDC client parameters ' .
345
                                    'in verifyOIDCParams() function for ' .
346
                                    'client_id="' .
347
                                    $clientparams['client_id'] . '".'
348
                                );
349
                                $clientparams = array();
350
                            }
351
                        } else {
352
                            // Either the output returned was not a valid
353
                            // JSON token, or there was no 'code' found in
354
                            // the returned JSON token.
355
                            $errortxt = getErrorStatusText($output, $clientparams);
356
357
                            Util::sendErrorAlert(
358
                                'OA4MP OIDC authz endpoint error',
359
                                (!empty($errortxt) ? $errortxt :
360
                                'The OA4MP OIDC authorization endpoint ' .
361
                                'returned an HTTP response 200, but either ' .
362
                                'the output was not a valid JSON token, or ' .
363
                                'there was no "code" in the JSON token. ' .
364
                                ((strlen($output) > 0) ?
365
                                    "\n\nReturned output =\n$output" : '')) .
366
                                "\n\n" .
367
                                'curl_getinfo = ' . print_r($info, true) . "\n\n" .
368
                                'clientparams = ' . print_r($clientparams, true) .
369
                                "\n"
370
                            );
371
                            Util::setSessionVar(
372
                                'client_error_msg',
373
                                'There was an unrecoverable error during the transaction. ' .
374
                                'CILogon system administrators have been notified. ' .
375
                                (!empty($errortxt) ? "<p><b>Error message: $errortxt</b><p>" : '')
376
                            );
377
                            $clientparams = array();
378
                        }
379
                    } elseif (
380
                        (isset($info['http_code'])) &&
381
                        ($info['http_code'] == 302)
382
                    ) {
383
                        // The OA4MP OIDC authz endpoint responded with 302
384
                        // (redirect) which indicates an OIDC error was
385
                        // detected. We need to check the response for an
386
                        // 'error' and simply redirect error to OIDC client.
387
                        $redirect_url = '';
388
                        if (isset($info['redirect_url'])) {
389
                            $redirect_url = $info['redirect_url'];
390
                            $clientparams['redirect_url'] = $redirect_url;
391
                            // CIL-407 - In case of two question marks '?'
392
                            // in redirect_url (caused by OIDC authz endpoint
393
                            // blindly appending "?error=..."), change all
394
                            // but the first '?' to '&'.
395
                            // https://stackoverflow.com/a/37150213
396
                            if (substr_count($redirect_url, '?') > 1) {
397
                                $arr = explode('?', $redirect_url, 2);
398
                                $arr[1] = str_replace('?', '&', $arr[1]);
399
                                $redirect_url = implode('?', $arr);
400
                            }
401
                        }
402
                        // Get components of redirect_url - need 'query'
403
                        $comps = parse_url($redirect_url);
404
                        if ($comps !== false) {
405
                            // Look for 'error' in query
406
                            $query = '';
407
                            if (isset($comps['query'])) {
408
                                $query = $comps['query'];
409
                                $query = html_entity_decode($query);
410
                            }
411
                            $queries = explode('&', $query);
412
                            $params = array();
413
                            foreach ($queries as $value) {
414
                                $x = explode('=', $value);
415
                                $params[$x[0]] = $x[1];
416
                            }
417
                            if (isset($params['error'])) {
418
                                // Got 'error' - simply return to OIDC client
419
                                Util::unsetAllUserSessionVars();
420
                                header("Location: $redirect_url");
421
                                exit; // No further processing necessary
422
                            } else { // Weird params - Should never get here!
423
                                Util::sendErrorAlert(
424
                                    'OA4MP OIDC 302 Error',
425
                                    'The OA4MP OIDC authz endpoint ' .
426
                                    'returned a 302 redirect (error) ' .
427
                                    'response, but there was no "error" ' .
428
                                    "query parameter.\n\n" .
429
                                    "redirect_url = $redirect_url\n\n" .
430
                                    'clientparams = ' .
431
                                    print_r($clientparams, true) .
432
                                    "\n"
433
                                );
434
                                $clientparams = array();
435
                            }
436
                        } else { // parse_url($redirect_url) gave error
437
                            Util::sendErrorAlert(
438
                                'parse_url(redirect_url) error',
439
                                'There was an error when attempting to ' .
440
                                'parse the redirect_url. This should never ' .
441
                                "happen.\n\n" .
442
                                "redirect_url = $redirect_url\n\n" .
443
                                'clientparams = ' . print_r($clientparams, true) .
444
                                "\n"
445
                            );
446
                            $clientparams = array();
447
                        }
448
                    } else {
449
                        // An HTTP return code other than 200 (success) or
450
                        // 302 (redirect) means that the OA4MP OIDC authz
451
                        // endpoint tried to handle an unrecoverable error,
452
                        // possibly by outputting HTML. If so, then we
453
                        // ignore it and output our own error message to the
454
                        // user.
455
                        Util::sendErrorAlert(
456
                            'OA4MP OIDC authz endpoint error',
457
                            'The OA4MP OIDC authorization endpoint returned ' .
458
                            'an HTTP response other than 200 or 302. ' .
459
                            ((strlen($output) > 0) ?
460
                                "\n\nReturned output =\n$output" : '') .
461
                            "\n\n" .
462
                            'curl_getinfo = ' . print_r($info, true) . "\n\n" .
463
                            'clientparams = ' . print_r($clientparams, true) .
464
                            "\n"
465
                        );
466
                        // CIL-423 Better end-user error output for errors.
467
                        // Scan output for ServletException message.
468
                        $errstr = '';
469
                        if (
470
                            preg_match(
471
                                '/javax.servlet.ServletException:\s?(.*)/',
472
                                $output,
473
                                $matches
474
                            )
475
                        ) {
476
                            $output = '';
477
                            $errstr = '
478
                            <div>
479
                            <p>Error Message: <b>' .
480
                            $matches[1] . '</b>.</p>
481
                            <ul>
482
                            <li>Did you <b>register</b> your OAuth2/OIDC client? If not, go
483
                            <b><a target="_blank" href="https://' .
484
                            Util::getHN()
485
                            . '/oauth2/register">here</a></b> to do so.</li>
486
                            <li>Did you receive confirmation that your OAuth2/OIDC client
487
                            was <b>approved</b>? If not, please wait up to 48 hours for an
488
                            approval email from CILogon administrators.</li>
489
                            <li>Did you configure your OAuth2/OIDC client with the
490
                            registered <b>client ID and secret</b>?</li>
491
                            </ul>
492
                            </div>';
493
                        }
494
                        Util::setSessionVar(
495
                            'client_error_msg',
496
                            'There was an unrecoverable error during the transaction. ' .
497
                            'CILogon system administrators have been notified.' .
498
                            ((strlen($errstr) > 0) ? $errstr : '') .
499
                            ((strlen($output) > 0) ?
500
                            '<br/><pre>' .
501
                            preg_replace('/\+/', ' ', $output) .
502
                            '</pre>' : '')
503
                        );
504
                        $clientparams = array();
505
                    }
506
                } else { // curl_getinfo() returned false - should not happen
507
                    Util::sendErrorAlert(
508
                        'curl_getinfo error',
509
                        'When attempting to talk to the OA4MP OIDC ' .
510
                        'authorization endpoint, curl_getinfo() returned ' .
511
                        "false. This should never happen.\n\n" .
512
                        'clientparams = ' . print_r($clientparams, true) . "\n"
513
                    );
514
                    $clientparams = array();
515
                }
516
            }
517
            curl_close($ch);
518
        } else { // curl_init() returned false - should not happen
519
            Util::sendErrorAlert(
520
                'curl_init error',
521
                'When attempting to talk to the OA4MP OIDC authorization ' .
522
                'endpoint, curl_init() returned false. This should never ' .
523
                "happen.\n\n" .
524
                'clientparams = ' . print_r($clientparams, true) . "\n"
525
            );
526
            $clientparams = array();
527
        }
528
529
    // If redirect_uri was not passed in, but one of the other required OIDC
530
    // parameters WAS passed in, then assume that this was an attempt by an
531
    // OIDC client to use the authz endpoint, and display an error message
532
    // that at least one parameter (redirect_uri) was missing from the
533
    // request. Note that since we don't have a redirect_uri, we cannot
534
    // return code flow back to the OIDC client.
535
    } elseif (
536
        (isset($clientparams['client_id'])) ||
537
        (isset($clientparams['scope'])) ||
538
        (isset($clientparams['response_type']))
539
    ) {
540
        $missing = 'redirect_uri' .
541
            ((isset($clientparams['client_id'])) ? '' : ', client_id') .
542
            ((isset($clientparams['scope'])) ? '' : ', scope') .
543
            ((isset($clientparams['response_type'])) ? '' : ', response_type');
544
        Util::sendErrorAlert(
545
            'CILogon OIDC authz endpoint error',
546
            'The CILogon OIDC authorization endpoint received a request ' .
547
            'from an OIDC client, but at least one of the required ' .
548
            'parameters (' . $missing . ') was missing. ' .
549
            "\n\n" .
550
            'clientparams = ' . print_r($clientparams, true) .
551
            "\n"
552
        );
553
        Util::setSessionVar(
554
            'client_error_msg',
555
            'It appears that an OpenID Connect client attempted to ' .
556
            'initiate a session with the CILogon Service, but at least ' .
557
            'one of the requried parameters (' . $missing . ') ' .
558
            'was missing. CILogon system administrators have been notified.'
559
        );
560
        $clientparams = array();
561
562
    // If none of the required OIDC authz endpoint parameters were passed
563
    // in, then this might be a later step in the authz process. So check
564
    // the session variable array 'clientparams' for the required
565
    // information.
566
    } else {
567
        $clientparams = json_decode(Util::getSessionVar('clientparams'), true);
568
    }
569
570
    // Now check to verify all variables have data
571
    if (
572
        (isset($clientparams['redirect_uri'])) &&
573
        (isset($clientparams['scope'])) &&
574
        (isset($clientparams['response_type'])) &&
575
        (isset($clientparams['client_id'])) &&
576
        (isset($clientparams['code'])) &&
577
        (isset($clientparams['client_name'])) &&
578
        (isset($clientparams['client_home_url'])) &&
579
        (isset($clientparams['client_callback_uri'])) &&
580
        (isset($clientparams['client_scopes'])) &&
581
        (isset($clientparams['redirect_url'])) &&
582
        (isset($clientparams['clientstatus'])) &&
583
        (!($clientparams['clientstatus'] & 1))
584
    ) { // STATUS_OK* are even
585
        $retval = true;
586
        Util::setSessionVar('clientparams', json_encode($clientparams));
587
    }
588
589
    return $retval;
590
}
591
592
/**
593
 * getErrorStatusText
594
 *
595
 * This function is called when the OA4MP OIDC authz endpoint responds with
596
 * a 200 (success), but the returned output was not a valid JSON token, or
597
 * there was no 'code' found in the returned JSON token. So attempt to scan
598
 * the returned $output for error messages that can be returned to the end
599
 * user and added to the alert email sent to admins.
600
 *
601
 * @param string $output The returned text from the OA4MP authz endpoint.
602
 * @param array $clientparams An array of the incoming OIDC client parameters.
603
 * @return string Error text to be displayed to the end user and added to
604
 *         the alert email sent to admins.
605
 */
606
function getErrorStatusText($output, $clientparams)
607
{
608
    $errstr = '';
609
    $errtxt = '';
610
611
    // CIL-575 Check the $output for a "status=..." line and convert
612
    // the error number to an error message defined in CILogon\Service\Util.
613
    if (preg_match('/status=(\d+)/', $output, $matches)) {
614
        $errnum = $matches[1];
615
        $errstr = array_search($errnum, DBService::$STATUS);
616
        $errtxt = @DBService::$STATUS_TEXT[$errstr];
617
    }
618
619
    // CIL-831 The OA4MP code returns a STATUS_INTERNAL_ERROR when there is
620
    // weirdness in the incoming client parameters. Look for some special
621
    // error conditions and set the error text appropriately.
622
    if (
623
        (strlen($errstr) == 0) ||
624
        ($errstr == 'STATUS_INTERNAL_ERROR') ||
625
        ($errstr == 'STATUS_CREATE_TRANSACTION_FAILED') ||
626
        ($errstr == 'STATUS_MISSING_PARAMETER_ERROR')
627
    ) {
628
        $params = [
629
            'redirect_uri',
630
            'scope',
631
            'response_type',
632
            'client_id',
633
            'prompt',
634
            'response_mode',
635
        ];
636
        foreach ($params as $value) {
637
            $$value = @$clientparams[$value];
638
        }
639
640
        if (empty($scope)) {
0 ignored issues
show
The variable $scope seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
641
            $errtxt = "Missing or empty scope parameter.";
642
        } elseif (empty($client_id)) {
0 ignored issues
show
The variable $client_id seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
643
            $errtxt = "Missing or empty client_id parameter.";
644
        } elseif (empty($response_type)) {
0 ignored issues
show
The variable $response_type seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
645
            $errtxt = "Missing or empty response_type parameter.";
646
        } elseif (preg_match('/[\+%"\']/', $scope)) {
647
            $errtxt = "Invalid characters found in scope parameter, may be URL encoded twice.";
648
        } elseif (preg_match('/[A-Z]/', $scope)) {
649
            $errtxt = "Upper case characters found in scope parameter.";
650
        } elseif ($response_type != 'code') {
651
            $errtxt = "Unsupported response_type parameter. Only code is supported.";
652
        } elseif ((!empty($prompt)) && ($prompt != 'login') && ($prompt != 'select_account')) {
0 ignored issues
show
The variable $prompt seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
653
            $errtxt = "Unsupported prompt parameter. Only login and select_account are supported.";
654
        } elseif (
655
            (!empty($response_mode)) &&
0 ignored issues
show
The variable $response_mode seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
656
            ($response_mode != 'query') &&
657
            ($response_mode != 'fragment') &&
658
            ($response_mode != 'form_post')
659
        ) {
660
            $errtxt = "Unsupported response_mode parameter.";
661
        }
662
663
        // CIL-697 The OA4MP code should eventually return an
664
        // "error_description=..." field that can give detailed error text to
665
        // replace the default text associated with STATUS_INTERNAL_ERROR.
666
        // CIL-909 Use the error_description field only if $errtxt is still
667
        // empty, OR if the $errstr was STATUS_CREATE_TRANSACTION_FAILED
668
        // (i.e., "Failed to initialize OIDC flow.").
669
        if (
670
            ((strlen($errtxt) == 0) ||
671
             ($errstr == 'STATUS_CREATE_TRANSACTION_FAILED')) &&
672
            (preg_match('/error_description=([^\r\n]+)/', $output, $matches))
673
        ) {
674
            $errtxt = urldecode($matches[1]);
675
        }
676
    }
677
678
    return $errtxt;
679
}
680