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