SUBAUTH.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:37k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*++
  2. Copyright (c) 1987-1996  Microsoft Corporation
  3. Module Name:
  4.     subauth.c
  5. Abstract:
  6.     Sample SubAuthentication Package.
  7. Author:
  8.     Cliff Van Dyke (cliffv) 23-May-1994
  9. Revisions:
  10.     Andy Herron (andyhe)    21-Jun-1994  Added code to read domain/user info
  11. Environment:
  12.     User mode only.
  13.     Contains NT-specific code.
  14.     Requires ANSI C extensions: slash-slash comments, long external names.
  15. Revision History:
  16. --*/
  17. #if ( _MSC_VER >= 800 )
  18. #pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
  19. #pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
  20. #endif
  21. #define WIN32_NO_STATUS
  22. #include <windef.h>
  23. #undef WIN32_NO_STATUS
  24. #include <windows.h>
  25. #include <lmcons.h>
  26. #include <lmaccess.h>
  27. #include <lmapibuf.h>
  28. #include "subauth.h"
  29. BOOLEAN
  30. EqualComputerName(
  31.     IN PUNICODE_STRING String1,
  32.     IN PUNICODE_STRING String2
  33.     );
  34. NTSTATUS
  35. QuerySystemTime (
  36.     OUT PLARGE_INTEGER SystemTime
  37.     );
  38. BOOL
  39. GetPasswordExpired(
  40.     IN LARGE_INTEGER PasswordLastSet,
  41.     IN LARGE_INTEGER MaxPasswordAge
  42.     );
  43. NTSTATUS
  44. AccountRestrictions(
  45.     IN ULONG UserRid,
  46.     IN PUNICODE_STRING LogonWorkStation,
  47.     IN PUNICODE_STRING WorkStations,
  48.     IN PLOGON_HOURS LogonHours,
  49.     OUT PLARGE_INTEGER LogoffTime,
  50.     OUT PLARGE_INTEGER KickoffTime
  51.     );
  52. LARGE_INTEGER
  53. NetpSecondsToDeltaTime(
  54.     IN ULONG Seconds
  55.     );
  56. VOID
  57. InitUnicodeString(
  58.     OUT PUNICODE_STRING DestinationString,
  59.     IN PCWSTR SourceString OPTIONAL
  60.     );
  61. VOID
  62. CopyUnicodeString(
  63.     OUT PUNICODE_STRING DestinationString,
  64.     IN PUNICODE_STRING SourceString OPTIONAL
  65.     );
  66.  
  67. NTSTATUS
  68. NTAPI
  69. Msv1_0SubAuthenticationRoutine (
  70.     IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  71.     IN PVOID LogonInformation,
  72.     IN ULONG Flags,
  73.     IN PUSER_ALL_INFORMATION UserAll,
  74.     OUT PULONG WhichFields,
  75.     OUT PULONG UserFlags,
  76.     OUT PBOOLEAN Authoritative,
  77.     OUT PLARGE_INTEGER LogoffTime,
  78.     OUT PLARGE_INTEGER KickoffTime
  79. )
  80. /*++
  81. Routine Description:
  82.     The subauthentication routine does client/server specific authentication
  83.     of a user. The credentials of the user are passed in addition to all the
  84.     information from SAM defining the user. This routine decides whether to
  85.     let the user log on.
  86. Arguments:
  87.     LogonLevel -- Specifies the level of information given in
  88.         LogonInformation.
  89.     LogonInformation -- Specifies the description for the user
  90.         logging on.  The LogonDomainName field should be ignored.
  91.     Flags - Flags describing the circumstances of the logon.
  92.         MSV1_0_PASSTHRU -- This is a PassThru authenication.  (i.e., the
  93.             user isn't connecting to this machine.)
  94.         MSV1_0_GUEST_LOGON -- This is a retry of the logon using the GUEST
  95.             user account.
  96.     UserAll -- The description of the user as returned from SAM.
  97.     WhichFields -- Returns which fields from UserAllInfo are to be written
  98.         back to SAM.  The fields will only be written if MSV returns success
  99.         to it's caller.  Only the following bits are valid.
  100.         USER_ALL_PARAMETERS - Write UserAllInfo->Parameters back to SAM.  If
  101.             the size of the buffer is changed, Msv1_0SubAuthenticationRoutine
  102.             must delete the old buffer using MIDL_user_free() and reallocate the
  103.             buffer using MIDL_user_allocate().
  104.     UserFlags -- Returns UserFlags to be returned from LsaLogonUser in the
  105.         LogonProfile.  The following bits are currently defined:
  106.             LOGON_GUEST -- This was a guest logon
  107.             LOGON_NOENCRYPTION -- The caller didn't specify encrypted credentials
  108.         SubAuthentication packages should restrict themselves to returning
  109.         bits in the high order byte of UserFlags.  However, this convention
  110.         isn't enforced giving the SubAuthentication package more flexibility.
  111.     Authoritative -- Returns whether the status returned is an
  112.         authoritative status which should be returned to the original
  113.         caller.  If not, this logon request may be tried again on another
  114.         domain controller.  This parameter is returned regardless of the
  115.         status code.
  116.     LogoffTime - Receives the time at which the user should log off the
  117.         system.  This time is specified as a GMT relative NT system time.
  118.     KickoffTime - Receives the time at which the user should be kicked
  119.         off the system. This time is specified as a GMT relative system
  120.         time.  Specify, a full scale positive number if the user isn't to
  121.         be kicked off.
  122. Return Value:
  123.     STATUS_SUCCESS: if there was no error.
  124.     STATUS_NO_SUCH_USER: The specified user has no account.
  125.     STATUS_WRONG_PASSWORD: The password was invalid.
  126.     STATUS_INVALID_INFO_CLASS: LogonLevel is invalid.
  127.     STATUS_ACCOUNT_LOCKED_OUT: The account is locked out
  128.     STATUS_ACCOUNT_DISABLED: The account is disabled
  129.     STATUS_ACCOUNT_EXPIRED: The account has expired.
  130.     STATUS_PASSWORD_MUST_CHANGE: Account is marked as Password must change
  131.         on next logon.
  132.     STATUS_PASSWORD_EXPIRED: The Password is expired.
  133.     STATUS_INVALID_LOGON_HOURS - The user is not authorized to log on at
  134.         this time.
  135.     STATUS_INVALID_WORKSTATION - The user is not authorized to log on to
  136.         the specified workstation.
  137. --*/
  138. {
  139.     NTSTATUS Status;
  140.     ULONG UserAccountControl;
  141.     LARGE_INTEGER LogonTime;
  142.     LARGE_INTEGER PasswordDateSet;
  143.     UNICODE_STRING LocalWorkstation;
  144.     PNETLOGON_NETWORK_INFO LogonNetworkInfo;
  145.     //
  146.     // Check whether the SubAuthentication package supports this type
  147.     //  of logon.
  148.     //
  149.     *Authoritative = TRUE;
  150.     *UserFlags = 0;
  151.     *WhichFields = 0;
  152.     (VOID) QuerySystemTime( &LogonTime );
  153.     switch ( LogonLevel ) {
  154.     case NetlogonInteractiveInformation:
  155.     case NetlogonServiceInformation:
  156.         //
  157.         // This SubAuthentication package only supports network logons.
  158.         //
  159.         return STATUS_INVALID_INFO_CLASS;
  160.     case NetlogonNetworkInformation:
  161.         //
  162.         // This SubAuthentication package doesn't support access via machine
  163.         // accounts.
  164.         //
  165.         UserAccountControl = USER_NORMAL_ACCOUNT;
  166.         //
  167.         // Local user (Temp Duplicate) accounts are only used on the machine
  168.         // being directly logged onto.
  169.         // (Nor are interactive or service logons allowed to them.)
  170.         //
  171.         if ( (Flags & MSV1_0_PASSTHRU) == 0 ) {
  172.             UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
  173.         }
  174.         LogonNetworkInfo = (PNETLOGON_NETWORK_INFO) LogonInformation;
  175.         break;
  176.     default:
  177.         *Authoritative = TRUE;
  178.         return STATUS_INVALID_INFO_CLASS;
  179.     }
  180.     //
  181.     // If the account type isn't allowed,
  182.     //  Treat this as though the User Account doesn't exist.
  183.     //
  184.     if ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) {
  185.         *Authoritative = FALSE;
  186.         Status = STATUS_NO_SUCH_USER;
  187.         goto Cleanup;
  188.     }
  189.     //
  190.     // This SubAuthentication package doesn't allow guest logons.
  191.     //
  192.     if ( Flags & MSV1_0_GUEST_LOGON ) {
  193.         *Authoritative = FALSE;
  194.         Status = STATUS_NO_SUCH_USER;
  195.         goto Cleanup;
  196.     }
  197.     //
  198.     // Ensure the account isn't locked out.
  199.     //
  200.     if ( UserAll->UserId != DOMAIN_USER_RID_ADMIN &&
  201.          (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) ) {
  202.         //
  203.         // Since the UI strongly encourages admins to disable user
  204.         // accounts rather than delete them.  Treat disabled acccount as
  205.         // non-authoritative allowing the search to continue for other
  206.         // accounts by the same name.
  207.         //
  208.         if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  209.             *Authoritative = FALSE;
  210.         } else {
  211.             *Authoritative = TRUE;
  212.         }
  213.         Status = STATUS_ACCOUNT_LOCKED_OUT;
  214.         goto Cleanup;
  215.     }
  216.     //
  217.     // Check the password.
  218.     //
  219.     if ( FALSE /* VALIDATE THE USER'S PASSWORD HERE */ ) {
  220.         Status = STATUS_WRONG_PASSWORD;
  221.         //
  222.         // Since the UI strongly encourages admins to disable user
  223.         // accounts rather than delete them.  Treat disabled acccount as
  224.         // non-authoritative allowing the search to continue for other
  225.         // accounts by the same name.
  226.         //
  227.         if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  228.             *Authoritative = FALSE;
  229.         } else {
  230.             *Authoritative = TRUE;
  231.         }
  232.         goto Cleanup;
  233.     }
  234.     //
  235.     // Prevent some things from effecting the Administrator user
  236.     //
  237.     if (UserAll->UserId == DOMAIN_USER_RID_ADMIN) {
  238.         //
  239.         //  The administrator account doesn't have a forced logoff time.
  240.         //
  241.         LogoffTime->HighPart = 0x7FFFFFFF;
  242.         LogoffTime->LowPart = 0xFFFFFFFF;
  243.         KickoffTime->HighPart = 0x7FFFFFFF;
  244.         KickoffTime->LowPart = 0xFFFFFFFF;
  245.     } else {
  246.         //
  247.         // Check if the account is disabled.
  248.         //
  249.         if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  250.             //
  251.             // Since the UI strongly encourages admins to disable user
  252.             // accounts rather than delete them.  Treat disabled acccount as
  253.             // non-authoritative allowing the search to continue for other
  254.             // accounts by the same name.
  255.             //
  256.             *Authoritative = FALSE;
  257.             Status = STATUS_ACCOUNT_DISABLED;
  258.             goto Cleanup;
  259.         }
  260.         //
  261.         // Check if the account has expired.
  262.         //
  263.         if ( UserAll->AccountExpires.QuadPart != 0 &&
  264.              LogonTime.QuadPart >= UserAll->AccountExpires.QuadPart ) {
  265.             *Authoritative = TRUE;
  266.             Status = STATUS_ACCOUNT_EXPIRED;
  267.             goto Cleanup;
  268.         }
  269. #if 1
  270.     //
  271.     //  If your using SAM's password expiration date, use this code, otherwise
  272.     //  use the code below and supply your own password set date...
  273.     //
  274.         //
  275.         // The password is valid, check to see if the password is expired.
  276.         //  (SAM will have appropriately set PasswordMustChange to reflect
  277.         //  USER_DONT_EXPIRE_PASSWORD)
  278.         //
  279.         // If the password checked above is not the SAM password, you may
  280.         // want to consider not checking the SAM password expiration times here.
  281.         //
  282.         if ( LogonTime.QuadPart >= UserAll->PasswordMustChange.QuadPart ) {
  283.             if ( UserAll->PasswordLastSet.QuadPart == 0 ) {
  284.                 Status = STATUS_PASSWORD_MUST_CHANGE;
  285.             } else {
  286.                 Status = STATUS_PASSWORD_EXPIRED;
  287.             }
  288.             *Authoritative = TRUE;
  289.             goto Cleanup;
  290.         }
  291. #else
  292.         //
  293.         // Response is correct. So, check if the password has expired or not
  294.         //
  295.         if (! (UserAll->UserAccountControl & USER_DONT_EXPIRE_PASSWORD)) {
  296.             LARGE_INTEGER MaxPasswordAge;
  297.             MaxPasswordAge.HighPart = 0x7FFFFFFF;
  298.             MaxPasswordAge.LowPart = 0xFFFFFFFF;
  299.             //
  300.             // PasswordDateSet should be modified to hold the last date the
  301.             // user's password was set.
  302.             //
  303.             PasswordDateSet.LowPart = 0;
  304.             PasswordDateSet.HighPart = 0;
  305.             if ( GetPasswordExpired( PasswordDateSet,
  306.                         MaxPasswordAge )) {
  307.                 Status = STATUS_PASSWORD_EXPIRED;
  308.                 goto Cleanup;
  309.             }
  310.         }
  311. #endif
  312. #if 1
  313.     //
  314.     // Validate the workstation the user logged on from.
  315.     //
  316.     // Ditch leading \ on workstation name before passing it to SAM.
  317.     //
  318.     LocalWorkstation = LogonNetworkInfo->Identity.Workstation;
  319.     if ( LocalWorkstation.Length > 0 &&
  320.          LocalWorkstation.Buffer[0] == L'\' &&
  321.          LocalWorkstation.Buffer[1] == L'\' ) {
  322.         LocalWorkstation.Buffer += 2;
  323.         LocalWorkstation.Length -= 2*sizeof(WCHAR);
  324.         LocalWorkstation.MaximumLength -= 2*sizeof(WCHAR);
  325.     }
  326.     //
  327.     //  To validate the user's logon hours as SAM does it, use this code,
  328.     //  otherwise, supply your own checks below this code.
  329.     //
  330.     Status = AccountRestrictions( UserAll->UserId,
  331.                                   &LocalWorkstation,
  332.                                   (PUNICODE_STRING) &UserAll->WorkStations,
  333.                                   &UserAll->LogonHours,
  334.                                   LogoffTime,
  335.                                   KickoffTime );
  336.     if ( !NT_SUCCESS( Status )) {
  337.         goto Cleanup;
  338.     }
  339. #else
  340.         //
  341.         // Validate the user's logon hours.
  342.         //
  343.         if ( TRUE /* VALIDATE THE LOGON HOURS */ ) {
  344.             //
  345.             // All times are allowed, so there's no logoff
  346.             // time.  Return forever for both logofftime and
  347.             // kickofftime.
  348.             //
  349.             LogoffTime->HighPart = 0x7FFFFFFF;
  350.             LogoffTime->LowPart = 0xFFFFFFFF;
  351.             KickoffTime->HighPart = 0x7FFFFFFF;
  352.             KickoffTime->LowPart = 0xFFFFFFFF;
  353.         } else {
  354.             Status = STATUS_INVALID_LOGON_HOURS;
  355.             *Authoritative = TRUE;
  356.             goto Cleanup;
  357.         }
  358. #endif
  359.         //
  360.         // Validate if the user can log on from this workstation.
  361.         //  (Supply subauthentication package specific code here.)
  362.         if ( LogonNetworkInfo->Identity.Workstation.Buffer == NULL ) {
  363.             Status = STATUS_INVALID_WORKSTATION;
  364.             *Authoritative = TRUE;
  365.             goto Cleanup;
  366.         }
  367.     }
  368.     //
  369.     // The user is valid.
  370.     //
  371.     *Authoritative = TRUE;
  372.     Status = STATUS_SUCCESS;
  373.     //
  374.     // Cleanup up before returning.
  375.     //
  376. Cleanup:
  377.     return Status;
  378. }  // Msv1_0SubAuthenticationRoutine
  379. BOOL
  380. GetPasswordExpired (
  381.     IN LARGE_INTEGER PasswordLastSet,
  382.     IN LARGE_INTEGER MaxPasswordAge
  383.     )
  384. /*++
  385. Routine Description:
  386.     This routine returns true if the password is expired, false otherwise.
  387. Arguments:
  388.     PasswordLastSet - Time when the password was last set for this user.
  389.     MaxPasswordAge - Maximum password age for any password in the domain.
  390. Return Value:
  391.     Returns true if password is expired.  False if not expired.
  392. --*/
  393. {
  394.     LARGE_INTEGER PasswordMustChange;
  395.     NTSTATUS Status;
  396.     BOOLEAN rc;
  397.     LARGE_INTEGER TimeNow;
  398.     //
  399.     // Compute the expiration time as the time the password was
  400.     // last set plus the maximum age.
  401.     //
  402.     if ( PasswordLastSet.QuadPart < 0 || MaxPasswordAge.QuadPart > 0 ) {
  403.         rc = TRUE;      // default for invalid times is that it is expired.
  404.     } else {
  405.         __try {
  406.             PasswordMustChange.QuadPart =
  407.                 PasswordLastSet.QuadPart - MaxPasswordAge.QuadPart;
  408.             //
  409.             // Limit the resultant time to the maximum valid absolute time
  410.             //
  411.             if ( PasswordMustChange.QuadPart < 0 ) {
  412.                 rc = FALSE;
  413.             } else {
  414.                 Status = QuerySystemTime( &TimeNow );
  415.                 if (NT_SUCCESS(Status)) {
  416.                     if ( TimeNow.QuadPart >= PasswordMustChange.QuadPart ) {
  417.                         rc = TRUE;
  418.                     } else {
  419.                         rc = FALSE;
  420.                     }
  421.                 } else {
  422.                     rc = FALSE;     // won't fail if QuerySystemTime failed.
  423.                 }
  424.             }
  425.         } __except(EXCEPTION_EXECUTE_HANDLER) {
  426.             rc = TRUE;
  427.         }
  428.     }
  429.     return rc;
  430. }  // GetPasswordExpired
  431.  
  432. NTSTATUS
  433. QuerySystemTime (
  434.     OUT PLARGE_INTEGER SystemTime
  435.     )
  436. /*++
  437. Routine Description:
  438.     This function returns the absolute system time. The time is in units of
  439.     100nsec ticks since the base time which is midnight January 1, 1601.
  440. Arguments:
  441.     SystemTime - Supplies the address of a variable that will receive the
  442.         current system time.
  443. Return Value:
  444.     STATUS_SUCCESS is returned if the service is successfully executed.
  445.     STATUS_ACCESS_VIOLATION is returned if the output parameter for the
  446.         system time cannot be written.
  447. --*/
  448. {
  449.     SYSTEMTIME CurrentTime;
  450.     GetSystemTime( &CurrentTime );
  451.     if ( !SystemTimeToFileTime( &CurrentTime, (LPFILETIME) SystemTime ) ) {
  452.         return STATUS_ACCESS_VIOLATION;
  453.     }
  454.     return STATUS_SUCCESS;
  455. }
  456. NTSTATUS
  457. SampMatchworkstation(
  458.     IN PUNICODE_STRING LogonWorkStation,
  459.     IN PUNICODE_STRING WorkStations
  460.     )
  461. /*++
  462. Routine Description:
  463.     Check if the given workstation is a member of the list of workstations
  464.     given.
  465. Arguments:
  466.     LogonWorkStations - UNICODE name of the workstation that the user is
  467.         trying to log into.
  468.     WorkStations - API list of workstations that the user is allowed to
  469.         log into.
  470. Return Value:
  471.     STATUS_SUCCESS - The user is allowed to log into the workstation.
  472. --*/
  473. {
  474.     PWCHAR          WorkStationName;
  475.     UNICODE_STRING  Unicode;
  476.     NTSTATUS        NtStatus;
  477.     WCHAR           Buffer[256];
  478.     USHORT          LocalBufferLength = 256;
  479.     UNICODE_STRING  WorkStationsListCopy;
  480.     BOOLEAN         BufferAllocated = FALSE;
  481.     PWCHAR          TmpBuffer;
  482.     //
  483.     // Local workstation is always allowed
  484.     // If WorkStations field is 0 everybody is allowed
  485.     //
  486.     if ( ( LogonWorkStation == NULL ) ||
  487.         ( LogonWorkStation->Length == 0 ) ||
  488.         ( WorkStations->Length == 0 ) ) {
  489.         return( STATUS_SUCCESS );
  490.     }
  491.     //
  492.     // Assume failure; change status only if we find the string.
  493.     //
  494.     NtStatus = STATUS_INVALID_WORKSTATION;
  495.     //
  496.     // WorkStationApiList points to our current location in the list of
  497.     // WorkStations.
  498.     //
  499.     if ( WorkStations->Length > LocalBufferLength ) {
  500.         WorkStationsListCopy.Buffer = LocalAlloc( 0, WorkStations->Length );
  501.         BufferAllocated = TRUE;
  502.         if ( WorkStationsListCopy.Buffer == NULL ) {
  503.             NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  504.             return( NtStatus );
  505.         }
  506.         WorkStationsListCopy.MaximumLength = WorkStations->Length;
  507.     } else {
  508.         WorkStationsListCopy.Buffer = Buffer;
  509.         WorkStationsListCopy.MaximumLength = LocalBufferLength;
  510.     }
  511.     CopyUnicodeString( &WorkStationsListCopy, WorkStations );
  512.     //
  513.     // wcstok requires a string the first time it's called, and NULL
  514.     // for all subsequent calls.  Use a temporary variable so we
  515.     // can do this.
  516.     //
  517.     TmpBuffer = WorkStationsListCopy.Buffer;
  518.     while( WorkStationName = wcstok(TmpBuffer, L",") ) {
  519.         TmpBuffer = NULL;
  520.         InitUnicodeString( &Unicode, WorkStationName );
  521.         if (EqualComputerName( &Unicode, LogonWorkStation )) {
  522.             NtStatus = STATUS_SUCCESS;
  523.             break;
  524.         }
  525.     }
  526.     if ( BufferAllocated ) {
  527.         LocalFree( WorkStationsListCopy.Buffer );
  528.     }
  529.     return( NtStatus );
  530. }
  531. NTSTATUS
  532. AccountRestrictions(
  533.     IN ULONG UserRid,
  534.     IN PUNICODE_STRING LogonWorkStation,
  535.     IN PUNICODE_STRING WorkStations,
  536.     IN PLOGON_HOURS LogonHours,
  537.     OUT PLARGE_INTEGER LogoffTime,
  538.     OUT PLARGE_INTEGER KickoffTime
  539.     )
  540. /*++
  541. Routine Description:
  542.     Validate a user's ability to log on at this time and at the workstation
  543.     being logged onto.
  544. Arguments:
  545.     UserRid - The user id of the user to operate on.
  546.     LogonWorkStation - The name of the workstation the logon is being
  547.         attempted at.
  548.     WorkStations - The list of workstations the user may log on to.  This
  549.         information comes from the user's account information.  It must
  550.         be in API list format.
  551.     LogonHours - The times the user may logon.  This information comes
  552.         from the user's account information.
  553.     LogoffTime - Receives the time at which the user should log off the
  554.         system.
  555.     KickoffTime - Receives the time at which the user should be kicked
  556.         off the system.
  557. Return Value:
  558.     STATUS_SUCCESS - Logon is permitted.
  559.     STATUS_INVALID_LOGON_HOURS - The user is not authorized to log on at
  560.         this time.
  561.     STATUS_INVALID_WORKSTATION - The user is not authorized to log on to
  562.         the specified workstation.
  563. --*/
  564. {
  565.     static BOOLEAN GetForceLogoff = TRUE;
  566.     static LARGE_INTEGER ForceLogoff = { 0x7fffffff, 0xFFFFFFF};
  567. #define MILLISECONDS_PER_WEEK 7 * 24 * 60 * 60 * 1000
  568.     SYSTEMTIME              CurrentTimeFields;
  569.     LARGE_INTEGER           CurrentTime, CurrentUTCTime;
  570.     LARGE_INTEGER           MillisecondsIntoWeekXUnitsPerWeek;
  571.     LARGE_INTEGER           LargeUnitsIntoWeek;
  572.     LARGE_INTEGER           Delta100Ns;
  573.     NTSTATUS                NtStatus = STATUS_SUCCESS;
  574.     ULONG                   CurrentMsIntoWeek;
  575.     ULONG                   LogoffMsIntoWeek;
  576.     ULONG                   DeltaMs;
  577.     ULONG                   MillisecondsPerUnit;
  578.     ULONG                   CurrentUnitsIntoWeek;
  579.     ULONG                   LogoffUnitsIntoWeek;
  580.     USHORT                  i;
  581.     TIME_ZONE_INFORMATION   TimeZoneInformation;
  582.     DWORD TimeZoneId;
  583.     LARGE_INTEGER           BiasIn100NsUnits;
  584.     LONG                    BiasInMinutes;
  585.     //
  586.     // Only check for users other than the builtin ADMIN
  587.     //
  588.     if ( UserRid != DOMAIN_USER_RID_ADMIN) {
  589.         //
  590.         // Scan to make sure the workstation being logged into is in the
  591.         // list of valid workstations - or if the list of valid workstations
  592.         // is null, which means that all are valid.
  593.         //
  594.         NtStatus = SampMatchworkstation( LogonWorkStation, WorkStations );
  595.         if ( NT_SUCCESS( NtStatus ) ) {
  596.             //
  597.             // Check to make sure that the current time is a valid time to log
  598.             // on in the LogonHours.
  599.             //
  600.             // We need to validate the time taking into account whether we are
  601.             // in daylight savings time or standard time.  Thus, if the logon
  602.             // hours specify that we are able to log on between 9am and 5pm,
  603.             // this means 9am to 5pm standard time during the standard time
  604.             // period, and 9am to 5pm daylight savings time when in the
  605.             // daylight savings time.  Since the logon hours stored by SAM are
  606.             // independent of daylight savings time, we need to add in the
  607.             // difference between standard time and daylight savings time to
  608.             // the current time before checking whether this time is a valid
  609.             // time to log on.  Since this difference (or bias as it is called)
  610.             // is actually held in the form
  611.             //
  612.             // Standard time = Daylight savings time + Bias
  613.             //
  614.             // the Bias is a negative number.  Thus we actually subtract the
  615.             // signed Bias from the Current Time.
  616.             //
  617.             // First, get the Time Zone Information.
  618.             //
  619.             TimeZoneId = GetTimeZoneInformation(
  620.                              (LPTIME_ZONE_INFORMATION) &TimeZoneInformation
  621.                              );
  622.             //
  623.             // Next, get the appropriate bias (signed integer in minutes) to subtract from
  624.             // the Universal Time Convention (UTC) time returned by NtQuerySystemTime
  625.             // to get the local time.  The bias to be used depends whether we're
  626.             // in Daylight Savings time or Standard Time as indicated by the
  627.             // TimeZoneId parameter.
  628.             //
  629.             // local time  = UTC time - bias in 100Ns units
  630.             //
  631.             switch (TimeZoneId) {
  632.             case TIME_ZONE_ID_UNKNOWN:
  633.                 //
  634.                 // There is no differentiation between standard and
  635.                 // daylight savings time.  Proceed as for Standard Time
  636.                 //
  637.                 BiasInMinutes = TimeZoneInformation.StandardBias;
  638.                 break;
  639.             case TIME_ZONE_ID_STANDARD:
  640.                 BiasInMinutes = TimeZoneInformation.StandardBias;
  641.                 break;
  642.             case TIME_ZONE_ID_DAYLIGHT:
  643.                 BiasInMinutes = TimeZoneInformation.DaylightBias;
  644.                 break;
  645.             default:
  646.                 //
  647.                 // Something is wrong with the time zone information.  Fail
  648.                 // the logon request.
  649.                 //
  650.                 NtStatus = STATUS_INVALID_LOGON_HOURS;
  651.                 break;
  652.             }
  653.             if (NT_SUCCESS(NtStatus)) {
  654.                 //
  655.                 // Convert the Bias from minutes to 100ns units
  656.                 //
  657.                 BiasIn100NsUnits.QuadPart = ((LONGLONG)BiasInMinutes)
  658.                                             * 60 * 10000000;
  659.                 //
  660.                 // Get the UTC time in 100Ns units used by Windows Nt.  This
  661.                 // time is GMT.
  662.                 //
  663.                 NtStatus = QuerySystemTime( &CurrentUTCTime );
  664.             }
  665.             if ( NT_SUCCESS( NtStatus ) ) {
  666.                 CurrentTime.QuadPart = CurrentUTCTime.QuadPart -
  667.                               BiasIn100NsUnits.QuadPart;
  668.                 FileTimeToSystemTime( (PFILETIME)&CurrentTime, &CurrentTimeFields );
  669.                 CurrentMsIntoWeek = (((( CurrentTimeFields.wDayOfWeek * 24 ) +
  670.                                        CurrentTimeFields.wHour ) * 60 +
  671.                                        CurrentTimeFields.wMinute ) * 60 +
  672.                                        CurrentTimeFields.wSecond ) * 1000 +
  673.                                        CurrentTimeFields.wMilliseconds;
  674.                 MillisecondsIntoWeekXUnitsPerWeek.QuadPart =
  675.                     ((LONGLONG)CurrentMsIntoWeek) *
  676.                     ((LONGLONG)LogonHours->UnitsPerWeek);
  677.                 LargeUnitsIntoWeek.QuadPart =
  678.                     MillisecondsIntoWeekXUnitsPerWeek.QuadPart / ((ULONG) MILLISECONDS_PER_WEEK);
  679.                 CurrentUnitsIntoWeek = LargeUnitsIntoWeek.LowPart;
  680.                 if ( !( LogonHours->LogonHours[ CurrentUnitsIntoWeek / 8] &
  681.                     ( 0x01 << ( CurrentUnitsIntoWeek % 8 ) ) ) ) {
  682.                     NtStatus = STATUS_INVALID_LOGON_HOURS;
  683.                 } else {
  684.                     //
  685.                     // Determine the next time that the user is NOT supposed to be logged
  686.                     // in, and return that as LogoffTime.
  687.                     //
  688.                     i = 0;
  689.                     LogoffUnitsIntoWeek = CurrentUnitsIntoWeek;
  690.                     do {
  691.                         i++;
  692.                         LogoffUnitsIntoWeek = ( LogoffUnitsIntoWeek + 1 ) % LogonHours->UnitsPerWeek;
  693.                     } while ( ( i <= LogonHours->UnitsPerWeek ) &&
  694.                         ( LogonHours->LogonHours[ LogoffUnitsIntoWeek / 8 ] &
  695.                         ( 0x01 << ( LogoffUnitsIntoWeek % 8 ) ) ) );
  696.                     if ( i > LogonHours->UnitsPerWeek ) {
  697.                         //
  698.                         // All times are allowed, so there's no logoff
  699.                         // time.  Return forever for both LogoffTime and
  700.                         // KickoffTime.
  701.                         //
  702.                         LogoffTime->HighPart = 0x7FFFFFFF;
  703.                         LogoffTime->LowPart = 0xFFFFFFFF;
  704.                         KickoffTime->HighPart = 0x7FFFFFFF;
  705.                         KickoffTime->LowPart = 0xFFFFFFFF;
  706.                     } else {
  707.                         //
  708.                         // LogoffUnitsIntoWeek points at which time unit the
  709.                         // user is to log off.  Calculate actual time from
  710.                         // the unit, and return it.
  711.                         //
  712.                         // CurrentTimeFields already holds the current
  713.                         // time for some time during this week; just adjust
  714.                         // to the logoff time during this week and convert
  715.                         // to time format.
  716.                         //
  717.                         MillisecondsPerUnit = MILLISECONDS_PER_WEEK / LogonHours->UnitsPerWeek;
  718.                         LogoffMsIntoWeek = MillisecondsPerUnit * LogoffUnitsIntoWeek;
  719.                         if ( LogoffMsIntoWeek < CurrentMsIntoWeek ) {
  720.                             DeltaMs = MILLISECONDS_PER_WEEK - ( CurrentMsIntoWeek - LogoffMsIntoWeek );
  721.                         } else {
  722.                             DeltaMs = LogoffMsIntoWeek - CurrentMsIntoWeek;
  723.                         }
  724.                         Delta100Ns.QuadPart = (LONGLONG) DeltaMs * 10000;
  725.                         LogoffTime->QuadPart = CurrentUTCTime.QuadPart +
  726.                                       Delta100Ns.QuadPart;
  727.                         //
  728.                         // Grab the domain's ForceLogoff time.
  729.                         //
  730.                         if ( GetForceLogoff ) {
  731.                             NET_API_STATUS NetStatus;
  732.                             LPUSER_MODALS_INFO_0 UserModals0;
  733.                             NetStatus = NetUserModalsGet( NULL,
  734.                                                           0,
  735.                                                           (LPBYTE *)&UserModals0 );
  736.                             if ( NetStatus == 0 ) {
  737.                                 GetForceLogoff = FALSE;
  738.                                 ForceLogoff = NetpSecondsToDeltaTime( UserModals0->usrmod0_force_logoff );
  739.                                 NetApiBufferFree( UserModals0 );
  740.                             }
  741.                         }
  742.                         //
  743.                         // Subtract Domain->ForceLogoff from LogoffTime, and return
  744.                         // that as KickoffTime.  Note that Domain->ForceLogoff is a
  745.                         // negative delta.  If its magnitude is sufficiently large
  746.                         // (in fact, larger than the difference between LogoffTime
  747.                         // and the largest positive large integer), we'll get overflow
  748.                         // resulting in a KickOffTime that is negative.  In this
  749.                         // case, reset the KickOffTime to this largest positive
  750.                         // large integer (i.e. "never") value.
  751.                         //
  752.                         KickoffTime->QuadPart = LogoffTime->QuadPart - ForceLogoff.QuadPart;
  753.                         if (KickoffTime->QuadPart < 0) {
  754.                             KickoffTime->HighPart = 0x7FFFFFFF;
  755.                             KickoffTime->LowPart = 0xFFFFFFFF;
  756.                         }
  757.                     }
  758.                 }
  759.             }
  760.         }
  761.     } else {
  762.         //
  763.         // Never kick administrators off
  764.         //
  765.         LogoffTime->HighPart  = 0x7FFFFFFF;
  766.         LogoffTime->LowPart   = 0xFFFFFFFF;
  767.         KickoffTime->HighPart = 0x7FFFFFFF;
  768.         KickoffTime->LowPart  = 0xFFFFFFFF;
  769.     }
  770.     return( NtStatus );
  771. }
  772. LARGE_INTEGER
  773. NetpSecondsToDeltaTime(
  774.     IN ULONG Seconds
  775.     )
  776. /*++
  777. Routine Description:
  778.     Convert a number of seconds to an NT delta time specification
  779. Arguments:
  780.     Seconds - a positive number of seconds
  781. Return Value:
  782.     Returns the NT Delta time.  NT delta time is a negative number
  783.         of 100ns units.
  784. --*/
  785. {
  786.     LARGE_INTEGER DeltaTime;
  787.     LARGE_INTEGER LargeSeconds;
  788.     LARGE_INTEGER Answer;
  789.     //
  790.     // Special case TIMEQ_FOREVER (return a full scale negative)
  791.     //
  792.     if ( Seconds == TIMEQ_FOREVER ) {
  793.         DeltaTime.LowPart = 0;
  794.         DeltaTime.HighPart = (LONG) 0x80000000;
  795.     //
  796.     // Convert seconds to 100ns units simply by multiplying by 10000000.
  797.     //
  798.     // Convert to delta time by negating.
  799.     //
  800.     } else {
  801.         LargeSeconds.LowPart = Seconds;
  802.         LargeSeconds.HighPart = 0;
  803.         Answer.QuadPart = LargeSeconds.QuadPart * 10000000;
  804.           if ( Answer.QuadPart < 0 ) {
  805.             DeltaTime.LowPart = 0;
  806.             DeltaTime.HighPart = (LONG) 0x80000000;
  807.         } else {
  808.             DeltaTime.QuadPart = -Answer.QuadPart;
  809.         }
  810.     }
  811.     return DeltaTime;
  812. } // NetpSecondsToDeltaTime
  813. BOOLEAN
  814. EqualComputerName(
  815.     IN PUNICODE_STRING String1,
  816.     IN PUNICODE_STRING String2
  817.     )
  818. /*++
  819. Routine Description:
  820.     Compare two computer names for equality.
  821. Arguments:
  822.     String1 - Name of first computer.
  823.     String2 - Name of second computer.
  824. Return Value:
  825.     TRUE if the names, converted to OEM, compare case-insensitively,
  826.     FALSE if they don't compare or can't be converted to OEM.
  827. --*/
  828. {
  829.     WCHAR Computer1[CNLEN+1];
  830.     WCHAR Computer2[CNLEN+1];
  831.     CHAR OemComputer1[CNLEN+1];
  832.     CHAR OemComputer2[CNLEN+1];
  833.     //
  834.     // Make sure the names are not too long
  835.     //
  836.     if ((String1->Length > CNLEN*sizeof(WCHAR)) ||
  837.         (String2->Length > CNLEN*sizeof(WCHAR))) {
  838.         return(FALSE);
  839.     }
  840.     //
  841.     // Copy them to null terminated strings
  842.     //
  843.     CopyMemory(
  844.         Computer1,
  845.         String1->Buffer,
  846.         String1->Length
  847.         );
  848.     Computer1[String1->Length/sizeof(WCHAR)] = L'';
  849.     CopyMemory(
  850.         Computer2,
  851.         String2->Buffer,
  852.         String2->Length
  853.         );
  854.     Computer2[String2->Length/sizeof(WCHAR)] = L'';
  855.     //
  856.     // Convert the computer names to OEM
  857.     //
  858.     if (!CharToOemW(
  859.             Computer1,
  860.             OemComputer1
  861.             )) {
  862.         return(FALSE);
  863.     }
  864.     if (!CharToOemW(
  865.             Computer2,
  866.             OemComputer2
  867.             )) {
  868.         return(FALSE);
  869.     }
  870.     //
  871.     // Do a case insensitive comparison of the oem computer names.
  872.     //
  873.     if (lstrcmpiA(OemComputer1, OemComputer2) == 0)
  874.     {
  875.         return(TRUE);
  876.     }
  877.     else
  878.     {
  879.         return(FALSE);
  880.     }
  881. }
  882. VOID
  883. InitUnicodeString(
  884.     OUT PUNICODE_STRING DestinationString,
  885.     IN PCWSTR SourceString OPTIONAL
  886.     )
  887. /*++
  888. Routine Description:
  889.     The InitUnicodeString function initializes an NT counted
  890.     unicode string.  The DestinationString is initialized to point to
  891.     the SourceString and the Length and MaximumLength fields of
  892.     DestinationString are initialized to the length of the SourceString,
  893.     which is zero if SourceString is not specified.
  894. Arguments:
  895.     DestinationString - Pointer to the counted string to initialize
  896.     SourceString - Optional pointer to a null terminated unicode string that
  897.         the counted string is to point to.
  898. Return Value:
  899.     None.
  900. --*/
  901. {
  902.     ULONG Length;
  903.     DestinationString->Buffer = (PWSTR)SourceString;
  904.     if (SourceString != NULL) {
  905.         Length = wcslen( SourceString ) * sizeof( WCHAR );
  906.         DestinationString->Length = (USHORT)Length;
  907.         DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
  908.         }
  909.     else {
  910.         DestinationString->MaximumLength = 0;
  911.         DestinationString->Length = 0;
  912.         }
  913. }
  914. VOID
  915. CopyUnicodeString(
  916.     OUT PUNICODE_STRING DestinationString,
  917.     IN PUNICODE_STRING SourceString OPTIONAL
  918.     )
  919. /*++
  920. Routine Description:
  921.     The CopyString function copies the SourceString to the
  922.     DestinationString.  If SourceString is not specified, then
  923.     the Length field of DestinationString is set to zero.  The
  924.     MaximumLength and Buffer fields of DestinationString are not
  925.     modified by this function.
  926.     The number of bytes copied from the SourceString is either the
  927.     Length of SourceString or the MaximumLength of DestinationString,
  928.     whichever is smaller.
  929. Arguments:
  930.     DestinationString - Pointer to the destination string.
  931.     SourceString - Optional pointer to the source string.
  932. Return Value:
  933.     None.
  934. --*/
  935. {
  936.     UNALIGNED WCHAR *src, *dst;
  937.     ULONG n;
  938.     if (SourceString != NULL) {
  939.         dst = DestinationString->Buffer;
  940.         src = SourceString->Buffer;
  941.         n = SourceString->Length;
  942.         if ((USHORT)n > DestinationString->MaximumLength) {
  943.             n = DestinationString->MaximumLength;
  944.         }
  945.         DestinationString->Length = (USHORT)n;
  946.         CopyMemory(dst, src, n);
  947.         if (DestinationString->Length < DestinationString->MaximumLength) {
  948.             dst[n / sizeof(WCHAR)] = UNICODE_NULL;
  949.         }
  950.     } else {
  951.         DestinationString->Length = 0;
  952.     }
  953.     return;
  954. }
  955.  
  956. NTSTATUS
  957. NTAPI
  958. Msv1_0SubAuthenticationFilter (
  959.     IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  960.     IN PVOID LogonInformation,
  961.     IN ULONG Flags,
  962.     IN PUSER_ALL_INFORMATION UserAll,
  963.     OUT PULONG WhichFields,
  964.     OUT PULONG UserFlags,
  965.     OUT PBOOLEAN Authoritative,
  966.     OUT PLARGE_INTEGER LogoffTime,
  967.     OUT PLARGE_INTEGER KickoffTime
  968. )
  969. {
  970.     return( Msv1_0SubAuthenticationRoutine(
  971.                 LogonLevel,
  972.                 LogonInformation,
  973.                 Flags,
  974.                 UserAll,
  975.                 WhichFields,
  976.                 UserFlags,
  977.                 Authoritative,
  978.                 LogoffTime,
  979.                 KickoffTime
  980.                 ) );
  981. }
  982. // subauth.c eof