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 KGSS_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 KGSS_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 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash);
199 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients);
200 KGSS_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 KGSS_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(&KGSS_VNET(svc_rpc_gss_client_hash)[i]);
231 TAILQ_INIT(&KGSS_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(KGSS_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(&KGSS_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, &KGSS_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(&KGSS_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(&KGSS_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, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) {
360 if (sname->sn_program == program
361 && sname->sn_version == version) {
362 SLIST_REMOVE(&KGSS_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_fallback(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 = &KGSS_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(&KGSS_VNET(svc_rpc_gss_clients), client,
616 cl_alllink);
617 TAILQ_INSERT_HEAD(&KGSS_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 = KGSS_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 = &KGSS_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(&KGSS_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 = &KGSS_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(&KGSS_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 = &KGSS_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(&KGSS_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(&KGSS_VNET(svc_rpc_gss_clients),
775 svc_rpc_gss_client_list);
776 }
777 again:
778 TAILQ_FOREACH(client, &KGSS_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
929 rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
930
931 /* Deserialize arguments. */
932 memset(&recv_tok, 0, sizeof(recv_tok));
933
934 if (!svc_getargs(rqst,
935 (xdrproc_t) xdr_gss_buffer_desc,
936 (caddr_t) &recv_tok)) {
937 client->cl_state = CLIENT_STALE;
938 return (FALSE);
939 }
940
941 /*
942 * First time round, try all the server names we have until
943 * one matches. Afterwards, stick with that one.
944 */
945 sx_xlock(&svc_rpc_gss_lock);
946 if (!client->cl_sname) {
947 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names),
948 sn_link) {
949 if (sname->sn_program == rqst->rq_prog
950 && sname->sn_version == rqst->rq_vers) {
951 retry:
952 gr->gr_major = gss_accept_sec_context(
953 &gr->gr_minor,
954 &client->cl_ctx,
955 sname->sn_cred,
956 &recv_tok,
957 GSS_C_NO_CHANNEL_BINDINGS,
958 &client->cl_cname,
959 &mech,
960 &gr->gr_token,
961 &ret_flags,
962 &cred_lifetime,
963 &client->cl_creds);
964 if (gr->gr_major ==
965 GSS_S_CREDENTIALS_EXPIRED) {
966 /*
967 * Either our creds really did
968 * expire or gssd was
969 * restarted.
970 */
971 if (rpc_gss_acquire_svc_cred(sname))
972 goto retry;
973 }
974 client->cl_sname = sname;
975 break;
976 }
977 }
978 if (!sname) {
979 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
980 (char *) &recv_tok);
981 sx_xunlock(&svc_rpc_gss_lock);
982 return (FALSE);
983 }
984 } else {
985 gr->gr_major = gss_accept_sec_context(
986 &gr->gr_minor,
987 &client->cl_ctx,
988 client->cl_sname->sn_cred,
989 &recv_tok,
990 GSS_C_NO_CHANNEL_BINDINGS,
991 &client->cl_cname,
992 &mech,
993 &gr->gr_token,
994 &ret_flags,
995 &cred_lifetime,
996 NULL);
997 }
998 sx_xunlock(&svc_rpc_gss_lock);
999
1000 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
1001
1002 /*
1003 * If we get an error from gss_accept_sec_context, send the
1004 * reply anyway so that the client gets a chance to see what
1005 * is wrong.
1006 */
1007 if (gr->gr_major != GSS_S_COMPLETE &&
1008 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
1009 rpc_gss_log_status("accept_sec_context", client->cl_mech,
1010 gr->gr_major, gr->gr_minor);
1011 client->cl_state = CLIENT_STALE;
1012 return (TRUE);
1013 }
1014
1015 gr->gr_handle.value = &client->cl_id;
1016 gr->gr_handle.length = sizeof(client->cl_id);
1017 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
1018
1019 /* Save client info. */
1020 client->cl_mech = mech;
1021 client->cl_qop = GSS_C_QOP_DEFAULT;
1022 client->cl_done_callback = FALSE;
1023
1024 if (gr->gr_major == GSS_S_COMPLETE) {
1025 gss_buffer_desc export_name;
1026
1027 /*
1028 * Change client expiration time to be near when the
1029 * client creds expire (or 24 hours if we can't figure
1030 * that out).
1031 */
1032 if (cred_lifetime == GSS_C_INDEFINITE)
1033 cred_lifetime = 24*60*60;
1034
1035 /*
1036 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
1037 */
1038 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
1039 svc_rpc_gss_lifetime_max)
1040 cred_lifetime = svc_rpc_gss_lifetime_max;
1041
1042 client->cl_expiration = time_uptime + cred_lifetime;
1043
1044 /*
1045 * Fill in cred details in the rawcred structure.
1046 */
1047 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
1048 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
1049 maj_stat = gss_export_name(&min_stat, client->cl_cname,
1050 &export_name);
1051 if (maj_stat != GSS_S_COMPLETE) {
1052 rpc_gss_log_status("gss_export_name", client->cl_mech,
1053 maj_stat, min_stat);
1054 return (FALSE);
1055 }
1056 client->cl_rawcred.client_principal =
1057 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
1058 + export_name.length);
1059 client->cl_rawcred.client_principal->len = export_name.length;
1060 memcpy(client->cl_rawcred.client_principal->name,
1061 export_name.value, export_name.length);
1062 gss_release_buffer(&min_stat, &export_name);
1063 client->cl_rawcred.svc_principal =
1064 client->cl_sname->sn_principal;
1065 client->cl_rawcred.service = gc->gc_svc;
1066
1067 /*
1068 * Use gss_pname_to_uid to map to unix creds. For
1069 * kerberos5, this uses krb5_aname_to_localname.
1070 */
1071 svc_rpc_gss_build_ucred(client, client->cl_cname);
1072 svc_rpc_gss_set_flavor(client);
1073 gss_release_name(&min_stat, &client->cl_cname);
1074
1075 #ifdef DEBUG
1076 {
1077 gss_buffer_desc mechname;
1078
1079 gss_oid_to_str(&min_stat, mech, &mechname);
1080
1081 rpc_gss_log_debug("accepted context for %s with "
1082 "<mech %.*s, qop %d, svc %d>",
1083 client->cl_rawcred.client_principal->name,
1084 mechname.length, (char *)mechname.value,
1085 client->cl_qop, client->cl_rawcred.service);
1086
1087 gss_release_buffer(&min_stat, &mechname);
1088 }
1089 #endif /* DEBUG */
1090 }
1091 return (TRUE);
1092 }
1093
1094 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)1095 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1096 gss_qop_t *qop, rpc_gss_proc_t gcproc)
1097 {
1098 struct opaque_auth *oa;
1099 gss_buffer_desc rpcbuf, checksum;
1100 OM_uint32 maj_stat, min_stat;
1101 gss_qop_t qop_state;
1102 int32_t rpchdr[128 / sizeof(int32_t)];
1103 int32_t *buf;
1104
1105 rpc_gss_log_debug("in svc_rpc_gss_validate()");
1106
1107 memset(rpchdr, 0, sizeof(rpchdr));
1108
1109 /* Reconstruct RPC header for signing (from xdr_callmsg). */
1110 buf = rpchdr;
1111 IXDR_PUT_LONG(buf, msg->rm_xid);
1112 IXDR_PUT_ENUM(buf, msg->rm_direction);
1113 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1114 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1115 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1116 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1117 oa = &msg->rm_call.cb_cred;
1118 IXDR_PUT_ENUM(buf, oa->oa_flavor);
1119 IXDR_PUT_LONG(buf, oa->oa_length);
1120 if (oa->oa_length) {
1121 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1122 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1123 }
1124 rpcbuf.value = rpchdr;
1125 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1126
1127 checksum.value = msg->rm_call.cb_verf.oa_base;
1128 checksum.length = msg->rm_call.cb_verf.oa_length;
1129
1130 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1131 &qop_state);
1132
1133 if (maj_stat != GSS_S_COMPLETE) {
1134 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1135 maj_stat, min_stat);
1136 /*
1137 * A bug in some versions of the Linux client generates a
1138 * Destroy operation with a bogus encrypted checksum. Deleting
1139 * the credential handle for that case causes the mount to fail.
1140 * Since the checksum is bogus (gss_verify_mic() failed), it
1141 * doesn't make sense to destroy the handle and not doing so
1142 * fixes the Linux mount.
1143 */
1144 if (gcproc != RPCSEC_GSS_DESTROY)
1145 client->cl_state = CLIENT_STALE;
1146 return (FALSE);
1147 }
1148
1149 *qop = qop_state;
1150 return (TRUE);
1151 }
1152
1153 static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)1154 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1155 struct svc_req *rqst, u_int seq)
1156 {
1157 gss_buffer_desc signbuf;
1158 gss_buffer_desc mic;
1159 OM_uint32 maj_stat, min_stat;
1160 uint32_t nseq;
1161
1162 rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1163
1164 nseq = htonl(seq);
1165 signbuf.value = &nseq;
1166 signbuf.length = sizeof(nseq);
1167
1168 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1169 &signbuf, &mic);
1170
1171 if (maj_stat != GSS_S_COMPLETE) {
1172 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1173 client->cl_state = CLIENT_STALE;
1174 return (FALSE);
1175 }
1176
1177 KASSERT(mic.length <= MAX_AUTH_BYTES,
1178 ("MIC too large for RPCSEC_GSS"));
1179
1180 rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1181 rqst->rq_verf.oa_length = mic.length;
1182 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1183
1184 gss_release_buffer(&min_stat, &mic);
1185
1186 return (TRUE);
1187 }
1188
1189 static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)1190 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1191 {
1192 struct svc_rpc_gss_callback *scb;
1193 rpc_gss_lock_t lock;
1194 void *cookie;
1195 bool_t cb_res;
1196 bool_t result;
1197
1198 /*
1199 * See if we have a callback for this guy.
1200 */
1201 result = TRUE;
1202 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
1203 if (scb->cb_callback.program == rqst->rq_prog
1204 && scb->cb_callback.version == rqst->rq_vers) {
1205 /*
1206 * This one matches. Call the callback and see
1207 * if it wants to veto or something.
1208 */
1209 lock.locked = FALSE;
1210 lock.raw_cred = &client->cl_rawcred;
1211 cb_res = scb->cb_callback.callback(rqst,
1212 client->cl_creds,
1213 client->cl_ctx,
1214 &lock,
1215 &cookie);
1216
1217 if (!cb_res) {
1218 client->cl_state = CLIENT_STALE;
1219 result = FALSE;
1220 break;
1221 }
1222
1223 /*
1224 * The callback accepted the connection - it
1225 * is responsible for freeing client->cl_creds
1226 * now.
1227 */
1228 client->cl_creds = GSS_C_NO_CREDENTIAL;
1229 client->cl_locked = lock.locked;
1230 client->cl_cookie = cookie;
1231 return (TRUE);
1232 }
1233 }
1234
1235 /*
1236 * Either no callback exists for this program/version or one
1237 * of the callbacks rejected the connection. We just need to
1238 * clean up the delegated client creds, if any.
1239 */
1240 if (client->cl_creds) {
1241 OM_uint32 min_ver;
1242 gss_release_cred(&min_ver, &client->cl_creds);
1243 }
1244 return (result);
1245 }
1246
1247 static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)1248 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1249 {
1250 uint32_t offset;
1251 int word, bit;
1252 bool_t result;
1253
1254 sx_xlock(&client->cl_lock);
1255 if (seq <= client->cl_seqlast) {
1256 /*
1257 * The request sequence number is less than
1258 * the largest we have seen so far. If it is
1259 * outside the window or if we have seen a
1260 * request with this sequence before, silently
1261 * discard it.
1262 */
1263 offset = client->cl_seqlast - seq;
1264 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1265 result = FALSE;
1266 goto out;
1267 }
1268 word = offset / 32;
1269 bit = offset % 32;
1270 if (client->cl_seqmask[word] & (1 << bit)) {
1271 result = FALSE;
1272 goto out;
1273 }
1274 }
1275
1276 result = TRUE;
1277 out:
1278 sx_xunlock(&client->cl_lock);
1279 return (result);
1280 }
1281
1282 static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)1283 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1284 {
1285 int offset, i, word, bit;
1286 uint32_t carry, newcarry;
1287
1288 sx_xlock(&client->cl_lock);
1289 if (seq > client->cl_seqlast) {
1290 /*
1291 * This request has a sequence number greater
1292 * than any we have seen so far. Advance the
1293 * seq window and set bit zero of the window
1294 * (which corresponds to the new sequence
1295 * number)
1296 */
1297 offset = seq - client->cl_seqlast;
1298 while (offset > 32) {
1299 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1300 i > 0; i--) {
1301 client->cl_seqmask[i] = client->cl_seqmask[i-1];
1302 }
1303 client->cl_seqmask[0] = 0;
1304 offset -= 32;
1305 }
1306 carry = 0;
1307 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1308 newcarry = client->cl_seqmask[i] >> (32 - offset);
1309 client->cl_seqmask[i] =
1310 (client->cl_seqmask[i] << offset) | carry;
1311 carry = newcarry;
1312 }
1313 client->cl_seqmask[0] |= 1;
1314 client->cl_seqlast = seq;
1315 } else {
1316 offset = client->cl_seqlast - seq;
1317 word = offset / 32;
1318 bit = offset % 32;
1319 client->cl_seqmask[word] |= (1 << bit);
1320 }
1321 sx_xunlock(&client->cl_lock);
1322 }
1323
1324 enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)1325 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1326
1327 {
1328 OM_uint32 min_stat;
1329 XDR xdrs;
1330 struct svc_rpc_gss_cookedcred *cc;
1331 struct svc_rpc_gss_client *client;
1332 struct rpc_gss_cred gc;
1333 struct rpc_gss_init_res gr;
1334 gss_qop_t qop;
1335 int call_stat;
1336 enum auth_stat result;
1337
1338 KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
1339 rpc_gss_log_debug("in svc_rpc_gss()");
1340
1341 /* Garbage collect old clients. */
1342 svc_rpc_gss_timeout_clients();
1343
1344 /* Initialize reply. */
1345 rqst->rq_verf = _null_auth;
1346
1347 /* Deserialize client credentials. */
1348 if (rqst->rq_cred.oa_length <= 0) {
1349 KGSS_CURVNET_RESTORE();
1350 return (AUTH_BADCRED);
1351 }
1352
1353 memset(&gc, 0, sizeof(gc));
1354
1355 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1356 rqst->rq_cred.oa_length, XDR_DECODE);
1357
1358 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1359 XDR_DESTROY(&xdrs);
1360 KGSS_CURVNET_RESTORE();
1361 return (AUTH_BADCRED);
1362 }
1363 XDR_DESTROY(&xdrs);
1364
1365 client = NULL;
1366
1367 /* Check version. */
1368 if (gc.gc_version != RPCSEC_GSS_VERSION) {
1369 result = AUTH_BADCRED;
1370 goto out;
1371 }
1372
1373 /* Check the proc and find the client (or create it) */
1374 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1375 if (gc.gc_handle.length != 0) {
1376 result = AUTH_BADCRED;
1377 goto out;
1378 }
1379 client = svc_rpc_gss_create_client();
1380 } else {
1381 struct svc_rpc_gss_clientid *p;
1382 if (gc.gc_handle.length != sizeof(*p)) {
1383 result = AUTH_BADCRED;
1384 goto out;
1385 }
1386 p = gc.gc_handle.value;
1387 client = svc_rpc_gss_find_client(p);
1388 if (!client) {
1389 /*
1390 * Can't find the client - we may have
1391 * destroyed it - tell the other side to
1392 * re-authenticate.
1393 */
1394 result = RPCSEC_GSS_CREDPROBLEM;
1395 goto out;
1396 }
1397 }
1398 cc = rqst->rq_clntcred;
1399 cc->cc_client = client;
1400 cc->cc_service = gc.gc_svc;
1401 cc->cc_seq = gc.gc_seq;
1402
1403 /*
1404 * The service and sequence number must be ignored for
1405 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1406 */
1407 if (gc.gc_proc != RPCSEC_GSS_INIT
1408 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1409 /*
1410 * Check for sequence number overflow.
1411 */
1412 if (gc.gc_seq >= MAXSEQ) {
1413 result = RPCSEC_GSS_CTXPROBLEM;
1414 goto out;
1415 }
1416
1417 /*
1418 * Check for valid service.
1419 */
1420 if (gc.gc_svc != rpc_gss_svc_none &&
1421 gc.gc_svc != rpc_gss_svc_integrity &&
1422 gc.gc_svc != rpc_gss_svc_privacy) {
1423 result = AUTH_BADCRED;
1424 goto out;
1425 }
1426 }
1427
1428 /* Handle RPCSEC_GSS control procedure. */
1429 switch (gc.gc_proc) {
1430
1431 case RPCSEC_GSS_INIT:
1432 case RPCSEC_GSS_CONTINUE_INIT:
1433 if (rqst->rq_proc != NULLPROC) {
1434 result = AUTH_REJECTEDCRED;
1435 break;
1436 }
1437
1438 memset(&gr, 0, sizeof(gr));
1439 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1440 result = AUTH_REJECTEDCRED;
1441 break;
1442 }
1443
1444 if (gr.gr_major == GSS_S_COMPLETE) {
1445 /*
1446 * We borrow the space for the call verf to
1447 * pack our reply verf.
1448 */
1449 rqst->rq_verf = msg->rm_call.cb_verf;
1450 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1451 result = AUTH_REJECTEDCRED;
1452 break;
1453 }
1454 } else {
1455 rqst->rq_verf = _null_auth;
1456 }
1457
1458 call_stat = svc_sendreply(rqst,
1459 (xdrproc_t) xdr_rpc_gss_init_res,
1460 (caddr_t) &gr);
1461
1462 gss_release_buffer(&min_stat, &gr.gr_token);
1463
1464 if (!call_stat) {
1465 result = AUTH_FAILED;
1466 break;
1467 }
1468
1469 if (gr.gr_major == GSS_S_COMPLETE)
1470 client->cl_state = CLIENT_ESTABLISHED;
1471
1472 result = RPCSEC_GSS_NODISPATCH;
1473 break;
1474
1475 case RPCSEC_GSS_DATA:
1476 case RPCSEC_GSS_DESTROY:
1477 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1478 result = RPCSEC_GSS_NODISPATCH;
1479 break;
1480 }
1481
1482 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1483 result = RPCSEC_GSS_CREDPROBLEM;
1484 break;
1485 }
1486
1487 /*
1488 * We borrow the space for the call verf to pack our
1489 * reply verf.
1490 */
1491 rqst->rq_verf = msg->rm_call.cb_verf;
1492 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1493 result = RPCSEC_GSS_CTXPROBLEM;
1494 break;
1495 }
1496
1497 svc_rpc_gss_update_seq(client, gc.gc_seq);
1498
1499 /*
1500 * Change the SVCAUTH ops on the request to point at
1501 * our own code so that we can unwrap the arguments
1502 * and wrap the result. The caller will re-set this on
1503 * every request to point to a set of null wrap/unwrap
1504 * methods. Acquire an extra reference to the client
1505 * which will be released by svc_rpc_gss_release()
1506 * after the request has finished processing.
1507 */
1508 refcount_acquire(&client->cl_refs);
1509 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1510 rqst->rq_auth.svc_ah_private = cc;
1511
1512 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1513 /*
1514 * We might be ready to do a callback to the server to
1515 * see if it wants to accept/reject the connection.
1516 */
1517 sx_xlock(&client->cl_lock);
1518 if (!client->cl_done_callback) {
1519 client->cl_done_callback = TRUE;
1520 client->cl_qop = qop;
1521 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1522 client->cl_rawcred.mechanism, qop);
1523 if (!svc_rpc_gss_callback(client, rqst)) {
1524 result = AUTH_REJECTEDCRED;
1525 sx_xunlock(&client->cl_lock);
1526 break;
1527 }
1528 }
1529 sx_xunlock(&client->cl_lock);
1530
1531 /*
1532 * If the server has locked this client to a
1533 * particular service+qop pair, enforce that
1534 * restriction now.
1535 */
1536 if (client->cl_locked) {
1537 if (client->cl_rawcred.service != gc.gc_svc) {
1538 result = AUTH_FAILED;
1539 break;
1540 } else if (client->cl_qop != qop) {
1541 result = AUTH_BADVERF;
1542 break;
1543 }
1544 }
1545
1546 /*
1547 * If the qop changed, look up the new qop
1548 * name for rawcred.
1549 */
1550 if (client->cl_qop != qop) {
1551 client->cl_qop = qop;
1552 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1553 client->cl_rawcred.mechanism, qop);
1554 }
1555
1556 /*
1557 * Make sure we use the right service value
1558 * for unwrap/wrap.
1559 */
1560 if (client->cl_rawcred.service != gc.gc_svc) {
1561 client->cl_rawcred.service = gc.gc_svc;
1562 svc_rpc_gss_set_flavor(client);
1563 }
1564
1565 result = AUTH_OK;
1566 } else {
1567 if (rqst->rq_proc != NULLPROC) {
1568 result = AUTH_REJECTEDCRED;
1569 break;
1570 }
1571
1572 call_stat = svc_sendreply(rqst,
1573 (xdrproc_t) xdr_void, (caddr_t) NULL);
1574
1575 if (!call_stat) {
1576 result = AUTH_FAILED;
1577 break;
1578 }
1579
1580 svc_rpc_gss_forget_client(client);
1581
1582 result = RPCSEC_GSS_NODISPATCH;
1583 break;
1584 }
1585 break;
1586
1587 default:
1588 result = AUTH_BADCRED;
1589 break;
1590 }
1591 out:
1592 if (client)
1593 svc_rpc_gss_release_client(client);
1594
1595 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1596 KGSS_CURVNET_RESTORE();
1597 return (result);
1598 }
1599
1600 static bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,struct mbuf ** mp)1601 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1602 {
1603 struct svc_rpc_gss_cookedcred *cc;
1604 struct svc_rpc_gss_client *client;
1605
1606 rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1607
1608 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1609 client = cc->cc_client;
1610 if (client->cl_state != CLIENT_ESTABLISHED
1611 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1612 return (TRUE);
1613 }
1614
1615 return (xdr_rpc_gss_wrap_data(mp,
1616 client->cl_ctx, client->cl_qop,
1617 cc->cc_service, cc->cc_seq));
1618 }
1619
1620 static bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,struct mbuf ** mp)1621 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1622 {
1623 struct svc_rpc_gss_cookedcred *cc;
1624 struct svc_rpc_gss_client *client;
1625
1626 rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1627
1628 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1629 client = cc->cc_client;
1630 if (client->cl_state != CLIENT_ESTABLISHED
1631 || cc->cc_service == rpc_gss_svc_none) {
1632 return (TRUE);
1633 }
1634
1635 return (xdr_rpc_gss_unwrap_data(mp,
1636 client->cl_ctx, client->cl_qop,
1637 cc->cc_service, cc->cc_seq));
1638 }
1639
1640 static void
svc_rpc_gss_release(SVCAUTH * auth)1641 svc_rpc_gss_release(SVCAUTH *auth)
1642 {
1643 struct svc_rpc_gss_cookedcred *cc;
1644 struct svc_rpc_gss_client *client;
1645
1646 rpc_gss_log_debug("in svc_rpc_gss_release()");
1647
1648 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1649 client = cc->cc_client;
1650 svc_rpc_gss_release_client(client);
1651 }
1652