Exemplo n.º 1
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);
}
Exemplo n.º 2
0
enum auth_stat
_svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
{
	XDR	 		 xdrs;
	SVCAUTH			*auth;
	struct svc_rpc_gss_data	*gd;
	struct rpc_gss_cred	*gc;
	struct rpc_gss_init_res	 gr;
	int			 call_stat, offset;

	log_debug("in svcauth_gss()");

	/* Initialize reply. */
	rqst->rq_xprt->xp_verf = _null_auth;

	/* Allocate and set up server auth handle. */
	if (rqst->rq_xprt->xp_auth == NULL ||
	    rqst->rq_xprt->xp_auth == &svc_auth_none) {
		if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
			fprintf(stderr, "svcauth_gss: out_of_memory\n");
			return (AUTH_FAILED);
		}
		if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
			fprintf(stderr, "svcauth_gss: out_of_memory\n");
			return (AUTH_FAILED);
		}
		auth->svc_ah_ops = &svc_auth_gss_ops;
		auth->svc_ah_private = (caddr_t) gd;
		rqst->rq_xprt->xp_auth = auth;
	}
	else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);

	/* Deserialize client credentials. */
	if (rqst->rq_cred.oa_length <= 0)
		return (AUTH_BADCRED);

	gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
	memset(gc, 0, sizeof(*gc));

	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
		      rqst->rq_cred.oa_length, XDR_DECODE);

	if (!xdr_rpc_gss_cred(&xdrs, gc)) {
		XDR_DESTROY(&xdrs);
		return (AUTH_BADCRED);
	}
	XDR_DESTROY(&xdrs);

	/* Check version. */
	if (gc->gc_v != RPCSEC_GSS_VERSION)
		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)
		return (AUTH_BADCRED);

	/* Check sequence number. */
	if (gd->established) {
		if (gc->gc_seq > MAXSEQ)
			return (RPCSEC_GSS_CTXPROBLEM);

		if ((offset = gd->seqlast - gc->gc_seq) < 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 = 1;
			return (RPCSEC_GSS_CTXPROBLEM);
		}
		gd->seq = gc->gc_seq;
		gd->seqmask |= (1 << offset);
	}

	if (gd->established) {
		rqst->rq_clntname = (char *)gd->client_name;
		rqst->rq_svcname = (char *)gd->ctx;
	}

	/* Handle RPCSEC_GSS control procedure. */
	switch (gc->gc_proc) {

	case RPCSEC_GSS_INIT:
	case RPCSEC_GSS_CONTINUE_INIT:
		if (rqst->rq_proc != NULLPROC)
			return (AUTH_FAILED);		/* XXX ? */

		if (_svcauth_gss_name == NULL) {
			if (!svcauth_gss_import_name("nfs"))
				return (AUTH_FAILED);
		}

		if (!svcauth_gss_acquire_cred())
			return (AUTH_FAILED);

		if (!svcauth_gss_accept_sec_context(rqst, &gr))
			return (AUTH_REJECTEDCRED);

		if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win)))
			return (AUTH_FAILED);

		*no_dispatch = TRUE;

		call_stat = svc_sendreply(rqst->rq_xprt, 
			(xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr);

		if (!call_stat)
			return (AUTH_FAILED);

		if (gr.gr_major == GSS_S_COMPLETE)
			gd->established = TRUE;

		break;

	case RPCSEC_GSS_DATA:
		if (!svcauth_gss_validate(gd, msg))
			return (RPCSEC_GSS_CREDPROBLEM);

		if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
			return (AUTH_FAILED);
		break;

	case RPCSEC_GSS_DESTROY:
		if (rqst->rq_proc != NULLPROC)
			return (AUTH_FAILED);		/* XXX ? */

		if (!svcauth_gss_validate(gd, msg))
			return (RPCSEC_GSS_CREDPROBLEM);

		if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
			return (AUTH_FAILED);

		if (!svcauth_gss_release_cred())
			return (AUTH_FAILED);

		SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);
		rqst->rq_xprt->xp_auth = &svc_auth_none;

		break;

	default:
		return (AUTH_REJECTEDCRED);
		break;
	}
	return (AUTH_OK);
}
Exemplo n.º 3
0
static bool_t
authgss_marshal(AUTH *auth, XDR *xdrs)
{
	XDR			 tmpxdrs;
	char			 tmp[MAX_AUTH_BYTES];
	struct rpc_gss_data	*gd;
	gss_buffer_desc		 rpcbuf, checksum;
	OM_uint32		 maj_stat, min_stat;
	bool_t			 xdr_stat;

	log_debug("in authgss_marshal()");

	gd = AUTH_PRIVATE(auth);

	if (gd->established)
		gd->gc.gc_seq++;

	xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);

	if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
		XDR_DESTROY(&tmpxdrs);
		return (FALSE);
	}
	auth->ah_cred.oa_flavor = RPCSEC_GSS;
	auth->ah_cred.oa_base = tmp;
	auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);

	XDR_DESTROY(&tmpxdrs);

	if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
		return (FALSE);

	if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
	    gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
		return (xdr_opaque_auth(xdrs, &_null_auth));
	}
	/* Checksum serialized RPC header, up to and including credential. */
	rpcbuf.length = XDR_GETPOS(xdrs);
	XDR_SETPOS(xdrs, 0);
	rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);

	maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
			    &rpcbuf, &checksum);

	if (maj_stat != GSS_S_COMPLETE) {
		log_status("gss_get_mic", maj_stat, min_stat);
		if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
			gd->established = FALSE;
			authgss_destroy_context(auth);
		}
		return (FALSE);
	}
	auth->ah_verf.oa_flavor = RPCSEC_GSS;
	auth->ah_verf.oa_base = checksum.value;
	auth->ah_verf.oa_length = checksum.length;

	xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
	gss_release_buffer(&min_stat, &checksum);

	return (xdr_stat);
}
Exemplo n.º 4
0
enum auth_stat
gssrpc__svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg,
	bool_t *no_dispatch)
{
	enum auth_stat		 retstat;
	XDR	 		 xdrs;
	SVCAUTH			*auth;
	struct svc_rpc_gss_data	*gd;
	struct rpc_gss_cred	*gc;
	struct rpc_gss_init_res	 gr;
	int			 call_stat, offset;
	OM_uint32		 min_stat;

	log_debug("in svcauth_gss()");

	/* Initialize reply. */
	rqst->rq_xprt->xp_verf = gssrpc__null_auth;

	/* Allocate and set up server auth handle. */
	if (rqst->rq_xprt->xp_auth == NULL ||
	    rqst->rq_xprt->xp_auth == &svc_auth_none) {
		if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
			fprintf(stderr, "svcauth_gss: out_of_memory\n");
			return (AUTH_FAILED);
		}
		if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
			fprintf(stderr, "svcauth_gss: out_of_memory\n");
			return (AUTH_FAILED);
		}
		auth->svc_ah_ops = &svc_auth_gss_ops;
		SVCAUTH_PRIVATE(auth) = gd;
		rqst->rq_xprt->xp_auth = auth;
	}
	else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);

	log_debug("xp_auth=%p, gd=%p", rqst->rq_xprt->xp_auth, gd);

	/* Deserialize client credentials. */
	if (rqst->rq_cred.oa_length <= 0)
		return (AUTH_BADCRED);

	gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
	memset(gc, 0, sizeof(*gc));

	log_debug("calling xdrmem_create()");
	log_debug("oa_base=%p, oa_length=%u", rqst->rq_cred.oa_base,
		  rqst->rq_cred.oa_length);
	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
		      rqst->rq_cred.oa_length, XDR_DECODE);
	log_debug("xdrmem_create() returned");

	if (!xdr_rpc_gss_cred(&xdrs, gc)) {
		log_debug("xdr_rpc_gss_cred() failed");
		XDR_DESTROY(&xdrs);
		return (AUTH_BADCRED);
	}
	XDR_DESTROY(&xdrs);

	retstat = AUTH_FAILED;

