void authgss_ctx_gc_idle(void) { struct rbtree_x_part *xp; struct authgss_x_part *axp; struct svc_rpc_gss_data *gd; int ix, cnt, part; cond_init_authgss_hash(); for (ix = 0, part = IDLE_NEXT(); ix < authgss_hash_st.xt.npart; ++ix, part = IDLE_NEXT()) { xp = &(authgss_hash_st.xt.tree[part]); axp = (struct authgss_x_part *)xp->u1; cnt = 0; mutex_lock(&xp->mtx); again: gd = TAILQ_FIRST(&axp->lru_q); if (!gd) goto next_t; if (unlikely((authgss_hash_st.size > __svc_params->gss.max_gc) || ((abs(axp->gen - gd->gen) > __svc_params->gss.max_idle_gen)) || (authgss_ctx_expired(gd)))) { /* remove entry */ rbtree_x_cached_remove(&authgss_hash_st.xt, xp, &gd->node_k, gd->hk.k); TAILQ_REMOVE(&axp->lru_q, gd, lru_q); (void)atomic_dec_uint32_t(&authgss_hash_st.size); /* drop sentinel ref (may free gd) */ unref_svc_rpc_gss_data(gd, SVC_RPC_GSS_FLAG_NONE); if (++cnt < authgss_hash_st.max_part) goto again; } next_t: mutex_unlock(&xp->mtx); } /* perturb by 1 */ (void)IDLE_NEXT(); }
static bool svcauth_gss_release(SVCAUTH *auth, struct svc_req *req) { struct svc_rpc_gss_data *gd; caddr_t last_oa_base; gd = SVCAUTH_PRIVATE(auth); if (gd) unref_svc_rpc_gss_data(gd, SVC_RPC_GSS_FLAG_NONE); req->rq_auth = NULL; if ((last_oa_base = req->rq_verf.oa_base)) { /* XXX wrapper conflict: mem_free vs. gssalloc_free ? */ /* ... but this only matters for win32 | kernel */ req->rq_verf.oa_base = 0; mem_free(last_oa_base, req->rq_verf.oa_length); } return (true); }
bool authgss_ctx_hash_del(struct svc_rpc_gss_data *gd) { struct rbtree_x_part *t; struct authgss_x_part *axp; cond_init_authgss_hash(); t = rbtx_partition_of_scalar(&authgss_hash_st.xt, gd->hk.k); mutex_lock(&t->mtx); rbtree_x_cached_remove(&authgss_hash_st.xt, t, &gd->node_k, gd->hk.k); axp = (struct authgss_x_part *)t->u1; TAILQ_REMOVE(&axp->lru_q, gd, lru_q); mutex_unlock(&t->mtx); /* global size */ (void)atomic_dec_uint32_t(&authgss_hash_st.size); /* release gd */ unref_svc_rpc_gss_data(gd, SVC_RPC_GSS_FLAG_NONE); return (true); }
enum auth_stat _svcauth_gss(struct svc_req *req, struct rpc_msg *msg, bool *no_dispatch) { XDR xdrs[1]; SVCAUTH *auth; struct svc_rpc_gss_data *gd = NULL; struct rpc_gss_cred *gc = NULL; struct rpc_gss_init_res gr; int call_stat, offset; OM_uint32 min_stat; bool gd_locked = false; bool gd_hashed = false; /* Initialize reply. */ req->rq_verf = _null_auth; /* Unserialize client credentials. */ if (req->rq_cred.oa_length <= 0) svcauth_gss_return(AUTH_BADCRED); gc = (struct rpc_gss_cred *)req->rq_clntcred; memset(gc, 0, sizeof(struct rpc_gss_cred)); xdrmem_create(xdrs, req->rq_cred.oa_base, req->rq_cred.oa_length, XDR_DECODE); if (!xdr_rpc_gss_cred(xdrs, gc)) { XDR_DESTROY(xdrs); svcauth_gss_return(AUTH_BADCRED); } XDR_DESTROY(xdrs); /* Check version. */ if (gc->gc_v != RPCSEC_GSS_VERSION) svcauth_gss_return(AUTH_BADCRED); if (gc->gc_seq > RPCSEC_GSS_MAXSEQ) svcauth_gss_return(RPCSEC_GSS_CTXPROBLEM); if (gc->gc_proc > RPCSEC_GSS_MAXPROC) svcauth_gss_return(AUTH_BADCRED); /* Check RPCSEC_GSS service. */ if (gc->gc_svc != RPCSEC_GSS_SVC_NONE && gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY && gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY) svcauth_gss_return(AUTH_BADCRED); /* Context lookup. */ if ((gc->gc_proc == RPCSEC_GSS_DATA) || (gc->gc_proc == RPCSEC_GSS_DESTROY)) { /* Per RFC 2203 5.3.3.3, if a valid security context * cannot be found to authorize a request, the * implementation returns RPCSEC_GSS_CREDPROBLEM. * N.B., we are explicitly allowed to discard contexts * for any reason (e.g., to save space). */ gd = authgss_ctx_hash_get(gc); if (!gd) svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM); gd_hashed = true; if (gc->gc_svc != gd->sec.svc) gd->sec.svc = gc->gc_svc; } if (!gd) { /* Allocate and set up server auth handle. */ auth = mem_alloc(sizeof(SVCAUTH)); gd = alloc_svc_rpc_gss_data(); auth->svc_ah_ops = &svc_auth_gss_ops; auth->svc_ah_private = (caddr_t) gd; gd->auth = auth; } /* Serialize context. */ mutex_lock(&gd->lock); gd_locked = true; /* thread auth */ req->rq_auth = gd->auth; /* Check sequence number. */ if (gd->established) { if (get_time_fast() >= gd->endtime) { *no_dispatch = true; svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM); } /* XXX implied serialization? or just fudging? advance if * greater? */ offset = gd->seqlast - gc->gc_seq; if (offset < 0) { gd->seqlast = gc->gc_seq; offset = 0 - offset; gd->seqmask <<= offset; offset = 0; } else if (offset >= gd->win || (gd->seqmask & (1 << offset))) { *no_dispatch = true; svcauth_gss_return(AUTH_OK); } gd->seqmask |= (1 << offset); /* XXX harmless */ req->rq_ap1 = (void *)(uintptr_t) gc->gc_seq; /* GCC casts */ req->rq_clntname = (char *) gd->client_name; req->rq_svcname = (char *) gd->ctx; } /* gd->established */ /* Handle RPCSEC_GSS control procedure. */ switch (gc->gc_proc) { case RPCSEC_GSS_INIT: case RPCSEC_GSS_CONTINUE_INIT: if (req->rq_proc != NULLPROC) svcauth_gss_return(AUTH_FAILED); /* XXX ? */ /* XXX why unconditionally acquire creds? */ if (!svcauth_gss_acquire_cred()) svcauth_gss_return(AUTH_FAILED); if (!svcauth_gss_accept_sec_context(req, gd, &gr)) svcauth_gss_return(AUTH_REJECTEDCRED); if (!svcauth_gss_nextverf(req, gd, htonl(gr.gr_win))) { /* XXX check */ gss_release_buffer(&min_stat, &gr.gr_token); mem_free(gr.gr_ctx.value, 0); svcauth_gss_return(AUTH_FAILED); } *no_dispatch = true; call_stat = svc_sendreply(req->rq_xprt, req, (xdrproc_t) xdr_rpc_gss_init_res, (caddr_t) &gr); /* XXX */ gss_release_buffer(&min_stat, &gr.gr_token); gss_release_buffer(&min_stat, &gd->checksum); mem_free(gr.gr_ctx.value, 0); if (!call_stat) svcauth_gss_return(AUTH_FAILED); if (gr.gr_major == GSS_S_COMPLETE) { gd->established = true; if (!gd_hashed) { /* krb5 pac -- try all that apply */ gss_buffer_desc attr, display_buffer; /* completely generic */ int auth = 1, comp = 0, more = -1; memset(&gd->pac.ms_pac, 0, sizeof(gss_buffer_desc)); memset(&display_buffer, 0, sizeof(gss_buffer_desc)); /* MS AD */ attr.value = "urn:mspac:"; attr.length = 10; gr.gr_major = gss_get_name_attribute(&gr.gr_minor, gd->client_name, &attr, &auth, &comp, &gd->pac.ms_pac, &display_buffer, &more); if (gr.gr_major == GSS_S_COMPLETE) { /* dont need it */ gss_release_buffer(&gr.gr_minor, &display_buffer); gd->flags |= SVC_RPC_GSS_FLAG_MSPAC; } (void)authgss_ctx_hash_set(gd); } } break; /* XXX next 2 cases: is it correct to leave gd in cache * after a validate or verf failure ? */ case RPCSEC_GSS_DATA: call_stat = svcauth_gss_validate(req, gd, msg); switch (call_stat) { default: svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM); case 0: break; } if (!svcauth_gss_nextverf(req, gd, htonl(gc->gc_seq))) svcauth_gss_return(AUTH_FAILED); break; case RPCSEC_GSS_DESTROY: if (req->rq_proc != NULLPROC) svcauth_gss_return(AUTH_FAILED); /* XXX ? */ if (svcauth_gss_validate(req, gd, msg)) svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM); if (!svcauth_gss_nextverf(req, gd, htonl(gc->gc_seq))) svcauth_gss_return(AUTH_FAILED); *no_dispatch = true; (void)authgss_ctx_hash_del(gd); /* avoid lock order reversal gd->lock, xprt->xp_lock */ mutex_unlock(&gd->lock); gd_locked = false; call_stat = svc_sendreply(req->rq_xprt, req, (xdrproc_t) xdr_void, (caddr_t) NULL); /* We acquired a reference on gd with authgss_ctx_hash_get * call. Time to release the reference as we don't need * gd anymore. */ unref_svc_rpc_gss_data(gd, SVC_RPC_GSS_FLAG_NONE); req->rq_auth = &svc_auth_none; break; default: svcauth_gss_return(AUTH_REJECTEDCRED); break; } svcauth_gss_return(AUTH_OK); }