os/security/authorisation/userpromptservice/server/source/upsserver/authoriser.cpp
First public contribution.
2 * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
15 * Implements CAuthoriser. See class and function definitions for
25 #include "upsserver.h"
26 #include "policycache.h"
27 #include "authoriser.h"
28 #include <ups/upsdbw.h>
30 #include <ups/dialogcreator.h>
31 #include <ups/policyevaluator.h>
33 namespace UserPromptService
36 CAuthoriser* CAuthoriser::NewL(RPolicyCacheCountedHandle &aPolicyCache,
37 CUpsSession* aSession, CUpsSubsession* aSubsession, TBool aServerCheckOk,
38 TThreadId &aClientTid, TProcessId &aClientPId,
39 const RMessage2& aMessage, const TServiceId& aServiceId,
40 RBuf &aDestination, RBuf8 &aOpaqueData)
42 Factory function allocates a new, initialized instance of CAuthoriser,
43 registered for the supplied session and subsession.
45 If construction is successful, the caller should pass ownership to
46 the SCS framework (TransferToScsFrameworkL), and pop the
47 CAuthoriser from the cleanupstack. From this point onwards the
48 request should be completed by the CAuthoriser/CAsyncRequst code,
49 so the caller must not return or leave with any code except
50 KErrScsSetupAsync. This is why the StartProcessingRequest function
51 is defined as non-leaving.
53 @param aPolicyCache The server policy cache manager.
54 @param aSession Session on which this request was launched.
55 @param aSubsession Subsession on which this request was launched.
56 @param aServerCheckOk Did the system server checks pass?
57 @param aClientThread Handle to the user client thread.
58 @param aMessage Standard server-side handle to message.
59 @param aDestination We take ownership of the RBuf and close the callers handle
60 @param aOpaqueData We take ownership of the RBuf and close the callers handle
61 @return New, initialized instance of CAuthoriser, which
62 is owned by the server's collection of outstanding
66 CAuthoriser* self = new(ELeave) CAuthoriser(aPolicyCache, aSession, aSubsession, aMessage);
67 CleanupStack::PushL(self);
68 self->ConstructL(aServerCheckOk, aClientTid, aClientPId, aServiceId, aDestination, aOpaqueData);
69 CleanupStack::Pop(self);
73 CAuthoriser::CAuthoriser(RPolicyCacheCountedHandle &aPolicyCache,
74 CUpsSession* aSession, CUpsSubsession* aSubsession,
75 const RMessage2& aMessage)
77 This private constructor initializes the superclass and prevents direct instantiation.
79 @param aPolicyCache The server policy cache manager.
80 @param aSession Session on which this request was launched.
81 @param aSubsession Subsession on which this request was launched.
82 @param aMessage Standard server-side handle to message.
83 @param aDestination HBufC description, takes ownership.
84 @param aOpaqueData 0 or HBufC opaqueData, takes ownership.
86 : CAsyncRequest(aSession, aSubsession, aMessage),
87 iPolicyCache(aPolicyCache)
92 void CAuthoriser::ConstructL(TBool aServerCheckOk, TThreadId &aClientTid, TProcessId &aClientPid, const TServiceId& aServiceId, RBuf &aDestination, RBuf8 &aOpaqueData)
94 Second-phase constructor allocates the timer which is used to generate
95 a delay, and adds this object to the server's collection of outstanding
99 iState = ECheckPolicy;
101 RProcess clientProcess;
102 TInt r = clientProcess.Open(aClientPid);
105 User::Leave(KErrUpsBadClientProcessId);
107 CleanupClosePushL(clientProcess);
109 // Create a CPromptReqest for the policy lookup
110 iPromptRequest = CPromptRequest::NewL(clientProcess.SecureId(), // Client details
111 clientProcess.VendorId(),
114 iMessagePtr2.SecureId(), // Server SID
115 aServiceId, // Server service ID
116 aDestination, // Request detail, takes ownership
117 aOpaqueData, // takes ownership
120 CleanupStack::PopAndDestroy(&clientProcess);
123 void CAuthoriser::ProcessEventL(TAuthoriserEvent aEvent)
125 // DEBUG_PRINTF4(_L8("0x%x CAuthoriser::ProcessEventL state %d, event %d\n"), this, iState, aEvent);
126 _LIT(KServerPanicState, "UPS-ProcessEventL");
130 CheckPolicyStateL(aEvent);
132 case ECreatingFingerprints:
133 CreatingFingerprintsStateL(aEvent);
136 CheckDatabaseStateL(aEvent);
138 case EPreparingDialog:
139 PreparingDialogStateL(aEvent);
141 case EExecutingDialog:
142 ExecutingDialogStateL(aEvent);
147 // Should not get here
148 User::Panic(KServerPanicState, iState);
153 void CAuthoriser::CheckPolicyStateL(TAuthoriserEvent aEvent)
157 case EInternalRequestComplete:
158 // Caused by the ups sub session code calling Wakeup
159 // to start the request processing. We are now within the
160 // active scheduler error handling framework.
162 // This call will lookup the policy and move to the next
168 case EClearedToDisplayDialog:
174 // This WILL happen if am authorise request is made and immediately cancelled (before CAuthoriser::Runl has
175 // the chance to run). We should complete our "fake" internal request so we do not block
176 // when we return to the DoCancel/CActive::Cancel which caused this event.
184 void CAuthoriser::CreatingFingerprintsStateL(TAuthoriserEvent aEvent)
188 case EInternalRequestComplete:
189 // This will move to the next state and lookup the fingerprints, or leave.
190 LookupFingerprintsL();
194 case EClearedToDisplayDialog:
200 // Cancel the request to create the fingerprints
201 iPolicyEvaluator->Imp().Cancel();
208 void CAuthoriser::CheckDatabaseStateL(TAuthoriserEvent aEvent)
212 case EInternalRequestComplete:
213 // Normally the create fingerprints state code will call
214 // LookupFingerprintsL to move to this state and do the
215 // lookup, but if we need to re-lookup the fingerprints a
216 // "fake" request will be issued and this code will be
218 LookupFingerprintsL();
220 case EClearedToDisplayDialog:
221 // Change state and ask the dialog creator to start creating
222 // the dialog, or leave.
226 // Complete the fake request which was issued to try and
227 // get us to redo the fingerprint lookup.
235 void CAuthoriser::PreparingDialogStateL(TAuthoriserEvent aEvent)
239 case EInternalRequestComplete:
240 // Created dialog, now change state to execute and display
246 case EClearedToDisplayDialog:
252 // Cancel the request to create the dialog
253 iDialogCreator->Imp().Cancel();
260 void CAuthoriser::ExecutingDialogStateL(TAuthoriserEvent aEvent)
264 case EInternalRequestComplete:
265 // Dialog complete, process the result and complete the
267 ProcessDialogResultL();
271 case EClearedToDisplayDialog:
277 // Cancel the request to execute the dialog
278 iDialogCreator->Imp().Cancel();
285 void CAuthoriser::Wakeup()
287 Raise and complete a request against self. Will cause RunL to
288 be run within the active scheduler.
291 iStatus = KRequestPending;
296 void CAuthoriser::CompleteSelf()
298 _LIT(KServerPanicState, "UPS-CompleteSelf");
306 case ECreatingFingerprints:
307 case EPreparingDialog:
308 case EExecutingDialog:
309 User::Panic(KServerPanicState, iState);
310 /*lint -unreachable */
311 return; // Not legal in these states
315 if(iStatus == KRequestPending)
317 TRequestStatus *status = &iStatus;
318 User::RequestComplete(status, KErrNone);
324 void CAuthoriser::ClearedToDisplayL()
326 The UPS server GateKeeper has granted us permission to proceed
327 with dialog generation and display.
330 ProcessEventL(EClearedToDisplayDialog);
338 CAuthoriser::~CAuthoriser()
340 Close the timer which was initialized in ConstructL.
343 iDialogFingerprint = 0; // MUST not delete, owned by CDialogCreator
345 delete iDialogCreator;
348 iDialogCreatorParams = 0; // MUST not delete, owned by evaluator
350 iClientEntity = 0; // MUST not delete, owned by evaluator
352 iFingerprints.ResetAndDestroy();
354 delete iPolicyEvaluator;
355 iPolicyEvaluator = 0;
357 iPolicyCache.Release();
358 iPolicy = 0; // MUST not delete, ownded by policy cache
360 delete iPromptRequest;
364 void CAuthoriser::RunL()
366 // Wake up the next pending request now (if any), to make sure it
367 // will be processed ahead of any incoming requests.
368 UpsServer()->WakeupNextPendingL();
370 // Reset our priority to standard
371 if(Priority() != CActive::EPriorityStandard)
373 SetPriority(CActive::EPriorityStandard);
375 User::LeaveIfError(iStatus.Int());
377 ProcessEventL(EInternalRequestComplete);
381 void CAuthoriser::DoCleanup()
385 // Have previously called ForcePromptL on a database record and it told us to
386 // prompt, so have previously marked the record as disputed.
387 TRAP_IGNORE(UpsServer()->UnDisputeRecordIdL(iPromptForcedRecordId));
388 iPromptForced = EFalse;
391 // Cancel current internal operation
394 // Let the server know we are done
395 UpsServer()->AuthoriserDone(this);
397 // The framework will complete the client request and schedule us
404 void CAuthoriser::DoCancel()
406 Cancel the current operation. This does not complete the client
407 request or mark this object for deletion.
411 // Wake up the next pending request now (if any), to make sure it
412 // will be processed ahead of any incoming requests.
413 UpsServer()->WakeupNextPendingL();
414 ProcessEventL(ECancel);
418 void CAuthoriser::LookupPolicyL()
420 // This returns a ptr into the cache, so we have to be careful not to use it after the cache is deleted.
421 // This is why we wrap the cache in a RPolicyCacheCountedHandle.
422 iPolicy = iPolicyCache->MatchL(*iPromptRequest);
426 if(iPolicy->PromptRequired())
428 // Policy contains both yes and no responses...
429 // Start creating the fingerprints
430 CreateFingerprintsL();
434 // Return result to system server
435 TUpsDecisionPckgBuf decisionBuf;
436 decisionBuf() = MapCPolicyTOptions2TUpsDecision(iPolicy->Options());
437 iMessagePtr2.WriteL(0, decisionBuf);
439 CompleteAndMarkForDeletion(KErrNone);
443 void CAuthoriser::CreateFingerprintsL()
445 Load Policy Evaluator and tell it to start generating the fingerprints
448 iState = ECreatingFingerprints;
450 iPolicyEvaluator = UpsServer()->iPluginManager->CreateEvaluatorL(iPolicy->PolicyEvaluator());
452 iPolicyEvaluator->Imp().GenerateFingerprints(*iPromptRequest, *iPolicy,
453 iFingerprints, iClientEntity, iDialogCreatorParams, iStatus);
458 _LIT8(KDefaultFingerprint,"*");
459 //_LIT(KServerPanicDb, "UPS-BadDB");
460 static const char * const KCorruptDb = "UPS-BadDB error=%d line=%d\n";
462 void CAuthoriser::LookupFingerprintsL()
465 iState = ECheckDatabase;
467 TPtrC8 clientEntityName(KNullDesC8());
470 clientEntityName.Set(iClientEntity->Name());
473 CDecisionFilter *filter = CDecisionFilter::NewLC(iPromptRequest->ClientSid(),
474 iPolicy->PolicyEvaluator(),
475 iPromptRequest->ServiceId(),
476 iPromptRequest->ServerSid(),
477 KDefaultFingerprint(),
479 iPolicy->MajorVersion());
481 // Lookup the fingerprints in the DB and return the first match
483 CDecisionRecord *record = 0;
484 TInt fingerprintCount = iFingerprints.Count();
485 for(TInt i=0; i<fingerprintCount; ++i)
487 filter->SetFingerprintL(iFingerprints[i]->Fingerprint(), EEqual);
489 TRAPD(err, record = UpsServer()->iDbHandle->GetDecisionL(*filter));
504 CleanupStack::PushL(record);
505 // Read current eval info value from record.
506 iDialogEvaluatorInfo = record->iEvaluatorInfo;
508 if(UpsServer()->IsRecordIdDisputed(record->iRecordId))
510 // Matching record is under dispute so a dialog must be in progress.
511 // Queue for later re-evaluation.
512 UpsServer()->GateKeeperL(this);
516 if(iPromptForced && (record->iRecordId != iPromptForcedRecordId))
518 // Have previously called ForcePromptL on a database record and it told us to
519 // prompt, BUT we have now matched a different record ID! Presumably
520 // someone else overwrote or deleted our record....
522 // We need to undispute the old record id and continue as if ForcePromptL had
523 // never been called/returned yes.
524 iPromptForced = EFalse;
525 UpsServer()->UnDisputeRecordIdL(iPromptForcedRecordId);
528 // Call ForcePrompt to see if we need to prompt anyway
529 TUint newDialogEvaluatorInfo = iDialogEvaluatorInfo;
530 TBool forcePrompt = iPolicyEvaluator->Imp().ForcePromptL(*record, newDialogEvaluatorInfo);
533 iPromptForced = ETrue;
534 iPromptForcedRecordId = record->iRecordId;
535 UpsServer()->DisputeRecordIdL(iPromptForcedRecordId);
538 if(newDialogEvaluatorInfo != iDialogEvaluatorInfo)
540 // Eavluator info changes, update our member variable copy
541 // so it is used in the dialog
542 iDialogEvaluatorInfo = newDialogEvaluatorInfo;
543 // If we ARE displaying a prompt, then when it completes,
544 // we will delete the existing record. If the user choice
545 // requires a new record we will use the evaluator value
546 // from our member variable
549 // Not forcing a prompt, so update the DB now.
550 record->iEvaluatorInfo = newDialogEvaluatorInfo;
551 // If the update fails, we carry on anyway...
552 TRAPD(err, UpsServer()->iDbHandle->UpdateDecisionL(*filter, *record));
562 // Return the result found in the DB to the system server
563 TUpsDecisionPckgBuf decisionBuf;
564 decisionBuf() = MapCPolicyTOptions2TUpsDecision((record->iResult) ? (CPolicy::EAlways) : (CPolicy::ENever));
565 iMessagePtr2.WriteL(0, decisionBuf);
567 CleanupStack::PopAndDestroy(record);
568 CleanupStack::PopAndDestroy(filter);
569 CompleteAndMarkForDeletion(KErrNone);
573 // Prompt is being forced, so fall through
574 CleanupStack::PopAndDestroy(record);
577 // Record not found, or prompt forced
578 CleanupStack::PopAndDestroy(filter);
580 // Queue for clearence to display a dialog
581 UpsServer()->GateKeeperL(this);
584 void CAuthoriser::PrepareDialogL()
586 iState = EPreparingDialog;
588 iDialogCreator = UpsServer()->iPluginManager->CreateDialogCreatorL(iPolicy->DialogCreator());
590 iDialogCreator->Imp().PrepareDialog(*iPromptRequest, *iPolicy, iFingerprints,
591 iClientEntity, iDialogCreatorParams, iStatus);
598 void CAuthoriser::ExecuteDialogL()
600 iState = EExecutingDialog;
601 iDialogCreator->Imp().DisplayDialog(iDialogSelectedOption, iDialogFingerprint, iDialogEvaluatorInfo, iStatus);
605 void CAuthoriser::ProcessDialogResultL()
609 // Pormpt was forced therefore we must have matched an existing record in the
611 // Delete the old decision.
612 CDecisionFilter *filter = CDecisionFilter::NewLC();
613 filter->SetRecordId(iPromptForcedRecordId, EEqual);
614 TRAPD(err, UpsServer()->iDbHandle->RemoveDecisionsL(*filter));
615 CleanupStack::PopAndDestroy(filter);
621 // No longer disputing the record ID because we have a new decision
622 iPromptForced = EFalse;
623 UpsServer()->UnDisputeRecordIdL(iPromptForcedRecordId);
626 TUpsDecisionPckgBuf decisionBuf;
627 // Mask out any illegal responses - ie buttons which we did not
628 // ask to be displayed...
629 iDialogSelectedOption = CPolicy::TOptions(TUint(iDialogSelectedOption) & TUint(iPolicy->Options()));
631 decisionBuf() = MapCPolicyTOptions2TUpsDecision(iDialogSelectedOption);
633 if((iDialogSelectedOption & CPolicy::EAlways) || (iDialogSelectedOption & CPolicy::ENever))
636 __ASSERT_ALWAYS(NULL != iDialogFingerprint,
637 User::Panic(KUpsServerName,
638 KErrUpsBadFingerprintLength));
640 // Save yes/no result to database
641 TPtrC8 clientEntityName(KNullDesC8());
644 clientEntityName.Set(iClientEntity->Name());
647 CDecisionRecord *record = CDecisionRecord::NewLC(iPromptRequest->ClientSid(),
648 iPolicy->PolicyEvaluator(),
649 iPromptRequest->ServiceId(),
650 iPromptRequest->ServerSid(),
651 iDialogFingerprint->Fingerprint(),
653 iDialogFingerprint->Description(),
654 ((iDialogSelectedOption & CPolicy::EAlways) != 0),
655 iPolicy->MajorVersion(),
656 iDialogEvaluatorInfo);
658 // Create new record - Failure is not fatal...
659 TRAPD(err, UpsServer()->iDbHandle->CreateDecisionL(*record));
660 if(err == KErrAlreadyExists)
662 /// You might think this will never happen, but under OOM conditions it can/does. The original database query can fail,
663 /// even though there is a matching record in the database, we then choose to display a prompt, then the above code attempts to
664 /// insert a "new" record and fails.... We recover by just updating the existing record.
665 CDecisionFilter *filter = CDecisionFilter::NewLC(iPromptRequest->ClientSid(),
666 iPolicy->PolicyEvaluator(),
667 iPromptRequest->ServiceId(),
668 iPromptRequest->ServerSid(),
669 iDialogFingerprint->Fingerprint(),
671 iPolicy->MajorVersion());
673 TRAP(err, UpsServer()->iDbHandle->UpdateDecisionL(*filter, *record));
674 CleanupStack::PopAndDestroy(filter);
681 CleanupStack::PopAndDestroy(record);
684 iMessagePtr2.WriteL(0, decisionBuf);
686 CompleteAndMarkForDeletion(KErrNone);
689 TUpsDecision CAuthoriser::MapCPolicyTOptions2TUpsDecision(CPolicy::TOptions aOptions)
691 Map the specified policy option bitmap (normally only a single bit should be set) into
694 If the server supports session decisions, then return never/always as EUpsDecSessionNo/EUpsDecSessionYes.
696 Usually a single option should be specified, but we consider the options in the order which
697 minimises UPS traffic...
700 if( (aOptions & (CPolicy::ENever | CPolicy::ESessionNo)) != 0)
702 // A Never - If the session supports "session no" then return it to cut down on UPS traffic
703 if(iPolicy->Options() & CPolicy::ESessionNo)
705 return EUpsDecSessionNo;
707 // Otherwise return "single shot no" so the server will re-query us later (we may silently answer).
711 if( (aOptions & (CPolicy::EAlways| CPolicy::ESessionYes)) != 0)
713 // An Always - If the session supports "session yes" then return it to cut down on UPS traffic
714 if(iPolicy->Options() & CPolicy::ESessionYes)
716 return EUpsDecSessionYes;
718 // Otherwise return "single shot yes" so the server will re-query us later (so force prompt handling works).
722 if( (aOptions & CPolicy::ENo) != 0)
724 // A "single shot no"
728 // Only possibility left is a single shot yes
729 // If it is not one, then map to no
731 if( (aOptions & CPolicy::EYes) == 0)
733 return EUpsDecNo; // No option set!
737 // A "single shot yes"
741 void CAuthoriser::HandleDbErrorL(TInt aError)
743 if((aError != KErrNoMemory) && (aError != KErrDiskFull))
745 RDebug::Printf(KCorruptDb, aError, __LINE__);
746 UpsServer()->iDbHandle->DeleteDatabaseL(UpsServer()->iFs);
747 UpsServer()->iDbHandle.Close(); // Will auto-reopen when next used
750 UpsServer()->iDbHandle.Close(); // Will auto-reopen when next used
753 CAuthoriserFifo* CAuthoriserFifo::NewL()
755 Create a new CAuthoriserFifo instance
758 CAuthoriserFifo *self = new(ELeave) CAuthoriserFifo;
762 CAuthoriserFifo::~CAuthoriserFifo()
764 Does not delete CAuthoriser objects in the FIFO, just frees
765 storage used by the FIFO itself.
773 void CAuthoriserFifo::PushL(CAuthoriser *aAuthoriser)
775 @param aAuthoriser The CAuthoriser to be pushed onto the FIFO.
777 This class does NOT take ownership.
780 iPtrArray.AppendL(aAuthoriser);
783 CAuthoriser *CAuthoriserFifo::PopL()
786 @return A CAuthoriser ptr, or Leaves KErrUnderflow if FIFO is empty.
788 TInt count = iPtrArray.Count();
792 User::Leave(KErrUnderflow);
796 CAuthoriser *ret = iPtrArray[0];
801 void CAuthoriserFifo::RemoveL(CAuthoriser *aAuthoriser)
803 Remove the specified object from the FIFO.
805 It is not considered to be an error if the object is not in the
808 @param aAuthoriser The CAuthoriser to be removed from the FIFO.
811 TInt i = iPtrArray.Find(aAuthoriser);
813 if( i == KErrNotFound)
815 return; // Not found is not considered an error.
819 User::LeaveIfError(i);
824 TBool CAuthoriserFifo::IsEmpty() const
826 @return ETrue if empty
829 return (iPtrArray.Count() == 0);
832 void CAuthoriserFifo::Compress()
834 Reduce memory usage as much as possible. Typically used during OOM testing.
837 iPtrArray.Compress();
843 } // End of namespace UserPromptService