diff options
| author | Marc Horowitz <marc@mit.edu> | 1996-07-22 20:49:46 +0000 |
|---|---|---|
| committer | Marc Horowitz <marc@mit.edu> | 1996-07-22 20:49:46 +0000 |
| commit | edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1 (patch) | |
| tree | 6c2974a97b448c040fa4a31708ec5e02f187526c /src/lib/rpc/svc_auth_gssapi.c | |
| parent | 013bb1391582ed9e653ae706e398ddb8d08cfcc9 (diff) | |
| download | krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.gz krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.xz krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.zip | |
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.
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8774 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/rpc/svc_auth_gssapi.c')
| -rw-r--r-- | src/lib/rpc/svc_auth_gssapi.c | 1181 |
1 files changed, 1181 insertions, 0 deletions
diff --git a/src/lib/rpc/svc_auth_gssapi.c b/src/lib/rpc/svc_auth_gssapi.c new file mode 100644 index 000000000..07cf59ab8 --- /dev/null +++ b/src/lib/rpc/svc_auth_gssapi.c @@ -0,0 +1,1181 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.37 1996/07/22 20:41:00 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.36.4.1 1996/07/18 04:19:34 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.36.2.1 1996/06/20 23:39:22 marc + * File added to the repository on a branch + * + * Revision 1.36 1996/05/30 19:25:02 bjaspan + * zero bindings structure before using it + * + * Revision 1.35 1996/05/12 06:17:25 marc + * changed around the file includes, since krb5 has changed some. + * + * added conditionalization GSS_BACKWARD_HACK until and if this hack is + * reimplemented in the newly merged gssapi. + * + * conditionalize out the host-specific cruft for setting the local + * address to INADDR_ANY, since you can just assign it that way on all + * platforms I know of. + * + * Revision 1.34 1996/02/12 15:14:00 grier + * [secure/3570] + * restore (struct sockaddr *) cast that got mangled + * + * Revision 1.33 1996/02/07 13:09:52 jik + * Actually, I should have used krb5_error_code, not krb5_int32. + * + * Revision 1.32 1996/02/07 13:08:31 jik + * Include <krb5/krb5.h> to get the krb5_int32 typedef, which we then use + * in a cast when checking if the GSS-API minor status value is equal to + * a krb5 error code. + * + * Revision 1.31 1996/02/01 18:29:29 grier + * Restore use of error code definition. + * Return original code structure. + * + * Revision 1.30 1996/01/31 19:15:49 grier + * [secure/3570] + * Remove (void *) casts to memcpy() args + * + * Revision 1.29 1996/01/25 03:58:04 grier + * Remove debug code + * + * Revision 1.28 1996/01/25 03:56:50 grier + * secure/3570 - missed Alpha checkin + * + * Revision 1.26 1995/11/07 23:17:23 grier + * memcpy() cast + * + * Revision 1.25 1995/08/24 21:05:48 bjaspan + * set acceptor channel bindings + * + * Revision 1.24 1995/08/23 20:28:02 bjaspan + * [secure-rpc/3392] add channel bindinds to the rpc + * + * Revision 1.23 1995/07/10 18:49:22 bjaspan + * [secure-build/3377] remove use of BSD db + * + * Revision 1.22 1995/05/25 18:35:53 bjaspan + * [secure-rpc/3103] log misc errors from RPC + * + * Revision 1.21 1995/05/24 17:34:03 bjaspan + * [secure-rpc/3302] don't allow client to make server exit unless + * debugging is enabled + * + * Revision 1.20 1995/05/08 22:32:44 marc + * if a new client is in use, set the krb5 gssapi mech into + * backward-compatibility mode. + * + * Revision 1.19 1994/10/27 12:38:51 jik + * [secure-rpc/2808: add credential versioning] + * + * Sandbox: + * + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.22 1994/10/26 20:03:27 bjaspan + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.21 1994/05/23 01:26:01 bjaspan + * [secure-rpc/1911] set rq_svccred to the context instead of the service + * gss name + * + * Revision 1.20 1994/05/09 17:48:39 shanzer + * change sys/fcntl.h to fcntl.h + * + * Revision 1.19 1994/04/08 17:21:32 bjaspan + * remove KRB5KTNAME hack + * + * Revision 1.18 1994/03/18 15:48:13 shanzer + * include sys/fcntl.h + * + * Revision 1.17 1994/03/08 00:05:56 shanzer + * call rand() instead random() + * + * Revision 1.16 1993/12/08 21:43:54 bjaspan + * gss_delete_sec_context failure is not fatal (in fact, the context + * will often be expired); use AUTH_GSSAPI_DISPLAY_STATUS macro + * + * Revision 1.15 1993/12/08 20:20:08 bjaspan + * add debugging info to expire_client, correct comment above btree->put + * + * Revision 1.14 1993/12/08 06:52:49 bjaspan + * *many* debugging improvements to help find secure-rpc/586, and (I hope) + * the fix: don't change client_data->expiration without deleting it + * and reinserting it into the btree + * + * Revision 1.13 1993/12/06 21:22:26 bjaspan + * debugging levels, #ifdef PURIFY, call abort() on impossible failures + * + * Revision 1.12 1993/11/12 02:33:14 bjaspan + * add badauth + * / + * + * Revision 1.11 1993/11/03 23:46:15 bjaspan + * new log_badverf format + * + * Revision 1.10 1993/11/03 21:23:30 bjaspan + * handle GSS_C_INDEFINITE expiration, add log_badverf, set rq_svccred + * + * Revision 1.9 1993/11/03 01:30:36 bjaspan + * don't include gssapi_krb5.h, it isn't needed + * + * Revision 1.8 1993/11/02 22:09:02 bjaspan + * support multiple service-side names via _svcauth_gssapi_set_names + * + * Revision 1.7 1993/11/01 19:56:22 bjaspan + * unstatic svc_debug_gssapi, and send gss_{major,minor} back if + * accept_sec_context fails + * + * Revision 1.6 1993/10/28 22:09:58 bjaspan + * fix verifier mem leak, clean_clients() first to avoid dangling ref, + * only include hacked kt_default_name if DEBUG_GSSAPI defined + * + * Revision 1.5 1993/10/27 18:26:51 bjaspan + * use xdr_free instead of gss_release_buffer; this fixes memory leaks + * that were probably caused by zero-length seal/unseal tokens + * + * Revision 1.4 1993/10/26 21:12:51 bjaspan + * fully working + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +/* + * svc_auth_gssapi.c + * Handles the GSS-API flavor authentication parameters on the service + * side of RPC. + */ + +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <rpc/rpc.h> + +#include <gssapi/gssapi_generic.h> +#include <rpc/auth_gssapi.h> + +#ifdef GSS_BACKWARD_HACK +#include <gssapi/gssapi_krb5.h> +#endif + +/* This is here for the krb5_error_code typedef and the + KRB5KRB_AP_WRONG_PRINC #define.*/ +#include <krb5.h> + +#include <sys/file.h> +#include <fcntl.h> + +#define INITIATION_TIMEOUT 60*15 /* seconds until partially created */ + /* context is destroed */ +#define INDEF_EXPIRE 60*60*24 /* seconds until an context with no */ + /* expiration time is expired */ + +#ifdef __CODECENTER__ +#define DEBUG_GSSAPI 1 +#endif + +#ifdef DEBUG_GSSAPI +int svc_debug_gssapi = DEBUG_GSSAPI; +#define L_PRINTF(l,args) if (svc_debug_gssapi >= l) printf args +#define PRINTF(args) L_PRINTF(99, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) \ + if (svc_debug_gssapi) auth_gssapi_display_status args +#else +#define PRINTF(args) +#define L_PRINTF(l, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) +#endif + +typedef struct _svc_auth_gssapi_data { + bool_t established; + + gss_ctx_id_t context; + gss_name_t client_name, server_name; + gss_cred_id_t server_creds; + + rpc_u_int32 expiration; + rpc_u_int32 seq_num; + rpc_u_int32 key; + + SVCAUTH svcauth; + + /* kludge to free verifiers on next call */ + gss_buffer_desc prev_verf; +} svc_auth_gssapi_data; + +#define SVCAUTH_PRIVATE(auth) \ + ((svc_auth_gssapi_data *)(auth)->svc_ah_private) + +static bool_t svc_auth_gssapi_wrap(); +static bool_t svc_auth_gssapi_unwrap(); +static svc_auth_gssapi_data *create_client(); +static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle); +static void destroy_client(svc_auth_gssapi_data *client_data); +static void clean_client(), cleanup(); +static void client_expire(svc_auth_gssapi_data *client_data, rpc_u_int32 exp); +static void dump_db(char *msg); + +struct svc_auth_ops svc_auth_gssapi_ops = { + svc_auth_gssapi_wrap, + svc_auth_gssapi_unwrap, +}; + +/* + * Globals! Eeek! Run for the hills! + */ +static gss_cred_id_t *server_creds_list = NULL; +static gss_name_t *server_name_list = NULL; +static int server_creds_count = 0; + +static auth_gssapi_log_badauth_func log_badauth = NULL; +static caddr_t log_badauth_data = NULL; +static auth_gssapi_log_badverf_func log_badverf = NULL; +static caddr_t log_badverf_data = NULL; +static auth_gssapi_log_miscerr_func log_miscerr = NULL; +static caddr_t log_miscerr_data = NULL; + +#define LOG_MISCERR(arg) if (log_miscerr) \ + (*log_miscerr)(rqst, msg, arg, log_miscerr_data) + +typedef struct _client_list { + svc_auth_gssapi_data *client; + struct _client_list *next; +} client_list; + +static client_list *clients = NULL; + +extern int errno; + +enum auth_stat _svcauth_gssapi(rqst, msg, no_dispatch) + 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; + struct sockaddr_in sockname; + OM_uint32 gssstat, minor_stat, time_rec; + struct opaque_auth *cred, *verf; + svc_auth_gssapi_data *client_data; + int ret_flags, ret, i; + rpc_u_int32 seq_num; + int flag; + + 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_any; + + memset((char *) &call_res, 0, sizeof(call_res)); + + 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_DESTROY(&xdrs); + ret = AUTH_BADCRED; + goto error; + } + XDR_DESTROY(&xdrs); + + PRINTF(("svcauth_gssapi: got credentials, version %d, " + "client_handle len %d\n", creds.version, + 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", + *((rpc_u_int32 *) creds.client_handle.value), + 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: + 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) { + int len; + + 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; + + len = sizeof(sockname); + if (getsockname(rqst->rq_xprt->xp_sock, + (struct sockaddr *) &sockname, &len) < 0) { + LOG_MISCERR("cannot get local address"); + PRINTF(("svcauth_gssapi: errno %d while getting address", + errno)); + ret = AUTH_FAILED; + goto error; + } + + bindings.acceptor_addrtype = GSS_C_AF_INET; + bindings.acceptor_address.length = 4; + bindings.acceptor_address.value = &sockname.sin_addr.s_addr; + + 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]; + } + + 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; + + 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 || + /* + * XXX 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)) { + 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\n", + call_res.gss_major)); + 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); + + 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"); + 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"); + 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); + 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", + 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", + creds.client_handle.length)); + xdr_free(xdr_authgssapi_creds, &creds); + } + + PRINTF(("\n")); + return ret; +} + +static void cleanup() +{ + client_list *c, *c2; + + PRINTF(("cleanup_and_exit: starting\n")); + + c = clients; + while (c) { + c2 = c; + c = c->next; + destroy_client(c2->client); + free(c2); + } + + exit(0); +} + +/* + * Function: create_client + * + * Purpose: Creates an new client_data structure and stores it in the + * database. + * + * Returns: the new client_data structure, or NULL on failure. + * + * Effects: + * + * A new client_data is created and stored in the hash table and + * b-tree. A new key that is unique in the current database is + * chosen; this key should be used as the client's client_handle. + */ +static svc_auth_gssapi_data *create_client() +{ + client_list *c; + svc_auth_gssapi_data *client_data; + static int client_key = 1; + int ret; + + PRINTF(("svcauth_gssapi: empty creds, creating\n")); + + client_data = (svc_auth_gssapi_data *) malloc(sizeof(*client_data)); + if (client_data == NULL) + return NULL; + memset((char *) client_data, 0, sizeof(*client_data)); + L_PRINTF(2, ("create_client: new client_data = %#x\n", client_data)); + + /* set up client data structure */ + client_data->established = 0; + client_data->context = GSS_C_NO_CONTEXT; + client_data->expiration = time(0) + INITIATION_TIMEOUT; + + /* set up psycho-recursive SVCAUTH hack */ + client_data->svcauth.svc_ah_ops = &svc_auth_gssapi_ops; + client_data->svcauth.svc_ah_private = (caddr_t) client_data; + + client_data->key = client_key++; + + c = (client_list *) malloc(sizeof(client_list)); + if (c == NULL) + return NULL; + c->client = client_data; + c->next = NULL; + + + if (clients == NULL) + clients = c; + else { + c->next = clients; + clients = c; + } + + PRINTF(("svcauth_gssapi: new handle %d\n", client_data->key)); + L_PRINTF(2, ("create_client: done\n")); + + return client_data; +} + +/* + * Function: client_expire + * + * Purpose: change the expiration time of a client in the database + * + * Arguments: + * + * client_data (r) the client_data to expire + * exp (r) the new expiration time + * + * Effects: + * + * client_data->expiration = exp + * + * This function used to remove client_data from the database, change + * its expiration time, and re-add it, which was necessary because the + * database was sorted by expiration time so a simple modification + * would break the rep invariant. Now the database is an unsorted + * linked list, so it doesn't matter. + */ +static void client_expire(svc_auth_gssapi_data *client_data, rpc_u_int32 exp) +{ + client_data->expiration = exp; +} + +/* + * Function get_client + * + * Purpose: retrieve a client_data structure from the database based + * on its client handle (key) + * + * Arguments: + * + * client_handle (r) the handle (key) to retrieve + * + * Effects: + * + * Searches the list and returns the client_data whose key field + * matches the contents of client_handle, or returns NULL if none was + * found. + */ +static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle) +{ + client_list *c; + rpc_u_int32 handle; + + memcpy(&handle, client_handle->value, 4); + + L_PRINTF(2, ("get_client: looking for client %d\n", handle)); + + c = clients; + while (c) { + if (c->client->key == handle) + return c->client; + c = c->next; + } + + L_PRINTF(2, ("get_client: client_handle lookup failed\n")); + return NULL; +} + +/* + * 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; + int ret; + + PRINTF(("destroy_client: destroying client_data\n")); + L_PRINTF(2, ("destroy_client: client_data = %#x\n", 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 + 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); + +#ifdef PURIFY + purify_watch_n(client_data, sizeof(*client_data), "rw"); +#endif +} + +static void dump_db(char *msg) +{ + svc_auth_gssapi_data *client_data; + client_list *c; + + L_PRINTF(3, ("dump_db: %s:\n", msg)); + + c = clients; + while (c) { + client_data = c->client; + L_PRINTF(3, ("\tclient_data = %#x, exp = %d\n", + client_data, client_data->expiration)); + c = c->next; + } + + L_PRINTF(3, ("\n")); +} + +static void clean_client() +{ + svc_auth_gssapi_data *client_data; + client_list *c; + + PRINTF(("clean_client: starting\n")); + + c = clients; + while (c) { + client_data = c->client; + + L_PRINTF(2, ("clean_client: client_data = %#x\n", + client_data)); + + if (client_data->expiration < time(0)) { + PRINTF(("clean_client: client %d expired\n", + client_data->key)); + destroy_client(client_data); + c = clients; /* start over, just to be safe */ + } else { + c = c->next; + } + } + +done: + PRINTF(("clean_client: done\n")); +} + +/* + * Function: _svcauth_gssapi_set_name + * + * 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++) { + in_buf.value = names[i].name; + in_buf.length = strlen(in_buf.value) + 1; + + 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; + } + } + + server_creds_count = num; + + return TRUE; + +fail: + /* memory leak: not releasing names/creds already acquired */ + if (server_creds_list) + free(server_creds_list); + if (server_name_list) + free(server_name_list); + return FALSE; +} + +/* + * Function: _svcauth_gssapi_set_log_badauth_func + * + * Purpose: sets the logging function called when an invalid RPC call + * arrives + * + * See functional specifications. + */ +void _svcauth_gssapi_set_log_badauth_func + (auth_gssapi_log_badauth_func func, caddr_t data) +{ + log_badauth = func; + log_badauth_data = data; +} + +/* + * Function: _svcauth_gssapi_set_log_badverf_func + * + * Purpose: sets the logging function called when an invalid RPC call + * arrives + * + * See functional specifications. + */ +void _svcauth_gssapi_set_log_badverf_func + (auth_gssapi_log_badverf_func func, caddr_t data) +{ + log_badverf = func; + log_badverf_data = data; +} + +/* + * Function: _svcauth_gssapi_set_log_miscerr_func + * + * Purpose: sets the logging function called when a miscellaneous + * AUTH_GSSAPI error occurs + * + * See functional specifications. + */ +void _svcauth_gssapi_set_log_miscerr_func + (auth_gssapi_log_miscerr_func func, caddr_t data) +{ + log_miscerr = func; + log_miscerr_data = data; +} + +/* + * Encrypt the serialized arguments from xdr_func applied to xdr_ptr + * and write the result to xdrs. + */ +static bool_t svc_auth_gssapi_wrap(auth, out_xdrs, xdr_func, xdr_ptr) + 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(auth, in_xdrs, xdr_func, xdr_ptr) + 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 *) 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; +} |
