]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/rpc/rpcsec_gss/rpcsec_gss.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / rpc / rpcsec_gss / 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   auth_gss.c
28
29   RPCSEC_GSS client routines.
30   
31   Copyright (c) 2000 The Regents of the University of Michigan.
32   All rights reserved.
33
34   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35   All rights reserved, all wrongs reversed.
36
37   Redistribution and use in source and binary forms, with or without
38   modification, are permitted provided that the following conditions
39   are met:
40
41   1. Redistributions of source code must retain the above copyright
42      notice, this list of conditions and the following disclaimer.
43   2. Redistributions in binary form must reproduce the above copyright
44      notice, this list of conditions and the following disclaimer in the
45      documentation and/or other materials provided with the distribution.
46   3. Neither the name of the University nor the names of its
47      contributors may be used to endorse or promote products derived
48      from this software without specific prior written permission.
49
50   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61
62   $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
63 */
64
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
67
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/hash.h>
71 #include <sys/kernel.h>
72 #include <sys/kobj.h>
73 #include <sys/lock.h>
74 #include <sys/malloc.h>
75 #include <sys/mbuf.h>
76 #include <sys/mutex.h>
77 #include <sys/proc.h>
78 #include <sys/refcount.h>
79 #include <sys/sx.h>
80 #include <sys/ucred.h>
81
82 #include <rpc/rpc.h>
83 #include <rpc/rpcsec_gss.h>
84
85 #include "rpcsec_gss_int.h"
86
87 static void     rpc_gss_nextverf(AUTH*);
88 static bool_t   rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
89 static bool_t   rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
90 static bool_t   rpc_gss_refresh(AUTH *, void *);
91 static bool_t   rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
92     struct mbuf **);
93 static void     rpc_gss_destroy(AUTH *);
94 static void     rpc_gss_destroy_context(AUTH *, bool_t);
95
96 static struct auth_ops rpc_gss_ops = {
97         rpc_gss_nextverf,
98         rpc_gss_marshal,
99         rpc_gss_validate,
100         rpc_gss_refresh,
101         rpc_gss_destroy,
102 };
103
104 enum rpcsec_gss_state {
105         RPCSEC_GSS_START,
106         RPCSEC_GSS_CONTEXT,
107         RPCSEC_GSS_ESTABLISHED,
108         RPCSEC_GSS_DESTROYING
109 };
110
111 struct rpc_pending_request {
112         uint32_t                pr_xid;         /* XID of rpc */
113         uint32_t                pr_seq;         /* matching GSS seq */
114         LIST_ENTRY(rpc_pending_request) pr_link;
115 };
116 LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
117
118 struct rpc_gss_data {
119         volatile u_int          gd_refs;        /* number of current users */
120         struct mtx              gd_lock;
121         uint32_t                gd_hash;
122         AUTH                    *gd_auth;       /* link back to AUTH */
123         struct ucred            *gd_ucred;      /* matching local cred */
124         char                    *gd_principal;  /* server principal name */
125         rpc_gss_options_req_t   gd_options;     /* GSS context options */
126         enum rpcsec_gss_state   gd_state;       /* connection state */
127         gss_buffer_desc         gd_verf;        /* save GSS_S_COMPLETE
128                                                  * NULL RPC verfier to
129                                                  * process at end of
130                                                  * context negotiation */
131         CLIENT                  *gd_clnt;       /* client handle */
132         gss_OID                 gd_mech;        /* mechanism to use */
133         gss_qop_t               gd_qop;         /* quality of protection */
134         gss_ctx_id_t            gd_ctx;         /* context id */
135         struct rpc_gss_cred     gd_cred;        /* client credentials */
136         uint32_t                gd_seq;         /* next sequence number */
137         u_int                   gd_win;         /* sequence window */
138         struct rpc_pending_request_list gd_reqs;
139         TAILQ_ENTRY(rpc_gss_data) gd_link;
140         TAILQ_ENTRY(rpc_gss_data) gd_alllink;
141 };
142 TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
143
144 #define AUTH_PRIVATE(auth)      ((struct rpc_gss_data *)auth->ah_private)
145
146 static struct timeval AUTH_TIMEOUT = { 25, 0 };
147
148 #define RPC_GSS_HASH_SIZE       11
149 #define RPC_GSS_MAX             256
150 static struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
151 static struct rpc_gss_data_list rpc_gss_all;
152 static struct sx rpc_gss_lock;
153 static int rpc_gss_count;
154
155 static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
156     gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
157     rpc_gss_options_ret_t *);
158
159 static void
160 rpc_gss_hashinit(void *dummy)
161 {
162         int i;
163
164         for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
165                 TAILQ_INIT(&rpc_gss_cache[i]);
166         TAILQ_INIT(&rpc_gss_all);
167         sx_init(&rpc_gss_lock, "rpc_gss_lock");
168 }
169 SYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
170
171 static uint32_t
172 rpc_gss_hash(const char *principal, gss_OID mech,
173     struct ucred *cred, rpc_gss_service_t service)
174 {
175         uint32_t h;
176
177         h = HASHSTEP(HASHINIT, cred->cr_uid);
178         h = hash32_str(principal, h);
179         h = hash32_buf(mech->elements, mech->length, h);
180         h = HASHSTEP(h, (int) service);
181
182         return (h % RPC_GSS_HASH_SIZE);
183 }
184
185 /*
186  * Simplified interface to create a security association for the
187  * current thread's * ucred.
188  */
189 AUTH *
190 rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
191     gss_OID mech_oid, rpc_gss_service_t service)
192 {
193         uint32_t                h, th;
194         AUTH                    *auth;
195         struct rpc_gss_data     *gd, *tgd;
196         rpc_gss_options_ret_t   options;
197
198         if (rpc_gss_count > RPC_GSS_MAX) {
199                 while (rpc_gss_count > RPC_GSS_MAX) {
200                         sx_xlock(&rpc_gss_lock);
201                         tgd = TAILQ_FIRST(&rpc_gss_all);
202                         th = tgd->gd_hash;
203                         TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
204                         TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
205                         rpc_gss_count--;
206                         sx_xunlock(&rpc_gss_lock);
207                         AUTH_DESTROY(tgd->gd_auth);
208                 }
209         }
210
211         /*
212          * See if we already have an AUTH which matches.
213          */
214         h = rpc_gss_hash(principal, mech_oid, cred, service);
215
216 again:
217         sx_slock(&rpc_gss_lock);
218         TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
219                 if (gd->gd_ucred->cr_uid == cred->cr_uid
220                     && !strcmp(gd->gd_principal, principal)
221                     && gd->gd_mech == mech_oid
222                     && gd->gd_cred.gc_svc == service) {
223                         refcount_acquire(&gd->gd_refs);
224                         if (sx_try_upgrade(&rpc_gss_lock)) {
225                                 /*
226                                  * Keep rpc_gss_all LRU sorted.
227                                  */
228                                 TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
229                                 TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
230                                     gd_alllink);
231                                 sx_xunlock(&rpc_gss_lock);
232                         } else {
233                                 sx_sunlock(&rpc_gss_lock);
234                         }
235
236                         /*
237                          * If the state != ESTABLISHED, try and initialize
238                          * the authenticator again. This will happen if the
239                          * user's credentials have expired. It may succeed now,
240                          * if they have done a kinit or similar.
241                          */
242                         if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
243                                 memset(&options, 0, sizeof (options));
244                                 (void) rpc_gss_init(gd->gd_auth, &options);
245                         }
246                         return (gd->gd_auth);
247                 }
248         }
249         sx_sunlock(&rpc_gss_lock);
250
251         /*
252          * We missed in the cache - create a new association.
253          */
254         auth = rpc_gss_seccreate_int(clnt, cred, principal, mech_oid, service,
255             GSS_C_QOP_DEFAULT, NULL, NULL);
256         if (!auth)
257                 return (NULL);
258
259         gd = AUTH_PRIVATE(auth);
260         gd->gd_hash = h;
261         
262         sx_xlock(&rpc_gss_lock);
263         TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
264                 if (tgd->gd_ucred->cr_uid == cred->cr_uid
265                     && !strcmp(tgd->gd_principal, principal)
266                     && tgd->gd_mech == mech_oid
267                     && tgd->gd_cred.gc_svc == service) {
268                         /*
269                          * We lost a race to create the AUTH that
270                          * matches this cred.
271                          */
272                         sx_xunlock(&rpc_gss_lock);
273                         AUTH_DESTROY(auth);
274                         goto again;
275                 }
276         }
277
278         rpc_gss_count++;
279         TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
280         TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
281         refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */
282         sx_xunlock(&rpc_gss_lock);
283
284         return (auth);
285 }
286
287 void
288 rpc_gss_secpurge(CLIENT *clnt)
289 {
290         uint32_t                h;
291         struct rpc_gss_data     *gd, *tgd;
292
293         TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
294                 if (gd->gd_clnt == clnt) {
295                         sx_xlock(&rpc_gss_lock);
296                         h = gd->gd_hash;
297                         TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
298                         TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
299                         rpc_gss_count--;
300                         sx_xunlock(&rpc_gss_lock);
301                         AUTH_DESTROY(gd->gd_auth);
302                 }
303         }
304 }
305
306 AUTH *
307 rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *principal,
308     const char *mechanism, rpc_gss_service_t service, const char *qop,
309     rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
310 {
311         gss_OID                 oid;
312         u_int                   qop_num;
313
314         /*
315          * Bail out now if we don't know this mechanism.
316          */
317         if (!rpc_gss_mech_to_oid(mechanism, &oid))
318                 return (NULL);
319
320         if (qop) {
321                 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
322                         return (NULL);
323         } else {
324                 qop_num = GSS_C_QOP_DEFAULT;
325         }
326
327         return (rpc_gss_seccreate_int(clnt, cred, principal, oid, service,
328                 qop_num, options_req, options_ret));
329 }
330
331 static AUTH *
332 rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, const char *principal,
333     gss_OID mech_oid, rpc_gss_service_t service, u_int qop_num,
334     rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
335 {
336         AUTH                    *auth;
337         rpc_gss_options_ret_t   options;
338         struct rpc_gss_data     *gd;
339
340         /*
341          * If the caller doesn't want the options, point at local
342          * storage to simplify the code below.
343          */
344         if (!options_ret)
345                 options_ret = &options;
346
347         /*
348          * Default service is integrity.
349          */
350         if (service == rpc_gss_svc_default)
351                 service = rpc_gss_svc_integrity;
352
353         memset(options_ret, 0, sizeof(*options_ret));
354
355         rpc_gss_log_debug("in rpc_gss_seccreate()");
356         
357         memset(&rpc_createerr, 0, sizeof(rpc_createerr));
358         
359         auth = mem_alloc(sizeof(*auth));
360         if (auth == NULL) {
361                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
362                 rpc_createerr.cf_error.re_errno = ENOMEM;
363                 return (NULL);
364         }
365         gd = mem_alloc(sizeof(*gd));
366         if (gd == NULL) {
367                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
368                 rpc_createerr.cf_error.re_errno = ENOMEM;
369                 mem_free(auth, sizeof(*auth));
370                 return (NULL);
371         }
372
373         auth->ah_ops = &rpc_gss_ops;
374         auth->ah_private = (caddr_t) gd;
375         auth->ah_cred.oa_flavor = RPCSEC_GSS;
376         
377         refcount_init(&gd->gd_refs, 1);
378         mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
379         gd->gd_auth = auth;
380         gd->gd_ucred = crdup(cred);
381         gd->gd_principal = strdup(principal, M_RPC);
382
383
384         if (options_req) {
385                 gd->gd_options = *options_req;
386         } else {
387                 gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
388                 gd->gd_options.time_req = 0;
389                 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
390                 gd->gd_options.input_channel_bindings = NULL;
391         }
392         CLNT_ACQUIRE(clnt);
393         gd->gd_clnt = clnt;
394         gd->gd_ctx = GSS_C_NO_CONTEXT;
395         gd->gd_mech = mech_oid;
396         gd->gd_qop = qop_num;
397
398         gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
399         gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
400         gd->gd_cred.gc_seq = 0;
401         gd->gd_cred.gc_svc = service;
402         LIST_INIT(&gd->gd_reqs);
403         
404         if (!rpc_gss_init(auth, options_ret)) {
405                 goto bad;
406         }
407         
408         return (auth);
409
410  bad:
411         AUTH_DESTROY(auth);
412         return (NULL);
413 }
414
415 bool_t
416 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
417 {
418         struct rpc_gss_data     *gd;
419         u_int                   qop_num;
420         const char              *mechanism;
421
422         gd = AUTH_PRIVATE(auth);
423         if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
424                 return (FALSE);
425         }
426
427         if (qop) {
428                 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
429                         return (FALSE);
430                 }
431         } else {
432                 qop_num = GSS_C_QOP_DEFAULT;
433         }
434
435         gd->gd_cred.gc_svc = service;
436         gd->gd_qop = qop_num;
437         return (TRUE);
438 }
439
440 static void
441 rpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
442 {
443         struct rpc_pending_request *pr, *npr;
444         struct rpc_pending_request_list reqs;
445
446         LIST_INIT(&reqs);
447         mtx_lock(&gd->gd_lock);
448         LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
449                 if (pr->pr_xid == xid) {
450                         LIST_REMOVE(pr, pr_link);
451                         LIST_INSERT_HEAD(&reqs, pr, pr_link);
452                 }
453         }
454
455         mtx_unlock(&gd->gd_lock);
456
457         LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
458                 mem_free(pr, sizeof(*pr));
459         }
460 }
461
462 static uint32_t
463 rpc_gss_alloc_seq(struct rpc_gss_data *gd)
464 {
465         uint32_t seq;
466
467         mtx_lock(&gd->gd_lock);
468         seq = gd->gd_seq;
469         gd->gd_seq++;
470         mtx_unlock(&gd->gd_lock);
471
472         return (seq);
473 }
474
475 static void
476 rpc_gss_nextverf(__unused AUTH *auth)
477 {
478
479         /* not used */
480 }
481
482 static bool_t
483 rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
484 {
485         struct rpc_gss_data     *gd;
486         struct rpc_pending_request *pr;
487         uint32_t                 seq;
488         XDR                      tmpxdrs;
489         struct rpc_gss_cred      gsscred;
490         char                     credbuf[MAX_AUTH_BYTES];
491         struct opaque_auth       creds, verf;
492         gss_buffer_desc          rpcbuf, checksum;
493         OM_uint32                maj_stat, min_stat;
494         bool_t                   xdr_stat;
495
496         rpc_gss_log_debug("in rpc_gss_marshal()");
497
498         gd = AUTH_PRIVATE(auth);
499         
500         gsscred = gd->gd_cred;
501         seq = rpc_gss_alloc_seq(gd);
502         gsscred.gc_seq = seq;
503
504         xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
505         if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
506                 XDR_DESTROY(&tmpxdrs);
507                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
508                 return (FALSE);
509         }
510         creds.oa_flavor = RPCSEC_GSS;
511         creds.oa_base = credbuf;
512         creds.oa_length = XDR_GETPOS(&tmpxdrs);
513         XDR_DESTROY(&tmpxdrs);
514
515         xdr_opaque_auth(xdrs, &creds);
516
517         if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
518             gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
519                 if (!xdr_opaque_auth(xdrs, &_null_auth)) {
520                         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
521                         return (FALSE);
522                 }
523                 xdrmbuf_append(xdrs, args);
524                 return (TRUE);
525         } else {
526                 /*
527                  * Keep track of this XID + seq pair so that we can do
528                  * the matching gss_verify_mic in AUTH_VALIDATE.
529                  */
530                 pr = mem_alloc(sizeof(struct rpc_pending_request));
531                 mtx_lock(&gd->gd_lock);
532                 pr->pr_xid = xid;
533                 pr->pr_seq = seq;
534                 LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
535                 mtx_unlock(&gd->gd_lock);
536
537                 /*
538                  * Checksum serialized RPC header, up to and including
539                  * credential. For the in-kernel environment, we
540                  * assume that our XDR stream is on a contiguous
541                  * memory buffer (e.g. an mbuf).
542                  */
543                 rpcbuf.length = XDR_GETPOS(xdrs);
544                 XDR_SETPOS(xdrs, 0);
545                 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
546
547                 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
548                     &rpcbuf, &checksum);
549
550                 if (maj_stat != GSS_S_COMPLETE) {
551                         rpc_gss_log_status("gss_get_mic", gd->gd_mech,
552                             maj_stat, min_stat);
553                         if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
554                                 rpc_gss_destroy_context(auth, TRUE);
555                         }
556                         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
557                         return (FALSE);
558                 }
559
560                 verf.oa_flavor = RPCSEC_GSS;
561                 verf.oa_base = checksum.value;
562                 verf.oa_length = checksum.length;
563
564                 xdr_stat = xdr_opaque_auth(xdrs, &verf);
565                 gss_release_buffer(&min_stat, &checksum);
566                 if (!xdr_stat) {
567                         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
568                         return (FALSE);
569                 }
570                 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
571                     gd->gd_cred.gc_svc == rpc_gss_svc_none) {
572                         xdrmbuf_append(xdrs, args);
573                         return (TRUE);
574                 } else {
575                         if (!xdr_rpc_gss_wrap_data(&args,
576                                 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
577                                 seq))
578                                 return (FALSE);
579                         xdrmbuf_append(xdrs, args);
580                         return (TRUE);
581                 }
582         }
583
584         return (TRUE);
585 }
586
587 static bool_t
588 rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
589     struct mbuf **resultsp)
590 {
591         struct rpc_gss_data     *gd;
592         struct rpc_pending_request *pr, *npr;
593         struct rpc_pending_request_list reqs;
594         gss_qop_t               qop_state;
595         uint32_t                num, seq;
596         gss_buffer_desc         signbuf, checksum;
597         OM_uint32               maj_stat, min_stat;
598
599         rpc_gss_log_debug("in rpc_gss_validate()");
600         
601         gd = AUTH_PRIVATE(auth);
602
603         /*
604          * The client will call us with a NULL verf when it gives up
605          * on an XID.
606          */
607         if (!verf) {
608                 rpc_gss_purge_xid(gd, xid);
609                 return (TRUE);
610         }
611
612         if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
613                 /*
614                  * Save the on the wire verifier to validate last INIT
615                  * phase packet after decode if the major status is
616                  * GSS_S_COMPLETE.
617                  */
618                 if (gd->gd_verf.value)
619                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
620                             (char *) &gd->gd_verf);
621                 gd->gd_verf.value = mem_alloc(verf->oa_length);
622                 if (gd->gd_verf.value == NULL) {
623                         printf("gss_validate: out of memory\n");
624                         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
625                         m_freem(*resultsp);
626                         *resultsp = NULL;
627                         return (FALSE);
628                 }
629                 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
630                 gd->gd_verf.length = verf->oa_length;
631
632                 return (TRUE);
633         }
634
635         /*
636          * We need to check the verifier against all the requests
637          * we've send for this XID - for unreliable protocols, we
638          * retransmit with the same XID but different sequence
639          * number. We temporarily take this set of requests out of the
640          * list so that we can work through the list without having to
641          * hold the lock.
642          */
643         mtx_lock(&gd->gd_lock);
644         LIST_INIT(&reqs);
645         LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
646                 if (pr->pr_xid == xid) {
647                         LIST_REMOVE(pr, pr_link);
648                         LIST_INSERT_HEAD(&reqs, pr, pr_link);
649                 }
650         }
651         mtx_unlock(&gd->gd_lock);
652         LIST_FOREACH(pr, &reqs, pr_link) {
653                 if (pr->pr_xid == xid) {
654                         seq = pr->pr_seq;
655                         num = htonl(seq);
656                         signbuf.value = &num;
657                         signbuf.length = sizeof(num);
658         
659                         checksum.value = verf->oa_base;
660                         checksum.length = verf->oa_length;
661         
662                         maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
663                             &signbuf, &checksum, &qop_state);
664                         if (maj_stat != GSS_S_COMPLETE
665                             || qop_state != gd->gd_qop) {
666                                 continue;
667                         }
668                         if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
669                                 rpc_gss_destroy_context(auth, TRUE);
670                                 break;
671                         }
672                         //rpc_gss_purge_reqs(gd, seq);
673                         LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
674                                 mem_free(pr, sizeof(*pr));
675
676                         if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
677                                 return (TRUE);
678                         } else {
679                                 if (!xdr_rpc_gss_unwrap_data(resultsp,
680                                         gd->gd_ctx, gd->gd_qop,
681                                         gd->gd_cred.gc_svc, seq)) {
682                                         return (FALSE);
683                                 }
684                         }
685                         return (TRUE);
686                 }
687         }
688
689         /*
690          * We didn't match - put back any entries for this XID so that
691          * a future call to validate can retry.
692          */
693         mtx_lock(&gd->gd_lock);
694         LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
695                 LIST_REMOVE(pr, pr_link);
696                 LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
697         }
698         mtx_unlock(&gd->gd_lock);
699
700         /*
701          * Nothing matches - give up.
702          */
703         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
704         m_freem(*resultsp);
705         *resultsp = NULL;
706         return (FALSE);
707 }
708
709 static bool_t
710 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
711 {
712         struct thread           *td = curthread;
713         struct ucred            *crsave;
714         struct rpc_gss_data     *gd;
715         struct rpc_gss_init_res  gr;
716         gss_buffer_desc         principal_desc;
717         gss_buffer_desc         *recv_tokenp, recv_token, send_token;
718         gss_name_t              name;
719         OM_uint32                maj_stat, min_stat, call_stat;
720         const char              *mech;
721         struct rpc_callextra     ext;
722
723         rpc_gss_log_debug("in rpc_gss_refresh()");
724         
725         gd = AUTH_PRIVATE(auth);
726         
727         mtx_lock(&gd->gd_lock);
728         /*
729          * If the context isn't in START state, someone else is
730          * refreshing - we wait till they are done. If they fail, they
731          * will put the state back to START and we can try (most
732          * likely to also fail).
733          */
734         while (gd->gd_state != RPCSEC_GSS_START
735             && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
736                 msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
737         }
738         if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
739                 mtx_unlock(&gd->gd_lock);
740                 return (TRUE);
741         }
742         gd->gd_state = RPCSEC_GSS_CONTEXT;
743         mtx_unlock(&gd->gd_lock);
744
745         gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
746         gd->gd_cred.gc_seq = 0;
747
748         principal_desc.value = (void *)gd->gd_principal;
749         principal_desc.length = strlen(gd->gd_principal);
750         maj_stat = gss_import_name(&min_stat, &principal_desc,
751             GSS_C_NT_HOSTBASED_SERVICE, &name);
752         if (maj_stat != GSS_S_COMPLETE) {
753                 options_ret->major_status = maj_stat;
754                 options_ret->minor_status = min_stat;
755                 goto out;
756         }
757
758         /* GSS context establishment loop. */
759         memset(&recv_token, 0, sizeof(recv_token));
760         memset(&gr, 0, sizeof(gr));
761         memset(options_ret, 0, sizeof(*options_ret));
762         options_ret->major_status = GSS_S_FAILURE;
763         recv_tokenp = GSS_C_NO_BUFFER;
764         
765         for (;;) {
766                 crsave = td->td_ucred;
767                 td->td_ucred = gd->gd_ucred;
768                 maj_stat = gss_init_sec_context(&min_stat,
769                     gd->gd_options.my_cred,
770                     &gd->gd_ctx,
771                     name,
772                     gd->gd_mech,
773                     gd->gd_options.req_flags,
774                     gd->gd_options.time_req,
775                     gd->gd_options.input_channel_bindings,
776                     recv_tokenp,
777                     &gd->gd_mech,       /* used mech */
778                     &send_token,
779                     &options_ret->ret_flags,
780                     &options_ret->time_req);
781                 td->td_ucred = crsave;
782                 
783                 /*
784                  * Free the token which we got from the server (if
785                  * any).  Remember that this was allocated by XDR, not
786                  * GSS-API.
787                  */
788                 if (recv_tokenp != GSS_C_NO_BUFFER) {
789                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
790                             (char *) &recv_token);
791                         recv_tokenp = GSS_C_NO_BUFFER;
792                 }
793                 if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
794                         strlcpy(options_ret->actual_mechanism,
795                             mech,
796                             sizeof(options_ret->actual_mechanism));
797                 }
798                 if (maj_stat != GSS_S_COMPLETE &&
799                     maj_stat != GSS_S_CONTINUE_NEEDED) {
800                         rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
801                             maj_stat, min_stat);
802                         options_ret->major_status = maj_stat;
803                         options_ret->minor_status = min_stat;
804                         break;
805                 }
806                 if (send_token.length != 0) {
807                         memset(&gr, 0, sizeof(gr));
808                         
809                         bzero(&ext, sizeof(ext));
810                         ext.rc_auth = auth;
811                         call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
812                             (xdrproc_t)xdr_gss_buffer_desc,
813                             &send_token,
814                             (xdrproc_t)xdr_rpc_gss_init_res,
815                             (caddr_t)&gr, AUTH_TIMEOUT);
816                         
817                         gss_release_buffer(&min_stat, &send_token);
818                         
819                         if (call_stat != RPC_SUCCESS)
820                                 break;
821
822                         if (gr.gr_major != GSS_S_COMPLETE &&
823                             gr.gr_major != GSS_S_CONTINUE_NEEDED) {
824                                 rpc_gss_log_status("server reply", gd->gd_mech,
825                                     gr.gr_major, gr.gr_minor);
826                                 options_ret->major_status = gr.gr_major;
827                                 options_ret->minor_status = gr.gr_minor;
828                                 break;
829                         }
830                         
831                         /*
832                          * Save the server's gr_handle value, freeing
833                          * what we have already (remember that this
834                          * was allocated by XDR, not GSS-API).
835                          */
836                         if (gr.gr_handle.length != 0) {
837                                 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
838                                     (char *) &gd->gd_cred.gc_handle);
839                                 gd->gd_cred.gc_handle = gr.gr_handle;
840                         }
841
842                         /*
843                          * Save the server's token as well.
844                          */
845                         if (gr.gr_token.length != 0) {
846                                 recv_token = gr.gr_token;
847                                 recv_tokenp = &recv_token;
848                         }
849
850                         /*
851                          * Since we have copied out all the bits of gr
852                          * which XDR allocated for us, we don't need
853                          * to free it.
854                          */
855                         gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
856                 }
857
858                 if (maj_stat == GSS_S_COMPLETE) {
859                         gss_buffer_desc   bufin;
860                         u_int seq, qop_state = 0;
861
862                         /* 
863                          * gss header verifier,
864                          * usually checked in gss_validate
865                          */
866                         seq = htonl(gr.gr_win);
867                         bufin.value = (unsigned char *)&seq;
868                         bufin.length = sizeof(seq);
869
870                         maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
871                             &bufin, &gd->gd_verf, &qop_state);
872
873                         if (maj_stat != GSS_S_COMPLETE ||
874                             qop_state != gd->gd_qop) {
875                                 rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
876                                     maj_stat, min_stat);
877                                 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
878                                         rpc_gss_destroy_context(auth, TRUE);
879                                 }
880                                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
881                                     EPERM);
882                                 options_ret->major_status = maj_stat;
883                                 options_ret->minor_status = min_stat;
884                                 break;
885                         }
886
887                         options_ret->major_status = GSS_S_COMPLETE;
888                         options_ret->minor_status = 0;
889                         options_ret->rpcsec_version = gd->gd_cred.gc_version;
890                         options_ret->gss_context = gd->gd_ctx;
891
892                         gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
893                         gd->gd_seq = 1;
894                         gd->gd_win = gr.gr_win;
895                         break;
896                 }
897         }
898
899         gss_release_name(&min_stat, &name);
900         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
901             (char *) &gd->gd_verf);
902
903 out:
904         /* End context negotiation loop. */
905         if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
906                 rpc_createerr.cf_stat = RPC_AUTHERROR;
907                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
908                 if (gd->gd_ctx) {
909                         gss_delete_sec_context(&min_stat, &gd->gd_ctx,
910                                 GSS_C_NO_BUFFER);
911                 }
912                 mtx_lock(&gd->gd_lock);
913                 gd->gd_state = RPCSEC_GSS_START;
914                 wakeup(gd);
915                 mtx_unlock(&gd->gd_lock);
916                 return (FALSE);
917         }
918         
919         mtx_lock(&gd->gd_lock);
920         gd->gd_state = RPCSEC_GSS_ESTABLISHED;
921         wakeup(gd);
922         mtx_unlock(&gd->gd_lock);
923
924         return (TRUE);
925 }
926
927 static bool_t
928 rpc_gss_refresh(AUTH *auth, void *msg)
929 {
930         struct rpc_msg *reply = (struct rpc_msg *) msg;
931         rpc_gss_options_ret_t options;
932         struct rpc_gss_data *gd;
933
934         gd = AUTH_PRIVATE(auth);
935         
936         /*
937          * If the context is in DESTROYING state, then just return, since
938          * there is no point in refreshing the credentials.
939          */
940         mtx_lock(&gd->gd_lock);
941         if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
942                 mtx_unlock(&gd->gd_lock);
943                 return (FALSE);
944         }
945         mtx_unlock(&gd->gd_lock);
946
947         /*
948          * If the error was RPCSEC_GSS_CREDPROBLEM of
949          * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
950          * other errors are fatal.
951          */
952         if (reply->rm_reply.rp_stat == MSG_DENIED
953             && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
954             && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
955                 || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
956                 rpc_gss_destroy_context(auth, FALSE);
957                 memset(&options, 0, sizeof(options));
958                 return (rpc_gss_init(auth, &options));
959         }
960
961         return (FALSE);
962 }
963
964 static void
965 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
966 {
967         struct rpc_gss_data     *gd;
968         struct rpc_pending_request *pr;
969         OM_uint32                min_stat;
970         struct rpc_callextra     ext;
971
972         rpc_gss_log_debug("in rpc_gss_destroy_context()");
973         
974         gd = AUTH_PRIVATE(auth);
975         
976         mtx_lock(&gd->gd_lock);
977         /*
978          * If the context isn't in ESTABISHED state, someone else is
979          * destroying/refreshing - we wait till they are done.
980          */
981         if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
982                 while (gd->gd_state != RPCSEC_GSS_START
983                     && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
984                         msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
985                 mtx_unlock(&gd->gd_lock);
986                 return;
987         }
988         gd->gd_state = RPCSEC_GSS_DESTROYING;
989         mtx_unlock(&gd->gd_lock);
990
991         if (send_destroy) {
992                 gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
993                 bzero(&ext, sizeof(ext));
994                 ext.rc_auth = auth;
995                 CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
996                     (xdrproc_t)xdr_void, NULL,
997                     (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
998         }
999
1000         while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1001                 LIST_REMOVE(pr, pr_link);
1002                 mem_free(pr, sizeof(*pr));
1003         }
1004
1005         /*
1006          * Free the context token. Remember that this was
1007          * allocated by XDR, not GSS-API.
1008          */
1009         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1010             (char *) &gd->gd_cred.gc_handle);
1011         gd->gd_cred.gc_handle.length = 0;
1012
1013         if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1014                 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1015
1016         mtx_lock(&gd->gd_lock);
1017         gd->gd_state = RPCSEC_GSS_START;
1018         wakeup(gd);
1019         mtx_unlock(&gd->gd_lock);
1020 }
1021
1022 static void
1023 rpc_gss_destroy(AUTH *auth)
1024 {
1025         struct rpc_gss_data     *gd;
1026         
1027         rpc_gss_log_debug("in rpc_gss_destroy()");
1028         
1029         gd = AUTH_PRIVATE(auth);
1030         
1031         if (!refcount_release(&gd->gd_refs))
1032                 return;
1033
1034         rpc_gss_destroy_context(auth, TRUE);
1035         
1036         CLNT_RELEASE(gd->gd_clnt);
1037         crfree(gd->gd_ucred);
1038         free(gd->gd_principal, M_RPC);
1039         if (gd->gd_verf.value)
1040                 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1041                     (char *) &gd->gd_verf);
1042         mtx_destroy(&gd->gd_lock);
1043
1044         mem_free(gd, sizeof(*gd));
1045         mem_free(auth, sizeof(*auth));
1046 }
1047
1048 int
1049 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1050 {
1051         struct rpc_gss_data     *gd;
1052         int                     want_conf;
1053         OM_uint32               max;
1054         OM_uint32               maj_stat, min_stat;
1055         int                     result;
1056
1057         gd = AUTH_PRIVATE(auth);
1058
1059         switch (gd->gd_cred.gc_svc) {
1060         case rpc_gss_svc_none:
1061                 return (max_tp_unit_len);
1062                 break;
1063
1064         case rpc_gss_svc_default:
1065         case rpc_gss_svc_integrity:
1066                 want_conf = FALSE;
1067                 break;
1068
1069         case rpc_gss_svc_privacy:
1070                 want_conf = TRUE;
1071                 break;
1072
1073         default:
1074                 return (0);
1075         }
1076
1077         maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1078             gd->gd_qop, max_tp_unit_len, &max);
1079
1080         if (maj_stat == GSS_S_COMPLETE) {
1081                 result = (int) max;
1082                 if (result < 0)
1083                         result = 0;
1084                 return (result);
1085         } else {
1086                 rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1087                     maj_stat, min_stat);
1088                 return (0);
1089         }
1090 }