def bin2str(token_bplist, account_bplist=None): # Convert the decrypted binary plist to an NSData object that can be read. bin_list = NSData.dataWithBytes_length_(token_bplist, len(token_bplist)) # Convert the binary NSData object into a dictionary object. token_plist = NSPropertyListSerialization.propertyListWithData_options_format_error_( bin_list, 0, None, None)[0] # Accounts DB cache if "$objects" in token_plist: # Because it is accounts db cache, we should also have been passed # account_bplist. bin_list = NSData.dataWithBytes_length_(account_bplist, len(account_bplist)) dsid_plist = NSPropertyListSerialization.propertyListWithData_options_format_error_( bin_list, 0, None, None)[0] for obj in dsid_plist["$objects"]: if "{}".format(obj).startswith("urn:ds:"): dsid = obj.replace("urn:ds:", "") token_dict = {"dsid": dsid} # Do some parsing to get the data out because it is not stored # in a format that is easy to process with stdlibs token_l = [ x.strip().replace(",", "") for x in "{}".format(token_plist["$objects"]).splitlines() ] pos_start = token_l.index("mmeBTMMInfiniteToken") pos_end = (token_l.index("cloudKitToken") - pos_start + 1) * 2 token_short = token_l[pos_start:pos_start + pos_end] zipped = zip(token_short[:len(token_short) / 2], token_short[len(token_short) / 2:]) for token_type, token_value in zipped: # Attempt to get generation time # this parsing is a little hacky, but it seems to be the best way # to handle all different kinds of iCloud tokens (new and old) gen_time = get_generation_time(token_value) token_dict[token_type] = (token_value, gen_time) return token_dict else: return token_plist
def writeBytes_(self, data): # NSLog(u'Writing bytes: %s', data) try: self.remoteFileHandle.writeData_( NSData.dataWithBytes_length_(data, len(data))) except objc.error: self.close()
def value(self, data): if not self.properties["write"]: return # data needs to be byte array if not isinstance(data, bytearray): raise TypeError("data needs to be a bytearray") rawdata = NSData.dataWithBytes_length_(data, len(data)) self.service.peripheral.writeValueForCharacteristic(rawdata, self.instance)
def nsdata(obj): """Convert ``object`` to `NSData`.""" if isinstance(obj, unicode): s = obj.encode('utf-8') else: s = str(obj) return NSData.dataWithBytes_length_(s, len(s))
def nsdata(obj): """Convert ``object`` to `NSData`.""" if isinstance(obj, unicode): s = obj.encode('utf-8') else: s = str(obj) return NSData.dataWithBytes_length_(s, len(s))
def nsdata(s): """Return an NSData instance for string `s`.""" if isinstance(s, unicode): s = s.encode('utf-8') else: s = str(s) return NSData.dataWithBytes_length_(s, len(s))
def value_async_nr(self, data): if not self.properties["write"]: return # data needs to be byte array if not isinstance(data, bytearray) and not isinstance(data, bytes): raise TypeError("data needs to be a bytearray or bytes") rawdata = NSData.dataWithBytes_length_(data, len(data)) self.service.peripheral.writeValueForCharacteristicAsync( rawdata, self.instance, withResponse=False)
def decode_bdisk(key): """Return the relative path of file referenced by key """ decodedBytes = base64.b64decode(key) nsData = NSData.dataWithBytes_length_(decodedBytes, len(decodedBytes)) plist, fmt, error = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_( nsData, 0, None, None) if plist is None: return "Missed" else: return plist['relativePath']
def __ns_data_from_array(a): from Foundation import NSData a = np.asarray(a).flatten() if a.dtype == np.uint8 or a.dtype == np.int8: datasize = 1 elif a.dtype == np.uint16 or a.dtype == np.int16: datasize = 2 elif a.dtype == np.float32: datasize = 4 else: assert 0, "unhandled data type %s" % (a.dtype) buf = buffer(a) return datasize, NSData.dataWithBytes_length_(buf, len(buf) * datasize)
def getStoredHeaders(self): '''Returns any stored headers for self.destination_path''' # try to read stored headers try: stored_plist_bytestr = xattr.getxattr(self.destination_path, self.GURL_XATTR) except (KeyError, IOError): return {} data = NSData.dataWithBytes_length_(stored_plist_bytestr, len(stored_plist_bytestr)) dataObject, _plistFormat, error = ( NSPropertyListSerialization. propertyListFromData_mutabilityOption_format_errorDescription_( data, NSPropertyListMutableContainersAndLeaves, None, None)) if error: return {} return dataObject
def readPlistFromString(data): '''Read a plist data from a (byte)string. Return the root object.''' plistData = NSData.dataWithBytes_length_(data, len(data)) if not plistData: raise NSPropertyListSerializationException( "Could not convert string to NSData") dataObject, dummy_plistFormat, error = ( NSPropertyListSerialization. propertyListFromData_mutabilityOption_format_errorDescription_( plistData, NSPropertyListMutableContainers, None, None)) if dataObject is None: if error: error = error.encode('ascii', 'ignore') else: error = "Unknown error" raise NSPropertyListSerializationException(error) else: return dataObject
def start(self): '''Start the connection''' if not self.destination_path: self.log('No output file specified.') self.done = True return url = NSURL.URLWithString_(self.url) request = ( NSMutableURLRequest.requestWithURL_cachePolicy_timeoutInterval_( url, NSURLRequestReloadIgnoringLocalCacheData, self.connection_timeout)) if self.post_data: request.setHTTPMethod_('POST') data_unicode = unicode(self.post_data) data = NSData.dataWithBytes_length_( NSString.stringWithString_(data_unicode).UTF8String(), len(data_unicode.encode('utf-8'))) request.setHTTPBody_(data) if self.additional_headers: for header, value in self.additional_headers.items(): request.setValue_forHTTPHeaderField_(value, header) # does the file already exist? See if we can resume a partial download if os.path.isfile(self.destination_path): stored_data = self.get_stored_headers() if (self.can_resume and 'expected-length' in stored_data and ('last-modified' in stored_data or 'etag' in stored_data)): # we have a partial file and we're allowed to resume self.resume = True local_filesize = os.path.getsize(self.destination_path) byte_range = 'bytes=%s-' % local_filesize request.setValue_forHTTPHeaderField_(byte_range, 'Range') if self.download_only_if_changed and not self.resume: stored_data = self.cache_data or self.get_stored_headers() if 'last-modified' in stored_data: request.setValue_forHTTPHeaderField_( stored_data['last-modified'], 'if-modified-since') if 'etag' in stored_data: request.setValue_forHTTPHeaderField_(stored_data['etag'], 'if-none-match') self.connection = NSURLConnection.alloc().initWithRequest_delegate_( request, self)
def start(self): '''Start the connection''' if not self.destination_path: self.log('No output file specified.') self.done = True return url = NSURL.URLWithString_(self.url) request = ( NSMutableURLRequest.requestWithURL_cachePolicy_timeoutInterval_( url, NSURLRequestReloadIgnoringLocalCacheData, self.connection_timeout)) if self.post_data: request.setHTTPMethod_('POST') data_unicode = unicode(self.post_data) data = NSData.dataWithBytes_length_(NSString.stringWithString_(data_unicode).UTF8String(), len(data_unicode.encode('utf-8'))) request.setHTTPBody_(data) if self.additional_headers: for header, value in self.additional_headers.items(): request.setValue_forHTTPHeaderField_(value, header) # does the file already exist? See if we can resume a partial download if os.path.isfile(self.destination_path): stored_data = self.get_stored_headers() if (self.can_resume and 'expected-length' in stored_data and ('last-modified' in stored_data or 'etag' in stored_data)): # we have a partial file and we're allowed to resume self.resume = True local_filesize = os.path.getsize(self.destination_path) byte_range = 'bytes=%s-' % local_filesize request.setValue_forHTTPHeaderField_(byte_range, 'Range') if self.download_only_if_changed and not self.resume: stored_data = self.cache_data or self.get_stored_headers() if 'last-modified' in stored_data: request.setValue_forHTTPHeaderField_( stored_data['last-modified'], 'if-modified-since') if 'etag' in stored_data: request.setValue_forHTTPHeaderField_( stored_data['etag'], 'if-none-match') self.connection = NSURLConnection.alloc().initWithRequest_delegate_( request, self)
def start(self): """Start the connection.""" url = NSURL.URLWithString_(self.url) request = NSMutableURLRequest.requestWithURL_cachePolicy_timeoutInterval_( url, NSURLRequestReloadIgnoringLocalCacheData, self.connection_timeout) if self.additional_headers: for header, value in self.additional_headers.items(): request.setValue_forHTTPHeaderField_(value, header) request.setHTTPMethod_(self.method) if self.method == "POST": body_unicode = unicode(self.body) body_data = NSData.dataWithBytes_length_( NSString.stringWithString_(body_unicode).UTF8String(), len(body_unicode.encode("utf-8")), ) request.setHTTPBody_(body_data) self.connection = NSURLConnection.alloc().initWithRequest_delegate_( request, self)
def unplist(s): from Foundation import NSData, NSPropertyListSerialization d = NSData.dataWithBytes_length_(s, len(s)) return NSPropertyListSerialization.propertyListWithData_options_format_error_( d, 0, None, None)
sys.exit() msg = base64.b64decode(icloud_key) key = "t9s\"lx^awe.580Gj%'ld+0LG<#9xa?>vb)-fkwb92[}" # Constant key used for hashing Hmac on all versions of macOS. # Create Hmac with this key and icloud_key using MD5 hashed = hmac.new(key, msg, digestmod=hashlib.md5).digest() hexed_key = binascii.hexlify(hashed) # Turn into hex for openssl subprocess IV = 16 * '0' mme_token_file = glob.glob("%s/Library/Application Support/iCloud/Accounts/*" % os.path.expanduser("~")) for x in mme_token_file: try: int(x.split("/")[-1]) # If we can cast to int we have the DSID / account file. mme_token_file = x except ValueError: continue if not isinstance(mme_token_file, str): print "Failed to find MMeTokenFile." sys.exit() # Perform decryption with zero dependencies by using openssl binary decrypted_binary = subprocess.check_output( "openssl enc -d -aes-128-cbc -iv '%s' -K %s < '%s'" % (IV, hexed_key, mme_token_file), shell=True) # Convert the decrypted binary plist to an NSData object that can be read bin_to_plist = NSData.dataWithBytes_length_(decrypted_binary, len(decrypted_binary)) # Convert the binary NSData object into a dictionary object token_plist = NSPropertyListSerialization.propertyListWithData_options_format_error_(bin_to_plist, 0, None, None)[0] print_tokens_json(token_plist)
def get_input(): try: while True: line = sys.stdin.readline() if not line: break if line == '': continue print "info:Read %s" % line sys.stdout.flush() cfg = None try: cfg = json.loads(line) except: pass if cfg == None: print "info:Unable to parse line" sys.stdout.flush() continue if cfg.has_key("Action"): print "info:Running %s" % cfg["Action"] sys.stdout.flush() if cfg["Action"] == "setmenu": menu = cfg["Menu"] if lookup.has_key(menu["Key"]): lookup[menu["Key"]].title = menu["Text"] if menu.has_key("Enabled") and not menu["Enabled"]: lookup[menu["Key"]].set_callback(None) else: lookup[menu["Key"]].set_callback(item_clicked) app.menu.update([]) else: print "warn:Key not found %s" % cfg["Action"] sys.stdout.flush() elif cfg["Action"] == "setmenus": app.menu.clear() app.menu = parsemenus(cfg) print "info:Updated menus" sys.stdout.flush() elif cfg["Action"] == "seticon": try: raw = base64.b64decode(cfg["Image"]) data = NSData.dataWithBytes_length_(raw, len(raw)) img = NSImage.alloc().initWithData_(data) img.setScalesWhenResized_(True) img.setSize_((18, 18)) img.setTemplate_(True) app.icon = img print "info:Image updated" sys.stdout.flush() except: print "warn:Failed to set image" sys.stdout.flush() elif cfg["Action"] == "setappicon": try: raw = base64.b64decode(cfg["Image"]) data = NSData.dataWithBytes_length_(raw, len(raw)) img = NSImage.alloc().initWithData_(data) #img.setScalesWhenResized_(True) #img.setSize_((21, 21)) NSApplication.sharedApplication().setApplicationIconImage_(img) print "info:AppImage updated" sys.stdout.flush() except: print "warn:Failed to set image" sys.stdout.flush() elif cfg["Action"] == "shutdown": break elif cfg["Action"] == "foreground": transform_app_type(True) elif cfg["Action"] == "background": transform_app_type(False) elif cfg["Action"] == "notification": if rumps._NOTIFICATIONS: title = cfg["Title"] message = cfg["Message"] subtitle = '' playSound = True image = None if cfg.has_key("SubTitle"): subtitle = cfg["SubTitle"] if cfg.has_key("PlaySound"): playSound = cfg["PlaySound"] if cfg.has_key("Image"): try: raw = base64.b64decode(cfg["Image"]) data = NSData.dataWithBytes_length_(raw, len(raw)) image = NSImage.alloc().initWithData_(data) except: print "warn:Failed to decode image" sys.stdout.flush() rumps.notification(cfg["Title"], subtitle, cfg["Message"], sound=playSound, image=image) finally: try: traceback.print_exc() except: pass rumps.quit_application() print "info:Shutdown" sys.stdout.flush() print "info:Stdin close" sys.stdout.flush() sys.stdin.close()
def main(): parser = argparse.ArgumentParser( description= 'Sign or encrypt mobileconfig profiles, using either a cert + key file, or a keychain certificate.' ) parser.add_argument( 'sign', choices=('sign', 'encrypt', 'both'), help='Choose to sign, encrypt, or do both on a profile.') key_group = parser.add_argument_group( 'Keychain arguments', description='Use these if you wish to sign with a Keychain certificate.' ) key_group.add_argument( '-k', '--keychain', help='Name of keychain to search for cert. Defaults to login.keychain', default='login.keychain') key_group.add_argument( '-n', '--name', help='Common name of certificate to use from keychain.', required=True) parser.add_argument('infile', help='Path to input .mobileconfig file') parser.add_argument( 'outfile', help= 'Path to output .mobileconfig file. Defaults to outputting into the same directory.' ) args = parser.parse_args() print "f**k yeah" if args.sign == 'encrypt' or args.sign == 'both': # encrypt the profile only, do not sign # Encrypting a profile: # 1. Extract payload content into its own file # 2. Serial that file as its own plist # 3. CMS-envelope that content (using openssl for now) # 4. Remove "PayloadContent" key and replace with "EncryptedPayloadContent" key # 5. Replace the PayloadContent <array> with <data> tags instead # Step 1: Extract payload content into its own file myProfile = plistlib.readPlist(args.infile) payloadContent = myProfile['PayloadContent'] # Step 2: Serialize that file into its own plist (pContentFile, pContentPath) = tempfile.mkstemp() plistlib.writePlist(payloadContent, pContentPath) # Step 3: Use openssl to encrypt that content # First, we need to extract the certificate we want to use from the keychain security_cmd = [ '/usr/bin/security', 'find-certificate', '-c', args.name, '-p' ] if args.keychain: security_cmd += [args.keychain] print security_cmd proc = subprocess.Popen(security_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, "Error: %s" % serr sys.exit(1) # Now write the certificate to a temp file (certfile, certpath) = tempfile.mkstemp('.pem') try: with open(certpath, 'wb') as f: f.write(sout) except IOError: print >> sys.stderr, "Could not write to file!" sys.exit(1) # Now use openssl to encrypt the payload content using that certificate (encPContentfile, encPContentPath) = tempfile.mkstemp('.plist') enc_cmd = [ '/usr/bin/openssl', 'smime', '-encrypt', '-aes256', '-outform', 'pem', '-in', pContentPath, '-out', encPContentPath ] enc_cmd += [certpath] proc = subprocess.Popen(enc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (encout, encerr) = proc.communicate() if encerr: print >> sys.stderr, "Error: %s" % encerr sys.exit(1) # openssl smime -encrypt produces no output if successful # Step 4: Add the new encrypted payload content back into the plist with open(encPContentPath, 'rb') as f: content = f.readlines() content = content[ 1: -1] #this is to skip the ---BEGIN PKCS7--- and ----END PKCS7---- lines encPayload = "" for line in content: encPayload += ''.join( line.rstrip()) # to get rid of Python's \n everywhere del myProfile['PayloadContent'] binaryEncPayload = base64.b64decode(encPayload) wrapped_data = NSData.dataWithBytes_length_(binaryEncPayload, len(binaryEncPayload)) myProfile['EncryptedPayloadContent'] = wrapped_data plistData, error = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_( myProfile, NSPropertyListXMLFormat_v1_0, None) plistData.writeToFile_atomically_(args.outfile, True) # Step 5: Use plistlib.Data to properly encode the content # myProfile['EncryptedPayloadContent'] = plistlib.Data(encPayload) # now save the profile # plistlib.writePlist(myProfile, args.outfile) # Now clean up after ourselves if args.sign == 'sign' or args.sign == 'both': # sign the profile only # Keychain check: if not args.name: print >> sys.stderr, 'Error: A certificate common name is required to sign profiles with the Keychain.' sys.exit(22) cmd = [ '/usr/bin/security', 'cms', '-S', '-N', args.name, '-i', args.infile, '-o', args.outfile ] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, 'Error: %s' % serr sys.exit(1)
paperpath = os.path.join(os.environ['HOME'], 'Documents', 'Papers') with open(mainfile) as f: bib = f.read() aliases = re.findall(r"Bdsk-File-\d = {(\S*)}", bib) files = [] for a in aliases: tmp = base64.b64decode(a.encode()) files.append( os.path.join( paperpath, re.search(b"Documents/Papers/(.+?)(?=\x00)", tmp).groups()[0].decode())) diskfiles = glob.glob(os.path.join(paperpath, '*.*')) set(files) - set(diskfiles) missing = list(set(diskfiles) - set(files)) np.sort(missing) #for when pyobjc is fixed: from Foundation import NSPropertyListSerialization, NSData decodedBytes = base64.b64decode(a) nsData = NSData.dataWithBytes_length_(decodedBytes, len(decodedBytes)) plist, fmt, error = NSPropertyListSerialization\ .propertyListFromData_mutabilityOption_format_errorDescription_( nsData, 0, None, None)
def main(): parser = argparse.ArgumentParser(description='Sign or encrypt mobileconfig profiles, using either a cert + key file, or a keychain certificate.') parser.add_argument('sign', choices=('sign', 'encrypt', 'both'), help='Choose to sign, encrypt, or do both on a profile.') key_group = parser.add_argument_group('Keychain arguments', description='Use these if you wish to sign with a Keychain certificate.') key_group.add_argument('-k', '--keychain', help='Name of keychain to search for cert. Defaults to login.keychain', default='login.keychain') key_group.add_argument('-n', '--name', help='Common name of certificate to use from keychain.', required=True) parser.add_argument('infile', help='Path to input .mobileconfig file') parser.add_argument('outfile', help='Path to output .mobileconfig file. Defaults to outputting into the same directory.') args = parser.parse_args() if args.sign == 'encrypt' or args.sign == 'both': if args.sign == 'both': outputFile = args.outfile + '_unsigned' else: outputFile = args.outfile # encrypt the profile only, do not sign # Encrypting a profile: # 1. Extract payload content into its own file # 2. Serial that file as its own plist # 3. CMS-envelope that content (using openssl for now) # 4. Remove "PayloadContent" key and replace with "EncryptedPayloadContent" key # 5. Replace the PayloadContent <array> with <data> tags instead # Step 1: Extract payload content into its own file myProfile = readPlist(args.infile) payloadContent = myProfile['PayloadContent'] # Step 2: Serialize that file into its own plist (pContentFile, pContentPath) = tempfile.mkstemp() #print "pContentPath: %s" % pContentPath writePlist(payloadContent, pContentPath) # Step 3: Use openssl to encrypt that content # First, we need to extract the certificate we want to use from the keychain security_cmd = ['/usr/bin/security', 'find-certificate', '-c', args.name, '-p' ] if args.keychain: security_cmd += [args.keychain] proc = subprocess.Popen(security_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, "Error: %s" % serr sys.exit(1) # Now write the certificate to a temp file (certfile, certpath) = tempfile.mkstemp('.der') #print "Certpath: %s" % certpath try: with open(certpath, 'wb') as f: f.write(sout) except IOError: print >> sys.stderr, "Could not write to file!" sys.exit(1) # Now use openssl to encrypt the payload content using that certificate (encPContentfile, encPContentPath) = tempfile.mkstemp('.plist') #print "encPContentPath: %s" % encPContentPath enc_cmd = ['/usr/bin/openssl', 'smime', '-encrypt', '-aes256', '-outform', 'der', '-in', pContentPath, '-out', encPContentPath] enc_cmd += [certpath] proc = subprocess.Popen(enc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (encout, encerr) = proc.communicate() if encerr: print >> sys.stderr, "Error: %s" % encerr sys.exit(1) # openssl smime -encrypt produces no output if successful # Step 4: Add the new encrypted payload content back into the plist with open(encPContentPath, 'rb') as f: binaryEncPayload = f.read() del myProfile['PayloadContent'] wrapped_data = NSData.dataWithBytes_length_(binaryEncPayload, len(binaryEncPayload)) myProfile['EncryptedPayloadContent'] = wrapped_data # Step 5: Replace the plist with the new content plistData, error = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(myProfile, NSPropertyListXMLFormat_v1_0, None) plistData.writeToFile_atomically_(outputFile, True) # Now clean up after ourselves os.remove(pContentPath) os.remove(certpath) os.remove(encPContentPath) if args.sign == 'sign' or args.sign == 'both': # Keychain check: if not args.name: print >> sys.stderr, 'Error: A certificate common name is required to sign profiles with the Keychain.' sys.exit(22) if args.sign == 'both': # If we already encrypted it, then the correct file is already in outputFile inputFile = outputFile else: inputFile = args.infile cmd = ['/usr/bin/security', 'cms', '-S', '-N', args.name, '-i', inputFile, '-o', args.outfile ] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, 'Error: %s' % serr sys.exit(1) if args.sign == 'both': os.remove(outputFile)
def unplist(s): from Foundation import NSData, NSPropertyListSerialization d = NSData.dataWithBytes_length_(s, len(s)) return NSPropertyListSerialization.propertyListWithData_options_format_error_(d, 0, None, None)
def main(): parser = argparse.ArgumentParser(description='Sign or encrypt mobileconfig profiles, using either a cert + key file, or a keychain certificate.') parser.add_argument('sign', choices=('sign', 'encrypt', 'both'), help='Choose to sign, encrypt, or do both on a profile.') key_group = parser.add_argument_group('Keychain arguments', description='Use these if you wish to sign with a Keychain certificate.') key_group.add_argument('-k', '--keychain', help='Name of keychain to search for cert. Defaults to login.keychain', default='login.keychain') key_group.add_argument('-n', '--name', help='Common name of certificate to use from keychain.', required=True) parser.add_argument('infile', help='Path to input .mobileconfig file') parser.add_argument('outfile', help='Path to output .mobileconfig file. Defaults to outputting into the same directory.') args = parser.parse_args() print "f**k yeah" if args.sign == 'encrypt' or args.sign == 'both': # encrypt the profile only, do not sign # Encrypting a profile: # 1. Extract payload content into its own file # 2. Serial that file as its own plist # 3. CMS-envelope that content (using openssl for now) # 4. Remove "PayloadContent" key and replace with "EncryptedPayloadContent" key # 5. Replace the PayloadContent <array> with <data> tags instead # Step 1: Extract payload content into its own file myProfile = plistlib.readPlist(args.infile) payloadContent = myProfile['PayloadContent'] # Step 2: Serialize that file into its own plist (pContentFile, pContentPath) = tempfile.mkstemp() plistlib.writePlist(payloadContent, pContentPath) # Step 3: Use openssl to encrypt that content # First, we need to extract the certificate we want to use from the keychain security_cmd = ['/usr/bin/security', 'find-certificate', '-c', args.name, '-p' ] if args.keychain: security_cmd += [args.keychain] print security_cmd proc = subprocess.Popen(security_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, "Error: %s" % serr sys.exit(1) # Now write the certificate to a temp file (certfile, certpath) = tempfile.mkstemp('.pem') try: with open(certpath, 'wb') as f: f.write(sout) except IOError: print >> sys.stderr, "Could not write to file!" sys.exit(1) # Now use openssl to encrypt the payload content using that certificate (encPContentfile, encPContentPath) = tempfile.mkstemp('.plist') enc_cmd = ['/usr/bin/openssl', 'smime', '-encrypt', '-aes256', '-outform', 'pem', '-in', pContentPath, '-out', encPContentPath] enc_cmd += [certpath] proc = subprocess.Popen(enc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (encout, encerr) = proc.communicate() if encerr: print >> sys.stderr, "Error: %s" % encerr sys.exit(1) # openssl smime -encrypt produces no output if successful # Step 4: Add the new encrypted payload content back into the plist with open(encPContentPath, 'rb') as f: content = f.readlines() content = content[1:-1] #this is to skip the ---BEGIN PKCS7--- and ----END PKCS7---- lines encPayload = "" for line in content: encPayload += ''.join(line.rstrip()) # to get rid of Python's \n everywhere del myProfile['PayloadContent'] binaryEncPayload = base64.b64decode(encPayload) wrapped_data = NSData.dataWithBytes_length_(binaryEncPayload, len(binaryEncPayload)) myProfile['EncryptedPayloadContent'] = wrapped_data plistData, error = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(myProfile, NSPropertyListXMLFormat_v1_0, None) plistData.writeToFile_atomically_(args.outfile, True) # Step 5: Use plistlib.Data to properly encode the content # myProfile['EncryptedPayloadContent'] = plistlib.Data(encPayload) # now save the profile # plistlib.writePlist(myProfile, args.outfile) # Now clean up after ourselves if args.sign == 'sign' or args.sign == 'both': # sign the profile only # Keychain check: if not args.name: print >> sys.stderr, 'Error: A certificate common name is required to sign profiles with the Keychain.' sys.exit(22) cmd = ['/usr/bin/security', 'cms', '-S', '-N', args.name, '-i', args.infile, '-o', args.outfile ] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, 'Error: %s' % serr sys.exit(1)
def set_clipboard(self, data): ns_data = NSData.dataWithBytes_length_(data, len(data)) pb = self._ns_pasteboard pb.clearContents() pb.setData_forType_(ns_data, NSStringPboardType)
def main(): parser = argparse.ArgumentParser(description='Sign or encrypt mobileconfig profiles, using either a cert + key file, or a keychain certificate.') parser.add_argument('sign', choices=('sign', 'encrypt', 'both'), help='Choose to sign, encrypt, or do both on a profile.') key_group = parser.add_argument_group('Keychain arguments', description='Use these if you wish to sign with a Keychain certificate.') key_group.add_argument('-k', '--keychain', help='Name of keychain to search for cert. Defaults to login.keychain', default='login.keychain') key_group.add_argument('-n', '--name', help='Common name of certificate to use from keychain.', required=True) parser.add_argument('infile', help='Path to input .mobileconfig file') parser.add_argument('outfile', help='Path to output .mobileconfig file. Defaults to outputting into the same directory.') args = parser.parse_args() if args.sign == 'encrypt' or args.sign == 'both': if args.sign == 'both': outputFile = args.outfile + '_unsigned' else: outputFile = args.outfile # encrypt the profile only, do not sign # Encrypting a profile: # 1. Extract payload content into its own file # 2. Serial that file as its own plist # 3. CMS-envelope that content (using openssl for now) # 4. Remove "PayloadContent" key and replace with "EncryptedPayloadContent" key # 5. Replace the PayloadContent <array> with <data> tags instead # Step 1: Extract payload content into its own file myProfile = readPlist(args.infile) payloadContent = myProfile['PayloadContent'] # Step 2: Serialize that file into its own plist (pContentFile, pContentPath) = tempfile.mkstemp() #print "pContentPath: %s" % pContentPath writePlist(payloadContent, pContentPath) # Step 3: Use openssl to encrypt that content # First, we need to extract the certificate we want to use from the keychain security_cmd = ['/usr/bin/security', 'find-certificate', '-c', args.name, '-p' ] if args.keychain: security_cmd += [args.keychain] proc = subprocess.Popen(security_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, "Error: %s" % serr sys.exit(1) # Now write the certificate to a temp file (certfile, certpath) = tempfile.mkstemp('.der') #print "Certpath: %s" % certpath try: with open(certpath, 'wb') as f: f.write(sout) except IOError: print >> sys.stderr, "Could not write to file!" sys.exit(1) # Now use openssl to encrypt the payload content using that certificate (encPContentfile, encPContentPath) = tempfile.mkstemp('.plist') #print "encPContentPath: %s" % encPContentPath enc_cmd = ['/usr/bin/openssl', 'smime', '-encrypt', '-aes256', '-outform', 'der', '-in', pContentPath, '-out', encPContentPath] enc_cmd += [certpath] proc = subprocess.Popen(enc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (encout, encerr) = proc.communicate() if encerr: print >> sys.stderr, "Error: %s" % encerr sys.exit(1) # openssl smime -encrypt produces no output if successful # Step 4: Add the new encrypted payload content back into the plist with open(encPContentPath, 'rb') as f: binaryEncPayload = f.read() del myProfile['PayloadContent'] wrapped_data = NSData.dataWithBytes_length_(binaryEncPayload, len(binaryEncPayload)) myProfile['EncryptedPayloadContent'] = wrapped_data # Step 5: Replace the plist with the new content plistData, error = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(myProfile, NSPropertyListXMLFormat_v1_0, None) plistData.writeToFile_atomically_(outputFile, True) # Now clean up after ourselves os.remove(pContentPath) os.remove(certpath) os.remove(encPContentPath) if args.sign == 'sign' or args.sign == 'both': # Keychain check: if not args.name: print >> sys.stderr, 'Error: A certificate common name is required to sign profiles with the Keychain.' sys.exit(22) if args.sign == 'both': # If we already encrypted it, then the correct file is already in outputFile inputFile = outputFile else: inputFile = args.infile cmd = ['/usr/bin/security', 'cms', '-S', '-N', args.name, '-i', inputFile, '-o', args.outfile ] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (sout, serr) = proc.communicate() if serr: print >> sys.stderr, 'Error: %s' % serr sys.exit(1) if args.sign == 'both': os.remove(outputFile)
os.path.expanduser("~")) for x in mmeTokenFile: try: int( x.split("/")[-1] ) #if we can cast to int, that means we have the DSID / account file. mmeTokenFile = x except ValueError: continue if not isinstance(mmeTokenFile, str): print "Could not find MMeTokenFile. You can specify the file manually." sys.exit() else: print "Decrypting token plist -> [%s]\n" % mmeTokenFile #perform decryption with zero dependencies by using openssl binary decryptedBinary = subprocess.check_output( "openssl enc -d -aes-128-cbc -iv '%s' -K %s < '%s'" % (IV, hexedKey, mmeTokenFile), shell=True) #convert the decrypted binary plist to an NSData object that can be read binToPlist = NSData.dataWithBytes_length_(decryptedBinary, len(decryptedBinary)) #convert the binary NSData object into a dictionary object tokenPlist = NSPropertyListSerialization.propertyListWithData_options_format_error_( binToPlist, 0, None, None)[0] #ta-da print "Successfully decrypted token plist!\n" print "%s [%s -> %s]" % (tokenPlist["appleAccountInfo"]["primaryEmail"], tokenPlist["appleAccountInfo"]["fullName"], tokenPlist["appleAccountInfo"]["dsPrsID"]) print tokenPlist["tokens"]
def get_input(): try: while True: line = sys.stdin.readline() if not line: break if line == '': continue print "info:Read %s" % line sys.stdout.flush() cfg = None try: cfg = json.loads(line) except: pass if cfg == None: print "info:Unable to parse line" sys.stdout.flush() continue if cfg.has_key("Action"): print "info:Running %s" % cfg["Action"] sys.stdout.flush() if cfg["Action"] == "setmenu": menu = cfg["Menu"] if lookup.has_key(menu["Key"]): lookup[menu["Key"]].title = menu["Text"] if menu.has_key("Enabled") and not menu["Enabled"]: lookup[menu["Key"]].set_callback(None) else: lookup[menu["Key"]].set_callback(item_clicked) app.menu.update([]) else: print "warn:Key not found %s" % cfg["Action"] sys.stdout.flush() elif cfg["Action"] == "setmenus": app.menu.clear() app.menu = parsemenus(cfg) print "info:Updated menus" sys.stdout.flush() elif cfg["Action"] == "seticon": try: raw = base64.b64decode(cfg["Image"]) data = NSData.dataWithBytes_length_(raw, len(raw)) img = NSImage.alloc().initWithData_(data) img.setScalesWhenResized_(True) img.setSize_((21, 21)) app.icon = img print "info:Image updated" sys.stdout.flush() except: print "warn:Failed to set image" sys.stdout.flush() elif cfg["Action"] == "shutdown": break elif cfg["Action"] == "notification": if rumps_NOTIFICATIONS: rumps.notification(cfg["Title"], '', cfg["Message"]) finally: rumps.quit_application() print "info:Shutdown" sys.stdout.flush() print "info:Stdin close" sys.stdout.flush() sys.stdin.close()
def parse_plist(info_plist_string): # Use PyObjC, pre-installed in Apple's Python dist. data = NSData.dataWithBytes_length_(info_plist_string, len(info_plist_string)) return NSPropertyListSerialization.propertyListWithData_options_format_error_(data, 0, None, None)
def update_user_account(user_data): """ Updates the user account. When being executed on a booted volume, updates via the Open Directory API. When being executed on a targeted volume, updates via the user account Property List. """ name = user_data["name"] if is_booted_volume(): node = get_od_node() if not node: print "Unable to look up OpenDirectory node, aborting..." exit(1) record, error = node.recordWithRecordType_name_attributes_error_( kODRecordTypeUsers, name, None, None) if error: print error exit(1) for attribute, value in user_data.items(): # jpegphoto and ShadowHashData are data blobs, not strings, so a # little conversion is required if attribute == "jpegphoto" or attribute == "ShadowHashData": data = NSData.dataWithBytes_length_(value, len(value)) value = NSArray.alloc().initWithObjects_(data) success, error = record.setValue_forAttribute_error_( value, attribute, None) if error: print error exit(1) # we don't want to spew out the data blobs to stdout, so we just # replace it with something simple. this is purely for formatting # reasons if attribute == "jpegphoto" or attribute == "ShadowHashData": value = "DATA" print "User account '" + name + "' updated attribute " + \ attribute + ": " + str(value) else: # Property List logic path = get_target() + get_user_plist_path() + name + ".plist" dictionary = plistlib.readPlist(path) for attribute, value in user_data.items(): # jpegphoto and ShadowHashData are data blobs, not strings, so a # little conversion is required if attribute == "jpegphoto" or attribute == "ShadowHashData": value = plistlib.Data(value) dictionary[attribute] = [value] # we don't want to spew out the data blobs to stdout, so we just # replace it with something simple. this is purely for formatting # reasons if attribute == "jpegphoto" or attribute == "ShadowHashData": value = "DATA" print "User account '" + name + "' updated attribute " + \ attribute + ": " + str(value) plistlib.writePlist(dictionary, path)
def set_clipboard(self, data): ns_data = NSData.dataWithBytes_length_(data, len(data)) pb = self._ns_pasteboard pb.clearContents() pb.setData_forType_(ns_data, NSStringPboardType)
def parse_plist(info_plist_string): # Use PyObjC, pre-installed in Apple's Python dist. data = NSData.dataWithBytes_length_(info_plist_string, len(info_plist_string)) return NSPropertyListSerialization.propertyListWithData_options_format_error_( data, 0, None, None)