Beispiel #1
0
/*
 * Function: auth_gssapi_destroy
 *
 * Purpose: Destroy a GSS-API authentication structure.
 *
 * Effects:  This function destroys the GSS-API authentication
 * context, and sends a message to the server instructing it to
 * invokte gss_process_token() and thereby destroy its corresponding
 * context.  Since the client doesn't really care whether the server
 * gets this message, no failures are reported.
 */
static void auth_gssapi_destroy(AUTH *auth)
{
     struct timeval timeout;
     OM_uint32 gssstat, minor_stat;
     gss_cred_id_t cred;
     int callstat;
     
     if (AUTH_PRIVATE(auth)->client_handle.length == 0) {
	  PRINTF(("gssapi_destroy: no client_handle, not calling destroy\n"));
	  goto skip_call;
     }
     
     PRINTF(("gssapi_destroy: marshalling new creds\n"));
     if (!marshall_new_creds(auth, TRUE, &AUTH_PRIVATE(auth)->client_handle)) {
	  PRINTF(("gssapi_destroy: marshall_new_creds failed\n"));
	  goto skip_call;
     }
     
     PRINTF(("gssapi_destroy: calling GSSAPI_DESTROY\n"));
     timeout.tv_sec = 1;
     timeout.tv_usec = 0;
     callstat = clnt_call(AUTH_PRIVATE(auth)->clnt, AUTH_GSSAPI_DESTROY,
			  xdr_void, NULL, xdr_void, NULL, timeout);
     if (callstat != RPC_SUCCESS)
	  clnt_sperror(AUTH_PRIVATE(auth)->clnt,
		       "gssapi_destroy: GSSAPI_DESTROY failed");
     
skip_call:
     PRINTF(("gssapi_destroy: deleting context\n"));
     gssstat = gss_delete_sec_context(&minor_stat,
				      &AUTH_PRIVATE(auth)->context,
				      NULL);
     if (gssstat != GSS_S_COMPLETE)
	  AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat,
				      minor_stat));
     if (AUTH_PRIVATE(auth)->def_cred) {
	  cred = GSS_C_NO_CREDENTIAL;
	  gssstat = gss_release_cred(&minor_stat, &cred);
	  if (gssstat != GSS_S_COMPLETE)
	       AUTH_GSSAPI_DISPLAY_STATUS(("deleting default credential",
					   gssstat, minor_stat));
     }
     
     if (AUTH_PRIVATE(auth)->client_handle.length != 0)
	  gss_release_buffer(&minor_stat,
			     &AUTH_PRIVATE(auth)->client_handle);
     
#if 0
     PRINTF(("gssapi_destroy: calling GSSAPI_EXIT\n"));
     AUTH_PRIVATE(auth)->established = FALSE;
     callstat = clnt_call(AUTH_PRIVATE(auth)->clnt, AUTH_GSSAPI_EXIT,
			  xdr_void, NULL, xdr_void, NULL, timeout);
#endif
     
     free(auth->ah_private);
     free(auth);
     PRINTF(("gssapi_destroy: done\n"));
}
Beispiel #2
0
/*
 * Function: auth_gssapi_create_default
 *
 * Purpose:  Create a GSS-API style authenticator, with default
 * options, and return the handle.
 *
 * Effects: See design document, section XXX.
 */
AUTH *auth_gssapi_create_default(CLIENT *clnt, char *service_name)
{
     AUTH *auth;
     OM_uint32 gssstat, minor_stat;
     gss_buffer_desc input_name;
     gss_name_t target_name;
     
     input_name.value = service_name;
     input_name.length = strlen(service_name) + 1;
     
     gssstat = gss_import_name(&minor_stat, &input_name, 
			       gss_nt_service_name, &target_name);
     if (gssstat != GSS_S_COMPLETE) {
	  AUTH_GSSAPI_DISPLAY_STATUS(("parsing name", gssstat,
				      minor_stat));
	  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
	  rpc_createerr.cf_error.re_errno = ENOMEM;
	  return NULL;
     }
     
     auth = auth_gssapi_create(clnt,
			       &gssstat,
			       &minor_stat,
			       GSS_C_NO_CREDENTIAL,
			       target_name,
			       GSS_C_NULL_OID,
			       GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
			       0,
			       NULL,
			       NULL,
			       NULL);
     
     gss_release_name(&minor_stat, &target_name);
     return auth;
}
Beispiel #3
0
bool_t auth_gssapi_unseal_seq(
     gss_ctx_id_t context,
     gss_buffer_t in_buf,
     uint32_t *seq_num)
{
     gss_buffer_desc out_buf;
     OM_uint32 gssstat, minor_stat;
     uint32_t nl_seq_num;

     gssstat = gss_unseal(&minor_stat, context, in_buf, &out_buf,
			  NULL, NULL);
     if (gssstat != GSS_S_COMPLETE) {
	  PRINTF(("gssapi_unseal_seq: failed\n"));
	  AUTH_GSSAPI_DISPLAY_STATUS(("unsealing sequence number",
				      gssstat, minor_stat));
	  return FALSE;
     } else if (out_buf.length != sizeof(uint32_t)) {
	  PRINTF(("gssapi_unseal_seq: unseal gave %d bytes\n",
		  (int) out_buf.length));
	  gss_release_buffer(&minor_stat, &out_buf);
	  return FALSE;
     }

     nl_seq_num = *((uint32_t *) out_buf.value);
     *seq_num = (uint32_t) ntohl(nl_seq_num);
     gss_release_buffer(&minor_stat, &out_buf);

     return TRUE;
}
/*
 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
 * and write the result to xdrs.
 */
