Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/rpc/auth_gss.c
39536 views
1
/* lib/rpc/auth_gss.c */
2
/*
3
Copyright (c) 2000 The Regents of the University of Michigan.
4
All rights reserved.
5
6
Copyright (c) 2000 Dug Song <[email protected]>.
7
All rights reserved, all wrongs reversed.
8
9
Redistribution and use in source and binary forms, with or without
10
modification, are permitted provided that the following conditions
11
are met:
12
13
1. Redistributions of source code must retain the above copyright
14
notice, this list of conditions and the following disclaimer.
15
2. Redistributions in binary form must reproduce the above copyright
16
notice, this list of conditions and the following disclaimer in the
17
documentation and/or other materials provided with the distribution.
18
3. Neither the name of the University nor the names of its
19
contributors may be used to endorse or promote products derived
20
from this software without specific prior written permission.
21
22
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34
Id: auth_gss.c,v 1.35 2002/10/15 21:25:25 kwc Exp
35
*/
36
37
/* RPCSEC_GSS client routines. */
38
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <unistd.h>
42
#include <string.h>
43
#include <errno.h>
44
#include <gssrpc/types.h>
45
#include <gssrpc/xdr.h>
46
#include <gssrpc/auth.h>
47
#include <gssrpc/auth_gss.h>
48
#include <gssrpc/clnt.h>
49
#include <netinet/in.h>
50
#ifdef HAVE_HEIMDAL
51
#include <gssapi.h>
52
#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
53
#else
54
#include <gssapi/gssapi.h>
55
#include <gssapi/gssapi_generic.h>
56
#endif
57
58
#ifdef DEBUG_GSSAPI
59
int auth_debug_gss = DEBUG_GSSAPI;
60
int misc_debug_gss = DEBUG_GSSAPI;
61
#endif
62
63
static void authgss_nextverf(AUTH *);
64
static bool_t authgss_marshal(AUTH *, XDR *);
65
static bool_t authgss_refresh(AUTH *, struct rpc_msg *);
66
static bool_t authgss_validate(AUTH *, struct opaque_auth *);
67
static void authgss_destroy(AUTH *);
68
static void authgss_destroy_context(AUTH *);
69
static bool_t authgss_wrap(AUTH *, XDR *, xdrproc_t, caddr_t);
70
static bool_t authgss_unwrap(AUTH *, XDR *, xdrproc_t, caddr_t);
71
72
73
/*
74
* from mit-krb5-1.2.1 mechglue/mglueP.h:
75
* Array of context IDs typed by mechanism OID
76
*/
77
typedef struct gss_union_ctx_id_t {
78
gss_OID mech_type;
79
gss_ctx_id_t internal_ctx_id;
80
} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
81
82
static struct auth_ops authgss_ops = {
83
authgss_nextverf,
84
authgss_marshal,
85
authgss_validate,
86
authgss_refresh,
87
authgss_destroy,
88
authgss_wrap,
89
authgss_unwrap
90
};
91
92
#ifdef DEBUG
93
94
/* useful as i add more mechanisms */
95
void
96
print_rpc_gss_sec(struct rpc_gss_sec *ptr)
97
{
98
int i;
99
char *p;
100
101
log_debug("rpc_gss_sec:");
102
if(ptr->mech == NULL)
103
log_debug("NULL gss_OID mech");
104
else {
105
fprintf(stderr, " mechanism_OID: {");
106
p = (char *)ptr->mech->elements;
107
for (i=0; i < ptr->mech->length; i++)
108
/* First byte of OIDs encoded to save a byte */
109
if (i == 0) {
110
int first, second;
111
if (*p < 40) {
112
first = 0;
113
second = *p;
114
}
115
else if (40 <= *p && *p < 80) {
116
first = 1;
117
second = *p - 40;
118
}
119
else if (80 <= *p && *p < 127) {
120
first = 2;
121
second = *p - 80;
122
}
123
else {
124
/* Invalid value! */
125
first = -1;
126
second = -1;
127
}
128
fprintf(stderr, " %u %u", first, second);
129
p++;
130
}
131
else {
132
fprintf(stderr, " %u", (unsigned char)*p++);
133
}
134
fprintf(stderr, " }\n");
135
}
136
fprintf(stderr, " qop: %d\n", ptr->qop);
137
fprintf(stderr, " service: %d\n", ptr->svc);
138
fprintf(stderr, " cred: %p\n", ptr->cred);
139
fprintf(stderr, " req_flags: 0x%08x", ptr->req_flags);
140
}
141
#endif /*DEBUG*/
142
143
struct rpc_gss_data {
144
bool_t established; /* context established */
145
bool_t inprogress;
146
gss_buffer_desc gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier
147
* to process at end of context negotiation*/
148
CLIENT *clnt; /* client handle */
149
gss_name_t name; /* service name */
150
struct rpc_gss_sec sec; /* security tuple */
151
gss_ctx_id_t ctx; /* context id */
152
struct rpc_gss_cred gc; /* client credentials */
153
uint32_t win; /* sequence window */
154
};
155
156
#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
157
158
static struct timeval AUTH_TIMEOUT = { 25, 0 };
159
160
AUTH *
161
authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
162
{
163
AUTH *auth, *save_auth;
164
struct rpc_gss_data *gd;
165
OM_uint32 min_stat = 0;
166
167
log_debug("in authgss_create()");
168
169
memset(&rpc_createerr, 0, sizeof(rpc_createerr));
170
171
if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
172
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
173
rpc_createerr.cf_error.re_errno = ENOMEM;
174
return (NULL);
175
}
176
if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
177
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
178
rpc_createerr.cf_error.re_errno = ENOMEM;
179
free(auth);
180
return (NULL);
181
}
182
if (name != GSS_C_NO_NAME) {
183
if (gss_duplicate_name(&min_stat, name, &gd->name)
184
!= GSS_S_COMPLETE) {
185
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
186
rpc_createerr.cf_error.re_errno = ENOMEM;
187
free(auth);
188
free(gd);
189
return (NULL);
190
}
191
}
192
else
193
gd->name = name;
194
195
gd->clnt = clnt;
196
gd->ctx = GSS_C_NO_CONTEXT;
197
gd->sec = *sec;
198
199
gd->gc.gc_v = RPCSEC_GSS_VERSION;
200
gd->gc.gc_proc = RPCSEC_GSS_INIT;
201
gd->gc.gc_svc = gd->sec.svc;
202
203
auth->ah_ops = &authgss_ops;
204
auth->ah_private = (caddr_t)gd;
205
206
save_auth = clnt->cl_auth;
207
clnt->cl_auth = auth;
208
209
if (!authgss_refresh(auth, NULL))
210
auth = NULL;
211
212
clnt->cl_auth = save_auth;
213
214
log_debug("authgss_create returning auth 0x%08x", auth);
215
return (auth);
216
}
217
218
AUTH *
219
authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
220
{
221
AUTH *auth;
222
OM_uint32 maj_stat = 0, min_stat = 0;
223
gss_buffer_desc sname;
224
gss_name_t name;
225
226
log_debug("in authgss_create_default()");
227
228
229
sname.value = service;
230
sname.length = strlen(service);
231
232
maj_stat = gss_import_name(&min_stat, &sname,
233
(gss_OID)gss_nt_service_name,
234
&name);
235
236
if (maj_stat != GSS_S_COMPLETE) {
237
log_status("gss_import_name", maj_stat, min_stat);
238
rpc_createerr.cf_stat = RPC_AUTHERROR;
239
return (NULL);
240
}
241
242
auth = authgss_create(clnt, name, sec);
243
244
if (name != GSS_C_NO_NAME)
245
gss_release_name(&min_stat, &name);
246
247
log_debug("authgss_create_default returning auth 0x%08x", auth);
248
return (auth);
249
}
250
251
bool_t
252
authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)
253
{
254
struct rpc_gss_data *gd;
255
256
log_debug("in authgss_get_private_data()");
257
258
if (!auth || !pd)
259
return (FALSE);
260
261
gd = AUTH_PRIVATE(auth);
262
263
if (!gd || !gd->established)
264
return (FALSE);
265
266
pd->pd_ctx = gd->ctx;
267
pd->pd_ctx_hndl = gd->gc.gc_ctx;
268
pd->pd_seq_win = gd->win;
269
270
return (TRUE);
271
}
272
273
static void
274
authgss_nextverf(AUTH *auth)
275
{
276
log_debug("in authgss_nextverf()\n");
277
/* no action necessary */
278
}
279
280
static bool_t
281
authgss_marshal(AUTH *auth, XDR *xdrs)
282
{
283
XDR tmpxdrs;
284
char tmp[MAX_AUTH_BYTES];
285
struct rpc_gss_data *gd;
286
gss_buffer_desc rpcbuf, checksum;
287
OM_uint32 maj_stat, min_stat;
288
bool_t xdr_stat;
289
290
log_debug("in authgss_marshal()");
291
292
gd = AUTH_PRIVATE(auth);
293
294
if (gd->established)
295
gd->gc.gc_seq++;
296
297
xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
298
299
if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
300
XDR_DESTROY(&tmpxdrs);
301
return (FALSE);
302
}
303
auth->ah_cred.oa_flavor = RPCSEC_GSS;
304
auth->ah_cred.oa_base = tmp;
305
auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
306
307
XDR_DESTROY(&tmpxdrs);
308
309
if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
310
return (FALSE);
311
312
if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
313
gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
314
return (xdr_opaque_auth(xdrs, &gssrpc__null_auth));
315
}
316
/* Checksum serialized RPC header, up to and including credential. */
317
rpcbuf.length = XDR_GETPOS(xdrs);
318
XDR_SETPOS(xdrs, 0);
319
rpcbuf.value = XDR_INLINE(xdrs, (int)rpcbuf.length);
320
321
maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
322
&rpcbuf, &checksum);
323
324
if (maj_stat != GSS_S_COMPLETE) {
325
log_status("gss_get_mic", maj_stat, min_stat);
326
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
327
gd->established = FALSE;
328
authgss_destroy_context(auth);
329
}
330
return (FALSE);
331
}
332
auth->ah_verf.oa_flavor = RPCSEC_GSS;
333
auth->ah_verf.oa_base = checksum.value;
334
auth->ah_verf.oa_length = checksum.length;
335
336
xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
337
gss_release_buffer(&min_stat, &checksum);
338
339
return (xdr_stat);
340
}
341
342
static bool_t
343
authgss_validate(AUTH *auth, struct opaque_auth *verf)
344
{
345
struct rpc_gss_data *gd;
346
uint32_t num;
347
gss_qop_t qop_state;
348
gss_buffer_desc signbuf, checksum;
349
OM_uint32 maj_stat, min_stat;
350
351
log_debug("in authgss_validate()");
352
353
gd = AUTH_PRIVATE(auth);
354
355
if (gd->established == FALSE) {
356
/* would like to do this only on NULL rpc - gc->established is good enough.
357
* save the on the wire verifier to validate last INIT phase packet
358
* after decode if the major status is GSS_S_COMPLETE
359
*/
360
if ((gd->gc_wire_verf.value = mem_alloc(verf->oa_length)) == NULL) {
361
fprintf(stderr, "gss_validate: out of memory\n");
362
return (FALSE);
363
}
364
memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
365
gd->gc_wire_verf.length = verf->oa_length;
366
return (TRUE);
367
}
368
369
if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
370
gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
371
num = htonl(gd->win);
372
}
373
else num = htonl(gd->gc.gc_seq);
374
375
signbuf.value = &num;
376
signbuf.length = sizeof(num);
377
378
checksum.value = verf->oa_base;
379
checksum.length = verf->oa_length;
380
381
maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf,
382
&checksum, &qop_state);
383
if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
384
log_status("gss_verify_mic", maj_stat, min_stat);
385
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
386
gd->established = FALSE;
387
authgss_destroy_context(auth);
388
}
389
return (FALSE);
390
}
391
return (TRUE);
392
}
393
394
static bool_t
395
authgss_refresh(AUTH *auth, struct rpc_msg *msg)
396
{
397
struct rpc_gss_data *gd;
398
struct rpc_gss_init_res gr;
399
gss_buffer_desc *recv_tokenp, send_token;
400
OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
401
402
log_debug("in authgss_refresh()");
403
404
gd = AUTH_PRIVATE(auth);
405
406
if (gd->established || gd->inprogress)
407
return (TRUE);
408
409
/* GSS context establishment loop. */
410
memset(&gr, 0, sizeof(gr));
411
recv_tokenp = GSS_C_NO_BUFFER;
412
413
#ifdef DEBUG
414
print_rpc_gss_sec(&gd->sec);
415
#endif /*DEBUG*/
416
417
for (;;) {
418
gd->inprogress = TRUE;
419
maj_stat = gss_init_sec_context(&min_stat,
420
gd->sec.cred,
421
&gd->ctx,
422
gd->name,
423
gd->sec.mech,
424
gd->sec.req_flags,
425
0, /* time req */
426
GSS_C_NO_CHANNEL_BINDINGS,
427
recv_tokenp,
428
NULL, /* used mech */
429
&send_token,
430
&ret_flags,
431
NULL); /* time rec */
432
433
log_status("gss_init_sec_context", maj_stat, min_stat);
434
if (recv_tokenp != GSS_C_NO_BUFFER) {
435
free(gr.gr_token.value);
436
gr.gr_token.value = NULL;
437
recv_tokenp = GSS_C_NO_BUFFER;
438
}
439
if (maj_stat != GSS_S_COMPLETE &&
440
maj_stat != GSS_S_CONTINUE_NEEDED) {
441
log_status("gss_init_sec_context (error)", maj_stat, min_stat);
442
break;
443
}
444
if (send_token.length != 0) {
445
memset(&gr, 0, sizeof(gr));
446
447
call_stat = clnt_call(gd->clnt, NULLPROC,
448
(xdrproc_t)xdr_rpc_gss_init_args,
449
&send_token,
450
(xdrproc_t)xdr_rpc_gss_init_res,
451
(caddr_t)&gr, AUTH_TIMEOUT);
452
453
gss_release_buffer(&min_stat, &send_token);
454
455
log_debug("authgss_refresh: call_stat=%d", call_stat);
456
log_debug("%s", clnt_sperror(gd->clnt, "authgss_refresh"));
457
if (call_stat != RPC_SUCCESS ||
458
(gr.gr_major != GSS_S_COMPLETE &&
459
gr.gr_major != GSS_S_CONTINUE_NEEDED))
460
break;
461
462
if (gr.gr_ctx.length != 0) {
463
free(gd->gc.gc_ctx.value);
464
gd->gc.gc_ctx = gr.gr_ctx;
465
}
466
if (gr.gr_token.length != 0) {
467
if (maj_stat != GSS_S_CONTINUE_NEEDED)
468
break;
469
recv_tokenp = &gr.gr_token;
470
}
471
gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
472
}
473
474
/* GSS_S_COMPLETE => check gss header verifier, usually checked in
475
* gss_validate
476
*/
477
if (maj_stat == GSS_S_COMPLETE) {
478
gss_buffer_desc bufin;
479
gss_buffer_desc bufout;
480
uint32_t seq;
481
gss_qop_t qop_state = 0;
482
483
seq = htonl(gr.gr_win);
484
bufin.value = (u_char *)&seq;
485
bufin.length = sizeof(seq);
486
bufout.value = (u_char *)gd->gc_wire_verf.value;
487
bufout.length = gd->gc_wire_verf.length;
488
489
log_debug("authgss_refresh: GSS_S_COMPLETE: calling verify_mic");
490
maj_stat = gss_verify_mic(&min_stat,gd->ctx,
491
&bufin, &bufout, &qop_state);
492
free(gd->gc_wire_verf.value);
493
gd->gc_wire_verf.length = 0;
494
gd->gc_wire_verf.value = NULL;
495
496
if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
497
log_status("gss_verify_mic", maj_stat, min_stat);
498
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
499
gd->established = FALSE;
500
authgss_destroy_context(auth);
501
}
502
return (FALSE);
503
}
504
gd->established = TRUE;
505
gd->inprogress = FALSE;
506
gd->gc.gc_proc = RPCSEC_GSS_DATA;
507
gd->gc.gc_seq = 0;
508
gd->win = gr.gr_win;
509
break;
510
}
511
}
512
log_status("authgss_refresh: at end of context negotiation", maj_stat, min_stat);
513
/* End context negotiation loop. */
514
if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
515
log_debug("authgss_refresh: returning ERROR (gc_proc %d)", gd->gc.gc_proc);
516
free(gr.gr_token.value);
517
authgss_destroy(auth);
518
auth = NULL;
519
rpc_createerr.cf_stat = RPC_AUTHERROR;
520
521
return (FALSE);
522
}
523
log_debug("authgss_refresh: returning SUCCESS");
524
return (TRUE);
525
}
526
527
bool_t
528
authgss_service(AUTH *auth, int svc)
529
{
530
struct rpc_gss_data *gd;
531
532
log_debug("in authgss_service()");
533
534
if (!auth)
535
return(FALSE);
536
gd = AUTH_PRIVATE(auth);
537
if (!gd || !gd->established)
538
return (FALSE);
539
gd->sec.svc = svc;
540
gd->gc.gc_svc = svc;
541
return (TRUE);
542
}
543
544
static void
545
authgss_destroy_context(AUTH *auth)
546
{
547
struct rpc_gss_data *gd;
548
OM_uint32 min_stat;
549
550
log_debug("in authgss_destroy_context()");
551
552
gd = AUTH_PRIVATE(auth);
553
554
if (gd->gc.gc_ctx.length != 0) {
555
if (gd->established) {
556
gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
557
(void)clnt_call(gd->clnt, NULLPROC, xdr_void, NULL,
558
xdr_void, NULL, AUTH_TIMEOUT);
559
log_debug("%s",
560
clnt_sperror(gd->clnt,
561
"authgss_destroy_context"));
562
}
563
free(gd->gc.gc_ctx.value);
564
/* XXX ANDROS check size of context - should be 8 */
565
memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
566
}
567
if (gd->ctx != GSS_C_NO_CONTEXT) {
568
gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
569
gd->ctx = GSS_C_NO_CONTEXT;
570
}
571
gd->established = FALSE;
572
573
log_debug("finished authgss_destroy_context()");
574
}
575
576
static void
577
authgss_destroy(AUTH *auth)
578
{
579
struct rpc_gss_data *gd;
580
OM_uint32 min_stat;
581
582
log_debug("in authgss_destroy()");
583
584
gd = AUTH_PRIVATE(auth);
585
586
authgss_destroy_context(auth);
587
588
if (gd->name != GSS_C_NO_NAME)
589
gss_release_name(&min_stat, &gd->name);
590
591
free(gd);
592
free(auth);
593
}
594
595
bool_t
596
authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
597
{
598
struct rpc_gss_data *gd;
599
600
log_debug("in authgss_wrap()");
601
602
gd = AUTH_PRIVATE(auth);
603
604
if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
605
return ((*xdr_func)(xdrs, xdr_ptr));
606
}
607
return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
608
gd->ctx, gd->sec.qop,
609
gd->sec.svc, gd->gc.gc_seq));
610
}
611
612
bool_t
613
authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
614
{
615
struct rpc_gss_data *gd;
616
617
log_debug("in authgss_unwrap()");
618
619
gd = AUTH_PRIVATE(auth);
620
621
if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
622
return ((*xdr_func)(xdrs, xdr_ptr));
623
}
624
return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
625
gd->ctx, gd->sec.qop,
626
gd->sec.svc, gd->gc.gc_seq));
627
}
628
629