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
31 Copyright (c) 2000 The Regents of the University of Michigan.
34 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35 All rights reserved, all wrongs reversed.
37 Redistribution and use in source and binary forms, with or without
38 modification, are permitted provided that the following conditions
41 1. Redistributions of source code must retain the above copyright
42 notice, this list of conditions and the following disclaimer.
43 2. Redistributions in binary form must reproduce the above copyright
44 notice, this list of conditions and the following disclaimer in the
45 documentation and/or other materials provided with the distribution.
46 3. Neither the name of the University nor the names of its
47 contributors may be used to endorse or promote products derived
48 from this software without specific prior written permission.
50 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
72 #include <sys/queue.h>
74 #include <rpc/rpcsec_gss.h>
75 #include "rpcsec_gss_int.h"
77 static bool_t svc_rpc_gss_initialised = FALSE;
79 static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
80 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
81 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
83 static struct svc_auth_ops svc_auth_gss_ops = {
88 struct svc_rpc_gss_callback {
89 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
90 rpc_gss_callback_t cb_callback;
92 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
93 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
95 struct svc_rpc_gss_svc_name {
96 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
100 gss_cred_id_t sn_cred;
104 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
105 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
107 enum svc_rpc_gss_client_state {
108 CLIENT_NEW, /* still authenticating */
109 CLIENT_ESTABLISHED, /* context established */
110 CLIENT_STALE /* garbage to collect */
113 #define SVC_RPC_GSS_SEQWINDOW 128
115 struct svc_rpc_gss_client {
116 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
117 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
119 time_t cl_expiration; /* when to gc */
120 enum svc_rpc_gss_client_state cl_state; /* client state */
121 bool_t cl_locked; /* fixed service+qop */
122 gss_ctx_id_t cl_ctx; /* context id */
123 gss_cred_id_t cl_creds; /* delegated creds */
124 gss_name_t cl_cname; /* client name */
125 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
126 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
127 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
128 bool_t cl_done_callback; /* TRUE after call */
129 void *cl_cookie; /* user cookie from callback */
130 gid_t cl_gid_storage[NGRPS];
131 gss_OID cl_mech; /* mechanism */
132 gss_qop_t cl_qop; /* quality of protection */
133 u_int cl_seq; /* current sequence number */
134 u_int cl_win; /* sequence window size */
135 u_int cl_seqlast; /* sequence window origin */
136 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
137 gss_buffer_desc cl_verf; /* buffer for verf checksum */
139 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
141 #define CLIENT_HASH_SIZE 256
142 #define CLIENT_MAX 128
143 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144 static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
145 static size_t svc_rpc_gss_client_count;
146 static uint32_t svc_rpc_gss_next_clientid = 1;
149 static void svc_rpc_gss_init(void) __attribute__ ((constructor));
153 svc_rpc_gss_init(void)
157 if (!svc_rpc_gss_initialised) {
158 for (i = 0; i < CLIENT_HASH_SIZE; i++)
159 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
160 TAILQ_INIT(&svc_rpc_gss_clients);
161 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
162 svc_rpc_gss_initialised = TRUE;
167 rpc_gss_set_callback(rpc_gss_callback_t *cb)
169 struct svc_rpc_gss_callback *scb;
171 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
173 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
176 scb->cb_callback = *cb;
177 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
183 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
184 u_int req_time, u_int program, u_int version)
186 OM_uint32 maj_stat, min_stat;
187 struct svc_rpc_gss_svc_name *sname;
188 gss_buffer_desc namebuf;
191 gss_OID_set_desc oid_set;
196 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
199 oid_set.elements = mech_oid;
201 namebuf.value = (void *)(intptr_t) principal;
202 namebuf.length = strlen(principal);
204 maj_stat = gss_import_name(&min_stat, &namebuf,
205 GSS_C_NT_HOSTBASED_SERVICE, &name);
206 if (maj_stat != GSS_S_COMPLETE)
209 maj_stat = gss_acquire_cred(&min_stat, name,
210 req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
211 if (maj_stat != GSS_S_COMPLETE)
214 gss_release_name(&min_stat, &name);
216 sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
219 sname->sn_principal = strdup(principal);
220 sname->sn_mech = mech_oid;
221 sname->sn_req_time = req_time;
222 sname->sn_cred = cred;
223 sname->sn_program = program;
224 sname->sn_version = version;
225 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
231 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
232 const char *mech, const char *name, const char *node, const char *domain)
234 OM_uint32 maj_stat, min_stat;
238 gss_name_t gss_name, gss_mech_name;
239 rpc_gss_principal_t result;
243 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
247 * Construct a gss_buffer containing the full name formatted
248 * as "name/node@domain" where node and domain are optional.
250 namelen = strlen(name);
252 namelen += strlen(node) + 1;
255 namelen += strlen(domain) + 1;
258 buf.value = mem_alloc(namelen);
259 buf.length = namelen;
260 strcpy((char *) buf.value, name);
262 strcat((char *) buf.value, "/");
263 strcat((char *) buf.value, node);
266 strcat((char *) buf.value, "@");
267 strcat((char *) buf.value, domain);
271 * Convert that to a gss_name_t and then convert that to a
272 * mechanism name in the selected mechanism.
274 maj_stat = gss_import_name(&min_stat, &buf,
275 GSS_C_NT_USER_NAME, &gss_name);
276 mem_free(buf.value, buf.length);
277 if (maj_stat != GSS_S_COMPLETE) {
278 log_status("gss_import_name", mech_oid, maj_stat, min_stat);
281 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
283 if (maj_stat != GSS_S_COMPLETE) {
284 log_status("gss_canonicalize_name", mech_oid, maj_stat,
286 gss_release_name(&min_stat, &gss_name);
289 gss_release_name(&min_stat, &gss_name);
292 * Export the mechanism name and use that to construct the
293 * rpc_gss_principal_t result.
295 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
296 if (maj_stat != GSS_S_COMPLETE) {
297 log_status("gss_export_name", mech_oid, maj_stat, min_stat);
298 gss_release_name(&min_stat, &gss_mech_name);
301 gss_release_name(&min_stat, &gss_mech_name);
303 result = mem_alloc(sizeof(int) + buf.length);
305 gss_release_buffer(&min_stat, &buf);
308 result->len = buf.length;
309 memcpy(result->name, buf.value, buf.length);
310 gss_release_buffer(&min_stat, &buf);
317 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
318 rpc_gss_ucred_t **ucred, void **cookie)
320 struct svc_rpc_gss_client *client;
322 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
325 client = req->rq_clntcred;
327 *rcred = &client->cl_rawcred;
329 *ucred = &client->cl_ucred;
331 *cookie = client->cl_cookie;
336 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
338 struct svc_rpc_gss_client *client = req->rq_clntcred;
341 OM_uint32 maj_stat, min_stat;
344 switch (client->cl_rawcred.service) {
345 case rpc_gss_svc_none:
346 return (max_tp_unit_len);
349 case rpc_gss_svc_default:
350 case rpc_gss_svc_integrity:
354 case rpc_gss_svc_privacy:
362 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
363 client->cl_qop, max_tp_unit_len, &max);
365 if (maj_stat == GSS_S_COMPLETE) {
371 log_status("gss_wrap_size_limit", client->cl_mech,
377 static struct svc_rpc_gss_client *
378 svc_rpc_gss_find_client(uint32_t clientid)
380 struct svc_rpc_gss_client *client;
381 struct svc_rpc_gss_client_list *list;
384 log_debug("in svc_rpc_gss_find_client(%d)", clientid);
386 list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
387 TAILQ_FOREACH(client, list, cl_link) {
388 if (client->cl_id == clientid) {
390 * Move this client to the front of the LRU
393 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
394 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
403 static struct svc_rpc_gss_client *
404 svc_rpc_gss_create_client(void)
406 struct svc_rpc_gss_client *client;
407 struct svc_rpc_gss_client_list *list;
409 log_debug("in svc_rpc_gss_create_client()");
411 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
412 memset(client, 0, sizeof(struct svc_rpc_gss_client));
413 client->cl_id = svc_rpc_gss_next_clientid++;
414 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
415 TAILQ_INSERT_HEAD(list, client, cl_link);
416 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
419 * Start the client off with a short expiration time. We will
420 * try to get a saner value from the client creds later.
422 client->cl_state = CLIENT_NEW;
423 client->cl_locked = FALSE;
424 client->cl_expiration = time(0) + 5*60;
425 svc_rpc_gss_client_count++;
431 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
433 struct svc_rpc_gss_client_list *list;
436 log_debug("in svc_rpc_gss_destroy_client()");
439 gss_delete_sec_context(&min_stat,
440 &client->cl_ctx, GSS_C_NO_BUFFER);
442 if (client->cl_cname)
443 gss_release_name(&min_stat, &client->cl_cname);
445 if (client->cl_rawcred.client_principal)
446 mem_free(client->cl_rawcred.client_principal,
447 sizeof(*client->cl_rawcred.client_principal)
448 + client->cl_rawcred.client_principal->len);
450 if (client->cl_verf.value)
451 gss_release_buffer(&min_stat, &client->cl_verf);
453 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
454 TAILQ_REMOVE(list, client, cl_link);
455 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
456 svc_rpc_gss_client_count--;
457 mem_free(client, sizeof(*client));
461 svc_rpc_gss_timeout_clients(void)
463 struct svc_rpc_gss_client *client;
464 struct svc_rpc_gss_client *nclient;
465 time_t now = time(0);
467 log_debug("in svc_rpc_gss_timeout_clients()");
469 * First enforce the max client limit. We keep
470 * svc_rpc_gss_clients in LRU order.
472 while (svc_rpc_gss_client_count > CLIENT_MAX)
473 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
474 svc_rpc_gss_client_list));
475 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
476 if (client->cl_state == CLIENT_STALE
477 || now > client->cl_expiration) {
478 log_debug("expiring client %p", client);
479 svc_rpc_gss_destroy_client(client);
486 * OID<->string routines. These are uuuuugly.
489 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
492 unsigned long number;
494 size_t string_length;
499 /* Decoded according to krb5/gssapi_krb5.c */
501 /* First determine the size of the string */
505 cp = (unsigned char *) oid->elements;
506 number = (unsigned long) cp[0];
507 sprintf(numstr, "%ld ", number/40);
508 string_length += strlen(numstr);
509 sprintf(numstr, "%ld ", number%40);
510 string_length += strlen(numstr);
511 for (i=1; i<oid->length; i++) {
512 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
513 number = (number << 7) | (cp[i] & 0x7f);
518 return(GSS_S_FAILURE);
520 if ((cp[i] & 0x80) == 0) {
521 sprintf(numstr, "%ld ", number);
522 string_length += strlen(numstr);
528 * If we get here, we've calculated the length of "n n n ... n ". Add 4
529 * here for "{ " and "}\0".
532 if ((bp = (char *) mem_alloc(string_length))) {
534 number = (unsigned long) cp[0];
535 sprintf(numstr, "%ld ", number/40);
537 sprintf(numstr, "%ld ", number%40);
540 cp = (unsigned char *) oid->elements;
541 for (i=1; i<oid->length; i++) {
542 number = (number << 7) | (cp[i] & 0x7f);
543 if ((cp[i] & 0x80) == 0) {
544 sprintf(numstr, "%ld ", number);
550 oid_str->length = strlen(bp)+1;
551 oid_str->value = (void *) bp;
553 return(GSS_S_COMPLETE);
556 return(GSS_S_FAILURE);
561 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
562 const gss_name_t name)
564 OM_uint32 maj_stat, min_stat;
567 struct passwd pwd, *pw;
568 rpc_gss_ucred_t *uc = &client->cl_ucred;
573 uc->gidlist = client->cl_gid_storage;
575 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
576 if (maj_stat != GSS_S_COMPLETE)
579 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
582 uc->uid = pw->pw_uid;
583 uc->gid = pw->pw_gid;
584 uc->gidlist = client->cl_gid_storage;
585 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
591 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
592 struct svc_req *rqst,
593 struct rpc_gss_init_res *gr,
594 struct rpc_gss_cred *gc)
596 gss_buffer_desc recv_tok;
598 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
599 OM_uint32 cred_lifetime;
600 struct svc_rpc_gss_svc_name *sname;
602 log_debug("in svc_rpc_gss_accept_context()");
604 /* Deserialize arguments. */
605 memset(&recv_tok, 0, sizeof(recv_tok));
607 if (!svc_getargs(rqst->rq_xprt,
608 (xdrproc_t) xdr_gss_buffer_desc,
609 (caddr_t) &recv_tok)) {
610 client->cl_state = CLIENT_STALE;
615 * First time round, try all the server names we have until
616 * one matches. Afterwards, stick with that one.
618 if (!client->cl_sname) {
619 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
620 if (sname->sn_program == rqst->rq_prog
621 && sname->sn_version == rqst->rq_vers) {
622 gr->gr_major = gss_accept_sec_context(
627 GSS_C_NO_CHANNEL_BINDINGS,
634 client->cl_sname = sname;
639 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
644 gr->gr_major = gss_accept_sec_context(
647 client->cl_sname->sn_cred,
649 GSS_C_NO_CHANNEL_BINDINGS,
658 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
661 * If we get an error from gss_accept_sec_context, send the
662 * reply anyway so that the client gets a chance to see what
665 if (gr->gr_major != GSS_S_COMPLETE &&
666 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
667 log_status("accept_sec_context", client->cl_mech,
668 gr->gr_major, gr->gr_minor);
669 client->cl_state = CLIENT_STALE;
673 gr->gr_handle.value = &client->cl_id;
674 gr->gr_handle.length = sizeof(client->cl_id);
675 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
677 /* Save client info. */
678 client->cl_mech = mech;
679 client->cl_qop = GSS_C_QOP_DEFAULT;
680 client->cl_seq = gc->gc_seq;
681 client->cl_win = gr->gr_win;
682 client->cl_done_callback = FALSE;
684 if (gr->gr_major == GSS_S_COMPLETE) {
685 gss_buffer_desc export_name;
688 * Change client expiration time to be near when the
689 * client creds expire (or 24 hours if we can't figure
692 if (cred_lifetime == GSS_C_INDEFINITE)
693 cred_lifetime = time(0) + 24*60*60;
695 client->cl_expiration = time(0) + cred_lifetime;
698 * Fill in cred details in the rawcred structure.
700 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
701 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
702 maj_stat = gss_export_name(&min_stat, client->cl_cname,
704 if (maj_stat != GSS_S_COMPLETE) {
705 log_status("gss_export_name", client->cl_mech,
709 client->cl_rawcred.client_principal =
710 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
711 + export_name.length);
712 client->cl_rawcred.client_principal->len = export_name.length;
713 memcpy(client->cl_rawcred.client_principal->name,
714 export_name.value, export_name.length);
715 gss_release_buffer(&min_stat, &export_name);
716 client->cl_rawcred.svc_principal =
717 client->cl_sname->sn_principal;
718 client->cl_rawcred.service = gc->gc_svc;
721 * Use gss_pname_to_uid to map to unix creds. For
722 * kerberos5, this uses krb5_aname_to_localname.
724 svc_rpc_gss_build_ucred(client, client->cl_cname);
725 gss_release_name(&min_stat, &client->cl_cname);
729 gss_buffer_desc mechname;
731 gss_oid_to_str(&min_stat, mech, &mechname);
733 log_debug("accepted context for %s with "
734 "<mech %.*s, qop %d, svc %d>",
735 client->cl_rawcred.client_principal->name,
736 mechname.length, (char *)mechname.value,
737 client->cl_qop, client->rawcred.service);
739 gss_release_buffer(&min_stat, &mechname);
747 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
750 struct opaque_auth *oa;
751 gss_buffer_desc rpcbuf, checksum;
752 OM_uint32 maj_stat, min_stat;
754 int32_t rpchdr[128 / sizeof(int32_t)];
757 log_debug("in svc_rpc_gss_validate()");
759 memset(rpchdr, 0, sizeof(rpchdr));
761 /* Reconstruct RPC header for signing (from xdr_callmsg). */
763 IXDR_PUT_LONG(buf, msg->rm_xid);
764 IXDR_PUT_ENUM(buf, msg->rm_direction);
765 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
766 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
767 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
768 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
769 oa = &msg->rm_call.cb_cred;
770 IXDR_PUT_ENUM(buf, oa->oa_flavor);
771 IXDR_PUT_LONG(buf, oa->oa_length);
773 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
774 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
776 rpcbuf.value = rpchdr;
777 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
779 checksum.value = msg->rm_call.cb_verf.oa_base;
780 checksum.length = msg->rm_call.cb_verf.oa_length;
782 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
785 if (maj_stat != GSS_S_COMPLETE) {
786 log_status("gss_verify_mic", client->cl_mech,
788 client->cl_state = CLIENT_STALE;
796 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
797 struct svc_req *rqst, u_int seq)
799 gss_buffer_desc signbuf;
800 OM_uint32 maj_stat, min_stat;
803 log_debug("in svc_rpc_gss_nextverf()");
806 signbuf.value = &nseq;
807 signbuf.length = sizeof(nseq);
809 if (client->cl_verf.value)
810 gss_release_buffer(&min_stat, &client->cl_verf);
812 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
813 &signbuf, &client->cl_verf);
815 if (maj_stat != GSS_S_COMPLETE) {
816 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
817 client->cl_state = CLIENT_STALE;
820 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
821 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
822 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
828 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
830 struct svc_rpc_gss_callback *scb;
837 * See if we have a callback for this guy.
840 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
841 if (scb->cb_callback.program == rqst->rq_prog
842 && scb->cb_callback.version == rqst->rq_vers) {
844 * This one matches. Call the callback and see
845 * if it wants to veto or something.
848 lock.raw_cred = &client->cl_rawcred;
849 cb_res = scb->cb_callback.callback(rqst,
856 client->cl_state = CLIENT_STALE;
862 * The callback accepted the connection - it
863 * is responsible for freeing client->cl_creds
866 client->cl_creds = GSS_C_NO_CREDENTIAL;
867 client->cl_locked = lock.locked;
868 client->cl_cookie = cookie;
874 * Either no callback exists for this program/version or one
875 * of the callbacks rejected the connection. We just need to
876 * clean up the delegated client creds, if any.
878 if (client->cl_creds) {
880 gss_release_cred(&min_ver, &client->cl_creds);
886 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
891 if (seq <= client->cl_seqlast) {
893 * The request sequence number is less than
894 * the largest we have seen so far. If it is
895 * outside the window or if we have seen a
896 * request with this sequence before, silently
899 offset = client->cl_seqlast - seq;
900 if (offset >= SVC_RPC_GSS_SEQWINDOW)
904 if (client->cl_seqmask[word] & (1 << bit))
912 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
914 int offset, i, word, bit;
915 uint32_t carry, newcarry;
918 maskp = client->cl_seqmask;
919 if (seq > client->cl_seqlast) {
921 * This request has a sequence number greater
922 * than any we have seen so far. Advance the
923 * seq window and set bit zero of the window
924 * (which corresponds to the new sequence
927 offset = seq - client->cl_seqlast;
928 while (offset >= 32) {
929 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
931 maskp[i] = maskp[i-1];
938 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
939 newcarry = maskp[i] >> (32 - offset);
940 maskp[i] = (maskp[i] << offset) | carry;
945 client->cl_seqlast = seq;
947 offset = client->cl_seqlast - seq;
950 maskp[word] |= (1 << bit);
956 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
961 struct svc_rpc_gss_client *client;
962 struct rpc_gss_cred gc;
963 struct rpc_gss_init_res gr;
966 enum auth_stat result;
968 log_debug("in svc_rpc_gss()");
970 /* Garbage collect old clients. */
971 svc_rpc_gss_timeout_clients();
973 /* Initialize reply. */
974 rqst->rq_xprt->xp_verf = _null_auth;
976 /* Deserialize client credentials. */
977 if (rqst->rq_cred.oa_length <= 0)
978 return (AUTH_BADCRED);
980 memset(&gc, 0, sizeof(gc));
982 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
983 rqst->rq_cred.oa_length, XDR_DECODE);
985 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
987 return (AUTH_BADCRED);
992 if (gc.gc_version != RPCSEC_GSS_VERSION) {
993 result = AUTH_BADCRED;
997 /* Check the proc and find the client (or create it) */
998 if (gc.gc_proc == RPCSEC_GSS_INIT) {
999 if (gc.gc_handle.length != 0) {
1000 result = AUTH_BADCRED;
1003 client = svc_rpc_gss_create_client();
1005 if (gc.gc_handle.length != sizeof(uint32_t)) {
1006 result = AUTH_BADCRED;
1009 uint32_t *p = gc.gc_handle.value;
1010 client = svc_rpc_gss_find_client(*p);
1013 * Can't find the client - we may have
1014 * destroyed it - tell the other side to
1017 result = RPCSEC_GSS_CREDPROBLEM;
1021 rqst->rq_clntcred = client;
1024 * The service and sequence number must be ignored for
1025 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1027 if (gc.gc_proc != RPCSEC_GSS_INIT
1028 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1030 * Check for sequence number overflow.
1032 if (gc.gc_seq >= MAXSEQ) {
1033 result = RPCSEC_GSS_CTXPROBLEM;
1036 client->cl_seq = gc.gc_seq;
1039 * Check for valid service.
1041 if (gc.gc_svc != rpc_gss_svc_none &&
1042 gc.gc_svc != rpc_gss_svc_integrity &&
1043 gc.gc_svc != rpc_gss_svc_privacy) {
1044 result = AUTH_BADCRED;
1049 /* Handle RPCSEC_GSS control procedure. */
1050 switch (gc.gc_proc) {
1052 case RPCSEC_GSS_INIT:
1053 case RPCSEC_GSS_CONTINUE_INIT:
1054 if (rqst->rq_proc != NULLPROC) {
1055 result = AUTH_REJECTEDCRED;
1059 memset(&gr, 0, sizeof(gr));
1060 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1061 result = AUTH_REJECTEDCRED;
1065 if (gr.gr_major == GSS_S_COMPLETE) {
1066 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1067 result = AUTH_REJECTEDCRED;
1071 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1072 rqst->rq_xprt->xp_verf.oa_length = 0;
1075 call_stat = svc_sendreply(rqst->rq_xprt,
1076 (xdrproc_t) xdr_rpc_gss_init_res,
1079 gss_release_buffer(&min_stat, &gr.gr_token);
1082 result = AUTH_FAILED;
1086 if (gr.gr_major == GSS_S_COMPLETE)
1087 client->cl_state = CLIENT_ESTABLISHED;
1089 result = RPCSEC_GSS_NODISPATCH;
1092 case RPCSEC_GSS_DATA:
1093 case RPCSEC_GSS_DESTROY:
1094 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1095 result = RPCSEC_GSS_NODISPATCH;
1099 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1100 result = RPCSEC_GSS_CREDPROBLEM;
1104 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1105 result = RPCSEC_GSS_CTXPROBLEM;
1109 svc_rpc_gss_update_seq(client, gc.gc_seq);
1112 * Change the SVCAUTH ops on the transport to point at
1113 * our own code so that we can unwrap the arguments
1114 * and wrap the result. The caller will re-set this on
1115 * every request to point to a set of null wrap/unwrap
1118 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1119 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1121 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1123 * We might be ready to do a callback to the server to
1124 * see if it wants to accept/reject the connection.
1126 if (!client->cl_done_callback) {
1127 client->cl_done_callback = TRUE;
1128 client->cl_qop = qop;
1129 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1130 client->cl_rawcred.mechanism, qop);
1131 if (!svc_rpc_gss_callback(client, rqst)) {
1132 result = AUTH_REJECTEDCRED;
1138 * If the server has locked this client to a
1139 * particular service+qop pair, enforce that
1142 if (client->cl_locked) {
1143 if (client->cl_rawcred.service != gc.gc_svc) {
1144 result = AUTH_FAILED;
1146 } else if (client->cl_qop != qop) {
1147 result = AUTH_BADVERF;
1153 * If the qop changed, look up the new qop
1156 if (client->cl_qop != qop) {
1157 client->cl_qop = qop;
1158 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1159 client->cl_rawcred.mechanism, qop);
1163 * Make sure we use the right service value
1166 client->cl_rawcred.service = gc.gc_svc;
1170 if (rqst->rq_proc != NULLPROC) {
1171 result = AUTH_REJECTEDCRED;
1175 call_stat = svc_sendreply(rqst->rq_xprt,
1176 (xdrproc_t) xdr_void, (caddr_t) NULL);
1179 result = AUTH_FAILED;
1183 svc_rpc_gss_destroy_client(client);
1185 result = RPCSEC_GSS_NODISPATCH;
1191 result = AUTH_BADCRED;
1195 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1200 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1202 struct svc_rpc_gss_client *client;
1204 log_debug("in svc_rpc_gss_wrap()");
1206 client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1207 if (client->cl_state != CLIENT_ESTABLISHED
1208 || client->cl_rawcred.service == rpc_gss_svc_none) {
1209 return xdr_func(xdrs, xdr_ptr);
1211 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1212 client->cl_ctx, client->cl_qop,
1213 client->cl_rawcred.service, client->cl_seq));
1217 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1219 struct svc_rpc_gss_client *client;
1221 log_debug("in svc_rpc_gss_unwrap()");
1223 client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1224 if (client->cl_state != CLIENT_ESTABLISHED
1225 || client->cl_rawcred.service == rpc_gss_svc_none) {
1226 return xdr_func(xdrs, xdr_ptr);
1228 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1229 client->cl_ctx, client->cl_qop,
1230 client->cl_rawcred.service, client->cl_seq));