Taking Ownership

S

Stephen Engle

I am trying to allow for user account to take ownership of an Active
Directory object. I have assigned the Modify Owner permission to the user
on the AD object - a distribution list in this case. Using Active Directory
Users and Computers, the user can take ownership of the object. But I have
not been able to get the program I am working on to do so. Whenever I try
to write the Security Descriptor back to the object, I get the
E_ACCESSDENIED error. The program code works fine if ran as a domain
administrator. So I am thinking that I do not have a privilege activated
that I need. But I have tried using AdjustTokenPrivilege to grant the
SE_RESTORE_NAME and SE_TAKE_OWNERSHIP_NAME privileges. That does not seem
to have any effect.

Does anyone have an idea about what I am missing?


Thanks.




The test code is as follows:

void CADOwnDlg::OnBnClickedTakeownership()
{
ACL * abdacl;
DWORD abdacllen;
SID * abowner;
DWORD abownerlen;
SID * abpg;
DWORD abpglen;
ACL * absacl;
DWORD absacllen;
SECURITY_DESCRIPTOR * absd;
DWORD absdlen;
IDirectoryObject * adobject;
ADSVALUE adsvalue;
ADS_ATTR_INFO attrinfo;
LPWSTR attrstr;
DWORD buflen;
HRESULT hr;
LUID luid;
DWORD numobjattr;
ADS_ATTR_INFO * objattr;
WCHAR objectpath[256];
HANDLE prochandle;
SECURITY_DESCRIPTOR * sd;
SECURITY_DESCRIPTOR * sdsr;
unsigned char sid[256];
SID * sidnewowner;
HANDLE tokenhandle;
TOKEN_PRIVILEGES tp;

abdacl=NULL;
abdacllen=0;
abowner=NULL;
abownerlen=0;
abpg=NULL;
abpglen=0;
absacl=NULL;
absacllen=0;
absd=NULL;
absdlen=0;
adobject=NULL;
attrstr=L"nTSecurityDescriptor";
buflen=0;
DWORD numattr;
objattr=NULL;
sdsr=NULL;
memset(sid, 0, sizeof(sid));

memcpy(sid, SidVal, SidValSize);

sidnewowner=(SID *)sid;
wcscpy(objectpath, L"LDAP://CN=Test.One,OU=Client Distribution
Lists,OU=Public Distribution Lists,DC=aoins,DC=com");

hr=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

hr=ADsOpenObject(objectpath, NULL, NULL, ADS_SECURE_AUTHENTICATION,
IID_IDirectoryObject, (void **)&adobject);

hr=adobject->GetObjectAttributes(&attrstr, 1, &objattr, &numobjattr);

sd=(SECURITY_DESCRIPTOR *)objattr->pADsValues->SecurityDescriptor.lpValue;

MakeAbsoluteSD(sd, absd, &absdlen, abdacl, &abdacllen, absacl, &absacllen,
abowner, &abownerlen, abpg, &abpglen);
absd=(SECURITY_DESCRIPTOR *)malloc(absdlen);
abdacl=(ACL *)malloc(abdacllen);
absacl=(ACL *)malloc(absacllen);
abowner=(SID *)malloc(abownerlen);
abpg=(SID *)malloc(abpglen);
MakeAbsoluteSD(sd, absd, &absdlen, abdacl, &abdacllen, absacl,
&absacllen, abowner, &abownerlen, abpg, &abpglen);

SetSecurityDescriptorOwner(absd, sidnewowner, FALSE);

MakeSelfRelativeSD(absd, sdsr, &buflen);
sdsr=(SECURITY_DESCRIPTOR *)malloc(buflen);
MakeSelfRelativeSD(absd, sdsr, &buflen);

prochandle=GetCurrentProcess();

OpenProcessToken(prochandle, TOKEN_ADJUST_PRIVILEGES, &tokenhandle);

LookupPrivilegeValue("DCH1", SE_RESTORE_NAME, &luid);
tp.PrivilegeCount=1;
tp.Privileges[0].Luid=luid;
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(tokenhandle, FALSE, &tp, 0, NULL, NULL);

LookupPrivilegeValue("DCH1", SE_TAKE_OWNERSHIP_NAME, &luid);
tp.PrivilegeCount=1;
tp.Privileges[0].Luid=luid;
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(tokenhandle, FALSE, &tp, 0, NULL, NULL);

attrinfo.dwADsType=objattr->dwADsType;
attrinfo.dwControlCode=ADS_ATTR_UPDATE;
attrinfo.dwNumValues=1;
attrinfo.pADsValues=&adsvalue;
adsvalue.dwType=objattr->dwADsType;
adsvalue.SecurityDescriptor.lpValue=(LPBYTE)sdsr;
adsvalue.SecurityDescriptor.dwLength=buflen;
attrinfo.pszAttrName=attrstr;
hr=adobject->SetObjectAttributes(&attrinfo, 1, &numattr);

FreeADsMem(objattr);
adobject->Release();
if(abdacl!=NULL) free(abdacl);
if(abowner!=NULL) free(abowner);
if(abpg!=NULL) free(abpg);
if(absacl!=NULL) free(absacl);
if(absd!=NULL) free(absd);
if(sdsr!=NULL) free(sdsr);
CoUninitialize();
}
 
W

William DePalo [MVP VC++]

Stephen Engle said:
The program code works fine if ran as a domain
administrator. So I am thinking that I do not have a privilege activated
that I need. But I have tried using AdjustTokenPrivilege to grant the
SE_RESTORE_NAME and SE_TAKE_OWNERSHIP_NAME privileges. That does not seem
to have any effect.

Does anyone have an idea about what I am missing?

AdjustTokenPrivileges() does not grant privileges. If it did, there would be
no security at all as any thread could grant any old privilege. Rather it
enables privleges.

To review, privilges have three states:

1) not granted
2) granted and not enabled
3) granted and enabled

