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