2 * SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 1990 The Regents of the University of California.
5 * Copyright (c) 2008 Doug Rabson
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 Copyright (c) 2000 The Regents of the University of Michigan.
35 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
36 All rights reserved, all wrongs reversed.
38 Redistribution and use in source and binary forms, with or without
39 modification, are permitted provided that the following conditions
42 1. Redistributions of source code must retain the above copyright
43 notice, this list of conditions and the following disclaimer.
44 2. Redistributions in binary form must reproduce the above copyright
45 notice, this list of conditions and the following disclaimer in the
46 documentation and/or other materials provided with the distribution.
47 3. Neither the name of the University nor the names of its
48 contributors may be used to endorse or promote products derived
49 from this software without specific prior written permission.
51 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
52 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
58 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
59 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
60 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
66 #include <sys/cdefs.h>
67 __FBSDID("$FreeBSD$");
69 #include <sys/param.h>
70 #include <sys/systm.h>
72 #include <sys/kernel.h>
75 #include <sys/malloc.h>
77 #include <sys/mutex.h>
80 #include <sys/ucred.h>
83 #include <rpc/rpcsec_gss.h>
85 #include "rpcsec_gss_int.h"
87 static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
88 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
89 static void svc_rpc_gss_release(SVCAUTH *);
90 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
91 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
93 static struct svc_auth_ops svc_auth_gss_ops = {
99 struct sx svc_rpc_gss_lock;
101 struct svc_rpc_gss_callback {
102 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
103 rpc_gss_callback_t cb_callback;
105 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
106 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
108 struct svc_rpc_gss_svc_name {
109 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
113 gss_cred_id_t sn_cred;
117 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
118 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
120 enum svc_rpc_gss_client_state {
121 CLIENT_NEW, /* still authenticating */
122 CLIENT_ESTABLISHED, /* context established */
123 CLIENT_STALE /* garbage to collect */
126 #define SVC_RPC_GSS_SEQWINDOW 128
128 struct svc_rpc_gss_clientid {
129 unsigned long ci_hostid;
130 uint32_t ci_boottime;
134 struct svc_rpc_gss_client {
135 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
136 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
137 volatile u_int cl_refs;
139 struct svc_rpc_gss_clientid cl_id;
140 time_t cl_expiration; /* when to gc */
141 enum svc_rpc_gss_client_state cl_state; /* client state */
142 bool_t cl_locked; /* fixed service+qop */
143 gss_ctx_id_t cl_ctx; /* context id */
144 gss_cred_id_t cl_creds; /* delegated creds */
145 gss_name_t cl_cname; /* client name */
146 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
147 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
148 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
149 struct ucred *cl_cred; /* kernel-style credentials */
150 int cl_rpcflavor; /* RPC pseudo sec flavor */
151 bool_t cl_done_callback; /* TRUE after call */
152 void *cl_cookie; /* user cookie from callback */
153 gid_t cl_gid_storage[NGROUPS];
154 gss_OID cl_mech; /* mechanism */
155 gss_qop_t cl_qop; /* quality of protection */
156 uint32_t cl_seqlast; /* sequence window origin */
157 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
159 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
162 * This structure holds enough information to unwrap arguments or wrap
163 * results for a given request. We use the rq_clntcred area for this
164 * (which is a per-request buffer).
166 struct svc_rpc_gss_cookedcred {
167 struct svc_rpc_gss_client *cc_client;
168 rpc_gss_service_t cc_service;
172 #define CLIENT_HASH_SIZE 256
173 #define CLIENT_MAX 1024
174 u_int svc_rpc_gss_client_max = CLIENT_MAX;
175 u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
177 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW, 0, "RPC");
178 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW, 0, "GSS");
180 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
181 &svc_rpc_gss_client_max, 0,
182 "Max number of rpc-gss clients");
184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
185 &svc_rpc_gss_client_hash_size, 0,
186 "Size of rpc-gss client hash table");
188 static u_int svc_rpc_gss_lifetime_max = 0;
189 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
190 &svc_rpc_gss_lifetime_max, 0,
191 "Maximum lifetime (seconds) of rpc-gss clients");
193 static u_int svc_rpc_gss_client_count;
194 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
195 &svc_rpc_gss_client_count, 0,
196 "Number of rpc-gss clients");
198 struct svc_rpc_gss_client_list *svc_rpc_gss_client_hash;
199 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
200 static uint32_t svc_rpc_gss_next_clientid = 1;
203 svc_rpc_gss_init(void *arg)
207 svc_rpc_gss_client_hash = mem_alloc(sizeof(struct svc_rpc_gss_client_list) * svc_rpc_gss_client_hash_size);
208 for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
209 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
210 TAILQ_INIT(&svc_rpc_gss_clients);
211 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
212 sx_init(&svc_rpc_gss_lock, "gsslock");
214 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
217 rpc_gss_set_callback(rpc_gss_callback_t *cb)
219 struct svc_rpc_gss_callback *scb;
221 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
223 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
226 scb->cb_callback = *cb;
227 sx_xlock(&svc_rpc_gss_lock);
228 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
229 sx_xunlock(&svc_rpc_gss_lock);
235 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
237 struct svc_rpc_gss_callback *scb;
239 sx_xlock(&svc_rpc_gss_lock);
240 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
241 if (scb->cb_callback.program == cb->program
242 && scb->cb_callback.version == cb->version
243 && scb->cb_callback.callback == cb->callback) {
244 SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
245 svc_rpc_gss_callback, cb_link);
246 sx_xunlock(&svc_rpc_gss_lock);
247 mem_free(scb, sizeof(*scb));
251 sx_xunlock(&svc_rpc_gss_lock);
255 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
257 OM_uint32 maj_stat, min_stat;
258 gss_buffer_desc namebuf;
260 gss_OID_set_desc oid_set;
263 oid_set.elements = sname->sn_mech;
265 namebuf.value = (void *) sname->sn_principal;
266 namebuf.length = strlen(sname->sn_principal);
268 maj_stat = gss_import_name(&min_stat, &namebuf,
269 GSS_C_NT_HOSTBASED_SERVICE, &name);
270 if (maj_stat != GSS_S_COMPLETE)
273 if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
274 gss_release_cred(&min_stat, &sname->sn_cred);
276 maj_stat = gss_acquire_cred(&min_stat, name,
277 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
279 if (maj_stat != GSS_S_COMPLETE) {
280 gss_release_name(&min_stat, &name);
283 gss_release_name(&min_stat, &name);
289 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
290 u_int req_time, u_int program, u_int version)
292 struct svc_rpc_gss_svc_name *sname;
295 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
298 sname = mem_alloc(sizeof(*sname));
301 sname->sn_principal = strdup(principal, M_RPC);
302 sname->sn_mech = mech_oid;
303 sname->sn_req_time = req_time;
304 sname->sn_cred = GSS_C_NO_CREDENTIAL;
305 sname->sn_program = program;
306 sname->sn_version = version;
308 if (!rpc_gss_acquire_svc_cred(sname)) {
309 free(sname->sn_principal, M_RPC);
310 mem_free(sname, sizeof(*sname));
314 sx_xlock(&svc_rpc_gss_lock);
315 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
316 sx_xunlock(&svc_rpc_gss_lock);
322 rpc_gss_clear_svc_name(u_int program, u_int version)
325 struct svc_rpc_gss_svc_name *sname;
327 sx_xlock(&svc_rpc_gss_lock);
328 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
329 if (sname->sn_program == program
330 && sname->sn_version == version) {
331 SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
332 svc_rpc_gss_svc_name, sn_link);
333 sx_xunlock(&svc_rpc_gss_lock);
334 gss_release_cred(&min_stat, &sname->sn_cred);
335 free(sname->sn_principal, M_RPC);
336 mem_free(sname, sizeof(*sname));
340 sx_xunlock(&svc_rpc_gss_lock);
344 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
345 const char *mech, const char *name, const char *node, const char *domain)
347 OM_uint32 maj_stat, min_stat;
351 gss_name_t gss_name, gss_mech_name;
352 rpc_gss_principal_t result;
354 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
358 * Construct a gss_buffer containing the full name formatted
359 * as "name/node@domain" where node and domain are optional.
361 namelen = strlen(name) + 1;
363 namelen += strlen(node) + 1;
366 namelen += strlen(domain) + 1;
369 buf.value = mem_alloc(namelen);
370 buf.length = namelen;
371 strcpy((char *) buf.value, name);
373 strcat((char *) buf.value, "/");
374 strcat((char *) buf.value, node);
377 strcat((char *) buf.value, "@");
378 strcat((char *) buf.value, domain);
382 * Convert that to a gss_name_t and then convert that to a
383 * mechanism name in the selected mechanism.
385 maj_stat = gss_import_name(&min_stat, &buf,
386 GSS_C_NT_USER_NAME, &gss_name);
387 mem_free(buf.value, buf.length);
388 if (maj_stat != GSS_S_COMPLETE) {
389 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
392 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
394 if (maj_stat != GSS_S_COMPLETE) {
395 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
397 gss_release_name(&min_stat, &gss_name);
400 gss_release_name(&min_stat, &gss_name);
403 * Export the mechanism name and use that to construct the
404 * rpc_gss_principal_t result.
406 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
407 if (maj_stat != GSS_S_COMPLETE) {
408 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
409 gss_release_name(&min_stat, &gss_mech_name);
412 gss_release_name(&min_stat, &gss_mech_name);
414 result = mem_alloc(sizeof(int) + buf.length);
416 gss_release_buffer(&min_stat, &buf);
419 result->len = buf.length;
420 memcpy(result->name, buf.value, buf.length);
421 gss_release_buffer(&min_stat, &buf);
428 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
429 rpc_gss_ucred_t **ucred, void **cookie)
431 struct svc_rpc_gss_cookedcred *cc;
432 struct svc_rpc_gss_client *client;
434 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
437 cc = req->rq_clntcred;
438 client = cc->cc_client;
440 *rcred = &client->cl_rawcred;
442 *ucred = &client->cl_ucred;
444 *cookie = client->cl_cookie;
449 * This simpler interface is used by svc_getcred to copy the cred data
450 * into a kernel cred structure.
453 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
456 struct svc_rpc_gss_cookedcred *cc;
457 struct svc_rpc_gss_client *client;
460 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
463 cc = req->rq_clntcred;
464 client = cc->cc_client;
467 *flavorp = client->cl_rpcflavor;
469 if (client->cl_cred) {
470 *crp = crhold(client->cl_cred);
474 uc = &client->cl_ucred;
475 cr = client->cl_cred = crget();
476 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
477 cr->cr_rgid = cr->cr_svgid = uc->gid;
478 crsetgroups(cr, uc->gidlen, uc->gidlist);
479 cr->cr_prison = &prison0;
480 prison_hold(cr->cr_prison);
487 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
489 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
490 struct svc_rpc_gss_client *client = cc->cc_client;
493 OM_uint32 maj_stat, min_stat;
496 switch (client->cl_rawcred.service) {
497 case rpc_gss_svc_none:
498 return (max_tp_unit_len);
501 case rpc_gss_svc_default:
502 case rpc_gss_svc_integrity:
506 case rpc_gss_svc_privacy:
514 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
515 client->cl_qop, max_tp_unit_len, &max);
517 if (maj_stat == GSS_S_COMPLETE) {
523 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
529 static struct svc_rpc_gss_client *
530 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
532 struct svc_rpc_gss_client *client;
533 struct svc_rpc_gss_client_list *list;
534 struct timeval boottime;
535 unsigned long hostid;
537 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
539 getcredhostid(curthread->td_ucred, &hostid);
540 getboottime(&boottime);
541 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
544 list = &svc_rpc_gss_client_hash[id->ci_id % svc_rpc_gss_client_hash_size];
545 sx_xlock(&svc_rpc_gss_lock);
546 TAILQ_FOREACH(client, list, cl_link) {
547 if (client->cl_id.ci_id == id->ci_id) {
549 * Move this client to the front of the LRU
552 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
553 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
555 refcount_acquire(&client->cl_refs);
559 sx_xunlock(&svc_rpc_gss_lock);
564 static struct svc_rpc_gss_client *
565 svc_rpc_gss_create_client(void)
567 struct svc_rpc_gss_client *client;
568 struct svc_rpc_gss_client_list *list;
569 struct timeval boottime;
570 unsigned long hostid;
572 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
574 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
575 memset(client, 0, sizeof(struct svc_rpc_gss_client));
578 * Set the initial value of cl_refs to two. One for the caller
579 * and the other to hold onto the client structure until it expires.
581 refcount_init(&client->cl_refs, 2);
582 sx_init(&client->cl_lock, "GSS-client");
583 getcredhostid(curthread->td_ucred, &hostid);
584 client->cl_id.ci_hostid = hostid;
585 getboottime(&boottime);
586 client->cl_id.ci_boottime = boottime.tv_sec;
587 client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
590 * Start the client off with a short expiration time. We will
591 * try to get a saner value from the client creds later.
593 client->cl_state = CLIENT_NEW;
594 client->cl_locked = FALSE;
595 client->cl_expiration = time_uptime + 5*60;
597 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
598 sx_xlock(&svc_rpc_gss_lock);
599 TAILQ_INSERT_HEAD(list, client, cl_link);
600 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
601 svc_rpc_gss_client_count++;
602 sx_xunlock(&svc_rpc_gss_lock);
607 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
611 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
614 gss_delete_sec_context(&min_stat,
615 &client->cl_ctx, GSS_C_NO_BUFFER);
617 if (client->cl_cname)
618 gss_release_name(&min_stat, &client->cl_cname);
620 if (client->cl_rawcred.client_principal)
621 mem_free(client->cl_rawcred.client_principal,
622 sizeof(*client->cl_rawcred.client_principal)
623 + client->cl_rawcred.client_principal->len);
626 crfree(client->cl_cred);
628 sx_destroy(&client->cl_lock);
629 mem_free(client, sizeof(*client));
633 * Drop a reference to a client and free it if that was the last reference.
636 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
639 if (!refcount_release(&client->cl_refs))
641 svc_rpc_gss_destroy_client(client);
645 * Remove a client from our global lists.
646 * Must be called with svc_rpc_gss_lock held.
649 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
651 struct svc_rpc_gss_client_list *list;
653 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
654 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
655 TAILQ_REMOVE(list, client, cl_link);
656 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
657 svc_rpc_gss_client_count--;
661 * Remove a client from our global lists and free it if we can.
664 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
666 struct svc_rpc_gss_client_list *list;
667 struct svc_rpc_gss_client *tclient;
669 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
670 sx_xlock(&svc_rpc_gss_lock);
671 TAILQ_FOREACH(tclient, list, cl_link) {
673 * Make sure this client has not already been removed
674 * from the lists by svc_rpc_gss_forget_client() or
675 * svc_rpc_gss_forget_client_locked().
677 if (client == tclient) {
678 svc_rpc_gss_forget_client_locked(client);
679 sx_xunlock(&svc_rpc_gss_lock);
680 svc_rpc_gss_release_client(client);
684 sx_xunlock(&svc_rpc_gss_lock);
688 svc_rpc_gss_timeout_clients(void)
690 struct svc_rpc_gss_client *client;
691 time_t now = time_uptime;
693 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
696 * First enforce the max client limit. We keep
697 * svc_rpc_gss_clients in LRU order.
699 sx_xlock(&svc_rpc_gss_lock);
700 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
701 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
702 svc_rpc_gss_forget_client_locked(client);
703 sx_xunlock(&svc_rpc_gss_lock);
704 svc_rpc_gss_release_client(client);
705 sx_xlock(&svc_rpc_gss_lock);
706 client = TAILQ_LAST(&svc_rpc_gss_clients,
707 svc_rpc_gss_client_list);
710 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
711 if (client->cl_state == CLIENT_STALE
712 || now > client->cl_expiration) {
713 svc_rpc_gss_forget_client_locked(client);
714 sx_xunlock(&svc_rpc_gss_lock);
715 rpc_gss_log_debug("expiring client %p", client);
716 svc_rpc_gss_release_client(client);
717 sx_xlock(&svc_rpc_gss_lock);
721 sx_xunlock(&svc_rpc_gss_lock);
726 * OID<->string routines. These are uuuuugly.
729 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
732 unsigned long number;
734 size_t string_length;
739 /* Decoded according to krb5/gssapi_krb5.c */
741 /* First determine the size of the string */
745 cp = (unsigned char *) oid->elements;
746 number = (unsigned long) cp[0];
747 sprintf(numstr, "%ld ", number/40);
748 string_length += strlen(numstr);
749 sprintf(numstr, "%ld ", number%40);
750 string_length += strlen(numstr);
751 for (i=1; i<oid->length; i++) {
752 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
753 number = (number << 7) | (cp[i] & 0x7f);
758 return(GSS_S_FAILURE);
760 if ((cp[i] & 0x80) == 0) {
761 sprintf(numstr, "%ld ", number);
762 string_length += strlen(numstr);
768 * If we get here, we've calculated the length of "n n n ... n ". Add 4
769 * here for "{ " and "}\0".
772 if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
774 number = (unsigned long) cp[0];
775 sprintf(numstr, "%ld ", number/40);
777 sprintf(numstr, "%ld ", number%40);
780 cp = (unsigned char *) oid->elements;
781 for (i=1; i<oid->length; i++) {
782 number = (number << 7) | (cp[i] & 0x7f);
783 if ((cp[i] & 0x80) == 0) {
784 sprintf(numstr, "%ld ", number);
790 oid_str->length = strlen(bp)+1;
791 oid_str->value = (void *) bp;
793 return(GSS_S_COMPLETE);
796 return(GSS_S_FAILURE);
801 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
802 const gss_name_t name)
804 OM_uint32 maj_stat, min_stat;
805 rpc_gss_ucred_t *uc = &client->cl_ucred;
810 uc->gidlist = client->cl_gid_storage;
813 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
814 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
815 if (GSS_ERROR(maj_stat))
818 uc->gidlen = numgroups;
822 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
824 static gss_OID_desc krb5_mech_oid =
825 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
828 * Attempt to translate mech type and service into a
829 * 'pseudo flavor'. Hardwire in krb5 support for now.
831 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
832 switch (client->cl_rawcred.service) {
833 case rpc_gss_svc_default:
834 case rpc_gss_svc_none:
835 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
837 case rpc_gss_svc_integrity:
838 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
840 case rpc_gss_svc_privacy:
841 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
845 client->cl_rpcflavor = RPCSEC_GSS;
850 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
851 struct svc_req *rqst,
852 struct rpc_gss_init_res *gr,
853 struct rpc_gss_cred *gc)
855 gss_buffer_desc recv_tok;
857 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
858 OM_uint32 cred_lifetime;
859 struct svc_rpc_gss_svc_name *sname;
861 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
863 /* Deserialize arguments. */
864 memset(&recv_tok, 0, sizeof(recv_tok));
866 if (!svc_getargs(rqst,
867 (xdrproc_t) xdr_gss_buffer_desc,
868 (caddr_t) &recv_tok)) {
869 client->cl_state = CLIENT_STALE;
874 * First time round, try all the server names we have until
875 * one matches. Afterwards, stick with that one.
877 sx_xlock(&svc_rpc_gss_lock);
878 if (!client->cl_sname) {
879 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
880 if (sname->sn_program == rqst->rq_prog
881 && sname->sn_version == rqst->rq_vers) {
883 gr->gr_major = gss_accept_sec_context(
888 GSS_C_NO_CHANNEL_BINDINGS,
896 GSS_S_CREDENTIALS_EXPIRED) {
898 * Either our creds really did
902 if (rpc_gss_acquire_svc_cred(sname))
905 client->cl_sname = sname;
910 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
912 sx_xunlock(&svc_rpc_gss_lock);
916 gr->gr_major = gss_accept_sec_context(
919 client->cl_sname->sn_cred,
921 GSS_C_NO_CHANNEL_BINDINGS,
929 sx_xunlock(&svc_rpc_gss_lock);
931 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
934 * If we get an error from gss_accept_sec_context, send the
935 * reply anyway so that the client gets a chance to see what
938 if (gr->gr_major != GSS_S_COMPLETE &&
939 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
940 rpc_gss_log_status("accept_sec_context", client->cl_mech,
941 gr->gr_major, gr->gr_minor);
942 client->cl_state = CLIENT_STALE;
946 gr->gr_handle.value = &client->cl_id;
947 gr->gr_handle.length = sizeof(client->cl_id);
948 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
950 /* Save client info. */
951 client->cl_mech = mech;
952 client->cl_qop = GSS_C_QOP_DEFAULT;
953 client->cl_done_callback = FALSE;
955 if (gr->gr_major == GSS_S_COMPLETE) {
956 gss_buffer_desc export_name;
959 * Change client expiration time to be near when the
960 * client creds expire (or 24 hours if we can't figure
963 if (cred_lifetime == GSS_C_INDEFINITE)
964 cred_lifetime = 24*60*60;
967 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
969 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
970 svc_rpc_gss_lifetime_max)
971 cred_lifetime = svc_rpc_gss_lifetime_max;
973 client->cl_expiration = time_uptime + cred_lifetime;
976 * Fill in cred details in the rawcred structure.
978 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
979 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
980 maj_stat = gss_export_name(&min_stat, client->cl_cname,
982 if (maj_stat != GSS_S_COMPLETE) {
983 rpc_gss_log_status("gss_export_name", client->cl_mech,
987 client->cl_rawcred.client_principal =
988 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
989 + export_name.length);
990 client->cl_rawcred.client_principal->len = export_name.length;
991 memcpy(client->cl_rawcred.client_principal->name,
992 export_name.value, export_name.length);
993 gss_release_buffer(&min_stat, &export_name);
994 client->cl_rawcred.svc_principal =
995 client->cl_sname->sn_principal;
996 client->cl_rawcred.service = gc->gc_svc;
999 * Use gss_pname_to_uid to map to unix creds. For
1000 * kerberos5, this uses krb5_aname_to_localname.
1002 svc_rpc_gss_build_ucred(client, client->cl_cname);
1003 svc_rpc_gss_set_flavor(client);
1004 gss_release_name(&min_stat, &client->cl_cname);
1008 gss_buffer_desc mechname;
1010 gss_oid_to_str(&min_stat, mech, &mechname);
1012 rpc_gss_log_debug("accepted context for %s with "
1013 "<mech %.*s, qop %d, svc %d>",
1014 client->cl_rawcred.client_principal->name,
1015 mechname.length, (char *)mechname.value,
1016 client->cl_qop, client->cl_rawcred.service);
1018 gss_release_buffer(&min_stat, &mechname);
1026 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1027 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1029 struct opaque_auth *oa;
1030 gss_buffer_desc rpcbuf, checksum;
1031 OM_uint32 maj_stat, min_stat;
1032 gss_qop_t qop_state;
1033 int32_t rpchdr[128 / sizeof(int32_t)];
1036 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1038 memset(rpchdr, 0, sizeof(rpchdr));
1040 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1042 IXDR_PUT_LONG(buf, msg->rm_xid);
1043 IXDR_PUT_ENUM(buf, msg->rm_direction);
1044 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1045 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1046 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1047 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1048 oa = &msg->rm_call.cb_cred;
1049 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1050 IXDR_PUT_LONG(buf, oa->oa_length);
1051 if (oa->oa_length) {
1052 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1053 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1055 rpcbuf.value = rpchdr;
1056 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1058 checksum.value = msg->rm_call.cb_verf.oa_base;
1059 checksum.length = msg->rm_call.cb_verf.oa_length;
1061 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1064 if (maj_stat != GSS_S_COMPLETE) {
1065 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1066 maj_stat, min_stat);
1068 * A bug in some versions of the Linux client generates a
1069 * Destroy operation with a bogus encrypted checksum. Deleting
1070 * the credential handle for that case causes the mount to fail.
1071 * Since the checksum is bogus (gss_verify_mic() failed), it
1072 * doesn't make sense to destroy the handle and not doing so
1073 * fixes the Linux mount.
1075 if (gcproc != RPCSEC_GSS_DESTROY)
1076 client->cl_state = CLIENT_STALE;
1085 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1086 struct svc_req *rqst, u_int seq)
1088 gss_buffer_desc signbuf;
1089 gss_buffer_desc mic;
1090 OM_uint32 maj_stat, min_stat;
1093 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1096 signbuf.value = &nseq;
1097 signbuf.length = sizeof(nseq);
1099 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1102 if (maj_stat != GSS_S_COMPLETE) {
1103 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1104 client->cl_state = CLIENT_STALE;
1108 KASSERT(mic.length <= MAX_AUTH_BYTES,
1109 ("MIC too large for RPCSEC_GSS"));
1111 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1112 rqst->rq_verf.oa_length = mic.length;
1113 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1115 gss_release_buffer(&min_stat, &mic);
1121 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1123 struct svc_rpc_gss_callback *scb;
1124 rpc_gss_lock_t lock;
1130 * See if we have a callback for this guy.
1133 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1134 if (scb->cb_callback.program == rqst->rq_prog
1135 && scb->cb_callback.version == rqst->rq_vers) {
1137 * This one matches. Call the callback and see
1138 * if it wants to veto or something.
1140 lock.locked = FALSE;
1141 lock.raw_cred = &client->cl_rawcred;
1142 cb_res = scb->cb_callback.callback(rqst,
1149 client->cl_state = CLIENT_STALE;
1155 * The callback accepted the connection - it
1156 * is responsible for freeing client->cl_creds
1159 client->cl_creds = GSS_C_NO_CREDENTIAL;
1160 client->cl_locked = lock.locked;
1161 client->cl_cookie = cookie;
1167 * Either no callback exists for this program/version or one
1168 * of the callbacks rejected the connection. We just need to
1169 * clean up the delegated client creds, if any.
1171 if (client->cl_creds) {
1173 gss_release_cred(&min_ver, &client->cl_creds);
1179 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1185 sx_xlock(&client->cl_lock);
1186 if (seq <= client->cl_seqlast) {
1188 * The request sequence number is less than
1189 * the largest we have seen so far. If it is
1190 * outside the window or if we have seen a
1191 * request with this sequence before, silently
1194 offset = client->cl_seqlast - seq;
1195 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1201 if (client->cl_seqmask[word] & (1 << bit)) {
1209 sx_xunlock(&client->cl_lock);
1214 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1216 int offset, i, word, bit;
1217 uint32_t carry, newcarry;
1219 sx_xlock(&client->cl_lock);
1220 if (seq > client->cl_seqlast) {
1222 * This request has a sequence number greater
1223 * than any we have seen so far. Advance the
1224 * seq window and set bit zero of the window
1225 * (which corresponds to the new sequence
1228 offset = seq - client->cl_seqlast;
1229 while (offset > 32) {
1230 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1232 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1234 client->cl_seqmask[0] = 0;
1238 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1239 newcarry = client->cl_seqmask[i] >> (32 - offset);
1240 client->cl_seqmask[i] =
1241 (client->cl_seqmask[i] << offset) | carry;
1244 client->cl_seqmask[0] |= 1;
1245 client->cl_seqlast = seq;
1247 offset = client->cl_seqlast - seq;
1250 client->cl_seqmask[word] |= (1 << bit);
1252 sx_xunlock(&client->cl_lock);
1256 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1261 struct svc_rpc_gss_cookedcred *cc;
1262 struct svc_rpc_gss_client *client;
1263 struct rpc_gss_cred gc;
1264 struct rpc_gss_init_res gr;
1267 enum auth_stat result;
1269 rpc_gss_log_debug("in svc_rpc_gss()");
1271 /* Garbage collect old clients. */
1272 svc_rpc_gss_timeout_clients();
1274 /* Initialize reply. */
1275 rqst->rq_verf = _null_auth;
1277 /* Deserialize client credentials. */
1278 if (rqst->rq_cred.oa_length <= 0)
1279 return (AUTH_BADCRED);
1281 memset(&gc, 0, sizeof(gc));
1283 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1284 rqst->rq_cred.oa_length, XDR_DECODE);
1286 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1288 return (AUTH_BADCRED);
1294 /* Check version. */
1295 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1296 result = AUTH_BADCRED;
1300 /* Check the proc and find the client (or create it) */
1301 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1302 if (gc.gc_handle.length != 0) {
1303 result = AUTH_BADCRED;
1306 client = svc_rpc_gss_create_client();
1308 struct svc_rpc_gss_clientid *p;
1309 if (gc.gc_handle.length != sizeof(*p)) {
1310 result = AUTH_BADCRED;
1313 p = gc.gc_handle.value;
1314 client = svc_rpc_gss_find_client(p);
1317 * Can't find the client - we may have
1318 * destroyed it - tell the other side to
1321 result = RPCSEC_GSS_CREDPROBLEM;
1325 cc = rqst->rq_clntcred;
1326 cc->cc_client = client;
1327 cc->cc_service = gc.gc_svc;
1328 cc->cc_seq = gc.gc_seq;
1331 * The service and sequence number must be ignored for
1332 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1334 if (gc.gc_proc != RPCSEC_GSS_INIT
1335 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1337 * Check for sequence number overflow.
1339 if (gc.gc_seq >= MAXSEQ) {
1340 result = RPCSEC_GSS_CTXPROBLEM;
1345 * Check for valid service.
1347 if (gc.gc_svc != rpc_gss_svc_none &&
1348 gc.gc_svc != rpc_gss_svc_integrity &&
1349 gc.gc_svc != rpc_gss_svc_privacy) {
1350 result = AUTH_BADCRED;
1355 /* Handle RPCSEC_GSS control procedure. */
1356 switch (gc.gc_proc) {
1358 case RPCSEC_GSS_INIT:
1359 case RPCSEC_GSS_CONTINUE_INIT:
1360 if (rqst->rq_proc != NULLPROC) {
1361 result = AUTH_REJECTEDCRED;
1365 memset(&gr, 0, sizeof(gr));
1366 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1367 result = AUTH_REJECTEDCRED;
1371 if (gr.gr_major == GSS_S_COMPLETE) {
1373 * We borrow the space for the call verf to
1374 * pack our reply verf.
1376 rqst->rq_verf = msg->rm_call.cb_verf;
1377 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1378 result = AUTH_REJECTEDCRED;
1382 rqst->rq_verf = _null_auth;
1385 call_stat = svc_sendreply(rqst,
1386 (xdrproc_t) xdr_rpc_gss_init_res,
1389 gss_release_buffer(&min_stat, &gr.gr_token);
1392 result = AUTH_FAILED;
1396 if (gr.gr_major == GSS_S_COMPLETE)
1397 client->cl_state = CLIENT_ESTABLISHED;
1399 result = RPCSEC_GSS_NODISPATCH;
1402 case RPCSEC_GSS_DATA:
1403 case RPCSEC_GSS_DESTROY:
1404 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1405 result = RPCSEC_GSS_NODISPATCH;
1409 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1410 result = RPCSEC_GSS_CREDPROBLEM;
1415 * We borrow the space for the call verf to pack our
1418 rqst->rq_verf = msg->rm_call.cb_verf;
1419 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1420 result = RPCSEC_GSS_CTXPROBLEM;
1424 svc_rpc_gss_update_seq(client, gc.gc_seq);
1427 * Change the SVCAUTH ops on the request to point at
1428 * our own code so that we can unwrap the arguments
1429 * and wrap the result. The caller will re-set this on
1430 * every request to point to a set of null wrap/unwrap
1431 * methods. Acquire an extra reference to the client
1432 * which will be released by svc_rpc_gss_release()
1433 * after the request has finished processing.
1435 refcount_acquire(&client->cl_refs);
1436 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1437 rqst->rq_auth.svc_ah_private = cc;
1439 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1441 * We might be ready to do a callback to the server to
1442 * see if it wants to accept/reject the connection.
1444 sx_xlock(&client->cl_lock);
1445 if (!client->cl_done_callback) {
1446 client->cl_done_callback = TRUE;
1447 client->cl_qop = qop;
1448 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1449 client->cl_rawcred.mechanism, qop);
1450 if (!svc_rpc_gss_callback(client, rqst)) {
1451 result = AUTH_REJECTEDCRED;
1452 sx_xunlock(&client->cl_lock);
1456 sx_xunlock(&client->cl_lock);
1459 * If the server has locked this client to a
1460 * particular service+qop pair, enforce that
1463 if (client->cl_locked) {
1464 if (client->cl_rawcred.service != gc.gc_svc) {
1465 result = AUTH_FAILED;
1467 } else if (client->cl_qop != qop) {
1468 result = AUTH_BADVERF;
1474 * If the qop changed, look up the new qop
1477 if (client->cl_qop != qop) {
1478 client->cl_qop = qop;
1479 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1480 client->cl_rawcred.mechanism, qop);
1484 * Make sure we use the right service value
1487 if (client->cl_rawcred.service != gc.gc_svc) {
1488 client->cl_rawcred.service = gc.gc_svc;
1489 svc_rpc_gss_set_flavor(client);
1494 if (rqst->rq_proc != NULLPROC) {
1495 result = AUTH_REJECTEDCRED;
1499 call_stat = svc_sendreply(rqst,
1500 (xdrproc_t) xdr_void, (caddr_t) NULL);
1503 result = AUTH_FAILED;
1507 svc_rpc_gss_forget_client(client);
1509 result = RPCSEC_GSS_NODISPATCH;
1515 result = AUTH_BADCRED;
1520 svc_rpc_gss_release_client(client);
1522 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1527 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1529 struct svc_rpc_gss_cookedcred *cc;
1530 struct svc_rpc_gss_client *client;
1532 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1534 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1535 client = cc->cc_client;
1536 if (client->cl_state != CLIENT_ESTABLISHED
1537 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1541 return (xdr_rpc_gss_wrap_data(mp,
1542 client->cl_ctx, client->cl_qop,
1543 cc->cc_service, cc->cc_seq));
1547 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1549 struct svc_rpc_gss_cookedcred *cc;
1550 struct svc_rpc_gss_client *client;
1552 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1554 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1555 client = cc->cc_client;
1556 if (client->cl_state != CLIENT_ESTABLISHED
1557 || cc->cc_service == rpc_gss_svc_none) {
1561 return (xdr_rpc_gss_unwrap_data(mp,
1562 client->cl_ctx, client->cl_qop,
1563 cc->cc_service, cc->cc_seq));
1567 svc_rpc_gss_release(SVCAUTH *auth)
1569 struct svc_rpc_gss_cookedcred *cc;
1570 struct svc_rpc_gss_client *client;
1572 rpc_gss_log_debug("in svc_rpc_gss_release()");
1574 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1575 client = cc->cc_client;
1576 svc_rpc_gss_release_client(client);