# # 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) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # # uts/common/idmap/Makefile # # include global definitions include ../../../Makefile.master IDMAP_PROT_DIR= $(SRC)/uts/common/rpcsvc IDMAP_PROT_X= $(IDMAP_PROT_DIR)/idmap_prot.x DERIVED_SRCS= idmap_xdr.c DERIVED_FILES= $(DERIVED_SRCS) HDRS= kidmap_priv.h CHECKHDRS= $(HDRS:%.h=%.check) .KEEP_STATE: .PARALLEL: $(CHECKHDRS) install_h: all_h all_h: $(DERIVED_FILES) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # idmap_xdr.c: $(IDMAP_PROT_X) # $(RM) $@ # $(RPCGEN) -CMNc -DIDMAP_XDR_MAPPING_ONLY -o $@ $(IDMAP_PROT_X) check: $(CHECKHDRS) clean: @# Hammerhead: pre-generated files are permanent source, do not delete @true clobber: clean /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2023 RackTop Systems, Inc. */ /* * Windows to Solaris Identity Mapping kernel API * This module provides the kernel cache. */ #include #include #include #include #include #include #include #include "kidmap_priv.h" /* * External functions */ extern uintptr_t space_fetch(char *key); extern int space_store(char *key, uintptr_t ptr); /* * Internal definitions and functions */ #define CACHE_UID_TRIGGER_SIZE 4096 #define CACHE_GID_TRIGGER_SIZE 2048 #define CACHE_PID_TRIGGER_SIZE \ (CACHE_UID_TRIGGER_SIZE + CACHE_GID_TRIGGER_SIZE) #define UNDEF_UID ((uid_t)-1) #define UNDEF_GID ((gid_t)-1) #define UNDEF_ISUSER (-1) #define CACHE_PURGE_INTERVAL (60 * 3) #define CACHE_TTL (60 * 10) #define list_insert(head, ele)\ do {\ (ele)->flink = (head)->flink;\ (head)->flink = (ele);\ (ele)->blink = (ele)->flink->blink;\ (ele)->flink->blink = (ele);\ } while (0) #define list_remove(ele)\ do {\ (ele)->flink->blink = (ele)->blink;\ (ele)->blink->flink = (ele)->flink;\ } while (0) #define list_move(head, ele) \ do {\ if ((head)->flink != (ele)) {\ list_remove(ele);\ list_insert(head, ele);\ }\ } while (0) typedef struct sid_prefix_node { avl_node_t avl_link; const char *sid_prefix; } sid_prefix_node_t; struct sid_prefix_store { struct avl_tree tree; krwlock_t lock; }; struct sid_prefix_store *kidmap_sid_prefix_store = NULL; static void kidmap_purge_sid2pid_cache(idmap_sid2pid_cache_t *cache, size_t limit); static void kidmap_purge_pid2sid_cache(idmap_pid2sid_cache_t *cache, size_t limit); static char * kidmap_strdup(const char *s) { int len = strlen(s) + 1; char *ret = kmem_alloc(len, KM_SLEEP); bcopy(s, ret, len); return (ret); } static int kidmap_compare_sid(const void *p1, const void *p2) { const sid2pid_t *entry1 = p1; const sid2pid_t *entry2 = p2; int64_t comp = ((int64_t)entry2->rid) - ((int64_t)entry1->rid); if (comp == 0) comp = strcmp(entry2->sid_prefix, entry1->sid_prefix); if (comp < 0) comp = -1; else if (comp > 0) comp = 1; return ((int)comp); } static int kidmap_compare_pid(const void *p1, const void *p2) { const pid2sid_t *entry1 = p1; const pid2sid_t *entry2 = p2; if (entry2->pid > entry1->pid) return (1); if (entry2->pid < entry1->pid) return (-1); return (0); } static int kidmap_compare_sid_prefix(const void *p1, const void *p2) { const sid_prefix_node_t *entry1 = p1; const sid_prefix_node_t *entry2 = p2; int comp; comp = strcmp(entry2->sid_prefix, entry1->sid_prefix); if (comp < 0) comp = -1; else if (comp > 0) comp = 1; return (comp); } void kidmap_cache_create(idmap_cache_t *cache) { int i; /* * Create SID-2-PID hash table */ for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i]; avl_create(&sid2pid_hb->tree, kidmap_compare_sid, sizeof (sid2pid_t), offsetof(sid2pid_t, avl_link)); mutex_init(&sid2pid_hb->mutex, NULL, MUTEX_DEFAULT, NULL); sid2pid_hb->purge_time = 0; sid2pid_hb->head.flink = &sid2pid_hb->head; sid2pid_hb->head.blink = &sid2pid_hb->head; sid2pid_hb->uid_num = 0; sid2pid_hb->gid_num = 0; sid2pid_hb->pid_num = 0; } /* * Create UID-2-SID hash table */ for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i]; avl_create(&uid2sid_hb->tree, kidmap_compare_pid, sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link)); mutex_init(&uid2sid_hb->mutex, NULL, MUTEX_DEFAULT, NULL); uid2sid_hb->purge_time = 0; uid2sid_hb->head.flink = &uid2sid_hb->head; uid2sid_hb->head.blink = &uid2sid_hb->head; } /* * Create GID-2-SID hash table */ for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i]; avl_create(&gid2sid_hb->tree, kidmap_compare_pid, sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link)); mutex_init(&gid2sid_hb->mutex, NULL, MUTEX_DEFAULT, NULL); gid2sid_hb->purge_time = 0; gid2sid_hb->head.flink = &gid2sid_hb->head; gid2sid_hb->head.blink = &gid2sid_hb->head; } } void kidmap_cache_delete(idmap_cache_t *cache) { void *cookie; int i; for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i]; sid2pid_t *sid2pid; cookie = NULL; while ((sid2pid = avl_destroy_nodes(&sid2pid_hb->tree, &cookie)) != NULL) { kmem_free(sid2pid, sizeof (sid2pid_t)); } avl_destroy(&sid2pid_hb->tree); mutex_destroy(&sid2pid_hb->mutex); } for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i]; pid2sid_t *uid2sid; cookie = NULL; while ((uid2sid = avl_destroy_nodes(&uid2sid_hb->tree, &cookie)) != NULL) { kmem_free(uid2sid, sizeof (pid2sid_t)); } avl_destroy(&uid2sid_hb->tree); mutex_destroy(&uid2sid_hb->mutex); } for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i]; pid2sid_t *gid2sid; cookie = NULL; while ((gid2sid = avl_destroy_nodes(&gid2sid_hb->tree, &cookie)) != NULL) { kmem_free(gid2sid, sizeof (pid2sid_t)); } avl_destroy(&gid2sid_hb->tree); mutex_destroy(&gid2sid_hb->mutex); } } /* * Get counts of cache entries */ void kidmap_cache_get_data(idmap_cache_t *cache, size_t *uidbysid, size_t *gidbysid, size_t *pidbysid, size_t *sidbyuid, size_t *sidbygid) { int i; *uidbysid = 0; *gidbysid = 0; *pidbysid = 0; *sidbyuid = 0; *sidbygid = 0; for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i]; mutex_enter(&sid2pid_hb->mutex); *uidbysid += sid2pid_hb->uid_num; *gidbysid += sid2pid_hb->gid_num; *pidbysid += sid2pid_hb->pid_num; mutex_exit(&sid2pid_hb->mutex); } for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i]; mutex_enter(&uid2sid_hb->mutex); *sidbyuid += avl_numnodes(&uid2sid_hb->tree); mutex_exit(&uid2sid_hb->mutex); } for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i]; mutex_enter(&gid2sid_hb->mutex); *sidbygid += avl_numnodes(&gid2sid_hb->tree); mutex_exit(&gid2sid_hb->mutex); } } void kidmap_cache_purge(idmap_cache_t *cache) { void *cookie; int i; for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i]; sid2pid_t *sid2pid; mutex_enter(&sid2pid_hb->mutex); cookie = NULL; while ((sid2pid = avl_destroy_nodes(&sid2pid_hb->tree, &cookie)) != NULL) { kmem_free(sid2pid, sizeof (sid2pid_t)); } avl_destroy(&sid2pid_hb->tree); avl_create(&sid2pid_hb->tree, kidmap_compare_sid, sizeof (sid2pid_t), offsetof(sid2pid_t, avl_link)); sid2pid_hb->purge_time = 0; sid2pid_hb->head.flink = &sid2pid_hb->head; sid2pid_hb->head.blink = &sid2pid_hb->head; sid2pid_hb->uid_num = 0; sid2pid_hb->gid_num = 0; sid2pid_hb->pid_num = 0; mutex_exit(&sid2pid_hb->mutex); } for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i]; pid2sid_t *uid2sid; mutex_enter(&uid2sid_hb->mutex); cookie = NULL; while ((uid2sid = avl_destroy_nodes(&uid2sid_hb->tree, &cookie)) != NULL) { kmem_free(uid2sid, sizeof (pid2sid_t)); } avl_destroy(&uid2sid_hb->tree); avl_create(&uid2sid_hb->tree, kidmap_compare_pid, sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link)); uid2sid_hb->purge_time = 0; uid2sid_hb->head.flink = &uid2sid_hb->head; uid2sid_hb->head.blink = &uid2sid_hb->head; mutex_exit(&uid2sid_hb->mutex); } for (i = 0; i < KIDMAP_HASH_SIZE; i++) { idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i]; pid2sid_t *gid2sid; mutex_enter(&gid2sid_hb->mutex); cookie = NULL; while ((gid2sid = avl_destroy_nodes(&gid2sid_hb->tree, &cookie)) != NULL) { kmem_free(gid2sid, sizeof (pid2sid_t)); } avl_destroy(&gid2sid_hb->tree); avl_create(&gid2sid_hb->tree, kidmap_compare_pid, sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link)); gid2sid_hb->purge_time = 0; gid2sid_hb->head.flink = &gid2sid_hb->head; gid2sid_hb->head.blink = &gid2sid_hb->head; mutex_exit(&gid2sid_hb->mutex); } } int kidmap_cache_lookup_uidbysid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t *uid) { sid2pid_t entry; sid2pid_t *result; avl_index_t where; int status = IDMAP_ERR_NOMAPPING; int idx = (rid & KIDMAP_HASH_MASK); idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[idx]; time_t now = gethrestime_sec(); entry.sid_prefix = sid_prefix; entry.rid = rid; mutex_enter(&sid2pid_hb->mutex); result = avl_find(&sid2pid_hb->tree, &entry, &where); if (result != NULL) { list_move(&sid2pid_hb->head, result); if (result->uid != UNDEF_UID && result->uid_ttl > now) { *uid = result->uid; status = IDMAP_SUCCESS; } } mutex_exit(&sid2pid_hb->mutex); return (status); } int kidmap_cache_lookup_gidbysid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, gid_t *gid) { sid2pid_t entry; sid2pid_t *result; avl_index_t where; int status = IDMAP_ERR_NOMAPPING; int idx = (rid & KIDMAP_HASH_MASK); idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[idx]; time_t now = gethrestime_sec(); entry.sid_prefix = sid_prefix; entry.rid = rid; mutex_enter(&sid2pid_hb->mutex); result = avl_find(&sid2pid_hb->tree, &entry, &where); if (result != NULL) { list_move(&sid2pid_hb->head, result); if (result->gid != UNDEF_GID && result->gid_ttl > now) { *gid = result->gid; status = IDMAP_SUCCESS; } } mutex_exit(&sid2pid_hb->mutex); return (status); } int kidmap_cache_lookup_pidbysid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t *pid, int *is_user) { sid2pid_t entry; sid2pid_t *result; avl_index_t where; int status = IDMAP_ERR_NOMAPPING; int idx = (rid & KIDMAP_HASH_MASK); idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[idx]; time_t now = gethrestime_sec(); entry.sid_prefix = sid_prefix; entry.rid = rid; mutex_enter(&sid2pid_hb->mutex); result = avl_find(&sid2pid_hb->tree, &entry, &where); if (result != NULL) { list_move(&sid2pid_hb->head, result); if (result->is_user != UNDEF_ISUSER) { if (result->is_user && result->uid_ttl > now) { *pid = result->uid; *is_user = result->is_user; status = IDMAP_SUCCESS; } else if (!result->is_user && result->gid_ttl > now) { *pid = result->gid; *is_user = result->is_user; status = IDMAP_SUCCESS; } } } mutex_exit(&sid2pid_hb->mutex); return (status); } int kidmap_cache_lookup_sidbyuid(idmap_cache_t *cache, const char **sid_prefix, uint32_t *rid, uid_t uid) { pid2sid_t entry; pid2sid_t *result; avl_index_t where; int status = IDMAP_ERR_NOMAPPING; int idx = (uid & KIDMAP_HASH_MASK); idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[idx]; time_t now = gethrestime_sec(); entry.pid = uid; mutex_enter(&uid2sid_hb->mutex); result = avl_find(&uid2sid_hb->tree, &entry, &where); if (result != NULL) { list_move(&uid2sid_hb->head, result); if (result->ttl > now) { *sid_prefix = result->sid_prefix; *rid = result->rid; status = IDMAP_SUCCESS; } } mutex_exit(&uid2sid_hb->mutex); return (status); } int kidmap_cache_lookup_sidbygid(idmap_cache_t *cache, const char **sid_prefix, uint32_t *rid, gid_t gid) { pid2sid_t entry; pid2sid_t *result; avl_index_t where; int status = IDMAP_ERR_NOMAPPING; int idx = (gid & KIDMAP_HASH_MASK); idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[idx]; time_t now = gethrestime_sec(); entry.pid = gid; mutex_enter(&gid2sid_hb->mutex); result = avl_find(&gid2sid_hb->tree, &entry, &where); if (result != NULL) { list_move(&gid2sid_hb->head, result); if (result->ttl > now) { *sid_prefix = result->sid_prefix; *rid = result->rid; status = IDMAP_SUCCESS; } } mutex_exit(&gid2sid_hb->mutex); return (status); } void kidmap_cache_add_sid2uid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t uid, int direction) { avl_index_t where; time_t ttl = CACHE_TTL + gethrestime_sec(); if (direction == IDMAP_DIRECTION_BI || direction == IDMAP_DIRECTION_W2U) { sid2pid_t find; sid2pid_t *result; sid2pid_t *new; idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[rid & KIDMAP_HASH_MASK]; find.sid_prefix = sid_prefix; find.rid = rid; mutex_enter(&sid2pid_hb->mutex); result = avl_find(&sid2pid_hb->tree, &find, &where); if (result) { if (result->uid == UNDEF_UID) sid2pid_hb->uid_num++; result->uid = uid; result->uid_ttl = ttl; } else { new = kmem_alloc(sizeof (sid2pid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->uid = uid; new->uid_ttl = ttl; new->gid = UNDEF_GID; new->gid_ttl = 0; new->is_user = UNDEF_ISUSER; /* Unknown */ sid2pid_hb->uid_num++; list_insert(&sid2pid_hb->head, new); avl_insert(&sid2pid_hb->tree, new, where); } if ((avl_numnodes(&sid2pid_hb->tree) > CACHE_PID_TRIGGER_SIZE) && (sid2pid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_sid2pid_cache(sid2pid_hb, CACHE_PID_TRIGGER_SIZE); mutex_exit(&sid2pid_hb->mutex); } if (direction == IDMAP_DIRECTION_BI || direction == IDMAP_DIRECTION_U2W) { pid2sid_t find; pid2sid_t *result; pid2sid_t *new; idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[uid & KIDMAP_HASH_MASK]; find.pid = uid; mutex_enter(&uid2sid_hb->mutex); result = avl_find(&uid2sid_hb->tree, &find, &where); if (result) { result->sid_prefix = sid_prefix; result->rid = rid; result->ttl = ttl; } else { new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->pid = uid; new->ttl = ttl; list_insert(&uid2sid_hb->head, new); avl_insert(&uid2sid_hb->tree, new, where); } if ((avl_numnodes(&uid2sid_hb->tree) > CACHE_UID_TRIGGER_SIZE) && (uid2sid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_pid2sid_cache(uid2sid_hb, CACHE_UID_TRIGGER_SIZE); mutex_exit(&uid2sid_hb->mutex); } } void kidmap_cache_add_sid2gid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, gid_t gid, int direction) { avl_index_t where; time_t ttl = CACHE_TTL + gethrestime_sec(); if (direction == IDMAP_DIRECTION_BI || direction == IDMAP_DIRECTION_W2U) { sid2pid_t find; sid2pid_t *result; sid2pid_t *new; idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[rid & KIDMAP_HASH_MASK]; find.sid_prefix = sid_prefix; find.rid = rid; mutex_enter(&sid2pid_hb->mutex); result = avl_find(&sid2pid_hb->tree, &find, &where); if (result) { if (result->gid == UNDEF_GID) sid2pid_hb->gid_num++; result->gid = gid; result->gid_ttl = ttl; } else { new = kmem_alloc(sizeof (sid2pid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->uid = UNDEF_UID; new->uid_ttl = 0; new->gid = gid; new->gid_ttl = ttl; new->is_user = UNDEF_ISUSER; /* Unknown */ sid2pid_hb->gid_num++; list_insert(&sid2pid_hb->head, new); avl_insert(&sid2pid_hb->tree, new, where); } if ((avl_numnodes(&sid2pid_hb->tree) > CACHE_PID_TRIGGER_SIZE) && (sid2pid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_sid2pid_cache(sid2pid_hb, CACHE_PID_TRIGGER_SIZE); mutex_exit(&sid2pid_hb->mutex); } if (direction == IDMAP_DIRECTION_BI || direction == IDMAP_DIRECTION_U2W) { pid2sid_t find; pid2sid_t *result; pid2sid_t *new; idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[gid & KIDMAP_HASH_MASK]; find.pid = gid; mutex_enter(&gid2sid_hb->mutex); result = avl_find(&gid2sid_hb->tree, &find, &where); if (result) { result->sid_prefix = sid_prefix; result->rid = rid; result->ttl = ttl; } else { new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->pid = gid; new->ttl = ttl; list_insert(&gid2sid_hb->head, new); avl_insert(&gid2sid_hb->tree, new, where); } if ((avl_numnodes(&gid2sid_hb->tree) > CACHE_GID_TRIGGER_SIZE) && (gid2sid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_pid2sid_cache(gid2sid_hb, CACHE_GID_TRIGGER_SIZE); mutex_exit(&gid2sid_hb->mutex); } } void kidmap_cache_add_sid2pid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t pid, int is_user, int direction) { avl_index_t where; time_t ttl = CACHE_TTL + gethrestime_sec(); if (direction == IDMAP_DIRECTION_BI || direction == IDMAP_DIRECTION_W2U) { sid2pid_t find; sid2pid_t *result; sid2pid_t *new; idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[rid & KIDMAP_HASH_MASK]; find.sid_prefix = sid_prefix; find.rid = rid; mutex_enter(&sid2pid_hb->mutex); result = avl_find(&sid2pid_hb->tree, &find, &where); if (result) { if (result->is_user == UNDEF_ISUSER) sid2pid_hb->pid_num++; result->is_user = is_user; if (is_user) { if (result->uid == UNDEF_UID) sid2pid_hb->uid_num++; result->uid = pid; result->uid_ttl = ttl; } else { if (result->gid == UNDEF_GID) sid2pid_hb->gid_num++; result->gid = pid; result->gid_ttl = ttl; } } else { new = kmem_alloc(sizeof (sid2pid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->is_user = is_user; if (is_user) { new->uid = pid; new->uid_ttl = ttl; new->gid = UNDEF_GID; new->gid_ttl = 0; sid2pid_hb->uid_num++; } else { new->uid = UNDEF_UID; new->uid_ttl = 0; new->gid = pid; new->gid_ttl = ttl; sid2pid_hb->gid_num++; } sid2pid_hb->pid_num++; list_insert(&sid2pid_hb->head, new); avl_insert(&sid2pid_hb->tree, new, where); } if ((avl_numnodes(&sid2pid_hb->tree) > CACHE_PID_TRIGGER_SIZE) && (sid2pid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_sid2pid_cache(sid2pid_hb, CACHE_PID_TRIGGER_SIZE); mutex_exit(&sid2pid_hb->mutex); } if (direction == IDMAP_DIRECTION_BI || direction == IDMAP_DIRECTION_U2W) { pid2sid_t find; pid2sid_t *result; pid2sid_t *new; int idx = pid & KIDMAP_HASH_MASK; find.pid = pid; if (is_user) { idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[idx]; mutex_enter(&uid2sid_hb->mutex); result = avl_find(&uid2sid_hb->tree, &find, &where); if (result) { result->sid_prefix = sid_prefix; result->rid = rid; result->ttl = ttl; } else { new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->pid = pid; new->ttl = ttl; list_insert(&uid2sid_hb->head, new); avl_insert(&uid2sid_hb->tree, new, where); } if ((avl_numnodes(&uid2sid_hb->tree) > CACHE_UID_TRIGGER_SIZE) && (uid2sid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_pid2sid_cache(uid2sid_hb, CACHE_UID_TRIGGER_SIZE); mutex_exit(&uid2sid_hb->mutex); } else { idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[idx]; mutex_enter(&gid2sid_hb->mutex); result = avl_find(&gid2sid_hb->tree, &find, &where); if (result) { result->sid_prefix = sid_prefix; result->rid = rid; result->ttl = ttl; } else { new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP); new->sid_prefix = sid_prefix; new->rid = rid; new->pid = pid; new->ttl = ttl; list_insert(&gid2sid_hb->head, new); avl_insert(&gid2sid_hb->tree, new, where); } if ((avl_numnodes(&gid2sid_hb->tree) > CACHE_GID_TRIGGER_SIZE) && (gid2sid_hb->purge_time + CACHE_PURGE_INTERVAL < gethrestime_sec())) kidmap_purge_pid2sid_cache(gid2sid_hb, CACHE_GID_TRIGGER_SIZE); mutex_exit(&gid2sid_hb->mutex); } } } static void kidmap_purge_sid2pid_cache(idmap_sid2pid_cache_t *cache, size_t limit) { time_t now = gethrestime_sec(); sid2pid_t *item; while (avl_numnodes(&cache->tree) > limit) { /* Remove least recently used */ item = cache->head.blink; list_remove(item); avl_remove(&cache->tree, item); if (item->uid != UNDEF_UID) cache->uid_num--; if (item->gid != UNDEF_GID) cache->gid_num--; if (item->is_user != UNDEF_ISUSER) cache->pid_num--; kmem_free(item, sizeof (sid2pid_t)); } cache->purge_time = now; } static void kidmap_purge_pid2sid_cache(idmap_pid2sid_cache_t *cache, size_t limit) { time_t now = gethrestime_sec(); pid2sid_t *item; while (avl_numnodes(&cache->tree) > limit) { /* Remove least recently used */ item = cache->head.blink; list_remove(item); avl_remove(&cache->tree, item); kmem_free(item, sizeof (pid2sid_t)); } cache->purge_time = now; } void kidmap_sid_prefix_store_init(void) { kidmap_sid_prefix_store = (struct sid_prefix_store *) space_fetch("SUNW,idmap_sid_prefix"); if (kidmap_sid_prefix_store == NULL) { kidmap_sid_prefix_store = kmem_alloc( sizeof (struct sid_prefix_store), KM_SLEEP); rw_init(&kidmap_sid_prefix_store->lock, NULL, RW_DRIVER, NULL); avl_create(&kidmap_sid_prefix_store->tree, kidmap_compare_sid_prefix, sizeof (sid_prefix_node_t), offsetof(sid_prefix_node_t, avl_link)); (void) space_store("SUNW,idmap_sid_prefix", (uintptr_t)kidmap_sid_prefix_store); } else { /* * The AVL comparison function must be re-initialised on * re-load because may not be loaded into the same * address space. */ kidmap_sid_prefix_store->tree.avl_compar = kidmap_compare_sid_prefix; } } const char * kidmap_find_sid_prefix(const char *sid_prefix) { sid_prefix_node_t find; sid_prefix_node_t *result; sid_prefix_node_t *new; avl_index_t where; if (sid_prefix == NULL || *sid_prefix == '\0') return (NULL); find.sid_prefix = sid_prefix; rw_enter(&kidmap_sid_prefix_store->lock, RW_READER); result = avl_find(&kidmap_sid_prefix_store->tree, &find, &where); if (result) { rw_exit(&kidmap_sid_prefix_store->lock); return (result->sid_prefix); } if (rw_tryupgrade(&kidmap_sid_prefix_store->lock) == 0) { /* * Could not upgrade lock so release lock * and acquire the write lock */ rw_exit(&kidmap_sid_prefix_store->lock); rw_enter(&kidmap_sid_prefix_store->lock, RW_WRITER); result = avl_find(&kidmap_sid_prefix_store->tree, &find, &where); if (result) { rw_exit(&kidmap_sid_prefix_store->lock); return (result->sid_prefix); } } new = kmem_alloc(sizeof (sid_prefix_node_t), KM_SLEEP); new->sid_prefix = kidmap_strdup(sid_prefix); avl_insert(&kidmap_sid_prefix_store->tree, new, where); rw_exit(&kidmap_sid_prefix_store->lock); return (new->sid_prefix); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2018 Nexenta Systems, Inc. */ /* * Windows to Solaris Identity Mapping kernel API * This module provides an API to map Windows SIDs to * Solaris UID and GIDs. */ #include #include #include #include #include #include #include #ifdef DEBUG #include #endif /* DEBUG */ #include #include #include #include #include #include #include #include #include #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; int door_retried; 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; zs->door_retried = 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 num_retries = 5; int door_retried = 0; retry: mutex_enter(&zs->zone_mutex); if (zs->door_valid) { dh = zs->door_handle; door_ki_hold(dh); } else { dh = NULL; door_retried = zs->door_retried; } mutex_exit(&zs->zone_mutex); if (dh == NULL) { /* The door has been retried before so dont wait */ if (door_retried) return (-1); /* * There is no door handle yet. Give * smf a chance to restart idmapd */ if (num_retries-- > 0) { 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 mutex_enter(&zs->zone_mutex); if (!zs->door_valid) zs->door_retried = 1; mutex_exit(&zs->zone_mutex); return (-1); } status = door_ki_upcall_limited(dh, arg, NULL, SIZE_MAX, 0); 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); #ifdef DEBUG zcmn_err(zs->zone_id, CE_WARN, "idmap: Interrupted\n"); #endif 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; zs->door_retried = 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_sid2uid(&zs->cache, new_sid_prefix, rid, *uid, results.ids.ids_val[0].direction); } } 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_sid2gid(&zs->cache, new_sid_prefix, rid, *gid, results.ids.ids_val[0].direction); } } 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_sid2pid(&zs->cache, new_sid_prefix, rid, *pid, *is_user, results.ids.ids_val[0].direction); } } 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_sid2uid(&zs->cache, *sid_prefix, *rid, uid, results.ids.ids_val[0].direction); } } 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_sid2gid(&zs->cache, *sid_prefix, *rid, gid, results.ids.ids_val[0].direction); } } 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 * * Notes: * The output parameters will be set by idmap_get_mappings() * The sid_prefix is copied. */ 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); } /* Get a copy of sid_prefix */ sid_prefix = kidmap_find_sid_prefix(sid_prefix); 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 * * Notes: * The output parameters will be set by idmap_get_mappings() * The sid_prefix is copied. */ 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); } /* Get a copy of sid_prefix */ sid_prefix = kidmap_find_sid_prefix(sid_prefix); 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 * * Notes: * The output parameters will be set by idmap_get_mappings() * The sid_prefix is copied. */ 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); } /* Get a copy of sid_prefix */ sid_prefix = kidmap_find_sid_prefix(sid_prefix); 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 *request; idmap_get_res *result; idmap_id *id; int status; int i; const char *sid_prefix; int is_user; idmap_cache_t *cache; int direction; 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++) { request = &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; direction = rpc_res.ids.ids_val[i].direction; 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( request->id1.idmap_id_u.sid.prefix); if (*result->stat == IDMAP_SUCCESS && result->uid) kidmap_cache_add_sid2uid( cache, sid_prefix, request->id1.idmap_id_u.sid.rid, id->idmap_id_u.uid, direction); else if (*result->stat == IDMAP_SUCCESS && result->pid) kidmap_cache_add_sid2pid( cache, sid_prefix, request->id1.idmap_id_u.sid.rid, id->idmap_id_u.uid, 1, direction); 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( request->id1.idmap_id_u.sid.prefix); if (*result->stat == IDMAP_SUCCESS && result->gid) kidmap_cache_add_sid2gid( cache, sid_prefix, request->id1.idmap_id_u.sid.rid, id->idmap_id_u.gid, direction); else if (*result->stat == IDMAP_SUCCESS && result->pid) kidmap_cache_add_sid2pid( cache, sid_prefix, request->id1.idmap_id_u.sid.rid, id->idmap_id_u.gid, 0, direction); 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_ERR_NOTFOUND && sid_prefix != NULL) { /* IDMAP generated a local SID. Use it. */ *result->stat = IDMAP_SUCCESS; } if (*result->stat == IDMAP_SUCCESS && request->id1.idtype == IDMAP_UID) kidmap_cache_add_sid2uid( cache, sid_prefix, id->idmap_id_u.sid.rid, request->id1.idmap_id_u.uid, direction); else if (*result->stat == IDMAP_SUCCESS && request->id1.idtype == IDMAP_GID) kidmap_cache_add_sid2gid( cache, sid_prefix, id->idmap_id_u.sid.rid, request->id1.idmap_id_u.gid, direction); 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, ¶ms) != 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); } /* * 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. */ #include #include #include #ifdef DEBUG #include #endif /* DEBUG */ #include #include "kidmap_priv.h" extern struct mod_ops mod_miscops; static struct modlmisc misc = { &mod_miscops, "ID Mapping kernel module" }; static struct modlinkage linkage = { MODREV_1, (void *) &misc, NULL }; int _init() { int i; if ((i = mod_install(&linkage)) != 0) { #ifdef DEBUG cmn_err(CE_WARN, "idmap: Failed to load kernel module"); #endif /* DEBUG */ return (i); } if (kidmap_start() != 0) { #ifdef DEBUG cmn_err(CE_WARN, "idmap: Failed to start"); #endif /* DEBUG */ return (i); } return (i); } int _info(struct modinfo *modinfop) { return (mod_info(&linkage, modinfop)); } int _fini() { int i; if ((i = kidmap_stop()) != 0) { return (i); } if ((i = mod_remove(&linkage)) != 0) { #ifdef DEBUG cmn_err(CE_WARN, "idmap: Failed to remove kernel module"); #endif /* DEBUG */ return (i); } return (0); } /* * Please do not edit this file. * It was generated using rpcgen. */ #include "idmap_prot.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ #if defined(_KERNEL) #include #else #include #endif #if !defined(_KERNEL) #include #include #endif bool_t xdr_nvlist_t_ptr(XDR *xdrs, nvlist_t_ptr *n) { char *buf; u_int len; bool_t ret; int err; size_t sz; bool_t present; switch (xdrs->x_op) { case XDR_DECODE: if (!xdr_bool(xdrs, &present)) return (FALSE); if (!present) { *n = NULL; return (TRUE); } buf = NULL; if (!xdr_bytes(xdrs, &buf, &len, ~0)) return (FALSE); err = nvlist_unpack(buf, (size_t)len, n, 0); #if defined(_KERNEL) kmem_free(buf, len); #else free(buf); #endif if (err != 0) { #if !defined(_KERNEL) fprintf(stderr, "xdr_nvlist_t unpack: %s\n", strerror(err)); #endif return (FALSE); } return (TRUE); case XDR_ENCODE: present = (*n != NULL); if (!xdr_bool(xdrs, &present)) return (FALSE); if (!present) return (TRUE); buf = NULL; err = nvlist_pack(*n, &buf, &sz, NV_ENCODE_XDR, 0); if (err != 0) { #if !defined(_KERNEL) fprintf(stderr, "xdr_nvlist_t pack: %s\n", strerror(err)); #endif return (FALSE); } /* nvlist_pack() and xdr_bytes() want different types */ len = (u_int) sz; ret = xdr_bytes(xdrs, &buf, &len, ~0); #if defined(_KERNEL) kmem_free(buf, len); #else free(buf); #endif return (ret); case XDR_FREE: if (*n != NULL) { nvlist_free(*n); *n = NULL; } return (TRUE); default: return (FALSE); } } bool_t xdr_idmap_utf8str(XDR *xdrs, idmap_utf8str *objp) { rpc_inline_t *buf __unused; if (!xdr_string(xdrs, objp, ~0)) return (FALSE); return (TRUE); } bool_t xdr_idmap_utf8str_list(XDR *xdrs, idmap_utf8str_list *objp) { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->idmap_utf8str_list_val, (u_int *) &objp->idmap_utf8str_list_len, ~0, sizeof (idmap_utf8str), (xdrproc_t)xdr_idmap_utf8str)) return (FALSE); return (TRUE); } bool_t xdr_idmap_retcode(XDR *xdrs, idmap_retcode *objp) { rpc_inline_t *buf __unused; if (!xdr_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_idmap_id_type(XDR *xdrs, idmap_id_type *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_idmap_map_type(XDR *xdrs, idmap_map_type *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_idmap_map_src(XDR *xdrs, idmap_map_src *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_idmap_sid(XDR *xdrs, idmap_sid *objp) { rpc_inline_t *buf __unused; if (!xdr_string(xdrs, &objp->prefix, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rid)) return (FALSE); return (TRUE); } bool_t xdr_idmap_id(XDR *xdrs, idmap_id *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_id_type(xdrs, &objp->idtype)) return (FALSE); switch (objp->idtype) { case IDMAP_UID: if (!xdr_uint32_t(xdrs, &objp->idmap_id_u.uid)) return (FALSE); break; case IDMAP_GID: if (!xdr_uint32_t(xdrs, &objp->idmap_id_u.gid)) return (FALSE); break; case IDMAP_SID: if (!xdr_idmap_sid(xdrs, &objp->idmap_id_u.sid)) return (FALSE); break; case IDMAP_USID: if (!xdr_idmap_sid(xdrs, &objp->idmap_id_u.usid)) return (FALSE); break; case IDMAP_GSID: if (!xdr_idmap_sid(xdrs, &objp->idmap_id_u.gsid)) return (FALSE); break; case IDMAP_NONE: break; case IDMAP_POSIXID: break; default: return (FALSE); } return (TRUE); } bool_t xdr_idmap_namerule(XDR *xdrs, idmap_namerule *objp) { rpc_inline_t *buf __unused; if (!xdr_bool(xdrs, &objp->is_user)) return (FALSE); if (!xdr_bool(xdrs, &objp->is_wuser)) return (FALSE); if (!xdr_int(xdrs, &objp->direction)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->windomain)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->winname)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->unixname)) return (FALSE); if (!xdr_bool(xdrs, &objp->is_nt4)) return (FALSE); return (TRUE); } bool_t xdr_idmap_namerules_res(XDR *xdrs, idmap_namerules_res *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_retcode(xdrs, &objp->retcode)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->lastrowid)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->rules.rules_val, (u_int *) &objp->rules.rules_len, ~0, sizeof (idmap_namerule), (xdrproc_t)xdr_idmap_namerule)) return (FALSE); return (TRUE); } bool_t xdr_idmap_how_ds_based(XDR *xdrs, idmap_how_ds_based *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_utf8str(xdrs, &objp->dn)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->attr)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->value)) return (FALSE); return (TRUE); } bool_t xdr_idmap_how(XDR *xdrs, idmap_how *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_map_type(xdrs, &objp->map_type)) return (FALSE); switch (objp->map_type) { case IDMAP_MAP_TYPE_UNKNOWN: break; case IDMAP_MAP_TYPE_DS_AD: if (!xdr_idmap_how_ds_based(xdrs, &objp->idmap_how_u.ad)) return (FALSE); break; case IDMAP_MAP_TYPE_DS_NLDAP: if (!xdr_idmap_how_ds_based(xdrs, &objp->idmap_how_u.nldap)) return (FALSE); break; case IDMAP_MAP_TYPE_RULE_BASED: if (!xdr_idmap_namerule(xdrs, &objp->idmap_how_u.rule)) return (FALSE); break; case IDMAP_MAP_TYPE_EPHEMERAL: break; case IDMAP_MAP_TYPE_LOCAL_SID: break; case IDMAP_MAP_TYPE_KNOWN_SID: break; case IDMAP_MAP_TYPE_IDMU: if (!xdr_idmap_how_ds_based(xdrs, &objp->idmap_how_u.idmu)) return (FALSE); break; default: return (FALSE); } return (TRUE); } bool_t xdr_idmap_info(XDR *xdrs, idmap_info *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_map_src(xdrs, &objp->src)) return (FALSE); if (!xdr_idmap_how(xdrs, &objp->how)) return (FALSE); if (!xdr_nvlist_t_ptr(xdrs, &objp->trace)) return (FALSE); return (TRUE); } bool_t xdr_idmap_id_res(XDR *xdrs, idmap_id_res *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_retcode(xdrs, &objp->retcode)) return (FALSE); if (!xdr_idmap_id(xdrs, &objp->id)) return (FALSE); if (!xdr_int(xdrs, &objp->direction)) return (FALSE); if (!xdr_idmap_info(xdrs, &objp->info)) return (FALSE); return (TRUE); } bool_t xdr_idmap_ids_res(XDR *xdrs, idmap_ids_res *objp) { rpc_inline_t *buf __unused; if (!xdr_idmap_retcode(xdrs, &objp->retcode)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->ids.ids_val, (u_int *) &objp->ids.ids_len, ~0, sizeof (idmap_id_res), (xdrproc_t)xdr_idmap_id_res)) return (FALSE); return (TRUE); } bool_t xdr_idmap_mapping(XDR *xdrs, idmap_mapping *objp) { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->flag)) return (FALSE); if (!xdr_int(xdrs, &objp->direction)) return (FALSE); if (!xdr_idmap_id(xdrs, &objp->id1)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->id1domain)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->id1name)) return (FALSE); if (!xdr_idmap_id(xdrs, &objp->id2)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->id2domain)) return (FALSE); if (!xdr_idmap_utf8str(xdrs, &objp->id2name)) return (FALSE); if (!xdr_idmap_info(xdrs, &objp->info)) return (FALSE); return (TRUE); } bool_t xdr_idmap_mapping_batch(XDR *xdrs, idmap_mapping_batch *objp) { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->idmap_mapping_batch_val, (u_int *) &objp->idmap_mapping_batch_len, ~0, sizeof (idmap_mapping), (xdrproc_t)xdr_idmap_mapping)) return (FALSE); return (TRUE); } /* * 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. * * Copyright 2023 RackTop Systems, Inc. */ /* * Windows to Solaris Identity Mapping kernel API * This header file contains private definitions. */ #ifndef _KIDMAP_PRIV_H #define _KIDMAP_PRIV_H #include #ifdef __cplusplus extern "C" { #endif #define KIDMAP_HASH_SIZE (1<<8) #define KIDMAP_HASH_MASK (KIDMAP_HASH_SIZE-1) typedef struct sid2pid { avl_node_t avl_link; struct sid2pid *flink; struct sid2pid *blink; const char *sid_prefix; uint32_t rid; uid_t uid; time_t uid_ttl; gid_t gid; time_t gid_ttl; int is_user; } sid2pid_t; typedef struct pid2sid { avl_node_t avl_link; struct pid2sid *flink; struct pid2sid *blink; const char *sid_prefix; uint32_t rid; uid_t pid; time_t ttl; } pid2sid_t; typedef struct idmap_sid2pid_cache { avl_tree_t tree; kmutex_t mutex; struct sid2pid head; time_t purge_time; int uid_num; int gid_num; int pid_num; } idmap_sid2pid_cache_t; typedef struct idmap_pid2sid_cache { avl_tree_t tree; kmutex_t mutex; struct pid2sid head; time_t purge_time; } idmap_pid2sid_cache_t; /* * There is a cache for every mapping request because a group SID * on Windows can be set in a file owner field and versa-visa. * To stop this causing problems on Solaris a SID can map to * both a UID and a GID. */ typedef struct idmap_cache { idmap_sid2pid_cache_t sid2pid_hash[KIDMAP_HASH_SIZE]; idmap_pid2sid_cache_t uid2sid_hash[KIDMAP_HASH_SIZE]; idmap_pid2sid_cache_t gid2sid_hash[KIDMAP_HASH_SIZE]; } idmap_cache_t; void kidmap_cache_create(idmap_cache_t *cache); void kidmap_cache_delete(idmap_cache_t *cache); void kidmap_cache_purge(idmap_cache_t *cache); int kidmap_cache_lookup_uidbysid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t *uid); int kidmap_cache_lookup_gidbysid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, gid_t *gid); int kidmap_cache_lookup_pidbysid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t *pid, int *is_user); int kidmap_cache_lookup_sidbyuid(idmap_cache_t *cache, const char **sid_prefix, uint32_t *rid, uid_t uid); int kidmap_cache_lookup_sidbygid(idmap_cache_t *cache, const char **sid_prefix, uint32_t *rid, gid_t gid); void kidmap_cache_add_sid2uid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t uid, int direction); void kidmap_cache_add_sid2gid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, gid_t gid, int direction); void kidmap_cache_add_sid2pid(idmap_cache_t *cache, const char *sid_prefix, uint32_t rid, uid_t pid, int is_user, int direction); void kidmap_cache_get_data(idmap_cache_t *cache, size_t *uidbysid, size_t *gidbysid, size_t *pidbysid, size_t *sidbyuid, size_t *sidbygid); int kidmap_start(void); int kidmap_stop(void); void kidmap_sid_prefix_store_init(void); const char * kidmap_find_sid_prefix(const char *sid_prefix); #ifdef __cplusplus } #endif #endif /* _KIDMAP_PRIV_H */