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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
  28  *
  29  * $Header:
  30  * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi.c,v
  31  * 1.14 1995/03/22 22:07:55 jik Exp $
  32  */
  33 
  34 #include  <sys/systm.h>
  35 #include  <sys/types.h>
  36 #include  <gssapi/gssapi.h>
  37 #include  <rpc/rpc.h>
  38 #include  <rpc/rpcsec_defs.h>
  39 #include  <sys/debug.h>
  40 #include  <sys/cmn_err.h>
  41 #include  <sys/ddi.h>
  42 
  43 static  void    rpc_gss_nextverf();
  44 static  bool_t  rpc_gss_marshall();
  45 static  bool_t  rpc_gss_validate();
  46 static  bool_t  rpc_gss_refresh();
  47 static  void    rpc_gss_destroy();
  48 #if 0
  49 static  void    rpc_gss_destroy_pvt();
  50 #endif
  51 static  void    rpc_gss_free_pvt();
  52 static  int     rpc_gss_seccreate_pvt();
  53 static  bool_t  rpc_gss_wrap();
  54 static  bool_t  rpc_gss_unwrap();
  55 static  bool_t  validate_seqwin();
  56 
  57 
  58 #ifdef  DEBUG
  59 #include <sys/promif.h>
  60 #endif
  61 
  62 static struct auth_ops rpc_gss_ops = {
  63         rpc_gss_nextverf,
  64         rpc_gss_marshall,
  65         rpc_gss_validate,
  66         rpc_gss_refresh,
  67         rpc_gss_destroy,
  68         rpc_gss_wrap,
  69         rpc_gss_unwrap,
  70 };
  71 
  72 /*
  73  * Private data for RPCSEC_GSS.
  74  */
  75 typedef struct _rpc_gss_data {
  76         bool_t          established;    /* TRUE when established */
  77         CLIENT          *clnt;          /* associated client handle */
  78         int             version;        /* RPCSEC version */
  79         gss_ctx_id_t    context;        /* GSS context id */
  80         gss_buffer_desc ctx_handle;     /* RPCSEC GSS context handle */
  81         uint_t          seq_num;        /* last sequence number rcvd */
  82         gss_cred_id_t   my_cred;        /* caller's GSS credentials */
  83         OM_uint32       qop;            /* requested QOP */
  84         rpc_gss_service_t       service;        /* requested service */
  85         uint_t          gss_proc;       /* GSS control procedure */
  86         gss_name_t      target_name;    /* target server */
  87         int             req_flags;      /* GSS request bits */
  88         gss_OID         mech_type;      /* GSS mechanism */
  89         OM_uint32       time_req;       /* requested cred lifetime */
  90         bool_t          invalid;        /* can't use this any more */
  91         OM_uint32       seq_window;     /* server sequence window */
  92         struct opaque_auth *verifier;   /* rpc reply verifier saved for */
  93                                         /* validating the sequence window */
  94         gss_channel_bindings_t  icb;
  95 } rpc_gss_data;
  96 #define AUTH_PRIVATE(auth)      ((rpc_gss_data *)auth->ah_private)
  97 
  98 #define INTERRUPT_OK    1       /* allow interrupt */
  99 
 100 /*
 101  *  RPCSEC_GSS auth cache definitions.
 102  */
 103 
 104 /* The table size must be a power of two. */
 105 #define GSSAUTH_TABLESIZE 16
 106 #define HASH(keynum, uid_num) \
 107         ((((intptr_t)(keynum)) ^ ((int)uid_num)) & (GSSAUTH_TABLESIZE - 1))
 108 
 109 /*
 110  * gss auth cache entry.
 111  */
 112 typedef struct ga_cache_entry {
 113         void    *cache_key;
 114         uid_t   uid;
 115         zoneid_t zoneid;
 116         bool_t  in_use;
 117         time_t  ref_time; /* the time referenced previously */
 118         time_t  ctx_expired_time; /* when the context will be expired */
 119         AUTH    *auth;
 120         struct ga_cache_entry *next;
 121 } *ga_cache_list;
 122 
 123 struct ga_cache_entry   *ga_cache_table[GSSAUTH_TABLESIZE];
 124 static krwlock_t        ga_cache_table_lock;
 125 static struct kmem_cache *ga_cache_handle;
 126 static void gssauth_cache_reclaim(void *);
 127 
 128 static void gssauth_zone_fini(zoneid_t, void *);
 129 static zone_key_t       gssauth_zone_key;
 130 
 131 int ga_cache_hit;
 132 int ga_cache_miss;
 133 int ga_cache_reclaim;
 134 
 135 #define NOT_DEAD(ptr)   ASSERT((((intptr_t)(ptr)) != 0xdeadbeef))
 136 
 137 void
 138 gssauth_init(void)
 139 {
 140         /*
 141          * Initialize gss auth cache table lock
 142          */
 143         rw_init(&ga_cache_table_lock, NULL, RW_DEFAULT, NULL);
 144 
 145         /*
 146          * Allocate gss auth cache handle
 147          */
 148         ga_cache_handle = kmem_cache_create("ga_cache_handle",
 149             sizeof (struct ga_cache_entry), 0, NULL, NULL,
 150             gssauth_cache_reclaim, NULL, NULL, 0);
 151         zone_key_create(&gssauth_zone_key, NULL, NULL, gssauth_zone_fini);
 152 }
 153 
 154 /*
 155  * Destroy the structures previously initialized in gssauth_init()
 156  * This routine is called by _init() if mod_install() failed.
 157  */
 158 void
 159 gssauth_fini(void)
 160 {
 161         (void) zone_key_delete(gssauth_zone_key);
 162         kmem_cache_destroy(ga_cache_handle);
 163         rw_destroy(&ga_cache_table_lock);
 164 }
 165 
 166 /*
 167  * This is a cleanup routine to release cached entries when a zone is being
 168  * destroyed.  The code is also used when kmem calls us to free up memory, at
 169  * which point ``zoneid'' will be ALL_ZONES.  We don't honor the cache timeout
 170  * when the zone is going away, since the zoneid (and all associated cached
 171  * entries) are invalid.
 172  */
 173 time_t rpc_gss_cache_time = 60 * 60;
 174 
 175 /* ARGSUSED */
 176 static void
 177 gssauth_zone_fini(zoneid_t zoneid, void *unused)
 178 {
 179         struct ga_cache_entry *p, *prev, *next;
 180         int i;
 181         time_t now;
 182 
 183         rw_enter(&ga_cache_table_lock, RW_WRITER);
 184 
 185         for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
 186                 prev = NULL;
 187                 for (p = ga_cache_table[i]; p; p = next) {
 188                         NOT_DEAD(p->next);
 189                         next = p->next;
 190                         NOT_DEAD(next);
 191                         if (zoneid == ALL_ZONES) {      /* kmem callback */
 192                                 /*
 193                                  * Free entries that have not been
 194                                  * used for rpc_gss_cache_time seconds.
 195                                  */
 196                                 now = gethrestime_sec();
 197                                 if ((p->ref_time + rpc_gss_cache_time >
 198                                     now) || p->in_use) {
 199                                         if ((p->ref_time + rpc_gss_cache_time <=
 200                                             now) && p->in_use) {
 201                                                 RPCGSS_LOG0(2, "gssauth_cache_"
 202                                                     "reclaim: in_use\n");
 203                                         }
 204                                         prev = p;
 205                                         continue;
 206                                 }
 207                         } else {
 208                                 if (p->zoneid != zoneid) {
 209                                         prev = p;
 210                                         continue;
 211                                 }
 212                                 ASSERT(!p->in_use);
 213                         }
 214 
 215                         RPCGSS_LOG(2, "gssauth_cache_reclaim: destroy auth "
 216                             "%p\n", (void *)p->auth);
 217                         rpc_gss_destroy(p->auth);
 218                         kmem_cache_free(ga_cache_handle, (void *)p);
 219                         if (prev == NULL) {
 220                                 ga_cache_table[i] = next;
 221                         } else {
 222                                 NOT_DEAD(prev->next);
 223                                 prev->next = next;
 224                         }
 225                 }
 226         }
 227 
 228         rw_exit(&ga_cache_table_lock);
 229 
 230 }
 231 
 232 /*
 233  * Called by the kernel memory allocator when
 234  * memory is low. Free unused cache entries.
 235  * If that's not enough, the VM system will
 236  * call again for some more.
 237  */
 238 /*ARGSUSED*/
 239 static void
 240 gssauth_cache_reclaim(void *cdrarg)
 241 {
 242         gssauth_zone_fini(ALL_ZONES, NULL);
 243 }
 244 
 245 #define NOT_NULL(ptr)   ASSERT(ptr)
 246 #define IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0)
 247 
 248 /*
 249  *  Get the client gss security service handle.
 250  *  If it is in the cache table, get it, otherwise, create
 251  *  a new one by calling rpc_gss_seccreate().
 252  */
 253 int
 254 rpc_gss_secget(CLIENT *clnt,
 255         char    *principal,
 256         rpc_gss_OID     mechanism,
 257         rpc_gss_service_t service_type,
 258         uint_t  qop,
 259         rpc_gss_options_req_t *options_req,
 260         rpc_gss_options_ret_t *options_ret,
 261         void *cache_key,
 262         cred_t *cr,
 263         AUTH **retauth)
 264 {
 265         struct ga_cache_entry **head, *current, *new, *prev;
 266         AUTH *auth = NULL;
 267         rpc_gss_data    *ap;
 268         rpc_gss_options_ret_t opt_ret;
 269         int status = 0;
 270         uid_t uid = crgetuid(cr);
 271         zoneid_t zoneid = getzoneid();
 272 
 273         if (retauth == NULL)
 274                 return (EINVAL);
 275         *retauth = NULL;
 276 
 277         NOT_NULL(cr);
 278         IS_ALIGNED(cr);
 279 #ifdef DEBUG
 280 if (HASH(cache_key, uid) < 0) {
 281         prom_printf("cache_key %p, cr %p\n", cache_key, (void *)cr);
 282 }
 283 #endif
 284 
 285         /*
 286          *  Get a valid gss auth handle from the cache table.
 287          *  If auth in cache is invalid and not in use, destroy it.
 288          */
 289         prev = NULL;
 290         rw_enter(&ga_cache_table_lock, RW_WRITER);
 291 
 292         ASSERT(HASH(cache_key, uid) >= 0);
 293         head = &ga_cache_table[HASH(cache_key, uid)];
 294         NOT_NULL(head);
 295         IS_ALIGNED(head);
 296 
 297         for (current = *head; current; current = current->next) {
 298                 NOT_NULL(current);
 299                 IS_ALIGNED(current);
 300                 if ((cache_key == current->cache_key) &&
 301                         (uid == current->uid) && (zoneid == current->zoneid) &&
 302                         !current->in_use) {
 303                         current->in_use = TRUE;
 304                         current->ref_time = gethrestime_sec();
 305                         ap = AUTH_PRIVATE(current->auth);
 306                         ap->clnt = clnt;
 307                         ga_cache_hit++;
 308                         if (ap->invalid ||
 309                             ((current->ctx_expired_time != GSS_C_INDEFINITE) &&
 310                             (gethrestime_sec() >=
 311                             current->ctx_expired_time))) {
 312                             RPCGSS_LOG0(1, "NOTICE: rpc_gss_secget: time to "
 313                                         "refresh the auth\n");
 314                             if (prev == NULL) {
 315                                 *head = current->next;
 316                             } else {
 317                                 prev->next = current->next;
 318                             }
 319                             rpc_gss_destroy(current->auth);
 320                             kmem_cache_free(ga_cache_handle, (void *) current);
 321                             auth = NULL;
 322                         } else {
 323                             auth = current->auth;
 324                         }
 325                         break;
 326                 } else {
 327                         prev = current;
 328                 }
 329         }
 330         rw_exit(&ga_cache_table_lock);
 331 
 332         /*
 333          *  If no valid gss auth handle can be found in the cache, create
 334          *  a new one.
 335          */
 336         if (!auth) {
 337                 ga_cache_miss++;
 338                 if (options_ret == NULL)
 339                         options_ret = &opt_ret;
 340 
 341                 status = rpc_gss_seccreate(clnt, principal, mechanism,
 342                         service_type, qop, options_req, options_ret, cr, &auth);
 343                 if (status == 0) {
 344                         RPCGSS_LOG(2, "rpc_gss_secget: new auth %p\n",
 345                                         (void *)auth);
 346                         new = kmem_cache_alloc(ga_cache_handle, KM_NOSLEEP);
 347                         IS_ALIGNED(new);
 348                         NOT_DEAD(new);
 349                         if (new) {
 350                                 new->cache_key = cache_key;
 351                                 new->uid = uid;
 352                                 new->zoneid = zoneid;
 353                                 new->in_use = TRUE;
 354                                 new->ref_time = gethrestime_sec();
 355                                 if (options_ret->time_ret != GSS_C_INDEFINITE) {
 356                                     new->ctx_expired_time = new->ref_time +
 357                                         options_ret->time_ret;
 358                                 } else {
 359                                     new->ctx_expired_time = GSS_C_INDEFINITE;
 360                                 }
 361                                 new->auth = auth;
 362                                 rw_enter(&ga_cache_table_lock, RW_WRITER);
 363                                 NOT_DEAD(*head);
 364                                 NOT_DEAD(new->next);
 365                                 new->next = *head;
 366                                 *head = new;
 367                                 rw_exit(&ga_cache_table_lock);
 368                         }
 369                         /* done with opt_ret */
 370                         if (options_ret == &opt_ret) {
 371                             kgss_free_oid((gss_OID) opt_ret.actual_mechanism);
 372                         }
 373                 }
 374         }
 375 
 376         *retauth = auth;
 377         return (status);
 378 }
 379 
 380 
 381 
 382 /*
 383  *  rpc_gss_secfree will destroy a rpcsec_gss context only if
 384  *  the auth handle is not in the cache table.
 385  */
 386 void
 387 rpc_gss_secfree(AUTH *auth)
 388 {
 389         struct ga_cache_entry *next, *cur;
 390         int i;
 391 
 392         /*
 393          *  Check the cache table to find the auth.
 394          *  Marked it unused.
 395          */
 396         rw_enter(&ga_cache_table_lock, RW_WRITER);
 397         for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
 398                 for (cur = ga_cache_table[i]; cur; cur = next) {
 399                         NOT_DEAD(cur);
 400                         next = cur->next;
 401                         NOT_DEAD(next);
 402                         if (cur->auth == auth) {
 403                                 ASSERT(cur->in_use == TRUE);
 404                                 cur->in_use = FALSE;
 405                                 rw_exit(&ga_cache_table_lock);
 406                                 return;
 407                         }
 408                 }
 409         }
 410         rw_exit(&ga_cache_table_lock);
 411         RPCGSS_LOG(2, "rpc_gss_secfree: destroy auth %p\n", (void *)auth);
 412         rpc_gss_destroy(auth);
 413 }
 414 
 415 
 416 /*
 417  *  Create a gss security service context.
 418  */
 419 int
 420 rpc_gss_seccreate(CLIENT *clnt,
 421         char                    *principal,     /* target service@server */
 422         rpc_gss_OID             mechanism,      /* security mechanism */
 423         rpc_gss_service_t       service_type,   /* security service */
 424         uint_t                  qop,            /* requested QOP */
 425         rpc_gss_options_req_t   *options_req,   /* requested options */
 426         rpc_gss_options_ret_t   *options_ret,   /* returned options */
 427         cred_t                  *cr,            /* client's unix cred */
 428         AUTH                    **retauth)      /* auth handle */
 429 {
 430         OM_uint32               gssstat;
 431         OM_uint32               minor_stat;
 432         gss_name_t              target_name;
 433         int                     ret_flags;
 434         OM_uint32               time_rec;
 435         gss_buffer_desc         input_name;
 436         AUTH                    *auth = NULL;
 437         rpc_gss_data            *ap = NULL;
 438         int                     error;
 439 
 440         /*
 441          * convert name to GSS internal type
 442          */
 443         input_name.value = principal;
 444         input_name.length = strlen(principal);
 445 
 446         gssstat = gss_import_name(&minor_stat, &input_name,
 447             (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &target_name);
 448 
 449         if (gssstat != GSS_S_COMPLETE) {
 450                 RPCGSS_LOG0(1,
 451                     "rpc_gss_seccreate: unable to import gss name\n");
 452                 return (ENOMEM);
 453         }
 454 
 455         /*
 456          * Create AUTH handle.  Save the necessary interface information
 457          * so that the client can refresh the handle later if needed.
 458          */
 459         if ((auth = (AUTH *) kmem_alloc(sizeof (*auth), KM_SLEEP)) != NULL)
 460                 ap = (rpc_gss_data *) kmem_alloc(sizeof (*ap), KM_SLEEP);
 461         if (auth == NULL || ap == NULL) {
 462                 RPCGSS_LOG0(1, "rpc_gss_seccreate: out of memory\n");
 463                 if (auth != NULL)
 464                         kmem_free((char *)auth, sizeof (*auth));
 465                 (void) gss_release_name(&minor_stat, &target_name);
 466                 return (ENOMEM);
 467         }
 468 
 469         bzero((char *)ap, sizeof (*ap));
 470         ap->clnt = clnt;
 471         ap->version = RPCSEC_GSS_VERSION;
 472         if (options_req != NULL) {
 473                 ap->my_cred = options_req->my_cred;
 474                 ap->req_flags = options_req->req_flags;
 475                 ap->time_req = options_req->time_req;
 476                 ap->icb = options_req->input_channel_bindings;
 477         } else {
 478                 ap->my_cred = GSS_C_NO_CREDENTIAL;
 479                 ap->req_flags = GSS_C_MUTUAL_FLAG;
 480                 ap->time_req = 0;
 481                 ap->icb = GSS_C_NO_CHANNEL_BINDINGS;
 482         }
 483         if ((ap->service = service_type) == rpc_gss_svc_default)
 484                 ap->service = rpc_gss_svc_integrity;
 485         ap->qop = qop;
 486         ap->target_name = target_name;
 487 
 488         /*
 489          * Now invoke the real interface that sets up the context from
 490          * the information stashed away in the private data.
 491          */
 492         if (error = rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
 493             mechanism, &ap->mech_type, &ret_flags, &time_rec, cr, 0)) {
 494                 if (ap->target_name) {
 495                         (void) gss_release_name(&minor_stat, &ap->target_name);
 496                 }
 497                 kmem_free((char *)ap, sizeof (*ap));
 498                 kmem_free((char *)auth, sizeof (*auth));
 499                 RPCGSS_LOG(1, "rpc_gss_seccreate: init context failed"
 500                     " errno=%d\n", error);
 501                 return (error);
 502         }
 503 
 504         /*
 505          * Make sure that the requested service is supported.  In all
 506          * cases, integrity service must be available.
 507          */
 508         if ((ap->service == rpc_gss_svc_privacy &&
 509             !(ret_flags & GSS_C_CONF_FLAG)) ||
 510             !(ret_flags & GSS_C_INTEG_FLAG)) {
 511                 rpc_gss_destroy(auth);
 512                 RPCGSS_LOG0(1, "rpc_gss_seccreate: service not supported\n");
 513                 return (EPROTONOSUPPORT);
 514         }
 515 
 516         /*
 517          * return option values if requested
 518          */
 519         if (options_ret != NULL) {
 520                 options_ret->major_status = gssstat;
 521                 options_ret->minor_status = minor_stat;
 522                 options_ret->rpcsec_version = ap->version;
 523                 options_ret->ret_flags = ret_flags;
 524                 options_ret->time_ret = time_rec;
 525                 options_ret->gss_context = ap->context;
 526                 /*
 527                  *  Caller's responsibility to free this.
 528                  */
 529                 NOT_NULL(ap->mech_type);
 530                 __rpc_gss_dup_oid(ap->mech_type,
 531                     (gss_OID *)&options_ret->actual_mechanism);
 532         }
 533 
 534         *retauth = auth;
 535         return (0);
 536 }
 537 
 538 /*
 539  * Private interface to create a context.  This is the interface
 540  * that's invoked when the context has to be refreshed.
 541  */
 542 static int
 543 rpc_gss_seccreate_pvt(gssstat, minor_stat, auth, ap, desired_mech_type,
 544                         actual_mech_type, ret_flags, time_rec, cr, isrefresh)
 545         OM_uint32               *gssstat;
 546         OM_uint32               *minor_stat;
 547         AUTH                    *auth;
 548         rpc_gss_data            *ap;
 549         gss_OID                 desired_mech_type;
 550         gss_OID                 *actual_mech_type;
 551         int                     *ret_flags;
 552         OM_uint32               *time_rec;
 553         cred_t                  *cr;
 554         int                     isrefresh;
 555 {
 556         CLIENT                  *clnt = ap->clnt;
 557         AUTH                    *save_auth;
 558         enum clnt_stat          callstat;
 559         rpc_gss_init_arg        call_arg;
 560         rpc_gss_init_res        call_res;
 561         gss_buffer_desc         *input_token_p, input_token, process_token;
 562         int                     free_results = 0;
 563         k_sigset_t              smask;
 564         int                     error = 0;
 565 
 566         /*
 567          * (re)initialize AUTH handle and private data.
 568          */
 569         bzero((char *)auth, sizeof (*auth));
 570         auth->ah_ops = &rpc_gss_ops;
 571         auth->ah_private = (caddr_t)ap;
 572         auth->ah_cred.oa_flavor = RPCSEC_GSS;
 573 
 574         ap->established = FALSE;
 575         ap->ctx_handle.length = 0;
 576         ap->ctx_handle.value = NULL;
 577         ap->context = NULL;
 578         ap->seq_num = 0;
 579         ap->gss_proc = RPCSEC_GSS_INIT;
 580 
 581         /*
 582          * should not change clnt->cl_auth at this time, so save
 583          * old handle
 584          */
 585         save_auth = clnt->cl_auth;
 586         clnt->cl_auth = auth;
 587 
 588         /*
 589          * set state for starting context setup
 590          */
 591         bzero((char *)&call_arg, sizeof (call_arg));
 592         input_token_p = GSS_C_NO_BUFFER;
 593 
 594 next_token:
 595         *gssstat = kgss_init_sec_context(minor_stat,
 596                                         ap->my_cred,
 597                                         &ap->context,
 598                                         ap->target_name,
 599                                         desired_mech_type,
 600                                         ap->req_flags,
 601                                         ap->time_req,
 602                                         NULL,
 603                                         input_token_p,
 604                                         actual_mech_type,
 605                                         &call_arg,
 606                                         ret_flags,
 607                                         time_rec,
 608                                         crgetuid(cr));
 609 
 610         if (input_token_p != GSS_C_NO_BUFFER) {
 611                 OM_uint32 minor_stat2;
 612 
 613                 (void) gss_release_buffer(&minor_stat2, input_token_p);
 614                 input_token_p = GSS_C_NO_BUFFER;
 615         }
 616 
 617         if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
 618                 rpc_gss_display_status(*gssstat, *minor_stat,
 619                         desired_mech_type, crgetuid(cr),
 620                         "rpcsec_gss_secreate_pvt:gss_init_sec_context");
 621                 error = EACCES;
 622                 goto cleanup;
 623         }
 624 
 625         /*
 626          * if we got a token, pass it on
 627          */
 628         if (call_arg.length != 0) {
 629                 struct timeval timeout = {30, 0};
 630                 int      rpcsec_retry = isrefresh ?
 631                         RPCSEC_GSS_REFRESH_ATTEMPTS : 1;
 632                 uint32_t oldxid;
 633                 uint32_t zeroxid = 0;
 634 
 635                 bzero((char *)&call_res, sizeof (call_res));
 636 
 637                 (void) CLNT_CONTROL(clnt, CLGET_XID, (char *)&oldxid);
 638                 (void) CLNT_CONTROL(clnt, CLSET_XID, (char *)&zeroxid);
 639 
 640 
 641                 while (rpcsec_retry > 0) {
 642                         struct rpc_err rpcerr;
 643 
 644                         sigintr(&smask, INTERRUPT_OK);
 645 
 646                         callstat = clnt_call(clnt, NULLPROC,
 647                                 __xdr_rpc_gss_init_arg, (caddr_t)&call_arg,
 648                                 __xdr_rpc_gss_init_res, (caddr_t)&call_res,
 649                                 timeout);
 650 
 651                         sigunintr(&smask);
 652 
 653                         if (callstat == RPC_SUCCESS) {
 654                                 error = 0;
 655                                 if (isrefresh &&
 656                                     call_res.gss_major == GSS_S_FAILURE) {
 657 
 658                                         clock_t one_sec = drv_usectohz(1000000);
 659 
 660                                         rpcsec_retry--;
 661 
 662                                         /*
 663                                          * Pause a little and try again.
 664                                          */
 665 
 666                                         if (clnt->cl_nosignal == TRUE) {
 667                                                 delay(one_sec);
 668                                         } else {
 669                                                 if (delay_sig(one_sec)) {
 670                                                         error = EINTR;
 671                                                         break;
 672                                                 }
 673                                         }
 674                                         continue;
 675                                 }
 676                                 break;
 677                         }
 678 
 679                         if (callstat == RPC_TIMEDOUT) {
 680                                 error = ETIMEDOUT;
 681                                 break;
 682                         }
 683 
 684                         if (callstat == RPC_XPRTFAILED) {
 685                                 error = ECONNRESET;
 686                                 break;
 687                         }
 688 
 689                         if (callstat == RPC_INTR) {
 690                                 error = EINTR;
 691                                 break;
 692                         }
 693 
 694                         if (callstat == RPC_INPROGRESS) {
 695                                 continue;
 696                         }
 697 
 698                         clnt_geterr(clnt, &rpcerr);
 699                         error = rpcerr.re_errno;
 700                         break;
 701                 }
 702 
 703                 (void) CLNT_CONTROL(clnt, CLSET_XID, (char *)&oldxid);
 704 
 705                 (void) gss_release_buffer(minor_stat, &call_arg);
 706 
 707                 if (callstat != RPC_SUCCESS) {
 708                         RPCGSS_LOG(1,
 709                             "rpc_gss_seccreate_pvt: clnt_call failed %d\n",
 710                             callstat);
 711                         goto cleanup;
 712                 }
 713 
 714                 /*
 715                  * we have results - note that these need to be freed
 716                  */
 717                 free_results = 1;
 718 
 719                 if ((call_res.gss_major != GSS_S_COMPLETE) &&
 720                     (call_res.gss_major != GSS_S_CONTINUE_NEEDED)) {
 721                         RPCGSS_LOG1(1, "rpc_gss_seccreate_pvt: "
 722                                 "call_res gss_major %x, gss_minor %x\n",
 723                                 call_res.gss_major, call_res.gss_minor);
 724                         error = EACCES;
 725                         goto cleanup;
 726                 }
 727 
 728                 ap->gss_proc = RPCSEC_GSS_CONTINUE_INIT;
 729 
 730                 /*
 731                  * check for ctx_handle
 732                  */
 733                 if (ap->ctx_handle.length == 0) {
 734                         if (call_res.ctx_handle.length == 0) {
 735                                 RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt: zero "
 736                                         "length handle in response\n");
 737                                 error = EACCES;
 738                                 goto cleanup;
 739                         }
 740                         GSS_DUP_BUFFER(ap->ctx_handle,
 741                                         call_res.ctx_handle);
 742                 } else if (!GSS_BUFFERS_EQUAL(ap->ctx_handle,
 743                                                 call_res.ctx_handle)) {
 744                         RPCGSS_LOG0(1,
 745                         "rpc_gss_seccreate_pvt: ctx_handle not the same\n");
 746                         error = EACCES;
 747                         goto cleanup;
 748                 }
 749 
 750                 /*
 751                  * check for token
 752                  */
 753                 if (call_res.token.length != 0) {
 754                         if (*gssstat == GSS_S_COMPLETE) {
 755                                 RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt: non "
 756                                         "zero length token in response, but "
 757                                         "gsstat == GSS_S_COMPLETE\n");
 758                                 error = EACCES;
 759                                 goto cleanup;
 760                         }
 761                         GSS_DUP_BUFFER(input_token, call_res.token);
 762                         input_token_p = &input_token;
 763 
 764                 } else if (*gssstat != GSS_S_COMPLETE) {
 765                         RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt:zero length "
 766                                 "token in response, but "
 767                                 "gsstat != GSS_S_COMPLETE\n");
 768                         error = EACCES;
 769                         goto cleanup;
 770                 }
 771 
 772                 /* save the sequence window value; validate later */
 773                 ap->seq_window = call_res.seq_window;
 774                 xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
 775                 free_results = 0;
 776         }
 777 
 778         /*
 779          * results were okay.. continue if necessary
 780          */
 781         if (*gssstat == GSS_S_CONTINUE_NEEDED) {
 782                 goto next_token;
 783         }
 784 
 785         /*
 786          * Context is established. Now use kgss_export_sec_context and
 787          * kgss_import_sec_context to transfer the context from the user
 788          * land to kernel if the mechanism specific kernel module is
 789          * available.
 790          */
 791         *gssstat  = kgss_export_sec_context(minor_stat, ap->context,
 792                                                 &process_token);
 793         if (*gssstat == GSS_S_NAME_NOT_MN) {
 794                 RPCGSS_LOG(2, "rpc_gss_seccreate_pvt: export_sec_context "
 795                         "Kernel Module unavailable  gssstat = 0x%x\n",
 796                         *gssstat);
 797                 goto done;
 798         } else if (*gssstat != GSS_S_COMPLETE) {
 799                 (void) rpc_gss_display_status(*gssstat, *minor_stat,
 800                         isrefresh ? GSS_C_NULL_OID : *actual_mech_type,
 801                                         crgetuid(cr),
 802                         "rpcsec_gss_secreate_pvt:gss_export_sec_context");
 803                 (void) kgss_delete_sec_context(minor_stat,
 804                                         &ap->context, NULL);
 805                 error = EACCES;
 806                 goto cleanup;
 807         } else if (process_token.length == 0) {
 808                 RPCGSS_LOG0(1, "rpc_gss_seccreate_pvt:zero length "
 809                                 "token in response for export_sec_context, but "
 810                                 "gsstat == GSS_S_COMPLETE\n");
 811                 (void) kgss_delete_sec_context(minor_stat,
 812                                         &ap->context, NULL);
 813                 error = EACCES;
 814                 goto cleanup;
 815         } else
 816                 *gssstat = kgss_import_sec_context(minor_stat, &process_token,
 817                                                         ap->context);
 818 
 819         if (*gssstat == GSS_S_COMPLETE) {
 820                 (void) gss_release_buffer(minor_stat, &process_token);
 821         } else {
 822                 rpc_gss_display_status(*gssstat, *minor_stat,
 823                         desired_mech_type, crgetuid(cr),
 824                         "rpcsec_gss_secreate_pvt:gss_import_sec_context");
 825                 (void) kgss_delete_sec_context(minor_stat,
 826                                         &ap->context, NULL);
 827                 (void) gss_release_buffer(minor_stat, &process_token);
 828                 error = EACCES;
 829                 goto cleanup;
 830         }
 831 
 832 done:
 833         /*
 834          * Validate the sequence window - RFC 2203 section 5.2.3.1
 835          */
 836         if (!validate_seqwin(ap)) {
 837                 error = EACCES;
 838                 goto cleanup;
 839         }
 840 
 841         /*
 842          * Done!  Security context creation is successful.
 843          * Ready for exchanging data.
 844          */
 845         ap->established = TRUE;
 846         ap->seq_num = 1;
 847         ap->gss_proc = RPCSEC_GSS_DATA;
 848         ap->invalid = FALSE;
 849 
 850         clnt->cl_auth = save_auth;   /* restore cl_auth */
 851 
 852         return (0);
 853 
 854 cleanup:
 855         if (free_results)
 856                 xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
 857         clnt->cl_auth = save_auth;   /* restore cl_auth */
 858 
 859         /*
 860          * If need to retry for AUTH_REFRESH, do not cleanup the
 861          * auth private data.
 862          */
 863         if (isrefresh && (error == ETIMEDOUT || error == ECONNRESET)) {
 864                 return (error);
 865         }
 866 
 867         if (ap->context != NULL) {
 868                 rpc_gss_free_pvt(auth);
 869         }
 870 
 871         return (error? error : EACCES);
 872 }
 873 
 874 /*
 875  * Marshall credentials.
 876  */
 877 static bool_t
 878 marshall_creds(ap, xdrs, cred_buf_len)
 879         rpc_gss_data            *ap;
 880         XDR                     *xdrs;
 881         uint_t                  cred_buf_len;
 882 {
 883         rpc_gss_creds           ag_creds;
 884         char                    *cred_buf;
 885         struct opaque_auth      creds;
 886         XDR                     cred_xdrs;
 887 
 888         ag_creds.version = ap->version;
 889         ag_creds.gss_proc = ap->gss_proc;
 890         ag_creds.seq_num = ap->seq_num;
 891         ag_creds.service = ap->service;
 892 
 893         /*
 894          * If context has not been set up yet, use NULL handle.
 895          */
 896         if (ap->ctx_handle.length > 0)
 897                 ag_creds.ctx_handle = ap->ctx_handle;
 898         else {
 899                 ag_creds.ctx_handle.length = 0;
 900                 ag_creds.ctx_handle.value = NULL;
 901         }
 902 
 903         cred_buf = kmem_alloc(cred_buf_len, KM_SLEEP);
 904         xdrmem_create(&cred_xdrs, (caddr_t)cred_buf, cred_buf_len,
 905                                                                 XDR_ENCODE);
 906         if (!__xdr_rpc_gss_creds(&cred_xdrs, &ag_creds)) {
 907                 kmem_free(cred_buf, MAX_AUTH_BYTES);
 908                 XDR_DESTROY(&cred_xdrs);
 909                 return (FALSE);
 910         }
 911 
 912         creds.oa_flavor = RPCSEC_GSS;
 913         creds.oa_base = cred_buf;
 914         creds.oa_length = xdr_getpos(&cred_xdrs);
 915         XDR_DESTROY(&cred_xdrs);
 916 
 917         if (!xdr_opaque_auth(xdrs, &creds)) {
 918                 kmem_free(cred_buf, cred_buf_len);
 919                 return (FALSE);
 920         }
 921 
 922         kmem_free(cred_buf, cred_buf_len);
 923         return (TRUE);
 924 }
 925 
 926 /*
 927  * Marshall verifier.  The verifier is the checksum of the RPC header
 928  * up to and including the credential field.  The XDR handle that's
 929  * passed in has the header up to and including the credential field
 930  * encoded.  A pointer to the transmit buffer is also passed in.
 931  */
 932 static bool_t
 933 marshall_verf(ap, xdrs, buf)
 934         rpc_gss_data            *ap;
 935         XDR                     *xdrs;  /* send XDR */
 936         char                    *buf;   /* pointer of send buffer */
 937 {
 938         struct opaque_auth      verf;
 939         OM_uint32               major, minor;
 940         gss_buffer_desc         in_buf, out_buf;
 941         bool_t                  ret = FALSE;
 942 
 943         /*
 944          * If context is not established yet, use NULL verifier.
 945          */
 946         if (!ap->established) {
 947                 verf.oa_flavor = AUTH_NONE;
 948                 verf.oa_base = NULL;
 949                 verf.oa_length = 0;
 950                 return (xdr_opaque_auth(xdrs, &verf));
 951         }
 952 
 953         verf.oa_flavor = RPCSEC_GSS;
 954         in_buf.length = xdr_getpos(xdrs);
 955         in_buf.value = buf;
 956         if ((major = kgss_sign(&minor, ap->context, ap->qop, &in_buf,
 957                                 &out_buf)) != GSS_S_COMPLETE) {
 958                 if (major == GSS_S_CONTEXT_EXPIRED) {
 959                         ap->invalid = TRUE;
 960                 }
 961                 RPCGSS_LOG1(1,
 962                     "marshall_verf: kgss_sign failed GSS Major %x Minor %x\n",
 963                     major, minor);
 964                 return (FALSE);
 965         }
 966         verf.oa_base = out_buf.value;
 967         verf.oa_length = out_buf.length;
 968         ret = xdr_opaque_auth(xdrs, &verf);
 969         (void) gss_release_buffer(&minor, &out_buf);
 970 
 971         return (ret);
 972 }
 973 
 974 /*
 975  * Validate sequence window upon a successful RPCSEC_GSS INIT session.
 976  * The sequence window sent back by the server should be verifiable by
 977  * the verifier which is a checksum of the sequence window.
 978  */
 979 static bool_t
 980 validate_seqwin(rpc_gss_data *ap)
 981 {
 982         uint_t                  seq_win_net;
 983         OM_uint32               major = 0, minor = 0;
 984         gss_buffer_desc         msg_buf, tok_buf;
 985         int                     qop_state = 0;
 986 
 987         ASSERT(ap->verifier);
 988         ASSERT(ap->context);
 989         seq_win_net = (uint_t)htonl(ap->seq_window);
 990         msg_buf.length = sizeof (seq_win_net);
 991         msg_buf.value = (char *)&seq_win_net;
 992         tok_buf.length = ap->verifier->oa_length;
 993         tok_buf.value = ap->verifier->oa_base;
 994         major = kgss_verify(&minor, ap->context, &msg_buf, &tok_buf,
 995             &qop_state);
 996 
 997         if (major != GSS_S_COMPLETE) {
 998                 RPCGSS_LOG1(1,
 999                     "validate_seqwin: kgss_verify failed GSS Major "
1000                     "%x Minor %x\n", major, minor);
1001                 RPCGSS_LOG1(1, "seq_window %d, verf len %d ", ap->seq_window,
1002                     ap->verifier->oa_length);
1003                 return (FALSE);
1004         }
1005         return (TRUE);
1006 }
1007 
1008 /*
1009  * Validate RPC response verifier from server.  The response verifier
1010  * is the checksum of the request sequence number.
1011  */
1012 static bool_t
1013 rpc_gss_validate(auth, verf)
1014         AUTH                    *auth;
1015         struct opaque_auth      *verf;
1016 {
1017         rpc_gss_data            *ap = AUTH_PRIVATE(auth);
1018         uint_t                  seq_num_net;
1019         OM_uint32               major, minor;
1020         gss_buffer_desc         msg_buf, tok_buf;
1021         int                     qop_state;
1022 
1023         /*
1024          * If context is not established yet, save the verifier for
1025          * validating the sequence window later at the end of context
1026          * creation session.
1027          */
1028         if (!ap->established) {
1029             if (ap->verifier == NULL) {
1030                 ap->verifier = kmem_zalloc(sizeof (struct opaque_auth),
1031                                                 KM_SLEEP);
1032                 if (verf->oa_length > 0)
1033                     ap->verifier->oa_base = kmem_zalloc(verf->oa_length,
1034                                                 KM_SLEEP);
1035             } else {
1036                 if (ap->verifier->oa_length > 0)
1037                     kmem_free(ap->verifier->oa_base, ap->verifier->oa_length);
1038                 if (verf->oa_length > 0)
1039                     ap->verifier->oa_base = kmem_zalloc(verf->oa_length,
1040                                                 KM_SLEEP);
1041             }
1042             ap->verifier->oa_length = verf->oa_length;
1043             bcopy(verf->oa_base, ap->verifier->oa_base, verf->oa_length);
1044             return (TRUE);
1045         }
1046 
1047         seq_num_net = (uint_t)htonl(ap->seq_num);
1048         msg_buf.length = sizeof (seq_num_net);
1049         msg_buf.value = (char *)&seq_num_net;
1050         tok_buf.length = verf->oa_length;
1051         tok_buf.value = verf->oa_base;
1052         major = kgss_verify(&minor, ap->context, &msg_buf, &tok_buf,
1053                                 &qop_state);
1054         if (major != GSS_S_COMPLETE) {
1055                 RPCGSS_LOG1(1,
1056                 "rpc_gss_validate: kgss_verify failed GSS Major %x Minor %x\n",
1057                 major, minor);
1058                 return (FALSE);
1059         }
1060         return (TRUE);
1061 }
1062 
1063 /*
1064  * Refresh client context.  This is necessary sometimes because the
1065  * server will ocassionally destroy contexts based on LRU method, or
1066  * because of expired credentials.
1067  */
1068 static bool_t
1069 rpc_gss_refresh(auth, msg, cr)
1070         AUTH            *auth;
1071         struct rpc_msg  *msg;
1072         cred_t          *cr;
1073 {
1074         rpc_gss_data    *ap = AUTH_PRIVATE(auth);
1075         gss_ctx_id_t    ctx_sav = NULL;
1076         gss_buffer_desc ctx_hdle_sav = {0, NULL};
1077         uint_t          sn_sav, proc_sav;
1078         bool_t          est_sav;
1079         OM_uint32       gssstat, minor_stat;
1080         int error;
1081 
1082         /*
1083          * The context needs to be recreated only when the error status
1084          * returned from the server is one of the following:
1085          *      RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED
1086          * The existing context should not be destroyed unless the above
1087          * error status codes are received or if the context has not
1088          * been set up.
1089          */
1090 
1091         if (msg->rjcted_rply.rj_why == RPCSEC_GSS_NOCRED ||
1092                         msg->rjcted_rply.rj_why == RPCSEC_GSS_FAILED ||
1093                                                         !ap->established) {
1094                 /*
1095                  * Destroy the context if necessary.  Use the same memory
1096                  * for the new context since we've already passed a pointer
1097                  * to it to the user.
1098                  */
1099                 if (ap->context != NULL) {
1100                         ctx_sav = ap->context;
1101                         ap->context = NULL;
1102                 }
1103                 if (ap->ctx_handle.length != 0) {
1104                         ctx_hdle_sav.length = ap->ctx_handle.length;
1105                         ctx_hdle_sav.value = ap->ctx_handle.value;
1106                         ap->ctx_handle.length = 0;
1107                         ap->ctx_handle.value = NULL;
1108                 }
1109 
1110                 /*
1111                  * If the context was not already established, don't try to
1112                  * recreate it.
1113                  */
1114                 if (!ap->established) {
1115                         ap->invalid = TRUE;
1116                         RPCGSS_LOG0(1,
1117                         "rpc_gss_refresh: context was not established\n");
1118                         error = EINVAL;
1119                         goto out;
1120                 }
1121 
1122                 est_sav = ap->established;
1123                 sn_sav = ap->seq_num;
1124                 proc_sav = ap->gss_proc;
1125 
1126                 /*
1127                  * Recreate context.
1128                  */
1129                 error = rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth,
1130                                 ap, ap->mech_type, (gss_OID *)NULL, (int *)NULL,
1131                                 (OM_uint32 *)NULL, cr, 1);
1132 
1133                 switch (error) {
1134                 case 0:
1135                         RPCGSS_LOG(1,
1136                         "rpc_gss_refresh: auth %p refreshed\n", (void *)auth);
1137                         goto out;
1138 
1139                 case ETIMEDOUT:
1140                 case ECONNRESET:
1141                         RPCGSS_LOG0(1, "rpc_gss_refresh: try again\n");
1142 
1143                         if (ap->context != NULL) {
1144                             (void) kgss_delete_sec_context(&minor_stat,
1145                                         &ap->context, NULL);
1146                         }
1147                         if (ap->ctx_handle.length != 0) {
1148                             (void) gss_release_buffer(&minor_stat,
1149                                         &ap->ctx_handle);
1150                         }
1151 
1152                         /*
1153                          * Restore the original value for the caller to
1154                          * try again later.
1155                          */
1156                         ap->context = ctx_sav;
1157                         ap->ctx_handle.length = ctx_hdle_sav.length;
1158                         ap->ctx_handle.value = ctx_hdle_sav.value;
1159                         ap->established = est_sav;
1160                         ap->seq_num = sn_sav;
1161                         ap->gss_proc = proc_sav;
1162 
1163                         return (FALSE);
1164 
1165                 default:
1166                         ap->invalid = TRUE;
1167                         RPCGSS_LOG(1, "rpc_gss_refresh: can't refresh this "
1168                                 "auth, error=%d\n", error);
1169                         goto out;
1170                 }
1171         }
1172         RPCGSS_LOG0(1, "rpc_gss_refresh: don't refresh");
1173         return (FALSE);
1174 
1175 out:
1176         if (ctx_sav != NULL) {
1177                 (void) kgss_delete_sec_context(&minor_stat,
1178                                 &ctx_sav, NULL);
1179         }
1180         if (ctx_hdle_sav.length != 0) {
1181                 (void) gss_release_buffer(&minor_stat, &ctx_hdle_sav);
1182         }
1183 
1184         return (error == 0);
1185 }
1186 
1187 /*
1188  * Destroy a context.
1189  */
1190 static void
1191 rpc_gss_destroy(auth)
1192         AUTH            *auth;
1193 {
1194         rpc_gss_data    *ap = AUTH_PRIVATE(auth);
1195 
1196         /*
1197          *  XXX Currently, we do not ping the server (rpc_gss_destroy_pvt)
1198          *  to destroy the context in the server cache.
1199          *  We assume there is a good LRU/aging mechanism for the
1200          *  context cache on the server side.
1201          */
1202         rpc_gss_free_pvt(auth);
1203         kmem_free((char *)ap, sizeof (*ap));
1204         kmem_free(auth, sizeof (*auth));
1205 }
1206 
1207 /*
1208  * Private interface to free memory allocated in the rpcsec_gss private
1209  * data structure (rpc_gss_data).
1210  */
1211 static void
1212 rpc_gss_free_pvt(auth)
1213         AUTH            *auth;
1214 {
1215         OM_uint32       minor_stat;
1216         rpc_gss_data    *ap = AUTH_PRIVATE(auth);
1217 
1218         if (ap->ctx_handle.length != 0) {
1219                 (void) gss_release_buffer(&minor_stat, &ap->ctx_handle);
1220                 ap->ctx_handle.length = 0;
1221                 ap->ctx_handle.value = NULL;
1222         }
1223 
1224         /*
1225          * Destroy local GSS context.
1226          */
1227         if (ap->context != NULL) {
1228                 (void) kgss_delete_sec_context(&minor_stat, &ap->context, NULL);
1229                 ap->context = NULL;
1230         }
1231 
1232         /*
1233          * Looks like we need to release default credentials if we use it.
1234          * Non-default creds need to be released by user.
1235          */
1236         if (ap->my_cred == GSS_C_NO_CREDENTIAL)
1237                 (void) kgss_release_cred(&minor_stat, &ap->my_cred,
1238                                         crgetuid(CRED()));
1239 
1240         /*
1241          * Release any internal name structures.
1242          */
1243         if (ap->target_name != NULL) {
1244                 (void) gss_release_name(&minor_stat, &ap->target_name);
1245                 ap->target_name = NULL;
1246         }
1247 
1248         /*
1249          * Free mech_type oid structure.
1250          */
1251         if (ap->mech_type != NULL) {
1252                 kgss_free_oid(ap->mech_type);
1253                 ap->mech_type = NULL;
1254         }
1255 
1256         /*
1257          * Free the verifier saved for sequence window checking.
1258          */
1259         if (ap->verifier != NULL) {
1260             if (ap->verifier->oa_length > 0) {
1261                 kmem_free(ap->verifier->oa_base, ap->verifier->oa_length);
1262             }
1263             kmem_free(ap->verifier, sizeof (struct opaque_auth));
1264             ap->verifier = NULL;
1265         }
1266 }
1267 
1268 #if 0
1269 /*
1270  * XXX this function is not used right now.
1271  * There is a client handle issue needs to be resolved.
1272  *
1273  * This is a private interface which will destroy a context
1274  * without freeing up the memory used by it.  We need to do this when
1275  * a refresh fails, for example, so the user will still have a handle.
1276  */
1277 static void
1278 rpc_gss_destroy_pvt(auth)
1279         AUTH            *auth;
1280 {
1281         struct timeval  timeout;
1282         rpc_gss_data    *ap = AUTH_PRIVATE(auth);
1283 
1284         /*
1285          * If we have a server context id, inform server that we are
1286          * destroying the context.
1287          */
1288         if (ap->ctx_handle.length != 0) {
1289                 uint32_t oldxid;
1290                 uint32_t zeroxid = 0;
1291 
1292                 ap->gss_proc = RPCSEC_GSS_DESTROY;
1293                 timeout.tv_sec = 10;
1294                 timeout.tv_usec = 0;
1295                 (void) CLNT_CONTROL(ap->clnt, CLGET_XID, (char *)&oldxid);
1296                 (void) CLNT_CONTROL(ap->clnt, CLSET_XID, (char *)&zeroxid);
1297                 (void) clnt_call(ap->clnt, NULLPROC, xdr_void, NULL,
1298                                                 xdr_void, NULL, timeout);
1299                 (void) CLNT_CONTROL(ap->clnt, CLSET_XID, (char *)&oldxid);
1300         }
1301 
1302         rpc_gss_free_pvt(auth);
1303 }
1304 #endif
1305 
1306 /*
1307  * Wrap client side data.  The encoded header is passed in through
1308  * buf and buflen.  The header is up to but not including the
1309  * credential field.
1310  */
1311 bool_t
1312 rpc_gss_wrap(auth, buf, buflen, out_xdrs, xdr_func, xdr_ptr)
1313         AUTH                    *auth;
1314         char                    *buf;           /* encoded header */
1315 /* has been changed to u_int in the user land */
1316         uint_t                  buflen;         /* encoded header length */
1317         XDR                     *out_xdrs;
1318         xdrproc_t               xdr_func;
1319         caddr_t                 xdr_ptr;
1320 {
1321         rpc_gss_data            *ap = AUTH_PRIVATE(auth);
1322         XDR                     xdrs;
1323         char                    *tmp_buf;
1324         uint_t                  xdr_buf_len, cred_buf_len;
1325 
1326 /*
1327  *  Here is how MAX_SIGNED_LEN is estimated.
1328  *  Signing a 48 bytes buffer using des_cbc_md5 would end up with
1329  *  a buffer length 33 (padded data + 16 bytes of seq_num/checksum).
1330  *  Current known max seq_num/checksum size is 24 bytes.
1331  *  88 is derived from RNDUP(33+(24-16)) * 2.
1332  */
1333 #define MAX_SIGNED_LEN  88
1334 
1335         /*
1336          * Reject an invalid context.
1337          */
1338         if (ap->invalid) {
1339                 RPCGSS_LOG0(1, "rpc_gss_wrap: reject an invalid context\n");
1340                 return (FALSE);
1341         }
1342 
1343         /*
1344          * If context is established, bump up sequence number.
1345          */
1346         if (ap->established)
1347                 ap->seq_num++;
1348 
1349         /*
1350          * Create the header in a temporary XDR context and buffer
1351          * before putting it out.
1352          */
1353         cred_buf_len = RNDUP(sizeof (ap->version) + sizeof (ap->gss_proc) +
1354                         sizeof (ap->seq_num) + sizeof (ap->service) +
1355                         sizeof (ap->ctx_handle) + ap->ctx_handle.length);
1356 
1357         xdr_buf_len = buflen + cred_buf_len + sizeof (struct opaque_auth) +
1358                         MAX_SIGNED_LEN;
1359         tmp_buf = kmem_alloc(xdr_buf_len, KM_SLEEP);
1360         xdrmem_create(&xdrs, tmp_buf, xdr_buf_len, XDR_ENCODE);
1361         if (!XDR_PUTBYTES(&xdrs, buf, buflen)) {
1362                 kmem_free(tmp_buf, xdr_buf_len);
1363                 RPCGSS_LOG0(1, "rpc_gss_wrap: xdr putbytes failed\n");
1364                 return (FALSE);
1365         }
1366 
1367         /*
1368          * create cred field
1369          */
1370         if (!marshall_creds(ap, &xdrs, cred_buf_len)) {
1371                 kmem_free(tmp_buf, xdr_buf_len);
1372                 RPCGSS_LOG0(1, "rpc_gss_wrap: marshall_creds failed\n");
1373                 return (FALSE);
1374         }
1375 
1376         /*
1377          * create verifier
1378          */
1379         if (!marshall_verf(ap, &xdrs, tmp_buf)) {
1380                 kmem_free(tmp_buf, xdr_buf_len);
1381                 RPCGSS_LOG0(1, "rpc_gss_wrap: marshall_verf failed\n");
1382                 return (FALSE);
1383         }
1384 
1385         /*
1386          * write out header and destroy temp structures
1387          */
1388         if (!XDR_PUTBYTES(out_xdrs, tmp_buf, XDR_GETPOS(&xdrs))) {
1389                 kmem_free(tmp_buf, xdr_buf_len);
1390                 RPCGSS_LOG0(1, "rpc_gss_wrap: write out header failed\n");
1391                 return (FALSE);
1392         }
1393         XDR_DESTROY(&xdrs);
1394         kmem_free(tmp_buf, xdr_buf_len);
1395 
1396         /*
1397          * If context is not established, or if neither integrity
1398          * nor privacy is used, just XDR encode data.
1399          */
1400         if (!ap->established || ap->service == rpc_gss_svc_none) {
1401                 return ((*xdr_func)(out_xdrs, xdr_ptr));
1402         }
1403 
1404         return (__rpc_gss_wrap_data(ap->service, ap->qop, ap->context,
1405                                 ap->seq_num, out_xdrs, xdr_func, xdr_ptr));
1406 }
1407 
1408 /*
1409  * Unwrap received data.
1410  */
1411 bool_t
1412 rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
1413         AUTH                    *auth;
1414         XDR                     *in_xdrs;
1415         bool_t                  (*xdr_func)();
1416         caddr_t                 xdr_ptr;
1417 {
1418         rpc_gss_data            *ap = AUTH_PRIVATE(auth);
1419 
1420         /*
1421          * If context is not established, of if neither integrity
1422          * nor privacy is used, just XDR encode data.
1423          */
1424         if (!ap->established || ap->service == rpc_gss_svc_none)
1425                 return ((*xdr_func)(in_xdrs, xdr_ptr));
1426 
1427         return (__rpc_gss_unwrap_data(ap->service,
1428                                 ap->context,
1429                                 ap->seq_num,
1430                                 ap->qop,
1431                                 in_xdrs, xdr_func, xdr_ptr));
1432 }
1433 
1434 /*
1435  *  Revoke an GSSAPI based security credentials
1436  *  from the cache table.
1437  */
1438 int
1439 rpc_gss_revauth(uid_t uid, rpc_gss_OID mech)
1440 {
1441         struct ga_cache_entry *next, *prev, *cur;
1442         rpc_gss_data *ap;
1443         zoneid_t zoneid = getzoneid();
1444         int i;
1445 
1446         /*
1447          *  Check the cache table against the uid and the
1448          *  mechanism type.
1449          */
1450         rw_enter(&ga_cache_table_lock, RW_WRITER);
1451         for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
1452                 prev = NULL;
1453                 for (cur = ga_cache_table[i]; cur; cur = next) {
1454                         NOT_DEAD(cur);
1455                         next = cur->next;
1456                         NOT_DEAD(next);
1457                         ap = AUTH_PRIVATE(cur->auth);
1458                         if (__rpc_gss_oids_equal(ap->mech_type,
1459                             (gss_OID) mech) && (cur->uid == uid) &&
1460                             (cur->zoneid == zoneid)) {
1461                                 if (cur->in_use) {
1462                                         RPCGSS_LOG(2, "rpc_gss_revauth:invalid "
1463                                             "auth %p\n", (void *)cur->auth);
1464                                         ap->invalid = TRUE;
1465                                 } else {
1466                                         RPCGSS_LOG(2, "rpc_gss_revauth:destroy "
1467                                             "auth %p\n", (void *)cur->auth);
1468                                         rpc_gss_destroy(cur->auth);
1469                                         kmem_cache_free(ga_cache_handle,
1470                                             (void *)cur);
1471                                 }
1472                                 if (prev == NULL) {
1473                                         ga_cache_table[i] = next;
1474                                 } else {
1475                                         prev->next = next;
1476                                         NOT_DEAD(prev->next);
1477                                 }
1478                         } else {
1479                                 prev = cur;
1480                         }
1481                 }
1482         }
1483         rw_exit(&ga_cache_table_lock);
1484 
1485         return (0);
1486 }
1487 
1488 
1489 /*
1490  *  Delete all the entries indexed by the cache_key.
1491  *
1492  *  For example, the cache_key used for NFS is the address of the
1493  *  security entry for each mount point.  When the file system is unmounted,
1494  *  all the cache entries indexed by this key should be deleted.
1495  */
1496 void
1497 rpc_gss_secpurge(void *cache_key)
1498 {
1499         struct ga_cache_entry *next, *prev, *cur;
1500         int i;
1501 
1502         /*
1503          *  Check the cache table against the cache_key.
1504          */
1505         rw_enter(&ga_cache_table_lock, RW_WRITER);
1506         for (i = 0; i < GSSAUTH_TABLESIZE; i++) {
1507                 prev = NULL;
1508                 for (cur = ga_cache_table[i]; cur; cur = next) {
1509                         NOT_DEAD(cur);
1510                         next = cur->next;
1511                         NOT_DEAD(next);
1512                         if (cache_key == cur->cache_key) {
1513                                 RPCGSS_LOG(2, "rpc_gss_secpurge: destroy auth "
1514                                     "%p\n", (void *)cur->auth);
1515                                 if (cur->in_use == FALSE)
1516                                         rpc_gss_destroy(cur->auth);
1517                                 kmem_cache_free(ga_cache_handle, (void *)cur);
1518                                 if (prev == NULL) {
1519                                         ga_cache_table[i] = next;
1520                                 } else {
1521                                         NOT_DEAD(prev->next);
1522                                         prev->next = next;
1523                                 }
1524                         } else {
1525                                 prev = cur;
1526                         }
1527                 }
1528         }
1529         rw_exit(&ga_cache_table_lock);
1530 }
1531 
1532 /*
1533  * Function: rpc_gss_nextverf.  Not used.
1534  */
1535 static void
1536 rpc_gss_nextverf()
1537 {
1538 }
1539 
1540 /*
1541  * Function: rpc_gss_marshall - no op routine.
1542  *              rpc_gss_wrap() is doing the marshalling.
1543  */
1544 /*ARGSUSED*/
1545 static bool_t
1546 rpc_gss_marshall(auth, xdrs)
1547         AUTH            *auth;
1548         XDR             *xdrs;
1549 {
1550         return (TRUE);
1551 }
1552 
1553 /*
1554  * Set service defaults.
1555  * Not supported yet.
1556  */
1557 /* ARGSUSED */
1558 bool_t
1559 rpc_gss_set_defaults(auth, service, qop)
1560         AUTH                    *auth;
1561         rpc_gss_service_t       service;
1562         uint_t                  qop;
1563 {
1564         return (FALSE);
1565 }
1566 
1567 /* ARGSUSED */
1568 int
1569 rpc_gss_max_data_length(AUTH *rpcgss_handle, int max_tp_unit_len)
1570 {
1571         return (0);
1572 }
1573 
1574 rpc_gss_service_t
1575 rpc_gss_get_service_type(AUTH *auth)
1576 {
1577         rpc_gss_data            *ap = AUTH_PRIVATE(auth);
1578 
1579         return (ap->service);
1580 }