Example #1
0
NTSTATUS
NTAPI
NpGetClientSecurityContext(IN ULONG NamedPipeEnd,
                           IN PNP_CCB Ccb,
                           IN PETHREAD Thread,
                           IN PSECURITY_CLIENT_CONTEXT *Context)
{
    PSECURITY_CLIENT_CONTEXT NewContext;
    NTSTATUS Status;
    PAGED_CODE();

    if (NamedPipeEnd == FILE_PIPE_SERVER_END || Ccb->ClientQos.ContextTrackingMode != SECURITY_DYNAMIC_TRACKING)
    {
        NewContext = NULL;
        Status = STATUS_SUCCESS;
    }
    else
    {
        NewContext = ExAllocatePoolWithQuotaTag(PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
                                                sizeof(*NewContext),
                                                NPFS_CLIENT_SEC_CTX_TAG);
        if (!NewContext) return STATUS_INSUFFICIENT_RESOURCES;

        Status = SeCreateClientSecurity(Thread, &Ccb->ClientQos, 0, NewContext);
        if (!NT_SUCCESS(Status))
        {
            ExFreePool(NewContext);
            NewContext = NULL;
        }
    }
    *Context = NewContext;
    return Status;
}
Example #2
0
/*++
 * @name FsRtlAllocatePoolWithQuota
 * @implemented
 *
 * FILLME
 *
 * @param PoolType
 *        FILLME
 *
 * @param NumberOfBytes
 *        FILLME
 *
 * @return None
 *
 * @remarks The pool tag used is "FSrt".
 *
 *--*/
