/*++ Copyright (c) 1990 Microsoft Corporation Module Name: tmachine.c Abstract: This module tests the machine account creation facilities of SAM. Author: Jim Kelly (JimK) 7-Feb-1994 Environment: User Mode - Win32 Revision History: --*/ /////////////////////////////////////////////////////////////////////////////// // // // Includes // // // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include // prototypes for MIDL user functions #include #include /////////////////////////////////////////////////////////////////////////////// // // // Macros // // // /////////////////////////////////////////////////////////////////////////////// #ifndef SHIFT #define SHIFT(c,v) {c--; v++;} #endif //SHIFT /////////////////////////////////////////////////////////////////////////////// // // // Routines // // // /////////////////////////////////////////////////////////////////////////////// NTSTATUS TSampGetLsaDomainInfo( IN PUNICODE_STRING ServerName, OUT PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo ) /*++ Routine Description: This routine retrieves ACCOUNT domain information from the LSA policy database. Arguments: ServerName - name of machine to get account domain information from. PolicyAccountDomainInfo - Receives a pointer to a POLICY_ACCOUNT_DOMAIN_INFO structure containing the account domain info. Return Value: STATUS_SUCCESS - Succeeded. Other status values that may be returned from: LsaOpenPolicy() LsaQueryInformationPolicy() --*/ { NTSTATUS NtStatus, IgnoreStatus; LSA_HANDLE PolicyHandle; OBJECT_ATTRIBUTES PolicyObjectAttributes; // // Open the policy database // InitializeObjectAttributes( &PolicyObjectAttributes, NULL, // Name 0, // Attributes NULL, // Root NULL ); // Security Descriptor NtStatus = LsaOpenPolicy( ServerName, &PolicyObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle ); if ( NT_SUCCESS(NtStatus) ) { // // Query the account domain information // NtStatus = LsaQueryInformationPolicy( PolicyHandle, PolicyAccountDomainInformation, (PVOID *)PolicyAccountDomainInfo ); IgnoreStatus = LsaClose( PolicyHandle ); ASSERT(NT_SUCCESS(IgnoreStatus)); } return(NtStatus); } NTSTATUS TSampConnectToServer( IN PUNICODE_STRING ServerName, IN ACCESS_MASK DomainAccess, OUT PHANDLE ServerHandle, OUT PHANDLE DomainHandle, OUT PSID *DomainSid ) /*++ Routine Description: Open a handle to the SAM server on the specified server and then open the account domain on that same server. Arguments: ServerName - Name of server to connect to. DomainAccess - accesses needed to the account domain. ServerHandle - Receives a handle to the SAM server on the specified system. DomainHandle - Receives a handle to the account domain. DomainSid - Receives a pointer to the SID of the account domain. Return Value: --*/ { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo; // // get account domain info // NtStatus = TSampGetLsaDomainInfo( ServerName, &AccountDomainInfo); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Failed to get lsa domain info...\n" " Completion status is 0x%lx\n", NtStatus); return(NtStatus); } printf("SAM TEST: Target domain is %wZ\n", &AccountDomainInfo->DomainName); (*DomainSid) = AccountDomainInfo->DomainSid; InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); NtStatus = SamConnect( ServerName, ServerHandle, SAM_SERVER_READ | SAM_SERVER_EXECUTE, &ObjectAttributes ); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Failed to connect...\n" " Completion status is 0x%lx\n", NtStatus); return(NtStatus); } NtStatus = SamOpenDomain( (*ServerHandle), DomainAccess, *DomainSid, DomainHandle ); if (!NT_SUCCESS(NtStatus)) { printf("Failed account domain open\n" " Completion status is 0x%lx\n", NtStatus); return(NtStatus); } return(STATUS_SUCCESS); } BOOLEAN TSampEnableMachinePrivilege( VOID ) /*++ Routine Description: This function enabled the SeMachineAccountPrivilege privilege. Arguments: None. Return Value: TRUE if privilege successfully enabled. FALSE if not successfully enabled. --*/ { NTSTATUS Status; HANDLE Token; LUID SecurityPrivilege; PTOKEN_PRIVILEGES NewState; ULONG ReturnLength; // // Open our own token // Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token ); if (!NT_SUCCESS(Status)) { printf("SAM TEST: Can't open process token to enable Privilege.\n" " Completion status of NtOpenProcessToken() is: 0x%lx\n", Status); return(FALSE); } // // Initialize the adjustment structure // SecurityPrivilege = RtlConvertLongToLargeInteger(SE_MACHINE_ACCOUNT_PRIVILEGE); ASSERT( (sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) < 100); NewState = RtlAllocateHeap( RtlProcessHeap(), 0, 100 ); NewState->PrivilegeCount = 1; NewState->Privileges[0].Luid = SecurityPrivilege; NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // // Set the state of the privilege to ENABLED. // Status = NtAdjustPrivilegesToken( Token, // TokenHandle FALSE, // DisableAllPrivileges NewState, // NewState 0, // BufferLength NULL, // PreviousState (OPTIONAL) &ReturnLength // ReturnLength ); // don't use NT_SUCCESS here because STATUS_NOT_ALL_ASSIGNED is a success status if (Status != STATUS_SUCCESS) { return(FALSE); } // // Clean up some stuff before returning // RtlFreeHeap( RtlProcessHeap(), 0, NewState ); Status = NtClose( Token ); ASSERT(NT_SUCCESS(Status)); return TRUE; } NTSTATUS TSampCreateMachine( IN SAM_HANDLE DomainHandle, IN PUNICODE_STRING AccountName ) /*++ Routine Description: This routine attempts to create a machine account. One of two cases may be tested: 1) DomainHandle is open for DOMAIN_CREATE_USER, or 2) DomainHandle is open for DOMAIN_LOOKUP and the SeMachineAccountPrivilege privilege is enabled. It is the caller's responsibility to establish the correct case criteria before calling. Arguments: DomainHandle - handle to domain to create account in. AccountName - Name of the account to create. Return Value: --*/ { NTSTATUS NtStatus, IgnoreStatus; SAM_HANDLE UserHandle; ACCESS_MASK GrantedAccess; ULONG Rid; NtStatus = SamCreateUser2InDomain( DomainHandle, AccountName, USER_WORKSTATION_TRUST_ACCOUNT, MAXIMUM_ALLOWED, &UserHandle, &GrantedAccess, &Rid); if (NT_SUCCESS(NtStatus)) { IgnoreStatus = SamCloseHandle( UserHandle ); ASSERT(NT_SUCCESS(IgnoreStatus)); printf("SAM TEST: Machine account created.\n" " GrantedAccess: 0x%lx\n" " Rid: %d (0x%lx)\n", GrantedAccess, Rid, Rid); } else { printf("SAM TEST: Machine account creation failed.\n" " Status: 0x%lx\n", NtStatus); } return(NtStatus); } NTSTATUS TSampSetPasswordMachine( IN SAM_HANDLE DomainHandle, IN PUNICODE_STRING AccountName, IN PUNICODE_STRING Password ) /*++ Routine Description: This routine attempts to set the password of a machine account. Arguments: DomainHandle - handle to domain account is in. AccountName - Name of the account to set password. Password - New password. Return Value: --*/ { NTSTATUS NtStatus; SAM_HANDLE UserHandle; PULONG RelativeIds; PSID_NAME_USE Use; USER_SET_PASSWORD_INFORMATION PasswordInfo; PasswordInfo.Password = (*Password); PasswordInfo.PasswordExpired = FALSE; NtStatus = SamLookupNamesInDomain( DomainHandle, 1, AccountName, &RelativeIds, &Use); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Couldn't find account to set password.\n" " Lookup status: 0x%lx\n", NtStatus); return(NtStatus); } NtStatus = SamOpenUser( DomainHandle, USER_FORCE_PASSWORD_CHANGE, RelativeIds[0], &UserHandle); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Couldn't open user account for FORCE_PASSWORD_CHANGE.\n" " Lookup status: 0x%lx\n", NtStatus); return(NtStatus); } NtStatus = SamSetInformationUser( UserHandle, UserSetPasswordInformation, &PasswordInfo ); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Couldn't set password on user account.\n" " Set Info status: 0x%lx\n", NtStatus); return(NtStatus); } return(STATUS_SUCCESS); } NTSTATUS TSampDeleteMachine( IN SAM_HANDLE DomainHandle, IN PUNICODE_STRING AccountName ) /*++ Routine Description: This routine attempts to delete a machine account. Arguments: DomainHandle - handle to domain to delete account from. AccountName - Name of the account to delete. Return Value: --*/ { NTSTATUS NtStatus; SAM_HANDLE UserHandle; PULONG RelativeIds; PSID_NAME_USE Use; NtStatus = SamLookupNamesInDomain( DomainHandle, 1, AccountName, &RelativeIds, &Use); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Couldn't find account to delete.\n" " Lookup status: 0x%lx\n", NtStatus); return(NtStatus); } NtStatus = SamOpenUser( DomainHandle, DELETE, RelativeIds[0], &UserHandle); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Couldn't open user account for delete.\n" " Open status: 0x%lx\n", NtStatus); return(NtStatus); } NtStatus = SamDeleteUser( UserHandle ); if (!NT_SUCCESS(NtStatus)) { printf("SAM TEST: Couldn't delete user account.\n" " DeleteUser status: 0x%lx\n", NtStatus); return(NtStatus); } return(STATUS_SUCCESS); } VOID TSampPrintYesOrNo( IN BOOLEAN b ) { if (b) { printf("Yes\n"); } else { printf("No\n"); } } VOID TSampUsage( VOID ) { printf("\n\n Command format:\n"); printf(" tmachine [/c] [/p] [/d] []\n"); printf("\n"); printf(" Switches\n"); printf(" /c - create account\n"); printf(" /p - set password on account\n"); printf(" /d - delete account\n"); printf("\n"); printf(" if multiple switches are specified, they are attempted in\n"); printf(" the order listed above. An error in any attempt will prevent\n"); printf(" any further attempts.\n"); printf("\n"); return; } VOID main (c,v) int c; char **v; /*++ Routine Description: This is the main entry routine for this test. Arguments: Argv[1] - account name to create or delete Argv[2] - domain controller machine name Argv[3] - 'D' to delete account, otherwise account is created. Return Value: --*/ { NTSTATUS NtStatus, IgnoreStatus; UNICODE_STRING AccountName, ControllerName, Password; WCHAR AccountNameBuffer[80], ControllerNameBuffer[80], PasswordBuffer[80]; ANSI_STRING AnsiString; SAM_HANDLE ServerHandle, ServerHandle2, DomainHandle, DomainHandle2; PSID DomainSid; BOOLEAN Create = FALSE, SetPassword = FALSE, Delete = FALSE; ULONG ArgNum = 0; PCHAR p; CHAR ch; AccountName.Length = 0; ControllerName.Length = 0; Password.Length = 0; // // Command format: // // tmachine [/c] [/p] [/d] [] // // Switches // /c - create account // /p - set password on account // /d - delete account // // if multiple switches are specified, they are attempted in // the order listed above. An error in any attempt will prevent // any further attempts. // SHIFT (c,v); while ((c > 0) && ((ch = *v[0]))) { p = *v; if (ch == '/') { while (*++p != '\0') { if ((*p == 'c') || (*p == 'C')) { Create = TRUE; // printf("Create\n"); } else if ((*p == 'p') || (*p == 'P')) { SetPassword = TRUE; // printf("SetPassword\n"); } else if ((*p == 'd') || (*p == 'D')) { Delete = TRUE; // printf("Delete\n"); } else { TSampUsage(); return; } } } else { switch (ArgNum) { case 0: // // collecting account name // AccountName.Buffer = AccountNameBuffer; AccountName.MaximumLength = sizeof(AccountNameBuffer); RtlInitAnsiString(&AnsiString, (*v)); RtlAnsiStringToUnicodeString(&AccountName, &AnsiString, FALSE); // printf("account: %wZ\n", &AccountName); break; case 1: // // collecting machine name // ControllerName.Buffer = ControllerNameBuffer; ControllerName.MaximumLength = sizeof(ControllerNameBuffer); RtlInitAnsiString(&AnsiString, (*v)); RtlAnsiStringToUnicodeString(&ControllerName, &AnsiString, FALSE); // printf("machine: %wZ\n", &ControllerName); break; case 2: // // collecting password name // Password.Buffer = PasswordBuffer; Password.MaximumLength = sizeof(PasswordBuffer); RtlInitAnsiString(&AnsiString, (*v)); RtlAnsiStringToUnicodeString(&Password, &AnsiString, FALSE); // printf("password: %wZ\n", &Password); break; default: // // collecting garbage. // break; } ArgNum++; } SHIFT(c,v); } printf("parameters:\n"); printf(" Create Account: "); TSampPrintYesOrNo( Create ); printf(" Set Password : "); TSampPrintYesOrNo( SetPassword ); printf(" Delete Account: "); TSampPrintYesOrNo( Delete ); printf(" Account : *%wZ*\n", &AccountName); printf(" Machine : *%wZ*\n", &ControllerName); printf(" Password : *%wZ*\n", &Password); // // Make sure we don't have conflicting parameters // // Rules: // // 1) account name is always required. // 2) password and machine are required if /P was specified. // 3) machine is optional if /P not specified. // // if ( (AccountName.Length == 0) || ( SetPassword && (ControllerName.Length == 0) ) || ( SetPassword && (Password.Length == 0) ) ) { TSampUsage(); return; } // // Open the server and the account domain // NtStatus = TSampConnectToServer(&ControllerName, DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS, &ServerHandle, &DomainHandle, &DomainSid); if (Create) { // // try to create the machine account with privilege. // printf("SAM TEST: Creating machine account with privilege.\n"); TSampEnableMachinePrivilege(); NtStatus = TSampCreateMachine( DomainHandle, &AccountName ); if (NT_SUCCESS(NtStatus)) { printf(" Status: successful\n"); } else { if (NtStatus == STATUS_ACCESS_DENIED) { // // We didn't have the privilege, and didn't have // the domain open so that it would work without // the privilege. // printf(" Couldn't create account with privilege (0x%lx)\n" " Attempting normal creation (without privilege)\n" , NtStatus); NtStatus = TSampConnectToServer(&ControllerName, DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS | DOMAIN_CREATE_USER, &ServerHandle2, &DomainHandle2, &DomainSid); if (!NT_SUCCESS(NtStatus)) { printf(" Can't open domain for CREATE_USER access (0x%lx)\n", NtStatus); } else { NtStatus = TSampCreateMachine( DomainHandle2, &AccountName ); if (NT_SUCCESS(NtStatus)) { printf(" Status: successful\n"); } else { printf(" Failed: 0x%lx\n", NtStatus); } IgnoreStatus = SamCloseHandle( DomainHandle2 ); } } if (!NT_SUCCESS(NtStatus)) { printf(" Status: 0x%lx", NtStatus); return; } } } if (SetPassword) { // // Try to set the password on the account // printf("SAM TEST: Setting password of account ...\n"); NtStatus = TSampSetPasswordMachine( DomainHandle, &AccountName, &Password ); if (NT_SUCCESS(NtStatus)) { printf(" Status: successful\n"); } else { printf(" Status: 0x%lx", NtStatus); return; } } if (Delete) { printf("SAM TEST: Deleting account ...\n"); NtStatus = TSampDeleteMachine( DomainHandle, &AccountName ); if (NT_SUCCESS(NtStatus)) { printf(" Status: successful\n"); } else { printf(" Status: 0x%lx", NtStatus); return; } } return; }