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