541 Recipe 17.9 Delegating Control of Managing an Application Partition 17.9.1 Problem You want to delegate control over the management of an application partition. 17.9.2 Solution 17.9.2.1 Using a graphical user interface 1. Open ADSI Edit. 2. Connect to the Configuration naming context of the forest the application partition is in if it is not already present in the left pane. 3. Expand the Configuration naming context and click on the Partitions container. 4. In the right pane, right-click on the crossRef object that represents the application partition and select Properties. 5. Click the Security tab. 6. Click the Advanced button. 7. Click the Add button. 8. Use the object picker to find the user or group you want to delegate control to and click OK. 9. Click the Properties tab. 10. Under Allow, check the boxes beside Write msDS-NC-Replica-Locations, Write msDS- SDReferenceDomain, Write msDS-Replication-Notify-First-DSA-Delay, and Write msDS-Replication-Notify-Subsequent-DSA-Delay. 11. Click OK. 17.9.2.2 Using a command-line interface > dsacls <AppPartitionCrossRefDN> /G <UserOrGroup>:RPWP;msDS-NC-Replica- Locations > dsacls <AppPartitionCrossRefDN> /G <UserOrGroup>:RPWP;msDS- SDReferenceDomain > dsacls <AppPartitionCrossRefDN> /G <UserOrGroup>:RPWP;msDS-Replication- Notify-[RETURN] First-DSA-Delay > dsacls <AppPartitionCrossRefDN> /G <UserOrGroup>:RPWP;msDS-Replication- Notify-[RETURN] Subsequent-DSA-Delay 17.9.2.3 Using VBScript ' This script delegates control over the four key attributes ' of an app partition to the specified user or group. ' SCRIPT CONFIGURATION ' Fully qualified DNS name of app partition strAppPart = "<AppPartitionFQDN>" ' e.g. apps.rallencorp.com ' User or group to delegate control to strUser = "<UserOrGroup>" ' e.g. joe@rallencorp.com or RALLENCORP\joe 542 ' END CONFIGURATION '############################ ' Constants '############################ ' ADS_ACETYPE_ENUM Const ADS_ACETYPE_ACCESS_ALLOWED = &h0 Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &h5 ' ADS_FLAGTYPE_ENUM Const ADS_FLAG_OBJECT_TYPE_PRESENT = &h1 ' ADS_RIGHTS_ENUM Const ADS_RIGHT_DS_WRITE_PROP = &h20 Const ADS_RIGHT_DS_READ_PROP = &h10 ' schemaIDGUID values Const REPLICA_LOCATIONS = "{97de9615-b537-46bc-ac0f-10720f3909f3}" Const SDREFERENCEDOMAIN = "{4c51e316-f628-43a5-b06b-ffb695fcb4f3}" Const NOTIFY_FIRST_DSA_DELAY = "{85abd4f4-0a89-4e49-bdec-6f35bb2562ba}" Const NOTIFY_SUBSEQUENT_DSA_DELAY = "{d63db385-dd92-4b52-b1d8-0d3ecc0e86b6}" '############################ ' Find App Partition '############################ set objRootDSE = GetObject("LDAP://RootDSE") strBase = "<LDAP://cn=Partitions," & _ objRootDSE.Get("ConfigurationNamingContext") & ">;" strFilter = "(&(objectcategory=crossRef)(dnsRoot=" & _ strAppPart & "));" strAttrs = "cn,distinguishedName;" strScope = "onelevel" set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADsDSOObject" objConn.Open "Active Directory Provider" Set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope) if objRS.RecordCount <> 1 then WScript.Echo "Did not find a match for " & strAppPart else objRS.MoveLast set objAppPart = GetObject("LDAP://" & _ objRS.Fields("distinguishedName").Value ) end if '############################ ' Create ACL '############################ set objSD = objAppPart.Get("ntSecurityDescriptor") set objDACL = objSD.DiscretionaryAcl ' Read/Write Property: msDS-NC-Replica-Locations set objACE1 = CreateObject("AccessControlEntry") objACE1.Trustee = strUser objACE1.AccessMask = ADS_RIGHT_DS_WRITE_PROP Or ADS_RIGHT_DS_READ_PROP 543 objACE1.AceFlags = 0 objACE1.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT objACE1.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT objACE1.ObjectType = REPLICA_LOCATIONS ' ' Read/Write Property: msDS-SDReferenceDomain set objACE2 = CreateObject("AccessControlEntry") objACE2.Trustee = strUser objACE2.AccessMask = ADS_RIGHT_DS_WRITE_PROP Or ADS_RIGHT_DS_READ_PROP objACE2.AceFlags = 0 objACE2.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT objACE2.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT objACE2.ObjectType = SDREFERENCEDOMAIN ' Read/Write Property: msDS-Replication-Notify-First-DSA-Delay set objACE3 = CreateObject("AccessControlEntry") objACE3.Trustee = strUser objACE3.AccessMask = ADS_RIGHT_DS_WRITE_PROP Or ADS_RIGHT_DS_READ_PROP objACE3.AceFlags = 0 objACE3.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT objACE3.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT objACE3.ObjectType = NOTIFY_FIRST_DSA_DELAY ' Read/Write Property: msDS-Replication-Notify-Subsequent-DSA-Delay set objACE4 = CreateObject("AccessControlEntry") objACE4.Trustee = strUser objACE4.AccessMask = ADS_RIGHT_DS_WRITE_PROP Or ADS_RIGHT_DS_READ_PROP objACE4.AceFlags = 0 objACE4.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT objACE4.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT objACE4.ObjectType = NOTIFY_SUBSEQUENT_DSA_DELAY objDACL.AddAce objACE1 objDACL.AddAce objACE2 objDACL.AddAce objACE3 objDACL.AddAce objACE4 '############################ ' Set ACL '############################ objSD.DiscretionaryAcl = objDACL objAppPart.Put "ntSecurityDescriptor", objSD objAppPart.SetInfo WScript.Echo "Delegated control of " & strAppPart & " to " & strUser 17.9.3 Discussion If you want to delegate control of management of application partitions, you must grant control over four key attributes. Here is a description of each attribute and what can be accomplished by having control over it: msDS-NC-Replica-Locations 544 By having write access to this attribute, a user can add replica servers for the application partition. See Recipe 17.3 for more information. msDS-SDReferenceDomain By having write access to this attribute, a user can define the default security descriptor domain for the application partition. See Recipe 17.8 for more information. msDS-Replication-Notify-First-DSA-Delay See Recipe 17.7 for more information. msDS-Replication-Notify-Subsequent-DSA-Delay See Recipe 17.7 for more information. If you want to delegate control over managing objects within the application partition, you need to follow the same procedures you would when delegating control over objects in a domain naming context. See Recipe 14.5 for more information on delegating control. 17.9.4 See Also Recipe 14.5 for delegating control, Recipe 17.3 for more on adding and removing replica servers, Recipe 17.7 for more on the replication delay attributes, and Recipe 17.8 for more on the default security descriptor domain 545 Chapter 18. Interoperability and Integration Introduction Recipe 18.1. Accessing AD from a Non-Windows Platform Recipe 18.2. Programming with .NET Recipe 18.3. Programming with DSML Recipe 18.4. Programming with Perl Recipe 18.5. Programming with Java Recipe 18.6. Programming with Python Recipe 18.7. Integrating with MIT Kerberos Recipe 18.8. Integrating with Samba Recipe 18.9. Integrating with Apache Recipe 18.10. Replacing NIS Recipe 18.11. Using BIND for DNS Recipe 18.12. Authorizing a Microsoft DHCP Server Recipe 18.13. Using VMWare for Testing AD Introduction Active Directory supports several important industry standards, which allow other services and platforms to interoperate and integrate with it. The Lightweight Directory Access Protocol (LDAP) is the standards-based protocol used by all major directory service vendors for directory access and management. LDAP is platform neutral, which means you can access and manage data in Active Directory from a variety of platforms. Active Directory uses the Domain Name System (DNS) for its name resolution services so you can use tools, such as nslookup, to locate domain controllers by making DNS queries. Kerberos is the most widely used network authentication protocol and is supported by Active Directory, so even non-Windows-based Kerberos-enabled clients can authenticate. These are just a few of the standards Active Directory 546 supports. Throughout this chapter I will cover how you can access, manage, and integrate Active Directory in ways that are not typically documented. Recipe 18.1 Accessing AD from a Non-Windows Platform 18.1.1 Problem You want to access or manage AD from a non-Windows platform. 18.1.2 Solution 18.1.2.1 Using a graphical user interface One of the best platform-neutral graphical user interfaces for managing an LDAP directory, such as Active Directory, is the LDAP Browser/Editor. It was written in Java and can run on virtually any machine that has Java 1.2.2 or greater installed. It can be downloaded from the following site: http://www.iit.edu/~gawojar/ldap/. 18.1.2.2 Using a command-line interface The original LDAP server produced at the University of Michigan included a set of command- line utilities that can query and update an LDAP directory. Over time these tools have become very popular on the UNIX platforms, and they can even be used to query and update Active Directory. The OpenLDAP project took over maintenance of the University of Michigan's LDAP server and also the command-line tools. To download the latest version of the tools, go to the following site: http://www.openldap.org/. 18.1.2.3 Using a programming language Any programming language that supports LDAP can be used to programmatically access and manage Active Directory. See the other recipes in this chapter for using Perl, Python, Java, and PHP. 18.1.3 Discussion Due to the fact that LDAP is an open standard, it has been adopted on many platforms and programming languages. While you can perform 90% of the things you would need to do from a non-Windows platform, some tasks do still require a Windows GUI, CLI, or API. For example, there is no easy way to manage ACLs in Active Directory from a non-Windows platform. You can, however, do virtually anything you need to do as far as adding, modifying, and removing objects in Active Directory with the basic LDAP-enabled tools. 547 18.1.4 See Also Recipe 18.4, Recipe 18.5, and Recipe 18.6 for more on how to programmatically query and update Active Directory using Perl, Java, and Python Recipe 18.2 Programming with .NET 18.2.1 Problem You want to programmatically access Active Directory using the .NET Framework. 18.2.2 Solution The System.DirectoryServices namespace can be used to interface with Active Directory using the .NET Framework. The following code is a simple VB.NET program that prints the attributes of the RootDSE: Imports System.DirectoryServices Module Module1 Sub Main( ) Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE") Dim strAttrName As String Dim objValue As Object For Each strAttrName In objRootDSE.Properties.PropertyNames For Each objValue In objRootDSE.Properties(strAttrName) Console.WriteLine(strAttrName & " : " & objValue.ToString) Next objValue Next strAttrName End Sub End Module 18.2.3 Discussion The System.DirectoryServices namespace is a generic directory service interface that is intended to replace ADSI. It provides a rich set of properties and methods for accessing, querying, and manipulating objects in Active Directory. Currently, there is no native support for scripting languages, such as VBScript and Perl, but you can use Microsoft's version of JavaScript (i.e., JScript) with .NET to utilize System.DirectoryServices. The System.DirectoryServices DirectorySearcher class is a simple interface for making LDAP queries. The DirectoryEntry class is used for instantiating existing objects or creating new ones. In the Solution section, I used the DirectoryEntry class to access the RootDSE. 548 DirectorySearcher and DirectoryEntry are the two main classes to become familiar with if you want to do Active Directory programming with .NET. For more information and examples on using System.DirectoryServices, see Chapter 28 of Active Directory, Second Edition (O'Reilly). System.DirectoryServices does not currently provide interfaces for everything that could be done with ADSI. Instead, you can use the NativeObject property on an instantiated object to return an ADSI object, which you can then use to access any ADSI properties or methods for the object. 18.2.4 See Also Chapter 28 in Active Directory, Second Edition (O'Reilly) and System.DirectoryServices reference information can be found at http://msdn.microsoft.com/library/en- us/cpref/html/frlrfSystemDirectoryServices.asp Recipe 18.3 Programming with DSML 18.3.1 Problem You want to programmatically access Active Directory using the Directory Services Markup Language (DSML). DSML is the answer for all programmers who have been longing for an XML-based interface to query and access a directory. 18.3.2 Solution To use DSML with Active Directory, you have to install the Windows DSML client (DSFW) on a Windows 2000 or Windows Server 2003 computer that is running IIS. The DSML client can be downloaded from the following site: http://www.microsoft.com/windows2000/server/evaluation/news/bulletins/dsml.asp . If you are installing the client on a Windows 2000 machine, you will also need to make sure MSXML 3.0 SP2 is installed. After the client is installed, you can perform DSML queries against that server, which will translate the calls into LDAP queries to Active Directory. No additional software needs to be installed on domain controllers to support DSML. The following code shows a DSML request for the RootDSE: <se:Envelope xmlns:se="http://schemas.xmlsoap.org/soap/envelope/"> <se:Body xmlns="urn:oasis:names:tc:DSML:2:0:core"> <batchRequest> <searchRequest dn="" scope="baseObject"> <filter> <present name="objectclass"/> </filter> </searchRequest> 549 </batchRequest> </se:Body> </se:Envelope> 18.3.3 Discussion DSML is an XML alternative to using LDAP to access and manage a directory server. The Oasis standards body has driven the development of DSML (http://www.oasis- open.org/committees/dsml/) and now most directory vendors support it as of Version 2 (DSMLv2). DSML encodes LDAP-like functions in XML messages and transmits them to a SOAP client that can sit directly on the directory server or a separate server. Currently, Active Directory domain controllers do not support DSML directly and, thus, a separate client must be installed. For more information including the DSML specification, see the Oasis web site. 18.3.4 See Also DSMLfW home page: http://www.microsoft.com/windows2000/server/evaluation/news/bulletins/dsml.asp Recipe 18.4 Programming with Perl 18.4.1 Problem You want to programmatically access Active Directory using Perl. 18.4.2 Solution There are two options for accessing Active Directory with Perl. You can use the Net::LDAP modules that are cross platform and use the LDAP protocol, or you can use the Win32::OLE module that gives you access to ADSI and must be run on a Windows machine. Both modules can be downloaded from the Comprehensive Perl Archive Network (CPAN) web site, http://www.cpan.org/ . The following example shows how to use the Net::LDAP modules to query the RootDSE: #!/usr/SD/perl/bin/perl use strict; use Net::LDAP; my $ldap_server = $ARGV[0] || 'dc1'; my $ldapobj = Net::LDAP->new($ldap_server) or die " Could not connect: $@"; my $rootdse = $ldapobj->search( base => '', filter => '(objectclass=*)', scope => 'base', 550 ); die $rootdse->error if $rootdse->code; foreach $entry($rootdse->entries) { foreach $attr(sort $entry->attributes) { foreach ($entry->get($attr)) { print "$attr: $_\n"; } } } This next example uses the Win32::OLE module and ADSI to display the attributes of the RootDSE: use strict; use Win32::OLE 'in'; my $rootdse = Win32::OLE->GetObject("LDAP://RootDSE"); $rootdse->GetInfo; for my $i ( 0 $rootdse->PropertyCount - 1) { my $prop = $rootdse->Item($i); print $prop->Name,"\n"; foreach my $val (in $prop->Values) { print " ",$val->CaseIgnoreString,"\n"; } } It is worth noting that with Net::LDAP, you generally need to bind to the target domain controller before performing a search or any other operation. In the Net::LDAP example above, I didn't need to do that because I queried the RootDSE, which allows anonymous (i.e., unauthenticated) connections. A bind can be done using the following code: $ldapobj->bind('administrator@rallencorp.com', password => 'galt'); In the second code sample where I used ADSI with Win32::OLE, the credentials of the user running the script are used by default, so you only need to do an explicit bind if you need to authenticate as a different user. 18.4.3 Discussion The Net::LDAP modules are a robust set of modules for querying and modifying an LDAP directory. Net::LDAP also supports DSML, the abstract schema, and LDIF. Net::LDAP is a native Perl implementation, which means that it does not rely on an external LDAP SDK. Since it is a pure Perl implementation, you can write Net::LDAP-based scripts on a variety of platforms to interface with Active Directory or other LDAP-based directories. Graham Barr initially developed the Net::LDAP modules and more information can be found about the modules on the following web site: http://perl-ldap.sourceforge.net/. The Win32::OLE modules provide an interface into Microsoft's Component Object Model (COM). Most of the ADSI classes and methods are available from the COM automation interface, known as IDispatch. This allows you to combine the flexibility of Perl with the . management. LDAP is platform neutral, which means you can access and manage data in Active Directory from a variety of platforms. Active Directory uses the Domain Name System (DNS) for its name resolution. on the directory server or a separate server. Currently, Active Directory domain controllers do not support DSML directly and, thus, a separate client must be installed. For more information. integrate Active Directory in ways that are not typically documented. Recipe 18.1 Accessing AD from a Non -Windows Platform 18.1.1 Problem You want to access or manage AD from a non -Windows platform.