├── LICENSE ├── Orchard.js └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Cody Thomas 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Orchard.js: -------------------------------------------------------------------------------- 1 | //Author Cody Thomas, @its_a_feature_ 2 | ObjC.import("Foundation"); 3 | ObjC.import("stdio"); 4 | ObjC.import('OpenDirectory'); 5 | //for all of these, there is a switch to use ObjC calls vs terminal calls 6 | currApp = Application.currentApplication(); 7 | currApp.includeStandardAdditions = true; 8 | // Lookup tables for doing OpenDirectory queries via LDAP 9 | var object_class = { 10 | "AttributeTypes": "dsRecTypeStandard:AttributeTypes", 11 | "AFPServer": "dsRecTypeStandard:AFPServer", 12 | "Aliases": "dsRecTypeStandard:Aliases", 13 | "Augments": "dsRecTypeStandard:Augments", 14 | "Automount": "dsRecTypeStandard:Automount", 15 | "AutomountMap": "dsRecTypeStandard:AutomountMap", 16 | "AutoServerSetup": "dsRecTypeStandard:AutoServerSetup", 17 | "Bootp": "dsRecTypeStandard:Bootp", 18 | "CertificateAuthorities": "dsRecTypeStandard:CertificateAuthorities", 19 | "ComputerLists": "dsRecTypeStandard:ComputerLists", 20 | "ComputerGroups": "dsRecTypeStandard:ComputerGroups", 21 | "Computers": "dsRecTypeStandard:Computers", 22 | "Configuration": "dsRecTypeStandard:Config", 23 | "Ethernets": "dsRecTypeStandard:Ethernets", 24 | "FileMakerServers": "dsRecTypeStandard:FileMakerServers", 25 | "FTPServer": "dsRecTypeStandard:FTPServer", 26 | "Groups": "dsRecTypeStandard:Groups", 27 | "HostServices": "dsRecTypeStandard:HostServices", 28 | "Hosts": "dsRecTypeStandard:Hosts", 29 | "LDAPServer": "dsRecTypeStandard:LDAPServer", 30 | "Locations": "dsRecTypeStandard:Locations", 31 | "Mounts": "dsRecTypeStandard:Mounts", 32 | "NFS": "dsRecTypeStandard:NFS", 33 | "NetDomains": "dsRecTypeStandard:NetDomains", 34 | "NetGroups": "dsRecTypeStandard:NetGroups", 35 | "Networks": "dsRecTypeStandard:Networks", 36 | "PasswordServer": "dsRecTypeStandard:PasswordServer", 37 | "People": "dsRecTypeStandard:People", 38 | "Plugins": "dsRecTypeStandard:Plugins", 39 | "PresetComputers": "dsRecTypeStandard:PresetComputers", 40 | "PresetComputerGroups": "dsRecTypeStandard:PresetComputerGroups", 41 | "PresetComputerLists": "dsRecTypeStandard:PresetComputerLists", 42 | "PresetGroups": "dsRecTypeStandard:PresetGroups", 43 | "PresetUsers": "dsRecTypeStandard:PresetUsers", 44 | "PrintService": "dsRecTypeStandard:PrintService", 45 | "PrintServiceUser": "dsRecTypeStandard:PrintServiceUser", 46 | "Printers": "dsRecTypeStandard:Printers", 47 | "Protocols": "dsRecTypeStandard:Protocols", 48 | "QTSServer": "dsRecTypeStandard:QTSServer", 49 | "RecordTypes": "dsRecTypeStandard:RecordTypes", 50 | "Resources": "dsRecTypeStandard:Resources", 51 | "RPC": "dsRecTypeStandard:RPC", 52 | "SMBServer": "dsRecTypeStandard:SMBServer", 53 | "Server": "dsRecTypeStandard:Server", 54 | "Services": "dsRecTypeStandard:Services", 55 | "SharePoints": "dsRecTypeStandard:SharePoints", 56 | "UserAuthenticationData": "dsRecTypeStandard:UserAuthenticationData", 57 | "Users": "dsRecTypeStandard:Users", 58 | "WebServer": "dsRecTypeStandard:WebServer", 59 | } 60 | var match_type = { 61 | "Any": 0x01,//$.kODMatchAny, 62 | "BeginsWith": 0x2002,//$.kODMatchInsensitiveBeginsWith, 63 | "EndsWith": 0x2003,//$.kODMatchInsensitiveEndsWith, 64 | "Contains": 0x2004,//$.kODMatchInsensitiveContains, 65 | "EqualTo": 0x2001,//$.kODMatchInsensitiveEqualTo, 66 | "LessThan": 0x2007,//$.kODMatchLessThan, 67 | "GreaterThan": 0x2006,//$.kODMatchGreaterThan 68 | } 69 | var attributes_list = { 70 | "all": ["", "dsAttributesAll"], 71 | "*": ["", "dsAttributesAll"], 72 | "accountpolicydata": ["accountPolicyData", "dsAttrTypeNative:"], 73 | "accountexpires": ["accountExpires", "dsAttrTypeNative:"], 74 | "admincount": ["adminCount", "dsAttrTypeNative:"], 75 | "adminlimits": ["AdminLimits", "dsAttrTypeStandard:"], 76 | "altsecurityidentities": ["AltSecurityIdentities", "dsAttrTypeStandard:"], //x509 77 | "afp_guestaccess": ["afp_guestaccess", "dsAttrTypeNative:"], 78 | "afp_name": ["afp_name", "dsAttrTypeNative:"], 79 | "afp_shared": ["afp_shared", "dsAttrTypeNative:"], 80 | "authenticationhint": ["AuthenticationHint", "dsAttrtypeStandard:"], 81 | "badpasswordtime": ["badPasswordTime", "dsAttrTypeNative:"], 82 | "badpwdcount": ["badPwdCount", "dsAttrTypeNative:"], 83 | "bootfile": ["BootFile", "dsAttrTypeStandard:"], 84 | "bootparams": ["BootParams", "dsAttrTypeStandard:"], 85 | "cacertificiate": ["CACertificate", "dsAttrTypeStandard:"], 86 | "capacity": ["Capacity", "dsAttrTypeStandard:"], 87 | "category": ["Category", "dsAttrtypeStandard:"], 88 | "certificaterevocationlist": ["CertificateRevocationList", "dsAttrTypeStandard:"], 89 | "codepage": ["codePage", "dsAttrTypeNative:"], 90 | "comment": ["Comment", "dsAttrTypeStandard:"], 91 | "contactguid": ["ContactGUID", "dsAttrtypeStandard:"], 92 | "countrycode": ["countryCode", "dsAttrTypeNative:"], 93 | "creationtimestamp": ["CreationTimestamp", "dsAttrTypeStandard:"], 94 | "crosscertificatepair": ["CrossCertificatePair", "dsAttrTypeStandard:"], 95 | "cn": ["cn", "dsAttrTypeNative:"], 96 | "fullname": ["FullName", ""], //have to use realname 97 | "displayname": ["displayName", "dsAttrTypeNative:"], 98 | "distinguishedname": ["distinguishedName", "dsAttrTypeNative:"], 99 | "directory_path": ["directory_path", "dsAttrTypeNative:"], 100 | "dnsdomain": ["DNSDomain", "dsAttrTypeStandard:"], 101 | "dnsnameserver": ["DNSNameServer", "dsAttrTypeStandard:"], 102 | "dscorepropagationdata": ["dsCorePropagationData", "dsAttrTypeNative:"], 103 | "emailaddress": ["EMailAddress", "dsAttrTypeStandard:"], 104 | "enetaddress": ["ENetAddress", "dsAttrTypeNative:"], 105 | "expire": ["Expire", "dsAttrTypeStandard:"], 106 | "firstname": ["FirstName", "dsAttrTypeStandard:"], 107 | "ftp_name": ["ftp_name", "dsAttrTypeNative:"], 108 | "generateduid": ["GeneratedUID", "dsAttrTypeStandard:"], 109 | "grouptype": ["groupType", "dsAttrTypeNative:"], 110 | "hardwareuuid": ["HardwareUUID", "dsAttrTypeStandard:"], 111 | "heimdalsrpkey": ["HeimdalSRPKey", "dsAttrTypeNative:"], 112 | "ishidden": ["IsHidden", "dsAttrTypeNative:"], 113 | "instancetype": ["instanceType", "dsAttrTypeNative:"], 114 | "iscriticalsystemobject": ["isCriticalSystemObject", "dsAttrTypeNative:"], 115 | "jobtitle": ["JobTitle", "dsAttrTypeStandard:"], 116 | "kerberoskeys": ["KerberosKeys", "dsAttrTypeNative:"], 117 | "kerberosservices": ["KerberosServices", "dsAttrTypeStandard:"], //host, afpserver, cifs, vnc, etc 118 | "lastname": ["LastName", "dsAttrTypeStandard:"], 119 | "lastlogoff": ["lastLogoff", "dsAttrTypeNative:"], 120 | "lastlogon": ["lastLogon", "dsAttrTypeNative:"], 121 | "lastlogontimestamp": ["lastLogonTimestamp", "dsAttrTypeNative:"], 122 | "localpolicyglags": ["localPolicyFlags", "dsAttrTypeNative:"], 123 | "logoncount": ["logonCount", "dsAttrTypeNative:"], 124 | "logonhours": ["logonHours", "dsAttrTypeNative:"], 125 | "ldapsearchbasesuffix": ["LDAPSearchBaseSuffix", "dsAttrtypeStandard:"], 126 | "automountmap": ["AutomountMap", "dsAttrTypeStandard:"], 127 | "applemetanodelocation": ["AppleMetaNodeLocation", "dsAttrTypeStandard:"], 128 | "applemetarecordname": ["AppleMetaRecordName", "dsAttrTypeStandard:"], 129 | "machineserves": ["MachineServes", "dsAttrTypeStandard:"], 130 | "mcxflags": ["MCXFlags", "dsAttrTypeStandard:"], 131 | "mcxsettings": ["MCXSettings", "dsAttrTypeStandard:"], 132 | "middlename": ["MiddleName", "dsAttrTypeStandard:"], 133 | "member": ["member", "dsAttrTypeNative:"], 134 | "memberof": ["memberOf", "dsAttrTypeNative:"], 135 | "members": ["members", "dsAttrTypeNative:"], 136 | "msdfsr-computerreferencebl": ["msDFSR-ComputerReferenceBL", "dsAttrTypeNative:"], 137 | "msds-generationid": ["msDS-GenerationId", "dsAttrTypeNative:"], 138 | "msds-supportedencryptiontypes":["msDS-SupportedEncryptionTypes", "dsAttrTypeNative:"], 139 | "modificationtimestamp": ["ModificationTimestamp", "dsAttrTypeStandard:"], 140 | "name": ["name", "dsAttrTypeNative:"], 141 | "networkaddress": ["networkAddress", "dsAttrTypeNative:"], 142 | "networkview": ["NetworkView", "dsAttrTypeStandard:"], 143 | "nfshomedirectory": ["NFSHomeDirectory", "dsAttrTypeStandard:"], 144 | "nodesaslrealm": ["NodeSASLRealm", "dsAttrTypeStandard:"], 145 | "note": ["Note", "dsAttrTypeStandard:"],//says this is for last name attribute??? 146 | "objectclass": ["objectClass", "dsAttrTypeNative:"], 147 | "objectcategory": ["objectCategory", "dsAttrTypeNative:"], 148 | "objectguid": ["objectGUID", "dsAttrTypeNative:"], 149 | "objectsid": ["objectSid", "dsAttrTypeNative:"], 150 | "olcdatabase": ["OLCDatabase", "dsAttrTypeStandard:"], 151 | "olcdatabaseindex": ["OLCDatabaseIndex", "dsAttrTypeStandard:"], 152 | "olcsyncrepl": ["OLCSyncRepl", "dsAttrTypeStandard:"], 153 | "operatingsystem": ["operatingSystem", "dsAttrTypeNative:"], 154 | "operatingsystemversion": ["operatingSystemVersion", "dsAttrTypeNative:"], 155 | "owner": ["Owner", "dsAttrTypeStandard:"], 156 | "ownerguid": ["OwnerGUID", "dsAttrTypeStandard:"], 157 | "password": ["Password", "dsAttrTypeStandard:"], 158 | "passwordplus": ["PasswordPlus", "dsAttrTypeStandard:"],//indicates authentication redirection 159 | "passwordpolicyoptions": ["PasswordPolicyOptions", "dsAttrTypeStandard:"], 160 | "passwordserverlist": ["PasswordServerList", "dsAttrTypeStandard:"], 161 | "passwordserverlocation": ["PasswordServerLocation", "dsAttrTypeStandard:"], 162 | "port": ["Port", "dsAttrTypeStandard:"],//which port a service is on 163 | "presetuserisadmin": ["PresetUserIsAdmin", "dsAttrTypeStandard:"], 164 | "primarycomputerguid": ["PrimaryComputerGUID", "dsAttrTypeStandard:"], 165 | "primarycomputerlist": ["PrimaryComputerList", "dsAttrTypeStandard:"], 166 | "primarygroupid": ["PrimaryGroupID", "dsAttrTypeStandard:"], 167 | "profiles": ["Profiles", "dsAttrTypeStandard:"], 168 | "profilestimestamp": ["ProfilesTimestamp", "dsAttrTypeStandard:"], 169 | "realname": ["RealName", "dsAttrTypeStandard:"], //Yes, fullname maps to realname because... apple 170 | "realuserid": ["RealUserID", "dsAttrTypeStandard:"], 171 | "relativednprefix": ["RelativeDNPrefix", "dsAttrTypeStandard:"],//relative distinguished name, 172 | "ridsetreferences": ["rIDSetReferences", "dsAttrTypeNative:"], 173 | "samaccountname": ["sAMAccountName", "dsAttrTypeNative:"], 174 | "samaccounttype": ["sAMAccountType", "dsAttrTypeNative:"], 175 | "serverreferencebl": ["serverReferenceBL", "dsAttrTypeNative:"], 176 | "serviceprincipalname": ["servicePrincipalName", "dsAttrTypeNative:"], 177 | "shadowhashdata": ["ShadowHashData", "dsAttrTypeNative:"], 178 | "smbacctflags": ["SMBAccountFlags", "dsAttrTypeStandard:"],//account control flag 179 | "smbgrouprid": ["SMBGroupRID", "dsAttrTypeStandard:"], //define PDC SMB interaction with DirectoryService 180 | "smbhome": ["SMBHome", "dsAttrTypeStandard:"],//UNC address of a windows home directory mount point 181 | "smbhomedrive": ["SMBHomeDrive", "dsAttrTypeStandard:"], 182 | "smbprimarygroupsid": ["SMBPrimaryGroupSID", "dsAttrTypeStandard:"], 183 | "smbpasswordlastset": ["SMBPasswordLastSet", "dsAttrTypeStandard:"],// used in SMB interaction 184 | "smbprofilepath": ["SMBProfilePath", "dsAttrTypeStandard:"],//defines desktop management info 185 | "smbrid": ["SMBRID", "dsAttrTypeStandard:"], //used in SMB interaction 186 | "smbscriptpath": ["SMBScriptPath", "dsAttrTypeStandard:"],//define SMB login script path 187 | "smbsid": ["SMBSID", "dsAttrTypeStandard:"], //define SMB Security ID 188 | "smbuserworkstations": ["SMBUserWorkstations", "dsAttrTypeStandard:"],//list of workstations a user can log in from 189 | "smblogofftime": ["SMBLogoffTime", "dsAttrTypeStandard:"], 190 | "smblogontime": ["SMBLogonTime", "dsAttrTypeStandard:"], 191 | "smb_createmask": ["smb_createmask", "dsAttrTypeNative:"], 192 | "smb_directorymask": ["smb_directorymask", "dsAttrTypeNative:"], 193 | "smb_guestaccess": ["smb_guestaccess", "dsAttrTypeNative:"], 194 | "smb_name": ["smb_name", "dsAttrTypeNative:"], 195 | "smb_shared": ["smb_shared", "dsAttrTypeNative:"], 196 | "servicetype": ["ServiceType", "dsAttrTypeStandard:"],//define SMB login script path 197 | "serviceslocator": ["ServicesLocator", "dsAttrTypeStandard:"], 198 | "setupadvertising": ["SetupAssistantAdvertising", "dsAttrTypeStandard:"],//raw service type of a service, ex: http or https for kODRecordTypeWebServer 199 | "sharepoint_account_uuid": ["sharepoint_account_uuid", "dsAttrTypeNative:"], 200 | "sharepoint_group_id": ["sharepoint_group_id", "dsAttrTypeNative:"], 201 | "showinadvancedviewonly": ["showInAdvancedViewOnly", "dsAttrTypeNative:"], 202 | "uniqueid": ["UniqueID", "dsAttrTypeStandard:"], //user's 32bit ID in legacy manner 203 | "unlockoptions": ["unlockOptions", "dsAttrTypeNative:"], 204 | "url": ["URL", "dsAttrTypeStandard:"], 205 | "users": ["users", "dsAttrTypeNative:"], 206 | "usnchanged": ["uSNChanged", "dsAttrTypeNative:"], 207 | "usncreated": ["uSNCreated", "dsAttrTypeNative:"], 208 | "useraccountcontrol": ["userAccountControl", "dsAttrTypeNative:"], 209 | "usercertificate": ["UserCertificate", "dsAttrTypeStandard:"], 210 | "userpkcs12data": ["UserPKCS12Data", "dsAttrTypeStandard:"], 211 | "usershell": ["UserShell", "dsAttrTypeStandard:"], 212 | "usersmimecertificate": ["UserSMIMECertificate", "dsAttrTypeStandard:"], 213 | "webloguri": ["WeblogURI", "dsAttrTypeStandard:"],//URI of a user's weblog 214 | "whenchanged": ["whenChanged", "dsAttrTypeNative:"], 215 | "whencreated": ["whenCreated", "dsAttrTypeNative:"], 216 | "_writers_usercertificate": ["_writers_UserCertificate", "dsAttrTypeNative:"], 217 | "_writers_hint": ["_writers_hint", "dsAttrTypeNative:"], 218 | "_writers_passwd": ["_writers_passwd", "dsAttrTypeNative:"], 219 | "_writers_unlockoptions": ["_writers_unlockOptions", "dsAttrTypeNative:"], 220 | "_writers_usercertificate": ["_writers_UserCertificate", "dsAttrTypeNative:"], 221 | "xmlplist": ["XMLPlist", "dsAttrTypeStandard:"],//specify an XML Property List 222 | "protocolnumber": ["ProtocolNumber", "dsAttrTypeStandard:"], 223 | "rpcnumber": ["RPCNumber", "dsAttrTypeStandard:"], 224 | "networknumber": ["NetworkNumber", "dsAttrTypeStandard:"], 225 | "accesscontrolentry": ["AccessControlEntry", "dsAttrTypeStandard:"], 226 | "authenticationauthority": ["AuthenticationAuthority", "dsAttrTypeStandard:"], //specify mechanism used to verify or set a user's password 227 | "authorityrevocationlist": ["AuthorityRevocationList", "dsAttrTypeStandard:"], 228 | "automountinformation": ["AutomountInformation", "dsAttrTypeStandard:"], 229 | "computers": ["Computers", "dsAttrTypeStandard:"], 230 | "dnsname": ["DNSName", "dsAttrTypeStandard:"], 231 | "group": ["Group", "dsAttrTypeStandard:"],//store a list of groups 232 | "groupmembers": ["GroupMembers", "dsAttrTypeStandard:"], //specify GUID values of members of a group that are not groups 233 | "groupmembership": ["GroupMembership", "dsAttrTypeStandard:"], //specify list of users that belong to a given group 234 | "groupservices": ["GroupServices", "dsAttrTypeStandard:"],//XML plist to define group's services, 235 | "homedirectory": ["HomeDirectory", "dsAttrTypeStandard:"], 236 | "imhandle": ["IMHandle", "dsAttrTypeStandard:"],//user's instant messaging handles 237 | "ipaddress": ["IPAddress", "dsAttrTypeStandard:"], 238 | "ipv6address": ["IPv6Address", "dsAttrTypeStandard:"], 239 | "kdcauthkey": ["KDCAuthKey", "dsAttrTypeStandard:"],//store a KDC master key 240 | "kdcconfigdata": ["KDCConfigData", "dsAttrTypeStandard:"], 241 | "keywords": ["Keywords", "dsAttrTypeStandard:"], 242 | "ldapreadreplicas": ["LDAPReadReplicas", "dsAttrTypeStandard:"],//list of LDAP server URLs that can be used to read directory data 243 | "ldapwritereplicas": ["LDAPWriteReplicas", "dsAttrTypeStandard:"], 244 | "linkedidentity": ["LinkedIdentity", "dsAttrTypeNative:"], 245 | "localerelay": ["LocaleRelay", "dsAttrTypeStandard:"], 246 | "localesubnets": ["LocaleSubnets", "dsAttrTypeStandard:"], 247 | "nestedgroups": ["NestedGroups", "dsAttrTypeStandard:"], //specify list of nested group GUID values in a group attribute 248 | "netgroups": ["NetGroups", "dsAttrTypeStandard:"],//specify a list of net groups that a user or host record is a member of 249 | "nickname": ["NickName", "dsAttrTypeStandard:"], 250 | "organizationinfo": ["OrganizationInfo", "dsAttrTypeStandard:"], 251 | "organizationname": ["OrganizationName", "dsAttrTypeStandard:"], 252 | "pgppublickey": ["PGPPublicKey", "dsAttrTypeStandard:"], 253 | "protocols": ["Protocols", "dsAttrTypeStandard:"], 254 | "recordname": ["RecordName", "dsAttrTypeStandard:"], 255 | "record_daemon_version": ["record_daemon_version", "dsAttrTypeNative:"], 256 | "relationships": ["Relationships", "dsAttrTypeStandard:"], 257 | "resourceinfo": ["ResourceInfo", "dsAttrTypeStandard:"], 258 | "resourcetype": ["ResourceType", "dsAttrTypeStandard:"], 259 | "authcredential": ["AuthCredential", "dsAttrTypeStandard:"],//stores an authentication credential used to authenticate to a directory 260 | "daterecordcreated": ["DateRecordCreated", "dsAttrTypeStandard:"], 261 | "kerberosflags": ["KerberosFlags", "dsAttrTypeNative:"], 262 | "kerberosrealm": ["KerberosRealm", "dsAttrTypeStandard:"], 263 | "ntdomaincomputeraccount": ["NTDomainComputerAccount", "dsAttrTypeStandard:"],//support kerberos SMB server services 264 | "primaryntdomain": ["PrimaryNTDomain", "dsAttrTypeStandard:"], 265 | "pwdagingpolicy": ["PwdAgingPolicy", "dsAttrTypeStandard:"],//record's password aging policy 266 | "readonlynode": ["ReadOnlyNode", "dsAttrTypeStandard:"], 267 | "authmethod": ["AuthMethod", "dsAttrTypeStandard:"],//specify a record's authentication method 268 | "recordtype": ["RecordType", "dsAttrTypeStandard:"], //specify type of a record or directory node 269 | "advertisedservices": ["AdvertisedServices", "dsAttrTypeStandard:"],//specify (Bounjour) advertised services 270 | "networkinterfaces": ["NetworkInterfaces", "dsAttrTypeStandard:"], 271 | "primarylocale": ["PrimaryLocale", "dsAttrTypeStandard:"] 272 | } 273 | var node_list = { 274 | "network": 0x2205,//$.kODNodeTypeNetwork, 275 | "local": 0x2200,//$.kODNodeTypeLocalNodes, 276 | "config": 0x2202,//$.kODNodeTypeConfigure, 277 | "contacts": 0x2204,//$.kODNodeTypeContacts 278 | } 279 | // helper functions to actually do the OD queries and return results 280 | function Get_OD_ObjectClass({objectclass="Users", match="Any", value=null, max_results=0, query_attributes="All", return_attributes=[null], nodetype='network'} = {}){ 281 | //gets all attributes for all local users 282 | var session = Ref(); 283 | var node = Ref(); 284 | var query = Ref(); 285 | session = $.ODSession.defaultSession; 286 | //console.log(session); 287 | var fixed_return_attributes = []; 288 | for(var i in return_attributes){ 289 | if(return_attributes[i] != null){ 290 | ret_attr_lower = return_attributes[i].toLowerCase(); 291 | if(attributes_list.hasOwnProperty(ret_attr_lower)){ 292 | fixed_return_attributes.push(attributes_list[ret_attr_lower][1] + attributes_list[ret_attr_lower][0]); 293 | } 294 | }else{ 295 | fixed_return_attributes.push(null); 296 | } 297 | } 298 | if(fixed_return_attributes.length == 1){ 299 | fixed_return_attributes = fixed_return_attributes[0]; 300 | } 301 | if(attributes_list.hasOwnProperty(query_attributes.toLowerCase())){ 302 | query_attr_lower = query_attributes.toLowerCase(); 303 | query_attributes = attributes_list[query_attr_lower][1] + attributes_list[query_attr_lower][0]; 304 | } 305 | else{ 306 | return "query attribute " + query_attributes + " not found"; 307 | } 308 | //console.log(fixed_return_attributes); 309 | node = $.ODNode.nodeWithSessionTypeError(session, node_list[nodetype], null); 310 | //console.log("about to print subnode names\n"); 311 | //console.log(ObjC.deepUnwrap($.ODNodeCopySubnodeNames(node, $()))); 312 | //console.log("about to print supported attributes\n"); 313 | //console.log(JSON.stringify([ObjC.deepUnwrap($.ODNodeCopySupportedAttributes(node, object_class[objectclass], $()))], null, 2)); 314 | //https://developer.apple.com/documentation/opendirectory/odquery/1391709-querywithnode?language=objc 315 | //console.log("about to print supported record types\n"); 316 | //console.log(JSON.stringify([ObjC.deepUnwrap($.ODNodeCopySupportedRecordTypes(node, $()))], null, 2)); 317 | query = $.ODQuery.queryWithNodeForRecordTypesAttributeMatchTypeQueryValuesReturnAttributesMaximumResultsError( 318 | node, 319 | object_class[objectclass], //(objectclass) https://developer.apple.com/documentation/opendirectory/opendirectory_functions/record_types?language=objc 320 | query_attributes, //set to recordname that we're looking to match 321 | match_type[match], //( equals, beginsWith, contains, etc) https://developer.apple.com/documentation/opendirectory/opendirectory_functions/match_types?language=objc 322 | value, // input query (like admin) 323 | fixed_return_attributes, 324 | max_results, //maximum number of results, 0=all 325 | $()); //error 326 | var results = query.resultsAllowingPartialError(false, null); 327 | //results; 328 | //console.log(results); 329 | var output = {}; 330 | for(var i = 0; i < results.count; i++){ 331 | var error = Ref(); 332 | var attributes = results.objectAtIndex(i).recordDetailsForAttributesError($(),error); 333 | var keys = attributes.allKeys; 334 | name_index = ObjC.deepUnwrap(attributes.valueForKey($("dsAttrTypeStandard:RecordName"))) 335 | if(Array.isArray(name_index)){ 336 | name_index = name_index[0] 337 | } 338 | if(nodetype === "network" && ObjC.deepUnwrap(attributes.valueForKey($("dsAttrTypeStandard:AppleMetaNodeLocation")))[0] === "/Local/Default"){ 339 | continue; 340 | } 341 | output[name_index] = {}; 342 | for(var j = 0; j < keys.count; j++){ 343 | var key = ObjC.unwrap(keys.objectAtIndex(j)); 344 | var array = attributes.valueForKey(keys.objectAtIndex(j)); 345 | var array_length = parseInt($.CFArrayGetCount(array)); 346 | var val = []; 347 | for(var k = 0; k < array_length; k++){ 348 | if(fixed_return_attributes === null || fixed_return_attributes.includes("dsAttributesAll") || fixed_return_attributes.includes(keys.objectAtIndex(j).js)){ 349 | if(!array.objectAtIndex(k).isKindOfClass($.NSString.class)){ 350 | //console.log(array.objectAtIndex(k).base64EncodedStringWithOptions(null).js); 351 | val.push(array.objectAtIndex(k).base64EncodedStringWithOptions(null).js); 352 | }else{ 353 | //console.log(array.objectAtIndex(k)); 354 | val.push(array.objectAtIndex(k).js); 355 | } 356 | } 357 | 358 | } 359 | //var val = ObjC.deepUnwrap(attributes.valueForKey(keys.objectAtIndex(j))); 360 | if(val.length > 0){ 361 | output[name_index][key] = val; 362 | } 363 | } 364 | if(Object.keys(output[name_index]).length === 0){ 365 | delete output[name_index]; 366 | } 367 | } 368 | return output; 369 | } 370 | function Get_OD_Node_Configuration({node="all", return_text=true} = {}){ 371 | let session = $.ODSession.defaultSession; 372 | let names = session.nodeNamesAndReturnError($()); 373 | //console.log(names); 374 | names = ObjC.deepUnwrap(names); 375 | let configuration = {}; 376 | for(let i in names){ 377 | //console.log(names[i]); 378 | let config = session.configurationForNodename(names[i]); 379 | configuration[names[i]] = {}; 380 | if(config.nodeName.js !== undefined){ 381 | configuration[names[i]]['nodeName'] = config.nodeName.js; 382 | } 383 | configuration[names[i]]['trustAccount'] = ObjC.deepUnwrap(config.trustAccount); 384 | configuration[names[i]]['trustKerberosPrincipal'] = ObjC.deepUnwrap(config.trustKerberosPrincipal); 385 | configuration[names[i]]['trustMetaAccount'] = ObjC.deepUnwrap(config.trustMetaAccount); 386 | configuration[names[i]]['trustType'] = ObjC.deepUnwrap(config.trustType); 387 | configuration[names[i]]['trustUsesKerberosKeytab'] = config.trustUsesKerberosKeytab; 388 | configuration[names[i]]['trustUsesMutualAuthentication'] = ObjC.deepUnwrap(config.trustUsesMutualAuthentication); 389 | configuration[names[i]]['trustUsesSystemKeychain'] = ObjC.deepUnwrap(config.trustUsesSystemKeychain); 390 | if(config.defaultModuleEntries !== undefined){ 391 | configuration[names[i]]['defaultMappings'] = ObjC.deepUnwrap(config.defaultModuleEntries); 392 | } 393 | if(config.authenticationModuleEntries !== undefined){ 394 | configuration[names[i]]['authenticationModuleEntries'] = config.authenticationModuleEntries; 395 | } 396 | configuration[names[i]]['virtualSubnodes'] = ObjC.deepUnwrap(config.virtualSubnodes); 397 | configuration[names[i]]['templateName'] = ObjC.deepUnwrap(config.templateName); 398 | configuration[names[i]]['preferredDestinationHostName'] = ObjC.deepUnwrap(config.preferredDestinationHostName); 399 | configuration[names[i]]['preferredDestinationHostPort'] = ObjC.deepUnwrap(config.preferredDestinationHostPort); 400 | if(config.discoveryModuleEntries !== undefined){ 401 | configuration[names[i]]['discoveryModuleEntries'] = ObjC.deepUnwrap(config.discoveryModuleEntries); 402 | } 403 | } 404 | //node = $.ODNode.nodeWithSessionTypeError(session, $.kODNodeTypeLocalNodes, null); 405 | node = $.ODNode.nodeWithSessionTypeError(session, 0x2200, null); 406 | //var policies = $.ODNodeCopyAccountPolicies(node, $()); 407 | let policies = node.accountPoliciesAndReturnError($()); 408 | if(policies.js !== undefined){ 409 | configuration['Local_policies'] = ObjC.deepUnwrap(policies); 410 | } 411 | //node = $.ODNode.nodeWithSessionTypeError(session, $.kODNodeTypeNetwork, null); 412 | node = $.ODNode.nodeWithSessionTypeError(session, 0x2205, null); 413 | //var policies = $.ODNodeCopyAccountPolicies(node, $()); 414 | policies = node.accountPoliciesAndReturnError($()); 415 | if(policies.js !== undefined){ 416 | configuration['Network_policies'] = ObjC.deepUnwrap(policies); 417 | } 418 | if(return_text){ 419 | return JSON.stringify(configuration, null, 2); 420 | }else{ 421 | return configuration; 422 | } 423 | } 424 | // main functions 425 | function ConvertTo_SID({object=".\\root", type="Users",help=false} = {}){ 426 | //goes from "Domain\User" or "Domain\Group" or "Domain\Computer" to SID 427 | //type should be: Users, Groups, or Computers 428 | if(help){ 429 | var output = ""; 430 | output += "\\nConvert Users, Groups, Or Computers to domain or local SIDs."; 431 | output += "\\n\"object\" should be either \".\\\\localthing\" or \"NETBIOSDOMAIN\\\\thing\""; 432 | output += "\\n\"type\" should be \"Users\", \"Groups\", or \"Computers\""; 433 | output += "\\ncalled: ConvertTo_SID({object:\".\\\\root\",type:\"Users\"});"; 434 | return output; 435 | } 436 | splitObject = object.split('\\'); 437 | if (splitObject.length != 2) 438 | { 439 | return "Invalid format for the object. Should be DOMAIN\\object\n"; 440 | } 441 | //Use ObjC calls 442 | if(object.includes(".")){ 443 | //we need to do a local query instead 444 | var fixed_query = object.split("\\").slice(1); 445 | var query = Get_OD_ObjectClass({objectclass:type, max_results:1, value:fixed_query, match:"EqualTo", query_attributes:"RecordName", return_attributes:["SMBSID"], nodetype:"local"}); 446 | }else{ 447 | var query = Get_OD_ObjectClass({objectclass:type, max_results:1, value:object, match:"EqualTo", query_attributes:"RecordName", return_attributes:["SMBSID"]}); 448 | } 449 | try{ 450 | let keys = Object.keys(query); 451 | for(let i in keys){ 452 | return query[keys[i]]["dsAttrTypeStandard:SMBSID"][0]; 453 | } 454 | }catch(err){ 455 | return "No such object"; 456 | } 457 | } 458 | function ConvertFrom_SID({sid="S-1-5-21-3278496235-3004902057-1244587532-512", type="Users",help=false} = {}){ 459 | //goes from S-1-5-21-... to "Domain\User", "Domain\Group", or "Domain\Computer" 460 | if(help){ 461 | var output = ""; 462 | output += "\\nConvert Users, Groups, or Computers from SIDs to names"; 463 | output += "\\n\"sid\" should be a full SID value in quotes for either a User, Group, or Computer. No other type is currently supported."; 464 | output += "\\n\"type\" should be \"Users\",\"Groups\", or \"Computers\""; 465 | output += "\\ncalled: ConvertFrom_SID({sid:\"S-1-5-21-3278496235-3004902057-1244587532-512\",type:\"Users\"})"; 466 | return output; 467 | } 468 | var query = Get_OD_ObjectClass({objectclass:type, max_results:1, value:sid, match:"EqualTo", query_attributes:"SMBSID", return_attributes:["RecordName"]}); 469 | try{ 470 | let keys = Object.keys(query); 471 | for(let i in keys){ 472 | return keys[i]; 473 | } 474 | }catch(err){ 475 | return "No such object"; 476 | } 477 | } 478 | function Get_DomainUser({match_attribute="recordname", match_attribute_value, return_attributes_list=[null], limit=0, help=false} = {}){ 479 | //returns all users or specific user objects in AD 480 | //can specify different properties they want returned 481 | if(help){ 482 | var output = ""; 483 | output += "\\nList all domain users or get information on a specific user. If no match_attribute_value is specified, list all users."; 484 | output += "\\n\"match_attribute\" should be what field you want to search on, by default it's \"recordname\""; 485 | output += "\\n\"match_attribute_value\" should be the field you want to filter on, so if you're looking for a specific user, it's the username."; 486 | output += "\\n\"return_attributes_list\" should be a list of the attributes you want back."; 487 | output += "\\ncalled: Get_DomainUser() <--- list out all domain users"; 488 | output += "\\ncalled: Get_DomainUser({match_attribute_value:\"bob\",return_attributes_list:[\"name\", \"SMBSID\"]});"; 489 | output += "\\nNote: cannot currently query outside of the current forest"; 490 | return output; 491 | } 492 | let query = Get_OD_ObjectClass({value:match_attribute_value, match:"Contains", query_attributes:match_attribute, max_results:limit, return_attributes:return_attributes_list}); 493 | return JSON.stringify(query, null, 2); 494 | } 495 | function Get_LocalUser({match_attribute="recordname", match_attribute_value, return_attributes_list=[null], limit=0, help=false} = {}){ 496 | //returns all users or specific user objects in AD 497 | //can specify different properties they want returned 498 | if(help){ 499 | var output = ""; 500 | output += "\\nList all local users or get information on a specific user. If no match_attribute_value is specified, list all users."; 501 | output += "\\n\"match_attribute\" should be what field you want to search on, by default it's \"recordname\""; 502 | output += "\\n\"match_attribute_value\" should be the field you want to filter on, so if you're looking for a specific user, it's the username."; 503 | output += "\\n\"return_attributes_list\" should be a list of the attributes you want back."; 504 | output += "\\ncalled: Get_LocalUser() <--- list out all domain users"; 505 | output += "\\ncalled: Get_LocalUser({match_attribute_value:\"bob\",return_attributes_list:[\"name\", \"SMBSID\"]});"; 506 | output += "\\nNote: cannot currently query outside of the current forest"; 507 | return output; 508 | return output; 509 | } 510 | let query = Get_OD_ObjectClass({value:match_attribute_value, match:"Contains", query_attributes:match_attribute, max_results:limit, return_attributes:return_attributes_list, nodetype:"local"}); 511 | return JSON.stringify(query, null, 2); 512 | } 513 | function Get_DomainComputer({match_attribute="recordname", match_attribute_value, return_attributes_list=[null], limit=0, help=false} = {}){ 514 | //returns all computers or specific computer objects in AD 515 | if(help){ 516 | var output = ""; 517 | output += "\\nList all domain computers or get information on a specific computer. If no match_attribute_value is specified, list all computers."; 518 | output += "\\n\"match_attribute\" should be what field you want to search on, by default it's \"recordname\""; 519 | output += "\\n\"match_attribute_value\" should be the field you want to filter on, so if you're looking for a specific computer, it's the hostname."; 520 | output += "\\n\"return_attributes_list\" should be a list of the attributes you want back."; 521 | output += "\\ncalled: Get_DomainComputer() <--- list out all domain computers"; 522 | output += "\\ncalled: Get_DomainComputer({match_attribute_value:\"testmac$\",match_attribute:\"name\"});"; 523 | return output; 524 | } 525 | let query = Get_OD_ObjectClass({objectclass:"Computers", value:match_attribute_value, match:"Contains", query_attributes:match_attribute, max_results:limit, return_attributes:return_attributes_list, nodetype:"local"}); 526 | return JSON.stringify(query, null, 2); 527 | } 528 | function Get_DomainSID({help=false} = {}){ 529 | //returns SID for current domain or specified domain 530 | if(help){ 531 | var output = ""; 532 | output += "\\nGets the SID of the domain by truncating the SID for the \"Domain Admins\" group."; 533 | output += "\\ncalled: Get_DomainSID()"; 534 | return output; 535 | } 536 | var domain = Get_CurrentNETBIOSDomain(); 537 | if(domain === ""){ 538 | return "Failed to get Domain"; 539 | } 540 | var search_value = domain + "\\Domain Computers"; 541 | var domain_computers = Get_OD_ObjectClass({objectclass:"Groups", max_results:1, value:search_value, match:"Contains", query_attributes:"RecordName", return_attributes:["SMBSID"]}); 542 | var sid = domain_computers[search_value]["dsAttrTypeStandard:SMBSID"][0]; 543 | var sid_array = sid.split("-"); 544 | return sid_array.slice(0, sid_array.length-1).join("-"); 545 | } 546 | function Get_DomainGroup({match_attribute="recordname", match_attribute_value, return_attributes_list=[null], limit=0, help=false} = {}){ 547 | //returns all groups or specific group objects in AD 548 | if(help){ 549 | var output = ""; 550 | output += "\\nList all domain groups or get information on a specific group. If no match_attribute_value is specified, list all groups."; 551 | output += "\\n\"match_attribute\" should be what field you want to search on, by default it's \"recordname\""; 552 | output += "\\n\"match_attribute_value\" should be the field you want to filter on, so if you're looking for a specific group, it's the group name."; 553 | output += "\\n\"return_attributes_list\" should be a list of the attributes you want back."; 554 | output += "\\ncalled: Get_DomainGroup() <--- list out all domain groups"; 555 | output += "\\ncalled: Get_DomainGroup({match_attribute_value:\"Domain Admins\",match_attribute:\"name\"});"; 556 | return output; 557 | } 558 | let query = Get_OD_ObjectClass({objectclass:"Groups", value:match_attribute_value, match:"Contains", query_attributes:match_attribute, max_results:limit, return_attributes:return_attributes_list}); 559 | return JSON.stringify(query, null, 2); 560 | } 561 | function Get_LocalGroup({match_attribute="recordname", match_attribute_value, requested_domain, return_attributes_list=[null], limit=0, help=false} = {}){ 562 | //returns all groups or specific groups in an AD 563 | if(help){ 564 | var output = ""; 565 | output += "\\nList all local groups or get information on a specific group. If no match_attribute_value is specified, list all groups."; 566 | output += "\\n\"match_attribute\" should be what field you want to search on, by default it's \"recordname\""; 567 | output += "\\n\"match_attribute_value\" should be the field you want to filter on, so if you're looking for a specific group, it's the group name."; 568 | output += "\\n\"return_attributes_list\" should be a list of the attributes you want back."; 569 | output += "\\ncalled: Get_LocalUser() <--- list out all local groups"; 570 | output += "\\ncalled: Get_LocalUser({match_attribute_value:\"bob\",return_attributes_list:[\"name\", \"SMBSID\"]});"; 571 | output += "\\nNote: cannot currently query outside of the current forest"; 572 | return output; 573 | } 574 | let query = Get_OD_ObjectClass({objectclass:"Groups",value:match_attribute_value, match:"Contains", query_attributes:match_attribute, max_results:limit, return_attributes:return_attributes_list, nodetype:"local"}); 575 | return JSON.stringify(query, null, 2); 576 | } 577 | function Get_DomainGroupMember({ group="Domain Admins", domain,help=false, limit=0} = {}){ 578 | if(help){ 579 | var output = ""; 580 | output += "\\nGet all the members of a specific domain group"; 581 | output += "\\n\"group\" should be a specific domain group to query."; 582 | output += "\\n\"domain\" is the NETBIOS domain name to query, but if not specified, the function will figure it out."; 583 | output += "\\ncalled: Get_DomainGroupMember({group:\"Domain Admins\"});"; 584 | return output; 585 | } 586 | return Get_DomainGroup({match_attribute_value:group, return_attributes_list:["distinguishedName","member","memberOf","nestedgroups","groupmembership"], limit:limit}); 587 | } 588 | function Get_LocalGroupMember({group,help=false, limit=0} = {}){ 589 | if(help){ 590 | var output = ""; 591 | output += "\\nGet all the members of a specific local group"; 592 | output += "\\n\"group\" should be a specific local group to query."; 593 | output += "\\ncalled: Get_LocalGroupMember({group:\"admin\"});"; 594 | return output; 595 | } 596 | return Get_LocalGroup({match_attribute_value:group, return_attributes_list:["distinguishedName","member","memberOf","nestedgroups","groupmembership"], limit:limit}); 597 | } 598 | //////////////////////////////////////////////// 599 | ///////// HELPER FUNCTIONS ///////////////////// 600 | //////////////////////////////////////////////// 601 | function Get_CurrentDomain(help=false){ 602 | if(help){ 603 | var output = ""; 604 | output += "\\nGet the fully qualified current domain"; 605 | output += "\\ncalled: Get_CurrentDomain();"; 606 | return output; 607 | } 608 | var config = Get_OD_Node_Configuration({return_text:false}); 609 | var keys = Object.keys(config); 610 | for(var i in keys){ 611 | if(config[keys[i]]['nodeName'] != "Contacts" && config[keys[i]]['nodeName'] != "Search" && config[keys[i]]['nodeName']){ 612 | return config[keys[i]]['trustKerberosPrincipal'].split("@")[1]; 613 | } 614 | } 615 | return ""; 616 | } 617 | function Get_CurrentNETBIOSDomain(help=false){ 618 | if(help){ 619 | let output = ""; 620 | output += "\\nGet the NETBIOS name of the current domain"; 621 | output += "\\ncalled: Get_CurrentNETBIOSDomain();"; 622 | return output; 623 | } 624 | let config = Get_OD_Node_Configuration({return_text:false}); 625 | let keys = Object.keys(config); 626 | for(let i in keys){ 627 | if(config[keys[i]].hasOwnProperty('nodeName') && config[keys[i]]['nodeName'] != "Contacts" && config[keys[i]]['nodeName'] != "Search"){ 628 | return config[keys[i]]['nodeName']; 629 | } 630 | } 631 | return ""; 632 | } 633 | function Get_Forest(API=false,help=false){ 634 | if(help){ 635 | var output = ""; 636 | output += "\\nGet the fully qualified forest name"; 637 | output += "\\ncalled: Get_Forest();"; 638 | return output; 639 | } 640 | if(API === true){ 641 | return "API method not implemented yet"; 642 | } 643 | else{ 644 | try{ 645 | output = currApp.doShellScript("dsconfigad -show"); 646 | //Active Directory Forest = forest.tld 647 | //Active Directory Domain = domain.tld 648 | //Computer Account = computer-name 649 | //a bunch of others with (something = something) format 650 | //Look into Advanced Options - Administrative 651 | // preferred domain controller, allowed admin group 652 | components = output.split("\r"); 653 | forest = components[0].split("=")[1].trim(); 654 | return forest; 655 | } 656 | catch(err){ 657 | return err.toString(); 658 | } 659 | } 660 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Orchard 2 | JavaScript for Automation (JXA) tool to do Active Directory enumeration. Current version: 1.3 3 | 4 | # Purpose 5 | Live off the land for macOS. This program allows users to do Active Directory enumeration via macOS' JXA (JavaScript for Automation) code. This is the newest version of AppleScript, and thus has very poor documentation on the web. 6 | 7 | # Execution 8 | Host the Orchard.js code somewhere you can access and pull it into a scripting session like so: 9 | ```JavaScript 10 | testmac: ~$ osascript -l JavaScript -i 11 | >> eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfURL($.NSURL.URLWithString('https://raw.githubusercontent.com/its-a-feature/Orchard/master/Orchard.js')),$.NSUTF8StringEncoding))); 12 | => true 13 | >> Get_CurrentDomain(); 14 | => "test.local" 15 | >> 16 | ``` 17 | This requires the user to already have execution on the machine. Additionally, the capitalization for JavaScript in the first command is extremely important. **All of JXA is case sensitive.** The second line does an HTTP GET request for Orchard.js and loads the functions into the current session. If you just want to execute a single line (if there's only one function you want to execute for example) you can do the following instead: 18 | ```JavaScript 19 | testmac: ~$ osascript -l JavaScript -e "eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfURL($.NSURL.URLWithString('https://raw.githubusercontent.com/its-a-feature/Orchard/master/Orchard.js')),$.NSUTF8StringEncoding))); Get_CurrentDomain();" 20 | ``` 21 | 22 | ## Calling a Function 23 | After you've imported the Orchard.js code into your current session, you can call any of the functions with a slight tweak to normal JavaScript function calls. I wanted to make the code a bit more python-esque, so I modified the calling convention. You'll see functions defined as: 24 | 25 | `function ConvertTo_SID({object=".\\root", type="Users"} = {}) { code here; }`. This allows me to provide default values (if desired) to each function parameter as well as preventing them from being positional arguments like standard function calls. To call this function, simply do any of the following: 26 | ```JavaScript 27 | ConvertTo_SID(); //Uses all of the default values to call the function 28 | ConvertTo_SID({object:"TEST\\Bob"}); //Sets object and leaves type as "Users" 29 | ConvertTo_SID({object:"TEST\\Bob"}); //same as above - the order of the arguments doesn't matter 30 | ``` 31 | **Note1:** In defining a function this way, it's "name=value", but when calling the function it's "name:value". It's a little odd, I know, but you're most likely going to be just calling functions anyway. 32 | 33 | **Note2:** Every function has a Help flag that can be used to get information about how to run the function 34 | 35 | **Note3:** All of the APIs currently take advantage of the OpenDirectory APIs that are available through the JXA-ObjC bridge. These can be pretty powerful, but at the moment means that I can only query information within the current forest. These functions will by default query all domains within the forest. For exmaple: If I'm running on a computer, spooky$, in test.lab.local, then my queries will search the `test.lab.local` and `lab.local` domains automatically. I couldn't find a way to specify a specific server outside the forest to query though, so I cannot query a separate forest that you might have trust with. 36 | 37 | # Functions 38 | | Function | Version Introduced | Description| API Version is Default| 39 | | ---------|:------------------|:-----------|:--------| 40 | | ConvertTo_SID |1.2 |Convert Users, Groups, Or Computers to domain or local SIDs | True | 41 | | ConvertFrom_SID |1.2 |Convert Users, Groups, or Computers from SIDs to names | True | 42 | | Get_DomainUser |1.2 |List all domain users or get information on a specific user | True | 43 | | Get_DomainComputer |1.2 |List all domain computers or get information on a specific computer | True | 44 | | Get_DomainSID |1.2 |Gets the SID of the domain by truncating the SID for the "Domain Admins" group | True | 45 | | Get_DomainGroup |1.2 |List all domain groups or get information on a specific group | True | 46 | | Get_DomainGroupMember |1.2 |Get all the members of a specific domain group | True | 47 | | Get_CurrentDomain |1.2 |Get the fully qualified current domain | True | 48 | | Get_CurrentNETBIOSDomain |1.2 |Get the NETBIOS name of the current domain | True | 49 | | Get_LocalUser | 1.2 | List all local user or get information on a specific user | True | 50 | | Get_LocalGroup | 1.2 | List all local groups or get information on a specific group | True | 51 | | Get_LocalGroupMember | 1.2 | Get all members for a specific local group | True | 52 | | Get_OD_ObjectClass | 1.2 | Use the OpenDirectory APIs to query the domain, similar to LDAP | True | 53 | | Get_Forest | 1.3 | Use `dsconfigad` to get the name of the forest by running it via bash on the command line | False 54 | 55 | # Sample Outputs and Common Attributes 56 | These are some common attributes I've seen that might be useful to query: 57 | ## Users / Groups / Computers 58 | The following are specific commands, parameters, and outputs that are useful for working with users both locally and in a domain. 59 | 60 | ``` 61 | Get_DomainUser({match_attribute="recordname", match_attribute_value, return_attributes_list=[null], limit=0, help=false} = {}); 62 | ``` 63 | If you want to get all domain users: 64 | `Get_DomainUser();` 65 | If you want to get all information for a specific user: 66 | `Get_DomainUser({match_attribute_value:"username"}); 67 | If you want to get just the SMBSID for a specific user: 68 | `Get_DomainUser({match_attribute_value:"bob", return_attributes_list:["SMBSID"]});` 69 | If you want to look for users that container a certain attribute: 70 | `Get_DomainUser({match_attribute:"HomeDirectory", match_attribute_value:"\\", return_attributes_list:["samaccountname", "HomeDirectory"]});` 71 | 72 | The Local versions function exactly the same way: 73 | ``` 74 | function Get_LocalUser({match_attribute="recordname", match_attribute_value, return_attributes_list=[null], limit=0, help=false} = {}) 75 | ``` 76 | 77 | Apple's OpenDirectory standard is really weird and picky. When picking a main object class to query, you can select from any of the following on the left-hand side: 78 | ``` 79 | "AFPUserAliases": $.kODRecordTypeAFPUserAliases, 80 | "Aliases": $.kODRecordTypeAliases, 81 | "AutoMount": $.kODRecordTypeAutoMount, 82 | "AutomountMap": $.kODRecordTypeAutoMountMap, 83 | "CertificateAuthorities": $.kODRecordTypeCertificateAuthorities, 84 | "ComputerGroups": $.kODRecordTypeComputerGroups, 85 | "ComputerLists": $.kODRecordTypeComputerLists, 86 | "Computers": $.kODRecordTypeComputers, 87 | "Config": $.kODRecordTypeConfig, 88 | "Ethernets": $.kODRecordTypeEthernets, 89 | "FileMakerServers": $.kODRecordTypeFileMakerServers, 90 | "Groups": $.kODRecordTypeGroups, 91 | "Hosts": $.kODRecordTypeHosts, 92 | "Maps": $.kODRecordTypeMaps, 93 | "Mounts": $.kODRecordTypeMounts, 94 | "NetGroups": $.kODRecordTypeNetGroups, 95 | "Networks": $.kODRecordTypeNetworks, 96 | "OrganizationalUnit": $.kODRecordTypeOrganizationalUnit, 97 | "People": $.kODRecordTypePeople, 98 | "Places": $.kODRecordTypePlaces, 99 | "Printers": $.kODRecordTypePrinters, 100 | "Protocols": $.kODRecordTypeProtocols, 101 | "RPC": $.kODRecordTypeRPC, 102 | "Resources": $.kODRecordTypeResources, 103 | "Services": $.kODRecordTypeServices, 104 | "SharePoints": $.kODRecordTypeSharePoints, 105 | "Users": $.kODRecordTypeUsers, 106 | ``` 107 | There are more possibilities (listed below these in the actual code), but these are the only ones I saw that were supported. You can generally think of these as the `objectclass` in a standard ldap query (i.e. `(&(objectclass=user)(name=*admin*))`. 108 | When it comes to matching values, you can select any of the following match types: 109 | ``` 110 | "Any": $.kODMatchAny, 111 | "BeginsWith": $.kODMatchInsensitiveBeginsWith, 112 | "EndsWith": $.kODMatchInsensitiveEndsWith, 113 | "Contains": $.kODMatchInsensitiveContains, 114 | "EqualTo": $.kODMatchInsensitiveEqualTo, 115 | "LessThan": $.kODMatchLessThan, 116 | "GreaterThan": $.kODMatchGreaterThan 117 | ``` 118 | The most annoying part is the `query_attribute`. If you look in the code for this function, you'll see `var attributes_list = ` and a big list. When you want to use a match type other than `Any` with a specific field, the field **MUST** have a corresponding `$.kODAttributeType` field. If this doesn't exist, you can't match on it. For example, `accountExpires` is a valid property to return, but it cannot be used in your selection criteria because it doesn't have a `$.kODAttributeType` field. If anybody is able to help fill in the missing appropriate `kODAttributeType` values, that would be much appreciated! 119 | --------------------------------------------------------------------------------