static bool_t svc_auth_gssapi_wrap(
     SVCAUTH *auth,
     XDR *out_xdrs,
     bool_t (*xdr_func)(),
     caddr_t xdr_ptr)
{
     OM_uint32 gssstat, minor_stat;

     if (! SVCAUTH_PRIVATE(auth)->established) {
	  PRINTF(("svc_gssapi_wrap: not established, noop\n"));
	  return (*xdr_func)(out_xdrs, xdr_ptr);
     } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat,
					SVCAUTH_PRIVATE(auth)->context,
					SVCAUTH_PRIVATE(auth)->seq_num,
					out_xdrs, xdr_func, xdr_ptr)) {
	  if (gssstat != GSS_S_COMPLETE)
	       AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments",
					   gssstat, minor_stat));
	  return FALSE;
     } else
	  return TRUE;
}
static bool_t svc_auth_gssapi_unwrap(
     SVCAUTH *auth,
     XDR *in_xdrs,
     bool_t (*xdr_func)(),
     caddr_t xdr_ptr)
{
     svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth);
     OM_uint32 gssstat, minor_stat;

     if (! client_data->established) {
	  PRINTF(("svc_gssapi_unwrap: not established, noop\n"));
	  return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *)(void *) xdr_ptr);
     } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat,
					  client_data->context,
					  client_data->seq_num-1,
					  in_xdrs, xdr_func, xdr_ptr)) {
	  if (gssstat != GSS_S_COMPLETE)
	       AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments",
					   gssstat, minor_stat));
	  return FALSE;
     } else
	  return TRUE;
}
Beispiel #6
0
bool_t auth_gssapi_seal_seq(
     gss_ctx_id_t context,
     uint32_t seq_num,
     gss_buffer_t out_buf)
{
     gss_buffer_desc in_buf;
     OM_uint32 gssstat, minor_stat;
     uint32_t nl_seq_num;

     nl_seq_num = htonl(seq_num);

     in_buf.length = sizeof(uint32_t);
     in_buf.value = (char *) &nl_seq_num;
     gssstat = gss_seal(&minor_stat, context, 0, GSS_C_QOP_DEFAULT,
			&in_buf, NULL, out_buf);
     if (gssstat != GSS_S_COMPLETE) {
	  PRINTF(("gssapi_seal_seq: failed\n"));
	  AUTH_GSSAPI_DISPLAY_STATUS(("sealing sequence number",
				      gssstat, minor_stat));
	  return FALSE;
     }
     return TRUE;
}
/*
 * Function: svcauth_gssapi_set_names
 *
 * Purpose: Sets the list of service names for which incoming
 * authentication requests should be honored.
 *
 * See functional specifications.
 */
bool_t svcauth_gssapi_set_names(
     auth_gssapi_name *names,
     int num)
{
     OM_uint32 gssstat, minor_stat;
     gss_buffer_desc in_buf;
     int i;
     
     if (num == 0)
	  for (; names[num].name != NULL; num++)
	       ;

     server_creds_list = NULL;
     server_name_list = NULL;
     
     server_creds_list = (gss_cred_id_t *) malloc(num*sizeof(gss_cred_id_t));
     if (server_creds_list == NULL)
	  goto fail;
     server_name_list = (gss_name_t *) malloc(num*sizeof(gss_name_t));
     if (server_name_list == NULL)
	  goto fail;
     
     for (i = 0; i < num; i++) {
	  server_name_list[i] = 0;
	  server_creds_list[i] = 0;
     }

     server_creds_count = num;
     
     for (i = 0; i < num; i++) {
	  in_buf.value = names[i].name;
	  in_buf.length = strlen(in_buf.value) + 1;

	  PRINTF(("svcauth_gssapi_set_names: importing %s\n", names[i].name));

	  gssstat = gss_import_name(&minor_stat, &in_buf, names[i].type,
				    &server_name_list[i]); 
     
	  if (gssstat != GSS_S_COMPLETE) {
	       AUTH_GSSAPI_DISPLAY_STATUS(("importing name", gssstat,
					   minor_stat));
	       goto fail;
	  }

	  gssstat = gss_acquire_cred(&minor_stat, server_name_list[i], 0,
				     GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
				     &server_creds_list[i], NULL, NULL);
	  if (gssstat != GSS_S_COMPLETE) {
	       AUTH_GSSAPI_DISPLAY_STATUS(("acquiring credentials",
					   gssstat, minor_stat));
	       goto fail;
	  }
     }

     return TRUE;

fail:
     svcauth_gssapi_unset_names();

     return FALSE;
}
/*
 * Function: destroy_client
 *
 * Purpose: destroys a client entry and removes it from the database
 *
 * Arguments:
 *
 *	client_data	(r) the client to be destroyed
 *
 * Effects:
 *
 * client_data->context is deleted with gss_delete_sec_context.
 * client_data's entry in the database is destroyed.  client_data is
 * freed.
 */
