Esempio n. 1
0
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();
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
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);
}