diff options
Diffstat (limited to 'src/lib/rpc/auth_gssapi.c')
| -rw-r--r-- | src/lib/rpc/auth_gssapi.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/src/lib/rpc/auth_gssapi.c b/src/lib/rpc/auth_gssapi.c new file mode 100644 index 0000000000..0ffe96d188 --- /dev/null +++ b/src/lib/rpc/auth_gssapi.c @@ -0,0 +1,901 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * $Log$ + * Revision 1.23 1996/07/22 20:39:39 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.22.4.1 1996/07/18 04:18:29 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.22.2.1 1996/06/20 23:35:31 marc + * File added to the repository on a branch + * + * Revision 1.22 1996/06/05 20:56:16 bjaspan + * memset bindings to zero before use + * + * Revision 1.21 1996/05/12 06:11:38 marc + * renamed lots of types: u_foo to unsigned foo, and foo32 to rpc_foo32. This is to make autoconfiscation less painful. + * + * Revision 1.20 1995/12/13 14:02:45 grier + * Longs to ints for Alpha + * + * Revision 1.19 1995/10/31 16:36:00 bjaspan + * GS reported this bug. When talking to a 1.1 server, the server will + * not fail indicating the version is wrong because it never checks the + * version; it just responds with call_res.version 1. So we have to + * deal. Oops. + * + * It's amazing what can fall through the cracks when most of the + * development team is fired.. + * + * Revision 1.18 1995/10/31 16:07:06 bjaspan + * fix from grier + * + * Revision 1.17 1995/08/24 21:06:26 bjaspan + * set acceptor channel bindings + * + * Revision 1.16 1995/08/23 20:27:37 bjaspan + * [secure-rpc/3392] add channel bindinds to the rpc + * + * Revision 1.15 1995/05/08 22:32:01 marc + * change call_arg.version from 1 to 2 to indicate the new client is in use. + * + * Revision 1.14 1995/03/22 22:05:20 jik + * Reorder the auth_ops structure, to agree with the order of the version of + * this library in the GK source tree. I chose to reorder this one rather + * than reorder the GK tree because Barry says that the order in the GK tree + * makes more sense. + * + * Revision 1.13 1994/10/27 12:39:02 jik + * [secure-rpc/2808: add credential versioning] + * + * Sandbox: + * + * [secure-rpc/2808] add version field to client creds + * + * change to use GSS_ERROR &c macros; I don't think this is correct + * ============================================================================= + * + * Revision 1.14 1994/10/26 20:03:46 bjaspan + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.13 1994/04/07 16:12:06 jik + * The second argument to xdr_opaque_auth is struct auth *, not struct + * auth. + * + * Revision 1.12 1993/12/08 21:42:43 bjaspan + * use AUTH_GSSAPI_DISPLAY_STATUS macro, reindent + * + * Revision 1.11 1993/12/06 21:21:03 bjaspan + * debugging levels + * + * Revision 1.10 1993/11/18 23:13:07 bjaspan + * add some function comments + * + * Revision 1.9 1993/11/15 19:50:03 bjaspan + * redefine AUTH_REFRESH to take the error message as an argument, and + * change auth_gssapi_refresh to increment the seq_num on REJECTEDVERF + * + * Revision 1.8 1993/11/12 02:31:59 bjaspan + * set rpc_createerr as appropriate + * + * Revision 1.7 1993/11/03 21:21:02 bjaspan + * fix seq_num handling in cases of errors and retransmission + * + * Revision 1.6 1993/11/01 19:55:20 bjaspan + * display gss_major and gss_minor, and unstatic auth_gssapi_debug + * + * Revision 1.5 1993/10/28 22:07:33 bjaspan + * create_default takes char *, use seq_num in args/results + * + * Revision 1.4 1993/10/26 21:11:53 bjaspan + * working + * + * Revision 1.3 1993/10/21 19:01:09 bjaspan + * added auth_gssapi_destroy, cleaned up a few things + * + * Revision 1.2 1993/10/19 03:10:58 bjaspan + * snapshot: GSS-API working in hacked up state, not seal/unseal + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <string.h> +#include <sys/errno.h> + +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_generic.h> + +#include <rpc/rpc.h> +#include <rpc/auth_gssapi.h> + +#ifdef __CODECENTER__ +#define DEBUG_GSSAPI 1 +#endif + +#ifdef DEBUG_GSSAPI +int auth_debug_gssapi = DEBUG_GSSAPI; +#define L_PRINTF(l,args) if (auth_debug_gssapi >= l) printf args +#define PRINTF(args) L_PRINTF(99, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) \ + if (auth_debug_gssapi) auth_gssapi_display_status args +#else +#define PRINTF(args) +#define L_PRINTF(l, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) +#endif + +static void auth_gssapi_nextverf(); +static bool_t auth_gssapi_marshall(); +static bool_t auth_gssapi_validate(); +static bool_t auth_gssapi_refresh(); +static bool_t auth_gssapi_wrap(); +static bool_t auth_gssapi_unwrap(); +static void auth_gssapi_destroy(); + +static bool_t marshall_new_creds(); + +static struct auth_ops auth_gssapi_ops = { + auth_gssapi_nextverf, + auth_gssapi_marshall, + auth_gssapi_validate, + auth_gssapi_refresh, + auth_gssapi_destroy, + auth_gssapi_wrap, + auth_gssapi_unwrap, +}; + +/* + * the ah_private data structure for an auth_handle + */ +struct auth_gssapi_data { + bool_t established; + CLIENT *clnt; + gss_ctx_id_t context; + gss_buffer_desc client_handle; + rpc_u_int32 seq_num; + int def_cred; + + /* pre-serialized ah_cred */ + unsigned char cred_buf[MAX_AUTH_BYTES]; + rpc_u_int32 cred_len; +}; +#define AUTH_PRIVATE(auth) ((struct auth_gssapi_data *)auth->ah_private) + +extern struct rpc_createerr rpc_createerr; + +/* + * 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; +} + +/* + * 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, + int req_flags, + OM_uint32 time_req, + gss_OID *actual_mech_type, + int *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 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; + + auth = (AUTH *) malloc(sizeof(*auth)); + pdata = (struct auth_gssapi_data *) malloc(sizeof(*pdata)); + if (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); + + /* don't assume the caller will want to change clnt->cl_auth */ + save_auth = clnt->cl_auth; + clnt->cl_auth = auth; + + /* start by trying latest version */ + call_arg.version = 3; + +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; + + if (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", + *((rpc_u_int32 *)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(rpc_u_int32)) { + PRINTF(("gssapi_create: gss_unseal gave %d bytes\n", + isn_buf.length)); + goto cleanup; + } + + AUTH_PRIVATE(auth)->seq_num = (rpc_u_int32) + ntohl(*((rpc_u_int32*)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", + *((rpc_u_int32 *)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_PRIVATE(auth)) + auth_gssapi_destroy(auth); + else if (auth) + 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; +} + +/* + * Function: marshall_new_creds + * + * Purpose: (pre-)serialize auth_msg and client_handle fields of + * auth_gssapi_creds into auth->cred_buf + * + * Arguments: + * + * auth (r/w) the AUTH structure to modify + * auth_msg (r) the auth_msg field to serialize + * client_handle (r) the client_handle field to serialize, or + * NULL + * + * Returns: TRUE if successful, FALSE if not + * + * Requires: auth must point to a valid GSS-API auth structure, auth_msg + * must be TRUE or FALSE, client_handle must be a gss_buffer_t with a valid + * value and length field or NULL. + * + * Effects: auth->ah_cred is set to the serialized auth_gssapi_creds + * version 2 structure (stored in the cred_buf field of private data) + * containing version, auth_msg and client_handle. + * auth->ah_cred.oa_flavor is set to AUTH_GSSAPI. If cliend_handle is + * NULL, it is treated as if it had a length of 0 and a value of NULL. + * + * Modifies: auth + */ +static bool_t marshall_new_creds(auth, auth_msg, client_handle) + AUTH *auth; + bool_t auth_msg; + gss_buffer_t client_handle; +{ + auth_gssapi_creds creds; + XDR xdrs; + + PRINTF(("marshall_new_creds: starting\n")); + + creds.version = 2; + + creds.auth_msg = auth_msg; + if (client_handle) + GSS_COPY_BUFFER(creds.client_handle, *client_handle) + else { + creds.client_handle.length = 0; + creds.client_handle.value = NULL; + } + + xdrmem_create(&xdrs, AUTH_PRIVATE(auth)->cred_buf, + MAX_AUTH_BYTES, XDR_ENCODE); + if (! xdr_authgssapi_creds(&xdrs, &creds)) { + PRINTF(("marshall_new_creds: failed encoding auth_gssapi_creds\n")); + XDR_DESTROY(&xdrs); + return FALSE; + } + AUTH_PRIVATE(auth)->cred_len = xdr_getpos(&xdrs); + XDR_DESTROY(&xdrs); + + PRINTF(("marshall_new_creds: auth_gssapi_creds is %d bytes\n", + AUTH_PRIVATE(auth)->cred_len)); + + auth->ah_cred.oa_flavor = AUTH_GSSAPI; + auth->ah_cred.oa_base = (char *) AUTH_PRIVATE(auth)->cred_buf; + auth->ah_cred.oa_length = AUTH_PRIVATE(auth)->cred_len; + + PRINTF(("marshall_new_creds: succeeding\n")); + + return TRUE; +} + + +/* + * Function: auth_gssapi_nextverf + * + * Purpose: None. + * + * Effects: None. Never called. + */ +static void auth_gssapi_nextverf(/*auth*/) + /*AUTH *auth;*/ +{ +} + +/* + * Function: auth_gssapi_marhsall + * + * Purpose: Marshall RPC credentials and verifier onto xdr stream. + * + * Arguments: + * + * auth (r/w) AUTH structure for client + * xdrs (r/w) XDR stream to marshall to + * + * Returns: boolean indicating success/failure + * + * Effects: + * + * The pre-serialized credentials in cred_buf are serialized. If the + * context is established, the sealed sequence number is serialized as + * the verifier. If the context is not established, an empty verifier + * is serialized. The sequence number is *not* incremented, because + * this function is called multiple times if retransmission is required. + * + * If this took all the header fields as arguments, it could sign + * them. + */ +static bool_t auth_gssapi_marshall(auth, xdrs) + AUTH *auth; + XDR *xdrs; +{ + OM_uint32 minor_stat; + gss_buffer_desc out_buf; + rpc_u_int32 seq_num; + + if (AUTH_PRIVATE(auth)->established == TRUE) { + PRINTF(("gssapi_marshall: starting\n")); + + seq_num = AUTH_PRIVATE(auth)->seq_num + 1; + + PRINTF(("gssapi_marshall: sending seq_num %d\n", seq_num)); + + if (auth_gssapi_seal_seq(AUTH_PRIVATE(auth)->context, seq_num, + &out_buf) == FALSE) { + PRINTF(("gssapi_marhshall: seal failed\n")); + } + + auth->ah_verf.oa_base = out_buf.value; + auth->ah_verf.oa_length = out_buf.length; + + if (! xdr_opaque_auth(xdrs, &auth->ah_cred) || + ! xdr_opaque_auth(xdrs, &auth->ah_verf)) { + (void) gss_release_buffer(&minor_stat, &out_buf); + return FALSE; + } + (void) gss_release_buffer(&minor_stat, &out_buf); + } else { + PRINTF(("gssapi_marshall: not established, sending null verf\n")); + + auth->ah_verf.oa_base = NULL; + auth->ah_verf.oa_length = 0; + + if (! xdr_opaque_auth(xdrs, &auth->ah_cred) || + ! xdr_opaque_auth(xdrs, &auth->ah_verf)) { + return FALSE; + } + } + + return TRUE; +} + +/* + * Function: auth_gssapi_validate + * + * Purpose: Validate RPC response verifier from server. + * + * Effects: See design document, section XXX. + */ +static bool_t auth_gssapi_validate(auth, verf) + AUTH *auth; + struct opaque_auth *verf; +{ + gss_buffer_desc in_buf; + rpc_u_int32 seq_num; + + if (AUTH_PRIVATE(auth)->established == FALSE) { + PRINTF(("gssapi_validate: not established, noop\n")); + return TRUE; + } + + PRINTF(("gssapi_validate: starting\n")); + + in_buf.length = verf->oa_length; + in_buf.value = verf->oa_base; + if (auth_gssapi_unseal_seq(AUTH_PRIVATE(auth)->context, &in_buf, + &seq_num) == FALSE) { + PRINTF(("gssapi_validate: failed unsealing verifier\n")); + return FALSE; + } + + /* we sent seq_num+1, so we should get back seq_num+2 */ + if (AUTH_PRIVATE(auth)->seq_num+2 != seq_num) { + PRINTF(("gssapi_validate: expecting seq_num %d, got %d (%#x)\n", + AUTH_PRIVATE(auth)->seq_num + 2, seq_num, seq_num)); + return FALSE; + } + PRINTF(("gssapi_validate: seq_num %d okay\n", seq_num)); + + /* +1 for successful transmission, +1 for successful validation */ + AUTH_PRIVATE(auth)->seq_num += 2; + + PRINTF(("gssapi_validate: succeeding\n")); + + return TRUE; +} + +/* + * Function: auth_gssapi_refresh + * + * Purpose: Attempts to resyncrhonize the sequence number. + * + * Effects: + * + * When the server receives a properly authenticated RPC call, it + * increments the sequence number it is expecting from the client. + * But if the server's response is lost for any reason, the client + * can't know whether the server ever received it, assumes it didn't, + * and does *not* increment its sequence number. Thus, the client's + * next call will fail with AUTH_REJECTEDCRED because the server will + * think it is a replay attack. + * + * When an AUTH_REJECTEDCRED error arrives, this function attempts to + * resyncrhonize by incrementing the client's sequence number and + * returning TRUE. If any other error arrives, it returns FALSE. + */ +static bool_t auth_gssapi_refresh(auth, msg) + AUTH *auth; + struct rpc_msg *msg; +{ + if (msg->rm_reply.rp_rjct.rj_stat == AUTH_ERROR && + msg->rm_reply.rp_rjct.rj_why == AUTH_REJECTEDVERF) { + PRINTF(("gssapi_refresh: rejected verifier, incrementing\n")); + AUTH_PRIVATE(auth)->seq_num++; + return TRUE; + } else { + PRINTF(("gssapi_refresh: failing\n")); + return FALSE; + } +} + +/* + * 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 *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")); +} + +/* + * Function: auth_gssapi_wrap + * + * Purpose: encrypt the serialized arguments from xdr_func applied to + * xdr_ptr and write the result to xdrs. + * + * Effects: See design doc, section XXX. + */ +static bool_t auth_gssapi_wrap(auth, out_xdrs, xdr_func, xdr_ptr) + AUTH *auth; + XDR *out_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + OM_uint32 gssstat, minor_stat; + + if (! AUTH_PRIVATE(auth)->established) { + PRINTF(("gssapi_wrap: context not established, noop\n")); + return (*xdr_func)(out_xdrs, xdr_ptr); + } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat, + AUTH_PRIVATE(auth)->context, + AUTH_PRIVATE(auth)->seq_num+1, + 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; +} + +/* + * Function: auth_gssapi_unwrap + * + * Purpose: read encrypted arguments from xdrs, decrypt, and + * deserialize with xdr_func into xdr_ptr. + * + * Effects: See design doc, section XXX. + */ +static bool_t auth_gssapi_unwrap(auth, in_xdrs, xdr_func, xdr_ptr) + AUTH *auth; + XDR *in_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + OM_uint32 gssstat, minor_stat; + + if (! AUTH_PRIVATE(auth)->established) { + PRINTF(("gssapi_unwrap: context not established, noop\n")); + return (*xdr_func)(in_xdrs, xdr_ptr); + } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat, + AUTH_PRIVATE(auth)->context, + AUTH_PRIVATE(auth)->seq_num, + 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; +} |
