X-Git-Url: http://www.project-moonshot.org/gitweb/?p=trust_router.git;a=blobdiff_plain;f=common%2Ftr_gss.c;fp=common%2Ftr_gss.c;h=251c29e6a702ba599866ab44b85b7e3ee460d740;hp=7fa6876cc212d71d4d540c4d534e0cd39323d500;hb=6f65c9cce86719147d0b4dcc9823b25443c2d185;hpb=eaa1a8ceed54fbfadc2638cf383aaa12ab446a57 diff --git a/common/tr_gss.c b/common/tr_gss.c index 7fa6876..251c29e 100644 --- a/common/tr_gss.c +++ b/common/tr_gss.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, JANET(UK) + * Copyright (c) 2018, JANET(UK) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,100 +33,274 @@ */ #include +#include +#include +#include +#include +#include #include -static int tr_gss_names_destructor(void *obj) +/** + * tr_gss.c - GSS connection handler + * + * The chief entry point to this module is tr_gss_handle_connection(). This + * function accepts an incoming socket connection, runs the GSS authorization + * and authentication process, accepts a request, processes it, then sends + * the reply and returns without closing the connection. + * + * Callers need to provide two callbacks, each with a cookie for passing + * custom data to the callback. + * + * * TR_GSS_AUTH_FN auth_cb: Authorization callback + * - This callback is used during the GSS auth process to determine whether + * a credential should be authorized to connect. + * + * * TR_GSS_HANDLE_REQ_FN req_cb: Request handler callback + * - After auth, this callback is passed the string form of the incoming request. + * It should process the request and return a string form of the outgoing + * response, if any. + */ + +typedef struct tr_gss_cookie { + TR_GSS_AUTH_FN *auth_cb; + void *auth_cookie; +} TR_GSS_COOKIE; + +static int tr_gss_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data) { - TR_GSS_NAMES *gss_names=talloc_get_type_abort(obj, TR_GSS_NAMES); - int ii=0; + TR_GSS_COOKIE *cookie = talloc_get_type_abort(data, TR_GSS_COOKIE); + TR_NAME name ={(char *) displayName->value, (int) displayName->length}; + int result=0; - for (ii=0; iinames[ii]!=NULL) - tr_free_name(gss_names->names[ii]); + tr_debug("tr_gss_auth_cb: Calling auth handler for %.*s.", name.len, name.buf); + if (cookie->auth_cb(clientName, &name, cookie->auth_cookie)) { + tr_debug("tr_gss_auth_cb: client '%.*s' denied authorization.", name.len, name.buf); + result=EACCES; /* denied */ } - return 0; + + return result; } -TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx) + + +/** + * Handle GSS authentication and authorization + * + * @param conn connection file descriptor + * @param acceptor_service name of acceptor to present to initiator + * @param acceptor_hostname hostname of acceptor to present to initiator + * @param gssctx GSS context + * @param auth_cb authorization callback + * @param auth_cookie generic data to pass to the authorization callback + * @return 0 on successful auth, 1 on disallowed auth, -1 on error + */ +static int tr_gss_auth_connection(int conn, + const char *acceptor_service, + const char *acceptor_hostname, + gss_ctx_id_t *gssctx, + TR_GSS_AUTH_FN auth_cb, + void *auth_cookie) { - TR_GSS_NAMES *gn=talloc(mem_ctx, TR_GSS_NAMES); - int ii=0; + int rc = 0; + int auth, autherr = 0; + gss_buffer_desc nameBuffer = {0, NULL}; + TR_GSS_COOKIE *cookie = NULL; - if (gn!=NULL) { - for (ii=0; iinames[ii]=NULL; - talloc_set_destructor((void *)gn, tr_gss_names_destructor); + nameBuffer.value = talloc_asprintf(NULL, "%s@%s", acceptor_service, acceptor_hostname); + if (nameBuffer.value == NULL) { + tr_err("tr_gss_auth_connection: Error allocating acceptor name."); + return -1; } - return gn; -} + nameBuffer.length = strlen(nameBuffer.value); -void tr_gss_names_free(TR_GSS_NAMES *gn) -{ - talloc_free(gn); + /* Set up for the auth callback. There are two layers of callbacks here: we + * use our own, which handles gsscon interfacing and calls the auth_cb parameter + * to do the actual auth. Store the auth_cb information in a metacookie. */ + cookie = talloc(NULL, TR_GSS_COOKIE); + cookie->auth_cb=auth_cb; + cookie->auth_cookie=auth_cookie; + + /* Now call gsscon with *our* auth callback and cookie */ + tr_debug("tr_gss_auth_connection: Beginning passive authentication as %.*s", + nameBuffer.length, nameBuffer.value); + rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tr_gss_auth_cb, cookie); + talloc_free(cookie); + talloc_free(nameBuffer.value); + if (rc) { + tr_debug("tr_gss_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc); + return -1; + } + + tr_debug("tr_gss_auth_connection: Authentication succeeded, now authorizing."); + rc = gsscon_authorize(*gssctx, &auth, &autherr); + if (rc) { + tr_debug("tr_gss_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", + rc, autherr); + return -1; + } + + if (auth) + tr_debug("tr_gss_auth_connection: Connection authenticated, conn = %d.", conn); + else + tr_debug("tr_gss_auth_connection: Authentication failed, conn %d.", conn); + + return !auth; } -/* returns 0 on success */ -int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new) +/** + * Read a request from the GSS connection + * + * @param mem_ctx talloc context for the result + * @param conn file descriptor for the connection + * @param gssctx GSS context + * @return talloc'ed string containing the request, or null on error + */ +static char *tr_gss_read_req(TALLOC_CTX *mem_ctx, int conn, gss_ctx_id_t gssctx) { - int ii=0; + int err; + char *retval = NULL; + char *buf = NULL; + size_t buflen = 0; - for (ii=0; iinames[ii]==NULL) - break; + err = gsscon_read_encrypted_token(conn, gssctx, &buf, &buflen); + if (err || (buf == NULL)) { + if (buf) + free(buf); + tr_debug("tr_gss_read_req: Error reading from connection, rc=%d", err); + return NULL; } - if (ii!=TR_MAX_GSS_NAMES) { - gn->names[ii]=new; - return 0; - } else - return -1; + + tr_debug("tr_gss_read_req: Read %u bytes.", (unsigned) buflen); + + // get a talloc'ed version, guaranteed to have a null termination + retval = talloc_asprintf(mem_ctx, "%.*s", (int) buflen, buf); + free(buf); + + return retval; } -int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name) +/** + * Write a response to the GSS connection + * + * @param conn file descriptor for the connection + * @param gssctx GSS context + * @param resp encoded response string to send + * @return 0 on success, -1 on error + */ +static int tr_gss_write_resp(int conn, gss_ctx_id_t gssctx, const char *resp) { - int ii=0; + int err = 0; - if (!gn) - return 0; - - for (ii=0; iinames[ii]!=NULL) && - (0==tr_name_cmp(gn->names[ii], name))) - return 1; + /* Send the response over the connection */ + err = gsscon_write_encrypted_token (conn, gssctx, resp, strlen(resp) + 1); + if (err) { + tr_debug("tr_gss_send_response: Error sending response over connection, rc=%d.", err); + return -1; } return 0; } -/* iterators */ -TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx) +/** + * Handle a request/response connection + * + * Authorizes/authenticates the connection, then reads a response, passes that to a + * callback to get a response, sends that, then returns. + * + * @param conn connection file descriptor + * @param acceptor_service acceptor name to present + * @param acceptor_hostname acceptor hostname to present + * @param auth_cb callback for authorization + * @param auth_cookie cookie for the auth_cb + * @param req_cb callback to handle the request and produce the response + * @param req_cookie cookie for the req_cb + */ +TR_GSS_RC tr_gss_handle_connection(int conn, + const char *acceptor_service, + const char *acceptor_hostname, + TR_GSS_AUTH_FN auth_cb, + void *auth_cookie, + TR_GSS_HANDLE_REQ_FN req_cb, + void *req_cookie) { - TR_GSS_NAMES_ITER *iter=talloc(mem_ctx, TR_GSS_NAMES_ITER); - if (iter!=NULL) { - iter->gn=NULL; - iter->ii=0; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT; + char *req_str = NULL; + size_t req_len = 0; + TR_MSG *req_msg = NULL; + TR_MSG *resp_msg = NULL; + char *resp_str = NULL; + TR_GSS_RC rc = TR_GSS_ERROR; + + tr_debug("tr_gss_handle_connection: Attempting to accept %s connection on fd %d.", + acceptor_service, conn); + + if (tr_gss_auth_connection(conn, + acceptor_service, + acceptor_hostname, + &gssctx, + auth_cb, + auth_cookie)) { + tr_notice("tr_gss_handle_connection: Error authorizing connection."); + goto cleanup; } - return iter; -} -TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn) -{ - iter->gn=gn; - iter->ii=-1; - return tr_gss_names_iter_next(iter); -} + tr_debug("tr_gss_handle_connection: Connection authorized"); -TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter) -{ - for (iter->ii++; - (iter->ii < TR_MAX_GSS_NAMES) && (iter->gn->names[iter->ii]==NULL); - iter->ii++) { } - - if (iter->iign->names[iter->ii]; - - return NULL; -} + // TODO: should there be a timeout on this? + do { + /* continue until an error breaks us out */ + // try to read a request + req_str = tr_gss_read_req(tmp_ctx, conn, gssctx); -void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter) -{ - talloc_free(iter); + if (req_str == NULL) { + // an error occurred, give up + tr_notice("tr_gss_handle_connection: Error reading request"); + goto cleanup; + } + + req_len = strlen(req_str); + + /* If we got no characters, we will loop again. Free the empty response for the next loop. */ + if (req_len == 0) + talloc_free(req_str); + + } while (req_len == 0); + + /* Decode the request */ + req_msg = tr_msg_decode(tmp_ctx, req_str, req_len); + if (req_msg == NULL) { + tr_notice("tr_gss_handle_connection: Error decoding response"); + goto cleanup; + } + + /* Hand off the request for processing and get the response */ + rc = req_cb(tmp_ctx, req_msg, &resp_msg, req_cookie); + if (rc != TR_GSS_SUCCESS) + goto cleanup; + + if (resp_msg == NULL) { + // no response, clean up + goto cleanup; + } + + /* Encode the response */ + resp_str = tr_msg_encode(tmp_ctx, resp_msg); + if (resp_str == NULL) { + /* We apparently can't encode a response, so just return */ + tr_err("tr_gss_handle_connection: Error encoding response"); + goto cleanup; + } + + // send the response + if (tr_gss_write_resp(conn, gssctx, resp_str)) { + tr_err("tr_gss_handle_connection: Error writing response"); + goto cleanup; + } + + /* we successfully sent a response */ + rc = TR_GSS_SUCCESS; + +cleanup: + talloc_free(tmp_ctx); + return rc; }