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