PVOID
NTAPI
FsRtlAllocatePoolWithQuota(IN POOL_TYPE PoolType,
                           IN ULONG NumberOfBytes)
{
    PVOID	Address;

    Address = ExAllocatePoolWithQuotaTag(PoolType,
                                         NumberOfBytes,
                                         IFS_POOL_TAG);
    if (NULL == Address)
    {
        ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
    }
    return Address;
}
Example #3
0
NTSTATUS
NTAPI
NpInitializeSecurity(IN PNP_CCB Ccb,
                     IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
                     IN PETHREAD Thread)
{
    PSECURITY_CLIENT_CONTEXT ClientContext;
    NTSTATUS Status;
    PAGED_CODE();

    if (SecurityQos)
    {
        Ccb->ClientQos = *SecurityQos;
    }
    else
    {
        Ccb->ClientQos.Length = sizeof(Ccb->ClientQos);
        Ccb->ClientQos.ImpersonationLevel = SecurityImpersonation;
        Ccb->ClientQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
        Ccb->ClientQos.EffectiveOnly = TRUE;
    }

    NpUninitializeSecurity(Ccb);

    if (Ccb->ClientQos.ContextTrackingMode == SECURITY_DYNAMIC_TRACKING)
    {
        Status = STATUS_SUCCESS;
        Ccb->ClientContext = NULL;
        return Status;
    }

    ClientContext = ExAllocatePoolWithQuotaTag(PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
                                               sizeof(*ClientContext),
                                               NPFS_CLIENT_SEC_CTX_TAG);
    Ccb->ClientContext = ClientContext;
    if (!ClientContext) return STATUS_INSUFFICIENT_RESOURCES;

    Status = SeCreateClientSecurity(Thread, &Ccb->ClientQos, 0, ClientContext);
    if (!NT_SUCCESS(Status))
    {
        ExFreePool(Ccb->ClientContext);
        Ccb->ClientContext = NULL;
    }

    return Status;
}
//------------------------------------------------------------------------------
NTSTATUS powerlinkCreate(PDEVICE_OBJECT pDeviceObject_p,
                         PIRP pIrp_p)
{
    NDIS_TIMER_CHARACTERISTICS  timerChars;
    tFileContext*               pFileContext;
    PIO_STACK_LOCATION          irpStack;
    NDIS_STATUS                 status;

    UNUSED_PARAMETER(pDeviceObject_p);

    DEBUG_LVL_ALWAYS_TRACE("PLK: + %s() ...\n", __func__);

    if (pIrp_p == NULL)
        return NDIS_STATUS_RESOURCES;

    irpStack = IoGetCurrentIrpStackLocation(pIrp_p);

    pFileContext = ExAllocatePoolWithQuotaTag(NonPagedPool,
                                              sizeof(tFileContext),
                                              PLK_MEM_TAG);
    if (pFileContext == NULL)
    {
        DEBUG_LVL_ERROR_TRACE("PLK: Failed to create file context\n");
    }

    IoInitializeRemoveLock(&pFileContext->driverAccessLock, PLK_MEM_TAG, 0, 0);

    irpStack->FileObject->FsContext = (void*)pFileContext;

    if (!plkDriverInstance_l.fInitialized)
    {
        NdisZeroMemory(&timerChars, sizeof(timerChars));

        C_ASSERT(NDIS_SIZEOF_TIMER_CHARACTERISTICS_REVISION_1 <= sizeof(timerChars));
        timerChars.Header.Type = NDIS_OBJECT_TYPE_TIMER_CHARACTERISTICS;
        timerChars.Header.Size = NDIS_SIZEOF_TIMER_CHARACTERISTICS_REVISION_1;
        timerChars.Header.Revision = NDIS_TIMER_CHARACTERISTICS_REVISION_1;

        timerChars.TimerFunction = increaseHeartbeatCb;
        timerChars.FunctionContext = NULL;
        timerChars.AllocationTag = PLK_MEM_TAG;

        status = NdisAllocateTimerObject(plkDriverInstance_l.driverHandle,
                                         &timerChars,
                                         &heartbeatTimer_l);
        if (status != NDIS_STATUS_SUCCESS)
        {
            DEBUG_LVL_ERROR_TRACE("%s() Timer Creation Failed %x\n", __func__, status);
            return STATUS_SUCCESS;
        }

        if (ctrlk_init(NULL) != kErrorOk)
        {
            return NDIS_STATUS_RESOURCES;
        }

        startHeartbeatTimer(20);
        plkDriverInstance_l.fInitialized = TRUE;
    }

    // Increase the count for open instances
    plkDriverInstance_l.instanceCount++;

    pIrp_p->IoStatus.Information = 0;
    pIrp_p->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(pIrp_p, IO_NO_INCREMENT);

    DEBUG_LVL_ALWAYS_TRACE("PLK: + %s() - OK\n", __func__);

    return STATUS_SUCCESS;
}
Example #5
0
NTSYSAPI
NTSTATUS
NTAPI
NtQueueApcThread(
    IN HANDLE ThreadHandle,
    IN PPS_APC_ROUTINE ApcRoutine,
    IN PVOID ApcArgument1,
    IN PVOID ApcArgument2,
    IN PVOID ApcArgument3
    )

/*++

Routine Description:

    This function is used to queue a user-mode APC to the specified thread. The APC
    will fire when the specified thread does an alertable wait

Arguments:

    ThreadHandle - Supplies a handle to a thread object.  The caller
        must have THREAD_SET_CONTEXT access to the thread.

    ApcRoutine - Supplies the address of the APC routine to execute when the
        APC fires.

    ApcArgument1 - Supplies the first PVOID passed to the APC

    ApcArgument2 - Supplies the second PVOID passed to the APC

    ApcArgument3 - Supplies the third PVOID passed to the APC

Return Value:

    Returns an NT Status code indicating success or failure of the API

--*/

