diff options
Diffstat (limited to 'lib/librpcsec_gss/rpcsec_gss.c')
-rw-r--r-- | lib/librpcsec_gss/rpcsec_gss.c | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/lib/librpcsec_gss/rpcsec_gss.c b/lib/librpcsec_gss/rpcsec_gss.c new file mode 100644 index 0000000..6020881 --- /dev/null +++ b/lib/librpcsec_gss/rpcsec_gss.c @@ -0,0 +1,722 @@ +/*- + * Copyright (c) 2008 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + auth_gss.c + + RPCSEC_GSS client routines. + + Copyright (c) 2000 The Regents of the University of Michigan. + All rights reserved. + + Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. + All rights reserved, all wrongs reversed. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $ +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <netinet/in.h> +#include <rpc/rpc.h> +#include <rpc/rpcsec_gss.h> +#include "rpcsec_gss_int.h" + +static void rpc_gss_nextverf(AUTH*); +static bool_t rpc_gss_marshal(AUTH *, XDR *); +static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret); +static bool_t rpc_gss_refresh(AUTH *, void *); +static bool_t rpc_gss_validate(AUTH *, struct opaque_auth *); +static void rpc_gss_destroy(AUTH *); +static void rpc_gss_destroy_context(AUTH *, bool_t); + +static struct auth_ops rpc_gss_ops = { + rpc_gss_nextverf, + rpc_gss_marshal, + rpc_gss_validate, + rpc_gss_refresh, + rpc_gss_destroy +}; + +enum rpcsec_gss_state { + RPCSEC_GSS_START, + RPCSEC_GSS_CONTEXT, + RPCSEC_GSS_ESTABLISHED +}; + +struct rpc_gss_data { + rpc_gss_options_req_t gd_options; /* GSS context options */ + enum rpcsec_gss_state gd_state; /* connection state */ + gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE + * NULL RPC verfier to + * process at end of + * context negotiation */ + CLIENT *gd_clnt; /* client handle */ + gss_name_t gd_name; /* service name */ + gss_OID gd_mech; /* mechanism to use */ + gss_qop_t gd_qop; /* quality of protection */ + gss_ctx_id_t gd_ctx; /* context id */ + struct rpc_gss_cred gd_cred; /* client credentials */ + u_int gd_win; /* sequence window */ +}; + +#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private) + +static struct timeval AUTH_TIMEOUT = { 25, 0 }; + +AUTH * +rpc_gss_seccreate(CLIENT *clnt, const char *principal, + const char *mechanism, rpc_gss_service_t service, const char *qop, + rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) +{ + AUTH *auth, *save_auth; + rpc_gss_options_ret_t options; + gss_OID oid; + u_int qop_num; + struct rpc_gss_data *gd; + OM_uint32 maj_stat = 0, min_stat = 0; + gss_buffer_desc principal_desc; + + /* + * Bail out now if we don't know this mechanism. + */ + if (!rpc_gss_mech_to_oid(mechanism, &oid)) + return (NULL); + + if (qop) { + if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) + return (NULL); + } else { + qop_num = GSS_C_QOP_DEFAULT; + } + + /* + * If the caller doesn't want the options, point at local + * storage to simplify the code below. + */ + if (!options_ret) + options_ret = &options; + + /* + * Default service is integrity. + */ + if (service == rpc_gss_svc_default) + service = rpc_gss_svc_integrity; + + memset(options_ret, 0, sizeof(*options_ret)); + + log_debug("in rpc_gss_seccreate()"); + + memset(&rpc_createerr, 0, sizeof(rpc_createerr)); + + auth = mem_alloc(sizeof(*auth)); + if (auth == NULL) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = ENOMEM; + return (NULL); + } + gd = mem_alloc(sizeof(*gd)); + if (gd == NULL) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = ENOMEM; + free(auth); + return (NULL); + } + + auth->ah_ops = &rpc_gss_ops; + auth->ah_private = (caddr_t) gd; + auth->ah_cred.oa_flavor = RPCSEC_GSS; + + principal_desc.value = (void *)(intptr_t) principal; + principal_desc.length = strlen(principal); + maj_stat = gss_import_name(&min_stat, &principal_desc, + GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name); + if (maj_stat != GSS_S_COMPLETE) { + options_ret->major_status = maj_stat; + options_ret->minor_status = min_stat; + goto bad; + } + + if (options_req) { + gd->gd_options = *options_req; + } else { + gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG; + gd->gd_options.time_req = 0; + gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; + gd->gd_options.input_channel_bindings = NULL; + } + gd->gd_clnt = clnt; + gd->gd_ctx = GSS_C_NO_CONTEXT; + gd->gd_mech = oid; + gd->gd_qop = qop_num; + + gd->gd_cred.gc_version = RPCSEC_GSS_VERSION; + gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; + gd->gd_cred.gc_seq = 0; + gd->gd_cred.gc_svc = service; + + save_auth = clnt->cl_auth; + + clnt->cl_auth = auth; + if (!rpc_gss_init(auth, options_ret)) { + clnt->cl_auth = save_auth; + goto bad; + } + + clnt->cl_auth = save_auth; + + return (auth); + + bad: + AUTH_DESTROY(auth); + return (NULL); +} + +bool_t +rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop) +{ + struct rpc_gss_data *gd; + u_int qop_num; + const char *mechanism; + + gd = AUTH_PRIVATE(auth); + if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) { + return (FALSE); + } + + if (qop) { + if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) { + return (FALSE); + } + } else { + qop_num = GSS_C_QOP_DEFAULT; + } + + gd->gd_cred.gc_svc = service; + gd->gd_qop = qop_num; + return (TRUE); +} + +static void +rpc_gss_nextverf(__unused AUTH *auth) +{ + + /* not used */ +} + +static bool_t +rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs) +{ + + /* not used */ + return (FALSE); +} + +static bool_t +rpc_gss_validate(AUTH *auth, struct opaque_auth *verf) +{ + struct rpc_gss_data *gd; + gss_qop_t qop_state; + uint32_t num; + gss_buffer_desc signbuf, checksum; + OM_uint32 maj_stat, min_stat; + + log_debug("in rpc_gss_validate()"); + + gd = AUTH_PRIVATE(auth); + + if (gd->gd_state == RPCSEC_GSS_CONTEXT) { + /* + * Save the on the wire verifier to validate last INIT + * phase packet after decode if the major status is + * GSS_S_COMPLETE. + */ + if (gd->gd_verf.value) + xdr_free((xdrproc_t) xdr_gss_buffer_desc, + (char *) &gd->gd_verf); + gd->gd_verf.value = mem_alloc(verf->oa_length); + if (gd->gd_verf.value == NULL) { + fprintf(stderr, "gss_validate: out of memory\n"); + _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); + return (FALSE); + } + memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length); + gd->gd_verf.length = verf->oa_length; + return (TRUE); + } + + num = htonl(gd->gd_cred.gc_seq); + signbuf.value = # + signbuf.length = sizeof(num); + + checksum.value = verf->oa_base; + checksum.length = verf->oa_length; + + maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf, + &checksum, &qop_state); + if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) { + log_status("gss_verify_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); + } + return (TRUE); +} + +static bool_t +rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret) +{ + struct rpc_gss_data *gd; + struct rpc_gss_init_res gr; + gss_buffer_desc *recv_tokenp, recv_token, send_token; + OM_uint32 maj_stat, min_stat, call_stat; + const char *mech; + + log_debug("in rpc_gss_refresh()"); + + gd = AUTH_PRIVATE(auth); + + if (gd->gd_state != RPCSEC_GSS_START) + return (TRUE); + + /* GSS context establishment loop. */ + gd->gd_state = RPCSEC_GSS_CONTEXT; + gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; + gd->gd_cred.gc_seq = 0; + + memset(&recv_token, 0, sizeof(recv_token)); + memset(&gr, 0, sizeof(gr)); + recv_tokenp = GSS_C_NO_BUFFER; + + for (;;) { + maj_stat = gss_init_sec_context(&min_stat, + gd->gd_options.my_cred, + &gd->gd_ctx, + gd->gd_name, + gd->gd_mech, + gd->gd_options.req_flags, + gd->gd_options.time_req, + gd->gd_options.input_channel_bindings, + recv_tokenp, + &gd->gd_mech, /* used mech */ + &send_token, + &options_ret->ret_flags, + &options_ret->time_req); + + /* + * Free the token which we got from the server (if + * any). Remember that this was allocated by XDR, not + * GSS-API. + */ + if (recv_tokenp != GSS_C_NO_BUFFER) { + xdr_free((xdrproc_t) xdr_gss_buffer_desc, + (char *) &recv_token); + recv_tokenp = GSS_C_NO_BUFFER; + } + if (maj_stat != GSS_S_COMPLETE && + maj_stat != GSS_S_CONTINUE_NEEDED) { + log_status("gss_init_sec_context", gd->gd_mech, + maj_stat, min_stat); + options_ret->major_status = maj_stat; + options_ret->minor_status = min_stat; + break; + } + if (send_token.length != 0) { + memset(&gr, 0, sizeof(gr)); + + call_stat = clnt_call(gd->gd_clnt, NULLPROC, + (xdrproc_t)xdr_gss_buffer_desc, + &send_token, + (xdrproc_t)xdr_rpc_gss_init_res, + (caddr_t)&gr, AUTH_TIMEOUT); + + gss_release_buffer(&min_stat, &send_token); + + if (call_stat != RPC_SUCCESS) + break; + + if (gr.gr_major != GSS_S_COMPLETE && + gr.gr_major != GSS_S_CONTINUE_NEEDED) { + log_status("server reply", gd->gd_mech, + gr.gr_major, gr.gr_minor); + options_ret->major_status = gr.gr_major; + options_ret->minor_status = gr.gr_minor; + break; + } + + /* + * Save the server's gr_handle value, freeing + * what we have already (remember that this + * was allocated by XDR, not GSS-API). + */ + if (gr.gr_handle.length != 0) { + xdr_free((xdrproc_t) xdr_gss_buffer_desc, + (char *) &gd->gd_cred.gc_handle); + gd->gd_cred.gc_handle = gr.gr_handle; + } + + /* + * Save the server's token as well. + */ + if (gr.gr_token.length != 0) { + recv_token = gr.gr_token; + recv_tokenp = &recv_token; + } + + /* + * Since we have copied out all the bits of gr + * which XDR allocated for us, we don't need + * to free it. + */ + gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT; + } + + if (maj_stat == GSS_S_COMPLETE) { + gss_buffer_desc bufin; + u_int seq, qop_state = 0; + + /* + * gss header verifier, + * usually checked in gss_validate + */ + seq = htonl(gr.gr_win); + bufin.value = (unsigned char *)&seq; + bufin.length = sizeof(seq); + + maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, + &bufin, &gd->gd_verf, &qop_state); + + if (maj_stat != GSS_S_COMPLETE || + qop_state != gd->gd_qop) { + log_status("gss_verify_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); + options_ret->major_status = maj_stat; + options_ret->minor_status = min_stat; + return (FALSE); + } + + options_ret->major_status = GSS_S_COMPLETE; + options_ret->minor_status = 0; + options_ret->rpcsec_version = gd->gd_cred.gc_version; + options_ret->gss_context = gd->gd_ctx; + if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) { + strlcpy(options_ret->actual_mechanism, + mech, + sizeof(options_ret->actual_mechanism)); + } + + gd->gd_state = RPCSEC_GSS_ESTABLISHED; + gd->gd_cred.gc_proc = RPCSEC_GSS_DATA; + gd->gd_cred.gc_seq = 0; + gd->gd_win = gr.gr_win; + break; + } + } + xdr_free((xdrproc_t) xdr_gss_buffer_desc, + (char *) &gd->gd_verf); + + /* End context negotiation loop. */ + if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) { + rpc_createerr.cf_stat = RPC_AUTHERROR; + _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); + return (FALSE); + } + + return (TRUE); +} + +static bool_t +rpc_gss_refresh(AUTH *auth, void *msg) +{ + struct rpc_msg *reply = (struct rpc_msg *) msg; + rpc_gss_options_ret_t options; + + /* + * If the error was RPCSEC_GSS_CREDPROBLEM of + * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All + * other errors are fatal. + */ + if (reply->rm_reply.rp_stat == MSG_DENIED + && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR + && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM + || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) { + rpc_gss_destroy_context(auth, FALSE); + memset(&options, 0, sizeof(options)); + return (rpc_gss_init(auth, &options)); + } + + return (FALSE); +} + +static void +rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy) +{ + struct rpc_gss_data *gd; + OM_uint32 min_stat; + + log_debug("in rpc_gss_destroy_context()"); + + gd = AUTH_PRIVATE(auth); + + if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) { + gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY; + clnt_call(gd->gd_clnt, NULLPROC, + (xdrproc_t)xdr_void, NULL, + (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT); + } + + /* + * Free the context token. Remember that this was + * allocated by XDR, not GSS-API. + */ + xdr_free((xdrproc_t) xdr_gss_buffer_desc, + (char *) &gd->gd_cred.gc_handle); + gd->gd_cred.gc_handle.length = 0; + + if (gd->gd_ctx != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL); + + gd->gd_state = RPCSEC_GSS_START; +} + +static void +rpc_gss_destroy(AUTH *auth) +{ + struct rpc_gss_data *gd; + OM_uint32 min_stat; + + log_debug("in rpc_gss_destroy()"); + + gd = AUTH_PRIVATE(auth); + + rpc_gss_destroy_context(auth, TRUE); + + if (gd->gd_name != GSS_C_NO_NAME) + gss_release_name(&min_stat, &gd->gd_name); + if (gd->gd_verf.value) + xdr_free((xdrproc_t) xdr_gss_buffer_desc, + (char *) &gd->gd_verf); + + mem_free(gd, sizeof(*gd)); + mem_free(auth, sizeof(*auth)); +} + +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)); +} + +bool_t +__rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr) +{ + struct rpc_gss_data *gd; + + log_debug("in rpc_gss_unwrap()"); + + gd = AUTH_PRIVATE(auth); + + if (gd->gd_state != RPCSEC_GSS_ESTABLISHED || + gd->gd_cred.gc_svc == rpc_gss_svc_none) { + return (xdr_func(xdrs, xdr_ptr)); + } + return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, + gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc, + gd->gd_cred.gc_seq)); +} + +int +rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len) +{ + struct rpc_gss_data *gd; + int want_conf; + OM_uint32 max; + OM_uint32 maj_stat, min_stat; + int result; + + gd = AUTH_PRIVATE(auth); + + switch (gd->gd_cred.gc_svc) { + case rpc_gss_svc_none: + return (max_tp_unit_len); + break; + + case rpc_gss_svc_default: + case rpc_gss_svc_integrity: + want_conf = FALSE; + break; + + case rpc_gss_svc_privacy: + want_conf = TRUE; + break; + + default: + return (0); + } + + maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf, + gd->gd_qop, max_tp_unit_len, &max); + + if (maj_stat == GSS_S_COMPLETE) { + result = (int) max; + if (result < 0) + result = 0; + return (result); + } else { + log_status("gss_wrap_size_limit", gd->gd_mech, + maj_stat, min_stat); + return (0); + } +} |