#define ret_freegc(code) do { retstat = code; goto freegc; } while (0)

	/* Check version. */
	if (gc->gc_v != RPCSEC_GSS_VERSION)
		ret_freegc (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)
		ret_freegc (AUTH_BADCRED);

	/* Check sequence number. */
	if (gd->established) {
		if (gc->gc_seq > MAXSEQ)
			ret_freegc (RPCSEC_GSS_CTXPROBLEM);

		if ((offset = gd->seqlast - gc->gc_seq) < 0) {
			gd->seqlast = gc->gc_seq;
			offset = 0 - offset;
			gd->seqmask <<= offset;
			offset = 0;
		} else if ((u_int)offset >= gd->win ||
			   (gd->seqmask & (1 << offset))) {
			*no_dispatch = 1;
			ret_freegc (RPCSEC_GSS_CTXPROBLEM);
		}
		gd->seq = gc->gc_seq;
		gd->seqmask |= (1 << offset);
	}

	if (gd->established) {
		rqst->rq_clntname = (char *)gd->client_name;
		rqst->rq_svccred = (char *)gd->ctx;
	}

	/* Handle RPCSEC_GSS control procedure. */
	switch (gc->gc_proc) {

	case RPCSEC_GSS_INIT:
	case RPCSEC_GSS_CONTINUE_INIT:
		if (rqst->rq_proc != NULLPROC)
			ret_freegc (AUTH_FAILED);		/* XXX ? */

		if (!svcauth_gss_acquire_cred())
			ret_freegc (AUTH_FAILED);

		if (!svcauth_gss_accept_sec_context(rqst, &gr))
			ret_freegc (AUTH_REJECTEDCRED);

		if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win))) {
			gss_release_buffer(&min_stat, &gr.gr_token);
			ret_freegc (AUTH_FAILED);
		}
		*no_dispatch = TRUE;

		call_stat = svc_sendreply(rqst->rq_xprt, xdr_rpc_gss_init_res,
					  (caddr_t)&gr);

		gss_release_buffer(&min_stat, &gr.gr_token);
		gss_release_buffer(&min_stat, &gd->checksum);
		if (!call_stat)
			ret_freegc (AUTH_FAILED);

		if (gr.gr_major == GSS_S_COMPLETE)
			gd->established = TRUE;

		break;

	case RPCSEC_GSS_DATA:
		if (!svcauth_gss_validate(rqst, gd, msg))
			ret_freegc (RPCSEC_GSS_CREDPROBLEM);

		if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
 			ret_freegc (AUTH_FAILED);
		break;

	case RPCSEC_GSS_DESTROY:
		if (rqst->rq_proc != NULLPROC)
			ret_freegc (AUTH_FAILED);		/* XXX ? */

		if (!svcauth_gss_validate(rqst, gd, msg))
			ret_freegc (RPCSEC_GSS_CREDPROBLEM);

		if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
			ret_freegc (AUTH_FAILED);

		*no_dispatch = TRUE;

		call_stat = svc_sendreply(rqst->rq_xprt,
					  xdr_void, (caddr_t)NULL);

		log_debug("sendreply in destroy: %d", call_stat);

		if (!svcauth_gss_release_cred())
			ret_freegc (AUTH_FAILED);

		SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);
		rqst->rq_xprt->xp_auth = &svc_auth_none;

		break;

	default:
		ret_freegc (AUTH_REJECTEDCRED);
		break;
	}
	retstat = AUTH_OK;
