1 /* 2 * PROJECT: ReactOS Local Port Monitor 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Implementation of Xcv* and support functions 5 * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 static DWORD 11 _HandleAddPort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) 12 { 13 DWORD res, cbPortName; 14 HKEY hroot; 15 HKEY hToken = NULL; 16 PLOCALMON_PORT pPort; 17 PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon; 18 PWSTR PortName = (PWSTR)pInputData; 19 20 FIXME("LcmXcvAddPort : %s\n", debugstr_w( (LPWSTR) PortName ) ); 21 22 if (!pLocalmon ) 23 { 24 res = ERROR_INVALID_PARAMETER; 25 goto Cleanup; 26 } 27 28 // This action can only happen at SERVER_ACCESS_ADMINISTER access level. 29 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) 30 { 31 res = ERROR_ACCESS_DENIED; 32 goto Cleanup; 33 } 34 35 // Switch to the SYSTEM context for modifying the registry. 36 hToken = RevertToPrinterSelf(); 37 if (!hToken) 38 { 39 res = GetLastError(); 40 ERR("RevertToPrinterSelf failed with error %lu!\n", res); 41 goto Cleanup; 42 } 43 44 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot); 45 if (res == ERROR_SUCCESS) 46 { 47 if ( DoesPortExist( PortName ) ) 48 { 49 RegCloseKey(hroot); 50 FIXME("=> %u\n", ERROR_ALREADY_EXISTS); 51 res = ERROR_ALREADY_EXISTS; 52 goto Cleanup; 53 } 54 55 cbPortName = (wcslen( PortName ) + 1) * sizeof(WCHAR); 56 57 // Create a new LOCALMON_PORT structure for it. 58 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + cbPortName); 59 if (!pPort) 60 { 61 res = ERROR_NOT_ENOUGH_MEMORY; 62 RegCloseKey( hroot ); 63 goto Cleanup; 64 } 65 memset( pPort, 0, sizeof(LOCALMON_PORT) + cbPortName ); 66 67 pPort->hFile = INVALID_HANDLE_VALUE; 68 pPort->pLocalmon = pLocalmon; 69 pPort->pwszPortName = wcscpy( (PWSTR)(pPort+1), PortName ); 70 71 // Insert it into the Registry list. 72 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry); 73 74 res = RegSetValueExW(hroot, PortName, 0, REG_SZ, (const BYTE *) L"", sizeof(L"")); 75 RegCloseKey(hroot); 76 } 77 78 FIXME("=> %u\n", res); 79 80 Cleanup: 81 if (hToken) ImpersonatePrinterClient(hToken); 82 SetLastError(res); 83 return res; 84 } 85 86 /** 87 * @name _HandleConfigureLPTPortCommandOK 88 * 89 * Writes the value for "TransmissionRetryTimeout" to the registry. Checks for granted SERVER_ACCESS_ADMINISTER access. 90 * Actually the opposite of _HandleGetTransmissionRetryTimeout, but name kept for compatibility. 91 * 92 * @param pXcv 93 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port. 94 * 95 * @param pInputData 96 * Pointer to a Unicode string containing the value to be written to the registry. 97 * 98 * @param pcbOutputNeeded 99 * Pointer to a DWORD that will be zeroed on return. 100 * 101 * @return 102 * An error code indicating success or failure. 103 */ 104 static DWORD 105 _HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) 106 { 107 DWORD cbBuffer; 108 DWORD dwErrorCode; 109 HKEY hKey = NULL; 110 HKEY hToken = NULL; 111 112 // Sanity checks 113 if (!pXcv || !pInputData || !pcbOutputNeeded) 114 { 115 dwErrorCode = ERROR_INVALID_PARAMETER; 116 goto Cleanup; 117 } 118 119 *pcbOutputNeeded = 0; 120 121 // This action can only happen at SERVER_ACCESS_ADMINISTER access level. 122 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) 123 { 124 dwErrorCode = ERROR_ACCESS_DENIED; 125 goto Cleanup; 126 } 127 128 // Switch to the SYSTEM context for modifying the registry. 129 hToken = RevertToPrinterSelf(); 130 if (!hToken) 131 { 132 dwErrorCode = GetLastError(); 133 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode); 134 goto Cleanup; 135 } 136 137 // Open the key where our value is stored. 138 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_SET_VALUE, &hKey); 139 if (dwErrorCode != ERROR_SUCCESS) 140 { 141 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); 142 goto Cleanup; 143 } 144 145 // We don't use cbInputData here, because the buffer pInputData could be bigger than the data it contains. 146 cbBuffer = (wcslen((PWSTR)pInputData) + 1) * sizeof(WCHAR); 147 148 // Write the value to the registry. 149 dwErrorCode = (DWORD)RegSetValueExW(hKey, L"TransmissionRetryTimeout", 0, REG_SZ, pInputData, cbBuffer); 150 if (dwErrorCode != ERROR_SUCCESS) 151 { 152 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode); 153 goto Cleanup; 154 } 155 156 Cleanup: 157 if (hKey) 158 RegCloseKey(hKey); 159 160 if (hToken) 161 ImpersonatePrinterClient(hToken); 162 163 return dwErrorCode; 164 } 165 166 static DWORD 167 _HandleDeletePort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) 168 { 169 DWORD res; 170 HKEY hroot; 171 HKEY hToken = NULL; 172 PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon; 173 PLOCALMON_PORT pPort = NULL; 174 PLIST_ENTRY pEntry; 175 PWSTR pPortName = (PWSTR)pInputData; 176 177 FIXME("LcmXcvDeletePort : %s\n", debugstr_w( pPortName ) ); 178 179 if (!pLocalmon ) 180 { 181 res = ERROR_INVALID_PARAMETER; 182 goto Cleanup; 183 } 184 185 // This action can only happen at SERVER_ACCESS_ADMINISTER access level. 186 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) 187 { 188 res = ERROR_ACCESS_DENIED; 189 goto Cleanup; 190 } 191 192 // Switch to the SYSTEM context for modifying the registry. 193 hToken = RevertToPrinterSelf(); 194 if (!hToken) 195 { 196 res = GetLastError(); 197 ERR("RevertToPrinterSelf failed with error %lu!\n", res); 198 goto Cleanup; 199 } 200 201 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot); 202 if ( res == ERROR_SUCCESS ) 203 { 204 res = RegDeleteValueW(hroot, pPortName ); 205 206 RegCloseKey(hroot); 207 208 if ( res == ERROR_SUCCESS ) 209 { 210 EnterCriticalSection(&pLocalmon->Section); 211 212 if (!IsListEmpty(&pLocalmon->RegistryPorts) ) 213 { 214 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink) 215 { 216 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry); 217 218 if (wcscmp(pPort->pwszPortName, pPortName) == 0) 219 break; 220 } 221 } 222 223 LeaveCriticalSection(&pLocalmon->Section); 224 225 if ( pPort ) 226 { 227 FIXME("LcmXcvDeletePort removed Port Entry\n"); 228 EnterCriticalSection(&pPort->pLocalmon->Section); 229 RemoveEntryList(&pPort->Entry); 230 LeaveCriticalSection(&pPort->pLocalmon->Section); 231 232 DllFreeSplMem(pPort); 233 } 234 } 235 FIXME("LcmXcvDeletePort => %u with %u\n", res, GetLastError() ); 236 } 237 238 Cleanup: 239 if (hToken) ImpersonatePrinterClient(hToken); 240 SetLastError(res); 241 return res; 242 } 243 244 /** 245 * @name _HandleGetDefaultCommConfig 246 * 247 * Gets the default configuration of a legacy port. 248 * The opposite function is _HandleSetDefaultCommConfig. 249 * 250 * @param pInputData 251 * The port name (without colon!) whose default configuration you want to get. 252 * 253 * @param pOutputData 254 * Pointer to a COMMCONFIG structure that will receive the configuration information. 255 * 256 * @param cbOutputData 257 * Size of the variable pointed to by pOutputData. 258 * 259 * @param pcbOutputNeeded 260 * Pointer to a DWORD that contains the required size for pOutputData on return. 261 * 262 * @return 263 * An error code indicating success or failure. 264 */ 265 static DWORD 266 _HandleGetDefaultCommConfig(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) 267 { 268 // Sanity checks 269 if (!pInputData || !pcbOutputNeeded) 270 return ERROR_INVALID_PARAMETER; 271 272 *pcbOutputNeeded = sizeof(COMMCONFIG); 273 274 // Check if the supplied buffer is large enough. 275 if (cbOutputData < *pcbOutputNeeded) 276 return ERROR_INSUFFICIENT_BUFFER; 277 278 // Finally get the port configuration. 279 if (!GetDefaultCommConfigW((PCWSTR)pInputData, (LPCOMMCONFIG)pOutputData, pcbOutputNeeded)) 280 return GetLastError(); 281 282 return ERROR_SUCCESS; 283 } 284 285 /** 286 * @name _HandleGetTransmissionRetryTimeout 287 * 288 * Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD. 289 * The opposite function is _HandleConfigureLPTPortCommandOK. 290 * 291 * @param pOutputData 292 * Pointer to a DWORD that will receive the timeout value. 293 * 294 * @param cbOutputData 295 * Size of the variable pointed to by pOutputData. 296 * 297 * @param pcbOutputNeeded 298 * Pointer to a DWORD that contains the required size for pOutputData on return. 299 * 300 * @return 301 * An error code indicating success or failure. 302 */ 303 static DWORD 304 _HandleGetTransmissionRetryTimeout(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) 305 { 306 DWORD dwTimeout; 307 308 // Sanity checks 309 if (!pOutputData || !pcbOutputNeeded) 310 return ERROR_INVALID_PARAMETER; 311 312 *pcbOutputNeeded = sizeof(DWORD); 313 314 // Check if the supplied buffer is large enough. 315 if (cbOutputData < *pcbOutputNeeded) 316 return ERROR_INSUFFICIENT_BUFFER; 317 318 // Retrieve and copy the number. 319 dwTimeout = GetLPTTransmissionRetryTimeout(); 320 CopyMemory(pOutputData, &dwTimeout, sizeof(DWORD)); 321 return ERROR_SUCCESS; 322 } 323 324 /** 325 * @name _HandleMonitorUI 326 * 327 * Returns the filename of the associated UI DLL for this Port Monitor. 328 * 329 * @param pOutputData 330 * Pointer to a Unicode string that will receive the DLL filename. 331 * 332 * @param cbOutputData 333 * Size of the variable pointed to by pOutputData. 334 * 335 * @param pcbOutputNeeded 336 * Pointer to a DWORD that contains the required size for pOutputData on return. 337 * 338 * @return 339 * An error code indicating success or failure. 340 */ 341 static DWORD 342 _HandleMonitorUI(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) 343 { 344 const WCHAR wszMonitorUI[] = L"LocalUI.dll"; 345 346 // Sanity checks 347 if (!pcbOutputNeeded) 348 return ERROR_INVALID_PARAMETER; 349 350 *pcbOutputNeeded = sizeof(wszMonitorUI); 351 352 // Check if the supplied buffer is large enough. 353 if (cbOutputData < *pcbOutputNeeded) 354 return ERROR_INSUFFICIENT_BUFFER; 355 356 if (!pOutputData) 357 return ERROR_INVALID_PARAMETER; 358 359 // Copy the string. 360 CopyMemory(pOutputData, wszMonitorUI, sizeof(wszMonitorUI)); 361 return ERROR_SUCCESS; 362 } 363 364 /** 365 * @name _HandlePortExists 366 * 367 * Checks all Port Monitors installed on the local system to find out if a given port already exists. 368 * 369 * @param pInputData 370 * Pointer to a Unicode string specifying the port name to check. 371 * 372 * @param pOutputData 373 * Pointer to a BOOL that receives the result of the check. 374 * 375 * @param cbOutputData 376 * Size of the variable pointed to by pOutputData. 377 * 378 * @param pcbOutputNeeded 379 * Pointer to a DWORD that contains the required size for pOutputData on return. 380 * 381 * @return 382 * An error code indicating success or failure. 383 */ 384 static DWORD 385 _HandlePortExists(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) 386 { 387 // Sanity checks 388 if (!pInputData || !pOutputData || !pcbOutputNeeded) 389 return ERROR_INVALID_PARAMETER; 390 391 *pcbOutputNeeded = sizeof(BOOL); 392 393 // Check if the supplied buffer is large enough. 394 if (cbOutputData < *pcbOutputNeeded) 395 return ERROR_INSUFFICIENT_BUFFER; 396 397 // Return the check result and error code. 398 *(PBOOL)pOutputData = DoesPortExist((PCWSTR)pInputData); 399 return GetLastError(); 400 } 401 402 static DWORD 403 _HandlePortIsValid(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) 404 { 405 DWORD res; 406 407 TRACE("HandlePortIsValid : pInputData %s\n", debugstr_w( (LPWSTR) pInputData)); 408 409 res = GetTypeFromName((LPCWSTR) pInputData); 410 411 TRACE("HandlePortIsValid : detected as %u\n", res); 412 413 /* names, that we have recognized, are valid */ 414 if (res) return ERROR_SUCCESS; 415 416 TRACE("=> %u\n", GetLastError()); 417 418 /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */ 419 return GetLastError(); 420 } 421 422 /** 423 * @name _HandleSetDefaultCommConfig 424 * 425 * Sets the default configuration of a legacy port. Checks for granted SERVER_ACCESS_ADMINISTER access. 426 * You have to supply the port name (with colon!) in XcvOpenPort. 427 * The opposite function is _HandleGetDefaultCommConfig. 428 * 429 * @param pXcv 430 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port. 431 * 432 * @param pInputData 433 * Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW. 434 * 435 * @param pcbOutputNeeded 436 * Pointer to a DWORD that will be zeroed on return. 437 * 438 * @return 439 * An error code indicating success or failure. 440 */ 441 static DWORD 442 _HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded) 443 { 444 DWORD dwErrorCode; 445 HANDLE hToken = NULL; 446 LPCOMMCONFIG pCommConfig; 447 PWSTR pwszPortNameWithoutColon = NULL; 448 449 // Sanity checks 450 // pwszObject needs to be at least 2 characters long to be a port name with a trailing colon. 451 if (!pXcv || !pXcv->pwszObject || !pXcv->pwszObject[0] || !pXcv->pwszObject[1] || !pInputData || !pcbOutputNeeded) 452 { 453 dwErrorCode = ERROR_INVALID_PARAMETER; 454 goto Cleanup; 455 } 456 457 *pcbOutputNeeded = 0; 458 459 // This action can only happen at SERVER_ACCESS_ADMINISTER access level. 460 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER)) 461 { 462 dwErrorCode = ERROR_ACCESS_DENIED; 463 goto Cleanup; 464 } 465 466 // SetDefaultCommConfigW needs the port name without colon. 467 dwErrorCode = GetPortNameWithoutColon(pXcv->pwszObject, &pwszPortNameWithoutColon); 468 if (dwErrorCode != ERROR_SUCCESS) 469 goto Cleanup; 470 471 // Switch to the SYSTEM context for setting the port configuration. 472 hToken = RevertToPrinterSelf(); 473 if (!hToken) 474 { 475 dwErrorCode = GetLastError(); 476 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode); 477 goto Cleanup; 478 } 479 480 // Finally pass the parameters to SetDefaultCommConfigW. 481 pCommConfig = (LPCOMMCONFIG)pInputData; 482 if (!SetDefaultCommConfigW(pwszPortNameWithoutColon, pCommConfig, pCommConfig->dwSize)) 483 { 484 dwErrorCode = GetLastError(); 485 ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode); 486 goto Cleanup; 487 } 488 489 dwErrorCode = ERROR_SUCCESS; 490 491 Cleanup: 492 if (hToken) 493 ImpersonatePrinterClient(hToken); 494 495 if (pwszPortNameWithoutColon) 496 DllFreeSplMem(pwszPortNameWithoutColon); 497 498 return dwErrorCode; 499 } 500 501 BOOL WINAPI 502 LocalmonXcvClosePort(HANDLE hXcv) 503 { 504 PLOCALMON_XCV pXcv = (PLOCALMON_XCV)hXcv; 505 506 TRACE("LocalmonXcvClosePort(%p)\n", hXcv); 507 508 // Sanity checks 509 if (!pXcv) 510 { 511 SetLastError(ERROR_INVALID_PARAMETER); 512 return FALSE; 513 } 514 515 // Remove it from the list and free the memory. 516 EnterCriticalSection(&pXcv->pLocalmon->Section); 517 RemoveEntryList(&pXcv->Entry); 518 LeaveCriticalSection(&pXcv->pLocalmon->Section); 519 DllFreeSplMem(pXcv); 520 521 SetLastError(ERROR_SUCCESS); 522 return TRUE; 523 } 524 525 DWORD WINAPI 526 LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded) 527 { 528 FIXME("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded); 529 530 // Sanity checks 531 if (!pszDataName) 532 return ERROR_INVALID_PARAMETER; 533 534 // Call the appropriate handler for the requested data name. 535 if (wcscmp(pszDataName, L"AddPort") == 0) 536 return _HandleAddPort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); 537 538 if (wcscmp(pszDataName, L"ConfigureLPTPortCommandOK") == 0) 539 return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); 540 541 if (wcscmp(pszDataName, L"DeletePort") == 0) 542 return _HandleDeletePort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); 543 544 if (wcscmp(pszDataName, L"GetDefaultCommConfig") == 0) 545 return _HandleGetDefaultCommConfig(pInputData, pOutputData, cbOutputData, pcbOutputNeeded); 546 547 if (wcscmp(pszDataName, L"GetTransmissionRetryTimeout") == 0) 548 return _HandleGetTransmissionRetryTimeout(pOutputData, cbOutputData, pcbOutputNeeded); 549 550 if (wcscmp(pszDataName, L"MonitorUI") == 0) 551 return _HandleMonitorUI(pOutputData, cbOutputData, pcbOutputNeeded); 552 553 if (wcscmp(pszDataName, L"PortExists") == 0) 554 return _HandlePortExists(pInputData, pOutputData, cbOutputData, pcbOutputNeeded); 555 556 if (wcscmp(pszDataName, L"PortIsValid") == 0) 557 return _HandlePortIsValid(pInputData, pOutputData, cbOutputData, pcbOutputNeeded); 558 559 if (wcscmp(pszDataName, L"SetDefaultCommConfig") == 0) 560 return _HandleSetDefaultCommConfig((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded); 561 562 return ERROR_INVALID_PARAMETER; 563 } 564 565 BOOL WINAPI 566 LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pwszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv) 567 { 568 DWORD cbObject = 0; 569 DWORD dwErrorCode; 570 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor; 571 PLOCALMON_XCV pXcv; 572 573 FIXME("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor, pwszObject, GrantedAccess, phXcv); 574 575 // Sanity checks 576 if (!pLocalmon || !phXcv) 577 { 578 dwErrorCode = ERROR_INVALID_PARAMETER; 579 goto Cleanup; 580 } 581 582 if (pwszObject) 583 cbObject = (wcslen(pwszObject) + 1) * sizeof(WCHAR); 584 585 // Create a new LOCALMON_XCV structure and fill the relevant fields. 586 pXcv = DllAllocSplMem(sizeof(LOCALMON_XCV) + cbObject); 587 if (!pXcv) 588 { 589 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 590 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); 591 goto Cleanup; 592 } 593 594 pXcv->pLocalmon = pLocalmon; 595 pXcv->GrantedAccess = GrantedAccess; 596 597 if (cbObject) 598 { 599 pXcv->pwszObject = (PWSTR)((PBYTE)pXcv + sizeof(LOCALMON_XCV)); 600 CopyMemory(pXcv->pwszObject, pwszObject, cbObject); 601 } 602 603 InsertTailList(&pLocalmon->XcvHandles, &pXcv->Entry); 604 605 // Return it as the Xcv handle. 606 *phXcv = (HANDLE)pXcv; 607 dwErrorCode = ERROR_SUCCESS; 608 609 Cleanup: 610 SetLastError(dwErrorCode); 611 return (dwErrorCode == ERROR_SUCCESS); 612 } 613