/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Retrieve directory information for Active Directory users.
*/
#include <ldap.h>
#include <lber.h>
#include <pwd.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <libadutils.h>
#include <libuutil.h>
#include <note.h>
#include <assert.h>
#include "directory.h"
#include "directory_private.h"
#include "idmapd.h"
#include <rpcsvc/idmap_prot.h>
#include "directory_server_impl.h"
/*
* Information required by the function that handles the callback from LDAP
* when responses are received.
*/
struct cbinfo {
const char * const *attrs;
int nattrs;
const char *domain;
};
char **domain);
#if defined(DUMP_VALUES)
#endif
/*
* Add an entry to a NULL-terminated list, if it's not already there.
* Assumes that the list has been allocated large enough for all additions,
* and prefilled with NULL.
*/
static
void
{
if (uu_strcaseeq(*list, s))
return;
}
*list = s;
}
/*
* Copy a counted attribute list to a NULL-terminated one.
* In the process, examine the requested attributes and augment
* the list as required to support any synthesized attributes
* requested.
*/
static
const char **
{
const char **new_list;
int i;
new_list =
return (NULL);
for (i = 0; i < req_list_len; i++) {
const char *a = req_list[i];
/*
* Note that you must update MAX_EXTRA_ATTRS above if you
* add to this list.
*/
if (uu_strcaseeq(a, "x-sun-canonicalName")) {
continue;
}
/* None needed for x-sun-provider */
}
return (new_list);
}
/*
* Retrieve information by name.
* Called indirectly through the Directory_provider_static structure.
*/
static
char *types,
{
int i;
const char **attrs2;
/*
* If we don't have any AD servers handy, we can't find anything.
* XXX: this should be using our DC, not the GC.
*/
return (NULL);
}
/* 6835280 spurious lint error if the strlen is in the declaration */
/*
* Turn our counted-array argument into a NULL-terminated array.
* At the same time, add in any attributes that we need to support
* any requested synthesized attributes.
*/
goto nomem;
for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
int type;
/*
* Extract the type for this particular ID.
* Advance to the next type, if it's there, else keep
* using this type until we run out of IDs.
*/
types++;
/*
* If this entry has already been handled, one way or another,
* skip it.
*/
continue;
/*
* Allow for expanding every character to \xx, plus some
* space for the query syntax.
*/
if (type == DIRECTORY_ID_SID[0]) {
/*
* Mildly surprisingly, AD appears to allow searching
* based on text SIDs. Must be a special case on the
* server end.
*/
}
} else {
} else {
}
if (type == DIRECTORY_ID_USER[0])
else if (type == DIRECTORY_ID_GROUP[0])
else
/*
* Try samAccountName.
* Note that here we rely on checking the returned
* distinguishedName to make sure that we found an
* entry from the right domain, because there's no
* attribute we can straightforwardly filter for to
* match domain.
*
* Eventually we should perhaps also try
* userPrincipalName.
*/
"(&(samAccountName=%v1)(objectClass=%v3))",
}
}
}
goto out;
"Out of memory during AD lookup", NULL);
out:
return (de);
}
/*
* Note that attrs is NULL terminated, and that nattrs is the number
* of attributes requested by the user... which might be fewer than are
* in attrs because of attributes that we need for our own processing.
*/
static
const char * const * attrs,
int nattrs,
const char *domain,
const char *filter)
{
int rc;
/*
* NEEDSWORK: Should eventually handle other forests.
* NEEDSWORK: Should eventually handle non-GC attributes.
*/
/* Stash away information for the callback function. */
if (rc != ADUTILS_SUCCESS) {
return (directory_provider_ad_utils_error(
"adutils_lookup_batch_start", rc));
}
if (rc != ADUTILS_SUCCESS) {
return (directory_provider_ad_utils_error(
"adutils_lookup_batch_add", rc));
}
if (rc != ADUTILS_SUCCESS) {
return (directory_provider_ad_utils_error(
"adutils_lookup_batch_end", rc));
}
if (batchrc != ADUTILS_SUCCESS) {
/*
* NEEDSWORK: We're consistently getting -9997 here.
* What does it mean?
*/
return (NULL);
}
return (NULL);
}
/*
* Callback from the LDAP functions when they get responses.
* We don't really need (nor want) asynchronous handling, but it's
* what libadutils gives us.
*/
static
void
int rc,
int qid,
void *argp)
{
}
}
/*
* Process a single entry returned by an LDAP callback.
* Note that this performs a function roughly equivalent to the
* directory*Populate() functions in the other providers.
* Given an LDAP response, populate the directory entry for return to
* the caller. This one differs primarily in that we're working directly
* with LDAP, so we don't have to do any attribute translation.
*/
static
void
{
int i;
/*
* We don't have a way to filter for entries from the right domain
* in the LDAP query, so we check for it here. Searches based on
* samAccountName might yield results from the wrong domain.
*/
goto err;
goto out;
/*
* If we've already found a match, error.
*/
"Multiple matching entries found", NULL);
goto err;
}
goto nomem;
for (i = 0; i < nattrs; i++) {
const char *a = attrs[i];
#if defined(DUMP_VALUES)
#endif
goto err;
} else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
int n = ldap_count_values_len(bv);
if (n > 0) {
char *tmp;
domain);
goto nomem;
goto err;
}
}
} else if (uu_strcaseeq(a, "x-sun-provider")) {
}
}
goto out;
"No memory allocating return value for user lookup", NULL);
err:
out:
}
/*
* Given a struct berval, populate a directory attribute value (which is a
* list of values).
* Note that here we populate the DAV with the exact bytes that LDAP returns.
* Back over in the client it appends a \0 so that strings are null
* terminated.
*/
static
{
int n;
int i;
n = ldap_count_values_len(bv);
goto nomem;
for (i = 0; i < n; i++) {
goto nomem;
}
return (NULL);
return (directory_error("ENOMEM.bv_list_dav",
"Insufficient memory copying values"));
}
#if defined(DUMP_VALUES)
static
void
{
int i;
return;
}
}
}
#endif /* DUMP_VALUES */
/*
* Return the domain associated with the specified entry.
*/
static
char **domain)
{
char *m;
char *s;
return directory_error("AD.get_domain.ldap_get_dn",
"ldap_get_dn: %1 (%2)\n"
"matched: %3\n"
"error: %4",
m == NULL ? "(null)" : m,
s == NULL ? "(null)" : s,
NULL);
}
"get_domain: Unexpected error from adutils_dn2dns(%1)",
return (de);
}
return (NULL);
}
/*
* Given an error report from libadutils, generate a directory_error_t.
*/
static
{
return (directory_error(code,
}
"AD",
};