IssuerDBSAML.pm 70 KB
Newer Older
Yadd's avatar
Yadd committed
1
## @file
2
# SAML Issuer file
Yadd's avatar
Yadd committed
3
4

## @class
5
6
# SAML Issuer class
package Lemonldap::NG::Portal::IssuerDBSAML;
Yadd's avatar
Yadd committed
7
8

use strict;
9
use Lemonldap::NG::Common::Conf::SAML::Metadata;
Yadd's avatar
Yadd committed
10
use Lemonldap::NG::Portal::Simple;
Yadd's avatar
Yadd committed
11
12
use Lemonldap::NG::Portal::_SAML;
our @ISA = qw(Lemonldap::NG::Portal::_SAML);
Yadd's avatar
Yadd committed
13
14
15

our $VERSION = '0.01';

16
17
18
19
## @method void issuerDBInit()
# Load and check SAML configuration
# @return Lemonldap::NG::Portal error code
sub issuerDBInit {
Yadd's avatar
Yadd committed
20
    my $self = shift;
21
22

    # Load SAML service
23
    return PE_SAML_LOAD_SERVICE_ERROR unless $self->loadService();
24

25
26
    # Load SAML service providers
    return PE_SAML_LOAD_SP_ERROR unless $self->loadSPs();
27
28

    PE_OK;
Yadd's avatar
Yadd committed
29
30
}

31
## @apmethod int issuerForUnAuthUser()
32
33
# Check if there is an SAML authentication request
# Called only for unauthenticated users, check isPassive flag
Yadd's avatar
Yadd committed
34
# @return Lemonldap::NG::Portal error code
35
sub issuerForUnAuthUser {
Clément OUDOT's avatar
Clément OUDOT committed
36
    my $self   = shift;
37
38
    my $server = $self->{_lassoServer};

Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
39
    # Get configuration parameter
40
41
42
43
    my $saml_sso_soap_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceSOAP", 1 );
    my $saml_sso_soap_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceSOAP", 2 );
44
45
46
47
48
49
50
51
52
53
    my $saml_sso_get_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect", 1 );
    my $saml_sso_get_url_ret = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect", 2 );
    my $saml_sso_post_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceHTTPPost",
        1 );
    my $saml_sso_post_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceHTTPPost",
        2 );
54
55
56
57
    my $saml_sso_art_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact", 1 );
    my $saml_sso_art_url_ret = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact", 2 );
58
59
60
61
    my $saml_slo_soap_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceSOAP", 1 );
    my $saml_slo_soap_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceSOAP", 2 );
62
63
64
65
66
67
68
69
70
71
    my $saml_slo_get_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect", 1 );
    my $saml_slo_get_url_ret = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect", 2 );
    my $saml_slo_post_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceHTTPPost",
        1 );
    my $saml_slo_post_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceHTTPPost",
        2 );
72
73
    my $saml_ars_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorArtifactResolutionServiceArtifact");
Clément OUDOT's avatar
Clément OUDOT committed
74
75
    my $saml_slo_url_relay_soap =
      $self->{portal} . '/saml/relaySingleLogoutSOAP';
76
77
    my $saml_slo_url_relay_post =
      $self->{portal} . '/saml/relaySingleLogoutPOST';
78
79
    my $saml_slo_url_relay_term =
      $self->{portal} . '/saml/relaySingleLogoutTermination';
80
81
    my $saml_att_soap_url = $self->getMetaDataURL(
        "samlAttributeAuthorityDescriptorAttributeServiceSOAP", 1 );
82

Clément OUDOT's avatar
Clément OUDOT committed
83
84
85
86
87
    # Get HTTP request informations to know
    # if we are receving SAML request or response
    my $url            = $self->url();
    my $request_method = $self->request_method();
    my $content_type   = $self->content_type();
88

89
    # 1.1. SSO
