Chapter 4. Using the Library

After initialization of the library, the core part of the library is run within a loop until it has finished. The library is handed input from the other protocol entity and results in output which is to be sent to the other entity, or an error code. The library does not send data to the server itself, but only return it in buffers. The main interface to the library uses binary data, but since many common protocols uses Base 64 encoded data, a wrapper around the main function is also provided.

The following pseudo code illustrates how the library is used in a simple client. All the functions used are explained later on in this manual.

main()
{
  Gsasl_ctx          *ctx;
  Gsasl_session_ctx  *cctx;
  char *input, output[BUFFERSIZE];
  size_t output_len;
  int rc;

  rc = gsasl_init (&ctx);
  if (rc != GSASL_OK)
    die(gsasl_strerror(rc));

  /* XXX Set callbacks here */

  /* Read supported SASL mechanism from server */
  input = read_from_client();

  /* Select a good mechanism */
  mech = gsasl_client_suggest_mechanism (ctx, input);
  if (mech == NULL)
    die("Cannot find any commonly agreed SASL mechanism...");

  /* Start to use it */
  res = gsasl_client_start (ctx, mech, &cctx);
  if (res != GSASL_OK)
    die(gsasl_strerror (rc));

  input = NULL;
  do
    {
      /* Do one SASL step and unless we're done, send the output to
         server and read new data from server */

      rc = gsasl_client_step_base64 (cctx, input, output, BUFFERSIZE);
      if (rc != GSASL_NEEDS_MORE && rc != GSASL_OK)
        break;

      write_to_server(output);

      if (rc == GSASL_OK)
        break;

      input = read_from_server();
    }
  while (rc == GSASL_NEEDS_MORE);

  if (rc != GSASL_OK)
    die("Authentication failed... %s\n", gsasl_strerror(rc);

  /* Client is now authenticated -- proceed with actual protocol... */

  gsasl_client_finish (cctx);
  gsasl_done (ctx);
}

Notice the XXX comment that said you should specify the callbacks to use there. `Libgsasl' depend on callbacks to implement user interaction (in the client) and user validation (in the server). If you don't specify any callbacks, very few mechanisms will be supported (like EXTERNAL that don't need any additional information, Section 5.1). Since we are building a simple client, we define callbacks which are used by several SASL mechanisms to get username and password. We start by defining the function for querying the username, following the prototype for Gsasl_client_callback_authentication_id for the LOGIN mechanism (Section 5.4) .

int
callback_username (Gsasl_session_ctx *ctx,
                   char *out,
                   size_t *outlen)
{
  char username[BUFFERSIZE];

  if (out == NULL)
    *outlen = BUFFERSIZE;
  else
    {
      fprintf(stdout, "Enter username: ");
      fgets(username, BUFFERSIZE, stdin);
      *outlen = strlen(BUFFERSIZE);
    }

  return GSASL_OK;
}

As you can see, this is a simplistic function that reads a username from the user. The callback for entering the password is similar and follows the Gsasl_client_callback_password prototype:

int
callback_password (Gsasl_session_ctx *ctx,
                   char *out,
                   size_t *outlen)
{
  char password[BUFFERSIZE];

  if (out == NULL)
    *outlen = BUFFERSIZE;
  else
    {
      fprintf(stdout, "Enter password: ");
      fgets(password, BUFFERSIZE, stdin);
      *outlen = strlen(BUFFERSIZE);
    }

  return GSASL_OK;
}

In reality, the program should probably inhibit echo of the password to the terminal, but that is left as an exercise for the reader.

Now having implemented the callbacks, we are ready to replace the XXX comment with real code that set the callbacks (Chapter 7). The following does it.

  gsasl_client_callback_authentication_id_set(ctx, callback_username);
  gsasl_client_callback_authorization_id_set(ctx, callback_username);
  gsasl_client_callback_password_set(ctx, callback_password);

Notice that we use the same callback for the authentication identity and the authorization identity. In reality, this may be too simplistic, but will do for an example.

The simple client is now complete, and will be able to support SASL mechanisms such as PLAIN and CRAM-MD5.

Implementing a server is very similar to the client, the only difference is that you use gsasl_server_* functions instead of gsasl_client_* and instead of implementing Gsasl_client_* callbacks implement some Gsasl_server_* callbacks. See each mechanism (Chapter 5) for details on which callbacks are required and their prototype.

A note for server authors is in place, on the optional initial client output (discussed in section 5.1 of RFC 2222). In a server looking similar to the code above, the first call to gsasl_server_step_base64 would have a input set to NULL. The mechanisms interprete this as your protocol do not support initial client output. If the protocol in which you implement SASL supports initial client output, the first call to gsasl_server_step_base64 should include a real buffer with the initial client data.

One note for client authors is in place. The code above aborts processing if `Libgsasl' did not come out of the loop with a GSASL_OK exit code. It is a mistake to not require this, and instead only look at what the server is sending you. Even if the server said you are authenticated, it does not always mean that the SASL mechanism is satisfied. This is specifically true for SASL client mechanisms which perform server authentication. Thus, if you only trust what the server replied instead of requireing a GSASL_OK result, you may open up for fake servers. Don't shortcut the loop with a positive server response.