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