Merge in RC31_3 branch from gsoap-plugin.
authorZdeněk Salvet <salvet@ics.muni.cz>
Fri, 26 Oct 2007 08:13:02 +0000 (08:13 +0000)
committerZdeněk Salvet <salvet@ics.muni.cz>
Fri, 26 Oct 2007 08:13:02 +0000 (08:13 +0000)
org.glite.security.gss/src/glite_gss.c

index 81fae85..e28e70a 100644 (file)
@@ -320,12 +320,6 @@ end:
    return ret;
 }
 
-#define SSL_TOKEN_HEADER_LENGTH 5
-static size_t ssl_token_length(char *t, int tl) {
-       unsigned char *b = t;
-       return (((size_t)(b[3]) << 8) | b[4]) + 5;
-}
-
 static int
 recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
 {
@@ -334,7 +328,6 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
    char *t = NULL;
    char *tmp;
    size_t tl = 0;
-   size_t expect = 0;
    fd_set fds;
    struct timeval timeout,before,after;
    int ret;
@@ -345,7 +338,6 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
    }
 
    ret = 0;
-   expect = SSL_TOKEN_HEADER_LENGTH;
    do {
       FD_ZERO(&fds);
       FD_SET(sock,&fds);
@@ -360,7 +352,7 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
            break;
       }
       
-      count = read(sock, buf, MIN(expect - tl, sizeof(buf)));
+      count = read(sock, buf, sizeof(buf));
       if (count < 0) {
         if (errno == EINTR)
            continue;
@@ -384,12 +376,7 @@ recv_token(int sock, void **token, size_t *token_length, struct timeval *to)
       memcpy(t + tl, buf, count);
       tl += count;
 
-      if ((expect == SSL_TOKEN_HEADER_LENGTH) && 
-               (tl >= SSL_TOKEN_HEADER_LENGTH)) {
-        expect = ssl_token_length(t, tl);
-      }
-
-   } while (count != 0 && tl < expect);
+   } while (count < 0); /* restart on EINTR */
 
 end:
    if (to) {
@@ -605,6 +592,17 @@ end:
                                                                                      are in the bad state */
 #define _EXPIRED_ALERT_RETRY_DELAY 10   /* ms */
 
+/* XXX XXX This is black magic. "Sometimes" server refuses the client with SSL
+ *  * alert "certificate expired" even if it is not true. In this case the server
+ *   * slave terminates (which helps, usually), and we can reconnect transparently.
+ *    */
+
+/* This string appears in the error message in this case */
+#define _EXPIRED_ALERT_MESSAGE "function SSL3_READ_BYTES: sslv3 alert certificate expired"
+#define _EXPIRED_ALERT_RETRY_COUNT 10   /* default number of slaves, hope that not all
+                                                                                     are in the bad state */
+#define _EXPIRED_ALERT_RETRY_DELAY 10   /* ms */
+
 int 
 edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
                    struct timeval *timeout, edg_wll_GssConnection *connection,
@@ -654,6 +652,8 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
 
    do { /* XXX: the black magic above */
 
+   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 */
@@ -677,9 +677,12 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
 
       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);
+           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);
+               gss_release_buffer(&min_stat2, &output_token);
+           }
         }
         ret = EDG_WLL_GSS_ERROR_GSS;
         goto end;
@@ -716,6 +719,27 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port,
 
    } while (retry);
 
+   /* retry on false "certificate expired" */
+   if (ret == EDG_WLL_GSS_ERROR_GSS) {
+          edg_wll_GssStatus    gss_stat;
+          char *msg = NULL;
+
+          gss_stat.major_status = maj_stat;
+          gss_stat.minor_status = min_stat;
+          edg_wll_gss_get_error(&gss_stat,"",&msg);
+
+          if (strstr(msg,_EXPIRED_ALERT_MESSAGE)) {
+                  usleep(_EXPIRED_ALERT_RETRY_DELAY);
+                  retry--;
+          }
+          else retry = 0;
+
+          free(msg);
+   }
+   else retry = 0;
+
+   } while (retry);
+
    connection->sock = sock;
    connection->context = context;
    servername = NULL;
@@ -779,24 +803,29 @@ edg_wll_gss_accept(edg_wll_GssCred cred, int sock, struct timeval *timeout,
 
    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);
+        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);
+               gss_release_buffer(&min_stat2, &output_token);
+        }
       }
       ret = EDG_WLL_GSS_ERROR_GSS;
       goto end;
    }
 
+#if 0
    maj_stat = gss_display_name(&min_stat, client_name, &output_token, NULL);
+   gss_release_buffer(&min_stat2, &output_token);
    if (GSS_ERROR(maj_stat)) {
       /* XXX close context ??? */
       ret = EDG_WLL_GSS_ERROR_GSS;
       goto end;
    }
+#endif
 
    connection->sock = sock;
    connection->context = context;
-   memset(&output_token, 0, sizeof(output_token.value));
    ret = 0;
 
 end:
@@ -814,7 +843,7 @@ 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;
+   OM_uint32 maj_stat, min_stat, min_stat2;
    gss_buffer_desc  input_token;
    gss_buffer_desc  output_token;
    int  ret;
