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