Back to index

nordugrid-arc-nox  1.1.0~rc6
HTTPSClientConnector.cpp
Go to the documentation of this file.
00001 #ifdef WIN32
00002 #include <arc/win32.h>
00003 #else
00004 #include <sys/socket.h>
00005 #endif
00006 
00007 #include <sys/timeb.h>
00008 #include <sys/types.h>
00009 #include <string.h>
00010 #include <globus_io.h>
00011 #include <gssapi.h>
00012 #include <globus_gsi_credential.h>
00013 #include <globus_gsi_cert_utils.h>
00014 
00015 
00016 #include <arc/globusutils/GlobusErrorUtils.h>
00017 #include <arc/Utils.h>
00018 
00019 #include "HTTPSClient.h"
00020 
00021 // This is unexposed Globus internal structure. 
00022 // Without doubt its usage is dangerous. But it
00023 // seems to be stable across different version of
00024 // Globus Toolkit (tested for 4.0.8 and 4.2.1).
00025 typedef struct gss_cred_id_desc_struct {
00026     globus_gsi_cred_handle_t            cred_handle;
00027     gss_name_t                          globusid;
00028     gss_cred_usage_t                    cred_usage;
00029     SSL_CTX *                           ssl_context;
00030 } gss_cred_id_desc;
00031 
00032 namespace Arc {
00033   
00034   Logger HTTPSClientConnector::logger(Logger::getRootLogger(), "HTTPSClientConnector");
00035 
00036   // ------------------ Globus -------------------------------
00037 
00038   static globus_io_secure_delegation_mode_t ChooseDelegationMode(gss_cred_id_t cred) {
00039     globus_io_secure_delegation_mode_t mode = GLOBUS_IO_SECURE_DELEGATION_MODE_NONE;
00040     gss_cred_id_desc cred_desc;
00041     if(cred == GSS_C_NO_CREDENTIAL) {
00042       globus_gsi_cred_handle_init(&cred_desc.cred_handle,NULL);
00043       if(globus_gsi_cred_read(cred_desc.cred_handle,NULL) == GLOBUS_SUCCESS) {
00044         cred = &cred_desc;
00045       };
00046     }
00047     if(cred != GSS_C_NO_CREDENTIAL) {
00048       globus_gsi_cert_utils_cert_type_t cred_type;
00049       if(globus_gsi_cred_get_cert_type(cred->cred_handle,&cred_type) ==
00050          GLOBUS_SUCCESS) {
00051         if(GLOBUS_GSI_CERT_UTILS_IS_LIMITED_PROXY(cred_type)) {
00052           mode=GLOBUS_IO_SECURE_DELEGATION_MODE_LIMITED_PROXY;
00053         } else {
00054           mode=GLOBUS_IO_SECURE_DELEGATION_MODE_FULL_PROXY;
00055         };
00056       };
00057     };
00058     if(cred == &cred_desc) {
00059       globus_gsi_cred_handle_destroy(cred_desc.cred_handle);
00060     };
00061     return mode;
00062   }
00063     
00064   HTTPSClientConnectorGlobus::HTTPSClientConnectorGlobus(const char* base,bool heavy_encryption,int timeout_,gss_cred_id_t cred_) try: base_url(base) {
00065     valid=false; connected=false;
00066     read_registered=false;
00067     write_registered=false;
00068     read_size=NULL;
00069     cred=cred_;
00070     timeout=timeout_; // 1 min.
00071     /* initialize globus io connection related objects */
00072     globus_io_tcpattr_init(&attr);
00073     globus_io_secure_authorization_data_initialize(&auth);
00074     globus_io_secure_authorization_data_set_callback(&auth,&authorization_callback,NULL);
00075     if(strcasecmp(base_url.Protocol().c_str(),"http") == 0) {
00076       globus_io_attr_set_secure_authentication_mode(&attr,
00077                 GLOBUS_IO_SECURE_AUTHENTICATION_MODE_NONE,GSS_C_NO_CREDENTIAL);
00078       globus_io_attr_set_secure_authorization_mode(&attr,
00079                 GLOBUS_IO_SECURE_AUTHORIZATION_MODE_NONE,GLOBUS_NULL);
00080       globus_io_attr_set_secure_channel_mode(&attr,
00081                                    GLOBUS_IO_SECURE_CHANNEL_MODE_CLEAR);
00082       globus_io_attr_set_secure_protection_mode(&attr,
00083                                    GLOBUS_IO_SECURE_PROTECTION_MODE_NONE);
00084       globus_io_attr_set_secure_delegation_mode(&attr,
00085                                    GLOBUS_IO_SECURE_DELEGATION_MODE_NONE);
00086     } else if(strcasecmp(base_url.Protocol().c_str(),"https") == 0) {
00087       globus_io_attr_set_secure_authentication_mode(&attr,
00088                 GLOBUS_IO_SECURE_AUTHENTICATION_MODE_MUTUAL,cred);
00089       globus_io_attr_set_secure_authorization_mode(&attr,
00090                 GLOBUS_IO_SECURE_AUTHORIZATION_MODE_HOST,GLOBUS_NULL);
00091       globus_io_attr_set_secure_channel_mode(&attr,
00092                                    GLOBUS_IO_SECURE_CHANNEL_MODE_SSL_WRAP);
00093       if(heavy_encryption) {
00094         globus_io_attr_set_secure_protection_mode(&attr,
00095                                    GLOBUS_IO_SECURE_PROTECTION_MODE_PRIVATE);
00096       } else {
00097         globus_io_attr_set_secure_protection_mode(&attr,
00098                                    GLOBUS_IO_SECURE_PROTECTION_MODE_SAFE);
00099       };
00100       globus_io_attr_set_secure_delegation_mode(&attr,
00101                                    GLOBUS_IO_SECURE_DELEGATION_MODE_NONE);
00102     } else if(strcasecmp(base_url.Protocol().c_str(),"httpg") == 0) {
00103       globus_io_attr_set_secure_authentication_mode(&attr,
00104                 GLOBUS_IO_SECURE_AUTHENTICATION_MODE_GSSAPI,cred);
00105       globus_io_attr_set_secure_authorization_mode(&attr,
00106                 GLOBUS_IO_SECURE_AUTHORIZATION_MODE_HOST,GLOBUS_NULL);
00107       globus_io_attr_set_secure_channel_mode(&attr,
00108                                    GLOBUS_IO_SECURE_CHANNEL_MODE_GSI_WRAP);
00109       if(heavy_encryption) {
00110         globus_io_attr_set_secure_protection_mode(&attr,
00111                                    GLOBUS_IO_SECURE_PROTECTION_MODE_PRIVATE);
00112       } else {
00113         globus_io_attr_set_secure_protection_mode(&attr,
00114                                    GLOBUS_IO_SECURE_PROTECTION_MODE_SAFE);
00115       };
00116       globus_io_attr_set_secure_delegation_mode(&attr,
00117                                    ChooseDelegationMode(cred));
00118     } else {
00119       return;
00120     };
00121     globus_io_attr_set_secure_proxy_mode(&attr,GLOBUS_IO_SECURE_PROXY_MODE_NONE);
00122     valid=true;
00123   } catch(std::exception e) {
00124     timeout=timeout_; // 1 min.
00125     valid=false; connected=false;
00126     /* initialize globus io connection related objects */
00127     globus_io_tcpattr_init(&attr);
00128     globus_io_secure_authorization_data_initialize(&auth);
00129     globus_io_secure_authorization_data_set_callback(&auth,&authorization_callback,NULL);
00130   }
00131   
00132   HTTPSClientConnectorGlobus::~HTTPSClientConnectorGlobus(void) {
00133     disconnect();
00134     globus_io_secure_authorization_data_destroy(&auth);
00135     globus_io_tcpattr_destroy(&attr);
00136   }
00137   
00138   bool HTTPSClientConnectorGlobus::credentials(gss_cred_id_t cred_) {
00139     if(cred_ == GSS_C_NO_CREDENTIAL) return false;
00140     gss_cred_id_t cred_old;
00141     globus_io_secure_authentication_mode_t mode;
00142     if(globus_io_attr_get_secure_authentication_mode(&attr,&mode,&cred_old) !=
00143                                          GLOBUS_SUCCESS) return false;
00144     if(globus_io_attr_set_secure_authentication_mode(&attr,mode,cred_) !=
00145                                          GLOBUS_SUCCESS) return false;
00146     cred=cred_;
00147     return true;
00148   }
00149   
00150   bool HTTPSClientConnectorGlobus::read(char* buf,unsigned int* size) {
00151     if(!connected) return false;
00152     globus_result_t res;
00153     unsigned int size_=size?*size:0;
00154     if(size) *size=0;
00155     if((buf == NULL) || (size_ == 0)) {
00156       // cancel request
00157       if(read_registered) {
00158         res=globus_io_cancel(&s,GLOBUS_FALSE);
00159         if(res != GLOBUS_SUCCESS) {
00160           logger.msg(ERROR, "globus_io_cancel failed: %s", GlobusResult(res).str());
00161           return false;
00162         };
00163         read_registered=false;
00164         write_registered=false;
00165       };
00166       return true;
00167     };
00168     if(read_registered) return false;
00169     read_size=size;
00170     read_registered=true;
00171     read_done=-1; cond.reset();
00172     res=globus_io_register_read(&s,(globus_byte_t*)buf,size_,1,
00173                                                        &read_callback,this);
00174     if(res != GLOBUS_SUCCESS) {
00175       read_registered=false;
00176       logger.msg(ERROR, "globus_io_register_read failed: %s", GlobusResult(res).str());
00177       return false;
00178     };
00179     return true;
00180   }
00181   
00182   bool HTTPSClientConnectorGlobus::write(const char* buf,unsigned int size) {
00183     if(!connected) return false;
00184     globus_result_t res;
00185     if((buf == NULL) || (size == 0)) {
00186       // cancel request
00187       if(write_registered) {
00188         res=globus_io_cancel(&s,GLOBUS_FALSE);
00189         if(res != GLOBUS_SUCCESS) {
00190           logger.msg(ERROR, "globus_io_cancel failed: %s", GlobusResult(res).str());
00191           return false;
00192         };
00193         read_registered=false;
00194         write_registered=false;
00195       };
00196       return true;
00197     };
00198     if(write_registered) return false;
00199     write_registered=true;
00200     write_done=-1; cond.reset();
00201     res=globus_io_register_write(&s,(globus_byte_t*)buf,size,&write_callback,this);
00202     if(res != GLOBUS_SUCCESS) {
00203       write_registered=false;
00204       logger.msg(ERROR, "globus_io_register_write failed: %s", GlobusResult(res).str());
00205       return false;
00206     };
00207     return true;
00208   }
00209   
00210   bool HTTPSClientConnectorGlobus::connect(bool &timedout) {
00211     if(!valid) return false;
00212     if(connected) return true;
00213     globus_result_t res;
00214     read_registered=false; write_registered=false;
00215     read_done=-1; write_done=-1;
00216     cond.reset();
00217     connect_lock->lock();
00218     if((res=globus_io_tcp_register_connect(
00219                       (char*)(base_url.Host().c_str()),base_url.Port(),
00220                       &attr,&general_callback,this,&s)) != GLOBUS_SUCCESS) {
00221       logger.msg(ERROR, "Connect to %s failed: %s", base_url.str(), GlobusResult(res).str());
00222       connect_lock->unlock();
00223       return false;
00224     };
00225     globus_thread_blocking_will_block(); // ????
00226     if(!cond.wait(timeout)) {  // timeout
00227       logger.msg(ERROR, "Connection to %s timed out after %i seconds", base_url.str(), timeout/1000);
00228       globus_io_cancel(&s,GLOBUS_FALSE);
00229       globus_io_close(&s);
00230       connect_lock->unlock();
00231       timedout = true;
00232       return false;
00233     };
00234     connect_lock->unlock();
00235     connected=true;
00236     return true;
00237   }
00238   
00239   bool HTTPSClientConnectorGlobus::disconnect(void) {
00240     if(!connected) return true;
00241     globus_io_cancel(&s,GLOBUS_FALSE);
00242     globus_io_close(&s);
00243     connected=false;
00244     return true;
00245   }
00246   
00247   bool HTTPSClientConnectorGlobus::transfer(bool& read,bool& write,int timeout) {
00248     read=false; write=false;
00249     if((!read_registered) && (!write_registered)) return true;
00250     for(;;) {
00251       if(read_registered && (read_done!=-1)) {
00252         read_registered=false; read=(read_done==0); break;
00253       };
00254       if(write_registered && (write_done!=-1)) {
00255         write_registered=false; write=(write_done==0); break;
00256       };
00257       if(!cond.wait(timeout)) return false; // timeout
00258     };
00259     return true;
00260   }
00261   
00262   
00263   bool HTTPSClientConnectorGlobus::clear(void) {
00264     if(!valid) return false;
00265     globus_byte_t buf[256];
00266     globus_size_t l;
00267     for(;;) {
00268       if(globus_io_read(&s,buf,256,0,&l) != GLOBUS_SUCCESS) return false;
00269       if(l == 0) break;
00270       std::string buf_str;
00271       for(globus_size_t n=0;n<l;n++) buf_str += buf[n];
00272       logger.msg(DEBUG, "clear_input: %s", buf_str);
00273     };
00274     return true;
00275   }
00276   
00277   void HTTPSClientConnectorGlobus::general_callback(void *arg,globus_io_handle_t *handle,globus_result_t  result) {
00278     HTTPSClientConnectorGlobus* it = (HTTPSClientConnectorGlobus*)arg;
00279     if(result != GLOBUS_SUCCESS) logger.msg(ERROR, "Globus error: %s", GlobusResult(result).str());
00280     it->cond.signal();
00281   }
00282   
00283   void HTTPSClientConnectorGlobus::read_callback(void *arg,globus_io_handle_t *handle,globus_result_t result,globus_byte_t *buf,globus_size_t nbytes) {
00284     HTTPSClientConnectorGlobus* it = (HTTPSClientConnectorGlobus*)arg;
00285     int res = 0;
00286     if(result != GLOBUS_SUCCESS) {
00287       globus_object_t* err = globus_error_get(result);
00288       char* tmp=globus_object_printable_to_string(err);
00289       if(strstr(tmp,"end-of-file") != NULL) {
00290         logger.msg(DEBUG, "Connection closed");
00291         res=2; // eof
00292       } else {
00293         logger.msg(ERROR, "Globus error (read): %s", tmp);
00294         res=1;
00295       };
00296       free(tmp); globus_object_free(err);
00297     } else {
00298       std::string buf_str;
00299       for(globus_size_t n=0;n<nbytes;n++) buf_str += buf[n];
00300       logger.msg(DEBUG, "*** Server response: %s", buf_str);
00301       if(it->read_size) *(it->read_size)=nbytes;
00302     };
00303     it->cond.lock();
00304     it->read_done=res; it->cond.signal_nonblock();
00305     it->cond.unlock();
00306   }
00307   
00308   void HTTPSClientConnectorGlobus::write_callback(void *arg,globus_io_handle_t *handle,globus_result_t result,globus_byte_t *buf,globus_size_t nbytes) {
00309     HTTPSClientConnectorGlobus* it = (HTTPSClientConnectorGlobus*)arg;
00310     int res = 0;
00311     if(result != GLOBUS_SUCCESS) {
00312       logger.msg(ERROR, "Globus error (write): %s", GlobusResult(result).str());
00313       res=1;
00314     } else {
00315       std::string buf_str;
00316       for(globus_size_t n=0;n<nbytes;n++) buf_str += buf[n];
00317       logger.msg(DEBUG, "*** Client request: %s", buf_str);
00318     };
00319     it->cond.lock();
00320     it->write_done=res; it->cond.signal_nonblock();
00321     it->cond.unlock();
00322   }
00323   
00324   globus_bool_t HTTPSClientConnectorGlobus::authorization_callback(void* arg,globus_io_handle_t* h,globus_result_t result,char* identity,gss_ctx_id_t context_handle) {
00325     logger.msg(DEBUG, "Authenticating: %s", identity);
00326     return GLOBUS_TRUE;
00327   }
00328   
00329   bool HTTPSClientConnectorGlobus::eofread(void) {
00330     return (read_done==2);
00331   }
00332   
00333   bool HTTPSClientConnectorGlobus::eofwrite(void) {
00334     return (!write_registered);
00335   }
00336   
00337   // --------------- GSSAPI ---------------
00338    
00339   static std::string gss_error_string(OM_uint32 maj_status,OM_uint32 min_status) {
00340     std::string message;
00341     OM_uint32 major_status = 0;
00342     OM_uint32 minor_status = 0;
00343     OM_uint32 m_context = 0;
00344     gss_buffer_desc buf;
00345     for(;;) {
00346       buf.length=0; buf.value=NULL;
00347       major_status=gss_display_status(&minor_status,maj_status,
00348                      GSS_C_GSS_CODE,GSS_C_NULL_OID,&m_context,&buf);
00349       if(buf.value != NULL) {
00350         if(!message.empty()) message+="; ";
00351         message+=(char*)(buf.value);
00352         gss_release_buffer(&minor_status,&buf);
00353       };
00354       if(m_context == 0) break;
00355     };
00356     for(;;) {
00357       buf.length=0; buf.value=NULL;
00358       major_status=gss_display_status(&minor_status,min_status,
00359                      GSS_C_MECH_CODE,GSS_C_NULL_OID,&m_context,&buf);
00360       if(buf.value != NULL) {
00361         if(!message.empty()) message+="; ";
00362         message+=(char*)(buf.value);
00363         gss_release_buffer(&minor_status,&buf);
00364       };
00365       if(m_context == 0) break;
00366     };
00367     return message;
00368   }
00369   
00370   HTTPSClientConnectorGSSAPI::HTTPSClientConnectorGSSAPI(const char* base,bool heavy_encryption,int timeout_,gss_cred_id_t cred_,bool check_host) try: base_url(base), check_host_cert(check_host) {
00371     s=-1;
00372     cred=cred_;
00373     timeout=timeout_;
00374     context=GSS_C_NO_CONTEXT;
00375     valid=true;
00376   } catch(std::exception e) {
00377     valid=false;
00378   }
00379   
00380   bool HTTPSClientConnectorGSSAPI::connect(bool &timedout) {
00381     if(!valid) return false;
00382     if(s != -1) return true;
00383     read_buf=NULL; read_size=0; read_size_result=NULL;
00384     write_buf=NULL; write_size=0;
00385     read_eof_flag=false;
00386   
00387     struct addrinfo* res;
00388     int err;
00389     if((err=getaddrinfo(base_url.Host().c_str(),NULL,NULL,&res)) != 0) {
00390       logger.msg(ERROR, "Address resolution failed: %s", gai_strerror(err));
00391       return false;
00392     }
00393     struct addrinfo* r = res;
00394     for(;r;r=r->ai_next) {
00395       if(r->ai_addr == NULL) continue;
00396       if(r->ai_socktype != SOCK_STREAM) continue;
00397       if(r->ai_protocol != IPPROTO_TCP) continue;
00398       if(r->ai_family == AF_INET) {
00399         ((struct sockaddr_in*)(r->ai_addr))->sin_port=htons(base_url.Port());
00400         break;
00401       }
00402       if(r->ai_family == AF_INET6) {
00403         ((struct sockaddr_in6*)(r->ai_addr))->sin6_port=htons(base_url.Port());
00404         break;
00405       }
00406     }
00407     if(!r) {
00408       freeaddrinfo(res);
00409       logger.msg(ERROR, "Address resolution failed: %s", "no suitable address found");
00410       return false;
00411     }
00412     s=::socket(r->ai_family,r->ai_socktype,r->ai_protocol);
00413     if(s==-1) {
00414       freeaddrinfo(res);
00415       logger.msg(ERROR, "Socket creation failed: %s", Arc::StrError(errno));
00416       return false;
00417     };
00418   
00419     if(::connect(s,r->ai_addr,r->ai_addrlen)==-1) {
00420       freeaddrinfo(res);
00421       logger.msg(ERROR, "Connection to server failed: %s", Arc::StrError(errno));
00422       ::close(s); s=-1;
00423       return false;
00424     };
00425     freeaddrinfo(res);
00426   
00427     OM_uint32 major_status = 0;
00428     OM_uint32 minor_status = 0;
00429   
00430     //if(cred == GSS_C_NO_CREDENTIAL) {
00431     //  major_status = gss_acquire_cred(&minor_status,
00432     //                                  GSS_C_NO_NAME,
00433     //                                  0,
00434     //                                  GSS_C_NULL_OID_SET,
00435     //                                  GSS_C_ACCEPT,
00436     //                                  &cred,
00437     //                                  NULL,
00438     //                                  NULL);
00439     //  if (major_status != GSS_S_COMPLETE) {
00440     //    logger.msg(ERROR, "Failed to acquire local credentials");
00441     //    ::close(s); s=-1;
00442     //    return false;
00443     //  };
00444     //};
00445   
00446     OM_uint32 init_sec_min_stat;
00447     OM_uint32 ret_flags =  0;
00448     gss_name_t remote_name = GSS_C_NO_NAME;
00449     gss_OID oid = GSS_C_NO_OID; // ??????????
00450     gss_buffer_desc recv_tok;
00451     gss_buffer_desc send_tok;
00452     int context_flags = GSS_C_CONF_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | GSS_C_DELEG_FLAG; // GSS_C_GLOBUS_SSL_COMPATIBLE
00453   #ifdef HAVE_GLOBUS_GSS_ASSIST_AUTHORIZATION_HOST_NAME
00454     globus_gss_assist_authorization_host_name((char*)(base_url.Host().c_str()),&remote_name);
00455   #else
00456     {
00457       gss_buffer_desc name_tok;
00458       name_tok.value = (void*)(base_url.Host().c_str());
00459       name_tok.length = base_url.Host().length() + 1;
00460       major_status=gss_import_name(&minor_status,&name_tok,
00461                                    GSS_C_NT_HOSTBASED_SERVICE,&remote_name);
00462       // if(GSS_ERROR(major_status))
00463     };
00464   #endif
00465     // check if we want to do service host cert checks
00466     if(!check_host_cert) {
00467       remote_name=GSS_C_NO_NAME;
00468       // can't do delegation with no target host
00469       context_flags = GSS_C_CONF_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
00470     };
00471   
00472     recv_tok.length=0; recv_tok.value=NULL;
00473     send_tok.length=0; send_tok.value=NULL;
00474     connect_lock->lock();
00475     for(;;) {
00476       major_status = gss_init_sec_context(&init_sec_min_stat,
00477                                           cred,
00478                                           &context,
00479                                           remote_name,
00480                                           oid,
00481                                           context_flags,
00482                                           0,
00483                                           NULL,
00484                                       recv_tok.value?&recv_tok:GSS_C_NO_BUFFER,
00485                                           NULL,
00486                                           &send_tok,
00487                                           &ret_flags,
00488                                           NULL);
00489       if(recv_tok.value) { free(recv_tok.value); recv_tok.value=NULL; };
00490       if((major_status!=GSS_S_COMPLETE) &&
00491          (major_status!=GSS_S_CONTINUE_NEEDED)) {
00492         logger.msg(ERROR, "Failed to authenticate: %s", gss_error_string(major_status,init_sec_min_stat));
00493         ::close(s); s=-1; break;
00494       };
00495       if(context == NULL) {
00496         logger.msg(ERROR, "Failed to create GSI context: %s", gss_error_string(major_status,init_sec_min_stat));
00497         ::close(s); s=-1; break;
00498       };
00499       if(send_tok.length != 0) {
00500         int to = timeout;
00501         if(do_write((char*)(send_tok.value),send_tok.length,to) == -1) {
00502           ::close(s); s=-1; break;
00503         };
00504         gss_release_buffer(&minor_status,&send_tok); send_tok.length=0;
00505       };
00506       if(major_status==GSS_S_COMPLETE) break;
00507       int l=read_SSL_token(&(recv_tok.value),timeout);
00508       if(l <= 0) {
00509         logger.msg(ERROR, "Failed to read SSL token during authentication");
00510         if(context != GSS_C_NO_CONTEXT)
00511           gss_delete_sec_context(&minor_status,&context,GSS_C_NO_BUFFER);
00512         context=GSS_C_NO_CONTEXT;
00513         ::close(s); s=-1;
00514         connect_lock->unlock();
00515         return false;
00516       };
00517       recv_tok.length=l;
00518     };
00519     connect_lock->unlock();
00520     if(s == -1) {
00521       if(context != GSS_C_NO_CONTEXT) {
00522         gss_delete_sec_context(&minor_status,&context,GSS_C_NO_BUFFER);
00523         context=GSS_C_NO_CONTEXT;
00524       };
00525     };
00526     if(recv_tok.value) { free(recv_tok.value); recv_tok.value=NULL; };
00527     if(send_tok.length != 0) gss_release_buffer(&minor_status,&send_tok);
00528     if(remote_name != GSS_C_NO_NAME) gss_release_name(&minor_status,&remote_name);
00529     if(s == -1) {
00531     };
00532     return (s != -1);
00533   }
00534   
00535   bool HTTPSClientConnectorGSSAPI::disconnect(void) {
00536     if(s == -1) return true;
00537     ::close(s); s=-1;
00538     OM_uint32 minor_status = 0;
00539     if(context != GSS_C_NO_CONTEXT)
00540       gss_delete_sec_context(&minor_status,&context,GSS_C_NO_BUFFER);
00541     context=GSS_C_NO_CONTEXT;
00542     return true;
00543   }  
00544   
00545   bool HTTPSClientConnectorGSSAPI::read(char* buf,unsigned int* size) {
00546     if(s == -1) return false;
00547     read_size=size?*size:0;
00548     read_size_result=size;
00549     if(size) *size=0;
00550     read_buf=buf;
00551     return true;
00552   }
00553   
00554   bool HTTPSClientConnectorGSSAPI::write(const char* buf,unsigned int size) {
00555     if(s == -1) return false;
00556     write_size=size;
00557     write_buf=buf;
00558     return true;
00559   }
00560   
00561   bool HTTPSClientConnectorGSSAPI::transfer(bool& read,bool& write,int timeout) {
00562     read=false; write=false;
00563     if(write_buf) {
00564       OM_uint32 major_status;
00565       OM_uint32 minor_status;
00566       gss_buffer_desc send_tok;
00567       gss_buffer_desc data_tok;
00568       int conf_state;
00569       data_tok.length=write_size;
00570       data_tok.value=(void*)write_buf;
00571       logger.msg(DEBUG, "*** Client request: %s", (char*)(data_tok.value));
00572       //for(globus_size_t n=0;n<data_tok.length;n++) odlog_(DEBUG)<<((char*)(data_tok.value))[n];
00573       //odlog_(DEBUG));
00574       major_status = gss_wrap(&minor_status,
00575                               context,
00576                               0,
00577                               GSS_C_QOP_DEFAULT,
00578                               &data_tok,
00579                               &conf_state,
00580                               &send_tok);
00581       if(major_status != GSS_S_COMPLETE) {
00582         logger.msg(ERROR, "Failed wrapping GSI token: %s", gss_error_string(major_status,minor_status));
00583         return false;
00584       };
00585       int to = timeout;
00586       int r = do_write((char*)(send_tok.value),send_tok.length,to);
00587       gss_release_buffer(&minor_status,&send_tok);
00588       write_buf=NULL; write_size=0; write=(r!=-1);
00589       return true;
00590     };
00591     if(read_buf) {
00592       gss_buffer_desc recv_tok;
00593       gss_buffer_desc data_tok = GSS_C_EMPTY_BUFFER;
00594       OM_uint32 major_status;
00595       OM_uint32 minor_status;
00596       int ll = read_SSL_token(&(recv_tok.value),timeout);
00597       if(ll == 0) { read_eof_flag=true; read=false; return true; };
00598       if(ll == -1) { read=false; return true; };
00599       recv_tok.length=ll;
00600       major_status = gss_unwrap(&minor_status,
00601                                 context,
00602                                 &recv_tok,
00603                                 &data_tok,
00604                                 NULL,
00605                                 NULL);
00606       free(recv_tok.value);
00607       if(major_status != GSS_S_COMPLETE) {
00608         logger.msg(ERROR, "Failed unwrapping GSI token: %s", gss_error_string(major_status,minor_status));
00609         return false;
00610       };
00611       logger.msg(DEBUG, "*** Server response: %s", (char*)(data_tok.value));
00612       //for(globus_size_t n=0;n<data_tok.length;n++) odlog_(DEBUG)<<((char*)(data_tok.value))[n];
00613       //odlog_(DEBUG));
00614   
00615       if(data_tok.length > read_size) {
00616         logger.msg(ERROR, "Unwrapped data does not fit into buffer");
00617         return false;
00618       };
00619       memcpy(read_buf,data_tok.value,data_tok.length);
00620       if(read_size_result) (*read_size_result)=data_tok.length;
00621       gss_release_buffer(&minor_status,&data_tok);
00622       read_buf=NULL; read_size=0; read_size_result=NULL; read=true;
00623       return true;
00624     };
00625     return true;
00626   }
00627   
00628   bool HTTPSClientConnectorGSSAPI::eofread(void) {
00629     return read_eof_flag;
00630   }
00631   
00632   bool HTTPSClientConnectorGSSAPI::eofwrite(void) {
00633     return (write_buf == NULL);
00634   }
00635   
00636   static unsigned int timems(void) {
00637     struct timeval tv;
00638     struct timezone tz;
00639     if(gettimeofday(&tv,&tz) != 0) return (time(NULL)*1000);
00640     return (tv.tv_sec*1000+tv.tv_usec/1000);
00641   }
00642   
00643   static bool waitsocket(int r,int w,int& timeout) {
00644     unsigned int t_start = timems();
00645     int dt = 0;
00646     if(timeout == -1) return true; // for blocking operation
00647     for(;;) {
00648       fd_set rs; FD_ZERO(&rs); if(r>=0) FD_SET(r,&rs);
00649       fd_set ws; FD_ZERO(&ws); if(w>=0) FD_SET(w,&ws);
00650       struct timeval t;
00651       t.tv_sec=(timeout-dt)/1000; t.tv_usec=(timeout-dt-t.tv_sec*1000)*1000;
00652       int n = ::select((r>w?r:w)+1,&rs,&ws,NULL,&t);
00653       if(n > 0) break;
00654       if((n == -1) && (errno != EINTR)) break; // to cause error on read/write
00655       dt=timems()-t_start;
00656       if(dt >= timeout) { timeout=0; return false; };
00657     };
00658     dt=timems()-t_start; 
00659     if(dt > timeout) dt=timeout; timeout-=dt;
00660     return true;
00661   }
00662   
00663   int HTTPSClientConnectorGSSAPI::do_read(char* buf,int size,int& timeout) {
00664     int n = size;
00665     for(;n;) {
00666       if(!waitsocket(s,-1,timeout)) return -1;
00667       int l = ::recv(s,buf,n,0);
00668       if((l == -1) && (errno != EINTR)) return -1;
00669       if(l == 0) {
00670         if(n == size) return 0;
00671         return -1;
00672       };
00673       buf+=l; n-=l;
00674     };
00675     return size;
00676   }
00677   
00678   int HTTPSClientConnectorGSSAPI::do_write(char* buf,int size,int& timeout) {
00679     int n = size;
00680     for(;n;) {
00681       if(!waitsocket(-1,s,timeout)) return -1;
00682       int l = ::send(s,buf,n,0);
00683       if((l == -1) && (errno != EINTR)) return -1;
00684       buf+=l; n-=l;
00685     };
00686     return size;
00687   }
00688   
00689   int HTTPSClientConnectorGSSAPI::read_SSL_token(void** val,int timeout) {
00690     unsigned char header[5]; // SSL V3 header
00691     (*val)=NULL;
00692     int l = do_read((char*)header,sizeof(header),timeout);
00693     if(l == 0) return 0;
00694     if(l < 0) return -1;
00695     if(header[0] == (unsigned char)0x80) {
00696       /* SSL V2 - 2 byte header */
00697       l=((unsigned int)(header[1]))-3;
00698     } else if((header[0] >= 20) &&
00699               (header[0] <= 26) &&
00700               (header[1] == 3) &&
00701               (header[2] == 0 || header[2] == 1)) {
00702            /* SSL V3 */
00703       l=(((unsigned int)(header[3])) << 8) | ((unsigned int)(header[4]));
00704     } else {
00705       logger.msg(ERROR, "Urecognized SSL token received");
00706       return -1;
00707     };
00708     unsigned char* data = (unsigned char*)malloc(l+5);
00709     if(data == NULL) return -1;
00710     memcpy(data,header,5);
00711     if(l) {
00712       int ll = do_read((char*)(data+5),l,timeout);
00713       if(ll <= 0) {
00714         free(data);
00715         return -1;
00716       };
00717     };
00718     (*val)=data;
00719     return (l+5);
00720   }
00721   
00722   bool HTTPSClientConnectorGSSAPI::clear(void) {
00723     gss_buffer_desc recv_tok;
00724     int l;
00725     while(true) {
00726       l=read_SSL_token(&(recv_tok.value),0);
00727       if(l <= 0) return true;
00728       if(recv_tok.value) free(recv_tok.value);
00729     };
00730   }
00731   
00732   bool HTTPSClientConnectorGSSAPI::credentials(gss_cred_id_t cred_) {
00733     cred=cred_;
00734     return true;
00735   }
00736   
00737   HTTPSClientConnectorGSSAPI::~HTTPSClientConnectorGSSAPI(void) {
00738     disconnect();
00739   }
00740   } // namespace Arc