@@ -870,15 +899,17 @@ edg_wll_gss_read(edg_wll_GssConnection *connection, void *buf, size_t bufsize,
       ret = recv_token(connection->sock, &input_token.value, &input_token.length,
                       timeout);
       if (ret)
-        /* XXX cleanup */
         return ret;
 
-
+      ERR_clear_error();
       maj_stat = gss_unwrap(&min_stat, connection->context, &input_token,
                            &output_token, NULL, NULL);
-      gss_release_buffer(&min_stat, &input_token);
+      gss_release_buffer(&min_stat2, &input_token);
       if (GSS_ERROR(maj_stat)) {
-        /* XXX cleanup */
+        if (gss_code) {
+           gss_code->minor_status = min_stat;
+           gss_code->major_status = maj_stat;
+         }
         return EDG_WLL_GSS_ERROR_GSS;
       }
    } while (maj_stat == 0 && output_token.length == 0 && output_token.value == NULL);
@@ -946,37 +977,55 @@ edg_wll_gss_write_full(edg_wll_GssConnection *connection, const void *buf,
    return edg_wll_gss_write(connection, buf, bufsize, timeout, gss_code);
 }
 
-/* XXX: I'm afraid the contents of stuct stat is somewhat OS dependent */
+/* Request credential reload each 60 seconds in order to work around
+ * Globus bug (not reloading expired CRLs)
+ */
+#define GSS_CRED_WATCH_LIMIT  60
 int
-edg_wll_gss_watch_creds(const char *proxy_file, time_t *proxy_mtime)
+edg_wll_gss_watch_creds(const char *proxy_file, time_t *last_time)
 {
-       struct stat     pstat;
-       int     reload = 0;
+      struct stat     pstat;
+      time_t  now;
+
+      now = time(NULL);
 
-       if (!proxy_file) return 0;
-       if (stat(proxy_file,&pstat)) return -1;
+      if ( now >= (*last_time+GSS_CRED_WATCH_LIMIT) ) {
+              *last_time = now;
+              return 1;
+      }
 
-       if (!*proxy_mtime) *proxy_mtime = pstat.st_mtime;
+      if (!proxy_file) return 0;
+      if (stat(proxy_file,&pstat)) return -1;
 
-       if (*proxy_mtime != pstat.st_mtime) {
-               *proxy_mtime = pstat.st_mtime;
-               reload = 1;
-       }
+      if ( pstat.st_mtime >= *last_time ) {
+              *last_time = now + 1;
+              return 1;
+      }
 
-       return reload;
+      return 0;
 }
 
 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 */
+   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+   struct timeval def_timeout = { 0, 100000};
 
    if (con->context != GSS_C_NO_CONTEXT) {
-      gss_delete_sec_context(&min_stat, (gss_ctx_id_t *)&con->context, GSS_C_NO_BUFFER);
-      /* XXX send the buffer (if any) to the peer. GSSAPI specs doesn't
-       * recommend sending it, though */
+      gss_delete_sec_context(&min_stat, (gss_ctx_id_t *)&con->context, &output_token);
+
+#if 0
+      /* XXX: commented out till timeout handling in caller is fixed */
+
+      /* send the buffer (if any) to the peer. GSSAPI specs doesn't
+       * recommend sending it, but we want SSL compatibility */
+      if (output_token.length && con->sock>=0) {
+              send_token(con->sock, output_token.value, output_token.length,
+                              timeout ? timeout : &def_timeout);
+      }
+#endif
+      gss_release_buffer(&min_stat, &output_token);
 
       /* 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 */
@@ -1008,7 +1057,6 @@ edg_wll_gss_get_error(edg_wll_GssStatus *gss_err, const char *prefix, char **msg
                                    &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);
@@ -1168,3 +1216,47 @@ edg_wll_gss_gethostname(char *name, int len)
 
    return ret;
 }
+
+char *
+edg_wll_gss_normalize_subj(char *in, int replace_in)
+{
+       char *new, *ptr;
+       size_t len;
+
+       if (in == NULL) return NULL;
+       if (replace_in) 
+               new = in;
+       else
+               new = strdup(in);
+       
+       while ((ptr = strstr(new, "/emailAddress="))) {
+               memcpy(ptr, "/Email=",7);
+               memmove(ptr+7, ptr+14, strlen(ptr+14)+1);
+       }
+       
+       len = strlen(new);
+       while (len > 9 && !strcmp(new+len-9, "/CN=proxy")) {
+               *(new+len-9) = '\0';
+               len -= 9;
+       }
+
+       return new;
+}
+
+int
+edg_wll_gss_equal_subj(const char *a, const char *b)
+{
+       char *an,*bn;
+       int res;
+
+       an = edg_wll_gss_normalize_subj((char*)a, 0);
+       bn = edg_wll_gss_normalize_subj((char*)b, 0);
+
+       if (!an || !bn)
+               res = 0;
+       else 
+               res = !strcmp(an,bn);
+       
+       free(an); free(bn);
+       return res;
+}