/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1996,1997,1999,2002-2003 Sun Microsystems, Inc. * All rights reserved. Use is subject to license terms. */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * * $Header: * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi_misc.c,v 1.10 * 1994/10/27 12:39:23 jik Exp $ */ /* * Copyright (c) 2013 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #include /* * The initial allocation size for dynamic allocation. */ #define CKU_INITSIZE 2048 /* * The size of additional allocations, if required. It is larger to * reduce the number of actual allocations. */ #define CKU_ALLOCSIZE 8192 /* * Miscellaneous XDR routines. */ bool_t __xdr_gss_buf(xdrs, buf) XDR *xdrs; gss_buffer_t buf; { uint_t cast_len, bound_len; /* * We go through this contortion because size_t is a now a ulong, * GSS-API uses ulongs. */ if (xdrs->x_op != XDR_DECODE) { bound_len = cast_len = (uint_t)buf->length; } else { bound_len = (uint_t)-1; } if (xdr_bytes(xdrs, (char **)&buf->value, &cast_len, bound_len) == TRUE) { if (xdrs->x_op == XDR_DECODE) buf->length = cast_len; return (TRUE); } return (FALSE); } bool_t __xdr_rpc_gss_creds(xdrs, creds) XDR *xdrs; rpc_gss_creds *creds; { if (!xdr_u_int(xdrs, (uint_t *)&creds->version) || !xdr_u_int(xdrs, (uint_t *)&creds->gss_proc) || !xdr_u_int(xdrs, (uint_t *)&creds->seq_num) || !xdr_u_int(xdrs, (uint_t *)&creds->service) || !__xdr_gss_buf(xdrs, &creds->ctx_handle)) return (FALSE); return (TRUE); } bool_t __xdr_rpc_gss_init_arg(xdrs, init_arg) XDR *xdrs; rpc_gss_init_arg *init_arg; { if (!__xdr_gss_buf(xdrs, init_arg)) return (FALSE); return (TRUE); } bool_t __xdr_rpc_gss_init_res(xdrs, init_res) XDR *xdrs; rpc_gss_init_res *init_res; { if (!__xdr_gss_buf(xdrs, &init_res->ctx_handle) || !xdr_u_int(xdrs, (uint_t *)&init_res->gss_major) || !xdr_u_int(xdrs, (uint_t *)&init_res->gss_minor) || !xdr_u_int(xdrs, (uint_t *)&init_res->seq_window) || !__xdr_gss_buf(xdrs, &init_res->token)) return (FALSE); return (TRUE); } /* * Generic routine to wrap data used by client and server sides. */ bool_t __rpc_gss_wrap_data(service, qop, context, seq_num, out_xdrs, xdr_func, xdr_ptr) OM_uint32 qop; rpc_gss_service_t service; gss_ctx_id_t context; uint_t seq_num; XDR *out_xdrs; bool_t (*xdr_func)(); caddr_t xdr_ptr; { OM_uint32 major, minor; gss_buffer_desc in_buf, out_buf; XDR temp_xdrs; char *temp_data; bool_t conf_state; bool_t ret = FALSE; int size; /* * Create a temporary XDR/buffer to hold the data to be wrapped. * We need an extra bit for the sequence number serialized first. */ size = xdr_sizeof(xdr_func, xdr_ptr) + BYTES_PER_XDR_UNIT; temp_data = kmem_alloc(size, KM_SLEEP); out_buf.length = 0; xdrmem_create(&temp_xdrs, temp_data, size, XDR_ENCODE); /* * serialize the sequence number into tmp memory */ if (!xdr_u_int(&temp_xdrs, &seq_num)) goto fail; /* * serialize the arguments into tmp memory */ if (!(*xdr_func)(&temp_xdrs, xdr_ptr)) goto fail; /* * Data to be wrapped goes in in_buf. If privacy is used, * out_buf will have wrapped data (in_buf will no longer be * needed). If integrity is used, out_buf will have checksum * which will follow the data in in_buf. */ in_buf.length = xdr_getpos(&temp_xdrs); in_buf.value = (char *)temp_xdrs.x_base; switch (service) { case rpc_gss_svc_privacy: if ((major = kgss_seal(&minor, context, TRUE, qop, &in_buf, &conf_state, &out_buf)) != GSS_S_COMPLETE) { RPCGSS_LOG1(1, "rpc_gss_wrap: kgss_seal failed." "major = %x, minor = %x", major, minor); goto fail; } in_buf.length = 0; /* in_buf not needed */ if (!conf_state) goto fail; break; case rpc_gss_svc_integrity: if ((major = kgss_sign(&minor, context, qop, &in_buf, &out_buf)) != GSS_S_COMPLETE) { RPCGSS_LOG1(1, "rpc_gss_wrap: kgss_sign failed." "major = %x, minor = %x", major, minor); goto fail; } break; default: goto fail; } /* * write out in_buf and out_buf as needed */ if (in_buf.length != 0) { if (!__xdr_gss_buf(out_xdrs, &in_buf)) goto fail; } if (!__xdr_gss_buf(out_xdrs, &out_buf)) goto fail; ret = TRUE; fail: kmem_free(temp_data, size); if (out_buf.length != 0) (void) gss_release_buffer(&minor, &out_buf); return (ret); } /* * Generic routine to unwrap data used by client and server sides. */ bool_t __rpc_gss_unwrap_data(service, context, seq_num, qop_check, in_xdrs, xdr_func, xdr_ptr) rpc_gss_service_t service; gss_ctx_id_t context; uint_t seq_num; OM_uint32 qop_check; XDR *in_xdrs; bool_t (*xdr_func)(); caddr_t xdr_ptr; { gss_buffer_desc in_buf, out_buf; XDR temp_xdrs; uint_t seq_num2; bool_t conf = FALSE; OM_uint32 major = GSS_S_COMPLETE, minor = 0; int qop = 0; in_buf.value = NULL; out_buf.value = NULL; /* * Pull out wrapped data. For privacy service, this is the * encrypted data. For integrity service, this is the data * followed by a checksum. */ if (!__xdr_gss_buf(in_xdrs, &in_buf)) { return (FALSE); } if (service == rpc_gss_svc_privacy) { major = GSS_S_FAILURE; major = kgss_unseal(&minor, context, &in_buf, &out_buf, &conf, &qop); kmem_free(in_buf.value, in_buf.length); if (major != GSS_S_COMPLETE) { RPCGSS_LOG1(1, "rpc_gss_unwrap: kgss_unseal failed." "major = %x, minor = %x", major, minor); return (FALSE); } /* * Keep the returned token (unencrypted data) in in_buf. */ in_buf.length = out_buf.length; in_buf.value = out_buf.value; /* * If privacy was not used, or if QOP is not what we are * expecting, fail. */ if (!conf || qop != qop_check) goto fail; } else if (service == rpc_gss_svc_integrity) { if (!__xdr_gss_buf(in_xdrs, &out_buf)) { return (FALSE); } major = kgss_verify(&minor, context, &in_buf, &out_buf, &qop); kmem_free(out_buf.value, out_buf.length); if (major != GSS_S_COMPLETE) { kmem_free(in_buf.value, in_buf.length); RPCGSS_LOG1(1, "rpc_gss_unwrap: kgss_verify failed." "major = %x, minor = %x", major, minor); return (FALSE); } /* * If QOP is not what we are expecting, fail. */ if (qop != qop_check) goto fail; } xdrmem_create(&temp_xdrs, in_buf.value, in_buf.length, XDR_DECODE); /* * The data consists of the sequence number followed by the * arguments. Make sure sequence number is what we are * expecting (i.e., the value in the header). */ if (!xdr_u_int(&temp_xdrs, &seq_num2)) goto fail; if (seq_num2 != seq_num) goto fail; /* * Deserialize the arguments into xdr_ptr, and release in_buf. */ if (!(*xdr_func)(&temp_xdrs, xdr_ptr)) { goto fail; } if (service == rpc_gss_svc_privacy) (void) gss_release_buffer(&minor, &in_buf); else kmem_free(in_buf.value, in_buf.length); XDR_DESTROY(&temp_xdrs); return (TRUE); fail: XDR_DESTROY(&temp_xdrs); if (service == rpc_gss_svc_privacy) (void) gss_release_buffer(&minor, &in_buf); else kmem_free(in_buf.value, in_buf.length); return (FALSE); }