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 SPDX-License-Identifier: BSD-3-Clause
33 Copyright (c) 2000 The Regents of the University of Michigan.
36 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37 All rights reserved, all wrongs reversed.
39 Redistribution and use in source and binary forms, with or without
40 modification, are permitted provided that the following conditions
43 1. Redistributions of source code must retain the above copyright
44 notice, this list of conditions and the following disclaimer.
45 2. Redistributions in binary form must reproduce the above copyright
46 notice, this list of conditions and the following disclaimer in the
47 documentation and/or other materials provided with the distribution.
48 3. Neither the name of the University nor the names of its
49 contributors may be used to endorse or promote products derived
50 from this software without specific prior written permission.
52 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
74 #include <sys/queue.h>
76 #include <rpc/rpcsec_gss.h>
77 #include "rpcsec_gss_int.h"
79 static bool_t svc_rpc_gss_initialised = FALSE;
81 static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
82 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
83 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
85 static struct svc_auth_ops svc_auth_gss_ops = {
90 struct svc_rpc_gss_callback {
91 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
92 rpc_gss_callback_t cb_callback;
94 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
95 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
97 struct svc_rpc_gss_svc_name {
98 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
102 gss_cred_id_t sn_cred;
106 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
107 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
109 enum svc_rpc_gss_client_state {
110 CLIENT_NEW, /* still authenticating */
111 CLIENT_ESTABLISHED, /* context established */
112 CLIENT_STALE /* garbage to collect */
115 #define SVC_RPC_GSS_SEQWINDOW 128
117 struct svc_rpc_gss_client {
118 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
119 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
121 time_t cl_expiration; /* when to gc */
122 enum svc_rpc_gss_client_state cl_state; /* client state */
123 bool_t cl_locked; /* fixed service+qop */
124 gss_ctx_id_t cl_ctx; /* context id */
125 gss_cred_id_t cl_creds; /* delegated creds */
126 gss_name_t cl_cname; /* client name */
127 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
128 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
129 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
130 bool_t cl_done_callback; /* TRUE after call */
131 void *cl_cookie; /* user cookie from callback */
132 gid_t cl_gid_storage[NGRPS];
133 gss_OID cl_mech; /* mechanism */
134 gss_qop_t cl_qop; /* quality of protection */
135 u_int cl_seq; /* current sequence number */
136 u_int cl_win; /* sequence window size */
137 u_int cl_seqlast; /* sequence window origin */
138 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
139 gss_buffer_desc cl_verf; /* buffer for verf checksum */
141 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
143 #define CLIENT_HASH_SIZE 256
144 #define CLIENT_MAX 128
145 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
146 static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
147 static size_t svc_rpc_gss_client_count;
148 static uint32_t svc_rpc_gss_next_clientid = 1;
151 static void svc_rpc_gss_init(void) __attribute__ ((constructor));
155 svc_rpc_gss_init(void)
159 if (!svc_rpc_gss_initialised) {
160 for (i = 0; i < CLIENT_HASH_SIZE; i++)
161 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
162 TAILQ_INIT(&svc_rpc_gss_clients);
163 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
164 svc_rpc_gss_initialised = TRUE;
169 rpc_gss_set_callback(rpc_gss_callback_t *cb)
171 struct svc_rpc_gss_callback *scb;
173 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
175 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
178 scb->cb_callback = *cb;
179 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
185 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
186 u_int req_time, u_int program, u_int version)
188 OM_uint32 maj_stat, min_stat;
189 struct svc_rpc_gss_svc_name *sname;
190 gss_buffer_desc namebuf;
193 gss_OID_set_desc oid_set;
198 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
201 oid_set.elements = mech_oid;
203 namebuf.value = (void *)(intptr_t) principal;
204 namebuf.length = strlen(principal);
206 maj_stat = gss_import_name(&min_stat, &namebuf,
207 GSS_C_NT_HOSTBASED_SERVICE, &name);
208 if (maj_stat != GSS_S_COMPLETE)
211 maj_stat = gss_acquire_cred(&min_stat, name,
212 req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
213 if (maj_stat != GSS_S_COMPLETE)
216 gss_release_name(&min_stat, &name);
218 sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
221 sname->sn_principal = strdup(principal);
222 sname->sn_mech = mech_oid;
223 sname->sn_req_time = req_time;
224 sname->sn_cred = cred;
225 sname->sn_program = program;
226 sname->sn_version = version;
227 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
233 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
234 const char *mech, const char *name, const char *node, const char *domain)
236 OM_uint32 maj_stat, min_stat;
240 gss_name_t gss_name, gss_mech_name;
241 rpc_gss_principal_t result;
245 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
249 * Construct a gss_buffer containing the full name formatted
250 * as "name/node@domain" where node and domain are optional.
252 namelen = strlen(name);
254 namelen += strlen(node) + 1;
257 namelen += strlen(domain) + 1;
260 buf.value = mem_alloc(namelen);
261 buf.length = namelen;
262 strcpy((char *) buf.value, name);
264 strcat((char *) buf.value, "/");
265 strcat((char *) buf.value, node);
268 strcat((char *) buf.value, "@");
269 strcat((char *) buf.value, domain);
273 * Convert that to a gss_name_t and then convert that to a
274 * mechanism name in the selected mechanism.
276 maj_stat = gss_import_name(&min_stat, &buf,
277 GSS_C_NT_USER_NAME, &gss_name);
278 mem_free(buf.value, buf.length);
279 if (maj_stat != GSS_S_COMPLETE) {
280 log_status("gss_import_name", mech_oid, maj_stat, min_stat);
283 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
285 if (maj_stat != GSS_S_COMPLETE) {
286 log_status("gss_canonicalize_name", mech_oid, maj_stat,
288 gss_release_name(&min_stat, &gss_name);
291 gss_release_name(&min_stat, &gss_name);
294 * Export the mechanism name and use that to construct the
295 * rpc_gss_principal_t result.
297 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
298 if (maj_stat != GSS_S_COMPLETE) {
299 log_status("gss_export_name", mech_oid, maj_stat, min_stat);
300 gss_release_name(&min_stat, &gss_mech_name);
303 gss_release_name(&min_stat, &gss_mech_name);
305 result = mem_alloc(sizeof(int) + buf.length);
307 gss_release_buffer(&min_stat, &buf);
310 result->len = buf.length;
311 memcpy(result->name, buf.value, buf.length);
312 gss_release_buffer(&min_stat, &buf);
319 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
320 rpc_gss_ucred_t **ucred, void **cookie)
322 struct svc_rpc_gss_client *client;
324 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
327 client = req->rq_clntcred;
329 *rcred = &client->cl_rawcred;
331 *ucred = &client->cl_ucred;
333 *cookie = client->cl_cookie;
338 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
340 struct svc_rpc_gss_client *client = req->rq_clntcred;
343 OM_uint32 maj_stat, min_stat;
346 switch (client->cl_rawcred.service) {
347 case rpc_gss_svc_none:
348 return (max_tp_unit_len);
351 case rpc_gss_svc_default:
352 case rpc_gss_svc_integrity:
356 case rpc_gss_svc_privacy:
364 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
365 client->cl_qop, max_tp_unit_len, &max);
367 if (maj_stat == GSS_S_COMPLETE) {
373 log_status("gss_wrap_size_limit", client->cl_mech,
379 static struct svc_rpc_gss_client *
380 svc_rpc_gss_find_client(uint32_t clientid)
382 struct svc_rpc_gss_client *client;
383 struct svc_rpc_gss_client_list *list;
386 log_debug("in svc_rpc_gss_find_client(%d)", clientid);
388 list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
389 TAILQ_FOREACH(client, list, cl_link) {
390 if (client->cl_id == clientid) {
392 * Move this client to the front of the LRU
395 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
396 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
405 static struct svc_rpc_gss_client *
406 svc_rpc_gss_create_client(void)
408 struct svc_rpc_gss_client *client;
409 struct svc_rpc_gss_client_list *list;
411 log_debug("in svc_rpc_gss_create_client()");
413 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
414 memset(client, 0, sizeof(struct svc_rpc_gss_client));
415 client->cl_id = svc_rpc_gss_next_clientid++;
416 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
417 TAILQ_INSERT_HEAD(list, client, cl_link);
418 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
421 * Start the client off with a short expiration time. We will
422 * try to get a saner value from the client creds later.
424 client->cl_state = CLIENT_NEW;
425 client->cl_locked = FALSE;
426 client->cl_expiration = time(0) + 5*60;
427 svc_rpc_gss_client_count++;
433 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
435 struct svc_rpc_gss_client_list *list;
438 log_debug("in svc_rpc_gss_destroy_client()");
441 gss_delete_sec_context(&min_stat,
442 &client->cl_ctx, GSS_C_NO_BUFFER);
444 if (client->cl_cname)
445 gss_release_name(&min_stat, &client->cl_cname);
447 if (client->cl_rawcred.client_principal)
448 mem_free(client->cl_rawcred.client_principal,
449 sizeof(*client->cl_rawcred.client_principal)
450 + client->cl_rawcred.client_principal->len);
452 if (client->cl_verf.value)
453 gss_release_buffer(&min_stat, &client->cl_verf);
455 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
456 TAILQ_REMOVE(list, client, cl_link);
457 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
458 svc_rpc_gss_client_count--;
459 mem_free(client, sizeof(*client));
463 svc_rpc_gss_timeout_clients(void)
465 struct svc_rpc_gss_client *client;
466 struct svc_rpc_gss_client *nclient;
467 time_t now = time(0);
469 log_debug("in svc_rpc_gss_timeout_clients()");
471 * First enforce the max client limit. We keep
472 * svc_rpc_gss_clients in LRU order.
474 while (svc_rpc_gss_client_count > CLIENT_MAX)
475 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
476 svc_rpc_gss_client_list));
477 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
478 if (client->cl_state == CLIENT_STALE
479 || now > client->cl_expiration) {
480 log_debug("expiring client %p", client);
481 svc_rpc_gss_destroy_client(client);
488 * OID<->string routines. These are uuuuugly.
491 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
494 unsigned long number;
496 size_t string_length;
501 /* Decoded according to krb5/gssapi_krb5.c */
503 /* First determine the size of the string */
507 cp = (unsigned char *) oid->elements;
508 number = (unsigned long) cp[0];
509 sprintf(numstr, "%ld ", number/40);
510 string_length += strlen(numstr);
511 sprintf(numstr, "%ld ", number%40);
512 string_length += strlen(numstr);
513 for (i=1; i<oid->length; i++) {
514 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
515 number = (number << 7) | (cp[i] & 0x7f);
520 return(GSS_S_FAILURE);
522 if ((cp[i] & 0x80) == 0) {
523 sprintf(numstr, "%ld ", number);
524 string_length += strlen(numstr);
530 * If we get here, we've calculated the length of "n n n ... n ". Add 4
531 * here for "{ " and "}\0".
534 if ((bp = (char *) mem_alloc(string_length))) {
536 number = (unsigned long) cp[0];
537 sprintf(numstr, "%ld ", number/40);
539 sprintf(numstr, "%ld ", number%40);
542 cp = (unsigned char *) oid->elements;
543 for (i=1; i<oid->length; i++) {
544 number = (number << 7) | (cp[i] & 0x7f);
545 if ((cp[i] & 0x80) == 0) {
546 sprintf(numstr, "%ld ", number);
552 oid_str->length = strlen(bp)+1;
553 oid_str->value = (void *) bp;
555 return(GSS_S_COMPLETE);
558 return(GSS_S_FAILURE);
563 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
564 const gss_name_t name)
566 OM_uint32 maj_stat, min_stat;
569 struct passwd pwd, *pw;
570 rpc_gss_ucred_t *uc = &client->cl_ucred;
575 uc->gidlist = client->cl_gid_storage;
577 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
578 if (maj_stat != GSS_S_COMPLETE)
581 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
584 uc->uid = pw->pw_uid;
585 uc->gid = pw->pw_gid;
586 uc->gidlist = client->cl_gid_storage;
587 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
593 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
594 struct svc_req *rqst,
595 struct rpc_gss_init_res *gr,
596 struct rpc_gss_cred *gc)
598 gss_buffer_desc recv_tok;
600 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
601 OM_uint32 cred_lifetime;
602 struct svc_rpc_gss_svc_name *sname;
604 log_debug("in svc_rpc_gss_accept_context()");
606 /* Deserialize arguments. */
607 memset(&recv_tok, 0, sizeof(recv_tok));
609 if (!svc_getargs(rqst->rq_xprt,
610 (xdrproc_t) xdr_gss_buffer_desc,
611 (caddr_t) &recv_tok)) {
612 client->cl_state = CLIENT_STALE;
617 * First time round, try all the server names we have until
618 * one matches. Afterwards, stick with that one.
620 if (!client->cl_sname) {
621 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
622 if (sname->sn_program == rqst->rq_prog
623 && sname->sn_version == rqst->rq_vers) {
624 gr->gr_major = gss_accept_sec_context(
629 GSS_C_NO_CHANNEL_BINDINGS,
636 client->cl_sname = sname;
641 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
646 gr->gr_major = gss_accept_sec_context(
649 client->cl_sname->sn_cred,
651 GSS_C_NO_CHANNEL_BINDINGS,
660 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
663 * If we get an error from gss_accept_sec_context, send the
664 * reply anyway so that the client gets a chance to see what
667 if (gr->gr_major != GSS_S_COMPLETE &&
668 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
669 log_status("accept_sec_context", client->cl_mech,
670 gr->gr_major, gr->gr_minor);
671 client->cl_state = CLIENT_STALE;
675 gr->gr_handle.value = &client->cl_id;
676 gr->gr_handle.length = sizeof(client->cl_id);
677 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
679 /* Save client info. */
680 client->cl_mech = mech;
681 client->cl_qop = GSS_C_QOP_DEFAULT;
682 client->cl_seq = gc->gc_seq;
683 client->cl_win = gr->gr_win;
684 client->cl_done_callback = FALSE;
686 if (gr->gr_major == GSS_S_COMPLETE) {
687 gss_buffer_desc export_name;
690 * Change client expiration time to be near when the
691 * client creds expire (or 24 hours if we can't figure
694 if (cred_lifetime == GSS_C_INDEFINITE)
695 cred_lifetime = time(0) + 24*60*60;
697 client->cl_expiration = time(0) + cred_lifetime;
700 * Fill in cred details in the rawcred structure.
702 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
703 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
704 maj_stat = gss_export_name(&min_stat, client->cl_cname,
706 if (maj_stat != GSS_S_COMPLETE) {
707 log_status("gss_export_name", client->cl_mech,
711 client->cl_rawcred.client_principal =
712 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
713 + export_name.length);
714 client->cl_rawcred.client_principal->len = export_name.length;
715 memcpy(client->cl_rawcred.client_principal->name,
716 export_name.value, export_name.length);
717 gss_release_buffer(&min_stat, &export_name);
718 client->cl_rawcred.svc_principal =
719 client->cl_sname->sn_principal;
720 client->cl_rawcred.service = gc->gc_svc;
723 * Use gss_pname_to_uid to map to unix creds. For
724 * kerberos5, this uses krb5_aname_to_localname.
726 svc_rpc_gss_build_ucred(client, client->cl_cname);
727 gss_release_name(&min_stat, &client->cl_cname);
731 gss_buffer_desc mechname;
733 gss_oid_to_str(&min_stat, mech, &mechname);
735 log_debug("accepted context for %s with "
736 "<mech %.*s, qop %d, svc %d>",
737 client->cl_rawcred.client_principal->name,
738 mechname.length, (char *)mechname.value,
739 client->cl_qop, client->rawcred.service);
741 gss_release_buffer(&min_stat, &mechname);
749 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
752 struct opaque_auth *oa;
753 gss_buffer_desc rpcbuf, checksum;
754 OM_uint32 maj_stat, min_stat;
756 int32_t rpchdr[128 / sizeof(int32_t)];
759 log_debug("in svc_rpc_gss_validate()");
761 memset(rpchdr, 0, sizeof(rpchdr));
763 /* Reconstruct RPC header for signing (from xdr_callmsg). */
765 IXDR_PUT_LONG(buf, msg->rm_xid);
766 IXDR_PUT_ENUM(buf, msg->rm_direction);
767 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
768 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
769 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
770 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
771 oa = &msg->rm_call.cb_cred;
772 IXDR_PUT_ENUM(buf, oa->oa_flavor);
773 IXDR_PUT_LONG(buf, oa->oa_length);
775 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
776 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
778 rpcbuf.value = rpchdr;
779 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
781 checksum.value = msg->rm_call.cb_verf.oa_base;
782 checksum.length = msg->rm_call.cb_verf.oa_length;
784 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
787 if (maj_stat != GSS_S_COMPLETE) {
788 log_status("gss_verify_mic", client->cl_mech,
790 client->cl_state = CLIENT_STALE;
798 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
799 struct svc_req *rqst, u_int seq)
801 gss_buffer_desc signbuf;
802 OM_uint32 maj_stat, min_stat;
805 log_debug("in svc_rpc_gss_nextverf()");
808 signbuf.value = &nseq;
809 signbuf.length = sizeof(nseq);
811 if (client->cl_verf.value)
812 gss_release_buffer(&min_stat, &client->cl_verf);
814 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
815 &signbuf, &client->cl_verf);
817 if (maj_stat != GSS_S_COMPLETE) {
818 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
819 client->cl_state = CLIENT_STALE;
822 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
823 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
824 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
830 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
832 struct svc_rpc_gss_callback *scb;
839 * See if we have a callback for this guy.
842 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
843 if (scb->cb_callback.program == rqst->rq_prog
844 && scb->cb_callback.version == rqst->rq_vers) {
846 * This one matches. Call the callback and see
847 * if it wants to veto or something.
850 lock.raw_cred = &client->cl_rawcred;
851 cb_res = scb->cb_callback.callback(rqst,
858 client->cl_state = CLIENT_STALE;
864 * The callback accepted the connection - it
865 * is responsible for freeing client->cl_creds
868 client->cl_creds = GSS_C_NO_CREDENTIAL;
869 client->cl_locked = lock.locked;
870 client->cl_cookie = cookie;
876 * Either no callback exists for this program/version or one
877 * of the callbacks rejected the connection. We just need to
878 * clean up the delegated client creds, if any.
880 if (client->cl_creds) {
882 gss_release_cred(&min_ver, &client->cl_creds);
888 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
893 if (seq <= client->cl_seqlast) {
895 * The request sequence number is less than
896 * the largest we have seen so far. If it is
897 * outside the window or if we have seen a
898 * request with this sequence before, silently
901 offset = client->cl_seqlast - seq;
902 if (offset >= SVC_RPC_GSS_SEQWINDOW)
906 if (client->cl_seqmask[word] & (1 << bit))
914 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
916 int offset, i, word, bit;
917 uint32_t carry, newcarry;
920 maskp = client->cl_seqmask;
921 if (seq > client->cl_seqlast) {
923 * This request has a sequence number greater
924 * than any we have seen so far. Advance the
925 * seq window and set bit zero of the window
926 * (which corresponds to the new sequence
929 offset = seq - client->cl_seqlast;
930 while (offset >= 32) {
931 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
933 maskp[i] = maskp[i-1];
940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941 newcarry = maskp[i] >> (32 - offset);
942 maskp[i] = (maskp[i] << offset) | carry;
947 client->cl_seqlast = seq;
949 offset = client->cl_seqlast - seq;
952 maskp[word] |= (1 << bit);
958 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
963 struct svc_rpc_gss_client *client;
964 struct rpc_gss_cred gc;
965 struct rpc_gss_init_res gr;
968 enum auth_stat result;
970 log_debug("in svc_rpc_gss()");
972 /* Garbage collect old clients. */
973 svc_rpc_gss_timeout_clients();
975 /* Initialize reply. */
976 rqst->rq_xprt->xp_verf = _null_auth;
978 /* Deserialize client credentials. */
979 if (rqst->rq_cred.oa_length <= 0)
980 return (AUTH_BADCRED);
982 memset(&gc, 0, sizeof(gc));
984 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
985 rqst->rq_cred.oa_length, XDR_DECODE);
987 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
989 return (AUTH_BADCRED);
994 if (gc.gc_version != RPCSEC_GSS_VERSION) {
995 result = AUTH_BADCRED;
999 /* Check the proc and find the client (or create it) */
1000 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1001 if (gc.gc_handle.length != 0) {
1002 result = AUTH_BADCRED;
1005 client = svc_rpc_gss_create_client();
1007 if (gc.gc_handle.length != sizeof(uint32_t)) {
1008 result = AUTH_BADCRED;
1011 uint32_t *p = gc.gc_handle.value;
1012 client = svc_rpc_gss_find_client(*p);
1015 * Can't find the client - we may have
1016 * destroyed it - tell the other side to
1019 result = RPCSEC_GSS_CREDPROBLEM;
1023 rqst->rq_clntcred = client;
1026 * The service and sequence number must be ignored for
1027 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1029 if (gc.gc_proc != RPCSEC_GSS_INIT
1030 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1032 * Check for sequence number overflow.
1034 if (gc.gc_seq >= MAXSEQ) {
1035 result = RPCSEC_GSS_CTXPROBLEM;
1038 client->cl_seq = gc.gc_seq;
1041 * Check for valid service.
1043 if (gc.gc_svc != rpc_gss_svc_none &&
1044 gc.gc_svc != rpc_gss_svc_integrity &&
1045 gc.gc_svc != rpc_gss_svc_privacy) {
1046 result = AUTH_BADCRED;
1051 /* Handle RPCSEC_GSS control procedure. */
1052 switch (gc.gc_proc) {
1054 case RPCSEC_GSS_INIT:
1055 case RPCSEC_GSS_CONTINUE_INIT:
1056 if (rqst->rq_proc != NULLPROC) {
1057 result = AUTH_REJECTEDCRED;
1061 memset(&gr, 0, sizeof(gr));
1062 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1063 result = AUTH_REJECTEDCRED;
1067 if (gr.gr_major == GSS_S_COMPLETE) {
1068 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1069 result = AUTH_REJECTEDCRED;
1073 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1074 rqst->rq_xprt->xp_verf.oa_length = 0;
1077 call_stat = svc_sendreply(rqst->rq_xprt,
1078 (xdrproc_t) xdr_rpc_gss_init_res,
1081 gss_release_buffer(&min_stat, &gr.gr_token);
1084 result = AUTH_FAILED;
1088 if (gr.gr_major == GSS_S_COMPLETE)
1089 client->cl_state = CLIENT_ESTABLISHED;
1091 result = RPCSEC_GSS_NODISPATCH;
1094 case RPCSEC_GSS_DATA:
1095 case RPCSEC_GSS_DESTROY:
1096 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1097 result = RPCSEC_GSS_NODISPATCH;
1101 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1102 result = RPCSEC_GSS_CREDPROBLEM;
1106 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1107 result = RPCSEC_GSS_CTXPROBLEM;
1111 svc_rpc_gss_update_seq(client, gc.gc_seq);
1114 * Change the SVCAUTH ops on the transport to point at
1115 * our own code so that we can unwrap the arguments
1116 * and wrap the result. The caller will re-set this on
1117 * every request to point to a set of null wrap/unwrap
1120 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1121 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1123 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1125 * We might be ready to do a callback to the server to
1126 * see if it wants to accept/reject the connection.
1128 if (!client->cl_done_callback) {
1129 client->cl_done_callback = TRUE;
1130 client->cl_qop = qop;
1131 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1132 client->cl_rawcred.mechanism, qop);
1133 if (!svc_rpc_gss_callback(client, rqst)) {
1134 result = AUTH_REJECTEDCRED;
1140 * If the server has locked this client to a
1141 * particular service+qop pair, enforce that
1144 if (client->cl_locked) {
1145 if (client->cl_rawcred.service != gc.gc_svc) {
1146 result = AUTH_FAILED;
1148 } else if (client->cl_qop != qop) {
1149 result = AUTH_BADVERF;
1155 * If the qop changed, look up the new qop
1158 if (client->cl_qop != qop) {
1159 client->cl_qop = qop;
1160 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1161 client->cl_rawcred.mechanism, qop);
1165 * Make sure we use the right service value
1168 client->cl_rawcred.service = gc.gc_svc;
1172 if (rqst->rq_proc != NULLPROC) {
1173 result = AUTH_REJECTEDCRED;
1177 call_stat = svc_sendreply(rqst->rq_xprt,
1178 (xdrproc_t) xdr_void, (caddr_t) NULL);
1181 result = AUTH_FAILED;
1185 svc_rpc_gss_destroy_client(client);
1187 result = RPCSEC_GSS_NODISPATCH;
1193 result = AUTH_BADCRED;
1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1202 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1204 struct svc_rpc_gss_client *client;
1206 log_debug("in svc_rpc_gss_wrap()");
1208 client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1209 if (client->cl_state != CLIENT_ESTABLISHED
1210 || client->cl_rawcred.service == rpc_gss_svc_none) {
1211 return xdr_func(xdrs, xdr_ptr);
1213 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1214 client->cl_ctx, client->cl_qop,
1215 client->cl_rawcred.service, client->cl_seq));
1219 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1221 struct svc_rpc_gss_client *client;
1223 log_debug("in svc_rpc_gss_unwrap()");
1225 client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1226 if (client->cl_state != CLIENT_ESTABLISHED
1227 || client->cl_rawcred.service == rpc_gss_svc_none) {
1228 return xdr_func(xdrs, xdr_ptr);
1230 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1231 client->cl_ctx, client->cl_qop,
1232 client->cl_rawcred.service, client->cl_seq));