Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/rpc/rpcsec_gss/rpcsec_gss.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 2008 Doug Rabson
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
/*
29
auth_gss.c
30
31
RPCSEC_GSS client routines.
32
33
Copyright (c) 2000 The Regents of the University of Michigan.
34
All rights reserved.
35
36
Copyright (c) 2000 Dug Song <[email protected]>.
37
All rights reserved, all wrongs reversed.
38
39
Redistribution and use in source and binary forms, with or without
40
modification, are permitted provided that the following conditions
41
are met:
42
43
1. Redistributions of source code must retain the above copyright
44
notice, this list of conditions and the following disclaimer.
45
2. Redistributions in binary form must reproduce the above copyright
46
notice, this list of conditions and the following disclaimer in the
47
documentation and/or other materials provided with the distribution.
48
3. Neither the name of the University nor the names of its
49
contributors may be used to endorse or promote products derived
50
from this software without specific prior written permission.
51
52
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63
64
$Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65
*/
66
67
#include <sys/param.h>
68
#include <sys/systm.h>
69
#include <sys/hash.h>
70
#include <sys/jail.h>
71
#include <sys/kernel.h>
72
#include <sys/kobj.h>
73
#include <sys/lock.h>
74
#include <sys/malloc.h>
75
#include <sys/mbuf.h>
76
#include <sys/mutex.h>
77
#include <sys/proc.h>
78
#include <sys/refcount.h>
79
#include <sys/sx.h>
80
#include <sys/ucred.h>
81
82
#include <rpc/rpc.h>
83
#include <rpc/rpcsec_gss.h>
84
85
#include <kgssapi/krb5/kcrypto.h>
86
87
#include "rpcsec_gss_int.h"
88
89
static void rpc_gss_nextverf(AUTH*);
90
static bool_t rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
91
static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
92
static bool_t rpc_gss_refresh(AUTH *, void *);
93
static bool_t rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
94
struct mbuf **);
95
static void rpc_gss_destroy(AUTH *);
96
static void rpc_gss_destroy_context(AUTH *, bool_t);
97
98
static const struct auth_ops rpc_gss_ops = {
99
.ah_nextverf = rpc_gss_nextverf,
100
.ah_marshal = rpc_gss_marshal,
101
.ah_validate = rpc_gss_validate,
102
.ah_refresh = rpc_gss_refresh,
103
.ah_destroy = rpc_gss_destroy,
104
};
105
106
enum rpcsec_gss_state {
107
RPCSEC_GSS_START,
108
RPCSEC_GSS_CONTEXT,
109
RPCSEC_GSS_ESTABLISHED,
110
RPCSEC_GSS_DESTROYING
111
};
112
113
struct rpc_pending_request {
114
uint32_t pr_xid; /* XID of rpc */
115
uint32_t pr_seq; /* matching GSS seq */
116
LIST_ENTRY(rpc_pending_request) pr_link;
117
};
118
LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
119
120
struct rpc_gss_data {
121
volatile u_int gd_refs; /* number of current users */
122
struct mtx gd_lock;
123
uint32_t gd_hash;
124
AUTH *gd_auth; /* link back to AUTH */
125
struct ucred *gd_ucred; /* matching local cred */
126
char *gd_principal; /* server principal name */
127
char *gd_clntprincipal; /* client principal name */
128
rpc_gss_options_req_t gd_options; /* GSS context options */
129
enum rpcsec_gss_state gd_state; /* connection state */
130
gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE
131
* NULL RPC verfier to
132
* process at end of
133
* context negotiation */
134
CLIENT *gd_clnt; /* client handle */
135
gss_OID gd_mech; /* mechanism to use */
136
gss_qop_t gd_qop; /* quality of protection */
137
gss_ctx_id_t gd_ctx; /* context id */
138
struct rpc_gss_cred gd_cred; /* client credentials */
139
uint32_t gd_seq; /* next sequence number */
140
u_int gd_win; /* sequence window */
141
struct rpc_pending_request_list gd_reqs;
142
TAILQ_ENTRY(rpc_gss_data) gd_link;
143
TAILQ_ENTRY(rpc_gss_data) gd_alllink;
144
};
145
TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
146
147
#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
148
149
static struct timeval AUTH_TIMEOUT = { 25, 0 };
150
151
#define RPC_GSS_HASH_SIZE 11
152
#define RPC_GSS_MAX 256
153
static struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
154
static struct rpc_gss_data_list rpc_gss_all;
155
static struct sx rpc_gss_lock;
156
static int rpc_gss_count;
157
158
static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
159
const char *, gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
160
rpc_gss_options_ret_t *);
161
162
static void
163
rpc_gss_hashinit(void *dummy)
164
{
165
int i;
166
167
for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
168
TAILQ_INIT(&rpc_gss_cache[i]);
169
TAILQ_INIT(&rpc_gss_all);
170
sx_init(&rpc_gss_lock, "rpc_gss_lock");
171
}
172
SYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
173
174
static uint32_t
175
rpc_gss_hash(const char *principal, gss_OID mech,
176
struct ucred *cred, rpc_gss_service_t service)
177
{
178
uint32_t h;
179
180
h = HASHSTEP(HASHINIT, cred->cr_uid);
181
h = hash32_str(principal, h);
182
h = hash32_buf(mech->elements, mech->length, h);
183
h = HASHSTEP(h, (int) service);
184
185
return (h % RPC_GSS_HASH_SIZE);
186
}
187
188
/*
189
* Simplified interface to create a security association for the
190
* current thread's * ucred.
191
*/
192
AUTH *
193
rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
194
gss_OID mech_oid, rpc_gss_service_t service)
195
{
196
uint32_t h, th;
197
AUTH *auth;
198
struct rpc_gss_data *gd, *tgd;
199
rpc_gss_options_ret_t options;
200
201
if (rpc_gss_count > RPC_GSS_MAX) {
202
while (rpc_gss_count > RPC_GSS_MAX) {
203
sx_xlock(&rpc_gss_lock);
204
tgd = TAILQ_FIRST(&rpc_gss_all);
205
th = tgd->gd_hash;
206
TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
207
TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
208
rpc_gss_count--;
209
sx_xunlock(&rpc_gss_lock);
210
AUTH_DESTROY(tgd->gd_auth);
211
}
212
}
213
214
/*
215
* See if we already have an AUTH which matches.
216
*/
217
h = rpc_gss_hash(principal, mech_oid, cred, service);
218
219
again:
220
sx_slock(&rpc_gss_lock);
221
TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
222
if (gd->gd_ucred->cr_uid == cred->cr_uid
223
&& !strcmp(gd->gd_principal, principal)
224
&& gd->gd_mech == mech_oid
225
&& gd->gd_cred.gc_svc == service) {
226
refcount_acquire(&gd->gd_refs);
227
if (sx_try_upgrade(&rpc_gss_lock)) {
228
/*
229
* Keep rpc_gss_all LRU sorted.
230
*/
231
TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
232
TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
233
gd_alllink);
234
sx_xunlock(&rpc_gss_lock);
235
} else {
236
sx_sunlock(&rpc_gss_lock);
237
}
238
239
/*
240
* If the state != ESTABLISHED, try and initialize
241
* the authenticator again. This will happen if the
242
* user's credentials have expired. It may succeed now,
243
* if they have done a kinit or similar.
244
*/
245
if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
246
memset(&options, 0, sizeof (options));
247
(void) rpc_gss_init(gd->gd_auth, &options);
248
}
249
return (gd->gd_auth);
250
}
251
}
252
sx_sunlock(&rpc_gss_lock);
253
254
/*
255
* We missed in the cache - create a new association.
256
*/
257
auth = rpc_gss_seccreate_int(clnt, cred, NULL, principal, mech_oid,
258
service, GSS_C_QOP_DEFAULT, NULL, NULL);
259
if (!auth)
260
return (NULL);
261
262
gd = AUTH_PRIVATE(auth);
263
gd->gd_hash = h;
264
265
sx_xlock(&rpc_gss_lock);
266
TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
267
if (tgd->gd_ucred->cr_uid == cred->cr_uid
268
&& !strcmp(tgd->gd_principal, principal)
269
&& tgd->gd_mech == mech_oid
270
&& tgd->gd_cred.gc_svc == service) {
271
/*
272
* We lost a race to create the AUTH that
273
* matches this cred.
274
*/
275
sx_xunlock(&rpc_gss_lock);
276
AUTH_DESTROY(auth);
277
goto again;
278
}
279
}
280
281
rpc_gss_count++;
282
TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
283
TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
284
refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */
285
sx_xunlock(&rpc_gss_lock);
286
287
return (auth);
288
}
289
290
void
291
rpc_gss_secpurge(CLIENT *clnt)
292
{
293
uint32_t h;
294
struct rpc_gss_data *gd, *tgd;
295
296
TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
297
if (gd->gd_clnt == clnt) {
298
sx_xlock(&rpc_gss_lock);
299
h = gd->gd_hash;
300
TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
301
TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
302
rpc_gss_count--;
303
sx_xunlock(&rpc_gss_lock);
304
AUTH_DESTROY(gd->gd_auth);
305
}
306
}
307
}
308
309
AUTH *
310
rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *clnt_principal,
311
const char *principal, const char *mechanism, rpc_gss_service_t service,
312
const char *qop, rpc_gss_options_req_t *options_req,
313
rpc_gss_options_ret_t *options_ret)
314
{
315
gss_OID oid;
316
u_int qop_num;
317
318
/*
319
* Bail out now if we don't know this mechanism.
320
*/
321
if (!rpc_gss_mech_to_oid(mechanism, &oid))
322
return (NULL);
323
324
if (qop) {
325
if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
326
return (NULL);
327
} else {
328
qop_num = GSS_C_QOP_DEFAULT;
329
}
330
331
return (rpc_gss_seccreate_int(clnt, cred, clnt_principal, principal,
332
oid, service, qop_num, options_req, options_ret));
333
}
334
335
void
336
rpc_gss_refresh_auth(AUTH *auth)
337
{
338
struct rpc_gss_data *gd;
339
rpc_gss_options_ret_t options;
340
341
gd = AUTH_PRIVATE(auth);
342
/*
343
* If the state != ESTABLISHED, try and initialize
344
* the authenticator again. This will happen if the
345
* user's credentials have expired. It may succeed now,
346
* if they have done a kinit or similar.
347
*/
348
if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
349
memset(&options, 0, sizeof (options));
350
(void) rpc_gss_init(auth, &options);
351
}
352
}
353
354
static AUTH *
355
rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred,
356
const char *clnt_principal, const char *principal, gss_OID mech_oid,
357
rpc_gss_service_t service, u_int qop_num,
358
rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
359
{
360
AUTH *auth;
361
rpc_gss_options_ret_t options;
362
struct rpc_gss_data *gd;
363
364
/*
365
* If the caller doesn't want the options, point at local
366
* storage to simplify the code below.
367
*/
368
if (!options_ret)
369
options_ret = &options;
370
371
/*
372
* Default service is integrity.
373
*/
374
if (service == rpc_gss_svc_default)
375
service = rpc_gss_svc_integrity;
376
377
memset(options_ret, 0, sizeof(*options_ret));
378
379
rpc_gss_log_debug("in rpc_gss_seccreate()");
380
381
memset(&rpc_createerr, 0, sizeof(rpc_createerr));
382
383
auth = mem_alloc(sizeof(*auth));
384
if (auth == NULL) {
385
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
386
rpc_createerr.cf_error.re_errno = ENOMEM;
387
return (NULL);
388
}
389
gd = mem_alloc(sizeof(*gd));
390
if (gd == NULL) {
391
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
392
rpc_createerr.cf_error.re_errno = ENOMEM;
393
mem_free(auth, sizeof(*auth));
394
return (NULL);
395
}
396
397
auth->ah_ops = &rpc_gss_ops;
398
auth->ah_private = (caddr_t) gd;
399
auth->ah_cred.oa_flavor = RPCSEC_GSS;
400
401
refcount_init(&gd->gd_refs, 1);
402
mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
403
gd->gd_auth = auth;
404
gd->gd_ucred = crdup(cred);
405
gd->gd_principal = strdup(principal, M_RPC);
406
if (clnt_principal != NULL)
407
gd->gd_clntprincipal = strdup(clnt_principal, M_RPC);
408
else
409
gd->gd_clntprincipal = NULL;
410
411
412
if (options_req) {
413
gd->gd_options = *options_req;
414
} else {
415
gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
416
gd->gd_options.time_req = 0;
417
gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
418
gd->gd_options.input_channel_bindings = NULL;
419
}
420
CLNT_ACQUIRE(clnt);
421
gd->gd_clnt = clnt;
422
gd->gd_ctx = GSS_C_NO_CONTEXT;
423
gd->gd_mech = mech_oid;
424
gd->gd_qop = qop_num;
425
426
gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
427
gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
428
gd->gd_cred.gc_seq = 0;
429
gd->gd_cred.gc_svc = service;
430
LIST_INIT(&gd->gd_reqs);
431
432
if (!rpc_gss_init(auth, options_ret)) {
433
goto bad;
434
}
435
436
return (auth);
437
438
bad:
439
AUTH_DESTROY(auth);
440
return (NULL);
441
}
442
443
bool_t
444
rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
445
{
446
struct rpc_gss_data *gd;
447
u_int qop_num;
448
const char *mechanism;
449
450
gd = AUTH_PRIVATE(auth);
451
if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
452
return (FALSE);
453
}
454
455
if (qop) {
456
if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
457
return (FALSE);
458
}
459
} else {
460
qop_num = GSS_C_QOP_DEFAULT;
461
}
462
463
gd->gd_cred.gc_svc = service;
464
gd->gd_qop = qop_num;
465
return (TRUE);
466
}
467
468
static void
469
rpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
470
{
471
struct rpc_pending_request *pr, *npr;
472
struct rpc_pending_request_list reqs;
473
474
LIST_INIT(&reqs);
475
mtx_lock(&gd->gd_lock);
476
LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
477
if (pr->pr_xid == xid) {
478
LIST_REMOVE(pr, pr_link);
479
LIST_INSERT_HEAD(&reqs, pr, pr_link);
480
}
481
}
482
483
mtx_unlock(&gd->gd_lock);
484
485
LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
486
mem_free(pr, sizeof(*pr));
487
}
488
}
489
490
static uint32_t
491
rpc_gss_alloc_seq(struct rpc_gss_data *gd)
492
{
493
uint32_t seq;
494
495
mtx_lock(&gd->gd_lock);
496
seq = gd->gd_seq;
497
gd->gd_seq++;
498
mtx_unlock(&gd->gd_lock);
499
500
return (seq);
501
}
502
503
static void
504
rpc_gss_nextverf(__unused AUTH *auth)
505
{
506
507
/* not used */
508
}
509
510
static bool_t
511
rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
512
{
513
struct rpc_gss_data *gd;
514
struct rpc_pending_request *pr;
515
uint32_t seq;
516
XDR tmpxdrs;
517
struct rpc_gss_cred gsscred;
518
char credbuf[MAX_AUTH_BYTES];
519
struct opaque_auth creds, verf;
520
gss_buffer_desc rpcbuf, checksum;
521
OM_uint32 maj_stat, min_stat;
522
bool_t xdr_stat;
523
524
rpc_gss_log_debug("in rpc_gss_marshal()");
525
526
gd = AUTH_PRIVATE(auth);
527
528
gsscred = gd->gd_cred;
529
seq = rpc_gss_alloc_seq(gd);
530
gsscred.gc_seq = seq;
531
532
xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
533
if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
534
XDR_DESTROY(&tmpxdrs);
535
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
536
return (FALSE);
537
}
538
creds.oa_flavor = RPCSEC_GSS;
539
creds.oa_base = credbuf;
540
creds.oa_length = XDR_GETPOS(&tmpxdrs);
541
XDR_DESTROY(&tmpxdrs);
542
543
xdr_opaque_auth(xdrs, &creds);
544
545
if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
546
gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
547
if (!xdr_opaque_auth(xdrs, &_null_auth)) {
548
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
549
return (FALSE);
550
}
551
return (xdr_putmbuf(xdrs, args));
552
} else {
553
/*
554
* Keep track of this XID + seq pair so that we can do
555
* the matching gss_verify_mic in AUTH_VALIDATE.
556
*/
557
pr = mem_alloc(sizeof(struct rpc_pending_request));
558
mtx_lock(&gd->gd_lock);
559
pr->pr_xid = xid;
560
pr->pr_seq = seq;
561
LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
562
mtx_unlock(&gd->gd_lock);
563
564
/*
565
* Checksum serialized RPC header, up to and including
566
* credential. For the in-kernel environment, we
567
* assume that our XDR stream is on a contiguous
568
* memory buffer (e.g. an mbuf).
569
*/
570
rpcbuf.length = XDR_GETPOS(xdrs);
571
XDR_SETPOS(xdrs, 0);
572
rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
573
574
maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
575
&rpcbuf, &checksum);
576
577
if (maj_stat != GSS_S_COMPLETE) {
578
rpc_gss_log_status("gss_get_mic", gd->gd_mech,
579
maj_stat, min_stat);
580
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
581
rpc_gss_destroy_context(auth, TRUE);
582
}
583
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
584
return (FALSE);
585
}
586
587
verf.oa_flavor = RPCSEC_GSS;
588
verf.oa_base = checksum.value;
589
verf.oa_length = checksum.length;
590
591
xdr_stat = xdr_opaque_auth(xdrs, &verf);
592
gss_release_buffer(&min_stat, &checksum);
593
if (!xdr_stat) {
594
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
595
return (FALSE);
596
}
597
if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
598
gd->gd_cred.gc_svc == rpc_gss_svc_none) {
599
return (xdr_putmbuf(xdrs, args));
600
} else {
601
if (!xdr_rpc_gss_wrap_data(&args,
602
gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
603
seq))
604
return (FALSE);
605
return (xdr_putmbuf(xdrs, args));
606
}
607
}
608
609
return (TRUE);
610
}
611
612
static bool_t
613
rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
614
struct mbuf **resultsp)
615
{
616
struct rpc_gss_data *gd;
617
struct rpc_pending_request *pr, *npr;
618
struct rpc_pending_request_list reqs;
619
gss_qop_t qop_state;
620
uint32_t num, seq;
621
gss_buffer_desc signbuf, checksum;
622
OM_uint32 maj_stat, min_stat;
623
624
rpc_gss_log_debug("in rpc_gss_validate()");
625
626
gd = AUTH_PRIVATE(auth);
627
628
/*
629
* The client will call us with a NULL verf when it gives up
630
* on an XID.
631
*/
632
if (!verf) {
633
rpc_gss_purge_xid(gd, xid);
634
return (TRUE);
635
}
636
637
if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
638
/*
639
* Save the on the wire verifier to validate last INIT
640
* phase packet after decode if the major status is
641
* GSS_S_COMPLETE.
642
*/
643
if (gd->gd_verf.value)
644
xdr_free((xdrproc_t) xdr_gss_buffer_desc,
645
(char *) &gd->gd_verf);
646
gd->gd_verf.value = mem_alloc(verf->oa_length);
647
if (gd->gd_verf.value == NULL) {
648
printf("gss_validate: out of memory\n");
649
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
650
m_freem(*resultsp);
651
*resultsp = NULL;
652
return (FALSE);
653
}
654
memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
655
gd->gd_verf.length = verf->oa_length;
656
657
return (TRUE);
658
}
659
660
/*
661
* We need to check the verifier against all the requests
662
* we've send for this XID - for unreliable protocols, we
663
* retransmit with the same XID but different sequence
664
* number. We temporarily take this set of requests out of the
665
* list so that we can work through the list without having to
666
* hold the lock.
667
*/
668
mtx_lock(&gd->gd_lock);
669
LIST_INIT(&reqs);
670
LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
671
if (pr->pr_xid == xid) {
672
LIST_REMOVE(pr, pr_link);
673
LIST_INSERT_HEAD(&reqs, pr, pr_link);
674
}
675
}
676
mtx_unlock(&gd->gd_lock);
677
LIST_FOREACH(pr, &reqs, pr_link) {
678
if (pr->pr_xid == xid) {
679
seq = pr->pr_seq;
680
num = htonl(seq);
681
signbuf.value = &num;
682
signbuf.length = sizeof(num);
683
684
checksum.value = verf->oa_base;
685
checksum.length = verf->oa_length;
686
687
maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
688
&signbuf, &checksum, &qop_state);
689
if (maj_stat != GSS_S_COMPLETE
690
|| qop_state != gd->gd_qop) {
691
continue;
692
}
693
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
694
rpc_gss_destroy_context(auth, TRUE);
695
break;
696
}
697
//rpc_gss_purge_reqs(gd, seq);
698
LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
699
mem_free(pr, sizeof(*pr));
700
701
if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
702
return (TRUE);
703
} else {
704
if (!xdr_rpc_gss_unwrap_data(resultsp,
705
gd->gd_ctx, gd->gd_qop,
706
gd->gd_cred.gc_svc, seq)) {
707
return (FALSE);
708
}
709
}
710
return (TRUE);
711
}
712
}
713
714
/*
715
* We didn't match - put back any entries for this XID so that
716
* a future call to validate can retry.
717
*/
718
mtx_lock(&gd->gd_lock);
719
LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
720
LIST_REMOVE(pr, pr_link);
721
LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
722
}
723
mtx_unlock(&gd->gd_lock);
724
725
/*
726
* Nothing matches - give up.
727
*/
728
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
729
m_freem(*resultsp);
730
*resultsp = NULL;
731
return (FALSE);
732
}
733
734
static bool_t
735
rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
736
{
737
struct thread *td = curthread;
738
struct ucred *crsave;
739
struct rpc_gss_data *gd;
740
struct rpc_gss_init_res gr;
741
gss_buffer_desc principal_desc;
742
gss_buffer_desc *recv_tokenp, recv_token, send_token;
743
gss_name_t name;
744
OM_uint32 maj_stat, min_stat, call_stat;
745
const char *mech;
746
struct rpc_callextra ext;
747
gss_OID mech_oid;
748
gss_OID_set mechlist;
749
static enum krb_imp my_krb_imp = KRBIMP_UNKNOWN;
750
751
rpc_gss_log_debug("in rpc_gss_refresh()");
752
753
gd = AUTH_PRIVATE(auth);
754
755
mtx_lock(&gd->gd_lock);
756
/*
757
* If the context isn't in START state, someone else is
758
* refreshing - we wait till they are done. If they fail, they
759
* will put the state back to START and we can try (most
760
* likely to also fail).
761
*/
762
while (gd->gd_state != RPCSEC_GSS_START
763
&& gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
764
msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
765
}
766
if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
767
mtx_unlock(&gd->gd_lock);
768
return (TRUE);
769
}
770
gd->gd_state = RPCSEC_GSS_CONTEXT;
771
mtx_unlock(&gd->gd_lock);
772
773
gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
774
gd->gd_cred.gc_seq = 0;
775
776
/*
777
* XXX Threads from inside jails can get here via calls
778
* to clnt_vc_call()->AUTH_REFRESH()->rpc_gss_refresh()
779
* but the NFS mount is always done outside of the
780
* jails in vnet0. Since the thread credentials won't
781
* necessarily have cr_prison == vnet0 and this function
782
* has no access to the socket, using vnet0 seems the
783
* only option. This is broken if NFS mounts are enabled
784
* within vnet prisons.
785
*/
786
KGSS_CURVNET_SET_QUIET(vnet0);
787
/*
788
* For KerberosV, if there is a client principal name, that implies
789
* that this is a host based initiator credential in the default
790
* keytab file. For this case, it is necessary to do a
791
* gss_acquire_cred(). When this is done, the gssd daemon will
792
* do the equivalent of "kinit -k" to put a TGT for the name in
793
* the credential cache file for the gssd daemon.
794
*/
795
if (gd->gd_clntprincipal != NULL &&
796
rpc_gss_mech_to_oid("kerberosv5", &mech_oid) &&
797
gd->gd_mech == mech_oid) {
798
/* Get rid of any old credential. */
799
if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) {
800
gss_release_cred(&min_stat, &gd->gd_options.my_cred);
801
gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
802
}
803
804
/*
805
* The mechanism must be set to KerberosV for acquisition
806
* of credentials to work reliably.
807
*/
808
maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist);
809
if (maj_stat != GSS_S_COMPLETE) {
810
options_ret->major_status = maj_stat;
811
options_ret->minor_status = min_stat;
812
goto out;
813
}
814
maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech,
815
&mechlist);
816
if (maj_stat != GSS_S_COMPLETE) {
817
options_ret->major_status = maj_stat;
818
options_ret->minor_status = min_stat;
819
gss_release_oid_set(&min_stat, &mechlist);
820
goto out;
821
}
822
823
principal_desc.value = (void *)gd->gd_clntprincipal;
824
principal_desc.length = strlen(gd->gd_clntprincipal);
825
maj_stat = gss_import_name(&min_stat, &principal_desc,
826
GSS_C_NT_HOSTBASED_SERVICE, &name);
827
if (maj_stat != GSS_S_COMPLETE) {
828
options_ret->major_status = maj_stat;
829
options_ret->minor_status = min_stat;
830
gss_release_oid_set(&min_stat, &mechlist);
831
goto out;
832
}
833
/* Acquire the credentials. */
834
maj_stat = gss_acquire_cred(&min_stat, name, 0,
835
mechlist, GSS_C_INITIATE,
836
&gd->gd_options.my_cred, NULL, NULL);
837
gss_release_name(&min_stat, &name);
838
gss_release_oid_set(&min_stat, &mechlist);
839
if (maj_stat != GSS_S_COMPLETE) {
840
options_ret->major_status = maj_stat;
841
options_ret->minor_status = min_stat;
842
goto out;
843
}
844
}
845
846
principal_desc.value = (void *)gd->gd_principal;
847
principal_desc.length = strlen(gd->gd_principal);
848
maj_stat = gss_import_name(&min_stat, &principal_desc,
849
GSS_C_NT_HOSTBASED_SERVICE, &name);
850
if (maj_stat != GSS_S_COMPLETE) {
851
options_ret->major_status = maj_stat;
852
options_ret->minor_status = min_stat;
853
goto out;
854
}
855
856
if (my_krb_imp == KRBIMP_UNKNOWN) {
857
maj_stat = gss_supports_lucid(&min_stat, NULL);
858
if (maj_stat == GSS_S_COMPLETE)
859
my_krb_imp = KRBIMP_MIT;
860
else
861
my_krb_imp = KRBIMP_HEIMDALV1;
862
}
863
864
/* GSS context establishment loop. */
865
memset(&recv_token, 0, sizeof(recv_token));
866
memset(&gr, 0, sizeof(gr));
867
memset(options_ret, 0, sizeof(*options_ret));
868
options_ret->major_status = GSS_S_FAILURE;
869
recv_tokenp = GSS_C_NO_BUFFER;
870
871
for (;;) {
872
crsave = td->td_ucred;
873
td->td_ucred = gd->gd_ucred;
874
if (my_krb_imp == KRBIMP_MIT)
875
maj_stat = gss_init_sec_context_lucid_v1(&min_stat,
876
gd->gd_options.my_cred,
877
&gd->gd_ctx,
878
name,
879
gd->gd_mech,
880
gd->gd_options.req_flags,
881
gd->gd_options.time_req,
882
gd->gd_options.input_channel_bindings,
883
recv_tokenp,
884
&gd->gd_mech, /* used mech */
885
&send_token,
886
&options_ret->ret_flags,
887
&options_ret->time_req);
888
else
889
maj_stat = gss_init_sec_context(&min_stat,
890
gd->gd_options.my_cred,
891
&gd->gd_ctx,
892
name,
893
gd->gd_mech,
894
gd->gd_options.req_flags,
895
gd->gd_options.time_req,
896
gd->gd_options.input_channel_bindings,
897
recv_tokenp,
898
&gd->gd_mech, /* used mech */
899
&send_token,
900
&options_ret->ret_flags,
901
&options_ret->time_req);
902
td->td_ucred = crsave;
903
904
/*
905
* Free the token which we got from the server (if
906
* any). Remember that this was allocated by XDR, not
907
* GSS-API.
908
*/
909
if (recv_tokenp != GSS_C_NO_BUFFER) {
910
xdr_free((xdrproc_t) xdr_gss_buffer_desc,
911
(char *) &recv_token);
912
recv_tokenp = GSS_C_NO_BUFFER;
913
}
914
if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
915
strlcpy(options_ret->actual_mechanism,
916
mech,
917
sizeof(options_ret->actual_mechanism));
918
}
919
if (maj_stat != GSS_S_COMPLETE &&
920
maj_stat != GSS_S_CONTINUE_NEEDED) {
921
rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
922
maj_stat, min_stat);
923
options_ret->major_status = maj_stat;
924
options_ret->minor_status = min_stat;
925
break;
926
}
927
if (send_token.length != 0) {
928
memset(&gr, 0, sizeof(gr));
929
930
bzero(&ext, sizeof(ext));
931
ext.rc_auth = auth;
932
call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
933
(xdrproc_t)xdr_gss_buffer_desc,
934
&send_token,
935
(xdrproc_t)xdr_rpc_gss_init_res,
936
(caddr_t)&gr, AUTH_TIMEOUT);
937
938
gss_release_buffer(&min_stat, &send_token);
939
940
if (call_stat != RPC_SUCCESS)
941
break;
942
943
if (gr.gr_major != GSS_S_COMPLETE &&
944
gr.gr_major != GSS_S_CONTINUE_NEEDED) {
945
rpc_gss_log_status("server reply", gd->gd_mech,
946
gr.gr_major, gr.gr_minor);
947
options_ret->major_status = gr.gr_major;
948
options_ret->minor_status = gr.gr_minor;
949
break;
950
}
951
952
/*
953
* Save the server's gr_handle value, freeing
954
* what we have already (remember that this
955
* was allocated by XDR, not GSS-API).
956
*/
957
if (gr.gr_handle.length != 0) {
958
xdr_free((xdrproc_t) xdr_gss_buffer_desc,
959
(char *) &gd->gd_cred.gc_handle);
960
gd->gd_cred.gc_handle = gr.gr_handle;
961
}
962
963
/*
964
* Save the server's token as well.
965
*/
966
if (gr.gr_token.length != 0) {
967
recv_token = gr.gr_token;
968
recv_tokenp = &recv_token;
969
}
970
971
/*
972
* Since we have copied out all the bits of gr
973
* which XDR allocated for us, we don't need
974
* to free it.
975
*/
976
gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
977
}
978
979
if (maj_stat == GSS_S_COMPLETE) {
980
gss_buffer_desc bufin;
981
u_int seq, qop_state = 0;
982
983
/*
984
* gss header verifier,
985
* usually checked in gss_validate
986
*/
987
seq = htonl(gr.gr_win);
988
bufin.value = (unsigned char *)&seq;
989
bufin.length = sizeof(seq);
990
991
maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
992
&bufin, &gd->gd_verf, &qop_state);
993
994
if (maj_stat != GSS_S_COMPLETE ||
995
qop_state != gd->gd_qop) {
996
rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
997
maj_stat, min_stat);
998
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
999
rpc_gss_destroy_context(auth, TRUE);
1000
}
1001
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
1002
EPERM);
1003
options_ret->major_status = maj_stat;
1004
options_ret->minor_status = min_stat;
1005
break;
1006
}
1007
1008
options_ret->major_status = GSS_S_COMPLETE;
1009
options_ret->minor_status = 0;
1010
options_ret->rpcsec_version = gd->gd_cred.gc_version;
1011
options_ret->gss_context = gd->gd_ctx;
1012
1013
gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
1014
gd->gd_seq = 1;
1015
gd->gd_win = gr.gr_win;
1016
break;
1017
}
1018
}
1019
1020
gss_release_name(&min_stat, &name);
1021
xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1022
(char *) &gd->gd_verf);
1023
1024
out:
1025
/* End context negotiation loop. */
1026
if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
1027
rpc_createerr.cf_stat = RPC_AUTHERROR;
1028
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
1029
if (gd->gd_ctx) {
1030
gss_delete_sec_context(&min_stat, &gd->gd_ctx,
1031
GSS_C_NO_BUFFER);
1032
}
1033
KGSS_CURVNET_RESTORE();
1034
mtx_lock(&gd->gd_lock);
1035
gd->gd_state = RPCSEC_GSS_START;
1036
wakeup(gd);
1037
mtx_unlock(&gd->gd_lock);
1038
return (FALSE);
1039
}
1040
KGSS_CURVNET_RESTORE();
1041
1042
mtx_lock(&gd->gd_lock);
1043
gd->gd_state = RPCSEC_GSS_ESTABLISHED;
1044
wakeup(gd);
1045
mtx_unlock(&gd->gd_lock);
1046
1047
return (TRUE);
1048
}
1049
1050
static bool_t
1051
rpc_gss_refresh(AUTH *auth, void *msg)
1052
{
1053
struct rpc_msg *reply = (struct rpc_msg *) msg;
1054
rpc_gss_options_ret_t options;
1055
struct rpc_gss_data *gd;
1056
1057
gd = AUTH_PRIVATE(auth);
1058
1059
/*
1060
* If the context is in DESTROYING state, then just return, since
1061
* there is no point in refreshing the credentials.
1062
*/
1063
mtx_lock(&gd->gd_lock);
1064
if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
1065
mtx_unlock(&gd->gd_lock);
1066
return (FALSE);
1067
}
1068
mtx_unlock(&gd->gd_lock);
1069
1070
/*
1071
* If the error was RPCSEC_GSS_CREDPROBLEM of
1072
* RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
1073
* other errors are fatal.
1074
*/
1075
if (reply->rm_reply.rp_stat == MSG_DENIED
1076
&& reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
1077
&& (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
1078
|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
1079
rpc_gss_destroy_context(auth, FALSE);
1080
memset(&options, 0, sizeof(options));
1081
return (rpc_gss_init(auth, &options));
1082
}
1083
1084
return (FALSE);
1085
}
1086
1087
static void
1088
rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
1089
{
1090
struct rpc_gss_data *gd;
1091
struct rpc_pending_request *pr;
1092
OM_uint32 min_stat;
1093
struct rpc_callextra ext;
1094
1095
rpc_gss_log_debug("in rpc_gss_destroy_context()");
1096
1097
gd = AUTH_PRIVATE(auth);
1098
1099
mtx_lock(&gd->gd_lock);
1100
/*
1101
* If the context isn't in ESTABISHED state, someone else is
1102
* destroying/refreshing - we wait till they are done.
1103
*/
1104
if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
1105
while (gd->gd_state != RPCSEC_GSS_START
1106
&& gd->gd_state != RPCSEC_GSS_ESTABLISHED)
1107
msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
1108
mtx_unlock(&gd->gd_lock);
1109
return;
1110
}
1111
gd->gd_state = RPCSEC_GSS_DESTROYING;
1112
mtx_unlock(&gd->gd_lock);
1113
1114
if (send_destroy) {
1115
gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
1116
bzero(&ext, sizeof(ext));
1117
ext.rc_auth = auth;
1118
CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
1119
(xdrproc_t)xdr_void, NULL,
1120
(xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
1121
}
1122
1123
while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1124
LIST_REMOVE(pr, pr_link);
1125
mem_free(pr, sizeof(*pr));
1126
}
1127
1128
/*
1129
* Free the context token. Remember that this was
1130
* allocated by XDR, not GSS-API.
1131
*/
1132
xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1133
(char *) &gd->gd_cred.gc_handle);
1134
gd->gd_cred.gc_handle.length = 0;
1135
1136
if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1137
gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1138
1139
mtx_lock(&gd->gd_lock);
1140
gd->gd_state = RPCSEC_GSS_START;
1141
wakeup(gd);
1142
mtx_unlock(&gd->gd_lock);
1143
}
1144
1145
static void
1146
rpc_gss_destroy(AUTH *auth)
1147
{
1148
struct rpc_gss_data *gd;
1149
1150
rpc_gss_log_debug("in rpc_gss_destroy()");
1151
1152
gd = AUTH_PRIVATE(auth);
1153
1154
if (!refcount_release(&gd->gd_refs))
1155
return;
1156
1157
rpc_gss_destroy_context(auth, TRUE);
1158
1159
CLNT_RELEASE(gd->gd_clnt);
1160
crfree(gd->gd_ucred);
1161
free(gd->gd_principal, M_RPC);
1162
if (gd->gd_clntprincipal != NULL)
1163
free(gd->gd_clntprincipal, M_RPC);
1164
if (gd->gd_verf.value)
1165
xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1166
(char *) &gd->gd_verf);
1167
mtx_destroy(&gd->gd_lock);
1168
1169
mem_free(gd, sizeof(*gd));
1170
mem_free(auth, sizeof(*auth));
1171
}
1172
1173
int
1174
rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1175
{
1176
struct rpc_gss_data *gd;
1177
int want_conf;
1178
OM_uint32 max;
1179
OM_uint32 maj_stat, min_stat;
1180
int result;
1181
1182
gd = AUTH_PRIVATE(auth);
1183
1184
switch (gd->gd_cred.gc_svc) {
1185
case rpc_gss_svc_none:
1186
return (max_tp_unit_len);
1187
break;
1188
1189
case rpc_gss_svc_default:
1190
case rpc_gss_svc_integrity:
1191
want_conf = FALSE;
1192
break;
1193
1194
case rpc_gss_svc_privacy:
1195
want_conf = TRUE;
1196
break;
1197
1198
default:
1199
return (0);
1200
}
1201
1202
maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1203
gd->gd_qop, max_tp_unit_len, &max);
1204
1205
if (maj_stat == GSS_S_COMPLETE) {
1206
result = (int) max;
1207
if (result < 0)
1208
result = 0;
1209
return (result);
1210
} else {
1211
rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1212
maj_stat, min_stat);
1213
return (0);
1214
}
1215
}
1216
1217