adutils.c revision e3f2c991a8548408db0a2787bd8b43d5124821d3
/*
* 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 UIDNUMBER "uidNumber"
#define GIDNUMBER "gidNumber"
#define UIDNUMBERFILTER "(&(objectclass=user)(uidNumber=%u))"
#define GIDNUMBERFILTER "(&(objectclass=group)(gidNumber=%u))"
#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;
int directory_based_mapping; /* enum */
char *default_domain;
};
/*
* 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 */
}
int directory_based_mapping, const char *default_domain,
{
return (IDMAP_ERR_MEMORY);
!= ADUTILS_SUCCESS) {
return (map_adrc2idmaprc(rc));
}
return (IDMAP_ERR_MEMORY);
}
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.
*
* Except for dn and attr, all strings are consumed, either by transferring
* them over into the request results (where the caller will eventually free
* them) or by freeing them here. Note that this aligns with the "const"
* declarations below.
*/
static
void
idmap_q_t *q,
char *san,
const char *dn,
const char *attr,
char *value,
char *sid,
int sid_type,
char *unixname,
{
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 consider Computer to be a subclass of User, so we can just
* ignore Computer entries and pay attention to the accompanying
* User entries.
*/
break;
break;
}
/*
* "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 *dn;
int sid_type;
int ok;
return;
/*
* Didn't find objectclass. Something's wrong with our
* AD data.
*/
goto out;
}
if (!ok) {
/*
* Didn't understand objectclass. Something's wrong with our
* AD data.
*/
goto out;
}
if (sid_type == _IDMAP_T_USER)
else if (sid_type == _IDMAP_T_GROUP)
"%s has Invalid %s value \"%s\"",
}
}
}
}
/*
* If the caller has requested unixname then determine the
* AD attribute name that will have the unixname, and retrieve
* its value.
*/
int unix_type;
/*
* Determine the target UNIX type.
*
* If the caller specified one, use that. Otherwise, give the
* same type that as we found for the Windows user.
*/
if (unix_type == _IDMAP_T_UNDEF) {
if (sid_type == _IDMAP_T_USER)
else if (sid_type == _IDMAP_T_GROUP)
}
if (unix_type == _IDMAP_T_USER)
else if (unix_type == _IDMAP_T_GROUP)
}
}
}
}
}
}
out:
}
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;
/*
* 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 */
/* Add attributes that are not always needed */
i = 0;
if (eunixtype != _IDMAP_T_GROUP &&
if (eunixtype != _IDMAP_T_USER &&
}
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));
}
{
/*
* 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
*/
return (IDMAP_ERR_MEMORY);
/* 'name' not qualified and dname not given */
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);
}
{
int ret;
char *filter;
/*
* 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);
}
{
char *filter, *s_unixname;
const char *attrname;
/* 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);
}
}
}
return (retcode);
}
{
char *filter;
const char *attrname;
/* Assemble filter */
if (is_user) {
} else {
}
return (IDMAP_ERR_MEMORY);
}
}
return (retcode);
}