xref: /freebsd/crypto/krb5/src/lib/rpc/svc_auth_gssapi.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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((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 
cleanup(void)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  */
create_client(void)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  */
client_expire(svc_auth_gssapi_data * client_data,uint32_t exp)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  */
get_client(gss_buffer_t client_handle)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  */
destroy_client(svc_auth_gssapi_data * client_data)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 
dump_db(char * msg)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 
clean_client(void)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  */
svcauth_gssapi_set_names(auth_gssapi_name * names,int num)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 
svcauth_gssapi_unset_names(void)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  */
svcauth_gssapi_set_log_badauth_func(auth_gssapi_log_badauth_func func,caddr_t data)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
svcauth_gssapi_set_log_badauth2_func(auth_gssapi_log_badauth2_func func,caddr_t data)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  */
svcauth_gssapi_set_log_badverf_func(auth_gssapi_log_badverf_func func,caddr_t data)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  */
svcauth_gssapi_set_log_miscerr_func(auth_gssapi_log_miscerr_func func,caddr_t data)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  */
svc_auth_gssapi_wrap(SVCAUTH * auth,XDR * out_xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)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 
svc_auth_gssapi_unwrap(SVCAUTH * auth,XDR * in_xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)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 
svc_auth_gssapi_destroy(SVCAUTH * auth)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