Guest User

Untitled

a guest
Apr 9th, 2018
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.31 KB | None | 0 0
  1. #!/usr/bin/python
  2. #
  3. #
  4.  
  5. """Demotes a Mobile Account to a standalone account."""
  6.  
  7.  
  8. import os
  9. import shutil
  10. import subprocess
  11. import sys
  12. import plistlib
  13. import xml
  14.  
  15.  
  16. class ConvertMobileError(Exception):
  17. """Module specific Exception."""
  18. pass
  19.  
  20.  
  21. def _ConvertPlistToXML(plist):
  22. """Converts a plist to the xml1 format.
  23.  
  24. Args:
  25. plist: the property list to be converted.
  26. Raises:
  27. ConvertMobileError: plist does not exist.
  28. ConvertMobileError: Error converting.
  29. """
  30. if not os.path.isfile(plist):
  31. raise ConvertMobileError("%s does not exist" % plist)
  32. command = ["/usr/bin/plutil", "-convert", "xml1", plist]
  33. task = subprocess.Popen(command, stdout=subprocess.PIPE,
  34. stderr=subprocess.PIPE)
  35. (unused_stdout, stderr) = task.communicate()
  36. if task.returncode:
  37. raise ConvertMobileError("Error converting %s : %s" % (plist, stderr))
  38.  
  39.  
  40. def _ReadPlist(plist):
  41. """Reads a plist, converting to xml1 if necessary.
  42.  
  43. Args:
  44. plist: the property list to be read/converted.
  45. Raises:
  46. ConvertMobileError: plist is not a file
  47. Returns:
  48. plist_dict: a dictionary object with the plist contents.
  49. """
  50. if not os.path.isfile(plist):
  51. raise ConvertMobileError("%s is not a file" % plist)
  52. try:
  53. plist_dict = plistlib.readPlist(plist)
  54. except xml.parsers.expat.ExpatError:
  55. _ConvertPlistToXML(plist)
  56. plist_dict = plistlib.readPlist(plist)
  57. return plist_dict
  58.  
  59.  
  60. def _VerifyMacOSXVersion():
  61. """Checks the Mac OS X version is 10.5.
  62.  
  63. Returns:
  64. boolean: whether this is OS X 10.5
  65. """
  66. sysvers_plist = "/System/Library/CoreServices/SystemVersion.plist"
  67. plist = _ReadPlist(sysvers_plist)
  68.  
  69. if plist["ProductVersion"].startswith("10.5"):
  70. return True
  71. else:
  72. return False
  73.  
  74.  
  75. def _CheckUserIsLocal(user):
  76. """Checks whether a given account exists in the local domain.
  77.  
  78. Args:
  79. user: the username to check
  80. Returns:
  81. boolean: whether an account exists for that user.
  82. """
  83. command = ["/usr/bin/dscl", ".", "-read", "/Users/%s" % user, "UniqueID"]
  84. if subprocess.call(command, stdout=subprocess.PIPE):
  85. return False
  86. else:
  87. return True
  88.  
  89.  
  90. def _GenerateUUID():
  91. """generates a uuid.
  92.  
  93. Returns:
  94. uuid: the uuid
  95. Raises:
  96. ConvertMobileError: Unable to generate uuid.
  97. """
  98. task = subprocess.Popen(["/usr/bin/uuidgen"], stdout=subprocess.PIPE,
  99. stderr=subprocess.PIPE)
  100. (stdout, stderr) = task.communicate()
  101. if task.returncode:
  102. raise ConvertMobileError("Cannot generate UUID: %s" % stderr)
  103.  
  104. return str(stdout).strip()
  105.  
  106.  
  107. def _EnsureAccountIsNotMobile(user):
  108. """Ensures a given user account is not a Mobile Account.
  109.  
  110. Strips attributes if necessary.
  111.  
  112. Args:
  113. user: the short name of the user account.
  114. """
  115. user_plist = "/var/db/dslocal/nodes/Default/users/%s.plist" % user
  116. account = _ReadPlist(user_plist)
  117.  
  118. if "account_instance" in account:
  119. uuid = account["account_instance"][0]
  120. elif "generateduid" in account:
  121. uuid = account["generateduid"][0]
  122. else:
  123. uuid = _GenerateUUID()
  124.  
  125. mobile_attrs = ["account_instance", "cached_groups", "copy_timestamp",
  126. "original_home", "original_node_name", "original_passwd",
  127. "original_realname", "original_shell",
  128. "preserved_attributes", "mcx_flags", "mcx_settings"]
  129.  
  130. for attr in mobile_attrs:
  131. if attr in account:
  132. del account[attr]
  133.  
  134. account["gid"] = [str(20)] # 'staff' group.
  135. account["generateduid"] = [uuid]
  136.  
  137. new_auth_authority = []
  138. new_auth_authority.append(";ShadowHash;")
  139.  
  140. for authority in account["authentication_authority"]:
  141. if authority.startswith(";Kerberosv5;"): # keep their LKDC authority
  142. new_auth_authority.append(authority)
  143.  
  144. account["authentication_authority"] = new_auth_authority
  145.  
  146. # write out new account to the user plist.
  147. plistlib.writePlist(account, user_plist)
  148.  
  149. # move the shadow hash file to the proper location
  150. old_hash = "/var/db/shadow/hash/%s" % user
  151. new_hash = "/var/db/shadow/hash/%s" % uuid
  152.  
  153. if not os.path.isfile(old_hash) and not os.path.isfile(new_hash):
  154. print "No password hashes at: %s or %s" % (old_hash, new_hash)
  155. print "You should run the following command now to set a password:"
  156. print ""
  157. print "dscl . -passwd /Users/%s" % user
  158. print ""
  159. if os.path.isfile(old_hash) and not os.path.isfile(new_hash):
  160. shutil.move(old_hash, new_hash)
  161.  
  162.  
  163. def _RestartDirectoryServices():
  164. """Restarts DirectoryServices process."""
  165. command = ["/usr/bin/killall", "-TERM", "DirectoryService"]
  166. if subprocess.call(command, stdout=subprocess.PIPE):
  167. raise ConvertMobileError("Problem killing DirectoryService.")
  168.  
  169.  
  170. def main():
  171. try:
  172. user = sys.argv[1]
  173. except IndexError:
  174. raise ConvertMobileError("You must pass the username as the only arg.")
  175.  
  176. if os.getuid() is not 0:
  177. raise ConvertMobileError("This script must be run as root.")
  178.  
  179. if not _VerifyMacOSXVersion():
  180. raise ConvertMobileError("This script must be run on Mac OS X 10.5.")
  181.  
  182. if not _CheckUserIsLocal(user):
  183. raise ConvertMobileError("User: %s does not exist locally." % user)
  184.  
  185. _EnsureAccountIsNotMobile(user)
  186. _RestartDirectoryServices()
  187.  
  188. if __name__ == "__main__":
  189. main()
Add Comment
Please, Sign In to add comment