freegc:
	xdr_free(xdr_rpc_gss_cred, gc);
	log_debug("returning %d from svcauth_gss()", retstat);
	return (retstat);
}
Exemplo n.º 5
0
bool_t
__rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
    XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
{
	XDR			 tmpxdrs;
	char			 credbuf[MAX_AUTH_BYTES];
	char			 tmpheader[MAX_AUTH_BYTES];
	struct opaque_auth	 creds, verf;
	struct rpc_gss_data	*gd;
	gss_buffer_desc		 rpcbuf, checksum;
	OM_uint32		 maj_stat, min_stat;
	bool_t			 xdr_stat;
	
	log_debug("in rpc_gss_wrap()");
	
	gd = AUTH_PRIVATE(auth);

	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
		gd->gd_cred.gc_seq++;
	
	/*
	 * We need to encode our creds and then put the header and
	 * creds together in a buffer so that we can create a checksum
	 * for the verf.
	 */
	xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
	if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) {
		XDR_DESTROY(&tmpxdrs);
		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
		return (FALSE);
	}
	creds.oa_flavor = RPCSEC_GSS;
	creds.oa_base = credbuf;
	creds.oa_length = XDR_GETPOS(&tmpxdrs);
	XDR_DESTROY(&tmpxdrs);
	
	xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE);
	if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) ||
	    !xdr_opaque_auth(&tmpxdrs, &creds)) {
		XDR_DESTROY(&tmpxdrs);
		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
		return (FALSE);
	}
	headerlen = XDR_GETPOS(&tmpxdrs);
	XDR_DESTROY(&tmpxdrs);
		
	if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
		return (FALSE);
	}
	
	if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
	    gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
		if (!xdr_opaque_auth(xdrs, &_null_auth)) {
			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
			return (FALSE);
		}
	} else {
		/*
		 * Checksum serialized RPC header, up to and including
		 * credential.
		 */
		rpcbuf.length = headerlen;
		rpcbuf.value = tmpheader;
	
		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
		    &rpcbuf, &checksum);

		if (maj_stat != GSS_S_COMPLETE) {
			log_status("gss_get_mic", gd->gd_mech,
			    maj_stat, min_stat);
			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
				rpc_gss_destroy_context(auth, TRUE);
			}
			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
			return (FALSE);
		}

		verf.oa_flavor = RPCSEC_GSS;
		verf.oa_base = checksum.value;
		verf.oa_length = checksum.length;

		xdr_stat = xdr_opaque_auth(xdrs, &verf);
		gss_release_buffer(&min_stat, &checksum);
		if (!xdr_stat) {
			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
			return (FALSE);
		}
	}
	
	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
	    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
		return (xdr_args(xdrs, args_ptr));
	}
	return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr,
		gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
		gd->gd_cred.gc_seq));
}
Exemplo n.º 6
0
enum auth_stat
Gssrpc__svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t * no_dispatch)
{
  enum auth_stat retstat;
  XDR xdrs;
  SVCAUTH *auth;
  struct svc_rpc_gss_data *gd;
  struct rpc_gss_cred *gc;
  struct rpc_gss_init_res gr;
  int call_stat, offset;
  OM_uint32 min_stat;
  gss_union_ctx_id_desc *gss_ctx_data;
  char ctx_str[64];

  /* Used to update the hashtable entries. 
   * These should not be used for purposes other than updating
   * hashtable entries. */
  bool_t *p_established = NULL;
  u_int *p_seqlast = NULL;
  uint32_t *p_seqmask = NULL;

  /* Initialize reply. */
  LogFullDebug(COMPONENT_RPCSEC_GSS, "Gssrpc__svcauth_gss called");

  /* Allocate and set up server auth handle. */
  if(rqst->rq_xprt->xp_auth == NULL || rqst->rq_xprt->xp_auth == &Svc_auth_none)
    {
      if((auth = (SVCAUTH *)Mem_Calloc(1, sizeof(*auth))) == NULL)
        {
          LogCrit(COMPONENT_RPCSEC_GSS, "svcauth_gss: out_of_memory");
          return (AUTH_FAILED);
        }
      if((gd = (struct svc_rpc_gss_data *)Mem_Calloc(1, sizeof(*gd))) == NULL)
        {
          LogCrit(COMPONENT_RPCSEC_GSS, "svcauth_gss: out_of_memory");
          Mem_Free(auth);
          return (AUTH_FAILED);
        }
      auth->svc_ah_ops = &Svc_auth_gss_ops;
      auth->svc_ah_private = (void *)gd;
      rqst->rq_xprt->xp_auth = auth;
    }
  else
    gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);

  /* Deserialize client credentials. */
  if(rqst->rq_cred.oa_length <= 0)
    return (AUTH_BADCRED);

  gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
  memset(gc, 0, sizeof(*gc));

  xdrmem_create(&xdrs, rqst->rq_cred.oa_base, rqst->rq_cred.oa_length, XDR_DECODE);

  if(!xdr_rpc_gss_cred(&xdrs, gc))
    {
      XDR_DESTROY(&xdrs);
      return (AUTH_BADCRED);
    }
  XDR_DESTROY(&xdrs);

  if(gc->gc_ctx.length != 0)
    gss_ctx_data = (gss_union_ctx_id_desc *)(gc->gc_ctx.value);
  else
    gss_ctx_data = NULL;

  if(isFullDebug(COMPONENT_RPCSEC_GSS))
    {
      sprint_ctx(ctx_str, (char *)gc->gc_ctx.value, gc->gc_ctx.length);
      LogFullDebug(COMPONENT_RPCSEC_GSS,
                   "Gssrpc__svcauth_gss gc_proc (%u) %s context %s",
                   gc->gc_proc, str_gc_proc(gc->gc_proc), ctx_str);
    }

  /* If we do not retrieve gss data from the cache, then this important
   * variables could not possibly be meaningful. */
  gd->seqlast = 0;
  gd->seqmask = 0;
  gd->established = 0;

  /** @todo Think about restoring the correct lines */
  //if( gd->established == 0 && gc->gc_proc == RPCSEC_GSS_DATA   )
  if(gc->gc_proc == RPCSEC_GSS_DATA || gc->gc_proc == RPCSEC_GSS_DESTROY)
    {
      if(isFullDebug(COMPONENT_RPCSEC_GSS))
        {
          LogFullDebug(COMPONENT_RPCSEC_GSS,
                       "Dump context hash table");
          Gss_ctx_Hash_Print();
        }
      
      LogFullDebug(COMPONENT_RPCSEC_GSS, "Getting gss data struct from hashtable.");
      
      /* Fill in svc_rpc_gss_data from cache */
      if(!Gss_ctx_Hash_Get(gss_ctx_data,
			   gd,
			   &p_established,
			   &p_seqlast,
			   &p_seqmask))
	{
          LogCrit(COMPONENT_RPCSEC_GSS, "Could not find gss context ");
          ret_freegc(AUTH_REJECTEDCRED);
        }
      else
        {
          /* If you 'mount -o sec=krb5i' you will have gc->gc_proc > RPCSEC_GSS_SVN_NONE, but the
           * negociation will have been made as if option was -o sec=krb5, the value of sec.svc has to be updated
           * id the stored gd that we got fromn the hash */
          if(gc->gc_svc != gd->sec.svc)
            gd->sec.svc = gc->gc_svc;
        }
    }

  if(isFullDebug(COMPONENT_RPCSEC_GSS))
    {
      char ctx_str_2[64];

      sprint_ctx(ctx_str_2, (unsigned char *)gd->ctx, sizeof(gss_ctx_data));
      sprint_ctx(ctx_str, (unsigned char *)gc->gc_ctx.value, gc->gc_ctx.length);

      LogFullDebug(COMPONENT_RPCSEC_GSS,
                   "Call to Gssrpc__svcauth_gss ----> Client=%s length=%lu (GD: established=%u ctx=%s) (RQ:sock=%u) (GC: Proc=%u Svc=%u ctx=%s)",
                   (char *)gd->cname.value,
                   (long unsigned int)gd->cname.length,
                   gd->established,
                   ctx_str_2,
                   rqst->rq_xprt->XP_SOCK,
                   gc->gc_proc,
                   gc->gc_svc,
                   ctx_str);
    }

  retstat = AUTH_FAILED;

  /* Check version. */
  if(gc->gc_v != RPCSEC_GSS_VERSION)
    {
      LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: bad GSS version.");
      ret_freegc(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)
    {
      LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: bad GSS service (krb5, krb5i, krb5p)");
      ret_freegc(AUTH_BADCRED);
    }

  /* Check sequence number. */
  if(gd->established)
    {
      /* Sequence should be less than the max sequence number */
      if(gc->gc_seq > MAXSEQ)
	{
	  LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: max sequence number exceeded.");
	  ret_freegc(RPCSEC_GSS_CTXPROBLEM);
	}

      /* Check the difference between the current sequence number 
       * and the last sequence number. */
      LogFullDebug(COMPONENT_RPCSEC_GSS, "seqlast: %d  seqnum: %d offset: %d seqwin: %d seqmask: %x",
		   gd->seqlast, gc->gc_seq, gd->seqlast - gc->gc_seq, gd->win, gd->seqmask);

      if((offset = gd->seqlast - gc->gc_seq) < 0)
        {
          gd->seqlast = gc->gc_seq;
          offset = 0 - offset;
          gd->seqmask <<= offset;
          offset = 0;
        }
      else if((unsigned int)offset >= gd->win
              || (gd->seqmask & (1 << (unsigned int)offset)))
        {
	  if ((unsigned int)offset >= gd->win)
	    LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: the current seqnum is lower"
		     " than seqlast by %d and out of the seq window of size %d.", offset, gd->win);
	  else
	    LogDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: the current seqnum has already been used.");

          *no_dispatch = TRUE;
          ret_freegc(RPCSEC_GSS_CTXPROBLEM);
        }
      gd->seq = gc->gc_seq;
      gd->seqmask |= (1 << offset);
    }

  if(gd->established)
    {
      rqst->rq_clntname = (char *)gd->client_name;
#ifndef _USE_TIRPC
      rqst->rq_svccred = (char *)gd->ctx;
#else
      rqst->rq_svcname = (char *)gd->ctx;
#endif
    }

  /* Handle RPCSEC_GSS control procedure. */
  switch (gc->gc_proc)
    {

    case RPCSEC_GSS_INIT:
      LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_INIT:");
    case RPCSEC_GSS_CONTINUE_INIT:
      LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_CONTINUE_INIT:");
      if(rqst->rq_proc != NULLPROC)
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: request proc != NULL during INIT request");
	  ret_freegc(AUTH_FAILED);        /* XXX ? */
	}

      if(!Svcauth_gss_acquire_cred())
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Can't acquire credentials from RPC request.");
	  ret_freegc(AUTH_FAILED);
	}

      if(!Svcauth_gss_accept_sec_context(rqst, &gr))
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Can't accept the security context.");
	  ret_freegc(AUTH_REJECTEDCRED);
	}

      if(!Svcauth_gss_nextverf(rqst, htonl(gr.gr_win)))
        {
          gss_release_buffer(&min_stat, &gr.gr_token);
          Mem_Free(gr.gr_ctx.value);
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Checksum verification failed");
          ret_freegc(AUTH_FAILED);
        }
      *no_dispatch = TRUE;

      if(isFullDebug(COMPONENT_RPCSEC_GSS))
        {
          sprint_ctx(ctx_str, (unsigned char *)gr.gr_ctx.value, gr.gr_ctx.length);
          LogFullDebug(COMPONENT_RPCSEC_GSS,
                       "Call to Gssrpc__svcauth_gss ----> Client=%s length=%lu (GD: established=%u) (RQ:sock=%u) (GR: maj=%u min=%u ctx=%s)",
                       (char *)gd->cname.value,
                       (long unsigned int)gd->cname.length,
                       gd->established,
                       rqst->rq_xprt->XP_SOCK,
                       gr.gr_major,
                       gr.gr_minor,
                       ctx_str);
        }
      call_stat = svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t) & gr);

      gss_release_buffer(&min_stat, &gr.gr_token);
      gss_release_buffer(&min_stat, &gd->checksum);
      Mem_Free(gr.gr_ctx.value);

      if(!call_stat)
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: svc_sendreply failed.");
	  ret_freegc(AUTH_FAILED);
	}

      if(gr.gr_major == GSS_S_COMPLETE)
        {
          gss_union_ctx_id_desc *gss_ctx_data2 = (gss_union_ctx_id_desc *)gd->ctx;

          gd->established = TRUE;

          /* Keep the gss context in a hash, gr.gr_ctx.value is used as key */
          (void) Gss_ctx_Hash_Set(gss_ctx_data2, gd);
        }

      break;

    case RPCSEC_GSS_DATA:
      LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_DATA:");
      if(!Svcauth_gss_validate(rqst, gd, msg))
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Couldn't validate request.");
	  ret_freegc(RPCSEC_GSS_CREDPROBLEM);
	}

      if(!Svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Checksum verification failed.");
	  ret_freegc(AUTH_FAILED);
	}

      /* Update a few important values in the hashtable entry */
      if ( p_established != NULL)
	*p_established = gd->established;
      if ( p_seqlast != NULL)
	*p_seqlast = gd->seqlast;
      if (p_seqmask != NULL)
	*p_seqmask = gd->seqmask;

      break;

    case RPCSEC_GSS_DESTROY:
      LogFullDebug(COMPONENT_RPCSEC_GSS, "Reached RPCSEC_GSS_DESTROY:");
      if(rqst->rq_proc != NULLPROC)
        ret_freegc(AUTH_FAILED);        /* XXX ? */

      if(!Svcauth_gss_validate(rqst, gd, msg))
        ret_freegc(RPCSEC_GSS_CREDPROBLEM);

      if(!Svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Checksum verification failed.");
	  ret_freegc(AUTH_FAILED);
	}

      *no_dispatch = TRUE;

      call_stat = svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_void, (caddr_t) NULL);

      if(!Gss_ctx_Hash_Del(gss_ctx_data))
        {
          LogCrit(COMPONENT_RPCSEC_GSS,
                  "Could not delete Gss Context from hash");
        }
      else
        LogFullDebug(COMPONENT_RPCSEC_GSS, "Gss_ctx_Hash_Del OK");

      if(!Svcauth_gss_release_cred())
	{
	  LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Failed to release credentials.");
	  ret_freegc(AUTH_FAILED);
	}

      if(rqst->rq_xprt->xp_auth)
        SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);
      rqst->rq_xprt->xp_auth = &Svc_auth_none;

      break;

    default:
      LogFullDebug(COMPONENT_RPCSEC_GSS, "BAD AUTH: Request is not INIT, INIT_CONTINUE, DATA, OR DESTROY.");
      ret_freegc(AUTH_REJECTEDCRED);
      break;
    }

  LogFullDebug(COMPONENT_RPCSEC_GSS,
               "Call to Gssrpc__svcauth_gss - OK ---> (RQ:sock=%u)",
               rqst->rq_xprt->XP_SOCK);

  retstat = AUTH_OK;
 freegc:
  if(retstat != AUTH_OK)
    LogCrit(COMPONENT_RPCSEC_GSS,
            "Call to Gssrpc__svcauth_gss - FAILED ---> (RQ:sock=%u)",
            rqst->rq_xprt->XP_SOCK);

  xdr_free((xdrproc_t)xdr_rpc_gss_cred, gc);
  return (retstat);
}