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.

903 lines
27 KiB

  1. ' clonepr.vbi start
  2. // VB Script "Include" file for CloneSecurityPrincipal scripts
  3. //
  4. // contains code common to all the scripts
  5. //
  6. // Copyright (C) 1999 Microsoft Corporation.
  7. ' various manifest constants
  8. const CLASS_USER = 0
  9. const CLASS_LOCAL_GROUP = 1
  10. const CLASS_GLOBAL_GROUP = 2
  11. const CLASS_OTHER = 3
  12. ' the elements of this array are indexed by the above constants
  13. dim classNames(2)
  14. classNames(CLASS_USER) = "User"
  15. classNames(CLASS_LOCAL_GROUP) = "Group"
  16. classNames(CLASS_GLOBAL_GROUP) = "Group"
  17. ' from iads.h
  18. const ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP = &H4
  19. const ADS_GROUP_TYPE_GLOBAL_GROUP = &H2
  20. const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &H8
  21. const ADS_GROUP_TYPE_SECURITY_ENABLED = &H80000000
  22. const ADS_NAME_INITTYPE_DOMAIN = 1
  23. const ADS_NAME_INITTYPE_SERVER = 2
  24. const ADS_NAME_TYPE_1779 = 1
  25. const ADS_NAME_TYPE_NT4 = 3
  26. const ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME = 12
  27. const ADS_PROPERTY_APPEND = 3
  28. const ADS_PROPERTY_DELETE = 4
  29. const ADS_PROPERTY_UPDATE = 2
  30. ' from lmaccess.h
  31. const UF_TEMP_DUPLICATE_ACCOUNT = &H0100
  32. const UF_NORMAL_ACCOUNT = &H0200
  33. ' from andyhar's adsi reskit
  34. const ADS_SID_RAW = 0
  35. const ADS_SID_HEXSTRING = 1
  36. const ADS_SID_SDDL = 4
  37. const ADS_SID_WINNT_PATH = 5
  38. const ADS_SID_ACTIVE_DIRECTORY_PATH = 6
  39. const E_ADS_UNKNOWN_OBJECT = &H80005004
  40. const E_ADS_ERROR_DS_NO_SUCH_OBJECT = &H80072030
  41. const E_ADS_ERROR_DS_NAME_NOT_FOUND = &H80072116
  42. ' create the COM object implementing ICloneSecurityPrincipal
  43. dim clonepr
  44. set clonepr = CreateObject("DSUtils.ClonePrincipal")
  45. if Err.Number then DumpErrAndQuit
  46. ' create the COM object implementing IADsNameTranslate
  47. dim nameTranslate
  48. set nameTranslate = CreateObject("NameTranslate")
  49. if Err.Number then DumpErrAndQuit
  50. ' create the COM object implementing IADsPathname
  51. dim adsPathname
  52. set adsPathname = CreateObject("Pathname")
  53. if Err.Number then DumpErrAndQuit
  54. ' create the COM object implementing IADsError
  55. dim adsError
  56. set adsError = CreateObject("DSUtils.ADsError")
  57. if Err.Number then DumpErrAndQuit
  58. ' create the COM object implementing IADsSID
  59. dim sid
  60. set sid = CreateObject("DSUtils.ADsSID")
  61. if Err.Number then DumpErrAndQuit
  62. '
  63. ' functions and subroutines follow
  64. '
  65. sub CloneSecurityPrincipal(byref srcObject, byval srcSam, byval dstDom, byval dstDC, byval dstSam, byval dstDN)
  66. on error resume next
  67. ' verify that the source object is of a type that we support
  68. dim srcObjectClass
  69. srcObjectClass = ObjectClass(srcObject)
  70. select case srcObjectClass
  71. case CLASS_USER
  72. if srcObject.UserFlags and UF_TEMP_DUPLICATE_ACCOUNT then
  73. Echo "Source object is a temporary local user account, which is not supported."
  74. wscript.quit(0)
  75. end if
  76. case CLASS_LOCAL_GROUP
  77. case CLASS_GLOBAL_GROUP
  78. ' do nothing
  79. case else
  80. ' not a supported object class
  81. Echo "Source object is of type " & srcObject.Class & ", which is not supported by this tool."
  82. wscript.quit(0)
  83. end select
  84. ' bind to the destination object
  85. ' we attempt to locate the destination object by it's sam account name, in
  86. ' order to determine if that name is already in use by a security principal
  87. ' in the destination domain.
  88. dim dstObjectSamPath
  89. dstObjectSamPath = "WinNT://" & dstDom & "/" & dstDC & "/" & dstSam
  90. dim dstObjectDNPath
  91. dstObjectDNPath = "LDAP://" & dstDC & "/" & dstDN
  92. dim dstObjectClass
  93. dim dstObject
  94. Err.Clear
  95. set dstObject = GetObject(dstObjectSamPath)
  96. dim errnum1
  97. errnum1 = Err.Number
  98. select case errnum1
  99. case E_ADS_UNKNOWN_OBJECT
  100. ' destination is not found
  101. Echo "Destination object " & dstSam & " not found (by SAM name) path used: " & dstObjectSamPath
  102. ' bind to the DN of the object, then
  103. Err.Clear
  104. set dstObject = GetObject(dstObjectDNPath)
  105. dim errnum2
  106. errnum2 = Err.Number
  107. select case errnum2
  108. case E_ADS_ERROR_DS_NO_SUCH_OBJECT
  109. Echo "Destination object " & dstDN & " not found (by DN) path used: " & dstObjectDNPath
  110. ' create the dstDN object of the same type as the source
  111. Err.Clear
  112. set dstObject = CreateDestinationDN(dstSam, dstDN, dstDC, srcObjectClass)
  113. case 0
  114. ' dstDN found
  115. Echo "Destination DN found"
  116. dstObjectClass = ObjectClass(dstObject)
  117. if dstObjectClass <> srcObjectClass then
  118. Bail "Source and destination objects differ in class type."
  119. end if
  120. if UCase(dstObject.SamAccountName) <> UCase(dstSam) then
  121. ' sam name of the object is not the same as the sam name
  122. ' specified on the command line
  123. Bail "SAM account name of " & dstDN & " is " & dstObject.SamAccountName & " not " & dstSam
  124. end if
  125. case else
  126. Echo "Error attempting to bind to " & dstObjectDNPath
  127. DumpErrAndQuit
  128. end select
  129. case 0
  130. ' dstSam found. Find the DN of the object it refers to
  131. Echo "Destination SAM name found"
  132. nameTranslate.Init ADS_NAME_INITTYPE_SERVER, dstDC
  133. if Err.Number then DumpErrAndQuit
  134. nameTranslate.Set ADS_NAME_TYPE_NT4, dstDom & "\" & dstSam
  135. if Err.Number then DumpErrAndQuit
  136. dim foundDN
  137. foundDN = nameTranslate.Get(ADS_NAME_TYPE_1779) ' aka full DN
  138. if Err.Number then DumpErrAndQuit
  139. Echo dstSam & " refers to " & foundDN
  140. if UCase(dstDN) <> UCase(foundDN) then
  141. ' sam name is in use by another object than the one the user
  142. ' indicated.
  143. Bail "SAM account name " & dstSam & " is in use by object " & foundDN & ", not " & dstDN
  144. end if
  145. ' at this point, we've verified that the sam name specified by the
  146. ' user matches the DN. Now verify that the DN refers to an object
  147. ' of the same type as the source
  148. set dstObject = GetObject("LDAP://" & dstDC & "/" & foundDN)
  149. if Err.Number then DumpErrAndQuit
  150. dstObjectClass = ObjectClass(dstObject)
  151. if dstObjectClass <> srcObjectClass then
  152. Bail "Source and destination objects differ in class type."
  153. end if
  154. case else
  155. Echo "Error attempting to bind to destination object " & dstObjectSamPath
  156. DumpErrAndQuit
  157. end select
  158. ' at this point, dstObject is bound to the object onto which we
  159. ' should clone the source object
  160. ' copy the source object's properties
  161. Echo "Setting properties for target " & dstObject.Class & " " & dstObject.Name
  162. select case srcObjectClass
  163. case CLASS_USER
  164. ' copy the properties of the source user to the destination user
  165. clonepr.CopyDownlevelUserProperties srcSam, dstSam, 0
  166. if Err.Number then DumpErrAndQuit
  167. Echo "Downlevel properties set."
  168. ' fixup the destination user's group memberships
  169. FixupUserGroupMemberships srcObject, dstObject, dstDC
  170. if Err.Number then DumpErrAndQuit
  171. Echo "User's Group memberships restored."
  172. ' commit the changes
  173. dstObject.SetInfo
  174. if Err.Number then DumpErrAndQuit
  175. Echo "User changes commited."
  176. case CLASS_LOCAL_GROUP
  177. ' copy the source group's description
  178. if srcObject.Description <> "" then
  179. dstObject.Put "Description", srcObject.Description
  180. dstObject.SetInfo
  181. if Err.Number then DumpErrAndQuit
  182. end if
  183. Echo "Local group description set."
  184. ' copy the source local group's membership
  185. CopyLocalGroupMembership srcObject, dstObject
  186. if Err.Number then DumpErrAndQuit
  187. Echo "Local group membership copied."
  188. ' commit the changes
  189. dstObject.SetInfo
  190. if Err.Number then DumpErrAndQuit
  191. Echo "Local group changes commited."
  192. case CLASS_GLOBAL_GROUP
  193. ' copy the source group's description
  194. if srcObject.Description <> "" then
  195. dstObject.Put "Description", srcObject.Description
  196. dstObject.SetInfo
  197. if Err.Number then DumpErrAndQuit
  198. end if
  199. Echo "Global group description set."
  200. ' fixup the destination group's members
  201. FixupGlobalGroupMembers srcObject, dstObject, dstDC
  202. if Err.Number then DumpErrAndQuit
  203. Echo "Global group memberships restored."
  204. ' commit the change
  205. dstObject.SetInfo
  206. if Err.Number then DumpErrAndQuit
  207. Echo "Global group changes commited."
  208. case else
  209. ' why are we here? what is my purpose in life?
  210. wscript "illegal code path"
  211. wscript.quit(0)
  212. end select
  213. ' Add the SID of the source principal to the sid history of the destination
  214. ' principal.
  215. Echo "Adding SID for source " & srcObject.Class & " " & srcObject.Name & " to SID history of target " & dstObject.Class & " " & dstObject.Name
  216. clonepr.AddSidHistory srcSam, dstSam, 0
  217. if Err.Number then DumpErrAndQuit
  218. Echo "SID history set successfully."
  219. ' all done
  220. Echo srcObject.Name & " cloned successfully."
  221. end sub
  222. ' Create a DS security principal object, and return a bound reference to it.
  223. '
  224. ' samName - in, sam account name of object-to-be
  225. '
  226. ' DN - in, full DN of the object to be created
  227. '
  228. ' DC - in, name of domain controller on which the object is to be created
  229. '
  230. ' objectClass - in, CLASS_ constant for the type of object to create
  231. function CreateDestinationDN(byval samName, byval DN, byval DC, byval objectClass)
  232. on error resume next
  233. Echo "Creating " & DN
  234. ' determine the name of the container to place the new object by removing
  235. ' the leaf-most portion of the DN
  236. dim p
  237. p = InStr(1, DN, ",", 1)
  238. dim dstCN
  239. dstCN = Mid(DN, 1, p - 1) ' - 1 to omit the comma
  240. dim ouDN, ouDNPath
  241. ouDN = Mid(DN, p + 1) ' + 1 to skip the comma
  242. ouDNPath = "LDAP://" & DC & "/" & ouDN
  243. dim container, errnum3
  244. set container = GetObject(ouDNPath)
  245. select case Err.Number
  246. case E_ADS_ERROR_DS_NO_SUCH_OBJECT
  247. Bail "Container " & ouDN & " not found"
  248. case 0
  249. ' do nothing
  250. case else
  251. Echo "Error attempting to bind to " & ouDN
  252. DumpErrAndQuit
  253. end select
  254. dim dstObject
  255. set dstObject = container.Create(classNames(objectClass), dstCN)
  256. if Err.Number then
  257. Echo "Error attempting to create " & DN
  258. DumpErrAndQuit
  259. end if
  260. dstObject.Put "samAccountName", samName
  261. if Err.Number then
  262. Echo "Error attempting to set samAccountName for " & DN
  263. DumpErrAndQuit
  264. end if
  265. select case objectClass
  266. case CLASS_USER
  267. ' nothing more to add
  268. case CLASS_LOCAL_GROUP
  269. ' set group type to local
  270. dstObject.Put "groupType", ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP + ADS_GROUP_TYPE_SECURITY_ENABLED
  271. if Err.Number then
  272. Echo "Error attempting to set local group type for " & DN
  273. DumpErrAndQuit
  274. end if
  275. case CLASS_GLOBAL_GROUP
  276. ' set group type to global
  277. dstObject.Put "groupType", ADS_GROUP_TYPE_GLOBAL_GROUP + ADS_GROUP_TYPE_SECURITY_ENABLED
  278. if Err.Number then
  279. Echo "Error attempting to set global group type for " & DN
  280. DumpErrAndQuit
  281. end if
  282. end select
  283. dstObject.SetInfo
  284. if Err.Number then
  285. Echo "Error attempting to commit create of " & DN
  286. DumpErrAndQuit
  287. end if
  288. Echo "Created " & DN
  289. set CreateDestinationDN = dstObject
  290. end function
  291. ' for each group to which the source user object belongs, look for that
  292. ' group's sid in the sid histories of objects in the destination forest
  293. ' (domain?). If found, add the destination user as a member of the located
  294. ' group. Thus, when a user is cloned, the clone becomes a member of all the
  295. ' existing cloned groups corresponding to the original groups the
  296. ' orignal user belonged to.
  297. sub FixupUserGroupMemberships(byref srcObject, byref dstObject, byval dstDC)
  298. on error resume next
  299. Echo "Fixing group memberships for " & dstObject.Class & " " & dstObject.Name
  300. nameTranslate.Init ADS_NAME_INITTYPE_SERVER, dstDC
  301. if Err.Number then DumpErrAndQuit
  302. dim group
  303. dim sidString
  304. for each group in srcObject.Groups
  305. if (ObjectClass(group) = CLASS_GLOBAL_GROUP) then
  306. Echo " Found global group " & group.ADsPath
  307. sid.SetAs ADS_SID_WINNT_PATH, group.AdsPath & "," & group.Class
  308. if Err.Number then DumpErrAndQuit
  309. sidString = sid.GetAs(ADS_SID_SDDL)
  310. if Err.Number then DumpErrAndQuit
  311. if IsBuiltInSid(sidString) then
  312. Echo " " & group.ADsPath & " is a built-in group"
  313. ' built-ins are present in every domain with the same sid. So we
  314. ' can't search for the corresponding destination object by sid, or
  315. ' we may be multiple matches (if there is more than 1 domain in the
  316. ' destination forest, and the destination DC also happens to be
  317. ' a global catalog). So, here we compose a sid-style LDAP path
  318. ' for the built-in destination object.
  319. sidString = "<sid=" & sid.GetAs(ADS_SID_HEXSTRING) & ">"
  320. if Err.Number then DumpErrAndQuit
  321. dim mypath
  322. mypath = "LDAP://" & dstDC & "/" & sidString
  323. dim mygroup
  324. set mygroup = GetObject(mypath)
  325. if Err.Number then DumpErrAndQuit
  326. if not IsUserMemberOfGroup(mygroup, dstObject) then
  327. Echo " Adding " & dstObject.Name & " to group " & mygroup.Name
  328. mygroup.Add dstObject.AdsPath
  329. else
  330. Echo " " & dstObject.Name & " is already member of " & mygroup.Name
  331. end if
  332. if Err.Number then DumpErrAndQuit
  333. else
  334. ' find the DN of the object with that sid as its object sid or in
  335. ' its sid history (the sid history is where it will be, if the object
  336. ' is a clone).
  337. nameTranslate.Set ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, sidString
  338. select case Err.Number
  339. case E_ADS_ERROR_DS_NAME_NOT_FOUND
  340. ' do nothing: skip this member; it hasn't been cloned yet
  341. Echo " Skipping " & group.ADsPath & " -- not cloned yet"
  342. case 0
  343. ' found!
  344. dim foundDN
  345. foundDN = ""
  346. foundDN = nameTranslate.Get(ADS_NAME_TYPE_1779) ' aka full DN
  347. select case Err.Number
  348. case E_ADS_ERROR_DS_NAME_NOT_FOUND
  349. ' do nothing: skip this member; it hasn't been cloned yet
  350. case 0
  351. AddUserToGroup dstObject, foundDN, dstDC
  352. case else
  353. DumpErrAndQuit
  354. end select
  355. case else
  356. DumpErrAndQuit
  357. end select
  358. end if
  359. else
  360. Echo " Skipping group " & group.AdsPath & " -- not global group"
  361. end if
  362. ' need to clear this so next iteration won't choke.
  363. Err.Clear
  364. next
  365. end sub
  366. ' for each member of the source local group, obtain the member's SID and add
  367. ' that SID as a member of the destination local group. If that SID does not
  368. ' refer to a security principal in the destination domain, then the SAM will
  369. ' create a Foreign Principal Object (FPO) to represent that SID. then SAM
  370. ' will replace the reference to the SID in the group membership with the DN
  371. ' of the FPO. An FPO acts like a proxy for the SID.
  372. sub CopyLocalGroupMembership(byref srcObject, byref dstObject)
  373. on error resume next
  374. Echo "Copying local group membership"
  375. ' get the sids in string form of each of the members of the source
  376. ' group. collect them in an array
  377. dim member
  378. dim sidString
  379. dim sidStringArray()
  380. dim i
  381. i = 0
  382. dim dn
  383. dn = dstObject.Get("distinguishedName")
  384. if Err.Number then DumpErrAndQuit
  385. Echo " Getting destination group membership as SIDs"
  386. dim dstExistingMemberSIDs
  387. dstExistingMemberSIDs = clonepr.GetMembersSIDs(dn)
  388. if Err.Number then DumpErrAndQuit
  389. dim numExistingMembers
  390. numExistingMembers = 0
  391. dim x
  392. for each x in dstExistingMemberSIDs
  393. numExistingMembers = numExistingMembers + 1
  394. next
  395. for each member in srcObject.Members
  396. dim sidDeletedAccount
  397. if IsDeletedAccount(member.AdsPath, sidDeletedAccount) then
  398. Echo " Considering deleted account: " & sidDeletedAccount
  399. sid.SetAs ADS_SID_SDDL, sidDeletedAccount
  400. else
  401. Echo " Considering normal account: " & member.AdsPath
  402. sid.SetAs ADS_SID_WINNT_PATH, member.AdsPath & "," & member.Class
  403. end if
  404. if Err.Number then DumpErrAndQuit
  405. sidString = "<sid=" & sid.GetAs(ADS_SID_HEXSTRING) & ">"
  406. if Err.Number then DumpErrAndQuit
  407. if (0 = numExistingMembers) Or (not SidStringExists(sidString, dstExistingMemberSIDs)) then
  408. Echo " Adding " & sidString
  409. redim preserve sidStringArray(i)
  410. sidStringArray(i) = sidString
  411. i = i + 1
  412. end if
  413. next
  414. ' use the array to update the destination group in one whack.
  415. if i then
  416. if 0 = numExistingMembers then
  417. dstObject.PutEx ADS_PROPERTY_UPDATE, "member", sidStringArray
  418. else
  419. dstObject.PutEx ADS_PROPERTY_APPEND, "member", sidStringArray
  420. end if
  421. if Err.Number then DumpErrAndQuit
  422. dstObject.SetInfo
  423. if Err.Number then DumpErrAndQuit
  424. end if
  425. end sub
  426. function IsDeletedAccount(byref AdsPath, byref sidDeletedAccount)
  427. dim pos0, pos1
  428. pos0 = InStr(1, AdsPath, "://", 1)
  429. pos1 = InStr(pos0 + 3, AdsPath, "/", 1)
  430. if 0 = pos1 then
  431. IsDeletedAccount = True
  432. sidDeletedAccount = Mid(AdsPath, pos0 + 3)
  433. else
  434. IsDeletedAccount = False
  435. end if
  436. end function
  437. function SidStringExists(byref sidString, byref dstExistingMemberSIDs)
  438. dim sid
  439. sid = UCase(sidString)
  440. SidStringExists = False
  441. dim x
  442. For each x in dstExistingMemberSIDs
  443. if UCase(x) = sid then
  444. Echo " Skipping existing sid " & x
  445. SidStringExists = True
  446. exit function
  447. end if
  448. next
  449. end function
  450. ' for each member of the source global group, look for that member's sid in
  451. ' the sid histories of objects the destination forest (domain?). If found,
  452. ' add that located object as a member of the destination group. Thus,
  453. ' when a global group is cloned, the existing clones of all users that belong
  454. ' to the original group will belong to the cloned group.
  455. sub FixupGlobalGroupMembers(byref srcObject, byref dstObject, byval dstDC)
  456. on error resume next
  457. Echo "Fixing group membership for " & dstObject.Class & " " & dstObject.Name
  458. nameTranslate.Init ADS_NAME_INITTYPE_SERVER, dstDC
  459. if Err.Number then DumpErrAndQuit
  460. dim member
  461. dim sidString
  462. for each member in srcObject.Members
  463. if member.UserFlags and UF_NORMAL_ACCOUNT then
  464. ' extract the sid of the account
  465. sid.SetAs ADS_SID_WINNT_PATH, member.AdsPath & "," & member.Class
  466. if Err.Number then DumpErrAndQuit
  467. sidString = sid.GetAs(ADS_SID_SDDL)
  468. if Err.Number then DumpErrAndQuit
  469. ' find the DN of the member with that sid as its object sid or in
  470. ' its sid history (the sid history is where it will be, if the member
  471. ' is a clone).
  472. nameTranslate.Set ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, sidString
  473. select case Err.Number
  474. case E_ADS_ERROR_DS_NAME_NOT_FOUND
  475. ' do nothing: skip this member; it hasn't been cloned yet
  476. case 0
  477. ' found!
  478. dim foundDN
  479. foundDN = ""
  480. foundDN = nameTranslate.Get(ADS_NAME_TYPE_1779) ' aka full DN
  481. select case Err.Number
  482. case E_ADS_ERROR_DS_NAME_NOT_FOUND
  483. ' do nothing: skip this member; it hasn't been cloned yet
  484. case 0
  485. ' add the dn to the members property of the dst object
  486. dim path
  487. path = "LDAP://" & dstDC & "/" & foundDN
  488. Dim tempObj
  489. set tempObj = GetObject(path)
  490. if Err.Number then DumpErrAndQuit
  491. if NOT IsUserMemberOfGroup( dstObject, tempObj ) then
  492. Echo " adding " & foundDN & " to group " & dstObject.Name
  493. dstObject.Add path
  494. end if
  495. if Err.Number then DumpErrAndQuit
  496. case else
  497. DumpErrAndQuit
  498. end select
  499. case else
  500. DumpErrAndQuit
  501. end select
  502. ' need to clear this so the next iteration doesn't choke
  503. Err.Clear
  504. else
  505. ' skip computer, temp and trust accounts
  506. Echo " Skipping non-user account " & member.Name
  507. end if
  508. next
  509. end sub
  510. ' user - in, reference to user object, bound with LDAP provider.
  511. '
  512. ' groupDN - in, full DN of the group to which the user is to be added
  513. '
  514. ' dstDC - in, name of destination domain controller
  515. sub AddUserToGroup(byref user, byval groupDN, byval dstDC)
  516. on error resume next
  517. dim path
  518. path = "LDAP://" & dstDC & "/" & groupDN
  519. dim group
  520. set group = GetObject(path)
  521. if Err.Number then DumpErrAndQuit
  522. if not IsUserMemberOfGroup(group,user) then
  523. Echo " Adding " & user.Name & " to group " & group.Name
  524. group.Add user.AdsPath
  525. else
  526. Echo " " & user.Name & " is already member of " & group.Name
  527. end if
  528. if Err.Number then DumpErrAndQuit
  529. end sub
  530. function IsUserMemberOfGroup( byref group, byref user )
  531. if group.IsMember(user.AdsPath) then
  532. IsUserMemberOfGroup = True
  533. exit function
  534. end if
  535. sid.SetAs ADS_SID_ACTIVE_DIRECTORY_PATH, group.AdsPath
  536. if Err.Number then DumpErrAndQuit
  537. dim sidString
  538. sidString = sid.GetAs(ADS_SID_SDDL)
  539. if Err.Number then DumpErrAndQuit
  540. if Len(sidString) > 9 then
  541. dim lastDash
  542. lastDash = InStrRev(sidString, "-", -1, 1)
  543. if lastDash then
  544. dim ridString
  545. ridString = Mid(sidString, lastDash + 1)
  546. if StrComp(ridString,user.PrimaryGroupId,1) = 0 then
  547. IsUserMemberOfGroup = True
  548. exit function
  549. end if
  550. end if
  551. end if
  552. IsUserMemberOfGroup = False
  553. end function
  554. ' based on the class of the object, return one of CLASS_USER,
  555. ' CLASS_LOCAL_GROUP, CLASS_GLOBAL_GROUP, CLASS_OTHER
  556. function ObjectClass(object)
  557. dim cls
  558. cls = UCase(object.Class)
  559. if cls = "GROUP" then
  560. if (object.GroupType and ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP) then
  561. ' type is local group
  562. ObjectClass = CLASS_LOCAL_GROUP
  563. exit function
  564. else
  565. if ((object.GroupType and ADS_GROUP_TYPE_GLOBAL_GROUP) or (object.GroupType and ADS_GROUP_TYPE_UNIVERSAL_GROUP)) then
  566. ' type is global group
  567. ObjectClass = CLASS_GLOBAL_GROUP
  568. exit function
  569. end if
  570. end if
  571. else
  572. if cls = "USER" then
  573. ' type is user
  574. ObjectClass = CLASS_USER
  575. exit function
  576. end if
  577. end if
  578. ' type is not recognized
  579. ObjectClass = CLASS_OTHER
  580. exit function
  581. end function
  582. ' returns non-zero if the stringized SID refers to a well-known rid, zero
  583. ' otherwise
  584. function HasWellKnownRid(byval sidString)
  585. ' a SID refers to a well-known account if the first sub-authority (aka
  586. ' RID) is < 1000. The first subauthority is the last portion of the
  587. ' stringized SID
  588. if Len(sidString) > 9 then
  589. dim lastDash
  590. lastDash = InStrRev(sidString, "-", -1, 1)
  591. if lastDash then
  592. dim ridString
  593. ridString = Mid(sidString, lastDash + 1)
  594. if CLng(ridString) < 1000 then
  595. HasWellKnownRid = True
  596. exit function
  597. end if
  598. end if
  599. end if
  600. HasWellKnownRid = False
  601. end function
  602. ' returns non-zero if the stringized SID refers to a builtin sid, zero
  603. ' otherwise
  604. function IsBuiltInSid(byval sidString)
  605. ' a SID refers to builtin account or group if it has prefix S-1-5-32-
  606. if Len(sidString) > 9 then
  607. dim prefixString
  608. prefixString = Mid(sidString, 1, 9)
  609. if StrComp( prefixString, "S-1-5-32-", 1 ) = 0 then
  610. IsBuiltInSid = true
  611. exit function
  612. end if
  613. end if
  614. IsBuiltInSid = False
  615. end function
  616. ' searches for and returns the value of a command line argument of the form
  617. ' /argName:value from the supplied array. erases the entry in the array so
  618. ' that only untouched entries remain.
  619. function GetArgValue(argName, args())
  620. dim a
  621. dim v
  622. dim argNameLength
  623. dim x
  624. dim argCount
  625. dim fullArgName
  626. fullArgName = "/" & argName & ":"
  627. argCount = Ubound(args)
  628. ' Get the length of the argname we are looking for
  629. argNameLength = Len(fullArgName)
  630. GetArgValue = "" ' default to nothing
  631. for x = 0 To argCount
  632. if Len(args(x)) >= argNameLength then
  633. a = Mid(args(x), 1, argNameLength)
  634. if UCase(a) = UCase(fullArgName) then
  635. ' erase it so we can look for unknown args later
  636. v = args(x)
  637. args(x) = ""
  638. if Len(v) > argNameLength then
  639. GetArgValue = Mid(v, argNameLength + 1)
  640. exit function
  641. else
  642. GetArgValue = ""
  643. exit function
  644. end if
  645. end if
  646. end if
  647. next
  648. end function
  649. ' walks thru the array searching for any non-empty element. if at least one
  650. ' is found, then return non-zero. Otherwise return 0.
  651. function CheckForBadArgs(byref args())
  652. dim i
  653. for i = 0 to UBound(args)
  654. if Len(args(i)) > 0 then
  655. CheckForBadArgs = 1
  656. exit function
  657. end if
  658. next
  659. CheckForBadArgs = 0
  660. end function
  661. sub DumpErrAndQuit
  662. dim errnum
  663. errnum = Err.Number
  664. Echo "Error 0x" & CStr(Hex(errnum)) & " occurred."
  665. if len(Err.Description) then
  666. Echo "Error Description: " & Err.Description
  667. end if
  668. if len(Err.Source) then
  669. Echo "Error Source : " & Err.Source
  670. end if
  671. Echo "ADsError Description: "
  672. Echo adsError.GetErrorMsg(errnum)
  673. wscript.quit(0)
  674. end sub
  675. sub Bail(byref message)
  676. Echo "Error: " & message
  677. wscript.quit(0)
  678. end sub
  679. sub Echo(byref message)
  680. wscript.echo message
  681. end sub
  682. ' clonepr.vbi end