/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Retrieve directory information for built-in users and groups
*/
#include <stdio.h>
#include <limits.h>
#include <sys/idmap.h>
#include <sys/param.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <libuutil.h>
#include <note.h>
#include "idmapd.h"
#include "directory.h"
#include "directory_private.h"
#include <rpcsvc/idmap_prot.h>
#include "directory_server_impl.h"
#include "sidutil.h"
static directory_error_t sid_dav(directory_values_rpc *lvals,
const wksids_table_t *wksid);
static directory_error_t directory_provider_builtin_populate(
directory_entry_rpc *pent, const wksids_table_t *wksid,
idmap_utf8str_list *attrs);
/*
* Retrieve information by name.
* Called indirectly through the directory_provider_static structure.
*/
static
directory_error_t
directory_provider_builtin_get(
directory_entry_rpc *del,
idmap_utf8str_list *ids,
idmap_utf8str types,
idmap_utf8str_list *attrs)
{
int i;
for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
const wksids_table_t *wksid;
directory_error_t de;
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.
*/
type = *types;
if (*(types+1) != '\0')
types++;
/*
* If this entry has already been handled, one way or another,
* skip it.
*/
if (del[i].status != DIRECTORY_NOT_FOUND)
continue;
char *id = ids->idmap_utf8str_list_val[i];
/*
* End-to-end error injection point.
* NEEDSWORK: should probably eliminate this for production
*/
if (uu_streq(id, " DEBUG BUILTIN ERROR ")) {
directory_entry_set_error(&del[i],
directory_error("Directory_provider_builtin.debug",
"Directory_provider_builtin: artificial error",
NULL));
continue;
}
if (type == DIRECTORY_ID_SID[0])
wksid = find_wk_by_sid(id);
else {
int idmap_id_type;
if (type == DIRECTORY_ID_NAME[0])
idmap_id_type = IDMAP_POSIXID;
else if (type == DIRECTORY_ID_USER[0])
idmap_id_type = IDMAP_UID;
else if (type == DIRECTORY_ID_GROUP[0])
idmap_id_type = IDMAP_GID;
else {
directory_entry_set_error(&del[i],
directory_error("invalid_arg.id_type",
"Invalid ID type \"%1\"",
types, NULL));
continue;
}
int id_len = strlen(id);
char name[id_len + 1];
char domain[id_len + 1];
split_name(name, domain, id);
wksid = find_wksid_by_name(name, domain, idmap_id_type);
}
if (wksid == NULL)
continue;
de = directory_provider_builtin_populate(&del[i], wksid, attrs);
if (de != NULL) {
directory_entry_set_error(&del[i], de);
de = NULL;
}
}
return (NULL);
}
/*
* Given a well-known name entry and a list of attributes that were
* requested, populate the structure to return to the caller.
*/
static
directory_error_t
directory_provider_builtin_populate(
directory_entry_rpc *pent,
const wksids_table_t *wksid,
idmap_utf8str_list *attrs)
{
int j;
directory_values_rpc *llvals;
int nattrs;
nattrs = attrs->idmap_utf8str_list_len;
llvals = calloc(nattrs, sizeof (directory_values_rpc));
if (llvals == NULL)
goto nomem;
pent->status = DIRECTORY_FOUND;
pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
for (j = 0; j < nattrs; j++) {
directory_values_rpc *val;
char *a;
directory_error_t de;
/*
* We're going to refer to these a lot, so make a shorthand
* copy.
*/
a = attrs->idmap_utf8str_list_val[j];
val = &llvals[j];
/*
* Start by assuming no errors and that we don't have
* the information.
*/
val->found = FALSE;
de = NULL;
if (uu_strcaseeq(a, "uid")) {
de = str_list_dav(val, &wksid->winname, 1);
} else if (uu_strcaseeq(a, "uidNumber")) {
if (wksid->pid != IDMAP_SENTINEL_PID &&
wksid->is_user) {
de = uint_list_dav(val, &wksid->pid, 1);
}
} else if (uu_strcaseeq(a, "gidNumber")) {
if (wksid->pid != IDMAP_SENTINEL_PID &&
!wksid->is_user) {
de = uint_list_dav(val, &wksid->pid, 1);
}
} else if (uu_strcaseeq(a, "displayName") ||
uu_strcaseeq(a, "cn")) {
de = str_list_dav(val, &wksid->winname, 1);
} else if (uu_strcaseeq(a, "distinguishedName")) {
char *container;
if (wksid->domain == NULL) {
container = "Users";
} else {
container = "Builtin";
}
RDLOCK_CONFIG();
char *dn;
(void) asprintf(&dn,
"CN=%s,CN=%s,DC=%s",
wksid->winname, container, _idmapdstate.hostname);
UNLOCK_CONFIG();
const char *cdn = dn;
de = str_list_dav(val, &cdn, 1);
free(dn);
} else if (uu_strcaseeq(a, "objectClass")) {
if (wksid->is_wuser) {
static const char *objectClasses[] = {
"top",
"person",
"organizationalPerson",
"user",
};
de = str_list_dav(val, objectClasses,
UU_NELEM(objectClasses));
} else {
static const char *objectClasses[] = {
"top",
"group",
};
de = str_list_dav(val, objectClasses,
UU_NELEM(objectClasses));
}
} else if (uu_strcaseeq(a, "objectSid")) {
de = sid_dav(val, wksid);
} else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
char *canon;
if (wksid->domain == NULL) {
RDLOCK_CONFIG();
(void) asprintf(&canon, "%s@%s",
wksid->winname, _idmapdstate.hostname);
UNLOCK_CONFIG();
} else if (uu_streq(wksid->domain, "")) {
canon = strdup(wksid->winname);
} else {
(void) asprintf(&canon, "%s@%s",
wksid->winname, wksid->domain);
}
if (canon == NULL)
goto nomem;
const char *ccanon = canon;
de = str_list_dav(val, &ccanon, 1);
free(canon);
} else if (uu_strcaseeq(a, "x-sun-provider")) {
const char *provider = "Builtin";
de = str_list_dav(val, &provider, 1);
}
if (de != NULL)
return (de);
}
return (NULL);
nomem:
return (directory_error("ENOMEM.users",
"No memory allocating return value for user lookup", NULL));
}
/*
* Given a well-known name structure, generate a binary-format SID.
* It's a bit perverse that we must take a text-format SID and turn it into
* a binary-format SID, only to have the caller probably turn it back into
* text format, but SIDs are carried across LDAP in binary format.
*/
static
directory_error_t
sid_dav(directory_values_rpc *lvals, const wksids_table_t *wksid)
{
char *text_sid;
sid_t *sid;
directory_error_t de;
if (wksid->sidprefix == NULL) {
RDLOCK_CONFIG();
(void) asprintf(&text_sid, "%s-%d",
_idmapdstate.cfg->pgcfg.machine_sid,
wksid->rid);
UNLOCK_CONFIG();
} else {
(void) asprintf(&text_sid, "%s-%d",
wksid->sidprefix, wksid->rid);
}
if (text_sid == NULL)
goto nomem;
sid = sid_fromstr(text_sid);
free(text_sid);
if (sid == NULL)
goto nomem;
sid_to_le(sid);
de = bin_list_dav(lvals, sid, 1, sid_len(sid));
sid_free(sid);
return (de);
nomem:
return (directory_error("ENOMEM.sid_dav",
"No memory allocating SID for user lookup", NULL));
}
struct directory_provider_static directory_provider_builtin = {
"builtin",
directory_provider_builtin_get,
};