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>
85 #include <rpc/rpcsec_gss.h>
87 #include "rpcsec_gss_int.h"
89 static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
90 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
91 static void svc_rpc_gss_release(SVCAUTH *);
92 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
93 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
95 static const struct svc_auth_ops svc_auth_gss_ops = {
96 .svc_ah_wrap = svc_rpc_gss_wrap,
97 .svc_ah_unwrap = svc_rpc_gss_unwrap,
98 .svc_ah_release = svc_rpc_gss_release,
101 struct sx svc_rpc_gss_lock;
103 struct svc_rpc_gss_callback {
104 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
105 rpc_gss_callback_t cb_callback;
107 SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback);
108 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_callback_list,
109 svc_rpc_gss_callbacks) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
111 struct svc_rpc_gss_svc_name {
112 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
116 gss_cred_id_t sn_cred;
120 SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name);
121 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_svc_name_list,
122 svc_rpc_gss_svc_names) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
124 enum svc_rpc_gss_client_state {
125 CLIENT_NEW, /* still authenticating */
126 CLIENT_ESTABLISHED, /* context established */
127 CLIENT_STALE /* garbage to collect */
130 #define SVC_RPC_GSS_SEQWINDOW 128
132 struct svc_rpc_gss_clientid {
133 unsigned long ci_hostid;
134 uint32_t ci_boottime;
138 struct svc_rpc_gss_client {
139 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
140 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
141 volatile u_int cl_refs;
143 struct svc_rpc_gss_clientid cl_id;
144 time_t cl_expiration; /* when to gc */
145 enum svc_rpc_gss_client_state cl_state; /* client state */
146 bool_t cl_locked; /* fixed service+qop */
147 gss_ctx_id_t cl_ctx; /* context id */
148 gss_cred_id_t cl_creds; /* delegated creds */
149 gss_name_t cl_cname; /* client name */
150 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
151 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
152 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
153 struct ucred *cl_cred; /* kernel-style credentials */
154 int cl_rpcflavor; /* RPC pseudo sec flavor */
155 bool_t cl_done_callback; /* TRUE after call */
156 void *cl_cookie; /* user cookie from callback */
157 gid_t cl_gid_storage[NGROUPS];
158 gss_OID cl_mech; /* mechanism */
159 gss_qop_t cl_qop; /* quality of protection */
160 uint32_t cl_seqlast; /* sequence window origin */
161 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
163 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
166 * This structure holds enough information to unwrap arguments or wrap
167 * results for a given request. We use the rq_clntcred area for this
168 * (which is a per-request buffer).
170 struct svc_rpc_gss_cookedcred {
171 struct svc_rpc_gss_client *cc_client;
172 rpc_gss_service_t cc_service;
176 #define CLIENT_HASH_SIZE 256
177 #define CLIENT_MAX 1024
178 u_int svc_rpc_gss_client_max = CLIENT_MAX;
179 u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
181 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
183 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
186 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
187 &svc_rpc_gss_client_max, 0,
188 "Max number of rpc-gss clients");
190 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
191 &svc_rpc_gss_client_hash_size, 0,
192 "Size of rpc-gss client hash table");
194 static u_int svc_rpc_gss_lifetime_max = 0;
195 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
196 &svc_rpc_gss_lifetime_max, 0,
197 "Maximum lifetime (seconds) of rpc-gss clients");
199 static u_int svc_rpc_gss_client_count;
200 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
201 &svc_rpc_gss_client_count, 0,
202 "Number of rpc-gss clients");
204 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash);
205 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients);
206 KGSS_VNET_DEFINE_STATIC(uint32_t, svc_rpc_gss_next_clientid) = 1;
209 svc_rpc_gss_init(void *arg)
213 KGSS_VNET(svc_rpc_gss_client_hash) = mem_alloc(
214 sizeof(struct svc_rpc_gss_client_list) *
215 svc_rpc_gss_client_hash_size);
216 for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
217 TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_client_hash)[i]);
218 TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_clients));
219 if (IS_DEFAULT_VNET(curvnet)) {
220 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
221 sx_init(&svc_rpc_gss_lock, "gsslock");
224 VNET_SYSINIT(svc_rpc_gss_init, SI_SUB_VNET_DONE, SI_ORDER_ANY,
225 svc_rpc_gss_init, NULL);
228 rpc_gss_set_callback(rpc_gss_callback_t *cb)
230 struct svc_rpc_gss_callback *scb;
232 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
234 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
237 scb->cb_callback = *cb;
238 sx_xlock(&svc_rpc_gss_lock);
239 SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_callbacks), scb, cb_link);
240 sx_xunlock(&svc_rpc_gss_lock);
246 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
248 struct svc_rpc_gss_callback *scb;
250 sx_xlock(&svc_rpc_gss_lock);
251 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
252 if (scb->cb_callback.program == cb->program
253 && scb->cb_callback.version == cb->version
254 && scb->cb_callback.callback == cb->callback) {
255 SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_callbacks), scb,
256 svc_rpc_gss_callback, cb_link);
257 sx_xunlock(&svc_rpc_gss_lock);
258 mem_free(scb, sizeof(*scb));
262 sx_xunlock(&svc_rpc_gss_lock);
266 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
268 OM_uint32 maj_stat, min_stat;
269 gss_buffer_desc namebuf;
271 gss_OID_set_desc oid_set;
274 oid_set.elements = sname->sn_mech;
276 namebuf.value = (void *) sname->sn_principal;
277 namebuf.length = strlen(sname->sn_principal);
279 maj_stat = gss_import_name(&min_stat, &namebuf,
280 GSS_C_NT_HOSTBASED_SERVICE, &name);
281 if (maj_stat != GSS_S_COMPLETE)
284 if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
285 gss_release_cred(&min_stat, &sname->sn_cred);
287 maj_stat = gss_acquire_cred(&min_stat, name,
288 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
290 if (maj_stat != GSS_S_COMPLETE) {
291 gss_release_name(&min_stat, &name);
294 gss_release_name(&min_stat, &name);
300 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
301 u_int req_time, u_int program, u_int version)
303 struct svc_rpc_gss_svc_name *sname;
306 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
309 sname = mem_alloc(sizeof(*sname));
312 sname->sn_principal = strdup(principal, M_RPC);
313 sname->sn_mech = mech_oid;
314 sname->sn_req_time = req_time;
315 sname->sn_cred = GSS_C_NO_CREDENTIAL;
316 sname->sn_program = program;
317 sname->sn_version = version;
319 if (!rpc_gss_acquire_svc_cred(sname)) {
320 free(sname->sn_principal, M_RPC);
321 mem_free(sname, sizeof(*sname));
325 sx_xlock(&svc_rpc_gss_lock);
326 SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_svc_names), sname, sn_link);
327 sx_xunlock(&svc_rpc_gss_lock);
333 rpc_gss_clear_svc_name(u_int program, u_int version)
336 struct svc_rpc_gss_svc_name *sname;
338 sx_xlock(&svc_rpc_gss_lock);
339 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) {
340 if (sname->sn_program == program
341 && sname->sn_version == version) {
342 SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_svc_names), sname,
343 svc_rpc_gss_svc_name, sn_link);
344 sx_xunlock(&svc_rpc_gss_lock);
345 gss_release_cred(&min_stat, &sname->sn_cred);
346 free(sname->sn_principal, M_RPC);
347 mem_free(sname, sizeof(*sname));
351 sx_xunlock(&svc_rpc_gss_lock);
355 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
356 const char *mech, const char *name, const char *node, const char *domain)
358 OM_uint32 maj_stat, min_stat;
362 gss_name_t gss_name, gss_mech_name;
363 rpc_gss_principal_t result;
365 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
369 * Construct a gss_buffer containing the full name formatted
370 * as "name/node@domain" where node and domain are optional.
372 namelen = strlen(name) + 1;
374 namelen += strlen(node) + 1;
377 namelen += strlen(domain) + 1;
380 buf.value = mem_alloc(namelen);
381 buf.length = namelen;
382 strcpy((char *) buf.value, name);
384 strcat((char *) buf.value, "/");
385 strcat((char *) buf.value, node);
388 strcat((char *) buf.value, "@");
389 strcat((char *) buf.value, domain);
393 * Convert that to a gss_name_t and then convert that to a
394 * mechanism name in the selected mechanism.
396 maj_stat = gss_import_name(&min_stat, &buf,
397 GSS_C_NT_USER_NAME, &gss_name);
398 mem_free(buf.value, buf.length);
399 if (maj_stat != GSS_S_COMPLETE) {
400 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
403 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
405 if (maj_stat != GSS_S_COMPLETE) {
406 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
408 gss_release_name(&min_stat, &gss_name);
411 gss_release_name(&min_stat, &gss_name);
414 * Export the mechanism name and use that to construct the
415 * rpc_gss_principal_t result.
417 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
418 if (maj_stat != GSS_S_COMPLETE) {
419 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
420 gss_release_name(&min_stat, &gss_mech_name);
423 gss_release_name(&min_stat, &gss_mech_name);
425 result = mem_alloc(sizeof(int) + buf.length);
427 gss_release_buffer(&min_stat, &buf);
430 result->len = buf.length;
431 memcpy(result->name, buf.value, buf.length);
432 gss_release_buffer(&min_stat, &buf);
439 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
440 rpc_gss_ucred_t **ucred, void **cookie)
442 struct svc_rpc_gss_cookedcred *cc;
443 struct svc_rpc_gss_client *client;
445 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
448 cc = req->rq_clntcred;
449 client = cc->cc_client;
451 *rcred = &client->cl_rawcred;
453 *ucred = &client->cl_ucred;
455 *cookie = client->cl_cookie;
460 * This simpler interface is used by svc_getcred to copy the cred data
461 * into a kernel cred structure.
464 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
467 struct svc_rpc_gss_cookedcred *cc;
468 struct svc_rpc_gss_client *client;
471 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
474 cc = req->rq_clntcred;
475 client = cc->cc_client;
478 *flavorp = client->cl_rpcflavor;
480 if (client->cl_cred) {
481 *crp = crhold(client->cl_cred);
485 uc = &client->cl_ucred;
486 cr = client->cl_cred = crget();
487 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
488 cr->cr_rgid = cr->cr_svgid = uc->gid;
489 crsetgroups(cr, uc->gidlen, uc->gidlist);
490 cr->cr_prison = curthread->td_ucred->cr_prison;
491 prison_hold(cr->cr_prison);
498 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
500 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
501 struct svc_rpc_gss_client *client = cc->cc_client;
504 OM_uint32 maj_stat, min_stat;
507 switch (client->cl_rawcred.service) {
508 case rpc_gss_svc_none:
509 return (max_tp_unit_len);
512 case rpc_gss_svc_default:
513 case rpc_gss_svc_integrity:
517 case rpc_gss_svc_privacy:
525 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
526 client->cl_qop, max_tp_unit_len, &max);
528 if (maj_stat == GSS_S_COMPLETE) {
534 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
540 static struct svc_rpc_gss_client *
541 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
543 struct svc_rpc_gss_client *client;
544 struct svc_rpc_gss_client_list *list;
545 struct timeval boottime;
546 unsigned long hostid;
548 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
550 getcredhostid(curthread->td_ucred, &hostid);
551 getboottime(&boottime);
552 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
555 list = &KGSS_VNET(svc_rpc_gss_client_hash)
556 [id->ci_id % svc_rpc_gss_client_hash_size];
557 sx_xlock(&svc_rpc_gss_lock);
558 TAILQ_FOREACH(client, list, cl_link) {
559 if (client->cl_id.ci_id == id->ci_id) {
561 * Move this client to the front of the LRU
564 TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client,
566 TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients),
568 refcount_acquire(&client->cl_refs);
572 sx_xunlock(&svc_rpc_gss_lock);
577 static struct svc_rpc_gss_client *
578 svc_rpc_gss_create_client(void)
580 struct svc_rpc_gss_client *client;
581 struct svc_rpc_gss_client_list *list;
582 struct timeval boottime;
583 unsigned long hostid;
585 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
587 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
588 memset(client, 0, sizeof(struct svc_rpc_gss_client));
591 * Set the initial value of cl_refs to two. One for the caller
592 * and the other to hold onto the client structure until it expires.
594 refcount_init(&client->cl_refs, 2);
595 sx_init(&client->cl_lock, "GSS-client");
596 getcredhostid(curthread->td_ucred, &hostid);
597 client->cl_id.ci_hostid = hostid;
598 getboottime(&boottime);
599 client->cl_id.ci_boottime = boottime.tv_sec;
600 client->cl_id.ci_id = KGSS_VNET(svc_rpc_gss_next_clientid)++;
603 * Start the client off with a short expiration time. We will
604 * try to get a saner value from the client creds later.
606 client->cl_state = CLIENT_NEW;
607 client->cl_locked = FALSE;
608 client->cl_expiration = time_uptime + 5*60;
610 list = &KGSS_VNET(svc_rpc_gss_client_hash)
611 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
612 sx_xlock(&svc_rpc_gss_lock);
613 TAILQ_INSERT_HEAD(list, client, cl_link);
614 TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
615 svc_rpc_gss_client_count++;
616 sx_xunlock(&svc_rpc_gss_lock);
621 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
625 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
628 gss_delete_sec_context(&min_stat,
629 &client->cl_ctx, GSS_C_NO_BUFFER);
631 if (client->cl_cname)
632 gss_release_name(&min_stat, &client->cl_cname);
634 if (client->cl_rawcred.client_principal)
635 mem_free(client->cl_rawcred.client_principal,
636 sizeof(*client->cl_rawcred.client_principal)
637 + client->cl_rawcred.client_principal->len);
640 crfree(client->cl_cred);
642 sx_destroy(&client->cl_lock);
643 mem_free(client, sizeof(*client));
647 * Drop a reference to a client and free it if that was the last reference.
650 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
653 if (!refcount_release(&client->cl_refs))
655 svc_rpc_gss_destroy_client(client);
659 * Remove a client from our global lists.
660 * Must be called with svc_rpc_gss_lock held.
663 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
665 struct svc_rpc_gss_client_list *list;
667 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
668 list = &KGSS_VNET(svc_rpc_gss_client_hash)
669 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
670 TAILQ_REMOVE(list, client, cl_link);
671 TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
672 svc_rpc_gss_client_count--;
676 * Remove a client from our global lists and free it if we can.
679 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
681 struct svc_rpc_gss_client_list *list;
682 struct svc_rpc_gss_client *tclient;
684 list = &KGSS_VNET(svc_rpc_gss_client_hash)
685 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
686 sx_xlock(&svc_rpc_gss_lock);
687 TAILQ_FOREACH(tclient, list, cl_link) {
689 * Make sure this client has not already been removed
690 * from the lists by svc_rpc_gss_forget_client() or
691 * svc_rpc_gss_forget_client_locked().
693 if (client == tclient) {
694 svc_rpc_gss_forget_client_locked(client);
695 sx_xunlock(&svc_rpc_gss_lock);
696 svc_rpc_gss_release_client(client);
700 sx_xunlock(&svc_rpc_gss_lock);
704 svc_rpc_gss_timeout_clients(void)
706 struct svc_rpc_gss_client *client;
707 time_t now = time_uptime;
709 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
712 * First enforce the max client limit. We keep
713 * svc_rpc_gss_clients in LRU order.
715 sx_xlock(&svc_rpc_gss_lock);
716 client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
717 svc_rpc_gss_client_list);
718 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
719 svc_rpc_gss_forget_client_locked(client);
720 sx_xunlock(&svc_rpc_gss_lock);
721 svc_rpc_gss_release_client(client);
722 sx_xlock(&svc_rpc_gss_lock);
723 client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
724 svc_rpc_gss_client_list);
727 TAILQ_FOREACH(client, &KGSS_VNET(svc_rpc_gss_clients), cl_alllink) {
728 if (client->cl_state == CLIENT_STALE
729 || now > client->cl_expiration) {
730 svc_rpc_gss_forget_client_locked(client);
731 sx_xunlock(&svc_rpc_gss_lock);
732 rpc_gss_log_debug("expiring client %p", client);
733 svc_rpc_gss_release_client(client);
734 sx_xlock(&svc_rpc_gss_lock);
738 sx_xunlock(&svc_rpc_gss_lock);
743 * OID<->string routines. These are uuuuugly.
746 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
749 unsigned long number;
751 size_t string_length;
756 /* Decoded according to krb5/gssapi_krb5.c */
758 /* First determine the size of the string */
762 cp = (unsigned char *) oid->elements;
763 number = (unsigned long) cp[0];
764 sprintf(numstr, "%ld ", number/40);
765 string_length += strlen(numstr);
766 sprintf(numstr, "%ld ", number%40);
767 string_length += strlen(numstr);
768 for (i=1; i<oid->length; i++) {
769 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
770 number = (number << 7) | (cp[i] & 0x7f);
775 return(GSS_S_FAILURE);
777 if ((cp[i] & 0x80) == 0) {
778 sprintf(numstr, "%ld ", number);
779 string_length += strlen(numstr);
785 * If we get here, we've calculated the length of "n n n ... n ". Add 4
786 * here for "{ " and "}\0".
789 if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
791 number = (unsigned long) cp[0];
792 sprintf(numstr, "%ld ", number/40);
794 sprintf(numstr, "%ld ", number%40);
797 cp = (unsigned char *) oid->elements;
798 for (i=1; i<oid->length; i++) {
799 number = (number << 7) | (cp[i] & 0x7f);
800 if ((cp[i] & 0x80) == 0) {
801 sprintf(numstr, "%ld ", number);
807 oid_str->length = strlen(bp)+1;
808 oid_str->value = (void *) bp;
810 return(GSS_S_COMPLETE);
813 return(GSS_S_FAILURE);
818 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
819 const gss_name_t name)
821 OM_uint32 maj_stat, min_stat;
822 rpc_gss_ucred_t *uc = &client->cl_ucred;
827 uc->gidlist = client->cl_gid_storage;
830 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
831 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
832 if (GSS_ERROR(maj_stat))
835 uc->gidlen = numgroups;
839 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
841 static gss_OID_desc krb5_mech_oid =
842 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
845 * Attempt to translate mech type and service into a
846 * 'pseudo flavor'. Hardwire in krb5 support for now.
848 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
849 switch (client->cl_rawcred.service) {
850 case rpc_gss_svc_default:
851 case rpc_gss_svc_none:
852 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
854 case rpc_gss_svc_integrity:
855 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
857 case rpc_gss_svc_privacy:
858 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
862 client->cl_rpcflavor = RPCSEC_GSS;
867 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
868 struct svc_req *rqst,
869 struct rpc_gss_init_res *gr,
870 struct rpc_gss_cred *gc)
872 gss_buffer_desc recv_tok;
874 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
875 OM_uint32 cred_lifetime;
876 struct svc_rpc_gss_svc_name *sname;
878 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
880 /* Deserialize arguments. */
881 memset(&recv_tok, 0, sizeof(recv_tok));
883 if (!svc_getargs(rqst,
884 (xdrproc_t) xdr_gss_buffer_desc,
885 (caddr_t) &recv_tok)) {
886 client->cl_state = CLIENT_STALE;
891 * First time round, try all the server names we have until
892 * one matches. Afterwards, stick with that one.
894 sx_xlock(&svc_rpc_gss_lock);
895 if (!client->cl_sname) {
896 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names),
898 if (sname->sn_program == rqst->rq_prog
899 && sname->sn_version == rqst->rq_vers) {
901 gr->gr_major = gss_accept_sec_context(
906 GSS_C_NO_CHANNEL_BINDINGS,
914 GSS_S_CREDENTIALS_EXPIRED) {
916 * Either our creds really did
920 if (rpc_gss_acquire_svc_cred(sname))
923 client->cl_sname = sname;
928 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
930 sx_xunlock(&svc_rpc_gss_lock);
934 gr->gr_major = gss_accept_sec_context(
937 client->cl_sname->sn_cred,
939 GSS_C_NO_CHANNEL_BINDINGS,
947 sx_xunlock(&svc_rpc_gss_lock);
949 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
952 * If we get an error from gss_accept_sec_context, send the
953 * reply anyway so that the client gets a chance to see what
956 if (gr->gr_major != GSS_S_COMPLETE &&
957 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
958 rpc_gss_log_status("accept_sec_context", client->cl_mech,
959 gr->gr_major, gr->gr_minor);
960 client->cl_state = CLIENT_STALE;
964 gr->gr_handle.value = &client->cl_id;
965 gr->gr_handle.length = sizeof(client->cl_id);
966 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
968 /* Save client info. */
969 client->cl_mech = mech;
970 client->cl_qop = GSS_C_QOP_DEFAULT;
971 client->cl_done_callback = FALSE;
973 if (gr->gr_major == GSS_S_COMPLETE) {
974 gss_buffer_desc export_name;
977 * Change client expiration time to be near when the
978 * client creds expire (or 24 hours if we can't figure
981 if (cred_lifetime == GSS_C_INDEFINITE)
982 cred_lifetime = 24*60*60;
985 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
987 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
988 svc_rpc_gss_lifetime_max)
989 cred_lifetime = svc_rpc_gss_lifetime_max;
991 client->cl_expiration = time_uptime + cred_lifetime;
994 * Fill in cred details in the rawcred structure.
996 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
997 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
998 maj_stat = gss_export_name(&min_stat, client->cl_cname,
1000 if (maj_stat != GSS_S_COMPLETE) {
1001 rpc_gss_log_status("gss_export_name", client->cl_mech,
1002 maj_stat, min_stat);
1005 client->cl_rawcred.client_principal =
1006 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
1007 + export_name.length);
1008 client->cl_rawcred.client_principal->len = export_name.length;
1009 memcpy(client->cl_rawcred.client_principal->name,
1010 export_name.value, export_name.length);
1011 gss_release_buffer(&min_stat, &export_name);
1012 client->cl_rawcred.svc_principal =
1013 client->cl_sname->sn_principal;
1014 client->cl_rawcred.service = gc->gc_svc;
1017 * Use gss_pname_to_uid to map to unix creds. For
1018 * kerberos5, this uses krb5_aname_to_localname.
1020 svc_rpc_gss_build_ucred(client, client->cl_cname);
1021 svc_rpc_gss_set_flavor(client);
1022 gss_release_name(&min_stat, &client->cl_cname);
1026 gss_buffer_desc mechname;
1028 gss_oid_to_str(&min_stat, mech, &mechname);
1030 rpc_gss_log_debug("accepted context for %s with "
1031 "<mech %.*s, qop %d, svc %d>",
1032 client->cl_rawcred.client_principal->name,
1033 mechname.length, (char *)mechname.value,
1034 client->cl_qop, client->cl_rawcred.service);
1036 gss_release_buffer(&min_stat, &mechname);
1044 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1045 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1047 struct opaque_auth *oa;
1048 gss_buffer_desc rpcbuf, checksum;
1049 OM_uint32 maj_stat, min_stat;
1050 gss_qop_t qop_state;
1051 int32_t rpchdr[128 / sizeof(int32_t)];
1054 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1056 memset(rpchdr, 0, sizeof(rpchdr));
1058 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1060 IXDR_PUT_LONG(buf, msg->rm_xid);
1061 IXDR_PUT_ENUM(buf, msg->rm_direction);
1062 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1063 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1064 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1065 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1066 oa = &msg->rm_call.cb_cred;
1067 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1068 IXDR_PUT_LONG(buf, oa->oa_length);
1069 if (oa->oa_length) {
1070 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1071 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1073 rpcbuf.value = rpchdr;
1074 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1076 checksum.value = msg->rm_call.cb_verf.oa_base;
1077 checksum.length = msg->rm_call.cb_verf.oa_length;
1079 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1082 if (maj_stat != GSS_S_COMPLETE) {
1083 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1084 maj_stat, min_stat);
1086 * A bug in some versions of the Linux client generates a
1087 * Destroy operation with a bogus encrypted checksum. Deleting
1088 * the credential handle for that case causes the mount to fail.
1089 * Since the checksum is bogus (gss_verify_mic() failed), it
1090 * doesn't make sense to destroy the handle and not doing so
1091 * fixes the Linux mount.
1093 if (gcproc != RPCSEC_GSS_DESTROY)
1094 client->cl_state = CLIENT_STALE;
1103 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1104 struct svc_req *rqst, u_int seq)
1106 gss_buffer_desc signbuf;
1107 gss_buffer_desc mic;
1108 OM_uint32 maj_stat, min_stat;
1111 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1114 signbuf.value = &nseq;
1115 signbuf.length = sizeof(nseq);
1117 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1120 if (maj_stat != GSS_S_COMPLETE) {
1121 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1122 client->cl_state = CLIENT_STALE;
1126 KASSERT(mic.length <= MAX_AUTH_BYTES,
1127 ("MIC too large for RPCSEC_GSS"));
1129 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1130 rqst->rq_verf.oa_length = mic.length;
1131 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1133 gss_release_buffer(&min_stat, &mic);
1139 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1141 struct svc_rpc_gss_callback *scb;
1142 rpc_gss_lock_t lock;
1148 * See if we have a callback for this guy.
1151 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
1152 if (scb->cb_callback.program == rqst->rq_prog
1153 && scb->cb_callback.version == rqst->rq_vers) {
1155 * This one matches. Call the callback and see
1156 * if it wants to veto or something.
1158 lock.locked = FALSE;
1159 lock.raw_cred = &client->cl_rawcred;
1160 cb_res = scb->cb_callback.callback(rqst,
1167 client->cl_state = CLIENT_STALE;
1173 * The callback accepted the connection - it
1174 * is responsible for freeing client->cl_creds
1177 client->cl_creds = GSS_C_NO_CREDENTIAL;
1178 client->cl_locked = lock.locked;
1179 client->cl_cookie = cookie;
1185 * Either no callback exists for this program/version or one
1186 * of the callbacks rejected the connection. We just need to
1187 * clean up the delegated client creds, if any.
1189 if (client->cl_creds) {
1191 gss_release_cred(&min_ver, &client->cl_creds);
1197 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1203 sx_xlock(&client->cl_lock);
1204 if (seq <= client->cl_seqlast) {
1206 * The request sequence number is less than
1207 * the largest we have seen so far. If it is
1208 * outside the window or if we have seen a
1209 * request with this sequence before, silently
1212 offset = client->cl_seqlast - seq;
1213 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1219 if (client->cl_seqmask[word] & (1 << bit)) {
1227 sx_xunlock(&client->cl_lock);
1232 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1234 int offset, i, word, bit;
1235 uint32_t carry, newcarry;
1237 sx_xlock(&client->cl_lock);
1238 if (seq > client->cl_seqlast) {
1240 * This request has a sequence number greater
1241 * than any we have seen so far. Advance the
1242 * seq window and set bit zero of the window
1243 * (which corresponds to the new sequence
1246 offset = seq - client->cl_seqlast;
1247 while (offset > 32) {
1248 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1250 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1252 client->cl_seqmask[0] = 0;
1256 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1257 newcarry = client->cl_seqmask[i] >> (32 - offset);
1258 client->cl_seqmask[i] =
1259 (client->cl_seqmask[i] << offset) | carry;
1262 client->cl_seqmask[0] |= 1;
1263 client->cl_seqlast = seq;
1265 offset = client->cl_seqlast - seq;
1268 client->cl_seqmask[word] |= (1 << bit);
1270 sx_xunlock(&client->cl_lock);
1274 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1279 struct svc_rpc_gss_cookedcred *cc;
1280 struct svc_rpc_gss_client *client;
1281 struct rpc_gss_cred gc;
1282 struct rpc_gss_init_res gr;
1285 enum auth_stat result;
1287 KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
1288 rpc_gss_log_debug("in svc_rpc_gss()");
1290 /* Garbage collect old clients. */
1291 svc_rpc_gss_timeout_clients();
1293 /* Initialize reply. */
1294 rqst->rq_verf = _null_auth;
1296 /* Deserialize client credentials. */
1297 if (rqst->rq_cred.oa_length <= 0) {
1298 KGSS_CURVNET_RESTORE();
1299 return (AUTH_BADCRED);
1302 memset(&gc, 0, sizeof(gc));
1304 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1305 rqst->rq_cred.oa_length, XDR_DECODE);
1307 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1309 KGSS_CURVNET_RESTORE();
1310 return (AUTH_BADCRED);
1316 /* Check version. */
1317 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1318 result = AUTH_BADCRED;
1322 /* Check the proc and find the client (or create it) */
1323 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1324 if (gc.gc_handle.length != 0) {
1325 result = AUTH_BADCRED;
1328 client = svc_rpc_gss_create_client();
1330 struct svc_rpc_gss_clientid *p;
1331 if (gc.gc_handle.length != sizeof(*p)) {
1332 result = AUTH_BADCRED;
1335 p = gc.gc_handle.value;
1336 client = svc_rpc_gss_find_client(p);
1339 * Can't find the client - we may have
1340 * destroyed it - tell the other side to
1343 result = RPCSEC_GSS_CREDPROBLEM;
1347 cc = rqst->rq_clntcred;
1348 cc->cc_client = client;
1349 cc->cc_service = gc.gc_svc;
1350 cc->cc_seq = gc.gc_seq;
1353 * The service and sequence number must be ignored for
1354 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1356 if (gc.gc_proc != RPCSEC_GSS_INIT
1357 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1359 * Check for sequence number overflow.
1361 if (gc.gc_seq >= MAXSEQ) {
1362 result = RPCSEC_GSS_CTXPROBLEM;
1367 * Check for valid service.
1369 if (gc.gc_svc != rpc_gss_svc_none &&
1370 gc.gc_svc != rpc_gss_svc_integrity &&
1371 gc.gc_svc != rpc_gss_svc_privacy) {
1372 result = AUTH_BADCRED;
1377 /* Handle RPCSEC_GSS control procedure. */
1378 switch (gc.gc_proc) {
1380 case RPCSEC_GSS_INIT:
1381 case RPCSEC_GSS_CONTINUE_INIT:
1382 if (rqst->rq_proc != NULLPROC) {
1383 result = AUTH_REJECTEDCRED;
1387 memset(&gr, 0, sizeof(gr));
1388 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1389 result = AUTH_REJECTEDCRED;
1393 if (gr.gr_major == GSS_S_COMPLETE) {
1395 * We borrow the space for the call verf to
1396 * pack our reply verf.
1398 rqst->rq_verf = msg->rm_call.cb_verf;
1399 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1400 result = AUTH_REJECTEDCRED;
1404 rqst->rq_verf = _null_auth;
1407 call_stat = svc_sendreply(rqst,
1408 (xdrproc_t) xdr_rpc_gss_init_res,
1411 gss_release_buffer(&min_stat, &gr.gr_token);
1414 result = AUTH_FAILED;
1418 if (gr.gr_major == GSS_S_COMPLETE)
1419 client->cl_state = CLIENT_ESTABLISHED;
1421 result = RPCSEC_GSS_NODISPATCH;
1424 case RPCSEC_GSS_DATA:
1425 case RPCSEC_GSS_DESTROY:
1426 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1427 result = RPCSEC_GSS_NODISPATCH;
1431 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1432 result = RPCSEC_GSS_CREDPROBLEM;
1437 * We borrow the space for the call verf to pack our
1440 rqst->rq_verf = msg->rm_call.cb_verf;
1441 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1442 result = RPCSEC_GSS_CTXPROBLEM;
1446 svc_rpc_gss_update_seq(client, gc.gc_seq);
1449 * Change the SVCAUTH ops on the request to point at
1450 * our own code so that we can unwrap the arguments
1451 * and wrap the result. The caller will re-set this on
1452 * every request to point to a set of null wrap/unwrap
1453 * methods. Acquire an extra reference to the client
1454 * which will be released by svc_rpc_gss_release()
1455 * after the request has finished processing.
1457 refcount_acquire(&client->cl_refs);
1458 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1459 rqst->rq_auth.svc_ah_private = cc;
1461 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1463 * We might be ready to do a callback to the server to
1464 * see if it wants to accept/reject the connection.
1466 sx_xlock(&client->cl_lock);
1467 if (!client->cl_done_callback) {
1468 client->cl_done_callback = TRUE;
1469 client->cl_qop = qop;
1470 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1471 client->cl_rawcred.mechanism, qop);
1472 if (!svc_rpc_gss_callback(client, rqst)) {
1473 result = AUTH_REJECTEDCRED;
1474 sx_xunlock(&client->cl_lock);
1478 sx_xunlock(&client->cl_lock);
1481 * If the server has locked this client to a
1482 * particular service+qop pair, enforce that
1485 if (client->cl_locked) {
1486 if (client->cl_rawcred.service != gc.gc_svc) {
1487 result = AUTH_FAILED;
1489 } else if (client->cl_qop != qop) {
1490 result = AUTH_BADVERF;
1496 * If the qop changed, look up the new qop
1499 if (client->cl_qop != qop) {
1500 client->cl_qop = qop;
1501 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1502 client->cl_rawcred.mechanism, qop);
1506 * Make sure we use the right service value
1509 if (client->cl_rawcred.service != gc.gc_svc) {
1510 client->cl_rawcred.service = gc.gc_svc;
1511 svc_rpc_gss_set_flavor(client);
1516 if (rqst->rq_proc != NULLPROC) {
1517 result = AUTH_REJECTEDCRED;
1521 call_stat = svc_sendreply(rqst,
1522 (xdrproc_t) xdr_void, (caddr_t) NULL);
1525 result = AUTH_FAILED;
1529 svc_rpc_gss_forget_client(client);
1531 result = RPCSEC_GSS_NODISPATCH;
1537 result = AUTH_BADCRED;
1542 svc_rpc_gss_release_client(client);
1544 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1545 KGSS_CURVNET_RESTORE();
1550 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1552 struct svc_rpc_gss_cookedcred *cc;
1553 struct svc_rpc_gss_client *client;
1555 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1557 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1558 client = cc->cc_client;
1559 if (client->cl_state != CLIENT_ESTABLISHED
1560 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1564 return (xdr_rpc_gss_wrap_data(mp,
1565 client->cl_ctx, client->cl_qop,
1566 cc->cc_service, cc->cc_seq));
1570 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1572 struct svc_rpc_gss_cookedcred *cc;
1573 struct svc_rpc_gss_client *client;
1575 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1577 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1578 client = cc->cc_client;
1579 if (client->cl_state != CLIENT_ESTABLISHED
1580 || cc->cc_service == rpc_gss_svc_none) {
1584 return (xdr_rpc_gss_unwrap_data(mp,
1585 client->cl_ctx, client->cl_qop,
1586 cc->cc_service, cc->cc_seq));
1590 svc_rpc_gss_release(SVCAUTH *auth)
1592 struct svc_rpc_gss_cookedcred *cc;
1593 struct svc_rpc_gss_client *client;
1595 rpc_gss_log_debug("in svc_rpc_gss_release()");
1597 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1598 client = cc->cc_client;
1599 svc_rpc_gss_release_client(client);