Full passthrough gateway with direct security session

A client can establish a security session using the remote server only. No per-packet security services are performed at any intermediaries between the client and the remote server. If the client requests confidentiality, then the gateway cannot retrieve TDS tokens from the message packets. This arrangement saves overhead since no per-packet services are performed within the gateway, such as those used to decrypt received packets, and to re-encrypt them before transmission.

There may be multiple gateway intermediaries forming a chain of “forwarding servers.” In this case, each of these forwarding servers must support the same security mechanism.

To set up a direct security session, take the following steps in the connection handler of the Open Server gateway application:

  1. Use srv_getloginfo to obtain login information from the client thread.

  2. Use ct_setloginfo to set this information in the connection structure to be used for connecting to the remote server.

  3. Install a security session callback, using the following command:

    ct_callback(conn, CS_SET, CS_SECSESSION_CB, secsession_cb)
    

    When the connection to the remote server is made, the callback acts as an intermediary for the handshaking required between the remote server and the gateway’s client.

    See “Security session callbacks” for information on what the callback should contain.

    See the Open Client Client-Library/C Reference Manual for further information on callbacks.

  4. Call ct_connect to connect to the remote server. This call initiates negotiations between the client and remote server to establish a security session. If ct_connect returns CS_SUCCEED, then a security session has been successfully established.

  5. Use srv_senddone(SRV_DONE_FINAL) to signal to the client that the login is complete.


Example connection handler

CS_RETCODE CS_PUBLIC connect_handler(spp) 
 SRV_PROC *spp;
 {
    CS_CONNECTION *conn;    /* connection handle */
    CS_VOID       *creds;   /* security credentials */
    CS_LOGINFO    *loginfo; /* login information */
    CS_BOOL       boolval;
   ......
   allocate and set user data in spp
   /* Allocate a connection handle for the connection attempt. */
    if (ct_con_alloc(Context, &(userdata->conn)) == CS_FAIL)
    {
       handle failure...
   }
   ......
   conn = userdata->conn;
   /* 
    ** Save the pointer to thread control structure in the
    ** connection handle 
    */
    if (ct_con_props(conn, CS_SET, CS_USERDATA, &spp,
       CS_SIZEOF(spp), (CS_INT *)NULL) == CS_FAIL)
    {
       handle failure...
   }
   /* Verify that security based login is requested */
    if (srv_thread_props(spp, CS_GET, SRV_T_SEC_NETWORKAUTH,
       (CS_VOID *)&boolval, CS_SIZEOF(CS_BOOL), (CS_INT *)NULL)
       == CS_FAIL) 
    {
       handle failure...
   }
   if (boolval != CS_TRUE)
    {
       handle the client request that does not use security
       session based login
      ......
      return(CS_SUCCEED);
    }
  /* Get and set the login information */
    if (srv_getloginfo(spp, &loginfo) == CS_FAIL)
    {
       handle failure...
   }
   if (ct_setloginfo(conn, loginfo) == CS_FAIL)
    {
       handle failure...
   }
   /* Install a security session callback for this connection */
    if (ct_callback((CS_CONTEXT *)NULL, conn, CS_SET,
       CS_SECSESSION_CB, (CS_VOID *)secsession_cb) == CS_FAIL)
    {
       handle failure...
   }
   /* Attempt a connection to the remote server */
    if (ct_connect(conn, Servername, CS_NULLTERM) == CS_FAIL)
    {
       handle failure...
   }
   /* Get and set the login information */
    if (ct_getloginfo(conn, &loginfo) == CS_FAIL)
    {
       handle failure...
   }
   if (srv_setloginfo(spp, loginfo) == CS_FAIL)
    {
       handle failure...
   }
   ......
   /* 
    ** You do not need to test this srv_senddone’s return value
    ** since Open Server will kill this thread if this call fails. 
    */
    (CS_VOID)srv_senddone(spp, SRV_DONE_FINAL, CS_TRAN_UNDEFINED,
                  (CS_INT)0);
    return(CS_SUCCEED);
 }

Security session callbacks

The security session callback routine exchanges security tokens between the target server (or the next intermediary of the gateway) and the gateway’s client applications to establish a direct security session between the client and the remote server. This callback procedure is similar to a challenge-response callback, except that it uses different parameters.

When the gateway calls ct_connect, the remote server issues one or more messages that contain security session information. For each security message, Client-Library invokes the callback with the message parameters sent by the remote server.

