Support for multiple GSSAPI mechanisms
authorDaniel Kouřil <kouril@ics.muni.cz>
Tue, 29 Nov 2011 14:01:38 +0000 (14:01 +0000)
committerDaniel Kouřil <kouril@ics.muni.cz>
Tue, 29 Nov 2011 14:01:38 +0000 (14:01 +0000)
- added example client and server

org.glite.lbjp-common.gss/Makefile
org.glite.lbjp-common.gss/examples/example_common.c [new file with mode: 0644]
org.glite.lbjp-common.gss/examples/example_locl.h [new file with mode: 0644]
org.glite.lbjp-common.gss/examples/gss_client.c [new file with mode: 0644]
org.glite.lbjp-common.gss/examples/gss_server.c [new file with mode: 0644]
org.glite.lbjp-common.gss/interface/glite_gss.h
org.glite.lbjp-common.gss/src/glite_gss.c

index 87fce4d..8d26b4c 100644 (file)
@@ -34,7 +34,7 @@ offset=6
 version_info:=-version-info ${shell \
        perl -e '$$,=":"; @F=split "\\.","${version}"; print $$F[0]+$$F[1]+${offset},$$F[2],$$F[1]' }
 
-VPATH=${top_srcdir}/src:${top_srcdir}/test
+VPATH=${top_srcdir}/src:${top_srcdir}/test:${top_srcdir}/examples
 
 default: all
 
@@ -138,6 +138,12 @@ test_coverage:
        cd coverage && $(MAKE) -f ../Makefile top_srcdir=../../ COVERAGE_FLAGS="-fprofile-arcs -ftest-coverage" check
        cd coverage && for i in ${OBJS}; do gcov -o .libs/ $$i ; done
 
+gss_server: gss_server.o example_common.o
+       ${LINK} -o $@ ${default_gss} $^ -lefence
+       
+gss_client: gss_client.o example_common.o
+       ${LINK} -o $@ ${default_gss} $^ -lefence
+
 examples:
 
 doc:
@@ -157,7 +163,7 @@ install:
        fi
 
 clean:
-       rm -rf *.o *.lo *.a *.la .libs test_gss
+       rm -rf *.o *.lo *.a *.la .libs test_gss gss_server gss_client
        rm -rvf log.xml rpmbuild/ RPMS/ tgz/ debian/
 
 %.o: %.c
