windows-nt/Source/XPSP1/NT/ds/netapi/rpcxlate/rxapi/rxuser.c
2020-09-26 16:20:57 +08:00

2230 lines
65 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
wcscpy( Workstations, callersWorkstations );
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
//
RtlZeroMemory(OldAnsiPassword, sizeof(OldAnsiPassword));
RtlZeroMemory(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 ) {
wcscpy( Workstations, callersWorkstations );
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;
wcscpy(connectionName, ServerName);
wcscat(connectionName, L"\\IPC$");
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;
}