Path: blob/main/crypto/krb5/src/lib/rpc/auth_gss.c
39536 views
/* lib/rpc/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: auth_gss.c,v 1.35 2002/10/15 21:25:25 kwc Exp34*/3536/* RPCSEC_GSS client routines. */3738#include <stdio.h>39#include <stdlib.h>40#include <unistd.h>41#include <string.h>42#include <errno.h>43#include <gssrpc/types.h>44#include <gssrpc/xdr.h>45#include <gssrpc/auth.h>46#include <gssrpc/auth_gss.h>47#include <gssrpc/clnt.h>48#include <netinet/in.h>49#ifdef HAVE_HEIMDAL50#include <gssapi.h>51#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE52#else53#include <gssapi/gssapi.h>54#include <gssapi/gssapi_generic.h>55#endif5657#ifdef DEBUG_GSSAPI58int auth_debug_gss = DEBUG_GSSAPI;59int misc_debug_gss = DEBUG_GSSAPI;60#endif6162static void authgss_nextverf(AUTH *);63static bool_t authgss_marshal(AUTH *, XDR *);64static bool_t authgss_refresh(AUTH *, struct rpc_msg *);65static bool_t authgss_validate(AUTH *, struct opaque_auth *);66static void authgss_destroy(AUTH *);67static void authgss_destroy_context(AUTH *);68static bool_t authgss_wrap(AUTH *, XDR *, xdrproc_t, caddr_t);69static bool_t authgss_unwrap(AUTH *, XDR *, xdrproc_t, caddr_t);707172/*73* from mit-krb5-1.2.1 mechglue/mglueP.h:74* Array of context IDs typed by mechanism OID75*/76typedef struct gss_union_ctx_id_t {77gss_OID mech_type;78gss_ctx_id_t internal_ctx_id;79} gss_union_ctx_id_desc, *gss_union_ctx_id_t;8081static struct auth_ops authgss_ops = {82authgss_nextverf,83authgss_marshal,84authgss_validate,85authgss_refresh,86authgss_destroy,87authgss_wrap,88authgss_unwrap89};9091#ifdef DEBUG9293/* useful as i add more mechanisms */94void95print_rpc_gss_sec(struct rpc_gss_sec *ptr)96{97int i;98char *p;99100log_debug("rpc_gss_sec:");101if(ptr->mech == NULL)102log_debug("NULL gss_OID mech");103else {104fprintf(stderr, " mechanism_OID: {");105p = (char *)ptr->mech->elements;106for (i=0; i < ptr->mech->length; i++)107/* First byte of OIDs encoded to save a byte */108if (i == 0) {109int first, second;110if (*p < 40) {111first = 0;112second = *p;113}114else if (40 <= *p && *p < 80) {115first = 1;116second = *p - 40;117}118else if (80 <= *p && *p < 127) {119first = 2;120second = *p - 80;121}122else {123/* Invalid value! */124first = -1;125second = -1;126}127fprintf(stderr, " %u %u", first, second);128p++;129}130else {131fprintf(stderr, " %u", (unsigned char)*p++);132}133fprintf(stderr, " }\n");134}135fprintf(stderr, " qop: %d\n", ptr->qop);136fprintf(stderr, " service: %d\n", ptr->svc);137fprintf(stderr, " cred: %p\n", ptr->cred);138fprintf(stderr, " req_flags: 0x%08x", ptr->req_flags);139}140#endif /*DEBUG*/141142struct rpc_gss_data {143bool_t established; /* context established */144bool_t inprogress;145gss_buffer_desc gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier146* to process at end of context negotiation*/147CLIENT *clnt; /* client handle */148gss_name_t name; /* service name */149struct rpc_gss_sec sec; /* security tuple */150gss_ctx_id_t ctx; /* context id */151struct rpc_gss_cred gc; /* client credentials */152uint32_t win; /* sequence window */153};154155#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)156157static struct timeval AUTH_TIMEOUT = { 25, 0 };158159AUTH *160authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)161{162AUTH *auth, *save_auth;163struct rpc_gss_data *gd;164OM_uint32 min_stat = 0;165166log_debug("in authgss_create()");167168memset(&rpc_createerr, 0, sizeof(rpc_createerr));169170if ((auth = calloc(sizeof(*auth), 1)) == NULL) {171rpc_createerr.cf_stat = RPC_SYSTEMERROR;172rpc_createerr.cf_error.re_errno = ENOMEM;173return (NULL);174}175if ((gd = calloc(sizeof(*gd), 1)) == NULL) {176rpc_createerr.cf_stat = RPC_SYSTEMERROR;177rpc_createerr.cf_error.re_errno = ENOMEM;178free(auth);179return (NULL);180}181if (name != GSS_C_NO_NAME) {182if (gss_duplicate_name(&min_stat, name, &gd->name)183!= GSS_S_COMPLETE) {184rpc_createerr.cf_stat = RPC_SYSTEMERROR;185rpc_createerr.cf_error.re_errno = ENOMEM;186free(auth);187free(gd);188return (NULL);189}190}191else192gd->name = name;193194gd->clnt = clnt;195gd->ctx = GSS_C_NO_CONTEXT;196gd->sec = *sec;197198gd->gc.gc_v = RPCSEC_GSS_VERSION;199gd->gc.gc_proc = RPCSEC_GSS_INIT;200gd->gc.gc_svc = gd->sec.svc;201202auth->ah_ops = &authgss_ops;203auth->ah_private = (caddr_t)gd;204205save_auth = clnt->cl_auth;206clnt->cl_auth = auth;207208if (!authgss_refresh(auth, NULL))209auth = NULL;210211clnt->cl_auth = save_auth;212213log_debug("authgss_create returning auth 0x%08x", auth);214return (auth);215}216217AUTH *218authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)219{220AUTH *auth;221OM_uint32 maj_stat = 0, min_stat = 0;222gss_buffer_desc sname;223gss_name_t name;224225log_debug("in authgss_create_default()");226227228sname.value = service;229sname.length = strlen(service);230231maj_stat = gss_import_name(&min_stat, &sname,232(gss_OID)gss_nt_service_name,233&name);234235if (maj_stat != GSS_S_COMPLETE) {236log_status("gss_import_name", maj_stat, min_stat);237rpc_createerr.cf_stat = RPC_AUTHERROR;238return (NULL);239}240241auth = authgss_create(clnt, name, sec);242243if (name != GSS_C_NO_NAME)244gss_release_name(&min_stat, &name);245246log_debug("authgss_create_default returning auth 0x%08x", auth);247return (auth);248}249250bool_t251authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)252{253struct rpc_gss_data *gd;254255log_debug("in authgss_get_private_data()");256257if (!auth || !pd)258return (FALSE);259260gd = AUTH_PRIVATE(auth);261262if (!gd || !gd->established)263return (FALSE);264265pd->pd_ctx = gd->ctx;266pd->pd_ctx_hndl = gd->gc.gc_ctx;267pd->pd_seq_win = gd->win;268269return (TRUE);270}271272static void273authgss_nextverf(AUTH *auth)274{275log_debug("in authgss_nextverf()\n");276/* no action necessary */277}278279static bool_t280authgss_marshal(AUTH *auth, XDR *xdrs)281{282XDR tmpxdrs;283char tmp[MAX_AUTH_BYTES];284struct rpc_gss_data *gd;285gss_buffer_desc rpcbuf, checksum;286OM_uint32 maj_stat, min_stat;287bool_t xdr_stat;288289log_debug("in authgss_marshal()");290291gd = AUTH_PRIVATE(auth);292293if (gd->established)294gd->gc.gc_seq++;295296xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);297298if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {299XDR_DESTROY(&tmpxdrs);300return (FALSE);301}302auth->ah_cred.oa_flavor = RPCSEC_GSS;303auth->ah_cred.oa_base = tmp;304auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);305306XDR_DESTROY(&tmpxdrs);307308if (!xdr_opaque_auth(xdrs, &auth->ah_cred))309return (FALSE);310311if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||312gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {313return (xdr_opaque_auth(xdrs, &gssrpc__null_auth));314}315/* Checksum serialized RPC header, up to and including credential. */316rpcbuf.length = XDR_GETPOS(xdrs);317XDR_SETPOS(xdrs, 0);318rpcbuf.value = XDR_INLINE(xdrs, (int)rpcbuf.length);319320maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,321&rpcbuf, &checksum);322323if (maj_stat != GSS_S_COMPLETE) {324log_status("gss_get_mic", maj_stat, min_stat);325if (maj_stat == GSS_S_CONTEXT_EXPIRED) {326gd->established = FALSE;327authgss_destroy_context(auth);328}329return (FALSE);330}331auth->ah_verf.oa_flavor = RPCSEC_GSS;332auth->ah_verf.oa_base = checksum.value;333auth->ah_verf.oa_length = checksum.length;334335xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);336gss_release_buffer(&min_stat, &checksum);337338return (xdr_stat);339}340341static bool_t342authgss_validate(AUTH *auth, struct opaque_auth *verf)343{344struct rpc_gss_data *gd;345uint32_t num;346gss_qop_t qop_state;347gss_buffer_desc signbuf, checksum;348OM_uint32 maj_stat, min_stat;349350log_debug("in authgss_validate()");351352gd = AUTH_PRIVATE(auth);353354if (gd->established == FALSE) {355/* would like to do this only on NULL rpc - gc->established is good enough.356* save the on the wire verifier to validate last INIT phase packet357* after decode if the major status is GSS_S_COMPLETE358*/359if ((gd->gc_wire_verf.value = mem_alloc(verf->oa_length)) == NULL) {360fprintf(stderr, "gss_validate: out of memory\n");361return (FALSE);362}363memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);364gd->gc_wire_verf.length = verf->oa_length;365return (TRUE);366}367368if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||369gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {370num = htonl(gd->win);371}372else num = htonl(gd->gc.gc_seq);373374signbuf.value = #375signbuf.length = sizeof(num);376377checksum.value = verf->oa_base;378checksum.length = verf->oa_length;379380maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf,381&checksum, &qop_state);382if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {383log_status("gss_verify_mic", maj_stat, min_stat);384if (maj_stat == GSS_S_CONTEXT_EXPIRED) {385gd->established = FALSE;386authgss_destroy_context(auth);387}388return (FALSE);389}390return (TRUE);391}392393static bool_t394authgss_refresh(AUTH *auth, struct rpc_msg *msg)395{396struct rpc_gss_data *gd;397struct rpc_gss_init_res gr;398gss_buffer_desc *recv_tokenp, send_token;399OM_uint32 maj_stat, min_stat, call_stat, ret_flags;400401log_debug("in authgss_refresh()");402403gd = AUTH_PRIVATE(auth);404405if (gd->established || gd->inprogress)406return (TRUE);407408/* GSS context establishment loop. */409memset(&gr, 0, sizeof(gr));410recv_tokenp = GSS_C_NO_BUFFER;411412#ifdef DEBUG413print_rpc_gss_sec(&gd->sec);414#endif /*DEBUG*/415416for (;;) {417gd->inprogress = TRUE;418maj_stat = gss_init_sec_context(&min_stat,419gd->sec.cred,420&gd->ctx,421gd->name,422gd->sec.mech,423gd->sec.req_flags,4240, /* time req */425GSS_C_NO_CHANNEL_BINDINGS,426recv_tokenp,427NULL, /* used mech */428&send_token,429&ret_flags,430NULL); /* time rec */431432log_status("gss_init_sec_context", maj_stat, min_stat);433if (recv_tokenp != GSS_C_NO_BUFFER) {434free(gr.gr_token.value);435gr.gr_token.value = NULL;436recv_tokenp = GSS_C_NO_BUFFER;437}438if (maj_stat != GSS_S_COMPLETE &&439maj_stat != GSS_S_CONTINUE_NEEDED) {440log_status("gss_init_sec_context (error)", maj_stat, min_stat);441break;442}443if (send_token.length != 0) {444memset(&gr, 0, sizeof(gr));445446call_stat = clnt_call(gd->clnt, NULLPROC,447(xdrproc_t)xdr_rpc_gss_init_args,448&send_token,449(xdrproc_t)xdr_rpc_gss_init_res,450(caddr_t)&gr, AUTH_TIMEOUT);451452gss_release_buffer(&min_stat, &send_token);453454log_debug("authgss_refresh: call_stat=%d", call_stat);455log_debug("%s", clnt_sperror(gd->clnt, "authgss_refresh"));456if (call_stat != RPC_SUCCESS ||457(gr.gr_major != GSS_S_COMPLETE &&458gr.gr_major != GSS_S_CONTINUE_NEEDED))459break;460461if (gr.gr_ctx.length != 0) {462free(gd->gc.gc_ctx.value);463gd->gc.gc_ctx = gr.gr_ctx;464}465if (gr.gr_token.length != 0) {466if (maj_stat != GSS_S_CONTINUE_NEEDED)467break;468recv_tokenp = &gr.gr_token;469}470gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;471}472473/* GSS_S_COMPLETE => check gss header verifier, usually checked in474* gss_validate475*/476if (maj_stat == GSS_S_COMPLETE) {477gss_buffer_desc bufin;478gss_buffer_desc bufout;479uint32_t seq;480gss_qop_t qop_state = 0;481482seq = htonl(gr.gr_win);483bufin.value = (u_char *)&seq;484bufin.length = sizeof(seq);485bufout.value = (u_char *)gd->gc_wire_verf.value;486bufout.length = gd->gc_wire_verf.length;487488log_debug("authgss_refresh: GSS_S_COMPLETE: calling verify_mic");489maj_stat = gss_verify_mic(&min_stat,gd->ctx,490&bufin, &bufout, &qop_state);491free(gd->gc_wire_verf.value);492gd->gc_wire_verf.length = 0;493gd->gc_wire_verf.value = NULL;494495if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {496log_status("gss_verify_mic", maj_stat, min_stat);497if (maj_stat == GSS_S_CONTEXT_EXPIRED) {498gd->established = FALSE;499authgss_destroy_context(auth);500}501return (FALSE);502}503gd->established = TRUE;504gd->inprogress = FALSE;505gd->gc.gc_proc = RPCSEC_GSS_DATA;506gd->gc.gc_seq = 0;507gd->win = gr.gr_win;508break;509}510}511log_status("authgss_refresh: at end of context negotiation", maj_stat, min_stat);512/* End context negotiation loop. */513if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {514log_debug("authgss_refresh: returning ERROR (gc_proc %d)", gd->gc.gc_proc);515free(gr.gr_token.value);516authgss_destroy(auth);517auth = NULL;518rpc_createerr.cf_stat = RPC_AUTHERROR;519520return (FALSE);521}522log_debug("authgss_refresh: returning SUCCESS");523return (TRUE);524}525526bool_t527authgss_service(AUTH *auth, int svc)528{529struct rpc_gss_data *gd;530531log_debug("in authgss_service()");532533if (!auth)534return(FALSE);535gd = AUTH_PRIVATE(auth);536if (!gd || !gd->established)537return (FALSE);538gd->sec.svc = svc;539gd->gc.gc_svc = svc;540return (TRUE);541}542543static void544authgss_destroy_context(AUTH *auth)545{546struct rpc_gss_data *gd;547OM_uint32 min_stat;548549log_debug("in authgss_destroy_context()");550551gd = AUTH_PRIVATE(auth);552553if (gd->gc.gc_ctx.length != 0) {554if (gd->established) {555gd->gc.gc_proc = RPCSEC_GSS_DESTROY;556(void)clnt_call(gd->clnt, NULLPROC, xdr_void, NULL,557xdr_void, NULL, AUTH_TIMEOUT);558log_debug("%s",559clnt_sperror(gd->clnt,560"authgss_destroy_context"));561}562free(gd->gc.gc_ctx.value);563/* XXX ANDROS check size of context - should be 8 */564memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));565}566if (gd->ctx != GSS_C_NO_CONTEXT) {567gss_delete_sec_context(&min_stat, &gd->ctx, NULL);568gd->ctx = GSS_C_NO_CONTEXT;569}570gd->established = FALSE;571572log_debug("finished authgss_destroy_context()");573}574575static void576authgss_destroy(AUTH *auth)577{578struct rpc_gss_data *gd;579OM_uint32 min_stat;580581log_debug("in authgss_destroy()");582583gd = AUTH_PRIVATE(auth);584585authgss_destroy_context(auth);586587if (gd->name != GSS_C_NO_NAME)588gss_release_name(&min_stat, &gd->name);589590free(gd);591free(auth);592}593594bool_t595authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)596{597struct rpc_gss_data *gd;598599log_debug("in authgss_wrap()");600601gd = AUTH_PRIVATE(auth);602603if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {604return ((*xdr_func)(xdrs, xdr_ptr));605}606return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,607gd->ctx, gd->sec.qop,608gd->sec.svc, gd->gc.gc_seq));609}610611bool_t612authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)613{614struct rpc_gss_data *gd;615616log_debug("in authgss_unwrap()");617618gd = AUTH_PRIVATE(auth);619620if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {621return ((*xdr_func)(xdrs, xdr_ptr));622}623return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,624gd->ctx, gd->sec.qop,625gd->sec.svc, gd->gc.gc_seq));626}627628629