The callback routine must perform the following functions:

  1. Retrieve the parameters from the remote server’s message.

  2. Send the parameters to the client, using:

    • srv_negotiate(..., CS_SET, SRV_NEG_SECSESSION)

    • srv_descfmt(..., CS_SET, SRV_NEGDATA, ...)

    • srv_bind(..., CS_SET, ...)

    • srv_xferdata(..., CS_SET, ...)

  3. Send a srv_senddone(SRV_DONE_FINAL) to the client.

  4. Wait for a response from the client, using srv_negotiate(CS_GET, SRV_NEG_SECSESSION).

  5. When the client responds, the callback routine copies the corresponding session data from the client to output buffers and sends it to the remote server, using the following functions:

    • srv_descfmt(CS_GET)

    • srv_bind(CS_GET)

    • srv_xferdata(CS_GET)

  6. If the remote server sends another security message, the process repeats.

See the Open Client Client-Library/C Reference Manual for information on defining security session callbacks.


Example Client-Library security session callback routine

CS_RETCODE CS_PUBLIC secsession_cb(conn, innumparams, infmt,
    inbuf, outnumparams, outfmt, outbuf, outlen)
 CS_CONNECTION *conn;
 CS_INT        innumparams;
 CS_DATAFMT    *infmt;
 CS_BYTE       **inbuf;
 CS_INT        *outnumparams;
 CS_DATAFMT    *outfmt;
 CS_BYTE       **outbuf;
 CS_INT        *outlen;
 {
   SRV_PROC *spp; /* The SRVPROC structure associated with the
                   ** client connection */
    CS_INT i;
   /* Get the previously saved spp for the client */
    if (ct_con_props(conn, CS_GET, CS_USERDATA, &spp, 
       CS_SIZEOF(spp), (CS_INT *)NULL) != CS_SUCCEED)
    {
       return(CS_FAIL);
    }
   /* 
    ** Use srv_negotiate to tell the client to expect a security
    ** token 
    */
    if (srv_negotiate(spp, CS_SET, SRV_NEG_SECSESSION)
       != CS_SUCCEED)
    {
       return(CS_FAIL);
    }
  /* Describe and send the security token */
    for (i = 0; i < innumparams; i++) 
    {
       if (srv_descfmt(spp, CS_SET, SRV_NEGDATA, i + 1, &infmt[i] 
          != CS_SUCCEED) 
       {
          return(CS_FAIL);
       }
      if (srv_bind(spp, CS_SET, SRV_NEGDATA, i + 1, &infmt[i], 
          inbuf[i], &(infmt[i]->maxlength), (CS_SMALLINT *)NULL)
          != CS_SUCCEED)
       {
          return(CS_FAIL);
       }
   }
   if (srv_xferdata(spp, CS_SET, SRV_NEGDATA) != CS_SUCCEED) 
    {
       return(CS_FAIL);
    }
   /* Complete this portion of the exchange */
    if (srv_senddone(spp, SRV_DONE_FINAL, CS_TRAN_UNDEFINED, 0) 
       != CS_SUCCEED)
    {
       return(CS_FAIL);
    }
   /* Wait until the client responds */
    if (srv_negotiate(spp, CS_GET, SRV_NEG_SECSESSION)
       != CS_SUCCEED)
    {
       return(CS_FAIL);
    }
   /* Get the number of parameters in the client’s response */
    if (srv_numparams(spp, outnumparams) != CS_SUCCEED)
    {
       return(CS_FAIL);
    }
   /* Read in the client’s response */
    for (i = 0; i < (*outnumparams); i++)
    {
       srv_bzero(&outfmt[i], sizeof(CS_DATAFMT));
      if (srv_descfmt(spp, CS_GET, SRV_NEGDATA, i + 1, &outfmt[i]
          != CS_SUCCEED) 
       {
          return(CS_FAIL);
       }
      if (srv_bind(spp, CS_GET, SRV_NEGDATA, i + 1, &outfmt[i], 
          outbuf[i], &outlen[i], (CS_SMALLINT *)NULL)
          != CS_SUCCEED)
       {
          return(CS_FAIL);
       }
   }
   if (srv_xferdata(spp, CS_GET, SRV_NEGDATA) != CS_SUCCEED) 
    {
       return(CS_FAIL);
    }
   /* Return success */
    return(CS_SUCCEED);
 }