/*++
Copyright (c) 1991-1993 Microsoft Corporation
Module Name:
PortLib.C
Abstract:
Main entry code for the PortUas runtime library function.
Author:
Shanku Niyogi (W-SHANKN) 29-Oct-1991
Revision History:
29-Oct-1991 w-shankn
Created.
06-Feb-1992 JohnRo
Fixed memory leak with uPassword.
Made changes suggested by PC-LINT.
27-Feb-1992 JohnRo
Changed user info level from 98 to 21 (avoid LAN/Server conflicts).
Added message about updating existing user.
28-Feb-1992 JohnRo
Fixed MIPS build errors.
03-Mar-1992 JohnRo
Fixed bug in handling group memberships.
Do special handling for privs and auth flags.
Avoid creating redundant groups.
11-Mar-1992 JohnRo
Fixed bug handling priv/auth difference on existing user.
Use WARNING_MSG(), ERROR_MSG(), PROGRESS_MSG() macros.
18-Mar-1992 JohnRo
Use iterator to allow hash-table collision handling.
23-Apr-1992 JohnRo
Handle probable LanMan bug: password required but password is null.
07-May-1992 JohnRo
Avoid dummy passwords altogether.
Added alias support for operators.
Added WORKAROUND_SAM_BUG support (NetUserAdd fails, try again).
Fixed a possible memory leak in PortUas().
Use FORMAT_ equates.
10-Jun-1992 JohnRo
RAID 10139: PortUAS should add to admin group/alias.
PortUAS was also leaving temporary modals set on exit.
Fixed bad (temp) value of max_passwd_age.
29-Sep-1992 JohnRo
RAID 8001: PORTUAS.EXE not in build (work with stdcall).
(Moved portuas.c to portlib.c and port2.c to portuas.c)
06-Oct-1992 JohnRo
RAID 9020: Setup: PortUAS fails (group name same as a domain name).
29-Oct-1992 JohnRo
RAID 9020 ("prompt on conflicts" version).
RAID 9613: PortUAS should prevent run on BDC.
20-Nov-1992 JohnRo
RAID 3875: PortUAS don't really ignore "redundant" groups.
27-Jan-1993 JohnRo
RAID 8683: PortUAS should set primary group from Mac parms.
26-Apr-1993 JohnRo
SAM bug seems to be obsolete, so avoid workaround for it.
11-Jun-1993 JohnRo
RAID 13257: PortUAS does not handle operator privileges.
11-Aug-1993 JohnRo
RAID NTBUG 16822: PortUAS should have ^C handler to restore user modals.
RAID NTISSUE 2260: PortUAS returns a NetUserAdd error=1379 with local
group.
--*/
// These must be included first:
#include <nt.h> // NTSTATUS, NT_SUCCESS().
#include <ntrtl.h> // (Needed with nt.h and windows.h)
#include <nturtl.h> // (Needed with ntrtl.h and windows.h)
#include <windows.h> // LocalFree(), SetConsoleCtrlHandler(), etc.
#include <lmcons.h>
// These may be included in any order:
#include <crypt.h> // LM_OWF_PASSWORD, Rtl encrypt/decrypt/equal routines.
#include <lmaccess.h> // AF_ and UF_ equates, LPUSER_INFO_22, etc.
#include <lmapibuf.h>
#include <lmerr.h>
#include <names.h> // NetpIsMacPrimaryGroupFieldValid().
#include <netdebug.h> // DBGSTATIC, NetpAssert(), etc.
#include <netlib.h> // LOCAL_DOMAIN_TYPE_ equates.
#include <netlibnt.h> // NetpNtStatusToApiStatus().
#include <portuas.h>
#include <portuasp.h> // WARNING_MSG(), PortUasSam*, Verbose, etc.
#include <wchar.h> // _wcsicmp().
#include <tchar.h>
#include "nlstxt.h" // NLS message ID's
// Don't need DOMAIN_GET_ALIAS_MEMBERSHIP or group equivalent...
#define OUR_DOMAIN_DESIRED_ACCESS DOMAIN_LOOKUP
// Enable real functionality.
#define PORTUAS_ENABLE
// Enable retry NetUserAdd (once) if it fails with ERROR_ACCESS_DENIED.
//#define WORKAROUND_SAM_BUG
#define REASON_NO_ERROR ((DWORD) -1)
#define SET_MAP_REASON( someValue ) \
{ \
mapNeeded = TRUE; \
if (originalMapReason == REASON_NO_ERROR) { \
originalMapReason = someValue; \
} \
mapReason = someValue; \
}
#define RESET_MAP_REASON( ) \
{ \
mapNeeded = FALSE; \
originalMapReason = REASON_NO_ERROR; \
untriedMapEntry = NULL; /* Null pointer so we don't kill it. */ \
}
// Global domain IDs. These are set here, used here and in alias code, and
// cleaned-up here.
PSID PortUasAccountsDomainId = NULL;
PSID PortUasBuiltinDomainId = NULL;
// Global SAM handles. These are set here, used here and in alias code, and
// cleaned-up here.
SAM_HANDLE PortUasSamConnectHandle = NULL;
SAM_HANDLE PortUasSamAccountsDomainHandle = NULL;
SAM_HANDLE PortUasSamBuiltinDomainHandle = NULL;
// Global variables which are used by PortUasModalsCleanup() in the
// event of a control-C...
DBGSTATIC LPUSER_MODALS_INFO_0 finalModals0 = NULL;
DBGSTATIC BOOL modalsTemporarilyChanged = FALSE;
DBGSTATIC BOOL WINAPI
PortUasModalsCleanup(
DWORD CtrlType
)
/*++
Routine Description:
BUGBUG
This routine is registered with the Windows SetConsoleCtrlHandler() API.
Arguments:
CtrlType - indicates which reason for calling this routine.
Return Value:
BOOL - TRUE iff we want to be last handler. We don't care...
--*/
{
NET_API_STATUS rc;
UNREFERENCED_PARAMETER( CtrlType );
if (modalsTemporarilyChanged) {
#ifdef PORTUAS_ENABLE
NetpAssert( finalModals0 != NULL );
if ( rc = NetUserModalsSet( NULL, 0, (LPVOID)finalModals0, NULL )) {
UNEXPECTED_MSG( "NetUserModalsSet(final)", rc );
rc = PortUasError( rc );
}
// BUGBUG: We lose the "rc" value here...
#endif
}
modalsTemporarilyChanged = FALSE;
if (finalModals0 != NULL) {
(void) NetApiBufferFree( finalModals0 );
finalModals0 = NULL;
}
if (Verbose) {
DEBUG_MSG(( "Done setting modals to their final values.\n" ));
}
return (FALSE); // we don't want to be only routine to process this...
} // PortUasModalsCleanup
NET_API_STATUS
PortUas(
IN LPTSTR UasPathName
)
/*++
Routine Description:
PortUas ports account information in a LanMan 2.0 UAS database into
the SAM database.
Arguments:
UasPathName - supplies the path name to the UAS database file.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
--*/
{
NET_API_STATUS rc;
BOOL groupAdded[UAS_MAXGROUP];
USER_MODALS_INFO_0 tempModals0;
LPGROUP_INFO_1 groups;
LPDWORD gIds;
DWORD gCount;
LPMAP_ENTRY untriedMapEntry = NULL; // NULL once map entry is good.
BOOL mapNeeded;
DWORD mapReason; // REASON_ equates from PortUAS.h
DWORD originalMapReason; // REASON_ equates from PortUAS.h
BOOL mapSetup = FALSE;
USER_ITERATOR UserIterator;
LPUSER_INFO_22 user = NULL;
LPBYTE userEncryptedPassword = NULL;
LPGROUP_INFO_0 userGroups;
LPDWORD ugIds;
DWORD ugCount;
BOOL aliasesSetup = FALSE;
BOOL databaseOpen = FALSE;
DWORD i;
NTSTATUS ntStatus;
LM_OWF_PASSWORD nullEncryptedPassword;
//
// Are we running on a machine which allows updates to security info?
// (Can't update a Backup Domain Controller directly, for instance.)
//
rc = PortUasMachineAllowsUpdates();
if (rc == NERR_NotPrimary) {
//ERROR_MSG((
// "PortUAS must be run on a Primary Domain Controller (PDC) or\n"
// "Windows/NT system, not a Backup Domain Controller (BDC).\n" ));
(void)NlsPutMsg(STDOUT, PUAS_PDC_GOOD_BDC_BAD);
goto Cleanup;
} else if (rc == ERROR_ACCESS_DENIED) {
//ERROR_MSG((
// "Your account does not have privilege for this.\n" ));
(void)NlsPutMsg(STDOUT, PUAS_NO_PRIVILEGE);
goto Cleanup;
} else if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasMachineAllowsUpdates", rc );
goto Cleanup;
}
//
// Start off by setting-up a null (encrypted) password.
//
ntStatus = RtlCalculateLmOwfPassword( "", &nullEncryptedPassword );
NetpAssert( NT_SUCCESS( ntStatus ) );
//
// Open (at least, try to) the UAS database.
//
if ( UasPathName == NULL ) {
rc = NERR_ACFNotFound;
goto Cleanup;
}
if ( rc = PortUasOpen( UasPathName )) {
goto Cleanup;
}
databaseOpen = TRUE;
//
// Initialize name mapping table.
//
RESET_MAP_REASON( );
rc = PortUasMapTableInit( );
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasTableMapInit", rc );
goto Cleanup;
}
mapSetup = TRUE;
NetpAssert( PortUasSamConnectHandle == NULL );
NetpAssert( PortUasSamAccountsDomainHandle == NULL );
NetpAssert( PortUasSamBuiltinDomainHandle == NULL );
//
// Get a connection to SAM.
//
ntStatus = SamConnect(
NULL, // no server name (local)
&PortUasSamConnectHandle, // resulting SAM handle
// SAM_SERVER_READ | // desired access
// SAM_SERVER_CONNECT |
SAM_SERVER_LOOKUP_DOMAIN,
NULL ); // no object attributes
if ( !NT_SUCCESS( ntStatus ) ) {
rc = NetpNtStatusToApiStatus( ntStatus );
UNEXPECTED_MSG( "SamConnect", rc );
goto Cleanup;
}
NetpAssert( PortUasSamConnectHandle != NULL );
//
// To open the accounts and builtin domains (below), we'll need the
// domain IDs.
//
rc = NetpGetLocalDomainId (
LOCAL_DOMAIN_TYPE_ACCOUNTS, // type we want.
& PortUasAccountsDomainId );
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "NetpGetDomainId(accounts)", rc );
goto Cleanup;
}
NetpAssert( PortUasAccountsDomainId != NULL );
rc = NetpGetLocalDomainId (
LOCAL_DOMAIN_TYPE_BUILTIN, // type we want.
& PortUasBuiltinDomainId );
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "NetpGetDomainId(builtin)", rc );
goto Cleanup;
}
NetpAssert( PortUasBuiltinDomainId != NULL );
NetpAssert( PortUasBuiltinDomainId != PortUasAccountsDomainId );
//
// We also need to open the accounts and builtin domains.
//
ntStatus = SamOpenDomain(
PortUasSamConnectHandle,
OUR_DOMAIN_DESIRED_ACCESS,
PortUasAccountsDomainId,
&PortUasSamAccountsDomainHandle );
if ( !NT_SUCCESS( ntStatus ) ) {
rc = NetpNtStatusToApiStatus( ntStatus );
UNEXPECTED_MSG( "SamOpenDomain(accounts)", rc );
goto Cleanup;
}
NetpAssert( PortUasSamAccountsDomainHandle != NULL );
ntStatus = SamOpenDomain(
PortUasSamConnectHandle,
OUR_DOMAIN_DESIRED_ACCESS,
PortUasBuiltinDomainId,
&PortUasSamBuiltinDomainHandle );
if ( !NT_SUCCESS( ntStatus ) ) {
rc = NetpNtStatusToApiStatus( ntStatus );
UNEXPECTED_MSG( "SamOpenDomain(builtin)", rc );
goto Cleanup;
}
NetpAssert( PortUasSamBuiltinDomainHandle != NULL );
//
// Initialize alias stuff.
//
rc = PortUasAliasSetup();
if (rc != NO_ERROR) {
goto Cleanup;
}
aliasesSetup = TRUE;
//
// Everything we've done so far is transient. (If someone hits control-C
// or whatever, then the system would close alias handles, free memory, etc,
// as part of the usual process cleanup.) However, we're about to set the
// user modals (to some temporary values), which are NOT transient. So
// we need to make arragements to restore the user modals if we die from
// control-C or whatever...
//
// Register our cleanup routine with the Win32 runtime.
//
if ( !SetConsoleCtrlHandler( PortUasModalsCleanup, TRUE /* add */ ) ) {
rc = GetLastError();
UNEXPECTED_MSG( "SetConsoleCtrlHandler", rc ); // report the error.
NetpAssert( rc != NO_ERROR );
goto Cleanup;
}
//
//
// Get final version of modals.
//
if ( rc = PortUasGetModals( &finalModals0 )) {
goto Cleanup;
}
//
// We want to (1) be able to set NULL passwords in the process of
// creating an account, and (2) want to avoid repetitive password
// errors if PortUAS is run twice in a row. So CliffV suggests
// temporarily changing the modals to allow this. So, let's set
// the temporary version now.
//
tempModals0.usrmod0_min_passwd_len = 0;
tempModals0.usrmod0_max_passwd_age = ONE_DAY;
tempModals0.usrmod0_min_passwd_age = 0;
tempModals0.usrmod0_force_logoff = 0;
tempModals0.usrmod0_password_hist_len = 0;
#ifdef PORTUAS_ENABLE
if ( rc = NetUserModalsSet( NULL, 0, (LPBYTE)&tempModals0, NULL )) {
UNEXPECTED_MSG( "NetUserModalsSet(temp)", rc );
rc = PortUasError( rc );
goto Cleanup;
}
#endif
modalsTemporarilyChanged = TRUE;
//
// Initialize added group table.
//
for ( i = 0; i < UAS_MAXGROUP; i++ ) {
groupAdded[i] = FALSE;
}
//
// Get a list of groups (including the redundant ones).
//
rc = PortUasGetGroups(
(LPBYTE *) (LPVOID) &groups,
(LPBYTE *) (LPVOID) &gIds,
&gCount );
if (rc != NO_ERROR) {
goto Cleanup;
}
//
// Add the groups one by one.
//
for ( i = 0; i < gCount; i++ ) {
BOOL ignoreThisGroup = FALSE;
LPWSTR originalGroupName = groups[i].grpi1_name;
LPWSTR groupName = originalGroupName;
NetpAssert( groupName[0] != NULLC );
RESET_MAP_REASON( );
if (Verbose) {
DEBUG_MSG( ("UAS database has group '" FORMAT_LPWSTR "'...\n",
originalGroupName ));
}
//
// Repeat adding under different names until...
//
do {
#ifdef FAT8
//
// BUGBUG: Temporary!!! Current reg stuff uses FAT (8.3) for
// registry, so avoid long names!
//
if ( wcslen( groupName ) > PORTUAS_MAX_GROUP_LEN ) {
SET_MAP_REASON( REASON_NAME_LONG_FOR_TEMP_REG );
continue; // Loop again checking this name.
}
#endif // FAT8
if ( PortUasIsGroupRedundant( groupName ) ) {
//WARNING_MSG((
// "Ignoring redundant group '" FORMAT_LPWSTR "'...\n",
// groupName ));
(void)NlsPutMsg(STDOUT, PUAS_IGNORING_REDUNDANT_GROUP,
groupName );
break; // Stop trying to add this group under any name.
}
#ifdef PORTUAS_ENABLE
//
// Actually create the group.
//
groups[i].grpi1_name = groupName;
rc = NetGroupAdd( NULL, 1, (LPBYTE)( &groups[i] ), NULL );
#else
rc = NERR_Success;
#endif
if ( !rc ) {
groupAdded[gIds[i]] = TRUE;
//PROGRESS_MSG(( "Group '" FORMAT_LPWSTR "' added.\n",
// groupName ));
(void)NlsPutMsg(STDOUT, PUAS_GROUP_ADDED, groupName);
RESET_MAP_REASON( );
break; // Done trying to add this group under any name.
//
// If the group already exists in NT, update it.
//
} else if ( rc == NERR_GroupExists ) {
//PROGRESS_MSG(( "Changing group '" FORMAT_LPWSTR "'.\n",
// groupName ));
(void)NlsPutMsg(STDOUT, PUAS_CHANGING_GROUP,
groupName );
#ifdef PORTUAS_ENABLE
if (( rc = NetGroupSetInfo(
NULL,
groupName,
GROUP_COMMENT_INFOLEVEL,
(LPBYTE)( &(groups[i].grpi1_comment) ),
NULL )) && rc != NERR_SpeGroupOp ) {
UNEXPECTED_MSG( "NetGroupSetInfo", rc );
(void) NetApiBufferFree( groups );
(void) NetApiBufferFree( gIds );
rc = PortUasError( rc );
goto Cleanup;
}
#endif
groupAdded[gIds[i]] = TRUE;
RESET_MAP_REASON( );
break; // Done trying to add this group under any name.
//
// If the group exists as a username, map another name for it.
//
} else if ( rc == NERR_UserExists ) {
SET_MAP_REASON( REASON_CONFLICT_WITH_USERNAME );
} else if ( rc == NERR_BadUsername ) {
SET_MAP_REASON( REASON_BAD_NAME_SYNTAX );
} else if ( rc == ERROR_ALIAS_EXISTS ) {
SET_MAP_REASON( REASON_CONFLICT_WITH_LOCALGROUP );
} else if ( rc == ERROR_DOMAIN_EXISTS ) {
SET_MAP_REASON( REASON_CONFLICT_WITH_DOMAIN );
//
// Special group: ignore and continue.
//
} else if ( rc == NERR_SpeGroupOp ) {
RESET_MAP_REASON( );
//
// Return any other error.
//
} else {
UNEXPECTED_MSG( "NetGroupAdd", rc );
(void) NetApiBufferFree( groups );
(void) NetApiBufferFree( gIds );
rc = PortUasError( rc );
goto Cleanup;
}
//
// If old map entry was a failure, then tell admin and delete entry.
//
if (untriedMapEntry != NULL) {
// BUGBUG: can new name be NULL here?
rc = PortUasComplainAboutBadName(
untriedMapEntry->NewName,
FALSE, // no, this isn't a user name
mapReason ); // reason for latest failue.
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasComplainAboutBadName(redo group)",
rc );
goto Cleanup;
}
rc = PortUasDeleteBadMapEntry( untriedMapEntry );
untriedMapEntry = NULL; // Null pointer so we don't kill it.
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasDeleteBadMapEntry(group)", rc );
goto Cleanup;
}
}
//
// If this name needs to be mapped, then try to create a map table
// entry for the original bad name and the original reason.
//
if (mapNeeded) {
rc = PortUasFindOrCreateMapEntry(
originalGroupName, // old name
FALSE, // no, this isn't a user name
originalMapReason,
& ignoreThisGroup,
& untriedMapEntry ); // Do NOT free this!
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasFindOrCreateMapEntry(group)", rc );
goto Cleanup;
}
if (ignoreThisGroup) {
break; // Stop trying to add this group under any name.
}
NetpAssert( untriedMapEntry != NULL );
groupName = untriedMapEntry->NewName;
NetpAssert( groupName != NULL );
continue; // Loop again checking this name.
}
} while ( mapNeeded );
} // for each group
(void) NetApiBufferFree( groups );
(void) NetApiBufferFree( gIds );
//
// Now add users.
//
PortUasInitUserIterator( UserIterator );
for ( ; ; ) {
BOOL ignoreThisUser = FALSE;
DWORD originalAuthFlags; // AF_OP_ value for this user.
LPWSTR originalUserName;
DWORD originalUserPriv; // UAS USER_PRIV_ value for this user.
LPWSTR userName;
BOOL userUpdated = FALSE;
RESET_MAP_REASON( );
//
// Get user data from database. Note that this will return a null
// pointer for a password.
//
rc = PortUasGetUser( &UserIterator, (LPBYTE *) (LPVOID) &user );
//
// No more users to port?
//
if ( rc == NERR_UserNotFound ) {
break;
}
if ( rc ) {
rc = PortUasError( rc );
goto Cleanup;
}
NetpAssert( user != NULL );
originalUserName = user->usri22_name;
userName = originalUserName;
NetpAssert( userName[0] != NULLC );
//
// Repeat adding user under different names until...
//
do {
#ifdef FAT8
//
// BUGBUG: Temporary!!! Current reg stuff uses FAT (8.3) for
// registry, so avoid long names!
//
if ( wcslen( userName ) > PORTUAS_MAX_USER_LEN ) {
SET_MAP_REASON( REASON_NAME_LONG_FOR_TEMP_REG );
continue;
}
#endif // FAT8
//
// Read the (one-way-encrypted) password.
//
rc = PortUasGetUserOWFPassword(
&UserIterator,
&userEncryptedPassword );
if (rc != NO_ERROR ) {
rc = PortUasError( rc );
goto Cleanup;
}
//
// Update priviledges and authorization to be compatible with NT as
// we're adding/changing the user. Then we'll use the original info
// to update NT via aliases.
//
originalAuthFlags = user->usri22_auth_flags;
originalUserPriv = user->usri22_priv;
if ( user->usri22_priv == USER_PRIV_ADMIN ) {
//WARNING_MSG(( "Adding admin '" FORMAT_LPWSTR
// "' as regular user...\n",
// userName ));
(void)NlsPutMsg(STDOUT, PUAS_ADDING_ADMIN_AS_REGULAR_USER,
userName );
user->usri22_priv = USER_PRIV_USER;
} else if ( user->usri22_priv == USER_PRIV_GUEST ) {
//WARNING_MSG(( "Adding guest '" FORMAT_LPWSTR
// "' as regular user...\n",
// userName ));
(void)NlsPutMsg(STDOUT, PUAS_ADDING_GUEST_AS_REGULAR_USER,
userName );
user->usri22_priv = USER_PRIV_USER;
} else if ( user->usri22_priv != USER_PRIV_USER ) {
//WARNING_MSG((
// "Changing unknown priv for '" FORMAT_LPWSTR "' from "
// FORMAT_DWORD " to regular user.\n",
// userName, user->usri22_priv ));
(void)NlsPutMsg(STDOUT, PUAS_CHANGING_UNK_PRIV_TO_REGULAR_USER,
userName, user->usri22_priv );
user->usri22_priv = USER_PRIV_USER;
}
if ( user->usri22_auth_flags != 0 ) {
//WARNING_MSG(( "Adding operator '" FORMAT_LPWSTR
// "' as regular user...\n",
// userName ));
(void)NlsPutMsg(STDOUT, PUAS_ADDING_OPERATOR_AS_REGULAR_USER,
userName );
user->usri22_auth_flags = 0;
}
//
// copy encrypted password.
//
*(PLM_OWF_PASSWORD)(&(user->usri22_password[0])) =
*(PLM_OWF_PASSWORD) userEncryptedPassword;
//
// Handle probable Lanman bug where password is null but password
// is "required". CliffV has seen this in actual LM 2.x UAS
// databases.
//
if (RtlEqualLmOwfPassword(
&nullEncryptedPassword,
(PLM_OWF_PASSWORD) userEncryptedPassword )) {
if ( ! (user->usri22_flags & UF_PASSWD_NOTREQD) ) {
//WARNING_MSG(( "Working around probable LanMan bug for user "
// "'" FORMAT_LPWSTR "'.\n", userName ));
(void)NlsPutMsg(STDOUT, PUAS_WORKING_AROUND_LANMAN_BUG, userName );
user->usri22_flags |= UF_PASSWD_NOTREQD;
}
}
//
// Try to add the user, possibly under another name.
//
user->usri22_name = userName;
#ifdef PORTUAS_ENABLE
rc = NetUserAdd( NULL, 22, (LPBYTE)user, NULL );
#ifdef WORKAROUND_SAM_BUG
if (rc == ERROR_ACCESS_DENIED) {
//WARNING_MSG((
// "Access denied on NetUserAdd, "
// "possible SAM bug, retrying...\n" ));
(void)NlsPutMsg(STDOUT, PUAS_ACCESS_DENIED_POSSIBLE_SAM_BUG);
rc = NetUserAdd( NULL, 22, (LPBYTE)user, NULL );
}
#endif // WORKAROUND_SAM_BUG
#else // ndef PORTUAS_ENABLE
rc = NERR_Success;
#endif // ndef PORTUAS_ENABLE
if (rc == NO_ERROR) {
(void)NlsPutMsg(STDOUT, PUAS_ADDED_USER_OK,
userName );
userUpdated = TRUE;
RESET_MAP_REASON( );
} else if ( rc == NERR_BadUsername ) {
SET_MAP_REASON( REASON_BAD_NAME_SYNTAX );
} else if ( rc == ERROR_INVALID_PARAMETER ) {
//WARNING_MSG(( "Error 87 adding user '" FORMAT_LPWSTR
// "', user info:\n", userName ));
(void)NlsPutMsg(STDOUT, PUAS_ERROR_87_ADDING_USER, userName );
DumpUserInfo( user ); // Log this info so we can track bogus...
//
// If the user exists as a (global) group name, map the name.
//
} else if ( rc == NERR_GroupExists ) {
SET_MAP_REASON( REASON_CONFLICT_WITH_GROUP );
//
// If the user exists as a local group name, map the name.
//
} else if ( rc == ERROR_ALIAS_EXISTS ) {
SET_MAP_REASON( REASON_CONFLICT_WITH_LOCALGROUP );
//
// Update data for existing user.
//
} else if ( rc == NERR_UserExists ) {
LPUSER_INFO_2 CurrentInfo;
//PROGRESS_MSG(( "User " FORMAT_LPWSTR
// " already exists; updating...\n", userName ));
(void)NlsPutMsg(STDOUT, PUAS_USER_ALREADY_EXISTS_UPDATING, userName );
//
// We must preserve what SAM thinks the priv and auth flags are.
//
rc = NetUserGetInfo(
NULL,
userName,
2,
(LPBYTE *) (LPVOID) & CurrentInfo );
if ( rc != NO_ERROR) {
//
// continue to process next user.
//
break;
}
RESET_MAP_REASON( );
user->usri22_auth_flags = CurrentInfo->usri2_auth_flags;
user->usri22_priv = CurrentInfo->usri2_priv;
(void) NetApiBufferFree( CurrentInfo );
//
// Update everything except priv and auth flags.
//
if ( rc = NetUserSetInfo( NULL, userName, 22,
(LPBYTE)user, NULL )) {
if ( rc == ERROR_INVALID_PARAMETER ) {
//WARNING_MSG(( "Error 87 changing user '" FORMAT_LPWSTR
// "', user info:\n", userName ));
(void)NlsPutMsg(STDOUT, PUAS_ERROR_87_CHANGING_USER,
userName );
DumpUserInfo( user ); // Log this info.
} else {
UNEXPECTED_MSG( "NetUserSetInfo(normal)", rc );
}
//
// continue to process next user.
//
break;
} else {
userUpdated = TRUE;
}
//
// Report any other error.
//
} else {
UNEXPECTED_MSG( "NetUserAdd", rc );
//
// continue to process next user.
//
break;
}
//
// If the user was updated, assign this user to groups and aliases.
//
if ( userUpdated ) {
NetpAssert( !mapNeeded );
//
// Get a list of groups for the user.
//
rc = PortUasGetUserGroups(
&UserIterator,
(LPBYTE *) (LPVOID) &userGroups,
(LPBYTE *) (LPVOID) &ugIds,
&ugCount );
if (rc != NERR_Success) {
rc = PortUasError( rc );
goto Cleanup;
}
//
// Add the groups one by one.
//
for ( i = 0; i < ugCount; i++ ) {
if ( groupAdded[ugIds[i]] ) {
LPMAP_ENTRY goodMapEntry;
LPWSTR groupName = userGroups[i].grpi0_name;
BOOL ignoreThisGroup = FALSE;
NetpAssert( groupName != NULL );
NetpAssert( !PortUasIsGroupRedundant( groupName ) );
//
// Find existing map for group name (if any).
// Note: PortUasFindMapEntry returns NO_ERROR
// and sets *goodMapEntry=NULL if not found.
//
rc = PortUasFindMapEntry(
groupName, // Name to find.
& ignoreThisGroup,
& goodMapEntry ); // Do NOT free this!
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasFindMapEntry", rc );
(VOID) NetApiBufferFree( ugIds );
goto Cleanup;
}
if (ignoreThisGroup) {
continue; // ignore this group
}
if (goodMapEntry != NULL) {
NetpAssert( goodMapEntry->NewName != NULL);
groupName = goodMapEntry->NewName;
}
#ifdef PORTUAS_ENABLE
rc = NetGroupAddUser( NULL,
groupName, // mapped group name
userName );
if ( rc && rc != NERR_SpeGroupOp
&& rc != NERR_UserInGroup ) {
UNEXPECTED_MSG( "NetGroupAddUser", rc );
(void) NetApiBufferFree( ugIds );
//
// continue to process next user.
//
break;
}
//PROGRESS_MSG((
// "User '" FORMAT_LPWSTR "' added to group '"
// FORMAT_LPWSTR "'.\n", userName, groupName ));
(void)NlsPutMsg(STDOUT, PUAS_USER_ADDED_TO_GROUP,
userName, groupName );
#endif
}
}
(void) NetApiBufferFree( userGroups );
(void) NetApiBufferFree( ugIds );
if ( rc && rc != NERR_SpeGroupOp
&& rc != NERR_UserInGroup ) {
//
// continue to process next user.
//
break;
}
//
// If user was an operator of some kind, add him/her to one or
// more aliases. Ditto if this is an admin.
//
rc = PortUasAddUserToAliases(
userName,
originalUserPriv,
originalAuthFlags & AF_SETTABLE_BITS );
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasAddUserToAliases", rc );
//
// continue to process next user.
//
break;
}
//
// Finally, if there was a Macintosh primary group field for
// this user, then set the primary group for him/her.
//
if ( NetpIsMacPrimaryGroupFieldValid(
(LPCTSTR) (user->usri22_parms) ) ) {
rc = PortUasSetMacPrimaryGroup(
(LPCTSTR) userName,
(LPCTSTR) (user->usri22_parms) );
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasSetMacPrimaryGroup", rc );
// Continue with next user.
}
}
}
//
// If old map entry was a failure, then tell admin and delete entry.
//
if (untriedMapEntry != NULL) {
// BUGBUG: can new name be NULL here?
rc = PortUasComplainAboutBadName(
untriedMapEntry->NewName,
TRUE, // yes, this is user name.
mapReason ); // reason for latest failue.
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasComplainAboutBadName(redo user)",
rc );
goto Cleanup;
}
rc = PortUasDeleteBadMapEntry( untriedMapEntry );
untriedMapEntry = NULL; // Null pointer so we don't kill it.
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasDeleteBadMapEntry(user)", rc );
goto Cleanup;
}
}
//
// If this name needs to be mapped, then try to create a map table
// entry for the original bad name and the original reason.
//
if (mapNeeded) {
NetpAssert( !userUpdated );
rc = PortUasFindOrCreateMapEntry(
originalUserName, // old name
TRUE, // yes, this is user name.
originalMapReason,
& ignoreThisUser,
& untriedMapEntry ); // Do NOT free this!
if (rc != NO_ERROR) {
UNEXPECTED_MSG( "PortUasFindOrCreateMapEntry(user)", rc );
goto Cleanup;
}
if (ignoreThisUser) {
break; // Stop trying to add this user under any name.
}
userName = untriedMapEntry->NewName;
NetpAssert( userName != NULL );
continue; // Loop again checking this name.
}
} while (mapNeeded);
//
// Now that we're done with this user, we can free data for him/her.
//
(void) NetApiBufferFree( user );
user = NULL; // avoid confusing cleanup code.
} // for each user
//
// Everything has been ported, we can actually return successfully.
//
rc = NERR_Success;
//
// Handle error or normal cleanup. (rc must be set before we get here.)
//
Cleanup:
(VOID) PortUasModalsCleanup( CTRL_CLOSE_EVENT );
if (aliasesSetup) {
(VOID) PortUasAliasCleanup();
}
if (databaseOpen) {
PortUasClose();
}
if (mapSetup) {
(VOID) PortUasFreeMapTable( );
}
//
// Free the domain ID memory.
//
if (PortUasAccountsDomainId != NULL) {
(VOID) LocalFree( PortUasAccountsDomainId );
}
if (PortUasBuiltinDomainId != NULL) {
(VOID) LocalFree( PortUasBuiltinDomainId );
}
CLOSE_SAM_HANDLE( PortUasSamConnectHandle );
CLOSE_SAM_HANDLE( PortUasSamAccountsDomainHandle );
CLOSE_SAM_HANDLE( PortUasSamBuiltinDomainHandle );
if (user != NULL) {
(void) NetApiBufferFree( user );
}
if (userEncryptedPassword != NULL) {
(void) NetApiBufferFree( userEncryptedPassword );
}
return (rc);
} // PortUas()
// Expected returns: NERR_NotPrimary, ERROR_ACCESS_DENIED, or NO_ERROR.
NET_API_STATUS
PortUasMachineAllowsUpdates(
VOID
)
{
NET_API_STATUS ApiStatus;
LPUSER_MODALS_INFO_1 Modals = NULL;
if (Verbose) {
NetpKdPrint(( PREFIX_PORTUAS "PortUasMachineAllowsUpdates: "
"getting role...\n" ));
}
ApiStatus = NetUserModalsGet(
NULL, // no server name (local)
1, // info level
(LPBYTE *) (LPVOID) &Modals ); // alloc and set ptr
if (ApiStatus == ERROR_ACCESS_DENIED) {
goto Cleanup;
} else if (ApiStatus != NO_ERROR) {
UNEXPECTED_MSG( "NetUserModalsGet", ApiStatus );
goto Cleanup;
}
NetpAssert( Modals != NULL );
switch ( Modals->usrmod1_role ) {
case UAS_ROLE_PRIMARY:
break;
case UAS_ROLE_BACKUP:
ApiStatus = NERR_NotPrimary;
goto Cleanup;
case UAS_ROLE_MEMBER: /*FALLTHROUGH*/
case UAS_ROLE_STANDALONE: /*FALLTHROUGH*/
default:
// CliffV says we won't ever see member or standalone for NT.
NetpKdPrint(( PREFIX_PORTUAS "PortUasMachineAllowsUpdates: "
"unexpected value " FORMAT_DWORD " for role.\n",
Modals->usrmod1_role ));
NetpAssert( FALSE );
ApiStatus = NERR_InternalError;
goto Cleanup;
}
//
// Now find out if we're really an admin.
//
if (Verbose) {
NetpKdPrint(( PREFIX_PORTUAS "PortUasMachineAllowsUpdates: "
"seeing if we're an admin.\n" ));
}
ApiStatus = NetUserModalsSet (
NULL, // no server name
1, // level
(LPVOID) Modals, // buffer
NULL ); // don't care about parm err
if (ApiStatus == ERROR_ACCESS_DENIED) {
// caller will tell user.
goto Cleanup;
} else if (ApiStatus != NO_ERROR) {
UNEXPECTED_MSG( "NetUserModalsSet(test)", ApiStatus );
goto Cleanup;
}
Cleanup:
if (Modals != NULL) {
(VOID) NetApiBufferFree( Modals );
}
return (ApiStatus);
}
BOOL
PortUasIsGroupRedundant(
IN LPWSTR GroupName
)
{
NetpAssert( GroupName != NULL );
NetpAssert( (*GroupName) != NULLC );
if ( _wcsicmp( GroupName, (LPCTSTR) GROUP_SPECIALGRP_ADMINS ) == 0) {
return (TRUE); // Match, must be redundant.
} else if ( _wcsicmp( GroupName, (LPCTSTR) GROUP_SPECIALGRP_GUESTS ) == 0) {
return (TRUE); // Match, must be redundant.
} else if ( _wcsicmp( GroupName, (LPCTSTR) GROUP_SPECIALGRP_LOCAL ) == 0) {
return (TRUE); // Match, must be redundant.
} else if ( _wcsicmp( GroupName, (LPCTSTR) GROUP_SPECIALGRP_USERS ) == 0) {
return (TRUE); // Match, must be redundant.
} else {
return (FALSE); // No match, must not be redundant.
}
/*NOTREACHED*/
} // PortUasIsGroupRedundant
NET_API_STATUS
PortUasError(
IN NET_API_STATUS Error
)
/*++
Routine Description:
Maps certain errors onto ones that PortUas can return.
Arguments:
Error - the error code to map.
Return Value:
NET_API_STATUS - the mapped error code.
--*/
{
switch ( Error ) {
case NERR_NoRoom:
return ERROR_NOT_ENOUGH_MEMORY;
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_PASSWORD:
case NERR_UserNotFound:
return NERR_InternalError;
default:
return Error;
}
} // PortUasError
int FileIsConsole(HANDLE fh)
{
unsigned htype ;
return GetConsoleMode( fh, &htype );
// htype = GetFileType(fh);
// htype &= ~FILE_TYPE_REMOTE;
// return htype == FILE_TYPE_CHAR;
}
#define MAX_BUF_SIZE 1024
TCHAR ConBuf[MAX_BUF_SIZE];
static CHAR AnsiBuf[MAX_BUF_SIZE*2]; /* 2 because of DBCS */
int
MyWriteConsole(int fOutOrErr, int cch)
{
HANDLE hOut;
if (fOutOrErr == STDOUT)
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
else
hOut = GetStdHandle(STD_ERROR_HANDLE);
if (FileIsConsole(hOut))
WriteConsole(hOut, ConBuf, cch, &cch, NULL);
else {
cch = WideCharToMultiByte(CP_OEMCP, 0,
ConBuf, cch,
AnsiBuf, MAX_BUF_SIZE*3,
NULL, NULL);
WriteFile(hOut, AnsiBuf, cch, &cch, NULL);
}
return cch;
}
int
WriteToCon(TCHAR*fmt, ...)
{
va_list args;
int cch;
va_start( args, fmt );
cch = _vsntprintf( ConBuf, MAX_BUF_SIZE, fmt, args );
va_end( args );
return MyWriteConsole(STDOUT, cch);
}