Back to index

nordugrid-arc-nox  1.1.0~rc6
MCCGSI.cpp
Go to the documentation of this file.
00001 // -*- indent-tabs-mode: nil -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 #include <config.h>
00005 #endif
00006 
00007 #ifdef WIN32 
00008 #include <arc/win32.h>
00009 #endif
00010 
00011 #include <sys/types.h>
00012 
00013 #ifndef WIN32 
00014 #include <sys/socket.h>
00015 #include <arpa/inet.h>
00016 #endif
00017 
00018 #include <globus_openssl.h>
00019 
00020 #include <arc/globusutils/GSSCredential.h>
00021 #include <arc/globusutils/GlobusWorkarounds.h>
00022 #include <arc/loader/Loader.h>
00023 #include <arc/message/PayloadStream.h>
00024 #include <arc/message/PayloadRaw.h>
00025 #include <arc/message/MCCLoader.h>
00026 #include <arc/Logger.h>
00027 #include <arc/XMLNode.h>
00028 
00029 #include "MCCGSI.h"
00030 #include "PayloadGSIStream.h"
00031 
00032 
00033 namespace Arc {
00034 
00035   Logger MCC_GSI_Service::logger(MCC::logger, "GSI Service");
00036 
00037   Logger MCC_GSI_Client::logger(MCC::logger, "GSI Client");
00038 
00039   static bool proxy_initialized = false;
00040 
00041   // This function tries to activate Globus OpenSSL module
00042   // and keep it active forever because on deacivation Globus
00043   // destroys global structures of OpenSSL.
00044   static void globus_openldap_lock(ModuleManager& mm) {
00045     static bool done = false;
00046     if (done)
00047       return;
00048     // Increasing globus module counter so it is never deactivated
00049     globus_module_activate(GLOBUS_OPENSSL_MODULE);
00050     // Tackel GLOBUS_GSI_GSSAPI_MODULE the same
00051     // way as GLOBUS_OPENSSL_MODULE. Because it
00052     // seems globus deactive destroy the structure
00053     // X509V3_EXT_METHOD (both of the two X509V3_EXT_METHOD
00054     // generated by globus and Credential class are destroyed)
00055     // Maybe singleton pattern can be used.
00056     globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE);
00057 
00058     // Making sure this plugin is never unloaded
00059     // TODO: This is hack - probably proper solution would be
00060     // to decouple Globus libraries from plugin.
00061     std::string path = mm.findLocation("mccgsi");
00062     // Let's hope nothing bad will happen. We can't
00063     // influence that anyway.
00064     if (!path.empty())
00065       new Glib::Module(path, Glib::ModuleFlags(0));
00066   }
00067 
00068   static Plugin* get_mcc_service(PluginArgument *arg) {
00069     MCCPluginArgument *mccarg =
00070       arg ? dynamic_cast<MCCPluginArgument*>(arg) : NULL;
00071     if (!mccarg)
00072       return NULL;
00073     return new MCC_GSI_Service(*(Config*)(*mccarg),
00074                                *(PluginsFactory*)(*(ChainContext*)(*mccarg)));
00075   }
00076 
00077   static Plugin* get_mcc_client(PluginArgument *arg) {
00078     MCCPluginArgument *mccarg =
00079       arg ? dynamic_cast<MCCPluginArgument*>(arg) : NULL;
00080     if (!mccarg)
00081       return NULL;
00082     return new MCC_GSI_Client(*(Config*)(*mccarg),
00083                               *(PluginsFactory*)(*(ChainContext*)(*mccarg)));
00084   }
00085 
00086   class MCC_GSI_Context
00087     : public MessageContextElement {
00088   public:
00089     MCC_GSI_Context(const std::string& proxyPath,
00090                     const std::string& certificatePath,
00091                     const std::string& keyPath,
00092                     Logger& logger);
00093     ~MCC_GSI_Context();
00094     MCC_Status process(MCCInterface *next, Message& inmsg, Message& outmsg);
00095     operator bool() {
00096       return (ctx != GSS_C_NO_CONTEXT);
00097     }
00098   private:
00099     gss_ctx_id_t ctx;
00100     GSSCredential cred;
00101     gss_name_t client;
00102     OM_uint32 ret_flags;
00103     gss_OID oid;
00104     OM_uint32 time_req;
00105     gss_cred_id_t delegated_cred;
00106     bool completed;
00107     Logger& logger;
00108   };
00109 
00110   MCC_GSI_Context::MCC_GSI_Context(const std::string& proxyPath,
00111                                    const std::string& certificatePath,
00112                                    const std::string& keyPath,
00113                                    Logger& logger)
00114     : ctx(GSS_C_NO_CONTEXT),
00115       cred(proxyPath, certificatePath, keyPath),
00116       client(GSS_C_NO_NAME),
00117       oid(GSS_C_NO_OID),
00118       delegated_cred(GSS_C_NO_CREDENTIAL),
00119       completed(false),
00120       logger(logger) {}
00121 
00122   MCC_GSI_Context::~MCC_GSI_Context() {
00123     if (ctx != GSS_C_NO_CONTEXT) {
00124       OM_uint32 majstat, minstat;
00125       majstat = gss_delete_sec_context(&minstat, &ctx, GSS_C_NO_BUFFER);
00126       ctx = GSS_C_NO_CONTEXT;
00127     }
00128   }
00129 
00130   MCC_Status MCC_GSI_Context::process(MCCInterface *next,
00131                                       Message& inmsg, Message& outmsg) {
00132 
00133     if (!inmsg.Payload())
00134       return MCC_Status();
00135 
00136     PayloadStreamInterface *inpayload =
00137       dynamic_cast<PayloadStreamInterface*>(inmsg.Payload());
00138 
00139     int pos = 0;
00140     char readbuf[5];
00141     while (5 > pos) {
00142       int len = 5 - pos;
00143       inpayload->Get(&readbuf[pos], len);
00144       pos += len;
00145     }
00146     //TODO: for different types (GSI, Globus SSL, TLS/SSL3, SSL2) of communication
00147     //request from client side, differently process the header of received data
00148     //and the sent data correspondingly.
00149 
00150     gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
00151     gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
00152 
00153     recv_tok.length = (unsigned char)readbuf[3] * 256 +
00154                       (unsigned char)readbuf[4] + 5;
00155     // While allocating buffer with malloc it will be freed using
00156     // gssapi's gss_release_buffer()
00157     recv_tok.value = malloc(recv_tok.length);
00158     memcpy(recv_tok.value, readbuf, 5);
00159 
00160     logger.msg(VERBOSE, "Recieved token length: %i", recv_tok.length);
00161 
00162     while (recv_tok.length > pos) {
00163       int len = recv_tok.length - pos;
00164       inpayload->Get(&((char*)recv_tok.value)[pos], len);
00165       pos += len;
00166     }
00167 
00168     OM_uint32 majstat, minstat;
00169 
00170     if (!completed) {
00171 
00172       majstat = gss_accept_sec_context(&minstat,
00173                                        &ctx,
00174                                        cred,
00175                                        &recv_tok,
00176                                        GSS_C_NO_CHANNEL_BINDINGS,
00177                                        &client,
00178                                        &oid,
00179                                        &send_tok,
00180                                        &ret_flags,
00181                                        &time_req,
00182                                        &delegated_cred);
00183       if (GSS_ERROR(majstat)) {
00184         logger.msg(ERROR, "GSS accept security context failed: %i/%i%s", majstat, minstat, GSSCredential::ErrorStr(majstat, minstat));
00185         majstat = gss_release_buffer(&minstat, &send_tok);
00186         majstat = gss_release_buffer(&minstat, &recv_tok);
00187         return MCC_Status();
00188       }
00189 
00190       logger.msg(INFO, "GSS accept security context: %i/%i", majstat, minstat);
00191 
00192       logger.msg(VERBOSE, "Returned token length: %i", send_tok.length);
00193 
00194       PayloadRaw *outpayload = new PayloadRaw;
00195       if (send_tok.length > 0)
00196         outpayload->Insert((const char*)send_tok.value, 0, send_tok.length);
00197       outmsg.Payload(outpayload);
00198 
00199       if ((majstat & GSS_C_SUPPLEMENTARY_MASK) != GSS_S_CONTINUE_NEEDED)
00200         completed = true;
00201     }
00202     else {
00203 
00204       majstat = gss_unwrap(&minstat,
00205                            ctx,
00206                            &recv_tok,
00207                            &send_tok,
00208                            NULL,
00209                            GSS_C_QOP_DEFAULT);
00210       if (GSS_ERROR(majstat)) {
00211         logger.msg(ERROR, "GSS unwrap failed: %i/%i%s", majstat, minstat, GSSCredential::ErrorStr(majstat, minstat));
00212         majstat = gss_release_buffer(&minstat, &send_tok);
00213         majstat = gss_release_buffer(&minstat, &recv_tok);
00214         return MCC_Status();
00215       }
00216 
00217       logger.msg(INFO, "GSS unwrap: %i/%i", majstat, minstat);
00218 
00219       logger.msg(VERBOSE, "Sent token length: %i", send_tok.length);
00220 
00221       PayloadRaw payload;
00222       payload.Insert((const char*)send_tok.value, 0, send_tok.length);
00223 
00224       Message nextinmsg = inmsg;
00225       nextinmsg.Payload(&payload);
00226       Message nextoutmsg = outmsg;
00227       nextoutmsg.Payload(NULL);
00228 
00229       MCC_Status ret = next->process(nextinmsg, nextoutmsg);
00230       // TODO: Handle error and incompatible payloads
00231 
00232       outmsg = nextoutmsg;
00233 
00234       PayloadStreamInterface *outpayload =
00235         dynamic_cast<PayloadStreamInterface*>(nextoutmsg.Payload());
00236 
00237       outmsg.Payload(new PayloadGSIStream(outpayload, ctx, logger, false));
00238     }
00239 
00240     majstat = gss_release_buffer(&minstat, &send_tok);
00241     majstat = gss_release_buffer(&minstat, &recv_tok);
00242 
00243     return MCC_Status(STATUS_OK);
00244   }
00245 
00246   MCC_GSI_Service::MCC_GSI_Service(Config& cfg, ModuleManager& mm)
00247     : MCC(&cfg) {
00248     //globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE);
00249     globus_openldap_lock(mm);
00250     if (!proxy_initialized)
00251       proxy_initialized = GlobusRecoverProxyOpenSSL();
00252     proxyPath = (std::string)cfg["ProxyPath"];
00253     certificatePath = (std::string)cfg["CertificatePath"];
00254     keyPath = (std::string)cfg["KeyPath"];
00255   }
00256 
00257   MCC_GSI_Service::~MCC_GSI_Service() {
00258     //Probably for the service side, we can deactivate?
00259     //since the MCC object exists the whole lifetime of
00260     //container?
00261     //globus_module_deactivate(GLOBUS_GSI_GSSAPI_MODULE);
00262   }
00263 
00264   MCC_Status MCC_GSI_Service::process(Message& inmsg, Message& outmsg) {
00265 
00266     MessageContextElement *msgctx = (*inmsg.Context())["gsi.service"];
00267     MCC_GSI_Context *gsictx = NULL;
00268     if (msgctx)
00269       gsictx = dynamic_cast<MCC_GSI_Context*>(msgctx);
00270     if (!gsictx) {
00271       gsictx = new MCC_GSI_Context(proxyPath, certificatePath, keyPath, logger);
00272       inmsg.Context()->Add("gsi.service", gsictx);
00273     }
00274 
00275     if (*gsictx)
00276       if (!ProcessSecHandlers(inmsg, "incoming")) {
00277         logger.msg(ERROR,
00278                    "Security check failed in GSI MCC for incoming message");
00279         return MCC_Status();
00280       }
00281 
00282     return gsictx->process(MCC::Next(), inmsg, outmsg);
00283 
00284     if (!ProcessSecHandlers(outmsg, "outgoinging")) {
00285       logger.msg(ERROR,
00286                  "Security check failed in GSI MCC for outgoing message");
00287       return MCC_Status();
00288     }
00289   }
00290 
00291   MCC_GSI_Client::MCC_GSI_Client(Config& cfg, ModuleManager& mm)
00292     : MCC(&cfg),
00293       ctx(GSS_C_NO_CONTEXT) {
00294     //globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE);
00295     globus_openldap_lock(mm);
00296     //if (!proxy_initialized)
00297     //  proxy_initialized = GlobusRecoverProxyOpenSSL();
00298     proxyPath = (std::string)cfg["ProxyPath"];
00299     certificatePath = (std::string)cfg["CertificatePath"];
00300     keyPath = (std::string)cfg["KeyPath"];
00301   }
00302 
00303   MCC_GSI_Client::~MCC_GSI_Client() {
00304     if (ctx != GSS_C_NO_CONTEXT) {
00305       OM_uint32 majstat, minstat;
00306       majstat = gss_delete_sec_context(&minstat, &ctx, GSS_C_NO_BUFFER);
00307       ctx = GSS_C_NO_CONTEXT;
00308     }
00309     //globus_module_deactivate(GLOBUS_GSI_GSSAPI_MODULE);
00310   }
00311 
00312   MCC_Status MCC_GSI_Client::process(Message& inmsg, Message& outmsg) {
00313 
00314     if (ctx == GSS_C_NO_CONTEXT) {
00315       MCC_Status status = InitContext();
00316       if (!status)
00317         return status;
00318     }
00319 
00320     if (!inmsg.Payload())
00321       return MCC_Status();
00322 
00323     PayloadRawInterface *inpayload =
00324       dynamic_cast<PayloadRawInterface*>(inmsg.Payload());
00325 
00326     if (!ProcessSecHandlers(inmsg, "outgoing")) {
00327       logger.msg(ERROR,
00328                  "Security check failed in GSI MCC for outgoing message");
00329       return MCC_Status();
00330     }
00331 
00332     PayloadRaw gsipayload;
00333     int size = 0;
00334 
00335     for (int n = 0; inpayload->Buffer(n); ++n) {
00336 
00337       gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
00338       gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
00339 
00340       recv_tok.value = inpayload->Buffer(n);
00341       recv_tok.length = inpayload->BufferSize(n);
00342 
00343       logger.msg(VERBOSE, "Recieved token length: %i", recv_tok.length);
00344 
00345       OM_uint32 majstat, minstat;
00346 
00347       majstat = gss_wrap(&minstat,
00348                          ctx,
00349                          0,
00350                          GSS_C_QOP_DEFAULT,
00351                          &recv_tok,
00352                          NULL,
00353                          &send_tok);
00354       if (GSS_ERROR(majstat)) {
00355         logger.msg(ERROR, "GSS wrap failed: %i/%i%s", majstat, minstat, GSSCredential::ErrorStr(majstat, minstat));
00356         return MCC_Status();
00357       }
00358 
00359       logger.msg(INFO, "GSS wrap: %i/%i", majstat, minstat);
00360 
00361       logger.msg(VERBOSE, "Sent token length: %i", send_tok.length);
00362 
00363       gsipayload.Insert((const char*)send_tok.value, size, send_tok.length);
00364       size += send_tok.length;
00365     }
00366 
00367     Message nextinmsg = inmsg;
00368     nextinmsg.Payload(&gsipayload);
00369     Message nextoutmsg = outmsg;
00370     nextoutmsg.Payload(NULL);
00371 
00372     MCCInterface *next = MCC::Next();
00373     if (!next)
00374       return MCC_Status();
00375     MCC_Status ret = next->process(nextinmsg, nextoutmsg);
00376     // TODO: handle errors and incompatible payloads
00377 
00378     if (!ProcessSecHandlers(outmsg, "incoming")) {
00379       logger.msg(ERROR,
00380                  "Security check failed in GSI MCC for incoming message");
00381       return MCC_Status();
00382     }
00383 
00384     PayloadStreamInterface *payload =
00385       dynamic_cast<PayloadStreamInterface*>(nextoutmsg.Payload());
00386 
00387     outmsg.Payload(new PayloadGSIStream(payload, ctx, logger, true));
00388 
00389     return MCC_Status(STATUS_OK);
00390   }
00391 
00392   void MCC_GSI_Client::Next(MCCInterface *next, const std::string& label) {
00393     if (label.empty())
00394       if (ctx != GSS_C_NO_CONTEXT) {
00395         OM_uint32 majstat, minstat;
00396         majstat = gss_delete_sec_context(&minstat, &ctx, GSS_C_NO_BUFFER);
00397         ctx = GSS_C_NO_CONTEXT;
00398       }
00399     MCC::Next(next, label);
00400   }
00401 
00402   MCC_Status MCC_GSI_Client::InitContext() {
00403 
00404     // Send empty payload in order to get access to message attributes
00405     MessageAttributes reqattr;
00406     MessageAttributes repattr;
00407     Message reqmsg;
00408     Message repmsg;
00409     MessageContext context;
00410 
00411     reqmsg.Attributes(&reqattr);
00412     reqmsg.Context(&context);
00413     repmsg.Attributes(&repattr);
00414     repmsg.Context(&context);
00415 
00416     PayloadRaw request;
00417     reqmsg.Payload(&request);
00418 
00419     MCC_Status status = MCC::Next()->process(reqmsg, repmsg);
00420 
00421     std::string remoteip = repmsg.Attributes()->get("TCP:REMOTEHOST");
00422 
00423     sockaddr_in sa;
00424     sa.sin_family = AF_INET;
00425     inet_pton(AF_INET, remoteip.c_str(), &sa.sin_addr);
00426 
00427     char host[NI_MAXHOST];
00428     memset(host,0,NI_MAXHOST);
00429     if(getnameinfo((sockaddr*)&sa, sizeof(sa), host, NI_MAXHOST, NULL, 0,
00430                 NI_NAMEREQD)) {
00431       struct hostent* hp;
00432 #ifndef WIN32
00433       hp = gethostbyaddr((void*)(&(sa.sin_addr.s_addr)), sizeof(sa.sin_addr.s_addr), AF_INET);
00434 #else
00435       hp = gethostbyaddr((char*)(&(sa.sin_addr.s_addr)), sizeof(sa.sin_addr.s_addr), AF_INET);
00436 #endif
00437       if(hp == NULL) {
00438         logger.msg(ERROR, "Could not resolve peer side's hostname");
00439         return MCC_Status();
00440       }
00441       std::string tmp(hp->h_name);
00442       memcpy(host, tmp.c_str(), tmp.length()); 
00443    }
00444 
00445     OM_uint32 majstat, minstat;
00446 
00447     GSSCredential cred(proxyPath, certificatePath, keyPath);
00448 
00449     gss_name_t target_name = GSS_C_NO_NAME;
00450 
00451     gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
00452     gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
00453 
00454     OM_uint32 req_flags;
00455     OM_uint32 ret_flags;
00456 
00457     std::string hostname = "host@";
00458     hostname += host;
00459 
00460     gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
00461     namebuf.value = (void*)hostname.c_str();
00462     namebuf.length = hostname.size();
00463 
00464     logger.msg(VERBOSE, "Peer host name to which this client will access: %s", hostname.c_str());
00465 
00466     majstat = gss_import_name(&minstat, &namebuf, GSS_C_NT_HOSTBASED_SERVICE,
00467                               &target_name);
00468     if (GSS_ERROR(majstat)) {
00469       logger.msg(ERROR, "GSS import name failed: %i/%i%s", majstat, minstat, GSSCredential::ErrorStr(majstat, minstat));
00470       return MCC_Status();
00471     }
00472 
00473     //if(((gss_cred_id_t&)(cred)) == GSS_C_NO_CREDENTIAL) { 
00474     //  req_flags = GSS_C_ANON_FLAG;
00475     //  logger.msg(DEBUG, "Anonymous GSI communication (no client-side authentication)");
00476     //}
00477     //req_flags |= GSS_C_CONF_FLAG;
00478     //req_flags |= GSS_C_MUTUAL_FLAG;
00479     //req_flags |= GSS_C_INTEG_FLAG;
00480     //req_flags |= GSS_C_REPLAY_FLAG;
00481     //req_flags |= GSS_C_DELEG_FLAG;
00482 
00483     req_flags = (gss_cred_id_t&)(cred) == GSS_C_NO_CREDENTIAL ? 
00484         (GSS_C_ANON_FLAG | GSS_C_CONF_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG) :
00485         (GSS_C_CONF_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG);
00486 
00487     do {
00488       majstat = gss_init_sec_context(&minstat,
00489                                      cred,
00490                                      &ctx,
00491                                      target_name,
00492                                      GSS_C_NO_OID,
00493                                      req_flags,
00494                                      0,
00495                                      GSS_C_NO_CHANNEL_BINDINGS,
00496                                      &recv_tok,
00497                                      NULL,
00498                                      &send_tok,
00499                                      &ret_flags,
00500                                      NULL);
00501       if (GSS_ERROR(majstat)) {
00502         logger.msg(ERROR, "GSS init security context failed: %i/%i%s", majstat, minstat, GSSCredential::ErrorStr(majstat, minstat));
00503         return MCC_Status();
00504       }
00505 
00506       logger.msg(INFO, "GSS init security context: %i/%i", majstat, minstat);
00507 
00508       logger.msg(VERBOSE, "Sent token length: %i", send_tok.length);
00509 
00510       MessageAttributes reqattr;
00511       MessageAttributes repattr;
00512       Message reqmsg;
00513       Message repmsg;
00514       MessageContext context;
00515 
00516       reqmsg.Attributes(&reqattr);
00517       reqmsg.Context(&context);
00518       repmsg.Attributes(&repattr);
00519       repmsg.Context(&context);
00520 
00521       PayloadRaw request;
00522       if (send_tok.length > 0)
00523         request.Insert((const char*)send_tok.value, 0, send_tok.length);
00524       reqmsg.Payload(&request);
00525 
00526       MCC_Status status = MCC::Next()->process(reqmsg, repmsg);
00527 
00528       if ((majstat & GSS_C_SUPPLEMENTARY_MASK) == GSS_S_CONTINUE_NEEDED) {
00529 
00530         if (!repmsg.Payload()) {
00531           logger.msg(ERROR, "No payload during GSI context initialisation");
00532           return MCC_Status();
00533         }
00534 
00535         PayloadStreamInterface *response =
00536           dynamic_cast<PayloadStreamInterface*>(repmsg.Payload());
00537 
00538         int pos = 0;
00539         char readbuf[5];
00540         while (5 > pos) {
00541           int len = 5 - pos;
00542           response->Get(&readbuf[pos], len);
00543           pos += len;
00544         }
00545 
00546         if (readbuf[0] >= 20 && readbuf[0] <= 23)
00547           logger.msg(VERBOSE, "Transfer protocol is TLS or SSL3");
00548         else if (readbuf[0] == 26)
00549           logger.msg(VERBOSE, "Transfer protocol is GLOBUS SSL");
00550         else if (readbuf[0] & 0x80 && readbuf[0] <= 23)
00551           logger.msg(VERBOSE, "Transfer protocol is SSL2");
00552         else
00553           logger.msg(VERBOSE, "Transfer protocol is GSI");
00554 
00555         recv_tok.length = (unsigned char)readbuf[3] * 256 +
00556                           (unsigned char)readbuf[4] + 5;
00557         recv_tok.value = malloc(recv_tok.length);
00558         memcpy(recv_tok.value, readbuf, 5);
00559 
00560         logger.msg(VERBOSE, "Recieved token length: %i", recv_tok.length);
00561 
00562         while (recv_tok.length > pos) {
00563           int len = recv_tok.length - pos;
00564           response->Get(&((char*)recv_tok.value)[pos], len);
00565           pos += len;
00566         }
00567       }
00568     } while ((majstat & GSS_C_SUPPLEMENTARY_MASK) == GSS_S_CONTINUE_NEEDED);
00569 
00570     majstat = gss_release_buffer(&minstat, &send_tok);
00571     majstat = gss_release_buffer(&minstat, &recv_tok);
00572 
00573     return MCC_Status(STATUS_OK);
00574   }
00575 
00576 } // namespace Arc
00577 
00578 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00579   { "gsi.service", "HED:MCC", 0, &Arc::get_mcc_service },
00580   { "gsi.client", "HED:MCC", 0, &Arc::get_mcc_client },
00581   { NULL, NULL, 0, NULL }
00582 };