2 * Copyright (c) 2008 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 Copyright (c) 2000 The Regents of the University of Michigan.
32 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
33 All rights reserved, all wrongs reversed.
35 Redistribution and use in source and binary forms, with or without
36 modification, are permitted provided that the following conditions
39 1. Redistributions of source code must retain the above copyright
40 notice, this list of conditions and the following disclaimer.
41 2. Redistributions in binary form must reproduce the above copyright
42 notice, this list of conditions and the following disclaimer in the
43 documentation and/or other materials provided with the distribution.
44 3. Neither the name of the University nor the names of its
45 contributors may be used to endorse or promote products derived
46 from this software without specific prior written permission.
48 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
49 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
50 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
51 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
55 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
63 #include <sys/cdefs.h>
64 __FBSDID("$FreeBSD$");
66 #include <sys/param.h>
67 #include <sys/systm.h>
69 #include <sys/kernel.h>
72 #include <sys/malloc.h>
74 #include <sys/mutex.h>
77 #include <sys/ucred.h>
80 #include <rpc/rpcsec_gss.h>
82 #include "rpcsec_gss_int.h"
84 static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
85 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
86 static void svc_rpc_gss_release(SVCAUTH *);
87 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
88 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
90 static struct svc_auth_ops svc_auth_gss_ops = {
96 struct sx svc_rpc_gss_lock;
98 struct svc_rpc_gss_callback {
99 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
100 rpc_gss_callback_t cb_callback;
102 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
103 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
105 struct svc_rpc_gss_svc_name {
106 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
110 gss_cred_id_t sn_cred;
114 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
115 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
117 enum svc_rpc_gss_client_state {
118 CLIENT_NEW, /* still authenticating */
119 CLIENT_ESTABLISHED, /* context established */
120 CLIENT_STALE /* garbage to collect */
123 #define SVC_RPC_GSS_SEQWINDOW 128
125 struct svc_rpc_gss_clientid {
126 unsigned long ci_hostid;
127 uint32_t ci_boottime;
131 struct svc_rpc_gss_client {
132 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
133 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
134 volatile u_int cl_refs;
136 struct svc_rpc_gss_clientid cl_id;
137 time_t cl_expiration; /* when to gc */
138 enum svc_rpc_gss_client_state cl_state; /* client state */
139 bool_t cl_locked; /* fixed service+qop */
140 gss_ctx_id_t cl_ctx; /* context id */
141 gss_cred_id_t cl_creds; /* delegated creds */
142 gss_name_t cl_cname; /* client name */
143 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
144 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
145 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
146 struct ucred *cl_cred; /* kernel-style credentials */
147 int cl_rpcflavor; /* RPC pseudo sec flavor */
148 bool_t cl_done_callback; /* TRUE after call */
149 void *cl_cookie; /* user cookie from callback */
150 gid_t cl_gid_storage[NGROUPS];
151 gss_OID cl_mech; /* mechanism */
152 gss_qop_t cl_qop; /* quality of protection */
153 uint32_t cl_seqlast; /* sequence window origin */
154 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
156 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
159 * This structure holds enough information to unwrap arguments or wrap
160 * results for a given request. We use the rq_clntcred area for this
161 * (which is a per-request buffer).
163 struct svc_rpc_gss_cookedcred {
164 struct svc_rpc_gss_client *cc_client;
165 rpc_gss_service_t cc_service;
169 #define CLIENT_HASH_SIZE 256
170 #define CLIENT_MAX 128
171 struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
172 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
173 static size_t svc_rpc_gss_client_count;
174 static uint32_t svc_rpc_gss_next_clientid = 1;
177 svc_rpc_gss_init(void *arg)
181 for (i = 0; i < CLIENT_HASH_SIZE; i++)
182 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
183 TAILQ_INIT(&svc_rpc_gss_clients);
184 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
185 sx_init(&svc_rpc_gss_lock, "gsslock");
187 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
190 rpc_gss_set_callback(rpc_gss_callback_t *cb)
192 struct svc_rpc_gss_callback *scb;
194 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
196 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
199 scb->cb_callback = *cb;
200 sx_xlock(&svc_rpc_gss_lock);
201 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
202 sx_xunlock(&svc_rpc_gss_lock);
208 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
210 struct svc_rpc_gss_callback *scb;
212 sx_xlock(&svc_rpc_gss_lock);
213 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
214 if (scb->cb_callback.program == cb->program
215 && scb->cb_callback.version == cb->version
216 && scb->cb_callback.callback == cb->callback) {
217 SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
218 svc_rpc_gss_callback, cb_link);
219 sx_xunlock(&svc_rpc_gss_lock);
220 mem_free(scb, sizeof(*scb));
224 sx_xunlock(&svc_rpc_gss_lock);
228 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
230 OM_uint32 maj_stat, min_stat;
231 gss_buffer_desc namebuf;
233 gss_OID_set_desc oid_set;
236 oid_set.elements = sname->sn_mech;
238 namebuf.value = (void *) sname->sn_principal;
239 namebuf.length = strlen(sname->sn_principal);
241 maj_stat = gss_import_name(&min_stat, &namebuf,
242 GSS_C_NT_HOSTBASED_SERVICE, &name);
243 if (maj_stat != GSS_S_COMPLETE)
246 if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
247 gss_release_cred(&min_stat, &sname->sn_cred);
249 maj_stat = gss_acquire_cred(&min_stat, name,
250 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
252 if (maj_stat != GSS_S_COMPLETE) {
253 gss_release_name(&min_stat, &name);
256 gss_release_name(&min_stat, &name);
262 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
263 u_int req_time, u_int program, u_int version)
265 struct svc_rpc_gss_svc_name *sname;
268 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
271 sname = mem_alloc(sizeof(*sname));
274 sname->sn_principal = strdup(principal, M_RPC);
275 sname->sn_mech = mech_oid;
276 sname->sn_req_time = req_time;
277 sname->sn_cred = GSS_C_NO_CREDENTIAL;
278 sname->sn_program = program;
279 sname->sn_version = version;
281 if (!rpc_gss_acquire_svc_cred(sname)) {
282 free(sname->sn_principal, M_RPC);
283 mem_free(sname, sizeof(*sname));
287 sx_xlock(&svc_rpc_gss_lock);
288 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
289 sx_xunlock(&svc_rpc_gss_lock);
295 rpc_gss_clear_svc_name(u_int program, u_int version)
298 struct svc_rpc_gss_svc_name *sname;
300 sx_xlock(&svc_rpc_gss_lock);
301 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
302 if (sname->sn_program == program
303 && sname->sn_version == version) {
304 SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
305 svc_rpc_gss_svc_name, sn_link);
306 sx_xunlock(&svc_rpc_gss_lock);
307 gss_release_cred(&min_stat, &sname->sn_cred);
308 free(sname->sn_principal, M_RPC);
309 mem_free(sname, sizeof(*sname));
313 sx_xunlock(&svc_rpc_gss_lock);
317 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
318 const char *mech, const char *name, const char *node, const char *domain)
320 OM_uint32 maj_stat, min_stat;
324 gss_name_t gss_name, gss_mech_name;
325 rpc_gss_principal_t result;
327 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
331 * Construct a gss_buffer containing the full name formatted
332 * as "name/node@domain" where node and domain are optional.
334 namelen = strlen(name) + 1;
336 namelen += strlen(node) + 1;
339 namelen += strlen(domain) + 1;
342 buf.value = mem_alloc(namelen);
343 buf.length = namelen;
344 strcpy((char *) buf.value, name);
346 strcat((char *) buf.value, "/");
347 strcat((char *) buf.value, node);
350 strcat((char *) buf.value, "@");
351 strcat((char *) buf.value, domain);
355 * Convert that to a gss_name_t and then convert that to a
356 * mechanism name in the selected mechanism.
358 maj_stat = gss_import_name(&min_stat, &buf,
359 GSS_C_NT_USER_NAME, &gss_name);
360 mem_free(buf.value, buf.length);
361 if (maj_stat != GSS_S_COMPLETE) {
362 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
365 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
367 if (maj_stat != GSS_S_COMPLETE) {
368 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
370 gss_release_name(&min_stat, &gss_name);
373 gss_release_name(&min_stat, &gss_name);
376 * Export the mechanism name and use that to construct the
377 * rpc_gss_principal_t result.
379 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
380 if (maj_stat != GSS_S_COMPLETE) {
381 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
382 gss_release_name(&min_stat, &gss_mech_name);
385 gss_release_name(&min_stat, &gss_mech_name);
387 result = mem_alloc(sizeof(int) + buf.length);
389 gss_release_buffer(&min_stat, &buf);
392 result->len = buf.length;
393 memcpy(result->name, buf.value, buf.length);
394 gss_release_buffer(&min_stat, &buf);
401 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
402 rpc_gss_ucred_t **ucred, void **cookie)
404 struct svc_rpc_gss_cookedcred *cc;
405 struct svc_rpc_gss_client *client;
407 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
410 cc = req->rq_clntcred;
411 client = cc->cc_client;
413 *rcred = &client->cl_rawcred;
415 *ucred = &client->cl_ucred;
417 *cookie = client->cl_cookie;
422 * This simpler interface is used by svc_getcred to copy the cred data
423 * into a kernel cred structure.
426 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
429 struct svc_rpc_gss_cookedcred *cc;
430 struct svc_rpc_gss_client *client;
433 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
436 cc = req->rq_clntcred;
437 client = cc->cc_client;
440 *flavorp = client->cl_rpcflavor;
442 if (client->cl_cred) {
443 *crp = crhold(client->cl_cred);
447 uc = &client->cl_ucred;
448 cr = client->cl_cred = crget();
449 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
450 cr->cr_rgid = cr->cr_svgid = uc->gid;
451 crsetgroups(cr, uc->gidlen, uc->gidlist);
452 cr->cr_prison = &prison0;
453 prison_hold(cr->cr_prison);
460 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
462 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
463 struct svc_rpc_gss_client *client = cc->cc_client;
466 OM_uint32 maj_stat, min_stat;
469 switch (client->cl_rawcred.service) {
470 case rpc_gss_svc_none:
471 return (max_tp_unit_len);
474 case rpc_gss_svc_default:
475 case rpc_gss_svc_integrity:
479 case rpc_gss_svc_privacy:
487 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
488 client->cl_qop, max_tp_unit_len, &max);
490 if (maj_stat == GSS_S_COMPLETE) {
496 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
502 static struct svc_rpc_gss_client *
503 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
505 struct svc_rpc_gss_client *client;
506 struct svc_rpc_gss_client_list *list;
507 unsigned long hostid;
509 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
511 getcredhostid(curthread->td_ucred, &hostid);
512 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
515 list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
516 sx_xlock(&svc_rpc_gss_lock);
517 TAILQ_FOREACH(client, list, cl_link) {
518 if (client->cl_id.ci_id == id->ci_id) {
520 * Move this client to the front of the LRU
523 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
524 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
526 refcount_acquire(&client->cl_refs);
530 sx_xunlock(&svc_rpc_gss_lock);
535 static struct svc_rpc_gss_client *
536 svc_rpc_gss_create_client(void)
538 struct svc_rpc_gss_client *client;
539 struct svc_rpc_gss_client_list *list;
540 unsigned long hostid;
542 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
544 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
545 memset(client, 0, sizeof(struct svc_rpc_gss_client));
548 * Set the initial value of cl_refs to two. One for the caller
549 * and the other to hold onto the client structure until it expires.
551 refcount_init(&client->cl_refs, 2);
552 sx_init(&client->cl_lock, "GSS-client");
553 getcredhostid(curthread->td_ucred, &hostid);
554 client->cl_id.ci_hostid = hostid;
555 client->cl_id.ci_boottime = boottime.tv_sec;
556 client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
559 * Start the client off with a short expiration time. We will
560 * try to get a saner value from the client creds later.
562 client->cl_state = CLIENT_NEW;
563 client->cl_locked = FALSE;
564 client->cl_expiration = time_uptime + 5*60;
566 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
567 sx_xlock(&svc_rpc_gss_lock);
568 TAILQ_INSERT_HEAD(list, client, cl_link);
569 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
570 svc_rpc_gss_client_count++;
571 sx_xunlock(&svc_rpc_gss_lock);
576 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
580 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
583 gss_delete_sec_context(&min_stat,
584 &client->cl_ctx, GSS_C_NO_BUFFER);
586 if (client->cl_cname)
587 gss_release_name(&min_stat, &client->cl_cname);
589 if (client->cl_rawcred.client_principal)
590 mem_free(client->cl_rawcred.client_principal,
591 sizeof(*client->cl_rawcred.client_principal)
592 + client->cl_rawcred.client_principal->len);
595 crfree(client->cl_cred);
597 sx_destroy(&client->cl_lock);
598 mem_free(client, sizeof(*client));
602 * Drop a reference to a client and free it if that was the last reference.
605 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
608 if (!refcount_release(&client->cl_refs))
610 svc_rpc_gss_destroy_client(client);
614 * Remove a client from our global lists.
615 * Must be called with svc_rpc_gss_lock held.
618 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
620 struct svc_rpc_gss_client_list *list;
622 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
623 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
624 TAILQ_REMOVE(list, client, cl_link);
625 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
626 svc_rpc_gss_client_count--;
630 * Remove a client from our global lists and free it if we can.
633 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
635 struct svc_rpc_gss_client_list *list;
636 struct svc_rpc_gss_client *tclient;
638 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
639 sx_xlock(&svc_rpc_gss_lock);
640 TAILQ_FOREACH(tclient, list, cl_link) {
642 * Make sure this client has not already been removed
643 * from the lists by svc_rpc_gss_forget_client() or
644 * svc_rpc_gss_forget_client_locked().
646 if (client == tclient) {
647 svc_rpc_gss_forget_client_locked(client);
648 sx_xunlock(&svc_rpc_gss_lock);
649 svc_rpc_gss_release_client(client);
653 sx_xunlock(&svc_rpc_gss_lock);
657 svc_rpc_gss_timeout_clients(void)
659 struct svc_rpc_gss_client *client;
660 time_t now = time_uptime;
662 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
665 * First enforce the max client limit. We keep
666 * svc_rpc_gss_clients in LRU order.
668 sx_xlock(&svc_rpc_gss_lock);
669 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
670 while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) {
671 svc_rpc_gss_forget_client_locked(client);
672 sx_xunlock(&svc_rpc_gss_lock);
673 svc_rpc_gss_release_client(client);
674 sx_xlock(&svc_rpc_gss_lock);
675 client = TAILQ_LAST(&svc_rpc_gss_clients,
676 svc_rpc_gss_client_list);
679 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
680 if (client->cl_state == CLIENT_STALE
681 || now > client->cl_expiration) {
682 svc_rpc_gss_forget_client_locked(client);
683 sx_xunlock(&svc_rpc_gss_lock);
684 rpc_gss_log_debug("expiring client %p", client);
685 svc_rpc_gss_release_client(client);
686 sx_xlock(&svc_rpc_gss_lock);
690 sx_xunlock(&svc_rpc_gss_lock);
695 * OID<->string routines. These are uuuuugly.
698 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
701 unsigned long number;
703 size_t string_length;
708 /* Decoded according to krb5/gssapi_krb5.c */
710 /* First determine the size of the string */
714 cp = (unsigned char *) oid->elements;
715 number = (unsigned long) cp[0];
716 sprintf(numstr, "%ld ", number/40);
717 string_length += strlen(numstr);
718 sprintf(numstr, "%ld ", number%40);
719 string_length += strlen(numstr);
720 for (i=1; i<oid->length; i++) {
721 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
722 number = (number << 7) | (cp[i] & 0x7f);
727 return(GSS_S_FAILURE);
729 if ((cp[i] & 0x80) == 0) {
730 sprintf(numstr, "%ld ", number);
731 string_length += strlen(numstr);
737 * If we get here, we've calculated the length of "n n n ... n ". Add 4
738 * here for "{ " and "}\0".
741 if ((bp = (char *) mem_alloc(string_length))) {
743 number = (unsigned long) cp[0];
744 sprintf(numstr, "%ld ", number/40);
746 sprintf(numstr, "%ld ", number%40);
749 cp = (unsigned char *) oid->elements;
750 for (i=1; i<oid->length; i++) {
751 number = (number << 7) | (cp[i] & 0x7f);
752 if ((cp[i] & 0x80) == 0) {
753 sprintf(numstr, "%ld ", number);
759 oid_str->length = strlen(bp)+1;
760 oid_str->value = (void *) bp;
762 return(GSS_S_COMPLETE);
765 return(GSS_S_FAILURE);
770 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
771 const gss_name_t name)
773 OM_uint32 maj_stat, min_stat;
774 rpc_gss_ucred_t *uc = &client->cl_ucred;
779 uc->gidlist = client->cl_gid_storage;
782 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
783 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
784 if (GSS_ERROR(maj_stat))
787 uc->gidlen = numgroups;
791 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
793 static gss_OID_desc krb5_mech_oid =
794 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
797 * Attempt to translate mech type and service into a
798 * 'pseudo flavor'. Hardwire in krb5 support for now.
800 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
801 switch (client->cl_rawcred.service) {
802 case rpc_gss_svc_default:
803 case rpc_gss_svc_none:
804 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
806 case rpc_gss_svc_integrity:
807 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
809 case rpc_gss_svc_privacy:
810 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
814 client->cl_rpcflavor = RPCSEC_GSS;
819 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
820 struct svc_req *rqst,
821 struct rpc_gss_init_res *gr,
822 struct rpc_gss_cred *gc)
824 gss_buffer_desc recv_tok;
826 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
827 OM_uint32 cred_lifetime;
828 struct svc_rpc_gss_svc_name *sname;
830 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
832 /* Deserialize arguments. */
833 memset(&recv_tok, 0, sizeof(recv_tok));
835 if (!svc_getargs(rqst,
836 (xdrproc_t) xdr_gss_buffer_desc,
837 (caddr_t) &recv_tok)) {
838 client->cl_state = CLIENT_STALE;
843 * First time round, try all the server names we have until
844 * one matches. Afterwards, stick with that one.
846 sx_xlock(&svc_rpc_gss_lock);
847 if (!client->cl_sname) {
848 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
849 if (sname->sn_program == rqst->rq_prog
850 && sname->sn_version == rqst->rq_vers) {
852 gr->gr_major = gss_accept_sec_context(
857 GSS_C_NO_CHANNEL_BINDINGS,
865 GSS_S_CREDENTIALS_EXPIRED) {
867 * Either our creds really did
871 if (rpc_gss_acquire_svc_cred(sname))
874 client->cl_sname = sname;
879 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
881 sx_xunlock(&svc_rpc_gss_lock);
885 gr->gr_major = gss_accept_sec_context(
888 client->cl_sname->sn_cred,
890 GSS_C_NO_CHANNEL_BINDINGS,
898 sx_xunlock(&svc_rpc_gss_lock);
900 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
903 * If we get an error from gss_accept_sec_context, send the
904 * reply anyway so that the client gets a chance to see what
907 if (gr->gr_major != GSS_S_COMPLETE &&
908 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
909 rpc_gss_log_status("accept_sec_context", client->cl_mech,
910 gr->gr_major, gr->gr_minor);
911 client->cl_state = CLIENT_STALE;
915 gr->gr_handle.value = &client->cl_id;
916 gr->gr_handle.length = sizeof(client->cl_id);
917 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
919 /* Save client info. */
920 client->cl_mech = mech;
921 client->cl_qop = GSS_C_QOP_DEFAULT;
922 client->cl_done_callback = FALSE;
924 if (gr->gr_major == GSS_S_COMPLETE) {
925 gss_buffer_desc export_name;
928 * Change client expiration time to be near when the
929 * client creds expire (or 24 hours if we can't figure
932 if (cred_lifetime == GSS_C_INDEFINITE)
933 cred_lifetime = time_uptime + 24*60*60;
935 client->cl_expiration = time_uptime + cred_lifetime;
938 * Fill in cred details in the rawcred structure.
940 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
941 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
942 maj_stat = gss_export_name(&min_stat, client->cl_cname,
944 if (maj_stat != GSS_S_COMPLETE) {
945 rpc_gss_log_status("gss_export_name", client->cl_mech,
949 client->cl_rawcred.client_principal =
950 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
951 + export_name.length);
952 client->cl_rawcred.client_principal->len = export_name.length;
953 memcpy(client->cl_rawcred.client_principal->name,
954 export_name.value, export_name.length);
955 gss_release_buffer(&min_stat, &export_name);
956 client->cl_rawcred.svc_principal =
957 client->cl_sname->sn_principal;
958 client->cl_rawcred.service = gc->gc_svc;
961 * Use gss_pname_to_uid to map to unix creds. For
962 * kerberos5, this uses krb5_aname_to_localname.
964 svc_rpc_gss_build_ucred(client, client->cl_cname);
965 svc_rpc_gss_set_flavor(client);
966 gss_release_name(&min_stat, &client->cl_cname);
970 gss_buffer_desc mechname;
972 gss_oid_to_str(&min_stat, mech, &mechname);
974 rpc_gss_log_debug("accepted context for %s with "
975 "<mech %.*s, qop %d, svc %d>",
976 client->cl_rawcred.client_principal->name,
977 mechname.length, (char *)mechname.value,
978 client->cl_qop, client->cl_rawcred.service);
980 gss_release_buffer(&min_stat, &mechname);
988 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
989 gss_qop_t *qop, rpc_gss_proc_t gcproc)
991 struct opaque_auth *oa;
992 gss_buffer_desc rpcbuf, checksum;
993 OM_uint32 maj_stat, min_stat;
995 int32_t rpchdr[128 / sizeof(int32_t)];
998 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1000 memset(rpchdr, 0, sizeof(rpchdr));
1002 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1004 IXDR_PUT_LONG(buf, msg->rm_xid);
1005 IXDR_PUT_ENUM(buf, msg->rm_direction);
1006 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1007 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1008 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1009 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1010 oa = &msg->rm_call.cb_cred;
1011 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1012 IXDR_PUT_LONG(buf, oa->oa_length);
1013 if (oa->oa_length) {
1014 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1015 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1017 rpcbuf.value = rpchdr;
1018 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1020 checksum.value = msg->rm_call.cb_verf.oa_base;
1021 checksum.length = msg->rm_call.cb_verf.oa_length;
1023 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1026 if (maj_stat != GSS_S_COMPLETE) {
1027 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1028 maj_stat, min_stat);
1030 * A bug in some versions of the Linux client generates a
1031 * Destroy operation with a bogus encrypted checksum. Deleting
1032 * the credential handle for that case causes the mount to fail.
1033 * Since the checksum is bogus (gss_verify_mic() failed), it
1034 * doesn't make sense to destroy the handle and not doing so
1035 * fixes the Linux mount.
1037 if (gcproc != RPCSEC_GSS_DESTROY)
1038 client->cl_state = CLIENT_STALE;
1047 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1048 struct svc_req *rqst, u_int seq)
1050 gss_buffer_desc signbuf;
1051 gss_buffer_desc mic;
1052 OM_uint32 maj_stat, min_stat;
1055 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1058 signbuf.value = &nseq;
1059 signbuf.length = sizeof(nseq);
1061 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1064 if (maj_stat != GSS_S_COMPLETE) {
1065 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1066 client->cl_state = CLIENT_STALE;
1070 KASSERT(mic.length <= MAX_AUTH_BYTES,
1071 ("MIC too large for RPCSEC_GSS"));
1073 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1074 rqst->rq_verf.oa_length = mic.length;
1075 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1077 gss_release_buffer(&min_stat, &mic);
1083 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1085 struct svc_rpc_gss_callback *scb;
1086 rpc_gss_lock_t lock;
1092 * See if we have a callback for this guy.
1095 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1096 if (scb->cb_callback.program == rqst->rq_prog
1097 && scb->cb_callback.version == rqst->rq_vers) {
1099 * This one matches. Call the callback and see
1100 * if it wants to veto or something.
1102 lock.locked = FALSE;
1103 lock.raw_cred = &client->cl_rawcred;
1104 cb_res = scb->cb_callback.callback(rqst,
1111 client->cl_state = CLIENT_STALE;
1117 * The callback accepted the connection - it
1118 * is responsible for freeing client->cl_creds
1121 client->cl_creds = GSS_C_NO_CREDENTIAL;
1122 client->cl_locked = lock.locked;
1123 client->cl_cookie = cookie;
1129 * Either no callback exists for this program/version or one
1130 * of the callbacks rejected the connection. We just need to
1131 * clean up the delegated client creds, if any.
1133 if (client->cl_creds) {
1135 gss_release_cred(&min_ver, &client->cl_creds);
1141 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1147 sx_xlock(&client->cl_lock);
1148 if (seq <= client->cl_seqlast) {
1150 * The request sequence number is less than
1151 * the largest we have seen so far. If it is
1152 * outside the window or if we have seen a
1153 * request with this sequence before, silently
1156 offset = client->cl_seqlast - seq;
1157 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1163 if (client->cl_seqmask[word] & (1 << bit)) {
1171 sx_xunlock(&client->cl_lock);
1176 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1178 int offset, i, word, bit;
1179 uint32_t carry, newcarry;
1181 sx_xlock(&client->cl_lock);
1182 if (seq > client->cl_seqlast) {
1184 * This request has a sequence number greater
1185 * than any we have seen so far. Advance the
1186 * seq window and set bit zero of the window
1187 * (which corresponds to the new sequence
1190 offset = seq - client->cl_seqlast;
1191 while (offset > 32) {
1192 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1194 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1196 client->cl_seqmask[0] = 0;
1200 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1201 newcarry = client->cl_seqmask[i] >> (32 - offset);
1202 client->cl_seqmask[i] =
1203 (client->cl_seqmask[i] << offset) | carry;
1206 client->cl_seqmask[0] |= 1;
1207 client->cl_seqlast = seq;
1209 offset = client->cl_seqlast - seq;
1212 client->cl_seqmask[word] |= (1 << bit);
1214 sx_xunlock(&client->cl_lock);
1218 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1223 struct svc_rpc_gss_cookedcred *cc;
1224 struct svc_rpc_gss_client *client;
1225 struct rpc_gss_cred gc;
1226 struct rpc_gss_init_res gr;
1229 enum auth_stat result;
1231 rpc_gss_log_debug("in svc_rpc_gss()");
1233 /* Garbage collect old clients. */
1234 svc_rpc_gss_timeout_clients();
1236 /* Initialize reply. */
1237 rqst->rq_verf = _null_auth;
1239 /* Deserialize client credentials. */
1240 if (rqst->rq_cred.oa_length <= 0)
1241 return (AUTH_BADCRED);
1243 memset(&gc, 0, sizeof(gc));
1245 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1246 rqst->rq_cred.oa_length, XDR_DECODE);
1248 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1250 return (AUTH_BADCRED);
1256 /* Check version. */
1257 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1258 result = AUTH_BADCRED;
1262 /* Check the proc and find the client (or create it) */
1263 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1264 if (gc.gc_handle.length != 0) {
1265 result = AUTH_BADCRED;
1268 client = svc_rpc_gss_create_client();
1270 struct svc_rpc_gss_clientid *p;
1271 if (gc.gc_handle.length != sizeof(*p)) {
1272 result = AUTH_BADCRED;
1275 p = gc.gc_handle.value;
1276 client = svc_rpc_gss_find_client(p);
1279 * Can't find the client - we may have
1280 * destroyed it - tell the other side to
1283 result = RPCSEC_GSS_CREDPROBLEM;
1287 cc = rqst->rq_clntcred;
1288 cc->cc_client = client;
1289 cc->cc_service = gc.gc_svc;
1290 cc->cc_seq = gc.gc_seq;
1293 * The service and sequence number must be ignored for
1294 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1296 if (gc.gc_proc != RPCSEC_GSS_INIT
1297 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1299 * Check for sequence number overflow.
1301 if (gc.gc_seq >= MAXSEQ) {
1302 result = RPCSEC_GSS_CTXPROBLEM;
1307 * Check for valid service.
1309 if (gc.gc_svc != rpc_gss_svc_none &&
1310 gc.gc_svc != rpc_gss_svc_integrity &&
1311 gc.gc_svc != rpc_gss_svc_privacy) {
1312 result = AUTH_BADCRED;
1317 /* Handle RPCSEC_GSS control procedure. */
1318 switch (gc.gc_proc) {
1320 case RPCSEC_GSS_INIT:
1321 case RPCSEC_GSS_CONTINUE_INIT:
1322 if (rqst->rq_proc != NULLPROC) {
1323 result = AUTH_REJECTEDCRED;
1327 memset(&gr, 0, sizeof(gr));
1328 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1329 result = AUTH_REJECTEDCRED;
1333 if (gr.gr_major == GSS_S_COMPLETE) {
1335 * We borrow the space for the call verf to
1336 * pack our reply verf.
1338 rqst->rq_verf = msg->rm_call.cb_verf;
1339 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1340 result = AUTH_REJECTEDCRED;
1344 rqst->rq_verf = _null_auth;
1347 call_stat = svc_sendreply(rqst,
1348 (xdrproc_t) xdr_rpc_gss_init_res,
1351 gss_release_buffer(&min_stat, &gr.gr_token);
1354 result = AUTH_FAILED;
1358 if (gr.gr_major == GSS_S_COMPLETE)
1359 client->cl_state = CLIENT_ESTABLISHED;
1361 result = RPCSEC_GSS_NODISPATCH;
1364 case RPCSEC_GSS_DATA:
1365 case RPCSEC_GSS_DESTROY:
1366 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1367 result = RPCSEC_GSS_NODISPATCH;
1371 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1372 result = RPCSEC_GSS_CREDPROBLEM;
1377 * We borrow the space for the call verf to pack our
1380 rqst->rq_verf = msg->rm_call.cb_verf;
1381 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1382 result = RPCSEC_GSS_CTXPROBLEM;
1386 svc_rpc_gss_update_seq(client, gc.gc_seq);
1389 * Change the SVCAUTH ops on the request to point at
1390 * our own code so that we can unwrap the arguments
1391 * and wrap the result. The caller will re-set this on
1392 * every request to point to a set of null wrap/unwrap
1393 * methods. Acquire an extra reference to the client
1394 * which will be released by svc_rpc_gss_release()
1395 * after the request has finished processing.
1397 refcount_acquire(&client->cl_refs);
1398 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1399 rqst->rq_auth.svc_ah_private = cc;
1401 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1403 * We might be ready to do a callback to the server to
1404 * see if it wants to accept/reject the connection.
1406 sx_xlock(&client->cl_lock);
1407 if (!client->cl_done_callback) {
1408 client->cl_done_callback = TRUE;
1409 client->cl_qop = qop;
1410 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1411 client->cl_rawcred.mechanism, qop);
1412 if (!svc_rpc_gss_callback(client, rqst)) {
1413 result = AUTH_REJECTEDCRED;
1414 sx_xunlock(&client->cl_lock);
1418 sx_xunlock(&client->cl_lock);
1421 * If the server has locked this client to a
1422 * particular service+qop pair, enforce that
1425 if (client->cl_locked) {
1426 if (client->cl_rawcred.service != gc.gc_svc) {
1427 result = AUTH_FAILED;
1429 } else if (client->cl_qop != qop) {
1430 result = AUTH_BADVERF;
1436 * If the qop changed, look up the new qop
1439 if (client->cl_qop != qop) {
1440 client->cl_qop = qop;
1441 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1442 client->cl_rawcred.mechanism, qop);
1446 * Make sure we use the right service value
1449 if (client->cl_rawcred.service != gc.gc_svc) {
1450 client->cl_rawcred.service = gc.gc_svc;
1451 svc_rpc_gss_set_flavor(client);
1456 if (rqst->rq_proc != NULLPROC) {
1457 result = AUTH_REJECTEDCRED;
1461 call_stat = svc_sendreply(rqst,
1462 (xdrproc_t) xdr_void, (caddr_t) NULL);
1465 result = AUTH_FAILED;
1469 svc_rpc_gss_forget_client(client);
1471 result = RPCSEC_GSS_NODISPATCH;
1477 result = AUTH_BADCRED;
1482 svc_rpc_gss_release_client(client);
1484 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1489 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1491 struct svc_rpc_gss_cookedcred *cc;
1492 struct svc_rpc_gss_client *client;
1494 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1496 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1497 client = cc->cc_client;
1498 if (client->cl_state != CLIENT_ESTABLISHED
1499 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1503 return (xdr_rpc_gss_wrap_data(mp,
1504 client->cl_ctx, client->cl_qop,
1505 cc->cc_service, cc->cc_seq));
1509 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1511 struct svc_rpc_gss_cookedcred *cc;
1512 struct svc_rpc_gss_client *client;
1514 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1516 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1517 client = cc->cc_client;
1518 if (client->cl_state != CLIENT_ESTABLISHED
1519 || cc->cc_service == rpc_gss_svc_none) {
1523 return (xdr_rpc_gss_unwrap_data(mp,
1524 client->cl_ctx, client->cl_qop,
1525 cc->cc_service, cc->cc_seq));
1529 svc_rpc_gss_release(SVCAUTH *auth)
1531 struct svc_rpc_gss_cookedcred *cc;
1532 struct svc_rpc_gss_client *client;
1534 rpc_gss_log_debug("in svc_rpc_gss_release()");
1536 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1537 client = cc->cc_client;
1538 svc_rpc_gss_release_client(client);