home *** CD-ROM | disk | FTP | other *** search
- /*++
-
- Copyright 1996 - 1997 Microsoft Corporation
-
- Module Name:
-
- domtrust.c
-
- Abstract:
-
- This sample illustrates how to manage Windows NT trusted domains at the
- domain controller level.
-
- The first command line argument indicates the name of the new or existing
- trusted domain to create or modify.
-
- The second command line argument indicates the new password for the
- trusted domain specified in the first argument.
-
- The optional third argument indicates the domain name that is the target
- of the trusted domain update operation. If this argument is not specified,
- the update will occur on the local domain. Note that this sample will not
- allow a trusted domain update to occur on a non-domain controller.
-
- This sample works correctly compiled ANSI or Unicode. Note that LAN
- Manager NetXxx API are Unicode only, and Windows NT LSA API are Unicode
- only.
-
- Author:
-
- Scott Field (sfield) 05-Feb-96
-
- --*/
-
- #ifndef UNICODE
- #define UNICODE
- #define _UNICODE
- #endif
-
- #include <windows.h>
- #include <lm.h> // for NetXxx API
-
- #include "ntsecapi.h" // \mstools\samples\win32\winnt\security\include\ntsecapi.h
-
- #include <stdio.h>
-
- void
- InitLsaString(
- PLSA_UNICODE_STRING LsaString,
- LPWSTR String
- );
-
- NTSTATUS
- OpenPolicy(
- LPWSTR ServerName,
- DWORD DesiredAccess,
- PLSA_HANDLE PolicyHandle
- );
-
- BOOL
- GetDomainSid(
- LPWSTR DomainName, // domain name to acquire Sid of
- PSID *pDomainSid // points to allocated Sid on success
- );
-
- NTSTATUS
- SetTrustedDomainInfo(
- LSA_HANDLE PolicyHandle,
- PSID DomainSid, // Sid of domain to manipulate
- LPWSTR TrustedDomainName, // trusted domain name to add/update
- LPWSTR Password // new trust password for trusted domain
- );
-
- BOOL
- VerifyTrustRelationship(
- LPWSTR TargetDomainName, // domain name to verify trust at
- LPWSTR TrustAccountToVerify, // trusted domain name to verify
- LPWSTR Password, // password associated with trust
- LPBOOL bTrustVerified // indicates if trust was verified
- );
-
- void
- DisplayNtStatus(
- LPSTR szAPI, // ANSI string containing API name
- NTSTATUS Status
- );
-
- void
- DisplayError(
- LPSTR szAPI, // pointer to failed API name
- DWORD dwLastError
- );
-
- #define RTN_OK 0
- #define RTN_USAGE 1
- #define RTN_ERROR 13
-
- //
- // if you have the DDK, include Ntstatus.h
- //
- #ifndef STATUS_SUCCESS
- #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
- #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
- #define STATUS_INVALID_SID ((NTSTATUS)0xC0000078L)
- #endif
-
- //
- // Unicode entry point and argv
- //
-
- int
- __cdecl
- wmain(
- int argc,
- wchar_t *argv[]
- )
- {
- BOOL bAllocTargetDomain; // did you allocate memory for target?
- LSA_HANDLE PolicyHandle;
- LPWSTR TargetDomainName; // target domain of policy update
- LPWSTR DomainController; // computer name where policy update
- PSID DomainSid; // allocated Sid representing domain to trust
- LPWSTR TrustedDomainName;
- LPWSTR Password;
- BOOL bTrustVerified;
- NET_API_STATUS nas;
- NTSTATUS Status;
-
- if(argc < 3) {
- fprintf(stderr,
- "Usage: %ls <NewTrustedDomain> <Password> [TargetDomain]\n",
- argv[0]);
- return RTN_USAGE;
- }
-
- TrustedDomainName = argv[1]; // existing or new TrustedDomain
- Password = argv[2]; // new password for TrustedDomain
-
- //
- // if a TargetDomain was specified, point to that.
- // if not, set TargetDomain to the current domain if a DC.
- //
- if(argc == 4 && *argv[3] != L'\0') {
- TargetDomainName = argv[3]; // domain to update trust at
- bAllocTargetDomain = FALSE; // no memory allocated
- }
- else {
- PSERVER_INFO_101 si101;
- DWORD Type;
- PUSER_MODALS_INFO_2 umi2;
-
- //
- // ensure that the local computer is a DC. This operation is only
- // appropriate against a domain controller.
- //
- nas = NetServerGetInfo(NULL, 101, (LPBYTE *)&si101);
- if(nas != NERR_Success) {
- DisplayError("NetServerGetInfo", nas);
- return RTN_ERROR;
- }
-
- Type = si101->sv101_type;
- NetApiBufferFree(si101);
-
- if( !(Type & SV_TYPE_DOMAIN_CTRL) &&
- !(Type & SV_TYPE_DOMAIN_BAKCTRL) ) {
- printf("Error: Specify a TargetDomain; this operation"
- " is only valid against a domain.\n");
- return RTN_ERROR;
- }
-
- //
- // obtain the local computer's domain name
- //
- nas = NetUserModalsGet(NULL, 2, (LPBYTE *)&umi2);
-
- if(nas != NERR_Success) {
- DisplayError("NetUserModalsGet", nas);
- return RTN_ERROR;
- }
-
- //
- // copy the domain name to new storage
- //
- TargetDomainName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
- (lstrlenW(umi2->usrmod2_domain_name) + 1) * sizeof(WCHAR));
-
- if(TargetDomainName != NULL) {
- lstrcpyW(TargetDomainName, umi2->usrmod2_domain_name);
- bAllocTargetDomain = TRUE; // we allocated memory
- }
-
- NetApiBufferFree(umi2); // free memory allocated by NetXxx
-
- //
- // if an error occurred allocating memory, exit
- //
- if(TargetDomainName == NULL) {
- DisplayError("HeapAlloc", 0);
- return RTN_ERROR;
- }
- }
-
- //
- // do not allow a Domain to trust itself
- //
- if(lstrcmpiW(TargetDomainName, TrustedDomainName) == 0) {
- fprintf(stderr,"Error: Domain %ls cannot trust itself.\n",
- TargetDomainName);
-
- return RTN_ERROR;
- }
-
- //
- // ensure Password and TrustedDomainName are the correct length
- //
- if(lstrlenW(Password) > LM20_PWLEN)
- Password[LM20_PWLEN] = L'\0'; // truncate
-
- if(lstrlenW(TrustedDomainName) > MAX_COMPUTERNAME_LENGTH)
- TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
-
- //
- // obtain the primary DC computer name from the specified
- // TargetDomainName
- //
- nas = NetGetDCName(
- NULL, TargetDomainName, (LPBYTE *)&DomainController);
-
- if(nas != NERR_Success) {
- DisplayError("NetGetDCName", nas);
- return RTN_ERROR;
- }
-
- //
- // verify the trust relationship
- //
- if(!VerifyTrustRelationship(
- TrustedDomainName,
- TargetDomainName, // trust account to verify
- Password,
- &bTrustVerified
- )) {
- //
- // an error occurred during trust relationship verification
- //
- DisplayError("VerifyTrustRelationship", GetLastError());
- }
- else {
- //
- // inform the user if the trust was not verified
- //
- if(!bTrustVerified) {
- DWORD dwTrustVerifyFailReason = GetLastError();
-
- //
- // You could exit here, but this is optional. Reasons for
- // trust verification failure could be non-existent
- // "permitted to trust" on the verified domain (rc=1317),
- // wrong password (rc=1326), etc
- //
- printf("Warning: ");
-
- switch(dwTrustVerifyFailReason) {
- case ERROR_NO_SUCH_USER:
- printf("%ls not permitted-to-trust on %ls\n",
- TargetDomainName, TrustedDomainName);
- break;
-
- case ERROR_LOGON_FAILURE:
- printf("Trust %ls has incorrect password on %ls\n",
- TrustedDomainName, TargetDomainName);
- break;
-
- default:
- DisplayError("Trust was not verified. Non-fatal",
- dwTrustVerifyFailReason);
- } // switch
- }
- }
-
- //
- // fetch the DomainSid of the domain to trust
- //
- if(!GetDomainSid(TrustedDomainName, &DomainSid)) {
- DisplayError("GetDomainSid", GetLastError());
- return RTN_ERROR;
- }
-
- //
- // open the policy on the target domain
- //
- Status = OpenPolicy(
- DomainController,
- POLICY_CREATE_SECRET | // for password set operation
- POLICY_TRUST_ADMIN, // for trust creation
- &PolicyHandle
- );
-
- if(Status != STATUS_SUCCESS) {
- DisplayNtStatus("OpenPolicy", Status);
- return RTN_ERROR;
- }
-
- //
- // Update TrustedDomainInfo to reflect the specified trust.
- //
- Status = SetTrustedDomainInfo(
- PolicyHandle,
- DomainSid,
- TrustedDomainName,
- Password
- );
-
- if(Status != STATUS_SUCCESS) {
- DisplayNtStatus("SetTrustedDomainInfo", Status);
- return RTN_ERROR;
- }
-
- //
- // if you allocated memory for TargetDomainName, free it
- //
- if(bAllocTargetDomain)
- HeapFree(GetProcessHeap(), 0, TargetDomainName);
-
- //
- // free the Sid which was allocated for the TrustedDomain Sid
- //
- FreeSid(DomainSid);
-
- //
- // close the policy handle
- //
- LsaClose(PolicyHandle);
-
- if(DomainController != NULL)
- NetApiBufferFree(DomainController);
-
- return RTN_OK;
- }
-
- void
- InitLsaString(
- PLSA_UNICODE_STRING LsaString,
- LPWSTR String
- )
- {
- DWORD StringLength;
-
- if(String == NULL) {
- LsaString->Buffer = NULL;
- LsaString->Length = 0;
- LsaString->MaximumLength = 0;
-
- return;
- }
-
- StringLength = lstrlenW(String);
- LsaString->Buffer = String;
- LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
- LsaString->MaximumLength = (USHORT) (StringLength + 1) *
- sizeof(WCHAR);
- }
-
- NTSTATUS
- OpenPolicy(
- LPWSTR ServerName,
- DWORD DesiredAccess,
- PLSA_HANDLE PolicyHandle
- )
- {
- LSA_OBJECT_ATTRIBUTES ObjectAttributes;
- LSA_UNICODE_STRING ServerString;
- PLSA_UNICODE_STRING Server;
-
- //
- // Always initialize the object attributes to all zeroes
- //
- ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
-
- if(ServerName != NULL) {
- //
- // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
- //
- InitLsaString(&ServerString, ServerName);
-
- Server = &ServerString;
- } else {
- Server = NULL;
- }
-
- //
- // Attempt to open the policy
- //
- return LsaOpenPolicy(
- Server,
- &ObjectAttributes,
- DesiredAccess,
- PolicyHandle
- );
- }
-
- /*++
- This function retrieves the Sid representing the specified DomainName.
-
- If the function succeeds, the return value is TRUE and pDomainSid will
- point to the Sid representing the specified DomainName. The caller
- should free the memory associated with this Sid with the Win32 API
- FreeSid() when the Sid is no longer needed.
-
- If the function fails, the return value is FALSE, and pDomainSid will
- be set to NULL.
-
- --*/
-
- BOOL
- GetDomainSid(
- LPWSTR DomainName, // domain name to acquire Sid of
- PSID *pDomainSid // points to allocated Sid on success
- )
- {
- NET_API_STATUS nas;
- LPWSTR DomainController;
- PUSER_MODALS_INFO_2 umi2 = NULL;
- DWORD dwSidSize;
- BOOL bSuccess = FALSE; // assume this function will fail
-
- *pDomainSid = NULL; // invalidate pointer
-
- //
- // obtain the PDC computer name of the supplied domain name
- //
- nas = NetGetDCName(NULL, DomainName, (LPBYTE *)&DomainController);
- if(nas != NERR_Success) {
- SetLastError(nas);
- return FALSE;
- }
-
- __try {
-
- //
- // obtain the domain Sid from the PDC
- // NOTE: this may fail if the credentials of the caller are not
- // recognized or valid on the target computer. In this case, it
- // is necessary to first establish a connection via NetUseAdd()
- // at info-level 2 to the remote IPC$, specifying EMPTY
- // credentials.
- //
- nas = NetUserModalsGet(DomainController, 2, (LPBYTE *)&umi2);
- if(nas != NERR_Success) __leave;
-
- //
- // if the Sid is valid, obtain the size of the Sid
- //
- if(!IsValidSid(umi2->usrmod2_domain_id)) __leave;
- dwSidSize = GetLengthSid(umi2->usrmod2_domain_id);
-
- //
- // allocate storage and copy the Sid
- //
- *pDomainSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwSidSize);
- if(*pDomainSid == NULL) __leave;
-
- if(!CopySid(dwSidSize, *pDomainSid, umi2->usrmod2_domain_id))
- __leave;
-
- bSuccess = TRUE; // indicate success
-
- } // try
- __finally {
-
- //
- // free allocated memory
- //
- NetApiBufferFree(DomainController);
-
- if(umi2 != NULL)
- NetApiBufferFree(umi2);
-
- if(!bSuccess) {
- //
- // if the function failed, free memory and indicate result code
- //
-
- if(*pDomainSid != NULL) {
- FreeSid(*pDomainSid);
- *pDomainSid = NULL;
- }
-
- if(nas != NERR_Success) {
- SetLastError(nas);
- }
- }
-
- }
- return bSuccess;
- }
-
- /*++
- This function manipulates the trust associated with the supplied
- DomainSid.
-
- If the domain trust does not exist, it is created with the
- specified password. In this case, the supplied PolicyHandle must
- have been opened with POLICY_TRUST_ADMIN and POLICY_CREATE_SECRET
- access to the policy object.
-
- If the domain trust exists, the password is updated with the
- password specified by the Password parameter. The trust OldPassword
- is set to the previous password.
-
- If DomainName and Password are NULL, the trusted domain specified by
- DomainSid is deleted.
-
- --*/
-
- NTSTATUS
- SetTrustedDomainInfo(
- LSA_HANDLE PolicyHandle,
- PSID DomainSid, // Sid of domain to manipulate
- LPWSTR TrustedDomainName, // trusted domain name to add/update
- LPWSTR Password // new trust password for trusted domain
- )
- {
- PTRUSTED_PASSWORD_INFO tpi;
- // PTRUSTED_DOMAIN_NAME_INFO tdni;
- LSA_UNICODE_STRING LsaPassword;
- TRUSTED_PASSWORD_INFO Newtpi;
- BOOL bTrustExists = TRUE; // assume trust exists
- NTSTATUS Status;
-
- //
- // Make sure you were passed a valid Sid
- //
- if(DomainSid == NULL || !IsValidSid(DomainSid))
- return STATUS_INVALID_SID;
-
- //
- // if TristedDomainName and Password is NULL (or emptry strings),
- // delete the specified trust
- //
- if( (TrustedDomainName == NULL || *TrustedDomainName == L'\0') &&
- (Password == NULL || *Password == L'\0') ) {
- return LsaDeleteTrustedDomain(PolicyHandle, DomainSid);
- }
-
- //
- // query the current password, which is used to update the
- // OldPassword. If the trust doesn't exist, indicate this so that the
- // trust will be created.
- //
- Status = LsaQueryTrustedDomainInfo(
- PolicyHandle,
- DomainSid,
- TrustedPasswordInformation,
- &tpi);
-
- if(Status != STATUS_SUCCESS) {
- if(Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- bTrustExists = FALSE; // indicate trust does not exist
- }
- else {
- DisplayNtStatus("LsaQueryTrustedDomainInfo", Status);
- return Status;
- }
- }
-
- InitLsaString(&LsaPassword, Password);
-
- //
- // set the new password to the supplied password
- //
- Newtpi.Password = LsaPassword;
-
- if(bTrustExists) {
- //
- // if the trust already existed, set OldPassword to the
- // current password...
- //
- Newtpi.OldPassword = tpi->Password;
- }
- else {
- LSA_UNICODE_STRING LsaDomainName;
- DWORD cchDomainName; // number of chars in TrustedDomainName
-
- //
- // if the trust did not exist, set OldPassword to the
- // supplied password...
- //
- Newtpi.OldPassword = LsaPassword;
-
- InitLsaString(&LsaDomainName, TrustedDomainName);
-
- //
- // ...convert TrustedDomainName to uppercase...
- //
- cchDomainName = LsaDomainName.Length / sizeof(WCHAR);
- while(cchDomainName--) {
- LsaDomainName.Buffer[cchDomainName] =
- towupper(LsaDomainName.Buffer[cchDomainName]);
- }
-
- //
- // ...create the trusted domain object
- //
- Status = LsaSetTrustedDomainInformation(
- PolicyHandle,
- DomainSid,
- TrustedDomainNameInformation,
- &LsaDomainName
- );
-
- if(Status != STATUS_SUCCESS) return Status;
- }
-
- //
- // update TrustedPasswordInformation for the specified trust
- //
- Status = LsaSetTrustedDomainInformation(
- PolicyHandle,
- DomainSid,
- TrustedPasswordInformation,
- &Newtpi
- );
-
- //
- // if a trust already existed, free the memory associated with
- // the retrieved information
- //
- if(bTrustExists) LsaFreeMemory(tpi);
-
- return Status;
- }
-
- /*++
-
- This function verifies that the "permitted to trust" side of the
- trust relationship has been established and that the specified
- password matches the "permitted to trust" password.
-
- If the function fails, the return value is FALSE, and bTrustVerified
- is undefined.
-
- If the function succeeds, the trust verification succeeded, and
- bTrustVerified is set to the result of the verification.
-
- If bTrustVerified is TRUE, the trust relationship is verfied.
-
- If bTrustVerified is FALSE, the trust was not verified. Call
- GetLastError() to get extended error information.
-
- ERROR_NO_SUCH_USER indicates that the "permitted to trust"
- UF_INTERDOMAIN_TRUST_ACCOUNT does not exist on the specified domain.
-
- ERROR_LOGON_FAILURE indicates that the supplied password does
- not match the password in the "permitted to trust"
- UF_INTERDOMAIN_TRUST_ACCOUNT.
-
- NOTE: if a connection exists to the domain controller of the
- TargetDomainName prior to calling this function, this function will
- fail due to a credential conflict (WinError == 1219).
-
- --*/
-
- BOOL
- VerifyTrustRelationship(
- LPWSTR TargetDomainName, // domain name to verify trust at
- LPWSTR TrustAccountToVerify, // trusted domain name to verify
- LPWSTR Password, // password associated with trust
- LPBOOL bTrustVerified // indicates if trust was verified
- )
- {
- NET_API_STATUS nas;
- USE_INFO_2 ui2;
- LPWSTR DomainController;
- LPWSTR szIpc = L"\\IPC$";
- LPWSTR RemoteResource = NULL;
- LPWSTR UserName = NULL;
- BOOL bSuccess = FALSE; // assume this function will fail
-
- //
- // fetch the computer name of the domain you try to connect to
- //
- nas = NetGetDCName(
- NULL, TargetDomainName, (LPBYTE *)&DomainController);
- if(nas != NERR_Success) {
- SetLastError(nas);
- return FALSE;
- }
-
- __try {
-
- //
- // build the \\<DCName>\ipc$ as the remote resource
- //
- RemoteResource = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
- (lstrlenW(DomainController) + lstrlenW(szIpc) + 1 ) *
- sizeof(WCHAR)
- );
-
- if(RemoteResource == NULL) __leave;
-
- if(lstrcpyW(RemoteResource, DomainController) == NULL) __leave;
- if(lstrcatW(RemoteResource, szIpc) == NULL) __leave;
-
- //
- // build the user name as <TrustAccountToVerify>$
- //
- UserName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
- (lstrlenW(TrustAccountToVerify) + 1 + 1) * sizeof(WCHAR)
- );
-
- if(UserName == NULL) __leave;
-
- if(lstrcpyW(UserName, TrustAccountToVerify) == NULL) __leave;
- if(lstrcatW(UserName, L"$") == NULL) __leave;
-
- ZeroMemory(&ui2, sizeof(ui2));
-
- ui2.ui2_local = NULL;
- ui2.ui2_remote = RemoteResource;
- ui2.ui2_asg_type = USE_IPC;
- ui2.ui2_password = Password;
- ui2.ui2_username = UserName;
- ui2.ui2_domainname = TargetDomainName;
-
- //
- // Attempt to establish a connection to the target domain.
- // If the result is ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT,
- // this illustrates the supplied Password matches an existing
- // trust account because the credentials were validated but
- // a logon is not allowed for this account type.
- //
- nas = NetUseAdd(NULL, 2, (LPBYTE)&ui2, NULL);
-
- if(nas == ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT) {
- *bTrustVerified = TRUE; // indicate trust verified
- }
- else {
- *bTrustVerified = FALSE; // indicate trust not verified
-
- //
- // if the connection succeeded, the UF_INTERDOMAIN_TRUST_ACCOUNT
- // does not exist, because the supplied credentials aren't
- // recognized by the remote (default credentials are applied).
- // Delete the connection and indicate the "permitted to trust"
- // account is non-existent.
- //
- if(nas == NERR_Success) {
- NetUseDel(NULL, RemoteResource, 0);
- nas = ERROR_NO_SUCH_USER;
- }
- }
-
- bSuccess = TRUE; // indicate this function succeeded
-
- } // try
- __finally {
-
- //
- // free allocated memory
- //
- NetApiBufferFree(DomainController);
-
- if(RemoteResource != NULL) {
- HeapFree(GetProcessHeap(), 0, RemoteResource);
- }
-
- if(UserName != NULL) {
- HeapFree(GetProcessHeap(), 0, UserName);
- }
-
- //
- // if you succeeded, but trust could not be verified, indicate why
- //
- if(bSuccess == TRUE && *bTrustVerified == FALSE) {
- SetLastError(nas);
- }
-
- }
-
- return bSuccess;
- }
-
- void
- DisplayNtStatus(
- LPSTR szAPI, // ANSI string containing API name
- NTSTATUS Status
- )
- {
- //
- // convert the NTSTATUS to Winerror and display the result
- //
- DisplayError(szAPI, LsaNtStatusToWinError(Status));
- }
-
-
- void
- DisplayError(
- LPSTR szAPI, // pointer to failed API name
- DWORD dwLastError
- )
- {
- HMODULE hModule = NULL;
- LPSTR MessageBuffer;
- DWORD dwBufferLength;
-
- fprintf(stderr,"%s error! (rc=%lu)\n", szAPI, dwLastError);
-
- if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
- hModule = LoadLibraryEx(
- TEXT("netmsg.dll"),
- NULL,
- LOAD_LIBRARY_AS_DATAFILE
- );
- }
-
- if(dwBufferLength=FormatMessageA(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_IGNORE_INSERTS |
- FORMAT_MESSAGE_FROM_SYSTEM |
- ((hModule != NULL) ? FORMAT_MESSAGE_FROM_HMODULE : 0),
- hModule, // module to get message from
- dwLastError,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
- (LPSTR) &MessageBuffer,
- 0,
- NULL
- ))
- {
- DWORD dwBytesWritten;
-
- //
- // Output message string on stderr
- //
- WriteFile(
- GetStdHandle(STD_ERROR_HANDLE),
- MessageBuffer,
- dwBufferLength,
- &dwBytesWritten,
- NULL
- );
-
- //
- // free the buffer allocated by the system
- //
- LocalFree(MessageBuffer);
- }
-
- if(hModule != NULL)
- FreeLibrary(hModule);
- }
-
-