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); }
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); }
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); }
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); }
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)); }
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); }