You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2229 lines
67 KiB
2229 lines
67 KiB
/*++
|
|
|
|
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;
|
|
}
|