Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
39564 views
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 <[email protected]>.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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, &KGSS_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
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
/* Reconstruct RPC header for signing (from xdr_callmsg). */
1174
buf = rpchdr;
1175
IXDR_PUT_LONG(buf, msg->rm_xid);
1176
IXDR_PUT_ENUM(buf, msg->rm_direction);
1177
IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1178
IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1179
IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1180
IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1181
oa = &msg->rm_call.cb_cred;
1182
IXDR_PUT_ENUM(buf, oa->oa_flavor);
1183
IXDR_PUT_LONG(buf, oa->oa_length);
1184
if (oa->oa_length) {
1185
memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1186
buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1187
}
1188
rpcbuf.value = rpchdr;
1189
rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1190
1191
checksum.value = msg->rm_call.cb_verf.oa_base;
1192
checksum.length = msg->rm_call.cb_verf.oa_length;
1193
1194
maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1195
&qop_state);
1196
1197
if (maj_stat != GSS_S_COMPLETE) {
1198
rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1199
maj_stat, min_stat);
1200
/*
1201
* A bug in some versions of the Linux client generates a
1202
* Destroy operation with a bogus encrypted checksum. Deleting
1203
* the credential handle for that case causes the mount to fail.
1204
* Since the checksum is bogus (gss_verify_mic() failed), it
1205
* doesn't make sense to destroy the handle and not doing so
1206
* fixes the Linux mount.
1207
*/
1208
if (gcproc != RPCSEC_GSS_DESTROY)
1209
client->cl_state = CLIENT_STALE;
1210
return (FALSE);
1211
}
1212
1213
*qop = qop_state;
1214
return (TRUE);
1215
}
1216
1217
static bool_t
1218
svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1219
struct svc_req *rqst, u_int seq)
1220
{
1221
gss_buffer_desc signbuf;
1222
gss_buffer_desc mic;
1223
OM_uint32 maj_stat, min_stat;
1224
uint32_t nseq;
1225
1226
rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1227
1228
nseq = htonl(seq);
1229
signbuf.value = &nseq;
1230
signbuf.length = sizeof(nseq);
1231
1232
maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1233
&signbuf, &mic);
1234
1235
if (maj_stat != GSS_S_COMPLETE) {
1236
rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1237
client->cl_state = CLIENT_STALE;
1238
return (FALSE);
1239
}
1240
1241
KASSERT(mic.length <= MAX_AUTH_BYTES,
1242
("MIC too large for RPCSEC_GSS"));
1243
1244
rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1245
rqst->rq_verf.oa_length = mic.length;
1246
bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1247
1248
gss_release_buffer(&min_stat, &mic);
1249
1250
return (TRUE);
1251
}
1252
1253
static bool_t
1254
svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1255
{
1256
struct svc_rpc_gss_callback *scb;
1257
rpc_gss_lock_t lock;
1258
void *cookie;
1259
bool_t cb_res;
1260
bool_t result;
1261
1262
/*
1263
* See if we have a callback for this guy.
1264
*/
1265
result = TRUE;
1266
SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
1267
if (scb->cb_callback.program == rqst->rq_prog
1268
&& scb->cb_callback.version == rqst->rq_vers) {
1269
/*
1270
* This one matches. Call the callback and see
1271
* if it wants to veto or something.
1272
*/
1273
lock.locked = FALSE;
1274
lock.raw_cred = &client->cl_rawcred;
1275
cb_res = scb->cb_callback.callback(rqst,
1276
client->cl_creds,
1277
client->cl_ctx,
1278
&lock,
1279
&cookie);
1280
1281
if (!cb_res) {
1282
client->cl_state = CLIENT_STALE;
1283
result = FALSE;
1284
break;
1285
}
1286
1287
/*
1288
* The callback accepted the connection - it
1289
* is responsible for freeing client->cl_creds
1290
* now.
1291
*/
1292
client->cl_creds = GSS_C_NO_CREDENTIAL;
1293
client->cl_locked = lock.locked;
1294
client->cl_cookie = cookie;
1295
return (TRUE);
1296
}
1297
}
1298
1299
/*
1300
* Either no callback exists for this program/version or one
1301
* of the callbacks rejected the connection. We just need to
1302
* clean up the delegated client creds, if any.
1303
*/
1304
if (client->cl_creds) {
1305
OM_uint32 min_ver;
1306
gss_release_cred(&min_ver, &client->cl_creds);
1307
}
1308
return (result);
1309
}
1310
1311
static bool_t
1312
svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1313
{
1314
uint32_t offset;
1315
int word, bit;
1316
bool_t result;
1317
1318
sx_xlock(&client->cl_lock);
1319
if (seq <= client->cl_seqlast) {
1320
/*
1321
* The request sequence number is less than
1322
* the largest we have seen so far. If it is
1323
* outside the window or if we have seen a
1324
* request with this sequence before, silently
1325
* discard it.
1326
*/
1327
offset = client->cl_seqlast - seq;
1328
if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1329
result = FALSE;
1330
goto out;
1331
}
1332
word = offset / 32;
1333
bit = offset % 32;
1334
if (client->cl_seqmask[word] & (1 << bit)) {
1335
result = FALSE;
1336
goto out;
1337
}
1338
}
1339
1340
result = TRUE;
1341
out:
1342
sx_xunlock(&client->cl_lock);
1343
return (result);
1344
}
1345
1346
static void
1347
svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1348
{
1349
int offset, i, word, bit;
1350
uint32_t carry, newcarry;
1351
1352
sx_xlock(&client->cl_lock);
1353
if (seq > client->cl_seqlast) {
1354
/*
1355
* This request has a sequence number greater
1356
* than any we have seen so far. Advance the
1357
* seq window and set bit zero of the window
1358
* (which corresponds to the new sequence
1359
* number)
1360
*/
1361
offset = seq - client->cl_seqlast;
1362
while (offset > 32) {
1363
for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1364
i > 0; i--) {
1365
client->cl_seqmask[i] = client->cl_seqmask[i-1];
1366
}
1367
client->cl_seqmask[0] = 0;
1368
offset -= 32;
1369
}
1370
carry = 0;
1371
for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1372
newcarry = client->cl_seqmask[i] >> (32 - offset);
1373
client->cl_seqmask[i] =
1374
(client->cl_seqmask[i] << offset) | carry;
1375
carry = newcarry;
1376
}
1377
client->cl_seqmask[0] |= 1;
1378
client->cl_seqlast = seq;
1379
} else {
1380
offset = client->cl_seqlast - seq;
1381
word = offset / 32;
1382
bit = offset % 32;
1383
client->cl_seqmask[word] |= (1 << bit);
1384
}
1385
sx_xunlock(&client->cl_lock);
1386
}
1387
1388
enum auth_stat
1389
svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1390
1391
{
1392
OM_uint32 min_stat;
1393
XDR xdrs;
1394
struct svc_rpc_gss_cookedcred *cc;
1395
struct svc_rpc_gss_client *client;
1396
struct rpc_gss_cred gc;
1397
struct rpc_gss_init_res gr;
1398
gss_qop_t qop;
1399
int call_stat;
1400
enum auth_stat result;
1401
1402
KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
1403
rpc_gss_log_debug("in svc_rpc_gss()");
1404
1405
/* Garbage collect old clients. */
1406
svc_rpc_gss_timeout_clients();
1407
1408
/* Initialize reply. */
1409
rqst->rq_verf = _null_auth;
1410
1411
/* Deserialize client credentials. */
1412
if (rqst->rq_cred.oa_length <= 0) {
1413
KGSS_CURVNET_RESTORE();
1414
return (AUTH_BADCRED);
1415
}
1416
1417
memset(&gc, 0, sizeof(gc));
1418
1419
xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1420
rqst->rq_cred.oa_length, XDR_DECODE);
1421
1422
if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1423
XDR_DESTROY(&xdrs);
1424
KGSS_CURVNET_RESTORE();
1425
return (AUTH_BADCRED);
1426
}
1427
XDR_DESTROY(&xdrs);
1428
1429
client = NULL;
1430
1431
/* Check version. */
1432
if (gc.gc_version != RPCSEC_GSS_VERSION) {
1433
result = AUTH_BADCRED;
1434
goto out;
1435
}
1436
1437
/* Check the proc and find the client (or create it) */
1438
if (gc.gc_proc == RPCSEC_GSS_INIT) {
1439
if (gc.gc_handle.length != 0) {
1440
result = AUTH_BADCRED;
1441
goto out;
1442
}
1443
client = svc_rpc_gss_create_client();
1444
} else {
1445
struct svc_rpc_gss_clientid *p;
1446
if (gc.gc_handle.length != sizeof(*p)) {
1447
result = AUTH_BADCRED;
1448
goto out;
1449
}
1450
p = gc.gc_handle.value;
1451
client = svc_rpc_gss_find_client(p);
1452
if (!client) {
1453
/*
1454
* Can't find the client - we may have
1455
* destroyed it - tell the other side to
1456
* re-authenticate.
1457
*/
1458
result = RPCSEC_GSS_CREDPROBLEM;
1459
goto out;
1460
}
1461
}
1462
cc = rqst->rq_clntcred;
1463
cc->cc_client = client;
1464
cc->cc_service = gc.gc_svc;
1465
cc->cc_seq = gc.gc_seq;
1466
1467
/*
1468
* The service and sequence number must be ignored for
1469
* RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1470
*/
1471
if (gc.gc_proc != RPCSEC_GSS_INIT
1472
&& gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1473
/*
1474
* Check for sequence number overflow.
1475
*/
1476
if (gc.gc_seq >= MAXSEQ) {
1477
result = RPCSEC_GSS_CTXPROBLEM;
1478
goto out;
1479
}
1480
1481
/*
1482
* Check for valid service.
1483
*/
1484
if (gc.gc_svc != rpc_gss_svc_none &&
1485
gc.gc_svc != rpc_gss_svc_integrity &&
1486
gc.gc_svc != rpc_gss_svc_privacy) {
1487
result = AUTH_BADCRED;
1488
goto out;
1489
}
1490
}
1491
1492
/* Handle RPCSEC_GSS control procedure. */
1493
switch (gc.gc_proc) {
1494
1495
case RPCSEC_GSS_INIT:
1496
case RPCSEC_GSS_CONTINUE_INIT:
1497
if (rqst->rq_proc != NULLPROC) {
1498
result = AUTH_REJECTEDCRED;
1499
break;
1500
}
1501
1502
memset(&gr, 0, sizeof(gr));
1503
if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1504
result = AUTH_REJECTEDCRED;
1505
break;
1506
}
1507
1508
if (gr.gr_major == GSS_S_COMPLETE) {
1509
/*
1510
* We borrow the space for the call verf to
1511
* pack our reply verf.
1512
*/
1513
rqst->rq_verf = msg->rm_call.cb_verf;
1514
if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1515
result = AUTH_REJECTEDCRED;
1516
break;
1517
}
1518
} else {
1519
rqst->rq_verf = _null_auth;
1520
}
1521
1522
call_stat = svc_sendreply(rqst,
1523
(xdrproc_t) xdr_rpc_gss_init_res,
1524
(caddr_t) &gr);
1525
1526
gss_release_buffer(&min_stat, &gr.gr_token);
1527
1528
if (!call_stat) {
1529
result = AUTH_FAILED;
1530
break;
1531
}
1532
1533
if (gr.gr_major == GSS_S_COMPLETE)
1534
client->cl_state = CLIENT_ESTABLISHED;
1535
1536
result = RPCSEC_GSS_NODISPATCH;
1537
break;
1538
1539
case RPCSEC_GSS_DATA:
1540
case RPCSEC_GSS_DESTROY:
1541
if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1542
result = RPCSEC_GSS_NODISPATCH;
1543
break;
1544
}
1545
1546
if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1547
result = RPCSEC_GSS_CREDPROBLEM;
1548
break;
1549
}
1550
1551
/*
1552
* We borrow the space for the call verf to pack our
1553
* reply verf.
1554
*/
1555
rqst->rq_verf = msg->rm_call.cb_verf;
1556
if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1557
result = RPCSEC_GSS_CTXPROBLEM;
1558
break;
1559
}
1560
1561
svc_rpc_gss_update_seq(client, gc.gc_seq);
1562
1563
/*
1564
* Change the SVCAUTH ops on the request to point at
1565
* our own code so that we can unwrap the arguments
1566
* and wrap the result. The caller will re-set this on
1567
* every request to point to a set of null wrap/unwrap
1568
* methods. Acquire an extra reference to the client
1569
* which will be released by svc_rpc_gss_release()
1570
* after the request has finished processing.
1571
*/
1572
refcount_acquire(&client->cl_refs);
1573
rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1574
rqst->rq_auth.svc_ah_private = cc;
1575
1576
if (gc.gc_proc == RPCSEC_GSS_DATA) {
1577
/*
1578
* We might be ready to do a callback to the server to
1579
* see if it wants to accept/reject the connection.
1580
*/
1581
sx_xlock(&client->cl_lock);
1582
if (!client->cl_done_callback) {
1583
client->cl_done_callback = TRUE;
1584
client->cl_qop = qop;
1585
client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1586
client->cl_rawcred.mechanism, qop);
1587
if (!svc_rpc_gss_callback(client, rqst)) {
1588
result = AUTH_REJECTEDCRED;
1589
sx_xunlock(&client->cl_lock);
1590
break;
1591
}
1592
}
1593
sx_xunlock(&client->cl_lock);
1594
1595
/*
1596
* If the server has locked this client to a
1597
* particular service+qop pair, enforce that
1598
* restriction now.
1599
*/
1600
if (client->cl_locked) {
1601
if (client->cl_rawcred.service != gc.gc_svc) {
1602
result = AUTH_FAILED;
1603
break;
1604
} else if (client->cl_qop != qop) {
1605
result = AUTH_BADVERF;
1606
break;
1607
}
1608
}
1609
1610
/*
1611
* If the qop changed, look up the new qop
1612
* name for rawcred.
1613
*/
1614
if (client->cl_qop != qop) {
1615
client->cl_qop = qop;
1616
client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1617
client->cl_rawcred.mechanism, qop);
1618
}
1619
1620
/*
1621
* Make sure we use the right service value
1622
* for unwrap/wrap.
1623
*/
1624
if (client->cl_rawcred.service != gc.gc_svc) {
1625
client->cl_rawcred.service = gc.gc_svc;
1626
svc_rpc_gss_set_flavor(client);
1627
}
1628
1629
result = AUTH_OK;
1630
} else {
1631
if (rqst->rq_proc != NULLPROC) {
1632
result = AUTH_REJECTEDCRED;
1633
break;
1634
}
1635
1636
call_stat = svc_sendreply(rqst,
1637
(xdrproc_t) xdr_void, (caddr_t) NULL);
1638
1639
if (!call_stat) {
1640
result = AUTH_FAILED;
1641
break;
1642
}
1643
1644
svc_rpc_gss_forget_client(client);
1645
1646
result = RPCSEC_GSS_NODISPATCH;
1647
break;
1648
}
1649
break;
1650
1651
default:
1652
result = AUTH_BADCRED;
1653
break;
1654
}
1655
out:
1656
if (client)
1657
svc_rpc_gss_release_client(client);
1658
1659
xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1660
KGSS_CURVNET_RESTORE();
1661
return (result);
1662
}
1663
1664
static bool_t
1665
svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1666
{
1667
struct svc_rpc_gss_cookedcred *cc;
1668
struct svc_rpc_gss_client *client;
1669
1670
rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1671
1672
cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1673
client = cc->cc_client;
1674
if (client->cl_state != CLIENT_ESTABLISHED
1675
|| cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1676
return (TRUE);
1677
}
1678
1679
return (xdr_rpc_gss_wrap_data(mp,
1680
client->cl_ctx, client->cl_qop,
1681
cc->cc_service, cc->cc_seq));
1682
}
1683
1684
static bool_t
1685
svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1686
{
1687
struct svc_rpc_gss_cookedcred *cc;
1688
struct svc_rpc_gss_client *client;
1689
1690
rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1691
1692
cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1693
client = cc->cc_client;
1694
if (client->cl_state != CLIENT_ESTABLISHED
1695
|| cc->cc_service == rpc_gss_svc_none) {
1696
return (TRUE);
1697
}
1698
1699
return (xdr_rpc_gss_unwrap_data(mp,
1700
client->cl_ctx, client->cl_qop,
1701
cc->cc_service, cc->cc_seq));
1702
}
1703
1704
static void
1705
svc_rpc_gss_release(SVCAUTH *auth)
1706
{
1707
struct svc_rpc_gss_cookedcred *cc;
1708
struct svc_rpc_gss_client *client;
1709
1710
rpc_gss_log_debug("in svc_rpc_gss_release()");
1711
1712
cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1713
client = cc->cc_client;
1714
svc_rpc_gss_release_client(client);
1715
}
1716
1717