1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 1996,1997,1999,2002-2003 Sun Microsystems, Inc.
  24  * All rights reserved.  Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
  29  *
  30  * $Header:
  31  * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi_misc.c,v 1.10
  32  * 1994/10/27 12:39:23 jik Exp $
  33  */
  34 
  35 /*
  36  * Copyright (c) 2013 by Delphix. All rights reserved.
  37  */
  38 
  39 #include <sys/param.h>
  40 #include <sys/types.h>
  41 #include <sys/stream.h>
  42 #include <sys/strsubr.h>
  43 #include <sys/cmn_err.h>
  44 #include <gssapi/gssapi.h>
  45 #include <rpc/rpc.h>
  46 #include <rpc/rpcsec_defs.h>
  47 
  48 /*
  49  * The initial allocation size for dynamic allocation.
  50  */
  51 #define CKU_INITSIZE    2048
  52 
  53 /*
  54  * The size of additional allocations, if required.  It is larger to
  55  * reduce the number of actual allocations.
  56  */
  57 #define CKU_ALLOCSIZE   8192
  58 
  59 
  60 /*
  61  * Miscellaneous XDR routines.
  62  */
  63 bool_t
  64 __xdr_gss_buf(xdrs, buf)
  65         XDR             *xdrs;
  66         gss_buffer_t    buf;
  67 {
  68         uint_t cast_len, bound_len;
  69 
  70         /*
  71          * We go through this contortion because size_t is a now a ulong,
  72          * GSS-API uses ulongs.
  73          */
  74 
  75         if (xdrs->x_op != XDR_DECODE) {
  76                 bound_len = cast_len = (uint_t)buf->length;
  77         } else {
  78                 bound_len = (uint_t)-1;
  79         }
  80 
  81         if (xdr_bytes(xdrs, (char **)&buf->value, &cast_len,
  82             bound_len) == TRUE) {
  83                 if (xdrs->x_op == XDR_DECODE)
  84                         buf->length = cast_len;
  85 
  86                 return (TRUE);
  87         }
  88 
  89         return (FALSE);
  90 }
  91 
  92 bool_t
  93 __xdr_rpc_gss_creds(xdrs, creds)
  94         XDR                     *xdrs;
  95         rpc_gss_creds           *creds;
  96 {
  97         if (!xdr_u_int(xdrs, (uint_t *)&creds->version) ||
  98                                 !xdr_u_int(xdrs, (uint_t *)&creds->gss_proc) ||
  99                                 !xdr_u_int(xdrs, (uint_t *)&creds->seq_num) ||
 100                                 !xdr_u_int(xdrs, (uint_t *)&creds->service) ||
 101                                 !__xdr_gss_buf(xdrs, &creds->ctx_handle))
 102                 return (FALSE);
 103         return (TRUE);
 104 }
 105 
 106 bool_t
 107 __xdr_rpc_gss_init_arg(xdrs, init_arg)
 108         XDR                     *xdrs;
 109         rpc_gss_init_arg        *init_arg;
 110 {
 111         if (!__xdr_gss_buf(xdrs, init_arg))
 112                 return (FALSE);
 113         return (TRUE);
 114 }
 115 
 116 bool_t
 117 __xdr_rpc_gss_init_res(xdrs, init_res)
 118         XDR                     *xdrs;
 119         rpc_gss_init_res        *init_res;
 120 {
 121         if (!__xdr_gss_buf(xdrs, &init_res->ctx_handle) ||
 122                         !xdr_u_int(xdrs, (uint_t *)&init_res->gss_major) ||
 123                         !xdr_u_int(xdrs, (uint_t *)&init_res->gss_minor) ||
 124                         !xdr_u_int(xdrs, (uint_t *)&init_res->seq_window) ||
 125                         !__xdr_gss_buf(xdrs, &init_res->token))
 126                 return (FALSE);
 127         return (TRUE);
 128 }
 129 
 130 /*
 131  * Generic routine to wrap data used by client and server sides.
 132  */
 133 bool_t
 134 __rpc_gss_wrap_data(service, qop, context, seq_num, out_xdrs,
 135                         xdr_func, xdr_ptr)
 136         OM_uint32               qop;
 137         rpc_gss_service_t       service;
 138         gss_ctx_id_t            context;
 139         uint_t                  seq_num;
 140         XDR                     *out_xdrs;
 141         bool_t                  (*xdr_func)();
 142         caddr_t                 xdr_ptr;
 143 {
 144         OM_uint32               major, minor;
 145         gss_buffer_desc         in_buf, out_buf;
 146         XDR                     temp_xdrs;
 147         char                    *temp_data;
 148 /* EXPORT DELETE START */
 149         bool_t                  conf_state;
 150 /* EXPORT DELETE END */
 151         bool_t                  ret = FALSE;
 152         int                     size;
 153 
 154         /*
 155          * Create a temporary XDR/buffer to hold the data to be wrapped.
 156          * We need an extra bit for the sequence number serialized first.
 157          */
 158         size = xdr_sizeof(xdr_func, xdr_ptr) + BYTES_PER_XDR_UNIT;
 159         temp_data = kmem_alloc(size, KM_SLEEP);
 160         out_buf.length = 0;
 161 
 162         xdrmem_create(&temp_xdrs, temp_data, size, XDR_ENCODE);
 163 
 164         /*
 165          * serialize the sequence number into tmp memory
 166          */
 167         if (!xdr_u_int(&temp_xdrs, &seq_num))
 168                 goto fail;
 169 
 170         /*
 171          * serialize the arguments into tmp memory
 172          */
 173         if (!(*xdr_func)(&temp_xdrs, xdr_ptr))
 174                 goto fail;
 175 
 176         /*
 177          * Data to be wrapped goes in in_buf.  If privacy is used,
 178          * out_buf will have wrapped data (in_buf will no longer be
 179          * needed).  If integrity is used, out_buf will have checksum
 180          * which will follow the data in in_buf.
 181          */
 182         in_buf.length = xdr_getpos(&temp_xdrs);
 183         in_buf.value = (char *)temp_xdrs.x_base;
 184 
 185         switch (service) {
 186         case rpc_gss_svc_privacy:
 187 
 188 /* EXPORT DELETE START */
 189                 if ((major = kgss_seal(&minor, context, TRUE, qop, &in_buf,
 190                                 &conf_state, &out_buf)) != GSS_S_COMPLETE) {
 191                         RPCGSS_LOG1(1, "rpc_gss_wrap: kgss_seal failed."
 192                                 "major = %x, minor = %x", major, minor);
 193                         goto fail;
 194                 }
 195                 in_buf.length = 0;      /* in_buf not needed */
 196                 if (!conf_state)
 197 /* EXPORT DELETE END */
 198                         goto fail;
 199 /* EXPORT DELETE START */
 200                 break;
 201 /* EXPORT DELETE END */
 202         case rpc_gss_svc_integrity:
 203                 if ((major = kgss_sign(&minor, context, qop, &in_buf,
 204                                 &out_buf)) != GSS_S_COMPLETE) {
 205                         RPCGSS_LOG1(1, "rpc_gss_wrap: kgss_sign failed."
 206                                 "major = %x, minor = %x", major, minor);
 207                         goto fail;
 208                 }
 209                 break;
 210         default:
 211                 goto fail;
 212         }
 213 
 214         /*
 215          * write out in_buf and out_buf as needed
 216          */
 217         if (in_buf.length != 0) {
 218                 if (!__xdr_gss_buf(out_xdrs, &in_buf))
 219                         goto fail;
 220         }
 221 
 222         if (!__xdr_gss_buf(out_xdrs, &out_buf))
 223                 goto fail;
 224         ret = TRUE;
 225 fail:
 226         kmem_free(temp_data, size);
 227         if (out_buf.length != 0)
 228                 (void) gss_release_buffer(&minor, &out_buf);
 229         return (ret);
 230 }
 231 
 232 /*
 233  * Generic routine to unwrap data used by client and server sides.
 234  */
 235 bool_t
 236 __rpc_gss_unwrap_data(service, context, seq_num, qop_check, in_xdrs,
 237                         xdr_func, xdr_ptr)
 238         rpc_gss_service_t       service;
 239         gss_ctx_id_t            context;
 240         uint_t                  seq_num;
 241         OM_uint32               qop_check;
 242         XDR                     *in_xdrs;
 243         bool_t                  (*xdr_func)();
 244         caddr_t                 xdr_ptr;
 245 {
 246         gss_buffer_desc         in_buf, out_buf;
 247         XDR                     temp_xdrs;
 248         uint_t                  seq_num2;
 249         bool_t                  conf = FALSE;
 250         OM_uint32               major = GSS_S_COMPLETE, minor = 0;
 251         int                     qop = 0;
 252 
 253         in_buf.value = NULL;
 254         out_buf.value = NULL;
 255 
 256         /*
 257          * Pull out wrapped data.  For privacy service, this is the
 258          * encrypted data.  For integrity service, this is the data
 259          * followed by a checksum.
 260          */
 261         if (!__xdr_gss_buf(in_xdrs, &in_buf)) {
 262                 return (FALSE);
 263         }
 264 
 265         if (service == rpc_gss_svc_privacy) {
 266                 major = GSS_S_FAILURE;
 267 /* EXPORT DELETE START */
 268                 major = kgss_unseal(&minor, context, &in_buf, &out_buf, &conf,
 269                                         &qop);
 270 /* EXPORT DELETE END */
 271                 kmem_free(in_buf.value, in_buf.length);
 272                 if (major != GSS_S_COMPLETE) {
 273                         RPCGSS_LOG1(1, "rpc_gss_unwrap: kgss_unseal failed."
 274                                 "major = %x, minor = %x", major, minor);
 275                         return (FALSE);
 276                 }
 277                 /*
 278                  * Keep the returned token (unencrypted data) in in_buf.
 279                  */
 280                 in_buf.length = out_buf.length;
 281                 in_buf.value = out_buf.value;
 282 
 283                 /*
 284                  * If privacy was not used, or if QOP is not what we are
 285                  * expecting, fail.
 286                  */
 287                 if (!conf || qop != qop_check)
 288                         goto fail;
 289 
 290         } else if (service == rpc_gss_svc_integrity) {
 291                 if (!__xdr_gss_buf(in_xdrs, &out_buf)) {
 292                         return (FALSE);
 293                 }
 294                 major = kgss_verify(&minor, context, &in_buf, &out_buf,
 295                                 &qop);
 296                 kmem_free(out_buf.value, out_buf.length);
 297                 if (major != GSS_S_COMPLETE) {
 298                         kmem_free(in_buf.value, in_buf.length);
 299                         RPCGSS_LOG1(1, "rpc_gss_unwrap: kgss_verify failed."
 300                                 "major = %x, minor = %x", major, minor);
 301                         return (FALSE);
 302                 }
 303 
 304                 /*
 305                  * If QOP is not what we are expecting, fail.
 306                  */
 307                 if (qop != qop_check)
 308                         goto fail;
 309         }
 310 
 311         xdrmem_create(&temp_xdrs, in_buf.value, in_buf.length, XDR_DECODE);
 312 
 313         /*
 314          * The data consists of the sequence number followed by the
 315          * arguments.  Make sure sequence number is what we are
 316          * expecting (i.e., the value in the header).
 317          */
 318         if (!xdr_u_int(&temp_xdrs, &seq_num2))
 319                 goto fail;
 320         if (seq_num2 != seq_num)
 321                 goto fail;
 322 
 323         /*
 324          * Deserialize the arguments into xdr_ptr, and release in_buf.
 325          */
 326         if (!(*xdr_func)(&temp_xdrs, xdr_ptr)) {
 327                 goto fail;
 328         }
 329 
 330         if (service == rpc_gss_svc_privacy)
 331                 (void) gss_release_buffer(&minor, &in_buf);
 332         else
 333                 kmem_free(in_buf.value, in_buf.length);
 334         XDR_DESTROY(&temp_xdrs);
 335         return (TRUE);
 336 fail:
 337         XDR_DESTROY(&temp_xdrs);
 338         if (service == rpc_gss_svc_privacy)
 339                 (void) gss_release_buffer(&minor, &in_buf);
 340         else
 341                 kmem_free(in_buf.value, in_buf.length);
 342         return (FALSE);
 343 }