/*++ 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 // NTSTATUS, NT_SUCCESS(). #include // (Needed with nt.h and windows.h) #include // (Needed with ntrtl.h and windows.h) #include // LocalFree(), SetConsoleCtrlHandler(), etc. #include // These may be included in any order: #include // LM_OWF_PASSWORD, Rtl encrypt/decrypt/equal routines. #include // AF_ and UF_ equates, LPUSER_INFO_22, etc. #include #include #include // NetpIsMacPrimaryGroupFieldValid(). #include // DBGSTATIC, NetpAssert(), etc. #include // LOCAL_DOMAIN_TYPE_ equates. #include // NetpNtStatusToApiStatus(). #include #include // WARNING_MSG(), PortUasSam*, Verbose, etc. #include // _wcsicmp(). #include #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); }