xref: /freebsd/crypto/krb5/src/lib/rpc/auth_gssapi.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /*
2  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
3  *
4  */
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <sys/errno.h>
9 
10 #include <gssapi/gssapi.h>
11 #include <gssapi/gssapi_generic.h>
12 #ifdef GSSAPI_KRB5
13 #include <gssapi/gssapi_krb5.h>
14 #endif
15 
16 #include <gssrpc/rpc.h>
17 #include <gssrpc/auth_gssapi.h>
18 
19 #include "gssrpcint.h"
20 
21 #ifdef __CODECENTER__
22 #define DEBUG_GSSAPI 1
23 #endif
24 
25 #ifdef DEBUG_GSSAPI
26 int auth_debug_gssapi = DEBUG_GSSAPI;
27 extern void gssrpcint_printf(const char *format, ...);
28 #define L_PRINTF(l,args) if (auth_debug_gssapi >= l) gssrpcint_printf args
29 #define PRINTF(args) L_PRINTF(99, args)
30 #define AUTH_GSSAPI_DISPLAY_STATUS(args) \
31 	if (auth_debug_gssapi) auth_gssapi_display_status args
32 #else
33 #define PRINTF(args)
34 #define L_PRINTF(l, args)
35 #define AUTH_GSSAPI_DISPLAY_STATUS(args)
36 #endif
37 
38 static void 	auth_gssapi_nextverf(AUTH *);
39 static bool_t 	auth_gssapi_marshall(AUTH *, XDR *);
40 static bool_t	auth_gssapi_validate(AUTH *, struct opaque_auth *);
41 static bool_t	auth_gssapi_refresh(AUTH *, struct rpc_msg *);
42 static bool_t	auth_gssapi_wrap(AUTH *, XDR *, xdrproc_t, caddr_t);
43 static bool_t	auth_gssapi_unwrap(AUTH *, XDR *, xdrproc_t, caddr_t);
44 static void	auth_gssapi_destroy(AUTH *);
45 
46 static bool_t	marshall_new_creds(AUTH *, bool_t, gss_buffer_t);
47 
48 static struct auth_ops auth_gssapi_ops = {
49      auth_gssapi_nextverf,
50      auth_gssapi_marshall,
51      auth_gssapi_validate,
52      auth_gssapi_refresh,
53      auth_gssapi_destroy,
54      auth_gssapi_wrap,
55      auth_gssapi_unwrap,
56 };
57 
58 /*
59  * the ah_private data structure for an auth_handle
60  */
61 struct auth_gssapi_data {
62      bool_t established;
63      CLIENT *clnt;
64      gss_ctx_id_t context;
65      gss_buffer_desc client_handle;
66      uint32_t seq_num;
67      int def_cred;
68 
69      /* pre-serialized ah_cred */
70      unsigned char cred_buf[MAX_AUTH_BYTES];
71      uint32_t cred_len;
72 };
73 #define AUTH_PRIVATE(auth) ((struct auth_gssapi_data *)auth->ah_private)
74 
75 /*
76  * Function: auth_gssapi_create_default
77  *
78  * Purpose:  Create a GSS-API style authenticator, with default
79  * options, and return the handle.
80  *
81  * Effects: See design document, section XXX.
82  */
auth_gssapi_create_default(CLIENT * clnt,char * service_name)83 AUTH *auth_gssapi_create_default(CLIENT *clnt, char *service_name)
84 {
85      AUTH *auth;
86      OM_uint32 gssstat, minor_stat;
87      gss_buffer_desc input_name;
88      gss_name_t target_name;
89 
90      input_name.value = service_name;
91      input_name.length = strlen(service_name) + 1;
92 
93      gssstat = gss_import_name(&minor_stat, &input_name,
94 			       gss_nt_service_name, &target_name);
95      if (gssstat != GSS_S_COMPLETE) {
96 	  AUTH_GSSAPI_DISPLAY_STATUS(("parsing name", gssstat,
97 				      minor_stat));
98 	  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
99 	  rpc_createerr.cf_error.re_errno = ENOMEM;
100 	  return NULL;
101      }
102 
103      auth = auth_gssapi_create(clnt,
104 			       &gssstat,
105 			       &minor_stat,
106 			       GSS_C_NO_CREDENTIAL,
107 			       target_name,
108 			       GSS_C_NULL_OID,
109 			       GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
110 			       0,
111 			       NULL,
112 			       NULL,
113 			       NULL);
114 
115      gss_release_name(&minor_stat, &target_name);
116      return auth;
117 }
118 
119 /*
120  * Function: auth_gssapi_create
121  *
122  * Purpose: Create a GSS-API style authenticator, with all the
123  * options, and return the handle.
124  *
125  * Effects: See design document, section XXX.
126  */
auth_gssapi_create(CLIENT * clnt,OM_uint32 * gssstat,OM_uint32 * minor_stat,gss_cred_id_t claimant_cred_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_OID * actual_mech_type,OM_uint32 * ret_flags,OM_uint32 * time_rec)127 AUTH *auth_gssapi_create(
128      CLIENT *clnt,
129      OM_uint32 *gssstat,
130      OM_uint32 *minor_stat,
131      gss_cred_id_t claimant_cred_handle,
132      gss_name_t target_name,
133      gss_OID mech_type,
134      OM_uint32 req_flags,
135      OM_uint32 time_req,
136      gss_OID *actual_mech_type,
137      OM_uint32 *ret_flags,
138      OM_uint32 *time_rec)
139 {
140      AUTH *auth, *save_auth;
141      struct auth_gssapi_data *pdata;
142      struct gss_channel_bindings_struct bindings, *bindp;
143      struct sockaddr_in laddr, raddr;
144      enum clnt_stat callstat;
145      struct timeval timeout;
146      int bindings_failed;
147      rpcproc_t init_func;
148 
149      auth_gssapi_init_arg call_arg;
150      auth_gssapi_init_res call_res;
151      gss_buffer_desc *input_token, isn_buf;
152 
153      memset(&rpc_createerr, 0, sizeof(rpc_createerr));
154 
155      /* this timeout is only used if clnt_control(clnt, CLSET_TIMEOUT) */
156      /* has not already been called.. therefore, we can just pick */
157      /* something reasonable-sounding.. */
158      timeout.tv_sec = 30;
159      timeout.tv_usec = 0;
160 
161      auth = NULL;
162      pdata = NULL;
163 
164      /* don't assume the caller will want to change clnt->cl_auth */
165      save_auth = clnt->cl_auth;
166 
167      auth = (AUTH *) malloc(sizeof(*auth));
168      pdata = (struct auth_gssapi_data *) malloc(sizeof(*pdata));
169      if (auth == NULL || pdata == NULL) {
170 	  /* They needn't both have failed; clean up.  */
171 	  free(auth);
172 	  free(pdata);
173 	  auth = NULL;
174 	  pdata = NULL;
175 	  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
176 	  rpc_createerr.cf_error.re_errno = ENOMEM;
177 	  goto cleanup;
178      }
179      memset(auth, 0, sizeof(*auth));
180      memset(pdata, 0, sizeof(*pdata));
181 
182      auth->ah_ops = &auth_gssapi_ops;
183      auth->ah_private = (caddr_t) pdata;
184 
185      /* initial creds are auth_msg TRUE and no handle */
186      marshall_new_creds(auth, TRUE, NULL);
187 
188      /* initial verifier is empty */
189      auth->ah_verf.oa_flavor = AUTH_GSSAPI;
190      auth->ah_verf.oa_base = NULL;
191      auth->ah_verf.oa_length = 0;
192 
193      AUTH_PRIVATE(auth)->established = FALSE;
194      AUTH_PRIVATE(auth)->clnt = clnt;
195      AUTH_PRIVATE(auth)->def_cred = (claimant_cred_handle ==
196 				     GSS_C_NO_CREDENTIAL);
197 
198      clnt->cl_auth = auth;
199 
200      /* start by trying latest version */
201      call_arg.version = 4;
202      bindings_failed = 0;
203 
204 try_new_version:
205      /* set state for initial call to init_sec_context */
206      input_token = GSS_C_NO_BUFFER;
207      AUTH_PRIVATE(auth)->context = GSS_C_NO_CONTEXT;
208      init_func = AUTH_GSSAPI_INIT;
209 
210 #ifdef GSSAPI_KRB5
211      /*
212       * OV servers up to version 3 used the old mech id.  Beta 7
213       * servers used version 3 with the new mech id; however, the beta
214       * 7 gss-api accept_sec_context accepts either mech id.  Thus, if
215       * any server rejects version 4, we fall back to version 3 with
216       * the old mech id; for the OV server it will be right, and for
217       * the beta 7 server it will be accepted.  Not ideal, but it
218       * works.
219       */
220      if (call_arg.version < 4 && (mech_type == gss_mech_krb5 ||
221 				  mech_type == GSS_C_NULL_OID))
222 	  mech_type = (gss_OID) gss_mech_krb5_old;
223 #endif
224 
225      if (!bindings_failed && call_arg.version >= 3) {
226 	  if (clnt_control(clnt, CLGET_LOCAL_ADDR, &laddr) == FALSE) {
227 	       PRINTF(("gssapi_create: CLGET_LOCAL_ADDR failed"));
228 	       goto cleanup;
229 	  }
230 	  if (clnt_control(clnt, CLGET_SERVER_ADDR, &raddr) == FALSE) {
231 	       PRINTF(("gssapi_create: CLGET_SERVER_ADDR failed"));
232 	       goto cleanup;
233 	  }
234 
235 	  memset(&bindings, 0, sizeof(bindings));
236 	  bindings.application_data.length = 0;
237 	  bindings.initiator_addrtype = GSS_C_AF_INET;
238 	  bindings.initiator_address.length = 4;
239 	  bindings.initiator_address.value = &laddr.sin_addr.s_addr;
240 
241 	  bindings.acceptor_addrtype = GSS_C_AF_INET;
242 	  bindings.acceptor_address.length = 4;
243 	  bindings.acceptor_address.value = &raddr.sin_addr.s_addr;
244 	  bindp = &bindings;
245      } else {
246 	  bindp = NULL;
247      }
248 
249      memset(&call_res, 0, sizeof(call_res));
250 
251 next_token:
252      *gssstat = gss_init_sec_context(minor_stat,
253 				     claimant_cred_handle,
254 				     &AUTH_PRIVATE(auth)->context,
255 				     target_name,
256 				     mech_type,
257 				     req_flags,
258 				     time_req,
259 				     bindp,
260 				     input_token,
261 				     actual_mech_type,
262 				     &call_arg.token,
263 				     ret_flags,
264 				     time_rec);
265 
266      if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
267 	  AUTH_GSSAPI_DISPLAY_STATUS(("initializing context", *gssstat,
268 				      *minor_stat));
269 	  goto cleanup;
270      }
271 
272      /* if we got a token, pass it on */
273      if (call_arg.token.length != 0) {
274 
275 	  /*
276 	   * sanity check: if we received a signed isn in the last
277 	   * response then there *cannot* be another token to send
278 	   */
279 	  if (call_res.signed_isn.length != 0) {
280 	       PRINTF(("gssapi_create: unexpected token from init_sec\n"));
281 	       goto cleanup;
282 	  }
283 
284 	  PRINTF(("gssapi_create: calling GSSAPI_INIT (%d)\n", init_func));
285 
286 	  xdr_free(xdr_authgssapi_init_res, &call_res);
287 	  memset(&call_res, 0, sizeof(call_res));
288 	  callstat = clnt_call(clnt, init_func,
289 			       xdr_authgssapi_init_arg, &call_arg,
290 			       xdr_authgssapi_init_res, &call_res,
291 			       timeout);
292 	  gss_release_buffer(minor_stat, &call_arg.token);
293 
294 	  if (callstat != RPC_SUCCESS) {
295 	       struct rpc_err err;
296 
297 	       clnt_geterr(clnt, &err);
298 	       if (callstat == RPC_AUTHERROR &&
299 		   (err.re_why == AUTH_BADCRED || err.re_why == AUTH_FAILED)
300 		   && call_arg.version >= 1) {
301 		    L_PRINTF(1,
302 			     ("call_arg protocol version %d rejected, trying %d.\n",
303 			    call_arg.version, call_arg.version-1));
304 		    call_arg.version--;
305 		    goto try_new_version;
306 	       } else {
307 		    PRINTF(("gssapi_create: GSSAPI_INIT (%d) failed, stat %d\n",
308 			    init_func, callstat));
309 	       }
310 
311 	       goto cleanup;
312 	  } else if (call_res.version != call_arg.version &&
313 		     !(call_arg.version == 2 && call_res.version == 1)) {
314 	       /*
315 		* The Secure 1.1 servers always respond with version
316 		* 1.  Thus, if we just tried a version >=3, fall all
317 		* the way back to version 1 since that is all they
318 		* understand
319 		*/
320 	       if (call_arg.version > 2 && call_res.version == 1) {
321 		    L_PRINTF(1,
322 			     ("Talking to Secure 1.1 server, using version 1.\n"));
323 		    call_arg.version = 1;
324 		    goto try_new_version;
325 	       }
326 
327 	       PRINTF(("gssapi_create: invalid call_res vers %d\n",
328 		       call_res.version));
329 	       goto cleanup;
330 	  } else if (call_res.gss_major != GSS_S_COMPLETE) {
331 	       AUTH_GSSAPI_DISPLAY_STATUS(("in response from server",
332 					   call_res.gss_major,
333 					   call_res.gss_minor));
334 	       goto cleanup;
335 	  }
336 
337 	  PRINTF(("gssapi_create: GSSAPI_INIT (%d) succeeded\n", init_func));
338 	  init_func = AUTH_GSSAPI_CONTINUE_INIT;
339 
340 	  /* check for client_handle */
341 	  if (AUTH_PRIVATE(auth)->client_handle.length == 0) {
342 	       if (call_res.client_handle.length == 0) {
343 		    PRINTF(("gssapi_create: expected client_handle\n"));
344 		    goto cleanup;
345 	       } else {
346 		    PRINTF(("gssapi_create: got client_handle %d\n",
347 			    *((uint32_t *)call_res.client_handle.value)));
348 
349 		    GSS_DUP_BUFFER(AUTH_PRIVATE(auth)->client_handle,
350 				   call_res.client_handle);
351 
352 		    /* auth_msg is TRUE; there may be more tokens */
353 		    marshall_new_creds(auth, TRUE,
354 				       &AUTH_PRIVATE(auth)->client_handle);
355 	       }
356 	  } else if (!GSS_BUFFERS_EQUAL(AUTH_PRIVATE(auth)->client_handle,
357 					call_res.client_handle)) {
358 	       PRINTF(("gssapi_create: got different client_handle\n"));
359 	       goto cleanup;
360 	  }
361 
362 	  /* check for token */
363 	  if (call_res.token.length==0 && *gssstat==GSS_S_CONTINUE_NEEDED) {
364 	       PRINTF(("gssapi_create: expected token\n"));
365 	       goto cleanup;
366 	  } else if (call_res.token.length != 0) {
367 	       if (*gssstat == GSS_S_COMPLETE) {
368 		    PRINTF(("gssapi_create: got unexpected token\n"));
369 		    goto cleanup;
370 	       } else {
371 		    /* assumes call_res is safe until init_sec_context */
372 		    input_token = &call_res.token;
373 		    PRINTF(("gssapi_create: got new token\n"));
374 	       }
375 	  }
376      }
377 
378      /* check for isn */
379      if (*gssstat == GSS_S_COMPLETE) {
380 	  if (call_res.signed_isn.length == 0) {
381 	       PRINTF(("gssapi_created: expected signed isn\n"));
382 	       goto cleanup;
383 	  } else {
384 	       PRINTF(("gssapi_create: processing signed isn\n"));
385 
386 	       /* don't check conf (integ only) or qop (accept default) */
387 	       *gssstat = gss_unseal(minor_stat,
388 				     AUTH_PRIVATE(auth)->context,
389 				     &call_res.signed_isn,
390 				     &isn_buf, NULL, NULL);
391 
392 	       if (*gssstat != GSS_S_COMPLETE) {
393 		    AUTH_GSSAPI_DISPLAY_STATUS(("unsealing isn",
394 						*gssstat, *minor_stat));
395 		    goto cleanup;
396 	       } else if (isn_buf.length != sizeof(uint32_t)) {
397 		    PRINTF(("gssapi_create: gss_unseal gave %d bytes\n",
398 			    (int) isn_buf.length));
399 		    goto cleanup;
400 	       }
401 
402 	       AUTH_PRIVATE(auth)->seq_num = (uint32_t)
403 		    ntohl(*((uint32_t*)isn_buf.value));
404 	       *gssstat = gss_release_buffer(minor_stat, &isn_buf);
405 	       if (*gssstat != GSS_S_COMPLETE) {
406 		    AUTH_GSSAPI_DISPLAY_STATUS(("releasing unsealed isn",
407 						*gssstat, *minor_stat));
408 		    goto cleanup;
409 	       }
410 
411 	       PRINTF(("gssapi_create: isn is %d\n",
412 		       AUTH_PRIVATE(auth)->seq_num));
413 	  }
414      } else if (call_res.signed_isn.length != 0) {
415 	  PRINTF(("gssapi_create: got signed isn, can't check yet\n"));
416      }
417 
418      /* results were okay.. continue if necessary */
419      if (*gssstat == GSS_S_CONTINUE_NEEDED) {
420 	  PRINTF(("gssapi_create: not done, continuing\n"));
421 	  goto next_token;
422      }
423 
424      /*
425       * Done!  Context is established, we have client_handle and isn.
426       */
427      AUTH_PRIVATE(auth)->established = TRUE;
428 
429      marshall_new_creds(auth, FALSE,
430 			&AUTH_PRIVATE(auth)->client_handle);
431 
432      PRINTF(("gssapi_create: done. client_handle %#x, isn %d\n\n",
433 	     *((uint32_t *)AUTH_PRIVATE(auth)->client_handle.value),
434 	     AUTH_PRIVATE(auth)->seq_num));
435 
436      /* don't assume the caller will want to change clnt->cl_auth */
437      clnt->cl_auth = save_auth;
438 
439      xdr_free(xdr_authgssapi_init_res, &call_res);
440      return auth;
441 
442      /******************************************************************/
443 
444 cleanup:
445      PRINTF(("gssapi_create: bailing\n\n"));
446 
447      if (auth) {
448 	 if (AUTH_PRIVATE(auth))
449 	     auth_gssapi_destroy(auth);
450 	 else
451 	     free(auth);
452 	 auth = NULL;
453      }
454 
455      /* don't assume the caller will want to change clnt->cl_auth */
456      clnt->cl_auth = save_auth;
457 
458      if (rpc_createerr.cf_stat == 0)
459 	  rpc_createerr.cf_stat = RPC_AUTHERROR;
460 
461      xdr_free(xdr_authgssapi_init_res, &call_res);
462      return auth;
463 }
464 
465 /*
466  * Function: marshall_new_creds
467  *
468  * Purpose: (pre-)serialize auth_msg and client_handle fields of
469  * auth_gssapi_creds into auth->cred_buf
470  *
471  * Arguments:
472  *
473  * 	auth		(r/w) the AUTH structure to modify
474  * 	auth_msg	(r) the auth_msg field to serialize
475  * 	client_handle	(r) the client_handle field to serialize, or
476  * 			NULL
477  *
478  * Returns: TRUE if successful, FALSE if not
479  *
480  * Requires: auth must point to a valid GSS-API auth structure, auth_msg
481  * must be TRUE or FALSE, client_handle must be a gss_buffer_t with a valid
482  * value and length field or NULL.
483  *
484  * Effects: auth->ah_cred is set to the serialized auth_gssapi_creds
485  * version 2 structure (stored in the cred_buf field of private data)
486  * containing version, auth_msg and client_handle.
487  * auth->ah_cred.oa_flavor is set to AUTH_GSSAPI.  If cliend_handle is
488  * NULL, it is treated as if it had a length of 0 and a value of NULL.
489  *
490  * Modifies: auth
491  */
marshall_new_creds(AUTH * auth,bool_t auth_msg,gss_buffer_t client_handle)492 static bool_t marshall_new_creds(
493      AUTH *auth,
494      bool_t auth_msg,
495      gss_buffer_t client_handle)
496 {
497      auth_gssapi_creds creds;
498      XDR xdrs;
499 
500      PRINTF(("marshall_new_creds: starting\n"));
501 
502      creds.version = 2;
503 
504      creds.auth_msg = auth_msg;
505      if (client_handle)
506 	  GSS_COPY_BUFFER(creds.client_handle, *client_handle)
507      else {
508 	  creds.client_handle.length = 0;
509 	  creds.client_handle.value = NULL;
510      }
511 
512      xdrmem_create(&xdrs, (caddr_t) AUTH_PRIVATE(auth)->cred_buf,
513 		   MAX_AUTH_BYTES, XDR_ENCODE);
514      if (! xdr_authgssapi_creds(&xdrs, &creds)) {
515 	  PRINTF(("marshall_new_creds: failed encoding auth_gssapi_creds\n"));
516 	  XDR_DESTROY(&xdrs);
517 	  return FALSE;
518      }
519      AUTH_PRIVATE(auth)->cred_len = xdr_getpos(&xdrs);
520      XDR_DESTROY(&xdrs);
521 
522      PRINTF(("marshall_new_creds: auth_gssapi_creds is %d bytes\n",
523 	     AUTH_PRIVATE(auth)->cred_len));
524 
525      auth->ah_cred.oa_flavor = AUTH_GSSAPI;
526      auth->ah_cred.oa_base = (char *) AUTH_PRIVATE(auth)->cred_buf;
527      auth->ah_cred.oa_length = AUTH_PRIVATE(auth)->cred_len;
528 
529      PRINTF(("marshall_new_creds: succeeding\n"));
530 
531      return TRUE;
532 }
533 
534 
535 /*
536  * Function: auth_gssapi_nextverf
537  *
538  * Purpose: None.
539  *
540  * Effects: None.  Never called.
541  */
auth_gssapi_nextverf(AUTH * auth)542 static void auth_gssapi_nextverf(AUTH *auth)
543 {
544 }
545 
546 /*
547  * Function: auth_gssapi_marhsall
548  *
549  * Purpose: Marshall RPC credentials and verifier onto xdr stream.
550  *
551  * Arguments:
552  *
553  * 	auth		(r/w) AUTH structure for client
554  * 	xdrs		(r/w) XDR stream to marshall to
555  *
556  * Returns: boolean indicating success/failure
557  *
558  * Effects:
559  *
560  * The pre-serialized credentials in cred_buf are serialized.  If the
561  * context is established, the sealed sequence number is serialized as
562  * the verifier.  If the context is not established, an empty verifier
563  * is serialized.  The sequence number is *not* incremented, because
564  * this function is called multiple times if retransmission is required.
565  *
566  * If this took all the header fields as arguments, it could sign
567  * them.
568  */
auth_gssapi_marshall(AUTH * auth,XDR * xdrs)569 static bool_t auth_gssapi_marshall(
570      AUTH *auth,
571      XDR *xdrs)
572 {
573      OM_uint32 minor_stat;
574      gss_buffer_desc out_buf;
575      uint32_t seq_num;
576 
577      if (AUTH_PRIVATE(auth)->established == TRUE)  {
578 	  PRINTF(("gssapi_marshall: starting\n"));
579 
580 	  seq_num = AUTH_PRIVATE(auth)->seq_num + 1;
581 
582 	  PRINTF(("gssapi_marshall: sending seq_num %d\n", seq_num));
583 
584 	  if (auth_gssapi_seal_seq(AUTH_PRIVATE(auth)->context, seq_num,
585 				   &out_buf) == FALSE) {
586 	       PRINTF(("gssapi_marhshall: seal failed\n"));
587 	  }
588 
589 	  auth->ah_verf.oa_base = out_buf.value;
590 	  auth->ah_verf.oa_length = out_buf.length;
591 
592 	  if (! xdr_opaque_auth(xdrs, &auth->ah_cred) ||
593 	      ! xdr_opaque_auth(xdrs, &auth->ah_verf)) {
594 	       (void) gss_release_buffer(&minor_stat, &out_buf);
595 	       return FALSE;
596 	  }
597 	  (void) gss_release_buffer(&minor_stat, &out_buf);
598      } else {
599 	  PRINTF(("gssapi_marshall: not established, sending null verf\n"));
600 
601 	  auth->ah_verf.oa_base = NULL;
602 	  auth->ah_verf.oa_length = 0;
603 
604 	  if (! xdr_opaque_auth(xdrs, &auth->ah_cred) ||
605 	      ! xdr_opaque_auth(xdrs, &auth->ah_verf)) {
606 	       return FALSE;
607 	  }
608      }
609 
610      return TRUE;
611 }
612 
613 /*
614  * Function: auth_gssapi_validate
615  *
616  * Purpose: Validate RPC response verifier from server.
617  *
618  * Effects: See design document, section XXX.
619  */
auth_gssapi_validate(AUTH * auth,struct opaque_auth * verf)620 static bool_t auth_gssapi_validate(
621      AUTH *auth,
622      struct opaque_auth *verf)
623 {
624      gss_buffer_desc in_buf;
625      uint32_t seq_num;
626 
627      if (AUTH_PRIVATE(auth)->established == FALSE) {
628 	  PRINTF(("gssapi_validate: not established, noop\n"));
629 	  return TRUE;
630      }
631 
632      PRINTF(("gssapi_validate: starting\n"));
633 
634      in_buf.length = verf->oa_length;
635      in_buf.value = verf->oa_base;
636      if (auth_gssapi_unseal_seq(AUTH_PRIVATE(auth)->context, &in_buf,
637 				&seq_num) == FALSE) {
638 	  PRINTF(("gssapi_validate: failed unsealing verifier\n"));
639 	  return FALSE;
640      }
641 
642      /* we sent seq_num+1, so we should get back seq_num+2 */
643      if (AUTH_PRIVATE(auth)->seq_num+2 != seq_num) {
644 	  PRINTF(("gssapi_validate: expecting seq_num %d, got %d (%#x)\n",
645 		  AUTH_PRIVATE(auth)->seq_num + 2, seq_num, seq_num));
646 	  return FALSE;
647      }
648      PRINTF(("gssapi_validate: seq_num %d okay\n", seq_num));
649 
650      /* +1 for successful transmission, +1 for successful validation */
651      AUTH_PRIVATE(auth)->seq_num += 2;
652 
653      PRINTF(("gssapi_validate: succeeding\n"));
654 
655      return TRUE;
656 }
657 
658 /*
659  * Function: auth_gssapi_refresh
660  *
661  * Purpose: Attempts to resyncrhonize the sequence number.
662  *
663  * Effects:
664  *
665  * When the server receives a properly authenticated RPC call, it
666  * increments the sequence number it is expecting from the client.
667  * But if the server's response is lost for any reason, the client
668  * can't know whether the server ever received it, assumes it didn't,
669  * and does *not* increment its sequence number.  Thus, the client's
670  * next call will fail with AUTH_REJECTEDCRED because the server will
671  * think it is a replay attack.
672  *
673  * When an AUTH_REJECTEDCRED error arrives, this function attempts to
674  * resyncrhonize by incrementing the client's sequence number and
675  * returning TRUE.  If any other error arrives, it returns FALSE.
676  */
auth_gssapi_refresh(AUTH * auth,struct rpc_msg * msg)677 static bool_t auth_gssapi_refresh(
678      AUTH *auth,
679      struct rpc_msg *msg)
680 {
681      if (msg->rm_reply.rp_rjct.rj_stat == AUTH_ERROR &&
682 	 msg->rm_reply.rp_rjct.rj_why == AUTH_REJECTEDVERF) {
683 	  PRINTF(("gssapi_refresh: rejected verifier, incrementing\n"));
684 	  AUTH_PRIVATE(auth)->seq_num++;
685 	  return TRUE;
686      } else {
687 	  PRINTF(("gssapi_refresh: failing\n"));
688 	  return FALSE;
689      }
690 }
691 
692 /*
693  * Function: auth_gssapi_destroy
694  *
695  * Purpose: Destroy a GSS-API authentication structure.
696  *
697  * Effects:  This function destroys the GSS-API authentication
698  * context, and sends a message to the server instructing it to
699  * invokte gss_process_token() and thereby destroy its corresponding
700  * context.  Since the client doesn't really care whether the server
701  * gets this message, no failures are reported.
702  */
auth_gssapi_destroy(AUTH * auth)703 static void auth_gssapi_destroy(AUTH *auth)
704 {
705      struct timeval timeout;
706      OM_uint32 gssstat, minor_stat;
707      gss_cred_id_t cred;
708      int callstat;
709 
710      if (AUTH_PRIVATE(auth)->client_handle.length == 0) {
711 	  PRINTF(("gssapi_destroy: no client_handle, not calling destroy\n"));
712 	  goto skip_call;
713      }
714 
715      PRINTF(("gssapi_destroy: marshalling new creds\n"));
716      if (!marshall_new_creds(auth, TRUE, &AUTH_PRIVATE(auth)->client_handle)) {
717 	  PRINTF(("gssapi_destroy: marshall_new_creds failed\n"));
718 	  goto skip_call;
719      }
720 
721      PRINTF(("gssapi_destroy: calling GSSAPI_DESTROY\n"));
722      timeout.tv_sec = 1;
723      timeout.tv_usec = 0;
724      callstat = clnt_call(AUTH_PRIVATE(auth)->clnt, AUTH_GSSAPI_DESTROY,
725 			  xdr_void, NULL, xdr_void, NULL, timeout);
726      if (callstat != RPC_SUCCESS)
727 	  clnt_sperror(AUTH_PRIVATE(auth)->clnt,
728 		       "gssapi_destroy: GSSAPI_DESTROY failed");
729 
730 skip_call:
731      PRINTF(("gssapi_destroy: deleting context\n"));
732      gssstat = gss_delete_sec_context(&minor_stat,
733 				      &AUTH_PRIVATE(auth)->context,
734 				      NULL);
735      if (gssstat != GSS_S_COMPLETE)
736 	  AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat,
737 				      minor_stat));
738      if (AUTH_PRIVATE(auth)->def_cred) {
739 	  cred = GSS_C_NO_CREDENTIAL;
740 	  gssstat = gss_release_cred(&minor_stat, &cred);
741 	  if (gssstat != GSS_S_COMPLETE)
742 	       AUTH_GSSAPI_DISPLAY_STATUS(("deleting default credential",
743 					   gssstat, minor_stat));
744      }
745 
746      free(AUTH_PRIVATE(auth)->client_handle.value);
747      free(auth->ah_private);
748      free(auth);
749      PRINTF(("gssapi_destroy: done\n"));
750 }
751 
752 /*
753  * Function: auth_gssapi_wrap
754  *
755  * Purpose: encrypt the serialized arguments from xdr_func applied to
756  * xdr_ptr and write the result to xdrs.
757  *
758  * Effects: See design doc, section XXX.
759  */
auth_gssapi_wrap(AUTH * auth,XDR * out_xdrs,bool_t (* xdr_func)(),caddr_t xdr_ptr)760 static bool_t auth_gssapi_wrap(
761      AUTH *auth,
762      XDR *out_xdrs,
763      bool_t (*xdr_func)(),
764      caddr_t xdr_ptr)
765 {
766      OM_uint32 gssstat, minor_stat;
767 
768      if (! AUTH_PRIVATE(auth)->established) {
769 	  PRINTF(("gssapi_wrap: context not established, noop\n"));
770 	  return (*xdr_func)(out_xdrs, xdr_ptr);
771      } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat,
772 					AUTH_PRIVATE(auth)->context,
773 					AUTH_PRIVATE(auth)->seq_num+1,
774 					out_xdrs, xdr_func, xdr_ptr)) {
775 	  if (gssstat != GSS_S_COMPLETE)
776 	       AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments",
777 					   gssstat, minor_stat));
778 	  return FALSE;
779      } else
780 	  return TRUE;
781 }
782 
783 /*
784  * Function: auth_gssapi_unwrap
785  *
786  * Purpose: read encrypted arguments from xdrs, decrypt, and
787  * deserialize with xdr_func into xdr_ptr.
788  *
789  * Effects: See design doc, section XXX.
790  */
auth_gssapi_unwrap(AUTH * auth,XDR * in_xdrs,bool_t (* xdr_func)(),caddr_t xdr_ptr)791 static bool_t auth_gssapi_unwrap(
792      AUTH *auth,
793      XDR *in_xdrs,
794      bool_t (*xdr_func)(),
795      caddr_t xdr_ptr)
796 {
797      OM_uint32 gssstat, minor_stat;
798 
799      if (! AUTH_PRIVATE(auth)->established) {
800 	  PRINTF(("gssapi_unwrap: context not established, noop\n"));
801 	  return (*xdr_func)(in_xdrs, xdr_ptr);
802      } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat,
803 					  AUTH_PRIVATE(auth)->context,
804 					  AUTH_PRIVATE(auth)->seq_num,
805 					  in_xdrs, xdr_func, xdr_ptr)) {
806 	  if (gssstat != GSS_S_COMPLETE)
807 	       AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments",
808 					   gssstat, minor_stat));
809 	  return FALSE;
810      } else
811 	  return TRUE;
812 }
813