Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1567 lines
41 KiB

  1. /*++
  2. Copyright (c) 1991-92 Microsoft Corporation
  3. Module Name:
  4. useaddel.c
  5. Abstract:
  6. This module contains the worker routines for the NetUseAdd and
  7. NetUseDel APIs implemented in the Workstation service.
  8. Author:
  9. Rita Wong (ritaw) 4-Mar-1991
  10. Revision History:
  11. --*/
  12. #include "wsutil.h"
  13. #include "wsdevice.h"
  14. #include "wsuse.h"
  15. //-------------------------------------------------------------------//
  16. // //
  17. // Local function prototypes //
  18. // //
  19. //-------------------------------------------------------------------//
  20. STATIC
  21. NET_API_STATUS
  22. WsAddUse(
  23. IN PLUID LogonId,
  24. IN HANDLE TreeConnection,
  25. IN LPTSTR Local OPTIONAL,
  26. IN DWORD LocalLength,
  27. IN LPTSTR UncName,
  28. IN DWORD UncNameLength,
  29. IN PUNICODE_STRING TreeConnectStr,
  30. IN DWORD Flags
  31. );
  32. STATIC
  33. NET_API_STATUS
  34. WsDeleteUse(
  35. IN PLUID LogonId,
  36. IN DWORD ForceLevel,
  37. IN PUSE_ENTRY MatchedPointer,
  38. IN DWORD Index
  39. );
  40. STATIC
  41. NET_API_STATUS
  42. WsCreateNewEntry(
  43. OUT PUSE_ENTRY *NewUse,
  44. IN HANDLE TreeConnection,
  45. IN LPTSTR Local OPTIONAL,
  46. IN DWORD LocalLength,
  47. IN LPTSTR UncName OPTIONAL,
  48. IN DWORD UncNameLength,
  49. IN PUNICODE_STRING TreeConnectStr,
  50. IN DWORD Flags
  51. );
  52. STATIC
  53. NET_API_STATUS
  54. WsCheckLocalAndDeviceType(
  55. IN LPTSTR Local,
  56. IN DWORD DeviceType,
  57. OUT LPDWORD ErrorParameter OPTIONAL
  58. );
  59. STATIC
  60. NET_API_STATUS
  61. WsCheckEstablishedDeviceType(
  62. IN HANDLE TreeConnection,
  63. IN DWORD RequestedDeviceType
  64. );
  65. STATIC
  66. NET_API_STATUS
  67. WsAllocateUseWorkBuffer(
  68. IN PUSE_INFO_2 UseInfo,
  69. IN DWORD Level,
  70. OUT LPTSTR *UncName,
  71. OUT LPTSTR *Local,
  72. OUT LPTSTR *UserName,
  73. OUT LPTSTR *DomainName
  74. );
  75. #if DBG
  76. STATIC
  77. VOID
  78. DumpUseList(
  79. DWORD Index
  80. );
  81. #endif
  82. //-------------------------------------------------------------------//
  83. // //
  84. // Global variables //
  85. // //
  86. //-------------------------------------------------------------------//
  87. //
  88. // Monotonically incrementing integer. A unique value is assigned to
  89. // each new use entry created so that we can provide an enumeration
  90. // resume handle.
  91. //
  92. STATIC DWORD GlobalResumeKey = 0;
  93. //-------------------------------------------------------------------//
  94. // //
  95. // Macros //
  96. // //
  97. //-------------------------------------------------------------------//
  98. #define GET_USE_INFO_POINTER(UseInfo, InfoStruct) \
  99. UseInfo = InfoStruct->UseInfo3;
  100. NET_API_STATUS NET_API_FUNCTION
  101. NetrUseAdd(
  102. IN LPTSTR ServerName OPTIONAL,
  103. IN DWORD Level,
  104. IN LPUSE_INFO InfoStruct,
  105. OUT LPDWORD ErrorParameter OPTIONAL
  106. )
  107. /*++
  108. Routine Description:
  109. This function is the NetUseAdd entry point in the Workstation service.
  110. Arguments:
  111. Level - Supplies the level of information specified in Buffer.
  112. Buffer - Supplies the parameters to create the new tree connection with.
  113. ErrorParameter - Returns the identifier to the invalid parameter in Buffer
  114. if this function returns ERROR_INVALID_PARAMETER.
  115. Return Value:
  116. NET_API_STATUS - NERR_Success or reason for failure.
  117. --*/
  118. {
  119. LUID LogonId;
  120. NET_API_STATUS status;
  121. LPTSTR UncName = NULL;
  122. DWORD UncNameLength = 0;
  123. PTSTR Local = NULL;
  124. DWORD LocalLength = 0;
  125. DWORD Flags;
  126. LPTSTR UserName = NULL;
  127. LPTSTR DomainName = NULL;
  128. LPTSTR Password = NULL;
  129. UNICODE_STRING EncodedPassword;
  130. ULONG CreateFlags;
  131. HANDLE TreeConnection;
  132. UNICODE_STRING TreeConnectStr;
  133. PUSE_INFO_3 pUseInfo;
  134. PUSE_INFO_2 UseInfo;
  135. DWORD SessionId;
  136. LPWSTR Session = NULL;
  137. UNREFERENCED_PARAMETER(ServerName);
  138. if (Level == 0) {
  139. return ERROR_INVALID_LEVEL;
  140. }
  141. #define NETR_USE_ADD_PASSWORD_SEED 0x56 // Pick a non-zero seed
  142. RtlInitUnicodeString( &EncodedPassword, NULL );
  143. GET_USE_INFO_POINTER(pUseInfo, InfoStruct);
  144. if (pUseInfo == NULL) {
  145. RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
  146. }
  147. //
  148. // Cast a pointer to USE_INFO_2 to make things easy ...
  149. //
  150. UseInfo = &pUseInfo->ui3_ui2;
  151. if(Level == 3)
  152. {
  153. CreateFlags = pUseInfo->ui3_flags;
  154. }
  155. else
  156. {
  157. CreateFlags = 0;
  158. }
  159. //
  160. // UNC name can never be NULL or empty string.
  161. //
  162. if ((UseInfo->ui2_remote == NULL) ||
  163. (UseInfo->ui2_remote[0] == TCHAR_EOS)) {
  164. RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
  165. }
  166. //
  167. // Allocate one large buffer for storing the UNC name, local device name,
  168. // username, and domain name.
  169. //
  170. if ((status = WsAllocateUseWorkBuffer(
  171. UseInfo,
  172. Level,
  173. &UncName, // Free using this pointer
  174. &Local,
  175. &UserName,
  176. &DomainName
  177. )) != NERR_Success) {
  178. return status;
  179. }
  180. //
  181. // If local device is a NULL string, it will be treated as a pointer to
  182. // NULL.
  183. //
  184. if ((UseInfo->ui2_local != NULL) &&
  185. (UseInfo->ui2_local[0] != TCHAR_EOS)) {
  186. //
  187. // Local device name is not NULL, canonicalize it
  188. //
  189. if (WsUseCheckLocal(
  190. UseInfo->ui2_local,
  191. Local,
  192. &LocalLength
  193. ) != NERR_Success) {
  194. (void) LocalFree(UncName);
  195. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  196. }
  197. }
  198. //
  199. // Check the format of the shared resource name.
  200. //
  201. if (WsUseCheckRemote(
  202. UseInfo->ui2_remote,
  203. UncName,
  204. &UncNameLength
  205. ) != NERR_Success) {
  206. (void) LocalFree(UncName);
  207. RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
  208. }
  209. if ((Level >= 2) &&
  210. (UseInfo->ui2_password != NULL) &&
  211. (UseInfo->ui2_password[0] == TCHAR_EOS) &&
  212. (UseInfo->ui2_username != NULL) &&
  213. (UseInfo->ui2_username[0] == TCHAR_EOS) &&
  214. (UseInfo->ui2_domainname != NULL) &&
  215. (UseInfo->ui2_domainname[0] == TCHAR_EOS)) {
  216. //
  217. // The user explicitly specified an empty password, username, and
  218. // domain. This means they want a null session.
  219. //
  220. *UserName = TCHAR_EOS;
  221. *DomainName = TCHAR_EOS;
  222. Password = TEXT("");
  223. } else {
  224. //
  225. // Canonicalize user and domain names.
  226. //
  227. if (UserName != NULL) {
  228. //
  229. // Canonicalize username
  230. //
  231. if ((status = I_NetNameCanonicalize(
  232. NULL,
  233. UseInfo->ui2_username,
  234. UserName,
  235. (UNLEN + 1) * sizeof(TCHAR),
  236. NAMETYPE_USER,
  237. 0
  238. )) != NERR_Success) {
  239. (void) LocalFree(UncName);
  240. RETURN_INVALID_PARAMETER(ErrorParameter, USE_USERNAME_PARMNUM);
  241. }
  242. }
  243. if ( (DomainName != NULL)
  244. && (UseInfo->ui2_domainname[0] != TCHAR_EOS) ) {
  245. // Must now allow null string for domain name to support UPNs
  246. // which contain the domain name in the username.
  247. //
  248. // Canonicalize domain name
  249. // Canonicalize as a computername since a computername can be
  250. // a valid domain (on the workstation to which you are connecting.
  251. // This allows computernames with spaces to work.
  252. //
  253. if ((status = I_NetNameCanonicalize(
  254. NULL,
  255. UseInfo->ui2_domainname,
  256. DomainName,
  257. (DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR),
  258. NAMETYPE_COMPUTER,
  259. 0
  260. )) != NERR_Success) {
  261. (void) LocalFree(UncName);
  262. RETURN_INVALID_PARAMETER(ErrorParameter, USE_DOMAINNAME_PARMNUM);
  263. }
  264. }
  265. //
  266. // Make sure password length is not too long
  267. //
  268. if (UseInfo->ui2_password != NULL) {
  269. Password = UseInfo->ui2_password;
  270. if (STRLEN(Password) > PWLEN) {
  271. (void) LocalFree(UncName);
  272. RETURN_INVALID_PARAMETER(ErrorParameter, USE_PASSWORD_PARMNUM);
  273. }
  274. //
  275. // Decode the password (the client obfuscated it.)
  276. //
  277. RtlInitUnicodeString( &EncodedPassword, Password );
  278. RtlRunDecodeUnicodeString( NETR_USE_ADD_PASSWORD_SEED,
  279. &EncodedPassword );
  280. }
  281. else {
  282. Flags |= USE_DEFAULT_CREDENTIALS;
  283. }
  284. }
  285. IF_DEBUG(USE) {
  286. NetpKdPrint(("[Wksta] NetrUseAdd %ws %ws\n", Local, UncName));
  287. }
  288. //
  289. // Check to see if the format of the local device name is correct based
  290. // on the shared resource type to be accessed. This function also checks
  291. // to see if the device is shared.
  292. //
  293. if ((status = WsCheckLocalAndDeviceType(
  294. Local,
  295. UseInfo->ui2_asg_type,
  296. ErrorParameter
  297. )) != NERR_Success) {
  298. IF_DEBUG(USE) {
  299. NetpKdPrint(("[Wksta] WsCheckLocalAndDeviceType return %lu\n", status));
  300. }
  301. goto FreeWorkBuffer;
  302. }
  303. //
  304. // Impersonate caller and get the logon id
  305. //
  306. if ((status = WsImpersonateAndGetSessionId(&SessionId)) != NERR_Success) {
  307. goto FreeWorkBuffer;
  308. }
  309. //
  310. // Replace \\ with \Device\LanmanRedirector in UncName, and create
  311. // the NT-style tree connection names (without password or user name)
  312. //
  313. if ((status = WsCreateTreeConnectName(
  314. UncName,
  315. UncNameLength,
  316. Local,
  317. SessionId,
  318. &TreeConnectStr
  319. )) != NERR_Success) {
  320. IF_DEBUG(USE) {
  321. NetpKdPrint(("[Wksta] NetrUseAdd Bad tree connect name: %lu\n",
  322. status));
  323. }
  324. goto FreeWorkBuffer;
  325. }
  326. //
  327. // Impersonate caller and get the logon id
  328. //
  329. if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
  330. goto FreeAllocatedBuffers;
  331. }
  332. //
  333. // Don't redirect comm or spooled devices if redirection is paused.
  334. //
  335. if( Local != NULL && WsRedirectionPaused(Local) ) {
  336. IF_DEBUG(USE) {
  337. NetpKdPrint(("[Wksta] NetrUseAdd Redirector paused\n"));
  338. }
  339. status = ERROR_REDIR_PAUSED;
  340. goto FreeAllocatedBuffers;
  341. }
  342. if (Local != NULL) {
  343. PUSE_ENTRY UseList;
  344. DWORD Index;
  345. //
  346. // Lock Use Table so nobody will do anything destructive to it while
  347. // we're in the middle of all this. If multiple threads are trying
  348. // to redirect the same drive, only one will succeed creating the
  349. // symbolic link, and the others will fail.
  350. //
  351. if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
  352. status = NERR_InternalError;
  353. goto FreeAllocatedBuffers;
  354. }
  355. //
  356. // Look for the matching LogonId in the Use Table, if none matched
  357. // create a new entry.
  358. //
  359. if (WsGetUserEntry(
  360. &Use,
  361. &LogonId,
  362. &Index,
  363. FALSE
  364. ) != NERR_Success) {
  365. UseList = NULL;
  366. }
  367. else {
  368. UseList = Use.Table[Index].List;
  369. }
  370. //
  371. // Create symbolic link for local device name. If there are multiple
  372. // threads trying to do this, only one will succeed.
  373. //
  374. if ((status = WsCreateSymbolicLink(
  375. Local,
  376. UseInfo->ui2_asg_type,
  377. TreeConnectStr.Buffer,
  378. UseList,
  379. &Session
  380. )) != NERR_Success) {
  381. if ((ARGUMENT_PRESENT(ErrorParameter)) &&
  382. (status == ERROR_INVALID_PARAMETER)) {
  383. *ErrorParameter = USE_LOCAL_PARMNUM;
  384. }
  385. }
  386. RtlReleaseResource(&Use.TableResource);
  387. if( status )
  388. goto FreeAllocatedBuffers;
  389. }
  390. //
  391. // Create the tree connection if none already exists; otherwise, open it.
  392. //
  393. status = WsOpenCreateConnection(
  394. &TreeConnectStr,
  395. UserName,
  396. DomainName,
  397. Password,
  398. CreateFlags,
  399. FILE_OPEN_IF,
  400. UseInfo->ui2_asg_type,
  401. &TreeConnection,
  402. NULL
  403. );
  404. if (status != NERR_Success) {
  405. if (status == NERR_UseNotFound) {
  406. status = ERROR_DEV_NOT_EXIST;
  407. }
  408. WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
  409. goto FreeAllocatedBuffers;
  410. }
  411. //
  412. // Make sure user was correct about the shared resource type
  413. //
  414. if ((status = WsCheckEstablishedDeviceType(
  415. TreeConnection,
  416. UseInfo->ui2_asg_type
  417. )) != NERR_Success) {
  418. WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE);
  419. WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
  420. goto FreeAllocatedBuffers;
  421. }
  422. //
  423. // Add use to the Use Table.
  424. //
  425. status = WsAddUse(
  426. &LogonId,
  427. TreeConnection,
  428. Local,
  429. LocalLength,
  430. UncName,
  431. UncNameLength,
  432. &TreeConnectStr,
  433. Flags
  434. );
  435. if( status ) {
  436. WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE);
  437. WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
  438. }
  439. FreeAllocatedBuffers:
  440. //
  441. // Free tree connection name buffer and work buffer
  442. //
  443. (void) LocalFree(TreeConnectStr.Buffer);
  444. FreeWorkBuffer:
  445. (void) LocalFree(UncName);
  446. (void) LocalFree(Session);
  447. //
  448. // Put the password back the way we found it.
  449. //
  450. if ( EncodedPassword.Length != 0 ) {
  451. UCHAR Seed = NETR_USE_ADD_PASSWORD_SEED;
  452. RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
  453. }
  454. IF_DEBUG(USE) {
  455. NetpKdPrint(("[Wksta] NetrUseAdd: about to return status=%lu\n",
  456. status));
  457. }
  458. return status;
  459. }
  460. NET_API_STATUS NET_API_FUNCTION
  461. NetrUseDel (
  462. IN LPTSTR ServerName OPTIONAL,
  463. IN LPTSTR UseName,
  464. IN DWORD ForceLevel
  465. )
  466. /*++
  467. Routine Description:
  468. This function is the NetUseDel entry point in the Workstation service.
  469. Arguments:
  470. UseName - Supplies the local device name or shared resource name of
  471. the tree connection to be deleted.
  472. ForceLevel - Supplies the level of force to delete the tree connection.
  473. Return Value:
  474. NET_API_STATUS - NERR_Success or reason for failure.
  475. --*/
  476. {
  477. NET_API_STATUS status;
  478. LUID LogonId; // Logon Id of user
  479. DWORD Index; // Index to user entry in Use Table
  480. PUSE_ENTRY MatchedPointer; // Points to found use entry
  481. PUSE_ENTRY BackPointer; // Points to node previous to
  482. // found use entry
  483. HANDLE TreeConnection; // Handle to connection
  484. TCHAR *FormattedUseName;
  485. // For canonicalizing a local device
  486. // name
  487. DWORD PathType = 0;
  488. PUSE_ENTRY UseList;
  489. UNREFERENCED_PARAMETER(ServerName);
  490. //
  491. // Check that ForceLevel parameter is valid
  492. //
  493. switch (ForceLevel) {
  494. case USE_NOFORCE:
  495. case USE_LOTS_OF_FORCE:
  496. case USE_FORCE:
  497. break;
  498. default:
  499. return ERROR_INVALID_PARAMETER;
  500. }
  501. FormattedUseName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(MAX_PATH+1)*sizeof(TCHAR));
  502. if (FormattedUseName == NULL) {
  503. return GetLastError();
  504. }
  505. //
  506. // Check to see if UseName is valid, and canonicalize it.
  507. //
  508. if (I_NetPathCanonicalize(
  509. NULL,
  510. UseName,
  511. FormattedUseName,
  512. (MAX_PATH+1)*sizeof(TCHAR),
  513. NULL,
  514. &PathType,
  515. 0
  516. ) != NERR_Success) {
  517. LocalFree(FormattedUseName);
  518. return NERR_UseNotFound;
  519. }
  520. IF_DEBUG(USE) {
  521. NetpKdPrint(("\n[Wksta] NetrUseDel %ws %u, formatted use name %ws\n",
  522. UseName, ForceLevel, FormattedUseName));
  523. }
  524. //
  525. // Impersonate caller and get the logon id
  526. //
  527. if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
  528. LocalFree(FormattedUseName);
  529. return status;
  530. }
  531. //
  532. // Lock Use Table while looking for entry to delete.
  533. //
  534. if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
  535. LocalFree(FormattedUseName);
  536. return NERR_InternalError;
  537. }
  538. //
  539. // See if the use entry is an explicit connection.
  540. //
  541. status = WsGetUserEntry(
  542. &Use,
  543. &LogonId,
  544. &Index,
  545. FALSE
  546. );
  547. UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
  548. NULL;
  549. if ((status = WsFindUse(
  550. &LogonId,
  551. UseList,
  552. FormattedUseName,
  553. &TreeConnection,
  554. &MatchedPointer,
  555. &BackPointer
  556. )) != NERR_Success) {
  557. RtlReleaseResource(&Use.TableResource);
  558. LocalFree(FormattedUseName);
  559. return status;
  560. }
  561. LocalFree(FormattedUseName);
  562. if (MatchedPointer == NULL) {
  563. //
  564. // UseName specified has an implicit connection. Don't need to hold
  565. // on to Use Table anymore.
  566. //
  567. RtlReleaseResource(&Use.TableResource);
  568. status = WsDeleteConnection(&LogonId, TreeConnection, ForceLevel);
  569. //
  570. // Close the connection handle if the API failed.
  571. //
  572. if (status != NERR_Success) {
  573. NtClose(TreeConnection);
  574. }
  575. return status;
  576. }
  577. else if ((MatchedPointer->Local != NULL) &&
  578. (MatchedPointer->LocalLength > 2)) {
  579. //
  580. // Don't allow delete on comm or spooled devices if redirection is
  581. // paused for the current user.
  582. //
  583. if (WsRedirectionPaused(MatchedPointer->Local)) {
  584. RtlReleaseResource(&Use.TableResource);
  585. return ERROR_REDIR_PAUSED;
  586. }
  587. }
  588. //
  589. // Delete tree connection and remove use entry from Use Table. This function
  590. // releases the TableResource
  591. //
  592. status = WsDeleteUse(
  593. &LogonId,
  594. ForceLevel,
  595. MatchedPointer,
  596. Index
  597. );
  598. IF_DEBUG(USE) {
  599. NetpKdPrint(("[Wksta] NetrUseDel: about to return status=%lu\n", status));
  600. }
  601. return status;
  602. }
  603. STATIC
  604. NET_API_STATUS
  605. WsAddUse(
  606. IN PLUID LogonId,
  607. IN HANDLE TreeConnection,
  608. IN LPTSTR Local OPTIONAL,
  609. IN DWORD LocalLength,
  610. IN LPTSTR UncName,
  611. IN DWORD UncNameLength,
  612. IN PUNICODE_STRING TreeConnectStr,
  613. IN DWORD Flags
  614. )
  615. /*++
  616. Routine Description:
  617. This function adds a use (tree connection) entry into the Use Table for
  618. the user specified by the Logon Id. There is a linked list of uses for
  619. each user. Each new use entry is inserted into the end of the linked
  620. list so that enumeration of the list is resumable.
  621. NOTE: This function locks the Use Table.
  622. It also closes the tree connection in case of a error, or if a tree
  623. connection to the same shared resource already exists.
  624. Arguments:
  625. LogonId - Supplies a pointer to the user's Logon Id.
  626. TreeConnection - Supplies the handle to the tree connection created.
  627. Local - Supplies the string of the local device name.
  628. LocalLength - Supplies the length of the local device name.
  629. UncName - Supplies the name of the shared resource (UNC name).
  630. UncNameLength - Supplies the length of the shared resource.
  631. TreeConnectStr - Supplies the string of UNC name in NT-style format
  632. (\Device\LanmanRedirector\X:\Orville\Razzle).
  633. Return Value:
  634. NET_API_STATUS - NERR_Success or reason for failure.
  635. --*/
  636. {
  637. NET_API_STATUS status;
  638. DWORD Index; // Index to user entry in Use Table
  639. PUSE_ENTRY MatchedPointer = NULL; // Points to matching shared resource
  640. PUSE_ENTRY InsertPointer = NULL; // Point of insertion into use list
  641. PUSE_ENTRY NewUse; // Pointer to the new use entry
  642. if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
  643. (void) NtClose(TreeConnection);
  644. return NERR_InternalError;
  645. }
  646. //
  647. // Look for the matching LogonId in the Use Table, if none matched
  648. // create a new entry.
  649. //
  650. if ((status = WsGetUserEntry(
  651. &Use,
  652. LogonId,
  653. &Index,
  654. TRUE
  655. )) != NERR_Success) {
  656. RtlReleaseResource(&Use.TableResource);
  657. (void) NtClose(TreeConnection);
  658. return status;
  659. }
  660. if (Use.Table[Index].List != NULL) {
  661. //
  662. // Traverse use list to look for location to insert new use entry.
  663. //
  664. WsFindInsertLocation(
  665. (PUSE_ENTRY) Use.Table[Index].List,
  666. UncName,
  667. &MatchedPointer,
  668. &InsertPointer
  669. );
  670. }
  671. if (MatchedPointer == NULL) {
  672. //
  673. // No matching UNC name found. Create a new entry with a
  674. // corresponding remote entry.
  675. //
  676. if ((status = WsCreateNewEntry(
  677. &NewUse,
  678. TreeConnection,
  679. Local,
  680. LocalLength,
  681. UncName,
  682. UncNameLength,
  683. TreeConnectStr,
  684. Flags
  685. )) != NERR_Success) {
  686. RtlReleaseResource(&Use.TableResource);
  687. (void) NtClose(TreeConnection);
  688. return status;
  689. }
  690. }
  691. else {
  692. //
  693. // Matching UNC name found.
  694. //
  695. //
  696. // It may be unnecessary to create a new use entry if the use
  697. // we are adding has a NULL local device and a NULL local device
  698. // entry already exists.
  699. //
  700. if (Local == NULL) {
  701. if (MatchedPointer->Local == NULL) {
  702. //
  703. // Yes, there is a NULL local device entry already.
  704. // Increment the use count and we are done.
  705. //
  706. MatchedPointer->UseCount++;
  707. MatchedPointer->Remote->TotalUseCount++;
  708. #if DBG
  709. DumpUseList(Index);
  710. #endif
  711. RtlReleaseResource(&Use.TableResource);
  712. //
  713. // Close newly opened handle to the same tree connection because
  714. // one already exists.
  715. //
  716. (void) NtClose(TreeConnection);
  717. return NERR_Success;
  718. }
  719. }
  720. //
  721. // If we get here means we need to create a new use entry but not
  722. // a corresponding remote entry because a use with the same UNC
  723. // name already exists.
  724. //
  725. if ((status = WsCreateNewEntry(
  726. &NewUse,
  727. TreeConnection,
  728. Local,
  729. LocalLength,
  730. NULL,
  731. 0,
  732. TreeConnectStr,
  733. Flags
  734. )) != NERR_Success) {
  735. RtlReleaseResource(&Use.TableResource);
  736. (void) NtClose(TreeConnection);
  737. return status;
  738. }
  739. NewUse->Remote = MatchedPointer->Remote;
  740. NewUse->Remote->TotalUseCount++;
  741. }
  742. //
  743. // Insert the new use entry into use list
  744. //
  745. if (InsertPointer == NULL) {
  746. //
  747. // Inserting into the head of list
  748. //
  749. Use.Table[Index].List = (PVOID) NewUse;
  750. }
  751. else {
  752. InsertPointer->Next = NewUse;
  753. }
  754. #if DBG
  755. DumpUseList(Index);
  756. #endif
  757. RtlReleaseResource(&Use.TableResource);
  758. return NERR_Success;
  759. }
  760. STATIC
  761. NET_API_STATUS
  762. WsDeleteUse(
  763. IN PLUID LogonId,
  764. IN DWORD ForceLevel,
  765. IN PUSE_ENTRY MatchedPointer,
  766. IN DWORD Index
  767. )
  768. /*++
  769. Routine Description:
  770. This function removes the use entry pointed by MatchedPointer and
  771. free it memory if it is a UNC connection deleted with force, or
  772. it is a UNC connection deleted with no force and the use count is
  773. decremented to 0, or it is a connection mapped to a local device.
  774. WARNING: This function assumes that the Use.TableResource is claimed.
  775. And it releases it on exit.
  776. Arguments:
  777. LogonId - Supplies a pointer to the user's Logon Id.
  778. ForceLevel - Supplies the level of force to delete.
  779. MatchedPointer - Supplies the pointer to the use entry to be deleted.
  780. Return Value:
  781. None.
  782. --*/
  783. {
  784. PUSE_ENTRY BackPointer;
  785. NET_API_STATUS status;
  786. //
  787. // No need to remove entry if UNC connection is deleted with USE_NOFORCE
  788. // level, and use count is not 0 after the deletion.
  789. //
  790. if ((MatchedPointer->Local == NULL) &&
  791. (ForceLevel == USE_NOFORCE) &&
  792. ((MatchedPointer->UseCount - 1) > 0)) {
  793. MatchedPointer->UseCount--;
  794. MatchedPointer->Remote->TotalUseCount--;
  795. NetpAssert(MatchedPointer->Remote->TotalUseCount);
  796. RtlReleaseResource(&Use.TableResource);
  797. return NERR_Success;
  798. }
  799. //
  800. // Delete the tree connection and close the handle.
  801. //
  802. if ((status = WsDeleteConnection(
  803. LogonId,
  804. MatchedPointer->TreeConnection,
  805. ForceLevel )) != NERR_Success) {
  806. RtlReleaseResource(&Use.TableResource);
  807. return status;
  808. }
  809. //
  810. // Successfully deleted connection, and refound our entry.
  811. //
  812. BackPointer = (PUSE_ENTRY)Use.Table[Index].List;
  813. if (BackPointer != MatchedPointer) {
  814. while (BackPointer->Next != NULL) {
  815. if (BackPointer->Next == MatchedPointer) {
  816. break;
  817. } else {
  818. BackPointer = BackPointer->Next;
  819. }
  820. }
  821. ASSERT(BackPointer->Next == MatchedPointer);
  822. BackPointer->Next = MatchedPointer->Next;
  823. } else {
  824. //
  825. // Use entry is the first one on the use list
  826. //
  827. Use.Table[Index].List = (PVOID) MatchedPointer->Next;
  828. }
  829. MatchedPointer->Remote->TotalUseCount -= MatchedPointer->UseCount;
  830. if (MatchedPointer->Remote->TotalUseCount == 0) {
  831. (void) LocalFree((HLOCAL) MatchedPointer->Remote);
  832. }
  833. RtlReleaseResource(&Use.TableResource);
  834. //
  835. // Delete symbolic link, if any.
  836. // Must perform the deletion outside of exclusively holding the
  837. // Use.TableResource.
  838. // Otherwise, when the shell tries to update the current status of
  839. // a drive letter change, the explorer.exe thread will block while
  840. // trying to acquire the Use.TableResource
  841. //
  842. WsDeleteSymbolicLink(
  843. MatchedPointer->Local,
  844. MatchedPointer->TreeConnectStr,
  845. NULL
  846. );
  847. (void) LocalFree((HLOCAL) MatchedPointer);
  848. return status;
  849. }
  850. STATIC
  851. NET_API_STATUS
  852. WsCreateNewEntry(
  853. OUT PUSE_ENTRY *NewUse,
  854. IN HANDLE TreeConnection,
  855. IN LPTSTR Local OPTIONAL,
  856. IN DWORD LocalLength,
  857. IN LPTSTR UncName OPTIONAL,
  858. IN DWORD UncNameLength,
  859. IN PUNICODE_STRING TreeConnectStr,
  860. IN DWORD Flags
  861. )
  862. /*++
  863. Routine Description:
  864. This function creates and initializes a new use entry. If the UncName
  865. is specified, a new remote entry is created and initialized with
  866. UncName.
  867. Arguments:
  868. NewUse - Returns a pointer to the newly allocated and initialized use
  869. entry.
  870. TreeConnection - Supplies the handle to the tree connection to set in
  871. the new use entry.
  872. Local - Supplies the local device name string to be copied into the new
  873. use entry.
  874. LocalLength - Supplies the length of the local device name string.
  875. UncName - Supplies the UNC name string to be copied into the new use entry.
  876. UncNameLength - Supplies the length of the UNC name string.
  877. TreeConnectStr - Supplies the string of UNC name in NT-style format
  878. (\Device\LanmanRedirector\X:\Orville\Razzle).
  879. Return Value:
  880. NET_API_STATUS - NERR_Success or reason for failure.
  881. --*/
  882. {
  883. PUNC_NAME NewRemoteEntry; // Common extension to use entries which
  884. // share the same UNC connection.
  885. //
  886. // Allocate memory for new use. String length does not include zero
  887. // terminator so add that.
  888. //
  889. if ((*NewUse = (PUSE_ENTRY) LocalAlloc(
  890. LMEM_ZEROINIT,
  891. ROUND_UP_COUNT(
  892. sizeof(USE_ENTRY) + (LocalLength + 1)
  893. * sizeof(TCHAR),
  894. ALIGN_WCHAR
  895. ) +
  896. (ARGUMENT_PRESENT(Local) ?
  897. TreeConnectStr->MaximumLength :
  898. 0
  899. )
  900. )) == NULL) {
  901. return GetLastError();
  902. }
  903. //
  904. // Put use information into the new use node
  905. //
  906. (*NewUse)->Next = NULL;
  907. (*NewUse)->LocalLength = LocalLength;
  908. (*NewUse)->UseCount = 1;
  909. (*NewUse)->TreeConnection = TreeConnection;
  910. (*NewUse)->ResumeKey = GlobalResumeKey++;
  911. (*NewUse)->Flags = Flags;
  912. //
  913. // GlobalResumeKey wraps back to 0 if it is 0x80000000 because we use the
  914. // high bit to indicate whether the resume handle for NetUseEnum comes
  915. // from the workstation service or from the redirector.
  916. //
  917. GlobalResumeKey &= ~(REDIR_LIST);
  918. //
  919. // Copy local device name into use entry after the LocalLength field,
  920. // if it is specified.
  921. //
  922. if (ARGUMENT_PRESENT(Local)) {
  923. (*NewUse)->Local = (LPTSTR) ((DWORD_PTR) *NewUse + sizeof(USE_ENTRY));
  924. STRCPY((*NewUse)->Local, Local);
  925. (*NewUse)->TreeConnectStr = (LPWSTR) ROUND_UP_COUNT(
  926. ((DWORD_PTR) *NewUse +
  927. sizeof(USE_ENTRY) +
  928. (LocalLength + 1) *
  929. sizeof(TCHAR)),
  930. ALIGN_WCHAR
  931. );
  932. wcscpy((*NewUse)->TreeConnectStr, TreeConnectStr->Buffer);
  933. }
  934. else {
  935. (*NewUse)->Local = NULL;
  936. (*NewUse)->TreeConnectStr = NULL;
  937. }
  938. //
  939. // If shared resource name is specified, create a new remote entry to hold
  940. // the UNC name, the tree connection handle, and total number of uses on
  941. // this shared resource.
  942. //
  943. if (ARGUMENT_PRESENT(UncName)) {
  944. if ((NewRemoteEntry = (PUNC_NAME) LocalAlloc(
  945. LMEM_ZEROINIT,
  946. (UINT) (sizeof(UNC_NAME) +
  947. UncNameLength * sizeof(TCHAR))
  948. )) == NULL) {
  949. (void) LocalFree((HLOCAL) *NewUse);
  950. return GetLastError();
  951. }
  952. STRCPY((LPWSTR) NewRemoteEntry->UncName, UncName);
  953. NewRemoteEntry->UncNameLength = UncNameLength;
  954. NewRemoteEntry->TotalUseCount = 1;
  955. // NewRemoteEntry->RedirUseInfo = NULL;
  956. (*NewUse)->Remote = NewRemoteEntry;
  957. }
  958. return NERR_Success;
  959. }
  960. STATIC
  961. NET_API_STATUS
  962. WsCheckLocalAndDeviceType(
  963. IN OUT LPTSTR Local,
  964. IN DWORD DeviceType,
  965. OUT LPDWORD ErrorParameter OPTIONAL
  966. )
  967. /*++
  968. Routine Description:
  969. This function checks the format of the specified local device name
  970. based on the device type of shared resource to be accessed, and at
  971. the same time verifies that the device type is valid.
  972. Arguments:
  973. Local - Supplies the local device name. Returns its canonicalized
  974. form.
  975. DeviceType - Supplies the shared resource device type.
  976. ErrorParameter - Returns the identifier to the invalid parameter in Buffer
  977. if this function returns ERROR_INVALID_PARAMETER.
  978. Return Value:
  979. NET_API_STATUS - NERR_Success or reason for failure.
  980. --*/
  981. {
  982. //
  983. // Validate local device name based on the shared resource type.
  984. //
  985. //
  986. // Check for wild card device type outside of the switch statement
  987. // below because compiler complains about constant too big.
  988. //
  989. if (DeviceType == USE_WILDCARD || DeviceType == USE_IPC) {
  990. //
  991. // Local device name must be NULL for wild card or IPC connection.
  992. //
  993. if (Local == NULL) {
  994. return NERR_Success;
  995. }
  996. else {
  997. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  998. }
  999. }
  1000. switch (DeviceType) {
  1001. case USE_DISKDEV:
  1002. if (Local == NULL) {
  1003. return NERR_Success;
  1004. }
  1005. //
  1006. // Local device name must have "<drive>:" format for disk
  1007. // device.
  1008. //
  1009. if (STRLEN(Local) != 2 || Local[1] != TCHAR_COLON) {
  1010. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1011. }
  1012. break;
  1013. case USE_SPOOLDEV:
  1014. if (Local == NULL) {
  1015. return NERR_Success;
  1016. }
  1017. //
  1018. // Local device name must have "LPTn:" or "PRN:" format
  1019. // for a print device.
  1020. //
  1021. if ((STRNICMP(Local, TEXT("PRN"), 3) != 0) &&
  1022. (STRNICMP(Local, TEXT("LPT"), 3) != 0)) {
  1023. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1024. }
  1025. break;
  1026. case USE_CHARDEV:
  1027. if (Local == NULL) {
  1028. return NERR_Success;
  1029. }
  1030. //
  1031. // Local device name must have "COMn:" or "AUX:" format
  1032. // for a comm device.
  1033. //
  1034. if ((STRNICMP(Local, TEXT("AUX"), 3) != 0) &&
  1035. (STRNICMP(Local, TEXT("COM"), 3) != 0)) {
  1036. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1037. }
  1038. break;
  1039. default:
  1040. IF_DEBUG(USE) {
  1041. NetpKdPrint((
  1042. "[Wksta] NetrUseAdd: Unknown shared resource type %lu\n",
  1043. DeviceType));
  1044. }
  1045. return NERR_BadAsgType;
  1046. }
  1047. return NERR_Success;
  1048. }
  1049. STATIC
  1050. NET_API_STATUS
  1051. WsCheckEstablishedDeviceType(
  1052. IN HANDLE TreeConnection,
  1053. IN DWORD RequestedDeviceType
  1054. )
  1055. /*++
  1056. Routine Description:
  1057. This function verifies that the device type of the shared resource we
  1058. have connected to is the same as the requested device type.
  1059. Arguments:
  1060. TreeConnection - Supplies handle to established tree connection.
  1061. RequestedDeviceType - Supplies the shared resource device type specified
  1062. by the user to create the tree connection.
  1063. Return Value:
  1064. NET_API_STATUS - NERR_Success or reason for failure.
  1065. --*/
  1066. {
  1067. NTSTATUS ntstatus;
  1068. FILE_FS_DEVICE_INFORMATION FileInformation;
  1069. IO_STATUS_BLOCK IoStatusBlock;
  1070. ntstatus = NtQueryVolumeInformationFile(
  1071. TreeConnection,
  1072. &IoStatusBlock,
  1073. (PVOID) &FileInformation,
  1074. sizeof(FILE_FS_DEVICE_INFORMATION),
  1075. FileFsDeviceInformation
  1076. );
  1077. if (! NT_SUCCESS(ntstatus) || ! NT_SUCCESS(IoStatusBlock.Status)) {
  1078. return NERR_InternalError;
  1079. }
  1080. //
  1081. // Check for wild card device type outside of the switch statement
  1082. // below because compiler complains about constant too big.
  1083. //
  1084. if (RequestedDeviceType == USE_WILDCARD) {
  1085. return NERR_Success;
  1086. }
  1087. switch (RequestedDeviceType) {
  1088. case USE_DISKDEV:
  1089. if (FileInformation.DeviceType != FILE_DEVICE_DISK) {
  1090. return ERROR_BAD_DEV_TYPE;
  1091. }
  1092. break;
  1093. case USE_SPOOLDEV:
  1094. if (FileInformation.DeviceType != FILE_DEVICE_PRINTER) {
  1095. return ERROR_BAD_DEV_TYPE;
  1096. }
  1097. break;
  1098. case USE_CHARDEV:
  1099. if (FileInformation.DeviceType != FILE_DEVICE_SERIAL_PORT) {
  1100. return ERROR_BAD_DEV_TYPE;
  1101. }
  1102. break;
  1103. case USE_IPC:
  1104. if (FileInformation.DeviceType != FILE_DEVICE_NAMED_PIPE) {
  1105. return ERROR_BAD_DEV_TYPE;
  1106. }
  1107. break;
  1108. default:
  1109. //
  1110. // This should have been error checked earlier.
  1111. //
  1112. NetpKdPrint((
  1113. "WsCheckEstablishedDeviceType: Unknown device type.\n"
  1114. ));
  1115. NetpAssert(FALSE);
  1116. return ERROR_BAD_DEV_TYPE;
  1117. }
  1118. return NERR_Success;
  1119. }
  1120. STATIC
  1121. NET_API_STATUS
  1122. WsAllocateUseWorkBuffer(
  1123. IN PUSE_INFO_2 UseInfo,
  1124. IN DWORD Level,
  1125. OUT LPTSTR *UncName,
  1126. OUT LPTSTR *Local,
  1127. OUT LPTSTR *UserName,
  1128. OUT LPTSTR *DomainName
  1129. )
  1130. /*++
  1131. Routine Description:
  1132. This function allocates the work buffer for NetrUseAdd. The buffer
  1133. is the maximum need for canonicalizing and storing the strings
  1134. described below. If any of the strings is NULL, no memory is allocated
  1135. for it.
  1136. UncName - UNC name of remote resource. Cannot be NULL.
  1137. Local - local device name specified in the NetUseAdd. May be NULL.
  1138. UserName - username to establish connection with. May be NULL.
  1139. DomainName - domain name. Must be specified if UserName is,
  1140. otherwise if UserName is NULL this string is ignored.
  1141. Arguments:
  1142. UseInfo - Supplies the input structure for NetUseAdd.
  1143. Level - Supplies the use info level.
  1144. Output pointers are set to point into allocated work buffer if its
  1145. corresponding input string is not NULL or empty.
  1146. Return Value:
  1147. Error from LocalAlloc.
  1148. --*/
  1149. {
  1150. DWORD WorkBufferSize = (MAX_PATH + 1) * sizeof(TCHAR);
  1151. LPBYTE WorkBuffer;
  1152. if ((UseInfo->ui2_local != NULL) &&
  1153. (UseInfo->ui2_local[0] != TCHAR_EOS)) {
  1154. WorkBufferSize += (DEVLEN + 1) * sizeof(TCHAR);
  1155. }
  1156. if (Level >= 2) {
  1157. if (UseInfo->ui2_username != NULL) {
  1158. WorkBufferSize += (UNLEN + 1) * sizeof(TCHAR);
  1159. }
  1160. if (UseInfo->ui2_domainname != NULL) {
  1161. WorkBufferSize += (DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR);
  1162. }
  1163. }
  1164. if ((WorkBuffer = (LPBYTE) LocalAlloc(
  1165. LMEM_ZEROINIT,
  1166. (UINT) WorkBufferSize
  1167. )) == NULL) {
  1168. return GetLastError();
  1169. }
  1170. *UncName = (LPTSTR) WorkBuffer;
  1171. IF_DEBUG(USE) {
  1172. NetpKdPrint((" Remote x%08lx\n", *UncName));
  1173. }
  1174. WorkBuffer += (MAX_PATH + 1) * sizeof(TCHAR);
  1175. if ((UseInfo->ui2_local != NULL) &&
  1176. (UseInfo->ui2_local[0] != TCHAR_EOS)) {
  1177. *Local = (LPTSTR) WorkBuffer;
  1178. WorkBuffer += (DEVLEN + 1) * sizeof(TCHAR);
  1179. }
  1180. else {
  1181. *Local = NULL;
  1182. }
  1183. IF_DEBUG(USE) {
  1184. NetpKdPrint((" Local x%08lx\n", *Local));
  1185. }
  1186. if (Level >= 2) {
  1187. if (UseInfo->ui2_username != NULL) {
  1188. *UserName = (LPTSTR) WorkBuffer;
  1189. WorkBuffer += (UNLEN + 1) * sizeof(TCHAR);
  1190. }
  1191. else {
  1192. *UserName = NULL;
  1193. }
  1194. if (UseInfo->ui2_domainname != NULL) {
  1195. *DomainName = (LPTSTR) WorkBuffer;
  1196. }
  1197. else {
  1198. *DomainName = NULL;
  1199. }
  1200. }
  1201. IF_DEBUG(USE) {
  1202. NetpKdPrint((" UserName x%08lx, DomainName x%08lx\n",
  1203. *UserName, *DomainName));
  1204. }
  1205. return NERR_Success;
  1206. }
  1207. #if DBG
  1208. STATIC
  1209. VOID
  1210. DumpUseList(
  1211. DWORD Index
  1212. )
  1213. /*++
  1214. Routine Description:
  1215. This function dumps the user's use list for debugging purposes.
  1216. Arguments:
  1217. Index - Supplies the index to the user entry in the Use Table.
  1218. Return Value:
  1219. None.
  1220. --*/
  1221. {
  1222. PUSE_ENTRY UseList = (PUSE_ENTRY) Use.Table[Index].List;
  1223. IF_DEBUG(USE) {
  1224. NetpKdPrint(("\nDump Use List @%08lx\n", UseList));
  1225. while (UseList != NULL) {
  1226. NetpKdPrint(("%ws %ws\n", UseList->Local,
  1227. UseList->Remote->UncName));
  1228. NetpKdPrint(("usecount=%lu, totalusecount=%lu\n",
  1229. UseList->UseCount, UseList->Remote->TotalUseCount));
  1230. NetpKdPrint(("Connection handle %08lx, resume key=%lu\n",
  1231. UseList->TreeConnection, UseList->ResumeKey));
  1232. UseList = UseList->Next;
  1233. }
  1234. }
  1235. }
  1236. #endif
  1237.