|
|
/*++
Copyright (c) 1991-1993 Microsoft Corporation
Module Name:
rxuser.c
Abstract:
Routines in this module implement the down-level User and Modals UAS _access functionality
Contains RxNetUser routines: RxNetUserAdd RxNetUserDel RxNetUserEnum RxNetUserGetGroups RxNetUserGetInfo RxNetUserModalsGet RxNetUserModalsSet RxNetUserPasswordSet RxNetUserSetGroups RxNetUserSetInfo RxNetUserValidate2 (GetUserDescriptors) (GetModalsDescriptors) GetLanmanSessionKey
Author:
Richard Firth (rfirth) 20-May-1991
Environment:
Win-32/flat address space Requires ANSI C extensions: slash-slash comments, long external names, _strupr() function.
Notes:
Routines in this module assume that caller-supplied parameters have already been verified. No effort is made to further check the veracity of parms. Any actions causing exceptions must be trapped at a higher level. This applies to ALL parameters - strings, pointers, buffers, etc.
Revision History:
20-May-1991 RFirth Created 25-Sep-1991 JohnRo PC-LINT found a bug computing buflen for info level 11. Fixed UNICODE handling of buflen increments. Fixed MIPS build. Changed buflen name to bufsize to reflect NT/LAN naming convention. Made other changes suggested by PC-LINT. 21-Nov-1991 JohnRo Removed NT dependencies to reduce recompiles. 05-Dec-1991 RFirth Enum returns in TotalEntries (or EntriesLeft) the number of items to be enumerated BEFORE this call. Used to be number left after this call 01-Apr-1992 JohnRo Use NetApiBufferAllocate() instead of private version. 06-Apr-1992 JohnRo RAID 8927: usrmgr.exe: _access violation, memory corruption. (Fixed RxNetUserSetGroups when it called NetpMoveMemory.) 02-Apr-1993 JohnRo RAID 5098: DOS app NetUserPasswordSet to downlevel gets NT return code. Made some changes suggested by PC-LINT 5.0
--*/
#include <nt.h> // Needed by NetUserPasswordSet
#include <ntrtl.h> // Needed by NetUserPasswordSet
#include <nturtl.h> // RtlConvertUiListToApiList
#include <crypt.h> // Needed by NetUserPasswordSet
#include "downlevl.h"
#include <rxuser.h>
#include <lmaccess.h>
#include <stdlib.h> // wcslen().
#include <ntddnfs.h> // LMR_REQUEST_PACKET
#include <lmuse.h> // USE_IPC
#include <netlibnt.h> // NetpRdrFsControlTree
#include <loghours.h> // NetpRotateLogonHours
#include <accessp.h> // NetpConvertWorkstationList
//
// down-level encryption now on by default!
//
#define DOWN_LEVEL_ENCRYPTION
//
// Maximum size of the Workstation list
//
#define MAX_WORKSTATION_LIST 256
//
// local routine prototypes
//
DBGSTATIC NET_API_STATUS GetUserDescriptors( IN DWORD Level, IN BOOL Encrypted, OUT LPDESC* ppDesc16, OUT LPDESC* ppDesc32, OUT LPDESC* ppDescSmb );
DBGSTATIC VOID GetModalsDescriptors( IN DWORD Level, OUT LPDESC* ppDesc16, OUT LPDESC* ppDesc32, OUT LPDESC* ppDescSmb );
NET_API_STATUS GetLanmanSessionKey( IN LPWSTR ServerName, OUT LPBYTE pSessionKey );
//
// Down-level remote API worker routines
//
NET_API_STATUS RxNetUserAdd( IN LPTSTR ServerName, IN DWORD Level, IN LPBYTE Buffer, OUT LPDWORD ParmError OPTIONAL )
/*++
Routine Description:
Adds a user to a down-level UAS database.
Assumes 1. This code assumes that a USER_INFO_1 is a subset of a USER_INFO_2 and that the fields in a USER_INFO_1 map 1-to-1 to a USER_INFO_2 2. Level has already been range-checked
Arguments:
ServerName - at which down-level server to run the NetUserAdd API Level - of user info - 1 or 2 Buffer - containing info ParmError - where to deposit id of failing info level
Return Value:
NET_API_STATUS Success - NERR_Success Failure - (return code from down-level API)
--*/
{ LPDESC pDesc16; LPDESC pDesc32; LPDESC pDescSmb; DWORD buflen; DWORD badparm; DWORD len; DWORD pwdlen; CHAR ansiPassword[LM20_PWLEN+1]; DWORD lmOwfPasswordLen; LPTSTR cleartext; NET_API_STATUS NetStatus = NERR_Success;
#ifdef DOWN_LEVEL_ENCRYPTION
LM_OWF_PASSWORD lmOwfPassword; LM_SESSION_KEY lanmanKey; ENCRYPTED_LM_OWF_PASSWORD encryptedLmOwfPassword; NTSTATUS Status;
#endif
BYTE logonHours[21]; PBYTE callersLogonHours = NULL;
WCHAR Workstations[MAX_WORKSTATION_LIST+1]; LPWSTR callersWorkstations = NULL;
if (Level < 1 || Level > 2) { return ERROR_INVALID_LEVEL; }
if (ParmError == NULL) { ParmError = &badparm; } *ParmError = PARM_ERROR_NONE;
//
// calculate the size of the data to be transferred on the wire. See
// assumption in rubric. We also allow ourselves the luxury of trapping
// any strings which may break the down-level limits so we can return a
// nice parameter error number. If a string breaks a down-level limit, we
// just get back an ERROR_INVALID_PARAMETER, which is not very helpful
//
buflen = (Level == 1) ? sizeof(USER_INFO_1) : sizeof(USER_INFO_2);
len = POSSIBLE_WCSLEN(((PUSER_INFO_1)Buffer)->usri1_name); if (len > LM20_UNLEN) { *ParmError = USER_NAME_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1;
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_1)Buffer)->usri1_password)) { if (len > LM20_PWLEN) { *ParmError = USER_PASSWORD_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; } pwdlen = len;
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_1)Buffer)->usri1_home_dir)) { if (len > LM20_PATHLEN) { *ParmError = USER_HOME_DIR_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; }
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_1)Buffer)->usri1_comment)) { if (len > LM20_MAXCOMMENTSZ) { *ParmError = USER_COMMENT_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; }
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_1)Buffer)->usri1_script_path)) { if (len > LM20_PATHLEN) { *ParmError = USER_SCRIPT_PATH_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; }
if (Level == 2) { if (len = POSSIBLE_WCSLEN(((PUSER_INFO_2)Buffer)->usri2_full_name)) { if (len > LM20_MAXCOMMENTSZ) { *ParmError = USER_FULL_NAME_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; }
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_2)Buffer)->usri2_usr_comment)) { if (len > LM20_MAXCOMMENTSZ) { *ParmError = USER_USR_COMMENT_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; }
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_2)Buffer)->usri2_parms)) { if (len > LM20_MAXCOMMENTSZ) { *ParmError = USER_PARMS_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1; }
if (len = POSSIBLE_WCSLEN(((PUSER_INFO_2)Buffer)->usri2_workstations)) {
if (len > MAX_WORKSTATION_LIST) { *ParmError = USER_WORKSTATIONS_PARMNUM; return ERROR_INVALID_PARAMETER; } buflen += len + 1;
} }
if (pwdlen) {
//
// copy the cleartext password out of the buffer - we will replace it with
// the encrypted version, but need to put the cleartext back before
// returning control to the caller
//
cleartext = ((PUSER_INFO_1)Buffer)->usri1_password;
//
// Calculate the one-way function of the password
//
RtlUnicodeToMultiByteN(ansiPassword, sizeof(ansiPassword), &lmOwfPasswordLen, ((PUSER_INFO_1)Buffer)->usri1_password, pwdlen * sizeof(WCHAR) ); ansiPassword[lmOwfPasswordLen] = 0; (VOID) _strupr(ansiPassword);
#ifdef DOWN_LEVEL_ENCRYPTION
NetStatus = NERR_Success; Status = RtlCalculateLmOwfPassword(ansiPassword, &lmOwfPassword); if (NT_SUCCESS(Status)) { NetStatus = GetLanmanSessionKey((LPWSTR)ServerName, (LPBYTE)&lanmanKey); if (NetStatus == NERR_Success) { Status = RtlEncryptLmOwfPwdWithLmSesKey(&lmOwfPassword, &lanmanKey, &encryptedLmOwfPassword ); if (NT_SUCCESS(Status)) { ((PUSER_INFO_1)Buffer)->usri1_password = (LPTSTR)&encryptedLmOwfPassword; } } } if (NetStatus != NERR_Success) return NetStatus; else if (!NT_SUCCESS(Status)) { return RtlNtStatusToDosError(Status); }
#else
((PUSER_INFO_1)Buffer)->usri1_password = (LPTSTR)ansiPassword;
#endif
} else { lmOwfPasswordLen = 0; }
//
// we have checked all the parms we can. If any other parameter breaks at
// the down-level server then we just have to be content with an unknown
// parameter error
//
*ParmError = PARM_ERROR_UNKNOWN;
#ifdef DOWN_LEVEL_ENCRYPTION
NetStatus = GetUserDescriptors(Level, TRUE, &pDesc16, &pDesc32, &pDescSmb);
#else
NetStatus = GetUserDescriptors(Level, FALSE, &pDesc16, &pDesc32, &pDescSmb);
#endif
if (NetStatus != NERR_Success) { //
// Copy the original password back to the user's buffer
//
if (pwdlen) { ((PUSER_INFO_1) Buffer)->usri1_password = cleartext; }
return NetStatus; }
//
// if this level supports logon hours, then convert the caller supplied
// logon hours from GMT to local time
//
if (Level == 2 && ((PUSER_INFO_2)Buffer)->usri2_logon_hours) { callersLogonHours = ((PUSER_INFO_2)Buffer)->usri2_logon_hours; RtlCopyMemory(logonHours, ((PUSER_INFO_2)Buffer)->usri2_logon_hours, sizeof(logonHours) );
//
// shuffle the bitmap and point the logon_hours field in the structure
// at the shuffled version
//
NetpRotateLogonHours(logonHours, UNITS_PER_WEEK, FALSE); ((PUSER_INFO_2)Buffer)->usri2_logon_hours = logonHours; }
//
// Convert the list of workstations from being comma separated
// to being space separated. Ditch workstation names containing
// spaces.
if (Level == 2 && ((PUSER_INFO_2)Buffer)->usri2_workstations) { UNICODE_STRING WorkstationString;
callersWorkstations = ((PUSER_INFO_2)Buffer)->usri2_workstations; wcsncpy( Workstations, callersWorkstations, MAX_WORKSTATION_LIST );
RtlInitUnicodeString( &WorkstationString, Workstations ); NetpConvertWorkstationList( &WorkstationString );
((PUSER_INFO_2)Buffer)->usri2_workstations = Workstations;
}
NetStatus = NERR_Success; NetStatus = RxRemoteApi(API_WUserAdd2, // which API it is
ServerName, // which server its at
REMSmb_NetUserAdd2_P, // parameter descriptor
pDesc16, pDesc32, pDescSmb, // data descriptors
NULL, NULL, NULL, // no aux descriptors reqd.
FALSE, // need to be logged on
Level, // caller parms
Buffer, buflen, // and one we created
#ifdef DOWN_LEVEL_ENCRYPTION
1, // encryption on
lmOwfPasswordLen // length of cleartext
#else
0, pwdlen
#endif
);
//
// copy the original password back to the user's buffer
//
if (pwdlen) { ((PUSER_INFO_1)Buffer)->usri1_password = cleartext; }
//
// and the original logon hours
//
if (callersLogonHours) { ((PUSER_INFO_2)Buffer)->usri2_logon_hours = callersLogonHours; }
//
// and the original workstation list
//
if ( callersWorkstations ) { ((PUSER_INFO_2)Buffer)->usri2_workstations = callersWorkstations; }
return NetStatus; }
NET_API_STATUS RxNetUserDel( IN LPTSTR ServerName, IN LPTSTR UserName )
/*++
Routine Description:
Removes a user from a down-level server's UAS (User Account Subsystem) database
Assumes 1. UserName has been checked for valid pointer to valid string
Arguments:
ServerName - on which (down-level) server to run the API UserName - which user to delete
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_PARAMETER UserName > LM20_UNLEN (return code from remoted NetUserDel)
--*/
{ if (STRLEN(UserName) > LM20_UNLEN) { return ERROR_INVALID_PARAMETER; }
return RxRemoteApi(API_WUserDel, // api being remoted
ServerName, // where to remote it
REMSmb_NetUserDel_P, // parameter descriptor
NULL, NULL, NULL, // data descriptors
NULL, NULL, NULL, // aux data descriptors
FALSE, // this call needs user logged on
UserName // remote API parms...
); }
NET_API_STATUS RxNetUserEnum( IN LPTSTR ServerName, IN DWORD Level, OUT LPBYTE* Buffer, IN DWORD PrefMaxLen, OUT LPDWORD EntriesRead, OUT LPDWORD EntriesLeft, IN OUT LPDWORD ResumeHandle OPTIONAL )
/*++
Routine Description:
Returns user information from a down-level server's UAS database
Arguments:
ServerName - where to run the API Level - of info required - 0, 1, 2, or 10 Buffer - place to return a pointer to an allocated buffer PrefMaxLen - caller's preferred maximum buffer size EntriesRead - number of <Level> info entries being returned in the buffer EntriesLeft - number of entries left after this one ResumeHandle- used to resume enumeration (Ignored)
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_PARAMETER non-NULL ResumeHandle ERROR_NOT_ENOUGH_MEMORY NetApiBufferAllocate failed (?!) (return code from down-level API)
--*/
{ LPDESC pDesc16; LPDESC pDesc32; LPDESC pDescSmb; NET_API_STATUS rc; LPBYTE bufptr; DWORD entries_read, total_avail; DWORD last_resume_handle, new_resume_handle = 0;
*EntriesRead = *EntriesLeft = 0; *Buffer = NULL;
rc = GetUserDescriptors(Level, FALSE, &pDesc16, &pDesc32, &pDescSmb); if (rc != NO_ERROR) { return(rc); } bufptr = NULL;
//
// try NetUserEnum2 (supports resume handle) with the requested amount
// of data. If the down-level server doesn't support this API then try
// NetUserEnum
//
if (ARGUMENT_PRESENT(ResumeHandle)) { last_resume_handle = *ResumeHandle; } else { last_resume_handle = 0; }
//
// irrespective of whether we can resume the enumeration, down-level
// servers can't generate >64K-1 bytes of data
//
if (PrefMaxLen > 65535) { PrefMaxLen = 65535; } rc = RxRemoteApi(API_WUserEnum2, ServerName, REMSmb_NetUserEnum2_P, pDesc16, pDesc32, pDescSmb, NULL, NULL, NULL, ALLOCATE_RESPONSE, // RxRemoteApi allocates buffer
Level, &bufptr, PrefMaxLen, // size of caller's buffer
last_resume_handle, // last key returned
&new_resume_handle, // returns this key
&entries_read, // number returned
&total_avail // total available at server
);
//
// WinBall returns ERROR_NOT_SUPPORTED. LM < 2.1 returns NERR_InvalidAPI?
// WinBall returns ERROR_NOT_SUPPORTED because it is share level, so this
// whole API fails. Therefore, no need to account (no pun intended) for
// WinBall
//
//
// RLF 10/01/92. Seemingly, IBM LAN Server returns Internal Error (2140).
// We'll handle that one too....
//
if (rc == NERR_InvalidAPI || rc == NERR_InternalError) {
//
// the down-level server doesn't support NetUserEnum2. Fall-back to
// NetUserEnum & try to get as much data as available
//
rc = RxRemoteApi(API_WUserEnum, ServerName, REMSmb_NetUserEnum_P, pDesc16, pDesc32, pDescSmb, NULL, NULL, NULL, ALLOCATE_RESPONSE, // RxRemoteApi allocates buffer
Level, &bufptr, 65535, // get as much data as possible
&entries_read, &total_avail ); } else if (rc == NERR_Success || rc == ERROR_MORE_DATA) {
//
// return the resume handle if NetUserEnum2 succeeded & the caller
// supplied a ResumeHandle parameter
//
if (ARGUMENT_PRESENT(ResumeHandle)) { *ResumeHandle = new_resume_handle; } } if (rc && rc != ERROR_MORE_DATA) { if (bufptr != NULL) { (void) NetApiBufferFree(bufptr); } } else {
//
// if level supports logon hours, convert from local time to GMT. Level
// 2 is the only level of user info handled by this routine that knows
// about logon hours
//
// if level support workstation list, convert from blank separated to
// comma separated list. Level 2 is the only level of user info
// handled by this routine that know about the workstation list.
//
if (Level == 2) {
DWORD numRead; LPUSER_INFO_2 ptr = (LPUSER_INFO_2)bufptr;
for (numRead = entries_read; numRead; --numRead) { NetpRotateLogonHours(ptr->usri2_logon_hours, UNITS_PER_WEEK, TRUE); if ( ptr->usri2_workstations != NULL ) { UNICODE_STRING BlankSeparated; UNICODE_STRING CommaSeparated; NTSTATUS Status;
RtlInitUnicodeString( &BlankSeparated, ptr->usri2_workstations );
Status = RtlConvertUiListToApiList( &BlankSeparated, &CommaSeparated, TRUE ); // Allow Blanks as delimiters
if ( !NT_SUCCESS(Status)) { return RtlNtStatusToDosError(Status); }
if ( CommaSeparated.Length > 0 ) { NetpAssert ( wcslen( ptr->usri2_workstations ) <= wcslen( CommaSeparated.Buffer ) ); if ( wcslen( ptr->usri2_workstations ) <= wcslen( CommaSeparated.Buffer ) ) { wcscpy( ptr->usri2_workstations, CommaSeparated.Buffer ); } }
} ++ptr; } } *Buffer = bufptr; *EntriesRead = entries_read; *EntriesLeft = total_avail; } return rc; }
NET_API_STATUS RxNetUserGetGroups( IN LPTSTR ServerName, IN LPTSTR UserName, IN DWORD Level, OUT LPBYTE* Buffer, IN DWORD PrefMaxLen, OUT LPDWORD EntriesRead, OUT LPDWORD EntriesLeft )
/*++
Routine Description:
Get the list of groups in a UAS database to which a particular user belongs
Arguments:
ServerName - where to run the API UserName - which user to get info for Level - of info requested - Must Be Zero Buffer - where to deposit the buffer we allocate containing the info PrefMaxLen - caller's preferred maximum buffer size EntriesRead - number of entries being returned in Buffer EntriesLeft - number of entries left to get
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_LEVEL ERROR_INVALID_PARAMETER
--*/
{ NET_API_STATUS rc; DWORD entries_read, total_avail; LPBYTE bufptr;
UNREFERENCED_PARAMETER(Level); UNREFERENCED_PARAMETER(PrefMaxLen);
*EntriesRead = *EntriesLeft = 0; *Buffer = NULL;
if (STRLEN(UserName) > LM20_UNLEN) { return ERROR_INVALID_PARAMETER; }
bufptr = NULL; rc = RxRemoteApi(API_WUserGetGroups, ServerName, REMSmb_NetUserGetGroups_P, REM16_user_info_0, REM32_user_info_0, REMSmb_user_info_0, NULL, NULL, NULL, ALLOCATE_RESPONSE, UserName, // API parameters
0, // fixed level
&bufptr, 65535, &entries_read, &total_avail // supplied by us
); if (rc) { if (bufptr != NULL) { (void) NetApiBufferFree(bufptr); } } else { *Buffer = bufptr; *EntriesRead = entries_read; *EntriesLeft = total_avail; } return rc; }
NET_API_STATUS RxNetUserGetInfo( IN LPTSTR ServerName, IN LPTSTR UserName, IN DWORD Level, OUT LPBYTE* Buffer )
/*++
Routine Description:
Get information about a particular user from a down-level server
Assumes: 1. UserName is a valid pointer to a valid string
Arguments:
ServerName - where to run the API UserName - which user to get info on Level - what level of info required - 0, 1, 2, 10, 11 Buffer - where to return buffer containing info
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_LEVEL ERROR_INVALID_PARAMETER
--*/
{ LPDESC pDesc16; LPDESC pDesc32; LPDESC pDescSmb; DWORD buflen; LPBYTE bufptr; DWORD total_avail; NET_API_STATUS rc;
*Buffer = NULL;
if (STRLEN(UserName) > LM20_UNLEN) { return ERROR_INVALID_PARAMETER; }
//
// work out the anount of buffer space we need to return the down-level
// structure as its 32-bit equivalent
//
switch (Level) { case 0: buflen = sizeof(USER_INFO_0) + STRING_SPACE_REQD(UNLEN + 1); break;
case 1: buflen = sizeof(USER_INFO_1) + STRING_SPACE_REQD(UNLEN + 1) // usri1_name
+ STRING_SPACE_REQD(ENCRYPTED_PWLEN) // usri1_password
+ STRING_SPACE_REQD(LM20_PATHLEN + 1) // usri1_home_dir
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri1_comment
+ STRING_SPACE_REQD(LM20_PATHLEN + 1); // usri1_script_path
break;
case 2: buflen = sizeof(USER_INFO_2) + STRING_SPACE_REQD(UNLEN + 1) // usri2_name
+ STRING_SPACE_REQD(ENCRYPTED_PWLEN) // usri2_password
+ STRING_SPACE_REQD(LM20_PATHLEN + 1) // usri2_home_dir
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri2_comment
+ STRING_SPACE_REQD(LM20_PATHLEN + 1) // usri2_script_path
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri2_full_name
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri2_usr_comment
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri2_parms
+ STRING_SPACE_REQD(MAX_WORKSTATION_LIST) // usri2_workstations
+ STRING_SPACE_REQD(MAX_PATH + 1) // usri2_logon_server
+ 21; // usri2_logon_hours
break;
case 10: buflen = sizeof(USER_INFO_10) + STRING_SPACE_REQD(UNLEN + 1) // usri10_name
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri10_comment
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri10_usr_comment
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1); // usri10_full_name
break;
case 11: buflen = sizeof(USER_INFO_11) + STRING_SPACE_REQD(UNLEN + 1) // usri11_name
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri11_comment
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri11_usr_comment
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri11_full_name
+ STRING_SPACE_REQD(LM20_PATHLEN + 1) // usri11_home_dir
+ STRING_SPACE_REQD(LM20_MAXCOMMENTSZ + 1) // usri11_parms
+ STRING_SPACE_REQD(MAX_PATH + 1) // usri11_logon_server
+ STRING_SPACE_REQD(MAX_WORKSTATION_LIST) // usri11_workstations
+ 21; // usri11_logon_hours
break;
default: return(ERROR_INVALID_LEVEL); } buflen = DWORD_ROUNDUP(buflen); if (rc = NetApiBufferAllocate(buflen, (LPVOID *) &bufptr)) { return rc; } (void)GetUserDescriptors(Level, FALSE, &pDesc16, &pDesc32, &pDescSmb); rc = RxRemoteApi(API_WUserGetInfo, ServerName, REMSmb_NetUserGetInfo_P, pDesc16, pDesc32, pDescSmb, NULL, NULL, NULL, FALSE, UserName, Level, bufptr, buflen, &total_avail ); if (rc) { (void) NetApiBufferFree(bufptr); } else {
//
// Convert the logon hours bitmap to UTC/GMT
// Convert the workstation list from blank separated to comma separated
//
if (Level == 2 || Level == 11) {
PBYTE logonHours; LPWSTR Workstations;
if (Level == 2) { logonHours = ((PUSER_INFO_2)bufptr)->usri2_logon_hours; Workstations = ((PUSER_INFO_2)bufptr)->usri2_workstations; } else { logonHours = ((PUSER_INFO_11)bufptr)->usri11_logon_hours; Workstations = ((PUSER_INFO_11)bufptr)->usri11_workstations; } NetpRotateLogonHours(logonHours, UNITS_PER_WEEK, TRUE);
if ( Workstations != NULL ) { UNICODE_STRING BlankSeparated; UNICODE_STRING CommaSeparated; NTSTATUS Status;
RtlInitUnicodeString( &BlankSeparated, Workstations );
Status = RtlConvertUiListToApiList( &BlankSeparated, &CommaSeparated, TRUE ); // Allow Blanks as delimiters
if ( !NT_SUCCESS(Status)) { return RtlNtStatusToDosError(Status); }
if ( CommaSeparated.Length > 0 ) { NetpAssert ( wcslen( Workstations ) <= wcslen( CommaSeparated.Buffer ) ); if ( wcslen(Workstations) <= wcslen(CommaSeparated.Buffer)){ wcscpy( Workstations, CommaSeparated.Buffer ); } }
} } *Buffer = bufptr; } return rc; }
NET_API_STATUS RxNetUserModalsGet( IN LPTSTR ServerName, IN DWORD Level, OUT LPBYTE* Buffer )
/*++
Routine Description:
Returns global information about all users and groups in a down-level UAS database
Assumes 1. Level has been validated
Arguments:
ServerName - where to run the API Level - of info required - 0 or 1 Buffer - where to deposit the returned info
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_LEVEL ERROR_INVALID_PARAMETER
--*/
{ LPDESC pDesc16; LPDESC pDesc32; LPDESC pDescSmb; DWORD buflen; LPBYTE bufptr; NET_API_STATUS rc; DWORD total_avail;
if (Level > 1) { return ERROR_INVALID_LEVEL; }
*Buffer = NULL; buflen = Level ? sizeof(USER_MODALS_INFO_1) : sizeof(USER_MODALS_INFO_0); buflen += Level ? STRING_SPACE_REQD(MAX_PATH + 1) : 0; buflen = DWORD_ROUNDUP(buflen); if (rc = NetApiBufferAllocate(buflen, (LPVOID *) &bufptr)) { return rc; } GetModalsDescriptors(Level, &pDesc16, &pDesc32, &pDescSmb); rc = RxRemoteApi(API_WUserModalsGet, ServerName, REMSmb_NetUserModalsGet_P, pDesc16, pDesc32, pDescSmb, NULL, NULL, NULL, FALSE, Level, bufptr, buflen, &total_avail ); if (rc) { (void) NetApiBufferFree(bufptr); } else { *Buffer = bufptr; } return rc; }
NET_API_STATUS RxNetUserModalsSet( IN LPTSTR ServerName, IN DWORD Level, IN LPBYTE Buffer, OUT LPDWORD ParmError OPTIONAL )
/*++
Routine Description:
Sets global information for all users and groups in a down-level UAS database
Assumes 1. Level parameter already verified
Arguments:
ServerName - where to run the API Level - level of information being supplied - 0, 1, 1001-1007 Buffer - pointer to buffer containing input information ParmError - pointer to place to store index of failing info
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_PARAMETER One of the fields in the input structure was invalid
--*/
{ DWORD parmnum; DWORD badparm; DWORD buflen; LPDESC pDesc16; LPDESC pDesc32; LPDESC pDescSmb;
//
// check for bad addresses and set ParmError to a known default
//
if (ParmError == NULL) { ParmError = &badparm; } *ParmError = PARM_ERROR_NONE;
if (Level) { if (Level == 1) { parmnum = PARMNUM_ALL; buflen = sizeof(USER_MODALS_INFO_1) + POSSIBLE_STRLEN(((PUSER_MODALS_INFO_1)Buffer)->usrmod1_primary); } else {
//
// Convert info levels 1006, 1007 to corresponding parmnums (1, 2)
// at old info level 1
//
if (Level >= MODALS_ROLE_INFOLEVEL) { parmnum = Level - (MODALS_ROLE_INFOLEVEL - 1); Level = 1; switch (parmnum) { case 1: // MODALS_ROLE_PARMNUM
buflen = sizeof(DWORD); break;
case 2: // MODALS_PRIMARY_PARMNUM
buflen = STRLEN( (LPTSTR) Buffer); if (buflen > MAX_PATH) { *ParmError = MODALS_PRIMARY_INFOLEVEL; return ERROR_INVALID_PARAMETER; } break;
default: #if DBG
NetpKdPrint(("error: RxNetUserModalsSet.%d: bad parmnum %d\n", __LINE__, parmnum )); #endif
return ERROR_INVALID_LEVEL; } } else if (Level >= MODALS_MIN_PASSWD_LEN_INFOLEVEL) {
//
// Convert info levels 1001-1005 to equivalent parmnums at
// level 0
//
parmnum = Level - PARMNUM_BASE_INFOLEVEL; Level = 0; buflen = sizeof(DWORD); } else { #if DBG
NetpKdPrint(("error: RxNetUserModalsSet.%d: bad level %d\n", __LINE__, Level )); #endif
return ERROR_INVALID_LEVEL; } } } else { parmnum = PARMNUM_ALL; buflen = sizeof(USER_MODALS_INFO_0); }
*ParmError = PARM_ERROR_UNKNOWN; GetModalsDescriptors(Level, &pDesc16, &pDesc32, &pDescSmb); return RxRemoteApi(API_WUserModalsSet, ServerName, REMSmb_NetUserModalsSet_P, pDesc16, pDesc32, pDescSmb, NULL, NULL, NULL, FALSE, Level, // API parms
Buffer, buflen, // supplied by us
MAKE_PARMNUM_PAIR(parmnum, parmnum) // ditto
); }
NET_API_STATUS RxNetUserPasswordSet( IN LPTSTR ServerName, IN LPTSTR UserName, IN LPTSTR OldPassword, IN LPTSTR NewPassword )
/*++
Routine Description:
Changes the password associated with a user account in a down-level UAS database
Assumes 1. The pointer parameters have already been verified
Arguments:
ServerName - where to change the password UserName - which user account to change it for OldPassword - the current password NewPassword - the new password
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_PARAMETER UserName, OldPassword or NewPassword would break down-level limits
--*/
{ NTSTATUS Status; NET_API_STATUS NetStatus; BOOL TryNullSession = TRUE; // Try null session first.
ULONG BytesWritten;
#ifdef DOWN_LEVEL_ENCRYPTION
CHAR OldAnsiPassword[LM20_PWLEN+1]; CHAR NewAnsiPassword[LM20_PWLEN+1]; LM_OWF_PASSWORD OldOwfPassword; LM_OWF_PASSWORD NewOwfPassword; ENCRYPTED_LM_OWF_PASSWORD OldEncryptedWithNew; ENCRYPTED_LM_OWF_PASSWORD NewEncryptedWithOld;
#else
CHAR OldAnsiPassword[ENCRYPTED_PWLEN]; CHAR NewAnsiPassword[ENCRYPTED_PWLEN];
#endif
//
// Reel in some easy errors before they get far.
//
if ((STRLEN(UserName) > LM20_UNLEN) || (STRLEN(OldPassword) > LM20_PWLEN) || (STRLEN(NewPassword) > LM20_PWLEN)) { return ERROR_INVALID_PARAMETER; }
//
// The passwords are sent in 16-byte ANSI buffers,
// so convert them from Unicode to multibyte.
//
#ifndef DOWN_LEVEL_ENCRYPTION
//
// this required because we always send fixed size char buffers, not strings
//
RtlSecureZeroMemory(OldAnsiPassword, sizeof(OldAnsiPassword)); RtlSecureZeroMemory(NewAnsiPassword, sizeof(NewAnsiPassword));
#endif
RtlUnicodeToMultiByteN( OldAnsiPassword, sizeof(OldAnsiPassword), &BytesWritten, OldPassword, wcslen(OldPassword) * sizeof(WCHAR) ); OldAnsiPassword[BytesWritten] = 0;
RtlUnicodeToMultiByteN( NewAnsiPassword, sizeof(NewAnsiPassword), &BytesWritten, NewPassword, wcslen(NewPassword) * sizeof(WCHAR) ); NewAnsiPassword[BytesWritten] = 0;
//
// twould seem that down-level servers require passwords to be in upper
// case (ie canonicalized) when they are decrypted. Same applies for
// cleartext
//
(VOID) _strupr(OldAnsiPassword); (VOID) _strupr(NewAnsiPassword);
#ifdef DOWN_LEVEL_ENCRYPTION
//
// Calculate the one-way functions of the passwords.
//
Status = RtlCalculateLmOwfPassword(OldAnsiPassword, &OldOwfPassword); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = RtlCalculateLmOwfPassword(NewAnsiPassword, &NewOwfPassword); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Cross-encrypt the passwords.
//
Status = RtlEncryptLmOwfPwdWithLmOwfPwd(&OldOwfPassword, &NewOwfPassword, &OldEncryptedWithNew ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = RtlEncryptLmOwfPwdWithLmOwfPwd(&NewOwfPassword, &OldOwfPassword, &NewEncryptedWithOld ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
#else
//
// Status hasn't been initialized, but is tested below to determine if we
// should pick up the NetStatus from Status.
//
Status = STATUS_SUCCESS;
#endif // DOWN_LEVEL_ENCRYPTION
TryTheEncryptedApi:
NetStatus = RxRemoteApi(API_WUserPasswordSet2, ServerName, REMSmb_NetUserPasswordSet2_P, NULL, NULL, NULL, // no data - just parms
NULL, NULL, NULL, // no aux data
(TryNullSession ? NO_PERMISSION_REQUIRED : 0), UserName, // parameters...
#ifdef DOWN_LEVEL_ENCRYPTION
&OldEncryptedWithNew, &NewEncryptedWithOld, TRUE, // data encrypted?
#else
OldAnsiPassword, NewAnsiPassword, FALSE, // passwords not encrypted
#endif
strlen(NewAnsiPassword) );
//
// LarryO says null session might have wrong credentials, so we
// should retry with non-null session.
//
if ( TryNullSession && (Status == ERROR_SESSION_CREDENTIAL_CONFLICT) ) {
TryNullSession = FALSE; goto TryTheEncryptedApi; // retry this one.
}
//
// If the encrypted attempt fails with NERR_InvalidAPI, try plaintext
//
if (NetStatus == NERR_InvalidAPI) {
TryThePlainTextApi:
TryNullSession = TRUE; // Try null session first.
NetStatus = RxRemoteApi(API_WUserPasswordSet, ServerName, REMSmb_NetUserPasswordSet_P, NULL, NULL, NULL, // no data - just parms
NULL, NULL, NULL, // no aux data
(TryNullSession ? NO_PERMISSION_REQUIRED : 0), UserName, // parameters...
OldAnsiPassword, NewAnsiPassword, FALSE // data encrypted?
); //
// LarryO says null session might have wrong credentials, so we
// should retry with non-null session.
//
if ( TryNullSession && (Status == ERROR_SESSION_CREDENTIAL_CONFLICT) ) {
TryNullSession = FALSE; goto TryThePlainTextApi; // retry this one.
} }
#ifdef DOWN_LEVEL_ENCRYPTION
Cleanup:
#endif
if (!NT_SUCCESS(Status)) { NetStatus = RtlNtStatusToDosError(Status); }
return NetStatus; }
NET_API_STATUS RxNetUserSetGroups( IN LPTSTR ServerName, IN LPTSTR UserName, IN DWORD Level, IN LPBYTE Buffer, IN DWORD Entries )
/*++
Routine Description:
Makes a user a member of the listed groups. This routine is virtually identical to RxNetGroupSetUsers and most of the code was lifted from there
Arguments:
ServerName - where to run the API UserName - which user to include Level - Must Be Zero (MBZ) Buffer - pointer to buffer containing a list of GROUP_INFO_0 structures Entries - number of GROUP_INFO_0 structures in Buffer
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_LEVEL ERROR_INVALID_PARAMETER
--*/
{ NET_API_STATUS rc; LPGROUP_INFO_0 group_info; DWORD i; DWORD buflen; LPBYTE newbuf; static LPDESC group_0_enumerator_desc16 = "B21BN"; // same as UNLEN
static LPDESC group_0_enumerator_desc32 = "zQA";
//
// This structure is required because the remoting code (particularly down
// level) can only handle there being >1 auxiliary structure, vs >1
// primary. Hence we have to convert the caller's supplied buffer of
// erstwhile primary structures to auxiliaries by forcing the structure
// below in at the head of the buffer, hence becoming the primary and
// providing an aux structure count (groan)
//
struct group_0_enumerator { LPTSTR user_name; // which user to set groups for
DWORD group_count; // number of GROUP_INFO_0 structures in buffer
};
if (Level) { return ERROR_INVALID_LEVEL; // MBZ, remember?
}
if (STRLEN(UserName) > LM20_UNLEN) { return ERROR_INVALID_PARAMETER; }
//
// iterate through the buffer, checking that each GROUP_INFO_0
// structure contains a pointer to a valid string which is in the
// correct range
//
group_info = (LPGROUP_INFO_0)Buffer; for (i=0; i<Entries; ++i) { if (!VALID_STRING(group_info->grpi0_name)) { return ERROR_INVALID_PARAMETER; } if (wcslen(group_info->grpi0_name) > LM20_GNLEN) { return ERROR_INVALID_PARAMETER; } ++group_info; }
//
// allocate a buffer large enough to fit in <Entries> number of
// GROUP_INFO_0 structures, and 1 group_0_enumerator structure.
// Don't worry about string space - unfortunately the Rxp and Rap routines
// called by RxRemoteApi will allocate yet another buffer, do yet another
// copy and this time copy in the strings from user space. Hopefully, this
// routine won't get called too often
//
buflen = Entries * sizeof(GROUP_INFO_0) + sizeof(struct group_0_enumerator); buflen = DWORD_ROUNDUP(buflen); if (rc = NetApiBufferAllocate(buflen, (LPVOID *) &newbuf)) { return rc; // aieegh! Failed to allocate memory?
}
((struct group_0_enumerator*)newbuf)->user_name = UserName; ((struct group_0_enumerator*)newbuf)->group_count = Entries;
if (Entries > 0) { // Append the group entries to the header we just built.
NetpMoveMemory( newbuf + sizeof(struct group_0_enumerator), // dest
Buffer, // src
buflen - sizeof(struct group_0_enumerator)); // byte count
}
rc = RxRemoteApi(API_WUserSetGroups, ServerName, REMSmb_NetUserSetGroups_P, group_0_enumerator_desc16, // the "fudged" 16-bit data descriptor
group_0_enumerator_desc32, // the "fudged" 32-bit data descriptor
group_0_enumerator_desc16, // SMB desc same as 16-bit
REM16_group_info_0, // "new" 16-bit aux descriptor
REM32_group_info_0, // "new" 32-bit aux descriptor
REMSmb_group_info_0, // SMB aux descriptor
FALSE, // this API requires user security
UserName, // parm 1
0, // info level must be 0
newbuf, // "fudged" buffer
buflen, // length of "fudged" buffer
Entries // number of GROUP_USERS_INFO_0
); NetpMemoryFree(newbuf); return rc; }
NET_API_STATUS RxNetUserSetInfo( IN LPTSTR ServerName, IN LPTSTR UserName, IN DWORD Level, IN LPBYTE Buffer, OUT LPDWORD ParmError OPTIONAL )
/*++
Routine Description:
Sets information in a user account in a down-level UAS database
Assumes: 1. UserName is a valid pointer to a valid string, Level is in the range below, Buffer is a valid pointer ParmError is a valid pointer
Arguments:
ServerName - where to run the API UserName - which user to change info for Level - of info supplied - 1-2, 1003, 1005-1014, 1017-1018, 1020, 1023-1025 Buffer - if PARMNUM_ALL, pointer to buffer containing info, else pointer to pointer to buffer containing info ParmError - which parameter was bad
Return Value:
NET_API_STATUS Success - NERR_Success Failure - ERROR_INVALID_LEVEL ERROR_INVALID_PARAMETER
--*/
{ DWORD parmnum; DWORD badparm; DWORD buflen; DWORD stringlen; LPWSTR pointer; // general pointer to string for range checking
LPDESC pDesc16; LPDESC pDesc32; LPDESC pDescSmb; DWORD passwordEncrypted = FALSE; DWORD originalPasswordLength = 0; CHAR ansiPassword[LM20_PWLEN+1]; DWORD lmOwfPasswordLen; LPTSTR cleartext; LPTSTR* lpClearText; NET_API_STATUS NetStatus = NERR_Success;
#ifdef DOWN_LEVEL_ENCRYPTION
LM_OWF_PASSWORD lmOwfPassword; LM_SESSION_KEY lanmanKey; ENCRYPTED_LM_OWF_PASSWORD encryptedLmOwfPassword; NTSTATUS Status;
#endif
BYTE logonHours[21]; PBYTE callersLogonHours = NULL; PBYTE* lpCallersLogonHours;
WCHAR Workstations[MAX_WORKSTATION_LIST+1]; LPWSTR callersWorkstations = NULL; LPWSTR *lpCallersWorkstations;
if (ParmError == NULL) { ParmError = &badparm; } *ParmError = PARM_ERROR_NONE;
if (STRLEN(UserName) > LM20_UNLEN) { NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; }
//
// throw out invalid level parameter
//
if ((Level > 2 && Level < USER_PASSWORD_INFOLEVEL)
// 2 < Level < 1003
|| (Level > USER_PASSWORD_INFOLEVEL && Level < USER_PRIV_INFOLEVEL)
// 1003 < Level < 1005 : Check compiler generates == 1004
|| (Level > USER_WORKSTATIONS_INFOLEVEL && Level < USER_ACCT_EXPIRES_INFOLEVEL)
// 1014 < Level < 1017
|| (Level > USER_MAX_STORAGE_INFOLEVEL && Level < USER_LOGON_HOURS_INFOLEVEL)
// 1018 < Level < 1020 : Check compiler generates == 1019
|| (Level > USER_LOGON_HOURS_INFOLEVEL && Level < USER_LOGON_SERVER_INFOLEVEL)
// 1020 < Level < 1023
|| (Level > USER_CODE_PAGE_INFOLEVEL)) {
// Level < 1025
NetStatus = ERROR_INVALID_LEVEL; goto Cleanup; }
//
// default to Level 2 for the descriptors (Level 2 works for level 1 also)
//
pDesc16 = REM16_user_info_2; pDesc32 = REM32_user_info_2; pDescSmb = REMSmb_user_info_2;
if (Level < PARMNUM_BASE_INFOLEVEL) { parmnum = PARMNUM_ALL; if (Level == 1) { pDesc16 = REM16_user_info_1; pDesc32 = REM32_user_info_1; pDescSmb = REMSmb_user_info_1; buflen = sizeof(USER_INFO_1); } else {
buflen = sizeof(USER_INFO_2) + 21; } } else { parmnum = Level - PARMNUM_BASE_INFOLEVEL;
//
// Because info level 1 is a subset of info level 2, setting the level
// to 2 is ok for those parmnums which can be set at level 1 AND 2.
// Set pointer = Buffer so that in the parmnum != PARMNUM_ALL case, we
// just check the length of whatever pointer points at
//
Level = 2; pointer = *(LPWSTR*) Buffer; buflen = 0; }
if (parmnum == PARMNUM_ALL) { if (pointer = ((PUSER_INFO_1)(LPVOID)Buffer)->usri1_name) { if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_UNLEN) { *ParmError = USER_NAME_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); } }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_PASSWORD_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_1)Buffer)->usri1_password; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_PWLEN) { *ParmError = USER_PASSWORD_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1);
//
// original password length is length of unencrypted string in
// characters, excluding terminating NUL
//
originalPasswordLength = stringlen;
//
// lpClearText is address of pointer to cleartext password
//
lpClearText = (parmnum == PARMNUM_ALL) ? (LPTSTR*)&((PUSER_INFO_1)Buffer)->usri1_password : (LPTSTR*)Buffer;
//
// copy the cleartext password out of the buffer - we will replace it with
// the encrypted version, but need to put the cleartext back before
// returning control to the caller
//
cleartext = *lpClearText; }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_HOME_DIR_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_1)Buffer)->usri1_home_dir; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_PATHLEN) { *ParmError = USER_HOME_DIR_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_COMMENT_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_1)Buffer)->usri1_comment; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_MAXCOMMENTSZ) { *ParmError = USER_COMMENT_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_SCRIPT_PATH_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_1)Buffer)->usri1_script_path; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_PATHLEN) { *ParmError = USER_SCRIPT_PATH_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
//
// the next set of checks only need to be done if we are setting PARMNUM_ALL
// with a Level of 2 or if the parmnum implicitly requires Level 2 (ie parms
// >= 10)
//
if (Level == 2) { if ((parmnum == PARMNUM_ALL) || (parmnum == USER_FULL_NAME_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_2)Buffer)->usri2_full_name; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_MAXCOMMENTSZ) { *ParmError = USER_FULL_NAME_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_USR_COMMENT_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_2)Buffer)->usri2_usr_comment; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_MAXCOMMENTSZ) { *ParmError = USER_USR_COMMENT_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_PARMS_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_2)Buffer)->usri2_parms; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > LM20_MAXCOMMENTSZ) { *ParmError = USER_PARMS_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_WORKSTATIONS_PARMNUM)) { UNICODE_STRING WorkstationString; if (parmnum == PARMNUM_ALL) { lpCallersWorkstations = &((PUSER_INFO_2)Buffer)->usri2_workstations; } else { lpCallersWorkstations = &((PUSER_INFO_1014)Buffer)->usri1014_workstations; } callersWorkstations = *lpCallersWorkstations;
if ((stringlen = POSSIBLE_WCSLEN(callersWorkstations)) > MAX_WORKSTATION_LIST) { *ParmError = USER_WORKSTATIONS_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1);
//
// Convert the list of workstations from being comma separated
// to being space separated. Ditch workstation names containing
// spaces.
if ( callersWorkstations != NULL ) { wcsncpy( Workstations, callersWorkstations, MAX_WORKSTATION_LIST ); RtlInitUnicodeString( &WorkstationString, Workstations ); NetpConvertWorkstationList( &WorkstationString ); *lpCallersWorkstations = Workstations; }
}
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_LOGON_SERVER_PARMNUM)) { if (parmnum == PARMNUM_ALL) { pointer = ((PUSER_INFO_2)Buffer)->usri2_logon_server; } if ((stringlen = POSSIBLE_WCSLEN(pointer)) > MAX_PATH) { *ParmError = USER_LOGON_SERVER_PARMNUM; NetStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } buflen += STRING_SPACE_REQD(stringlen + 1); }
//
// if the caller is setting the logon hours then we need to substitute
// shuffled bits for the logon hours bitmap
//
if ((parmnum == PARMNUM_ALL) || (parmnum == USER_LOGON_HOURS_PARMNUM)) { if (parmnum == PARMNUM_ALL) { lpCallersLogonHours = (PBYTE*)&((PUSER_INFO_2)Buffer)->usri2_logon_hours; } else { lpCallersLogonHours = (PBYTE*)&((PUSER_INFO_1020)Buffer)->usri1020_logon_hours; } callersLogonHours = *lpCallersLogonHours; RtlCopyMemory(logonHours, callersLogonHours, sizeof(logonHours));
//
// shuffle the bitmap and point the logon_hours field in the structure
// at the shuffled version
//
NetpRotateLogonHours(logonHours, UNITS_PER_WEEK, FALSE); *lpCallersLogonHours = logonHours; } }
//
// we have covered all the parameters that we are able to from this end. The
// down-level APIs don't know about the ParmError concept (it is, after all,
// a highly developed notion, too highbrow for the LanManDerthals...) so if
// we get back an ERROR_INVALID_PARAMETER, the caller will just have to be
// content with PARM_ERROR_UNKNOWN, and try to figure it out from there
//
*ParmError = PARM_ERROR_UNKNOWN;
//
// if originalPasswordLength is non-zero then we must be supplying a password;
// perform the encryption machinations
//
if (originalPasswordLength) {
//
// Calculate the one-way function of the password
//
RtlUnicodeToMultiByteN(ansiPassword, sizeof(ansiPassword), &lmOwfPasswordLen, *lpClearText, originalPasswordLength * sizeof(WCHAR) ); ansiPassword[lmOwfPasswordLen] = 0; (VOID) _strupr(ansiPassword); // down-level wants upper-cased passwords
#ifdef DOWN_LEVEL_ENCRYPTION
Status = RtlCalculateLmOwfPassword(ansiPassword, &lmOwfPassword); if (NT_SUCCESS(Status)) { NetStatus = GetLanmanSessionKey((LPWSTR)ServerName, (LPBYTE)&lanmanKey); if (NetStatus == NERR_Success) { Status = RtlEncryptLmOwfPwdWithLmSesKey(&lmOwfPassword, &lanmanKey, &encryptedLmOwfPassword ); if (NT_SUCCESS(Status)) { *lpClearText = (LPTSTR)&encryptedLmOwfPassword; passwordEncrypted = TRUE; if (parmnum == USER_PASSWORD_PARMNUM) { buflen = sizeof(encryptedLmOwfPassword); } } } } if (NetStatus != NERR_Success) { goto Cleanup; } else if (!NT_SUCCESS(Status)) { NetStatus = RtlNtStatusToDosError(Status); goto Cleanup; }
#else
*lpClearText = (LPTSTR)ansiPassword;
#endif
}
//
// New! Improved! Now, even better, RxNetUserSetInfo will use SetInfo2
// to fix the most stubborn user set info problems (ie password)
//
NetStatus = RxRemoteApi(API_WUserSetInfo2, ServerName, REMSmb_NetUserSetInfo2_P, pDesc16, pDesc32, pDescSmb, // data descriptors
NULL, NULL, NULL, // no aux data
FALSE, // must be logged on
UserName, // parameters...
Level,
//
// if we are sending the whole structure, then Buffer
// points to the structure, else Buffer points to a
// pointer to the field to set; RxRemoteApi expects
// a pointer to the data
//
parmnum == PARMNUM_ALL || parmnum == USER_PASSWORD_PARMNUM ? Buffer : *(LPBYTE*)Buffer, buflen, // supplied by us
//
// in this case, the field index and parm num are the
// same value
//
MAKE_PARMNUM_PAIR(parmnum, parmnum),
//
// add those extraneous WWs: whether the data is
// encrypted and the original password length. (By
// deduction: password is the only data that is
// encrypted)
//
passwordEncrypted, originalPasswordLength );
Cleanup: //
// copy the original password back to the user's buffer if we set a password
//
if (originalPasswordLength) { *lpClearText = cleartext; }
//
// restore the original logon hours string
//
if (callersLogonHours) { *lpCallersLogonHours = callersLogonHours; }
//
// restore the original workstation list
//
if ( callersWorkstations != NULL) { *lpCallersWorkstations = callersWorkstations; } return NetStatus; }
//NET_API_STATUS
//RxNetUserValidate2
// /** CANNOT BE REMOTED **/
//{
//
//}
DBGSTATIC NET_API_STATUS GetUserDescriptors( IN DWORD Level, IN BOOL Encrypted, OUT LPDESC* ppDesc16, OUT LPDESC* ppDesc32, OUT LPDESC* ppDescSmb )
/*++
Routine Description:
Returns pointers to descriptor strings for user info structures based on level of info required for RxNetUser routines
Arguments:
Level - of info being requested Encrypted - TRUE if info structure contains encrypted password ppDesc16 - where to return pointer to 16-bit data descriptor ppDesc32 - where to return pointer to 32-bit data descriptor ppDescSmb - where to return pointer to SMB data descriptor
Return Value:
ERROR_INVALID_LEVEL - If the level was not in the list.
NO_ERROR - If the operation was successful.
--*/
{ switch (Level) { case 0: *ppDesc16 = REM16_user_info_0; *ppDesc32 = REM32_user_info_0; *ppDescSmb = REMSmb_user_info_0; break;
case 1: *ppDesc16 = REM16_user_info_1; *ppDesc32 = Encrypted ? REM32_user_info_1 : REM32_user_info_1_NOCRYPT; *ppDescSmb = REMSmb_user_info_1; break;
case 2: *ppDesc16 = REM16_user_info_2; *ppDesc32 = Encrypted ? REM32_user_info_2 : REM32_user_info_2_NOCRYPT; *ppDescSmb = REMSmb_user_info_2; break;
case 10: *ppDesc16 = REM16_user_info_10; *ppDesc32 = REM32_user_info_10; *ppDescSmb = REMSmb_user_info_10; break;
case 11: *ppDesc16 = REM16_user_info_11; *ppDesc32 = REM32_user_info_11; *ppDescSmb = REMSmb_user_info_11; break;
default: return(ERROR_INVALID_LEVEL); } return(NO_ERROR); }
DBGSTATIC VOID GetModalsDescriptors( IN DWORD Level, OUT LPDESC* ppDesc16, OUT LPDESC* ppDesc32, OUT LPDESC* ppDescSmb )
/*++
Routine Description:
Returns pointers to descriptor strings for modals info structures based on level of info required for RxNetUserModals routines
Arguments:
Level - of info being requested ppDesc16 - where to return pointer to 16-bit data descriptor ppDesc32 - where to return pointer to 32-bit data descriptor ppDescSmb - where to return pointer to SMB data descriptor
Return Value:
None.
--*/
{ switch (Level) { case 0: *ppDesc16 = REM16_user_modals_info_0; *ppDesc32 = REM32_user_modals_info_0; *ppDescSmb = REMSmb_user_modals_info_0; break;
case 1: *ppDesc16 = REM16_user_modals_info_1; *ppDesc32 = REM32_user_modals_info_1; *ppDescSmb = REMSmb_user_modals_info_1; break; } }
NET_API_STATUS GetLanmanSessionKey( IN LPWSTR ServerName, OUT LPBYTE pSessionKey )
/*++
Routine Description:
Retrieves the LM session key for the connection from the redir FSD
Arguments:
ServerName - name of server to get session key for pSessionKey - pointer to where session key will be deposited
Return Value:
NET_API_STATUS Success - NERR_Success Failure -
--*/
{ NTSTATUS ntStatus; HANDLE hToken; TOKEN_STATISTICS stats; ULONG length; LMR_REQUEST_PACKET request; LMR_CONNECTION_INFO_2 connectInfo; NET_API_STATUS apiStatus; WCHAR connectionName[MAX_PATH];
ntStatus = NtOpenProcessToken(NtCurrentProcess(), GENERIC_READ, &hToken); if (NT_SUCCESS(ntStatus)) {
//
// Get the logon id of the current thread
//
ntStatus = NtQueryInformationToken(hToken, TokenStatistics, (PVOID)&stats, sizeof(stats), &length ); if (NT_SUCCESS(ntStatus)) {
RtlCopyLuid(&request.LogonId, &stats.AuthenticationId); request.Type = GetConnectionInfo; request.Version = REQUEST_PACKET_VERSION; request.Level = 2;
wcsncpy(connectionName, ServerName, MAX_PATH-1); wcsncat(connectionName, L"\\IPC$", MAX_PATH-1-wcslen(ServerName) );
apiStatus = NetpRdrFsControlTree(connectionName, NULL, USE_WILDCARD, FSCTL_LMR_GET_CONNECTION_INFO, NULL, (LPVOID)&request, sizeof(request), (LPVOID)&connectInfo, sizeof(connectInfo), FALSE ); if (apiStatus == NERR_Success) { RtlMoveMemory(pSessionKey, &connectInfo.LanmanSessionKey, sizeof(connectInfo.LanmanSessionKey) ); } } NtClose(hToken); } if (!NT_SUCCESS(ntStatus)) { apiStatus = NetpNtStatusToApiStatus(ntStatus); } return apiStatus; }
|