static void destroy_client(svc_auth_gssapi_data *client_data)
{
     OM_uint32 gssstat, minor_stat;
     gss_buffer_desc out_buf;
     client_list *c, *c2;

     PRINTF(("destroy_client: destroying client_data\n"));
     L_PRINTF(2, ("destroy_client: client_data = %p\n", (void *) client_data));

#ifdef DEBUG_GSSAPI
     if (svc_debug_gssapi >= 3)
	  dump_db("before frees");
#endif

     /* destroy client struct even if error occurs */

     gssstat = gss_delete_sec_context(&minor_stat, &client_data->context,
				      &out_buf);
     if (gssstat != GSS_S_COMPLETE)
	  AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat,
				      minor_stat));
     
     gss_release_buffer(&minor_stat, &out_buf);
     gss_release_name(&minor_stat, &client_data->client_name);
     if (client_data->prev_verf.length != 0)
	  gss_release_buffer(&minor_stat, &client_data->prev_verf);

     if (clients == NULL) {
	  PRINTF(("destroy_client: called on empty database\n"));
	  abort();
     } else if (clients->client == client_data) {
	  c = clients;
	  clients = clients->next;
	  free(c);
     } else {
	  c2 = clients;
	  c = clients->next;
	  while (c) {
	       if (c->client == client_data) {
		    c2->next = c->next;
		    free(c);
		    goto done;
	       } else {
		    c2 = c;
		    c = c->next;
	       }
	  }
	  PRINTF(("destroy_client: client_handle delete failed\n"));
	  abort();
     }
     
done:
     
     L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key));
     
     free(client_data);
     
#if 0 /*ifdef PURIFY*/
     purify_watch_n(client_data, sizeof(*client_data), "rw");
#endif
}
enum auth_stat gssrpc__svcauth_gssapi(
     register struct svc_req *rqst,
     register struct rpc_msg *msg,
     bool_t *no_dispatch)
{
     XDR xdrs;
     auth_gssapi_creds creds;
     auth_gssapi_init_arg call_arg;
     auth_gssapi_init_res call_res;
     gss_buffer_desc output_token, in_buf, out_buf;
     gss_cred_id_t server_creds;
     struct gss_channel_bindings_struct bindings, *bindp;
     OM_uint32 gssstat, minor_stat, time_rec;
     struct opaque_auth *cred, *verf;
     svc_auth_gssapi_data *client_data;
     int i;
     enum auth_stat ret;
     OM_uint32 ret_flags;
     uint32_t seq_num;

     PRINTF(("svcauth_gssapi: starting\n"));
     
     /* clean up expired entries */
     clean_client();

     /* use AUTH_NONE until there is a client_handle */
     rqst->rq_xprt->xp_auth = &svc_auth_none;
     
     memset((char *) &call_res, 0, sizeof(call_res));
     creds.client_handle.length = 0;
     creds.client_handle.value = NULL;
     
     cred = &msg->rm_call.cb_cred;
     verf = &msg->rm_call.cb_verf;
     
     if (cred->oa_length == 0) {
	  PRINTF(("svcauth_gssapi: empty creds, failing\n"));
	  LOG_MISCERR("empty client credentials");
	  ret = AUTH_BADCRED;
	  goto error;
     }

     PRINTF(("svcauth_gssapi: decoding credentials\n"));
     xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE); 
     memset((char *) &creds, 0, sizeof(creds));
     if (! xdr_authgssapi_creds(&xdrs, &creds)) {
	  PRINTF(("svcauth_gssapi: failed decoding creds\n"));
	  LOG_MISCERR("protocol error in client credentials");
	  xdr_free(xdr_authgssapi_creds, &creds);
	  XDR_DESTROY(&xdrs);
	  ret = AUTH_BADCRED;
	  goto error;
     }
     XDR_DESTROY(&xdrs);

     PRINTF(("svcauth_gssapi: got credentials, version %d, client_handle len %d\n",
	     creds.version, (int) creds.client_handle.length));

     if (creds.version != 2) {
 	  PRINTF(("svcauth_gssapi: bad credential version\n"));
 	  LOG_MISCERR("unsupported client credentials version");
 	  ret = AUTH_BADCRED;
 	  goto error;
     }

#ifdef DEBUG_GSSAPI
     if (svc_debug_gssapi) {
	  if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_EXIT) {
	       PRINTF(("svcauth_gssapi: GSSAPI_EXIT, cleaning up\n"));
	       svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
	       xdr_free(xdr_authgssapi_creds, &creds);
	       cleanup();
	       exit(0);
	  }
     }