90
    if ( $url =~
91
/^(\Q$saml_sso_soap_url\E|\Q$saml_sso_soap_url_ret\E|\Q$saml_sso_get_url\E|\Q$saml_sso_get_url_ret\E|\Q$saml_sso_post_url\E|\Q$saml_sso_post_url_ret\E|\Q$saml_sso_art_url\E|\Q$saml_sso_art_url_ret\E)$/io
92
93
      )
    {
94

Clément OUDOT's avatar
Clément OUDOT committed
95
        $self->lmLog( "URL $url detected as an SSO request URL", 'debug' );
96

Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
97
98
        # Check message
        my ( $request, $response, $method, $relaystate, $artifact ) =
Clément OUDOT's avatar
Clément OUDOT committed
99
          $self->checkMessage( $url, $request_method, $content_type );
100

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
101
102
        # Create Login object
        my $login = $self->createLogin($server);
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
103

Clément OUDOT's avatar
Clément OUDOT committed
104
105
106
107
108
109
        # Ignore signature verification
        $self->disableSignatureVerification($login);

        # Process the request
        if ($request) {

110
111
112
            # Process authentication request
            my $result;
            if ($artifact) {
113
                $result = $self->processArtResponseMsg( $login, $request );
114
115
116
117
118
119
120
121
            }
            else {
                $result = $self->processAuthnRequestMsg( $login, $request );
            }

            unless ($result) {
                $self->lmLog( "SSO: Fail to process authentication request",
                    'error' );
122
                return PE_SAML_SSO_ERROR;
123
124
125
126
            }

            $self->lmLog( "SSO: authentication request is valid", 'debug' );

Clément OUDOT's avatar
Clément OUDOT committed
127
128
129
130
131
132
133
134
135
136
137
            # Get SP entityID
            my $sp = $login->remote_providerID();

            $self->lmLog( "Found entityID $sp in SAML message", 'debug' );

            # SP conf key
            my $spConfKey = $self->{_spList}->{$sp}->{confKey};

            unless ($spConfKey) {
                $self->lmLog( "$sp do not match any SP in configuration",
                    'error' );
138
                return PE_SAML_UNKNOWN_ENTITY;
Clément OUDOT's avatar
Clément OUDOT committed
139
140
141
142
143
            }

            $self->lmLog( "$sp match $spConfKey SP in configuration", 'debug' );

            # Do we check signature?
144
145
146
147
148
149
150
            my $checkSSOMessageSignature =
              $self->{samlSPMetaDataOptions}->{$spConfKey}
              ->{samlSPMetaDataOptionsCheckSSOMessageSignature};

            if ($checkSSOMessageSignature) {
                unless ( $self->checkSignatureStatus($login) ) {
                    $self->lmLog( "Signature is not valid", 'error' );
151
                    return PE_SAML_SIGNATURE_ERROR;
152
153
154
155
156
157
158
159
160
                }
                else {
                    $self->lmLog( "Signature is valid", 'debug' );
                }
            }
            else {
                $self->lmLog( "Message signature will not be checked",
                    'debug' );
            }
Clément OUDOT's avatar
Clément OUDOT committed
161

162
163
164
165
            # Get SAML request
            my $saml_request = $login->request();
            unless ($saml_request) {
                $self->lmLog( "No SAML request found", 'error' );
166
                return PE_SAML_SSO_ERROR;
167
168
            }

169
            # Check Destination
170
            return PE_SAML_DESTINATION_ERROR
171
172
              unless ( $self->checkDestination( $saml_request, $url ) );

173
174
175
176
            # Check isPassive flag
            my $isPassive = $saml_request->IsPassive();

            if ($isPassive) {
177
178
179
180
                $self->lmLog(
"Found isPassive flag in SAML request, not compatible with unauthenticated user",
                    'error'
                );
181
                return PE_SAML_SSO_ERROR;
182
183
            }

184
185
186
187
188
189
            # Store SAML elements in memory in case of proxying
            $self->{_proxiedSamlRequest} = $saml_request;
            $self->{_proxiedRequest}     = $request;
            $self->{_proxiedMethod}      = $method;
            $self->{_proxiedRelayState}  = $relaystate;
            $self->{_proxiedArtifact}    = $artifact;
190

191
192
193
194
195
196
197
198
            # Create a back link on SP displayed on login page
            my $html = "<a href=\"" . $self->referer() . "\">";
            $html .=
              &Lemonldap::NG::Portal::_i18n::msg( PM_BACKTOSP,
                $ENV{HTTP_ACCEPT_LANGUAGE} );
            $html .= "</a>";
            $self->loginInfo($html);

199
            return PE_OK;
200
201
        }

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
        elsif ($response) {
            $self->lmLog(
                "Authentication responses are not managed by this module",
                'debug' );
            return PE_OK;
        }

        else {

            # No request or response
            # This should not happen
            $self->lmLog( "No request or response found", 'debug' );
            return PE_OK;
        }

217
218
    }

