Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/rpc/svc_auth_gssapi.c
39536 views
1
/*
2
* Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
3
*
4
* $Id$
5
*
6
*/
7
8
/*
9
* svc_auth_gssapi.c
10
* Handles the GSS-API flavor authentication parameters on the service
11
* side of RPC.
12
*/
13
14
#include <stdio.h>
15
#include <errno.h>
16
#include <string.h>
17
#include <gssrpc/rpc.h>
18
#include <sys/stat.h>
19
20
#include <gssapi/gssapi_generic.h>
21
#include <gssrpc/auth_gssapi.h>
22
23
#ifdef GSS_BACKWARD_HACK
24
#include <gssapi/gssapi_krb5.h>
25
#endif
26
27
#include "gssrpcint.h"
28
29
#ifdef GSSAPI_KRB5
30
/* This is here for the krb5_error_code typedef and the
31
* KRB5KRB_AP_ERR_NOT_US #define.*/
32
#include <krb5.h>
33
#endif
34
35
#include <sys/file.h>
36
#include <fcntl.h>
37
#include <time.h>
38
39
#define INITIATION_TIMEOUT 60*15 /* seconds until partially created */
40
/* context is destroed */
41
#define INDEF_EXPIRE 60*60*24 /* seconds until an context with no */
42
/* expiration time is expired */
43
44
#ifdef __CODECENTER__
45
#define DEBUG_GSSAPI 1
46
#endif
47
48
#ifdef DEBUG_GSSAPI
49
int svc_debug_gssapi = DEBUG_GSSAPI;
50
void gssrpcint_printf(const char *format, ...)
51
{
52
va_list ap;
53
va_start(ap, format);
54
#if 1
55
vprintf(format, ap);
56
#else
57
{
58
static FILE *f;
59
if (f == NULL)
60
f = fopen("/dev/pts/4", "a");
61
if (f) {
62
vfprintf(f, format, ap);
63
fflush(f);
64
}
65
}
66
#endif
67
va_end(ap);
68
}
69
#define L_PRINTF(l,args) if (svc_debug_gssapi >= l) gssrpcint_printf args
70
#define PRINTF(args) L_PRINTF(99, args)
71
#define AUTH_GSSAPI_DISPLAY_STATUS(args) \
72
if (svc_debug_gssapi) auth_gssapi_display_status args
73
#else
74
#define PRINTF(args)
75
#define L_PRINTF(l, args)
76
#define AUTH_GSSAPI_DISPLAY_STATUS(args)
77
#endif
78
79
typedef struct _svc_auth_gssapi_data {
80
bool_t established;
81
82
gss_ctx_id_t context;
83
gss_name_t client_name, server_name;
84
gss_cred_id_t server_creds;
85
86
uint32_t expiration;
87
uint32_t seq_num;
88
uint32_t key;
89
90
SVCAUTH svcauth;
91
92
/* kludge to free verifiers on next call */
93
gss_buffer_desc prev_verf;
94
} svc_auth_gssapi_data;
95
96
#define SVCAUTH_PRIVATE(auth) \
97
((svc_auth_gssapi_data *)(auth)->svc_ah_private)
98
99
static bool_t svc_auth_gssapi_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
100
static bool_t svc_auth_gssapi_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
101
static bool_t svc_auth_gssapi_destroy(SVCAUTH *);
102
103
static svc_auth_gssapi_data *create_client(void);
104
static svc_auth_gssapi_data *get_client
105
(gss_buffer_t client_handle);
106
static void destroy_client
107
(svc_auth_gssapi_data *client_data);
108
static void clean_client(void), cleanup(void);
109
static void client_expire
110
(svc_auth_gssapi_data *client_data, uint32_t exp);
111
static void dump_db (char *msg);
112
113
struct svc_auth_ops svc_auth_gssapi_ops = {
114
svc_auth_gssapi_wrap,
115
svc_auth_gssapi_unwrap,
116
svc_auth_gssapi_destroy
117
};
118
119
/*
120
* Globals! Eeek! Run for the hills!
121
*/
122
static gss_cred_id_t *server_creds_list = NULL;
123
static gss_name_t *server_name_list = NULL;
124
static int server_creds_count = 0;
125
126
static auth_gssapi_log_badauth_func log_badauth = NULL;
127
static caddr_t log_badauth_data = NULL;
128
static auth_gssapi_log_badauth2_func log_badauth2 = NULL;
129
static caddr_t log_badauth2_data = NULL;
130
static auth_gssapi_log_badverf_func log_badverf = NULL;
131
static caddr_t log_badverf_data = NULL;
132
static auth_gssapi_log_miscerr_func log_miscerr = NULL;
133
static caddr_t log_miscerr_data = NULL;
134
135
#define LOG_MISCERR(arg) if (log_miscerr) \
136
(*log_miscerr)(rqst, msg, arg, log_miscerr_data)
137
138
typedef struct _client_list {
139
svc_auth_gssapi_data *client;
140
struct _client_list *next;
141
} client_list;
142
143
static client_list *clients = NULL;
144
145
146
/* Invoke log_badauth callbacks for an authentication failure. */
147
static void
148
badauth(OM_uint32 maj, OM_uint32 minor, SVCXPRT *xprt)
149
{
150
if (log_badauth != NULL)
151
(*log_badauth)(maj, minor, &xprt->xp_raddr, log_badauth_data);
152
if (log_badauth2 != NULL)
153
(*log_badauth2)(maj, minor, xprt, log_badauth2_data);
154
}
155
156
enum auth_stat gssrpc__svcauth_gssapi(
157
struct svc_req *rqst,
158
struct rpc_msg *msg,
159
bool_t *no_dispatch)
160
{
161
XDR xdrs;
162
auth_gssapi_creds creds;
163
auth_gssapi_init_arg call_arg;
164
auth_gssapi_init_res call_res;
165
gss_buffer_desc output_token, in_buf, out_buf;
166
gss_cred_id_t server_creds;
167
struct gss_channel_bindings_struct bindings, *bindp;
168
OM_uint32 gssstat, minor_stat, time_rec;
169
struct opaque_auth *cred, *verf;
170
svc_auth_gssapi_data *client_data;
171
int i;
172
enum auth_stat ret;
173
OM_uint32 ret_flags;
174
uint32_t seq_num;
175
176
PRINTF(("svcauth_gssapi: starting\n"));
177
178
/* clean up expired entries */
179
clean_client();
180
181
/* use AUTH_NONE until there is a client_handle */
182
rqst->rq_xprt->xp_auth = &svc_auth_none;
183
184
memset(&call_res, 0, sizeof(call_res));
185
creds.client_handle.length = 0;
186
creds.client_handle.value = NULL;
187
188
cred = &msg->rm_call.cb_cred;
189
verf = &msg->rm_call.cb_verf;
190
191
if (cred->oa_length == 0) {
192
PRINTF(("svcauth_gssapi: empty creds, failing\n"));
193
LOG_MISCERR("empty client credentials");
194
ret = AUTH_BADCRED;
195
goto error;
196
}
197
198
PRINTF(("svcauth_gssapi: decoding credentials\n"));
199
xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
200
memset(&creds, 0, sizeof(creds));
201
if (! xdr_authgssapi_creds(&xdrs, &creds)) {
202
PRINTF(("svcauth_gssapi: failed decoding creds\n"));
203
LOG_MISCERR("protocol error in client credentials");
204
xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds);
205
XDR_DESTROY(&xdrs);
206
ret = AUTH_BADCRED;
207
goto error;
208
}
209
XDR_DESTROY(&xdrs);
210
211
PRINTF(("svcauth_gssapi: got credentials, version %d, client_handle len %d\n",
212
creds.version, (int) creds.client_handle.length));
213
214
if (creds.version != 2) {
215
PRINTF(("svcauth_gssapi: bad credential version\n"));
216
LOG_MISCERR("unsupported client credentials version");
217
ret = AUTH_BADCRED;
218
goto error;
219
}
220
221
#ifdef DEBUG_GSSAPI
222
if (svc_debug_gssapi) {
223
if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_EXIT) {
224
PRINTF(("svcauth_gssapi: GSSAPI_EXIT, cleaning up\n"));
225
svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
226
xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds);
227
cleanup();
228
exit(0);
229
}
230
}
231
#endif
232
233
/*
234
* If this is an auth_msg and proc is GSSAPI_INIT, then create a
235
* client handle for this client. Otherwise, look up the
236
* existing handle.
237
*/
238
if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) {
239
if (creds.client_handle.length != 0) {
240
PRINTF(("svcauth_gssapi: non-empty handle on GSSAPI_INIT\n"));
241
LOG_MISCERR("protocol error in client handle");
242
ret = AUTH_FAILED;
243
goto error;
244
}
245
246
PRINTF(("svcauth_gssapi: GSSAPI_INIT, creating client.\n"));
247
248
client_data = create_client();
249
if (client_data == NULL) {
250
PRINTF(("svcauth_gssapi: create_client failed\n"));
251
LOG_MISCERR("internal error creating client record");
252
ret = AUTH_FAILED;
253
goto error;
254
}
255
} else {
256
if (creds.client_handle.length == 0) {
257
PRINTF(("svcauth_gssapi: expected non-empty creds\n"));
258
LOG_MISCERR("protocol error in client credentials");
259
ret = AUTH_FAILED;
260
goto error;
261
}
262
263
PRINTF(("svcauth_gssapi: incoming client_handle %d, len %d\n",
264
*((uint32_t *) creds.client_handle.value),
265
(int) creds.client_handle.length));
266
267
client_data = get_client(&creds.client_handle);
268
if (client_data == NULL) {
269
PRINTF(("svcauth_gssapi: client_handle lookup failed\n"));
270
LOG_MISCERR("invalid client handle received");
271
ret = AUTH_BADCRED;
272
goto error;
273
}
274
PRINTF(("svcauth_gssapi: client_handle lookup succeeded\n"));
275
}
276
277
/* any response we send will use client_handle, so set it now */
278
call_res.client_handle.length = sizeof(client_data->key);
279
call_res.client_handle.value = (char *) &client_data->key;
280
281
/* mark this call as using AUTH_GSSAPI via client_data's SVCAUTH */
282
rqst->rq_xprt->xp_auth = &client_data->svcauth;
283
284
if (client_data->established == FALSE) {
285
PRINTF(("svcauth_gssapi: context is not established\n"));
286
287
if (creds.auth_msg == FALSE) {
288
PRINTF(("svcauth_gssapi: expected auth_msg TRUE\n"));
289
LOG_MISCERR("protocol error on incomplete connection");
290
ret = AUTH_REJECTEDCRED;
291
goto error;
292
}
293
294
/*
295
* If the context is not established, then only GSSAPI_INIT
296
* and _CONTINUE requests are valid.
297
*/
298
if (rqst->rq_proc != AUTH_GSSAPI_INIT && rqst->rq_proc !=
299
AUTH_GSSAPI_CONTINUE_INIT) {
300
PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
301
rqst->rq_proc));
302
LOG_MISCERR("protocol error on incomplete connection");
303
ret = AUTH_FAILED;
304
goto error;
305
}
306
307
/* call is for us, deserialize arguments */
308
memset(&call_arg, 0, sizeof(call_arg));
309
if (! svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_authgssapi_init_arg,
310
&call_arg)) {
311
PRINTF(("svcauth_gssapi: cannot decode args\n"));
312
LOG_MISCERR("protocol error in procedure arguments");
313
ret = AUTH_BADCRED;
314
goto error;
315
}
316
317
/*
318
* Process the call arg version number.
319
*
320
* Set the krb5_gss backwards-compatibility mode based on client
321
* version. This controls whether the AP_REP message is
322
* encrypted with the session key (version 2+, correct) or the
323
* session subkey (version 1, incorrect). This function can
324
* never fail, so we don't bother checking its return value.
325
*/
326
switch (call_arg.version) {
327
case 1:
328
case 2:
329
LOG_MISCERR("Warning: Accepted old RPC protocol request");
330
call_res.version = 1;
331
break;
332
case 3:
333
case 4:
334
/* 3 and 4 are essentially the same, don't bother warning */
335
call_res.version = call_arg.version;
336
break;
337
default:
338
PRINTF(("svcauth_gssapi: bad GSSAPI_INIT version\n"));
339
LOG_MISCERR("unsupported GSSAPI_INIT version");
340
ret = AUTH_BADCRED;
341
goto error;
342
}
343
344
#ifdef GSS_BACKWARD_HACK
345
krb5_gss_set_backward_mode(&minor_stat, call_arg.version == 1);
346
#endif
347
348
if (call_arg.version >= 3) {
349
memset(&bindings, 0, sizeof(bindings));
350
bindings.application_data.length = 0;
351
bindings.initiator_addrtype = GSS_C_AF_INET;
352
bindings.initiator_address.length = 4;
353
bindings.initiator_address.value =
354
&svc_getcaller(rqst->rq_xprt)->sin_addr.s_addr;
355
356
if (rqst->rq_xprt->xp_laddrlen > 0) {
357
bindings.acceptor_addrtype = GSS_C_AF_INET;
358
bindings.acceptor_address.length = 4;
359
bindings.acceptor_address.value =
360
&rqst->rq_xprt->xp_laddr.sin_addr.s_addr;
361
} else {
362
LOG_MISCERR("cannot get local address");
363
ret = AUTH_FAILED;
364
goto error;
365
}
366
367
368
bindp = &bindings;
369
} else {
370
bindp = GSS_C_NO_CHANNEL_BINDINGS;
371
}
372
373
/*
374
* If the client's server_creds is already set, use it.
375
* Otherwise, try each credential in server_creds_list until
376
* one of them succeeds, then set the client server_creds
377
* to that. If all fail, the client's server_creds isn't
378
* set (which is fine, because the client will be gc'ed
379
* anyway).
380
*
381
* If accept_sec_context returns something other than
382
* success and GSS_S_FAILURE, then assume different
383
* credentials won't help and stop looping.
384
*
385
* Note that there are really two cases here: (1) the client
386
* has a server_creds already, and (2) it does not. They
387
* are both written in the same loop so that there is only
388
* one textual call to gss_accept_sec_context; in fact, in
389
* case (1), the loop is executed exactly once.
390
*/
391
for (i = 0; i < server_creds_count; i++) {
392
if (client_data->server_creds != NULL) {
393
PRINTF(("svcauth_gssapi: using's clients server_creds\n"));
394
server_creds = client_data->server_creds;
395
} else {
396
PRINTF(("svcauth_gssapi: trying creds %d\n", i));
397
server_creds = server_creds_list[i];
398
}
399
400
/* Free previous output_token from loop */
401
if(i != 0) gss_release_buffer(&minor_stat, &output_token);
402
403
call_res.gss_major =
404
gss_accept_sec_context(&call_res.gss_minor,
405
&client_data->context,
406
server_creds,
407
&call_arg.token,
408
bindp,
409
&client_data->client_name,
410
NULL,
411
&output_token,
412
&ret_flags,
413
&time_rec,
414
NULL);
415
416
if (server_creds == client_data->server_creds)
417
break;
418
419
PRINTF(("accept_sec_context returned 0x%x 0x%x not-us=%#x\n",
420
call_res.gss_major, call_res.gss_minor,
421
(int) KRB5KRB_AP_ERR_NOT_US));
422
if (call_res.gss_major == GSS_S_COMPLETE ||
423
call_res.gss_major == GSS_S_CONTINUE_NEEDED) {
424
/* server_creds was right, set it! */
425
PRINTF(("svcauth_gssapi: creds are correct, storing\n"));
426
client_data->server_creds = server_creds;
427
client_data->server_name = server_name_list[i];
428
break;
429
} else if (call_res.gss_major != GSS_S_FAILURE
430
#ifdef GSSAPI_KRB5
431
/*
432
* hard-coded because there is no other way
433
* to prevent all GSS_S_FAILURES from
434
* returning a "wrong principal in request"
435
* error
436
*/
437
|| ((krb5_error_code) call_res.gss_minor !=
438
(krb5_error_code) KRB5KRB_AP_ERR_NOT_US)
439
#endif
440
) {
441
break;
442
}
443
}
444
445
gssstat = call_res.gss_major;
446
minor_stat = call_res.gss_minor;
447
448
/* done with call args */
449
xdr_free((xdrproc_t)xdr_authgssapi_init_arg, &call_arg);
450
451
PRINTF(("svcauth_gssapi: accept_sec_context returned %#x %#x\n",
452
call_res.gss_major, call_res.gss_minor));
453
if (call_res.gss_major != GSS_S_COMPLETE &&
454
call_res.gss_major != GSS_S_CONTINUE_NEEDED) {
455
AUTH_GSSAPI_DISPLAY_STATUS(("accepting context",
456
call_res.gss_major,
457
call_res.gss_minor));
458
459
badauth(call_res.gss_major, call_res.gss_minor, rqst->rq_xprt);
460
461
gss_release_buffer(&minor_stat, &output_token);
462
svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_authgssapi_init_res,
463
(caddr_t) &call_res);
464
*no_dispatch = TRUE;
465
ret = AUTH_OK;
466
goto error;
467
}
468
469
if (output_token.length != 0) {
470
PRINTF(("svcauth_gssapi: got new output token\n"));
471
GSS_COPY_BUFFER(call_res.token, output_token);
472
}
473
474
if (gssstat == GSS_S_COMPLETE) {
475
client_data->seq_num = rand();
476
client_expire(client_data,
477
(time_rec == GSS_C_INDEFINITE ?
478
INDEF_EXPIRE : time_rec) + time(0));
479
480
PRINTF(("svcauth_gssapi: context established, isn %d\n",
481
client_data->seq_num));
482
483
if (auth_gssapi_seal_seq(client_data->context,
484
client_data->seq_num,
485
&call_res.signed_isn) ==
486
FALSE) {
487
ret = AUTH_FAILED;
488
LOG_MISCERR("internal error sealing sequence number");
489
gss_release_buffer(&minor_stat, &output_token);
490
goto error;
491
}
492
}
493
494
PRINTF(("svcauth_gssapi: sending reply\n"));
495
svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_authgssapi_init_res,
496
(caddr_t) &call_res);
497
*no_dispatch = TRUE;
498
499
/*
500
* If appropriate, set established to TRUE *after* sending
501
* response (otherwise, the client will receive the final
502
* token encrypted)
503
*/
504
if (gssstat == GSS_S_COMPLETE) {
505
gss_release_buffer(&minor_stat, &call_res.signed_isn);
506
client_data->established = TRUE;
507
}
508
gss_release_buffer(&minor_stat, &output_token);
509
} else {
510
PRINTF(("svcauth_gssapi: context is established\n"));
511
512
/* check the verifier */
513
PRINTF(("svcauth_gssapi: checking verifier, len %d\n",
514
verf->oa_length));
515
516
in_buf.length = verf->oa_length;
517
in_buf.value = verf->oa_base;
518
519
if (auth_gssapi_unseal_seq(client_data->context, &in_buf,
520
&seq_num) == FALSE) {
521
ret = AUTH_BADVERF;
522
LOG_MISCERR("internal error unsealing sequence number");
523
goto error;
524
}
525
526
if (seq_num != client_data->seq_num + 1) {
527
PRINTF(("svcauth_gssapi: expected isn %d, got %d\n",
528
client_data->seq_num + 1, seq_num));
529
if (log_badverf != NULL)
530
(*log_badverf)(client_data->client_name,
531
client_data->server_name,
532
rqst, msg, log_badverf_data);
533
534
ret = AUTH_REJECTEDVERF;
535
goto error;
536
}
537
client_data->seq_num++;
538
539
PRINTF(("svcauth_gssapi: seq_num %d okay\n", seq_num));
540
541
/* free previous response verifier, if any */
542
if (client_data->prev_verf.length != 0) {
543
gss_release_buffer(&minor_stat, &client_data->prev_verf);
544
client_data->prev_verf.length = 0;
545
}
546
547
/* prepare response verifier */
548
seq_num = client_data->seq_num + 1;
549
if (auth_gssapi_seal_seq(client_data->context, seq_num,
550
&out_buf) == FALSE) {
551
ret = AUTH_FAILED;
552
LOG_MISCERR("internal error sealing sequence number");
553
goto error;
554
}
555
556
client_data->seq_num++;
557
558
PRINTF(("svcauth_gssapi; response seq_num %d\n", seq_num));
559
560
rqst->rq_xprt->xp_verf.oa_flavor = AUTH_GSSAPI;
561
rqst->rq_xprt->xp_verf.oa_base = out_buf.value;
562
rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
563
564
/* save verifier so it can be freed next time */
565
client_data->prev_verf.value = out_buf.value;
566
client_data->prev_verf.length = out_buf.length;
567
568
/*
569
* Message is authentic. If auth_msg if true, process the
570
* call; otherwise, return AUTH_OK so it will be dispatched
571
* to the application server.
572
*/
573
574
if (creds.auth_msg == TRUE) {
575
/*
576
* If process_token fails, then the token probably came
577
* from an attacker. No response (error or otherwise)
578
* should be returned to the client, since it won't be
579
* accepting one.
580
*/
581
582
switch (rqst->rq_proc) {
583
case AUTH_GSSAPI_MSG:
584
PRINTF(("svcauth_gssapi: GSSAPI_MSG, getting args\n"));
585
memset(&call_arg, 0, sizeof(call_arg));
586
if (! svc_getargs(rqst->rq_xprt,
587
(xdrproc_t)xdr_authgssapi_init_arg,
588
&call_arg)) {
589
PRINTF(("svcauth_gssapi: cannot decode args\n"));
590
LOG_MISCERR("protocol error in call arguments");
591
xdr_free((xdrproc_t)xdr_authgssapi_init_arg,
592
&call_arg);
593
ret = AUTH_BADCRED;
594
goto error;
595
}
596
597
PRINTF(("svcauth_gssapi: processing token\n"));
598
gssstat = gss_process_context_token(&minor_stat,
599
client_data->context,
600
&call_arg.token);
601
602
/* done with call args */
603
xdr_free((xdrproc_t)xdr_authgssapi_init_arg, &call_arg);
604
605
if (gssstat != GSS_S_COMPLETE) {
606
AUTH_GSSAPI_DISPLAY_STATUS(("processing token",
607
gssstat, minor_stat));
608
ret = AUTH_FAILED;
609
goto error;
610
}
611
612
svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
613
*no_dispatch = TRUE;
614
break;
615
616
case AUTH_GSSAPI_DESTROY:
617
PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n"));
618
619
PRINTF(("svcauth_gssapi: sending reply\n"));
620
svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
621
*no_dispatch = TRUE;
622
623
destroy_client(client_data);
624
rqst->rq_xprt->xp_auth = NULL;
625
break;
626
627
default:
628
PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
629
rqst->rq_proc));
630
LOG_MISCERR("invalid call procedure number");
631
ret = AUTH_FAILED;
632
goto error;
633
}
634
} else {
635
/* set credentials for app server; comment in svc.c */
636
/* seems to imply this is incorrect, but I don't see */
637
/* any problem with it... */
638
rqst->rq_clntcred = (char *)client_data->client_name;
639
rqst->rq_svccred = (char *)client_data->context;
640
}
641
}
642
643
if (creds.client_handle.length != 0) {
644
PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
645
(int) creds.client_handle.length));
646
xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds);
647
}
648
649
PRINTF(("\n"));
650
return AUTH_OK;
651
652
error:
653
if (creds.client_handle.length != 0) {
654
PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
655
(int) creds.client_handle.length));
656
xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds);
657
}
658
659
PRINTF(("\n"));
660
return ret;
661
}
662
663
static void cleanup(void)
664
{
665
client_list *c, *c2;
666
667
PRINTF(("cleanup_and_exit: starting\n"));
668
669
c = clients;
670
while (c) {
671
c2 = c;
672
c = c->next;
673
destroy_client(c2->client);
674
free(c2);
675
}
676
677
exit(0);
678
}
679
680
/*
681
* Function: create_client
682
*
683
* Purpose: Creates an new client_data structure and stores it in the
684
* database.
685
*
686
* Returns: the new client_data structure, or NULL on failure.
687
*
688
* Effects:
689
*
690
* A new client_data is created and stored in the hash table and
691
* b-tree. A new key that is unique in the current database is
692
* chosen; this key should be used as the client's client_handle.
693
*/
694
static svc_auth_gssapi_data *create_client(void)
695
{
696
client_list *c;
697
svc_auth_gssapi_data *client_data;
698
static int client_key = 1;
699
700
PRINTF(("svcauth_gssapi: empty creds, creating\n"));
701
702
client_data = (svc_auth_gssapi_data *) malloc(sizeof(*client_data));
703
if (client_data == NULL)
704
return NULL;
705
memset(client_data, 0, sizeof(*client_data));
706
L_PRINTF(2, ("create_client: new client_data = %p\n",
707
(void *) client_data));
708
709
/* set up client data structure */
710
client_data->established = 0;
711
client_data->context = GSS_C_NO_CONTEXT;
712
client_data->expiration = time(0) + INITIATION_TIMEOUT;
713
714
/* set up psycho-recursive SVCAUTH hack */
715
client_data->svcauth.svc_ah_ops = &svc_auth_gssapi_ops;
716
client_data->svcauth.svc_ah_private = (caddr_t) client_data;
717
718
client_data->key = client_key++;
719
720
c = (client_list *) malloc(sizeof(client_list));
721
if (c == NULL)
722
return NULL;
723
c->client = client_data;
724
c->next = NULL;
725
726
727
if (clients == NULL)
728
clients = c;
729
else {
730
c->next = clients;
731
clients = c;
732
}
733
734
PRINTF(("svcauth_gssapi: new handle %d\n", client_data->key));
735
L_PRINTF(2, ("create_client: done\n"));
736
737
return client_data;
738
}
739
740
/*
741
* Function: client_expire
742
*
743
* Purpose: change the expiration time of a client in the database
744
*
745
* Arguments:
746
*
747
* client_data (r) the client_data to expire
748
* exp (r) the new expiration time
749
*
750
* Effects:
751
*
752
* client_data->expiration = exp
753
*
754
* This function used to remove client_data from the database, change
755
* its expiration time, and re-add it, which was necessary because the
756
* database was sorted by expiration time so a simple modification
757
* would break the rep invariant. Now the database is an unsorted
758
* linked list, so it doesn't matter.
759
*/
760
static void client_expire(
761
svc_auth_gssapi_data *client_data,
762
uint32_t exp)
763
{
764
client_data->expiration = exp;
765
}
766
767
/*
768
* Function get_client
769
*
770
* Purpose: retrieve a client_data structure from the database based
771
* on its client handle (key)
772
*
773
* Arguments:
774
*
775
* client_handle (r) the handle (key) to retrieve
776
*
777
* Effects:
778
*
779
* Searches the list and returns the client_data whose key field
780
* matches the contents of client_handle, or returns NULL if none was
781
* found.
782
*/
783
static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle)
784
{
785
client_list *c;
786
uint32_t handle;
787
788
memcpy(&handle, client_handle->value, 4);
789
790
L_PRINTF(2, ("get_client: looking for client %d\n", handle));
791
792
c = clients;
793
while (c) {
794
if (c->client->key == handle)
795
return c->client;
796
c = c->next;
797
}
798
799
L_PRINTF(2, ("get_client: client_handle lookup failed\n"));
800
return NULL;
801
}
802
803
/*
804
* Function: destroy_client
805
*
806
* Purpose: destroys a client entry and removes it from the database
807
*
808
* Arguments:
809
*
810
* client_data (r) the client to be destroyed
811
*
812
* Effects:
813
*
814
* client_data->context is deleted with gss_delete_sec_context.
815
* client_data's entry in the database is destroyed. client_data is
816
* freed.
817
*/
818
static void destroy_client(svc_auth_gssapi_data *client_data)
819
{
820
OM_uint32 gssstat, minor_stat;
821
gss_buffer_desc out_buf;
822
client_list *c, *c2;
823
824
PRINTF(("destroy_client: destroying client_data\n"));
825
L_PRINTF(2, ("destroy_client: client_data = %p\n", (void *) client_data));
826
827
#ifdef DEBUG_GSSAPI
828
if (svc_debug_gssapi >= 3)
829
dump_db("before frees");
830
#endif
831
832
/* destroy client struct even if error occurs */
833
834
gssstat = gss_delete_sec_context(&minor_stat, &client_data->context,
835
&out_buf);
836
if (gssstat != GSS_S_COMPLETE)
837
AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat,
838
minor_stat));
839
840
gss_release_buffer(&minor_stat, &out_buf);
841
gss_release_name(&minor_stat, &client_data->client_name);
842
if (client_data->prev_verf.length != 0)
843
gss_release_buffer(&minor_stat, &client_data->prev_verf);
844
845
if (clients == NULL) {
846
PRINTF(("destroy_client: called on empty database\n"));
847
abort();
848
} else if (clients->client == client_data) {
849
c = clients;
850
clients = clients->next;
851
free(c);
852
} else {
853
c2 = clients;
854
c = clients->next;
855
while (c) {
856
if (c->client == client_data) {
857
c2->next = c->next;
858
free(c);
859
goto done;
860
} else {
861
c2 = c;
862
c = c->next;
863
}
864
}
865
PRINTF(("destroy_client: client_handle delete failed\n"));
866
abort();
867
}
868
869
done:
870
871
L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key));
872
873
free(client_data);
874
}
875
876
static void dump_db(char *msg)
877
{
878
svc_auth_gssapi_data *client_data;
879
client_list *c;
880
881
L_PRINTF(3, ("dump_db: %s:\n", msg));
882
883
c = clients;
884
while (c) {
885
client_data = c->client;
886
L_PRINTF(3, ("\tclient_data = %p, exp = %d\n",
887
(void *) client_data, client_data->expiration));
888
c = c->next;
889
}
890
891
L_PRINTF(3, ("\n"));
892
}
893
894
static void clean_client(void)
895
{
896
svc_auth_gssapi_data *client_data;
897
client_list *c;
898
899
PRINTF(("clean_client: starting\n"));
900
901
c = clients;
902
while (c) {
903
client_data = c->client;
904
905
L_PRINTF(2, ("clean_client: client_data = %p\n",
906
(void *) client_data));
907
908
if (client_data->expiration < time(0)) {
909
PRINTF(("clean_client: client %d expired\n",
910
client_data->key));
911
destroy_client(client_data);
912
c = clients; /* start over, just to be safe */
913
} else {
914
c = c->next;
915
}
916
}
917
918
PRINTF(("clean_client: done\n"));
919
}
920
921
/*
922
* Function: svcauth_gssapi_set_names
923
*
924
* Purpose: Sets the list of service names for which incoming
925
* authentication requests should be honored.
926
*
927
* See functional specifications.
928
*/
929
bool_t svcauth_gssapi_set_names(
930
auth_gssapi_name *names,
931
int num)
932
{
933
OM_uint32 gssstat, minor_stat;
934
gss_buffer_desc in_buf;
935
int i;
936
937
if (num == 0)
938
for (; names[num].name != NULL; num++)
939
;
940
941
server_creds_list = NULL;
942
server_name_list = NULL;
943
944
server_creds_list = (gss_cred_id_t *) malloc(num*sizeof(gss_cred_id_t));
945
if (server_creds_list == NULL)
946
goto fail;
947
server_name_list = (gss_name_t *) malloc(num*sizeof(gss_name_t));
948
if (server_name_list == NULL)
949
goto fail;
950
951
for (i = 0; i < num; i++) {
952
server_name_list[i] = 0;
953
server_creds_list[i] = 0;
954
}
955
956
server_creds_count = num;
957
958
for (i = 0; i < num; i++) {
959
in_buf.value = names[i].name;
960
in_buf.length = strlen(in_buf.value) + 1;
961
962
PRINTF(("svcauth_gssapi_set_names: importing %s\n", names[i].name));
963
964
gssstat = gss_import_name(&minor_stat, &in_buf, names[i].type,
965
&server_name_list[i]);
966
967
if (gssstat != GSS_S_COMPLETE) {
968
AUTH_GSSAPI_DISPLAY_STATUS(("importing name", gssstat,
969
minor_stat));
970
goto fail;
971
}
972
973
gssstat = gss_acquire_cred(&minor_stat, server_name_list[i], 0,
974
GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
975
&server_creds_list[i], NULL, NULL);
976
if (gssstat != GSS_S_COMPLETE) {
977
AUTH_GSSAPI_DISPLAY_STATUS(("acquiring credentials",
978
gssstat, minor_stat));
979
goto fail;
980
}
981
}
982
983
return TRUE;
984
985
fail:
986
svcauth_gssapi_unset_names();
987
988
return FALSE;
989
}
990
991
/* Function: svcauth_gssapi_unset_names
992
*
993
* Purpose: releases the names and credentials allocated by
994
* svcauth_gssapi_set_names
995
*/
996
997
void svcauth_gssapi_unset_names(void)
998
{
999
int i;
1000
OM_uint32 minor_stat;
1001
1002
if (server_creds_list) {
1003
for (i = 0; i < server_creds_count; i++)
1004
if (server_creds_list[i])
1005
gss_release_cred(&minor_stat, &server_creds_list[i]);
1006
free(server_creds_list);
1007
server_creds_list = NULL;
1008
}
1009
1010
if (server_name_list) {
1011
for (i = 0; i < server_creds_count; i++)
1012
if (server_name_list[i])
1013
gss_release_name(&minor_stat, &server_name_list[i]);
1014
free(server_name_list);
1015
server_name_list = NULL;
1016
}
1017
server_creds_count = 0;
1018
}
1019
1020
1021
/*
1022
* Function: svcauth_gssapi_set_log_badauth_func
1023
*
1024
* Purpose: sets the logging function called when an invalid RPC call
1025
* arrives
1026
*
1027
* See functional specifications.
1028
*/
1029
void svcauth_gssapi_set_log_badauth_func(
1030
auth_gssapi_log_badauth_func func,
1031
caddr_t data)
1032
{
1033
log_badauth = func;
1034
log_badauth_data = data;
1035
}
1036
1037
void
1038
svcauth_gssapi_set_log_badauth2_func(auth_gssapi_log_badauth2_func func,
1039
caddr_t data)
1040
{
1041
log_badauth2 = func;
1042
log_badauth2_data = data;
1043
}
1044
1045
/*
1046
* Function: svcauth_gssapi_set_log_badverf_func
1047
*
1048
* Purpose: sets the logging function called when an invalid RPC call
1049
* arrives
1050
*
1051
* See functional specifications.
1052
*/
1053
void svcauth_gssapi_set_log_badverf_func(
1054
auth_gssapi_log_badverf_func func,
1055
caddr_t data)
1056
{
1057
log_badverf = func;
1058
log_badverf_data = data;
1059
}
1060
1061
/*
1062
* Function: svcauth_gssapi_set_log_miscerr_func
1063
*
1064
* Purpose: sets the logging function called when a miscellaneous
1065
* AUTH_GSSAPI error occurs
1066
*
1067
* See functional specifications.
1068
*/
1069
void svcauth_gssapi_set_log_miscerr_func(
1070
auth_gssapi_log_miscerr_func func,
1071
caddr_t data)
1072
{
1073
log_miscerr = func;
1074
log_miscerr_data = data;
1075
}
1076
1077
/*
1078
* Encrypt the serialized arguments from xdr_func applied to xdr_ptr
1079
* and write the result to xdrs.
1080
*/
1081
static bool_t svc_auth_gssapi_wrap(
1082
SVCAUTH *auth,
1083
XDR *out_xdrs,
1084
xdrproc_t xdr_func,
1085
caddr_t xdr_ptr)
1086
{
1087
OM_uint32 gssstat, minor_stat;
1088
1089
if (! SVCAUTH_PRIVATE(auth)->established) {
1090
PRINTF(("svc_gssapi_wrap: not established, noop\n"));
1091
return (*xdr_func)(out_xdrs, xdr_ptr);
1092
} else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat,
1093
SVCAUTH_PRIVATE(auth)->context,
1094
SVCAUTH_PRIVATE(auth)->seq_num,
1095
out_xdrs, xdr_func, xdr_ptr)) {
1096
if (gssstat != GSS_S_COMPLETE)
1097
AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments",
1098
gssstat, minor_stat));
1099
return FALSE;
1100
} else
1101
return TRUE;
1102
}
1103
1104
static bool_t svc_auth_gssapi_unwrap(
1105
SVCAUTH *auth,
1106
XDR *in_xdrs,
1107
xdrproc_t xdr_func,
1108
caddr_t xdr_ptr)
1109
{
1110
svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth);
1111
OM_uint32 gssstat, minor_stat;
1112
1113
if (! client_data->established) {
1114
PRINTF(("svc_gssapi_unwrap: not established, noop\n"));
1115
return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *)(void *) xdr_ptr);
1116
} else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat,
1117
client_data->context,
1118
client_data->seq_num-1,
1119
in_xdrs, xdr_func, xdr_ptr)) {
1120
if (gssstat != GSS_S_COMPLETE)
1121
AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments",
1122
gssstat, minor_stat));
1123
return FALSE;
1124
} else
1125
return TRUE;
1126
}
1127
1128
static bool_t svc_auth_gssapi_destroy(SVCAUTH *auth)
1129
{
1130
svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth);
1131
1132
destroy_client(client_data);
1133
return TRUE;
1134
}
1135
1136