idmap_kapi.c revision f7b4b2fefbe31d31fbe1e6a4b494a8fbed3f49b1
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Windows to Solaris Identity Mapping kernel API
* This module provides an API to map Windows SIDs to
* Solaris UID and GIDs.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/ksynch.h>
#include <sys/door.h>
#include <rpc/rpc_msg.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/rpc_sztypes.h>
#ifdef DEBUG
#include <sys/cmn_err.h>
#endif /* DEBUG */
#include <sys/proc.h>
#include <sys/sunddi.h>
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/sysmacros.h>
#include <sys/disp.h>
#include <sys/kidmap.h>
#include <sys/zone.h>
#include "idmap_prot.h"
#include "kidmap_priv.h"
/*
* Defined types
*/
/*
* This structure holds pointers for the
* batch mapping results.
*/
typedef struct idmap_get_res {
idmap_id_type idtype;
uid_t *uid;
gid_t *gid;
uid_t *pid;
int *is_user;
const char **sid_prefix;
uint32_t *rid;
idmap_stat *stat;
} idmap_get_res;
/* Batch mapping handle structure */
struct idmap_get_handle {
struct idmap_zone_specific *zs;
int mapping_num;
int mapping_size;
idmap_mapping *mapping;
idmap_get_res *result;
};
/* Zone specific data */
typedef struct idmap_zone_specific {
zoneid_t zone_id;
kmutex_t zone_mutex;
idmap_cache_t cache;
door_handle_t door_handle;
int door_valid;
uint32_t message_id;
} idmap_zone_specific_t;
/*
* Module global data
*/
static kmutex_t idmap_zone_mutex;
static zone_key_t idmap_zone_key;
/*
* Local function definitions
*/
static int
kidmap_rpc_call(idmap_zone_specific_t *zs, uint32_t op,
xdrproc_t xdr_args, caddr_t args,
xdrproc_t xdr_res, caddr_t res);
static int
kidmap_call_door(idmap_zone_specific_t *zs, door_arg_t *arg);
static idmap_zone_specific_t *
idmap_get_zone_specific(zone_t *zone);
int
idmap_reg_dh(zone_t *zone, door_handle_t dh)
{
idmap_zone_specific_t *zs;
zs = idmap_get_zone_specific(zone);
mutex_enter(&zs->zone_mutex);
if (zs->door_valid)
door_ki_rele(zs->door_handle);
zs->door_handle = dh;
zs->door_valid = 1;
mutex_exit(&zs->zone_mutex);
return (0);
}
/*
* idmap_unreg_dh
*
* This routine is called by system call idmap_unreg().
* idmap_unreg() calls door_ki_rele() on the supplied
* door handle after this routine returns. We only
* need to perform one door release on zs->door_handle
*/
int
idmap_unreg_dh(zone_t *zone, door_handle_t dh)
{
idmap_zone_specific_t *zs;
zs = idmap_get_zone_specific(zone);
kidmap_cache_purge(&zs->cache);
mutex_enter(&zs->zone_mutex);
if (!zs->door_valid || zs->door_handle != dh) {
mutex_exit(&zs->zone_mutex);
return (EINVAL);
}
door_ki_rele(zs->door_handle);
zs->door_valid = 0;
mutex_exit(&zs->zone_mutex);
return (0);
}
/*
* IMPORTANT. This function idmap_get_cache_data() is project
* private and is for use of the test system only and should
* not be used for other purposes.
*/
void
idmap_get_cache_data(zone_t *zone, size_t *uidbysid, size_t *gidbysid,
size_t *pidbysid, size_t *sidbyuid, size_t *sidbygid)
{
idmap_zone_specific_t *zs;
zs = idmap_get_zone_specific(zone);
kidmap_cache_get_data(&zs->cache, uidbysid, gidbysid,
pidbysid, sidbyuid, sidbygid);
}
static int
kidmap_call_door(idmap_zone_specific_t *zs, door_arg_t *arg)
{
door_handle_t dh;
door_info_t di;
int status = 0;
int nretries = 0;
retry:
dh = NULL;
mutex_enter(&zs->zone_mutex);
if (zs->door_valid) {
dh = zs->door_handle;
door_ki_hold(dh);
}
mutex_exit(&zs->zone_mutex);
if (dh == NULL) {
/*
* There is no door handle yet. Give
* smf a chance to restarts the idmap daemon
*/
if (nretries++ < 5) {
delay(hz);
goto retry;
}
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: Error no registered door to call the "
"idmap daemon\n");
#endif
return (-1);
}
status = door_ki_upcall(dh, arg);
switch (status) {
case 0: /* Success */
door_ki_rele(dh);
return (0);
case EINTR:
/* If we took an interrupt we have to bail out. */
if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
door_ki_rele(dh);
zcmn_err(zs->zone_id, CE_WARN,
"idmap: Interrupted\n");
return (-1);
}
/*
* Just retry and see what happens.
*/
/* FALLTHROUGH */
case EAGAIN:
/* A resouce problem */
door_ki_rele(dh);
/* Back off before retrying */
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: Door call returned error %d. Retrying\n", status);
#endif /* DEBUG */
delay(hz);
goto retry;
case EBADF:
/* Stale door handle. See if smf restarts the daemon. */
door_ki_rele(dh);
mutex_enter(&zs->zone_mutex);
if (zs->door_valid && dh == zs->door_handle) {
zs->door_valid = 0;
door_ki_rele(zs->door_handle);
}
mutex_exit(&zs->zone_mutex);
/* Back off before retrying */
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: Door call returned error %d. Retrying\n", status);
#endif /* DEBUG */
delay(hz);
goto retry;
default:
/* Unknown error */
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: Door call returned error %d.\n", status);
#endif /* DEBUG */
door_ki_rele(dh);
return (-1);
}
}
static idmap_zone_specific_t *
idmap_get_zone_specific(zone_t *zone)
{
idmap_zone_specific_t *zs;
ASSERT(zone != NULL);
zs = zone_getspecific(idmap_zone_key, zone);
if (zs != NULL)
return (zs);
mutex_enter(&idmap_zone_mutex);
zs = zone_getspecific(idmap_zone_key, zone);
if (zs == NULL) {
zs = kmem_zalloc(sizeof (idmap_zone_specific_t), KM_SLEEP);
mutex_init(&zs->zone_mutex, NULL, MUTEX_DEFAULT, NULL);
kidmap_cache_create(&zs->cache);
zs->zone_id = zone->zone_id;
(void) zone_setspecific(idmap_zone_key, zone, zs);
mutex_exit(&idmap_zone_mutex);
return (zs);
}
mutex_exit(&idmap_zone_mutex);
return (zs);
}
static void
/* ARGSUSED */
idmap_zone_destroy(zoneid_t zone_id, void *arg)
{
idmap_zone_specific_t *zs = arg;
if (zs != NULL) {
kidmap_cache_delete(&zs->cache);
if (zs->door_valid) {
door_ki_rele(zs->door_handle);
}
mutex_destroy(&zs->zone_mutex);
kmem_free(zs, sizeof (idmap_zone_specific_t));
}
}
int
kidmap_start(void)
{
mutex_init(&idmap_zone_mutex, NULL, MUTEX_DEFAULT, NULL);
zone_key_create(&idmap_zone_key, NULL, NULL, idmap_zone_destroy);
kidmap_sid_prefix_store_init();
return (0);
}
int
kidmap_stop(void)
{
return (EBUSY);
}
/*
* idmap_get_door
*
* This is called by the system call allocids() to get the door for the
* given zone.
*/
door_handle_t
idmap_get_door(zone_t *zone)
{
door_handle_t dh = NULL;
idmap_zone_specific_t *zs;
zs = idmap_get_zone_specific(zone);
mutex_enter(&zs->zone_mutex);
if (zs->door_valid) {
dh = zs->door_handle;
door_ki_hold(dh);
}
mutex_exit(&zs->zone_mutex);
return (dh);
}
/*
* idmap_purge_cache
*
* This is called by the system call allocids() to purge the cache for the
* given zone.
*/
void
idmap_purge_cache(zone_t *zone)
{
idmap_zone_specific_t *zs;
zs = idmap_get_zone_specific(zone);
kidmap_cache_purge(&zs->cache);
}
/*
* Given Domain SID and RID, get UID
*
* Input:
* sid_prefix - Domain SID in canonical form
* rid - RID
*
* Output:
* uid - POSIX UID if return == IDMAP_SUCCESS
*
* Return:
* Success return IDMAP_SUCCESS else IDMAP error
*/
idmap_stat
kidmap_getuidbysid(zone_t *zone, const char *sid_prefix, uint32_t rid,
uid_t *uid)
{
idmap_zone_specific_t *zs;
idmap_mapping_batch args;
idmap_mapping mapping;
idmap_ids_res results;
uint32_t op = IDMAP_GET_MAPPED_IDS;
const char *new_sid_prefix;
idmap_stat status;
if (sid_prefix == NULL || uid == NULL)
return (IDMAP_ERR_ARG);
zs = idmap_get_zone_specific(zone);
if (kidmap_cache_lookup_uidbysid(&zs->cache, sid_prefix, rid, uid)
== IDMAP_SUCCESS)
return (IDMAP_SUCCESS);
bzero(&mapping, sizeof (idmap_mapping));
mapping.id1.idtype = IDMAP_SID;
mapping.id1.idmap_id_u.sid.prefix = (char *)sid_prefix;
mapping.id1.idmap_id_u.sid.rid = rid;
mapping.id2.idtype = IDMAP_UID;
bzero(&results, sizeof (idmap_ids_res));
args.idmap_mapping_batch_len = 1;
args.idmap_mapping_batch_val = &mapping;
if (kidmap_rpc_call(zs, op, xdr_idmap_mapping_batch,
(caddr_t)&args, xdr_idmap_ids_res,
(caddr_t)&results) == 0) {
/* Door call succeded */
if (results.retcode != IDMAP_SUCCESS) {
status = results.retcode;
*uid = UID_NOBODY;
} else if (results.ids.ids_len >= 1 &&
results.ids.ids_val[0].id.idtype == IDMAP_UID) {
status = results.ids.ids_val[0].retcode;
*uid = results.ids.ids_val[0].id.idmap_id_u.uid;
if (status == IDMAP_SUCCESS) {
new_sid_prefix = kidmap_find_sid_prefix(
sid_prefix);
kidmap_cache_add_uidbysid(&zs->cache,
new_sid_prefix, rid, *uid);
}
} else {
status = IDMAP_ERR_NOMAPPING;
*uid = UID_NOBODY;
}
xdr_free(xdr_idmap_ids_res, (char *)&results);
} else {
/* Door call failed */
status = IDMAP_ERR_NOMAPPING;
*uid = UID_NOBODY;
}
return (status);
}
/*
* Given Domain SID and RID, get GID
*
* Input:
* sid_prefix - Domain SID in canonical form
* rid - RID
*
* Output:
* gid - POSIX UID if return == IDMAP_SUCCESS
*
* Return:
* Success return IDMAP_SUCCESS else IDMAP error
*/
idmap_stat
kidmap_getgidbysid(zone_t *zone, const char *sid_prefix, uint32_t rid,
gid_t *gid)
{
idmap_zone_specific_t *zs;
idmap_mapping_batch args;
idmap_mapping mapping;
idmap_ids_res results;
uint32_t op = IDMAP_GET_MAPPED_IDS;
const char *new_sid_prefix;
idmap_stat status;
if (sid_prefix == NULL || gid == NULL)
return (IDMAP_ERR_ARG);
zs = idmap_get_zone_specific(zone);
if (kidmap_cache_lookup_gidbysid(&zs->cache, sid_prefix, rid, gid)
== IDMAP_SUCCESS)
return (IDMAP_SUCCESS);
bzero(&mapping, sizeof (idmap_mapping));
mapping.id1.idtype = IDMAP_SID;
mapping.id1.idmap_id_u.sid.prefix = (char *)sid_prefix;
mapping.id1.idmap_id_u.sid.rid = rid;
mapping.id2.idtype = IDMAP_GID;
bzero(&results, sizeof (idmap_ids_res));
args.idmap_mapping_batch_len = 1;
args.idmap_mapping_batch_val = &mapping;
if (kidmap_rpc_call(zs, op, xdr_idmap_mapping_batch,
(caddr_t)&args, xdr_idmap_ids_res,
(caddr_t)&results) == 0) {
/* Door call succeded */
if (results.retcode != IDMAP_SUCCESS) {
status = results.retcode;
*gid = GID_NOBODY;
} else if (results.ids.ids_len >= 1 &&
results.ids.ids_val[0].id.idtype == IDMAP_GID) {
status = results.ids.ids_val[0].retcode;
*gid = results.ids.ids_val[0].id.idmap_id_u.gid;
if (status == IDMAP_SUCCESS) {
new_sid_prefix = kidmap_find_sid_prefix(
sid_prefix);
kidmap_cache_add_gidbysid(&zs->cache,
new_sid_prefix, rid, *gid);
}
} else {
status = IDMAP_ERR_NOMAPPING;
*gid = GID_NOBODY;
}
xdr_free(xdr_idmap_ids_res, (char *)&results);
} else {
/* Door call failed */
status = IDMAP_ERR_NOMAPPING;
*gid = GID_NOBODY;
}
return (status);
}
/*
* Given Domain SID and RID, get Posix ID
*
* Input:
* sid_prefix - Domain SID in canonical form
* rid - RID
*
* Output:
* pid - POSIX ID if return == IDMAP_SUCCESS
* is_user - 1 == UID, 0 == GID if return == IDMAP_SUCCESS
*
* Return:
* Success return IDMAP_SUCCESS else IDMAP error
*/
idmap_stat
kidmap_getpidbysid(zone_t *zone, const char *sid_prefix, uint32_t rid,
uid_t *pid, int *is_user)
{
idmap_zone_specific_t *zs;
idmap_mapping_batch args;
idmap_mapping mapping;
idmap_ids_res results;
uint32_t op = IDMAP_GET_MAPPED_IDS;
const char *new_sid_prefix;
idmap_stat status;
if (sid_prefix == NULL || pid == NULL || is_user == NULL)
return (IDMAP_ERR_ARG);
zs = idmap_get_zone_specific(zone);
if (kidmap_cache_lookup_pidbysid(&zs->cache, sid_prefix, rid, pid,
is_user) == IDMAP_SUCCESS)
return (IDMAP_SUCCESS);
bzero(&mapping, sizeof (idmap_mapping));
mapping.id1.idtype = IDMAP_SID;
mapping.id1.idmap_id_u.sid.prefix = (char *)sid_prefix;
mapping.id1.idmap_id_u.sid.rid = rid;
mapping.id2.idtype = IDMAP_POSIXID;
bzero(&results, sizeof (idmap_ids_res));
args.idmap_mapping_batch_len = 1;
args.idmap_mapping_batch_val = &mapping;
if (kidmap_rpc_call(zs, op, xdr_idmap_mapping_batch,
(caddr_t)&args, xdr_idmap_ids_res,
(caddr_t)&results) == 0) {
/* Door call succeded */
if (results.retcode != IDMAP_SUCCESS) {
status = results.retcode;
*is_user = 1;
*pid = UID_NOBODY;
} else if (results.ids.ids_len >= 1 && (
results.ids.ids_val[0].id.idtype == IDMAP_UID ||
results.ids.ids_val[0].id.idtype == IDMAP_GID)) {
status = results.ids.ids_val[0].retcode;
if (results.ids.ids_val[0].id.idtype == IDMAP_UID) {
*is_user = 1;
*pid = results.ids.ids_val[0].id.idmap_id_u.uid;
} else {
*is_user = 0;
*pid = results.ids.ids_val[0].id.idmap_id_u.gid;
}
if (status == IDMAP_SUCCESS) {
new_sid_prefix = kidmap_find_sid_prefix(
sid_prefix);
kidmap_cache_add_pidbysid(&zs->cache,
new_sid_prefix, rid, *pid,
*is_user);
}
} else {
status = IDMAP_ERR_NOMAPPING;
*is_user = 1;
*pid = UID_NOBODY;
}
xdr_free(xdr_idmap_ids_res, (char *)&results);
} else {
/* Door call failed */
status = IDMAP_ERR_NOMAPPING;
*is_user = 1;
*pid = UID_NOBODY;
}
return (status);
}
/*
* Given UID, get Domain SID and RID
*
* Input:
* uid - Posix UID
*
* Output:
* sid_prefix - Domain SID if return == IDMAP_SUCCESS
* rid - RID if return == IDMAP_SUCCESS
*
* Return:
* Success return IDMAP_SUCCESS else IDMAP error
*/
idmap_stat
kidmap_getsidbyuid(zone_t *zone, uid_t uid, const char **sid_prefix,
uint32_t *rid)
{
idmap_zone_specific_t *zs;
idmap_mapping_batch args;
idmap_mapping mapping;
idmap_ids_res results;
uint32_t op = IDMAP_GET_MAPPED_IDS;
idmap_stat status;
time_t entry_ttl;
idmap_id *id;
if (sid_prefix == NULL || rid == NULL)
return (IDMAP_ERR_ARG);
zs = idmap_get_zone_specific(zone);
if (kidmap_cache_lookup_sidbyuid(&zs->cache, sid_prefix, rid, uid)
== IDMAP_SUCCESS) {
return (IDMAP_SUCCESS);
}
bzero(&mapping, sizeof (idmap_mapping));
mapping.id1.idtype = IDMAP_UID;
mapping.id1.idmap_id_u.uid = uid;
mapping.id2.idtype = IDMAP_SID;
bzero(&results, sizeof (idmap_ids_res));
args.idmap_mapping_batch_len = 1;
args.idmap_mapping_batch_val = &mapping;
if (kidmap_rpc_call(zs, op, xdr_idmap_mapping_batch,
(caddr_t)&args, xdr_idmap_ids_res,
(caddr_t)&results) == 0) {
/* Door call succeded */
if (results.retcode != IDMAP_SUCCESS) {
status = results.retcode;
*rid = 0;
*sid_prefix = NULL;
} else if (results.ids.ids_len >= 1 &&
(results.ids.ids_val[0].id.idtype == IDMAP_SID ||
results.ids.ids_val[0].id.idtype == IDMAP_USID ||
results.ids.ids_val[0].id.idtype == IDMAP_GSID)) {
status = results.ids.ids_val[0].retcode;
id = &results.ids.ids_val[0].id;
*sid_prefix = kidmap_find_sid_prefix(
id->idmap_id_u.sid.prefix);
*rid = id->idmap_id_u.sid.rid;
if (status == IDMAP_SUCCESS) {
kidmap_cache_add_sidbyuid(&zs->cache,
*sid_prefix, *rid, uid);
}
} else {
status = IDMAP_ERR_NOMAPPING;
*rid = 0;
*sid_prefix = NULL;
}
xdr_free(xdr_idmap_ids_res, (char *)&results);
} else {
/* Door call failed */
status = IDMAP_ERR_NOMAPPING;
*rid = 0;
*sid_prefix = NULL;
}
return (status);
}
/*
* Given GID, get Domain SID and RID
*
* Input:
* gid - Posix GID
*
* Output:
* sid_prefix - Domain SID if return == IDMAP_SUCCESS
* rid - RID if return == IDMAP_SUCCESS
*
* Return:
* Success return IDMAP_SUCCESS else IDMAP error
*/
idmap_stat
kidmap_getsidbygid(zone_t *zone, gid_t gid, const char **sid_prefix,
uint32_t *rid)
{
idmap_zone_specific_t *zs;
idmap_mapping_batch args;
idmap_mapping mapping;
idmap_ids_res results;
uint32_t op = IDMAP_GET_MAPPED_IDS;
idmap_stat status;
idmap_id *id;
if (sid_prefix == NULL || rid == NULL)
return (IDMAP_ERR_ARG);
zs = idmap_get_zone_specific(zone);
if (kidmap_cache_lookup_sidbygid(&zs->cache, sid_prefix, rid, gid)
== IDMAP_SUCCESS) {
return (IDMAP_SUCCESS);
}
bzero(&mapping, sizeof (idmap_mapping));
mapping.id1.idtype = IDMAP_GID;
mapping.id1.idmap_id_u.uid = gid;
mapping.id2.idtype = IDMAP_SID;
bzero(&results, sizeof (idmap_ids_res));
args.idmap_mapping_batch_len = 1;
args.idmap_mapping_batch_val = &mapping;
if (kidmap_rpc_call(zs, op, xdr_idmap_mapping_batch,
(caddr_t)&args, xdr_idmap_ids_res,
(caddr_t)&results) == 0) {
/* Door call succeded */
if (results.retcode != IDMAP_SUCCESS) {
status = results.retcode;
*rid = 0;
*sid_prefix = NULL;
} else if (results.ids.ids_len >= 1 &&
(results.ids.ids_val[0].id.idtype == IDMAP_SID ||
results.ids.ids_val[0].id.idtype == IDMAP_USID ||
results.ids.ids_val[0].id.idtype == IDMAP_GSID)) {
status = results.ids.ids_val[0].retcode;
id = &results.ids.ids_val[0].id;
*sid_prefix = kidmap_find_sid_prefix(
id->idmap_id_u.sid.prefix);
*rid = id->idmap_id_u.sid.rid;
if (status == IDMAP_SUCCESS) {
kidmap_cache_add_sidbygid(&zs->cache,
*sid_prefix, *rid, gid);
}
} else {
status = IDMAP_ERR_NOMAPPING;
*rid = 0;
*sid_prefix = NULL;
}
xdr_free(xdr_idmap_ids_res, (char *)&results);
} else {
/* Door call failed */
status = IDMAP_ERR_NOMAPPING;
*rid = 0;
*sid_prefix = NULL;
}
return (status);
}
/*
* Create handle to get SID to UID/GID mapping entries
*
* Input:
* none
* Return:
* get_handle
*
*/
idmap_get_handle_t *
kidmap_get_create(zone_t *zone)
{
idmap_zone_specific_t *zs;
idmap_get_handle_t *handle;
#define INIT_MAPPING_SIZE 32
zs = idmap_get_zone_specific(zone);
handle = kmem_zalloc(sizeof (idmap_get_handle_t), KM_SLEEP);
handle->mapping = kmem_zalloc((sizeof (idmap_mapping)) *
INIT_MAPPING_SIZE, KM_SLEEP);
handle->result = kmem_zalloc((sizeof (idmap_get_res)) *
INIT_MAPPING_SIZE, KM_SLEEP);
handle->mapping_size = INIT_MAPPING_SIZE;
handle->zs = zs;
return (handle);
}
/*
* Internal routine to extend a "get_handle"
*/
static void
kidmap_get_extend(idmap_get_handle_t *get_handle)
{
idmap_mapping *mapping;
idmap_get_res *result;
int new_size = get_handle->mapping_size + INIT_MAPPING_SIZE;
mapping = kmem_zalloc((sizeof (idmap_mapping)) *
new_size, KM_SLEEP);
(void) memcpy(mapping, get_handle->mapping,
(sizeof (idmap_mapping)) * get_handle->mapping_size);
result = kmem_zalloc((sizeof (idmap_get_res)) *
new_size, KM_SLEEP);
(void) memcpy(result, get_handle->result,
(sizeof (idmap_get_res)) * get_handle->mapping_size);
kmem_free(get_handle->mapping,
(sizeof (idmap_mapping)) * get_handle->mapping_size);
get_handle->mapping = mapping;
kmem_free(get_handle->result,
(sizeof (idmap_get_res)) * get_handle->mapping_size);
get_handle->result = result;
get_handle->mapping_size = new_size;
}
/*
* Given Domain SID and RID, get UID
*
* Input:
* sid_prefix - Domain SID in canonical form
* rid - RID
*
* Output:
* stat - status of the get request
* uid - POSIX UID if stat == IDMAP_SUCCESS
*
* Note: The output parameters will be set by idmap_get_mappings()
*/
idmap_stat
kidmap_batch_getuidbysid(idmap_get_handle_t *get_handle, const char *sid_prefix,
uint32_t rid, uid_t *uid, idmap_stat *stat)
{
idmap_mapping *mapping;
idmap_get_res *result;
if (get_handle == NULL || sid_prefix == NULL ||
uid == NULL || stat == NULL)
return (IDMAP_ERR_ARG);
if (kidmap_cache_lookup_uidbysid(&get_handle->zs->cache, sid_prefix,
rid, uid) == IDMAP_SUCCESS) {
*stat = IDMAP_SUCCESS;
return (IDMAP_SUCCESS);
}
if (get_handle->mapping_num >= get_handle->mapping_size)
kidmap_get_extend(get_handle);
mapping = &get_handle->mapping[get_handle->mapping_num];
mapping->flag = 0;
mapping->id1.idtype = IDMAP_SID;
mapping->id1.idmap_id_u.sid.prefix = (char *)sid_prefix;
mapping->id1.idmap_id_u.sid.rid = rid;
mapping->id2.idtype = IDMAP_UID;
result = &get_handle->result[get_handle->mapping_num];
result->idtype = IDMAP_UID;
result->uid = uid;
result->gid = NULL;
result->pid = NULL;
result->sid_prefix = NULL;
result->rid = NULL;
result->is_user = NULL;
result->stat = stat;
get_handle->mapping_num++;
return (IDMAP_SUCCESS);
}
/*
* Given Domain SID and RID, get GID
*
* Input:
* sid_prefix - Domain SID in canonical form
* rid - RID
*
* Output:
* stat - status of the get request
* gid - POSIX GID if stat == IDMAP_SUCCESS
*
* Note: The output parameters will be set by idmap_get_mappings()
*/
idmap_stat
kidmap_batch_getgidbysid(idmap_get_handle_t *get_handle, const char *sid_prefix,
uint32_t rid, uid_t *gid, idmap_stat *stat)
{
idmap_mapping *mapping;
idmap_get_res *result;
if (get_handle == NULL || sid_prefix == NULL ||
gid == NULL || stat == NULL)
return (IDMAP_ERR_ARG);
if (kidmap_cache_lookup_gidbysid(&get_handle->zs->cache, sid_prefix,
rid, gid) == IDMAP_SUCCESS) {
*stat = IDMAP_SUCCESS;
return (IDMAP_SUCCESS);
}
if (get_handle->mapping_num >= get_handle->mapping_size)
kidmap_get_extend(get_handle);
mapping = &get_handle->mapping[get_handle->mapping_num];
mapping->flag = 0;
mapping->id1.idtype = IDMAP_SID;
mapping->id1.idmap_id_u.sid.prefix = (char *)sid_prefix;
mapping->id1.idmap_id_u.sid.rid = rid;
mapping->id2.idtype = IDMAP_GID;
result = &get_handle->result[get_handle->mapping_num];
result->idtype = IDMAP_GID;
result->uid = NULL;
result->gid = gid;
result->pid = NULL;
result->sid_prefix = NULL;
result->rid = NULL;
result->is_user = NULL;
result->stat = stat;
get_handle->mapping_num++;
return (IDMAP_SUCCESS);
}
/*
* Given Domain SID and RID, get Posix ID
*
* Input:
* sid_prefix - Domain SID in canonical form
* rid - RID
*
* Output:
* stat - status of the get request
* is_user - user or group
* pid - POSIX UID if stat == IDMAP_SUCCESS and is_user == 1
* POSIX GID if stat == IDMAP_SUCCESS and is_user == 0
*
* Note: The output parameters will be set by idmap_get_mappings()
*/
idmap_stat
kidmap_batch_getpidbysid(idmap_get_handle_t *get_handle, const char *sid_prefix,
uint32_t rid, uid_t *pid, int *is_user, idmap_stat *stat)
{
idmap_mapping *mapping;
idmap_get_res *result;
if (get_handle == NULL || sid_prefix == NULL || pid == NULL ||
is_user == NULL || stat == NULL)
return (IDMAP_ERR_ARG);
if (kidmap_cache_lookup_pidbysid(&get_handle->zs->cache, sid_prefix,
rid, pid, is_user) == IDMAP_SUCCESS) {
*stat = IDMAP_SUCCESS;
return (IDMAP_SUCCESS);
}
if (get_handle->mapping_num >= get_handle->mapping_size)
kidmap_get_extend(get_handle);
mapping = &get_handle->mapping[get_handle->mapping_num];
mapping->flag = 0;
mapping->id1.idtype = IDMAP_SID;
mapping->id1.idmap_id_u.sid.prefix = (char *)sid_prefix;
mapping->id1.idmap_id_u.sid.rid = rid;
mapping->id2.idtype = IDMAP_POSIXID;
result = &get_handle->result[get_handle->mapping_num];
result->idtype = IDMAP_POSIXID;
result->uid = NULL;
result->gid = NULL;
result->pid = pid;
result->sid_prefix = NULL;
result->rid = NULL;
result->is_user = is_user;
result->stat = stat;
get_handle->mapping_num++;
return (IDMAP_SUCCESS);
}
/*
* Given UID, get SID and RID
*
* Input:
* uid - POSIX UID
*
* Output:
* stat - status of the get request
* sid - SID in canonical form (if stat == IDMAP_SUCCESS)
* rid - RID (if stat == IDMAP_SUCCESS)
*
* Note: The output parameters will be set by idmap_get_mappings()
*/
idmap_stat
kidmap_batch_getsidbyuid(idmap_get_handle_t *get_handle, uid_t uid,
const char **sid_prefix, uint32_t *rid, idmap_stat *stat)
{
idmap_mapping *mapping;
idmap_get_res *result;
if (get_handle == NULL || sid_prefix == NULL ||
rid == NULL || stat == NULL)
return (IDMAP_ERR_ARG);
if (kidmap_cache_lookup_sidbyuid(&get_handle->zs->cache,
sid_prefix, rid, uid) == IDMAP_SUCCESS) {
*stat = IDMAP_SUCCESS;
return (IDMAP_SUCCESS);
}
if (get_handle->mapping_num >= get_handle->mapping_size)
kidmap_get_extend(get_handle);
mapping = &get_handle->mapping[get_handle->mapping_num];
mapping->flag = 0;
mapping->id1.idtype = IDMAP_UID;
mapping->id1.idmap_id_u.uid = uid;
mapping->id2.idtype = IDMAP_SID;
result = &get_handle->result[get_handle->mapping_num];
result->idtype = IDMAP_SID;
result->uid = NULL;
result->gid = NULL;
result->pid = NULL;
result->sid_prefix = sid_prefix;
result->rid = rid;
result->is_user = NULL;
result->stat = stat;
get_handle->mapping_num++;
return (IDMAP_SUCCESS);
}
/*
* Given GID, get SID and RID
*
* Input:
* gid - POSIX GID
*
* Output:
* stat - status of the get request
* sid - SID in canonical form (if stat == IDMAP_SUCCESS)
* rid - RID (if stat == IDMAP_SUCCESS)
*
* Note: The output parameters will be set by idmap_get_mappings()
*/
idmap_stat
kidmap_batch_getsidbygid(idmap_get_handle_t *get_handle, gid_t gid,
const char **sid_prefix, uint32_t *rid, idmap_stat *stat)
{
idmap_mapping *mapping;
idmap_get_res *result;
if (get_handle == NULL || sid_prefix == NULL ||
rid == NULL || stat == NULL)
return (IDMAP_ERR_ARG);
if (kidmap_cache_lookup_sidbygid(&get_handle->zs->cache,
sid_prefix, rid, gid) == IDMAP_SUCCESS) {
*stat = IDMAP_SUCCESS;
return (IDMAP_SUCCESS);
}
if (get_handle->mapping_num >= get_handle->mapping_size)
kidmap_get_extend(get_handle);
mapping = &get_handle->mapping[get_handle->mapping_num];
mapping->flag = 0;
mapping->id1.idtype = IDMAP_GID;
mapping->id1.idmap_id_u.gid = gid;
mapping->id2.idtype = IDMAP_SID;
result = &get_handle->result[get_handle->mapping_num];
result->idtype = IDMAP_SID;
result->uid = NULL;
result->gid = NULL;
result->pid = NULL;
result->sid_prefix = sid_prefix;
result->rid = rid;
result->is_user = NULL;
result->stat = stat;
get_handle->mapping_num++;
return (IDMAP_SUCCESS);
}
/*
* Process the batched "get mapping" requests. The results (i.e.
* status and identity) will be available in the data areas
* provided by individual requests.
*
* If the door call fails the status IDMAP_ERR_NOMAPPING is
* return and the UID or UID result is set to "nobody"
*/
idmap_stat
kidmap_get_mappings(idmap_get_handle_t *get_handle)
{
idmap_mapping_batch rpc_args;
idmap_ids_res rpc_res;
uint32_t op = IDMAP_GET_MAPPED_IDS;
idmap_mapping *mapping;
idmap_get_res *result;
idmap_id *id;
int status;
int i;
const char *sid_prefix;
int is_user;
idmap_cache_t *cache;
if (get_handle == NULL)
return (IDMAP_ERR_ARG);
if (get_handle->mapping_num == 0)
return (IDMAP_SUCCESS);
cache = &get_handle->zs->cache;
bzero(&rpc_res, sizeof (idmap_ids_res));
rpc_args.idmap_mapping_batch_len = get_handle->mapping_num;
rpc_args.idmap_mapping_batch_val = get_handle->mapping;
if (kidmap_rpc_call(get_handle->zs, op, xdr_idmap_mapping_batch,
(caddr_t)&rpc_args, xdr_idmap_ids_res,
(caddr_t)&rpc_res) != 0) {
/* Door call failed */
status = IDMAP_ERR_NOMAPPING;
goto error;
}
status = rpc_res.retcode;
if (status != IDMAP_SUCCESS) {
/* RPC returned idmap error code */
xdr_free(xdr_idmap_ids_res, (char *)&rpc_res);
goto error;
}
for (i = 0; i < get_handle->mapping_num; i++) {
mapping = &get_handle->mapping[i];
result = &get_handle->result[i];
if (i >= rpc_res.ids.ids_len) {
*result->stat = IDMAP_ERR_NOMAPPING;
if (result->uid)
*result->uid = UID_NOBODY;
if (result->gid)
*result->gid = GID_NOBODY;
if (result->pid)
*result->pid = UID_NOBODY;
if (result->is_user)
*result->is_user = 1;
if (result->sid_prefix)
*result->sid_prefix = NULL;
if (result->rid)
*result->rid = 0;
continue;
}
*result->stat = rpc_res.ids.ids_val[i].retcode;
id = &rpc_res.ids.ids_val[i].id;
switch (id->idtype) {
case IDMAP_UID:
if (result->uid)
*result->uid = id->idmap_id_u.uid;
if (result->pid)
*result->pid = id->idmap_id_u.uid;
if (result->is_user)
*result->is_user = 1;
sid_prefix = kidmap_find_sid_prefix(
mapping->id1.idmap_id_u.sid.prefix);
if (*result->stat == IDMAP_SUCCESS && result->uid)
kidmap_cache_add_uidbysid(
cache, sid_prefix,
mapping->id1.idmap_id_u.sid.rid,
id->idmap_id_u.uid);
else if (*result->stat == IDMAP_SUCCESS && result->pid)
kidmap_cache_add_pidbysid(
cache, sid_prefix,
mapping->id1.idmap_id_u.sid.rid,
id->idmap_id_u.uid, 1);
break;
case IDMAP_GID:
if (result->gid)
*result->gid = id->idmap_id_u.gid;
if (result->pid)
*result->pid = id->idmap_id_u.gid;
if (result->is_user)
*result->is_user = 0;
sid_prefix = kidmap_find_sid_prefix(
mapping->id1.idmap_id_u.sid.prefix);
if (*result->stat == IDMAP_SUCCESS && result->gid)
kidmap_cache_add_gidbysid(
cache, sid_prefix,
mapping->id1.idmap_id_u.sid.rid,
id->idmap_id_u.gid);
else if (*result->stat == IDMAP_SUCCESS && result->pid)
kidmap_cache_add_pidbysid(
cache, sid_prefix,
mapping->id1.idmap_id_u.sid.rid,
id->idmap_id_u.gid, 0);
break;
case IDMAP_SID:
case IDMAP_USID:
case IDMAP_GSID:
sid_prefix = kidmap_find_sid_prefix(
id->idmap_id_u.sid.prefix);
if (result->sid_prefix && result->rid) {
*result->sid_prefix = sid_prefix;
*result->rid = id->idmap_id_u.sid.rid;
}
if (*result->stat == IDMAP_SUCCESS &&
mapping->id1.idtype == IDMAP_UID)
kidmap_cache_add_sidbyuid(
cache, sid_prefix,
id->idmap_id_u.sid.rid,
mapping->id1.idmap_id_u.uid);
else if (*result->stat == IDMAP_SUCCESS &&
mapping->id1.idtype == IDMAP_GID)
kidmap_cache_add_sidbygid(
cache, sid_prefix,
id->idmap_id_u.sid.rid,
mapping->id1.idmap_id_u.gid);
break;
default:
*result->stat = IDMAP_ERR_NORESULT;
if (result->uid)
*result->uid = UID_NOBODY;
if (result->gid)
*result->gid = GID_NOBODY;
if (result->pid)
*result->pid = UID_NOBODY;
if (result->is_user)
*result->is_user = 1;
if (result->sid_prefix)
*result->sid_prefix = NULL;
if (result->rid)
*result->rid = 0;
break;
}
}
xdr_free(xdr_idmap_ids_res, (char *)&rpc_res);
/* Reset get_handle for new resquests */
get_handle->mapping_num = 0;
return (status);
error:
for (i = 0; i < get_handle->mapping_num; i++) {
result = &get_handle->result[i];
*result->stat = status;
if (result->uid)
*result->uid = UID_NOBODY;
if (result->gid)
*result->gid = GID_NOBODY;
if (result->pid)
*result->pid = UID_NOBODY;
if (result->is_user)
*result->is_user = 1;
if (result->sid_prefix)
*result->sid_prefix = NULL;
if (result->rid)
*result->rid = 0;
}
/* Reset get_handle for new resquests */
get_handle->mapping_num = 0;
return (status);
}
/*
* Destroy the "get mapping" handle
*/
void
kidmap_get_destroy(idmap_get_handle_t *get_handle)
{
if (get_handle == NULL)
return;
kmem_free(get_handle->mapping,
(sizeof (idmap_mapping)) * get_handle->mapping_size);
get_handle->mapping = NULL;
kmem_free(get_handle->result,
(sizeof (idmap_get_res)) * get_handle->mapping_size);
get_handle->result = NULL;
kmem_free(get_handle, sizeof (idmap_get_handle_t));
}
static int
kidmap_rpc_call(idmap_zone_specific_t *zs, uint32_t op, xdrproc_t xdr_args,
caddr_t args, xdrproc_t xdr_res, caddr_t res)
{
XDR xdr_ctx;
struct rpc_msg reply_msg;
char *inbuf_ptr = NULL;
size_t inbuf_size = 4096;
char *outbuf_ptr = NULL;
size_t outbuf_size = 4096;
size_t size;
int status = 0;
door_arg_t params;
int retry = 0;
struct rpc_msg call_msg;
params.rbuf = NULL;
params.rsize = 0;
retry:
inbuf_ptr = kmem_alloc(inbuf_size, KM_SLEEP);
outbuf_ptr = kmem_alloc(outbuf_size, KM_SLEEP);
xdrmem_create(&xdr_ctx, inbuf_ptr, inbuf_size, XDR_ENCODE);
call_msg.rm_call.cb_prog = IDMAP_PROG;
call_msg.rm_call.cb_vers = IDMAP_V1;
call_msg.rm_xid = atomic_inc_32_nv(&zs->message_id);
if (!xdr_callhdr(&xdr_ctx, &call_msg)) {
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: xdr encoding header error");
#endif /* DEBUG */
status = -1;
goto exit;
}
if (!xdr_uint32(&xdr_ctx, &op) ||
/* Auth none */
!xdr_opaque_auth(&xdr_ctx, &_null_auth) ||
!xdr_opaque_auth(&xdr_ctx, &_null_auth) ||
/* RPC args */
!xdr_args(&xdr_ctx, args)) {
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN, "idmap: xdr encoding error");
#endif /* DEBUG */
if (retry > 2) {
status = -1;
goto exit;
}
retry++;
if (inbuf_ptr) {
kmem_free(inbuf_ptr, inbuf_size);
inbuf_ptr = NULL;
}
if (outbuf_ptr) {
kmem_free(outbuf_ptr, outbuf_size);
outbuf_ptr = NULL;
}
if ((size = xdr_sizeof(xdr_args, args)) == 0) {
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: xdr_sizeof error");
#endif /* DEBUG */
status = -1;
goto exit;
}
inbuf_size = size + 1024;
outbuf_size = size + 1024;
goto retry;
}
params.data_ptr = inbuf_ptr;
params.data_size = XDR_GETPOS(&xdr_ctx);
params.desc_ptr = NULL;
params.desc_num = 0;
params.rbuf = outbuf_ptr;
params.rsize = outbuf_size;
if (kidmap_call_door(zs, &params) != 0) {
status = -1;
goto exit;
}
reply_msg.acpted_rply.ar_verf = _null_auth;
reply_msg.acpted_rply.ar_results.where = res;
reply_msg.acpted_rply.ar_results.proc = xdr_res;
xdrmem_create(&xdr_ctx, params.data_ptr, params.data_size, XDR_DECODE);
if (xdr_replymsg(&xdr_ctx, &reply_msg)) {
if (reply_msg.rm_reply.rp_stat != MSG_ACCEPTED ||
reply_msg.rm_reply.rp_acpt.ar_stat != SUCCESS) {
status = -1;
goto exit;
}
} else {
#ifdef DEBUG
zcmn_err(zs->zone_id, CE_WARN,
"idmap: xdr decoding reply message error");
#endif /* DEBUG */
status = -1;
}
exit:
if (outbuf_ptr != params.rbuf && params.rbuf != NULL)
kmem_free(params.rbuf, params.rsize);
if (inbuf_ptr)
kmem_free(inbuf_ptr, inbuf_size);
if (outbuf_ptr)
kmem_free(outbuf_ptr, outbuf_size);
return (status);
}