#include <sys/systm.h>
#include <sys/kstat.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/time.h>
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
#include <rpc/rpc.h>
#include <rpc/rpcsec_defs.h>
#include <sys/sunddi.h>
#include <sys/atomic.h>
#include <sys/disp.h>
extern bool_t __rpc_gss_make_principal(rpc_gss_principal_t *, gss_buffer_t);
#ifdef DEBUG
extern void prom_printf(const char *, ...);
#endif
#ifdef _KERNEL
#define memcmp(a, b, l) bcmp((a), (b), (l))
#endif
#define SEQ_ARR_SIZE 4
#define SEQ_WIN (SEQ_ARR_SIZE*32)
#define SEQ_HI_BIT 0x80000000
#define SEQ_LO_BIT 1
#define DIV_BY_32 5
#define SEQ_MASK 0x1f
#define SEQ_MAX ((unsigned int)0x80000000)
typedef struct _retrans_entry {
uint32_t xid;
rpc_gss_init_res result;
} retrans_entry;
typedef struct _svc_rpc_gss_data {
struct _svc_rpc_gss_data *next, *prev;
struct _svc_rpc_gss_data *lru_next, *lru_prev;
bool_t established;
gss_ctx_id_t context;
gss_buffer_desc client_name;
time_t expiration;
uint_t seq_num;
uint_t seq_bits[SEQ_ARR_SIZE];
uint_t key;
OM_uint32 qop;
bool_t done_docallback;
bool_t locked;
rpc_gss_rawcred_t raw_cred;
rpc_gss_ucred_t u_cred;
time_t u_cred_set;
void *cookie;
gss_cred_id_t deleg;
kmutex_t clm;
int ref_cnt;
time_t last_ref_time;
bool_t stale;
retrans_entry *retrans_data;
} svc_rpc_gss_data;
#define HASH(key) ((key) % svc_rpc_gss_hashmod)
#define GSS_DATA_HASH_SIZE 1024
#define ACTIVE_DELTA 30*60
#define INACTIVE_DELTA 8*60*60
int svc_rpc_gss_hashmod = GSS_DATA_HASH_SIZE;
static svc_rpc_gss_data **clients;
static svc_rpc_gss_data *lru_first, *lru_last;
static time_t sweep_interval = 60*60;
static time_t last_swept = 0;
static int num_gss_contexts = 0;
static time_t svc_rpcgss_gid_timeout = 60*60*12;
static kmem_cache_t *svc_data_handle;
static time_t svc_rpc_gss_active_delta = ACTIVE_DELTA;
static time_t svc_rpc_gss_inactive_delta = INACTIVE_DELTA;
static kmutex_t ctx_mutex;
static struct {
int64_t total_entries_allocated;
int64_t no_reclaims;
int64_t no_returned_by_reclaim;
} svc_rpc_gss_cache_stats;
static krwlock_t cred_lock;
typedef struct rpc_gss_cblist_s {
struct rpc_gss_cblist_s *next;
rpc_gss_callback_t cb;
} rpc_gss_cblist_t;
static rpc_gss_cblist_t *rpc_gss_cblist = NULL;
static kmutex_t cb_mutex;
static bool_t svc_rpc_gss_wrap();
static bool_t svc_rpc_gss_unwrap();
static svc_rpc_gss_data *create_client();
static svc_rpc_gss_data *get_client();
static svc_rpc_gss_data *find_client();
static void destroy_client();
static void sweep_clients(bool_t);
static void insert_client();
static bool_t check_verf(struct rpc_msg *, gss_ctx_id_t,
int *, uid_t);
static bool_t set_response_verf();
static void retrans_add(svc_rpc_gss_data *, uint32_t,
rpc_gss_init_res *);
static void retrans_del(svc_rpc_gss_data *);
static bool_t transfer_sec_context(svc_rpc_gss_data *);
static void common_client_data_free(svc_rpc_gss_data *);
struct svc_auth_ops svc_rpc_gss_ops = {
svc_rpc_gss_wrap,
svc_rpc_gss_unwrap,
};
typedef struct svcrpcsec_gss_taskq_arg {
SVCXPRT *rq_xprt;
rpc_gss_init_arg *rpc_call_arg;
struct rpc_msg *msg;
svc_rpc_gss_data *client_data;
uint_t cr_version;
rpc_gss_service_t cr_service;
} svcrpcsec_gss_taskq_arg_t;
int rpcsec_gss_init_taskq_nthreads = 1;
extern struct rpc_msg *rpc_msg_dup(struct rpc_msg *);
extern void rpc_msg_free(struct rpc_msg **, int);
struct udp_data {
mblk_t *ud_resp;
mblk_t *ud_inmp;
};
static zone_key_t svc_gss_zone_key;
static uint_t svc_gss_tsd_key;
typedef struct svc_gss_zsd {
zoneid_t sgz_zoneid;
kmutex_t sgz_lock;
taskq_t *sgz_init_taskq;
} svc_gss_zsd_t;
static taskq_t *
svc_gss_create_taskq(zone_t *zone)
{
taskq_t *tq;
if (zone == NULL) {
cmn_err(CE_NOTE, "%s: couldn't find zone", __func__);
return (NULL);
}
tq = taskq_create_proc("rpcsec_gss_init_taskq",
rpcsec_gss_init_taskq_nthreads, minclsyspri,
rpcsec_gss_init_taskq_nthreads, INT_MAX, zone->zone_zsched,
TASKQ_PREPOPULATE);
if (tq == NULL)
cmn_err(CE_NOTE, "%s: taskq_create_proc failed", __func__);
return (tq);
}
static void *
svc_gss_zone_init(zoneid_t zoneid)
{
svc_gss_zsd_t *zsd;
zone_t *zone = curzone;
zsd = kmem_alloc(sizeof (*zsd), KM_SLEEP);
mutex_init(&zsd->sgz_lock, NULL, MUTEX_DEFAULT, NULL);
zsd->sgz_zoneid = zoneid;
if (zone->zone_id != zoneid)
zone = zone_find_by_id_nolock(zoneid);
zsd->sgz_init_taskq = svc_gss_create_taskq(zone);
return (zsd);
}
static void
svc_gss_zone_shutdown(zoneid_t zoneid, void *arg)
{
svc_gss_zsd_t *zsd = arg;
mutex_enter(&zsd->sgz_lock);
if (zsd->sgz_init_taskq != NULL) {
taskq_destroy(zsd->sgz_init_taskq);
zsd->sgz_init_taskq = NULL;
}
mutex_exit(&zsd->sgz_lock);
}
static void
svc_gss_zone_fini(zoneid_t zoneid, void *arg)
{
svc_gss_zsd_t *zsd = arg;
mutex_destroy(&zsd->sgz_lock);
kmem_free(zsd, sizeof (*zsd));
}
static svc_gss_zsd_t *
svc_gss_get_zsd(void)
{
svc_gss_zsd_t *zsd;
zsd = tsd_get(svc_gss_tsd_key);
if (zsd == NULL) {
zsd = zone_getspecific(svc_gss_zone_key, curzone);
(void) tsd_set(svc_gss_tsd_key, zsd);
}
return (zsd);
}
static int
svc_gss_data_create(void *buf, void *pdata, int kmflag)
{
svc_rpc_gss_data *client_data = (svc_rpc_gss_data *)buf;
mutex_init(&client_data->clm, NULL, MUTEX_DEFAULT, NULL);
return (0);
}
static void
svc_gss_data_destroy(void *buf, void *pdata)
{
svc_rpc_gss_data *client_data = (svc_rpc_gss_data *)buf;
mutex_destroy(&client_data->clm);
}
static void
svc_gss_data_reclaim(void *pdata)
{
mutex_enter(&ctx_mutex);
svc_rpc_gss_cache_stats.no_reclaims++;
sweep_clients(TRUE);
mutex_exit(&ctx_mutex);
}
void
svc_gss_init()
{
mutex_init(&cb_mutex, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ctx_mutex, NULL, MUTEX_DEFAULT, NULL);
rw_init(&cred_lock, NULL, RW_DEFAULT, NULL);
clients = (svc_rpc_gss_data **)
kmem_zalloc(svc_rpc_gss_hashmod * sizeof (svc_rpc_gss_data *),
KM_SLEEP);
svc_data_handle = kmem_cache_create("rpc_gss_data_cache",
sizeof (svc_rpc_gss_data), 0,
svc_gss_data_create,
svc_gss_data_destroy,
svc_gss_data_reclaim,
NULL, NULL, 0);
tsd_create(&svc_gss_tsd_key, NULL);
zone_key_create(&svc_gss_zone_key, svc_gss_zone_init,
svc_gss_zone_shutdown, svc_gss_zone_fini);
}
void
svc_gss_fini()
{
if (zone_key_delete(svc_gss_zone_key) != 0)
cmn_err(CE_WARN, "%s: failed to delete zone key", __func__);
tsd_destroy(&svc_gss_tsd_key);
mutex_destroy(&cb_mutex);
mutex_destroy(&ctx_mutex);
rw_destroy(&cred_lock);
kmem_free(clients, svc_rpc_gss_hashmod * sizeof (svc_rpc_gss_data *));
kmem_cache_destroy(svc_data_handle);
}
void
rpc_gss_cleanup(SVCXPRT *clone_xprt)
{
svc_rpc_gss_data *cl;
SVCAUTH *svcauth;
svcauth = &clone_xprt->xp_auth;
mutex_enter(&ctx_mutex);
if ((cl = (svc_rpc_gss_data *)svcauth->svc_ah_private) != NULL) {
mutex_enter(&cl->clm);
ASSERT(cl->ref_cnt > 0);
if (--cl->ref_cnt == 0 && cl->stale) {
mutex_exit(&cl->clm);
destroy_client(cl);
svcauth->svc_ah_private = NULL;
} else
mutex_exit(&cl->clm);
}
if ((gethrestime_sec() - last_swept) > sweep_interval)
sweep_clients(FALSE);
mutex_exit(&ctx_mutex);
}
static void
shift_bits(uint_t *arr, int arrlen, int nbits)
{
int i, j;
uint_t lo, hi;
if (nbits < SEQ_WIN) {
for (i = 0; i < nbits; i++) {
hi = 0;
for (j = 0; j < arrlen; j++) {
lo = arr[j] & SEQ_LO_BIT;
arr[j] >>= 1;
if (hi)
arr[j] |= SEQ_HI_BIT;
hi = lo;
}
}
} else {
for (j = 0; j < arrlen; j++)
arr[j] = 0;
}
}
static bool_t
check_seq(svc_rpc_gss_data *cl, uint_t seq_num, bool_t *kill_context)
{
int i, j;
uint_t bit;
if (seq_num >= SEQ_MAX) {
*kill_context = TRUE;
RPCGSS_LOG0(4, "check_seq: seq_num not valid\n");
return (FALSE);
}
if (seq_num > cl->seq_num) {
(void) shift_bits(cl->seq_bits, SEQ_ARR_SIZE,
(int)(seq_num - cl->seq_num));
cl->seq_bits[0] |= SEQ_HI_BIT;
cl->seq_num = seq_num;
return (TRUE);
}
i = cl->seq_num - seq_num;
if (i >= SEQ_WIN) {
RPCGSS_LOG0(4, "check_seq: seq_num is outside the window\n");
return (FALSE);
}
j = SEQ_MASK - (i & SEQ_MASK);
bit = j > 0 ? (1 << j) : 1;
i >>= DIV_BY_32;
if (cl->seq_bits[i] & bit) {
RPCGSS_LOG0(4, "check_seq: sequence number already seen\n");
return (FALSE);
}
cl->seq_bits[i] |= bit;
return (TRUE);
}
bool_t
rpc_gss_set_callback(rpc_gss_callback_t *cb)
{
rpc_gss_cblist_t *cbl, *tmp;
if (cb->callback == NULL) {
RPCGSS_LOG0(1, "rpc_gss_set_callback: no callback to set\n");
return (FALSE);
}
mutex_enter(&cb_mutex);
if (rpc_gss_cblist) {
for (tmp = rpc_gss_cblist; tmp != NULL; tmp = tmp->next) {
if ((tmp->cb.callback == cb->callback) &&
(tmp->cb.version == cb->version) &&
(tmp->cb.program == cb->program)) {
mutex_exit(&cb_mutex);
return (TRUE);
}
}
}
if ((cbl = (rpc_gss_cblist_t *)kmem_alloc(sizeof (*cbl), KM_SLEEP))
== NULL) {
mutex_exit(&cb_mutex);
return (FALSE);
}
cbl->cb = *cb;
cbl->next = rpc_gss_cblist;
rpc_gss_cblist = cbl;
mutex_exit(&cb_mutex);
return (TRUE);
}
static bool_t
do_callback(struct svc_req *req, svc_rpc_gss_data *client_data)
{
rpc_gss_cblist_t *cbl;
bool_t ret = TRUE, found = FALSE;
rpc_gss_lock_t lock;
OM_uint32 minor;
mutex_enter(&cb_mutex);
for (cbl = rpc_gss_cblist; cbl != NULL; cbl = cbl->next) {
if (req->rq_prog != cbl->cb.program ||
req->rq_vers != cbl->cb.version)
continue;
found = TRUE;
lock.locked = FALSE;
lock.raw_cred = &client_data->raw_cred;
ret = (*cbl->cb.callback)(req, client_data->deleg,
client_data->context, &lock, &client_data->cookie);
req->rq_xprt->xp_cookie = client_data->cookie;
if (ret) {
client_data->locked = lock.locked;
client_data->deleg = GSS_C_NO_CREDENTIAL;
}
break;
}
if (!found) {
if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
(void) kgss_release_cred(&minor, &client_data->deleg,
crgetuid(CRED()));
client_data->deleg = GSS_C_NO_CREDENTIAL;
}
}
mutex_exit(&cb_mutex);
return (ret);
}
bool_t
rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
rpc_gss_ucred_t **ucred, void **cookie)
{
SVCAUTH *svcauth;
svc_rpc_gss_data *client_data;
int gssstat, gidlen;
svcauth = &req->rq_xprt->xp_auth;
client_data = (svc_rpc_gss_data *)svcauth->svc_ah_private;
mutex_enter(&client_data->clm);
if (rcred != NULL) {
svcauth->raw_cred = client_data->raw_cred;
*rcred = &svcauth->raw_cred;
}
if (ucred != NULL) {
*ucred = &client_data->u_cred;
if (client_data->u_cred_set == 0 ||
client_data->u_cred_set < gethrestime_sec()) {
if (client_data->u_cred_set == 0) {
if ((gssstat = kgsscred_expname_to_unix_cred(
&client_data->client_name,
&client_data->u_cred.uid,
&client_data->u_cred.gid,
&client_data->u_cred.gidlist,
&gidlen, crgetuid(CRED())))
!= GSS_S_COMPLETE) {
RPCGSS_LOG(1, "rpc_gss_getcred: "
"kgsscred_expname_to_unix_cred "
"failed %x\n", gssstat);
*ucred = NULL;
} else {
client_data->u_cred.gidlen =
(short)gidlen;
client_data->u_cred_set =
gethrestime_sec() +
svc_rpcgss_gid_timeout;
}
} else if (client_data->u_cred_set
< gethrestime_sec()) {
if ((gssstat = kgss_get_group_info(
client_data->u_cred.uid,
&client_data->u_cred.gid,
&client_data->u_cred.gidlist,
&gidlen, crgetuid(CRED())))
!= GSS_S_COMPLETE) {
RPCGSS_LOG(1, "rpc_gss_getcred: "
"kgss_get_group_info failed %x\n",
gssstat);
*ucred = NULL;
} else {
client_data->u_cred.gidlen =
(short)gidlen;
client_data->u_cred_set =
gethrestime_sec() +
svc_rpcgss_gid_timeout;
}
}
}
}
if (cookie != NULL)
*cookie = client_data->cookie;
req->rq_xprt->xp_cookie = client_data->cookie;
mutex_exit(&client_data->clm);
return (TRUE);
}
bool_t transfer_sec_context(svc_rpc_gss_data *client_data) {
gss_buffer_desc process_token;
OM_uint32 gssstat, minor;
gssstat = kgss_export_sec_context(&minor, client_data->context,
&process_token);
if (gssstat == GSS_S_NAME_NOT_MN) {
RPCGSS_LOG0(4, "svc_rpcsec_gss: export sec context "
"Kernel mod unavailable\n");
} else if (gssstat != GSS_S_COMPLETE) {
RPCGSS_LOG(1, "svc_rpcsec_gss: export sec context failed "
" gssstat = 0x%x\n", gssstat);
(void) gss_release_buffer(&minor, &process_token);
(void) kgss_delete_sec_context(&minor, &client_data->context,
NULL);
return (FALSE);
} else if (process_token.length == 0) {
RPCGSS_LOG0(1, "svc_rpcsec_gss:zero length token in response "
"for export_sec_context, but "
"gsstat == GSS_S_COMPLETE\n");
(void) kgss_delete_sec_context(&minor, &client_data->context,
NULL);
return (FALSE);
} else {
gssstat = kgss_import_sec_context(&minor, &process_token,
client_data->context);
if (gssstat != GSS_S_COMPLETE) {
RPCGSS_LOG(1, "svc_rpcsec_gss: import sec context "
" failed gssstat = 0x%x\n", gssstat);
(void) kgss_delete_sec_context(&minor,
&client_data->context, NULL);
(void) gss_release_buffer(&minor, &process_token);
return (FALSE);
}
RPCGSS_LOG0(4, "gss_import_sec_context successful\n");
(void) gss_release_buffer(&minor, &process_token);
}
return (TRUE);
}
static enum auth_stat
do_gss_accept(
SVCXPRT *xprt,
rpc_gss_init_arg *call_arg,
struct rpc_msg *msg,
svc_rpc_gss_data *client_data,
uint_t cr_version,
rpc_gss_service_t cr_service)
{
rpc_gss_init_res call_res;
gss_buffer_desc output_token;
OM_uint32 gssstat, minor, minor_stat, time_rec;
int ret_flags, ret;
gss_OID mech_type = GSS_C_NULL_OID;
int free_mech_type = 1;
struct svc_req r, *rqst;
rqst = &r;
rqst->rq_xprt = xprt;
output_token.length = 0;
output_token.value = NULL;
bzero((char *)&call_res, sizeof (call_res));
mutex_enter(&client_data->clm);
if (client_data->stale) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
goto error2;
}
call_res.ctx_handle.length = sizeof (client_data->key);
call_res.ctx_handle.value = (char *)&client_data->key;
call_res.seq_window = SEQ_WIN;
gssstat = GSS_S_FAILURE;
minor = 0;
minor_stat = 0;
rw_enter(&cred_lock, RW_READER);
if (client_data->client_name.length) {
(void) gss_release_buffer(&minor,
&client_data->client_name);
}
gssstat = kgss_accept_sec_context(&minor_stat,
&client_data->context,
GSS_C_NO_CREDENTIAL,
call_arg,
GSS_C_NO_CHANNEL_BINDINGS,
&client_data->client_name,
&mech_type,
&output_token,
&ret_flags,
&time_rec,
NULL,
crgetuid(CRED()));
RPCGSS_LOG(4, "gssstat 0x%x \n", gssstat);
if (gssstat == GSS_S_COMPLETE) {
client_data->raw_cred.version = cr_version;
client_data->raw_cred.service = cr_service;
if (client_data->raw_cred.mechanism) {
kgss_free_oid(client_data->raw_cred.mechanism);
client_data->raw_cred.mechanism = NULL;
}
client_data->raw_cred.mechanism = (rpc_gss_OID) mech_type;
free_mech_type = 0;
if (client_data->raw_cred.client_principal) {
kmem_free((caddr_t)client_data->\
raw_cred.client_principal,
client_data->raw_cred.\
client_principal->len + sizeof (int));
client_data->raw_cred.client_principal = NULL;
}
if (! __rpc_gss_make_principal(
&client_data->raw_cred.client_principal,
&client_data->client_name)) {
RPCGSS_LOG0(1, "_svcrpcsec_gss: "
"make principal failed\n");
gssstat = GSS_S_FAILURE;
(void) gss_release_buffer(&minor_stat, &output_token);
}
}
rw_exit(&cred_lock);
call_res.gss_major = gssstat;
call_res.gss_minor = minor_stat;
if (gssstat != GSS_S_COMPLETE &&
gssstat != GSS_S_CONTINUE_NEEDED) {
call_res.ctx_handle.length = 0;
call_res.ctx_handle.value = NULL;
call_res.seq_window = 0;
rpc_gss_display_status(gssstat, minor_stat, mech_type,
crgetuid(CRED()),
"_svc_rpcsec_gss gss_accept_sec_context");
(void) svc_sendreply(rqst->rq_xprt,
__xdr_rpc_gss_init_res, (caddr_t)&call_res);
client_data->stale = TRUE;
ret = AUTH_OK;
goto error2;
}
if (gssstat == GSS_S_COMPLETE) {
client_data->seq_num = 1;
if ((time_rec == GSS_C_INDEFINITE) || (time_rec == 0)) {
client_data->expiration = GSS_C_INDEFINITE;
} else {
client_data->expiration =
time_rec + gethrestime_sec();
}
if (!transfer_sec_context(client_data)) {
ret = RPCSEC_GSS_FAILED;
client_data->stale = TRUE;
RPCGSS_LOG0(1,
"_svc_rpcsec_gss: transfer sec context failed\n");
goto error2;
}
client_data->established = TRUE;
}
if (output_token.length != 0)
GSS_COPY_BUFFER(call_res.token, output_token);
if (gssstat == GSS_S_COMPLETE) {
if (!set_response_verf(rqst, msg, client_data,
(uint_t)SEQ_WIN)) {
ret = RPCSEC_GSS_FAILED;
client_data->stale = TRUE;
RPCGSS_LOG0(1,
"_svc_rpcsec_gss:set response verifier failed\n");
goto error2;
}
}
if (!svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
(caddr_t)&call_res)) {
ret = RPCSEC_GSS_FAILED;
client_data->stale = TRUE;
RPCGSS_LOG0(1, "_svc_rpcsec_gss:send reply failed\n");
goto error2;
}
(void) retrans_add(client_data, msg->rm_xid, &call_res);
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
mutex_exit(&client_data->clm);
(void) gss_release_buffer(&minor_stat, &output_token);
return (AUTH_OK);
error2:
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
mutex_exit(&client_data->clm);
(void) gss_release_buffer(&minor_stat, &output_token);
if (free_mech_type && mech_type)
kgss_free_oid(mech_type);
return (ret);
}
static void
svcrpcsec_gss_taskq_func(void *svcrpcsecgss_taskq_arg)
{
enum auth_stat retval;
svcrpcsec_gss_taskq_arg_t *arg = svcrpcsecgss_taskq_arg;
retval = do_gss_accept(arg->rq_xprt, arg->rpc_call_arg, arg->msg,
arg->client_data, arg->cr_version, arg->cr_service);
if (retval != AUTH_OK) {
cmn_err(CE_NOTE,
"svcrpcsec_gss_taskq_func: do_gss_accept fail 0x%x",
retval);
}
rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
SVC_RELE(arg->rq_xprt, NULL, FALSE);
svc_clone_unlink(arg->rq_xprt);
svc_clone_free(arg->rq_xprt);
xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)arg->rpc_call_arg);
kmem_free(arg->rpc_call_arg, sizeof (*arg->rpc_call_arg));
kmem_free(arg, sizeof (*arg));
}
static enum auth_stat
rpcsec_gss_init(
struct svc_req *rqst,
struct rpc_msg *msg,
rpc_gss_creds creds,
bool_t *no_dispatch,
svc_rpc_gss_data *c_d)
{
svc_rpc_gss_data *client_data;
int ret;
svcrpcsec_gss_taskq_arg_t *arg;
svc_gss_zsd_t *zsd = svc_gss_get_zsd();
taskq_t *tq = zsd->sgz_init_taskq;
if (tq == NULL) {
mutex_enter(&zsd->sgz_lock);
if (zsd->sgz_init_taskq == NULL)
zsd->sgz_init_taskq = svc_gss_create_taskq(curzone);
tq = zsd->sgz_init_taskq;
mutex_exit(&zsd->sgz_lock);
if (tq == NULL) {
cmn_err(CE_NOTE, "%s: no taskq available", __func__);
return (RPCSEC_GSS_FAILED);
}
}
if (creds.ctx_handle.length != 0) {
RPCGSS_LOG0(1, "_svcrpcsec_gss: ctx_handle not null\n");
ret = AUTH_BADCRED;
return (ret);
}
client_data = c_d ? c_d : create_client();
if (client_data == NULL) {
RPCGSS_LOG0(1,
"_svcrpcsec_gss: can't create a new cache entry\n");
ret = AUTH_FAILED;
return (ret);
}
mutex_enter(&client_data->clm);
if (client_data->stale) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
goto error2;
}
arg = kmem_alloc(sizeof (*arg), KM_SLEEP);
arg->rpc_call_arg = kmem_zalloc(sizeof (*arg->rpc_call_arg), KM_SLEEP);
if (!SVC_GETARGS(rqst->rq_xprt, __xdr_rpc_gss_init_arg,
(caddr_t)arg->rpc_call_arg)) {
ret = RPCSEC_GSS_FAILED;
client_data->stale = TRUE;
goto error2;
}
arg->rq_xprt = svc_clone_init();
svc_clone_link(rqst->rq_xprt->xp_master, arg->rq_xprt, rqst->rq_xprt);
arg->rq_xprt->xp_xid = rqst->rq_xprt->xp_xid;
SVC_HOLD(arg->rq_xprt);
arg->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
arg->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
arg->msg = rpc_msg_dup(msg);
arg->client_data = client_data;
arg->cr_version = creds.version;
arg->cr_service = creds.service;
if (taskq_dispatch(tq, svcrpcsec_gss_taskq_func, arg, TQ_SLEEP)
== DDI_FAILURE) {
cmn_err(CE_NOTE, "%s: taskq dispatch fail", __func__);
ret = RPCSEC_GSS_FAILED;
rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
SVC_RELE(arg->rq_xprt, NULL, FALSE);
svc_clone_unlink(arg->rq_xprt);
svc_clone_free(arg->rq_xprt);
kmem_free(arg, sizeof (*arg));
goto error2;
}
mutex_exit(&client_data->clm);
*no_dispatch = TRUE;
return (AUTH_OK);
error2:
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
mutex_exit(&client_data->clm);
cmn_err(CE_NOTE, "rpcsec_gss_init: error 0x%x", ret);
return (ret);
}
static enum auth_stat
rpcsec_gss_continue_init(
struct svc_req *rqst,
struct rpc_msg *msg,
rpc_gss_creds creds,
bool_t *no_dispatch)
{
int ret;
svc_rpc_gss_data *client_data;
svc_rpc_gss_parms_t *gss_parms;
rpc_gss_init_res *retrans_result;
if (creds.ctx_handle.length == 0) {
RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
ret = AUTH_BADCRED;
return (ret);
}
if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
return (ret);
}
mutex_enter(&client_data->clm);
if (client_data->stale) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
goto error2;
}
if (!client_data->established) {
mutex_exit(&client_data->clm);
return (rpcsec_gss_init(rqst, msg, creds, no_dispatch,
client_data));
}
rqst->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
rqst->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
gss_parms = &rqst->rq_xprt->xp_auth.svc_gss_parms;
gss_parms->established = client_data->established;
gss_parms->service = creds.service;
gss_parms->qop_rcvd = (uint_t)client_data->qop;
gss_parms->context = (void *)client_data->context;
gss_parms->seq_num = creds.seq_num;
if (client_data->retrans_data &&
(client_data->retrans_data->xid == msg->rm_xid)) {
retrans_result = &client_data->retrans_data->result;
if (set_response_verf(rqst, msg, client_data,
(uint_t)retrans_result->seq_window)) {
gss_parms->established = FALSE;
(void) svc_sendreply(rqst->rq_xprt,
__xdr_rpc_gss_init_res, (caddr_t)retrans_result);
*no_dispatch = TRUE;
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
}
}
mutex_exit(&client_data->clm);
return (AUTH_OK);
error2:
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
mutex_exit(&client_data->clm);
return (ret);
}
static enum auth_stat
rpcsec_gss_data(
struct svc_req *rqst,
struct rpc_msg *msg,
rpc_gss_creds creds,
bool_t *no_dispatch)
{
int ret;
svc_rpc_gss_parms_t *gss_parms;
svc_rpc_gss_data *client_data;
switch (creds.service) {
case rpc_gss_svc_none:
case rpc_gss_svc_integrity:
case rpc_gss_svc_privacy:
break;
default:
cmn_err(CE_NOTE, "__svcrpcsec_gss: unknown service type=0x%x",
creds.service);
RPCGSS_LOG(1, "_svcrpcsec_gss: unknown service type: 0x%x\n",
creds.service);
ret = AUTH_BADCRED;
return (ret);
}
if (creds.ctx_handle.length == 0) {
RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
ret = AUTH_BADCRED;
return (ret);
}
if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
return (ret);
}
mutex_enter(&client_data->clm);
if (!client_data->established) {
ret = AUTH_FAILED;
goto error2;
}
if (client_data->stale) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
goto error2;
}
if (client_data->retrans_data)
retrans_del(client_data);
rqst->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
rqst->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
gss_parms = &rqst->rq_xprt->xp_auth.svc_gss_parms;
gss_parms->established = client_data->established;
gss_parms->service = creds.service;
gss_parms->qop_rcvd = (uint_t)client_data->qop;
gss_parms->context = (void *)client_data->context;
gss_parms->seq_num = creds.seq_num;
if (!check_verf(msg, client_data->context,
(int *)&gss_parms->qop_rcvd, client_data->u_cred.uid)) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: check verf failed\n");
goto error2;
}
if (!client_data->done_docallback) {
client_data->done_docallback = TRUE;
client_data->qop = gss_parms->qop_rcvd;
client_data->raw_cred.qop = gss_parms->qop_rcvd;
client_data->raw_cred.service = creds.service;
if (!do_callback(rqst, client_data)) {
ret = AUTH_FAILED;
RPCGSS_LOG0(1, "_svc_rpcsec_gss:callback failed\n");
goto error2;
}
}
if (client_data->locked && gss_parms->qop_rcvd != client_data->qop) {
ret = AUTH_BADVERF;
RPCGSS_LOG0(1, "_svcrpcsec_gss: can not change qop\n");
goto error2;
}
if (!check_seq(client_data, creds.seq_num, &client_data->stale)) {
if (client_data->stale) {
ret = RPCSEC_GSS_FAILED;
RPCGSS_LOG0(1,
"_svc_rpcsec_gss:check seq failed\n");
} else {
RPCGSS_LOG0(4, "_svc_rpcsec_gss:check seq "
"failed on good context. Ignoring "
"request\n");
ret = AUTH_OK;
*no_dispatch = TRUE;
}
goto error2;
}
if (!set_response_verf(rqst, msg, client_data, creds.seq_num)) {
ret = RPCSEC_GSS_FAILED;
client_data->stale = TRUE;
RPCGSS_LOG0(1,
"_svc_rpcsec_gss:set response verifier failed\n");
goto error2;
}
if (client_data->locked &&
client_data->raw_cred.service != creds.service) {
RPCGSS_LOG0(1, "_svc_rpcsec_gss: "
"security service changed.\n");
ret = AUTH_FAILED;
goto error2;
}
rqst->rq_clntcred = (char *)&client_data->raw_cred;
mutex_exit(&client_data->clm);
return (AUTH_OK);
error2:
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
mutex_exit(&client_data->clm);
return (ret);
}
static enum auth_stat
rpcsec_gss_destroy(
struct svc_req *rqst,
rpc_gss_creds creds,
bool_t *no_dispatch)
{
svc_rpc_gss_data *client_data;
int ret;
if (creds.ctx_handle.length == 0) {
RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
ret = AUTH_BADCRED;
return (ret);
}
if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
return (ret);
}
mutex_enter(&client_data->clm);
if (!client_data->established) {
ret = AUTH_FAILED;
goto error2;
}
if (client_data->stale) {
ret = RPCSEC_GSS_NOCRED;
RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
goto error2;
}
(void) svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
*no_dispatch = TRUE;
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
client_data->stale = TRUE;
mutex_exit(&client_data->clm);
return (AUTH_OK);
error2:
ASSERT(client_data->ref_cnt > 0);
client_data->ref_cnt--;
client_data->stale = TRUE;
mutex_exit(&client_data->clm);
return (ret);
}
enum auth_stat
__svcrpcsec_gss(
struct svc_req *rqst,
struct rpc_msg *msg,
bool_t *no_dispatch)
{
XDR xdrs;
rpc_gss_creds creds;
struct opaque_auth *cred;
int ret;
*no_dispatch = FALSE;
rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NONE;
rqst->rq_xprt->xp_verf.oa_base = NULL;
rqst->rq_xprt->xp_verf.oa_length = 0;
cred = &msg->rm_call.cb_cred;
if (cred->oa_length == 0) {
RPCGSS_LOG0(1, "_svcrpcsec_gss: zero length cred\n");
return (AUTH_BADCRED);
}
xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
bzero((char *)&creds, sizeof (creds));
if (!__xdr_rpc_gss_creds(&xdrs, &creds)) {
XDR_DESTROY(&xdrs);
RPCGSS_LOG0(1, "_svcrpcsec_gss: can't decode creds\n");
ret = AUTH_BADCRED;
return (AUTH_BADCRED);
}
XDR_DESTROY(&xdrs);
switch (creds.gss_proc) {
case RPCSEC_GSS_INIT:
ret = rpcsec_gss_init(rqst, msg, creds, no_dispatch, NULL);
break;
case RPCSEC_GSS_CONTINUE_INIT:
ret = rpcsec_gss_continue_init(rqst, msg, creds, no_dispatch);
break;
case RPCSEC_GSS_DATA:
ret = rpcsec_gss_data(rqst, msg, creds, no_dispatch);
break;
case RPCSEC_GSS_DESTROY:
ret = rpcsec_gss_destroy(rqst, creds, no_dispatch);
break;
default:
cmn_err(CE_NOTE, "__svcrpcsec_gss: bad proc=%d",
creds.gss_proc);
ret = AUTH_BADCRED;
}
if (creds.ctx_handle.length != 0)
xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
return (ret);
}
static bool_t
check_verf(struct rpc_msg *msg, gss_ctx_id_t context, int *qop_state, uid_t uid)
{
int *buf, *tmp;
char hdr[128];
struct opaque_auth *oa;
int len;
gss_buffer_desc msg_buf;
gss_buffer_desc tok_buf;
OM_uint32 gssstat, minor_stat;
oa = &msg->rm_call.cb_cred;
if (oa->oa_length > MAX_AUTH_BYTES)
return (FALSE);
if (sizeof (hdr) < (8 * BYTES_PER_XDR_UNIT +
RNDUP(oa->oa_length)))
return (FALSE);
buf = (int *)hdr;
IXDR_PUT_U_INT32(buf, msg->rm_xid);
IXDR_PUT_ENUM(buf, msg->rm_direction);
IXDR_PUT_U_INT32(buf, msg->rm_call.cb_rpcvers);
IXDR_PUT_U_INT32(buf, msg->rm_call.cb_prog);
IXDR_PUT_U_INT32(buf, msg->rm_call.cb_vers);
IXDR_PUT_U_INT32(buf, msg->rm_call.cb_proc);
IXDR_PUT_ENUM(buf, oa->oa_flavor);
IXDR_PUT_U_INT32(buf, oa->oa_length);
if (oa->oa_length) {
len = RNDUP(oa->oa_length);
tmp = buf;
buf += len / sizeof (int);
*(buf - 1) = 0;
(void) bcopy(oa->oa_base, (caddr_t)tmp, oa->oa_length);
}
len = ((char *)buf) - hdr;
msg_buf.length = len;
msg_buf.value = hdr;
oa = &msg->rm_call.cb_verf;
tok_buf.length = oa->oa_length;
tok_buf.value = oa->oa_base;
gssstat = kgss_verify(&minor_stat, context, &msg_buf, &tok_buf,
qop_state);
if (gssstat != GSS_S_COMPLETE) {
RPCGSS_LOG(1, "check_verf: kgss_verify status 0x%x\n", gssstat);
RPCGSS_LOG(4, "check_verf: msg_buf length %d\n", len);
RPCGSS_LOG(4, "check_verf: msg_buf value 0x%x\n", *(int *)hdr);
RPCGSS_LOG(4, "check_verf: tok_buf length %ld\n",
tok_buf.length);
RPCGSS_LOG(4, "check_verf: tok_buf value 0x%p\n",
(void *)oa->oa_base);
RPCGSS_LOG(4, "check_verf: context 0x%p\n", (void *)context);
return (FALSE);
}
return (TRUE);
}
static bool_t
set_response_verf(struct svc_req *rqst, struct rpc_msg *msg,
svc_rpc_gss_data *cl, uint_t num)
{
OM_uint32 minor;
gss_buffer_desc in_buf, out_buf;
uint_t num_net;
num_net = (uint_t)htonl(num);
in_buf.length = sizeof (num);
in_buf.value = (char *)&num_net;
if ((kgss_sign(&minor, cl->context, cl->qop, &in_buf, &out_buf))
!= GSS_S_COMPLETE)
return (FALSE);
rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
bcopy(out_buf.value, rqst->rq_xprt->xp_verf.oa_base, out_buf.length);
(void) gss_release_buffer(&minor, &out_buf);
return (TRUE);
}
static svc_rpc_gss_data *
create_client()
{
svc_rpc_gss_data *client_data;
static uint_t key = 1;
client_data = (svc_rpc_gss_data *) kmem_cache_alloc(svc_data_handle,
KM_SLEEP);
if (client_data == NULL)
return (NULL);
client_data->next = NULL;
client_data->prev = NULL;
client_data->lru_next = NULL;
client_data->lru_prev = NULL;
client_data->client_name.length = 0;
client_data->client_name.value = NULL;
client_data->seq_num = 0;
bzero(client_data->seq_bits, sizeof (client_data->seq_bits));
client_data->key = 0;
client_data->cookie = NULL;
bzero(&client_data->u_cred, sizeof (client_data->u_cred));
client_data->established = FALSE;
client_data->locked = FALSE;
client_data->u_cred_set = 0;
client_data->context = GSS_C_NO_CONTEXT;
client_data->expiration = GSS_C_INDEFINITE;
client_data->deleg = GSS_C_NO_CREDENTIAL;
client_data->ref_cnt = 1;
client_data->last_ref_time = gethrestime_sec();
client_data->qop = GSS_C_QOP_DEFAULT;
client_data->done_docallback = FALSE;
client_data->stale = FALSE;
client_data->retrans_data = NULL;
bzero(&client_data->raw_cred, sizeof (client_data->raw_cred));
svc_rpc_gss_cache_stats.total_entries_allocated++;
mutex_enter(&ctx_mutex);
for (;;) {
client_data->key = key++;
if (find_client(client_data->key) == NULL) {
insert_client(client_data);
mutex_exit(&ctx_mutex);
return (client_data);
}
}
}
static void
insert_client(svc_rpc_gss_data *client_data)
{
svc_rpc_gss_data *cl;
int index = HASH(client_data->key);
ASSERT(mutex_owned(&ctx_mutex));
client_data->prev = NULL;
cl = clients[index];
if ((client_data->next = cl) != NULL)
cl->prev = client_data;
clients[index] = client_data;
client_data->lru_prev = NULL;
if ((client_data->lru_next = lru_first) != NULL)
lru_first->lru_prev = client_data;
else
lru_last = client_data;
lru_first = client_data;
num_gss_contexts++;
}
static svc_rpc_gss_data *
get_client(gss_buffer_t ctx_handle)
{
uint_t key = *(uint_t *)ctx_handle->value;
svc_rpc_gss_data *cl;
mutex_enter(&ctx_mutex);
if ((cl = find_client(key)) != NULL) {
mutex_enter(&cl->clm);
if (cl->stale) {
if (cl->ref_cnt == 0) {
mutex_exit(&cl->clm);
destroy_client(cl);
} else {
mutex_exit(&cl->clm);
}
mutex_exit(&ctx_mutex);
return (NULL);
}
cl->ref_cnt++;
cl->last_ref_time = gethrestime_sec();
mutex_exit(&cl->clm);
if (cl != lru_first) {
cl->lru_prev->lru_next = cl->lru_next;
if (cl->lru_next != NULL)
cl->lru_next->lru_prev = cl->lru_prev;
else
lru_last = cl->lru_prev;
cl->lru_prev = NULL;
cl->lru_next = lru_first;
lru_first->lru_prev = cl;
lru_first = cl;
}
}
mutex_exit(&ctx_mutex);
return (cl);
}
static svc_rpc_gss_data *
find_client(uint_t key)
{
int index = HASH(key);
svc_rpc_gss_data *cl = NULL;
ASSERT(mutex_owned(&ctx_mutex));
for (cl = clients[index]; cl != NULL; cl = cl->next) {
if (cl->key == key)
break;
}
return (cl);
}
static void
destroy_client(svc_rpc_gss_data *client_data)
{
OM_uint32 minor;
int index = HASH(client_data->key);
ASSERT(mutex_owned(&ctx_mutex));
if (client_data->prev == NULL)
clients[index] = client_data->next;
else
client_data->prev->next = client_data->next;
if (client_data->next != NULL)
client_data->next->prev = client_data->prev;
if (client_data->lru_prev == NULL)
lru_first = client_data->lru_next;
else
client_data->lru_prev->lru_next = client_data->lru_next;
if (client_data->lru_next != NULL)
client_data->lru_next->lru_prev = client_data->lru_prev;
else
lru_last = client_data->lru_prev;
if (client_data->context != GSS_C_NO_CONTEXT) {
(void) kgss_delete_sec_context(&minor, &client_data->context,
NULL);
common_client_data_free(client_data);
if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
(void) kgss_release_cred(&minor, &client_data->deleg,
crgetuid(CRED()));
}
}
if (client_data->u_cred.gidlist != NULL) {
kmem_free((char *)client_data->u_cred.gidlist,
client_data->u_cred.gidlen * sizeof (gid_t));
client_data->u_cred.gidlist = NULL;
}
if (client_data->retrans_data != NULL)
retrans_del(client_data);
kmem_cache_free(svc_data_handle, client_data);
num_gss_contexts--;
}
static void
sweep_clients(bool_t from_reclaim)
{
svc_rpc_gss_data *cl, *next;
time_t last_reference_needed;
time_t now = gethrestime_sec();
ASSERT(mutex_owned(&ctx_mutex));
last_reference_needed = now - (from_reclaim ?
svc_rpc_gss_active_delta : svc_rpc_gss_inactive_delta);
cl = lru_last;
while (cl) {
next = cl->lru_prev;
mutex_enter(&cl->clm);
if ((cl->expiration != GSS_C_INDEFINITE &&
cl->expiration <= now) || cl->stale ||
cl->last_ref_time <= last_reference_needed) {
if ((cl->expiration != GSS_C_INDEFINITE &&
cl->expiration <= now) || cl->stale ||
(cl->last_ref_time <= last_reference_needed &&
cl->ref_cnt == 0)) {
cl->stale = TRUE;
if (cl->ref_cnt == 0) {
mutex_exit(&cl->clm);
if (from_reclaim)
svc_rpc_gss_cache_stats.
no_returned_by_reclaim++;
destroy_client(cl);
} else
mutex_exit(&cl->clm);
} else
mutex_exit(&cl->clm);
} else
mutex_exit(&cl->clm);
cl = next;
}
last_swept = gethrestime_sec();
}
static bool_t
svc_rpc_gss_wrap(SVCAUTH *auth, XDR *out_xdrs, bool_t (*xdr_func)(),
caddr_t xdr_ptr)
{
svc_rpc_gss_parms_t *gss_parms = SVCAUTH_GSSPARMS(auth);
bool_t ret;
if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
return ((*xdr_func)(out_xdrs, xdr_ptr));
ret = __rpc_gss_wrap_data(gss_parms->service,
(OM_uint32)gss_parms->qop_rcvd,
(gss_ctx_id_t)gss_parms->context,
gss_parms->seq_num,
out_xdrs, xdr_func, xdr_ptr);
return (ret);
}
static bool_t
svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *in_xdrs, bool_t (*xdr_func)(),
caddr_t xdr_ptr)
{
svc_rpc_gss_parms_t *gss_parms = SVCAUTH_GSSPARMS(auth);
if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
return ((*xdr_func)(in_xdrs, xdr_ptr));
return (__rpc_gss_unwrap_data(gss_parms->service,
(gss_ctx_id_t)gss_parms->context,
gss_parms->seq_num,
gss_parms->qop_rcvd,
in_xdrs, xdr_func, xdr_ptr));
}
int
rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
{
return (0);
}
static void retrans_add(client, xid, result)
svc_rpc_gss_data *client;
uint32_t xid;
rpc_gss_init_res *result;
{
retrans_entry *rdata;
if (client->retrans_data && client->retrans_data->xid == xid)
return;
rdata = kmem_zalloc(sizeof (*rdata), KM_SLEEP);
if (rdata == NULL)
return;
rdata->xid = xid;
rdata->result = *result;
if (result->token.length != 0) {
GSS_DUP_BUFFER(rdata->result.token, result->token);
}
if (client->retrans_data)
retrans_del(client);
client->retrans_data = rdata;
}
static void retrans_del(client)
svc_rpc_gss_data *client;
{
retrans_entry *rdata;
OM_uint32 minor_stat;
if (client->retrans_data == NULL)
return;
rdata = client->retrans_data;
if (rdata->result.token.length != 0) {
(void) gss_release_buffer(&minor_stat, &rdata->result.token);
}
kmem_free((caddr_t)rdata, sizeof (*rdata));
client->retrans_data = NULL;
}
static void
common_client_data_free(svc_rpc_gss_data *client_data)
{
if (client_data->client_name.length > 0) {
(void) gss_release_buffer(NULL, &client_data->client_name);
}
if (client_data->raw_cred.client_principal) {
kmem_free((caddr_t)client_data->raw_cred.client_principal,
client_data->raw_cred.client_principal->len +
sizeof (int));
client_data->raw_cred.client_principal = NULL;
}
if (client_data->raw_cred.mechanism) {
kgss_free_oid(client_data->raw_cred.mechanism);
client_data->raw_cred.mechanism = NULL;
}
}