Networking and Security • 39:08
This session covers integration of Directory Services into Mac OS X and Mac OS X Server. Learn about Access APIs and API utilities, Authentication, Directory Setup, NetInfo, and LDAPv2. This session includes sample code and information on how to write a Directory Access plug-in.
Speakers: David O'Rourke, Jason Townsend
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
out of their busy afternoon to attend the Directory Services talk. in the Mac OS X Server presentation, you might have heard a little bit more about. I'll be talking and later on I'll be inviting up a coworker of mine, Jason Townsen, who will give you a little more detail on developing a Directory plug-in and a demo.
What we're going to cover today is what are Directory Services? How are we using Directory Services in Apple software? What do the APIs look like? What's plug-in development look like? We'll give you a little demo and then hopefully cover some questions and answers at the end of the session.
The concept of Directory Services arose about three years ago when my management asked me to figure out what Apple's directory story was moving forward for Mac OS X Server. And one of the key pieces of feedback we had from our customers was they no longer wanted to use just an Apple proprietary database like users and groups. They wanted at least the option to integrate with an existing or perhaps a proprietary directory system.
So the concept is relatively simple: allow Apple software to use customer data. The implementation, all of us are engineers in the room or have seen this, engineers draw boxes, is fairly straightforward. We're going to build an abstraction API, we're going to put Apple services on top of that abstraction, and that abstraction will grant access to customer data.
The architecture, a little more flushed out. We have Apple software based on Directory Services. We have developer software that could be based on Directory Services. And we provide plug-ins to two Directory Services that Apple provides. The primary Directory Service in Mac OS X is NetInfo, and that's our default plug-in. We also provide read-only access to LDAPv2. And part of the reason some of you are in the audience, hopefully, is so that you can develop a Directory Access plug-in for a Directory Service that you can make money on or possibly help your site.
The runtime of the API leverages the Mac OS X architecture. A Directory Service client using Directory Service API calls into a Mac OS X framework. That framework uses Mach IPC to communicate with the daemon. The daemon then talks to plug-ins to handle various requests. The reply comes back up through Mach IPC. It's very secure and leverages the Mac OS X architecture.
Status since last year. I've given this talk now, I think this is my third year up on stage talking about this, so it'll be familiar if you were here the previous couple of years. Hopefully this is new for some of you. What have we done since last year? Directory Access APIs are included in Mac OS X Desktop. This is not a server-only technology. You can use Directory Services in the Desktop.
They are located in the system library frameworks, directory service.framework. The API manifests itself in 5C header files. Going over header files is laborious and sometimes boring, so they're there. I'd encourage you to review them. The Directory plug-in APIs are included in functionally the Desktop and the Server. This is something you can use in either location, although the Server leverages them more heavily than the Desktop does. There's also an SDK. The term SDK is a little over-explanatory.
It's overused. What we have is a collection of some sample code and some helper code. That's available from Tom Weier. Everything you need to actually develop and use Directory Services is already in Mac OS X with developer tools. We are writing some sample code, so the SDK includes the sample code and the documentation in one convenient place.
What's new since last year is we have the Directory Setup application, which we only alluded to last year. You'll be getting a demo of that later. We have a search policy. We've received some feedback. Directory Services embodies the concept of a search policy for authentication. What directories do we look into when the OS wants to authenticate a user? We've also added a contact search policy, which we think could be integrated into PIMs or email clients. That search policy is configured separately and could search public databases.
It provides a lightweight way for you to provide a fine user functionality into your applications. We will also be releasing, via the SDK, DS Wrappers, which is some C++ sample code that was developed inside of the group that makes it a little easier to approach the Directory Service APIs. All of that will be bundled up and made available. Contact Tom Weyer. I think we'll post it to the website. Okay.
What is Directory Setup? Well, Directory Services come out of the box and the first thing you need to know is if you're using NetInfo, you don't need to do a thing. It just works. Out of the box, no one needs to visit Directory Setup, no one needs to modify anything. Directory Setup is for those clients that wish to use a directory other than NetInfo. And Directory Setup allows you to control what directory systems Mac OS X interacts with. It's located in Applications Utilities.
You can enable and disable individual directory plug-ins. Right now there's only two, the NetInfo and the LDAP. If third parties add more, this is where you as a system administrator would turn those third party plug-ins on or off. And it also provides the ability for you to put up your own human interface for configuring your plug-in.
You'll see more about that later and I believe after this I have a sample screenshot. Yes, if you look at the screen, this is a screenshot from Mac OS X. The lock is in the lower left-hand corner. This is a secure thing. Only the system administrator can change these settings.
And we provide the two standard plug-ins, NetInfo and LDAP. If you wrote a third party plug-in or developed a plug-in, it would be listed here. The user could select your plug-in and click on the Configure button there in the bottom center. And that would launch a human interface or Cocoa or Carbon application that you wrote which puts up your own user interface for configuring your plug-in. The three tabs across the top of Services, Authentication and Contacts. The Services are for controlling the plug-ins. Authentication controls the authentication search policy. allows the user to configure the contacts search policy.
What do the APIs include? They include full read/write capabilities. You can do, if your Directory plug-in supports read/write, you can do anything with a particular Directory through these APIs. Mac OS X Server's administration software is based on these APIs for reading and writing NetInfo. The API offers the notion of simple, complex, and custom authentication methods, proof of identity. So if you have a custom authentication method that your particular Directory system supports, you can publish it and come up with a scheme that clients could use it. The Directory APIs are standard C API and they're accessible from any runtime other than classic on Mac OS X.
So a MachO binary, a Cocoa app, a Carbon app. We have a variety of applications calling Directory Services. We have the Mac OS X Server administration software, which is a Carbon application. The Directory Setup calls Directory APIs. That's a MachO binary Cocoa application. And Directory Services calls itself, which is running more at the positive layer. So we're fairly confident you can call the Directory Services. We call the Directory Service API from pretty much anywhere except classic on Mac OS X.
Part of the Directory Services provides an abstraction for accessing. It also provides an abstraction for the data. And this is where we get some of the most questions. So this year we've added this slide. Hopefully we'll do more next year. But the abstraction that Directory Service presents is a notion of directory nodes at the top level. And those are groups or collections of data.
If you had multiple LDAP servers configured, each one would manifest itself as a single directory node in the Directory Service Abstraction API. Records come underneath nodes. Records are contained within nodes. They consist of names and types. Attribute types with the data and attribute values come under attribute types. We're going to go into that in more detail in the next slides.
Directory Nodes. Each Directory Node is published by a plug-in and a single plug-in. A plug-in can register zero or more nodes. You as a plug-in developer can register as many Directory Nodes as you want that you want to grant clients access to. Hopefully you're matching that to some reality of some kind. But when someone opens a Directory Node using the Directory Service API, it will dispatch to your plug-in and you will then be responsible for all calls regarding that Directory Node.
API calls to access Directory Nodes from a client level are DSOpenDirNode, DSGetDirNodeList, FindDirNodes, and CloseDirNodes. They're relatively straightforward. So you as an API client, if you wanted to work with a particular LDAP Directory Node, you would call OpenDirNode and now you're talking to an LDAP server. The important point is it doesn't matter whether you're talking to an LDAP server, an ActiveX server, or a NetInfo server, OpenDirNode gets you the session with that Directory Server regardless of what the protocol is. An example path of a Directory Node. The directory plug-in would be /ldapv2/ldap.company.com. That's how we configure our LDAP sessions. So they are multi-path and they are hierarchical. So a directory plug-in can represent a hierarchical Directory System into Directory Services.
Records. Records reside in Directory Nodes. They consist of at least one name and exactly one type. A record cannot simultaneously be a user and a group. Maybe it can in some directories, but not in the Directory Service abstraction. Examples of a record name would be John Smith, Jay Smith.
The important thing is the at least one name. Directory Services supports the notion of multiple names per record. And a sample record type is KDS, Standard Record Type Users. We have standard types defined for obvious record types, users, groups, printers, AFP servers, web servers, whatever you use. The records that you need to access can be listed inside of a Directory Node. API calls to access records are DSOpenRecord, DSGetRecordList, DSCreateRecord, and DSCloseRecord. There are more than that, but that's some samples.
Attribute types. Attribute types are contained within records, and these are essentially the descriptors of what type of data you're fetching. These are UID, home directory, email address, phone number, any type of data is represented as an attribute type. They're contained within records. Each attribute type can contain zero or more values. An example attribute types are KDSN adder record name, KDSN adder home directory, and KDS1 adder password.
The naming convention in the API is consistent. The KDSN attribute record name is that means that typically that's a multi-value. There are N instances of a record name. The KDS1 attribute password is that means in general you can expect that that attribute type would only have one value. This is not enforced.
It should help you as a plug-in developer and help you as a client know how many values you have. There are many different pieces of data to expect for a particular attribute type. API calls used to access attribute types are get attribute entry, get record attribute info. There are more than that. Attribute values.
These are the actual blobs of data once you get down through the hierarchy. You've opened your directory node. You've opened your record. You've found the attribute type you're interested in, the phone number. Where do I get the list of values? Each instance of a phone number. Those are attribute values. Attribute values are contained within the attribute. Attribute values contain the raw data specified by the attribute type.
Again, the attribute type can lead you into expecting a certain type of data and format. The API calls used to access attribute values are dsget attribute value, dsget record attribute value by index, and dsget record attribute value by ID. These are all useful techniques and had to be used in all of our software. Come see me more if you want to know the differences between those types of attributes. In particular, directory services is data agnostic. We treat all attribute values as binary blobs.
If your directory is capable of storing binary data, we'll just pass it right on through to your plug-in. If your directory is incapable of storing binary data, it would be up to the plug-in to make some sort of modification to the data before it's stored if it received binary data. Directory services is data agnostic, although we provide the tools for you to discover and utilize the data.
[Transcript missing]
The API Model Usage. The general model is that this allows, by Directory Setup and this architecture, allows Apple software to be configured to use Directory of the customer's choice. We only ask for standard record types and their attributes. Those standard record types and attributes are documented in the Directory Services and Server Documentation. This enables Apple and third-party software to access any Directory system. By now you should understand that. Customers configure Directory access via the Search Policy and Directory Setup.
Data Access by OS and Applications. What type of data is typically requested by the server software and by the desktop software? Well, obviously, users and groups is a big winner. But the Mac OS X desktop also requests mount information for specific file systems, NFS and AFP, home directory information. And in some cases, host information comes out of NetInfo or could come out of LDAP by this architecture. And that would be host name and IP address. The previous Mac OS X Server session, if you weren't there, I'd recommend you get the tape.
Greg Vaughn covered the issue that a lot of the configuration information in Mac OS X that normally is stored in /etc on Unix has been migrated to NetInfo. Well, that's an accurate and inaccurate statement. I'll say really what it's been migrated to is Directory Services. So now information or config information stored in Mac OS X or required by Mac OS X can be accessed via Directory Services.
Apple plug-in usage and development. Apple's developed three basic Access plug-ins. We've developed NetInfo. That's our primary and default Directory system. LDAPv2, read-only for Internet compatibility. We get a lot of mileage out of the LDAPv2. We can talk to Active Directory. We can talk to Novell. We can talk to OpenLDAP. We can talk to iPlanet.
So if you can find an LDAP server to access your data, you can get Directory Services to access the data as well. We also implemented the search policy as a Directory Services plug-in. That's another thing that gives us a lot of confidence in this architecture. We use the architecture to solve our own problems. Apple software products are being tested with these plug-ins. So if you're developing a plug-in and you can make your plug-in behave like any one of these plug-ins, there's a very good chance that it will work unmodified.
Apple Desktop and Server Software uses the architecture to provide customer directory flexibility and we're really looking for developer plug-ins. If you want to develop a plug-in for Directory Services, contact Tom Weir, contact me. Please get to us. You're going to need some help, but we're going to provide it for you and hopefully it'll be pretty simple. At this time, I'd like to invite Jason Townsen up on stage.
He's going to go into some of the details of what it takes to develop a Directory plug-in, and I'll be back later for a wrap-up and Q&A. Thanks. So I'm going to talk about Directory plug-ins and the main points we're going to talk about are the structure, how do you test your plug-in once you've got it up and running, and then a little more technical, what are the entry points and callbacks that you're interested in as a Directory Services plug-in? So plug-ins are dynamically loaded code modules. They're actually CF plug-ins.
So we use Core Foundation's CF load function to load them. and you have to provide a set of entry points. We'll go into detail on what each of those is later. And then you can call back into Directory Services to do various things like primarily registering the nodes that you're going to provide service on.
So where does your plug-in live in the system? All of the plug-ins that Apple provides are stored inside the Directory Service framework with that big, long, nasty path you see at the top there. But that is generally considered the place for only Apple plug-ins. For your third-party plug-ins, you should put them in Library Directory Services plug-ins. And that's guaranteed to be a writable part of the file system for root, and that's where we will load plug-ins from. The other thing to keep in mind is you need to put the .dsplug extension on your bundle so that Directory Service daemon will load it.
Here's an example plug-in bundle. You don't really need to worry too much about this. If you use Project Builder, it's just a matter of creating a CF plug-in and it will deal with all the packaging issues for you. The main thing you need to be concerned about is the plist file, which has some of the metadata that we use to load your plug-in and also to determine what app we should use to configure your plug-in when you're showing up in Directory Setup and the user needs to configure how your plugin works.
So we have some sample code we've talked about before that is sort of a starting point for you with your plug-in. and it shows you stubs that you would use for all the API calls. So basically all you need to do is fill in the functionality that you're providing and we handle setting up all of the callbacks and show you how to use those as well.
So, configuring your plug-in. There's two keys in your plist file that are important for you to set up for your plug-in to be configurable in Directory Setup. The first one is CFBundle config avail and the second is CFBundle config file. So, each of them is optional. Basically, what will happen if, for example, you just want to provide a text file, you can provide only the CFBundle config file key and we will just launch that text file with the default text editor, probably TextEdit.
Alternately, you could have an application that you launch and just provide CFBundle config avail. If you provide both, then we will launch the application you specify with the file you specify. And all this happens when your plug-in is selected in Directory Setup and you click the Configure button, then we do this launching as appropriate.
So once you've got your plug-in up and running, how do you test it? There is code on the system, a lot of code on Mac OS X Server and even personal file sharing on 10 Desktop uses Directory Services extensively, but it's probably better to start out a little bit-- oh, sorry, wrong slide. So first, you've got to copy your plug-in in to the appropriate location in Library Directory Services plug-ins.
The other point to keep in mind is that You need to kill and restart the Directory Service process. This is a daemon process. There's no GUI per se for it, so the easiest way to do this is just use the terminal and kill and restart it. And you need to be root in order to do that, because Directory Service needs to run as root to access its log files and such.
Then you can take a look at the log file and library logs to see that your plug-in loaded successfully and we keep other logging information in there. So, what I was getting into before, I was getting a little bit ahead of myself. When you're testing your plug-in, it's better, instead of throwing a full client at it to begin with, it's better to actually control the client side yourself as well and use the APIs that you've provided. So you can test a subset of the Directory Service API that you've created to that point.
For example, you can leave out all of the right portions and just test that you can find a user with get record list and you can do a DS do dirt node auth to authenticate that user rather than having to support all the functions that a client would use. Because sometimes, even for a fairly simple operation, there might be APIs in there that you haven't implemented yet.
So just because things don't work right off the bat doesn't mean that you haven't done things right. You just need to add all of the appropriate calls in. So we have a sample tool that has these calls in there called DSTestTool that comes with our SDK sample code.
And you can easily modify that or use what's already there. It does simple things like look for a user, create a user, and so on. And you could also develop your own test tool, especially if your plug-in is going to provide specific functionality that's outside of the standard, like custom auth methods, then you might want to just develop your own tool to test those things out.
So, what are the entry points? We have Validate. This is called once when your plug-in is loaded. The primary purpose of that is to provide a token to your plug-in that you can use later in all of your callbacks. So that allows us to recognize which plug-in is calling back into Directory Services.
We call initialize on your plug-in after all the plug-ins have loaded. This is the point where you would probably load up your config file or do any startup initialization like that, establish any persistent network connections that you might need, and so on. Set plug-in state is called when your plug-in is enabled or disabled from Directory Setup.
And that'll tell you, okay, your plug-in's now inactive. You can go ahead and close down any resources that you're using that you don't need anymore or bring them back online if you've been reactivated. Process request, this is where the bulk of the work is. You can probably implement all these other functions, you know, in a couple hours really easily. And we've got, you know, sample implementations. We've got some sample implementations in our sample code. But process request is where all the API calls come through.
So essentially that's going to look like a big switch statement that looks at what the actual request was. And then based on that, you'll probably hand it off to another function to deal with the particular request that's coming through. And we're just stuffing all the parameters that are coming in into a big struct that you need to interpret. Finally, shutdown is called just before Directory Services shuts down. So, again, another time to release any resources that you're using before the Directory Service process goes away or save any changes that you might need to save.
So the other half of it is the callbacks. We have DS Register Node, which is how you publish your nodes. The guideline that we suggest you follow is to prefix the name of your node with your plug-in's name. So the reason for this is that when a client makes a dsopender node call, We don't know which plug-in to use for that. So if the name starts with the name of one of the plug-ins, so for example, /netinfo/ldapv2 are the plug-ins that we have, then we can directly go to that plug-in out of our table and dispatch directly to you.
If you give it some other name, of course there's the possibility of collisions, but the other thing is we have to scan through the list of plug-ins to find one that might handle it. And depending upon what operations the plug-ins do to handle Opener node, it can be less efficient.
DSUnregisterNode is used when you want to take a node out of the list that you're no longer providing service on. So if your config file changed and that LDAP server is no longer there, you would use that function. And then we have DSDebugLog to log debugging information. That's an aid when you're developing your plug-in.
So as I mentioned before, the Directory Service daemon runs as a root process. And your plug-in is considered a trusted entity. So you really have to use your power wisely. It's not quite as bad as working with a kernel, but you can really do a lot of damage if you don't know what you're doing as root.
So you need to make sure that you limit your access to the system to only what you need. And also, don't do anything on behalf of a client that you're expecting the system to prevent you from doing. So in other words, you're running in a UID zero process.
So anything you try to do will probably succeed. So if you want to provide any kind of security, then you need to be aware of that in your plug-in. And as I said before also, you need to be logged in as root or at least have a root shell available to be able to develop a plug-in. So this means on Mac OS X desktop that you'll have to go into NetInfo Manager and enable root to get access to it or use sudo or other tools like that. We also restrict The plug-in directory, Library Directory Services plug-ins to only root access.
[Transcript missing]
So again, take a look at the sample code on this, and that will help you see where the cleanup calls need to correspond. And DSWrappers is a good example of what you need to do to clean up Directory Service structures. So, again, if you're interested in plug-in development, please talk to Developer Relations. We can help you with this and we want to help you with that.
And also, take a look at the API and plug-in documentation. It's actually included with the 10 developer tools. So, if you've installed 10 developer tools, it's on your hard disk already. Take a look at it. It's very helpful in understanding the API and we've got all the constants and functions in there that you need.
Also, if you're going to develop a plug-in, you should look at our API and decide how you're going to map our abstraction onto your directory system. So, what is a node in your directory system? What is a record? And so on. Alright, so now we're going to have a demo of Directory Setup. So can we switch over to Demo 1 please? It's asleep.
Can we switch to demo one instead of two? Yes. Excellent. Thank you. Okay, so I have here actually Mac OS X Server on this machine, but as we said before, Directory Setup is also part of Mac OS X Desktop. So let's take a look at Directory Setup. You saw the screenshot earlier.
Basically, we come up showing you the list of plug-ins. And also, you notice that the interface came up locked. So this means that I need to click the lock button to unlock it. Because what we're doing here in Directory Setup is actually changing who can log into the system.
So we really don't want anyone doing that other than an administrator. So now that I've unlocked it, I can make changes. We have the ability to configure NetInfo if you want to bind to a NetInfo parent or just use the local. Right now we're just using local. Alright, let's take a look at LDAP.
I assure you this was working for me before the session started. So if you configure LDAP, you get a list of LDAP servers. And we can go ahead and add a new server. Basically what we do here is we specify what mappings we want to use. So, and also what the IP address is and so on. So, say, test LDAP server. You can put in an IP address or a domain name in that field. So I'll just put in an IP address.
Then we need to set up the mappings that we're going to use for our record types and our attribute types. And this is kind of similar to the way that the TCP control panel works or the network setup works. Basically, each line of this right text box represents one mapping, but you can have multiple lines, so you can map things to more than one search base. For the record types, we're using search bases as our mapping.
So you can change these to whatever you want. For the purposes of just getting the file server working, we really only need to worry about users mapping. And we can leave the others there at their default or delete them. It doesn't matter. They're not actually necessary. We also documented in the 10 server documentation for particular services what you would need to map in Directory Setup to use that service with LDAP.
So then on the data or attribute types, again, we have a list of our standard types and you can map those to-- The types in LDAP that you want to use and again it's multiple mapping so each line is one mapping. So I could do, for example, UID and CN for record name. And then for real name I could do UID.
Actually, I really wanted to do CN, so that's common name. It's LDAP's abbreviation for Then password, if you're using OpenLDAP, for example, or some LDAP server that has a crypt password stored in it, then we can use that in the password mapping and actually do a crypt auth without sending a clear text password out over the network. So if your LDAP server has a crypt password stored for its users, it's a good idea to map that. If you don't, then we can still do LDAP bind, but the security is better if you're using crypt.
Then the final tab here is Access, which allows you to configure whether you want to use anonymous access to talk to the LDAP server or specify a distinguished name and password. If you need to bind to the server to do anything with it, then this is important to set up.
So, for example, with Active Directory, you would need to use this. You can also specify the timeouts that you're going to use and the port number if you're using a non-standard port. And then once you've set up that LDAP server, it shows up in your list. You can enable it. Let me see if I can bring up Server Admin to show you some more of what Mac OS X Server is doing with Directory Services.
Alright, so this is the server admin for Mac OS X Server. It might look slightly familiar to you if you've worked with Apple Share IP, recent versions. So the primary integration I wanted to show you is in Users and Groups. So all of the Users and Groups information is Access through Directory Services and when we're populating the local NetInfo database, we use Directory Services to put that information in. So if I create a user, for example, then That would be using Directory Service calls to set the password, to set all the attributes that our services need to be in there, and so on.
And now once I've created a user there, again, it's available to all the services. The other thing we have in Users and Groups is the ability to use "To search on the search path, for example. So now that I've added that user, I can see that they actually are on the search path." Okay.
So basically this just illustrates that the Directory Service APIs provide everything you need even to do a full GUI like this and both create the data and access it. Although, you know, for Directory Services clients, you probably would mostly be interested in just getting at the user names and doing authentication, but you can also populate the database too if you need to. I think I'll go ahead and stop there since Directory Setup doesn't seem to be happy today. I'll go ahead and turn it back over to David O'Rourke to finish up.
I think we took a risk booting off the FireWire drive. For those of you who know it's not technically supported. That ends the demo. We're going to do a wrap-up. Whoops, I have this upside down. Directory Services wrap-up. So Directory Services and other app or technologies. This is a lower level technology, but it can be used to build some high level solutions.
But it complements other technologies and it provides common framework for higher level software to access directory data. Another complement to this is the NSL talk, Network Service Location. I'd recommend highly that you all attend that tomorrow if you're interested in Directory Services. There's some exciting integration going on there. The opportunities for developers are to directory enable your client software.
As you saw, you could use the Mac OS X Server Administration to populate a local NetInfo database. And then using the Directory APIs, your application would inherit all the users set up by Mac OS X Server Administration. If the user had configured the search policy to access LDAP, your application would access LDAP.
So there's a lot of benefit in you enabling that. In addition, you could provide alternatives to Apple software by either providing a competing web server or competing file server, but still leverage the technology, the setup technology, and you can provide value by adding a directory plug-in. Directory plug-in is clearly an opportunity. We're actively looking for anybody willing to develop a directory plug-in.
We think there's opportunities here for people to either promote their own directory service or to access existing infrastructure. And again, the Mac OS X Server Administration client uses Directory Services. If your plug-in is read right, there's integration there that our Mac OS X Administration software could actually be used to set up users in your directory. So the key message is we're using this architecture for our own software.
The Directory Service APIs brings directory access to all of Apple's software platforms. Customers are leveraging this API. We already have some customers using LDAP. There's some very large installations. There's one customer in particular that has over 2 million users in an LDAP server. They took the Mac OS X Server package, unpacked it, configured their LDAP server in Directory Setup, added it to the search policy, and promptly logged on as any one of those users that they actually knew the password of. And so opportunities exist for you to base your application on Directory Services or develop your own directory services. services or develop a plugin. and David O'Rourke, Jason Townsen, and David O'Rourke, Jason Townsen, and David O'Rourke,