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);
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);
458 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
460 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
461 struct svc_rpc_gss_client *client = cc->cc_client;
464 OM_uint32 maj_stat, min_stat;
467 switch (client->cl_rawcred.service) {
468 case rpc_gss_svc_none:
469 return (max_tp_unit_len);
472 case rpc_gss_svc_default:
473 case rpc_gss_svc_integrity:
477 case rpc_gss_svc_privacy:
485 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
486 client->cl_qop, max_tp_unit_len, &max);
488 if (maj_stat == GSS_S_COMPLETE) {
494 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
500 static struct svc_rpc_gss_client *
501 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
503 struct svc_rpc_gss_client *client;
504 struct svc_rpc_gss_client_list *list;
505 unsigned long hostid;
507 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
509 getcredhostid(curthread->td_ucred, &hostid);
510 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
513 list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
514 sx_xlock(&svc_rpc_gss_lock);
515 TAILQ_FOREACH(client, list, cl_link) {
516 if (client->cl_id.ci_id == id->ci_id) {
518 * Move this client to the front of the LRU
521 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
522 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
524 refcount_acquire(&client->cl_refs);
528 sx_xunlock(&svc_rpc_gss_lock);
533 static struct svc_rpc_gss_client *
534 svc_rpc_gss_create_client(void)
536 struct svc_rpc_gss_client *client;
537 struct svc_rpc_gss_client_list *list;
538 unsigned long hostid;
540 rpc_gss_log_debug("in svc_rpc_gss_create_client()");
542 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
543 memset(client, 0, sizeof(struct svc_rpc_gss_client));
544 refcount_init(&client->cl_refs, 1);
545 sx_init(&client->cl_lock, "GSS-client");
546 getcredhostid(curthread->td_ucred, &hostid);
547 client->cl_id.ci_hostid = hostid;
548 client->cl_id.ci_boottime = boottime.tv_sec;
549 client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
550 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
551 sx_xlock(&svc_rpc_gss_lock);
552 TAILQ_INSERT_HEAD(list, client, cl_link);
553 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
554 svc_rpc_gss_client_count++;
555 sx_xunlock(&svc_rpc_gss_lock);
558 * Start the client off with a short expiration time. We will
559 * try to get a saner value from the client creds later.
561 client->cl_state = CLIENT_NEW;
562 client->cl_locked = FALSE;
563 client->cl_expiration = time_uptime + 5*60;
569 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
573 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
576 gss_delete_sec_context(&min_stat,
577 &client->cl_ctx, GSS_C_NO_BUFFER);
579 if (client->cl_cname)
580 gss_release_name(&min_stat, &client->cl_cname);
582 if (client->cl_rawcred.client_principal)
583 mem_free(client->cl_rawcred.client_principal,
584 sizeof(*client->cl_rawcred.client_principal)
585 + client->cl_rawcred.client_principal->len);
588 crfree(client->cl_cred);
590 sx_destroy(&client->cl_lock);
591 mem_free(client, sizeof(*client));
595 * Drop a reference to a client and free it if that was the last reference.
598 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
601 if (!refcount_release(&client->cl_refs))
603 svc_rpc_gss_destroy_client(client);
607 * Remove a client from our global lists and free it if we can.
610 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
612 struct svc_rpc_gss_client_list *list;
614 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
615 sx_xlock(&svc_rpc_gss_lock);
616 TAILQ_REMOVE(list, client, cl_link);
617 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
618 svc_rpc_gss_client_count--;
619 sx_xunlock(&svc_rpc_gss_lock);
620 svc_rpc_gss_release_client(client);
624 svc_rpc_gss_timeout_clients(void)
626 struct svc_rpc_gss_client *client;
627 struct svc_rpc_gss_client *nclient;
628 time_t now = time_uptime;
630 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
633 * First enforce the max client limit. We keep
634 * svc_rpc_gss_clients in LRU order.
636 while (svc_rpc_gss_client_count > CLIENT_MAX)
637 svc_rpc_gss_forget_client(TAILQ_LAST(&svc_rpc_gss_clients,
638 svc_rpc_gss_client_list));
639 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
640 if (client->cl_state == CLIENT_STALE
641 || now > client->cl_expiration) {
642 rpc_gss_log_debug("expiring client %p", client);
643 svc_rpc_gss_forget_client(client);
650 * OID<->string routines. These are uuuuugly.
653 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
656 unsigned long number;
658 size_t string_length;
663 /* Decoded according to krb5/gssapi_krb5.c */
665 /* First determine the size of the string */
669 cp = (unsigned char *) oid->elements;
670 number = (unsigned long) cp[0];
671 sprintf(numstr, "%ld ", number/40);
672 string_length += strlen(numstr);
673 sprintf(numstr, "%ld ", number%40);
674 string_length += strlen(numstr);
675 for (i=1; i<oid->length; i++) {
676 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
677 number = (number << 7) | (cp[i] & 0x7f);
682 return(GSS_S_FAILURE);
684 if ((cp[i] & 0x80) == 0) {
685 sprintf(numstr, "%ld ", number);
686 string_length += strlen(numstr);
692 * If we get here, we've calculated the length of "n n n ... n ". Add 4
693 * here for "{ " and "}\0".
696 if ((bp = (char *) mem_alloc(string_length))) {
698 number = (unsigned long) cp[0];
699 sprintf(numstr, "%ld ", number/40);
701 sprintf(numstr, "%ld ", number%40);
704 cp = (unsigned char *) oid->elements;
705 for (i=1; i<oid->length; i++) {
706 number = (number << 7) | (cp[i] & 0x7f);
707 if ((cp[i] & 0x80) == 0) {
708 sprintf(numstr, "%ld ", number);
714 oid_str->length = strlen(bp)+1;
715 oid_str->value = (void *) bp;
717 return(GSS_S_COMPLETE);
720 return(GSS_S_FAILURE);
725 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
726 const gss_name_t name)
728 OM_uint32 maj_stat, min_stat;
729 rpc_gss_ucred_t *uc = &client->cl_ucred;
734 uc->gidlist = client->cl_gid_storage;
737 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
738 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
739 if (GSS_ERROR(maj_stat))
742 uc->gidlen = numgroups;
746 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
748 static gss_OID_desc krb5_mech_oid =
749 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
752 * Attempt to translate mech type and service into a
753 * 'pseudo flavor'. Hardwire in krb5 support for now.
755 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
756 switch (client->cl_rawcred.service) {
757 case rpc_gss_svc_default:
758 case rpc_gss_svc_none:
759 client->cl_rpcflavor = RPCSEC_GSS_KRB5;
761 case rpc_gss_svc_integrity:
762 client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
764 case rpc_gss_svc_privacy:
765 client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
769 client->cl_rpcflavor = RPCSEC_GSS;
774 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
775 struct svc_req *rqst,
776 struct rpc_gss_init_res *gr,
777 struct rpc_gss_cred *gc)
779 gss_buffer_desc recv_tok;
781 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
782 OM_uint32 cred_lifetime;
783 struct svc_rpc_gss_svc_name *sname;
785 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
787 /* Deserialize arguments. */
788 memset(&recv_tok, 0, sizeof(recv_tok));
790 if (!svc_getargs(rqst,
791 (xdrproc_t) xdr_gss_buffer_desc,
792 (caddr_t) &recv_tok)) {
793 client->cl_state = CLIENT_STALE;
798 * First time round, try all the server names we have until
799 * one matches. Afterwards, stick with that one.
801 sx_xlock(&svc_rpc_gss_lock);
802 if (!client->cl_sname) {
803 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
804 if (sname->sn_program == rqst->rq_prog
805 && sname->sn_version == rqst->rq_vers) {
807 gr->gr_major = gss_accept_sec_context(
812 GSS_C_NO_CHANNEL_BINDINGS,
820 GSS_S_CREDENTIALS_EXPIRED) {
822 * Either our creds really did
826 if (rpc_gss_acquire_svc_cred(sname))
829 client->cl_sname = sname;
834 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
836 sx_xunlock(&svc_rpc_gss_lock);
840 gr->gr_major = gss_accept_sec_context(
843 client->cl_sname->sn_cred,
845 GSS_C_NO_CHANNEL_BINDINGS,
853 sx_xunlock(&svc_rpc_gss_lock);
855 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
858 * If we get an error from gss_accept_sec_context, send the
859 * reply anyway so that the client gets a chance to see what
862 if (gr->gr_major != GSS_S_COMPLETE &&
863 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
864 rpc_gss_log_status("accept_sec_context", client->cl_mech,
865 gr->gr_major, gr->gr_minor);
866 client->cl_state = CLIENT_STALE;
870 gr->gr_handle.value = &client->cl_id;
871 gr->gr_handle.length = sizeof(client->cl_id);
872 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
874 /* Save client info. */
875 client->cl_mech = mech;
876 client->cl_qop = GSS_C_QOP_DEFAULT;
877 client->cl_done_callback = FALSE;
879 if (gr->gr_major == GSS_S_COMPLETE) {
880 gss_buffer_desc export_name;
883 * Change client expiration time to be near when the
884 * client creds expire (or 24 hours if we can't figure
887 if (cred_lifetime == GSS_C_INDEFINITE)
888 cred_lifetime = time_uptime + 24*60*60;
890 client->cl_expiration = time_uptime + cred_lifetime;
893 * Fill in cred details in the rawcred structure.
895 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
896 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
897 maj_stat = gss_export_name(&min_stat, client->cl_cname,
899 if (maj_stat != GSS_S_COMPLETE) {
900 rpc_gss_log_status("gss_export_name", client->cl_mech,
904 client->cl_rawcred.client_principal =
905 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
906 + export_name.length);
907 client->cl_rawcred.client_principal->len = export_name.length;
908 memcpy(client->cl_rawcred.client_principal->name,
909 export_name.value, export_name.length);
910 gss_release_buffer(&min_stat, &export_name);
911 client->cl_rawcred.svc_principal =
912 client->cl_sname->sn_principal;
913 client->cl_rawcred.service = gc->gc_svc;
916 * Use gss_pname_to_uid to map to unix creds. For
917 * kerberos5, this uses krb5_aname_to_localname.
919 svc_rpc_gss_build_ucred(client, client->cl_cname);
920 svc_rpc_gss_set_flavor(client);
921 gss_release_name(&min_stat, &client->cl_cname);
925 gss_buffer_desc mechname;
927 gss_oid_to_str(&min_stat, mech, &mechname);
929 rpc_gss_log_debug("accepted context for %s with "
930 "<mech %.*s, qop %d, svc %d>",
931 client->cl_rawcred.client_principal->name,
932 mechname.length, (char *)mechname.value,
933 client->cl_qop, client->rawcred.service);
935 gss_release_buffer(&min_stat, &mechname);
943 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
946 struct opaque_auth *oa;
947 gss_buffer_desc rpcbuf, checksum;
948 OM_uint32 maj_stat, min_stat;
950 int32_t rpchdr[128 / sizeof(int32_t)];
953 rpc_gss_log_debug("in svc_rpc_gss_validate()");
955 memset(rpchdr, 0, sizeof(rpchdr));
957 /* Reconstruct RPC header for signing (from xdr_callmsg). */
959 IXDR_PUT_LONG(buf, msg->rm_xid);
960 IXDR_PUT_ENUM(buf, msg->rm_direction);
961 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
962 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
963 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
964 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
965 oa = &msg->rm_call.cb_cred;
966 IXDR_PUT_ENUM(buf, oa->oa_flavor);
967 IXDR_PUT_LONG(buf, oa->oa_length);
969 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
970 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
972 rpcbuf.value = rpchdr;
973 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
975 checksum.value = msg->rm_call.cb_verf.oa_base;
976 checksum.length = msg->rm_call.cb_verf.oa_length;
978 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
981 if (maj_stat != GSS_S_COMPLETE) {
982 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
984 client->cl_state = CLIENT_STALE;
993 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
994 struct svc_req *rqst, u_int seq)
996 gss_buffer_desc signbuf;
998 OM_uint32 maj_stat, min_stat;
1001 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1004 signbuf.value = &nseq;
1005 signbuf.length = sizeof(nseq);
1007 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1010 if (maj_stat != GSS_S_COMPLETE) {
1011 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1012 client->cl_state = CLIENT_STALE;
1016 KASSERT(mic.length <= MAX_AUTH_BYTES,
1017 ("MIC too large for RPCSEC_GSS"));
1019 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1020 rqst->rq_verf.oa_length = mic.length;
1021 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1023 gss_release_buffer(&min_stat, &mic);
1029 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1031 struct svc_rpc_gss_callback *scb;
1032 rpc_gss_lock_t lock;
1038 * See if we have a callback for this guy.
1041 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1042 if (scb->cb_callback.program == rqst->rq_prog
1043 && scb->cb_callback.version == rqst->rq_vers) {
1045 * This one matches. Call the callback and see
1046 * if it wants to veto or something.
1048 lock.locked = FALSE;
1049 lock.raw_cred = &client->cl_rawcred;
1050 cb_res = scb->cb_callback.callback(rqst,
1057 client->cl_state = CLIENT_STALE;
1063 * The callback accepted the connection - it
1064 * is responsible for freeing client->cl_creds
1067 client->cl_creds = GSS_C_NO_CREDENTIAL;
1068 client->cl_locked = lock.locked;
1069 client->cl_cookie = cookie;
1075 * Either no callback exists for this program/version or one
1076 * of the callbacks rejected the connection. We just need to
1077 * clean up the delegated client creds, if any.
1079 if (client->cl_creds) {
1081 gss_release_cred(&min_ver, &client->cl_creds);
1087 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1093 sx_xlock(&client->cl_lock);
1094 if (seq <= client->cl_seqlast) {
1096 * The request sequence number is less than
1097 * the largest we have seen so far. If it is
1098 * outside the window or if we have seen a
1099 * request with this sequence before, silently
1102 offset = client->cl_seqlast - seq;
1103 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1109 if (client->cl_seqmask[word] & (1 << bit)) {
1117 sx_xunlock(&client->cl_lock);
1122 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1124 int offset, i, word, bit;
1125 uint32_t carry, newcarry;
1127 sx_xlock(&client->cl_lock);
1128 if (seq > client->cl_seqlast) {
1130 * This request has a sequence number greater
1131 * than any we have seen so far. Advance the
1132 * seq window and set bit zero of the window
1133 * (which corresponds to the new sequence
1136 offset = seq - client->cl_seqlast;
1137 while (offset > 32) {
1138 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1140 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1142 client->cl_seqmask[0] = 0;
1146 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1147 newcarry = client->cl_seqmask[i] >> (32 - offset);
1148 client->cl_seqmask[i] =
1149 (client->cl_seqmask[i] << offset) | carry;
1152 client->cl_seqmask[0] |= 1;
1153 client->cl_seqlast = seq;
1155 offset = client->cl_seqlast - seq;
1158 client->cl_seqmask[word] |= (1 << bit);
1160 sx_xunlock(&client->cl_lock);
1164 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1169 struct svc_rpc_gss_cookedcred *cc;
1170 struct svc_rpc_gss_client *client;
1171 struct rpc_gss_cred gc;
1172 struct rpc_gss_init_res gr;
1175 enum auth_stat result;
1177 rpc_gss_log_debug("in svc_rpc_gss()");
1179 /* Garbage collect old clients. */
1180 svc_rpc_gss_timeout_clients();
1182 /* Initialize reply. */
1183 rqst->rq_verf = _null_auth;
1185 /* Deserialize client credentials. */
1186 if (rqst->rq_cred.oa_length <= 0)
1187 return (AUTH_BADCRED);
1189 memset(&gc, 0, sizeof(gc));
1191 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1192 rqst->rq_cred.oa_length, XDR_DECODE);
1194 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1196 return (AUTH_BADCRED);
1202 /* Check version. */
1203 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1204 result = AUTH_BADCRED;
1208 /* Check the proc and find the client (or create it) */
1209 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1210 if (gc.gc_handle.length != 0) {
1211 result = AUTH_BADCRED;
1214 client = svc_rpc_gss_create_client();
1215 refcount_acquire(&client->cl_refs);
1217 struct svc_rpc_gss_clientid *p;
1218 if (gc.gc_handle.length != sizeof(*p)) {
1219 result = AUTH_BADCRED;
1222 p = gc.gc_handle.value;
1223 client = svc_rpc_gss_find_client(p);
1226 * Can't find the client - we may have
1227 * destroyed it - tell the other side to
1230 result = RPCSEC_GSS_CREDPROBLEM;
1234 cc = rqst->rq_clntcred;
1235 cc->cc_client = client;
1236 cc->cc_service = gc.gc_svc;
1237 cc->cc_seq = gc.gc_seq;
1240 * The service and sequence number must be ignored for
1241 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1243 if (gc.gc_proc != RPCSEC_GSS_INIT
1244 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1246 * Check for sequence number overflow.
1248 if (gc.gc_seq >= MAXSEQ) {
1249 result = RPCSEC_GSS_CTXPROBLEM;
1254 * Check for valid service.
1256 if (gc.gc_svc != rpc_gss_svc_none &&
1257 gc.gc_svc != rpc_gss_svc_integrity &&
1258 gc.gc_svc != rpc_gss_svc_privacy) {
1259 result = AUTH_BADCRED;
1264 /* Handle RPCSEC_GSS control procedure. */
1265 switch (gc.gc_proc) {
1267 case RPCSEC_GSS_INIT:
1268 case RPCSEC_GSS_CONTINUE_INIT:
1269 if (rqst->rq_proc != NULLPROC) {
1270 result = AUTH_REJECTEDCRED;
1274 memset(&gr, 0, sizeof(gr));
1275 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1276 result = AUTH_REJECTEDCRED;
1280 if (gr.gr_major == GSS_S_COMPLETE) {
1282 * We borrow the space for the call verf to
1283 * pack our reply verf.
1285 rqst->rq_verf = msg->rm_call.cb_verf;
1286 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1287 result = AUTH_REJECTEDCRED;
1291 rqst->rq_verf = _null_auth;
1294 call_stat = svc_sendreply(rqst,
1295 (xdrproc_t) xdr_rpc_gss_init_res,
1298 gss_release_buffer(&min_stat, &gr.gr_token);
1301 result = AUTH_FAILED;
1305 if (gr.gr_major == GSS_S_COMPLETE)
1306 client->cl_state = CLIENT_ESTABLISHED;
1308 result = RPCSEC_GSS_NODISPATCH;
1311 case RPCSEC_GSS_DATA:
1312 case RPCSEC_GSS_DESTROY:
1313 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1314 result = RPCSEC_GSS_NODISPATCH;
1318 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1319 result = RPCSEC_GSS_CREDPROBLEM;
1324 * We borrow the space for the call verf to pack our
1327 rqst->rq_verf = msg->rm_call.cb_verf;
1328 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1329 result = RPCSEC_GSS_CTXPROBLEM;
1333 svc_rpc_gss_update_seq(client, gc.gc_seq);
1336 * Change the SVCAUTH ops on the request to point at
1337 * our own code so that we can unwrap the arguments
1338 * and wrap the result. The caller will re-set this on
1339 * every request to point to a set of null wrap/unwrap
1340 * methods. Acquire an extra reference to the client
1341 * which will be released by svc_rpc_gss_release()
1342 * after the request has finished processing.
1344 refcount_acquire(&client->cl_refs);
1345 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1346 rqst->rq_auth.svc_ah_private = cc;
1348 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1350 * We might be ready to do a callback to the server to
1351 * see if it wants to accept/reject the connection.
1353 sx_xlock(&client->cl_lock);
1354 if (!client->cl_done_callback) {
1355 client->cl_done_callback = TRUE;
1356 client->cl_qop = qop;
1357 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1358 client->cl_rawcred.mechanism, qop);
1359 if (!svc_rpc_gss_callback(client, rqst)) {
1360 result = AUTH_REJECTEDCRED;
1361 sx_xunlock(&client->cl_lock);
1365 sx_xunlock(&client->cl_lock);
1368 * If the server has locked this client to a
1369 * particular service+qop pair, enforce that
1372 if (client->cl_locked) {
1373 if (client->cl_rawcred.service != gc.gc_svc) {
1374 result = AUTH_FAILED;
1376 } else if (client->cl_qop != qop) {
1377 result = AUTH_BADVERF;
1383 * If the qop changed, look up the new qop
1386 if (client->cl_qop != qop) {
1387 client->cl_qop = qop;
1388 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1389 client->cl_rawcred.mechanism, qop);
1393 * Make sure we use the right service value
1396 if (client->cl_rawcred.service != gc.gc_svc) {
1397 client->cl_rawcred.service = gc.gc_svc;
1398 svc_rpc_gss_set_flavor(client);
1403 if (rqst->rq_proc != NULLPROC) {
1404 result = AUTH_REJECTEDCRED;
1408 call_stat = svc_sendreply(rqst,
1409 (xdrproc_t) xdr_void, (caddr_t) NULL);
1412 result = AUTH_FAILED;
1416 svc_rpc_gss_forget_client(client);
1418 result = RPCSEC_GSS_NODISPATCH;
1424 result = AUTH_BADCRED;
1429 svc_rpc_gss_release_client(client);
1431 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1436 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1438 struct svc_rpc_gss_cookedcred *cc;
1439 struct svc_rpc_gss_client *client;
1441 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1443 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1444 client = cc->cc_client;
1445 if (client->cl_state != CLIENT_ESTABLISHED
1446 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1450 return (xdr_rpc_gss_wrap_data(mp,
1451 client->cl_ctx, client->cl_qop,
1452 cc->cc_service, cc->cc_seq));
1456 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1458 struct svc_rpc_gss_cookedcred *cc;
1459 struct svc_rpc_gss_client *client;
1461 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1463 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1464 client = cc->cc_client;
1465 if (client->cl_state != CLIENT_ESTABLISHED
1466 || cc->cc_service == rpc_gss_svc_none) {
1470 return (xdr_rpc_gss_unwrap_data(mp,
1471 client->cl_ctx, client->cl_qop,
1472 cc->cc_service, cc->cc_seq));
1476 svc_rpc_gss_release(SVCAUTH *auth)
1478 struct svc_rpc_gss_cookedcred *cc;
1479 struct svc_rpc_gss_client *client;
1481 rpc_gss_log_debug("in svc_rpc_gss_release()");
1483 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1484 client = cc->cc_client;
1485 svc_rpc_gss_release_client(client);