From 3648b53ccefd6e10376a1d4ac72e6dc7b158f17d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Daniel=20Kou=C5=99il?= Date: Fri, 20 Aug 2004 13:31:47 +0000 Subject: [PATCH] Forgot to add main part of GSSAPI support --- org.glite.lb.common/interface/lb_gss.h | 108 ++++ org.glite.lb.common/src/lb_gss.c | 877 +++++++++++++++++++++++++++++++++ 2 files changed, 985 insertions(+) create mode 100644 org.glite.lb.common/interface/lb_gss.h create mode 100644 org.glite.lb.common/src/lb_gss.c diff --git a/org.glite.lb.common/interface/lb_gss.h b/org.glite.lb.common/interface/lb_gss.h new file mode 100644 index 0000000..49dd2c2 --- /dev/null +++ b/org.glite.lb.common/interface/lb_gss.h @@ -0,0 +1,108 @@ +#ifndef __EDG_WORKLOAD_LOGGING_COMMON_LB_GSS_H__ +#define __EDG_WORKLOAD_LOGGING_COMMON_LB_GSS_H__ + +#ident "$Header$" + +#include + +enum { + EDG_WLL_GSS_OK = 0, /* no GSS errors */ + EDG_WLL_GSS_ERROR_GSS = -1, /* GSS specific error, call edg_wll_get_gss_error() for details */ + EDG_WLL_GSS_ERROR_TIMEOUT = -2, /* Timeout */ + EDG_WLL_GSS_ERROR_EOF = -3, /* EOF occured */ + EDG_WLL_GSS_ERROR_ERRNO = -4, /* System error. See errno */ + EDG_WLL_GSS_ERROR_HERRNO = -5 /* Resolver error. See h_errno */ +}; + +typedef struct _edg_wll_GssConnection { + gss_ctx_id_t context; + int sock; + char buffer[BUFSIZ]; + size_t bufsize; +} edg_wll_GssConnection; + +typedef struct _edg_wll_GssStatus { + OM_uint32 major_status; + OM_uint32 minor_status; +} edg_wll_GssStatus; + +/* XXX Support anonymous connections. Are we able/required to support + * anonymous servers as well. */ + +int +edg_wll_gss_acquire_cred_gsi(char *proxy_file, + gss_cred_id_t *cred, + char **name, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_connect(gss_cred_id_t cred, + char const *hostname, + int port, + struct timeval *timeout, + edg_wll_GssConnection *connection, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_accept(gss_cred_id_t cred, + int sock, + struct timeval *timeout, + edg_wll_GssConnection *connection, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_read(edg_wll_GssConnection *connection, + void *buf, + size_t bufsize, + struct timeval *timeout, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_write(edg_wll_GssConnection *connection, + const void *buf, + size_t bufsize, + struct timeval *timeout, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_read_full(edg_wll_GssConnection *connection, + void *buf, + size_t bufsize, + struct timeval *timeout, + size_t *total, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_write_full(edg_wll_GssConnection *connection, + const void *buf, + size_t bufsize, + struct timeval *timeout, + size_t *total, + edg_wll_GssStatus* gss_code); + +int +edg_wll_gss_watch_creds(const char * proxy_file, + time_t * proxy_mtime); + +int +edg_wll_gss_get_error(edg_wll_GssStatus* gss_code, + const char *prefix, + char **errmsg); + +int +edg_wll_gss_close(edg_wll_GssConnection *connection, + struct timeval *timeout); + +int +edg_wll_gss_reject(int sock); + +int +edg_wll_gss_oid_equal(const gss_OID a, + const gss_OID b); + +/* +int +edg_wll_gss_get_name(gss_cred_id_t cred, char **name); +*/ + +#endif /* __EDG_WORKLOAD_LOGGING_COMMON_LB_GSS_H__ */ diff --git a/org.glite.lb.common/src/lb_gss.c b/org.glite.lb.common/src/lb_gss.c new file mode 100644 index 0000000..7b2054f --- /dev/null +++ b/org.glite.lb.common/src/lb_gss.c @@ -0,0 +1,877 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lb_gss.h" + +#define tv_sub(a,b) {\ + (a).tv_usec -= (b).tv_usec;\ + (a).tv_sec -= (b).tv_sec;\ + if ((a).tv_usec < 0) {\ + (a).tv_sec--;\ + (a).tv_usec += 1000000;\ + }\ +} + +struct asyn_result { + struct hostent *ent; + int err; +}; + +static int decrement_timeout(struct timeval *timeout, struct timeval before, struct timeval after) +{ + (*timeout).tv_sec = (*timeout).tv_sec - (after.tv_sec - before.tv_sec); + (*timeout).tv_usec = (*timeout).tv_usec - (after.tv_usec - before.tv_usec); + while ( (*timeout).tv_usec < 0) { + (*timeout).tv_sec--; + (*timeout).tv_usec += 1000000; + } + if ( ((*timeout).tv_sec < 0) || (((*timeout).tv_sec == 0) && ((*timeout).tv_usec == 0)) ) return(1); + else return(0); +} + +/* ares callback handler for ares_gethostbyname() */ +static void callback_handler(void *arg, int status, struct hostent *h) { + struct asyn_result *arp = (struct asyn_result *) arg; + + switch (status) { + case ARES_SUCCESS: + if (h && h->h_addr_list[0]) { + arp->ent->h_addr_list = + (char **) malloc(2 * sizeof(char *)); + if (arp->ent->h_addr_list == NULL) { + arp->err = NETDB_INTERNAL; + break; + } + arp->ent->h_addr_list[0] = + malloc(sizeof(struct in_addr)); + if (arp->ent->h_addr_list[0] == NULL) { + free(arp->ent->h_addr_list); + arp->err = NETDB_INTERNAL; + break; + } + memcpy(arp->ent->h_addr_list[0], h->h_addr_list[0], + sizeof(struct in_addr)); + arp->ent->h_addr_list[1] = NULL; + arp->err = NETDB_SUCCESS; + } else { + arp->err = NO_DATA; + } + break; + case ARES_EBADNAME: + case ARES_ENOTFOUND: + arp->err = HOST_NOT_FOUND; + break; + case ARES_ENOTIMP: + arp->err = NO_RECOVERY; + break; + case ARES_ENOMEM: + case ARES_EDESTRUCTION: + default: + arp->err = NETDB_INTERNAL; + break; + } +} + +static void free_hostent(struct hostent *h){ + int i; + + if (h) { + if (h->h_name) free(h->h_name); + if (h->h_aliases) { + for (i=0; h->h_aliases[i]; i++) free(h->h_aliases[i]); + free(h->h_aliases); + } + if (h->h_addr_list) { + for (i=0; h->h_addr_list[i]; i++) free(h->h_addr_list[i]); + free(h->h_addr_list); + } + free(h); + } +} + +static int asyn_gethostbyname(char **addrOut, char const *name, struct timeval *timeout) { + struct asyn_result ar; + ares_channel channel; + int nfds; + fd_set readers, writers; + struct timeval tv, *tvp; + struct timeval start_time,check_time; + +/* start timer */ + gettimeofday(&start_time,0); + +/* ares init */ + if ( ares_init(&channel) != ARES_SUCCESS ) return(NETDB_INTERNAL); + ar.ent = (struct hostent *) calloc (sizeof(*ar.ent),1); + +/* query DNS server asynchronously */ + ares_gethostbyname(channel, name, AF_INET, callback_handler, + (void *) &ar); + +/* wait for result */ + while (1) { + FD_ZERO(&readers); + FD_ZERO(&writers); + nfds = ares_fds(channel, &readers, &writers); + if (nfds == 0) + break; + + gettimeofday(&check_time,0); + if (decrement_timeout(timeout, start_time, check_time)) { + ares_destroy(channel); + free_hostent(ar.ent); + return(TRY_AGAIN); + } + start_time = check_time; + + tvp = ares_timeout(channel, timeout, &tv); + + switch ( select(nfds, &readers, &writers, NULL, tvp) ) { + case -1: if (errno != EINTR) { + ares_destroy(channel); + free_hostent(ar.ent); + return NETDB_INTERNAL; + } else + continue; + case 0: + FD_ZERO(&readers); + FD_ZERO(&writers); + /* fallthrough */ + default: ares_process(channel, &readers, &writers); + } + } + + ares_destroy(channel); + + if (ar.err == NETDB_SUCCESS) { + *addrOut = malloc(sizeof(struct in_addr)); + memcpy(*addrOut,ar.ent->h_addr_list[0], sizeof(struct in_addr)); + free_hostent(ar.ent); + } + return(ar.err); +} + +static int +do_connect(int *s, char const *hostname, int port, struct timeval *timeout) +{ + int sock; + struct timeval before,after,to; + struct sockaddr_in a; + int sock_err; + socklen_t err_len; + char *addr; + int h_errno; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) return EDG_WLL_GSS_ERROR_ERRNO; + + if (timeout) { + int flags = fcntl(sock, F_GETFL, 0); + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) + return EDG_WLL_GSS_ERROR_ERRNO; + gettimeofday(&before,NULL); + } + + if (timeout) { + switch (h_errno = asyn_gethostbyname(&addr, hostname, timeout)) { + case NETDB_SUCCESS: + memset(&a,0,sizeof a); + a.sin_family = AF_INET; + memcpy(&a.sin_addr.s_addr,addr,sizeof a.sin_addr.s_addr); + a.sin_port = htons(port); + free(addr); + break; + case TRY_AGAIN: + close(sock); + return EDG_WLL_GSS_ERROR_TIMEOUT; + case NETDB_INTERNAL: + /* fall through */ + default: + close(sock); + /* h_errno may be thread safe with Linux pthread libs, + * but such an assumption is not portable + */ + errno = h_errno; + return EDG_WLL_GSS_ERROR_HERRNO; + } + } else { + struct hostent *hp; + + hp = gethostbyname(hostname); + if (hp == NULL) { + close(sock); + errno = h_errno; + return EDG_WLL_GSS_ERROR_HERRNO; + } + + memset(&a,0,sizeof a); + a.sin_family = AF_INET; + memcpy(&a.sin_addr.s_addr, hp->h_addr_list[0], sizeof(a.sin_addr.s_addr)); + a.sin_port = htons(port); + } + + if (connect(sock,(struct sockaddr *) &a,sizeof a) < 0) { + if (timeout && errno == EINPROGRESS) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock,&fds); + memcpy(&to,timeout,sizeof to); + gettimeofday(&before,NULL); + switch (select(sock+1,NULL,&fds,NULL,&to)) { + case -1: close(sock); + return EDG_WLL_GSS_ERROR_ERRNO; + case 0: close(sock); + return EDG_WLL_GSS_ERROR_TIMEOUT; + } + gettimeofday(&after,NULL); + tv_sub(after,before); + tv_sub(*timeout,after); + + err_len = sizeof sock_err; + if (getsockopt(sock,SOL_SOCKET,SO_ERROR,&sock_err,&err_len)) { + close(sock); + return EDG_WLL_GSS_ERROR_ERRNO; + } + if (sock_err) { + close(sock); + errno = sock_err; + return EDG_WLL_GSS_ERROR_ERRNO; + } + } + else { + close(sock); + return EDG_WLL_GSS_ERROR_ERRNO; + } + } + + *s = sock; + return 0; +} + +static int +send_token(int sock, void *token, size_t token_length, struct timeval *to) +{ + size_t num_written = 0; + ssize_t count; + fd_set fds; + struct timeval timeout,before,after; + int ret; + + if (to) { + memcpy(&timeout,to,sizeof(timeout)); + gettimeofday(&before,NULL); + } + + + ret = 0; + while(num_written < token_length) { + FD_ZERO(&fds); + FD_SET(sock,&fds); + switch (select(sock+1, NULL, &fds, NULL, to ? &timeout : NULL)) { + case 0: ret = EDG_WLL_GSS_ERROR_TIMEOUT; + goto end; + break; + case -1: ret = EDG_WLL_GSS_ERROR_ERRNO; + goto end; + break; + } + + count = write(sock, ((char *)token) + num_written, + token_length - num_written); + if(count < 0) { + if(errno == EINTR) + continue; + else { + ret = EDG_WLL_GSS_ERROR_ERRNO; + goto end; + } + } + num_written += count; + } + +end: + if (to) { + gettimeofday(&after,NULL); + tv_sub(after,before); + tv_sub(*to,after); + if (to->tv_sec < 0) { + to->tv_sec = 0; + to->tv_usec = 0; + } + } + + return ret; +} + +static int +recv_token(int sock, void **token, size_t *token_length, struct timeval *to) +{ + ssize_t count; + char buf[4098]; + char *t = NULL; + char *tmp; + size_t tl = 0; + fd_set fds; + struct timeval timeout,before,after; + int ret; + + if (to) { + memcpy(&timeout,to,sizeof(timeout)); + gettimeofday(&before,NULL); + } + + ret = 0; + do { + FD_ZERO(&fds); + FD_SET(sock,&fds); + switch (select(sock+1, &fds, NULL, NULL, to ? &timeout : NULL)) { + case 0: + ret = EDG_WLL_GSS_ERROR_TIMEOUT; + goto end; + break; + case -1: + ret = EDG_WLL_GSS_ERROR_ERRNO; + goto end; + break; + } + + count = read(sock, buf, sizeof(buf)); + if (count < 0) { + if (errno == EINTR) + continue; + else { + ret = EDG_WLL_GSS_ERROR_ERRNO; + goto end; + } + } + if (count == 0 && tl == 0 && errno == 0) + return EDG_WLL_GSS_ERROR_EOF; + tmp=realloc(t, tl + count); + if (tmp == NULL) { + errno = ENOMEM; + return EDG_WLL_GSS_ERROR_ERRNO; + } + t = tmp; + memcpy(t + tl, buf, count); + tl += count; + } while (count == sizeof(buf)); + +end: + if (to) { + gettimeofday(&after,NULL); + tv_sub(after,before); + tv_sub(*to,after); + if (to->tv_sec < 0) { + to->tv_sec = 0; + to->tv_usec = 0; + } + } + + if (ret == 0) { + *token = t; + *token_length = tl; + } else + free(t); + + return ret; +} + +int +edg_wll_gss_acquire_cred_gsi(char *proxy_file, gss_cred_id_t *cred, + char **name, edg_wll_GssStatus* gss_code) +{ + OM_uint32 major_status, minor_status, minor_status2; + gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL; + gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; + gss_name_t gss_name = GSS_C_NO_NAME; + OM_uint32 lifetime; + + if (proxy_file == NULL) { + major_status = gss_acquire_cred(&minor_status, GSS_C_NO_NAME, 0, + GSS_C_NO_OID_SET, GSS_C_BOTH, + &gss_cred, NULL, NULL); + if (GSS_ERROR(major_status)) + goto end; + } else { + asprintf((char**)&buffer.value, "X509_USER_PROXY=%s", proxy_file); + if (buffer.value == NULL) { + errno = ENOMEM; + return EDG_WLL_GSS_ERROR_ERRNO; + } + buffer.length = strlen(proxy_file); + + major_status = gss_import_cred(&minor_status, &gss_cred, GSS_C_NO_OID, 1, + &buffer, 0, NULL); + free(buffer.value); + if (GSS_ERROR(major_status)) + goto end; + } + + /* gss_import_cred() doesn't check validity of credential loaded, so let's + * verify it now */ + major_status = gss_inquire_cred(&minor_status, gss_cred, &gss_name, + &lifetime, NULL, NULL); + if (GSS_ERROR(major_status)) + goto end; + + if (lifetime == 0) { + major_status = GSS_S_CREDENTIALS_EXPIRED; + minor_status = 0; /* XXX */ + goto end; + } + + if (name) { + major_status = gss_display_name(&minor_status, gss_name, &buffer, NULL); + if (GSS_ERROR(major_status)) + goto end; + *name = buffer.value; + memset(&buffer, 0, sizeof(buffer)); + } + + *cred = gss_cred; + gss_cred = GSS_C_NO_CREDENTIAL; + +end: + if (gss_name != GSS_C_NO_NAME) + gss_release_name(&minor_status2, &gss_name); + + if (gss_cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&minor_status2, &gss_cred); + + if (GSS_ERROR(major_status)) { + if (gss_code) { + gss_code->major_status = major_status; + gss_code->minor_status = minor_status; + } + return EDG_WLL_GSS_ERROR_GSS; + } + + return 0; +} + +int +edg_wll_gss_connect(gss_cred_id_t cred, char const *hostname, int port, + struct timeval *timeout, edg_wll_GssConnection *connection, + edg_wll_GssStatus* gss_code) +{ + int sock, ret; + OM_uint32 maj_stat, min_stat, min_stat2, req_flags; + int context_established = 0; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + gss_name_t server = GSS_C_NO_NAME; + gss_ctx_id_t context = GSS_C_NO_CONTEXT; + char *servername = NULL; + + maj_stat = min_stat = min_stat2 = req_flags = 0; + + /* GSI specific */ + req_flags = GSS_C_GLOBUS_SSL_COMPATIBLE; + + ret = do_connect(&sock, hostname, port, timeout); + if (ret) + return ret; + + /* XXX find appropriate fqdn */ + asprintf (&servername, "host@%s", hostname); + if (servername == NULL) { + errno = ENOMEM; + ret = EDG_WLL_GSS_ERROR_ERRNO; + goto end; + } + input_token.value = servername; + input_token.length = strlen(servername) + 1; + + maj_stat = gss_import_name(&min_stat, &input_token, + GSS_C_NT_HOSTBASED_SERVICE, &server); + if (GSS_ERROR(maj_stat)) { + ret = EDG_WLL_GSS_ERROR_GSS; + goto end; + } + + free(servername); + memset(&input_token, 0, sizeof(input_token)); + + /* XXX if cred == GSS_C_NO_CREDENTIAL set the ANONYMOUS flag */ + + /* XXX prepsat na do {} while (maj_stat == CONT) a osetrit chyby*/ + while (!context_established) { + /* XXX verify ret_flags match what was requested */ + maj_stat = gss_init_sec_context(&min_stat, cred, &context, + GSS_C_NO_NAME, GSS_C_NO_OID, + req_flags | GSS_C_MUTUAL_FLAG, + 0, GSS_C_NO_CHANNEL_BINDINGS, + &input_token, NULL, &output_token, + NULL, NULL); + if (input_token.length > 0) { + free(input_token.value); + input_token.length = 0; + } + + if (output_token.length != 0) { + ret = send_token(sock, output_token.value, output_token.length, timeout); + gss_release_buffer(&min_stat2, &output_token); + if (ret) + goto end; + } + + if (GSS_ERROR(maj_stat)) { + if (context != GSS_C_NO_CONTEXT) { + /* XXX send closing token to the friend */ + gss_delete_sec_context(&min_stat2, &context, GSS_C_NO_BUFFER); + context = GSS_C_NO_CONTEXT; + } + ret = EDG_WLL_GSS_ERROR_GSS; + goto end; + } + + if(maj_stat & GSS_S_CONTINUE_NEEDED) { + ret = recv_token(sock, &input_token.value, &input_token.length, timeout); + if (ret) + goto end; + } else + context_established = 1; + } + + /* XXX check ret_flags matches to what was requested */ + + memset(connection, 0, sizeof(*connection)); + connection->sock = sock; + connection->context = context; + servername = NULL; + ret = 0; + +end: + if (ret == EDG_WLL_GSS_ERROR_GSS && gss_code) { + gss_code->major_status = maj_stat; + gss_code->minor_status = min_stat; + } + if (server != GSS_C_NO_NAME) + gss_release_name(&min_stat2, &server); + if (servername == NULL) + free(servername); + if (ret) + close(sock); + + return ret; +} + +int +edg_wll_gss_accept(gss_cred_id_t cred, int sock, struct timeval *timeout, + edg_wll_GssConnection *connection, edg_wll_GssStatus* gss_code) +{ + OM_uint32 maj_stat, min_stat, min_stat2; + OM_uint32 ret_flags = 0; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + gss_name_t client_name = GSS_C_NO_NAME; + gss_ctx_id_t context = GSS_C_NO_CONTEXT; + int ret; + + maj_stat = min_stat = min_stat2 = 0; + + /* GSI specific */ + ret_flags = GSS_C_GLOBUS_SSL_COMPATIBLE; + + do { + ret = recv_token(sock, &input_token.value, &input_token.length, timeout); + if (ret) + goto end; + + maj_stat = gss_accept_sec_context(&min_stat, &context, + cred, &input_token, + GSS_C_NO_CHANNEL_BINDINGS, + &client_name, NULL, &output_token, + &ret_flags, NULL, NULL); + if (input_token.length > 0) { + free(input_token.value); + input_token.length = 0; + } + + if (output_token.length) { + ret = send_token(sock, output_token.value, output_token.length, timeout); + gss_release_buffer(&min_stat2, &output_token); + if (ret) + goto end; + } + } while(maj_stat & GSS_S_CONTINUE_NEEDED); + + if (GSS_ERROR(maj_stat)) { + if (context != GSS_C_NO_CONTEXT) { + /* XXX send closing token to the friend */ + gss_delete_sec_context(&min_stat2, &context, GSS_C_NO_BUFFER); + context = GSS_C_NO_CONTEXT; + } + ret = EDG_WLL_GSS_ERROR_GSS; + goto end; + } + + maj_stat = gss_display_name(&min_stat, client_name, &output_token, NULL); + if (GSS_ERROR(maj_stat)) { + /* XXX close context ??? */ + ret = EDG_WLL_GSS_ERROR_GSS; + goto end; + } + + memset(connection, 0, sizeof(*connection)); + connection->sock = sock; + connection->context = context; + memset(&output_token, 0, sizeof(output_token.value)); + ret = 0; + +end: + if (ret == EDG_WLL_GSS_ERROR_GSS && gss_code) { + gss_code->major_status = maj_stat; + gss_code->minor_status = min_stat; + } + if (client_name != GSS_C_NO_NAME) + gss_release_name(&min_stat2, &client_name); + + return ret; +} + +int +edg_wll_gss_write(edg_wll_GssConnection *connection, const void *buf, size_t bufsize, + struct timeval *timeout, edg_wll_GssStatus* gss_code) +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token; + gss_buffer_desc output_token; + int ret; + + input_token.value = (void*)buf; + input_token.length = bufsize; + + maj_stat = gss_wrap (&min_stat, connection->context, 0, GSS_C_QOP_DEFAULT, + &input_token, NULL, &output_token); + if (GSS_ERROR(maj_stat)) { + if (gss_code) { + gss_code->minor_status = min_stat; + gss_code->major_status = maj_stat; + } + + return EDG_WLL_GSS_ERROR_GSS; + } + + ret = send_token(connection->sock, output_token.value, output_token.length, + timeout); + gss_release_buffer(&min_stat, &output_token); + + return ret; +} + + +int +edg_wll_gss_read(edg_wll_GssConnection *connection, void *buf, size_t bufsize, + struct timeval *timeout, edg_wll_GssStatus* gss_code) +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token; + gss_buffer_desc output_token; + int ret, i; + + if (connection->bufsize > 0) { + size_t len; + + len = (connection->bufsize < bufsize) ? connection->bufsize : bufsize; + memcpy(buf, connection->buffer, len); + connection->bufsize -= len; + if (connection->bufsize > 0) { + for (i = 0; i < sizeof(connection->buffer) - len; i++) + connection->buffer[i] = connection->buffer[i+len]; + } + + return len; + } + + do { + ret = recv_token(connection->sock, &input_token.value, &input_token.length, + timeout); + if (ret) + /* XXX cleanup */ + return ret; + + maj_stat = gss_unwrap(&min_stat, connection->context, &input_token, + &output_token, NULL, NULL); + if (GSS_ERROR(maj_stat)) { + /* XXX cleanup */ + return EDG_WLL_GSS_ERROR_GSS; + } + } while (maj_stat == 0 && output_token.length == 0 && output_token.value == NULL); + + if (output_token.length > bufsize) { + if (output_token.length - bufsize > sizeof(connection->buffer)) + return EINVAL; + connection->bufsize = output_token.length - bufsize; + memcpy(connection->buffer, output_token.value + bufsize, connection->bufsize); + output_token.length = bufsize; + } + memcpy(buf, output_token.value, output_token.length); + + return output_token.length; +} + +int +edg_wll_gss_read_full(edg_wll_GssConnection *connection, void *buf, + size_t bufsize, struct timeval *timeout, size_t *total, + edg_wll_GssStatus* gss_code) +{ + int len,i; + *total = 0; + + if (connection->bufsize > 0) { + size_t len; + + + len = (connection->bufsize < bufsize) ? connection->bufsize : bufsize; + memcpy(buf, connection->buffer, len); + connection->bufsize -= len; + if (connection->bufsize > 0) { + for (i = 0; i < sizeof(connection->buffer) - len; i++) + connection->buffer[i] = connection->buffer[i+len]; + } + *total = len; + } + + while (*total < bufsize) { + len = edg_wll_gss_read(connection, buf+*total, bufsize-*total, + timeout, gss_code); + if (len < 0) return len; + *total += len; + } + + return 0; +} + +int +edg_wll_gss_write_full(edg_wll_GssConnection *connection, const void *buf, + size_t bufsize, struct timeval *timeout, size_t *total, + edg_wll_GssStatus* gss_code) +{ + return edg_wll_gss_write(connection, buf, bufsize, timeout, gss_code); +} + +/* XXX: I'm afraid the contents of stuct stat is somewhat OS dependent */ +int +edg_wll_gss_watch_creds(const char *proxy_file, time_t *proxy_mtime) +{ + struct stat pstat; + int reload = 0; + + if (!proxy_file) return 0; + if (stat(proxy_file,&pstat)) return -1; + + if (!*proxy_mtime) *proxy_mtime = pstat.st_mtime; + + if (*proxy_mtime != pstat.st_mtime) { + *proxy_mtime = pstat.st_mtime; + reload = 1; + } + + return reload; +} + +int +edg_wll_gss_close(edg_wll_GssConnection *con, struct timeval *timeout) +{ + OM_uint32 min_stat; + + /* XXX if timeout is NULL use value of 120 secs */ + + if (con->context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&min_stat, &con->context, GSS_C_NO_BUFFER); + /* XXX send the buffer (if any) to the peer. GSSAPI specs doesn't + * recommend sending it, though */ + + /* XXX can socket be open even if context == GSS_C_NO_CONTEXT) ? */ + /* XXX ensure that edg_wll_GssConnection is created with sock set to -1 */ + if (con->sock >= 0) + close(con->sock); + } + memset(con, 0, sizeof(*con)); + con->context = GSS_C_NO_CONTEXT; + con->sock = -1; + return 0; +} + +int +edg_wll_gss_get_error(edg_wll_GssStatus *gss_err, const char *prefix, char **msg) +{ + OM_uint32 maj_stat, min_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc maj_status_string = GSS_C_EMPTY_BUFFER; + gss_buffer_desc min_status_string = GSS_C_EMPTY_BUFFER; + char *str = NULL; + char *line, *tmp; + + str = strdup(prefix); + do { + maj_stat = gss_display_status(&min_stat, gss_err->major_status, + GSS_C_GSS_CODE, GSS_C_NO_OID, + &msg_ctx, &maj_status_string); + if (GSS_ERROR(maj_stat)) + break; + + maj_stat = gss_display_status(&min_stat, gss_err->minor_status, + GSS_C_MECH_CODE, GSS_C_NULL_OID, + &msg_ctx, &min_status_string); + if (GSS_ERROR(maj_stat)) { + gss_release_buffer(&min_stat, &maj_status_string); + break; + } + + asprintf(&line, ": %s (%s)", (char *)maj_status_string.value, + (char *)min_status_string.value); + gss_release_buffer(&min_stat, &maj_status_string); + gss_release_buffer(&min_stat, &min_status_string); + + tmp = realloc(str, strlen(str) + strlen(line) + 1); + if (tmp == NULL) { + /* abort() ? */ + free(line); + free(str); + str = "WARNING: Not enough memory to produce error message"; + break; + } + str = tmp; + strcat(str, line); + free(line); + } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); + + *msg = str; + return 0; +} + +int +edg_wll_gss_oid_equal(const gss_OID a, const gss_OID b) +{ + if (a == b) + return 1; + else { + if (a == GSS_C_NO_OID || b == GSS_C_NO_OID || a->length != b->length) + return 0; + else + return (memcmp(a->elements, b->elements, a->length) == 0); + } +} + +int +edg_wll_gss_reject(int sock) +{ + /* XXX is it possible to cut & paste edg_wll_ssl_reject() ? */ + return 0; +} -- 1.8.2.3