#endif
	  
     /*
      * If this is an auth_msg and proc is GSSAPI_INIT, then create a
      * client handle for this client.  Otherwise, look up the
      * existing handle.
      */
     if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) {
	  if (creds.client_handle.length != 0) {
	       PRINTF(("svcauth_gssapi: non-empty handle on GSSAPI_INIT\n"));
	       LOG_MISCERR("protocol error in client handle");
	       ret = AUTH_FAILED;
	       goto error;
	  }
	       
	  PRINTF(("svcauth_gssapi: GSSAPI_INIT, creating client.\n"));
	  
	  client_data = create_client();
	  if (client_data == NULL) {
	       PRINTF(("svcauth_gssapi: create_client failed\n"));
	       LOG_MISCERR("internal error creating client record");
	       ret = AUTH_FAILED;
	       goto error;
	  }
     } else {
	  if (creds.client_handle.length == 0) {
	       PRINTF(("svcauth_gssapi: expected non-empty creds\n"));
	       LOG_MISCERR("protocol error in client credentials");
	       ret = AUTH_FAILED;
	       goto error;
	  }
	  
	  PRINTF(("svcauth_gssapi: incoming client_handle %d, len %d\n", 
		  *((uint32_t *) creds.client_handle.value),
		  (int) creds.client_handle.length));

	  client_data = get_client(&creds.client_handle);
	  if (client_data == NULL) {
	       PRINTF(("svcauth_gssapi: client_handle lookup failed\n"));
	       LOG_MISCERR("invalid client handle received");
	       ret = AUTH_BADCRED;
	       goto error;
	  }
	  PRINTF(("svcauth_gssapi: client_handle lookup succeeded\n"));
     }

     /* any response we send will use client_handle, so set it now */
     call_res.client_handle.length = sizeof(client_data->key);
     call_res.client_handle.value = (char *) &client_data->key;
     
     /* mark this call as using AUTH_GSSAPI via client_data's SVCAUTH */
     rqst->rq_xprt->xp_auth = &client_data->svcauth;

     if (client_data->established == FALSE) {
	  PRINTF(("svcauth_gssapi: context is not established\n"));

	  if (creds.auth_msg == FALSE) {
	       PRINTF(("svcauth_gssapi: expected auth_msg TRUE\n"));
	       LOG_MISCERR("protocol error on incomplete connection");
	       ret = AUTH_REJECTEDCRED;
	       goto error;
	  }

	  /*
	   * If the context is not established, then only GSSAPI_INIT
	   * and _CONTINUE requests are valid.
	   */
	  if (rqst->rq_proc != AUTH_GSSAPI_INIT && rqst->rq_proc !=
	      AUTH_GSSAPI_CONTINUE_INIT) {
	       PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
		       rqst->rq_proc));
	       LOG_MISCERR("protocol error on incomplete connection");
	       ret = AUTH_FAILED;
	       goto error;
	  }

	  /* call is for us, deserialize arguments */
	  memset(&call_arg, 0, sizeof(call_arg));
	  if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg,
			    &call_arg)) {
	       PRINTF(("svcauth_gssapi: cannot decode args\n"));
	       LOG_MISCERR("protocol error in procedure arguments");
	       ret = AUTH_BADCRED;
	       goto error;
	  }

	  /*
	   * Process the call arg version number.
	   * 
	   * Set the krb5_gss backwards-compatibility mode based on client
	   * version.  This controls whether the AP_REP message is
	   * encrypted with the session key (version 2+, correct) or the
	   * session subkey (version 1, incorrect).  This function can
	   * never fail, so we don't bother checking its return value.
	   */
	  switch (call_arg.version) {
	  case 1:
	  case 2:
	       LOG_MISCERR("Warning: Accepted old RPC protocol request");
	       call_res.version = 1;
	       break;
	  case 3:
	  case 4:
	       /* 3 and 4 are essentially the same, don't bother warning */
	       call_res.version = call_arg.version;
	       break;
	  default:
	       PRINTF(("svcauth_gssapi: bad GSSAPI_INIT version\n"));
	       LOG_MISCERR("unsupported GSSAPI_INIT version");
	       ret = AUTH_BADCRED;
	       goto error;
	  }

#ifdef GSS_BACKWARD_HACK
	  krb5_gss_set_backward_mode(&minor_stat, call_arg.version == 1);
