/*++ Copyright (c) 1993 Microsoft Corporation Module Name: gateway.c Abstract: This module contains gateway devices routines supported by NetWare Workstation service. Author: Chuck Y Chan (ChuckC) 31-Oct-1993 Revision History: --*/ #include #include #include #include #include #include #include //-------------------------------------------------------------------// // // // Local Function Prototypes // // // //-------------------------------------------------------------------// // // wrapper round the RPC routines. // DWORD NwrEnumGWDevices( LPWSTR Reserved, LPDWORD Index, LPBYTE Buffer, DWORD BufferSize, LPDWORD BytesNeeded, LPDWORD EntriesRead ) /*++ Routine Description: This routine enumerates the special gateway devices (redirections) that are cureently in use. Arguments: Index - Point to start enumeration. Should be zero for first call. This is set by the function and can be used to resume the enumeration. Buffer - buffer for return data BufferSize - size of buffer in bytes BytesNeeded - number of bytes needed to return all the data EntriesRead - number of entries read Return Value: Returns the appropriate Win32 error. If NO_ERROR or ERROR_MORE_DATA then EntriesRead will indicated the number of valid entries in buffer. --*/ { UNREFERENCED_PARAMETER(Reserved); return ( NwEnumerateGWDevices( Index, Buffer, BufferSize, BytesNeeded, EntriesRead ) ) ; } DWORD NwrAddGWDevice( LPWSTR Reserved, LPWSTR DeviceName, LPWSTR RemoteName, LPWSTR AccountName, LPWSTR Password, DWORD Flags ) /*++ Routine Description: This routine adds a gateway redirection. Arguments: DeviceName - the drive to redirect RemoteName - the remote network resource to redirect to Flags - supplies the options (eg. UpdateRegistry & make this sticky) Return Value: Returns the appropriate Win32 error. --*/ { DWORD status ; UNREFERENCED_PARAMETER(Reserved); // // make connection to the server to ensure we have a connection. // if GatewayConnectionAlways is false, the function will immediately // delete the connection, so this will just be an access check. // status = NwCreateGWConnection( RemoteName, AccountName, Password, GatewayConnectionAlways) ; if (status != NO_ERROR) { return status ; } // // make the symbolic link // return ( NwCreateGWDevice( DeviceName, RemoteName, Flags ) ) ; } DWORD NwrDeleteGWDevice( LPWSTR Reserved, LPWSTR DeviceName, DWORD Flags ) /*++ Routine Description: This routine enumerates the special gateway devices (redirections) that are cureently in use. Arguments: Index - Point to start enumeration. Should be zero for first call. This is set by the function and can be used to resume the enumeration. Buffer - buffer for return data BufferSize - size of buffer in bytes BytesNeeded - number of bytes needed to return all the data EntriesRead - number of entries read Return Value: Returns the appropriate Win32 error. --*/ { UNREFERENCED_PARAMETER(Reserved); return ( NwRemoveGWDevice( DeviceName, Flags ) ) ; } DWORD NwrQueryGatewayAccount( LPWSTR Reserved, LPWSTR AccountName, DWORD AccountNameLen, LPDWORD AccountCharsNeeded, LPWSTR Password, DWORD PasswordLen, LPDWORD PasswordCharsNeeded ) /*++ Routine Description: Query the gateway account info. specifically, the Account name and the passeord stored as an LSA secret. Arguments: AccountName - buffer used to return account name AccountNameLen - length of buffer AccountCharsNeeded - number of chars needed. only set properly if AccountNameLen is too small. Password - buffer used to return account name PasswordLen - length of buffer PasswordCharsNeeded - number of chars needed, only set properly if PasswordLen is too small. Return Value: Returns the appropriate Win32 error. --*/ { UNREFERENCED_PARAMETER(Reserved); return ( NwQueryGWAccount( AccountName, AccountNameLen, AccountCharsNeeded, Password, PasswordLen, PasswordCharsNeeded ) ) ; } DWORD NwrSetGatewayAccount( LPWSTR Reserved, LPWSTR AccountName, LPWSTR Password ) /*++ Routine Description: Set the account and password to be used for gateway access. Arguments: AccountName - the account (NULL terminated) Password - the password string (NULL terminated) Return Value: Returns the appropriate Win32 error. --*/ { UNREFERENCED_PARAMETER(Reserved); return ( NwSetGWAccount( AccountName, Password ) ) ; } // // actual functions // DWORD NwEnumerateGWDevices( LPDWORD Index, LPBYTE Buffer, DWORD BufferSize, LPDWORD BytesNeeded, LPDWORD EntriesRead ) /*++ Routine Description: This routine enumerates the special gateway devices (redirections) that are cureently in use. Arguments: Index - Point to start enumeration. Should be zero for first call. This is set by the function and can be used to resume the enumeration. Buffer - buffer for return data BufferSize - size of buffer in bytes BytesNeeded - number of bytes needed to return all the data EntriesRead - number of entries read Return Value: Returns the appropriate Win32 error. --*/ { DWORD NwRdrNameLength, NwProviderNameSize ; DWORD i, status ; DWORD Length, Count, BytesRequired, SkipCount ; WCHAR Drive[3] ; WCHAR Path[MAX_PATH+1] ; NETRESOURCEW *lpNetRes = (NETRESOURCEW *) Buffer ; LPBYTE BufferEnd = Buffer + ROUND_DOWN_COUNT(BufferSize,ALIGN_WCHAR) ; // // init the parts of the drives string we never change // Drive[1] = L':' ; Drive[2] = 0 ; Count = 0 ; BytesRequired = 0 ; SkipCount = *Index ; NwProviderNameSize = wcslen(NwProviderName) + 1 ; NwRdrNameLength = sizeof(DD_NWFS_DEVICE_NAME_U)/sizeof(WCHAR) - 1 ; // // for all logical drives // for (i = 0; i <26 ; i++) { BOOL GatewayDrive = FALSE ; Drive[0] = L'A' + (USHORT)i ; // // get the symbolic link // Length = QueryDosDeviceW(Drive, Path, sizeof(Path)/sizeof(Path[0])) ; // // the value must be at least as long as our device name // if (Length >= NwRdrNameLength + 4) { // // and it must match the following criteria: // 1) start with \device\nwrdr // 2) must have '\' after \device\nwrdr // 3) must not have colon (ie. must be` // \\device\nwrdr\server\share, and not // \\device\nwrdr\x:\server\share // if ((_wcsnicmp(Path,DD_NWFS_DEVICE_NAME_U,NwRdrNameLength) == 0) && (Path[NwRdrNameLength] == '\\') && (Path[NwRdrNameLength+2] != ':')) { // // if this is an indexed read, skip the first N. // this is inefficient, but we do not expect to // have to go thru this very often. there are few // such devices, and any reasonable buffer (even 1K) // should get them all first time. // if (SkipCount) SkipCount-- ; else GatewayDrive = TRUE ; // found a drive we want } } if (GatewayDrive) { // // we meet all criteria above // DWORD UncSize ; UncSize = Length - NwRdrNameLength + 2 ; BytesRequired += ( sizeof(NETRESOURCE) + (UncSize * sizeof(WCHAR)) + (NwProviderNameSize * sizeof(WCHAR)) + (3 * sizeof(WCHAR))) ; // 3 for drive, X:\0 if (BytesRequired <= BufferSize) { LPWSTR lpStr = (LPWSTR) BufferEnd ; Count++ ; // // copy the drive name // lpStr -= 3 ; wcscpy(lpStr, Drive) ; lpNetRes->lpLocalName = (LPWSTR) ((LPBYTE)lpStr - Buffer) ; // // copy the UNC name // lpStr -= UncSize ; // for the UNC name lpStr[0] = L'\\' ; wcscpy(lpStr+1, Path+NwRdrNameLength) ; lpNetRes->lpRemoteName = (LPWSTR) ((LPBYTE)lpStr - Buffer) ; // // copy the provider name // lpStr -= NwProviderNameSize ; // for the provider name wcscpy(lpStr, NwProviderName) ; lpNetRes->lpProvider = (LPWSTR) ((LPBYTE)lpStr - Buffer) ; // // set up the rest of the structure // lpNetRes->dwScope = RESOURCE_CONNECTED ; lpNetRes->dwType = RESOURCETYPE_DISK ; lpNetRes->dwDisplayType = 0 ; lpNetRes->dwUsage = 0 ; lpNetRes->lpComment = 0 ; lpNetRes++ ; BufferEnd = (LPBYTE) lpStr ; } } } *EntriesRead = Count ; // set number of entries *Index += Count ; // move index *BytesNeeded = BytesRequired ; // set bytes needed if (BytesRequired == 0) // no info left return (ERROR_NO_MORE_ITEMS) ; if (BytesRequired > BufferSize) { return (ERROR_MORE_DATA) ; } return NO_ERROR ; } DWORD NwCreateGWDevice( LPWSTR DeviceName, LPWSTR RemoteName, DWORD Flags ) /*++ Routine Description: This routine adds a gateway redirection. Arguments: DeviceName - the drive to redirect RemoteName - the remote network resource to redirect to Flags - supplies the options (eg. UpdateRegistry & make this sticky) Return Value: Returns the appropriate Win32 error. --*/ { LPWSTR ConnectName = NULL; DWORD status ; WCHAR Path [MAX_PATH + 1] ; // // validate/canon the name. Use a drive to specific we allow UNC only. // if ((status = NwLibCanonRemoteName( L"A:", RemoteName, &ConnectName, NULL )) != NO_ERROR) { return status; } // // build up the full name of \device\nwrdr\server\volume // wcscpy(Path, DD_NWFS_DEVICE_NAME_U) ; wcscat(Path, ConnectName+1 ) ; // // create the symbolic link // status = NwCreateSymbolicLink(DeviceName, Path) ; (void) LocalFree((HLOCAL) ConnectName); // // if update registry is set, write it out // if ((status == NO_ERROR) && (Flags & NW_GW_UPDATE_REGISTRY)) { HKEY hKey ; DWORD dwDisposition ; // // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Drives (create it if not there) // status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_DRIVES, 0, L"", REG_OPTION_NON_VOLATILE, KEY_WRITE, // desired access NULL, // default security &hKey, &dwDisposition // ignored ); if ( status ) return status ; status = RegSetValueExW( hKey, DeviceName, 0, REG_SZ, (LPBYTE) RemoteName, (wcslen(RemoteName)+1) * sizeof(WCHAR)) ; RegCloseKey( hKey ); } return status ; } DWORD NwRemoveGWDevice( LPWSTR DeviceName, DWORD Flags ) /*++ Routine Description: This routine deletes a gateway redirection. Arguments: DeviceName - the drive to delete Flags - supplies the options (eg. UpdateRegistry & make this sticky) Return Value: Returns the appropriate Win32 error. --*/ { DWORD status ; LPWSTR Local = NULL ; if (status = NwLibCanonLocalName(DeviceName, &Local, NULL)) return status ; // // delete the symbolic link // if (! DefineDosDeviceW(DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH, Local, DD_NWFS_DEVICE_NAME_U)) { status = ERROR_INVALID_DRIVE ; } // // If cleanup deleted (dangling) share is set go do it now. // We loop thru all the shares looking for one that matches that drive. // Then we ask the server to nuke that dangling share. // if ((status == NO_ERROR) && (Flags & NW_GW_CLEANUP_DELETED)) { HKEY hKey ; DWORD err ; // // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Shares // err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_SHARES, REG_OPTION_NON_VOLATILE, // options KEY_READ | KEY_WRITE, // desired access &hKey ); if (err == NO_ERROR) { WCHAR Path[MAX_PATH + 1], ShareName[MAX_PATH+1] ; DWORD dwType, i = 0, dwPathSize, dwShareNameSize ; do { dwPathSize = sizeof(Path), dwShareNameSize = sizeof(ShareName)/sizeof(ShareName[0]) ; dwType = REG_SZ ; err = RegEnumValueW(hKey, i, ShareName, &dwShareNameSize, NULL, &dwType, (LPBYTE)Path, &dwPathSize) ; // // Look for matching drive, eg. "X:" // If have match, cleanup as best we can and break out now. // if ((err == NO_ERROR) && (_wcsnicmp(Path,DeviceName,2) == 0)) { (void) NetShareDelSticky( NULL, ShareName, 0 ) ; (void) RegDeleteValueW(hKey, ShareName) ; break ; } i++ ; } while (err == NO_ERROR) ; RegCloseKey( hKey ); } } // // if update registry is set, write it out // if ((status == NO_ERROR) && (Flags & NW_GW_UPDATE_REGISTRY)) { HKEY hKey ; WCHAR Path[MAX_PATH + 1] ; DWORD dwType, dwSize = sizeof(Path) ; // // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Drives // status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_DRIVES, REG_OPTION_NON_VOLATILE, // options KEY_READ | KEY_WRITE, // desired access &hKey ); if ( status ) goto ExitPoint ; if (GatewayConnectionAlways) { // // Read the remote path and delete the connection // if we made one in the first place. // status = RegQueryValueExW( hKey, Local, NULL, &dwType, (LPBYTE) Path, &dwSize ); if (status == NO_ERROR) { (void) NwDeleteGWConnection(Path) ; } } status = RegDeleteValueW( hKey, DeviceName ) ; RegCloseKey( hKey ); } ExitPoint: if (Local) (void) LocalFree((HLOCAL)Local) ; return status ; } DWORD NwGetGatewayResource( IN LPWSTR LocalName, OUT LPWSTR RemoteName, IN DWORD RemoteNameLen, OUT LPDWORD CharsRequired ) /*++ Routine Description: For a gateway devicename, get the network resource associated with it. Arguments: LocalName - name of devive to query RemoteName - buffer to return the network resource RemoteNameLen - size of buffer (chars) CharsRequired - the number of chars needed Return Value: WN_SUCCESS - success (the device is a gateway redirection) WN_MORE_DATA - buffer too small, but device is a gateway redirection WN_NOT_CONNECTED - not a gateway redirection --*/ { WCHAR Path[MAX_PATH+1] ; DWORD Length ; DWORD NwRdrNameLength ; NwRdrNameLength = sizeof(DD_NWFS_DEVICE_NAME_U)/sizeof(WCHAR) - 1 ; // // retrieve symbolic link for the device // Length = QueryDosDeviceW(LocalName, Path, sizeof(Path)/sizeof(Path[0])) ; // // the result is only interesting if it can at least fit: // \device\nwrdr\x\y // if (Length >= NwRdrNameLength + 4) { // // check to make sure that the prefix is coreect, and that // it is not of form: \device\nwrdr\x:\... // if ((_wcsnicmp(Path,DD_NWFS_DEVICE_NAME_U,NwRdrNameLength) == 0) && (Path[NwRdrNameLength] == '\\') && (Path[NwRdrNameLength+2] != ':')) { // // check buffer size // if (RemoteNameLen < ((Length - NwRdrNameLength) + 1)) { if (CharsRequired) *CharsRequired = ((Length - NwRdrNameLength) + 1) ; return WN_MORE_DATA ; } *RemoteName = L'\\' ; wcscpy(RemoteName+1,Path+NwRdrNameLength) ; return WN_SUCCESS ; } } return WN_NOT_CONNECTED ; } DWORD NwQueryGWAccount( LPWSTR AccountName, DWORD AccountNameLen, LPDWORD AccountCharsNeeded, LPWSTR Password, DWORD PasswordLen, LPDWORD PasswordCharsNeeded ) /*++ Routine Description: Query the gateway account info. specifically, the Account name and the passeord stored as an LSA secret. Arguments: AccountName - buffer used to return account name AccountNameLen - length of buffer AccountCharsNeeded - number of chars needed. only set properly if AccountNameLen is too small. Password - buffer used to return account name PasswordLen - length of buffer PasswordCharsNeeded - number of chars needed, only set properly if PasswordLen is too small. Return Value: Returns the appropriate Win32 error. --*/ { DWORD status = NO_ERROR ; LONG RegError; HKEY WkstaKey = NULL; LPWSTR GatewayAccount = NULL; PUNICODE_STRING StoredPassword = NULL; PUNICODE_STRING StoredOldPassword = NULL; *AccountCharsNeeded = 0 ; *PasswordCharsNeeded = 0 ; // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Parameters // RegError = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_REGKEY, REG_OPTION_NON_VOLATILE, // options KEY_READ, // desired access &WkstaKey ); if (RegError != ERROR_SUCCESS) { return (RegError); } // // Read the gateway account from the registry. // status = NwReadRegValue( WkstaKey, NW_GATEWAYACCOUNT_VALUENAME, &GatewayAccount ); if (status != NO_ERROR) { if (status != ERROR_FILE_NOT_FOUND) goto CleanExit; if (AccountNameLen > 0) *AccountName = 0 ; status = NO_ERROR ; } else { *AccountCharsNeeded = wcslen(GatewayAccount) + 1 ; if (*AccountCharsNeeded > AccountNameLen) { status = ERROR_INSUFFICIENT_BUFFER ; goto CleanExit; } wcscpy(AccountName,GatewayAccount); } // // Read the password from its secret object in LSA. // status = NwGetPassword( GATEWAY_USER, &StoredPassword, // Must be freed with LsaFreeMemory &StoredOldPassword // Must be freed with LsaFreeMemory ); if (status != NO_ERROR) { if (status != ERROR_FILE_NOT_FOUND) goto CleanExit; if (PasswordLen > 0) *Password = 0 ; status = NO_ERROR ; } else { *PasswordCharsNeeded = StoredPassword->Length/sizeof(WCHAR) + 1 ; if ((StoredPassword->Length/sizeof(WCHAR)) >= PasswordLen) { status = ERROR_INSUFFICIENT_BUFFER ; goto CleanExit; } wcsncpy(Password, StoredPassword->Buffer, StoredPassword->Length/sizeof(WCHAR)); Password[StoredPassword->Length/sizeof(WCHAR)] = 0 ; } CleanExit: if (StoredPassword != NULL) { (void) LsaFreeMemory((PVOID) StoredPassword); } if (StoredOldPassword != NULL) { (void) LsaFreeMemory((PVOID) StoredOldPassword); } if (GatewayAccount != NULL) { (void) LocalFree((HLOCAL) GatewayAccount); } (void) RegCloseKey(WkstaKey); return status ; } DWORD NwSetGWAccount( LPWSTR AccountName, LPWSTR Password ) /*++ Routine Description: Set the account and password to be used for gateway access. Arguments: AccountName - the account (NULL terminated) Password - the password string (NULL terminated) Return Value: Returns the appropriate Win32 error. --*/ { DWORD status ; HKEY WkstaKey = NULL; // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Parameters // status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_REGKEY, REG_OPTION_NON_VOLATILE, // options KEY_WRITE, // desired access &WkstaKey ); if (status != ERROR_SUCCESS) { return (status); } // // Write the account name out // status = RegSetValueExW( WkstaKey, NW_GATEWAYACCOUNT_VALUENAME, 0, REG_SZ, (LPVOID) AccountName, (wcslen(AccountName) + 1) * sizeof(WCHAR) ); if (status == NO_ERROR) { status = NwSetPassword( GATEWAY_USER, Password) ; } return status ; } DWORD NwCreateRedirections( LPWSTR Account, LPWSTR Password ) /*++ Routine Description: Create the gateway redirections from what is stored in registry. As we go along, we validate that we have access to it using the gateway account. Arguments: AccountName - the account (NULL terminated) Password - the password string (NULL terminated) Return Value: Returns the appropriate Win32 error. --*/ { DWORD err, i, type ; HKEY hKey ; FILETIME FileTime ; WCHAR Class[256], Device[64], Path[MAX_PATH+1] ; DWORD dwClass, dwSubKeys, dwMaxSubKey, dwMaxClass, dwValues, dwMaxValueName, dwMaxValueData, dwSDLength, dwDeviceLength, dwPathLength ; // // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCGateway\Parameters // err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_DRIVES, REG_OPTION_NON_VOLATILE, // options KEY_READ, // desired access &hKey ); if ( err ) return err ; dwClass = sizeof(Class)/sizeof(Class[0]) ; err = RegQueryInfoKeyW(hKey, Class, &dwClass, NULL, &dwSubKeys, &dwMaxSubKey, &dwMaxClass, &dwValues, &dwMaxValueName, &dwMaxValueData, &dwSDLength, &FileTime) ; if ( err ) { RegCloseKey( hKey ); return err ; } // // for each value we have a redirection to recreate // for (i = 0; i < dwValues; i++) { dwDeviceLength = sizeof(Device)/sizeof(Device[0]) ; dwPathLength = sizeof(Path) ; type = REG_SZ ; err = RegEnumValueW(hKey, i, Device, &dwDeviceLength, NULL, &type, (LPBYTE)Path, &dwPathLength) ; // // connect to the server. this will take up a connection but // it will also make sure we have one. on a low limit server, if // we rely on UNC then it is quite likely that other people will // come along & use up all the connections, preventing the Gateway // from getting to it. // // user may turn this off by setting Registry value that results // GatewayConnectionAlways being false. // // regardless of result, we carry on. so if server is down & comes // up later, the symbolic link to UNC will still work. // if (!err) { (void) NwCreateGWConnection( Path, Account, Password, GatewayConnectionAlways) ; } // // create the symbolic link // if (!err) { err = NwCreateGWDevice(Device, Path, 0L) ; } if (err) { // // log the error in the event log // WCHAR Number[16] ; LPWSTR InsertStrings[3] ; wsprintfW(Number, L"%d", err) ; InsertStrings[0] = Device ; InsertStrings[1] = Path ; InsertStrings[2] = Number ; NwLogEvent(EVENT_NWWKSTA_CANNOT_REDIRECT_DEVICES, 3, InsertStrings, 0) ; } } RegCloseKey( hKey ); return NO_ERROR ; } DWORD NwDeleteRedirections( VOID ) /*++ Routine Description: Delete all gateway devices Arguments: Return Value: Returns the appropriate Win32 error. --*/ { LPBYTE Buffer ; DWORD i, status, Index, BufferSize, EntriesRead, BytesNeeded ; LPNETRESOURCE lpNetRes ; Index = 0 ; // // below is good initial guess // BufferSize = 26 * (sizeof(NETRESOURCE) + (3 + MAX_PATH + 1 + MAX_PATH + 1) * sizeof(WCHAR)) ; Buffer = (LPBYTE) LocalAlloc(LPTR, BufferSize) ; if (!Buffer) return (GetLastError()) ; lpNetRes = (LPNETRESOURCE) Buffer ; status = NwrEnumGWDevices(NULL, &Index, Buffer, BufferSize, &BytesNeeded, &EntriesRead) ; // // reallocate as need // if (status == ERROR_MORE_DATA || status == ERROR_INSUFFICIENT_BUFFER) { Buffer = (LPBYTE) LocalReAlloc((HLOCAL)Buffer, BytesNeeded, LMEM_ZEROINIT) ; if (!Buffer) return (GetLastError()) ; Index = 0 ; BufferSize = BytesNeeded ; status = NwrEnumGWDevices(NULL, &Index, Buffer, BufferSize, &BytesNeeded, &EntriesRead) ; } if (status != NO_ERROR) return status ; // // loop thru and delete all the devices // for (i = 0; i < EntriesRead; i++) { status = NwrDeleteGWDevice(NULL, (LPWSTR)((LPBYTE)Buffer + (DWORD)lpNetRes->lpLocalName), 0L) ; // // no need report the error, since we are shutting down. // there is no real deletion here - just removing the symbolic link. // lpNetRes++ ; } return NO_ERROR ; }