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_client_count;
189 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
190 &svc_rpc_gss_client_count, 0,
191 "Number of rpc-gss clients");
193 struct svc_rpc_gss_client_list *svc_rpc_gss_client_hash;
194 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
195 static uint32_t svc_rpc_gss_next_clientid = 1;
198 svc_rpc_gss_init(void *arg)
202 svc_rpc_gss_client_hash = mem_alloc(sizeof(struct svc_rpc_gss_client_list) * svc_rpc_gss_client_hash_size);
203 for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
204 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
205 TAILQ_INIT(&svc_rpc_gss_clients);
206 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
207 sx_init(&svc_rpc_gss_lock, "gsslock");
209 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
212 rpc_gss_set_callback(rpc_gss_callback_t *cb)
214 struct svc_rpc_gss_callback *scb;
216 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
218 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
221 scb->cb_callback = *cb;
222 sx_xlock(&svc_rpc_gss_lock);
223 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
224 sx_xunlock(&svc_rpc_gss_lock);
230 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
232 struct svc_rpc_gss_callback *scb;
234 sx_xlock(&svc_rpc_gss_lock);
235 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
236 if (scb->cb_callback.program == cb->program
237 && scb->cb_callback.version == cb->version
238 && scb->cb_callback.callback == cb->callback) {
239 SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
240 svc_rpc_gss_callback, cb_link);
241 sx_xunlock(&svc_rpc_gss_lock);
242 mem_free(scb, sizeof(*scb));
246 sx_xunlock(&svc_rpc_gss_lock);
250 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
252 OM_uint32 maj_stat, min_stat;
253 gss_buffer_desc namebuf;
255 gss_OID_set_desc oid_set;
258 oid_set.elements = sname->sn_mech;
260 namebuf.value = (void *) sname->sn_principal;
261 namebuf.length = strlen(sname->sn_principal);
263 maj_stat = gss_import_name(&min_stat, &namebuf,
264 GSS_C_NT_HOSTBASED_SERVICE, &name);
265 if (maj_stat != GSS_S_COMPLETE)
268 if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
269 gss_release_cred(&min_stat, &sname->sn_cred);
271 maj_stat = gss_acquire_cred(&min_stat, name,
272 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
274 if (maj_stat != GSS_S_COMPLETE) {
275 gss_release_name(&min_stat, &name);
278 gss_release_name(&min_stat, &name);
284 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
285 u_int req_time, u_int program, u_int version)
287 struct svc_rpc_gss_svc_name *sname;
290 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
293 sname = mem_alloc(sizeof(*sname));
296 sname->sn_principal = strdup(principal, M_RPC);
297 sname->sn_mech = mech_oid;
298 sname->sn_req_time = req_time;
299 sname->sn_cred = GSS_C_NO_CREDENTIAL;
300 sname->sn_program = program;
301 sname->sn_version = version;
303 if (!rpc_gss_acquire_svc_cred(sname)) {
304 free(sname->sn_principal, M_RPC);
305 mem_free(sname, sizeof(*sname));
309 sx_xlock(&svc_rpc_gss_lock);
310 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
311 sx_xunlock(&svc_rpc_gss_lock);
317 rpc_gss_clear_svc_name(u_int program, u_int version)
320 struct svc_rpc_gss_svc_name *sname;
322 sx_xlock(&svc_rpc_gss_lock);
323 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
324 if (sname->sn_program == program
325 && sname->sn_version == version) {
326 SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
327 svc_rpc_gss_svc_name, sn_link);
328 sx_xunlock(&svc_rpc_gss_lock);
329 gss_release_cred(&min_stat, &sname->sn_cred);
330 free(sname->sn_principal, M_RPC);
331 mem_free(sname, sizeof(*sname));
335 sx_xunlock(&svc_rpc_gss_lock);
339 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
340 const char *mech, const char *name, const char *node, const char *domain)
342 OM_uint32 maj_stat, min_stat;
346 gss_name_t gss_name, gss_mech_name;
347 rpc_gss_principal_t result;
349 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
353 * Construct a gss_buffer containing the full name formatted
354 * as "name/node@domain" where node and domain are optional.
356 namelen = strlen(name) + 1;
358 namelen += strlen(node) + 1;
361 namelen += strlen(domain) + 1;
364 buf.value = mem_alloc(namelen);
365 buf.length = namelen;
366 strcpy((char *) buf.value, name);
368 strcat((char *) buf.value, "/");
369 strcat((char *) buf.value, node);
372 strcat((char *) buf.value, "@");
373 strcat((char *) buf.value, domain);
377 * Convert that to a gss_name_t and then convert that to a
378 * mechanism name in the selected mechanism.
380 maj_stat = gss_import_name(&min_stat, &buf,
381 GSS_C_NT_USER_NAME, &gss_name);
382 mem_free(buf.value, buf.length);
383 if (maj_stat != GSS_S_COMPLETE) {
384 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
387 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
389 if (maj_stat != GSS_S_COMPLETE) {
390 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
392 gss_release_name(&min_stat, &gss_name);
395 gss_release_name(&min_stat, &gss_name);
398 * Export the mechanism name and use that to construct the
399 * rpc_gss_principal_t result.
401 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
402 if (maj_stat != GSS_S_COMPLETE) {
403 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
404 gss_release_name(&min_stat, &gss_mech_name);
407 gss_release_name(&min_stat, &gss_mech_name);
409 result = mem_alloc(sizeof(int) + buf.length);
411 gss_release_buffer(&min_stat, &buf);
414 result->len = buf.length;
415 memcpy(result->name, buf.value, buf.length);
416 gss_release_buffer(&min_stat, &buf);
423 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
424 rpc_gss_ucred_t **ucred, void **cookie)
426 struct svc_rpc_gss_cookedcred *cc;
427 struct svc_rpc_gss_client *client;
429 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
432 cc = req->rq_clntcred;
433 client = cc->cc_client;
435 *rcred = &client->cl_rawcred;
437 *ucred = &client->cl_ucred;
439 *cookie = client->cl_cookie;
444 * This simpler interface is used by svc_getcred to copy the cred data
445 * into a kernel cred structure.
448 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
451 struct svc_rpc_gss_cookedcred *cc;
452 struct svc_rpc_gss_client *client;
455 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
458 cc = req->rq_clntcred;
459 client = cc->cc_client;
462 *flavorp = client->cl_rpcflavor;
464 if (client->cl_cred) {
465 *crp = crhold(client->cl_cred);
469 uc = &client->cl_ucred;
470 cr = client->cl_cred = crget();
471 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
472 cr->cr_rgid = cr->cr_svgid = uc->gid;
473 crsetgroups(cr, uc->gidlen, uc->gidlist);
474 cr->cr_prison = &prison0;
475 prison_hold(cr->cr_prison);
482 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
484 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
485 struct svc_rpc_gss_client *client = cc->cc_client;
488 OM_uint32 maj_stat, min_stat;
491 switch (client->cl_rawcred.service) {
492 case rpc_gss_svc_none:
493 return (max_tp_unit_len);
496 case rpc_gss_svc_default:
497 case rpc_gss_svc_integrity:
501 case rpc_gss_svc_privacy:
509 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
510 client->cl_qop, max_tp_unit_len, &max);
512 if (maj_stat == GSS_S_COMPLETE) {
518 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
524 static struct svc_rpc_gss_client *
525 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
527 struct svc_rpc_gss_client *client;
528 struct svc_rpc_gss_client_list *list;
529 struct timeval boottime;
530 unsigned long hostid;
532 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
534 getcredhostid(curthread->td_ucred, &hostid);
535 getboottime(&boottime);
536 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
539 list = &svc_rpc_gss_client_hash[id->ci_id % svc_rpc_gss_client_hash_size];
540 sx_xlock(&svc_rpc_gss_lock);
541 TAILQ_FOREACH(client, list, cl_link) {
542 if (client->cl_id.ci_id == id->ci_id) {
544 * Move this client to the front of the LRU
547 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
548 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
550 refcount_acquire(&client->cl_refs);
554 sx_xunlock(&svc_rpc_gss_lock);
559 static struct svc_rpc_gss_client *
560 svc_rpc_gss_create_client(void)
562 struct svc_rpc_gss_client *client;
563 struct svc_rpc_gss_client_list *list;
564 struct timeval boottime;
565 unsigned long hostid;
567 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
569 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
570 memset(client, 0, sizeof(struct svc_rpc_gss_client));
571 refcount_init(&client->cl_refs, 2);
572 sx_init(&client->cl_lock, "GSS-client");
573 getcredhostid(curthread->td_ucred, &hostid);
574 client->cl_id.ci_hostid = hostid;
575 getboottime(&boottime);
576 client->cl_id.ci_boottime = boottime.tv_sec;
577 client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
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;
587 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
588 sx_xlock(&svc_rpc_gss_lock);
589 TAILQ_INSERT_HEAD(list, client, cl_link);
590 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
591 svc_rpc_gss_client_count++;
592 sx_xunlock(&svc_rpc_gss_lock);
597 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
601 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
604 gss_delete_sec_context(&min_stat,
605 &client->cl_ctx, GSS_C_NO_BUFFER);
607 if (client->cl_cname)
608 gss_release_name(&min_stat, &client->cl_cname);
610 if (client->cl_rawcred.client_principal)
611 mem_free(client->cl_rawcred.client_principal,
612 sizeof(*client->cl_rawcred.client_principal)
613 + client->cl_rawcred.client_principal->len);
616 crfree(client->cl_cred);
618 sx_destroy(&client->cl_lock);
619 mem_free(client, sizeof(*client));
623 * Drop a reference to a client and free it if that was the last reference.
626 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
629 if (!refcount_release(&client->cl_refs))
631 svc_rpc_gss_destroy_client(client);
635 * Remove a client from our global lists.
636 * Must be called with svc_rpc_gss_lock held.
639 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
641 struct svc_rpc_gss_client_list *list;
643 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
644 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
645 TAILQ_REMOVE(list, client, cl_link);
646 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
647 svc_rpc_gss_client_count--;
651 * Remove a client from our global lists and free it if we can.
654 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
656 struct svc_rpc_gss_client_list *list;
657 struct svc_rpc_gss_client *tclient;
659 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
660 sx_xlock(&svc_rpc_gss_lock);
661 TAILQ_FOREACH(tclient, list, cl_link) {
663 * Make sure this client has not already been removed
664 * from the lists by svc_rpc_gss_forget_client() or
665 * svc_rpc_gss_forget_client_locked().
667 if (client == tclient) {
668 svc_rpc_gss_forget_client_locked(client);
669 sx_xunlock(&svc_rpc_gss_lock);
670 svc_rpc_gss_release_client(client);
674 sx_xunlock(&svc_rpc_gss_lock);
678 svc_rpc_gss_timeout_clients(void)
680 struct svc_rpc_gss_client *client;
681 time_t now = time_uptime;
683 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
686 * First enforce the max client limit. We keep
687 * svc_rpc_gss_clients in LRU order.
689 sx_xlock(&svc_rpc_gss_lock);
690 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
691 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
692 svc_rpc_gss_forget_client_locked(client);
693 sx_xunlock(&svc_rpc_gss_lock);
694 svc_rpc_gss_release_client(client);
695 sx_xlock(&svc_rpc_gss_lock);
696 client = TAILQ_LAST(&svc_rpc_gss_clients,
697 svc_rpc_gss_client_list);
700 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
701 if (client->cl_state == CLIENT_STALE
702 || now > client->cl_expiration) {
703 svc_rpc_gss_forget_client_locked(client);
704 sx_xunlock(&svc_rpc_gss_lock);
705 rpc_gss_log_debug("expiring client %p", client);
706 svc_rpc_gss_release_client(client);
707 sx_xlock(&svc_rpc_gss_lock);
711 sx_xunlock(&svc_rpc_gss_lock);
716 * OID<->string routines. These are uuuuugly.
719 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
722 unsigned long number;
724 size_t string_length;
729 /* Decoded according to krb5/gssapi_krb5.c */
731 /* First determine the size of the string */
735 cp = (unsigned char *) oid->elements;
736 number = (unsigned long) cp[0];
737 sprintf(numstr, "%ld ", number/40);
738 string_length += strlen(numstr);
739 sprintf(numstr, "%ld ", number%40);
740 string_length += strlen(numstr);
741 for (i=1; i<oid->length; i++) {
742 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
743 number = (number << 7) | (cp[i] & 0x7f);
748 return(GSS_S_FAILURE);
750 if ((cp[i] & 0x80) == 0) {
751 sprintf(numstr, "%ld ", number);
752 string_length += strlen(numstr);
758 * If we get here, we've calculated the length of "n n n ... n ". Add 4
759 * here for "{ " and "}\0".
762 if ((bp = (char *) mem_alloc(string_length))) {
764 number = (unsigned long) cp[0];
765 sprintf(numstr, "%ld ", number/40);
767 sprintf(numstr, "%ld ", number%40);
770 cp = (unsigned char *) oid->elements;
771 for (i=1; i<oid->length; i++) {
772 number = (number << 7) | (cp[i] & 0x7f);
773 if ((cp[i] & 0x80) == 0) {
774 sprintf(numstr, "%ld ", number);
780 oid_str->length = strlen(bp)+1;
781 oid_str->value = (void *) bp;
783 return(GSS_S_COMPLETE);
786 return(GSS_S_FAILURE);
791 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
792 const gss_name_t name)
794 OM_uint32 maj_stat, min_stat;
795 rpc_gss_ucred_t *uc = &client->cl_ucred;
800 uc->gidlist = client->cl_gid_storage;
803 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
804 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
805 if (GSS_ERROR(maj_stat))
808 uc->gidlen = numgroups;
812 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
814 static gss_OID_desc krb5_mech_oid =
815 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
818 * Attempt to translate mech type and service into a
819 * 'pseudo flavor'. Hardwire in krb5 support for now.
821 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
822 switch (client->cl_rawcred.service) {
823 case rpc_gss_svc_default:
824 case rpc_gss_svc_none:
825 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
827 case rpc_gss_svc_integrity:
828 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
830 case rpc_gss_svc_privacy:
831 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
835 client->cl_rpcflavor = RPCSEC_GSS;
840 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
841 struct svc_req *rqst,
842 struct rpc_gss_init_res *gr,
843 struct rpc_gss_cred *gc)
845 gss_buffer_desc recv_tok;
847 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
848 OM_uint32 cred_lifetime;
849 struct svc_rpc_gss_svc_name *sname;
851 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
853 /* Deserialize arguments. */
854 memset(&recv_tok, 0, sizeof(recv_tok));
856 if (!svc_getargs(rqst,
857 (xdrproc_t) xdr_gss_buffer_desc,
858 (caddr_t) &recv_tok)) {
859 client->cl_state = CLIENT_STALE;
864 * First time round, try all the server names we have until
865 * one matches. Afterwards, stick with that one.
867 sx_xlock(&svc_rpc_gss_lock);
868 if (!client->cl_sname) {
869 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
870 if (sname->sn_program == rqst->rq_prog
871 && sname->sn_version == rqst->rq_vers) {
873 gr->gr_major = gss_accept_sec_context(
878 GSS_C_NO_CHANNEL_BINDINGS,
886 GSS_S_CREDENTIALS_EXPIRED) {
888 * Either our creds really did
892 if (rpc_gss_acquire_svc_cred(sname))
895 client->cl_sname = sname;
900 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
902 sx_xunlock(&svc_rpc_gss_lock);
906 gr->gr_major = gss_accept_sec_context(
909 client->cl_sname->sn_cred,
911 GSS_C_NO_CHANNEL_BINDINGS,
919 sx_xunlock(&svc_rpc_gss_lock);
921 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
924 * If we get an error from gss_accept_sec_context, send the
925 * reply anyway so that the client gets a chance to see what
928 if (gr->gr_major != GSS_S_COMPLETE &&
929 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
930 rpc_gss_log_status("accept_sec_context", client->cl_mech,
931 gr->gr_major, gr->gr_minor);
932 client->cl_state = CLIENT_STALE;
936 gr->gr_handle.value = &client->cl_id;
937 gr->gr_handle.length = sizeof(client->cl_id);
938 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
940 /* Save client info. */
941 client->cl_mech = mech;
942 client->cl_qop = GSS_C_QOP_DEFAULT;
943 client->cl_done_callback = FALSE;
945 if (gr->gr_major == GSS_S_COMPLETE) {
946 gss_buffer_desc export_name;
949 * Change client expiration time to be near when the
950 * client creds expire (or 24 hours if we can't figure
953 if (cred_lifetime == GSS_C_INDEFINITE)
954 cred_lifetime = time_uptime + 24*60*60;
956 client->cl_expiration = time_uptime + cred_lifetime;
959 * Fill in cred details in the rawcred structure.
961 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
962 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
963 maj_stat = gss_export_name(&min_stat, client->cl_cname,
965 if (maj_stat != GSS_S_COMPLETE) {
966 rpc_gss_log_status("gss_export_name", client->cl_mech,
970 client->cl_rawcred.client_principal =
971 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
972 + export_name.length);
973 client->cl_rawcred.client_principal->len = export_name.length;
974 memcpy(client->cl_rawcred.client_principal->name,
975 export_name.value, export_name.length);
976 gss_release_buffer(&min_stat, &export_name);
977 client->cl_rawcred.svc_principal =
978 client->cl_sname->sn_principal;
979 client->cl_rawcred.service = gc->gc_svc;
982 * Use gss_pname_to_uid to map to unix creds. For
983 * kerberos5, this uses krb5_aname_to_localname.
985 svc_rpc_gss_build_ucred(client, client->cl_cname);
986 svc_rpc_gss_set_flavor(client);
987 gss_release_name(&min_stat, &client->cl_cname);
991 gss_buffer_desc mechname;
993 gss_oid_to_str(&min_stat, mech, &mechname);
995 rpc_gss_log_debug("accepted context for %s with "
996 "<mech %.*s, qop %d, svc %d>",
997 client->cl_rawcred.client_principal->name,
998 mechname.length, (char *)mechname.value,
999 client->cl_qop, client->cl_rawcred.service);
1001 gss_release_buffer(&min_stat, &mechname);
1009 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1010 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1012 struct opaque_auth *oa;
1013 gss_buffer_desc rpcbuf, checksum;
1014 OM_uint32 maj_stat, min_stat;
1015 gss_qop_t qop_state;
1016 int32_t rpchdr[128 / sizeof(int32_t)];
1019 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1021 memset(rpchdr, 0, sizeof(rpchdr));
1023 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1025 IXDR_PUT_LONG(buf, msg->rm_xid);
1026 IXDR_PUT_ENUM(buf, msg->rm_direction);
1027 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1028 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1029 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1030 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1031 oa = &msg->rm_call.cb_cred;
1032 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1033 IXDR_PUT_LONG(buf, oa->oa_length);
1034 if (oa->oa_length) {
1035 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1036 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1038 rpcbuf.value = rpchdr;
1039 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1041 checksum.value = msg->rm_call.cb_verf.oa_base;
1042 checksum.length = msg->rm_call.cb_verf.oa_length;
1044 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1047 if (maj_stat != GSS_S_COMPLETE) {
1048 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1049 maj_stat, min_stat);
1051 * A bug in some versions of the Linux client generates a
1052 * Destroy operation with a bogus encrypted checksum. Deleting
1053 * the credential handle for that case causes the mount to fail.
1054 * Since the checksum is bogus (gss_verify_mic() failed), it
1055 * doesn't make sense to destroy the handle and not doing so
1056 * fixes the Linux mount.
1058 if (gcproc != RPCSEC_GSS_DESTROY)
1059 client->cl_state = CLIENT_STALE;
1068 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1069 struct svc_req *rqst, u_int seq)
1071 gss_buffer_desc signbuf;
1072 gss_buffer_desc mic;
1073 OM_uint32 maj_stat, min_stat;
1076 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1079 signbuf.value = &nseq;
1080 signbuf.length = sizeof(nseq);
1082 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1085 if (maj_stat != GSS_S_COMPLETE) {
1086 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1087 client->cl_state = CLIENT_STALE;
1091 KASSERT(mic.length <= MAX_AUTH_BYTES,
1092 ("MIC too large for RPCSEC_GSS"));
1094 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1095 rqst->rq_verf.oa_length = mic.length;
1096 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1098 gss_release_buffer(&min_stat, &mic);
1104 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1106 struct svc_rpc_gss_callback *scb;
1107 rpc_gss_lock_t lock;
1113 * See if we have a callback for this guy.
1116 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1117 if (scb->cb_callback.program == rqst->rq_prog
1118 && scb->cb_callback.version == rqst->rq_vers) {
1120 * This one matches. Call the callback and see
1121 * if it wants to veto or something.
1123 lock.locked = FALSE;
1124 lock.raw_cred = &client->cl_rawcred;
1125 cb_res = scb->cb_callback.callback(rqst,
1132 client->cl_state = CLIENT_STALE;
1138 * The callback accepted the connection - it
1139 * is responsible for freeing client->cl_creds
1142 client->cl_creds = GSS_C_NO_CREDENTIAL;
1143 client->cl_locked = lock.locked;
1144 client->cl_cookie = cookie;
1150 * Either no callback exists for this program/version or one
1151 * of the callbacks rejected the connection. We just need to
1152 * clean up the delegated client creds, if any.
1154 if (client->cl_creds) {
1156 gss_release_cred(&min_ver, &client->cl_creds);
1162 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1168 sx_xlock(&client->cl_lock);
1169 if (seq <= client->cl_seqlast) {
1171 * The request sequence number is less than
1172 * the largest we have seen so far. If it is
1173 * outside the window or if we have seen a
1174 * request with this sequence before, silently
1177 offset = client->cl_seqlast - seq;
1178 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1184 if (client->cl_seqmask[word] & (1 << bit)) {
1192 sx_xunlock(&client->cl_lock);
1197 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1199 int offset, i, word, bit;
1200 uint32_t carry, newcarry;
1202 sx_xlock(&client->cl_lock);
1203 if (seq > client->cl_seqlast) {
1205 * This request has a sequence number greater
1206 * than any we have seen so far. Advance the
1207 * seq window and set bit zero of the window
1208 * (which corresponds to the new sequence
1211 offset = seq - client->cl_seqlast;
1212 while (offset > 32) {
1213 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1215 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1217 client->cl_seqmask[0] = 0;
1221 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1222 newcarry = client->cl_seqmask[i] >> (32 - offset);
1223 client->cl_seqmask[i] =
1224 (client->cl_seqmask[i] << offset) | carry;
1227 client->cl_seqmask[0] |= 1;
1228 client->cl_seqlast = seq;
1230 offset = client->cl_seqlast - seq;
1233 client->cl_seqmask[word] |= (1 << bit);
1235 sx_xunlock(&client->cl_lock);
1239 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1244 struct svc_rpc_gss_cookedcred *cc;
1245 struct svc_rpc_gss_client *client;
1246 struct rpc_gss_cred gc;
1247 struct rpc_gss_init_res gr;
1250 enum auth_stat result;
1252 rpc_gss_log_debug("in svc_rpc_gss()");
1254 /* Garbage collect old clients. */
1255 svc_rpc_gss_timeout_clients();
1257 /* Initialize reply. */
1258 rqst->rq_verf = _null_auth;
1260 /* Deserialize client credentials. */
1261 if (rqst->rq_cred.oa_length <= 0)
1262 return (AUTH_BADCRED);
1264 memset(&gc, 0, sizeof(gc));
1266 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1267 rqst->rq_cred.oa_length, XDR_DECODE);
1269 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1271 return (AUTH_BADCRED);
1277 /* Check version. */
1278 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1279 result = AUTH_BADCRED;
1283 /* Check the proc and find the client (or create it) */
1284 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1285 if (gc.gc_handle.length != 0) {
1286 result = AUTH_BADCRED;
1289 client = svc_rpc_gss_create_client();
1291 struct svc_rpc_gss_clientid *p;
1292 if (gc.gc_handle.length != sizeof(*p)) {
1293 result = AUTH_BADCRED;
1296 p = gc.gc_handle.value;
1297 client = svc_rpc_gss_find_client(p);
1300 * Can't find the client - we may have
1301 * destroyed it - tell the other side to
1304 result = RPCSEC_GSS_CREDPROBLEM;
1308 cc = rqst->rq_clntcred;
1309 cc->cc_client = client;
1310 cc->cc_service = gc.gc_svc;
1311 cc->cc_seq = gc.gc_seq;
1314 * The service and sequence number must be ignored for
1315 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1317 if (gc.gc_proc != RPCSEC_GSS_INIT
1318 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1320 * Check for sequence number overflow.
1322 if (gc.gc_seq >= MAXSEQ) {
1323 result = RPCSEC_GSS_CTXPROBLEM;
1328 * Check for valid service.
1330 if (gc.gc_svc != rpc_gss_svc_none &&
1331 gc.gc_svc != rpc_gss_svc_integrity &&
1332 gc.gc_svc != rpc_gss_svc_privacy) {
1333 result = AUTH_BADCRED;
1338 /* Handle RPCSEC_GSS control procedure. */
1339 switch (gc.gc_proc) {
1341 case RPCSEC_GSS_INIT:
1342 case RPCSEC_GSS_CONTINUE_INIT:
1343 if (rqst->rq_proc != NULLPROC) {
1344 result = AUTH_REJECTEDCRED;
1348 memset(&gr, 0, sizeof(gr));
1349 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1350 result = AUTH_REJECTEDCRED;
1354 if (gr.gr_major == GSS_S_COMPLETE) {
1356 * We borrow the space for the call verf to
1357 * pack our reply verf.
1359 rqst->rq_verf = msg->rm_call.cb_verf;
1360 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1361 result = AUTH_REJECTEDCRED;
1365 rqst->rq_verf = _null_auth;
1368 call_stat = svc_sendreply(rqst,
1369 (xdrproc_t) xdr_rpc_gss_init_res,
1372 gss_release_buffer(&min_stat, &gr.gr_token);
1375 result = AUTH_FAILED;
1379 if (gr.gr_major == GSS_S_COMPLETE)
1380 client->cl_state = CLIENT_ESTABLISHED;
1382 result = RPCSEC_GSS_NODISPATCH;
1385 case RPCSEC_GSS_DATA:
1386 case RPCSEC_GSS_DESTROY:
1387 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1388 result = RPCSEC_GSS_NODISPATCH;
1392 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1393 result = RPCSEC_GSS_CREDPROBLEM;
1398 * We borrow the space for the call verf to pack our
1401 rqst->rq_verf = msg->rm_call.cb_verf;
1402 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1403 result = RPCSEC_GSS_CTXPROBLEM;
1407 svc_rpc_gss_update_seq(client, gc.gc_seq);
1410 * Change the SVCAUTH ops on the request to point at
1411 * our own code so that we can unwrap the arguments
1412 * and wrap the result. The caller will re-set this on
1413 * every request to point to a set of null wrap/unwrap
1414 * methods. Acquire an extra reference to the client
1415 * which will be released by svc_rpc_gss_release()
1416 * after the request has finished processing.
1418 refcount_acquire(&client->cl_refs);
1419 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1420 rqst->rq_auth.svc_ah_private = cc;
1422 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1424 * We might be ready to do a callback to the server to
1425 * see if it wants to accept/reject the connection.
1427 sx_xlock(&client->cl_lock);
1428 if (!client->cl_done_callback) {
1429 client->cl_done_callback = TRUE;
1430 client->cl_qop = qop;
1431 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1432 client->cl_rawcred.mechanism, qop);
1433 if (!svc_rpc_gss_callback(client, rqst)) {
1434 result = AUTH_REJECTEDCRED;
1435 sx_xunlock(&client->cl_lock);
1439 sx_xunlock(&client->cl_lock);
1442 * If the server has locked this client to a
1443 * particular service+qop pair, enforce that
1446 if (client->cl_locked) {
1447 if (client->cl_rawcred.service != gc.gc_svc) {
1448 result = AUTH_FAILED;
1450 } else if (client->cl_qop != qop) {
1451 result = AUTH_BADVERF;
1457 * If the qop changed, look up the new qop
1460 if (client->cl_qop != qop) {
1461 client->cl_qop = qop;
1462 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1463 client->cl_rawcred.mechanism, qop);
1467 * Make sure we use the right service value
1470 if (client->cl_rawcred.service != gc.gc_svc) {
1471 client->cl_rawcred.service = gc.gc_svc;
1472 svc_rpc_gss_set_flavor(client);
1477 if (rqst->rq_proc != NULLPROC) {
1478 result = AUTH_REJECTEDCRED;
1482 call_stat = svc_sendreply(rqst,
1483 (xdrproc_t) xdr_void, (caddr_t) NULL);
1486 result = AUTH_FAILED;
1490 svc_rpc_gss_forget_client(client);
1492 result = RPCSEC_GSS_NODISPATCH;
1498 result = AUTH_BADCRED;
1503 svc_rpc_gss_release_client(client);
1505 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1510 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1512 struct svc_rpc_gss_cookedcred *cc;
1513 struct svc_rpc_gss_client *client;
1515 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1517 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1518 client = cc->cc_client;
1519 if (client->cl_state != CLIENT_ESTABLISHED
1520 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1524 return (xdr_rpc_gss_wrap_data(mp,
1525 client->cl_ctx, client->cl_qop,
1526 cc->cc_service, cc->cc_seq));
1530 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1532 struct svc_rpc_gss_cookedcred *cc;
1533 struct svc_rpc_gss_client *client;
1535 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1537 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1538 client = cc->cc_client;
1539 if (client->cl_state != CLIENT_ESTABLISHED
1540 || cc->cc_service == rpc_gss_svc_none) {
1544 return (xdr_rpc_gss_unwrap_data(mp,
1545 client->cl_ctx, client->cl_qop,
1546 cc->cc_service, cc->cc_seq));
1550 svc_rpc_gss_release(SVCAUTH *auth)
1552 struct svc_rpc_gss_cookedcred *cc;
1553 struct svc_rpc_gss_client *client;
1555 rpc_gss_log_debug("in svc_rpc_gss_release()");
1557 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1558 client = cc->cc_client;
1559 svc_rpc_gss_release_client(client);