219
220
    # 1.2. SLO
    if ( $url =~
221
/^(\Q$saml_slo_soap_url\E|\Q$saml_slo_soap_url_ret\E|\Q$saml_slo_get_url\E|\Q$saml_slo_get_url_ret\E|\Q$saml_slo_post_url\E|\Q$saml_slo_post_url_ret\E)$/io
222
223
224
225
226
227
228
229
230
      )
    {

        $self->lmLog( "URL $url detected as an SLO URL", 'debug' );

        # Check SAML Message
        my ( $request, $response, $method, $relaystate, $artifact ) =
          $self->checkMessage( $url, $request_method, $content_type, "logout" );

Clément OUDOT's avatar
Clément OUDOT committed
231
232
233
234
235
236
        # Create Logout object
        my $logout = $self->createLogout($server);

        # Ignore signature verification
        $self->disableSignatureVerification($logout);

237
238
239
240
241
        if ($request) {

            # Process logout request
            unless ( $self->processLogoutRequestMsg( $logout, $request ) ) {
                $self->lmLog( "SLO: Fail to process logout request", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
242
243

                # Cannot send SLO error response if request not processed
244
                return PE_SAML_SLO_ERROR;
245
246
247
248
            }

            $self->lmLog( "SLO: Logout request is valid", 'debug' );

Clément OUDOT's avatar
   
Clément OUDOT committed
249
250
251
252
253
254
            # We accept only SOAP here
            unless ( $method eq $self->getHttpMethod('soap') ) {
                $self->lmLog( "Only SOAP requests allowed here", 'error' );
                $self->sendSLOErrorResponse( $logout, $method );
            }

Clément OUDOT's avatar
Clément OUDOT committed
255
256
257
258
259
260
261
262
263
264
265
            # Get SP entityID
            my $sp = $logout->remote_providerID();

            $self->lmLog( "Found entityID $sp in SAML message", 'debug' );

            # SP conf key
            my $spConfKey = $self->{_spList}->{$sp}->{confKey};

            unless ($spConfKey) {
                $self->lmLog( "$sp do not match any SP in configuration",
                    'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
266
                $self->sendSLOErrorResponse( $logout, $method );
Clément OUDOT's avatar
Clément OUDOT committed
267
268
269
270
271
            }

            $self->lmLog( "$sp match $spConfKey SP in configuration", 'debug' );

            # Do we check signature?
272
273
274
275
276
277
278
            my $checkSLOMessageSignature =
              $self->{samlSPMetaDataOptions}->{$spConfKey}
              ->{samlSPMetaDataOptionsCheckSLOMessageSignature};

            if ($checkSLOMessageSignature) {
                unless ( $self->checkSignatureStatus($logout) ) {
                    $self->lmLog( "Signature is not valid", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
279
                    $self->sendSLOErrorResponse( $logout, $method );
280
281
282
283
284
285
286
287
288
                }
                else {
                    $self->lmLog( "Signature is valid", 'debug' );
                }
            }
            else {
                $self->lmLog( "Message signature will not be checked",
                    'debug' );
            }
Clément OUDOT's avatar
Clément OUDOT committed
289

290
291
292
293
            # Get SAML request
            my $saml_request = $logout->request();
            unless ($saml_request) {
                $self->lmLog( "No SAML request found", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
294
                $self->sendSLOErrorResponse( $logout, $method );
295
296
            }

297
            # Check Destination
Clément OUDOT's avatar
   
Clément OUDOT committed
298
            $self->sendSLOErrorResponse( $logout, $method )
299
300
              unless ( $self->checkDestination( $saml_request, $url ) );

301
302
303
304
305
306
307
308
309
            # Get session index
            my $session_index;
            eval { $session_index = $logout->request()->SessionIndex; };

            # SLO requests without session index are not accepted
            unless ( defined $session_index ) {
                $self->lmLog(
                    "No session index in SLO request from $spConfKey SP",
                    'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
310
                $self->sendSLOErrorResponse( $logout, $method );
311
312
313
314
315
316
317
318
319
320
321
322
323
            }

            # Decrypt session index
            my $local_session_id = $self->{cipher}->decrypt($session_index);

            $self->lmLog(
"Get session id $local_session_id (decrypted from $session_index)",
                'debug'
            );

            # Open local session
            my $local_session = $self->getApacheSession( $local_session_id, 1 );

324
325
            unless ($local_session) {
                $self->lmLog( "No local session found", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
326
                $self->sendSLOErrorResponse( $logout, $method );
327
            }
328

329
330
331
332
333
334
335
            # Load Session and Identity if they exist
            my $session  = $local_session->{_lassoSessionDump};
            my $identity = $local_session->{_lassoIdentityDump};

            if ($session) {
                unless ( $self->setSessionFromDump( $logout, $session ) ) {
                    $self->lmLog( "Unable to load Lasso Session", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
336
                    $self->sendSLOErrorResponse( $logout, $method );
337
338
339
340
341
                }
                $self->lmLog( "Lasso Session loaded", 'debug' );
            }

            if ($identity) {
342
343
                unless ( $self->setIdentityFromDump( $logout, $identity ) ) {
                    $self->lmLog( "Unable to load Lasso Identity", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
344
                    $self->sendSLOErrorResponse( $logout, $method );
345
346
347
348
                }
                $self->lmLog( "Lasso Identity loaded", 'debug' );
            }

349
            # Close SAML sessions
350
            unless ( $self->deleteSAMLSecondarySessions($local_session_id) ) {
351
                $self->lmLog( "Fail to delete SAML sessions", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
352
                $self->sendSLOErrorResponse( $logout, $method );
353
354
            }

355
356
357
358
            # Close local session
            unless ( $self->_deleteSession($local_session) ) {
                $self->lmLog( "Fail to delete session $local_session_id",
                    'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
359
                $self->sendSLOErrorResponse( $logout, $method );
360
361
            }

362
363
364
            # Validate request if no previous error
            unless ( $self->validateLogoutRequest($logout) ) {
                $self->lmLog( "SLO request is not valid", 'error' );
Clément OUDOT's avatar
   
Clément OUDOT committed
365
                $self->sendSLOErrorResponse( $logout, $method );
366
367
            }

368
369
370
371
372
373
            # Try to send SLO request trough SOAP
            $self->resetProviderIdIndex($logout);
            while ( my $providerID = $self->getNextProviderId($logout) ) {

                # Send logout request
                my ( $rstatus, $rmethod, $rinfo ) =
374
375
                  $self->sendLogoutRequestToProvider( $logout, $providerID,
                    $self->getHttpMethod('soap'), 0 );
376

377
378
379
380
381
382
383
                if ($rstatus) {
                    $self->lmLog( "SOAP SLO successful on $providerID",
                        'debug' );
                }
                else {
                    $self->lmLog( "SOAP SLO error on $providerID", 'debug' );
                }
384
            }
385

386
387
388
389
390
391
            # Set RelayState
            if ($relaystate) {
                $logout->msg_relayState($relaystate);
                $self->lmLog( "Set $relaystate in RelayState", 'debug' );
            }

392
393
394
395
396
            # Signature
            my $signSLOMessage =
              $self->{samlSPMetaDataOptions}->{$spConfKey}
              ->{samlSPMetaDataOptionsSignSLOMessage};

Clément OUDOT's avatar
   
Clément OUDOT committed
397
398
399
400
401
402
403
404
405
406
407
            if ( $signSLOMessage == 0 ) {
                $self->lmLog( "SLO response will not be signed", 'debug' );
                $self->disableSignature($logout);
            }
            elsif ( $signSLOMessage == 1 ) {
                $self->lmLog( "SLO response will be signed", 'debug' );
                $self->forceSignature($logout);
            }
            else {
                $self->lmLog( "SLO response signature according to metadata",
                    'debug' );
408
409
            }

410
            # Send logout response
Clément OUDOT's avatar
   
Clément OUDOT committed
411
            $self->sendSLOErrorResponse( $logout, $method )
412
              unless (
Clément OUDOT's avatar
   
Clément OUDOT committed
413
                $self->sendLogoutResponseToServiceProvider( $logout, $method )
414
              );
415
416
417

        }

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
        elsif ($response) {

            # Process logout response
            my $result = $self->processLogoutResponseMsg( $logout, $response );

            unless ($result) {
                $self->lmLog( "Fail to process logout response", 'error' );
                return PE_IMG_NOK;
            }

            $self->lmLog( "Logout response is valid", 'debug' );

            # Check Destination
            return PE_IMG_NOK
              unless ( $self->checkDestination( $logout->response, $url ) );

            # Get SP entityID
            my $sp = $logout->remote_providerID();

            $self->lmLog( "Found entityID $sp in SAML message", 'debug' );

            # SP conf key
            my $spConfKey = $self->{_spList}->{$sp}->{confKey};

            unless ($spConfKey) {
                $self->lmLog( "$sp do not match any SP in configuration",
                    'error' );
                return PE_IMG_NOK;
            }

            $self->lmLog( "$sp match $spConfKey SP in configuration", 'debug' );

            # Do we check signature?
            my $checkSLOMessageSignature =
              $self->{samlSPMetaDataOptions}->{$spConfKey}
              ->{samlSPMetaDataOptionsCheckSLOMessageSignature};

            if ($checkSLOMessageSignature) {
                unless ( $self->checkSignatureStatus($logout) ) {
                    $self->lmLog( "Signature is not valid", 'error' );
                    return PE_IMG_NOK;
                }
                else {
                    $self->lmLog( "Signature is valid", 'debug' );
                }
            }
            else {
                $self->lmLog( "Message signature will not be checked",
                    'debug' );
            }

469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
            # Store success status for this SLO request
            my $sloStatusSessionInfos = $self->getSamlSession($relaystate);

            if ($sloStatusSessionInfos) {
                $sloStatusSessionInfos->{$spConfKey} = 1;
                untie %$sloStatusSessionInfos;
                $self->lmLog(
                    "Store SLO status for $spConfKey in session $relaystate",
                    'debug' );
            }
            else {
                $self->lmLog(
"Unable to store SLO status for $spConfKey in session $relaystate",
                    'warn'
                );
            }

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
486
487
488
489
490
491
492
493
494
495
496
497
498
            # SLO response is OK
            $self->lmLog( "Display OK status for SLO on $spConfKey", 'debug' );
            return PE_IMG_OK;
        }

        else {

            # No request or response
            # This should not happen
            $self->lmLog( "No request or response found", 'debug' );
            return PE_OK;
        }

499
500
    }

501
    # 1.3. SLO relay
502
503

    # 1.3.1 SOAP
504
    #      This URL is used by IMG html tag, and should returned PE_IMG_*
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
505
    if ( $url =~ /^(\Q$saml_slo_url_relay_soap\E)/io ) {
506

507
508
        $self->lmLog( "URL $url detected as a SOAP relay service URL",
            'debug' );
509

Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
510
        # Check if relay parameter is present (mandatory)
511
512
        my $relayID;
        unless ( $relayID = $self->param('relay') ) {
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
513
            $self->lmLog( "No relayID detected", 'error' );
Clément OUDOT's avatar
Clément OUDOT committed
514
            return PE_IMG_NOK;
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
515
516
517
        }

        # Retrieve the corresponding data from samlStorage
518
519
520
        my $relayInfos = $self->getSamlSession($relayID);
        unless ($relayInfos) {
            $self->lmLog( "Could not get relay session $relayID", 'error' );
Clément OUDOT's avatar
Clément OUDOT committed
521
            return PE_IMG_NOK;
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
522
523
        }

524
525
        $self->lmLog( "Found relay session $relayID", 'debug' );

Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
526
527
528
        # Rebuild the logout object
        my $logout;
        unless ( $logout = $self->createLogout($server) ) {
Clément OUDOT's avatar
Clément OUDOT committed
529
530
            $self->lmLog( "Could not rebuild logout object", 'error' );
            return PE_IMG_NOK;
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
531
532
533
        }

        # Load Session and Identity if they exist
534
535
536
537
538
        my $session    = $relayInfos->{_lassoSessionDump};
        my $identity   = $relayInfos->{_lassoIdentityDump};
        my $providerID = $relayInfos->{_providerID};
        my $relayState = $relayInfos->{_relayState};
        my $spConfKey  = $self->{_spList}->{$providerID}->{confKey};
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
539
540
541
542

        if ($session) {
            unless ( $self->setSessionFromDump( $logout, $session ) ) {
                $self->lmLog( "Unable to load Lasso Session", 'error' );
Clément OUDOT's avatar
Clément OUDOT committed
543
                return PE_IMG_NOK;
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
544
545
546
547
548
549
550
            }
            $self->lmLog( "Lasso Session loaded", 'debug' );
        }

        if ($identity) {
            unless ( $self->setIdentityFromDump( $logout, $identity ) ) {
                $self->lmLog( "Unable to load Lasso Identity", 'error' );
Clément OUDOT's avatar
Clément OUDOT committed
551
                return PE_IMG_NOK;
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
552
553
554
555
556
557
            }
            $self->lmLog( "Lasso Identity loaded", 'debug' );
        }

        # Send the logout request
        my ( $rstatus, $rmethod, $rinfo ) =
558
          $self->sendLogoutRequestToProvider( $logout, $providerID,
Clément OUDOT's avatar
Clément OUDOT committed
559
            Lasso::Constants::HTTP_METHOD_SOAP );
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
560
561
        unless ($rstatus) {
            $self->lmLog( "Fail to process SOAP logout request to $providerID",
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
562
                'error' );
563
            return PE_IMG_NOK;
Thomas Chemineau's avatar
SAML:    
Thomas Chemineau committed
564
565
        }

566
567
        # Store success status for this SLO request
        my $sloStatusSessionInfos = $self->getSamlSession($relayState);
568

569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
        if ($sloStatusSessionInfos) {
            $sloStatusSessionInfos->{$spConfKey} = 1;
            untie %$sloStatusSessionInfos;
            $self->lmLog(
                "Store SLO status for $spConfKey in session $relayState",
                'debug' );
        }
        else {
            $self->lmLog(
"Unable to store SLO status for $spConfKey in session $relayState",
                'warn'
            );
        }

        # SLO response is OK
        $self->lmLog( "Display OK status for SLO on $spConfKey", 'debug' );
        return PE_IMG_OK;
586
587
    }

588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
    # 1.3.2 POST
    #      This URL is used as iframe source, and autoPost a form
    #      Can return an error img
    if ( $url =~ /^(\Q$saml_slo_url_relay_post\E)/io ) {

        $self->lmLog( "URL $url detected as a POST relay service URL",
            'debug' );

        # Check if relay parameter is present (mandatory)
        my $relayID;
        unless ( $relayID = $self->param('relay') ) {
            $self->lmLog( "No relayID detected", 'error' );
            return PE_IMG_NOK;
        }

        # Retrieve the corresponding data from samlStorage
        my $relayInfos = $self->getSamlSession($relayID);
        unless ($relayInfos) {
            $self->lmLog( "Could not get relay session $relayID", 'error' );
            return PE_IMG_NOK;
        }

        $self->lmLog( "Found relay session $relayID", 'debug' );

        # Get data to build POST form
        $self->{postUrl} = $relayInfos->{url};
614
        $self->{postFields}->{'SAMLRequest'} = $relayInfos->{body};
615
        $self->{postFields}->{'RelayState'}  = $relayInfos->{relayState};
616
617
618
619
620
621
622
623

        $self->_subProcess(qw(autoPost));

        # If we are here, there was a problem with POST response
        $self->lmLog( "Logout response was not sent trough POST", 'error' );
        return PE_IMG_NOK;
    }

624
625
626
627
628
629
630
631
632
633
    # 1.3.3 Termination
    #      Used to send SLO response to SP issuing SLO request
    if ( $url =~ /^(\Q$saml_slo_url_relay_term\E)/io ) {

        $self->lmLog(
            "URL $url detected as a SLO Termination relay service URL",
            'debug' );

        # Check if relay parameter is present (mandatory)
        my $relayID;
634
        unless ( $relayID = $self->getHiddenFormValue('relay') ) {
635
            $self->lmLog( "No relayID detected", 'error' );
636
            return PE_SAML_SLO_ERROR;
637
638
639
640
641
642
        }

        # Retrieve the corresponding data from samlStorage
        my $relayInfos = $self->getSamlSession($relayID);
        unless ($relayInfos) {
            $self->lmLog( "Could not get relay session $relayID", 'error' );
643
            return PE_SAML_SESSION_ERROR;
644
645
646
647
        }

        $self->lmLog( "Found relay session $relayID", 'debug' );

648
649
650
651
652
653
654
        # Get data from relay session
        my $logout_dump  = $relayInfos->{_logout};
        my $session_dump = $relayInfos->{_session};
        my $method       = $relayInfos->{_method};

        unless ($logout_dump) {
            $self->lmLog( "Could not get logout dump", 'error' );
655
            return PE_SAML_SLO_ERROR;
656
657
658
659
660
661
662
        }

        # Rebuild Lasso::Logout object
        my $logout = $self->createLogout( $server, $logout_dump );

        unless ($logout) {
            $self->lmLog( "Could not build Lasso::Logout", 'error' );
663
            return PE_SAML_SLO_ERROR;
664
665
666
667
668
        }

        # Inject session
        unless ($session_dump) {
            $self->lmLog( "Could not get session dump", 'error' );
669
            return PE_SAML_SLO_ERROR;
670
671
672
673
        }

        unless ( $self->setSessionFromDump( $logout, $session_dump ) ) {
            $self->lmLog( "Could not set session from dump", 'error' );
674
            return PE_SAML_SLO_ERROR;
675
676
677
678
679
680
681
        }

        # Get Lasso::Session
        my $session = $logout->get_session();

        unless ($session) {
            $self->lmLog( "Could not get session from logout", 'error' );
682
            return PE_SAML_SLO_ERROR;
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
        }

        # Loop on assertions and remove them if SLO status is OK
        $self->resetProviderIdIndex($logout);

        while ( my $sp = $self->getNextProviderId($logout) ) {

            # Try to get SLO status from SLO session
            my $spConfKey = $self->{_spList}->{$sp}->{confKey};
            my $status    = $relayInfos->{$spConfKey};

            # Remove assertion if status is OK
            if ($status) {
                eval { $session->remove_assertion($sp); };

                if ($@) {
                    $self->lmLog( "Unable to remove assertion for $sp",
                        'warn' );
                }
                else {
                    $self->lmLog( "Assertion removed for $sp", 'debug' );
                }
            }
            else {
                $self->lmLog(
                    "SLO status was not ok for $sp, assertion not removed",
                    'debug' );
            }
        }

        # Reinject session
        unless ( $session->is_empty() ) {
            $self->setSessionFromDump( $logout, $session->dump );
        }

        # Send SLO response
        $self->sendLogoutResponseToServiceProvider( $logout, $method );

        # If we are here, SLO response was not sent
        $self->lmLog( "Fail to send SLO response", 'error' );
723
        return PE_SAML_SLO_ERROR;
724
725
    }

726
    # 1.4. Artifacts
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
727
    if ( $url =~ /^(\Q$saml_ars_url\E)$/io ) {
728

729
        $self->lmLog( "URL $url detected as an artifact resolution service URL",
730
731
            'debug' );

732
733
734
735
        # Artifact request are sent with SOAP trough POST
        my $art_request = $self->param('POSTDATA');
        my $art_response;

736
737
738
        # Create Login object
        my $login = $self->createLogin($server);

739
740
741
742
        # Process request message
        unless ( $self->processArtRequestMsg( $login, $art_request ) ) {
            $self->lmLog( "Unable to process artifact request message",
                'error' );
743
            $self->returnSOAPMessage();
744
745
746
        }

        # Check Destination
747
        $self->returnSOAPMessage()
748
749
          unless ( $self->checkDestination( $login->request, $url ) );

750
        # Create artifact response
751
        unless ( $art_response = $self->createArtifactResponse($login) ) {
752
            $self->lmLog( "Unable to create artifact response message",
753
                'error' );
754
            $self->returnSOAPMessage();
755
756
        }

757
        $self->{SOAPMessage} = $art_response;
758
759

        $self->lmLog( "Send SOAP Message: " . $self->{SOAPMessage}, 'debug' );
760
761
762
763

        # Return SOAP message
        $self->returnSOAPMessage();

764
765
        # If we are here, there was a problem with SOAP request
        $self->lmLog( "Artifact response was not sent trough SOAP", 'error' );
766
        $self->quit();
767

768
769
    }

Clément OUDOT's avatar
Clément OUDOT committed
770
    # 1.5 Attribute query
771
772
773
774
775
776
777
778
779
    if ( $url =~ /^(\Q$saml_att_soap_url\E)$/io ) {

        $self->lmLog( "URL $url detected as an attribute service URL",
            'debug' );

        # Attribute request are sent with SOAP trough POST
        my $att_request = $self->param('POSTDATA');
        my $att_response;

780
781
        # Process request
        my $query = $self->processAttributeRequest( $server, $att_request );
782
        unless ($query) {
783
            $self->lmLog( "Unable to process attribute request", 'error' );
784
785
786
            $self->returnSOAPMessage();
        }

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
787
788
789
790
791
792
793
794
795
796
        # Get SP entityID
        my $sp = $query->remote_providerID();

        $self->lmLog( "Found entityID $sp in SAML message", 'debug' );

        # SP conf key
        my $spConfKey = $self->{_spList}->{$sp}->{confKey};

        unless ($spConfKey) {
            $self->lmLog( "$sp do not match any SP in configuration", 'error' );
797
            return PE_SAML_UNKNOWN_ENTITY;
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
798
799
800
801
802
803
804
805
        }

        $self->lmLog( "$sp match $spConfKey SP in configuration", 'debug' );

        # Check Destination
        $self->returnSOAPMessage()
          unless ( $self->checkDestination( $query->request, $url ) );

806
807
808
809
810
        # Validate request
        unless ( $self->validateAttributeRequest($query) ) {
            $self->lmLog( "Attribute request not valid", 'error' );
            $self->returnSOAPMessage();
        }
811

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
812
        # Get NameID
813
        my $name_id = $query->nameIdentifier();
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
814
815
816
817
818
819
820

        unless ($name_id) {
            $self->lmLog( "Fail to get NameID from attribute request",
                'error' );
            $self->returnSOAPMessage();
        }

821
822
        my $user = $name_id->content();

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
        # Get sessionInfo for the given NameID
        my $sessionInfo;
        my $saml_sessions =
          $self->{samlStorage}
          ->searchOn( $self->{samlStorageOptions}, "_nameID", $name_id->dump );

        if ( my @saml_sessions_keys = keys %$saml_sessions ) {

            # Warning if more than one session found
            if ( $#saml_sessions_keys > 0 ) {
                $self->lmLog( "More than one SAML session found for user $user",
                    'warn' );
            }

            # Take the first session
            my $saml_session = shift @saml_sessions_keys;

            # Get session
            $self->lmLog( "Retrieve SAML session $saml_session for user $user",
                'debug' );

            my $samlSessionInfo = $self->getSamlSession($saml_session);

            # Get real session
            my $real_session = $samlSessionInfo->{_id};

            $self->lmLog( "Retrieve real session $real_session for user $user",
                'debug' );

            $sessionInfo = $self->getApacheSession( $real_session, 1 );

            unless ($sessionInfo) {
                $self->lmLog( "Cannot get session $real_session", 'error' );
                $self->returnSOAPMessage();
            }

        }
        else {
            $self->lmLog( "No SAML session found for user $user", 'error' );
            $self->returnSOAPMessage();
        }

865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
        # Get requested attributes
        my @requested_attributes;
        eval { @requested_attributes = $query->request()->Attribute(); };
        if ($@) {
            $self->checkLassoError($@);
            $self->returnSOAPMessage();
        }

        # Returned attributes
        my @returned_attributes;

        # Browse SP authorized attributes
        foreach (
            keys %{ $self->{samlSPMetaDataExportedAttributes}->{$spConfKey} } )
        {
            my $sp_attr = $_;

            # Extract fields from exportedAttr value
            my ( $mandatory, $name, $format, $friendly_name ) =
              split( /;/,
                $self->{samlSPMetaDataExportedAttributes}->{$spConfKey}
                  ->{$sp_attr} );

            foreach (@requested_attributes) {
                my $req_attr       = $_;
                my $rname          = $req_attr->Name();
                my $rformat        = $req_attr->NameFormat();
                my $rfriendly_name = $req_attr->FriendlyName();

                # Skip if name does not match
                next unless ( $rname =~ /^$name$/ );

897
898
899
900
901
                # Check format and friendly name
                next if ( $rformat and $rformat !~ /^$format$/ );
                next
                  if (  $rfriendly_name
                    and $rfriendly_name !~ /^$friendly_name$/ );
902
903
904
905
906
907
908
909
910

                $self->lmLog(
                    "SP $spConfKey is authorized to access attribute $rname",
                    'debug' );

                $self->lmLog(
                    "Attribute $rname is linked to $sp_attr session key",
                    'debug' );

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
911
912
913
914
915
916
917
918
919
                # Check if values are given
                my $rvalue =
                  $self->getAttributeValue( $rname, $rformat, $rfriendly_name,
                    [$req_attr] );

                $self->lmLog( "Some values are explicitely requested: $rvalue",
                    'debug' )
                  if defined $rvalue;

920
921
922
923
924
925
926
                # Get session value
                if ( $sessionInfo->{$sp_attr} ) {

                    my @values = split $self->{multiValuesSeparator},
                      $sessionInfo->{$sp_attr};
                    my @saml2values;

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
927
928
929
930
                    # SAML2 attribute
                    my $ret_attr =
                      $self->createAttribute( $rname, $rformat,
                        $rfriendly_name );
931

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
932
933
934
                    unless ($ret_attr) {
                        $self->lmLog( "Unable to create a new SAML attribute",
                            'error' );
935
936
                        $self->returnSOAPMessage();
                    }
937

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
938
                    foreach (@values) {
939

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
940
                        my $local_value = $_;
941

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
                        # Check if values were set in requested attribute
                        # In this case, only requested values can be returned
                        if (
                            $rvalue
                            and !map( /^$local_value$/,
                                split( $self->{multiValuesSeparator}, $rvalue )
                            )
                          )
                        {
                            $self->lmLog(
"$local_value value is not in requested values, it will not be sent",
                                'warn'
                            );
                            next;
                        }
957

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
958
959
960
                        # SAML2 attribute value
                        my $saml2value =
                          $self->createAttributeValue($local_value);
961

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
962
963
964
965
966
967
                        unless ($saml2value) {
                            $self->lmLog(
                                "Unable to create a new SAML attribute value",
                                'error' );
                            $self->returnSOAPMessage();
                        }
968
969
970

                        push @saml2values, $saml2value;

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
971
972
                        $self->lmLog(
                            "Push $local_value in SAML attribute $name",
973
974
975
976
                            'debug' );

                    }

Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
977
                    $ret_attr->AttributeValue(@saml2values);
978
979

                    # Push attribute in attribute list
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
980
                    push @returned_attributes, $ret_attr;
981
982
983
984
985
986
987
988
989
990
991

                }
                else {
                    $self->lmLog( "No session value for $sp_attr", 'debug' );
                }

            }

        }

        # Create attribute statement
992
        if ( scalar @returned_attributes ) {
Yadd's avatar
Yadd committed
993
            my $attribute_statement;
994

995
996
997
            eval {
                $attribute_statement = Lasso::Saml2AttributeStatement->new();
            };
Yadd's avatar
Yadd committed
998
999
1000
1001
            if ($@) {
                $self->checkLassoError($@);
                $self->returnSOAPMessage();
            }
1002

Yadd's avatar
Yadd committed
1003
1004
            # Register attributes in attribute statement
            $attribute_statement->Attribute(@returned_attributes);
1005

Yadd's avatar
Yadd committed
1006
1007
            # Create assetion
            my $assertion;
1008

Yadd's avatar
Yadd committed
1009
1010
1011
1012
1013
            eval { $assertion = Lasso::Saml2Assertion->new(); };
            if ($@) {
                $self->checkLassoError($@);
                $self->returnSOAPMessage();
            }
1014

Yadd's avatar
Yadd committed
1015
1016
1017
            # Add attribute statement in response assertion
            my @attributes_statement = ($attribute_statement);
            $assertion->AttributeStatement(@attributes_statement);
1018

Yadd's avatar
Yadd committed
1019
1020
            # Set response assertion
            $query->response->Assertion( ($assertion) );
1021
        }
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031

        # Build response
        $att_response = $self->buildAttributeResponse($query);

        unless ($att_response) {
            $self->lmLog( "Unable to build attribute response", 'error' );
            $self->returnSOAPMessage();
        }

        $self->{SOAPMessage} = $att_response;
Clément OUDOT's avatar
Clément OUDOT committed
1032

1033
1034
1035
1036
1037
1038
1039
1040
1041
        # Return SOAP message
        $self->returnSOAPMessage();

        # If we are here, there was a problem with SOAP request
        $self->lmLog( "Attribute response was not sent trough SOAP", 'error' );
        $self->quit();

    }

Yadd's avatar
Yadd committed
1042
1043
1044
    PE_OK;
}

1045
## @apmethod int issuerForAuthUser()
Yadd's avatar
Yadd committed
1046
# Check if there is an SAML authentication request for an authenticated user
1047
# Build assertions and redirect user
Yadd's avatar
Yadd committed
1048
# @return Lemonldap::NG::Portal error code
1049
sub issuerForAuthUser {
Clément OUDOT's avatar
   
Clément OUDOT committed
1050
    my $self   = shift;
1051
    my $server = $self->{_lassoServer};
1052
    my $login;
Clément OUDOT's avatar
SAML:    
Clément OUDOT committed
1053
1054
    my $protocolProfile;
    my $artifact_method;
Clément OUDOT's avatar
Clément OUDOT committed
1055
    my $authn_context;
1056

1057
1058
1059
    # Session ID
    my $session_id = $self->{sessionInfo}->{_session_id} || $self->{id};

1060
1061
1062
    # Session creation timestamp
    my $time = $self->{sessionInfo}->{_utime} || time();

1063
1064
1065
1066
1067
    # Get configuration parameter
    my $saml_sso_soap_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceSOAP", 1 );
    my $saml_sso_soap_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceSOAP", 2 );
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
    my $saml_sso_get_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect", 1 );
    my $saml_sso_get_url_ret = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect", 2 );
    my $saml_sso_post_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceHTTPPost",
        1 );
    my $saml_sso_post_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleSignOnServiceHTTPPost",
        2 );
1078
1079
1080
1081
    my $saml_sso_art_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact", 1 );
    my $saml_sso_art_url_ret = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact", 2 );
1082
1083
1084
1085
    my $saml_slo_soap_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceSOAP", 1 );
    my $saml_slo_soap_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceSOAP", 2 );
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
    my $saml_slo_get_url = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect", 1 );
    my $saml_slo_get_url_ret = $self->getMetaDataURL(
        "samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect", 2 );
    my $saml_slo_post_url =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceHTTPPost",
        1 );
    my $saml_slo_post_url_ret =
      $self->getMetaDataURL( "samlIDPSSODescriptorSingleLogoutServiceHTTPPost",
        2 );
1096
1097
1098
1099
1100
1101
1102

    # Get HTTP request informations to know
    # if we are receving SAML request or response
    my $url            = $self->url();
    my $request_method = $self->request_method();
    my $content_type   = $self->content_type();

1103
    # 1.1. SSO (SSO URL or Proxy Mode)
1104
    if ( $url =~
1105
/^(\Q$saml_sso_soap_url\E|\Q$saml_sso_soap_url_ret\E|\Q$saml_sso_get_url\E|\Q$saml_sso_get_url_ret\E|\Q$saml_sso_post_url\E|\Q$saml_sso_post_url_ret\E|\Q$saml_sso_art_url\E|\Q$saml_sso_art_url_ret\E)$/io
1106
        or $self->{_proxiedRequest} )
1107
    {
1108
1109
1110
1111

        $self->lmLog( "URL $url detected as an SSO request URL", 'debug' );

        # Check message
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
        my ( $request, $response, $method, $relaystate, $artifact );

        if ( $self->{_proxiedRequest} ) {
            $request    = $self->{_proxiedRequest};
            $method     = $self->{_proxiedMethod};
            $relaystate = $self->{_proxiedRelayState};
            $artifact   = $self->{_proxiedArtifact};
        }
        else {
            ( $request, $response, $method, $relaystate, $artifact ) =
1122
          $self->checkMessage( $url, $request_method, $content_type );
1123
        }
1124

Clément OUDOT's avatar
Clément OUDOT committed
1125
1126
1127
1128
1129
1130
        # Create Login object
        my $login = $self->createLogin($server);

        # Ignore signature verification
        $self->disableSignatureVerification($login);

1131
        # Process the request
1132
        if ($request) {
1133

1134
1135
1136
1137
1138
1139
1140
            # Load Session and Identity if they exist
            my $session  = $self->{sessionInfo}->{_lassoSessionDump};
            my $identity = $self->{sessionInfo}->{_lassoIdentityDump};

            if ($session) {
                unless ( $self->setSessionFromDump( $login, $session ) ) {
                    $self->lmLog( "Unable to load Lasso Session", 'error' );
1141
                    return PE_SAML_SSO_ERROR;
1142
1143
1144
1145
1146
1147
1148
                }
                $self->lmLog( "Lasso Session loaded", 'debug' );
            }

            if ($identity) {
                unless ( $self->setIdentityFromDump( $login, $identity ) ) {
                    $self->lmLog( "Unable to load Lasso Identity", 'error' );
1149
                    return PE_SAML_SSO_ERROR;
1150
1151
1152
1153
                }
                $self->lmLog( "Lasso Identity loaded", 'debug' );
            }

1154
1155
1156
            # Process authentication request
            my $result;
            if ($artifact) {
1157
                $result = $self->processArtResponseMsg( $login, $request );
1158
1159
1160
1161
1162
1163
1164
1165
            }
            else {
                $result = $self->processAuthnRequestMsg( $login, $request );
            }

            unless ($result) {
                $self->lmLog( "SSO: Fail to process authentication request",
                    'error' );
1166
                return PE_SAML_SSO_ERROR;
1167
1168
            }

Clément OUDOT's avatar
Clément OUDOT committed
1169
1170
            # Get SP entityID
            my $sp = $login->remote_providerID();
1171

Clément OUDOT's avatar
Clément OUDOT committed
1172
            $self->lmLog( "Found entityID $sp in SAML message", 'debug' );
1173

Clément OUDOT's avatar
Clément OUDOT committed
1174
1175
1176
1177
1178
1179
            # SP conf key
            my $spConfKey = $self->{_spList}->{$sp}->{confKey};

            unless ($spConfKey) {
                $self->lmLog( "$sp do not match any SP in configuration",
                    'error' );
1180
                return PE_SAML_UNKNOWN_ENTITY;
1181
1182
            }

Clément OUDOT's avatar
Clément OUDOT committed
1183
            $self->lmLog( "$sp match $spConfKey SP in configuration", 'debug' );
1184

Clément OUDOT's avatar
Clément OUDOT committed
1185
            # Do we check signature?
1186
1187
1188
1189
1190
1191
1192
            my $checkSSOMessageSignature =
              $self->{samlSPMetaDataOptions}->{$spConfKey}
              ->{samlSPMetaDataOptionsCheckSSOMessageSignature};

            if ($checkSSOMessageSignature) {
                unless ( $self->checkSignatureStatus($login) ) {
                    $self->lmLog( "Signature is not valid", 'error' );
1193
                    return PE_SAML_SIGNATURE_ERROR;
1194
1195
1196
1197
1198
1199
1200
1201
1202
                }
                else {
                    $self->lmLog( "Signature is valid", 'debug' );
                }
            }
            else {
                $self->lmLog( "Message signature will not be checked",
                    'debug' );
            }
1203

1204
1205
1206
1207
            # Validate request
            unless ( $self->validateRequestMsg( $login, 1, 1 ) ) {
                $self->lmLog( "Unable to validate SSO request message",
                    'error' );
1208
                return PE_SAML_SSO_ERROR;
Clément OUDOT's avatar
   
Clément OUDOT committed
1209
            }
1210

1211
1212
            $self->lmLog( "SSO: authentication request is valid", 'debug' );

1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
            # Get ForceAuthn flag
            my $force_authn;

            eval { $force_authn = $login->request()->ForceAuthn(); };
            if ($@) {
                $self->lmLog( "Unable to get ForceAuthn flag, set it to false",
                    'warn' );
                $force_authn = 0;
            }

            $self->lmLog( "Found ForceAuthn flag with value $force_authn",
                'debug' );

            # Get ForceAuthn sessions for this session_id
            my $forceAuthn_sessions =
              $self->{samlStorage}->searchOn( $self->{samlStorageOptions},
                "_idForceAuthn", $session_id );

            my $forceAuthn_session;
1232
            my $forceAuthnSessionInfo;
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252

            if ( my @forceAuthn_sessions_keys = keys %$forceAuthn_sessions ) {

                # Warning if more than one session found
                if ( $#forceAuthn_sessions_keys > 0 ) {
                    $self->lmLog(
"More than one ForceAuthn session found for session $session_id",
                        'warn'
                    );
                }

                # Take the first session
                $forceAuthn_session = shift @forceAuthn_sessions_keys;

                # Get session
                $self->lmLog(
"Retrieve ForceAuthn session $forceAuthn_session for session $session_id",
                    'debug'
                );

1253
                $forceAuthnSessionInfo =
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
                  $self->getSamlSession($forceAuthn_session);

                # Check forceAuthn flag for current SP
                if ( $forceAuthnSessionInfo->{$spConfKey} ) {

                    $self->lmLog(
"User was already forced to reauthenticate for SP $spConfKey",
                        'debug'
                    );
                    $force_authn = 1;
                }