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