]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
MFC: r345818, r345828
[FreeBSD/stable/10.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) + 1;
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
547         /*
548          * Set the initial value of cl_refs to two.  One for the caller
549          * and the other to hold onto the client structure until it expires.
550          */
551         refcount_init(&client->cl_refs, 2);
552         sx_init(&client->cl_lock, "GSS-client");
553         getcredhostid(curthread->td_ucred, &hostid);
554         client->cl_id.ci_hostid = hostid;
555         client->cl_id.ci_boottime = boottime.tv_sec;
556         client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
557
558         /*
559          * Start the client off with a short expiration time. We will
560          * try to get a saner value from the client creds later.
561          */
562         client->cl_state = CLIENT_NEW;
563         client->cl_locked = FALSE;
564         client->cl_expiration = time_uptime + 5*60;
565
566         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
567         sx_xlock(&svc_rpc_gss_lock);
568         TAILQ_INSERT_HEAD(list, client, cl_link);
569         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
570         svc_rpc_gss_client_count++;
571         sx_xunlock(&svc_rpc_gss_lock);
572         return (client);
573 }
574
575 static void
576 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
577 {
578         OM_uint32 min_stat;
579
580         rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
581
582         if (client->cl_ctx)
583                 gss_delete_sec_context(&min_stat,
584                     &client->cl_ctx, GSS_C_NO_BUFFER);
585
586         if (client->cl_cname)
587                 gss_release_name(&min_stat, &client->cl_cname);
588
589         if (client->cl_rawcred.client_principal)
590                 mem_free(client->cl_rawcred.client_principal,
591                     sizeof(*client->cl_rawcred.client_principal)
592                     + client->cl_rawcred.client_principal->len);
593
594         if (client->cl_cred)
595                 crfree(client->cl_cred);
596
597         sx_destroy(&client->cl_lock);
598         mem_free(client, sizeof(*client));
599 }
600
601 /*
602  * Drop a reference to a client and free it if that was the last reference.
603  */
604 static void
605 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
606 {
607
608         if (!refcount_release(&client->cl_refs))
609                 return;
610         svc_rpc_gss_destroy_client(client);
611 }
612
613 /*
614  * Remove a client from our global lists.
615  * Must be called with svc_rpc_gss_lock held.
616  */
617 static void
618 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
619 {
620         struct svc_rpc_gss_client_list *list;
621
622         sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
623         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
624         TAILQ_REMOVE(list, client, cl_link);
625         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
626         svc_rpc_gss_client_count--;
627 }
628
629 /*
630  * Remove a client from our global lists and free it if we can.
631  */
632 static void
633 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
634 {
635         struct svc_rpc_gss_client_list *list;
636         struct svc_rpc_gss_client *tclient;
637
638         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
639         sx_xlock(&svc_rpc_gss_lock);
640         TAILQ_FOREACH(tclient, list, cl_link) {
641                 /*
642                  * Make sure this client has not already been removed
643                  * from the lists by svc_rpc_gss_forget_client() or
644                  * svc_rpc_gss_forget_client_locked().
645                  */
646                 if (client == tclient) {
647                         svc_rpc_gss_forget_client_locked(client);
648                         sx_xunlock(&svc_rpc_gss_lock);
649                         svc_rpc_gss_release_client(client);
650                         return;
651                 }
652         }
653         sx_xunlock(&svc_rpc_gss_lock);
654 }
655
656 static void
657 svc_rpc_gss_timeout_clients(void)
658 {
659         struct svc_rpc_gss_client *client;
660         time_t now = time_uptime;
661
662         rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
663
664         /*
665          * First enforce the max client limit. We keep
666          * svc_rpc_gss_clients in LRU order.
667          */
668         sx_xlock(&svc_rpc_gss_lock);
669         client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
670         while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) {
671                 svc_rpc_gss_forget_client_locked(client);
672                 sx_xunlock(&svc_rpc_gss_lock);
673                 svc_rpc_gss_release_client(client);
674                 sx_xlock(&svc_rpc_gss_lock);
675                 client = TAILQ_LAST(&svc_rpc_gss_clients,
676                     svc_rpc_gss_client_list);
677         }
678 again:
679         TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
680                 if (client->cl_state == CLIENT_STALE
681                     || now > client->cl_expiration) {
682                         svc_rpc_gss_forget_client_locked(client);
683                         sx_xunlock(&svc_rpc_gss_lock);
684                         rpc_gss_log_debug("expiring client %p", client);
685                         svc_rpc_gss_release_client(client);
686                         sx_xlock(&svc_rpc_gss_lock);
687                         goto again;
688                 }
689         }
690         sx_xunlock(&svc_rpc_gss_lock);
691 }
692
693 #ifdef DEBUG
694 /*
695  * OID<->string routines.  These are uuuuugly.
696  */
697 static OM_uint32
698 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
699 {
700         char            numstr[128];
701         unsigned long   number;
702         int             numshift;
703         size_t          string_length;
704         size_t          i;
705         unsigned char   *cp;
706         char            *bp;
707
708         /* Decoded according to krb5/gssapi_krb5.c */
709
710         /* First determine the size of the string */
711         string_length = 0;
712         number = 0;
713         numshift = 0;
714         cp = (unsigned char *) oid->elements;
715         number = (unsigned long) cp[0];
716         sprintf(numstr, "%ld ", number/40);
717         string_length += strlen(numstr);
718         sprintf(numstr, "%ld ", number%40);
719         string_length += strlen(numstr);
720         for (i=1; i<oid->length; i++) {
721                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
722                         number = (number << 7) | (cp[i] & 0x7f);
723                         numshift += 7;
724                 }
725                 else {
726                         *minor_status = 0;
727                         return(GSS_S_FAILURE);
728                 }
729                 if ((cp[i] & 0x80) == 0) {
730                         sprintf(numstr, "%ld ", number);
731                         string_length += strlen(numstr);
732                         number = 0;
733                         numshift = 0;
734                 }
735         }
736         /*
737          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
738          * here for "{ " and "}\0".
739          */
740         string_length += 4;
741         if ((bp = (char *) mem_alloc(string_length))) {
742                 strcpy(bp, "{ ");
743                 number = (unsigned long) cp[0];
744                 sprintf(numstr, "%ld ", number/40);
745                 strcat(bp, numstr);
746                 sprintf(numstr, "%ld ", number%40);
747                 strcat(bp, numstr);
748                 number = 0;
749                 cp = (unsigned char *) oid->elements;
750                 for (i=1; i<oid->length; i++) {
751                         number = (number << 7) | (cp[i] & 0x7f);
752                         if ((cp[i] & 0x80) == 0) {
753                                 sprintf(numstr, "%ld ", number);
754                                 strcat(bp, numstr);
755                                 number = 0;
756                         }
757                 }
758                 strcat(bp, "}");
759                 oid_str->length = strlen(bp)+1;
760                 oid_str->value = (void *) bp;
761                 *minor_status = 0;
762                 return(GSS_S_COMPLETE);
763         }
764         *minor_status = 0;
765         return(GSS_S_FAILURE);
766 }
767 #endif
768
769 static void
770 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
771     const gss_name_t name)
772 {
773         OM_uint32               maj_stat, min_stat;
774         rpc_gss_ucred_t         *uc = &client->cl_ucred;
775         int                     numgroups;
776
777         uc->uid = 65534;
778         uc->gid = 65534;
779         uc->gidlist = client->cl_gid_storage;
780
781         numgroups = NGROUPS;
782         maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
783             &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
784         if (GSS_ERROR(maj_stat))
785                 uc->gidlen = 0;
786         else
787                 uc->gidlen = numgroups;
788 }
789
790 static void
791 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
792 {
793         static gss_OID_desc krb5_mech_oid =
794                 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
795
796         /*
797          * Attempt to translate mech type and service into a
798          * 'pseudo flavor'. Hardwire in krb5 support for now.
799          */
800         if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
801                 switch (client->cl_rawcred.service) {
802                 case rpc_gss_svc_default:
803                 case rpc_gss_svc_none:
804                         client->cl_rpcflavor = RPCSEC_GSS_KRB5;
805                         break;
806                 case rpc_gss_svc_integrity:
807                         client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
808                         break;
809                 case rpc_gss_svc_privacy:
810                         client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
811                         break;
812                 }
813         } else {
814                 client->cl_rpcflavor = RPCSEC_GSS;
815         }
816 }
817
818 static bool_t
819 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
820                                struct svc_req *rqst,
821                                struct rpc_gss_init_res *gr,
822                                struct rpc_gss_cred *gc)
823 {
824         gss_buffer_desc         recv_tok;
825         gss_OID                 mech;
826         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
827         OM_uint32               cred_lifetime;
828         struct svc_rpc_gss_svc_name *sname;
829
830         rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
831         
832         /* Deserialize arguments. */
833         memset(&recv_tok, 0, sizeof(recv_tok));
834         
835         if (!svc_getargs(rqst,
836                 (xdrproc_t) xdr_gss_buffer_desc,
837                 (caddr_t) &recv_tok)) {
838                 client->cl_state = CLIENT_STALE;
839                 return (FALSE);
840         }
841
842         /*
843          * First time round, try all the server names we have until
844          * one matches. Afterwards, stick with that one.
845          */
846         sx_xlock(&svc_rpc_gss_lock);
847         if (!client->cl_sname) {
848                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
849                         if (sname->sn_program == rqst->rq_prog
850                             && sname->sn_version == rqst->rq_vers) {
851                         retry:
852                                 gr->gr_major = gss_accept_sec_context(
853                                         &gr->gr_minor,
854                                         &client->cl_ctx,
855                                         sname->sn_cred,
856                                         &recv_tok,
857                                         GSS_C_NO_CHANNEL_BINDINGS,
858                                         &client->cl_cname,
859                                         &mech,
860                                         &gr->gr_token,
861                                         &ret_flags,
862                                         &cred_lifetime,
863                                         &client->cl_creds);
864                                 if (gr->gr_major == 
865                                     GSS_S_CREDENTIALS_EXPIRED) {
866                                         /*
867                                          * Either our creds really did
868                                          * expire or gssd was
869                                          * restarted.
870                                          */
871                                         if (rpc_gss_acquire_svc_cred(sname))
872                                                 goto retry;
873                                 }
874                                 client->cl_sname = sname;
875                                 break;
876                         }
877                 }
878                 if (!sname) {
879                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
880                             (char *) &recv_tok);
881                         sx_xunlock(&svc_rpc_gss_lock);
882                         return (FALSE);
883                 }
884         } else {
885                 gr->gr_major = gss_accept_sec_context(
886                         &gr->gr_minor,
887                         &client->cl_ctx,
888                         client->cl_sname->sn_cred,
889                         &recv_tok,
890                         GSS_C_NO_CHANNEL_BINDINGS,
891                         &client->cl_cname,
892                         &mech,
893                         &gr->gr_token,
894                         &ret_flags,
895                         &cred_lifetime,
896                         NULL);
897         }
898         sx_xunlock(&svc_rpc_gss_lock);
899         
900         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
901
902         /*
903          * If we get an error from gss_accept_sec_context, send the
904          * reply anyway so that the client gets a chance to see what
905          * is wrong.
906          */
907         if (gr->gr_major != GSS_S_COMPLETE &&
908             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
909                 rpc_gss_log_status("accept_sec_context", client->cl_mech,
910                     gr->gr_major, gr->gr_minor);
911                 client->cl_state = CLIENT_STALE;
912                 return (TRUE);
913         }
914
915         gr->gr_handle.value = &client->cl_id;
916         gr->gr_handle.length = sizeof(client->cl_id);
917         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
918         
919         /* Save client info. */
920         client->cl_mech = mech;
921         client->cl_qop = GSS_C_QOP_DEFAULT;
922         client->cl_done_callback = FALSE;
923
924         if (gr->gr_major == GSS_S_COMPLETE) {
925                 gss_buffer_desc export_name;
926
927                 /*
928                  * Change client expiration time to be near when the
929                  * client creds expire (or 24 hours if we can't figure
930                  * that out).
931                  */
932                 if (cred_lifetime == GSS_C_INDEFINITE)
933                         cred_lifetime = time_uptime + 24*60*60;
934
935                 client->cl_expiration = time_uptime + cred_lifetime;
936
937                 /*
938                  * Fill in cred details in the rawcred structure.
939                  */
940                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
941                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
942                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
943                     &export_name);
944                 if (maj_stat != GSS_S_COMPLETE) {
945                         rpc_gss_log_status("gss_export_name", client->cl_mech,
946                             maj_stat, min_stat);
947                         return (FALSE);
948                 }
949                 client->cl_rawcred.client_principal =
950                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
951                             + export_name.length);
952                 client->cl_rawcred.client_principal->len = export_name.length;
953                 memcpy(client->cl_rawcred.client_principal->name,
954                     export_name.value, export_name.length);
955                 gss_release_buffer(&min_stat, &export_name);
956                 client->cl_rawcred.svc_principal =
957                         client->cl_sname->sn_principal;
958                 client->cl_rawcred.service = gc->gc_svc;
959
960                 /*
961                  * Use gss_pname_to_uid to map to unix creds. For
962                  * kerberos5, this uses krb5_aname_to_localname.
963                  */
964                 svc_rpc_gss_build_ucred(client, client->cl_cname);
965                 svc_rpc_gss_set_flavor(client);
966                 gss_release_name(&min_stat, &client->cl_cname);
967
968 #ifdef DEBUG
969                 {
970                         gss_buffer_desc mechname;
971
972                         gss_oid_to_str(&min_stat, mech, &mechname);
973                         
974                         rpc_gss_log_debug("accepted context for %s with "
975                             "<mech %.*s, qop %d, svc %d>",
976                             client->cl_rawcred.client_principal->name,
977                             mechname.length, (char *)mechname.value,
978                             client->cl_qop, client->cl_rawcred.service);
979
980                         gss_release_buffer(&min_stat, &mechname);
981                 }
982 #endif /* DEBUG */
983         }
984         return (TRUE);
985 }
986
987 static bool_t
988 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
989     gss_qop_t *qop, rpc_gss_proc_t gcproc)
990 {
991         struct opaque_auth      *oa;
992         gss_buffer_desc          rpcbuf, checksum;
993         OM_uint32                maj_stat, min_stat;
994         gss_qop_t                qop_state;
995         int32_t                  rpchdr[128 / sizeof(int32_t)];
996         int32_t                 *buf;
997
998         rpc_gss_log_debug("in svc_rpc_gss_validate()");
999         
1000         memset(rpchdr, 0, sizeof(rpchdr));
1001
1002         /* Reconstruct RPC header for signing (from xdr_callmsg). */
1003         buf = rpchdr;
1004         IXDR_PUT_LONG(buf, msg->rm_xid);
1005         IXDR_PUT_ENUM(buf, msg->rm_direction);
1006         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1007         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1008         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1009         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1010         oa = &msg->rm_call.cb_cred;
1011         IXDR_PUT_ENUM(buf, oa->oa_flavor);
1012         IXDR_PUT_LONG(buf, oa->oa_length);
1013         if (oa->oa_length) {
1014                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1015                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1016         }
1017         rpcbuf.value = rpchdr;
1018         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1019
1020         checksum.value = msg->rm_call.cb_verf.oa_base;
1021         checksum.length = msg->rm_call.cb_verf.oa_length;
1022         
1023         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1024                                   &qop_state);
1025         
1026         if (maj_stat != GSS_S_COMPLETE) {
1027                 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1028                     maj_stat, min_stat);
1029                 /*
1030                  * A bug in some versions of the Linux client generates a
1031                  * Destroy operation with a bogus encrypted checksum. Deleting
1032                  * the credential handle for that case causes the mount to fail.
1033                  * Since the checksum is bogus (gss_verify_mic() failed), it
1034                  * doesn't make sense to destroy the handle and not doing so
1035                  * fixes the Linux mount.
1036                  */
1037                 if (gcproc != RPCSEC_GSS_DESTROY)
1038                         client->cl_state = CLIENT_STALE;
1039                 return (FALSE);
1040         }
1041
1042         *qop = qop_state;
1043         return (TRUE);
1044 }
1045
1046 static bool_t
1047 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1048     struct svc_req *rqst, u_int seq)
1049 {
1050         gss_buffer_desc         signbuf;
1051         gss_buffer_desc         mic;
1052         OM_uint32               maj_stat, min_stat;
1053         uint32_t                nseq;       
1054
1055         rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1056
1057         nseq = htonl(seq);
1058         signbuf.value = &nseq;
1059         signbuf.length = sizeof(nseq);
1060
1061         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1062             &signbuf, &mic);
1063
1064         if (maj_stat != GSS_S_COMPLETE) {
1065                 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1066                 client->cl_state = CLIENT_STALE;
1067                 return (FALSE);
1068         }
1069
1070         KASSERT(mic.length <= MAX_AUTH_BYTES,
1071             ("MIC too large for RPCSEC_GSS"));
1072
1073         rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1074         rqst->rq_verf.oa_length = mic.length;
1075         bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1076
1077         gss_release_buffer(&min_stat, &mic);
1078         
1079         return (TRUE);
1080 }
1081
1082 static bool_t
1083 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1084 {
1085         struct svc_rpc_gss_callback *scb;
1086         rpc_gss_lock_t  lock;
1087         void            *cookie;
1088         bool_t          cb_res;
1089         bool_t          result;
1090
1091         /*
1092          * See if we have a callback for this guy.
1093          */
1094         result = TRUE;
1095         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1096                 if (scb->cb_callback.program == rqst->rq_prog
1097                     && scb->cb_callback.version == rqst->rq_vers) {
1098                         /*
1099                          * This one matches. Call the callback and see
1100                          * if it wants to veto or something.
1101                          */
1102                         lock.locked = FALSE;
1103                         lock.raw_cred = &client->cl_rawcred;
1104                         cb_res = scb->cb_callback.callback(rqst,
1105                             client->cl_creds,
1106                             client->cl_ctx,
1107                             &lock,
1108                             &cookie);
1109
1110                         if (!cb_res) {
1111                                 client->cl_state = CLIENT_STALE;
1112                                 result = FALSE;
1113                                 break;
1114                         }
1115
1116                         /*
1117                          * The callback accepted the connection - it
1118                          * is responsible for freeing client->cl_creds
1119                          * now.
1120                          */
1121                         client->cl_creds = GSS_C_NO_CREDENTIAL;
1122                         client->cl_locked = lock.locked;
1123                         client->cl_cookie = cookie;
1124                         return (TRUE);
1125                 }
1126         }
1127
1128         /*
1129          * Either no callback exists for this program/version or one
1130          * of the callbacks rejected the connection. We just need to
1131          * clean up the delegated client creds, if any.
1132          */
1133         if (client->cl_creds) {
1134                 OM_uint32 min_ver;
1135                 gss_release_cred(&min_ver, &client->cl_creds);
1136         }
1137         return (result);
1138 }
1139
1140 static bool_t
1141 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1142 {
1143         u_int32_t offset;
1144         int word, bit;
1145         bool_t result;
1146
1147         sx_xlock(&client->cl_lock);
1148         if (seq <= client->cl_seqlast) {
1149                 /*
1150                  * The request sequence number is less than
1151                  * the largest we have seen so far. If it is
1152                  * outside the window or if we have seen a
1153                  * request with this sequence before, silently
1154                  * discard it.
1155                  */
1156                 offset = client->cl_seqlast - seq;
1157                 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1158                         result = FALSE;
1159                         goto out;
1160                 }
1161                 word = offset / 32;
1162                 bit = offset % 32;
1163                 if (client->cl_seqmask[word] & (1 << bit)) {
1164                         result = FALSE;
1165                         goto out;
1166                 }
1167         }
1168
1169         result = TRUE;
1170 out:
1171         sx_xunlock(&client->cl_lock);
1172         return (result);
1173 }
1174
1175 static void
1176 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1177 {
1178         int offset, i, word, bit;
1179         uint32_t carry, newcarry;
1180
1181         sx_xlock(&client->cl_lock);
1182         if (seq > client->cl_seqlast) {
1183                 /*
1184                  * This request has a sequence number greater
1185                  * than any we have seen so far. Advance the
1186                  * seq window and set bit zero of the window
1187                  * (which corresponds to the new sequence
1188                  * number)
1189                  */
1190                 offset = seq - client->cl_seqlast;
1191                 while (offset > 32) {
1192                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1193                              i > 0; i--) {
1194                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1195                         }
1196                         client->cl_seqmask[0] = 0;
1197                         offset -= 32;
1198                 }
1199                 carry = 0;
1200                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1201                         newcarry = client->cl_seqmask[i] >> (32 - offset);
1202                         client->cl_seqmask[i] =
1203                                 (client->cl_seqmask[i] << offset) | carry;
1204                         carry = newcarry;
1205                 }
1206                 client->cl_seqmask[0] |= 1;
1207                 client->cl_seqlast = seq;
1208         } else {
1209                 offset = client->cl_seqlast - seq;
1210                 word = offset / 32;
1211                 bit = offset % 32;
1212                 client->cl_seqmask[word] |= (1 << bit);
1213         }
1214         sx_xunlock(&client->cl_lock);
1215 }
1216
1217 enum auth_stat
1218 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1219
1220 {
1221         OM_uint32                min_stat;
1222         XDR                      xdrs;
1223         struct svc_rpc_gss_cookedcred *cc;
1224         struct svc_rpc_gss_client *client;
1225         struct rpc_gss_cred      gc;
1226         struct rpc_gss_init_res  gr;
1227         gss_qop_t                qop;
1228         int                      call_stat;
1229         enum auth_stat           result;
1230         
1231         rpc_gss_log_debug("in svc_rpc_gss()");
1232         
1233         /* Garbage collect old clients. */
1234         svc_rpc_gss_timeout_clients();
1235
1236         /* Initialize reply. */
1237         rqst->rq_verf = _null_auth;
1238
1239         /* Deserialize client credentials. */
1240         if (rqst->rq_cred.oa_length <= 0)
1241                 return (AUTH_BADCRED);
1242         
1243         memset(&gc, 0, sizeof(gc));
1244         
1245         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1246             rqst->rq_cred.oa_length, XDR_DECODE);
1247         
1248         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1249                 XDR_DESTROY(&xdrs);
1250                 return (AUTH_BADCRED);
1251         }
1252         XDR_DESTROY(&xdrs);
1253
1254         client = NULL;
1255
1256         /* Check version. */
1257         if (gc.gc_version != RPCSEC_GSS_VERSION) {
1258                 result = AUTH_BADCRED;
1259                 goto out;
1260         }
1261
1262         /* Check the proc and find the client (or create it) */
1263         if (gc.gc_proc == RPCSEC_GSS_INIT) {
1264                 if (gc.gc_handle.length != 0) {
1265                         result = AUTH_BADCRED;
1266                         goto out;
1267                 }
1268                 client = svc_rpc_gss_create_client();
1269         } else {
1270                 struct svc_rpc_gss_clientid *p;
1271                 if (gc.gc_handle.length != sizeof(*p)) {
1272                         result = AUTH_BADCRED;
1273                         goto out;
1274                 }
1275                 p = gc.gc_handle.value;
1276                 client = svc_rpc_gss_find_client(p);
1277                 if (!client) {
1278                         /*
1279                          * Can't find the client - we may have
1280                          * destroyed it - tell the other side to
1281                          * re-authenticate.
1282                          */
1283                         result = RPCSEC_GSS_CREDPROBLEM;
1284                         goto out;
1285                 }
1286         }
1287         cc = rqst->rq_clntcred;
1288         cc->cc_client = client;
1289         cc->cc_service = gc.gc_svc;
1290         cc->cc_seq = gc.gc_seq;
1291
1292         /*
1293          * The service and sequence number must be ignored for
1294          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1295          */
1296         if (gc.gc_proc != RPCSEC_GSS_INIT
1297             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1298                 /*
1299                  * Check for sequence number overflow.
1300                  */
1301                 if (gc.gc_seq >= MAXSEQ) {
1302                         result = RPCSEC_GSS_CTXPROBLEM;
1303                         goto out;
1304                 }
1305
1306                 /*
1307                  * Check for valid service.
1308                  */
1309                 if (gc.gc_svc != rpc_gss_svc_none &&
1310                     gc.gc_svc != rpc_gss_svc_integrity &&
1311                     gc.gc_svc != rpc_gss_svc_privacy) {
1312                         result = AUTH_BADCRED;
1313                         goto out;
1314                 }
1315         }
1316
1317         /* Handle RPCSEC_GSS control procedure. */
1318         switch (gc.gc_proc) {
1319
1320         case RPCSEC_GSS_INIT:
1321         case RPCSEC_GSS_CONTINUE_INIT:
1322                 if (rqst->rq_proc != NULLPROC) {
1323                         result = AUTH_REJECTEDCRED;
1324                         break;
1325                 }
1326
1327                 memset(&gr, 0, sizeof(gr));
1328                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1329                         result = AUTH_REJECTEDCRED;
1330                         break;
1331                 }
1332
1333                 if (gr.gr_major == GSS_S_COMPLETE) {
1334                         /*
1335                          * We borrow the space for the call verf to
1336                          * pack our reply verf.
1337                          */
1338                         rqst->rq_verf = msg->rm_call.cb_verf;
1339                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1340                                 result = AUTH_REJECTEDCRED;
1341                                 break;
1342                         }
1343                 } else {
1344                         rqst->rq_verf = _null_auth;
1345                 }
1346                 
1347                 call_stat = svc_sendreply(rqst,
1348                     (xdrproc_t) xdr_rpc_gss_init_res,
1349                     (caddr_t) &gr);
1350
1351                 gss_release_buffer(&min_stat, &gr.gr_token);
1352
1353                 if (!call_stat) {
1354                         result = AUTH_FAILED;
1355                         break;
1356                 }
1357
1358                 if (gr.gr_major == GSS_S_COMPLETE)
1359                         client->cl_state = CLIENT_ESTABLISHED;
1360
1361                 result = RPCSEC_GSS_NODISPATCH;
1362                 break;
1363                 
1364         case RPCSEC_GSS_DATA:
1365         case RPCSEC_GSS_DESTROY:
1366                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1367                         result = RPCSEC_GSS_NODISPATCH;
1368                         break;
1369                 }
1370
1371                 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1372                         result = RPCSEC_GSS_CREDPROBLEM;
1373                         break;
1374                 }
1375                 
1376                 /*
1377                  * We borrow the space for the call verf to pack our
1378                  * reply verf.
1379                  */
1380                 rqst->rq_verf = msg->rm_call.cb_verf;
1381                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1382                         result = RPCSEC_GSS_CTXPROBLEM;
1383                         break;
1384                 }
1385
1386                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1387
1388                 /*
1389                  * Change the SVCAUTH ops on the request to point at
1390                  * our own code so that we can unwrap the arguments
1391                  * and wrap the result. The caller will re-set this on
1392                  * every request to point to a set of null wrap/unwrap
1393                  * methods. Acquire an extra reference to the client
1394                  * which will be released by svc_rpc_gss_release()
1395                  * after the request has finished processing.
1396                  */
1397                 refcount_acquire(&client->cl_refs);
1398                 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1399                 rqst->rq_auth.svc_ah_private = cc;
1400
1401                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1402                         /*
1403                          * We might be ready to do a callback to the server to
1404                          * see if it wants to accept/reject the connection.
1405                          */
1406                         sx_xlock(&client->cl_lock);
1407                         if (!client->cl_done_callback) {
1408                                 client->cl_done_callback = TRUE;
1409                                 client->cl_qop = qop;
1410                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1411                                         client->cl_rawcred.mechanism, qop);
1412                                 if (!svc_rpc_gss_callback(client, rqst)) {
1413                                         result = AUTH_REJECTEDCRED;
1414                                         sx_xunlock(&client->cl_lock);
1415                                         break;
1416                                 }
1417                         }
1418                         sx_xunlock(&client->cl_lock);
1419
1420                         /*
1421                          * If the server has locked this client to a
1422                          * particular service+qop pair, enforce that
1423                          * restriction now.
1424                          */
1425                         if (client->cl_locked) {
1426                                 if (client->cl_rawcred.service != gc.gc_svc) {
1427                                         result = AUTH_FAILED;
1428                                         break;
1429                                 } else if (client->cl_qop != qop) {
1430                                         result = AUTH_BADVERF;
1431                                         break;
1432                                 }
1433                         }
1434
1435                         /*
1436                          * If the qop changed, look up the new qop
1437                          * name for rawcred.
1438                          */
1439                         if (client->cl_qop != qop) {
1440                                 client->cl_qop = qop;
1441                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1442                                         client->cl_rawcred.mechanism, qop);
1443                         }
1444
1445                         /*
1446                          * Make sure we use the right service value
1447                          * for unwrap/wrap.
1448                          */
1449                         if (client->cl_rawcred.service != gc.gc_svc) {
1450                                 client->cl_rawcred.service = gc.gc_svc;
1451                                 svc_rpc_gss_set_flavor(client);
1452                         }
1453
1454                         result = AUTH_OK;
1455                 } else {
1456                         if (rqst->rq_proc != NULLPROC) {
1457                                 result = AUTH_REJECTEDCRED;
1458                                 break;
1459                         }
1460
1461                         call_stat = svc_sendreply(rqst,
1462                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1463
1464                         if (!call_stat) {
1465                                 result = AUTH_FAILED;
1466                                 break;
1467                         }
1468
1469                         svc_rpc_gss_forget_client(client);
1470
1471                         result = RPCSEC_GSS_NODISPATCH;
1472                         break;
1473                 }
1474                 break;
1475
1476         default:
1477                 result = AUTH_BADCRED;
1478                 break;
1479         }
1480 out:
1481         if (client)
1482                 svc_rpc_gss_release_client(client);
1483
1484         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1485         return (result);
1486 }
1487
1488 static bool_t
1489 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1490 {
1491         struct svc_rpc_gss_cookedcred *cc;
1492         struct svc_rpc_gss_client *client;
1493         
1494         rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1495
1496         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1497         client = cc->cc_client;
1498         if (client->cl_state != CLIENT_ESTABLISHED
1499             || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1500                 return (TRUE);
1501         }
1502         
1503         return (xdr_rpc_gss_wrap_data(mp,
1504                 client->cl_ctx, client->cl_qop,
1505                 cc->cc_service, cc->cc_seq));
1506 }
1507
1508 static bool_t
1509 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1510 {
1511         struct svc_rpc_gss_cookedcred *cc;
1512         struct svc_rpc_gss_client *client;
1513
1514         rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1515         
1516         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1517         client = cc->cc_client;
1518         if (client->cl_state != CLIENT_ESTABLISHED
1519             || cc->cc_service == rpc_gss_svc_none) {
1520                 return (TRUE);
1521         }
1522
1523         return (xdr_rpc_gss_unwrap_data(mp,
1524                 client->cl_ctx, client->cl_qop,
1525                 cc->cc_service, cc->cc_seq));
1526 }
1527
1528 static void
1529 svc_rpc_gss_release(SVCAUTH *auth)
1530 {
1531         struct svc_rpc_gss_cookedcred *cc;
1532         struct svc_rpc_gss_client *client;
1533
1534         rpc_gss_log_debug("in svc_rpc_gss_release()");
1535
1536         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1537         client = cc->cc_client;
1538         svc_rpc_gss_release_client(client);
1539 }