Link to home
Start Free TrialLog in
Avatar of essbb
essbb

asked on

Testing NT Security

I am writing a DLL function which accepts a path to a file or directory and returns a bitmask double word which represents the current Access permissions.

I wish to be able to check that I can Read a file, write to a file, Add or delete a file from a direcotory. before I actually attempt to do it. The purpose being so that I can build my GUI dynamically, disabling areas of the GUI if the required  file permissions are not available.

I am using Vis C++ 6 on windows NT with NTFS.

I am trying to tackle this using AccessCheck().
firstly calling GetNamedSecurityInfo to get the SD for the passed file. I then call OpenProcessToken to get the AccessToken for the current process - I think OpenThreadToken would be better here but I could not get it working ;-(

And finally I call AccessCheck to compare the SD and AccessToken.

My Problem :
AccessCheck always fails with error 998 (invalid access to memory location) I dont know why.

I think it may be due to the generic mapping structure I need to pass to AccessCheck, I have been passing NULL and empty PGENERIC_MAPPING's - I dont know how to get the generic mapping for the file.

My Question : Basicly in 3 parts -

a) am I tackling this in the correct way? Is there an easier way?

b) how do I get AccessCheck() to work!! (I would increase the points for an example that works!!)

c) how do I interperate the results - ie the access mask returned from accesscheck().
Avatar of essbb
essbb

ASKER

Edited text of question.
It might help if you post your code.

I suspect the AccessCheck does not work because with one or more of the pointer parameter, you are not passing an initialized pointer.  i.e for parameters like, PrivilegeSet, you must pass an initialized pointer.  The pointer must point to a PRIVILEGE_SET structure that you have created.  
HOWTO: Program a Secure Server on Windows NT
http://support.microsoft.com/support/kb/articles/Q171/2/73.ASP
Avatar of essbb

ASKER

nietod,

Here is the code, I have made a some progress since my original posting. I will explain at the bottom

//--------------------------------------

static const ACCESS_READ  = 1;
static const ACCESS_WRITE = 2;


int __stdcall secGetObjectPermissions(const char * pchObjectName, int &iPermissions)
{
    DWORD dwResult;
    PSECURITY_DESCRIPTOR pSD;
      HANDLE hdlToken = 0;
      HANDLE hdlCurrentThread = 0;
      DWORD dwGrantedAccess = 0;
      DWORD dwError;
      PRIVILEGE_SET ps;
      GENERIC_MAPPING gm;

      DWORD dwAccessDesired;
      DWORD dwStructureSize = sizeof(PRIVILEGE_SET);
      
      int bAccessStatus = true;
      char * pchObject = new char[strlen(pchObjectName)];

      strcpy(pchObject,pchObjectName);
      
      ImpersonateSelf(SecurityImpersonation);
    //1 Get Security Descriptor (SD) for file
    if (GetNamedSecurityInfo( pchObject,SE_FILE_OBJECT,NULL,NULL,NULL,NULL, NULL, &pSD) == ERROR_SUCCESS)
      {

            dwResult = IsValidSecurityDescriptor(pSD);

            hdlCurrentThread = GetCurrentThread();
          //2  Get the client Access Token
          if (OpenThreadToken(hdlCurrentThread,TOKEN_READ,FALSE,&hdlToken) != 0)
            
            
            {
              
                dwAccessDesired = ACCESS_READ;
                  // Initialize GenericMapping structure to map all.
                  memset(&gm,0xff,sizeof(GENERIC_MAPPING));
                  gm.GenericRead = ACCESS_READ;
                  gm.GenericWrite = ACCESS_WRITE;
                  gm.GenericExecute = 0;
                  gm.GenericAll = ACCESS_READ | ACCESS_WRITE;
            // This only does something if we want to use generic access rights,
                  // like GENERIC_ALL, in our call to AccessCheck. We are not.      //
                  MapGenericMask(&dwAccessDesired, &gm);
                  
                  
                  //3 Call Access Check
            if (AccessCheck(&pSD,&hdlToken,dwAccessDesired,&gm,&ps,&dwStructureSize,&dwGrantedAccess,&bAccessStatus) == 0)
                  {
                        // if failed get errorcode for debug
                        dwError = GetLastError();
                  }
            }
            else
            {
             dwError = GetLastError();            
            }


      
            
            //4 free objects
       LocalFree(pSD);
         RevertToSelf();
      }

      return (1);
}


//--------------------------------------

There is a fair bit of code in here for debugging.

