/*
* 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 2011 Nexenta Systems, Inc. All rights reserved.
*/
/*
* native LDAP related utility routines
*/
#include "idmapd.h"
#include "idmap_priv.h"
#include "ns_sldap.h"
#include "nldaputils.h"
#include <assert.h>
/*
* The following are format strings used to construct LDAP search filters
* when looking up Native LDAP directory service. The _F_XXX_SSD format
* is used by the libsldap API if a corresponding SSD is defined in
* Native LDAP configuration. The SSD contains a string that replaces
* the first %s in _F_XXX_SSD. If no SSD is defined then the regular
* _F_XXX format is used.
*
* Note that '\\' needs to be represented as "\\5c" in LDAP filters.
*/
/* Native LDAP lookup using UNIX username */
/*
* Native LDAP user lookup using names of well-known SIDs
* Note the use of 1$, 2$ in the format string which basically
* allows snprintf to re-use its first two arguments.
*/
#define _F_GETPWWNAMWK \
"(&(objectClass=posixAccount)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
/* Native LDAP user lookup using winname@windomain OR windomain\winname */
#define _F_GETPWWNAMDOM \
"(&(objectClass=posixAccount)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
/* Native LDAP lookup using UID */
/* Native LDAP lookup using UNIX groupname */
/* Native LDAP group lookup using names of well-known SIDs */
#define _F_GETGRWNAMWK \
"(&(objectClass=posixGroup)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
/* Native LDAP group lookup using winname@windomain OR windomain\winname */
#define _F_GETGRWNAMDOM \
"(&(objectClass=posixGroup)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
/* Native LDAP lookup using GID */
/* Native LDAP attribute names */
typedef struct idmap_nldap_q {
char **winname;
char **windomain;
char **unixname;
char **dn;
char **attr;
char **value;
int is_user;
int lrc;
char *filter;
char *udata;
typedef struct idmap_nldap_query_state {
const char *nldap_winname_attr;
const char *defdom;
int nqueries;
int qid;
int flag;
/*
* This routine has been copied from lib/nsswitch/ldap/common/ldap_utils.c
* after removing the debug statements.
*
* This is a generic filter callback function for merging the filter
* from service search descriptor with an existing search filter. This
* routine expects userdata to contain a format string with a single %s
* in it, and will use the format string with sprintf() to insert the
* SSD filter.
*
* This routine and userdata are passed to the __ns_ldap_list_batch_add()
* API.
*
* Consider an example that uses __ns_ldap_list_batch_add() to lookup
* native LDAP directory using a given userid 'xy12345'. In this
* example the userdata will contain the filter "(&(%s)(cn=xy1234))".
* If a SSD is defined to replace the rfc2307bis specified filter
* i.e. (objectClass=posixAccount) by a site-specific filter
* say (department=sds) then this routine when called will produce
* "(&(department=sds)(uid=xy1234))" as the real search filter.
*/
static
int
char **realfilter, const void *userdata)
{
int len;
char *checker;
if (realfilter == NULL)
return (NS_LDAP_INVALID_PARAM);
*realfilter = NULL;
return (NS_LDAP_INVALID_PARAM);
/* Parameter check. We only want one %s here, otherwise bail. */
len = 0; /* Reuse 'len' as "Number of %s hits"... */
do {
return (NS_LDAP_INVALID_PARAM);
len++; /* Got our %s. */
checker += 2;
} else if (len != 1)
return (NS_LDAP_INVALID_PARAM);
if (*realfilter == NULL)
return (NS_LDAP_MEMORY);
return (NS_LDAP_SUCCESS);
}
static
char
hex_char(int n)
{
return ("0123456789abcdef"[n & 0xf]);
}
/*
* If the input string contains special characters that needs to be
* escaped before the string can be used in a LDAP filter then this
* function will return a new sanitized string. Otherwise this function
* returns the input string (This saves us un-necessary memory allocations
* especially when processing a batch of requests). The caller must free
* the returned string if it isn't the input string.
*
* The escape mechanism for LDAP filter is described in RFC2254 basically
* it's \hh where hh are the two hexadecimal digits representing the ASCII
* value of the encoded character (case of hh is not significant).
* Example: * -> \2a, ( -> \28, ) -> \29, \ -> \5c,
*
* outstring = sanitize_for_ldap_filter(instring);
* if (outstring == NULL)
* Out of memory
* else
* Use outstring
* if (outstring != instring)
* free(outstring);
* done
*/
char *
{
const char *p;
int n;
/* Get a count of special characters */
for (p = str, n = 0; *p; p++)
if (*p == '*' || *p == '(' || *p == ')' ||
*p == '\\' || *p == '%')
n++;
/* If count is zero then no need to sanitize */
if (n == 0)
return ((char *)str);
/* Create output buffer that will contain the sanitized value */
return (NULL);
if (*p == '*' || *p == '(' || *p == ')' ||
*p == '\\' || *p == '%') {
*q++ = '\\';
*q++ = hex_char(*p >> 4);
*q++ = hex_char(*p & 0xf);
} else
*q++ = *p;
}
return (s_str);
}
/*
* Map libsldap status to idmap status
*/
static
{
switch (rc) {
case NS_LDAP_SUCCESS:
return (IDMAP_SUCCESS);
case NS_LDAP_NOTFOUND:
return (IDMAP_ERR_NOTFOUND);
case NS_LDAP_MEMORY:
return (IDMAP_ERR_MEMORY);
case NS_LDAP_CONFIG:
return (IDMAP_ERR_NS_LDAP_CFG);
case NS_LDAP_OP_FAILED:
return (IDMAP_ERR_NS_LDAP_OP_FAILED);
case NS_LDAP_PARTIAL:
return (IDMAP_ERR_NS_LDAP_PARTIAL);
case NS_LDAP_INTERNAL:
return (IDMAP_ERR_INTERNAL);
case NS_LDAP_INVALID_PARAM:
return (IDMAP_ERR_ARG);
default:
return (IDMAP_ERR_OTHER);
}
/*NOTREACHED*/
}
/*
* Create a batch for native LDAP lookup.
*/
static
{
s = calloc(1, sizeof (*s) +
if (s == NULL)
return (IDMAP_ERR_MEMORY);
free(s);
return (IDMAP_ERR_MEMORY);
}
s->flag = NS_LDAP_KEEP_CONN;
*qs = s;
return (IDMAP_SUCCESS);
}
/*
* Add a lookup by winname request to the batch.
*/
static
{
idmap_nldap_q_t *q;
const char **attrs;
if (is_user) {
db = "passwd";
wksid = 1;
} else {
*q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
goto errout;
}
} else {
db = "group";
wksid = 1;
} else {
*q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
goto errout;
}
}
/*
* Sanitize names. No need to sanitize qs->nldap_winname_attr
* because if it contained any of the special characters then
* it would have been rejected by the function that reads it
* from the SMF config. LDAP attribute names can only contain
* letters, digits or hyphens.
*/
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
/* windomain could be NULL for names of well-known SIDs */
if (s_windomain == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
}
/* Construct the filter and udata using snprintf. */
if (wksid) {
s_winname) + 1;
s_winname) + 1;
} else {
}
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
*q->rc = IDMAP_ERR_MEMORY;
goto errout;
}
if (wksid) {
} else {
}
if (s_windomain != windomain)
if (IS_NLDAP_RC_FATAL(q->lrc))
return (nldaprc2retcode(q->lrc));
return (IDMAP_SUCCESS);
/* query q and its content will be freed by batch_release */
if (s_windomain != windomain)
return (*q->rc);
}
/*
*/
static
{
idmap_nldap_q_t *q;
int len;
const char **attrs;
if (is_user) {
db = "passwd";
} else {
db = "group";
}
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
if (IS_NLDAP_RC_FATAL(q->lrc))
return (nldaprc2retcode(q->lrc));
return (IDMAP_SUCCESS);
}
/*
*/
static
{
idmap_nldap_q_t *q;
int len;
const char **attrs;
if (is_user) {
db = "passwd";
} else {
db = "group";
}
if (s_unixname == NULL) {
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
if (s_unixname != unixname)
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
if (s_unixname != unixname)
*q->rc = IDMAP_ERR_MEMORY;
return (IDMAP_ERR_MEMORY);
}
if (s_unixname != unixname)
if (IS_NLDAP_RC_FATAL(q->lrc))
return (nldaprc2retcode(q->lrc));
return (IDMAP_SUCCESS);
}
/*
* Free the batch
*/
static
void
{
idmap_nldap_q_t *q;
int i;
(void) __ns_ldap_freeError(&q->errorp);
(void) __ns_ldap_freeResult(&q->result);
}
}
/*
* Process all requests added to the batch and then free the batch.
* The results for individual requests will be accessible using the
* pointers passed during idmap_nldap_lookup_batch_end.
*/
static
{
idmap_nldap_q_t *q;
int i;
if (*q->rc != IDMAP_SUCCESS)
continue;
!q->result->entries_count ||
!entry->attr_count) {
*q->rc = IDMAP_ERR_NOTFOUND;
continue;
}
}
/* Get unixname */
goto out;
}
}
}
/* Get DN for how info */
goto out;
}
}
}
/* Get nldap name mapping attr name for how info */
goto out;
}
}
/* Get nldap name mapping attr value for how info */
continue;
goto out;
}
}
/* Get winname and windomain */
continue;
/*
* We need to split the value into winname and
* windomain. The value could be either in NT4
* style (i.e. dom\name) or AD-style (i.e. name@dom).
* We choose the first '\\' if it's in NT4 style and
* the last '@' if it's in AD-style for the split.
*/
NULL) == IDMAP_SUCCESS) {
*str = '\0';
*str = '\0';
} else {
"winname (%s) found in Native LDAP", *val);
*q->rc = IDMAP_ERR_NS_LDAP_BAD_WINNAME;
continue;
}
goto out;
}
}
goto out;
}
}
}
out:
(void) idmap_nldap_lookup_batch_release(qs);
return (rc);
}
/* ARGSUSED */
{
int i, add;
if (state->nldap_nqueries == 0)
return (IDMAP_SUCCESS);
/* Create nldap lookup batch */
if (retcode != IDMAP_SUCCESS) {
"Failed to create batch for native LDAP lookup");
goto out;
}
/* Add requests to the batch */
/* Skip if not marked for nldap lookup */
continue;
/* win2unix request: */
/*
* When processing a win2unix request, nldap lookup
* is performed after AD lookup or a successful
* name-cache lookup. Therefore we should already
* have sid, winname and sidtype. Note that
* windomain could be NULL e.g. well-known SIDs.
*/
/* Skip if we already have pid and unixname */
continue;
}
/* Clear leftover value */
/* Lookup nldap by winname to get pid and unixname */
add = 1;
/* unix2win request: */
/* Skip if we already have winname */
continue;
}
/* Clear old value */
/* Set how info */
/* Lookup nldap by pid or unixname to get winname */
add = 1;
add = 1;
}
}
/*
* nldap_batch_add API returns error only on fatal failures
* otherwise it returns success and the actual status
* is stored in the individual request (res->retcode).
* Stop adding requests to this batch on fatal failures
* (i.e. if retcode != success)
*/
if (retcode != IDMAP_SUCCESS)
break;
}
if (!add)
else if (retcode != IDMAP_SUCCESS)
else
out:
for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
continue;
/* Reset nldap flag */
/*
* As noted earlier retcode != success if there were fatal
* errors during batch_start and batch_adds. If so then set
* the status of each nldap request to that error.
*/
if (retcode != IDMAP_SUCCESS) {
continue;
}
if (!add)
continue;
/*
* If we successfully retrieved winname from nldap entry
* then lookup winname2sid locally. If not found locally
* then mark this request for AD lookup.
*/
req, 1);
if (rc1 == IDMAP_ERR_NOTFOUND) {
state->ad_nqueries++;
} else
}
/*
* Unset non-fatal errors in individual request. This allows
* the next pass to process other mapping mechanisms for
* this request.
*/
}
}
state->nldap_nqueries = 0;
return (retcode);
}