/*++ Copyright (c) 1993 Microsoft Corporation Module Name: reg.c Abstract: This module provides helpers to call the registry used by both the client and server sides of the workstation. Author: Rita Wong (ritaw) 22-Apr-1993 --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LMSERVER_LINKAGE_REGKEY L"System\\CurrentControlSet\\Services\\LanmanServer\\Linkage" #define OTHERDEPS_VALUENAME L"OtherDependencies" #define LANMAN_SERVER L"LanmanServer" // // Forward Declare // static DWORD NwRegQueryValueExW( IN HKEY hKey, IN LPWSTR lpValueName, OUT LPDWORD lpReserved, OUT LPDWORD lpType, OUT LPBYTE lpData, IN OUT LPDWORD lpcbData ); static DWORD EnumAndDeleteShares( VOID ) ; DWORD CalcNullNullSize( WCHAR *pszNullNull ) ; WCHAR * FindStringInNullNull( WCHAR *pszNullNull, WCHAR *pszString ) ; VOID RemoveNWCFromNullNullList( WCHAR *OtherDeps ) ; DWORD RemoveNwcDependency( VOID ) ; DWORD NwReadRegValue( IN HKEY Key, IN LPWSTR ValueName, OUT LPWSTR *Value ) /*++ Routine Description: This function allocates the output buffer and reads the requested value from the registry into it. Arguments: Key - Supplies opened handle to the key to read from. ValueName - Supplies name of the value to retrieve data. Value - Returns a pointer to the output buffer which points to the memory allocated and contains the data read in from the registry. This pointer must be freed with LocalFree when done. Return Value: ERROR_NOT_ENOUGH_MEMORY - Failed to create buffer to read value into. Error from registry call. --*/ { LONG RegError; DWORD NumRequired = 0; DWORD ValueType; // // Set returned buffer pointer to NULL. // *Value = NULL; RegError = NwRegQueryValueExW( Key, ValueName, NULL, &ValueType, (LPBYTE) NULL, &NumRequired ); if (RegError != ERROR_SUCCESS && NumRequired > 0) { if ((*Value = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, (UINT) NumRequired )) == NULL) { KdPrint(("NWWORKSTATION: NwReadRegValue: LocalAlloc of size %lu failed %lu\n", NumRequired, GetLastError())); return ERROR_NOT_ENOUGH_MEMORY; } RegError = NwRegQueryValueExW( Key, ValueName, NULL, &ValueType, (LPBYTE) *Value, &NumRequired ); } else if (RegError == ERROR_SUCCESS) { KdPrint(("NWWORKSTATION: NwReadRegValue got SUCCESS with NULL buffer.")); return ERROR_FILE_NOT_FOUND; } if (RegError != ERROR_SUCCESS) { if (*Value != NULL) { (void) LocalFree((HLOCAL) *Value); *Value = NULL; } return (DWORD) RegError; } return NO_ERROR; } static DWORD NwRegQueryValueExW( IN HKEY hKey, IN LPWSTR lpValueName, OUT LPDWORD lpReserved, OUT LPDWORD lpType, OUT LPBYTE lpData, IN OUT LPDWORD lpcbData ) /*++ Routine Description: This routine supports the same functionality as Win32 RegQueryValueEx API, except that it works. It returns the correct lpcbData value when a NULL output buffer is specified. This code is stolen from the service controller. Arguments: same as RegQueryValueEx Return Value: NO_ERROR or reason for failure. --*/ { NTSTATUS ntstatus; UNICODE_STRING ValueName; PKEY_VALUE_FULL_INFORMATION KeyValueInfo; DWORD BufSize; UNREFERENCED_PARAMETER(lpReserved); // // Make sure we have a buffer size if the buffer is present. // if ((ARGUMENT_PRESENT(lpData)) && (! ARGUMENT_PRESENT(lpcbData))) { return ERROR_INVALID_PARAMETER; } RtlInitUnicodeString(&ValueName, lpValueName); // // Allocate memory for the ValueKeyInfo // BufSize = *lpcbData + sizeof(KEY_VALUE_FULL_INFORMATION) + ValueName.Length - sizeof(WCHAR); // subtract memory for 1 char because it's included // in the sizeof(KEY_VALUE_FULL_INFORMATION). KeyValueInfo = (PKEY_VALUE_FULL_INFORMATION) LocalAlloc( LMEM_ZEROINIT, (UINT) BufSize ); if (KeyValueInfo == NULL) { KdPrint(("NWWORKSTATION: NwRegQueryValueExW: LocalAlloc failed %lu\n", GetLastError())); return ERROR_NOT_ENOUGH_MEMORY; } ntstatus = NtQueryValueKey( hKey, &ValueName, KeyValueFullInformation, (PVOID) KeyValueInfo, (ULONG) BufSize, (PULONG) &BufSize ); if ((NT_SUCCESS(ntstatus) || (ntstatus == STATUS_BUFFER_OVERFLOW)) && ARGUMENT_PRESENT(lpcbData)) { *lpcbData = KeyValueInfo->DataLength; } if (NT_SUCCESS(ntstatus)) { if (ARGUMENT_PRESENT(lpType)) { *lpType = KeyValueInfo->Type; } if (ARGUMENT_PRESENT(lpData)) { memcpy( lpData, (LPBYTE)KeyValueInfo + KeyValueInfo->DataOffset, KeyValueInfo->DataLength ); } } (void) LocalFree((HLOCAL) KeyValueInfo); return RtlNtStatusToDosError(ntstatus); } VOID NwLuidToWStr( IN PLUID LogonId, OUT LPWSTR LogonIdStr ) /*++ Routine Description: This routine converts a LUID into a string in hex value format so that it can be used as a registry key. Arguments: LogonId - Supplies the LUID. LogonIdStr - Receives the string. This routine assumes that this buffer is large enough to fit 17 characters. Return Value: None. --*/ { swprintf(LogonIdStr, L"%08lx%08lx", LogonId->HighPart, LogonId->LowPart); } VOID NwWStrToLuid( IN LPWSTR LogonIdStr, OUT PLUID LogonId ) /*++ Routine Description: This routine converts a string in hex value format into a LUID. Arguments: LogonIdStr - Supplies the string. LogonId - Receives the LUID. Return Value: None. --*/ { swscanf(LogonIdStr, L"%08lx%08lx", &LogonId->HighPart, &LogonId->LowPart); } DWORD NwDeleteInteractiveLogon( IN PLUID Id OPTIONAL ) /*++ Routine Description: This routine deletes a specific interactive logon ID key in the registry if a logon ID is specified, otherwise it deletes all interactive logon ID keys. Arguments: Id - Supplies the logon ID to delete. NULL means delete all. Return Status: None. --*/ { LONG RegError; LONG DelError = ERROR_SUCCESS; HKEY InteractiveLogonKey; WCHAR LogonIdKey[NW_MAX_LOGON_ID_LEN]; RegError = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_INTERACTIVE_LOGON_REGKEY, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE | DELETE, &InteractiveLogonKey ); if (RegError != ERROR_SUCCESS) { return RegError; } if (ARGUMENT_PRESENT(Id)) { // // Delete the key specified. // NwLuidToWStr(Id, LogonIdKey); DelError = RegDeleteKeyW(InteractiveLogonKey, LogonIdKey); if ( DelError ) KdPrint((" NwDeleteInteractiveLogon: failed to delete logon %lu\n", DelError)); } else { // // Delete all interactive logon ID keys. // do { RegError = RegEnumKeyW( InteractiveLogonKey, 0, LogonIdKey, sizeof(LogonIdKey) / sizeof(WCHAR) ); if (RegError == ERROR_SUCCESS) { // // Got a logon id key, delete it. // DelError = RegDeleteKeyW(InteractiveLogonKey, LogonIdKey); } else if (RegError != ERROR_NO_MORE_ITEMS) { KdPrint((" NwDeleteInteractiveLogon: failed to enum logon IDs %lu\n", RegError)); } } while (RegError == ERROR_SUCCESS); } (void) RegCloseKey(InteractiveLogonKey); return ((DWORD) DelError); } VOID NwDeleteCurrentUser( VOID ) /*++ Routine Description: This routine deletes the current user value under the parameters key. Arguments: None. Return Value: None. --*/ { LONG RegError; HKEY WkstaKey; // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Parameters // RegError = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_REGKEY, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE | DELETE, &WkstaKey ); if (RegError != NO_ERROR) { KdPrint(("NWPROVAU: NwpInitializeRegistry open NWCWorkstation\\Parameters key unexpected error %lu!\n", RegError)); return; } // // Delete CurrentUser value first so that the workstation won't be // reading this stale value. Ignore error since it may not exist. // (void) RegDeleteValueW( WkstaKey, NW_CURRENTUSER_VALUENAME ); (void) RegCloseKey(WkstaKey); } DWORD NwDeleteServiceLogon( IN PLUID Id OPTIONAL ) /*++ Routine Description: This routine deletes a specific service logon ID key in the registry if a logon ID is specified, otherwise it deletes all service logon ID keys. Arguments: Id - Supplies the logon ID to delete. NULL means delete all. Return Status: None. --*/ { LONG RegError; LONG DelError = STATUS_SUCCESS; HKEY ServiceLogonKey; WCHAR LogonIdKey[NW_MAX_LOGON_ID_LEN]; RegError = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_SERVICE_LOGON_REGKEY, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE | DELETE, &ServiceLogonKey ); if (RegError != ERROR_SUCCESS) { return RegError; } if (ARGUMENT_PRESENT(Id)) { // // Delete the key specified. // NwLuidToWStr(Id, LogonIdKey); DelError = RegDeleteKeyW(ServiceLogonKey, LogonIdKey); } else { // // Delete all service logon ID keys. // do { RegError = RegEnumKeyW( ServiceLogonKey, 0, LogonIdKey, sizeof(LogonIdKey) / sizeof(WCHAR) ); if (RegError == ERROR_SUCCESS) { // // Got a logon id key, delete it. // DelError = RegDeleteKeyW(ServiceLogonKey, LogonIdKey); } else if (RegError != ERROR_NO_MORE_ITEMS) { KdPrint((" NwDeleteServiceLogon: failed to enum logon IDs %lu\n", RegError)); } } while (RegError == ERROR_SUCCESS); } (void) RegCloseKey(ServiceLogonKey); return ((DWORD) DelError); } DWORD NwpRegisterGatewayShare( IN LPWSTR ShareName, IN LPWSTR DriveName ) /*++ Routine Description: This routine remembers that a gateway share has been created so that it can be cleanup up when NWCS is uninstalled. Arguments: ShareName - name of share DriveName - name of drive that is shared Return Status: Win32 error of any failure. --*/ { DWORD status ; // // make sure we have valid parameters // if (ShareName && DriveName) { HKEY hKey ; DWORD dwDisposition ; // // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Shares (create it if not there) // status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_SHARES, 0, L"", REG_OPTION_NON_VOLATILE, KEY_WRITE, // desired access NULL, // default security &hKey, &dwDisposition // ignored ); if ( status ) return status ; // // wtite out value with valuename=sharename, valuedata=drive // status = RegSetValueExW( hKey, ShareName, 0, REG_SZ, (LPBYTE) DriveName, (wcslen(DriveName)+1) * sizeof(WCHAR)) ; (void) RegCloseKey( hKey ); } else status = ERROR_INVALID_PARAMETER ; return status ; } DWORD NwpCleanupGatewayShares( VOID ) /*++ Routine Description: This routine cleans up all persistent share info and also tidies up the registry for NWCS. Later is not needed in uninstall, but is there so we have a single routine that completely disables the gateway. Arguments: None. Return Status: Win32 error for failed APIs. --*/ { DWORD status, FinalStatus = NO_ERROR ; HKEY WkstaKey = NULL, ServerLinkageKey = NULL ; LPWSTR OtherDeps = NULL ; // // Enumeratre and delete all shares // FinalStatus = status = EnumAndDeleteShares() ; // // if update registry by cleaning out both Drive and Shares keys. // ignore return values here. the keys may not be present. // (void) RegDeleteKeyW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_DRIVES ) ; (void) RegDeleteKeyW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_SHARES ) ; // // 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) { // // delete the gateway account and gateway enabled flag. // ignore failures here (the values may not be present) // (void) RegDeleteValueW( WkstaKey, NW_GATEWAYACCOUNT_VALUENAME ) ; (void) RegDeleteValueW( WkstaKey, NW_GATEWAY_ENABLE ) ; (void) RegCloseKey( WkstaKey ); } // // store new status if necessary // if (FinalStatus == NO_ERROR) FinalStatus = status ; // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \LanmanServer\Linkage // status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, LMSERVER_LINKAGE_REGKEY, REG_OPTION_NON_VOLATILE, // options KEY_WRITE | KEY_READ, // desired access &ServerLinkageKey ); if (status == ERROR_SUCCESS) { // // remove us from the OtherDependencies. // ignore read failures here (it may not be present) // status = NwReadRegValue( ServerLinkageKey, OTHERDEPS_VALUENAME, &OtherDeps ); if (status == NO_ERROR) { // // this call munges the list to remove NWC if there. // RemoveNWCFromNullNullList(OtherDeps) ; status = RegSetValueExW( ServerLinkageKey, OTHERDEPS_VALUENAME, 0, REG_MULTI_SZ, (BYTE *)OtherDeps, CalcNullNullSize(OtherDeps) * sizeof(WCHAR)) ; (void) LocalFree(OtherDeps) ; (void) RemoveNwcDependency() ; // make this happen right away // ignore errors - reboot will fix } else { status = NO_ERROR ; } (void) RegCloseKey( ServerLinkageKey ); } // // store new status if necessary // if (FinalStatus == NO_ERROR) FinalStatus = status ; return (FinalStatus) ; } DWORD NwpClearGatewayShare( IN LPWSTR ShareName ) /*++ Routine Description: This routine deletes a specific share from the remembered gateway shares in the registry. Arguments: ShareName - share value to delete Return Status: Win32 status code. --*/ { DWORD status = NO_ERROR ; // // check that paramter is non null // if (ShareName) { HKEY hKey ; // // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\Drives // status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_SHARES, REG_OPTION_NON_VOLATILE, // options KEY_WRITE, // desired access &hKey ); if ( status ) return status ; status = RegDeleteValueW( hKey, ShareName ) ; (void) RegCloseKey( hKey ); } else status = ERROR_INVALID_PARAMETER ; return status ; } typedef NET_API_STATUS (*PF_NETSHAREDEL) ( LPWSTR server, LPWSTR name, DWORD reserved) ; #define NETSHAREDELSTICKY_API "NetShareDelSticky" #define NETSHAREDEL_API "NetShareDel" #define NETAPI_DLL L"NETAPI32" DWORD EnumAndDeleteShares( VOID ) /*++ Routine Description: This routine removes all persister share info in the server for all gateway shares. Arguments: None. Return Status: Win32 error code. --*/ { DWORD err, i, type ; HKEY hKey = NULL ; FILETIME FileTime ; HANDLE hNetapi = NULL ; PF_NETSHAREDEL pfNetShareDel, pfNetShareDelSticky ; WCHAR Class[256], Share[NNLEN+1], Device[MAX_PATH+1] ; DWORD dwClass, dwSubKeys, dwMaxSubKey, dwMaxClass, dwValues, dwMaxValueName, dwMaxValueData, dwSDLength, dwShareLength, dwDeviceLength ; // // load the library so that not everyone needs link to netapi32 // if (!(hNetapi = LoadLibraryW(NETAPI_DLL))) return (GetLastError()) ; // // get addresses of the 2 functions we are interested in // if (!(pfNetShareDel = (PF_NETSHAREDEL) GetProcAddress(hNetapi, NETSHAREDEL_API))) { err = GetLastError() ; goto ExitPoint ; } if (!(pfNetShareDelSticky = (PF_NETSHAREDEL) GetProcAddress(hNetapi, NETSHAREDELSTICKY_API))) { err = GetLastError() ; goto ExitPoint ; } // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCGateway\Shares // err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_GATEWAY_SHARES, REG_OPTION_NON_VOLATILE, // options KEY_READ, // desired access &hKey ); if ( err ) goto ExitPoint ; // // read the info about that key // dwClass = sizeof(Class)/sizeof(Class[0]) ; err = RegQueryInfoKeyW(hKey, Class, &dwClass, NULL, &dwSubKeys, &dwMaxSubKey, &dwMaxClass, &dwValues, &dwMaxValueName, &dwMaxValueData, &dwSDLength, &FileTime) ; if ( err ) { goto ExitPoint ; } // // for each value found, we have a share to delete // for (i = 0; i < dwValues; i++) { dwShareLength = sizeof(Share)/sizeof(Share[0]) ; dwDeviceLength = sizeof(Device) ; type = REG_SZ ; err = RegEnumValueW(hKey, i, Share, &dwShareLength, NULL, &type, (LPBYTE)Device, &dwDeviceLength) ; // // cleanup the share. try delete the share proper. if not // there, remove the sticky info instead. // if (!err) { err = (*pfNetShareDel)(NULL, Share, 0) ; if (err == NERR_NetNameNotFound) { (void) (*pfNetShareDelSticky)(NULL, Share, 0) ; } } // // ignore errors within the loop. we can to carry on to // cleanup as much as possible. // err = NO_ERROR ; } ExitPoint: if (hKey) (void) RegCloseKey( hKey ); if (hNetapi) (void) FreeLibrary(hNetapi) ; return err ; } DWORD CalcNullNullSize( WCHAR *pszNullNull ) /*++ Routine Description: Walk thru a NULL NULL string, counting the number of characters, including the 2 nulls at the end. Arguments: Pointer to a NULL NULL string Return Status: Count of number of *characters*. See description. --*/ { DWORD dwSize = 0 ; WCHAR *pszTmp = pszNullNull ; if (!pszNullNull) return 0 ; while (*pszTmp) { DWORD dwLen = wcslen(pszTmp) + 1 ; dwSize += dwLen ; pszTmp += dwLen ; } return (dwSize+1) ; } WCHAR * FindStringInNullNull( WCHAR *pszNullNull, WCHAR *pszString ) /*++ Routine Description: Walk thru a NULL NULL string, looking for the search string Arguments: pszNullNull: the string list we will search. pszString: what we are searching for. Return Status: The start of the string if found. Null, otherwise. --*/ { WCHAR *pszTmp = pszNullNull ; if (!pszNullNull || !*pszNullNull) return NULL ; do { if (_wcsicmp(pszTmp,pszString)==0) return pszTmp ; pszTmp += wcslen(pszTmp) + 1 ; } while (*pszTmp) ; return NULL ; } VOID RemoveNWCFromNullNullList( WCHAR *OtherDeps ) /*++ Routine Description: Remove the NWCWorkstation string from a null null string. Arguments: OtherDeps: the string list we will munge. Return Status: None. --*/ { LPWSTR pszTmp0, pszTmp1 ; // // find the NWCWorkstation string // pszTmp0 = FindStringInNullNull(OtherDeps, NW_WORKSTATION_SERVICE) ; if (!pszTmp0) return ; pszTmp1 = pszTmp0 + wcslen(pszTmp0) + 1 ; // skip past it // // shift the rest up // memmove(pszTmp0, pszTmp1, CalcNullNullSize(pszTmp1)*sizeof(WCHAR)) ; } DWORD RemoveNwcDependency( VOID ) { SC_HANDLE ScManager = NULL; SC_HANDLE Service = NULL; LPQUERY_SERVICE_CONFIGW lpServiceConfig = NULL; DWORD err = NO_ERROR, dwBufferSize = 4096, dwBytesNeeded = 0; LPWSTR Deps = NULL ; lpServiceConfig = (LPQUERY_SERVICE_CONFIGW) LocalAlloc(LPTR, dwBufferSize) ; if (lpServiceConfig == NULL) { err = GetLastError(); goto ExitPoint ; } ScManager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ); if (ScManager == NULL) { err = GetLastError(); goto ExitPoint ; } Service = OpenServiceW( ScManager, LANMAN_SERVER, (SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG) ); if (Service == NULL) { err = GetLastError(); goto ExitPoint ; } if (!QueryServiceConfigW( Service, lpServiceConfig, // address of service config. structure dwBufferSize, // size of service configuration buffer &dwBytesNeeded // address of variable for bytes needed )) { err = GetLastError(); if (err == ERROR_INSUFFICIENT_BUFFER) { err = NO_ERROR ; dwBufferSize = dwBytesNeeded ; lpServiceConfig = (LPQUERY_SERVICE_CONFIGW) LocalAlloc(LPTR, dwBufferSize) ; if (lpServiceConfig == NULL) { err = GetLastError(); goto ExitPoint ; } if (!QueryServiceConfigW( Service, lpServiceConfig, // address of service config. structure dwBufferSize, // size of service configuration buffer &dwBytesNeeded // address of variable for bytes needed )) { err = GetLastError(); } } if (err != NO_ERROR) { goto ExitPoint ; } } Deps = lpServiceConfig->lpDependencies ; RemoveNWCFromNullNullList(Deps) ; if (!ChangeServiceConfigW( Service, SERVICE_NO_CHANGE, // service type (no change) SERVICE_NO_CHANGE, // start type (no change) SERVICE_NO_CHANGE, // error control (no change) NULL, // binary path name (NULL for no change) NULL, // load order group (NULL for no change) NULL, // tag id (NULL for no change) Deps, NULL, // service start name (NULL for no change) NULL, // password (NULL for no change) NULL // display name (NULL for no change) )) { err = GetLastError(); goto ExitPoint ; } ExitPoint: if (ScManager) { CloseServiceHandle(ScManager); } if (Service) { CloseServiceHandle(Service); } if (lpServiceConfig) { (void) LocalFree(lpServiceConfig) ; } return err ; }