]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
Merge ACPICA 20191018.
[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
572         /*
573          * Set the initial value of cl_refs to two.  One for the caller
574          * and the other to hold onto the client structure until it expires.
575          */
576         refcount_init(&client->cl_refs, 2);
577         sx_init(&client->cl_lock, "GSS-client");
578         getcredhostid(curthread->td_ucred, &hostid);
579         client->cl_id.ci_hostid = hostid;
580         getboottime(&boottime);
581         client->cl_id.ci_boottime = boottime.tv_sec;
582         client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
583
584         /*
585          * Start the client off with a short expiration time. We will
586          * try to get a saner value from the client creds later.
587          */
588         client->cl_state = CLIENT_NEW;
589         client->cl_locked = FALSE;
590         client->cl_expiration = time_uptime + 5*60;
591
592         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
593         sx_xlock(&svc_rpc_gss_lock);
594         TAILQ_INSERT_HEAD(list, client, cl_link);
595         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
596         svc_rpc_gss_client_count++;
597         sx_xunlock(&svc_rpc_gss_lock);
598         return (client);
599 }
600
601 static void
602 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
603 {
604         OM_uint32 min_stat;
605
606         rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
607
608         if (client->cl_ctx)
609                 gss_delete_sec_context(&min_stat,
610                     &client->cl_ctx, GSS_C_NO_BUFFER);
611
612         if (client->cl_cname)
613                 gss_release_name(&min_stat, &client->cl_cname);
614
615         if (client->cl_rawcred.client_principal)
616                 mem_free(client->cl_rawcred.client_principal,
617                     sizeof(*client->cl_rawcred.client_principal)
618                     + client->cl_rawcred.client_principal->len);
619
620         if (client->cl_cred)
621                 crfree(client->cl_cred);
622
623         sx_destroy(&client->cl_lock);
624         mem_free(client, sizeof(*client));
625 }
626
627 /*
628  * Drop a reference to a client and free it if that was the last reference.
629  */
630 static void
631 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
632 {
633
634         if (!refcount_release(&client->cl_refs))
635                 return;
636         svc_rpc_gss_destroy_client(client);
637 }
638
639 /*
640  * Remove a client from our global lists.
641  * Must be called with svc_rpc_gss_lock held.
642  */
643 static void
644 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
645 {
646         struct svc_rpc_gss_client_list *list;
647
648         sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
649         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
650         TAILQ_REMOVE(list, client, cl_link);
651         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
652         svc_rpc_gss_client_count--;
653 }
654
655 /*
656  * Remove a client from our global lists and free it if we can.
657  */
658 static void
659 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
660 {
661         struct svc_rpc_gss_client_list *list;
662         struct svc_rpc_gss_client *tclient;
663
664         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
665         sx_xlock(&svc_rpc_gss_lock);
666         TAILQ_FOREACH(tclient, list, cl_link) {
667                 /*
668                  * Make sure this client has not already been removed
669                  * from the lists by svc_rpc_gss_forget_client() or
670                  * svc_rpc_gss_forget_client_locked().
671                  */
672                 if (client == tclient) {
673                         svc_rpc_gss_forget_client_locked(client);
674                         sx_xunlock(&svc_rpc_gss_lock);
675                         svc_rpc_gss_release_client(client);
676                         return;
677                 }
678         }
679         sx_xunlock(&svc_rpc_gss_lock);
680 }
681
682 static void
683 svc_rpc_gss_timeout_clients(void)
684 {
685         struct svc_rpc_gss_client *client;
686         time_t now = time_uptime;
687
688         rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
689
690         /*
691          * First enforce the max client limit. We keep
692          * svc_rpc_gss_clients in LRU order.
693          */
694         sx_xlock(&svc_rpc_gss_lock);
695         client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
696         while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
697                 svc_rpc_gss_forget_client_locked(client);
698                 sx_xunlock(&svc_rpc_gss_lock);
699                 svc_rpc_gss_release_client(client);
700                 sx_xlock(&svc_rpc_gss_lock);
701                 client = TAILQ_LAST(&svc_rpc_gss_clients,
702                     svc_rpc_gss_client_list);
703         }
704 again:
705         TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
706                 if (client->cl_state == CLIENT_STALE
707                     || now > client->cl_expiration) {
708                         svc_rpc_gss_forget_client_locked(client);
709                         sx_xunlock(&svc_rpc_gss_lock);
710                         rpc_gss_log_debug("expiring client %p", client);
711                         svc_rpc_gss_release_client(client);
712                         sx_xlock(&svc_rpc_gss_lock);
713                         goto again;
714                 }
715         }
716         sx_xunlock(&svc_rpc_gss_lock);
717 }
718
719 #ifdef DEBUG
720 /*
721  * OID<->string routines.  These are uuuuugly.
722  */
723 static OM_uint32
724 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
725 {
726         char            numstr[128];
727         unsigned long   number;
728         int             numshift;
729         size_t          string_length;
730         size_t          i;
731         unsigned char   *cp;
732         char            *bp;
733
734         /* Decoded according to krb5/gssapi_krb5.c */
735
736         /* First determine the size of the string */
737         string_length = 0;
738         number = 0;
739         numshift = 0;
740         cp = (unsigned char *) oid->elements;
741         number = (unsigned long) cp[0];
742         sprintf(numstr, "%ld ", number/40);
743         string_length += strlen(numstr);
744         sprintf(numstr, "%ld ", number%40);
745         string_length += strlen(numstr);
746         for (i=1; i<oid->length; i++) {
747                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
748                         number = (number << 7) | (cp[i] & 0x7f);
749                         numshift += 7;
750                 }
751                 else {
752                         *minor_status = 0;
753                         return(GSS_S_FAILURE);
754                 }
755                 if ((cp[i] & 0x80) == 0) {
756                         sprintf(numstr, "%ld ", number);
757                         string_length += strlen(numstr);
758                         number = 0;
759                         numshift = 0;
760                 }
761         }
762         /*
763          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
764          * here for "{ " and "}\0".
765          */
766         string_length += 4;
767         if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
768                 strcpy(bp, "{ ");
769                 number = (unsigned long) cp[0];
770                 sprintf(numstr, "%ld ", number/40);
771                 strcat(bp, numstr);
772                 sprintf(numstr, "%ld ", number%40);
773                 strcat(bp, numstr);
774                 number = 0;
775                 cp = (unsigned char *) oid->elements;
776                 for (i=1; i<oid->length; i++) {
777                         number = (number << 7) | (cp[i] & 0x7f);
778                         if ((cp[i] & 0x80) == 0) {
779                                 sprintf(numstr, "%ld ", number);
780                                 strcat(bp, numstr);
781                                 number = 0;
782                         }
783                 }
784                 strcat(bp, "}");
785                 oid_str->length = strlen(bp)+1;
786                 oid_str->value = (void *) bp;
787                 *minor_status = 0;
788                 return(GSS_S_COMPLETE);
789         }
790         *minor_status = 0;
791         return(GSS_S_FAILURE);
792 }
793 #endif
794
795 static void
796 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
797     const gss_name_t name)
798 {
799         OM_uint32               maj_stat, min_stat;
800         rpc_gss_ucred_t         *uc = &client->cl_ucred;
801         int                     numgroups;
802
803         uc->uid = 65534;
804         uc->gid = 65534;
805         uc->gidlist = client->cl_gid_storage;
806
807         numgroups = NGROUPS;
808         maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
809             &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
810         if (GSS_ERROR(maj_stat))
811                 uc->gidlen = 0;
812         else
813                 uc->gidlen = numgroups;
814 }
815
816 static void
817 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
818 {
819         static gss_OID_desc krb5_mech_oid =
820                 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
821
822         /*
823          * Attempt to translate mech type and service into a
824          * 'pseudo flavor'. Hardwire in krb5 support for now.
825          */
826         if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
827                 switch (client->cl_rawcred.service) {
828                 case rpc_gss_svc_default:
829                 case rpc_gss_svc_none:
830                         client->cl_rpcflavor = RPCSEC_GSS_KRB5;
831                         break;
832                 case rpc_gss_svc_integrity:
833                         client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
834                         break;
835                 case rpc_gss_svc_privacy:
836                         client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
837                         break;
838                 }
839         } else {
840                 client->cl_rpcflavor = RPCSEC_GSS;
841         }
842 }
843
844 static bool_t
845 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
846                                struct svc_req *rqst,
847                                struct rpc_gss_init_res *gr,
848                                struct rpc_gss_cred *gc)
849 {
850         gss_buffer_desc         recv_tok;
851         gss_OID                 mech;
852         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
853         OM_uint32               cred_lifetime;
854         struct svc_rpc_gss_svc_name *sname;
855
856         rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
857         
858         /* Deserialize arguments. */
859         memset(&recv_tok, 0, sizeof(recv_tok));
860         
861         if (!svc_getargs(rqst,
862                 (xdrproc_t) xdr_gss_buffer_desc,
863                 (caddr_t) &recv_tok)) {
864                 client->cl_state = CLIENT_STALE;
865                 return (FALSE);
866         }
867
868         /*
869          * First time round, try all the server names we have until
870          * one matches. Afterwards, stick with that one.
871          */
872         sx_xlock(&svc_rpc_gss_lock);
873         if (!client->cl_sname) {
874                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
875                         if (sname->sn_program == rqst->rq_prog
876                             && sname->sn_version == rqst->rq_vers) {
877                         retry:
878                                 gr->gr_major = gss_accept_sec_context(
879                                         &gr->gr_minor,
880                                         &client->cl_ctx,
881                                         sname->sn_cred,
882                                         &recv_tok,
883                                         GSS_C_NO_CHANNEL_BINDINGS,
884                                         &client->cl_cname,
885                                         &mech,
886                                         &gr->gr_token,
887                                         &ret_flags,
888                                         &cred_lifetime,
889                                         &client->cl_creds);
890                                 if (gr->gr_major == 
891                                     GSS_S_CREDENTIALS_EXPIRED) {
892                                         /*
893                                          * Either our creds really did
894                                          * expire or gssd was
895                                          * restarted.
896                                          */
897                                         if (rpc_gss_acquire_svc_cred(sname))
898                                                 goto retry;
899                                 }
900                                 client->cl_sname = sname;
901                                 break;
902                         }
903                 }
904                 if (!sname) {
905                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
906                             (char *) &recv_tok);
907                         sx_xunlock(&svc_rpc_gss_lock);
908                         return (FALSE);
909                 }
910         } else {
911                 gr->gr_major = gss_accept_sec_context(
912                         &gr->gr_minor,
913                         &client->cl_ctx,
914                         client->cl_sname->sn_cred,
915                         &recv_tok,
916                         GSS_C_NO_CHANNEL_BINDINGS,
917                         &client->cl_cname,
918                         &mech,
919                         &gr->gr_token,
920                         &ret_flags,
921                         &cred_lifetime,
922                         NULL);
923         }
924         sx_xunlock(&svc_rpc_gss_lock);
925         
926         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
927
928         /*
929          * If we get an error from gss_accept_sec_context, send the
930          * reply anyway so that the client gets a chance to see what
931          * is wrong.
932          */
933         if (gr->gr_major != GSS_S_COMPLETE &&
934             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
935                 rpc_gss_log_status("accept_sec_context", client->cl_mech,
936                     gr->gr_major, gr->gr_minor);
937                 client->cl_state = CLIENT_STALE;
938                 return (TRUE);
939         }
940
941         gr->gr_handle.value = &client->cl_id;
942         gr->gr_handle.length = sizeof(client->cl_id);
943         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
944         
945         /* Save client info. */
946         client->cl_mech = mech;
947         client->cl_qop = GSS_C_QOP_DEFAULT;
948         client->cl_done_callback = FALSE;
949
950         if (gr->gr_major == GSS_S_COMPLETE) {
951                 gss_buffer_desc export_name;
952
953                 /*
954                  * Change client expiration time to be near when the
955                  * client creds expire (or 24 hours if we can't figure
956                  * that out).
957                  */
958                 if (cred_lifetime == GSS_C_INDEFINITE)
959                         cred_lifetime = time_uptime + 24*60*60;
960
961                 client->cl_expiration = time_uptime + cred_lifetime;
962
963                 /*
964                  * Fill in cred details in the rawcred structure.
965                  */
966                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
967                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
968                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
969                     &export_name);
970                 if (maj_stat != GSS_S_COMPLETE) {
971                         rpc_gss_log_status("gss_export_name", client->cl_mech,
972                             maj_stat, min_stat);
973                         return (FALSE);
974                 }
975                 client->cl_rawcred.client_principal =
976                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
977                             + export_name.length);
978                 client->cl_rawcred.client_principal->len = export_name.length;
979                 memcpy(client->cl_rawcred.client_principal->name,
980                     export_name.value, export_name.length);
981                 gss_release_buffer(&min_stat, &export_name);
982                 client->cl_rawcred.svc_principal =
983                         client->cl_sname->sn_principal;
984                 client->cl_rawcred.service = gc->gc_svc;
985
986                 /*
987                  * Use gss_pname_to_uid to map to unix creds. For
988                  * kerberos5, this uses krb5_aname_to_localname.
989                  */
990                 svc_rpc_gss_build_ucred(client, client->cl_cname);
991                 svc_rpc_gss_set_flavor(client);
992                 gss_release_name(&min_stat, &client->cl_cname);
993
994 #ifdef DEBUG
995                 {
996                         gss_buffer_desc mechname;
997
998                         gss_oid_to_str(&min_stat, mech, &mechname);
999                         
1000                         rpc_gss_log_debug("accepted context for %s with "
1001                             "<mech %.*s, qop %d, svc %d>",
1002                             client->cl_rawcred.client_principal->name,
1003                             mechname.length, (char *)mechname.value,
1004                             client->cl_qop, client->cl_rawcred.service);
1005
1006                         gss_release_buffer(&min_stat, &mechname);
1007                 }
1008 #endif /* DEBUG */
1009         }
1010         return (TRUE);
1011 }
1012
1013 static bool_t
1014 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1015     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1016 {
1017         struct opaque_auth      *oa;
1018         gss_buffer_desc          rpcbuf, checksum;
1019         OM_uint32                maj_stat, min_stat;
1020         gss_qop_t                qop_state;
1021         int32_t                  rpchdr[128 / sizeof(int32_t)];
1022         int32_t                 *buf;
1023
1024         rpc_gss_log_debug("in svc_rpc_gss_validate()");
1025         
1026         memset(rpchdr, 0, sizeof(rpchdr));
1027
1028         /* Reconstruct RPC header for signing (from xdr_callmsg). */
1029         buf = rpchdr;
1030         IXDR_PUT_LONG(buf, msg->rm_xid);
1031         IXDR_PUT_ENUM(buf, msg->rm_direction);
1032         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1033         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1034         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1035         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1036         oa = &msg->rm_call.cb_cred;
1037         IXDR_PUT_ENUM(buf, oa->oa_flavor);
1038         IXDR_PUT_LONG(buf, oa->oa_length);
1039         if (oa->oa_length) {
1040                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1041                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1042         }
1043         rpcbuf.value = rpchdr;
1044         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1045
1046         checksum.value = msg->rm_call.cb_verf.oa_base;
1047         checksum.length = msg->rm_call.cb_verf.oa_length;
1048         
1049         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1050                                   &qop_state);
1051         
1052         if (maj_stat != GSS_S_COMPLETE) {
1053                 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1054                     maj_stat, min_stat);
1055                 /*
1056                  * A bug in some versions of the Linux client generates a
1057                  * Destroy operation with a bogus encrypted checksum. Deleting
1058                  * the credential handle for that case causes the mount to fail.
1059                  * Since the checksum is bogus (gss_verify_mic() failed), it
1060                  * doesn't make sense to destroy the handle and not doing so
1061                  * fixes the Linux mount.
1062                  */
1063                 if (gcproc != RPCSEC_GSS_DESTROY)
1064                         client->cl_state = CLIENT_STALE;
1065                 return (FALSE);
1066         }
1067
1068         *qop = qop_state;
1069         return (TRUE);
1070 }
1071
1072 static bool_t
1073 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1074     struct svc_req *rqst, u_int seq)
1075 {
1076         gss_buffer_desc         signbuf;
1077         gss_buffer_desc         mic;
1078         OM_uint32               maj_stat, min_stat;
1079         uint32_t                nseq;       
1080
1081         rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1082
1083         nseq = htonl(seq);
1084         signbuf.value = &nseq;
1085         signbuf.length = sizeof(nseq);
1086
1087         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1088             &signbuf, &mic);
1089
1090         if (maj_stat != GSS_S_COMPLETE) {
1091                 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1092                 client->cl_state = CLIENT_STALE;
1093                 return (FALSE);
1094         }
1095
1096         KASSERT(mic.length <= MAX_AUTH_BYTES,
1097             ("MIC too large for RPCSEC_GSS"));
1098
1099         rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1100         rqst->rq_verf.oa_length = mic.length;
1101         bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1102
1103         gss_release_buffer(&min_stat, &mic);
1104         
1105         return (TRUE);
1106 }
1107
1108 static bool_t
1109 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1110 {
1111         struct svc_rpc_gss_callback *scb;
1112         rpc_gss_lock_t  lock;
1113         void            *cookie;
1114         bool_t          cb_res;
1115         bool_t          result;
1116
1117         /*
1118          * See if we have a callback for this guy.
1119          */
1120         result = TRUE;
1121         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1122                 if (scb->cb_callback.program == rqst->rq_prog
1123                     && scb->cb_callback.version == rqst->rq_vers) {
1124                         /*
1125                          * This one matches. Call the callback and see
1126                          * if it wants to veto or something.
1127                          */
1128                         lock.locked = FALSE;
1129                         lock.raw_cred = &client->cl_rawcred;
1130                         cb_res = scb->cb_callback.callback(rqst,
1131                             client->cl_creds,
1132                             client->cl_ctx,
1133                             &lock,
1134                             &cookie);
1135
1136                         if (!cb_res) {
1137                                 client->cl_state = CLIENT_STALE;
1138                                 result = FALSE;
1139                                 break;
1140                         }
1141
1142                         /*
1143                          * The callback accepted the connection - it
1144                          * is responsible for freeing client->cl_creds
1145                          * now.
1146                          */
1147                         client->cl_creds = GSS_C_NO_CREDENTIAL;
1148                         client->cl_locked = lock.locked;
1149                         client->cl_cookie = cookie;
1150                         return (TRUE);
1151                 }
1152         }
1153
1154         /*
1155          * Either no callback exists for this program/version or one
1156          * of the callbacks rejected the connection. We just need to
1157          * clean up the delegated client creds, if any.
1158          */
1159         if (client->cl_creds) {
1160                 OM_uint32 min_ver;
1161                 gss_release_cred(&min_ver, &client->cl_creds);
1162         }
1163         return (result);
1164 }
1165
1166 static bool_t
1167 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1168 {
1169         u_int32_t offset;
1170         int word, bit;
1171         bool_t result;
1172
1173         sx_xlock(&client->cl_lock);
1174         if (seq <= client->cl_seqlast) {
1175                 /*
1176                  * The request sequence number is less than
1177                  * the largest we have seen so far. If it is
1178                  * outside the window or if we have seen a
1179                  * request with this sequence before, silently
1180                  * discard it.
1181                  */
1182                 offset = client->cl_seqlast - seq;
1183                 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1184                         result = FALSE;
1185                         goto out;
1186                 }
1187                 word = offset / 32;
1188                 bit = offset % 32;
1189                 if (client->cl_seqmask[word] & (1 << bit)) {
1190                         result = FALSE;
1191                         goto out;
1192                 }
1193         }
1194
1195         result = TRUE;
1196 out:
1197         sx_xunlock(&client->cl_lock);
1198         return (result);
1199 }
1200
1201 static void
1202 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1203 {
1204         int offset, i, word, bit;
1205         uint32_t carry, newcarry;
1206
1207         sx_xlock(&client->cl_lock);
1208         if (seq > client->cl_seqlast) {
1209                 /*
1210                  * This request has a sequence number greater
1211                  * than any we have seen so far. Advance the
1212                  * seq window and set bit zero of the window
1213                  * (which corresponds to the new sequence
1214                  * number)
1215                  */
1216                 offset = seq - client->cl_seqlast;
1217                 while (offset > 32) {
1218                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1219                              i > 0; i--) {
1220                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1221                         }
1222                         client->cl_seqmask[0] = 0;
1223                         offset -= 32;
1224                 }
1225                 carry = 0;
1226                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1227                         newcarry = client->cl_seqmask[i] >> (32 - offset);
1228                         client->cl_seqmask[i] =
1229                                 (client->cl_seqmask[i] << offset) | carry;
1230                         carry = newcarry;
1231                 }
1232                 client->cl_seqmask[0] |= 1;
1233                 client->cl_seqlast = seq;
1234         } else {
1235                 offset = client->cl_seqlast - seq;
1236                 word = offset / 32;
1237                 bit = offset % 32;
1238                 client->cl_seqmask[word] |= (1 << bit);
1239         }
1240         sx_xunlock(&client->cl_lock);
1241 }
1242
1243 enum auth_stat
1244 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1245
1246 {
1247         OM_uint32                min_stat;
1248         XDR                      xdrs;
1249         struct svc_rpc_gss_cookedcred *cc;
1250         struct svc_rpc_gss_client *client;
1251         struct rpc_gss_cred      gc;
1252         struct rpc_gss_init_res  gr;
1253         gss_qop_t                qop;
1254         int                      call_stat;
1255         enum auth_stat           result;
1256         
1257         rpc_gss_log_debug("in svc_rpc_gss()");
1258         
1259         /* Garbage collect old clients. */
1260         svc_rpc_gss_timeout_clients();
1261
1262         /* Initialize reply. */
1263         rqst->rq_verf = _null_auth;
1264
1265         /* Deserialize client credentials. */
1266         if (rqst->rq_cred.oa_length <= 0)
1267                 return (AUTH_BADCRED);
1268         
1269         memset(&gc, 0, sizeof(gc));
1270         
1271         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1272             rqst->rq_cred.oa_length, XDR_DECODE);
1273         
1274         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1275                 XDR_DESTROY(&xdrs);
1276                 return (AUTH_BADCRED);
1277         }
1278         XDR_DESTROY(&xdrs);
1279
1280         client = NULL;
1281
1282         /* Check version. */
1283         if (gc.gc_version != RPCSEC_GSS_VERSION) {
1284                 result = AUTH_BADCRED;
1285                 goto out;
1286         }
1287
1288         /* Check the proc and find the client (or create it) */
1289         if (gc.gc_proc == RPCSEC_GSS_INIT) {
1290                 if (gc.gc_handle.length != 0) {
1291                         result = AUTH_BADCRED;
1292                         goto out;
1293                 }
1294                 client = svc_rpc_gss_create_client();
1295         } else {
1296                 struct svc_rpc_gss_clientid *p;
1297                 if (gc.gc_handle.length != sizeof(*p)) {
1298                         result = AUTH_BADCRED;
1299                         goto out;
1300                 }
1301                 p = gc.gc_handle.value;
1302                 client = svc_rpc_gss_find_client(p);
1303                 if (!client) {
1304                         /*
1305                          * Can't find the client - we may have
1306                          * destroyed it - tell the other side to
1307                          * re-authenticate.
1308                          */
1309                         result = RPCSEC_GSS_CREDPROBLEM;
1310                         goto out;
1311                 }
1312         }
1313         cc = rqst->rq_clntcred;
1314         cc->cc_client = client;
1315         cc->cc_service = gc.gc_svc;
1316         cc->cc_seq = gc.gc_seq;
1317
1318         /*
1319          * The service and sequence number must be ignored for
1320          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1321          */
1322         if (gc.gc_proc != RPCSEC_GSS_INIT
1323             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1324                 /*
1325                  * Check for sequence number overflow.
1326                  */
1327                 if (gc.gc_seq >= MAXSEQ) {
1328                         result = RPCSEC_GSS_CTXPROBLEM;
1329                         goto out;
1330                 }
1331
1332                 /*
1333                  * Check for valid service.
1334                  */
1335                 if (gc.gc_svc != rpc_gss_svc_none &&
1336                     gc.gc_svc != rpc_gss_svc_integrity &&
1337                     gc.gc_svc != rpc_gss_svc_privacy) {
1338                         result = AUTH_BADCRED;
1339                         goto out;
1340                 }
1341         }
1342
1343         /* Handle RPCSEC_GSS control procedure. */
1344         switch (gc.gc_proc) {
1345
1346         case RPCSEC_GSS_INIT:
1347         case RPCSEC_GSS_CONTINUE_INIT:
1348                 if (rqst->rq_proc != NULLPROC) {
1349                         result = AUTH_REJECTEDCRED;
1350                         break;
1351                 }
1352
1353                 memset(&gr, 0, sizeof(gr));
1354                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1355                         result = AUTH_REJECTEDCRED;
1356                         break;
1357                 }
1358
1359                 if (gr.gr_major == GSS_S_COMPLETE) {
1360                         /*
1361                          * We borrow the space for the call verf to
1362                          * pack our reply verf.
1363                          */
1364                         rqst->rq_verf = msg->rm_call.cb_verf;
1365                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1366                                 result = AUTH_REJECTEDCRED;
1367                                 break;
1368                         }
1369                 } else {
1370                         rqst->rq_verf = _null_auth;
1371                 }
1372                 
1373                 call_stat = svc_sendreply(rqst,
1374                     (xdrproc_t) xdr_rpc_gss_init_res,
1375                     (caddr_t) &gr);
1376
1377                 gss_release_buffer(&min_stat, &gr.gr_token);
1378
1379                 if (!call_stat) {
1380                         result = AUTH_FAILED;
1381                         break;
1382                 }
1383
1384                 if (gr.gr_major == GSS_S_COMPLETE)
1385                         client->cl_state = CLIENT_ESTABLISHED;
1386
1387                 result = RPCSEC_GSS_NODISPATCH;
1388                 break;
1389                 
1390         case RPCSEC_GSS_DATA:
1391         case RPCSEC_GSS_DESTROY:
1392                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1393                         result = RPCSEC_GSS_NODISPATCH;
1394                         break;
1395                 }
1396
1397                 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1398                         result = RPCSEC_GSS_CREDPROBLEM;
1399                         break;
1400                 }
1401                 
1402                 /*
1403                  * We borrow the space for the call verf to pack our
1404                  * reply verf.
1405                  */
1406                 rqst->rq_verf = msg->rm_call.cb_verf;
1407                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1408                         result = RPCSEC_GSS_CTXPROBLEM;
1409                         break;
1410                 }
1411
1412                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1413
1414                 /*
1415                  * Change the SVCAUTH ops on the request to point at
1416                  * our own code so that we can unwrap the arguments
1417                  * and wrap the result. The caller will re-set this on
1418                  * every request to point to a set of null wrap/unwrap
1419                  * methods. Acquire an extra reference to the client
1420                  * which will be released by svc_rpc_gss_release()
1421                  * after the request has finished processing.
1422                  */
1423                 refcount_acquire(&client->cl_refs);
1424                 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1425                 rqst->rq_auth.svc_ah_private = cc;
1426
1427                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1428                         /*
1429                          * We might be ready to do a callback to the server to
1430                          * see if it wants to accept/reject the connection.
1431                          */
1432                         sx_xlock(&client->cl_lock);
1433                         if (!client->cl_done_callback) {
1434                                 client->cl_done_callback = TRUE;
1435                                 client->cl_qop = qop;
1436                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1437                                         client->cl_rawcred.mechanism, qop);
1438                                 if (!svc_rpc_gss_callback(client, rqst)) {
1439                                         result = AUTH_REJECTEDCRED;
1440                                         sx_xunlock(&client->cl_lock);
1441                                         break;
1442                                 }
1443                         }
1444                         sx_xunlock(&client->cl_lock);
1445
1446                         /*
1447                          * If the server has locked this client to a
1448                          * particular service+qop pair, enforce that
1449                          * restriction now.
1450                          */
1451                         if (client->cl_locked) {
1452                                 if (client->cl_rawcred.service != gc.gc_svc) {
1453                                         result = AUTH_FAILED;
1454                                         break;
1455                                 } else if (client->cl_qop != qop) {
1456                                         result = AUTH_BADVERF;
1457                                         break;
1458                                 }
1459                         }
1460
1461                         /*
1462                          * If the qop changed, look up the new qop
1463                          * name for rawcred.
1464                          */
1465                         if (client->cl_qop != qop) {
1466                                 client->cl_qop = qop;
1467                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1468                                         client->cl_rawcred.mechanism, qop);
1469                         }
1470
1471                         /*
1472                          * Make sure we use the right service value
1473                          * for unwrap/wrap.
1474                          */
1475                         if (client->cl_rawcred.service != gc.gc_svc) {
1476                                 client->cl_rawcred.service = gc.gc_svc;
1477                                 svc_rpc_gss_set_flavor(client);
1478                         }
1479
1480                         result = AUTH_OK;
1481                 } else {
1482                         if (rqst->rq_proc != NULLPROC) {
1483                                 result = AUTH_REJECTEDCRED;
1484                                 break;
1485                         }
1486
1487                         call_stat = svc_sendreply(rqst,
1488                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1489
1490                         if (!call_stat) {
1491                                 result = AUTH_FAILED;
1492                                 break;
1493                         }
1494
1495                         svc_rpc_gss_forget_client(client);
1496
1497                         result = RPCSEC_GSS_NODISPATCH;
1498                         break;
1499                 }
1500                 break;
1501
1502         default:
1503                 result = AUTH_BADCRED;
1504                 break;
1505         }
1506 out:
1507         if (client)
1508                 svc_rpc_gss_release_client(client);
1509
1510         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1511         return (result);
1512 }
1513
1514 static bool_t
1515 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1516 {
1517         struct svc_rpc_gss_cookedcred *cc;
1518         struct svc_rpc_gss_client *client;
1519         
1520         rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1521
1522         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1523         client = cc->cc_client;
1524         if (client->cl_state != CLIENT_ESTABLISHED
1525             || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1526                 return (TRUE);
1527         }
1528         
1529         return (xdr_rpc_gss_wrap_data(mp,
1530                 client->cl_ctx, client->cl_qop,
1531                 cc->cc_service, cc->cc_seq));
1532 }
1533
1534 static bool_t
1535 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1536 {
1537         struct svc_rpc_gss_cookedcred *cc;
1538         struct svc_rpc_gss_client *client;
1539
1540         rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1541         
1542         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1543         client = cc->cc_client;
1544         if (client->cl_state != CLIENT_ESTABLISHED
1545             || cc->cc_service == rpc_gss_svc_none) {
1546                 return (TRUE);
1547         }
1548
1549         return (xdr_rpc_gss_unwrap_data(mp,
1550                 client->cl_ctx, client->cl_qop,
1551                 cc->cc_service, cc->cc_seq));
1552 }
1553
1554 static void
1555 svc_rpc_gss_release(SVCAUTH *auth)
1556 {
1557         struct svc_rpc_gss_cookedcred *cc;
1558         struct svc_rpc_gss_client *client;
1559
1560         rpc_gss_log_debug("in svc_rpc_gss_release()");
1561
1562         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1563         client = cc->cc_client;
1564         svc_rpc_gss_release_client(client);
1565 }