Path: blob/main/crypto/krb5/src/lib/rpc/svc_auth_gss.c
39536 views
/* lib/rpc/svc_auth_gss.c */1/*2Copyright (c) 2000 The Regents of the University of Michigan.3All rights reserved.45Copyright (c) 2000 Dug Song <[email protected]>.6All rights reserved, all wrongs reversed.78Redistribution and use in source and binary forms, with or without9modification, are permitted provided that the following conditions10are met:11121. Redistributions of source code must retain the above copyright13notice, this list of conditions and the following disclaimer.142. Redistributions in binary form must reproduce the above copyright15notice, this list of conditions and the following disclaimer in the16documentation and/or other materials provided with the distribution.173. Neither the name of the University nor the names of its18contributors may be used to endorse or promote products derived19from this software without specific prior written permission.2021THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED22WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF23MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE24DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR26CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF27SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR28BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF29LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING30NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS31SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.3233Id: svc_auth_gss.c,v 1.28 2002/10/15 21:29:36 kwc Exp34*/3536#include "k5-platform.h"37#include <gssrpc/rpc.h>38#include <gssrpc/auth_gssapi.h>39#ifdef HAVE_HEIMDAL40#include <gssapi.h>41#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE42#else43#include <gssapi/gssapi.h>44#include <gssapi/gssapi_generic.h>45#endif4647#ifdef DEBUG_GSSAPI48int svc_debug_gss = DEBUG_GSSAPI;49#endif5051#ifdef SPKM5253#ifndef OID_EQ54#define g_OID_equal(o1,o2) \55(((o1)->length == (o2)->length) && \56((o1)->elements != 0) && ((o2)->elements != 0) && \57(memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))58#define OID_EQ 159#endif /* OID_EQ */6061extern const gss_OID_desc * const gss_mech_spkm3;6263#endif /* SPKM */6465extern SVCAUTH svc_auth_none;6667static auth_gssapi_log_badauth_func log_badauth = NULL;68static caddr_t log_badauth_data = NULL;69static auth_gssapi_log_badauth2_func log_badauth2 = NULL;70static caddr_t log_badauth2_data = NULL;71static auth_gssapi_log_badverf_func log_badverf = NULL;72static caddr_t log_badverf_data = NULL;73static auth_gssapi_log_miscerr_func log_miscerr = NULL;74static caddr_t log_miscerr_data = NULL;7576#define LOG_MISCERR(arg) if (log_miscerr) \77(*log_miscerr)(rqst, msg, arg, log_miscerr_data)7879static bool_t svcauth_gss_destroy(SVCAUTH *);80static bool_t svcauth_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);81static bool_t svcauth_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);8283static bool_t svcauth_gss_nextverf(struct svc_req *, u_int);8485struct svc_auth_ops svc_auth_gss_ops = {86svcauth_gss_wrap,87svcauth_gss_unwrap,88svcauth_gss_destroy89};9091struct svc_rpc_gss_data {92bool_t established; /* context established */93gss_cred_id_t cred; /* credential */94gss_ctx_id_t ctx; /* context id */95struct rpc_gss_sec sec; /* security triple */96gss_buffer_desc cname; /* GSS client name */97u_int seq; /* sequence number */98u_int win; /* sequence window */99u_int seqlast; /* last sequence number */100uint32_t seqmask; /* bitmask of seqnums */101gss_name_t client_name; /* unparsed name string */102gss_buffer_desc checksum; /* so we can free it */103};104105#define SVCAUTH_PRIVATE(auth) \106(*(struct svc_rpc_gss_data **)&(auth)->svc_ah_private)107108/* Global server credentials. */109static gss_name_t svcauth_gss_name = NULL;110111bool_t112svcauth_gss_set_svc_name(gss_name_t name)113{114OM_uint32 maj_stat, min_stat;115116log_debug("in svcauth_gss_set_svc_name()");117118if (svcauth_gss_name != NULL) {119maj_stat = gss_release_name(&min_stat, &svcauth_gss_name);120121if (maj_stat != GSS_S_COMPLETE) {122log_status("gss_release_name", maj_stat, min_stat);123return (FALSE);124}125svcauth_gss_name = NULL;126}127if (svcauth_gss_name == GSS_C_NO_NAME)128return (TRUE);129130maj_stat = gss_duplicate_name(&min_stat, name, &svcauth_gss_name);131132if (maj_stat != GSS_S_COMPLETE) {133log_status("gss_duplicate_name", maj_stat, min_stat);134return (FALSE);135}136137return (TRUE);138}139140static bool_t141svcauth_gss_acquire_cred(struct svc_rpc_gss_data *gd)142{143OM_uint32 maj_stat, min_stat;144145log_debug("in svcauth_gss_acquire_cred()");146147/* We don't need to acquire a credential if using the default name. */148if (svcauth_gss_name == GSS_C_NO_NAME)149return (TRUE);150151/* Only acquire a credential once per authentication. */152if (gd->cred != GSS_C_NO_CREDENTIAL)153return (TRUE);154155maj_stat = gss_acquire_cred(&min_stat, svcauth_gss_name, 0,156GSS_C_NULL_OID_SET, GSS_C_ACCEPT,157&gd->cred, NULL, NULL);158159if (maj_stat != GSS_S_COMPLETE) {160log_status("gss_acquire_cred", maj_stat, min_stat);161return (FALSE);162}163return (TRUE);164}165166/* Invoke log_badauth callbacks for an authentication failure. */167static void168badauth(OM_uint32 maj, OM_uint32 minor, SVCXPRT *xprt)169{170if (log_badauth != NULL)171(*log_badauth)(maj, minor, &xprt->xp_raddr, log_badauth_data);172if (log_badauth2 != NULL)173(*log_badauth2)(maj, minor, xprt, log_badauth2_data);174}175176static bool_t177svcauth_gss_accept_sec_context(struct svc_req *rqst,178struct rpc_gss_init_res *gr)179{180struct svc_rpc_gss_data *gd;181struct rpc_gss_cred *gc;182gss_buffer_desc recv_tok, seqbuf;183gss_OID mech;184OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq;185186log_debug("in svcauth_gss_accept_context()");187188gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);189gc = (struct rpc_gss_cred *)rqst->rq_clntcred;190memset(gr, 0, sizeof(*gr));191192/* Deserialize arguments. */193memset(&recv_tok, 0, sizeof(recv_tok));194195if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args,196(caddr_t)&recv_tok))197return (FALSE);198199gr->gr_major = gss_accept_sec_context(&gr->gr_minor,200&gd->ctx,201gd->cred,202&recv_tok,203GSS_C_NO_CHANNEL_BINDINGS,204&gd->client_name,205&mech,206&gr->gr_token,207&ret_flags,208NULL,209NULL);210211svc_freeargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args,212(caddr_t)&recv_tok);213214log_status("accept_sec_context", gr->gr_major, gr->gr_minor);215if (gr->gr_major != GSS_S_COMPLETE &&216gr->gr_major != GSS_S_CONTINUE_NEEDED) {217badauth(gr->gr_major, gr->gr_minor, rqst->rq_xprt);218gd->ctx = GSS_C_NO_CONTEXT;219goto errout;220}221gr->gr_ctx.value = "xxxx";222gr->gr_ctx.length = 4;223224/* gr->gr_win = 0x00000005; ANDROS: for debugging linux kernel version... */225gr->gr_win = sizeof(gd->seqmask) * 8;226227/* Save client info. */228gd->sec.mech = mech;229gd->sec.qop = GSS_C_QOP_DEFAULT;230gd->sec.svc = gc->gc_svc;231gd->seq = gc->gc_seq;232gd->win = gr->gr_win;233234if (gr->gr_major == GSS_S_COMPLETE) {235#ifdef SPKM236/* spkm3: no src_name (anonymous) */237if(!g_OID_equal(gss_mech_spkm3, mech)) {238#endif239maj_stat = gss_display_name(&min_stat, gd->client_name,240&gd->cname, &gd->sec.mech);241#ifdef SPKM242}243#endif244if (maj_stat != GSS_S_COMPLETE) {245log_status("display_name", maj_stat, min_stat);246goto errout;247}248#ifdef DEBUG249#ifdef HAVE_HEIMDAL250log_debug("accepted context for %.*s with "251"<mech {}, qop %d, svc %d>",252gd->cname.length, (char *)gd->cname.value,253gd->sec.qop, gd->sec.svc);254#else255{256gss_buffer_desc mechname;257258gss_oid_to_str(&min_stat, mech, &mechname);259260log_debug("accepted context for %.*s with "261"<mech %.*s, qop %d, svc %d>",262gd->cname.length, (char *)gd->cname.value,263mechname.length, (char *)mechname.value,264gd->sec.qop, gd->sec.svc);265266gss_release_buffer(&min_stat, &mechname);267}268#endif269#endif /* DEBUG */270seq = htonl(gr->gr_win);271seqbuf.value = &seq;272seqbuf.length = sizeof(seq);273274gss_release_buffer(&min_stat, &gd->checksum);275maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT,276&seqbuf, &gd->checksum);277278if (maj_stat != GSS_S_COMPLETE) {279goto errout;280}281282283rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;284rqst->rq_xprt->xp_verf.oa_base = gd->checksum.value;285rqst->rq_xprt->xp_verf.oa_length = gd->checksum.length;286}287return (TRUE);288errout:289gss_release_buffer(&min_stat, &gr->gr_token);290return (FALSE);291}292293static bool_t294svcauth_gss_validate(struct svc_req *rqst, struct svc_rpc_gss_data *gd, struct rpc_msg *msg)295{296struct opaque_auth *oa;297gss_buffer_desc rpcbuf, checksum;298OM_uint32 maj_stat, min_stat, qop_state;299u_char rpchdr[32 + MAX_AUTH_BYTES];300int32_t *buf;301302log_debug("in svcauth_gss_validate()");303304memset(rpchdr, 0, sizeof(rpchdr));305306/* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */307oa = &msg->rm_call.cb_cred;308if (oa->oa_length > MAX_AUTH_BYTES)309return (FALSE);310311/* 8 XDR units from the IXDR macro calls. */312if (sizeof(rpchdr) < (8 * BYTES_PER_XDR_UNIT +313RNDUP(oa->oa_length)))314return (FALSE);315316buf = (int32_t *)(void *)rpchdr;317318/* Write the 32 first bytes of the header. */319IXDR_PUT_LONG(buf, msg->rm_xid);320IXDR_PUT_ENUM(buf, msg->rm_direction);321IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);322IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);323IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);324IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);325IXDR_PUT_ENUM(buf, oa->oa_flavor);326IXDR_PUT_LONG(buf, oa->oa_length);327328if (oa->oa_length) {329memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);330buf += RNDUP(oa->oa_length) / sizeof(int32_t);331}332rpcbuf.value = rpchdr;333rpcbuf.length = (u_char *)buf - rpchdr;334335checksum.value = msg->rm_call.cb_verf.oa_base;336checksum.length = msg->rm_call.cb_verf.oa_length;337338maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum,339&qop_state);340341if (maj_stat != GSS_S_COMPLETE) {342log_status("gss_verify_mic", maj_stat, min_stat);343if (log_badverf != NULL)344(*log_badverf)(gd->client_name,345svcauth_gss_name,346rqst, msg, log_badverf_data);347return (FALSE);348}349return (TRUE);350}351352static bool_t353svcauth_gss_nextverf(struct svc_req *rqst, u_int num)354{355struct svc_rpc_gss_data *gd;356gss_buffer_desc signbuf;357OM_uint32 maj_stat, min_stat;358359log_debug("in svcauth_gss_nextverf()");360361if (rqst->rq_xprt->xp_auth == NULL)362return (FALSE);363364gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);365366gss_release_buffer(&min_stat, &gd->checksum);367368signbuf.value = #369signbuf.length = sizeof(num);370371maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,372&signbuf, &gd->checksum);373374if (maj_stat != GSS_S_COMPLETE) {375log_status("gss_get_mic", maj_stat, min_stat);376return (FALSE);377}378rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;379rqst->rq_xprt->xp_verf.oa_base = (caddr_t)gd->checksum.value;380rqst->rq_xprt->xp_verf.oa_length = (u_int)gd->checksum.length;381382return (TRUE);383}384385enum auth_stat386gssrpc__svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg,387bool_t *no_dispatch)388{389enum auth_stat retstat;390XDR xdrs;391SVCAUTH *auth;392struct svc_rpc_gss_data *gd;393struct rpc_gss_cred *gc;394struct rpc_gss_init_res gr;395int call_stat, offset;396OM_uint32 min_stat;397398log_debug("in svcauth_gss()");399400/* Initialize reply. */401rqst->rq_xprt->xp_verf = gssrpc__null_auth;402403/* Allocate and set up server auth handle. */404if (rqst->rq_xprt->xp_auth == NULL ||405rqst->rq_xprt->xp_auth == &svc_auth_none) {406if ((auth = calloc(sizeof(*auth), 1)) == NULL) {407fprintf(stderr, "svcauth_gss: out_of_memory\n");408return (AUTH_FAILED);409}410if ((gd = calloc(sizeof(*gd), 1)) == NULL) {411fprintf(stderr, "svcauth_gss: out_of_memory\n");412return (AUTH_FAILED);413}414auth->svc_ah_ops = &svc_auth_gss_ops;415SVCAUTH_PRIVATE(auth) = gd;416rqst->rq_xprt->xp_auth = auth;417}418else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);419420log_debug("xp_auth=%p, gd=%p", rqst->rq_xprt->xp_auth, gd);421422/* Deserialize client credentials. */423if (rqst->rq_cred.oa_length <= 0)424return (AUTH_BADCRED);425426gc = (struct rpc_gss_cred *)rqst->rq_clntcred;427memset(gc, 0, sizeof(*gc));428429log_debug("calling xdrmem_create()");430log_debug("oa_base=%p, oa_length=%u", rqst->rq_cred.oa_base,431rqst->rq_cred.oa_length);432xdrmem_create(&xdrs, rqst->rq_cred.oa_base,433rqst->rq_cred.oa_length, XDR_DECODE);434log_debug("xdrmem_create() returned");435436if (!xdr_rpc_gss_cred(&xdrs, gc)) {437log_debug("xdr_rpc_gss_cred() failed");438XDR_DESTROY(&xdrs);439return (AUTH_BADCRED);440}441XDR_DESTROY(&xdrs);442443retstat = AUTH_FAILED;444445#define ret_freegc(code) do { retstat = code; goto freegc; } while (0)446447/* Check version. */448if (gc->gc_v != RPCSEC_GSS_VERSION)449ret_freegc (AUTH_BADCRED);450451/* Check RPCSEC_GSS service. */452if (gc->gc_svc != RPCSEC_GSS_SVC_NONE &&453gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY &&454gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY)455ret_freegc (AUTH_BADCRED);456457/* Check sequence number. */458if (gd->established) {459if (gc->gc_seq > MAXSEQ)460ret_freegc (RPCSEC_GSS_CTXPROBLEM);461462if ((offset = gd->seqlast - gc->gc_seq) < 0) {463gd->seqlast = gc->gc_seq;464offset = 0 - offset;465gd->seqmask <<= offset;466offset = 0;467} else if ((u_int)offset >= gd->win ||468(gd->seqmask & (1 << offset))) {469*no_dispatch = 1;470ret_freegc (RPCSEC_GSS_CTXPROBLEM);471}472gd->seq = gc->gc_seq;473gd->seqmask |= (1 << offset);474}475476if (gd->established) {477rqst->rq_clntname = (char *)gd->client_name;478rqst->rq_svccred = (char *)gd->ctx;479}480481/* Handle RPCSEC_GSS control procedure. */482switch (gc->gc_proc) {483484case RPCSEC_GSS_INIT:485case RPCSEC_GSS_CONTINUE_INIT:486if (rqst->rq_proc != NULLPROC)487ret_freegc (AUTH_FAILED); /* XXX ? */488489if (!svcauth_gss_acquire_cred(gd))490ret_freegc (AUTH_FAILED);491492if (!svcauth_gss_accept_sec_context(rqst, &gr))493ret_freegc (AUTH_REJECTEDCRED);494495if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win))) {496gss_release_buffer(&min_stat, &gr.gr_token);497ret_freegc (AUTH_FAILED);498}499*no_dispatch = TRUE;500501call_stat = svc_sendreply(rqst->rq_xprt,502(xdrproc_t)xdr_rpc_gss_init_res,503(caddr_t)&gr);504505gss_release_buffer(&min_stat, &gr.gr_token);506gss_release_buffer(&min_stat, &gd->checksum);507if (!call_stat)508ret_freegc (AUTH_FAILED);509510if (gr.gr_major == GSS_S_COMPLETE)511gd->established = TRUE;512513break;514515case RPCSEC_GSS_DATA:516if (!svcauth_gss_validate(rqst, gd, msg))517ret_freegc (RPCSEC_GSS_CREDPROBLEM);518519if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))520ret_freegc (AUTH_FAILED);521break;522523case RPCSEC_GSS_DESTROY:524if (rqst->rq_proc != NULLPROC)525ret_freegc (AUTH_FAILED); /* XXX ? */526527if (!svcauth_gss_validate(rqst, gd, msg))528ret_freegc (RPCSEC_GSS_CREDPROBLEM);529530if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))531ret_freegc (AUTH_FAILED);532533*no_dispatch = TRUE;534535call_stat = svc_sendreply(rqst->rq_xprt,536xdr_void, (caddr_t)NULL);537538log_debug("sendreply in destroy: %d", call_stat);539540SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);541rqst->rq_xprt->xp_auth = &svc_auth_none;542543break;544545default:546ret_freegc (AUTH_REJECTEDCRED);547break;548}549retstat = AUTH_OK;550freegc:551xdr_free((xdrproc_t)xdr_rpc_gss_cred, gc);552log_debug("returning %d from svcauth_gss()", retstat);553return (retstat);554}555556static bool_t557svcauth_gss_destroy(SVCAUTH *auth)558{559struct svc_rpc_gss_data *gd;560OM_uint32 min_stat;561562log_debug("in svcauth_gss_destroy()");563564gd = SVCAUTH_PRIVATE(auth);565566gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER);567gss_release_cred(&min_stat, &gd->cred);568gss_release_buffer(&min_stat, &gd->cname);569gss_release_buffer(&min_stat, &gd->checksum);570571if (gd->client_name)572gss_release_name(&min_stat, &gd->client_name);573574mem_free(gd, sizeof(*gd));575mem_free(auth, sizeof(*auth));576577return (TRUE);578}579580static bool_t581svcauth_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)582{583struct svc_rpc_gss_data *gd;584585log_debug("in svcauth_gss_wrap()");586587gd = SVCAUTH_PRIVATE(auth);588589if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {590return ((*xdr_func)(xdrs, xdr_ptr));591}592return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,593gd->ctx, gd->sec.qop,594gd->sec.svc, gd->seq));595}596597static bool_t598svcauth_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)599{600struct svc_rpc_gss_data *gd;601602log_debug("in svcauth_gss_unwrap()");603604gd = SVCAUTH_PRIVATE(auth);605606if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {607return ((*xdr_func)(xdrs, xdr_ptr));608}609return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,610gd->ctx, gd->sec.qop,611gd->sec.svc, gd->seq));612}613614char *615svcauth_gss_get_principal(SVCAUTH *auth)616{617struct svc_rpc_gss_data *gd;618char *pname;619620gd = SVCAUTH_PRIVATE(auth);621622if (gd->cname.length == 0 || gd->cname.length >= SIZE_MAX)623return (NULL);624625if ((pname = malloc(gd->cname.length + 1)) == NULL)626return (NULL);627628memcpy(pname, gd->cname.value, gd->cname.length);629pname[gd->cname.length] = '\0';630631return (pname);632}633634/*635* Function: svcauth_gss_set_log_badauth_func636*637* Purpose: sets the logging function called when an invalid RPC call638* arrives639*640* See functional specifications.641*/642void svcauth_gss_set_log_badauth_func(643auth_gssapi_log_badauth_func func,644caddr_t data)645{646log_badauth = func;647log_badauth_data = data;648}649650void651svcauth_gss_set_log_badauth2_func(auth_gssapi_log_badauth2_func func,652caddr_t data)653{654log_badauth2 = func;655log_badauth2_data = data;656}657658/*659* Function: svcauth_gss_set_log_badverf_func660*661* Purpose: sets the logging function called when an invalid RPC call662* arrives663*664* See functional specifications.665*/666void svcauth_gss_set_log_badverf_func(667auth_gssapi_log_badverf_func func,668caddr_t data)669{670log_badverf = func;671log_badverf_data = data;672}673674/*675* Function: svcauth_gss_set_log_miscerr_func676*677* Purpose: sets the logging function called when a miscellaneous678* AUTH_GSSAPI error occurs679*680* See functional specifications.681*/682void svcauth_gss_set_log_miscerr_func(683auth_gssapi_log_miscerr_func func,684caddr_t data)685{686log_miscerr = func;687log_miscerr_data = data;688}689690691