#endif

	  if (call_arg.version >= 3) {
	       memset(&bindings, 0, sizeof(bindings));
	       bindings.application_data.length = 0;
	       bindings.initiator_addrtype = GSS_C_AF_INET;
	       bindings.initiator_address.length = 4;
	       bindings.initiator_address.value =
		    &svc_getcaller(rqst->rq_xprt)->sin_addr.s_addr;

	       if (rqst->rq_xprt->xp_laddrlen > 0) {
		    bindings.acceptor_addrtype = GSS_C_AF_INET;
		    bindings.acceptor_address.length = 4;
		    bindings.acceptor_address.value =
			 &rqst->rq_xprt->xp_laddr.sin_addr.s_addr;
	       } else {
		    LOG_MISCERR("cannot get local address");
		    ret = AUTH_FAILED;
		    goto error;
	       }


	       bindp = &bindings;
	  } else {
	       bindp = GSS_C_NO_CHANNEL_BINDINGS;
	  }

	  /*
	   * If the client's server_creds is already set, use it.
	   * Otherwise, try each credential in server_creds_list until
	   * one of them succeedes, then set the client server_creds
	   * to that.  If all fail, the client's server_creds isn't
	   * set (which is fine, because the client will be gc'ed
	   * anyway).
	   *
	   * If accept_sec_context returns something other than
	   * success and GSS_S_FAILURE, then assume different
	   * credentials won't help and stop looping.
	   * 
	   * Note that there are really two cases here: (1) the client
	   * has a server_creds already, and (2) it does not.  They
	   * are both written in the same loop so that there is only
	   * one textual call to gss_accept_sec_context; in fact, in
	   * case (1), the loop is executed exactly once.
	   */
	  for (i = 0; i < server_creds_count; i++) {
	       if (client_data->server_creds != NULL) {
		    PRINTF(("svcauth_gssapi: using's clients server_creds\n"));
		    server_creds = client_data->server_creds;
	       } else {
		    PRINTF(("svcauth_gssapi: trying creds %d\n", i));
		    server_creds = server_creds_list[i];
	       }
	       
	       /* Free previous output_token from loop */
	       if(i != 0) gss_release_buffer(&minor_stat, &output_token);

	       call_res.gss_major =
		    gss_accept_sec_context(&call_res.gss_minor,
					   &client_data->context,
					   server_creds,
					   &call_arg.token,
					   bindp,
					   &client_data->client_name,
					   NULL,
					   &output_token,
					   &ret_flags,
					   &time_rec,
					   NULL);

	       if (server_creds == client_data->server_creds)
		    break;

	       PRINTF(("accept_sec_context returned 0x%x 0x%x wrong-princ=%#x\n",
		       call_res.gss_major, call_res.gss_minor, (int) KRB5KRB_AP_WRONG_PRINC));
	       if (call_res.gss_major == GSS_S_COMPLETE ||
		   call_res.gss_major == GSS_S_CONTINUE_NEEDED) {
		    /* server_creds was right, set it! */
		    PRINTF(("svcauth_gssapi: creds are correct, storing\n"));
		    client_data->server_creds = server_creds;
		    client_data->server_name = server_name_list[i];
		    break;
	       } else if (call_res.gss_major != GSS_S_FAILURE
#ifdef GSSAPI_KRB5
			  /*
			   * hard-coded because there is no other way
			   * to prevent all GSS_S_FAILURES from
			   * returning a "wrong principal in request"
			   * error
			   */
			  || ((krb5_error_code) call_res.gss_minor !=
			      (krb5_error_code) KRB5KRB_AP_WRONG_PRINC)
#endif
			  ) {
		    break;
	       }
	  }
	  
	  gssstat = call_res.gss_major;
	  minor_stat = call_res.gss_minor;

	  /* done with call args */
	  xdr_free(xdr_authgssapi_init_arg, &call_arg);

	  PRINTF(("svcauth_gssapi: accept_sec_context returned %#x %#x\n",
		  call_res.gss_major, call_res.gss_minor));
	  if (call_res.gss_major != GSS_S_COMPLETE &&
	      call_res.gss_major != GSS_S_CONTINUE_NEEDED) {
	       AUTH_GSSAPI_DISPLAY_STATUS(("accepting context",
					   call_res.gss_major,
					   call_res.gss_minor));

	       if (log_badauth != NULL)
		    (*log_badauth)(call_res.gss_major,
				   call_res.gss_minor,
				   &rqst->rq_xprt->xp_raddr,
				   log_badauth_data);
	       
	       gss_release_buffer(&minor_stat, &output_token);
	       svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res,
			     (caddr_t) &call_res);
	       *no_dispatch = TRUE;
	       ret = AUTH_OK;
	       goto error;
	  }
	      
	  if (output_token.length != 0) {
	       PRINTF(("svcauth_gssapi: got new output token\n"));
	       GSS_COPY_BUFFER(call_res.token, output_token);
	  }

	  if (gssstat == GSS_S_COMPLETE) {
	       client_data->seq_num = rand();
	       client_expire(client_data,
			     (time_rec == GSS_C_INDEFINITE ?
			      INDEF_EXPIRE : time_rec) + time(0));

	       PRINTF(("svcauth_gssapi: context established, isn %d\n", 
		       client_data->seq_num));

	       if (auth_gssapi_seal_seq(client_data->context,
					client_data->seq_num,
					&call_res.signed_isn) ==
		   FALSE) {
		    ret = AUTH_FAILED;
		    LOG_MISCERR("internal error sealing sequence number");
		    gss_release_buffer(&minor_stat, &output_token);
		    goto error;
	       }
	  }

	  PRINTF(("svcauth_gssapi: sending reply\n"));
	  svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res,
			(caddr_t) &call_res);
	  *no_dispatch = TRUE;

	  /*
	   * If appropriate, set established to TRUE *after* sending
	   * response (otherwise, the client will receive the final
	   * token encrypted)
	   */
	  if (gssstat == GSS_S_COMPLETE) {
	       gss_release_buffer(&minor_stat, &call_res.signed_isn);
	       client_data->established = TRUE;
	  }
	  gss_release_buffer(&minor_stat, &output_token);
     } else {
	  PRINTF(("svcauth_gssapi: context is established\n"));

	  /* check the verifier */
	  PRINTF(("svcauth_gssapi: checking verifier, len %d\n",
		  verf->oa_length));
	  
	  in_buf.length = verf->oa_length;
	  in_buf.value = verf->oa_base;
	  
	  if (auth_gssapi_unseal_seq(client_data->context, &in_buf,
				     &seq_num) == FALSE) {
	       ret = AUTH_BADVERF;
	       LOG_MISCERR("internal error unsealing sequence number");
	       goto error;
	  }
	  
	  if (seq_num != client_data->seq_num + 1) {
	       PRINTF(("svcauth_gssapi: expected isn %d, got %d\n",
		       client_data->seq_num + 1, seq_num));
	       if (log_badverf != NULL)
		    (*log_badverf)(client_data->client_name,
				   client_data->server_name,
				   rqst, msg, log_badverf_data);
	       
	       ret = AUTH_REJECTEDVERF;
	       goto error;
	  }
	  client_data->seq_num++;
	  
	  PRINTF(("svcauth_gssapi: seq_num %d okay\n", seq_num));

	  /* free previous response verifier, if any */
	  if (client_data->prev_verf.length != 0) {
	       gss_release_buffer(&minor_stat, &client_data->prev_verf);
	       client_data->prev_verf.length = 0;
	  }
	  
	  /* prepare response verifier */
	  seq_num = client_data->seq_num + 1;
	  if (auth_gssapi_seal_seq(client_data->context, seq_num,
				   &out_buf) == FALSE) {
	       ret = AUTH_FAILED;
	       LOG_MISCERR("internal error sealing sequence number");
	       goto error;
	  }
	  
	  client_data->seq_num++;
	  
	  PRINTF(("svcauth_gssapi; response seq_num %d\n", seq_num));
	  
	  rqst->rq_xprt->xp_verf.oa_flavor = AUTH_GSSAPI;
	  rqst->rq_xprt->xp_verf.oa_base = out_buf.value; 
	  rqst->rq_xprt->xp_verf.oa_length = out_buf.length;

	  /* save verifier so it can be freed next time */
	  client_data->prev_verf.value = out_buf.value; 
	  client_data->prev_verf.length = out_buf.length;

	  /*
	   * Message is authentic.  If auth_msg if true, process the
	   * call; otherwise, return AUTH_OK so it will be dispatched
	   * to the application server.
	   */

	  if (creds.auth_msg == TRUE) {
	       /*
		* If process_token fails, then the token probably came
		* from an attacker.  No response (error or otherwise)
		* should be returned to the client, since it won't be
		* accepting one.
		*/

	       switch (rqst->rq_proc) {
	       case AUTH_GSSAPI_MSG:
		    PRINTF(("svcauth_gssapi: GSSAPI_MSG, getting args\n"));
		    memset(&call_arg, 0, sizeof(call_arg));
		    if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg,
				      &call_arg)) {
			 PRINTF(("svcauth_gssapi: cannot decode args\n"));
			 LOG_MISCERR("protocol error in call arguments");
			 xdr_free(xdr_authgssapi_init_arg, &call_arg);
			 ret = AUTH_BADCRED;
			 goto error;
		    }

		    PRINTF(("svcauth_gssapi: processing token\n"));
		    gssstat = gss_process_context_token(&minor_stat,
							client_data->context,
							&call_arg.token);

		    /* done with call args */
		    xdr_free(xdr_authgssapi_init_arg, &call_arg);
		    
		    if (gssstat != GSS_S_COMPLETE) {
			 AUTH_GSSAPI_DISPLAY_STATUS(("processing token",
						     gssstat, minor_stat));
			 ret = AUTH_FAILED;
			 goto error;
		    }

		    svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
		    *no_dispatch = TRUE;
		    break;

	       case AUTH_GSSAPI_DESTROY:
		    PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n"));
		    
		    PRINTF(("svcauth_gssapi: sending reply\n"));
		    svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
		    *no_dispatch = TRUE;

		    destroy_client(client_data);
		    rqst->rq_xprt->xp_auth = NULL;
		    break;

	       default:
		    PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
			    rqst->rq_proc));
		    LOG_MISCERR("invalid call procedure number");
		    ret = AUTH_FAILED;
		    goto error;
	       }
	  } else {
	       /* set credentials for app server; comment in svc.c */
	       /* seems to imply this is incorrect, but I don't see */
	       /* any problem with it... */
	       rqst->rq_clntcred = (char *)client_data->client_name;
	       rqst->rq_svccred = (char *)client_data->context;
	  }
     }

     if (creds.client_handle.length != 0) {
	  PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
		  (int) creds.client_handle.length));
	  xdr_free(xdr_authgssapi_creds, &creds);
     }
     
     PRINTF(("\n"));
     return AUTH_OK;