diff --git a/org.glite.lbjp-common.gss/examples/example_common.c b/org.glite.lbjp-common.gss/examples/example_common.c
new file mode 100644 (file)
index 0000000..602d9af
--- /dev/null
@@ -0,0 +1,35 @@
+#include "example_locl.h"
+
+void
+print_gss_err(int ret, edg_wll_GssStatus *gss_ret, char *msg)
+{
+    const char *err;
+
+    if (ret == EDG_WLL_GSS_ERROR_GSS) {
+       char *gss_err;
+       edg_wll_gss_get_error(gss_ret, msg, &gss_err);
+       fprintf(stderr, "%s\n", gss_err);
+       free(gss_err);
+       return;
+    }
+
+    switch (ret) {
+       case EDG_WLL_GSS_ERROR_ERRNO:
+           err = strerror(errno);
+           break;
+       case EDG_WLL_GSS_ERROR_HERRNO:
+           err = hstrerror(errno);
+           break;
+       case EDG_WLL_GSS_ERROR_EOF:
+           err = "Peer has closed the connection";
+           break;
+       case EDG_WLL_GSS_ERROR_TIMEOUT:
+           err = "Connection timed out";
+           break;
+       default:
+           err = "Unknown error";
+           break;
+    }
+    fprintf(stderr, "%s: %s\n", msg, err);
+    return;
+}
diff --git a/org.glite.lbjp-common.gss/examples/example_locl.h b/org.glite.lbjp-common.gss/examples/example_locl.h
new file mode 100644 (file)
index 0000000..f24990d
--- /dev/null
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <glite_gss.h>
+
+void
+print_gss_err(int ret, edg_wll_GssStatus *gss_ret, char *msg);
diff --git a/org.glite.lbjp-common.gss/examples/gss_client.c b/org.glite.lbjp-common.gss/examples/gss_client.c
new file mode 100644 (file)
index 0000000..42c00ce
--- /dev/null
@@ -0,0 +1,53 @@
+#include "example_locl.h"
+
+int
+main(int argc, char *argv[])
+{
+    edg_wll_GssCred cred = NULL;
+    edg_wll_GssConnection conn;
+    edg_wll_GssStatus gss_ret;
+    struct timeval tv = {60, 0};
+    const char *hostname;
+    const char *msg = "Hello";
+    int port = 1234;
+    int ret;
+
+    if (argc < 2) {
+       fprintf(stderr, "Usage: %s <hostname>\n", argv[0]);
+       return 1;
+    }
+
+    hostname = argv[1];
+
+    ret = edg_wll_gss_initialize();
+    if (ret) {
+       fprintf(stderr, "Failed to initialize\n");
+       return 1;
+    }
+
+#if 0
+    ret = edg_wll_gss_acquire_cred_gsi(NULL, NULL, &cred, &gss_ret);
+    if (ret) {
+       print_gss_err(ret, &gss_ret, "Failed to load credentials");
+       return 1;
+    }
+#endif
+
+    ret = edg_wll_gss_connect(cred, hostname, port,
+                       //        NULL, GSS_C_NO_OID_SET,
+                                 &tv, &conn, &gss_ret);
+    if (ret) {
+       print_gss_err(ret, &gss_ret, "Failed to connect to server");
+       return 1;
+    }
+
+    ret = edg_wll_gss_write(&conn, msg, strlen(msg)+1, &tv, &gss_ret);
+    if (ret) {
+       print_gss_err(ret, &gss_ret, "Failed to send message");
+       return 1;
+    }
+
+    edg_wll_gss_close(&conn, &tv);
+
+    return 0;
+}
diff --git a/org.glite.lbjp-common.gss/examples/gss_server.c b/org.glite.lbjp-common.gss/examples/gss_server.c
new file mode 100644 (file)
index 0000000..960f00b
--- /dev/null
@@ -0,0 +1,99 @@
+#include "example_locl.h"
+
+int
+main(int argc, char *argv[])
+{
+    edg_wll_GssCred cred = NULL;
+    edg_wll_GssConnection conn;
+    edg_wll_GssStatus gss_ret;
+    int ret;
+    char buf[1024];
+    struct addrinfo hints;
+    struct addrinfo *ai;
+    int sock;
+
+    ret = edg_wll_gss_initialize();
+    if (ret) {
+       fprintf(stderr, "Failed to initialize\n");
+       return 1;
+    }
+
+    memset (&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE | AI_ADDRCONFIG;
+    hints.ai_socktype = SOCK_STREAM;
+
+    ret = getaddrinfo (NULL, "1234", &hints, &ai);
+    if (ret) {
+       fprintf(stderr, "getaddrinfo() failed: %s\n",
+               gai_strerror(ret));
+       return 1;
+    }
+
+    sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (sock < 0) {
+       perror("Can't create socket");
+       return 1;
+    }
+
+    ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
+    if (ret) {
+       perror("Can't bind socket");
+       return 1;
+    }
+
+    ret = listen(sock, 10);
+    if (ret) {
+       perror("Can't bind socket");
+       return 1;
+    }
+
+    ret = edg_wll_gss_acquire_cred_gsi(NULL, NULL, &cred, &gss_ret);
+    if (ret) {
+       print_gss_err(ret, &gss_ret, "Failed to load credentials");
+       return 1;
+    }
+
+    while (1) {
+       int client;
+       struct sockaddr_storage client_addr;
+       socklen_t client_addr_len;
+       edg_wll_GssPrincipal princ;
+       
+       client_addr_len = sizeof(client_addr);
+       memset(&client_addr, 0, client_addr_len);
+       client = accept(sock, (struct sockaddr *) &client_addr,
+                       &client_addr_len);
+       if (client < 0) {
+           perror("Failed to accept incomming connection\n");
+           continue;
+       }
+
+       ret = edg_wll_gss_accept(cred, client, NULL, &conn, &gss_ret);
+       if (ret) {
+           print_gss_err(ret, &gss_ret, "Failed to accept client");
+           edg_wll_gss_close(&conn, NULL);
+           continue;
+       }
+
+       ret = edg_wll_gss_get_client_conn(&conn, &princ, &gss_ret);
+       if (ret) {
+           print_gss_err(ret, &gss_ret, "Failed to get client's name");
+           edg_wll_gss_close(&conn, NULL);
+           continue;
+       }
+
+       printf("Connection from %s\n", princ->name);
+
+       ret = edg_wll_gss_read(&conn, buf, sizeof(buf), NULL, &gss_ret);
+       edg_wll_gss_close(&conn, NULL);
+       if (ret < 0) {
+           print_gss_err(ret, &gss_ret, "Failed to read message");
+
+           continue;
+       }
+
+       printf("Client sent: %s\n", buf);
+    }
+
+    return 0;
+}
index 162069f..78e0562 100644 (file)
@@ -26,6 +26,7 @@ extern "C" {
 
 #include <sys/time.h>
 #include <stdlib.h>
+#include <gssapi.h>
 
 enum {
   EDG_WLL_GSS_OK               =  0,  /* no GSS errors */
@@ -50,6 +51,7 @@ typedef struct _edg_wll_GssConnection {
   int sock;
   char *buffer;
   size_t bufsize;
+  gss_OID authn_mech;
 } edg_wll_GssConnection;
 
 typedef struct _edg_wll_GssStatus {
@@ -61,10 +63,7 @@ typedef struct _edg_wll_GssPrincipal_data {
    char *name;
    unsigned int flags;
    char **fqans;
-#if 0
-   char **voms_groups; /* needed for legacy LB server authZ mechanism */
-   edg_wll_GssOid authn_mech;
-#endif
+   gss_OID authn_mech;
 } edg_wll_GssPrincipal_data;
 typedef struct _edg_wll_GssPrincipal_data *edg_wll_GssPrincipal;
 
@@ -100,6 +99,16 @@ edg_wll_gss_connect(edg_wll_GssCred cred,
                    edg_wll_GssStatus* gss_code);
 
 int
+edg_wll_gss_connect_ext(edg_wll_GssCred cred,
+                       char const *hostname,
+                       int port,
+                       const char *service,
+                       gss_OID_set mechs,
+                       struct timeval *timeout,
+                       edg_wll_GssConnection *connection,
+                       edg_wll_GssStatus* gss_code);
+
+int
 edg_wll_gss_accept(edg_wll_GssCred cred,
                   int sock,
                   struct timeval *timeout,
@@ -137,6 +146,10 @@ edg_wll_gss_write_full(edg_wll_GssConnection *connection,
                       edg_wll_GssStatus* gss_code);
 
 int
+edg_wll_gss_watch_creds_gsi(const char * proxy_file,
+                           time_t * proxy_mtime);
+
+int
 edg_wll_gss_watch_creds(const char * proxy_file,
                        time_t * proxy_mtime);
 
index 7fada9d..30281be 100644 (file)
@@ -35,6 +35,7 @@ limitations under the License.
 #include <errno.h>
 
 #include <globus_common.h>
+
 #include <gssapi.h>
 #include <openssl/err.h>
 #include <openssl/ssl.h>
@@ -45,6 +46,10 @@ limitations under the License.
 
 #include "glite_gss.h"
 
+#ifndef GSS_C_GLOBUS_SSL_COMPATIBLE
+#define GSS_C_GLOBUS_SSL_COMPATIBLE    16384
+#endif
+
 #define tv_sub(a,b) {\
        (a).tv_usec -= (b).tv_usec;\
        (a).tv_sec -= (b).tv_sec;\
@@ -61,9 +66,33 @@ struct asyn_result {
 
 static int globus_common_activated = 0;
 
-static void free_hostent(struct hostent *h);
-static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *addr,  int addrtype, int port, struct timeval *timeout, edg_wll_GssConnection *connection,
-                                edg_wll_GssStatus* gss_code);
+typedef struct gssapi_mech {
+    const char* name;
+    gss_OID_desc oid;
+} gssapi_mech;
+
+static
+struct gssapi_mech gssapi_mechs[] = {
+    { "Kerberos", {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"} },
+    { "GSI", {9, "\x2b\x06\x01\x04\x01\x9b\x50\x01\x01"} },
+    { "EAP", {9, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01"} },
+};
+
+static gss_OID
+get_oid(const char *mech);
+
+static void
+free_hostent(struct hostent *h);
+
+static int
+try_conn_and_auth(edg_wll_GssCred cred, char const *hostname,
+                 const char *service, gss_OID mech,
+                 char *addr,  int addrtype, int port, struct timeval *timeout,
+                 edg_wll_GssConnection *connection, edg_wll_GssStatus* gss_code);
+
+static int
+edg_wll_gss_oid_equal(const gss_OID a, const gss_OID b);
+
 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);
@@ -309,6 +338,18 @@ do_connect(int *s, char *addr, int addrtype, int port, struct timeval *timeout)
    return 0;
 }
 
+static gss_OID
+get_oid(const char *mech)
+{
+    unsigned int i;
+
+    for (i = 0; i < sizeof(gssapi_mechs)/sizeof(gssapi_mechs[0]); i++)
+       if (strcasecmp(mech, gssapi_mechs[i].name) == 0)
+           return &gssapi_mechs[i].oid;
+
+    return GSS_C_NO_OID;
+}
+
 static int
 send_token(int sock, void *token, size_t token_length, struct timeval *to)
 {
@@ -365,13 +406,26 @@ end:
 }
 
 static int
-recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
+send_gss_token(int sock, gss_OID mech, void *token, size_t token_length, struct timeval *to)
+{
+    int ret;
+    uint32_t net_len = htonl(token_length);
+
+    /* Don't use the usual message framing when using Globus GSI, in order to be
+       compatible with SSL on the wire. */
+    if (!edg_wll_gss_oid_equal(mech, get_oid("GSI"))) {
+       ret = send_token(sock, &net_len, 4, to);
+       if (ret)
+           return ret;
+    }
+
+    return send_token(sock, token, token_length, to);
+}
+
+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;
@@ -381,7 +435,6 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
       gettimeofday(&before,NULL);
    }
 
-   ret = 0;
    do {
       FD_ZERO(&fds);
       FD_SET(sock,&fds);
@@ -396,7 +449,7 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
            break;
       }
       
-      count = read(sock, buf, sizeof(buf));
+      count = read(sock, token, token_length);
       if (count < 0) {
         if (errno == EINTR)
            continue;
@@ -407,23 +460,14 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
       }
 
       if (count==0) {
-         if (tl==0) {
-            free(t);
-            return EDG_WLL_GSS_ERROR_EOF;
-         } else goto end;
-      }
-      tmp=realloc(t, tl + count);
-      if (tmp == NULL) {
-       errno = ENOMEM;
-       free(t);
-       return EDG_WLL_GSS_ERROR_ERRNO;
+         ret = EDG_WLL_GSS_ERROR_EOF;
+         goto end;
       }
-      t = tmp;
-      memcpy(t + tl, buf, count);
-      tl += count;
 
    } while (count < 0); /* restart on EINTR */
 
+   ret = count;
+
 end:
    if (to) {
       gettimeofday(&after,NULL);
@@ -435,15 +479,110 @@ end:
       }
    }
 
-   if (ret == 0) {
-      *token = t;
-      *token_length = tl;
-   } else
-      free(t);
-
    return ret;
 }
 
+/* similar to recv_token() but never returns partial data */
+static int
+read_token(int sock, void *token, size_t length, struct timeval *to)
+{
+    size_t remain = length;
+    char *buf = token;
+    int count;
+
+    while (remain > 0) {
+       count = recv_token(sock, buf, remain, to);
+       if (count < 0)
+           return count;
+       buf += count;
+       remain -= count;
+    }
+
+    return length;
+}
+
+/*
+   SSL/TLS framing:
+   1st byte: SSL Handshake Record Type (\x16)
+   2nd-3rd bytes: SSL Version 3.0 (\x03 \x00)
+                  TLS Version 1.0 (\x03 \x01)
+   4th-5th bytes: Length
+*/
+#define SSLv3_HEADER "\x16\x03\x00"
+#define TLSv1_HEADER "\x16\x03\x01"
+
+static int
+is_ssl(unsigned char *header)
+{
+    if (memcmp(header, SSLv3_HEADER, 3) == 0)
+       return 1;
+    if (memcmp(header, TLSv1_HEADER, 3) == 0)
+       return 1;
+
+    return 0;
+    /* payload len == (size_t)(header[3]) << 8) | header[4]) + 5; */
+}
+
+
+static int
+recv_gss_token(int sock, gss_OID mech, void **token, size_t *token_length, struct timeval *to)
+{
+    int ret;
+    uint32_t len, net_len;
+    unsigned char *header;
+    char buf[4096];
+    char *b = NULL;
+
+    if (edg_wll_gss_oid_equal(mech, get_oid("GSI"))) {
+       ret = recv_token(sock, buf, sizeof(buf), to);
+       if (ret < 0)
+           return ret;
+
+       *token = malloc(ret);
+       if (*token == NULL)
+           return EDG_WLL_GSS_ERROR_ERRNO;
+
+       memcpy(*token, buf, ret);
+       *token_length = ret;
+
+       return 0;
+    }
+
+    ret = read_token(sock, &net_len, 4, to);
+    if (ret < 0)
+       return ret;
+
+    header = (unsigned char *)&net_len;
+    if (mech == GSS_C_NO_OID && is_ssl(header)) {
+       /* SSL detected. Let's return this fragment to the caller relying on
+          the Globus libs being able to cope with partial messages. */
+       *token = malloc(4);
+       if (*token == NULL)
+           return EDG_WLL_GSS_ERROR_ERRNO;
+
+       memcpy(*token, header, 4);
+       *token_length = 4;
+
+       return 0;
+    }
+
+    len =  ntohl(net_len);
+    b = malloc(len);
+    if (b == NULL)
+       return EDG_WLL_GSS_ERROR_ERRNO;
+
+    ret = read_token(sock, b, len, to);
+    if (ret < 0) {
+       free(b);
+       return ret;
+    }
+
+    *token = b;
+    *token_length = len;
+
+    return 0;
+}
+
 static int
 create_proxy(const char *cert_file, const char *key_file, char **proxy_file)
 {
@@ -524,6 +663,7 @@ destroy_proxy(char *proxy_file)
 }
 
 /** Load or reload credentials. It should be called regularly (credential files can be changed).
+  This call works only for GSSAPI from Globus.
  @see edg_wll_gss_watch_creds
  */
 int
@@ -534,6 +674,7 @@ edg_wll_gss_acquire_cred_gsi(const char *cert_file, const char *key_file, edg_wl
    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;
+   gss_OID_set_desc mechs;
    OM_uint32 lifetime;
    char *proxy_file = NULL;
    char *name = NULL;
@@ -544,14 +685,18 @@ edg_wll_gss_acquire_cred_gsi(const char *cert_file, const char *key_file, edg_wl
       return EINVAL;
 
    if (cert_file == NULL) {
+      mechs.count = 1;
+      mechs.elements = get_oid("GSI");
+      
       major_status = gss_acquire_cred(&minor_status, GSS_C_NO_NAME, 0,
-                                     GSS_C_NO_OID_SET, GSS_C_BOTH,
+                                     &mechs, GSS_C_BOTH,
                                      &gss_cred, NULL, NULL);
       if (GSS_ERROR(major_status)) {
         ret = EDG_WLL_GSS_ERROR_GSS;
         goto end;
       }
    } else {
+#ifndef NO_GLOBUS_GSSAPI
       proxy_file = (char *)cert_file;
       if (strcmp(cert_file, key_file) != 0 &&
          (ret = create_proxy(cert_file, key_file, &proxy_file))) {
@@ -567,13 +712,19 @@ edg_wll_gss_acquire_cred_gsi(const char *cert_file, const char *key_file, edg_wl
       }
       buffer.length = strlen(proxy_file);
 
-      major_status = gss_import_cred(&minor_status, &gss_cred, GSS_C_NO_OID, 1,
+      major_status = gss_import_cred(&minor_status, &gss_cred,
+                                    get_oid("GSI"), 1,
                                     &buffer, 0, NULL);
       free(buffer.value);
       if (GSS_ERROR(major_status)) {
         ret = EDG_WLL_GSS_ERROR_GSS;
         goto end;
       }
+#else
+      errno = EINVAL;
+      ret = EDG_WLL_GSS_ERROR_ERRNO;
+      goto end;
+#endif
    }
 
   /* gss_import_cred() doesn't check validity of credential loaded, so let's
@@ -651,17 +802,19 @@ end:
 
 /** Create a socket and initiate secured connection. */
 int 
-edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
-                   struct timeval *timeout, edg_wll_GssConnection *connection,
-                   edg_wll_GssStatus* gss_code)
+edg_wll_gss_connect_ext(edg_wll_GssCred cred, char const *hostname, int port,
+                       const char *service, gss_OID_set mechs, 
+                       struct timeval *timeout, edg_wll_GssConnection *connection,
+                       edg_wll_GssStatus* gss_code)
 {
    int ret;
    struct asyn_result ar;
    int h_errno;
    int addr_types[] = {AF_INET6, AF_INET};
    int ipver = AF_INET6; //def value; try IPv6 first
-   unsigned int j;
+   unsigned int j,k;
    int i;
+   gss_OID mech;
 
    memset(connection, 0, sizeof(*connection));
    for (j = 0; j< sizeof(addr_types)/sizeof(*addr_types); j++) {
@@ -689,13 +842,23 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
        i = 0;
        while (ar.ent->h_addr_list[i])
        {
-               ret = try_conn_and_auth (cred, hostname, ar.ent->h_addr_list[i], 
+           k = 0;
+           do {
+               if (mechs == GSS_C_NO_OID_SET || mechs->count == 0)
+                   mech = GSS_C_NO_OID;
+               else
+                   mech = &mechs->elements[k];
+
+               ret = try_conn_and_auth (cred, hostname, service, mech,
+                                       ar.ent->h_addr_list[i], 
                                        ar.ent->h_addrtype, port, timeout, connection, gss_code);
                if (ret == 0)
                        goto end;
                if (timeout->tv_sec < 0 ||(timeout->tv_sec == 0 && timeout->tv_usec <= 0))
                        goto end;
-               i++;
+               k++;
+           } while (mechs != GSS_C_NO_OID_SET && k < mechs->count);
+           i++;
        }
        free_hostent(ar.ent);
        ar.ent = NULL;
@@ -709,32 +872,62 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
    return ret;
 }
 
+int
+edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
+                    struct timeval *timeout, edg_wll_GssConnection *connection,
+                    edg_wll_GssStatus* gss_code)
+{
+    gss_OID_set_desc mechs = {.count = 0, .elements = GSS_C_NO_OID};
+    gss_OID oid;
+    const char *mech;
+
+    mech = getenv("GLITE_GSS_MECH");
+    if (mech == NULL)
+       mech = "GSI";
+
+    oid = get_oid(mech);
+    if (oid != GSS_C_NO_OID) {
+       mechs.elements = oid;
+       mechs.count = 1;
+    }
+
+    return edg_wll_gss_connect_ext(cred, hostname, port,
+                                  NULL, &mechs,
+                                  timeout, connection, gss_code);
+}
+
 /* try connection and authentication for the given addr*/
-static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *addr,  int addrtype, int port, struct timeval *timeout, edg_wll_GssConnection *connection,
-                               edg_wll_GssStatus* gss_code) 
+static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname,
+                             const char *service, gss_OID mech,
+                             char *addr,  int addrtype, int port,
+                             struct timeval *timeout, edg_wll_GssConnection *connection,
+                             edg_wll_GssStatus* gss_code) 
 {
-       int sock;
-       int ret = 0;
+   int sock;
+   int ret = 0;
    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;
+   gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
    char *servername = NULL;
    int retry = _EXPIRED_ALERT_RETRY_COUNT;
 
    maj_stat = min_stat = min_stat2 = req_flags = 0;
 
-   /* GSI specific */
-   req_flags = GSS_C_GLOBUS_SSL_COMPATIBLE;
+   if (edg_wll_gss_oid_equal(mech, get_oid("GSI"))) {
+       req_flags = GSS_C_GLOBUS_SSL_COMPATIBLE;
+       setenv("GLOBUS_GSSAPI_NAME_COMPATIBILITY", "STRICT_RFC2818", 0);
+   }
 
    ret = do_connect(&sock, addr, addrtype, port, timeout);
    if (ret)
       return ret;
 
-   /* XXX find appropriate fqdn */
-   asprintf (&servername, "host@%s", hostname);
+   asprintf(&servername, "%s@%s",
+           (service) ? service : "host", hostname);
    if (servername == NULL) {
       errno = ENOMEM;
       ret = EDG_WLL_GSS_ERROR_ERRNO;
@@ -754,15 +947,15 @@ static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *
    servername = NULL;
    memset(&input_token, 0, sizeof(input_token));
 
-   /* XXX if cred == GSS_C_NO_CREDENTIAL set the ANONYMOUS flag */
+   if (cred && cred->gss_cred)
+       gss_cred = cred->gss_cred;
 
    do { /* XXX: the black magic above */
 
-   /* 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->gss_cred, &context,
-                                     GSS_C_NO_NAME, GSS_C_NO_OID,
+      maj_stat = gss_init_sec_context(&min_stat, gss_cred, &context,
+                                     server, mech,
                                      req_flags | GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG,
                                      0, GSS_C_NO_CHANNEL_BINDINGS,
                                      &input_token, NULL, &output_token,
@@ -772,8 +965,18 @@ static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *
         input_token.length = 0;
       }
 
+      if (mech == GSS_C_NO_OID) {
+         gss_OID oid;
+
+         /* is it safe to inquire a partly-establised context? */
+         maj_stat = gss_inquire_context(&min_stat, context, NULL, NULL,
+                                        NULL, &oid, NULL, NULL, NULL);
+         if (!GSS_ERROR(maj_stat))
+             mech = oid;
+      }
+
       if (output_token.length != 0) {
-        ret = send_token(sock, output_token.value, output_token.length, timeout);
+        ret = send_gss_token(sock, mech, output_token.value, output_token.length, timeout);
         gss_release_buffer(&min_stat2, &output_token);
         if (ret)
            goto end;
@@ -784,7 +987,7 @@ static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *
            gss_delete_sec_context(&min_stat2, &context, &output_token);
            context = GSS_C_NO_CONTEXT;
            if (output_token.length) {
-               send_token(sock, output_token.value, output_token.length, timeout);
+               send_gss_token(sock, mech, output_token.value, output_token.length, timeout);
                gss_release_buffer(&min_stat2, &output_token);
            }
         }
@@ -793,7 +996,7 @@ static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *
       }
 
       if(maj_stat & GSS_S_CONTINUE_NEEDED) {
-        ret = recv_token(sock, &input_token.value, &input_token.length, timeout);
+        ret = recv_gss_token(sock, mech, &input_token.value, &input_token.length, timeout);
         if (ret)
            goto end;
       } else
@@ -825,6 +1028,7 @@ static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *
 
    connection->sock = sock;
    connection->context = context;
+   connection->authn_mech = mech;
    ret = 0;
 
 end:
@@ -853,19 +1057,20 @@ edg_wll_gss_accept(edg_wll_GssCred cred, int sock, struct timeval *timeout,
    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;
+   gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
+   gss_OID mech = GSS_C_NO_OID;
    int ret;
 
    maj_stat = min_stat = min_stat2 = 0;
    memset(connection, 0, sizeof(*connection));
 
-   if (cred == NULL)
-       return EINVAL;
+   if (cred && cred->gss_cred)
+       gss_cred = cred->gss_cred;
 
-   /* GSI specific */
    ret_flags = GSS_C_GLOBUS_SSL_COMPATIBLE;
 
    do {
-      ret = recv_token(sock, &input_token.value, &input_token.length, timeout);
+      ret = recv_gss_token(sock, mech, &input_token.value, &input_token.length, timeout);
       if (ret)
         goto end;
 
@@ -873,9 +1078,9 @@ edg_wll_gss_accept(edg_wll_GssCred cred, int sock, struct timeval *timeout,
         gss_release_name(&min_stat2, &client_name);
 
       maj_stat = gss_accept_sec_context(&min_stat, &context,
-                                       cred->gss_cred, &input_token,
+                                       gss_cred, &input_token,
                                        GSS_C_NO_CHANNEL_BINDINGS,
-                                       &client_name, NULL, &output_token,
+                                       &client_name, &mech, &output_token,
                                        &ret_flags, NULL, NULL);
       if (input_token.length > 0) {
         free(input_token.value);
@@ -883,7 +1088,7 @@ edg_wll_gss_accept(edg_wll_GssCred cred, int sock, struct timeval *timeout,
       }
 
       if (output_token.length) {
-        ret = send_token(sock, output_token.value, output_token.length, timeout);
+        ret = send_gss_token(sock, mech, output_token.value, output_token.length, timeout);
         gss_release_buffer(&min_stat2, &output_token);
         if (ret)
            goto end;
@@ -895,7 +1100,7 @@ edg_wll_gss_accept(edg_wll_GssCred cred, int sock, struct timeval *timeout,
         gss_delete_sec_context(&min_stat2, &context, &output_token);
         context = GSS_C_NO_CONTEXT;
         if (output_token.length) {
-               send_token(sock, output_token.value, output_token.length, timeout);
+               send_gss_token(sock, mech, output_token.value, output_token.length, timeout);
                gss_release_buffer(&min_stat2, &output_token);
         }
       }
@@ -915,6 +1120,7 @@ edg_wll_gss_accept(edg_wll_GssCred cred, int sock, struct timeval *timeout,
 
    connection->sock = sock;
    connection->context = context;
+   connection->authn_mech = mech;
    ret = 0;
 
 end:
@@ -952,7 +1158,8 @@ edg_wll_gss_write(edg_wll_GssConnection *connection, const void *buf, size_t buf
       return EDG_WLL_GSS_ERROR_GSS;
    }
 
-   ret = send_token(connection->sock, output_token.value, output_token.length,
+   ret = send_gss_token(connection->sock, connection->authn_mech,
+                   output_token.value, output_token.length,
                    timeout);
    gss_release_buffer(&min_stat, &output_token);
 
@@ -987,12 +1194,15 @@ edg_wll_gss_read(edg_wll_GssConnection *connection, void *buf, size_t bufsize,
    }
 
    do {
-      ret = recv_token(connection->sock, &input_token.value, &input_token.length,
+      ret = recv_gss_token(connection->sock, connection->authn_mech,
+                      &input_token.value, &input_token.length,
                       timeout);
       if (ret)
         return ret;
 
+      /* work around a Globus bug */
       ERR_clear_error();
+
       maj_stat = gss_unwrap(&min_stat, connection->context, &input_token,
                            &output_token, NULL, NULL);
       gss_release_buffer(&min_stat2, &input_token);
@@ -1082,6 +1292,8 @@ edg_wll_gss_watch_creds(const char *proxy_file, time_t *last_time)
 
       now = time(NULL);
 
+      /* to work around a globus bug we enforce reloading credentials
+        quite often */
       if ( now >= (*last_time+GSS_CRED_WATCH_LIMIT) ) {
               *last_time = now;
               return 1;
@@ -1098,6 +1310,12 @@ edg_wll_gss_watch_creds(const char *proxy_file, time_t *last_time)
       return 0;
 }
 
+int
+edg_wll_gss_watch_creds_gsi(const char *proxy_file, time_t *last_time)
+{
+    return edg_wll_gss_watch_creds(proxy_file, last_time);
+}
+
 /** Close the connection. */
 int
 edg_wll_gss_close(edg_wll_GssConnection *con, struct timeval *timeout)
@@ -1131,6 +1349,7 @@ edg_wll_gss_close(edg_wll_GssConnection *con, struct timeval *timeout)
    memset(con, 0, sizeof(*con));
    con->context = GSS_C_NO_CONTEXT;
    con->sock = -1;
+   con->authn_mech = NULL;
    return 0;
 }
 
@@ -1212,10 +1431,12 @@ edg_wll_gss_initialize(void)
 {
    int ret = 0;
 
+#ifndef NO_GLOBUS_GSSAPI
    if (globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE) != GLOBUS_SUCCESS) {
       errno = EINVAL;
       ret = EDG_WLL_GSS_ERROR_ERRNO;
    }
+#endif
 
    if (globus_module_activate(GLOBUS_COMMON_MODULE) == GLOBUS_SUCCESS)
        globus_common_activated = 1;
@@ -1231,7 +1452,9 @@ edg_wll_gss_initialize(void)
 void
 edg_wll_gss_finalize(void)
 {
+#ifndef NO_GLOBUS_GSSAPI
    globus_module_deactivate(GLOBUS_GSI_GSSAPI_MODULE);
+#endif
    if (globus_common_activated) {
       globus_module_deactivate(GLOBUS_COMMON_MODULE);
       globus_common_activated = 0;
@@ -1345,6 +1568,9 @@ get_peer_cred(edg_wll_GssConnection *gss, const char *my_cert_file,
    X509 *p_cert;
    char *orig_cert = NULL, *orig_key = NULL;
 
+   if (!edg_wll_gss_oid_equal(gss->authn_mech, get_oid("GSI")))
+       return EINVAL;
+
    maj_stat = gss_export_sec_context(&min_stat, (gss_ctx_id_t *) &gss->context,
                                     &buffer);
    if (GSS_ERROR(maj_stat)) {