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