{
    PETHREAD Thread;
    NTSTATUS st;
    KPROCESSOR_MODE Mode;
    KIRQL Irql;
    PKAPC Apc;

    PAGED_CODE();

    Mode = KeGetPreviousMode();

    st = ObReferenceObjectByHandle(
            ThreadHandle,
            THREAD_SET_CONTEXT,
            PsThreadType,
            Mode,
            (PVOID *)&Thread,
            NULL
            );

    if ( NT_SUCCESS(st) ) {
        st = STATUS_SUCCESS;
        if ( IS_SYSTEM_THREAD(Thread) ) {
            st = STATUS_INVALID_HANDLE;
            }
        else {
            Apc = ExAllocatePoolWithQuotaTag(
                    (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE),
                    sizeof(*Apc),
                    'pasP'
                    );

            if ( !Apc ) {
                st = STATUS_NO_MEMORY;
                }
            else {
                KeInitializeApc(
                    Apc,
                    &Thread->Tcb,
                    OriginalApcEnvironment,
                    PspQueueApcSpecialApc,
                    NULL,
                    (PKNORMAL_ROUTINE)ApcRoutine,
                    UserMode,
                    ApcArgument1
                    );

                if ( !KeInsertQueueApc(Apc,ApcArgument2,ApcArgument3,0) ) {
                    ExFreePool(Apc);
                    st = STATUS_UNSUCCESSFUL;
                    }
                }
            }
        ObDereferenceObject(Thread);
        }

    return st;
}
Example #6
0
// IRP_MJ_CREATE/IRP_MJ_CLOSE处理函数
NTSTATUS DispatchCreateClose(
    __in PDEVICE_OBJECT DeviceObject,
    __in PIRP Irp
    )
{
    PIO_STACK_LOCATION irpStack;
    NTSTATUS            status;
    PFILE_CONTEXT       fileContext;

    UNREFERENCED_PARAMETER(DeviceObject);

    PAGED_CODE();

    irpStack = IoGetCurrentIrpStackLocation(Irp);

    ASSERT(irpStack->FileObject != NULL);    

    switch (irpStack->MajorFunction)
    {
	case IRP_MJ_CREATE:
		{
			DebugPrint(("IRP_MJ_CREATE\n"));

			fileContext = (PFILE_CONTEXT)ExAllocatePoolWithQuotaTag(
				NonPagedPool, 
				sizeof(FILE_CONTEXT),
				TAG);

			if (NULL == fileContext) 
			{
				status =  STATUS_INSUFFICIENT_RESOURCES;
				break;
			}

			IoInitializeRemoveLock(&fileContext->FileRundownLock, TAG, 0, 0);

			ASSERT(irpStack->FileObject->FsContext == NULL);
			irpStack->FileObject->FsContext = (PVOID) fileContext;

			status = STATUS_SUCCESS;
			break;
		}

	case IRP_MJ_CLOSE:
		{
			DebugPrint(("IRP_MJ_CLOSE\n"));

			fileContext = irpStack->FileObject->FsContext;
			IoAcquireRemoveLock(&fileContext->FileRundownLock, 0);
			IoReleaseRemoveLockAndWait(&fileContext->FileRundownLock, 0);

			ExFreePoolWithTag(fileContext, TAG);

			status = STATUS_SUCCESS;
			break;
		}

	default:
		ASSERT(FALSE);  // should never hit this
		status = STATUS_NOT_IMPLEMENTED;
		break;
    }

    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return status;
}
Example #7
0
NTSTATUS
BowserConvertType3IoControlToType2IoControl (
    IN PIRP Irp,
    IN PIO_STACK_LOCATION IrpSp
    )

/*++

Routine Description:

    This routine does the work necessary to convert a type 3 IoCtl to a
    type 2 IoCtl.  We do this when we have to pass a user IRP to the FSP.


Arguments:

    IN PIRP Irp - Supplies an IRP to convert
    IN PIO_STACK_LOCATION IrpSp - Supplies an Irp Stack location for convenience

Return Value:

    NTSTATUS - Status of operation

Note: This must be called in the FSD.

--*/