error:
     if (creds.client_handle.length != 0) {
	  PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
		  (int) creds.client_handle.length));
	  xdr_free(xdr_authgssapi_creds, &creds);
     }
     
     PRINTF(("\n"));
     return ret;
}
Beispiel #10
0
/*
 * Function: auth_gssapi_create
 *
 * Purpose: Create a GSS-API style authenticator, with all the
 * options, and return the handle.
 *
 * Effects: See design document, section XXX.
 */
AUTH *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)
{
     AUTH *auth, *save_auth;
     struct auth_gssapi_data *pdata;
     struct gss_channel_bindings_struct bindings, *bindp;
     struct sockaddr_in laddr, raddr;
     enum clnt_stat callstat;
     struct timeval timeout;
     int bindings_failed;
     rpcproc_t init_func;
     
     auth_gssapi_init_arg call_arg;
     auth_gssapi_init_res call_res;
     gss_buffer_desc *input_token, isn_buf;
     
     memset(&rpc_createerr, 0, sizeof(rpc_createerr));
     
     /* this timeout is only used if clnt_control(clnt, CLSET_TIMEOUT) */
     /* has not already been called.. therefore, we can just pick */
     /* something reasonable-sounding.. */
     timeout.tv_sec = 30;
     timeout.tv_usec = 0;
     
     auth = NULL;
     pdata = NULL;
     
     /* don't assume the caller will want to change clnt->cl_auth */
     save_auth = clnt->cl_auth;

     auth = (AUTH *) malloc(sizeof(*auth));
     pdata = (struct auth_gssapi_data *) malloc(sizeof(*pdata));
     if (auth == NULL || pdata == NULL) {
	  /* They needn't both have failed; clean up.  */
	  free(auth);
	  free(pdata);
	  auth = NULL;
	  pdata = NULL;
	  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
	  rpc_createerr.cf_error.re_errno = ENOMEM;
	  goto cleanup;
     }
     memset((char *) auth, 0, sizeof(*auth));
     memset((char *) pdata, 0, sizeof(*pdata));
     
     auth->ah_ops = &auth_gssapi_ops;
     auth->ah_private = (caddr_t) pdata;
     
     /* initial creds are auth_msg TRUE and no handle */
     marshall_new_creds(auth, TRUE, NULL);
     
     /* initial verifier is empty */
     auth->ah_verf.oa_flavor = AUTH_GSSAPI;
     auth->ah_verf.oa_base = NULL;
     auth->ah_verf.oa_length = 0;
     
     AUTH_PRIVATE(auth)->established = FALSE;
     AUTH_PRIVATE(auth)->clnt = clnt;
     AUTH_PRIVATE(auth)->def_cred = (claimant_cred_handle ==
				     GSS_C_NO_CREDENTIAL);
     
     clnt->cl_auth = auth;

     /* start by trying latest version */
     call_arg.version = 4;
     bindings_failed = 0;

try_new_version:
     /* set state for initial call to init_sec_context */
     input_token = GSS_C_NO_BUFFER;
     AUTH_PRIVATE(auth)->context = GSS_C_NO_CONTEXT;
     init_func = AUTH_GSSAPI_INIT;

#ifdef GSSAPI_KRB5
     /*
      * OV servers up to version 3 used the old mech id.  Beta 7
      * servers used version 3 with the new mech id; however, the beta
      * 7 gss-api accept_sec_context accepts either mech id.  Thus, if
      * any server rejects version 4, we fall back to version 3 with
      * the old mech id; for the OV server it will be right, and for
      * the beta 7 server it will be accepted.  Not ideal, but it
      * works.
      */
     if (call_arg.version < 4 && (mech_type == gss_mech_krb5 ||
				  mech_type == GSS_C_NULL_OID))
	  mech_type = (gss_OID) gss_mech_krb5_old;
#endif

     if (!bindings_failed && call_arg.version >= 3) {
	  if (clnt_control(clnt, CLGET_LOCAL_ADDR, &laddr) == FALSE) {
	       PRINTF(("gssapi_create: CLGET_LOCAL_ADDR failed"));
	       goto cleanup;
	  }
	  if (clnt_control(clnt, CLGET_SERVER_ADDR, &raddr) == FALSE) {
	       PRINTF(("gssapi_create: CLGET_SERVER_ADDR failed"));
	       goto cleanup;
	  }

	  memset(&bindings, 0, sizeof(bindings));
	  bindings.application_data.length = 0;
	  bindings.initiator_addrtype = GSS_C_AF_INET;
	  bindings.initiator_address.length = 4;
	  bindings.initiator_address.value = &laddr.sin_addr.s_addr;
	  
	  bindings.acceptor_addrtype = GSS_C_AF_INET;
	  bindings.acceptor_address.length = 4;
	  bindings.acceptor_address.value = &raddr.sin_addr.s_addr;
	  bindp = &bindings;
     } else {
	  bindp = NULL;
     }
     
     memset((char *) &call_res, 0, sizeof(call_res));
     
next_token:
     *gssstat = gss_init_sec_context(minor_stat,
				     claimant_cred_handle,
				     &AUTH_PRIVATE(auth)->context,
				     target_name,
				     mech_type,
				     req_flags,
				     time_req,
				     bindp,
				     input_token,
				     actual_mech_type,
				     &call_arg.token,
				     ret_flags,
				     time_rec);
     
     if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
	  AUTH_GSSAPI_DISPLAY_STATUS(("initializing context", *gssstat,
				      *minor_stat));
	  goto cleanup;
     }
     
     /* if we got a token, pass it on */
     if (call_arg.token.length != 0) {
	  
	  /*
	   * sanity check: if we received a signed isn in the last
	   * response then there *cannot* be another token to send
	   */
	  if (call_res.signed_isn.length != 0) {
	       PRINTF(("gssapi_create: unexpected token from init_sec\n"));
	       goto cleanup;
	  }
	  
	  PRINTF(("gssapi_create: calling GSSAPI_INIT (%d)\n", init_func));
	  
	  memset((char *) &call_res, 0, sizeof(call_res));
	  callstat = clnt_call(clnt, init_func,
			       xdr_authgssapi_init_arg, &call_arg,
			       xdr_authgssapi_init_res, &call_res,
			       timeout);
	  gss_release_buffer(minor_stat, &call_arg.token);
	  
	  if (callstat != RPC_SUCCESS) {
	       struct rpc_err err;

	       clnt_geterr(clnt, &err);
	       if (callstat == RPC_AUTHERROR &&
		   (err.re_why == AUTH_BADCRED || err.re_why == AUTH_FAILED)
		   && call_arg.version >= 1) {
		    L_PRINTF(1,
			     ("call_arg protocol version %d rejected, trying %d.\n",
			    call_arg.version, call_arg.version-1));
		    call_arg.version--;
		    goto try_new_version;
	       } else {
		    PRINTF(("gssapi_create: GSSAPI_INIT (%d) failed, stat %d\n",
			    init_func, callstat));
	       }
	       
	       goto cleanup;
	  } else if (call_res.version != call_arg.version &&
		     !(call_arg.version == 2 && call_res.version == 1)) {
	       /*
		* The Secure 1.1 servers always respond with version
		* 1.  Thus, if we just tried a version >=3, fall all
		* the way back to version 1 since that is all they
		* understand
		*/
	       if (call_arg.version > 2 && call_res.version == 1) {
		    L_PRINTF(1,
			     ("Talking to Secure 1.1 server, using version 1.\n"));
		    call_arg.version = 1;
		    goto try_new_version;
	       }

	       PRINTF(("gssapi_create: invalid call_res vers %d\n",
		       call_res.version));
	       goto cleanup;
	  } else if (call_res.gss_major != GSS_S_COMPLETE) {
	       AUTH_GSSAPI_DISPLAY_STATUS(("in response from server",
					   call_res.gss_major,
					   call_res.gss_minor));
	       goto cleanup;
	  }
	  
	  PRINTF(("gssapi_create: GSSAPI_INIT (%d) succeeded\n", init_func));
	  init_func = AUTH_GSSAPI_CONTINUE_INIT;
	  
	  /* check for client_handle */
	  if (AUTH_PRIVATE(auth)->client_handle.length == 0) {
	       if (call_res.client_handle.length == 0) {
		    PRINTF(("gssapi_create: expected client_handle\n"));
		    goto cleanup;
	       } else {
		    PRINTF(("gssapi_create: got client_handle %d\n",
			    *((uint32_t *)call_res.client_handle.value)));
		    
		    GSS_DUP_BUFFER(AUTH_PRIVATE(auth)->client_handle,
				   call_res.client_handle);
		    
		    /* auth_msg is TRUE; there may be more tokens */
		    marshall_new_creds(auth, TRUE,
				       &AUTH_PRIVATE(auth)->client_handle); 
	       }
	  } else if (!GSS_BUFFERS_EQUAL(AUTH_PRIVATE(auth)->client_handle,
					call_res.client_handle)) {
	       PRINTF(("gssapi_create: got different client_handle\n"));
	       goto cleanup;
	  }
	  
	  /* check for token */
	  if (call_res.token.length==0 && *gssstat==GSS_S_CONTINUE_NEEDED) {
	       PRINTF(("gssapi_create: expected token\n"));
	       goto cleanup;
	  } else if (call_res.token.length != 0) {
	       if (*gssstat == GSS_S_COMPLETE) {
		    PRINTF(("gssapi_create: got unexpected token\n"));
		    goto cleanup;
	       } else {
		    /* assumes call_res is safe until init_sec_context */
		    input_token = &call_res.token;
		    PRINTF(("gssapi_create: got new token\n"));
	       }
	  }
     }
     
     /* check for isn */
     if (*gssstat == GSS_S_COMPLETE) {
	  if (call_res.signed_isn.length == 0) {
	       PRINTF(("gssapi_created: expected signed isn\n"));
	       goto cleanup;
	  } else {
	       PRINTF(("gssapi_create: processing signed isn\n"));
	       
	       /* don't check conf (integ only) or qop (accpet default) */
	       *gssstat = gss_unseal(minor_stat,
				     AUTH_PRIVATE(auth)->context,
				     &call_res.signed_isn,
				     &isn_buf, NULL, NULL);
	       
	       if (*gssstat != GSS_S_COMPLETE) {
		    AUTH_GSSAPI_DISPLAY_STATUS(("unsealing isn",
						*gssstat, *minor_stat)); 
		    goto cleanup;
	       } else if (isn_buf.length != sizeof(uint32_t)) {
		    PRINTF(("gssapi_create: gss_unseal gave %d bytes\n",
			    (int) isn_buf.length));
		    goto cleanup;
	       }
	       
	       AUTH_PRIVATE(auth)->seq_num = (uint32_t)
		    ntohl(*((uint32_t*)isn_buf.value)); 
	       *gssstat = gss_release_buffer(minor_stat, &isn_buf);
	       if (*gssstat != GSS_S_COMPLETE) {
		    AUTH_GSSAPI_DISPLAY_STATUS(("releasing unsealed isn",
						*gssstat, *minor_stat));
		    goto cleanup;
	       }
	       
	       PRINTF(("gssapi_create: isn is %d\n",
		       AUTH_PRIVATE(auth)->seq_num));
	       
	       /* we no longer need these results.. */
	       xdr_free(xdr_authgssapi_init_res, &call_res);
	  }
     } else if (call_res.signed_isn.length != 0) {
	  PRINTF(("gssapi_create: got signed isn, can't check yet\n"));
     }
     
     /* results were okay.. continue if necessary */
     if (*gssstat == GSS_S_CONTINUE_NEEDED) {
	  PRINTF(("gssapi_create: not done, continuing\n"));
	  goto next_token;
     }
     
     /*
      * Done!  Context is established, we have client_handle and isn.
      */
     AUTH_PRIVATE(auth)->established = TRUE;
     
     marshall_new_creds(auth, FALSE,
			&AUTH_PRIVATE(auth)->client_handle); 
     
     PRINTF(("gssapi_create: done. client_handle %#x, isn %d\n\n",
	     *((uint32_t *)AUTH_PRIVATE(auth)->client_handle.value),
	     AUTH_PRIVATE(auth)->seq_num));
     
     /* don't assume the caller will want to change clnt->cl_auth */
     clnt->cl_auth = save_auth;
     
     return auth;
     
     /******************************************************************/
     
cleanup:
     PRINTF(("gssapi_create: bailing\n\n"));

     if (auth) {
	 if (AUTH_PRIVATE(auth))
	     auth_gssapi_destroy(auth);
	 else
	     free(auth);
	 auth = NULL;
     }
     
     /* don't assume the caller will want to change clnt->cl_auth */
     clnt->cl_auth = save_auth;
     
     if (rpc_createerr.cf_stat == 0)
	  rpc_createerr.cf_stat = RPC_AUTHERROR;
     
     return auth;
}