5.1.3. Example for authentication integration

Adding LinOTP authentication to your existing source code is quite simple.

You will need to use the API as described in section Validate Controller.

Both examples use cURL [1] to talk to the LinOTP server.

5.1.3.1. C integration

For usual binary programs written in C, you could take a look at the pam_linotp implementation (file libpam-linotp/src/pam_linotp.c) which can be found here: http://linotp.org/download/LinOTP-2.x-CE.tar.gz.

This could look like this to create the URL, that needs to be sent:

char * createUrl(CURL *curl_handle,
    const char * validateurl,
    const char * realm, const char * resConf,
    const char * user, const char * password)
{
 char *   url   = NULL;
 int size    = 300;
 int nchars     = 0;

 // escape user and password
 char *escPassword;
 char *escUser;
 escPassword    = curl_easy_escape(curl_handle, password, 0);
 escUser     = curl_easy_escape(curl_handle, user, 0);
 char *escRealm = curl_easy_escape(curl_handle, realm, 0);
 char *escResConf     = curl_easy_escape(curl_handle, resConf, 0);

 if (escPassword == NULL || escUser == NULL)
 {
        log_error("ERROR: failed to escape user or password");
        goto cleanup;
 }

    url = (char*) malloc (size);
    if (url == NULL)
    {
        log_error("ERROR: could allocate size for url");
        goto cleanup;
    }

 // allocate the memory for url string
 memset(url,'\0',size);
 nchars = snprintf( url, size-1, "%s?user=%s&pass=%s&realm=%s&resConf=%s",
                validateurl,
          escUser, escPassword,
    escRealm, escResConf );
 if (nchars >= size-1)
 {
    // reallocate
    size = nchars +1;
    url = (char*) myrealloc (url, size);
    if (url == NULL)
    {
        log_error("ERROR: failed to alloc space for url + user and password");

             goto cleanup;
    }

    memset(url,'\0',size);
    snprintf(url, size-1, "%s?user=%s&pass=%s&realm=%s&resConf=%s", validateurl,
             escUser, escPassword,
             escRealm, escResConf );
 }

cleanup:
 return url;
}

The authentication itself could look like this:

int linotp_auth(const char *user, const char *password, const char *validateurl,
    const char *realm, const char *resConf,
    int nosslhostnameverify, int nosslcertverify, int debug)
{
 CURL *curl_handle       = NULL;
 int    returnValue         = PAM_AUTH_ERR;
 char *url               = NULL;
 CURLcode all_status        = 0;

 char errorBuffer[CURL_ERROR_SIZE];

 struct MemoryStruct chunk;
    chunk.memory      = NULL; /* we expect realloc(NULL, size) to work */
    chunk.size        = 0;    /* no data at this point */

    curl_global_init(CURL_GLOBAL_ALL);

    curl_handle = curl_easy_init();

    if (curl_handle == NULL)
    {
        log_error("ERROR: could not get curl_handle!");
       returnValue = PAM_AUTH_ERR;
       goto cleanup;
    }


    curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);

    url = createUrl(curl_handle, validateurl, realm, resConf, user, password);
 if (url == NULL)
 {
        log_error("ERROR: could allocate size for url");
    goto cleanup;
 }

 if (debug)
 {
    log_debug("DEBUG: connecting to url:%s",url);
 }

 all_status = sendRequest(curl_handle, url,
       (void *)&chunk, nosslhostnameverify, nosslcertverify);

  /* set default return*/
 returnValue = PAM_AUTH_ERR;

 if (all_status != 0)
 {
    log_error("ERROR: Error talking to linotpd server at %s: %s", url, errorBuffer);
    returnValue = PAM_AUTH_ERR;
    goto cleanup;
 }
 if(chunk.memory == NULL)
 {
    log_error("ERROR: No response returned for %s: %s", url, errorBuffer);
    returnValue = PAM_AUTH_ERR;
    goto cleanup;
 }
 if (strcmp(chunk.memory, LINOTPD_REJECT) == 0)
 {
    log_warning("WARNING: user '%s' rejected", user);
    returnValue = PAM_AUTH_ERR;
    goto cleanup;
 }
 if (strcmp(chunk.memory, LINOTPD_FAIL ) == 0)
 {
    log_warning("WARNING: authentication for '%s' failed", user);
    returnValue = PAM_AUTH_ERR;
    goto cleanup;
 }
 if (strcmp( chunk.memory, LINOTPD_OK ) == 0)
 {
    log_info("INFO: user '%s' authenticated successfully\n", user);
    returnValue = PAM_SUCCESS;
    goto cleanup;
 }
 // default
 {
    log_error("ERROR: An error occurred for '%s' on '%s'\n", user, validateurl);
    returnValue = PAM_AUTH_ERR;
    goto cleanup;
 }


cleanup:

 if (chunk.memory!= NULL)
 {
    free(chunk.memory);
 }
 if (url != NULL)
 {
    free(url);
 }

    /* we're done with libcurl, so clean it up
       cleanup also takes care of escPassword and escUser
    */
 curl_global_cleanup();
 if (debug)
 {
    log_debug("linotp_auth exited normally.");
 }
 return (returnValue);
}

In this example we defined a helper function sendRequest:

int sendRequest(CURL *curl_handle, char * url,
    struct MemoryStruct * chunk,
    int nosslhostnameverify, int nosslcertverify)
{
 int all_status = 0;
 int status     = 0;

 all_status  = 0;
 status      = curl_easy_setopt(curl_handle, CURLOPT_URL, url);
 all_status += status;

 status      = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
 all_status += status;

 status      = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, chunk);
 all_status += status;

 status      = curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
 all_status += status;

 if ( nosslhostnameverify )
    status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
 else
    status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L);
 all_status += status;

 if ( nosslcertverify )
       status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
 else
       status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L);
 all_status += status;

 status      = curl_easy_perform(curl_handle);
 all_status += status;

 curl_easy_cleanup(curl_handle);

 return all_status;
}

5.1.3.2. PHP integration

For usual web applications or portals that might be written in PHP you could use code like this:

<?php
public function linotp_auth($user="", $pass="", $realm="") {
   $ret=false;
   try {
      $server = $this->server;
      $REQUEST="https://$server/validate/check?pass=$pass&user=$user";
      if (""!=$realm)
         $REQUEST="$REQUEST&realm=$realm";
      if (!function_exists("curl_init"))
         die("cURL extension is not installed!");
      $ch=curl_init($REQUEST);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $this->verify_host);
      $r=curl_exec($ch);
      curl_close($ch);

      $Object = json_decode($r);
      if (true == $Object->{'result'}->{'status'} )
         if (true == $Object->{'result'}->{'value'} )
            $ret=true;
   } catch (Exception $e) {
      print "Error in receiving response from LinOTP server: $e";
   }
   return $ret;
}
?>
[1]http://curl.haxx.se