{
    NTSTATUS Status;

    PAGED_CODE();

    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0) {
        Status = BowserLockUsersBuffer(Irp, IoWriteAccess, IrpSp->Parameters.DeviceIoControl.OutputBufferLength);

        //
        //  If we were unable to lock the users output buffer, return now.
        //

        if (!NT_SUCCESS(Status)) {
            return Status;
        }

    }

    ASSERT (Irp->AssociatedIrp.SystemBuffer == NULL);

    try {
        if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0) {
            PCHAR InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
            ULONG InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;

            Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag(PagedPool,
                                                            InputBufferLength, '  GD');

            if (Irp->AssociatedIrp.SystemBuffer == NULL) {
                return STATUS_INSUFFICIENT_RESOURCES;
            }

            RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer,
                       InputBuffer,
                       InputBufferLength);

            Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);

        } else {
            Irp->AssociatedIrp.SystemBuffer = NULL;
        }

    } except (EXCEPTION_EXECUTE_HANDLER) {
        return GetExceptionCode();
    }

    return STATUS_SUCCESS;
}
Example #8
0
NTSTATUS
NTAPI
NpAddDataQueueEntry(IN ULONG NamedPipeEnd,
                    IN PNP_CCB Ccb,
                    IN PNP_DATA_QUEUE DataQueue,
                    IN ULONG Who,
                    IN ULONG Type,
                    IN ULONG DataSize,
                    IN PIRP Irp,
                    IN PVOID Buffer,
                    IN ULONG ByteOffset)
{
    NTSTATUS Status;
    PNP_DATA_QUEUE_ENTRY DataEntry;
    SIZE_T EntrySize;
    ULONG QuotaInEntry;
    PSECURITY_CLIENT_CONTEXT ClientContext;
    BOOLEAN HasSpace;

    ClientContext = NULL;
    ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));

    Status = STATUS_SUCCESS;

    if ((Type != 2) && (Who == WriteEntries))
    {
        Status = NpGetClientSecurityContext(NamedPipeEnd,
                                            Ccb,
                                            Irp ? Irp->Tail.Overlay.Thread :
                                            PsGetCurrentThread(),
                                            &ClientContext);
        if (!NT_SUCCESS(Status))
        {
            return Status;
        }
    }

    switch (Type)
    {
        case Unbuffered:
        case 2:
        case 3:

            ASSERT(Irp != NULL);
            DataEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
                                                   sizeof(*DataEntry),
                                                   NPFS_DATA_ENTRY_TAG);
            if (!DataEntry)
            {
                NpFreeClientSecurityContext(ClientContext);
                return STATUS_INSUFFICIENT_RESOURCES;
            }

            DataEntry->DataEntryType = Type;
            DataEntry->QuotaInEntry = 0;
            DataEntry->Irp = Irp;
            DataEntry->DataSize = DataSize;
            DataEntry->ClientSecurityContext = ClientContext;
            ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
            Status = STATUS_PENDING;
            break;

        case Buffered:

            EntrySize = sizeof(*DataEntry);
            if (Who != ReadEntries)
            {
                EntrySize += DataSize;
                if (EntrySize < DataSize)
                {
                    NpFreeClientSecurityContext(ClientContext);
                    return STATUS_INVALID_PARAMETER;
                }
            }

            QuotaInEntry = DataSize - ByteOffset;
            if (DataQueue->Quota - DataQueue->QuotaUsed < QuotaInEntry)
            {
                QuotaInEntry = DataQueue->Quota - DataQueue->QuotaUsed;
                HasSpace = TRUE;
            }
            else
            {
                HasSpace = FALSE;
            }

            DataEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
                                                   EntrySize,
                                                   NPFS_DATA_ENTRY_TAG);
            if (!DataEntry)
            {
                NpFreeClientSecurityContext(ClientContext);
                return STATUS_INSUFFICIENT_RESOURCES;
            }

            DataEntry->QuotaInEntry = QuotaInEntry;
            DataEntry->Irp = Irp;
            DataEntry->DataEntryType = Buffered;
            DataEntry->ClientSecurityContext = ClientContext;
            DataEntry->DataSize = DataSize;

            if (Who == ReadEntries)
            {
                ASSERT(Irp);

                Status = STATUS_PENDING;
                ASSERT((DataQueue->QueueState == Empty) ||
                       (DataQueue->QueueState == Who));
            }
            else
            {
                _SEH2_TRY
                {
                    RtlCopyMemory(DataEntry + 1,
                                  Irp ? Irp->UserBuffer: Buffer,
                                  DataSize);
                }
                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                {
                    NpFreeClientSecurityContext(ClientContext);
                    _SEH2_YIELD(return _SEH2_GetExceptionCode());
                }
                _SEH2_END;

                if (HasSpace && Irp)
                {
                    Status = STATUS_PENDING;
                }
                else
                {
                    DataEntry->Irp = NULL;
                    Status = STATUS_SUCCESS;
                }

                ASSERT((DataQueue->QueueState == Empty) ||
                       (DataQueue->QueueState == Who));
            }
            break;

        default:
            ASSERT(FALSE);
            NpFreeClientSecurityContext(ClientContext);
            return STATUS_INVALID_PARAMETER;
    }

    ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
    if (DataQueue->QueueState == Empty)
    {
        ASSERT(DataQueue->BytesInQueue == 0);
        ASSERT(DataQueue->EntriesInQueue == 0);
        ASSERT(IsListEmpty(&DataQueue->Queue));
    }
    else
    {
        ASSERT(DataQueue->QueueState == Who);
        ASSERT(DataQueue->QueueState != Empty);
        ASSERT(DataQueue->EntriesInQueue != 0);
    }

    DataQueue->QuotaUsed += DataEntry->QuotaInEntry;
    DataQueue->QueueState = Who;
    DataQueue->BytesInQueue += DataEntry->DataSize;
    DataQueue->EntriesInQueue++;

    if (ByteOffset)
    {
        DataQueue->ByteOffset = ByteOffset;
        ASSERT(Who == WriteEntries);
        ASSERT(ByteOffset < DataEntry->DataSize);
        ASSERT(DataQueue->EntriesInQueue == 1);
    }

    InsertTailList(&DataQueue->Queue, &DataEntry->QueueEntry);

    if (Status == STATUS_PENDING)
    {
        IoMarkIrpPending(Irp);
        Irp->Tail.Overlay.DriverContext[2] = DataQueue;
        Irp->Tail.Overlay.DriverContext[3] = DataEntry;

        IoSetCancelRoutine(Irp, NpCancelDataQueueIrp);

        if ((Irp->Cancel) && (IoSetCancelRoutine(Irp, NULL)))
        {
            NpCancelDataQueueIrp(NULL, Irp);
        }
    }

    return Status;
}
Example #9
0
NTSTATUS
CsampCreateClose(
    __in PDEVICE_OBJECT DeviceObject,
    __in PIRP Irp
    )
