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;
gssrpcint_printf(const char * format,...)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
badauth(OM_uint32 maj,OM_uint32 minor,SVCXPRT * xprt)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
gssrpc__svcauth_gssapi(struct svc_req * rqst,struct rpc_msg * msg,bool_t * no_dispatch)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(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(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, 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(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, 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, 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, xdr_authgssapi_init_arg,
587 &call_arg)) {
588 PRINTF(("svcauth_gssapi: cannot decode args\n"));
589 LOG_MISCERR("protocol error in call arguments");
590 xdr_free(xdr_authgssapi_init_arg, &call_arg);
591 ret = AUTH_BADCRED;
592 goto error;
593 }
594
595 PRINTF(("svcauth_gssapi: processing token\n"));
596 gssstat = gss_process_context_token(&minor_stat,
597 client_data->context,
598 &call_arg.token);
599
600 /* done with call args */
601 xdr_free(xdr_authgssapi_init_arg, &call_arg);
602
603 if (gssstat != GSS_S_COMPLETE) {
604 AUTH_GSSAPI_DISPLAY_STATUS(("processing token",
605 gssstat, minor_stat));
606 ret = AUTH_FAILED;
607 goto error;
608 }
609
610 svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
611 *no_dispatch = TRUE;
612 break;
613
614 case AUTH_GSSAPI_DESTROY:
615 PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n"));
616
617 PRINTF(("svcauth_gssapi: sending reply\n"));
618 svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
619 *no_dispatch = TRUE;
620
621 destroy_client(client_data);
622 rqst->rq_xprt->xp_auth = NULL;
623 break;
624
625 default:
626 PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
627 rqst->rq_proc));
628 LOG_MISCERR("invalid call procedure number");
629 ret = AUTH_FAILED;
630 goto error;
631 }
632 } else {
633 /* set credentials for app server; comment in svc.c */
634 /* seems to imply this is incorrect, but I don't see */
635 /* any problem with it... */
636 rqst->rq_clntcred = (char *)client_data->client_name;
637 rqst->rq_svccred = (char *)client_data->context;
638 }
639 }
640
641 if (creds.client_handle.length != 0) {
642 PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
643 (int) creds.client_handle.length));
644 xdr_free(xdr_authgssapi_creds, &creds);
645 }
646
647 PRINTF(("\n"));
648 return AUTH_OK;
649
650 error:
651 if (creds.client_handle.length != 0) {
652 PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
653 (int) creds.client_handle.length));
654 xdr_free(xdr_authgssapi_creds, &creds);
655 }
656
657 PRINTF(("\n"));
658 return ret;
659 }
660
cleanup(void)661 static void cleanup(void)
662 {
663 client_list *c, *c2;
664
665 PRINTF(("cleanup_and_exit: starting\n"));
666
667 c = clients;
668 while (c) {
669 c2 = c;
670 c = c->next;
671 destroy_client(c2->client);
672 free(c2);
673 }
674
675 exit(0);
676 }
677
678 /*
679 * Function: create_client
680 *
681 * Purpose: Creates an new client_data structure and stores it in the
682 * database.
683 *
684 * Returns: the new client_data structure, or NULL on failure.
685 *
686 * Effects:
687 *
688 * A new client_data is created and stored in the hash table and
689 * b-tree. A new key that is unique in the current database is
690 * chosen; this key should be used as the client's client_handle.
691 */
create_client(void)692 static svc_auth_gssapi_data *create_client(void)
693 {
694 client_list *c;
695 svc_auth_gssapi_data *client_data;
696 static int client_key = 1;
697
698 PRINTF(("svcauth_gssapi: empty creds, creating\n"));
699
700 client_data = (svc_auth_gssapi_data *) malloc(sizeof(*client_data));
701 if (client_data == NULL)
702 return NULL;
703 memset(client_data, 0, sizeof(*client_data));
704 L_PRINTF(2, ("create_client: new client_data = %p\n",
705 (void *) client_data));
706
707 /* set up client data structure */
708 client_data->established = 0;
709 client_data->context = GSS_C_NO_CONTEXT;
710 client_data->expiration = time(0) + INITIATION_TIMEOUT;
711
712 /* set up psycho-recursive SVCAUTH hack */
713 client_data->svcauth.svc_ah_ops = &svc_auth_gssapi_ops;
714 client_data->svcauth.svc_ah_private = (caddr_t) client_data;
715
716 client_data->key = client_key++;
717
718 c = (client_list *) malloc(sizeof(client_list));
719 if (c == NULL)
720 return NULL;
721 c->client = client_data;
722 c->next = NULL;
723
724
725 if (clients == NULL)
726 clients = c;
727 else {
728 c->next = clients;
729 clients = c;
730 }
731
732 PRINTF(("svcauth_gssapi: new handle %d\n", client_data->key));
733 L_PRINTF(2, ("create_client: done\n"));
734
735 return client_data;
736 }
737
738 /*
739 * Function: client_expire
740 *
741 * Purpose: change the expiration time of a client in the database
742 *
743 * Arguments:
744 *
745 * client_data (r) the client_data to expire
746 * exp (r) the new expiration time
747 *
748 * Effects:
749 *
750 * client_data->expiration = exp
751 *
752 * This function used to remove client_data from the database, change
753 * its expiration time, and re-add it, which was necessary because the
754 * database was sorted by expiration time so a simple modification
755 * would break the rep invariant. Now the database is an unsorted
756 * linked list, so it doesn't matter.
757 */
client_expire(svc_auth_gssapi_data * client_data,uint32_t exp)758 static void client_expire(
759 svc_auth_gssapi_data *client_data,
760 uint32_t exp)
761 {
762 client_data->expiration = exp;
763 }
764
765 /*
766 * Function get_client
767 *
768 * Purpose: retrieve a client_data structure from the database based
769 * on its client handle (key)
770 *
771 * Arguments:
772 *
773 * client_handle (r) the handle (key) to retrieve
774 *
775 * Effects:
776 *
777 * Searches the list and returns the client_data whose key field
778 * matches the contents of client_handle, or returns NULL if none was
779 * found.
780 */
get_client(gss_buffer_t client_handle)781 static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle)
782 {
783 client_list *c;
784 uint32_t handle;
785
786 memcpy(&handle, client_handle->value, 4);
787
788 L_PRINTF(2, ("get_client: looking for client %d\n", handle));
789
790 c = clients;
791 while (c) {
792 if (c->client->key == handle)
793 return c->client;
794 c = c->next;
795 }
796
797 L_PRINTF(2, ("get_client: client_handle lookup failed\n"));
798 return NULL;
799 }
800
801 /*
802 * Function: destroy_client
803 *
804 * Purpose: destroys a client entry and removes it from the database
805 *
806 * Arguments:
807 *
808 * client_data (r) the client to be destroyed
809 *
810 * Effects:
811 *
812 * client_data->context is deleted with gss_delete_sec_context.
813 * client_data's entry in the database is destroyed. client_data is
814 * freed.
815 */
destroy_client(svc_auth_gssapi_data * client_data)816 static void destroy_client(svc_auth_gssapi_data *client_data)
817 {
818 OM_uint32 gssstat, minor_stat;
819 gss_buffer_desc out_buf;
820 client_list *c, *c2;
821
822 PRINTF(("destroy_client: destroying client_data\n"));
823 L_PRINTF(2, ("destroy_client: client_data = %p\n", (void *) client_data));
824
825 #ifdef DEBUG_GSSAPI
826 if (svc_debug_gssapi >= 3)
827 dump_db("before frees");
828 #endif
829
830 /* destroy client struct even if error occurs */
831
832 gssstat = gss_delete_sec_context(&minor_stat, &client_data->context,
833 &out_buf);
834 if (gssstat != GSS_S_COMPLETE)
835 AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat,
836 minor_stat));
837
838 gss_release_buffer(&minor_stat, &out_buf);
839 gss_release_name(&minor_stat, &client_data->client_name);
840 if (client_data->prev_verf.length != 0)
841 gss_release_buffer(&minor_stat, &client_data->prev_verf);
842
843 if (clients == NULL) {
844 PRINTF(("destroy_client: called on empty database\n"));
845 abort();
846 } else if (clients->client == client_data) {
847 c = clients;
848 clients = clients->next;
849 free(c);
850 } else {
851 c2 = clients;
852 c = clients->next;
853 while (c) {
854 if (c->client == client_data) {
855 c2->next = c->next;
856 free(c);
857 goto done;
858 } else {
859 c2 = c;
860 c = c->next;
861 }
862 }
863 PRINTF(("destroy_client: client_handle delete failed\n"));
864 abort();
865 }
866
867 done:
868
869 L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key));
870
871 free(client_data);
872 }
873
dump_db(char * msg)874 static void dump_db(char *msg)
875 {
876 svc_auth_gssapi_data *client_data;
877 client_list *c;
878
879 L_PRINTF(3, ("dump_db: %s:\n", msg));
880
881 c = clients;
882 while (c) {
883 client_data = c->client;
884 L_PRINTF(3, ("\tclient_data = %p, exp = %d\n",
885 (void *) client_data, client_data->expiration));
886 c = c->next;
887 }
888
889 L_PRINTF(3, ("\n"));
890 }
891
clean_client(void)892 static void clean_client(void)
893 {
894 svc_auth_gssapi_data *client_data;
895 client_list *c;
896
897 PRINTF(("clean_client: starting\n"));
898
899 c = clients;
900 while (c) {
901 client_data = c->client;
902
903 L_PRINTF(2, ("clean_client: client_data = %p\n",
904 (void *) client_data));
905
906 if (client_data->expiration < time(0)) {
907 PRINTF(("clean_client: client %d expired\n",
908 client_data->key));
909 destroy_client(client_data);
910 c = clients; /* start over, just to be safe */
911 } else {
912 c = c->next;
913 }
914 }
915
916 PRINTF(("clean_client: done\n"));
917 }
918
919 /*
920 * Function: svcauth_gssapi_set_names
921 *
922 * Purpose: Sets the list of service names for which incoming
923 * authentication requests should be honored.
924 *
925 * See functional specifications.
926 */
svcauth_gssapi_set_names(auth_gssapi_name * names,int num)927 bool_t svcauth_gssapi_set_names(
928 auth_gssapi_name *names,
929 int num)
930 {
931 OM_uint32 gssstat, minor_stat;
932 gss_buffer_desc in_buf;
933 int i;
934
935 if (num == 0)
936 for (; names[num].name != NULL; num++)
937 ;
938
939 server_creds_list = NULL;
940 server_name_list = NULL;
941
942 server_creds_list = (gss_cred_id_t *) malloc(num*sizeof(gss_cred_id_t));
943 if (server_creds_list == NULL)
944 goto fail;
945 server_name_list = (gss_name_t *) malloc(num*sizeof(gss_name_t));
946 if (server_name_list == NULL)
947 goto fail;
948
949 for (i = 0; i < num; i++) {
950 server_name_list[i] = 0;
951 server_creds_list[i] = 0;
952 }
953
954 server_creds_count = num;
955
956 for (i = 0; i < num; i++) {
957 in_buf.value = names[i].name;
958 in_buf.length = strlen(in_buf.value) + 1;
959
960 PRINTF(("svcauth_gssapi_set_names: importing %s\n", names[i].name));
961
962 gssstat = gss_import_name(&minor_stat, &in_buf, names[i].type,
963 &server_name_list[i]);
964
965 if (gssstat != GSS_S_COMPLETE) {
966 AUTH_GSSAPI_DISPLAY_STATUS(("importing name", gssstat,
967 minor_stat));
968 goto fail;
969 }
970
971 gssstat = gss_acquire_cred(&minor_stat, server_name_list[i], 0,
972 GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
973 &server_creds_list[i], NULL, NULL);
974 if (gssstat != GSS_S_COMPLETE) {
975 AUTH_GSSAPI_DISPLAY_STATUS(("acquiring credentials",
976 gssstat, minor_stat));
977 goto fail;
978 }
979 }
980
981 return TRUE;
982
983 fail:
984 svcauth_gssapi_unset_names();
985
986 return FALSE;
987 }
988
989 /* Function: svcauth_gssapi_unset_names
990 *
991 * Purpose: releases the names and credentials allocated by
992 * svcauth_gssapi_set_names
993 */
994
svcauth_gssapi_unset_names(void)995 void svcauth_gssapi_unset_names(void)
996 {
997 int i;
998 OM_uint32 minor_stat;
999
1000 if (server_creds_list) {
1001 for (i = 0; i < server_creds_count; i++)
1002 if (server_creds_list[i])
1003 gss_release_cred(&minor_stat, &server_creds_list[i]);
1004 free(server_creds_list);
1005 server_creds_list = NULL;
1006 }
1007
1008 if (server_name_list) {
1009 for (i = 0; i < server_creds_count; i++)
1010 if (server_name_list[i])
1011 gss_release_name(&minor_stat, &server_name_list[i]);
1012 free(server_name_list);
1013 server_name_list = NULL;
1014 }
1015 server_creds_count = 0;
1016 }
1017
1018
1019 /*
1020 * Function: svcauth_gssapi_set_log_badauth_func
1021 *
1022 * Purpose: sets the logging function called when an invalid RPC call
1023 * arrives
1024 *
1025 * See functional specifications.
1026 */
svcauth_gssapi_set_log_badauth_func(auth_gssapi_log_badauth_func func,caddr_t data)1027 void svcauth_gssapi_set_log_badauth_func(
1028 auth_gssapi_log_badauth_func func,
1029 caddr_t data)
1030 {
1031 log_badauth = func;
1032 log_badauth_data = data;
1033 }
1034
1035 void
svcauth_gssapi_set_log_badauth2_func(auth_gssapi_log_badauth2_func func,caddr_t data)1036 svcauth_gssapi_set_log_badauth2_func(auth_gssapi_log_badauth2_func func,
1037 caddr_t data)
1038 {
1039 log_badauth2 = func;
1040 log_badauth2_data = data;
1041 }
1042
1043 /*
1044 * Function: svcauth_gssapi_set_log_badverf_func
1045 *
1046 * Purpose: sets the logging function called when an invalid RPC call
1047 * arrives
1048 *
1049 * See functional specifications.
1050 */
svcauth_gssapi_set_log_badverf_func(auth_gssapi_log_badverf_func func,caddr_t data)1051 void svcauth_gssapi_set_log_badverf_func(
1052 auth_gssapi_log_badverf_func func,
1053 caddr_t data)
1054 {
1055 log_badverf = func;
1056 log_badverf_data = data;
1057 }
1058
1059 /*
1060 * Function: svcauth_gssapi_set_log_miscerr_func
1061 *
1062 * Purpose: sets the logging function called when a miscellaneous
1063 * AUTH_GSSAPI error occurs
1064 *
1065 * See functional specifications.
1066 */
svcauth_gssapi_set_log_miscerr_func(auth_gssapi_log_miscerr_func func,caddr_t data)1067 void svcauth_gssapi_set_log_miscerr_func(
1068 auth_gssapi_log_miscerr_func func,
1069 caddr_t data)
1070 {
1071 log_miscerr = func;
1072 log_miscerr_data = data;
1073 }
1074
1075 /*
1076 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
1077 * and write the result to xdrs.
1078 */
svc_auth_gssapi_wrap(SVCAUTH * auth,XDR * out_xdrs,bool_t (* xdr_func)(),caddr_t xdr_ptr)1079 static bool_t svc_auth_gssapi_wrap(
1080 SVCAUTH *auth,
1081 XDR *out_xdrs,
1082 bool_t (*xdr_func)(),
1083 caddr_t xdr_ptr)
1084 {
1085 OM_uint32 gssstat, minor_stat;
1086
1087 if (! SVCAUTH_PRIVATE(auth)->established) {
1088 PRINTF(("svc_gssapi_wrap: not established, noop\n"));
1089 return (*xdr_func)(out_xdrs, xdr_ptr);
1090 } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat,
1091 SVCAUTH_PRIVATE(auth)->context,
1092 SVCAUTH_PRIVATE(auth)->seq_num,
1093 out_xdrs, xdr_func, xdr_ptr)) {
1094 if (gssstat != GSS_S_COMPLETE)
1095 AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments",
1096 gssstat, minor_stat));
1097 return FALSE;
1098 } else
1099 return TRUE;
1100 }
1101
svc_auth_gssapi_unwrap(SVCAUTH * auth,XDR * in_xdrs,bool_t (* xdr_func)(),caddr_t xdr_ptr)1102 static bool_t svc_auth_gssapi_unwrap(
1103 SVCAUTH *auth,
1104 XDR *in_xdrs,
1105 bool_t (*xdr_func)(),
1106 caddr_t xdr_ptr)
1107 {
1108 svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth);
1109 OM_uint32 gssstat, minor_stat;
1110
1111 if (! client_data->established) {
1112 PRINTF(("svc_gssapi_unwrap: not established, noop\n"));
1113 return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *)(void *) xdr_ptr);
1114 } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat,
1115 client_data->context,
1116 client_data->seq_num-1,
1117 in_xdrs, xdr_func, xdr_ptr)) {
1118 if (gssstat != GSS_S_COMPLETE)
1119 AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments",
1120 gssstat, minor_stat));
1121 return FALSE;
1122 } else
1123 return TRUE;
1124 }
1125
svc_auth_gssapi_destroy(SVCAUTH * auth)1126 static bool_t svc_auth_gssapi_destroy(SVCAUTH *auth)
1127 {
1128 svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth);
1129
1130 destroy_client(client_data);
1131 return TRUE;
1132 }
1133