AdjustTokenPrivileges() can transition from state 2 to 3 or 3 to 2 in my
list above.

Because you say the code works as a domain administrator you will have to
run the code in a more privileged security context.

You have a few options:

1) Run under a proper user context.

2) Use the credentials of a suitably privileged user and LogonUser(),
ImpersonateLoggedOnUser() and RevertToSelf() so that you have the necessary
credentials at the time you need them

3) make some inter-process request of a service installed with the proper
credentials to do your bidding. Of course, then you need a way for the
service to verify that the request comes from a party it trusts.

I'd post again in a more appropriate group. If security was my _thing_ I'd
point you, but it ain't so I can't. :)

Regards,
Will
 
S

Stephen Engle

The thing I can't figure out here, is why Active Directory Users and
Computers can take ownership, yet my program cannot (obviously something is
missing). I tried using ADSI objects as well but got the same result. Is
ADU&C doing something behind the scenes with the servers to facilitate this,
or is it some API calls that I'm not doing?
 
W

William DePalo [MVP VC++]

Stephen Engle said:
The thing I can't figure out here, is why Active Directory Users and
Computers can take ownership, yet my program cannot (obviously something
is
missing).

It is a good question for which I don't have an answer. I _think_ though
that you could find an answer by looking at that group and its privileges.
Assuming that the code is proper, the issue is a privilege granted to a user
in the group and which is lacking in the user under whose context your
application runs.

Do you have the credentials of one of the users in this group? If so, and if
you run on XP you could try this experiment:

LogonUser(); // use credentials of user in AD Users group
ImpersonateLoggedOnUser();

// do what you need to do

RevertToSelf();

I tried using ADSI objects as well but got the same result. Is
ADU&C doing something behind the scenes with the servers to facilitate
this,
or is it some API calls that I'm not doing?

Sorry, but I am not well versed in AD. You may want to search for a more
appropriate group in which to post your question.

Regards,
Will
 
S

Stephen Engle

Sorry - I should have been more plain about the user. I log on as the user
(a test id) in question to test my app (and also to test that user's rights
using Active Directory Users & Computers). Unfortunately, the only thing I
have been able to determine is that the user id has the appropriate rights
(because ADU&C can make the change) but my app still won't work, unless I'm
logged in as myself. I am a Domain Admin and therefore have so many rights
that I don't know for sure what the difference really is. But since ADU&C
can work for the test id, I would think there must be some way to do this
with my app.

Do you have any ideas as to what group might be more appropriate to post
this question to. I have looked at the huge list of Microsoft groups and
have only found this and potentially the microsoft.public.win2000.security
group that seemed to touch on this.


Thanks again.
 
W

William DePalo [MVP VC++]

Stephen Engle said:
Sorry - I should have been more plain about the user. I log on as the
user
(a test id) in question to test my app (and also to test that user's
rights
using Active Directory Users & Computers). Unfortunately, the only thing
I
have been able to determine is that the user id has the appropriate rights
(because ADU&C can make the change) but my app still won't work, unless
I'm
logged in as myself. I am a Domain Admin and therefore have so many
rights
that I don't know for sure what the difference really is. But since ADU&C
can work for the test id, I would think there must be some way to do this
with my app.
Do you have any ideas as to what group might be more appropriate to post
this question to. I have looked at the huge list of Microsoft groups and
have only found this and potentially the microsoft.public.win2000.security
group that seemed to touch on this.

The two languages groups (this and m.p.vc.language) field lots of questions
on things people do with the language even though the groups' focus is
ostensibly on the language itself.

The kernel group (m.p.win32.programmer.kernel) has a similar mix of skills
among the regulars and more than a few who grok security.

Apart from the kernel group, I'd plug

activedirectory

or

adsi

into the newsgroup search edit box to find a more appropriate one. If I read
any of these groups I'd suggest one, but as I said, I don't so I can't. :-(

At the risk of stating the obvious ... are you checking the return codes
from the functions that you call? It is a really really rare event for a
function in the Win32 API to fail silently. One thing you can do is type

err

in the watch window in the debugger. This pseudo-variable is an alias for
the calling thread's last error code. When a function fails (and only if it
fails so you need to check return codes or look at the EAX register if you
forgot to capture the return) this is the place to look for the underlying
cause of the failure.
Thanks again.

You are welcome.

Regards,
Will
 
S

Stephen Engle

I have indeed been checking my return codes. The only one that fails is the
call to apply the changes back to the AD group (E_ACCESSDENIED). But thanks
for all of your help and suggestions. I will look for another place to post
and check on this.
 
W

William DePalo [MVP VC++]

Stephen Engle said:
I have indeed been checking my return codes. The only one that fails is
the
call to apply the changes back to the AD group (E_ACCESSDENIED).

Does the function that fails not detail what it requires? Is there any
possibility that it is a wild pointer problem? It it is something in wide
use, someone in the kernel group may spot the problem.
But thanks for all of your help and suggestions.

You are welcome.

Regards.
Will
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top