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