/*++

Routine Description:

   Process the Create and close IRPs sent to this device.

Arguments:

   DeviceObject - pointer to a device object.

   Irp - pointer to an I/O Request Packet.

Return Value:

      NT Status code

--*/
{
    PIO_STACK_LOCATION  irpStack;
    NTSTATUS            status = STATUS_SUCCESS;
    PFILE_CONTEXT       fileContext;

    UNREFERENCED_PARAMETER(DeviceObject);

    PAGED_CODE ();

    CSAMP_KDPRINT(("CsampCreateClose Enter\n"));

    irpStack = IoGetCurrentIrpStackLocation(Irp);

    ASSERT(irpStack->FileObject != NULL);    

    switch(irpStack->MajorFunction)
    {
        case IRP_MJ_CREATE:

            //
            // The dispatch routine for IRP_MJ_CREATE is called when a
            // file object associated with the device is created.
            // This is typically because of a call to CreateFile() in
            // a user-mode program or because a another driver is
            // layering itself over a this driver. A driver is
            // required to supply a dispatch routine for IRP_MJ_CREATE.
            //
            fileContext = ExAllocatePoolWithQuotaTag(NonPagedPool, 
                                              sizeof(FILE_CONTEXT),
                                              TAG);

            if (NULL == fileContext) {
                status =  STATUS_INSUFFICIENT_RESOURCES;
                break;
            }

            IoInitializeRemoveLock(&fileContext->FileRundownLock, TAG, 0, 0);

            //
            // Make sure nobody is using the FsContext scratch area.
            //
            ASSERT(irpStack->FileObject->FsContext == NULL);    

            //
            // Store the context in the FileObject's scratch area.
            //
            irpStack->FileObject->FsContext = (PVOID) fileContext;
            
            CSAMP_KDPRINT(("IRP_MJ_CREATE\n"));
            break;

        case IRP_MJ_CLOSE:
            //
            // The IRP_MJ_CLOSE dispatch routine is called when a file object
            // opened on the driver is being removed from the system; that is,
            // all file object handles have been closed and the reference count
            // of the file object is down to 0.
            //
            fileContext = irpStack->FileObject->FsContext;
            
            ExFreePoolWithTag(fileContext, TAG);

            CSAMP_KDPRINT(("IRP_MJ_CLOSE\n"));
            break;

        default:
            CSAMP_KDPRINT((" Invalid CreateClose Parameter\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
    }

    //
    // Save Status for return and complete Irp
    //
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    CSAMP_KDPRINT((" CsampCreateClose Exit = %x\n", status));

    return status;
}
Example #10
0
NTSTATUS
NTAPI
NpSetClientProcess(IN PDEVICE_OBJECT DeviceObject,
                   IN PIRP Irp)
{
    PIO_STACK_LOCATION IoStackLocation;
    NODE_TYPE_CODE NodeTypeCode;
    PNP_CCB Ccb;
    ULONG Length;
    PNP_CLIENT_PROCESS InputBuffer, ClientSession, OldClientSession;
    PAGED_CODE();

    /* Get the current stack location */
    IoStackLocation = IoGetCurrentIrpStackLocation(Irp);

    /* Only kernel calls are allowed! */
    if (IoStackLocation->MinorFunction != IRP_MN_KERNEL_CALL)
    {
        return STATUS_ACCESS_DENIED;
    }

    /* Decode the file object and check the node type */
    NodeTypeCode = NpDecodeFileObject(IoStackLocation->FileObject, 0, &Ccb, 0);
    if (NodeTypeCode != NPFS_NTC_CCB)
    {
        return STATUS_PIPE_DISCONNECTED;
    }

    /* Get the length of the query buffer and check if it's valid */
    Length = IoStackLocation->Parameters.QueryFile.Length;
    if (Length != sizeof(NP_CLIENT_PROCESS))
    {
        return STATUS_INVALID_PARAMETER;
    }

    /* Get the buffer and check if the data Length is valid */
    InputBuffer = Irp->AssociatedIrp.SystemBuffer;
    if (InputBuffer->DataLength > 30)
    {
        return STATUS_INVALID_PARAMETER;
    }

    /* Allocate a new structure */
    ClientSession = ExAllocatePoolWithQuotaTag(PagedPool,
                    sizeof(NP_CLIENT_PROCESS),
                    'iFpN');

    /* Copy the full input buffer */
    RtlCopyMemory(ClientSession, InputBuffer, sizeof(NP_CLIENT_PROCESS));

    /* Lock the Ccb */
    ExAcquireResourceExclusiveLite(&Ccb->NonPagedCcb->Lock, TRUE);

    /* Get the old ClientSession and set the new */
    OldClientSession = Ccb->ClientSession;
    Ccb->ClientSession = ClientSession;

    /* Copy the process to the CCB */
    Ccb->Process = ClientSession->Process;

    /* Unlock the Ccb */
    ExReleaseResourceLite(&Ccb->NonPagedCcb->Lock);

    /* Check if there was already a ClientSession */
    if (OldClientSession != NULL)
    {
        /* Free it */
        ExFreePoolWithTag(OldClientSession, 'iFpN');
    }

    return STATUS_SUCCESS;
}