Re: LDAP
Re: LDAP
- Subject: Re: LDAP
- From: Quinn <email@hidden>
- Date: Fri, 11 Nov 2005 10:53:36 +0000
At 9:16 +0000 11/11/05, Martin Crane wrote:
Sorry, maybe I phrased my original question badly. I don't want to
authenticate the user, as seems to be the requirement in the
CryptNoMore sample. I simply want to find out the server which
already authenticated the user at the Login Window and retrieve its
LDAP search base - that which is set either in the Directory Access
app or supplied via a DHCP offer.
Right. My point in directing you to CryptNoMore is that:
a) you need to find out which directory node (that is, which LDAP
server) authenticated the user
b) CryptNoMore gets that information (the AppleMetaNodeLocation
attribute of the user's Directory Services record) as part of its
operation
c) the Directory Services API is complex, so it's better for you to
start with CryptNoMore than to try and figure it out from scratch
yourself
I answered a similar question for a developer quite recently. They
were doing advanced printer authentication, and wanted to know, for a
given user ID, whether that user was authenticated via Active
Directory and, if so, what their Active Directory user name and
Active Directory domain was. Sounds similar, huh?
I solved this problem by starting with CryptNoMore and bashing the
code until it returned the attributes I needed. The vast bulk of the
code didn't change. I've included the new code at the end of this
email.
[btw Just for the record, the technique shown in this code is
slightly wrong. After consulting with Apple's Active Directory DS
plugin engineer, we decided that the best solution for getting the AD
domain is to simply string the "/Active Directory/" from the front of
the AppleMetaNodeLocation attribute's value. This is preferred over
accessing the "dsAttrTypeNative:ADDomain" attribute. However, this
change is highly AD specific, and thus irrelevant to my proposed
solution to your LDAP problem.]
S+E
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Technical Support * Networking, Communications, Hardware
static tDirStatus FindUsersADInfo(
tDirReference dirRef,
tDirNodeReference nodeRef,
const char * username,
char ** adNamePtr,
char ** adDomainPtr
)
// Finds the authentication information for a given user.
// dirRef is the connection to Open Directory.
// nodeRef is the node to use to do the searching. Typically
// this is the authentication search node, whose path is found
// using GetSearchNodePathList. username is the user whose
// information we're looking for.
//
// On success, *pathListToAuthNodePtr is a data list that
// contain's the path components of the authentication node
// for the specified user.
// On success, *userNameForAuthPtr contains a pointer to C string
// that is the user's name for authentication. This can be
// different from username. For example, if user's long name is
// "Mr Gumby" and their short name is "mrgumby", username can be
// either the long name or the short name, but *userNameForAuthPtr
// will always be the short name. The caller is responsible for
// freeing this string using free.
{
tDirStatus err;
tDirStatus junk;
tDataBufferPtr buf;
tDataListPtr recordType;
tDataListPtr recordName;
tDataListPtr requestedAttributes;
unsigned long recordCount;
tAttributeListRef foundRecAttrList;
tContextData context;
tRecordEntryPtr foundRecEntry;
char * adName;
char * adDomain;
assert(dirRef != 0);
assert(nodeRef != 0);
assert(username != NULL);
assert( adNamePtr != NULL);
assert(*adNamePtr == NULL);
assert( adDomainPtr != NULL);
assert(*adDomainPtr == NULL);
recordType = NULL;
recordName = NULL;
requestedAttributes = NULL;
foundRecAttrList = 0;
context = NULL;
foundRecEntry = NULL;
adName = NULL;
adDomain = NULL;
// Allocate a buffer for the record results. We'll grow this
// buffer if it proves to be too small.
err = eDSNoErr;
buf = dsDataBufferAllocate(dirRef, kDefaultDSBufferSize);
if (buf == NULL) {
err = eDSAllocationFailed;
}
// Create the information needed for the search. We're searching for
// a record of type kDSStdRecordTypeUsers whose name is "username".
// We want to get back the kDSNAttrMetaNodeLocation and kDSNAttrRecordName
// attributes.
if (err == eDSNoErr) {
recordType = dsBuildListFromStrings(dirRef,
kDSStdRecordTypeUsers, NULL);
recordName = dsBuildListFromStrings(dirRef, username, NULL);
requestedAttributes = dsBuildListFromStrings(dirRef,
"dsAttrTypeNative:sAMAccountName", "dsAttrTypeNative:ADDomain", NULL);
if ( (recordType == NULL) || (recordName == NULL) ||
(requestedAttributes == NULL) ) {
err = eDSAllocationFailed;
}
}
// Search for a matching record.
if (err == eDSNoErr) {
recordCount = 1; // we only want one match (the first)
err = dsGetRecordListQ(
dirRef,
nodeRef,
&buf,
recordName,
eDSExact,
recordType,
requestedAttributes,
false,
&recordCount,
&context
);
}
if ( (err == eDSNoErr) && (recordCount < 1) ) {
err = eDSRecordNotFound;
}
// Get the first record from the search. Then enumerate the attributes for
// that record. For each attribute, extract the first value (remember that
// attributes can by multi-value). Then see if the attribute is one that
// we care about. If it is, remember the value for later processing.
if (err == eDSNoErr) {
assert(recordCount == 1); // we only asked for one
record, shouldn't get more back
err = dsGetRecordEntry(nodeRef, buf, 1, &foundRecAttrList,
&foundRecEntry);
}
if (err == eDSNoErr) {
unsigned long attrIndex;
// Iterate over the attributes.
for (attrIndex = 1; attrIndex <=
foundRecEntry->fRecordAttributeCount; attrIndex++) {
tAttributeValueListRef thisValue;
tAttributeEntryPtr thisAttrEntry;
tAttributeValueEntryPtr thisValueEntry;
const char * thisAttrName;
thisValue = 0;
thisAttrEntry = NULL;
thisValueEntry = NULL;
// Get the information for this attribute.
err = dsGetAttributeEntry(nodeRef, buf, foundRecAttrList,
attrIndex, &thisValue, &thisAttrEntry);
if (err == eDSNoErr) {
thisAttrName = thisAttrEntry->fAttributeSignature.fBufferData;
// We only care about attributes that have values.
if (thisAttrEntry->fAttributeValueCount > 0) {
// Get the first value for this attribute. This
is common code for
// the two potential attribute values listed
below, so we do it first.
err = dsGetAttributeValue(nodeRef, buf, 1,
thisValue, &thisValueEntry);
if (err == eDSNoErr) {
const char * thisValueDataPtr;
unsigned long thisValueDataLen;
thisValueDataPtr =
thisValueEntry->fAttributeValueData.fBufferData;
thisValueDataLen =
thisValueEntry->fAttributeValueData.fBufferLength;
// Handle each of the two attributes we care
about; ignore any others.
if ( strcmp(thisAttrName,
"dsAttrTypeNative:sAMAccountName") == 0 ) {
assert(adName == NULL); //
same attribute twice
adName = (char *) malloc( thisValueDataLen + 1 );
if (adName == NULL) {
err = eDSAllocationFailed;
} else {
memcpy(
adName,
thisValueDataPtr,
thisValueDataLen
);
adName[thisValueDataLen] = 0; //
terminating null
}
} else if ( strcmp(thisAttrName,
"dsAttrTypeNative:ADDomain") == 0 ) {
assert(adDomain == NULL); //
same attribute twice
adDomain = (char *) malloc( thisValueDataLen + 1 );
if (adDomain == NULL) {
err = eDSAllocationFailed;
} else {
memcpy(
adDomain,
thisValueDataPtr,
thisValueDataLen
);
adDomain[thisValueDataLen] = 0; //
terminating null
}
} else {
fprintf(stderr, "FindUsersADInfo:
Unexpected attribute '%s'.", thisAttrName);
}
}
} else {
fprintf(stderr, "FindUsersADInfo: Unexpected
no-value attribute '%s'.", thisAttrName);
}
}
// Clean up.
if (thisValueEntry != NULL) {
junk = dsDeallocAttributeValueEntry(dirRef, thisValueEntry);
assert(junk == eDSNoErr);
}
if (thisValue != 0) {
junk = dsCloseAttributeValueList(thisValue);
assert(junk == eDSNoErr);
}
if (thisAttrEntry != NULL) {
junk = dsDeallocAttributeEntry(dirRef, thisAttrEntry);
assert(junk == eDSNoErr);
}
if (err != eDSNoErr) {
break;
}
}
}
// Copy results out to caller.
if (err == eDSNoErr) {
if ( (adName != NULL) && (adDomain != NULL) ) {
// Copy out results.
*adNamePtr = adName;
*adDomainPtr = adDomain;
// NULL out locals so that we don't dispose them.
adName = NULL;
adDomain = NULL;
} else {
err = eDSAttributeNotFound;
}
}
// Clean up.
free(adName); // on success, this is NULLed out
above, so it doesn't actually free the result
free(adDomain); // on success, this is NULLed out
above, so it doesn't actually free the result
if (foundRecAttrList != 0) {
junk = dsCloseAttributeList(foundRecAttrList);
assert(junk == eDSNoErr);
}
if (context != NULL) {
junk = dsReleaseContinueData(dirRef, context);
assert(junk == eDSNoErr);
}
if (foundRecAttrList != 0) {
junk = dsDeallocRecordEntry(dirRef, foundRecEntry);
assert(junk == eDSNoErr);
}
if (requestedAttributes != NULL) {
junk = dsDataListAndHeaderDeallocate(dirRef, requestedAttributes);
assert(junk == eDSNoErr);
}
if (recordName != NULL) {
junk = dsDataListAndHeaderDeallocate(dirRef, recordName);
assert(junk == eDSNoErr);
}
if (recordType != NULL) {
junk = dsDataListAndHeaderDeallocate(dirRef, recordType);
assert(junk == eDSNoErr);
}
if (buf != NULL) {
junk = dsDataBufferDeAllocate(dirRef, buf);
assert(junk == eDSNoErr);
}
assert( (err == eDSNoErr) == ( (*adNamePtr != NULL) &&
(*adDomainPtr != NULL) ) );
return err;
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Macnetworkprog mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
- Follow-Ups:
- Re: LDAP
- From: Martin Crane <email@hidden>