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 128
174 u_int svc_rpc_gss_client_max = CLIENT_MAX;
176 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW, 0, "RPC");
177 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW, 0, "GSS");
179 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
180 &svc_rpc_gss_client_max, 0,
181 "Max number of rpc-gss clients");
183 static u_int svc_rpc_gss_client_count;
184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
185 &svc_rpc_gss_client_count, 0,
186 "Number of rpc-gss clients");
188 struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
189 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
190 static uint32_t svc_rpc_gss_next_clientid = 1;
193 svc_rpc_gss_init(void *arg)
197 for (i = 0; i < CLIENT_HASH_SIZE; i++)
198 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
199 TAILQ_INIT(&svc_rpc_gss_clients);
200 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
201 sx_init(&svc_rpc_gss_lock, "gsslock");
203 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
206 rpc_gss_set_callback(rpc_gss_callback_t *cb)
208 struct svc_rpc_gss_callback *scb;
210 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
212 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
215 scb->cb_callback = *cb;
216 sx_xlock(&svc_rpc_gss_lock);
217 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
218 sx_xunlock(&svc_rpc_gss_lock);
224 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
226 struct svc_rpc_gss_callback *scb;
228 sx_xlock(&svc_rpc_gss_lock);
229 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
230 if (scb->cb_callback.program == cb->program
231 && scb->cb_callback.version == cb->version
232 && scb->cb_callback.callback == cb->callback) {
233 SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
234 svc_rpc_gss_callback, cb_link);
235 sx_xunlock(&svc_rpc_gss_lock);
236 mem_free(scb, sizeof(*scb));
240 sx_xunlock(&svc_rpc_gss_lock);
244 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
246 OM_uint32 maj_stat, min_stat;
247 gss_buffer_desc namebuf;
249 gss_OID_set_desc oid_set;
252 oid_set.elements = sname->sn_mech;
254 namebuf.value = (void *) sname->sn_principal;
255 namebuf.length = strlen(sname->sn_principal);
257 maj_stat = gss_import_name(&min_stat, &namebuf,
258 GSS_C_NT_HOSTBASED_SERVICE, &name);
259 if (maj_stat != GSS_S_COMPLETE)
262 if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
263 gss_release_cred(&min_stat, &sname->sn_cred);
265 maj_stat = gss_acquire_cred(&min_stat, name,
266 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
268 if (maj_stat != GSS_S_COMPLETE) {
269 gss_release_name(&min_stat, &name);
272 gss_release_name(&min_stat, &name);
278 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
279 u_int req_time, u_int program, u_int version)
281 struct svc_rpc_gss_svc_name *sname;
284 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
287 sname = mem_alloc(sizeof(*sname));
290 sname->sn_principal = strdup(principal, M_RPC);
291 sname->sn_mech = mech_oid;
292 sname->sn_req_time = req_time;
293 sname->sn_cred = GSS_C_NO_CREDENTIAL;
294 sname->sn_program = program;
295 sname->sn_version = version;
297 if (!rpc_gss_acquire_svc_cred(sname)) {
298 free(sname->sn_principal, M_RPC);
299 mem_free(sname, sizeof(*sname));
303 sx_xlock(&svc_rpc_gss_lock);
304 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
305 sx_xunlock(&svc_rpc_gss_lock);
311 rpc_gss_clear_svc_name(u_int program, u_int version)
314 struct svc_rpc_gss_svc_name *sname;
316 sx_xlock(&svc_rpc_gss_lock);
317 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
318 if (sname->sn_program == program
319 && sname->sn_version == version) {
320 SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
321 svc_rpc_gss_svc_name, sn_link);
322 sx_xunlock(&svc_rpc_gss_lock);
323 gss_release_cred(&min_stat, &sname->sn_cred);
324 free(sname->sn_principal, M_RPC);
325 mem_free(sname, sizeof(*sname));
329 sx_xunlock(&svc_rpc_gss_lock);
333 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
334 const char *mech, const char *name, const char *node, const char *domain)
336 OM_uint32 maj_stat, min_stat;
340 gss_name_t gss_name, gss_mech_name;
341 rpc_gss_principal_t result;
343 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
347 * Construct a gss_buffer containing the full name formatted
348 * as "name/node@domain" where node and domain are optional.
350 namelen = strlen(name) + 1;
352 namelen += strlen(node) + 1;
355 namelen += strlen(domain) + 1;
358 buf.value = mem_alloc(namelen);
359 buf.length = namelen;
360 strcpy((char *) buf.value, name);
362 strcat((char *) buf.value, "/");
363 strcat((char *) buf.value, node);
366 strcat((char *) buf.value, "@");
367 strcat((char *) buf.value, domain);
371 * Convert that to a gss_name_t and then convert that to a
372 * mechanism name in the selected mechanism.
374 maj_stat = gss_import_name(&min_stat, &buf,
375 GSS_C_NT_USER_NAME, &gss_name);
376 mem_free(buf.value, buf.length);
377 if (maj_stat != GSS_S_COMPLETE) {
378 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
381 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
383 if (maj_stat != GSS_S_COMPLETE) {
384 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
386 gss_release_name(&min_stat, &gss_name);
389 gss_release_name(&min_stat, &gss_name);
392 * Export the mechanism name and use that to construct the
393 * rpc_gss_principal_t result.
395 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
396 if (maj_stat != GSS_S_COMPLETE) {
397 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
398 gss_release_name(&min_stat, &gss_mech_name);
401 gss_release_name(&min_stat, &gss_mech_name);
403 result = mem_alloc(sizeof(int) + buf.length);
405 gss_release_buffer(&min_stat, &buf);
408 result->len = buf.length;
409 memcpy(result->name, buf.value, buf.length);
410 gss_release_buffer(&min_stat, &buf);
417 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
418 rpc_gss_ucred_t **ucred, void **cookie)
420 struct svc_rpc_gss_cookedcred *cc;
421 struct svc_rpc_gss_client *client;
423 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
426 cc = req->rq_clntcred;
427 client = cc->cc_client;
429 *rcred = &client->cl_rawcred;
431 *ucred = &client->cl_ucred;
433 *cookie = client->cl_cookie;
438 * This simpler interface is used by svc_getcred to copy the cred data
439 * into a kernel cred structure.
442 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
445 struct svc_rpc_gss_cookedcred *cc;
446 struct svc_rpc_gss_client *client;
449 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
452 cc = req->rq_clntcred;
453 client = cc->cc_client;
456 *flavorp = client->cl_rpcflavor;
458 if (client->cl_cred) {
459 *crp = crhold(client->cl_cred);
463 uc = &client->cl_ucred;
464 cr = client->cl_cred = crget();
465 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
466 cr->cr_rgid = cr->cr_svgid = uc->gid;
467 crsetgroups(cr, uc->gidlen, uc->gidlist);
468 cr->cr_prison = &prison0;
469 prison_hold(cr->cr_prison);
476 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
478 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
479 struct svc_rpc_gss_client *client = cc->cc_client;
482 OM_uint32 maj_stat, min_stat;
485 switch (client->cl_rawcred.service) {
486 case rpc_gss_svc_none:
487 return (max_tp_unit_len);
490 case rpc_gss_svc_default:
491 case rpc_gss_svc_integrity:
495 case rpc_gss_svc_privacy:
503 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
504 client->cl_qop, max_tp_unit_len, &max);
506 if (maj_stat == GSS_S_COMPLETE) {
512 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
518 static struct svc_rpc_gss_client *
519 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
521 struct svc_rpc_gss_client *client;
522 struct svc_rpc_gss_client_list *list;
523 struct timeval boottime;
524 unsigned long hostid;
526 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
528 getcredhostid(curthread->td_ucred, &hostid);
529 getboottime(&boottime);
530 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
533 list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
534 sx_xlock(&svc_rpc_gss_lock);
535 TAILQ_FOREACH(client, list, cl_link) {
536 if (client->cl_id.ci_id == id->ci_id) {
538 * Move this client to the front of the LRU
541 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
542 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
544 refcount_acquire(&client->cl_refs);
548 sx_xunlock(&svc_rpc_gss_lock);
553 static struct svc_rpc_gss_client *
554 svc_rpc_gss_create_client(void)
556 struct svc_rpc_gss_client *client;
557 struct svc_rpc_gss_client_list *list;
558 struct timeval boottime;
559 unsigned long hostid;
561 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
563 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
564 memset(client, 0, sizeof(struct svc_rpc_gss_client));
565 refcount_init(&client->cl_refs, 1);
566 sx_init(&client->cl_lock, "GSS-client");
567 getcredhostid(curthread->td_ucred, &hostid);
568 client->cl_id.ci_hostid = hostid;
569 getboottime(&boottime);
570 client->cl_id.ci_boottime = boottime.tv_sec;
571 client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
572 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
573 sx_xlock(&svc_rpc_gss_lock);
574 TAILQ_INSERT_HEAD(list, client, cl_link);
575 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
576 svc_rpc_gss_client_count++;
577 sx_xunlock(&svc_rpc_gss_lock);
580 * Start the client off with a short expiration time. We will
581 * try to get a saner value from the client creds later.
583 client->cl_state = CLIENT_NEW;
584 client->cl_locked = FALSE;
585 client->cl_expiration = time_uptime + 5*60;
591 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
595 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
598 gss_delete_sec_context(&min_stat,
599 &client->cl_ctx, GSS_C_NO_BUFFER);
601 if (client->cl_cname)
602 gss_release_name(&min_stat, &client->cl_cname);
604 if (client->cl_rawcred.client_principal)
605 mem_free(client->cl_rawcred.client_principal,
606 sizeof(*client->cl_rawcred.client_principal)
607 + client->cl_rawcred.client_principal->len);
610 crfree(client->cl_cred);
612 sx_destroy(&client->cl_lock);
613 mem_free(client, sizeof(*client));
617 * Drop a reference to a client and free it if that was the last reference.
620 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
623 if (!refcount_release(&client->cl_refs))
625 svc_rpc_gss_destroy_client(client);
629 * Remove a client from our global lists.
630 * Must be called with svc_rpc_gss_lock held.
633 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
635 struct svc_rpc_gss_client_list *list;
637 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
638 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
639 TAILQ_REMOVE(list, client, cl_link);
640 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
641 svc_rpc_gss_client_count--;
645 * Remove a client from our global lists and free it if we can.
648 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
650 struct svc_rpc_gss_client_list *list;
651 struct svc_rpc_gss_client *tclient;
653 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
654 sx_xlock(&svc_rpc_gss_lock);
655 TAILQ_FOREACH(tclient, list, cl_link) {
657 * Make sure this client has not already been removed
658 * from the lists by svc_rpc_gss_forget_client() or
659 * svc_rpc_gss_forget_client_locked().
661 if (client == tclient) {
662 svc_rpc_gss_forget_client_locked(client);
663 sx_xunlock(&svc_rpc_gss_lock);
664 svc_rpc_gss_release_client(client);
668 sx_xunlock(&svc_rpc_gss_lock);
672 svc_rpc_gss_timeout_clients(void)
674 struct svc_rpc_gss_client *client;
675 time_t now = time_uptime;
677 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
680 * First enforce the max client limit. We keep
681 * svc_rpc_gss_clients in LRU order.
683 sx_xlock(&svc_rpc_gss_lock);
684 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
685 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
686 svc_rpc_gss_forget_client_locked(client);
687 sx_xunlock(&svc_rpc_gss_lock);
688 svc_rpc_gss_release_client(client);
689 sx_xlock(&svc_rpc_gss_lock);
690 client = TAILQ_LAST(&svc_rpc_gss_clients,
691 svc_rpc_gss_client_list);
694 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
695 if (client->cl_state == CLIENT_STALE
696 || now > client->cl_expiration) {
697 svc_rpc_gss_forget_client_locked(client);
698 sx_xunlock(&svc_rpc_gss_lock);
699 rpc_gss_log_debug("expiring client %p", client);
700 svc_rpc_gss_release_client(client);
701 sx_xlock(&svc_rpc_gss_lock);
705 sx_xunlock(&svc_rpc_gss_lock);
710 * OID<->string routines. These are uuuuugly.
713 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
716 unsigned long number;
718 size_t string_length;
723 /* Decoded according to krb5/gssapi_krb5.c */
725 /* First determine the size of the string */
729 cp = (unsigned char *) oid->elements;
730 number = (unsigned long) cp[0];
731 sprintf(numstr, "%ld ", number/40);
732 string_length += strlen(numstr);
733 sprintf(numstr, "%ld ", number%40);
734 string_length += strlen(numstr);
735 for (i=1; i<oid->length; i++) {
736 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
737 number = (number << 7) | (cp[i] & 0x7f);
742 return(GSS_S_FAILURE);
744 if ((cp[i] & 0x80) == 0) {
745 sprintf(numstr, "%ld ", number);
746 string_length += strlen(numstr);
752 * If we get here, we've calculated the length of "n n n ... n ". Add 4
753 * here for "{ " and "}\0".
756 if ((bp = (char *) mem_alloc(string_length))) {
758 number = (unsigned long) cp[0];
759 sprintf(numstr, "%ld ", number/40);
761 sprintf(numstr, "%ld ", number%40);
764 cp = (unsigned char *) oid->elements;
765 for (i=1; i<oid->length; i++) {
766 number = (number << 7) | (cp[i] & 0x7f);
767 if ((cp[i] & 0x80) == 0) {
768 sprintf(numstr, "%ld ", number);
774 oid_str->length = strlen(bp)+1;
775 oid_str->value = (void *) bp;
777 return(GSS_S_COMPLETE);
780 return(GSS_S_FAILURE);
785 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
786 const gss_name_t name)
788 OM_uint32 maj_stat, min_stat;
789 rpc_gss_ucred_t *uc = &client->cl_ucred;
794 uc->gidlist = client->cl_gid_storage;
797 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
798 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
799 if (GSS_ERROR(maj_stat))
802 uc->gidlen = numgroups;
806 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
808 static gss_OID_desc krb5_mech_oid =
809 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
812 * Attempt to translate mech type and service into a
813 * 'pseudo flavor'. Hardwire in krb5 support for now.
815 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
816 switch (client->cl_rawcred.service) {
817 case rpc_gss_svc_default:
818 case rpc_gss_svc_none:
819 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
821 case rpc_gss_svc_integrity:
822 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
824 case rpc_gss_svc_privacy:
825 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
829 client->cl_rpcflavor = RPCSEC_GSS;
834 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
835 struct svc_req *rqst,
836 struct rpc_gss_init_res *gr,
837 struct rpc_gss_cred *gc)
839 gss_buffer_desc recv_tok;
841 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
842 OM_uint32 cred_lifetime;
843 struct svc_rpc_gss_svc_name *sname;
845 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
847 /* Deserialize arguments. */
848 memset(&recv_tok, 0, sizeof(recv_tok));
850 if (!svc_getargs(rqst,
851 (xdrproc_t) xdr_gss_buffer_desc,
852 (caddr_t) &recv_tok)) {
853 client->cl_state = CLIENT_STALE;
858 * First time round, try all the server names we have until
859 * one matches. Afterwards, stick with that one.
861 sx_xlock(&svc_rpc_gss_lock);
862 if (!client->cl_sname) {
863 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
864 if (sname->sn_program == rqst->rq_prog
865 && sname->sn_version == rqst->rq_vers) {
867 gr->gr_major = gss_accept_sec_context(
872 GSS_C_NO_CHANNEL_BINDINGS,
880 GSS_S_CREDENTIALS_EXPIRED) {
882 * Either our creds really did
886 if (rpc_gss_acquire_svc_cred(sname))
889 client->cl_sname = sname;
894 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
896 sx_xunlock(&svc_rpc_gss_lock);
900 gr->gr_major = gss_accept_sec_context(
903 client->cl_sname->sn_cred,
905 GSS_C_NO_CHANNEL_BINDINGS,
913 sx_xunlock(&svc_rpc_gss_lock);
915 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
918 * If we get an error from gss_accept_sec_context, send the
919 * reply anyway so that the client gets a chance to see what
922 if (gr->gr_major != GSS_S_COMPLETE &&
923 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
924 rpc_gss_log_status("accept_sec_context", client->cl_mech,
925 gr->gr_major, gr->gr_minor);
926 client->cl_state = CLIENT_STALE;
930 gr->gr_handle.value = &client->cl_id;
931 gr->gr_handle.length = sizeof(client->cl_id);
932 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
934 /* Save client info. */
935 client->cl_mech = mech;
936 client->cl_qop = GSS_C_QOP_DEFAULT;
937 client->cl_done_callback = FALSE;
939 if (gr->gr_major == GSS_S_COMPLETE) {
940 gss_buffer_desc export_name;
943 * Change client expiration time to be near when the
944 * client creds expire (or 24 hours if we can't figure
947 if (cred_lifetime == GSS_C_INDEFINITE)
948 cred_lifetime = time_uptime + 24*60*60;
950 client->cl_expiration = time_uptime + cred_lifetime;
953 * Fill in cred details in the rawcred structure.
955 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
956 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
957 maj_stat = gss_export_name(&min_stat, client->cl_cname,
959 if (maj_stat != GSS_S_COMPLETE) {
960 rpc_gss_log_status("gss_export_name", client->cl_mech,
964 client->cl_rawcred.client_principal =
965 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
966 + export_name.length);
967 client->cl_rawcred.client_principal->len = export_name.length;
968 memcpy(client->cl_rawcred.client_principal->name,
969 export_name.value, export_name.length);
970 gss_release_buffer(&min_stat, &export_name);
971 client->cl_rawcred.svc_principal =
972 client->cl_sname->sn_principal;
973 client->cl_rawcred.service = gc->gc_svc;
976 * Use gss_pname_to_uid to map to unix creds. For
977 * kerberos5, this uses krb5_aname_to_localname.
979 svc_rpc_gss_build_ucred(client, client->cl_cname);
980 svc_rpc_gss_set_flavor(client);
981 gss_release_name(&min_stat, &client->cl_cname);
985 gss_buffer_desc mechname;
987 gss_oid_to_str(&min_stat, mech, &mechname);
989 rpc_gss_log_debug("accepted context for %s with "
990 "<mech %.*s, qop %d, svc %d>",
991 client->cl_rawcred.client_principal->name,
992 mechname.length, (char *)mechname.value,
993 client->cl_qop, client->cl_rawcred.service);
995 gss_release_buffer(&min_stat, &mechname);
1003 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1004 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1006 struct opaque_auth *oa;
1007 gss_buffer_desc rpcbuf, checksum;
1008 OM_uint32 maj_stat, min_stat;
1009 gss_qop_t qop_state;
1010 int32_t rpchdr[128 / sizeof(int32_t)];
1013 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1015 memset(rpchdr, 0, sizeof(rpchdr));
1017 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1019 IXDR_PUT_LONG(buf, msg->rm_xid);
1020 IXDR_PUT_ENUM(buf, msg->rm_direction);
1021 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1022 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1023 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1024 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1025 oa = &msg->rm_call.cb_cred;
1026 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1027 IXDR_PUT_LONG(buf, oa->oa_length);
1028 if (oa->oa_length) {
1029 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1030 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1032 rpcbuf.value = rpchdr;
1033 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1035 checksum.value = msg->rm_call.cb_verf.oa_base;
1036 checksum.length = msg->rm_call.cb_verf.oa_length;
1038 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1041 if (maj_stat != GSS_S_COMPLETE) {
1042 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1043 maj_stat, min_stat);
1045 * A bug in some versions of the Linux client generates a
1046 * Destroy operation with a bogus encrypted checksum. Deleting
1047 * the credential handle for that case causes the mount to fail.
1048 * Since the checksum is bogus (gss_verify_mic() failed), it
1049 * doesn't make sense to destroy the handle and not doing so
1050 * fixes the Linux mount.
1052 if (gcproc != RPCSEC_GSS_DESTROY)
1053 client->cl_state = CLIENT_STALE;
1062 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1063 struct svc_req *rqst, u_int seq)
1065 gss_buffer_desc signbuf;
1066 gss_buffer_desc mic;
1067 OM_uint32 maj_stat, min_stat;
1070 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1073 signbuf.value = &nseq;
1074 signbuf.length = sizeof(nseq);
1076 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1079 if (maj_stat != GSS_S_COMPLETE) {
1080 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1081 client->cl_state = CLIENT_STALE;
1085 KASSERT(mic.length <= MAX_AUTH_BYTES,
1086 ("MIC too large for RPCSEC_GSS"));
1088 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1089 rqst->rq_verf.oa_length = mic.length;
1090 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1092 gss_release_buffer(&min_stat, &mic);
1098 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1100 struct svc_rpc_gss_callback *scb;
1101 rpc_gss_lock_t lock;
1107 * See if we have a callback for this guy.
1110 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1111 if (scb->cb_callback.program == rqst->rq_prog
1112 && scb->cb_callback.version == rqst->rq_vers) {
1114 * This one matches. Call the callback and see
1115 * if it wants to veto or something.
1117 lock.locked = FALSE;
1118 lock.raw_cred = &client->cl_rawcred;
1119 cb_res = scb->cb_callback.callback(rqst,
1126 client->cl_state = CLIENT_STALE;
1132 * The callback accepted the connection - it
1133 * is responsible for freeing client->cl_creds
1136 client->cl_creds = GSS_C_NO_CREDENTIAL;
1137 client->cl_locked = lock.locked;
1138 client->cl_cookie = cookie;
1144 * Either no callback exists for this program/version or one
1145 * of the callbacks rejected the connection. We just need to
1146 * clean up the delegated client creds, if any.
1148 if (client->cl_creds) {
1150 gss_release_cred(&min_ver, &client->cl_creds);
1156 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1162 sx_xlock(&client->cl_lock);
1163 if (seq <= client->cl_seqlast) {
1165 * The request sequence number is less than
1166 * the largest we have seen so far. If it is
1167 * outside the window or if we have seen a
1168 * request with this sequence before, silently
1171 offset = client->cl_seqlast - seq;
1172 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1178 if (client->cl_seqmask[word] & (1 << bit)) {
1186 sx_xunlock(&client->cl_lock);
1191 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1193 int offset, i, word, bit;
1194 uint32_t carry, newcarry;
1196 sx_xlock(&client->cl_lock);
1197 if (seq > client->cl_seqlast) {
1199 * This request has a sequence number greater
1200 * than any we have seen so far. Advance the
1201 * seq window and set bit zero of the window
1202 * (which corresponds to the new sequence
1205 offset = seq - client->cl_seqlast;
1206 while (offset > 32) {
1207 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1209 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1211 client->cl_seqmask[0] = 0;
1215 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1216 newcarry = client->cl_seqmask[i] >> (32 - offset);
1217 client->cl_seqmask[i] =
1218 (client->cl_seqmask[i] << offset) | carry;
1221 client->cl_seqmask[0] |= 1;
1222 client->cl_seqlast = seq;
1224 offset = client->cl_seqlast - seq;
1227 client->cl_seqmask[word] |= (1 << bit);
1229 sx_xunlock(&client->cl_lock);
1233 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1238 struct svc_rpc_gss_cookedcred *cc;
1239 struct svc_rpc_gss_client *client;
1240 struct rpc_gss_cred gc;
1241 struct rpc_gss_init_res gr;
1244 enum auth_stat result;
1246 rpc_gss_log_debug("in svc_rpc_gss()");
1248 /* Garbage collect old clients. */
1249 svc_rpc_gss_timeout_clients();
1251 /* Initialize reply. */
1252 rqst->rq_verf = _null_auth;
1254 /* Deserialize client credentials. */
1255 if (rqst->rq_cred.oa_length <= 0)
1256 return (AUTH_BADCRED);
1258 memset(&gc, 0, sizeof(gc));
1260 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1261 rqst->rq_cred.oa_length, XDR_DECODE);
1263 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1265 return (AUTH_BADCRED);
1271 /* Check version. */
1272 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1273 result = AUTH_BADCRED;
1277 /* Check the proc and find the client (or create it) */
1278 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1279 if (gc.gc_handle.length != 0) {
1280 result = AUTH_BADCRED;
1283 client = svc_rpc_gss_create_client();
1284 refcount_acquire(&client->cl_refs);
1286 struct svc_rpc_gss_clientid *p;
1287 if (gc.gc_handle.length != sizeof(*p)) {
1288 result = AUTH_BADCRED;
1291 p = gc.gc_handle.value;
1292 client = svc_rpc_gss_find_client(p);
1295 * Can't find the client - we may have
1296 * destroyed it - tell the other side to
1299 result = RPCSEC_GSS_CREDPROBLEM;
1303 cc = rqst->rq_clntcred;
1304 cc->cc_client = client;
1305 cc->cc_service = gc.gc_svc;
1306 cc->cc_seq = gc.gc_seq;
1309 * The service and sequence number must be ignored for
1310 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1312 if (gc.gc_proc != RPCSEC_GSS_INIT
1313 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1315 * Check for sequence number overflow.
1317 if (gc.gc_seq >= MAXSEQ) {
1318 result = RPCSEC_GSS_CTXPROBLEM;
1323 * Check for valid service.
1325 if (gc.gc_svc != rpc_gss_svc_none &&
1326 gc.gc_svc != rpc_gss_svc_integrity &&
1327 gc.gc_svc != rpc_gss_svc_privacy) {
1328 result = AUTH_BADCRED;
1333 /* Handle RPCSEC_GSS control procedure. */
1334 switch (gc.gc_proc) {
1336 case RPCSEC_GSS_INIT:
1337 case RPCSEC_GSS_CONTINUE_INIT:
1338 if (rqst->rq_proc != NULLPROC) {
1339 result = AUTH_REJECTEDCRED;
1343 memset(&gr, 0, sizeof(gr));
1344 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1345 result = AUTH_REJECTEDCRED;
1349 if (gr.gr_major == GSS_S_COMPLETE) {
1351 * We borrow the space for the call verf to
1352 * pack our reply verf.
1354 rqst->rq_verf = msg->rm_call.cb_verf;
1355 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1356 result = AUTH_REJECTEDCRED;
1360 rqst->rq_verf = _null_auth;
1363 call_stat = svc_sendreply(rqst,
1364 (xdrproc_t) xdr_rpc_gss_init_res,
1367 gss_release_buffer(&min_stat, &gr.gr_token);
1370 result = AUTH_FAILED;
1374 if (gr.gr_major == GSS_S_COMPLETE)
1375 client->cl_state = CLIENT_ESTABLISHED;
1377 result = RPCSEC_GSS_NODISPATCH;
1380 case RPCSEC_GSS_DATA:
1381 case RPCSEC_GSS_DESTROY:
1382 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1383 result = RPCSEC_GSS_NODISPATCH;
1387 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1388 result = RPCSEC_GSS_CREDPROBLEM;
1393 * We borrow the space for the call verf to pack our
1396 rqst->rq_verf = msg->rm_call.cb_verf;
1397 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1398 result = RPCSEC_GSS_CTXPROBLEM;
1402 svc_rpc_gss_update_seq(client, gc.gc_seq);
1405 * Change the SVCAUTH ops on the request to point at
1406 * our own code so that we can unwrap the arguments
1407 * and wrap the result. The caller will re-set this on
1408 * every request to point to a set of null wrap/unwrap
1409 * methods. Acquire an extra reference to the client
1410 * which will be released by svc_rpc_gss_release()
1411 * after the request has finished processing.
1413 refcount_acquire(&client->cl_refs);
1414 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1415 rqst->rq_auth.svc_ah_private = cc;
1417 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1419 * We might be ready to do a callback to the server to
1420 * see if it wants to accept/reject the connection.
1422 sx_xlock(&client->cl_lock);
1423 if (!client->cl_done_callback) {
1424 client->cl_done_callback = TRUE;
1425 client->cl_qop = qop;
1426 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1427 client->cl_rawcred.mechanism, qop);
1428 if (!svc_rpc_gss_callback(client, rqst)) {
1429 result = AUTH_REJECTEDCRED;
1430 sx_xunlock(&client->cl_lock);
1434 sx_xunlock(&client->cl_lock);
1437 * If the server has locked this client to a
1438 * particular service+qop pair, enforce that
1441 if (client->cl_locked) {
1442 if (client->cl_rawcred.service != gc.gc_svc) {
1443 result = AUTH_FAILED;
1445 } else if (client->cl_qop != qop) {
1446 result = AUTH_BADVERF;
1452 * If the qop changed, look up the new qop
1455 if (client->cl_qop != qop) {
1456 client->cl_qop = qop;
1457 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1458 client->cl_rawcred.mechanism, qop);
1462 * Make sure we use the right service value
1465 if (client->cl_rawcred.service != gc.gc_svc) {
1466 client->cl_rawcred.service = gc.gc_svc;
1467 svc_rpc_gss_set_flavor(client);
1472 if (rqst->rq_proc != NULLPROC) {
1473 result = AUTH_REJECTEDCRED;
1477 call_stat = svc_sendreply(rqst,
1478 (xdrproc_t) xdr_void, (caddr_t) NULL);
1481 result = AUTH_FAILED;
1485 svc_rpc_gss_forget_client(client);
1487 result = RPCSEC_GSS_NODISPATCH;
1493 result = AUTH_BADCRED;
1498 svc_rpc_gss_release_client(client);
1500 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1505 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1507 struct svc_rpc_gss_cookedcred *cc;
1508 struct svc_rpc_gss_client *client;
1510 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1512 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1513 client = cc->cc_client;
1514 if (client->cl_state != CLIENT_ESTABLISHED
1515 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1519 return (xdr_rpc_gss_wrap_data(mp,
1520 client->cl_ctx, client->cl_qop,
1521 cc->cc_service, cc->cc_seq));
1525 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1527 struct svc_rpc_gss_cookedcred *cc;
1528 struct svc_rpc_gss_client *client;
1530 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1532 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1533 client = cc->cc_client;
1534 if (client->cl_state != CLIENT_ESTABLISHED
1535 || cc->cc_service == rpc_gss_svc_none) {
1539 return (xdr_rpc_gss_unwrap_data(mp,
1540 client->cl_ctx, client->cl_qop,
1541 cc->cc_service, cc->cc_seq));
1545 svc_rpc_gss_release(SVCAUTH *auth)
1547 struct svc_rpc_gss_cookedcred *cc;
1548 struct svc_rpc_gss_client *client;
1550 rpc_gss_log_debug("in svc_rpc_gss_release()");
1552 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1553 client = cc->cc_client;
1554 svc_rpc_gss_release_client(client);