Leaked source code of windows server 2003
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.

1583 lines
43 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, hToken = INVALID_HANDLE_VALUE;
  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. &hToken
  381. )) != NERR_Success) {
  382. if ((ARGUMENT_PRESENT(ErrorParameter)) &&
  383. (status == ERROR_INVALID_PARAMETER)) {
  384. *ErrorParameter = USE_LOCAL_PARMNUM;
  385. }
  386. }
  387. RtlReleaseResource(&Use.TableResource);
  388. if( status )
  389. goto FreeAllocatedBuffers;
  390. }
  391. //
  392. // Create the tree connection if none already exists; otherwise, open it.
  393. //
  394. status = WsOpenCreateConnection(
  395. &TreeConnectStr,
  396. UserName,
  397. DomainName,
  398. Password,
  399. CreateFlags,
  400. FILE_OPEN_IF,
  401. UseInfo->ui2_asg_type,
  402. &TreeConnection,
  403. NULL
  404. );
  405. if (status != NERR_Success) {
  406. if (status == NERR_UseNotFound) {
  407. status = ERROR_DEV_NOT_EXIST;
  408. }
  409. WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session, hToken );
  410. goto FreeAllocatedBuffers;
  411. }
  412. //
  413. // Make sure user was correct about the shared resource type
  414. //
  415. if ((status = WsCheckEstablishedDeviceType(
  416. TreeConnection,
  417. UseInfo->ui2_asg_type
  418. )) != NERR_Success) {
  419. if(WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE) != NERR_Success) {
  420. (void)NtClose(TreeConnection);
  421. }
  422. WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session, hToken );
  423. goto FreeAllocatedBuffers;
  424. }
  425. //
  426. // Add use to the Use Table.
  427. //
  428. status = WsAddUse(
  429. &LogonId,
  430. TreeConnection,
  431. Local,
  432. LocalLength,
  433. UncName,
  434. UncNameLength,
  435. &TreeConnectStr,
  436. Flags
  437. );
  438. if( status ) {
  439. if(WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE) != NERR_Success) {
  440. (void)NtClose(TreeConnection);
  441. }
  442. WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session, hToken );
  443. }
  444. FreeAllocatedBuffers:
  445. //
  446. // Free tree connection name buffer and work buffer
  447. //
  448. (void) LocalFree(TreeConnectStr.Buffer);
  449. FreeWorkBuffer:
  450. if (hToken != INVALID_HANDLE_VALUE)
  451. {
  452. CloseHandle(hToken);
  453. }
  454. (void) LocalFree(UncName);
  455. (void) LocalFree(Session);
  456. //
  457. // Put the password back the way we found it.
  458. //
  459. if ( EncodedPassword.Length != 0 ) {
  460. UCHAR Seed = NETR_USE_ADD_PASSWORD_SEED;
  461. RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
  462. }
  463. IF_DEBUG(USE) {
  464. NetpKdPrint(("[Wksta] NetrUseAdd: about to return status=%lu\n",
  465. status));
  466. }
  467. return status;
  468. }
  469. NET_API_STATUS NET_API_FUNCTION
  470. NetrUseDel (
  471. IN LPTSTR ServerName OPTIONAL,
  472. IN LPTSTR UseName,
  473. IN DWORD ForceLevel
  474. )
  475. /*++
  476. Routine Description:
  477. This function is the NetUseDel entry point in the Workstation service.
  478. Arguments:
  479. UseName - Supplies the local device name or shared resource name of
  480. the tree connection to be deleted.
  481. ForceLevel - Supplies the level of force to delete the tree connection.
  482. Return Value:
  483. NET_API_STATUS - NERR_Success or reason for failure.
  484. --*/
  485. {
  486. NET_API_STATUS status;
  487. LUID LogonId; // Logon Id of user
  488. DWORD Index; // Index to user entry in Use Table
  489. PUSE_ENTRY MatchedPointer; // Points to found use entry
  490. PUSE_ENTRY BackPointer; // Points to node previous to
  491. // found use entry
  492. HANDLE TreeConnection; // Handle to connection
  493. TCHAR *FormattedUseName;
  494. // For canonicalizing a local device
  495. // name
  496. DWORD PathType = 0;
  497. PUSE_ENTRY UseList;
  498. UNREFERENCED_PARAMETER(ServerName);
  499. //
  500. // Check that ForceLevel parameter is valid
  501. //
  502. switch (ForceLevel) {
  503. case USE_NOFORCE:
  504. case USE_LOTS_OF_FORCE:
  505. case USE_FORCE:
  506. break;
  507. default:
  508. return ERROR_INVALID_PARAMETER;
  509. }
  510. FormattedUseName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(MAX_PATH+1)*sizeof(TCHAR));
  511. if (FormattedUseName == NULL) {
  512. return GetLastError();
  513. }
  514. //
  515. // Check to see if UseName is valid, and canonicalize it.
  516. //
  517. if (I_NetPathCanonicalize(
  518. NULL,
  519. UseName,
  520. FormattedUseName,
  521. (MAX_PATH+1)*sizeof(TCHAR),
  522. NULL,
  523. &PathType,
  524. 0
  525. ) != NERR_Success) {
  526. LocalFree(FormattedUseName);
  527. return NERR_UseNotFound;
  528. }
  529. IF_DEBUG(USE) {
  530. NetpKdPrint(("\n[Wksta] NetrUseDel %ws %u, formatted use name %ws\n",
  531. UseName, ForceLevel, FormattedUseName));
  532. }
  533. //
  534. // Impersonate caller and get the logon id
  535. //
  536. if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
  537. LocalFree(FormattedUseName);
  538. return status;
  539. }
  540. //
  541. // Lock Use Table while looking for entry to delete.
  542. //
  543. if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
  544. LocalFree(FormattedUseName);
  545. return NERR_InternalError;
  546. }
  547. //
  548. // See if the use entry is an explicit connection.
  549. //
  550. status = WsGetUserEntry(
  551. &Use,
  552. &LogonId,
  553. &Index,
  554. FALSE
  555. );
  556. UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
  557. NULL;
  558. if ((status = WsFindUse(
  559. &LogonId,
  560. UseList,
  561. FormattedUseName,
  562. &TreeConnection,
  563. &MatchedPointer,
  564. &BackPointer
  565. )) != NERR_Success) {
  566. RtlReleaseResource(&Use.TableResource);
  567. LocalFree(FormattedUseName);
  568. return status;
  569. }
  570. LocalFree(FormattedUseName);
  571. if (MatchedPointer == NULL) {
  572. //
  573. // UseName specified has an implicit connection. Don't need to hold
  574. // on to Use Table anymore.
  575. //
  576. RtlReleaseResource(&Use.TableResource);
  577. status = WsDeleteConnection(&LogonId, TreeConnection, ForceLevel);
  578. //
  579. // Close the connection handle if the API failed.
  580. //
  581. if (status != NERR_Success) {
  582. NtClose(TreeConnection);
  583. }
  584. return status;
  585. }
  586. else if ((MatchedPointer->Local != NULL) &&
  587. (MatchedPointer->LocalLength > 2)) {
  588. //
  589. // Don't allow delete on comm or spooled devices if redirection is
  590. // paused for the current user.
  591. //
  592. if (WsRedirectionPaused(MatchedPointer->Local)) {
  593. RtlReleaseResource(&Use.TableResource);
  594. return ERROR_REDIR_PAUSED;
  595. }
  596. }
  597. //
  598. // Delete tree connection and remove use entry from Use Table. This function
  599. // releases the TableResource
  600. //
  601. status = WsDeleteUse(
  602. &LogonId,
  603. ForceLevel,
  604. MatchedPointer,
  605. Index
  606. );
  607. IF_DEBUG(USE) {
  608. NetpKdPrint(("[Wksta] NetrUseDel: about to return status=%lu\n", status));
  609. }
  610. return status;
  611. }
  612. STATIC
  613. NET_API_STATUS
  614. WsAddUse(
  615. IN PLUID LogonId,
  616. IN HANDLE TreeConnection,
  617. IN LPTSTR Local OPTIONAL,
  618. IN DWORD LocalLength,
  619. IN LPTSTR UncName,
  620. IN DWORD UncNameLength,
  621. IN PUNICODE_STRING TreeConnectStr,
  622. IN DWORD Flags
  623. )
  624. /*++
  625. Routine Description:
  626. This function adds a use (tree connection) entry into the Use Table for
  627. the user specified by the Logon Id. There is a linked list of uses for
  628. each user. Each new use entry is inserted into the end of the linked
  629. list so that enumeration of the list is resumable.
  630. NOTE: This function locks the Use Table.
  631. It also closes the tree connection if a tree
  632. connection to the same shared resource already exists.
  633. Arguments:
  634. LogonId - Supplies a pointer to the user's Logon Id.
  635. TreeConnection - Supplies the handle to the tree connection created.
  636. Local - Supplies the string of the local device name.
  637. LocalLength - Supplies the length of the local device name.
  638. UncName - Supplies the name of the shared resource (UNC name).
  639. UncNameLength - Supplies the length of the shared resource.
  640. TreeConnectStr - Supplies the string of UNC name in NT-style format
  641. (\Device\LanmanRedirector\X:\Orville\Razzle).
  642. Return Value:
  643. NET_API_STATUS - NERR_Success or reason for failure.
  644. --*/
  645. {
  646. NET_API_STATUS status;
  647. DWORD Index; // Index to user entry in Use Table
  648. PUSE_ENTRY MatchedPointer = NULL; // Points to matching shared resource
  649. PUSE_ENTRY InsertPointer = NULL; // Point of insertion into use list
  650. PUSE_ENTRY NewUse; // Pointer to the new use entry
  651. if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
  652. // (void) NtClose(TreeConnection);
  653. return NERR_InternalError;
  654. }
  655. //
  656. // Look for the matching LogonId in the Use Table, if none matched
  657. // create a new entry.
  658. //
  659. if ((status = WsGetUserEntry(
  660. &Use,
  661. LogonId,
  662. &Index,
  663. TRUE
  664. )) != NERR_Success) {
  665. RtlReleaseResource(&Use.TableResource);
  666. // (void) NtClose(TreeConnection);
  667. return status;
  668. }
  669. if (Use.Table[Index].List != NULL) {
  670. //
  671. // Traverse use list to look for location to insert new use entry.
  672. //
  673. WsFindInsertLocation(
  674. (PUSE_ENTRY) Use.Table[Index].List,
  675. UncName,
  676. &MatchedPointer,
  677. &InsertPointer
  678. );
  679. }
  680. if (MatchedPointer == NULL) {
  681. //
  682. // No matching UNC name found. Create a new entry with a
  683. // corresponding remote entry.
  684. //
  685. if ((status = WsCreateNewEntry(
  686. &NewUse,
  687. TreeConnection,
  688. Local,
  689. LocalLength,
  690. UncName,
  691. UncNameLength,
  692. TreeConnectStr,
  693. Flags
  694. )) != NERR_Success) {
  695. RtlReleaseResource(&Use.TableResource);
  696. // (void) NtClose(TreeConnection);
  697. return status;
  698. }
  699. }
  700. else {
  701. //
  702. // Matching UNC name found.
  703. //
  704. //
  705. // It may be unnecessary to create a new use entry if the use
  706. // we are adding has a NULL local device and a NULL local device
  707. // entry already exists.
  708. //
  709. if (Local == NULL) {
  710. if (MatchedPointer->Local == NULL) {
  711. //
  712. // Yes, there is a NULL local device entry already.
  713. // Increment the use count and we are done.
  714. //
  715. MatchedPointer->UseCount++;
  716. MatchedPointer->Remote->TotalUseCount++;
  717. #if DBG
  718. DumpUseList(Index);
  719. #endif
  720. RtlReleaseResource(&Use.TableResource);
  721. //
  722. // Close newly opened handle to the same tree connection because
  723. // one already exists.
  724. //
  725. (void) NtClose(TreeConnection);
  726. return NERR_Success;
  727. }
  728. }
  729. //
  730. // If we get here means we need to create a new use entry but not
  731. // a corresponding remote entry because a use with the same UNC
  732. // name already exists.
  733. //
  734. if ((status = WsCreateNewEntry(
  735. &NewUse,
  736. TreeConnection,
  737. Local,
  738. LocalLength,
  739. NULL,
  740. 0,
  741. TreeConnectStr,
  742. Flags
  743. )) != NERR_Success) {
  744. RtlReleaseResource(&Use.TableResource);
  745. // (void) NtClose(TreeConnection);
  746. return status;
  747. }
  748. NewUse->Remote = MatchedPointer->Remote;
  749. NewUse->Remote->TotalUseCount++;
  750. }
  751. //
  752. // Insert the new use entry into use list
  753. //
  754. if (InsertPointer == NULL) {
  755. //
  756. // Inserting into the head of list
  757. //
  758. Use.Table[Index].List = (PVOID) NewUse;
  759. }
  760. else {
  761. InsertPointer->Next = NewUse;
  762. }
  763. #if DBG
  764. DumpUseList(Index);
  765. #endif
  766. RtlReleaseResource(&Use.TableResource);
  767. return NERR_Success;
  768. }
  769. STATIC
  770. NET_API_STATUS
  771. WsDeleteUse(
  772. IN PLUID LogonId,
  773. IN DWORD ForceLevel,
  774. IN PUSE_ENTRY MatchedPointer,
  775. IN DWORD Index
  776. )
  777. /*++
  778. Routine Description:
  779. This function removes the use entry pointed by MatchedPointer and
  780. free it memory if it is a UNC connection deleted with force, or
  781. it is a UNC connection deleted with no force and the use count is
  782. decremented to 0, or it is a connection mapped to a local device.
  783. WARNING: This function assumes that the Use.TableResource is claimed.
  784. And it releases it on exit.
  785. Arguments:
  786. LogonId - Supplies a pointer to the user's Logon Id.
  787. ForceLevel - Supplies the level of force to delete.
  788. MatchedPointer - Supplies the pointer to the use entry to be deleted.
  789. Return Value:
  790. None.
  791. --*/
  792. {
  793. PUSE_ENTRY BackPointer;
  794. NET_API_STATUS status;
  795. //
  796. // No need to remove entry if UNC connection is deleted with USE_NOFORCE
  797. // level, and use count is not 0 after the deletion.
  798. //
  799. if ((MatchedPointer->Local == NULL) &&
  800. (ForceLevel == USE_NOFORCE) &&
  801. ((MatchedPointer->UseCount - 1) > 0)) {
  802. MatchedPointer->UseCount--;
  803. MatchedPointer->Remote->TotalUseCount--;
  804. NetpAssert(MatchedPointer->Remote->TotalUseCount);
  805. RtlReleaseResource(&Use.TableResource);
  806. return NERR_Success;
  807. }
  808. //
  809. // Delete the tree connection and close the handle.
  810. //
  811. if ((status = WsDeleteConnection(
  812. LogonId,
  813. MatchedPointer->TreeConnection,
  814. ForceLevel )) != NERR_Success) {
  815. RtlReleaseResource(&Use.TableResource);
  816. return status;
  817. }
  818. //
  819. // Successfully deleted connection, and refound our entry.
  820. //
  821. BackPointer = (PUSE_ENTRY)Use.Table[Index].List;
  822. if (BackPointer != MatchedPointer) {
  823. while (BackPointer->Next != NULL) {
  824. if (BackPointer->Next == MatchedPointer) {
  825. break;
  826. } else {
  827. BackPointer = BackPointer->Next;
  828. }
  829. }
  830. ASSERT(BackPointer->Next == MatchedPointer);
  831. BackPointer->Next = MatchedPointer->Next;
  832. } else {
  833. //
  834. // Use entry is the first one on the use list
  835. //
  836. Use.Table[Index].List = (PVOID) MatchedPointer->Next;
  837. }
  838. MatchedPointer->Remote->TotalUseCount -= MatchedPointer->UseCount;
  839. if (MatchedPointer->Remote->TotalUseCount == 0) {
  840. (void) LocalFree((HLOCAL) MatchedPointer->Remote);
  841. }
  842. RtlReleaseResource(&Use.TableResource);
  843. //
  844. // Delete symbolic link, if any.
  845. // Must perform the deletion outside of exclusively holding the
  846. // Use.TableResource.
  847. // Otherwise, when the shell tries to update the current status of
  848. // a drive letter change, the explorer.exe thread will block while
  849. // trying to acquire the Use.TableResource
  850. //
  851. WsDeleteSymbolicLink(
  852. MatchedPointer->Local,
  853. MatchedPointer->TreeConnectStr,
  854. NULL,
  855. INVALID_HANDLE_VALUE
  856. );
  857. (void) LocalFree((HLOCAL) MatchedPointer);
  858. return status;
  859. }
  860. STATIC
  861. NET_API_STATUS
  862. WsCreateNewEntry(
  863. OUT PUSE_ENTRY *NewUse,
  864. IN HANDLE TreeConnection,
  865. IN LPTSTR Local OPTIONAL,
  866. IN DWORD LocalLength,
  867. IN LPTSTR UncName OPTIONAL,
  868. IN DWORD UncNameLength,
  869. IN PUNICODE_STRING TreeConnectStr,
  870. IN DWORD Flags
  871. )
  872. /*++
  873. Routine Description:
  874. This function creates and initializes a new use entry. If the UncName
  875. is specified, a new remote entry is created and initialized with
  876. UncName.
  877. Arguments:
  878. NewUse - Returns a pointer to the newly allocated and initialized use
  879. entry.
  880. TreeConnection - Supplies the handle to the tree connection to set in
  881. the new use entry.
  882. Local - Supplies the local device name string to be copied into the new
  883. use entry.
  884. LocalLength - Supplies the length of the local device name string.
  885. UncName - Supplies the UNC name string to be copied into the new use entry.
  886. UncNameLength - Supplies the length of the UNC name string.
  887. TreeConnectStr - Supplies the string of UNC name in NT-style format
  888. (\Device\LanmanRedirector\X:\Orville\Razzle).
  889. Return Value:
  890. NET_API_STATUS - NERR_Success or reason for failure.
  891. --*/
  892. {
  893. PUNC_NAME NewRemoteEntry; // Common extension to use entries which
  894. // share the same UNC connection.
  895. //
  896. // Allocate memory for new use. String length does not include zero
  897. // terminator so add that.
  898. //
  899. if ((*NewUse = (PUSE_ENTRY) LocalAlloc(
  900. LMEM_ZEROINIT,
  901. ROUND_UP_COUNT(
  902. sizeof(USE_ENTRY) + (LocalLength + 1)
  903. * sizeof(TCHAR),
  904. ALIGN_WCHAR
  905. ) +
  906. (ARGUMENT_PRESENT(Local) ?
  907. TreeConnectStr->MaximumLength :
  908. 0
  909. )
  910. )) == NULL) {
  911. return GetLastError();
  912. }
  913. //
  914. // Put use information into the new use node
  915. //
  916. (*NewUse)->Next = NULL;
  917. (*NewUse)->LocalLength = LocalLength;
  918. (*NewUse)->UseCount = 1;
  919. (*NewUse)->TreeConnection = TreeConnection;
  920. (*NewUse)->ResumeKey = GlobalResumeKey++;
  921. (*NewUse)->Flags = Flags;
  922. //
  923. // GlobalResumeKey wraps back to 0 if it is 0x80000000 because we use the
  924. // high bit to indicate whether the resume handle for NetUseEnum comes
  925. // from the workstation service or from the redirector.
  926. //
  927. GlobalResumeKey &= ~(REDIR_LIST);
  928. //
  929. // Copy local device name into use entry after the LocalLength field,
  930. // if it is specified.
  931. //
  932. if (ARGUMENT_PRESENT(Local)) {
  933. (*NewUse)->Local = (LPTSTR) ((DWORD_PTR) *NewUse + sizeof(USE_ENTRY));
  934. STRCPY((*NewUse)->Local, Local);
  935. (*NewUse)->TreeConnectStr = (LPWSTR) ROUND_UP_COUNT(
  936. ((DWORD_PTR) *NewUse +
  937. sizeof(USE_ENTRY) +
  938. (LocalLength + 1) *
  939. sizeof(TCHAR)),
  940. ALIGN_WCHAR
  941. );
  942. wcscpy((*NewUse)->TreeConnectStr, TreeConnectStr->Buffer);
  943. }
  944. else {
  945. (*NewUse)->Local = NULL;
  946. (*NewUse)->TreeConnectStr = NULL;
  947. }
  948. //
  949. // If shared resource name is specified, create a new remote entry to hold
  950. // the UNC name, the tree connection handle, and total number of uses on
  951. // this shared resource.
  952. //
  953. if (ARGUMENT_PRESENT(UncName)) {
  954. if ((NewRemoteEntry = (PUNC_NAME) LocalAlloc(
  955. LMEM_ZEROINIT,
  956. (UINT) (sizeof(UNC_NAME) +
  957. UncNameLength * sizeof(TCHAR))
  958. )) == NULL) {
  959. (void) LocalFree((HLOCAL) *NewUse);
  960. return GetLastError();
  961. }
  962. STRCPY((LPWSTR) NewRemoteEntry->UncName, UncName);
  963. NewRemoteEntry->UncNameLength = UncNameLength;
  964. NewRemoteEntry->TotalUseCount = 1;
  965. // NewRemoteEntry->RedirUseInfo = NULL;
  966. (*NewUse)->Remote = NewRemoteEntry;
  967. }
  968. return NERR_Success;
  969. }
  970. STATIC
  971. NET_API_STATUS
  972. WsCheckLocalAndDeviceType(
  973. IN OUT LPTSTR Local,
  974. IN DWORD DeviceType,
  975. OUT LPDWORD ErrorParameter OPTIONAL
  976. )
  977. /*++
  978. Routine Description:
  979. This function checks the format of the specified local device name
  980. based on the device type of shared resource to be accessed, and at
  981. the same time verifies that the device type is valid.
  982. Arguments:
  983. Local - Supplies the local device name. Returns its canonicalized
  984. form.
  985. DeviceType - Supplies the shared resource device type.
  986. ErrorParameter - Returns the identifier to the invalid parameter in Buffer
  987. if this function returns ERROR_INVALID_PARAMETER.
  988. Return Value:
  989. NET_API_STATUS - NERR_Success or reason for failure.
  990. --*/
  991. {
  992. //
  993. // Validate local device name based on the shared resource type.
  994. //
  995. //
  996. // Check for wild card device type outside of the switch statement
  997. // below because compiler complains about constant too big.
  998. //
  999. if (DeviceType == USE_WILDCARD || DeviceType == USE_IPC) {
  1000. //
  1001. // Local device name must be NULL for wild card or IPC connection.
  1002. //
  1003. if (Local == NULL) {
  1004. return NERR_Success;
  1005. }
  1006. else {
  1007. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1008. }
  1009. }
  1010. switch (DeviceType) {
  1011. case USE_DISKDEV:
  1012. if (Local == NULL) {
  1013. return NERR_Success;
  1014. }
  1015. //
  1016. // Local device name must have "<drive>:" format for disk
  1017. // device.
  1018. //
  1019. if (STRLEN(Local) != 2 || Local[1] != TCHAR_COLON) {
  1020. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1021. }
  1022. break;
  1023. case USE_SPOOLDEV:
  1024. if (Local == NULL) {
  1025. return NERR_Success;
  1026. }
  1027. //
  1028. // Local device name must have "LPTn:" or "PRN:" format
  1029. // for a print device.
  1030. //
  1031. if ((STRNICMP(Local, TEXT("PRN"), 3) != 0) &&
  1032. (STRNICMP(Local, TEXT("LPT"), 3) != 0)) {
  1033. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1034. }
  1035. break;
  1036. case USE_CHARDEV:
  1037. if (Local == NULL) {
  1038. return NERR_Success;
  1039. }
  1040. //
  1041. // Local device name must have "COMn:" or "AUX:" format
  1042. // for a comm device.
  1043. //
  1044. if ((STRNICMP(Local, TEXT("AUX"), 3) != 0) &&
  1045. (STRNICMP(Local, TEXT("COM"), 3) != 0)) {
  1046. RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
  1047. }
  1048. break;
  1049. default:
  1050. IF_DEBUG(USE) {
  1051. NetpKdPrint((
  1052. "[Wksta] NetrUseAdd: Unknown shared resource type %lu\n",
  1053. DeviceType));
  1054. }
  1055. return NERR_BadAsgType;
  1056. }
  1057. return NERR_Success;
  1058. }
  1059. STATIC
  1060. NET_API_STATUS
  1061. WsCheckEstablishedDeviceType(
  1062. IN HANDLE TreeConnection,
  1063. IN DWORD RequestedDeviceType
  1064. )
  1065. /*++
  1066. Routine Description:
  1067. This function verifies that the device type of the shared resource we
  1068. have connected to is the same as the requested device type.
  1069. Arguments:
  1070. TreeConnection - Supplies handle to established tree connection.
  1071. RequestedDeviceType - Supplies the shared resource device type specified
  1072. by the user to create the tree connection.
  1073. Return Value:
  1074. NET_API_STATUS - NERR_Success or reason for failure.
  1075. --*/
  1076. {
  1077. NTSTATUS ntstatus;
  1078. FILE_FS_DEVICE_INFORMATION FileInformation;
  1079. IO_STATUS_BLOCK IoStatusBlock;
  1080. ntstatus = NtQueryVolumeInformationFile(
  1081. TreeConnection,
  1082. &IoStatusBlock,
  1083. (PVOID) &FileInformation,
  1084. sizeof(FILE_FS_DEVICE_INFORMATION),
  1085. FileFsDeviceInformation
  1086. );
  1087. if (! NT_SUCCESS(ntstatus) || ! NT_SUCCESS(IoStatusBlock.Status)) {
  1088. return NERR_InternalError;
  1089. }
  1090. //
  1091. // Check for wild card device type outside of the switch statement
  1092. // below because compiler complains about constant too big.
  1093. //
  1094. if (RequestedDeviceType == USE_WILDCARD) {
  1095. return NERR_Success;
  1096. }
  1097. switch (RequestedDeviceType) {
  1098. case USE_DISKDEV:
  1099. if (FileInformation.DeviceType != FILE_DEVICE_DISK) {
  1100. return ERROR_BAD_DEV_TYPE;
  1101. }
  1102. break;
  1103. case USE_SPOOLDEV:
  1104. if (FileInformation.DeviceType != FILE_DEVICE_PRINTER) {
  1105. return ERROR_BAD_DEV_TYPE;
  1106. }
  1107. break;
  1108. case USE_CHARDEV:
  1109. if (FileInformation.DeviceType != FILE_DEVICE_SERIAL_PORT) {
  1110. return ERROR_BAD_DEV_TYPE;
  1111. }
  1112. break;
  1113. case USE_IPC:
  1114. if (FileInformation.DeviceType != FILE_DEVICE_NAMED_PIPE) {
  1115. return ERROR_BAD_DEV_TYPE;
  1116. }
  1117. break;
  1118. default:
  1119. //
  1120. // This should have been error checked earlier.
  1121. //
  1122. NetpKdPrint((
  1123. "WsCheckEstablishedDeviceType: Unknown device type.\n"
  1124. ));
  1125. NetpAssert(FALSE);
  1126. return ERROR_BAD_DEV_TYPE;
  1127. }
  1128. return NERR_Success;
  1129. }
  1130. STATIC
  1131. NET_API_STATUS
  1132. WsAllocateUseWorkBuffer(
  1133. IN PUSE_INFO_2 UseInfo,
  1134. IN DWORD Level,
  1135. OUT LPTSTR *UncName,
  1136. OUT LPTSTR *Local,
  1137. OUT LPTSTR *UserName,
  1138. OUT LPTSTR *DomainName
  1139. )
  1140. /*++
  1141. Routine Description:
  1142. This function allocates the work buffer for NetrUseAdd. The buffer
  1143. is the maximum need for canonicalizing and storing the strings
  1144. described below. If any of the strings is NULL, no memory is allocated
  1145. for it.
  1146. UncName - UNC name of remote resource. Cannot be NULL.
  1147. Local - local device name specified in the NetUseAdd. May be NULL.
  1148. UserName - username to establish connection with. May be NULL.
  1149. DomainName - domain name. Must be specified if UserName is,
  1150. otherwise if UserName is NULL this string is ignored.
  1151. Arguments:
  1152. UseInfo - Supplies the input structure for NetUseAdd.
  1153. Level - Supplies the use info level.
  1154. Output pointers are set to point into allocated work buffer if its
  1155. corresponding input string is not NULL or empty.
  1156. Return Value:
  1157. Error from LocalAlloc.
  1158. --*/
  1159. {
  1160. DWORD WorkBufferSize = (MAX_PATH + 1) * sizeof(TCHAR);
  1161. LPBYTE WorkBuffer;
  1162. if ((UseInfo->ui2_local != NULL) &&
  1163. (UseInfo->ui2_local[0] != TCHAR_EOS)) {
  1164. WorkBufferSize += (DEVLEN + 1) * sizeof(TCHAR);
  1165. }
  1166. if (Level >= 2) {
  1167. if (UseInfo->ui2_username != NULL) {
  1168. WorkBufferSize += (UNLEN + 1) * sizeof(TCHAR);
  1169. }
  1170. if (UseInfo->ui2_domainname != NULL) {
  1171. WorkBufferSize += (DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR);
  1172. }
  1173. }
  1174. if ((WorkBuffer = (LPBYTE) LocalAlloc(
  1175. LMEM_ZEROINIT,
  1176. (UINT) WorkBufferSize
  1177. )) == NULL) {
  1178. return GetLastError();
  1179. }
  1180. *UncName = (LPTSTR) WorkBuffer;
  1181. IF_DEBUG(USE) {
  1182. NetpKdPrint((" Remote x%08lx\n", *UncName));
  1183. }
  1184. WorkBuffer += (MAX_PATH + 1) * sizeof(TCHAR);
  1185. if ((UseInfo->ui2_local != NULL) &&
  1186. (UseInfo->ui2_local[0] != TCHAR_EOS)) {
  1187. *Local = (LPTSTR) WorkBuffer;
  1188. WorkBuffer += (DEVLEN + 1) * sizeof(TCHAR);
  1189. }
  1190. else {
  1191. *Local = NULL;
  1192. }
  1193. IF_DEBUG(USE) {
  1194. NetpKdPrint((" Local x%08lx\n", *Local));
  1195. }
  1196. if (Level >= 2) {
  1197. if (UseInfo->ui2_username != NULL) {
  1198. *UserName = (LPTSTR) WorkBuffer;
  1199. WorkBuffer += (UNLEN + 1) * sizeof(TCHAR);
  1200. }
  1201. else {
  1202. *UserName = NULL;
  1203. }
  1204. if (UseInfo->ui2_domainname != NULL) {
  1205. *DomainName = (LPTSTR) WorkBuffer;
  1206. }
  1207. else {
  1208. *DomainName = NULL;
  1209. }
  1210. }
  1211. IF_DEBUG(USE) {
  1212. NetpKdPrint((" UserName x%08lx, DomainName x%08lx\n",
  1213. *UserName, *DomainName));
  1214. }
  1215. return NERR_Success;
  1216. }
  1217. #if DBG
  1218. STATIC
  1219. VOID
  1220. DumpUseList(
  1221. DWORD Index
  1222. )
  1223. /*++
  1224. Routine Description:
  1225. This function dumps the user's use list for debugging purposes.
  1226. Arguments:
  1227. Index - Supplies the index to the user entry in the Use Table.
  1228. Return Value:
  1229. None.
  1230. --*/
  1231. {
  1232. PUSE_ENTRY UseList = (PUSE_ENTRY) Use.Table[Index].List;
  1233. IF_DEBUG(USE) {
  1234. NetpKdPrint(("\nDump Use List @%08lx\n", UseList));
  1235. while (UseList != NULL) {
  1236. NetpKdPrint(("%ws %ws\n", UseList->Local,
  1237. UseList->Remote->UncName));
  1238. NetpKdPrint(("usecount=%lu, totalusecount=%lu\n",
  1239. UseList->UseCount, UseList->Remote->TotalUseCount));
  1240. NetpKdPrint(("Connection handle %08lx, resume key=%lu\n",
  1241. UseList->TreeConnection, UseList->ResumeKey));
  1242. UseList = UseList->Next;
  1243. }
  1244. }
  1245. }
  1246. #endif
  1247.