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