Impersonation

Some file systems might find it useful to perform operations on behalf of the original caller. For example, a network file system might need to capture the caller's security information at the time a file is opened so that a subsequent operation can be performed using the appropriate credentials. No doubt there are numerous other special cases where this type of feature is useful, both within a file system as well as in specific applications.

The key routines needed for impersonation include:

  • PsImpersonateClient SeImpersonateClientEx--initiates impersonation. Unless a specific thread is indicated, the impersonation is done in the current thread context.

  • PsRevertToSelf--terminates impersonation within the current thread context.

  • PsReferencePrimaryToken--holds a reference on the primary (process) token for the specified process. This function may be used to capture the token for any process on the system.

  • PsDereferencePrimaryToken--releases a reference on a previously referenced primary token.

  • SeCreateClientSecurityFromSubjectContext--returns a client security context useful for impersonation from a subject context (provided to the FSD during the IRP_MJ_CREATE handling, for example).

  • SeCreateClientSecurity--creates a client security context based upon the security credentials of an existing thread on the system.

  • ImpersonateSecurityContext--impersonates security context within ksecdd.sys, the kernel security service.

  • RevertSecurityContext--terminates impersonation within ksecdd.sys, the kernel security service.

Impersonation is straight-forward to implement. The following code example demonstrates basic impersonation:

NTSTATUS PerformSpecialTask(IN PFSD_CONTEXT Context)
{
  BOOLEAN CopyOnOpen;
  BOOLEAN EffectiveOnly;
  SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
  NTSTATUS Status;
  PACCESS_TOKEN oldToken;

  //
  // We need to perform a task in the system process context
  //
  if (NULL == Context->SystemProcess) {

    return STATUS_NO_TOKEN;

  }

  //
  // Save the existing token, if any (otherwise NULL)
  //
  oldToken = PsReferenceImpersonationToken(PsGetCurrentThread(),
                                           &CopyOnOpen,
                                           &EffectiveOnly,
                                           &ImpersonationLevel);

  Status = PsImpersonateClient( PsGetCurrentThread(),
                                Context->SystemProcess,
                                TRUE,
                                TRUE,
                                SecurityImpersonation);
  if (!NT_SUCCESS(Status)) {

    if (oldToken)
        PsDereferenceImpersonationToken(oldToken);
    return Status;

  }

  //
  // Perform task - whatever it is
  //


  //
  // Restore to previous impersonation level
  //
  if (oldToken) {
    Status = PsImpersonateClient(PsGetCurrentThread(),
                                 oldToken,
                                 CopyOnOpen,
                                 EffectiveOnly,
                                 ImpersonationLevel);

    if (!NT_SUCCESS(Status)) {
      //
      // This is bad - we can't restore, we can't leave it this way 
      //
      PsRevertToSelf();
    }
    PsDereferenceImpersonationToken(oldToken);
  } else {
    PsRevertToSelf();
  }

  return Status;
}

There are numerous variants of this impersonation code that are available to file systems developers, but this provides a basic illustration of the technique.