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