]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
MFV r342532: 5882 Temporary pool names
[FreeBSD/FreeBSD.git] / sys / rpc / rpcsec_gss / svc_rpcsec_gss.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright (c) 1990 The Regents of the University of California.
4  *
5  * Copyright (c) 2008 Doug Rabson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30   svc_rpcsec_gss.c
31   
32   Copyright (c) 2000 The Regents of the University of Michigan.
33   All rights reserved.
34
35   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
36   All rights reserved, all wrongs reversed.
37
38   Redistribution and use in source and binary forms, with or without
39   modification, are permitted provided that the following conditions
40   are met:
41
42   1. Redistributions of source code must retain the above copyright
43      notice, this list of conditions and the following disclaimer.
44   2. Redistributions in binary form must reproduce the above copyright
45      notice, this list of conditions and the following disclaimer in the
46      documentation and/or other materials provided with the distribution.
47   3. Neither the name of the University nor the names of its
48      contributors may be used to endorse or promote products derived
49      from this software without specific prior written permission.
50
51   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
52   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
58   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
59   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
60   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62
63   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
64  */
65
66 #include <sys/cdefs.h>
67 __FBSDID("$FreeBSD$");
68
69 #include <sys/param.h>
70 #include <sys/systm.h>
71 #include <sys/jail.h>
72 #include <sys/kernel.h>
73 #include <sys/kobj.h>
74 #include <sys/lock.h>
75 #include <sys/malloc.h>
76 #include <sys/mbuf.h>
77 #include <sys/mutex.h>
78 #include <sys/proc.h>
79 #include <sys/sx.h>
80 #include <sys/ucred.h>
81
82 #include <rpc/rpc.h>
83 #include <rpc/rpcsec_gss.h>
84
85 #include "rpcsec_gss_int.h"
86
87 static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
88 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
89 static void     svc_rpc_gss_release(SVCAUTH *);
90 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
91 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
92
93 static struct svc_auth_ops svc_auth_gss_ops = {
94         svc_rpc_gss_wrap,
95         svc_rpc_gss_unwrap,
96         svc_rpc_gss_release,
97 };
98
99 struct sx svc_rpc_gss_lock;
100
101 struct svc_rpc_gss_callback {
102         SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
103         rpc_gss_callback_t      cb_callback;
104 };
105 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
106         svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
107
108 struct svc_rpc_gss_svc_name {
109         SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
110         char                    *sn_principal;
111         gss_OID                 sn_mech;
112         u_int                   sn_req_time;
113         gss_cred_id_t           sn_cred;
114         u_int                   sn_program;
115         u_int                   sn_version;
116 };
117 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
118         svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
119
120 enum svc_rpc_gss_client_state {
121         CLIENT_NEW,                             /* still authenticating */
122         CLIENT_ESTABLISHED,                     /* context established */
123         CLIENT_STALE                            /* garbage to collect */
124 };
125
126 #define SVC_RPC_GSS_SEQWINDOW   128
127
128 struct svc_rpc_gss_clientid {
129         unsigned long           ci_hostid;
130         uint32_t                ci_boottime;
131         uint32_t                ci_id;
132 };
133
134 struct svc_rpc_gss_client {
135         TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
136         TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
137         volatile u_int          cl_refs;
138         struct sx               cl_lock;
139         struct svc_rpc_gss_clientid cl_id;
140         time_t                  cl_expiration;  /* when to gc */
141         enum svc_rpc_gss_client_state cl_state; /* client state */
142         bool_t                  cl_locked;      /* fixed service+qop */
143         gss_ctx_id_t            cl_ctx;         /* context id */
144         gss_cred_id_t           cl_creds;       /* delegated creds */
145         gss_name_t              cl_cname;       /* client name */
146         struct svc_rpc_gss_svc_name *cl_sname;  /* server name used */
147         rpc_gss_rawcred_t       cl_rawcred;     /* raw credentials */
148         rpc_gss_ucred_t         cl_ucred;       /* unix-style credentials */
149         struct ucred            *cl_cred;       /* kernel-style credentials */
150         int                     cl_rpcflavor;   /* RPC pseudo sec flavor */
151         bool_t                  cl_done_callback; /* TRUE after call */
152         void                    *cl_cookie;     /* user cookie from callback */
153         gid_t                   cl_gid_storage[NGROUPS];
154         gss_OID                 cl_mech;        /* mechanism */
155         gss_qop_t               cl_qop;         /* quality of protection */
156         uint32_t                cl_seqlast;     /* sequence window origin */
157         uint32_t                cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
158 };
159 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
160
161 /*
162  * This structure holds enough information to unwrap arguments or wrap
163  * results for a given request. We use the rq_clntcred area for this
164  * (which is a per-request buffer).
165  */
166 struct svc_rpc_gss_cookedcred {
167         struct svc_rpc_gss_client *cc_client;
168         rpc_gss_service_t       cc_service;
169         uint32_t                cc_seq;
170 };
171
172 #define CLIENT_HASH_SIZE        256
173 #define CLIENT_MAX              128
174 u_int svc_rpc_gss_client_max = CLIENT_MAX;
175
176 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW, 0, "RPC");
177 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW, 0, "GSS");
178
179 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
180     &svc_rpc_gss_client_max, 0,
181     "Max number of rpc-gss clients");
182
183 static u_int svc_rpc_gss_client_count;
184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
185     &svc_rpc_gss_client_count, 0,
186     "Number of rpc-gss clients");
187
188 struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
189 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
190 static uint32_t svc_rpc_gss_next_clientid = 1;
191
192 static void
193 svc_rpc_gss_init(void *arg)
194 {
195         int i;
196
197         for (i = 0; i < CLIENT_HASH_SIZE; i++)
198                 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
199         TAILQ_INIT(&svc_rpc_gss_clients);
200         svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
201         sx_init(&svc_rpc_gss_lock, "gsslock");
202 }
203 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
204
205 bool_t
206 rpc_gss_set_callback(rpc_gss_callback_t *cb)
207 {
208         struct svc_rpc_gss_callback *scb;
209
210         scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
211         if (!scb) {
212                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
213                 return (FALSE);
214         }
215         scb->cb_callback = *cb;
216         sx_xlock(&svc_rpc_gss_lock);
217         SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
218         sx_xunlock(&svc_rpc_gss_lock);
219
220         return (TRUE);
221 }
222
223 void
224 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
225 {
226         struct svc_rpc_gss_callback *scb;
227
228         sx_xlock(&svc_rpc_gss_lock);
229         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
230                 if (scb->cb_callback.program == cb->program
231                     && scb->cb_callback.version == cb->version
232                     && scb->cb_callback.callback == cb->callback) {
233                         SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
234                             svc_rpc_gss_callback, cb_link);
235                         sx_xunlock(&svc_rpc_gss_lock);
236                         mem_free(scb, sizeof(*scb));
237                         return;
238                 }
239         }
240         sx_xunlock(&svc_rpc_gss_lock);
241 }
242
243 static bool_t
244 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
245 {
246         OM_uint32               maj_stat, min_stat;
247         gss_buffer_desc         namebuf;
248         gss_name_t              name;
249         gss_OID_set_desc        oid_set;
250
251         oid_set.count = 1;
252         oid_set.elements = sname->sn_mech;
253
254         namebuf.value = (void *) sname->sn_principal;
255         namebuf.length = strlen(sname->sn_principal);
256
257         maj_stat = gss_import_name(&min_stat, &namebuf,
258                                    GSS_C_NT_HOSTBASED_SERVICE, &name);
259         if (maj_stat != GSS_S_COMPLETE)
260                 return (FALSE);
261
262         if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
263                 gss_release_cred(&min_stat, &sname->sn_cred);
264
265         maj_stat = gss_acquire_cred(&min_stat, name,
266             sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
267             NULL, NULL);
268         if (maj_stat != GSS_S_COMPLETE) {
269                 gss_release_name(&min_stat, &name);
270                 return (FALSE);
271         }
272         gss_release_name(&min_stat, &name);
273
274         return (TRUE);
275 }
276
277 bool_t
278 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
279     u_int req_time, u_int program, u_int version)
280 {
281         struct svc_rpc_gss_svc_name *sname;
282         gss_OID                 mech_oid;
283
284         if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
285                 return (FALSE);
286
287         sname = mem_alloc(sizeof(*sname));
288         if (!sname)
289                 return (FALSE);
290         sname->sn_principal = strdup(principal, M_RPC);
291         sname->sn_mech = mech_oid;
292         sname->sn_req_time = req_time;
293         sname->sn_cred = GSS_C_NO_CREDENTIAL;
294         sname->sn_program = program;
295         sname->sn_version = version;
296
297         if (!rpc_gss_acquire_svc_cred(sname)) {
298                 free(sname->sn_principal, M_RPC);
299                 mem_free(sname, sizeof(*sname));
300                 return (FALSE);
301         }
302
303         sx_xlock(&svc_rpc_gss_lock);
304         SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
305         sx_xunlock(&svc_rpc_gss_lock);
306
307         return (TRUE);
308 }
309
310 void
311 rpc_gss_clear_svc_name(u_int program, u_int version)
312 {
313         OM_uint32               min_stat;
314         struct svc_rpc_gss_svc_name *sname;
315
316         sx_xlock(&svc_rpc_gss_lock);
317         SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
318                 if (sname->sn_program == program
319                     && sname->sn_version == version) {
320                         SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
321                             svc_rpc_gss_svc_name, sn_link);
322                         sx_xunlock(&svc_rpc_gss_lock);
323                         gss_release_cred(&min_stat, &sname->sn_cred);
324                         free(sname->sn_principal, M_RPC);
325                         mem_free(sname, sizeof(*sname));
326                         return;
327                 }
328         }
329         sx_xunlock(&svc_rpc_gss_lock);
330 }
331
332 bool_t
333 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
334     const char *mech, const char *name, const char *node, const char *domain)
335 {
336         OM_uint32               maj_stat, min_stat;
337         gss_OID                 mech_oid;
338         size_t                  namelen;
339         gss_buffer_desc         buf;
340         gss_name_t              gss_name, gss_mech_name;
341         rpc_gss_principal_t     result;
342
343         if (!rpc_gss_mech_to_oid(mech, &mech_oid))
344                 return (FALSE);
345
346         /*
347          * Construct a gss_buffer containing the full name formatted
348          * as "name/node@domain" where node and domain are optional.
349          */
350         namelen = strlen(name) + 1;
351         if (node) {
352                 namelen += strlen(node) + 1;
353         }
354         if (domain) {
355                 namelen += strlen(domain) + 1;
356         }
357
358         buf.value = mem_alloc(namelen);
359         buf.length = namelen;
360         strcpy((char *) buf.value, name);
361         if (node) {
362                 strcat((char *) buf.value, "/");
363                 strcat((char *) buf.value, node);
364         }
365         if (domain) {
366                 strcat((char *) buf.value, "@");
367                 strcat((char *) buf.value, domain);
368         }
369
370         /*
371          * Convert that to a gss_name_t and then convert that to a
372          * mechanism name in the selected mechanism.
373          */
374         maj_stat = gss_import_name(&min_stat, &buf,
375             GSS_C_NT_USER_NAME, &gss_name);
376         mem_free(buf.value, buf.length);
377         if (maj_stat != GSS_S_COMPLETE) {
378                 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
379                 return (FALSE);
380         }
381         maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
382             &gss_mech_name);
383         if (maj_stat != GSS_S_COMPLETE) {
384                 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
385                     min_stat);
386                 gss_release_name(&min_stat, &gss_name);
387                 return (FALSE);
388         }
389         gss_release_name(&min_stat, &gss_name);
390
391         /*
392          * Export the mechanism name and use that to construct the
393          * rpc_gss_principal_t result.
394          */
395         maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
396         if (maj_stat != GSS_S_COMPLETE) {
397                 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
398                 gss_release_name(&min_stat, &gss_mech_name);
399                 return (FALSE);
400         }
401         gss_release_name(&min_stat, &gss_mech_name);
402
403         result = mem_alloc(sizeof(int) + buf.length);
404         if (!result) {
405                 gss_release_buffer(&min_stat, &buf);
406                 return (FALSE);
407         }
408         result->len = buf.length;
409         memcpy(result->name, buf.value, buf.length);
410         gss_release_buffer(&min_stat, &buf);
411
412         *principal = result;
413         return (TRUE);
414 }
415
416 bool_t
417 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
418     rpc_gss_ucred_t **ucred, void **cookie)
419 {
420         struct svc_rpc_gss_cookedcred *cc;
421         struct svc_rpc_gss_client *client;
422
423         if (req->rq_cred.oa_flavor != RPCSEC_GSS)
424                 return (FALSE);
425
426         cc = req->rq_clntcred;
427         client = cc->cc_client;
428         if (rcred)
429                 *rcred = &client->cl_rawcred;
430         if (ucred)
431                 *ucred = &client->cl_ucred;
432         if (cookie)
433                 *cookie = client->cl_cookie;
434         return (TRUE);
435 }
436
437 /*
438  * This simpler interface is used by svc_getcred to copy the cred data
439  * into a kernel cred structure.
440  */
441 static int
442 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
443 {
444         struct ucred *cr;
445         struct svc_rpc_gss_cookedcred *cc;
446         struct svc_rpc_gss_client *client;
447         rpc_gss_ucred_t *uc;
448
449         if (req->rq_cred.oa_flavor != RPCSEC_GSS)
450                 return (FALSE);
451
452         cc = req->rq_clntcred;
453         client = cc->cc_client;
454
455         if (flavorp)
456                 *flavorp = client->cl_rpcflavor;
457
458         if (client->cl_cred) {
459                 *crp = crhold(client->cl_cred);
460                 return (TRUE);
461         }
462
463         uc = &client->cl_ucred;
464         cr = client->cl_cred = crget();
465         cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
466         cr->cr_rgid = cr->cr_svgid = uc->gid;
467         crsetgroups(cr, uc->gidlen, uc->gidlist);
468         cr->cr_prison = &prison0;
469         prison_hold(cr->cr_prison);
470         *crp = crhold(cr);
471
472         return (TRUE);
473 }
474
475 int
476 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
477 {
478         struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
479         struct svc_rpc_gss_client *client = cc->cc_client;
480         int                     want_conf;
481         OM_uint32               max;
482         OM_uint32               maj_stat, min_stat;
483         int                     result;
484
485         switch (client->cl_rawcred.service) {
486         case rpc_gss_svc_none:
487                 return (max_tp_unit_len);
488                 break;
489
490         case rpc_gss_svc_default:
491         case rpc_gss_svc_integrity:
492                 want_conf = FALSE;
493                 break;
494
495         case rpc_gss_svc_privacy:
496                 want_conf = TRUE;
497                 break;
498
499         default:
500                 return (0);
501         }
502
503         maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
504             client->cl_qop, max_tp_unit_len, &max);
505
506         if (maj_stat == GSS_S_COMPLETE) {
507                 result = (int) max;
508                 if (result < 0)
509                         result = 0;
510                 return (result);
511         } else {
512                 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
513                     maj_stat, min_stat);
514                 return (0);
515         }
516 }
517
518 static struct svc_rpc_gss_client *
519 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
520 {
521         struct svc_rpc_gss_client *client;
522         struct svc_rpc_gss_client_list *list;
523         struct timeval boottime;
524         unsigned long hostid;
525
526         rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
527
528         getcredhostid(curthread->td_ucred, &hostid);
529         getboottime(&boottime);
530         if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
531                 return (NULL);
532
533         list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
534         sx_xlock(&svc_rpc_gss_lock);
535         TAILQ_FOREACH(client, list, cl_link) {
536                 if (client->cl_id.ci_id == id->ci_id) {
537                         /*
538                          * Move this client to the front of the LRU
539                          * list.
540                          */
541                         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
542                         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
543                             cl_alllink);
544                         refcount_acquire(&client->cl_refs);
545                         break;
546                 }
547         }
548         sx_xunlock(&svc_rpc_gss_lock);
549
550         return (client);
551 }
552
553 static struct svc_rpc_gss_client *
554 svc_rpc_gss_create_client(void)
555 {
556         struct svc_rpc_gss_client *client;
557         struct svc_rpc_gss_client_list *list;
558         struct timeval boottime;
559         unsigned long hostid;
560
561         rpc_gss_log_debug("in svc_rpc_gss_create_client()");
562
563         client = mem_alloc(sizeof(struct svc_rpc_gss_client));
564         memset(client, 0, sizeof(struct svc_rpc_gss_client));
565         refcount_init(&client->cl_refs, 1);
566         sx_init(&client->cl_lock, "GSS-client");
567         getcredhostid(curthread->td_ucred, &hostid);
568         client->cl_id.ci_hostid = hostid;
569         getboottime(&boottime);
570         client->cl_id.ci_boottime = boottime.tv_sec;
571         client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
572         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
573         sx_xlock(&svc_rpc_gss_lock);
574         TAILQ_INSERT_HEAD(list, client, cl_link);
575         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
576         svc_rpc_gss_client_count++;
577         sx_xunlock(&svc_rpc_gss_lock);
578
579         /*
580          * Start the client off with a short expiration time. We will
581          * try to get a saner value from the client creds later.
582          */
583         client->cl_state = CLIENT_NEW;
584         client->cl_locked = FALSE;
585         client->cl_expiration = time_uptime + 5*60;
586
587         return (client);
588 }
589
590 static void
591 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
592 {
593         OM_uint32 min_stat;
594
595         rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
596
597         if (client->cl_ctx)
598                 gss_delete_sec_context(&min_stat,
599                     &client->cl_ctx, GSS_C_NO_BUFFER);
600
601         if (client->cl_cname)
602                 gss_release_name(&min_stat, &client->cl_cname);
603
604         if (client->cl_rawcred.client_principal)
605                 mem_free(client->cl_rawcred.client_principal,
606                     sizeof(*client->cl_rawcred.client_principal)
607                     + client->cl_rawcred.client_principal->len);
608
609         if (client->cl_cred)
610                 crfree(client->cl_cred);
611
612         sx_destroy(&client->cl_lock);
613         mem_free(client, sizeof(*client));
614 }
615
616 /*
617  * Drop a reference to a client and free it if that was the last reference.
618  */
619 static void
620 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
621 {
622
623         if (!refcount_release(&client->cl_refs))
624                 return;
625         svc_rpc_gss_destroy_client(client);
626 }
627
628 /*
629  * Remove a client from our global lists.
630  * Must be called with svc_rpc_gss_lock held.
631  */
632 static void
633 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
634 {
635         struct svc_rpc_gss_client_list *list;
636
637         sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
638         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
639         TAILQ_REMOVE(list, client, cl_link);
640         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
641         svc_rpc_gss_client_count--;
642 }
643
644 /*
645  * Remove a client from our global lists and free it if we can.
646  */
647 static void
648 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
649 {
650         struct svc_rpc_gss_client_list *list;
651         struct svc_rpc_gss_client *tclient;
652
653         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
654         sx_xlock(&svc_rpc_gss_lock);
655         TAILQ_FOREACH(tclient, list, cl_link) {
656                 /*
657                  * Make sure this client has not already been removed
658                  * from the lists by svc_rpc_gss_forget_client() or
659                  * svc_rpc_gss_forget_client_locked().
660                  */
661                 if (client == tclient) {
662                         svc_rpc_gss_forget_client_locked(client);
663                         sx_xunlock(&svc_rpc_gss_lock);
664                         svc_rpc_gss_release_client(client);
665                         return;
666                 }
667         }
668         sx_xunlock(&svc_rpc_gss_lock);
669 }
670
671 static void
672 svc_rpc_gss_timeout_clients(void)
673 {
674         struct svc_rpc_gss_client *client;
675         time_t now = time_uptime;
676
677         rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
678
679         /*
680          * First enforce the max client limit. We keep
681          * svc_rpc_gss_clients in LRU order.
682          */
683         sx_xlock(&svc_rpc_gss_lock);
684         client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
685         while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
686                 svc_rpc_gss_forget_client_locked(client);
687                 sx_xunlock(&svc_rpc_gss_lock);
688                 svc_rpc_gss_release_client(client);
689                 sx_xlock(&svc_rpc_gss_lock);
690                 client = TAILQ_LAST(&svc_rpc_gss_clients,
691                     svc_rpc_gss_client_list);
692         }
693 again:
694         TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
695                 if (client->cl_state == CLIENT_STALE
696                     || now > client->cl_expiration) {
697                         svc_rpc_gss_forget_client_locked(client);
698                         sx_xunlock(&svc_rpc_gss_lock);
699                         rpc_gss_log_debug("expiring client %p", client);
700                         svc_rpc_gss_release_client(client);
701                         sx_xlock(&svc_rpc_gss_lock);
702                         goto again;
703                 }
704         }
705         sx_xunlock(&svc_rpc_gss_lock);
706 }
707
708 #ifdef DEBUG
709 /*
710  * OID<->string routines.  These are uuuuugly.
711  */
712 static OM_uint32
713 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
714 {
715         char            numstr[128];
716         unsigned long   number;
717         int             numshift;
718         size_t          string_length;
719         size_t          i;
720         unsigned char   *cp;
721         char            *bp;
722
723         /* Decoded according to krb5/gssapi_krb5.c */
724
725         /* First determine the size of the string */
726         string_length = 0;
727         number = 0;
728         numshift = 0;
729         cp = (unsigned char *) oid->elements;
730         number = (unsigned long) cp[0];
731         sprintf(numstr, "%ld ", number/40);
732         string_length += strlen(numstr);
733         sprintf(numstr, "%ld ", number%40);
734         string_length += strlen(numstr);
735         for (i=1; i<oid->length; i++) {
736                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
737                         number = (number << 7) | (cp[i] & 0x7f);
738                         numshift += 7;
739                 }
740                 else {
741                         *minor_status = 0;
742                         return(GSS_S_FAILURE);
743                 }
744                 if ((cp[i] & 0x80) == 0) {
745                         sprintf(numstr, "%ld ", number);
746                         string_length += strlen(numstr);
747                         number = 0;
748                         numshift = 0;
749                 }
750         }
751         /*
752          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
753          * here for "{ " and "}\0".
754          */
755         string_length += 4;
756         if ((bp = (char *) mem_alloc(string_length))) {
757                 strcpy(bp, "{ ");
758                 number = (unsigned long) cp[0];
759                 sprintf(numstr, "%ld ", number/40);
760                 strcat(bp, numstr);
761                 sprintf(numstr, "%ld ", number%40);
762                 strcat(bp, numstr);
763                 number = 0;
764                 cp = (unsigned char *) oid->elements;
765                 for (i=1; i<oid->length; i++) {
766                         number = (number << 7) | (cp[i] & 0x7f);
767                         if ((cp[i] & 0x80) == 0) {
768                                 sprintf(numstr, "%ld ", number);
769                                 strcat(bp, numstr);
770                                 number = 0;
771                         }
772                 }
773                 strcat(bp, "}");
774                 oid_str->length = strlen(bp)+1;
775                 oid_str->value = (void *) bp;
776                 *minor_status = 0;
777                 return(GSS_S_COMPLETE);
778         }
779         *minor_status = 0;
780         return(GSS_S_FAILURE);
781 }
782 #endif
783
784 static void
785 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
786     const gss_name_t name)
787 {
788         OM_uint32               maj_stat, min_stat;
789         rpc_gss_ucred_t         *uc = &client->cl_ucred;
790         int                     numgroups;
791
792         uc->uid = 65534;
793         uc->gid = 65534;
794         uc->gidlist = client->cl_gid_storage;
795
796         numgroups = NGROUPS;
797         maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
798             &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
799         if (GSS_ERROR(maj_stat))
800                 uc->gidlen = 0;
801         else
802                 uc->gidlen = numgroups;
803 }
804
805 static void
806 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
807 {
808         static gss_OID_desc krb5_mech_oid =
809                 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
810
811         /*
812          * Attempt to translate mech type and service into a
813          * 'pseudo flavor'. Hardwire in krb5 support for now.
814          */
815         if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
816                 switch (client->cl_rawcred.service) {
817                 case rpc_gss_svc_default:
818                 case rpc_gss_svc_none:
819                         client->cl_rpcflavor = RPCSEC_GSS_KRB5;
820                         break;
821                 case rpc_gss_svc_integrity:
822                         client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
823                         break;
824                 case rpc_gss_svc_privacy:
825                         client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
826                         break;
827                 }
828         } else {
829                 client->cl_rpcflavor = RPCSEC_GSS;
830         }
831 }
832
833 static bool_t
834 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
835                                struct svc_req *rqst,
836                                struct rpc_gss_init_res *gr,
837                                struct rpc_gss_cred *gc)
838 {
839         gss_buffer_desc         recv_tok;
840         gss_OID                 mech;
841         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
842         OM_uint32               cred_lifetime;
843         struct svc_rpc_gss_svc_name *sname;
844
845         rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
846         
847         /* Deserialize arguments. */
848         memset(&recv_tok, 0, sizeof(recv_tok));
849         
850         if (!svc_getargs(rqst,
851                 (xdrproc_t) xdr_gss_buffer_desc,
852                 (caddr_t) &recv_tok)) {
853                 client->cl_state = CLIENT_STALE;
854                 return (FALSE);
855         }
856
857         /*
858          * First time round, try all the server names we have until
859          * one matches. Afterwards, stick with that one.
860          */
861         sx_xlock(&svc_rpc_gss_lock);
862         if (!client->cl_sname) {
863                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
864                         if (sname->sn_program == rqst->rq_prog
865                             && sname->sn_version == rqst->rq_vers) {
866                         retry:
867                                 gr->gr_major = gss_accept_sec_context(
868                                         &gr->gr_minor,
869                                         &client->cl_ctx,
870                                         sname->sn_cred,
871                                         &recv_tok,
872                                         GSS_C_NO_CHANNEL_BINDINGS,
873                                         &client->cl_cname,
874                                         &mech,
875                                         &gr->gr_token,
876                                         &ret_flags,
877                                         &cred_lifetime,
878                                         &client->cl_creds);
879                                 if (gr->gr_major == 
880                                     GSS_S_CREDENTIALS_EXPIRED) {
881                                         /*
882                                          * Either our creds really did
883                                          * expire or gssd was
884                                          * restarted.
885                                          */
886                                         if (rpc_gss_acquire_svc_cred(sname))
887                                                 goto retry;
888                                 }
889                                 client->cl_sname = sname;
890                                 break;
891                         }
892                 }
893                 if (!sname) {
894                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
895                             (char *) &recv_tok);
896                         sx_xunlock(&svc_rpc_gss_lock);
897                         return (FALSE);
898                 }
899         } else {
900                 gr->gr_major = gss_accept_sec_context(
901                         &gr->gr_minor,
902                         &client->cl_ctx,
903                         client->cl_sname->sn_cred,
904                         &recv_tok,
905                         GSS_C_NO_CHANNEL_BINDINGS,
906                         &client->cl_cname,
907                         &mech,
908                         &gr->gr_token,
909                         &ret_flags,
910                         &cred_lifetime,
911                         NULL);
912         }
913         sx_xunlock(&svc_rpc_gss_lock);
914         
915         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
916
917         /*
918          * If we get an error from gss_accept_sec_context, send the
919          * reply anyway so that the client gets a chance to see what
920          * is wrong.
921          */
922         if (gr->gr_major != GSS_S_COMPLETE &&
923             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
924                 rpc_gss_log_status("accept_sec_context", client->cl_mech,
925                     gr->gr_major, gr->gr_minor);
926                 client->cl_state = CLIENT_STALE;
927                 return (TRUE);
928         }
929
930         gr->gr_handle.value = &client->cl_id;
931         gr->gr_handle.length = sizeof(client->cl_id);
932         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
933         
934         /* Save client info. */
935         client->cl_mech = mech;
936         client->cl_qop = GSS_C_QOP_DEFAULT;
937         client->cl_done_callback = FALSE;
938
939         if (gr->gr_major == GSS_S_COMPLETE) {
940                 gss_buffer_desc export_name;
941
942                 /*
943                  * Change client expiration time to be near when the
944                  * client creds expire (or 24 hours if we can't figure
945                  * that out).
946                  */
947                 if (cred_lifetime == GSS_C_INDEFINITE)
948                         cred_lifetime = time_uptime + 24*60*60;
949
950                 client->cl_expiration = time_uptime + cred_lifetime;
951
952                 /*
953                  * Fill in cred details in the rawcred structure.
954                  */
955                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
956                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
957                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
958                     &export_name);
959                 if (maj_stat != GSS_S_COMPLETE) {
960                         rpc_gss_log_status("gss_export_name", client->cl_mech,
961                             maj_stat, min_stat);
962                         return (FALSE);
963                 }
964                 client->cl_rawcred.client_principal =
965                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
966                             + export_name.length);
967                 client->cl_rawcred.client_principal->len = export_name.length;
968                 memcpy(client->cl_rawcred.client_principal->name,
969                     export_name.value, export_name.length);
970                 gss_release_buffer(&min_stat, &export_name);
971                 client->cl_rawcred.svc_principal =
972                         client->cl_sname->sn_principal;
973                 client->cl_rawcred.service = gc->gc_svc;
974
975                 /*
976                  * Use gss_pname_to_uid to map to unix creds. For
977                  * kerberos5, this uses krb5_aname_to_localname.
978                  */
979                 svc_rpc_gss_build_ucred(client, client->cl_cname);
980                 svc_rpc_gss_set_flavor(client);
981                 gss_release_name(&min_stat, &client->cl_cname);
982
983 #ifdef DEBUG
984                 {
985                         gss_buffer_desc mechname;
986
987                         gss_oid_to_str(&min_stat, mech, &mechname);
988                         
989                         rpc_gss_log_debug("accepted context for %s with "
990                             "<mech %.*s, qop %d, svc %d>",
991                             client->cl_rawcred.client_principal->name,
992                             mechname.length, (char *)mechname.value,
993                             client->cl_qop, client->cl_rawcred.service);
994
995                         gss_release_buffer(&min_stat, &mechname);
996                 }
997 #endif /* DEBUG */
998         }
999         return (TRUE);
1000 }
1001
1002 static bool_t
1003 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1004     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1005 {
1006         struct opaque_auth      *oa;
1007         gss_buffer_desc          rpcbuf, checksum;
1008         OM_uint32                maj_stat, min_stat;
1009         gss_qop_t                qop_state;
1010         int32_t                  rpchdr[128 / sizeof(int32_t)];
1011         int32_t                 *buf;
1012
1013         rpc_gss_log_debug("in svc_rpc_gss_validate()");
1014         
1015         memset(rpchdr, 0, sizeof(rpchdr));
1016
1017         /* Reconstruct RPC header for signing (from xdr_callmsg). */
1018         buf = rpchdr;
1019         IXDR_PUT_LONG(buf, msg->rm_xid);
1020         IXDR_PUT_ENUM(buf, msg->rm_direction);
1021         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1022         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1023         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1024         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1025         oa = &msg->rm_call.cb_cred;
1026         IXDR_PUT_ENUM(buf, oa->oa_flavor);
1027         IXDR_PUT_LONG(buf, oa->oa_length);
1028         if (oa->oa_length) {
1029                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1030                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1031         }
1032         rpcbuf.value = rpchdr;
1033         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1034
1035         checksum.value = msg->rm_call.cb_verf.oa_base;
1036         checksum.length = msg->rm_call.cb_verf.oa_length;
1037         
1038         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1039                                   &qop_state);
1040         
1041         if (maj_stat != GSS_S_COMPLETE) {
1042                 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1043                     maj_stat, min_stat);
1044                 /*
1045                  * A bug in some versions of the Linux client generates a
1046                  * Destroy operation with a bogus encrypted checksum. Deleting
1047                  * the credential handle for that case causes the mount to fail.
1048                  * Since the checksum is bogus (gss_verify_mic() failed), it
1049                  * doesn't make sense to destroy the handle and not doing so
1050                  * fixes the Linux mount.
1051                  */
1052                 if (gcproc != RPCSEC_GSS_DESTROY)
1053                         client->cl_state = CLIENT_STALE;
1054                 return (FALSE);
1055         }
1056
1057         *qop = qop_state;
1058         return (TRUE);
1059 }
1060
1061 static bool_t
1062 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1063     struct svc_req *rqst, u_int seq)
1064 {
1065         gss_buffer_desc         signbuf;
1066         gss_buffer_desc         mic;
1067         OM_uint32               maj_stat, min_stat;
1068         uint32_t                nseq;       
1069
1070         rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1071
1072         nseq = htonl(seq);
1073         signbuf.value = &nseq;
1074         signbuf.length = sizeof(nseq);
1075
1076         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1077             &signbuf, &mic);
1078
1079         if (maj_stat != GSS_S_COMPLETE) {
1080                 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1081                 client->cl_state = CLIENT_STALE;
1082                 return (FALSE);
1083         }
1084
1085         KASSERT(mic.length <= MAX_AUTH_BYTES,
1086             ("MIC too large for RPCSEC_GSS"));
1087
1088         rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1089         rqst->rq_verf.oa_length = mic.length;
1090         bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1091
1092         gss_release_buffer(&min_stat, &mic);
1093         
1094         return (TRUE);
1095 }
1096
1097 static bool_t
1098 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1099 {
1100         struct svc_rpc_gss_callback *scb;
1101         rpc_gss_lock_t  lock;
1102         void            *cookie;
1103         bool_t          cb_res;
1104         bool_t          result;
1105
1106         /*
1107          * See if we have a callback for this guy.
1108          */
1109         result = TRUE;
1110         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1111                 if (scb->cb_callback.program == rqst->rq_prog
1112                     && scb->cb_callback.version == rqst->rq_vers) {
1113                         /*
1114                          * This one matches. Call the callback and see
1115                          * if it wants to veto or something.
1116                          */
1117                         lock.locked = FALSE;
1118                         lock.raw_cred = &client->cl_rawcred;
1119                         cb_res = scb->cb_callback.callback(rqst,
1120                             client->cl_creds,
1121                             client->cl_ctx,
1122                             &lock,
1123                             &cookie);
1124
1125                         if (!cb_res) {
1126                                 client->cl_state = CLIENT_STALE;
1127                                 result = FALSE;
1128                                 break;
1129                         }
1130
1131                         /*
1132                          * The callback accepted the connection - it
1133                          * is responsible for freeing client->cl_creds
1134                          * now.
1135                          */
1136                         client->cl_creds = GSS_C_NO_CREDENTIAL;
1137                         client->cl_locked = lock.locked;
1138                         client->cl_cookie = cookie;
1139                         return (TRUE);
1140                 }
1141         }
1142
1143         /*
1144          * Either no callback exists for this program/version or one
1145          * of the callbacks rejected the connection. We just need to
1146          * clean up the delegated client creds, if any.
1147          */
1148         if (client->cl_creds) {
1149                 OM_uint32 min_ver;
1150                 gss_release_cred(&min_ver, &client->cl_creds);
1151         }
1152         return (result);
1153 }
1154
1155 static bool_t
1156 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1157 {
1158         u_int32_t offset;
1159         int word, bit;
1160         bool_t result;
1161
1162         sx_xlock(&client->cl_lock);
1163         if (seq <= client->cl_seqlast) {
1164                 /*
1165                  * The request sequence number is less than
1166                  * the largest we have seen so far. If it is
1167                  * outside the window or if we have seen a
1168                  * request with this sequence before, silently
1169                  * discard it.
1170                  */
1171                 offset = client->cl_seqlast - seq;
1172                 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1173                         result = FALSE;
1174                         goto out;
1175                 }
1176                 word = offset / 32;
1177                 bit = offset % 32;
1178                 if (client->cl_seqmask[word] & (1 << bit)) {
1179                         result = FALSE;
1180                         goto out;
1181                 }
1182         }
1183
1184         result = TRUE;
1185 out:
1186         sx_xunlock(&client->cl_lock);
1187         return (result);
1188 }
1189
1190 static void
1191 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1192 {
1193         int offset, i, word, bit;
1194         uint32_t carry, newcarry;
1195
1196         sx_xlock(&client->cl_lock);
1197         if (seq > client->cl_seqlast) {
1198                 /*
1199                  * This request has a sequence number greater
1200                  * than any we have seen so far. Advance the
1201                  * seq window and set bit zero of the window
1202                  * (which corresponds to the new sequence
1203                  * number)
1204                  */
1205                 offset = seq - client->cl_seqlast;
1206                 while (offset > 32) {
1207                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1208                              i > 0; i--) {
1209                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1210                         }
1211                         client->cl_seqmask[0] = 0;
1212                         offset -= 32;
1213                 }
1214                 carry = 0;
1215                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1216                         newcarry = client->cl_seqmask[i] >> (32 - offset);
1217                         client->cl_seqmask[i] =
1218                                 (client->cl_seqmask[i] << offset) | carry;
1219                         carry = newcarry;
1220                 }
1221                 client->cl_seqmask[0] |= 1;
1222                 client->cl_seqlast = seq;
1223         } else {
1224                 offset = client->cl_seqlast - seq;
1225                 word = offset / 32;
1226                 bit = offset % 32;
1227                 client->cl_seqmask[word] |= (1 << bit);
1228         }
1229         sx_xunlock(&client->cl_lock);
1230 }
1231
1232 enum auth_stat
1233 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1234
1235 {
1236         OM_uint32                min_stat;
1237         XDR                      xdrs;
1238         struct svc_rpc_gss_cookedcred *cc;
1239         struct svc_rpc_gss_client *client;
1240         struct rpc_gss_cred      gc;
1241         struct rpc_gss_init_res  gr;
1242         gss_qop_t                qop;
1243         int                      call_stat;
1244         enum auth_stat           result;
1245         
1246         rpc_gss_log_debug("in svc_rpc_gss()");
1247         
1248         /* Garbage collect old clients. */
1249         svc_rpc_gss_timeout_clients();
1250
1251         /* Initialize reply. */
1252         rqst->rq_verf = _null_auth;
1253
1254         /* Deserialize client credentials. */
1255         if (rqst->rq_cred.oa_length <= 0)
1256                 return (AUTH_BADCRED);
1257         
1258         memset(&gc, 0, sizeof(gc));
1259         
1260         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1261             rqst->rq_cred.oa_length, XDR_DECODE);
1262         
1263         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1264                 XDR_DESTROY(&xdrs);
1265                 return (AUTH_BADCRED);
1266         }
1267         XDR_DESTROY(&xdrs);
1268
1269         client = NULL;
1270
1271         /* Check version. */
1272         if (gc.gc_version != RPCSEC_GSS_VERSION) {
1273                 result = AUTH_BADCRED;
1274                 goto out;
1275         }
1276
1277         /* Check the proc and find the client (or create it) */
1278         if (gc.gc_proc == RPCSEC_GSS_INIT) {
1279                 if (gc.gc_handle.length != 0) {
1280                         result = AUTH_BADCRED;
1281                         goto out;
1282                 }
1283                 client = svc_rpc_gss_create_client();
1284                 refcount_acquire(&client->cl_refs);
1285         } else {
1286                 struct svc_rpc_gss_clientid *p;
1287                 if (gc.gc_handle.length != sizeof(*p)) {
1288                         result = AUTH_BADCRED;
1289                         goto out;
1290                 }
1291                 p = gc.gc_handle.value;
1292                 client = svc_rpc_gss_find_client(p);
1293                 if (!client) {
1294                         /*
1295                          * Can't find the client - we may have
1296                          * destroyed it - tell the other side to
1297                          * re-authenticate.
1298                          */
1299                         result = RPCSEC_GSS_CREDPROBLEM;
1300                         goto out;
1301                 }
1302         }
1303         cc = rqst->rq_clntcred;
1304         cc->cc_client = client;
1305         cc->cc_service = gc.gc_svc;
1306         cc->cc_seq = gc.gc_seq;
1307
1308         /*
1309          * The service and sequence number must be ignored for
1310          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1311          */
1312         if (gc.gc_proc != RPCSEC_GSS_INIT
1313             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1314                 /*
1315                  * Check for sequence number overflow.
1316                  */
1317                 if (gc.gc_seq >= MAXSEQ) {
1318                         result = RPCSEC_GSS_CTXPROBLEM;
1319                         goto out;
1320                 }
1321
1322                 /*
1323                  * Check for valid service.
1324                  */
1325                 if (gc.gc_svc != rpc_gss_svc_none &&
1326                     gc.gc_svc != rpc_gss_svc_integrity &&
1327                     gc.gc_svc != rpc_gss_svc_privacy) {
1328                         result = AUTH_BADCRED;
1329                         goto out;
1330                 }
1331         }
1332
1333         /* Handle RPCSEC_GSS control procedure. */
1334         switch (gc.gc_proc) {
1335
1336         case RPCSEC_GSS_INIT:
1337         case RPCSEC_GSS_CONTINUE_INIT:
1338                 if (rqst->rq_proc != NULLPROC) {
1339                         result = AUTH_REJECTEDCRED;
1340                         break;
1341                 }
1342
1343                 memset(&gr, 0, sizeof(gr));
1344                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1345                         result = AUTH_REJECTEDCRED;
1346                         break;
1347                 }
1348
1349                 if (gr.gr_major == GSS_S_COMPLETE) {
1350                         /*
1351                          * We borrow the space for the call verf to
1352                          * pack our reply verf.
1353                          */
1354                         rqst->rq_verf = msg->rm_call.cb_verf;
1355                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1356                                 result = AUTH_REJECTEDCRED;
1357                                 break;
1358                         }
1359                 } else {
1360                         rqst->rq_verf = _null_auth;
1361                 }
1362                 
1363                 call_stat = svc_sendreply(rqst,
1364                     (xdrproc_t) xdr_rpc_gss_init_res,
1365                     (caddr_t) &gr);
1366
1367                 gss_release_buffer(&min_stat, &gr.gr_token);
1368
1369                 if (!call_stat) {
1370                         result = AUTH_FAILED;
1371                         break;
1372                 }
1373
1374                 if (gr.gr_major == GSS_S_COMPLETE)
1375                         client->cl_state = CLIENT_ESTABLISHED;
1376
1377                 result = RPCSEC_GSS_NODISPATCH;
1378                 break;
1379                 
1380         case RPCSEC_GSS_DATA:
1381         case RPCSEC_GSS_DESTROY:
1382                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1383                         result = RPCSEC_GSS_NODISPATCH;
1384                         break;
1385                 }
1386
1387                 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1388                         result = RPCSEC_GSS_CREDPROBLEM;
1389                         break;
1390                 }
1391                 
1392                 /*
1393                  * We borrow the space for the call verf to pack our
1394                  * reply verf.
1395                  */
1396                 rqst->rq_verf = msg->rm_call.cb_verf;
1397                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1398                         result = RPCSEC_GSS_CTXPROBLEM;
1399                         break;
1400                 }
1401
1402                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1403
1404                 /*
1405                  * Change the SVCAUTH ops on the request to point at
1406                  * our own code so that we can unwrap the arguments
1407                  * and wrap the result. The caller will re-set this on
1408                  * every request to point to a set of null wrap/unwrap
1409                  * methods. Acquire an extra reference to the client
1410                  * which will be released by svc_rpc_gss_release()
1411                  * after the request has finished processing.
1412                  */
1413                 refcount_acquire(&client->cl_refs);
1414                 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1415                 rqst->rq_auth.svc_ah_private = cc;
1416
1417                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1418                         /*
1419                          * We might be ready to do a callback to the server to
1420                          * see if it wants to accept/reject the connection.
1421                          */
1422                         sx_xlock(&client->cl_lock);
1423                         if (!client->cl_done_callback) {
1424                                 client->cl_done_callback = TRUE;
1425                                 client->cl_qop = qop;
1426                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1427                                         client->cl_rawcred.mechanism, qop);
1428                                 if (!svc_rpc_gss_callback(client, rqst)) {
1429                                         result = AUTH_REJECTEDCRED;
1430                                         sx_xunlock(&client->cl_lock);
1431                                         break;
1432                                 }
1433                         }
1434                         sx_xunlock(&client->cl_lock);
1435
1436                         /*
1437                          * If the server has locked this client to a
1438                          * particular service+qop pair, enforce that
1439                          * restriction now.
1440                          */
1441                         if (client->cl_locked) {
1442                                 if (client->cl_rawcred.service != gc.gc_svc) {
1443                                         result = AUTH_FAILED;
1444                                         break;
1445                                 } else if (client->cl_qop != qop) {
1446                                         result = AUTH_BADVERF;
1447                                         break;
1448                                 }
1449                         }
1450
1451                         /*
1452                          * If the qop changed, look up the new qop
1453                          * name for rawcred.
1454                          */
1455                         if (client->cl_qop != qop) {
1456                                 client->cl_qop = qop;
1457                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1458                                         client->cl_rawcred.mechanism, qop);
1459                         }
1460
1461                         /*
1462                          * Make sure we use the right service value
1463                          * for unwrap/wrap.
1464                          */
1465                         if (client->cl_rawcred.service != gc.gc_svc) {
1466                                 client->cl_rawcred.service = gc.gc_svc;
1467                                 svc_rpc_gss_set_flavor(client);
1468                         }
1469
1470                         result = AUTH_OK;
1471                 } else {
1472                         if (rqst->rq_proc != NULLPROC) {
1473                                 result = AUTH_REJECTEDCRED;
1474                                 break;
1475                         }
1476
1477                         call_stat = svc_sendreply(rqst,
1478                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1479
1480                         if (!call_stat) {
1481                                 result = AUTH_FAILED;
1482                                 break;
1483                         }
1484
1485                         svc_rpc_gss_forget_client(client);
1486
1487                         result = RPCSEC_GSS_NODISPATCH;
1488                         break;
1489                 }
1490                 break;
1491
1492         default:
1493                 result = AUTH_BADCRED;
1494                 break;
1495         }
1496 out:
1497         if (client)
1498                 svc_rpc_gss_release_client(client);
1499
1500         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1501         return (result);
1502 }
1503
1504 static bool_t
1505 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1506 {
1507         struct svc_rpc_gss_cookedcred *cc;
1508         struct svc_rpc_gss_client *client;
1509         
1510         rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1511
1512         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1513         client = cc->cc_client;
1514         if (client->cl_state != CLIENT_ESTABLISHED
1515             || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1516                 return (TRUE);
1517         }
1518         
1519         return (xdr_rpc_gss_wrap_data(mp,
1520                 client->cl_ctx, client->cl_qop,
1521                 cc->cc_service, cc->cc_seq));
1522 }
1523
1524 static bool_t
1525 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1526 {
1527         struct svc_rpc_gss_cookedcred *cc;
1528         struct svc_rpc_gss_client *client;
1529
1530         rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1531         
1532         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1533         client = cc->cc_client;
1534         if (client->cl_state != CLIENT_ESTABLISHED
1535             || cc->cc_service == rpc_gss_svc_none) {
1536                 return (TRUE);
1537         }
1538
1539         return (xdr_rpc_gss_unwrap_data(mp,
1540                 client->cl_ctx, client->cl_qop,
1541                 cc->cc_service, cc->cc_seq));
1542 }
1543
1544 static void
1545 svc_rpc_gss_release(SVCAUTH *auth)
1546 {
1547         struct svc_rpc_gss_cookedcred *cc;
1548         struct svc_rpc_gss_client *client;
1549
1550         rpc_gss_log_debug("in svc_rpc_gss_release()");
1551
1552         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1553         client = cc->cc_client;
1554         svc_rpc_gss_release_client(client);
1555 }