]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
Copy head to stable/8 as part of 8.0 Release cycle.
[FreeBSD/stable/8.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);
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         *crp = crhold(cr);
453
454         return (TRUE);
455 }
456
457 int
458 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
459 {
460         struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
461         struct svc_rpc_gss_client *client = cc->cc_client;
462         int                     want_conf;
463         OM_uint32               max;
464         OM_uint32               maj_stat, min_stat;
465         int                     result;
466
467         switch (client->cl_rawcred.service) {
468         case rpc_gss_svc_none:
469                 return (max_tp_unit_len);
470                 break;
471
472         case rpc_gss_svc_default:
473         case rpc_gss_svc_integrity:
474                 want_conf = FALSE;
475                 break;
476
477         case rpc_gss_svc_privacy:
478                 want_conf = TRUE;
479                 break;
480
481         default:
482                 return (0);
483         }
484
485         maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
486             client->cl_qop, max_tp_unit_len, &max);
487
488         if (maj_stat == GSS_S_COMPLETE) {
489                 result = (int) max;
490                 if (result < 0)
491                         result = 0;
492                 return (result);
493         } else {
494                 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
495                     maj_stat, min_stat);
496                 return (0);
497         }
498 }
499
500 static struct svc_rpc_gss_client *
501 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
502 {
503         struct svc_rpc_gss_client *client;
504         struct svc_rpc_gss_client_list *list;
505         unsigned long hostid;
506
507         rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
508
509         getcredhostid(curthread->td_ucred, &hostid);
510         if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
511                 return (NULL);
512
513         list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
514         sx_xlock(&svc_rpc_gss_lock);
515         TAILQ_FOREACH(client, list, cl_link) {
516                 if (client->cl_id.ci_id == id->ci_id) {
517                         /*
518                          * Move this client to the front of the LRU
519                          * list.
520                          */
521                         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
522                         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
523                             cl_alllink);
524                         refcount_acquire(&client->cl_refs);
525                         break;
526                 }
527         }
528         sx_xunlock(&svc_rpc_gss_lock);
529
530         return (client);
531 }
532
533 static struct svc_rpc_gss_client *
534 svc_rpc_gss_create_client(void)
535 {
536         struct svc_rpc_gss_client *client;
537         struct svc_rpc_gss_client_list *list;
538         unsigned long hostid;
539
540         rpc_gss_log_debug("in svc_rpc_gss_create_client()");
541
542         client = mem_alloc(sizeof(struct svc_rpc_gss_client));
543         memset(client, 0, sizeof(struct svc_rpc_gss_client));
544         refcount_init(&client->cl_refs, 1);
545         sx_init(&client->cl_lock, "GSS-client");
546         getcredhostid(curthread->td_ucred, &hostid);
547         client->cl_id.ci_hostid = hostid;
548         client->cl_id.ci_boottime = boottime.tv_sec;
549         client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
550         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
551         sx_xlock(&svc_rpc_gss_lock);
552         TAILQ_INSERT_HEAD(list, client, cl_link);
553         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
554         svc_rpc_gss_client_count++;
555         sx_xunlock(&svc_rpc_gss_lock);
556
557         /*
558          * Start the client off with a short expiration time. We will
559          * try to get a saner value from the client creds later.
560          */
561         client->cl_state = CLIENT_NEW;
562         client->cl_locked = FALSE;
563         client->cl_expiration = time_uptime + 5*60;
564
565         return (client);
566 }
567
568 static void
569 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
570 {
571         OM_uint32 min_stat;
572
573         rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
574
575         if (client->cl_ctx)
576                 gss_delete_sec_context(&min_stat,
577                     &client->cl_ctx, GSS_C_NO_BUFFER);
578
579         if (client->cl_cname)
580                 gss_release_name(&min_stat, &client->cl_cname);
581
582         if (client->cl_rawcred.client_principal)
583                 mem_free(client->cl_rawcred.client_principal,
584                     sizeof(*client->cl_rawcred.client_principal)
585                     + client->cl_rawcred.client_principal->len);
586
587         if (client->cl_cred)
588                 crfree(client->cl_cred);
589
590         sx_destroy(&client->cl_lock);
591         mem_free(client, sizeof(*client));
592 }
593
594 /*
595  * Drop a reference to a client and free it if that was the last reference.
596  */
597 static void
598 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
599 {
600
601         if (!refcount_release(&client->cl_refs))
602                 return;
603         svc_rpc_gss_destroy_client(client);
604 }
605
606 /*
607  * Remove a client from our global lists and free it if we can.
608  */
609 static void
610 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
611 {
612         struct svc_rpc_gss_client_list *list;
613
614         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
615         sx_xlock(&svc_rpc_gss_lock);
616         TAILQ_REMOVE(list, client, cl_link);
617         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
618         svc_rpc_gss_client_count--;
619         sx_xunlock(&svc_rpc_gss_lock);
620         svc_rpc_gss_release_client(client);
621 }
622
623 static void
624 svc_rpc_gss_timeout_clients(void)
625 {
626         struct svc_rpc_gss_client *client;
627         struct svc_rpc_gss_client *nclient;
628         time_t now = time_uptime;
629
630         rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
631
632         /*
633          * First enforce the max client limit. We keep
634          * svc_rpc_gss_clients in LRU order.
635          */
636         while (svc_rpc_gss_client_count > CLIENT_MAX)
637                 svc_rpc_gss_forget_client(TAILQ_LAST(&svc_rpc_gss_clients,
638                             svc_rpc_gss_client_list));
639         TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
640                 if (client->cl_state == CLIENT_STALE
641                     || now > client->cl_expiration) {
642                         rpc_gss_log_debug("expiring client %p", client);
643                         svc_rpc_gss_forget_client(client);
644                 }
645         }
646 }
647
648 #ifdef DEBUG
649 /*
650  * OID<->string routines.  These are uuuuugly.
651  */
652 static OM_uint32
653 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
654 {
655         char            numstr[128];
656         unsigned long   number;
657         int             numshift;
658         size_t          string_length;
659         size_t          i;
660         unsigned char   *cp;
661         char            *bp;
662
663         /* Decoded according to krb5/gssapi_krb5.c */
664
665         /* First determine the size of the string */
666         string_length = 0;
667         number = 0;
668         numshift = 0;
669         cp = (unsigned char *) oid->elements;
670         number = (unsigned long) cp[0];
671         sprintf(numstr, "%ld ", number/40);
672         string_length += strlen(numstr);
673         sprintf(numstr, "%ld ", number%40);
674         string_length += strlen(numstr);
675         for (i=1; i<oid->length; i++) {
676                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
677                         number = (number << 7) | (cp[i] & 0x7f);
678                         numshift += 7;
679                 }
680                 else {
681                         *minor_status = 0;
682                         return(GSS_S_FAILURE);
683                 }
684                 if ((cp[i] & 0x80) == 0) {
685                         sprintf(numstr, "%ld ", number);
686                         string_length += strlen(numstr);
687                         number = 0;
688                         numshift = 0;
689                 }
690         }
691         /*
692          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
693          * here for "{ " and "}\0".
694          */
695         string_length += 4;
696         if ((bp = (char *) mem_alloc(string_length))) {
697                 strcpy(bp, "{ ");
698                 number = (unsigned long) cp[0];
699                 sprintf(numstr, "%ld ", number/40);
700                 strcat(bp, numstr);
701                 sprintf(numstr, "%ld ", number%40);
702                 strcat(bp, numstr);
703                 number = 0;
704                 cp = (unsigned char *) oid->elements;
705                 for (i=1; i<oid->length; i++) {
706                         number = (number << 7) | (cp[i] & 0x7f);
707                         if ((cp[i] & 0x80) == 0) {
708                                 sprintf(numstr, "%ld ", number);
709                                 strcat(bp, numstr);
710                                 number = 0;
711                         }
712                 }
713                 strcat(bp, "}");
714                 oid_str->length = strlen(bp)+1;
715                 oid_str->value = (void *) bp;
716                 *minor_status = 0;
717                 return(GSS_S_COMPLETE);
718         }
719         *minor_status = 0;
720         return(GSS_S_FAILURE);
721 }
722 #endif
723
724 static void
725 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
726     const gss_name_t name)
727 {
728         OM_uint32               maj_stat, min_stat;
729         rpc_gss_ucred_t         *uc = &client->cl_ucred;
730         int                     numgroups;
731
732         uc->uid = 65534;
733         uc->gid = 65534;
734         uc->gidlist = client->cl_gid_storage;
735
736         numgroups = NGROUPS;
737         maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
738             &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
739         if (GSS_ERROR(maj_stat))
740                 uc->gidlen = 0;
741         else
742                 uc->gidlen = numgroups;
743 }
744
745 static void
746 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
747 {
748         static gss_OID_desc krb5_mech_oid =
749                 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
750
751         /*
752          * Attempt to translate mech type and service into a
753          * 'pseudo flavor'. Hardwire in krb5 support for now.
754          */
755         if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
756                 switch (client->cl_rawcred.service) {
757                 case rpc_gss_svc_default:
758                 case rpc_gss_svc_none:
759                         client->cl_rpcflavor = RPCSEC_GSS_KRB5;
760                         break;
761                 case rpc_gss_svc_integrity:
762                         client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
763                         break;
764                 case rpc_gss_svc_privacy:
765                         client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
766                         break;
767                 }
768         } else {
769                 client->cl_rpcflavor = RPCSEC_GSS;
770         }
771 }
772
773 static bool_t
774 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
775                                struct svc_req *rqst,
776                                struct rpc_gss_init_res *gr,
777                                struct rpc_gss_cred *gc)
778 {
779         gss_buffer_desc         recv_tok;
780         gss_OID                 mech;
781         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
782         OM_uint32               cred_lifetime;
783         struct svc_rpc_gss_svc_name *sname;
784
785         rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
786         
787         /* Deserialize arguments. */
788         memset(&recv_tok, 0, sizeof(recv_tok));
789         
790         if (!svc_getargs(rqst,
791                 (xdrproc_t) xdr_gss_buffer_desc,
792                 (caddr_t) &recv_tok)) {
793                 client->cl_state = CLIENT_STALE;
794                 return (FALSE);
795         }
796
797         /*
798          * First time round, try all the server names we have until
799          * one matches. Afterwards, stick with that one.
800          */
801         sx_xlock(&svc_rpc_gss_lock);
802         if (!client->cl_sname) {
803                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
804                         if (sname->sn_program == rqst->rq_prog
805                             && sname->sn_version == rqst->rq_vers) {
806                         retry:
807                                 gr->gr_major = gss_accept_sec_context(
808                                         &gr->gr_minor,
809                                         &client->cl_ctx,
810                                         sname->sn_cred,
811                                         &recv_tok,
812                                         GSS_C_NO_CHANNEL_BINDINGS,
813                                         &client->cl_cname,
814                                         &mech,
815                                         &gr->gr_token,
816                                         &ret_flags,
817                                         &cred_lifetime,
818                                         &client->cl_creds);
819                                 if (gr->gr_major == 
820                                     GSS_S_CREDENTIALS_EXPIRED) {
821                                         /*
822                                          * Either our creds really did
823                                          * expire or gssd was
824                                          * restarted.
825                                          */
826                                         if (rpc_gss_acquire_svc_cred(sname))
827                                                 goto retry;
828                                 }
829                                 client->cl_sname = sname;
830                                 break;
831                         }
832                 }
833                 if (!sname) {
834                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
835                             (char *) &recv_tok);
836                         sx_xunlock(&svc_rpc_gss_lock);
837                         return (FALSE);
838                 }
839         } else {
840                 gr->gr_major = gss_accept_sec_context(
841                         &gr->gr_minor,
842                         &client->cl_ctx,
843                         client->cl_sname->sn_cred,
844                         &recv_tok,
845                         GSS_C_NO_CHANNEL_BINDINGS,
846                         &client->cl_cname,
847                         &mech,
848                         &gr->gr_token,
849                         &ret_flags,
850                         &cred_lifetime,
851                         NULL);
852         }
853         sx_xunlock(&svc_rpc_gss_lock);
854         
855         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
856
857         /*
858          * If we get an error from gss_accept_sec_context, send the
859          * reply anyway so that the client gets a chance to see what
860          * is wrong.
861          */
862         if (gr->gr_major != GSS_S_COMPLETE &&
863             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
864                 rpc_gss_log_status("accept_sec_context", client->cl_mech,
865                     gr->gr_major, gr->gr_minor);
866                 client->cl_state = CLIENT_STALE;
867                 return (TRUE);
868         }
869
870         gr->gr_handle.value = &client->cl_id;
871         gr->gr_handle.length = sizeof(client->cl_id);
872         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
873         
874         /* Save client info. */
875         client->cl_mech = mech;
876         client->cl_qop = GSS_C_QOP_DEFAULT;
877         client->cl_done_callback = FALSE;
878
879         if (gr->gr_major == GSS_S_COMPLETE) {
880                 gss_buffer_desc export_name;
881
882                 /*
883                  * Change client expiration time to be near when the
884                  * client creds expire (or 24 hours if we can't figure
885                  * that out).
886                  */
887                 if (cred_lifetime == GSS_C_INDEFINITE)
888                         cred_lifetime = time_uptime + 24*60*60;
889
890                 client->cl_expiration = time_uptime + cred_lifetime;
891
892                 /*
893                  * Fill in cred details in the rawcred structure.
894                  */
895                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
896                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
897                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
898                     &export_name);
899                 if (maj_stat != GSS_S_COMPLETE) {
900                         rpc_gss_log_status("gss_export_name", client->cl_mech,
901                             maj_stat, min_stat);
902                         return (FALSE);
903                 }
904                 client->cl_rawcred.client_principal =
905                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
906                             + export_name.length);
907                 client->cl_rawcred.client_principal->len = export_name.length;
908                 memcpy(client->cl_rawcred.client_principal->name,
909                     export_name.value, export_name.length);
910                 gss_release_buffer(&min_stat, &export_name);
911                 client->cl_rawcred.svc_principal =
912                         client->cl_sname->sn_principal;
913                 client->cl_rawcred.service = gc->gc_svc;
914
915                 /*
916                  * Use gss_pname_to_uid to map to unix creds. For
917                  * kerberos5, this uses krb5_aname_to_localname.
918                  */
919                 svc_rpc_gss_build_ucred(client, client->cl_cname);
920                 svc_rpc_gss_set_flavor(client);
921                 gss_release_name(&min_stat, &client->cl_cname);
922
923 #ifdef DEBUG
924                 {
925                         gss_buffer_desc mechname;
926
927                         gss_oid_to_str(&min_stat, mech, &mechname);
928                         
929                         rpc_gss_log_debug("accepted context for %s with "
930                             "<mech %.*s, qop %d, svc %d>",
931                             client->cl_rawcred.client_principal->name,
932                             mechname.length, (char *)mechname.value,
933                             client->cl_qop, client->rawcred.service);
934
935                         gss_release_buffer(&min_stat, &mechname);
936                 }
937 #endif /* DEBUG */
938         }
939         return (TRUE);
940 }
941
942 static bool_t
943 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
944     gss_qop_t *qop)
945 {
946         struct opaque_auth      *oa;
947         gss_buffer_desc          rpcbuf, checksum;
948         OM_uint32                maj_stat, min_stat;
949         gss_qop_t                qop_state;
950         int32_t                  rpchdr[128 / sizeof(int32_t)];
951         int32_t                 *buf;
952
953         rpc_gss_log_debug("in svc_rpc_gss_validate()");
954         
955         memset(rpchdr, 0, sizeof(rpchdr));
956
957         /* Reconstruct RPC header for signing (from xdr_callmsg). */
958         buf = rpchdr;
959         IXDR_PUT_LONG(buf, msg->rm_xid);
960         IXDR_PUT_ENUM(buf, msg->rm_direction);
961         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
962         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
963         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
964         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
965         oa = &msg->rm_call.cb_cred;
966         IXDR_PUT_ENUM(buf, oa->oa_flavor);
967         IXDR_PUT_LONG(buf, oa->oa_length);
968         if (oa->oa_length) {
969                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
970                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
971         }
972         rpcbuf.value = rpchdr;
973         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
974
975         checksum.value = msg->rm_call.cb_verf.oa_base;
976         checksum.length = msg->rm_call.cb_verf.oa_length;
977         
978         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
979                                   &qop_state);
980         
981         if (maj_stat != GSS_S_COMPLETE) {
982                 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
983                     maj_stat, min_stat);
984                 client->cl_state = CLIENT_STALE;
985                 return (FALSE);
986         }
987
988         *qop = qop_state;
989         return (TRUE);
990 }
991
992 static bool_t
993 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
994     struct svc_req *rqst, u_int seq)
995 {
996         gss_buffer_desc         signbuf;
997         gss_buffer_desc         mic;
998         OM_uint32               maj_stat, min_stat;
999         uint32_t                nseq;       
1000
1001         rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1002
1003         nseq = htonl(seq);
1004         signbuf.value = &nseq;
1005         signbuf.length = sizeof(nseq);
1006
1007         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1008             &signbuf, &mic);
1009
1010         if (maj_stat != GSS_S_COMPLETE) {
1011                 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1012                 client->cl_state = CLIENT_STALE;
1013                 return (FALSE);
1014         }
1015
1016         KASSERT(mic.length <= MAX_AUTH_BYTES,
1017             ("MIC too large for RPCSEC_GSS"));
1018
1019         rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1020         rqst->rq_verf.oa_length = mic.length;
1021         bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1022
1023         gss_release_buffer(&min_stat, &mic);
1024         
1025         return (TRUE);
1026 }
1027
1028 static bool_t
1029 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1030 {
1031         struct svc_rpc_gss_callback *scb;
1032         rpc_gss_lock_t  lock;
1033         void            *cookie;
1034         bool_t          cb_res;
1035         bool_t          result;
1036
1037         /*
1038          * See if we have a callback for this guy.
1039          */
1040         result = TRUE;
1041         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1042                 if (scb->cb_callback.program == rqst->rq_prog
1043                     && scb->cb_callback.version == rqst->rq_vers) {
1044                         /*
1045                          * This one matches. Call the callback and see
1046                          * if it wants to veto or something.
1047                          */
1048                         lock.locked = FALSE;
1049                         lock.raw_cred = &client->cl_rawcred;
1050                         cb_res = scb->cb_callback.callback(rqst,
1051                             client->cl_creds,
1052                             client->cl_ctx,
1053                             &lock,
1054                             &cookie);
1055
1056                         if (!cb_res) {
1057                                 client->cl_state = CLIENT_STALE;
1058                                 result = FALSE;
1059                                 break;
1060                         }
1061
1062                         /*
1063                          * The callback accepted the connection - it
1064                          * is responsible for freeing client->cl_creds
1065                          * now.
1066                          */
1067                         client->cl_creds = GSS_C_NO_CREDENTIAL;
1068                         client->cl_locked = lock.locked;
1069                         client->cl_cookie = cookie;
1070                         return (TRUE);
1071                 }
1072         }
1073
1074         /*
1075          * Either no callback exists for this program/version or one
1076          * of the callbacks rejected the connection. We just need to
1077          * clean up the delegated client creds, if any.
1078          */
1079         if (client->cl_creds) {
1080                 OM_uint32 min_ver;
1081                 gss_release_cred(&min_ver, &client->cl_creds);
1082         }
1083         return (result);
1084 }
1085
1086 static bool_t
1087 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1088 {
1089         u_int32_t offset;
1090         int word, bit;
1091         bool_t result;
1092
1093         sx_xlock(&client->cl_lock);
1094         if (seq <= client->cl_seqlast) {
1095                 /*
1096                  * The request sequence number is less than
1097                  * the largest we have seen so far. If it is
1098                  * outside the window or if we have seen a
1099                  * request with this sequence before, silently
1100                  * discard it.
1101                  */
1102                 offset = client->cl_seqlast - seq;
1103                 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1104                         result = FALSE;
1105                         goto out;
1106                 }
1107                 word = offset / 32;
1108                 bit = offset % 32;
1109                 if (client->cl_seqmask[word] & (1 << bit)) {
1110                         result = FALSE;
1111                         goto out;
1112                 }
1113         }
1114
1115         result = TRUE;
1116 out:
1117         sx_xunlock(&client->cl_lock);
1118         return (result);
1119 }
1120
1121 static void
1122 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1123 {
1124         int offset, i, word, bit;
1125         uint32_t carry, newcarry;
1126
1127         sx_xlock(&client->cl_lock);
1128         if (seq > client->cl_seqlast) {
1129                 /*
1130                  * This request has a sequence number greater
1131                  * than any we have seen so far. Advance the
1132                  * seq window and set bit zero of the window
1133                  * (which corresponds to the new sequence
1134                  * number)
1135                  */
1136                 offset = seq - client->cl_seqlast;
1137                 while (offset > 32) {
1138                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1139                              i > 0; i--) {
1140                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1141                         }
1142                         client->cl_seqmask[0] = 0;
1143                         offset -= 32;
1144                 }
1145                 carry = 0;
1146                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1147                         newcarry = client->cl_seqmask[i] >> (32 - offset);
1148                         client->cl_seqmask[i] =
1149                                 (client->cl_seqmask[i] << offset) | carry;
1150                         carry = newcarry;
1151                 }
1152                 client->cl_seqmask[0] |= 1;
1153                 client->cl_seqlast = seq;
1154         } else {
1155                 offset = client->cl_seqlast - seq;
1156                 word = offset / 32;
1157                 bit = offset % 32;
1158                 client->cl_seqmask[word] |= (1 << bit);
1159         }
1160         sx_xunlock(&client->cl_lock);
1161 }
1162
1163 enum auth_stat
1164 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1165
1166 {
1167         OM_uint32                min_stat;
1168         XDR                      xdrs;
1169         struct svc_rpc_gss_cookedcred *cc;
1170         struct svc_rpc_gss_client *client;
1171         struct rpc_gss_cred      gc;
1172         struct rpc_gss_init_res  gr;
1173         gss_qop_t                qop;
1174         int                      call_stat;
1175         enum auth_stat           result;
1176         
1177         rpc_gss_log_debug("in svc_rpc_gss()");
1178         
1179         /* Garbage collect old clients. */
1180         svc_rpc_gss_timeout_clients();
1181
1182         /* Initialize reply. */
1183         rqst->rq_verf = _null_auth;
1184
1185         /* Deserialize client credentials. */
1186         if (rqst->rq_cred.oa_length <= 0)
1187                 return (AUTH_BADCRED);
1188         
1189         memset(&gc, 0, sizeof(gc));
1190         
1191         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1192             rqst->rq_cred.oa_length, XDR_DECODE);
1193         
1194         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1195                 XDR_DESTROY(&xdrs);
1196                 return (AUTH_BADCRED);
1197         }
1198         XDR_DESTROY(&xdrs);
1199
1200         client = NULL;
1201
1202         /* Check version. */
1203         if (gc.gc_version != RPCSEC_GSS_VERSION) {
1204                 result = AUTH_BADCRED;
1205                 goto out;
1206         }
1207
1208         /* Check the proc and find the client (or create it) */
1209         if (gc.gc_proc == RPCSEC_GSS_INIT) {
1210                 if (gc.gc_handle.length != 0) {
1211                         result = AUTH_BADCRED;
1212                         goto out;
1213                 }
1214                 client = svc_rpc_gss_create_client();
1215                 refcount_acquire(&client->cl_refs);
1216         } else {
1217                 struct svc_rpc_gss_clientid *p;
1218                 if (gc.gc_handle.length != sizeof(*p)) {
1219                         result = AUTH_BADCRED;
1220                         goto out;
1221                 }
1222                 p = gc.gc_handle.value;
1223                 client = svc_rpc_gss_find_client(p);
1224                 if (!client) {
1225                         /*
1226                          * Can't find the client - we may have
1227                          * destroyed it - tell the other side to
1228                          * re-authenticate.
1229                          */
1230                         result = RPCSEC_GSS_CREDPROBLEM;
1231                         goto out;
1232                 }
1233         }
1234         cc = rqst->rq_clntcred;
1235         cc->cc_client = client;
1236         cc->cc_service = gc.gc_svc;
1237         cc->cc_seq = gc.gc_seq;
1238
1239         /*
1240          * The service and sequence number must be ignored for
1241          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1242          */
1243         if (gc.gc_proc != RPCSEC_GSS_INIT
1244             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1245                 /*
1246                  * Check for sequence number overflow.
1247                  */
1248                 if (gc.gc_seq >= MAXSEQ) {
1249                         result = RPCSEC_GSS_CTXPROBLEM;
1250                         goto out;
1251                 }
1252
1253                 /*
1254                  * Check for valid service.
1255                  */
1256                 if (gc.gc_svc != rpc_gss_svc_none &&
1257                     gc.gc_svc != rpc_gss_svc_integrity &&
1258                     gc.gc_svc != rpc_gss_svc_privacy) {
1259                         result = AUTH_BADCRED;
1260                         goto out;
1261                 }
1262         }
1263
1264         /* Handle RPCSEC_GSS control procedure. */
1265         switch (gc.gc_proc) {
1266
1267         case RPCSEC_GSS_INIT:
1268         case RPCSEC_GSS_CONTINUE_INIT:
1269                 if (rqst->rq_proc != NULLPROC) {
1270                         result = AUTH_REJECTEDCRED;
1271                         break;
1272                 }
1273
1274                 memset(&gr, 0, sizeof(gr));
1275                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1276                         result = AUTH_REJECTEDCRED;
1277                         break;
1278                 }
1279
1280                 if (gr.gr_major == GSS_S_COMPLETE) {
1281                         /*
1282                          * We borrow the space for the call verf to
1283                          * pack our reply verf.
1284                          */
1285                         rqst->rq_verf = msg->rm_call.cb_verf;
1286                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1287                                 result = AUTH_REJECTEDCRED;
1288                                 break;
1289                         }
1290                 } else {
1291                         rqst->rq_verf = _null_auth;
1292                 }
1293                 
1294                 call_stat = svc_sendreply(rqst,
1295                     (xdrproc_t) xdr_rpc_gss_init_res,
1296                     (caddr_t) &gr);
1297
1298                 gss_release_buffer(&min_stat, &gr.gr_token);
1299
1300                 if (!call_stat) {
1301                         result = AUTH_FAILED;
1302                         break;
1303                 }
1304
1305                 if (gr.gr_major == GSS_S_COMPLETE)
1306                         client->cl_state = CLIENT_ESTABLISHED;
1307
1308                 result = RPCSEC_GSS_NODISPATCH;
1309                 break;
1310                 
1311         case RPCSEC_GSS_DATA:
1312         case RPCSEC_GSS_DESTROY:
1313                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1314                         result = RPCSEC_GSS_NODISPATCH;
1315                         break;
1316                 }
1317
1318                 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1319                         result = RPCSEC_GSS_CREDPROBLEM;
1320                         break;
1321                 }
1322                 
1323                 /*
1324                  * We borrow the space for the call verf to pack our
1325                  * reply verf.
1326                  */
1327                 rqst->rq_verf = msg->rm_call.cb_verf;
1328                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1329                         result = RPCSEC_GSS_CTXPROBLEM;
1330                         break;
1331                 }
1332
1333                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1334
1335                 /*
1336                  * Change the SVCAUTH ops on the request to point at
1337                  * our own code so that we can unwrap the arguments
1338                  * and wrap the result. The caller will re-set this on
1339                  * every request to point to a set of null wrap/unwrap
1340                  * methods. Acquire an extra reference to the client
1341                  * which will be released by svc_rpc_gss_release()
1342                  * after the request has finished processing.
1343                  */
1344                 refcount_acquire(&client->cl_refs);
1345                 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1346                 rqst->rq_auth.svc_ah_private = cc;
1347
1348                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1349                         /*
1350                          * We might be ready to do a callback to the server to
1351                          * see if it wants to accept/reject the connection.
1352                          */
1353                         sx_xlock(&client->cl_lock);
1354                         if (!client->cl_done_callback) {
1355                                 client->cl_done_callback = TRUE;
1356                                 client->cl_qop = qop;
1357                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1358                                         client->cl_rawcred.mechanism, qop);
1359                                 if (!svc_rpc_gss_callback(client, rqst)) {
1360                                         result = AUTH_REJECTEDCRED;
1361                                         sx_xunlock(&client->cl_lock);
1362                                         break;
1363                                 }
1364                         }
1365                         sx_xunlock(&client->cl_lock);
1366
1367                         /*
1368                          * If the server has locked this client to a
1369                          * particular service+qop pair, enforce that
1370                          * restriction now.
1371                          */
1372                         if (client->cl_locked) {
1373                                 if (client->cl_rawcred.service != gc.gc_svc) {
1374                                         result = AUTH_FAILED;
1375                                         break;
1376                                 } else if (client->cl_qop != qop) {
1377                                         result = AUTH_BADVERF;
1378                                         break;
1379                                 }
1380                         }
1381
1382                         /*
1383                          * If the qop changed, look up the new qop
1384                          * name for rawcred.
1385                          */
1386                         if (client->cl_qop != qop) {
1387                                 client->cl_qop = qop;
1388                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1389                                         client->cl_rawcred.mechanism, qop);
1390                         }
1391
1392                         /*
1393                          * Make sure we use the right service value
1394                          * for unwrap/wrap.
1395                          */
1396                         if (client->cl_rawcred.service != gc.gc_svc) {
1397                                 client->cl_rawcred.service = gc.gc_svc;
1398                                 svc_rpc_gss_set_flavor(client);
1399                         }
1400
1401                         result = AUTH_OK;
1402                 } else {
1403                         if (rqst->rq_proc != NULLPROC) {
1404                                 result = AUTH_REJECTEDCRED;
1405                                 break;
1406                         }
1407
1408                         call_stat = svc_sendreply(rqst,
1409                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1410
1411                         if (!call_stat) {
1412                                 result = AUTH_FAILED;
1413                                 break;
1414                         }
1415
1416                         svc_rpc_gss_forget_client(client);
1417
1418                         result = RPCSEC_GSS_NODISPATCH;
1419                         break;
1420                 }
1421                 break;
1422
1423         default:
1424                 result = AUTH_BADCRED;
1425                 break;
1426         }
1427 out:
1428         if (client)
1429                 svc_rpc_gss_release_client(client);
1430
1431         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1432         return (result);
1433 }
1434
1435 static bool_t
1436 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1437 {
1438         struct svc_rpc_gss_cookedcred *cc;
1439         struct svc_rpc_gss_client *client;
1440         
1441         rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1442
1443         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1444         client = cc->cc_client;
1445         if (client->cl_state != CLIENT_ESTABLISHED
1446             || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1447                 return (TRUE);
1448         }
1449         
1450         return (xdr_rpc_gss_wrap_data(mp,
1451                 client->cl_ctx, client->cl_qop,
1452                 cc->cc_service, cc->cc_seq));
1453 }
1454
1455 static bool_t
1456 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1457 {
1458         struct svc_rpc_gss_cookedcred *cc;
1459         struct svc_rpc_gss_client *client;
1460
1461         rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1462         
1463         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1464         client = cc->cc_client;
1465         if (client->cl_state != CLIENT_ESTABLISHED
1466             || cc->cc_service == rpc_gss_svc_none) {
1467                 return (TRUE);
1468         }
1469
1470         return (xdr_rpc_gss_unwrap_data(mp,
1471                 client->cl_ctx, client->cl_qop,
1472                 cc->cc_service, cc->cc_seq));
1473 }
1474
1475 static void
1476 svc_rpc_gss_release(SVCAUTH *auth)
1477 {
1478         struct svc_rpc_gss_cookedcred *cc;
1479         struct svc_rpc_gss_client *client;
1480
1481         rpc_gss_log_debug("in svc_rpc_gss_release()");
1482
1483         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1484         client = cc->cc_client;
1485         svc_rpc_gss_release_client(client);
1486 }