#include #include #include #include #include #include #include #include #include #include // logonmsv.h needs this #include // SSI_SECRET_NAME defined here. #define TRUST_ENUM_PERF_BUF_SIZE sizeof(LSA_TRUST_INFORMATION) * 1000 // process max. 1000 trusted account records at atime !! #define NETLOGON_SECRET_NAME L"NETLOGON$" NTSTATUS OpenAndVerifyLSA( IN OUT PLSA_HANDLE LsaHandle, IN ACCESS_MASK DesiredMask, IN LPWSTR DomainName, OUT PPOLICY_PRIMARY_DOMAIN_INFO * ReturnPrimaryDomainInfo OPTIONAL ); NTSTATUS AddATrustedDomain( IN LSA_HANDLE PolicyHandle, IN PLSA_TRUST_INFORMATION TrustedDomainAccountInfo, IN LPWSTR TrustedAccountSecret ); NTSTATUS DeleteATrustedDomain( IN LSA_HANDLE PolicyHandle, IN PLSA_TRUST_INFORMATION TrustedDomainAccountInfo ); NTSTATUS MakeNetlogonSecretName( IN OUT PUNICODE_STRING SecretName ); VOID FailureMessage( IN char *ProcName, IN NTSTATUS NtStatus ); VOID FailureMessage( IN char *ProcName, IN NTSTATUS NtStatus ) { fprintf( stderr, "NETJOIN: %s failed - Status == %x\n", ProcName, NtStatus ); } int _cdecl main( int argc, char *argv[] ) { NTSTATUS NtStatus; HKEY hKey; WCHAR UnicodeDomainName[ 32 ]; WCHAR UnicodePassword[ 32 ]; DWORD cbUnicodePassword = sizeof( UnicodePassword ); DWORD cbUnicodeDomainName = sizeof( UnicodeDomainName ); DWORD dwType; DWORD rc; ACCESS_MASK DesiredAccess; LSA_HANDLE PolicyHandle = NULL; PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo = NULL; LSA_ENUMERATION_HANDLE TrustEnumContext = 0; PLSA_TRUST_INFORMATION TrustEnumBuffer = NULL; DWORD TrustEnumCount = 0; // // Get computer name as the password to use. // if (!GetComputerNameW( UnicodePassword, &cbUnicodePassword )) { fprintf( stderr, "NETJOIN: Unable to read computer name from registry - %u\n", GetLastError() ); exit( 1 ); } if ((rc = RegOpenKeyW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters", &hKey ) ) || (rc = RegQueryValueExW( hKey, L"Domain", NULL, &dwType, (LPBYTE)UnicodeDomainName, &cbUnicodeDomainName ) ) ) { fprintf( stderr, "NETJOIN: Unable to read domain name from registry - %u\n", rc ); exit( 1 ); } DesiredAccess = POLICY_VIEW_LOCAL_INFORMATION | // needed to read domain info and trusted account info POLICY_TRUST_ADMIN | // needed to add and delete trust accounts POLICY_CREATE_SECRET ; // needed to add and delete secret NtStatus = OpenAndVerifyLSA( &PolicyHandle, DesiredAccess, UnicodeDomainName, &PrimaryDomainInfo ); if (!NT_SUCCESS( NtStatus )) { fprintf( stderr, "NETJOIN: Unable to read domain name from registry - %u\n", GetLastError() ); exit( 1 ); } // // now the domain names match and the PrimaryDomainInfo has the SID of the // domain, we can add trust entry and secret in LSA for this domain. // Before adding this, clean up old entries. // for(;;) { DWORD i; PLSA_TRUST_INFORMATION TrustedDomainAccount; NtStatus = LsaEnumerateTrustedDomains( PolicyHandle, &TrustEnumContext, (PVOID *)&TrustEnumBuffer, TRUST_ENUM_PERF_BUF_SIZE, &TrustEnumCount ); if (NtStatus == STATUS_NO_MORE_ENTRIES) { // // we are done // break; } if (NtStatus != STATUS_MORE_ENTRIES) { if (!NT_SUCCESS( NtStatus )) { FailureMessage( "LsaEnumerateTrustedDomains", NtStatus ); goto Cleanup; } } // // delete trusted accounts and the corresponding secrets // for( i = 0, TrustedDomainAccount = TrustEnumBuffer; i < TrustEnumCount; TrustedDomainAccount++, i++ ) { NtStatus = DeleteATrustedDomain( PolicyHandle, TrustedDomainAccount ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "DeleteATrustedDomain", NtStatus ); goto Cleanup; } } if (NtStatus != STATUS_MORE_ENTRIES) { // // we have cleaned up all old entries. // break; } // // free up used enum buffer // if (TrustEnumBuffer != NULL) { LsaFreeMemory( TrustEnumBuffer ); TrustEnumBuffer = NULL; } } // // add a new trust for the specified domain // NtStatus = AddATrustedDomain( PolicyHandle, (PLSA_TRUST_INFORMATION) PrimaryDomainInfo, UnicodePassword ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "AddATrustedDomain", NtStatus ); } else { // // Give LSA a chance to do its thing. // Sleep( 10000 ); } Cleanup: if (PrimaryDomainInfo != NULL) { LsaFreeMemory( PrimaryDomainInfo ); } if (TrustEnumBuffer != NULL) { LsaFreeMemory( TrustEnumBuffer ); } if (PolicyHandle != NULL) { LsaClose( PolicyHandle ); } if (NT_SUCCESS( NtStatus )) { fprintf( stderr, "NETJOIN: Computer == '%ws' joined the '%ws' domain.\n", UnicodePassword, UnicodeDomainName ); return 0; } else { fprintf( stderr, "NETJOIN: Computer == '%ws' unable to join the '%ws' domain - Status == %08x\n", UnicodePassword, UnicodeDomainName, NtStatus ); return 1; } } NTSTATUS OpenAndVerifyLSA( IN OUT PLSA_HANDLE PolicyHandle, IN ACCESS_MASK DesiredAccess, IN LPWSTR DomainName, OUT PPOLICY_PRIMARY_DOMAIN_INFO * ReturnPrimaryDomainInfo OPTIONAL ) /*++ Routine Description: This function opens the local LSA policy and verifies that the LSA is configured for the workstation. Optionally it returns the primary domain information that is read form the LSA. Arguments: LsaHandle - Pointer to location where the LSA handle will be retured. DesiredMask - Access mask used to open the LSA. DomainName - Name of the trusted domain. ReturnPrimaryDomainInfo - Primary domain info is returned here. Return Value: Error code of the operation. --*/ { NTSTATUS NtStatus; PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo = NULL; OBJECT_ATTRIBUTES ObjectAttributes; DWORD PrimaryDomainNameLength; LPWSTR PrimaryDomainName = NULL; // // open LSA *PolicyHandle = NULL; InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL ); NtStatus = LsaOpenPolicy( NULL, &ObjectAttributes, DesiredAccess, PolicyHandle ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "OpenAndVerifyLSA: LsaOpenPolicy", NtStatus ); return NtStatus; } // // now read primary domain info from LSA. // NtStatus = LsaQueryInformationPolicy( *PolicyHandle, PolicyPrimaryDomainInformation, (PVOID *) &PrimaryDomainInfo ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "OpenAndVerifyLSA: LsaQueryInformationPolicy", NtStatus ); return NtStatus; } // // compare domain names // PrimaryDomainNameLength = PrimaryDomainInfo->Name.Length + sizeof( WCHAR ); PrimaryDomainName = malloc( PrimaryDomainNameLength ); if (PrimaryDomainName == NULL) { NtStatus = STATUS_NO_MEMORY; FailureMessage( "OpenAndVerifyLSA: malloc", NtStatus ); goto Cleanup; } RtlMoveMemory( PrimaryDomainName, PrimaryDomainInfo->Name.Buffer, PrimaryDomainInfo->Name.Length ); PrimaryDomainName[ PrimaryDomainInfo->Name.Length / sizeof(WCHAR) ] = UNICODE_NULL; if (_wcsicmp( DomainName, PrimaryDomainName )) { // // domain names don't match // NtStatus = STATUS_OBJECT_NAME_NOT_FOUND; FailureMessage( "OpenAndVerifyLSA: wcsicmp", NtStatus ); goto Cleanup; } NtStatus = STATUS_SUCCESS; Cleanup: if (PrimaryDomainName != NULL) { free( PrimaryDomainName ); } if (PrimaryDomainInfo != NULL) { if (ARGUMENT_PRESENT( ReturnPrimaryDomainInfo ) ) { if (NT_SUCCESS( NtStatus )) { *ReturnPrimaryDomainInfo = PrimaryDomainInfo; } else { LsaFreeMemory( PrimaryDomainInfo ); *ReturnPrimaryDomainInfo = NULL; } } else { LsaFreeMemory( PrimaryDomainInfo ); } } if (!NT_SUCCESS( NtStatus ) && *PolicyHandle != NULL) { // // close LSA if an error occurred. // LsaClose( *PolicyHandle ); *PolicyHandle = NULL; } return NtStatus; } #if 0 NET_API_STATUS NET_API_FUNCTION I_NetGetDCList( IN LPWSTR ServerName OPTIONAL, IN LPWSTR TrustedDomainName, OUT PULONG DCCount, OUT PUNICODE_STRING * DCNames ); #endif NTSTATUS AddATrustedDomain( IN LSA_HANDLE PolicyHandle, IN PLSA_TRUST_INFORMATION TrustedDomainAccountInfo, IN LPWSTR TrustedAccountSecret ) /*++ Routine Description: This function adds trusted domain account and a secret for the corresponding account in LSA. This function does not do any check before adding this account in LSA. Arguments: PolicyHandle - LSA policy handle TrustedDomainAccountInfo - Pointer to the LSA_TRUST_INFORMATION structure. TrustedAccountSecret - Pointer to the secret for the trusted domain account. Return Value: Error code of the operation. --*/ { NTSTATUS NtStatus; LSA_HANDLE TrustedDomainHandle = NULL; DWORD DCCount; PUNICODE_STRING DCNames = NULL; TRUSTED_CONTROLLERS_INFO TrustedControllersInfo; UNICODE_STRING SecretName = {0, 0, NULL}; LSA_HANDLE SecretHandle = NULL; UNICODE_STRING CurrentSecretValue; DWORD UnicodeDomainNameLength; LPWSTR UnicodeDomainName = NULL; NtStatus = LsaCreateTrustedDomain( PolicyHandle, TrustedDomainAccountInfo, TRUSTED_SET_CONTROLLERS | DELETE, &TrustedDomainHandle ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "AddATrustedDomain: LsaCreateTrustedDomain", NtStatus ); return NtStatus; } // // Determine the DC List. This list will be stored in trusted domain // account. // // Specify the server name NULL, the domain name is the primary domain // of this workstation and so it must be listening DC announcements. // UnicodeDomainNameLength = TrustedDomainAccountInfo->Name.Length + sizeof(WCHAR); UnicodeDomainName = malloc( UnicodeDomainNameLength ); if (UnicodeDomainName == NULL) { NtStatus = STATUS_NO_MEMORY; FailureMessage( "AddATrustedDomain: malloc", NtStatus ); goto Cleanup; } RtlMoveMemory( UnicodeDomainName, TrustedDomainAccountInfo->Name.Buffer, TrustedDomainAccountInfo->Name.Length ); UnicodeDomainName[ (UnicodeDomainNameLength / sizeof(WCHAR)) - 1 ] = '\0'; #if 0 if (I_NetGetDCList( NULL, UnicodeDomainName, &DCCount, &DCNames ) ) { // // if unable to find the DC list for the specified domain, set // the Dc list to null and proceed. // DCCount = 0; DCNames = NULL; } #else DCCount = 0; DCNames = NULL; #endif TrustedControllersInfo.Entries = DCCount; TrustedControllersInfo.Names = DCNames; // // set controller info in trusted domain object. // NtStatus = LsaSetInformationTrustedDomain( TrustedDomainHandle, TrustedControllersInformation, &TrustedControllersInfo ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "AddATrustedDomain: LsaSetInformationTrustedDomain", NtStatus ); goto Cleanup; } // // Add a secret for this trusted account // MakeNetlogonSecretName( &SecretName ); NtStatus = LsaCreateSecret( PolicyHandle, &SecretName, SECRET_SET_VALUE, &SecretHandle ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "AddATrustedDomain: LsaCreateSecret", NtStatus ); goto Cleanup; } RtlInitUnicodeString( &CurrentSecretValue, TrustedAccountSecret ); NtStatus = LsaSetSecret( SecretHandle, &CurrentSecretValue, &CurrentSecretValue ); Cleanup: if (DCNames != NULL) { free( DCNames ); } if (UnicodeDomainName != NULL) { free( UnicodeDomainName ); } if (SecretHandle != NULL) { if (!NT_SUCCESS( NtStatus)) { // // since we are not successful completely to create the trusted // account, delete it. // LsaDelete( SecretHandle ); } else { LsaClose( SecretHandle ); } } if (TrustedDomainHandle != NULL) { if (!NT_SUCCESS( NtStatus)) { // // since we are not successful completely to create the trusted // account, delete it. // LsaDelete( TrustedDomainHandle ); } else { LsaClose( TrustedDomainHandle ); } } return NtStatus; } NTSTATUS DeleteATrustedDomain( IN LSA_HANDLE PolicyHandle, IN PLSA_TRUST_INFORMATION TrustedDomainAccountInfo ) /*++ Routine Description: This function deletes a trusted domain account and the corresponding secret from LSA. This function however does not check any conditions before deleting this account. Arguments: PolicyHandle - LSA policy handle TurstedDoaminAccountInfo - Pointer to the LSA_TRUST_INFORMATION structure. Return Value: Error code of the operation. --*/ { NTSTATUS NtStatus; LSA_HANDLE TrustedDomainHandle = NULL; LSA_HANDLE SecretHandle = NULL; UNICODE_STRING SecretName = { 0, 0, NULL }; MakeNetlogonSecretName( &SecretName ); // // open trusted domain account secret // NtStatus = LsaOpenSecret( PolicyHandle, &SecretName, DELETE, &SecretHandle ); if (NtStatus != STATUS_OBJECT_NAME_NOT_FOUND) { if (!NT_SUCCESS( NtStatus )) { FailureMessage( "DeleteATrustedDomain: LsaOpenSecret", NtStatus ); goto Cleanup; } LsaDelete( SecretHandle ); } // // open trusted domain account // NtStatus = LsaOpenTrustedDomain( PolicyHandle, TrustedDomainAccountInfo->Sid, DELETE, &TrustedDomainHandle ); if (!NT_SUCCESS( NtStatus )) { FailureMessage( "DeleteATrustedDomain: LsaOpenTrustedDomain", NtStatus ); goto Cleanup; } LsaDelete( TrustedDomainHandle ); Cleanup: return NtStatus; } NTSTATUS MakeNetlogonSecretName( IN OUT PUNICODE_STRING SecretName ) /*++ Routine Description: This function makes a secret name that is used for the netlogon. Arguments: SecretName - Pointer to a unicode structure in which the netlogon secret name will be returned. Return Value: NERR_Success; --*/ { SecretName->Length = wcslen(SSI_SECRET_NAME) * sizeof(WCHAR); SecretName->MaximumLength = SecretName->Length + 2; SecretName->Buffer = SSI_SECRET_NAME; return STATUS_SUCCESS; }