adutils.c revision 1fcced4c370617db71610fecffd5451a5894ca5e
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Processes name2sid & sid2name batched lookups for a given user or
* computer from an AD Directory server using GSSAPI authentication
*/
#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include <strings.h>
#include <lber.h>
#include <ldap.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <synch.h>
#include <atomic.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include <time.h>
#include <sys/u8_textprep.h>
#include "libadutils.h"
#include "nldaputils.h"
#include "idmapd.h"
/* Attribute names and filter format strings */
#define SAN "sAMAccountName"
#define OBJSID "objectSid"
#define OBJCLASS "objectClass"
#define SANFILTER "(sAMAccountName=%.*s)"
#define OBJSIDFILTER "(objectSid=%s)"
/*
* A place to put the results of a batched (async) query
*
* There is one of these for every query added to a batch object
* (idmap_query_state, see below).
*/
typedef struct idmap_q {
/*
* data used for validating search result entries for name->SID
* lookups
*/
char *ecanonname; /* expected canon name */
char *edomain; /* expected domain name */
int eunixtype; /* expected unix type */
/* results */
char **canonname; /* actual canon name */
char **domain; /* name of domain of object */
char **sid; /* stringified SID */
int *sid_type; /* user or group SID? */
char **unixname; /* unixname for name mapping */
char **dn; /* DN of entry */
char **attr; /* Attr for name mapping */
char **value; /* value for name mapping */
/*
* The LDAP search entry result is placed here to be processed
* when the search done result is received.
*/
} idmap_q_t;
/* Batch context structure; typedef is in header file */
struct idmap_query_state {
int qsize; /* Queue size */
const char *ad_unixuser_attr;
const char *ad_unixgroup_attr;
};
/*
* Keep connection management simple for now, extend or replace later
* with updated libsldap code.
*/
#define ADREAPERSLEEP 60
/*
* Idle connection reaping side of connection management
*
* Every minute wake up and look for connections that have been idle for
* five minutes or more and close them.
*/
/*ARGSUSED*/
static
void
{
for (;;) {
/*
* nanosleep(3RT) is thead-safe (no SIGALRM) and more
* portable than usleep(3C)
*/
}
}
/*
* Take ad_host_config_t information, create a ad_host_t,
* populate it and add it to the list of hosts.
*/
int
{
int ret = -1;
ret = 0;
/* Start reaper if it doesn't exist */
return (ret);
}
static
{
switch (adrc) {
case ADUTILS_SUCCESS:
return (IDMAP_SUCCESS);
case ADUTILS_ERR_NOTFOUND:
return (IDMAP_ERR_NOTFOUND);
case ADUTILS_ERR_MEMORY:
return (IDMAP_ERR_MEMORY);
case ADUTILS_ERR_DOMAIN:
return (IDMAP_ERR_DOMAIN);
case ADUTILS_ERR_OTHER:
return (IDMAP_ERR_OTHER);
return (IDMAP_ERR_RETRIABLE_NET_ERR);
default:
return (IDMAP_ERR_INTERNAL);
}
/* NOTREACHED */
}
{
return (IDMAP_ERR_MEMORY);
!= ADUTILS_SUCCESS) {
return (map_adrc2idmaprc(rc));
}
return (IDMAP_SUCCESS);
}
/*
* Set unixuser_attr and unixgroup_attr for AD-based name mapping
*/
void
const char *unixuser_attr, const char *unixgroup_attr)
{
}
/*
* Take parsed attribute values from a search result entry and check if
* it is the result that was desired and, if so, set the result fields
* of the given idmap_q_t.
*
* Frees the unused char * values.
*/
static
void
{
char *domain;
int err1;
goto out;
/* Check that this is the canonname that we were looking for */
U8_STRCMP_CI_LOWER, /* no normalization, for now */
goto out;
}
/* Check that this is the domain that we were looking for */
goto out;
}
/* Copy the DN and attr and value */
/* Set results */
if (q->sid) {
}
if (q->rid)
if (q->sid_type)
if (q->unixname) {
}
}
/*
* The caller may be replacing the given winname by its
* canonical name and therefore free any old name before
* overwriting the field by the canonical name.
*/
}
q->ad_rc = ADUTILS_SUCCESS;
out:
/* Free unused attribute values */
}
/*
* Extract the class of the result entry. Returns 1 on success, 0 on
* failure.
*/
static
int
{
return (0);
/*
* We iterate over all the values because computer is a
* sub-class of user.
*/
break;
break;
/* Continue looping -- this may be a computer yet */
}
/*
* "else if (*sid_type = _IDMAP_T_USER)" then this is a
* new sub-class of user -- what to do with it??
*/
}
return (1);
}
/*
* Handle a given search result entry
*/
static
void
{
char *attr;
const char *unixuser_attr = NULL;
const char *unixgroup_attr = NULL;
int sid_type = _IDMAP_T_UNDEF;
int has_unixuser, has_unixgroup;
return;
/*
* If the caller has requested unixname then determine the
* AD attribute name that will have the unixname.
*/
if (q->eunixtype == _IDMAP_T_USER)
else if (q->eunixtype == _IDMAP_T_GROUP)
else if (q->eunixtype == _IDMAP_T_UNDEF) {
/*
* This is the case where we don't know
* before hand whether we need unixuser
* or unixgroup. This will be determined
* by the "sid_type" (i.e whether the given
* winname is user or group). If sid_type
* turns out to be user we will return
* unixuser (if found) and if it is a group
* we will return unixgroup (if found). We
* lookup for both ad_unixuser_attr and
* ad_unixgroup_attr and discard one of them
* after we know the "sidtype". This
* supports the following type of lookups.
*
* Example:
* $idmap show -c winname:foo
* In the above example, idmap will
* return uid if winname is winuser
* and gid if winname is wingroup.
*/
}
}
/*
* If this is an attribute we are looking for and
* haven't seen it yet, parse it
*/
}
}
&sid_type);
q->eunixtype == _IDMAP_T_UNDEF) {
/*
* This is the case where we didn't
* know whether we wanted unixuser or
* unixgroup as described above.
* Now since we know the "sid_type"
* we discard the unwanted value
* if it was retrieved before we
* got here.
*/
if (sid_type == _IDMAP_T_USER) {
} else if (sid_type == _IDMAP_T_GROUP) {
} else {
}
}
}
}
}
/* Got what we need */
break;
}
}
if (!has_class) {
/*
* Didn't find objectclass. Something's wrong with our
* AD data.
*/
} else {
/*
* Either we got what we needed and came out of the loop
* early OR we completed the loop in which case we didn't
* find some attributes that we were looking for. In either
* case set the result with what we got.
*/
}
}
void
void *argp)
{
switch (rc) {
case LDAP_RES_SEARCH_RESULT:
if (q->search_res != NULL) {
(void) ldap_msgfree(q->search_res);
q->search_res = NULL;
} else
q->ad_rc = ADUTILS_ERR_NOTFOUND;
break;
case LDAP_RES_SEARCH_ENTRY:
if (q->search_res == NULL) {
q->search_res = *res;
}
break;
default:
break;
}
}
static
void
{
int i;
}
}
/*
* This routine frees the idmap_query_state_t structure
*/
void
{
return;
}
{
int i;
/*
* Map adutils rc to idmap_retcode in each
* query because consumers in dbutils.c
* expects idmap_retcode.
*/
}
return (map_adrc2idmaprc(ad_rc));
}
/*
* Send one prepared search, queue up msgid, process what results are
* available
*/
static
{
int qid, i;
idmap_q_t *q;
static char *attrs[] = {
SAN,
NULL, /* placeholder for unixname attr */
NULL, /* placeholder for unixname attr */
};
/*
* Remember the expected canonname, domainname and unix type
* so we can check the results * against it
*/
q->ecanonname = ecanonname;
/* Remember where to put the results */
i = 3;
if (eunixtype != _IDMAP_T_GROUP &&
if (eunixtype != _IDMAP_T_USER &&
}
/*
* Provide sane defaults for the results in case we never hear
* back from the DS before closing the connection.
*
* In particular we default the result to indicate a retriable
* error. The first complete matching result entry will cause
* this to be set to IDMAP_SUCCESS, and the end of the results
* for this search will cause this to indicate "not found" if no
* result entries arrived or no complete ones matched the lookup
* we were doing.
*/
*rid = 0;
/*
* Don't set *canonname to NULL because it may be pointing to the
* given winname. Later on if we get a canonical name from AD the
* old name if any will be freed before assigning the new name.
*/
/*
* Invoke the mother of all APIs i.e. the adutils API
*/
(const char **)attrs,
return (map_adrc2idmaprc(ad_rc));
}
{
int len, samAcctNameLen;
/*
* entries will be checked to conform to the name and domain
* name given here. The DN, sAMAccountName, userPrincipalName,
* objectSid and objectClass of the result entries are all we
* need to figure out which entries match the lookup, the SID of
*/
/*
* We need the name and the domain name separately and as
* name@domain. We also allow the domain to be provided
* separately.
*/
return (IDMAP_ERR_MEMORY);
/* 'name' is qualified with a domain name */
return (IDMAP_ERR_MEMORY);
}
} else {
/* 'name' not qualified and dname not given */
if (*dname == '\0') {
return (IDMAP_ERR_DOMAIN);
}
return (IDMAP_ERR_MEMORY);
}
}
} else {
return (IDMAP_ERR_MEMORY);
}
}
return (IDMAP_ERR_DOMAIN_NOTFOUND);
}
return (IDMAP_ERR_MEMORY);
}
/* Assemble filter */
return (IDMAP_ERR_MEMORY);
}
return (retcode);
}
{
/*
* objectSid = SID with empty base DN. The DN, sAMAccountName
* and objectClass of the result are all we need to figure out
* the name of the SID and whether it is a user, a group or a
* computer.
*/
return (IDMAP_ERR_DOMAIN_NOTFOUND);
if (ret != 0)
return (IDMAP_ERR_SID);
/* Assemble filter */
return (IDMAP_ERR_MEMORY);
return (retcode);
}
{
/* Get unixuser or unixgroup AD attribute name */
return (IDMAP_ERR_NOTFOUND);
if (s_unixname == NULL)
return (IDMAP_ERR_MEMORY);
/* Assemble filter */
if (s_unixname != unixname)
return (IDMAP_ERR_MEMORY);
}
if (s_unixname != unixname)
}
if (ulen > 0) {
}
else
}
return (retcode);
}