Since I posted my original message I have added the impersonation code and the generic mask code, I have also revenrted to OpenThreadToken which now works with the impersonation on.

The AccessCheck call now returns error code 6 (in the GetLastError call which is " the handle is invalid", It doesnt say which one of course. Since I am checking the handle to the SD it must be the token handle? not sure why though.

I am also not sure why I have to set up the GenericMapping structure? I am not sure if accesscheck is too low level for what I am trying to accomplish?
Avatar of essbb

ASKER

chensu

thanks for the idea, I have seen this article - I have had my head in the MSDN Library CD's for 2 days now.

The problem with this article - and you may be able to fill in the gaps??? is that it shows you how to test for custom permissions in your own custom objects - what I am trying to do is read standard NT permissions for Standard NT securable objects.

The example shows how to test an SD to see if it has Generic read or generic write but does not show me how to test that the SD I have for a directory (i can get that bit) has Delete permission (delete being different from write)

If you have seen another article or have tried this yourself I would love to know ;-)

cheers - Brett
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Agree with jkr.
Avatar of essbb

ASKER

OK so a rookie mistake? - DOH!!

mind you this is my very first Vis C++ program so Im not doing too bad I think

- Will try this out and post results

-Brett
Avatar of essbb

ASKER

OK I have tried the code. I now get a "invalid security descriptor". from GetLastError Error.

I have moved the IsValidSecurityDescriptor() function to protect the AccessCheck(). IsValidSecurityDescriptor is returing 1 (TRUE)

the code now looks like this
//----------------------------------------------------

if (IsValidSecurityDescriptor(pSD))
{
    if (AccessCheck(pSD,hdlToken,dwAccessDesired,&gm,&ps,&dwStructureSize,&dwGrantedAccess,&bAccessStatus) == 0)
    {
        // if failed get errorcode for debug
        dwError = GetLastError();
    }
}

//---------------------------------------------

any suggestions?

Also the other part of this  
Avatar of essbb

ASKER

also the other part of this question - am I tackling this correctly?

How do I test for Generic Delete Permissions ??
Avatar of essbb

ASKER

Ok

have solved this one myself.

I needed to pass a bit more information to this function

GetNamedSecurityInfo( pchObject,SE_FILE_OBJECT,NULL,NULL,NULL,NULL, NULL, &pSD) == ERROR_SUCCESS)


although I am not sure if i need to freelocal on pDACL, pSidGroup or pSidOwner.. or hdlToken for that matter?




>>although I am not sure if i need to freelocal on pDACL,
>>pSidGroup or pSidOwner.. or hdlToken for that matter?

Hmm, I don't see pDACL, pSidGroup or pSidOwner in your code - the rule of thumb is: If you allocated them, free them... About 'hdlToken' - tokens are windows handles, so, if you no longer need them, call 'CloseHandle()'
Avatar of essbb

ASKER

Thanks for the help
Dear essbb

I think you forgot this question. I will ask Community Support to close it unless you finalize it within 7 days. You can always request to keep this question open. But remember, experts can only help you if you provide feedback to their questions.
Unless there is objection or further activity,  I will suggest to accept

     "refund the points and PAQ at zero points"

since you found your own solution.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
======
Werner
>>since you found your own solution.

... with the "little hint" that a "void**" is different from a "void*"
OOOOOOOPS - there is a bigger problem!!!!

This _is_ indeed a PAQ, I received the following 11/23/99:

*** Email header removed by Netminder, EE Admin ***

Regarding essbb's question in the Experts Exchange C++ Programming
topic area titled "Testing NT Security"...

Congratulations!  You earned 400 quality points for answering essbb's
question!  These points will be added to your expert score in the C++
Programming topic area.

essbb also left a comment along with his answer.
If you'd like to return to the question and continue discussion, here
is the question's URL:
https://www.experts-exchange.com/EQ.10236213
      - or -
https://www.experts-exchange.com/Q.10236213 (non-cookie login)

If you'd like to check for more questions to answer in the C++
Programming topic area, head back to Experts Exchange:

https://www.experts-exchange.com/Computers/Programming/Languages/C++/

and thank you for participating with Experts Exchange!
Thanks for your comments jkr.

This is the dark time of EE, when the sorting of comments followed its own rules. I appreciate ANY help experts can give here, since some times I am completely in the dark :-(

I will notify CS about this closed question.

======
Werner
Thank you, Werner!

It sometimes pays to store "old" emails :o)
Force-accepted on behalf of the packrat jkr by
Netminder
CS Moderator

*grin*
:o)