home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / security / winnt / subauth / subauth.c < prev    next >
C/C++ Source or Header  |  1996-07-09  |  38KB  |  1,356 lines

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