License Rehosting

Rehostable Licenses

Activation can be set up to support rehosting of licenses by the end user without ISV involvement. A rehostable hostid is requested at the time that rlm_activate() is called, and the activation server issues the license to this hostid. Later, if the user wishes to move this license to another system, the rlm_act_rehost_revoke() call can be used to revoke the rehostable license and allow it to be activated on another system. Note that the rehostable hostid is a copy-protected file, and if the system disk should crash, the hostid cannot be retrieved. As an ISV, you will need a policy to deal with this situation – e.g., to give the customer another activation in order to continue. Since this situation should be rare, a non-automated request procedure might be quite sufficient.

Rehostable license support is limited to nodelocked, uncounted (or single) licenses and product definitions that specify only a single license. In addition, only one version of any product can exist with a rehostable hostid on a system at any given time. Attempting to activate a 2nd product with a different version number will give an RLM_EH_REHOST_EXISTS (-153) error from activation. Attempting to activate either a counted license or a product with multiple licenses will give an RLM_ACT_NO_REHOST (-1035) error. Finally, you should always use a fixed expiration date if you are using the Normal fulfillment type for Rehostable licenses. If you want an expiration date that is relative to the time of fulfillment, then you should use a ReActivate fulfillment type and (beginning in RLM v11.0) set the count to the # of times you want your customer to be able to revoke and re-activate the license.

Note that you have a choice when it comes to the revocation of rehostable licenses. By default, RLM Activation Pro will delete the hostid when rlm_act_rehost_revoke() is called, but if the license has expired, the Activation Pro server will not return count to the activation key, and it will return the status RLM_ACT_REVOKE_TOOLATE to the application. However, you can configure Activation Pro to allow the server to return count to the activation key in this case, and return a good status to the application. See Database Tab for more information on setting this parameter.

Rehostable hostids do two checks at verification time which fail on certain systems. These checks are:

  • Checking the file ID of each file in the rehostable hierarchy, and

  • Checking the native hostid of the system (we refer to this hostid as the “reference hostid”)

The file ID check fails on Windows systems if drives are added or removed from the disk controller which has the rehostable file hierarchy. We have seen the native hostid change on CentOS systems when the network cable is unplugged. Beginning in RLM v12.3, you can disable the file ID checks on windows by calling rlm_isv_cfg_disable_windows_fileid_check() . You can disable the native hostid check by calling rlm_isv_cfg_disable_reference_hostid_check(). Note that you can call these functions in your rlm_isv_config.c file, so that they apply new defaults to all your RLM-licensed applications, or you can call them inline when you want to disable either check.

RLM selects a reference hostid automatically, however you can override this choice starting in RLM v12.3 by calling rlm_set_attr_reference_hostid(). If you do override the reference hostid, you must be sure to set the same reference hostid that you set when you created the rehostable hostid when you attempt to call rlm_act_rehost_revoke(… RLM_ACT_REVOKE_REFERENCE) (or rlm_act_revoke_reference()). See rlm_set_attr_reference_hostid() for a description of the call.


How to activate with a rehostable hostid

In order to create a rehostable hostid, you must set the activation handle to indicate that you want to activate with a rehostable hostid. To do this, you use code similar to the following (this code is from the example in the section below):

act_handle = rlm_act_new_handle(rh);
rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_REHOST, (void *) 1);

stat = rlm_activate(
        rh,             /* RLM handle */
        "http://reprisesoftware.com", /* URL */
        akey,           /* Activation key */
        1,              /* count - # of licenses */
        license,        /* Space for the returned license */
        act_handle      /* Activation handle data */
        );

rlm_act_destroy_handle(act_handle); /* Done with this */

If you do not set the handle to RLM_ACT_HANDLE_REHOST before the rlm_activate() call, you will not create a rehostable hostid and the license will be activated using the default hostid on the machine.


If You Don’t Want to Always Use Rehostable Hostids

Since a rehostable hostid must be requested at activation time by setting the handle before you call rlm_activate(), you must know before activation time whether you want to use a normal or a rehostable hostid. If you know this on a product-by-product basis, you can set the allowed hostids in the product definition, setting only “rehostable” in the product definition for a product that always uses rehostable hostids. If your products can use rehostable or regular hostids, but you know which at activation key creation time, you can set the allowed hostids in the activation key. In either case, use an algorithm similar to the following:

  1. Attempt the activation for a “normal” hostid. If this succeeds, you are done.

  2. If the normal hostid activation fails, re-attempt the activation specifying a rehostable hostid.

  3. If both attempts (1) and (2) fail, then the product definition doesn’t specify a rehostable hostid and the default hostid on this system isn’t in the list of allowed hostids, either.

If you don’t know before activation time, you will have to prompt the user and allow both rehostable and regular hostids in the product definition/activation key, then make the rlm_activate() request with the correct parameter.

How do I set up Activation Pro to accept the correct type of hostid?

This is really straightforward.

  • The only way to get a rehostable hostid is to request it explicitly, in which case, only the rehostable hostid will get sent to the ActPro server.

  • If you don’t request it, RLM will fill in the list of “standard” hostids and send those to the server.

Now, as far as your Activation Pro server is concerned:

  • If your activation key specifies rehostables only, then only a rehostable will work.

  • If your activation key specifies other types of hostids, then only those hostids will work.

  • If your activation keys specifies nothing, then the hostid requirement falls back to the product definition:

    • If your product definition specifies rehostables only, then only a rehostable will work.

    • If your product definition specifies other types of hostids, then only those hostids will work.

  • If neither the activation key nor the product definition specifies allowed hostids, then the default is used, which is set to include rehostables (unless you change it in rlm_isv_config.c).


What happens when my customer inadvertently deletes the rehostable hostid data?

When you create a rehostable license, you write a license file which specifies a rehostable hostid. The rehostable hostid is a copy-protected directory hierarchy on the computer. Sometimes, your customer will delete the license file or delete/invalidate the hostid. What do you do in this case?

If your customer deletes the license file, you first have to determine the hostid that was used for this product, then you must contact the activation server to retrieve the license. You can do this by first retrieving the rehostable hostid with the rlm_get_rehost() API call (v11.1), then asking the activation server for the license activated on that hostid with the rlm_act_keyvalid_license() call. You will need to know the product name and activation key to do this. The following code snippet illustrates this operation:

char product_name[RLM_MAX_PRODUCT+1];
char hostid[RLM_MAX_HOSTID_STRING+1];
char license[RLM_ACT_MAX_LICENSE+1];
char *akey;
stat = rlm_get_rehost(rh, product_name, hostid);
akey = "activation key";
if (!stat)
{
        /* Request the license from the activation server */
        stat = rlm_act_keyvalid_license(rh, "yourURL.com", akey, hostid, license);
        if (!stat) /* Write license out to license file */
}

If your customer deletes or invalidates the hostid, you have the option of allowing them to revoke the license on the same machine where they activated it. In your call to rlm_act_rehost_revoke(), specify RLM_ACT_REVOKE_REFERENCE in the flags (4th parameter). rlm_act_rehost_revoke() is new in RLM v14.1, and should be used for all new applications. Prior to v14.1, the rlm_act_revoke_reference() call performed this function. Prior to RLM v14.1, you revoked by reference with the rlm_act_revoke_reference() call.

Note

You should always attempt to do the rlm_act_revoke() operation first; only if it returns RLM_EH_CANT_GET_REHOST should you call rlm_act_revoke_reference(). The following sample code snippet illustrates this operation:

stat = rlm_act_revoke(rh, "yourURL.com", product_name);
if (stat == RLM_EH_CANT_GET_REHOST)
{
        /* Some error message here, perhaps */
        stat = rlm_act_revoke_reference(rh, "yourURL.com", product_name);
}

Rehostable License Example Code

An example for rehostable licenses is on the RLM kit in the examples directory, called rehost_example.c It is repeated here.

Rehostable License Example code
/******************************************************************************

  COPYRIGHT (c) 2005, 2022 by Reprise Software, Inc.
  This software has been provided pursuant to a License Agreement
  containing restrictions on its use.  This software contains
  valuable trade secrets and proprietary information of
  Reprise Software Inc and is protected by law.  It may not be
  copied or distributed in any form or medium, disclosed to third
  parties, reverse engineered or used in any manner not provided
  for in said License Agreement except with the prior written
  authorization from Reprise Software Inc.

 *****************************************************************************/
/*
 *  Description:    Example client for RLM license rehosting/revoking
 *
 *  M. Christiano
 *  11/18/11 - modified from the regular activation example
 *
 *  $Id: rehost_example.c,v 1.22 2022/03/08 17:06:26 matt Exp $
 */

#include "license.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define URL "hostedactivation.com"
#define ISVNAME "rlmactdemo"
#define PRODUCT "actdemo"

static int checkstat(RLM_HANDLE, RLM_LICENSE, const char *);
static int doactivation(RLM_HANDLE, const char *);
static int retrieve_license(RLM_HANDLE, const char *);
static int dodeactivation(RLM_HANDLE, const char *);
char akey[RLM_MAX_LINE+1];

    int
main(int argc, char *argv[])
{
    RLM_HANDLE rh = (RLM_HANDLE) NULL;
    RLM_LICENSE lic = (RLM_LICENSE) NULL;
    int stat;
    const char *product = PRODUCT;
    int pass;

    akey[0] = '\0';
    for (pass = 1; pass <= 3; pass++)
    {
        if (pass == 3)
        {
            /*
             *      We did not get the license on the first pass, and
             *      activation didn't work.  See if we can figure out what
             *      happened and correct it.
             */
            if (stat == RLM_EH_REHOST_EXISTS)
            {
                /*
                 *          The rehostable hostid exists for the "actdemo" product.
                 *          Most likely, this is because the license file was deleted
                 *          or misplaced.  In this case, retrieve the rehostable hostid
                 *          and request a "standard" activation with that hostid.
                 */
                printf("The rehostable hostid exists.  We will now\n");
                printf("attempt to re-activate using this hostid.\n\n");
                stat = retrieve_license(rh, product);
                rlm_close(rh);      /* Close the old handle */
                if (stat < 0) exit(2);
            }
        }
        else if (pass == 2)
        {
            /*
             *      We did not get the license on the first pass.  See
             *      if we can activate it now.
             */
            stat = doactivation(rh, product);
            if (stat == RLM_EH_REHOST_EXISTS) continue;
            rlm_close(rh);      /* Close the old handle */
        }
        /*
         *      NOTE that rlm_init() is in this loop.  This is necessary
         *      because if the license is not acquired on the first pass,
         *      the activation request (above) will create a new
         *      license file.  rlm_init() must be called to see this
         *      new license file.
         */
        rh = rlm_init(".", argv[0], (char *) NULL);
        stat = rlm_stat(rh);
        if ((pass == 1) && (stat == RLM_EH_READ_NOLICENSE))
        {
            /*
             *      We didn't find a license.  Checkout won't work, so just
             *      skip to the 2nd pass where we try to activate it.
             */
            continue;
        }
        else if (stat)
        {
            (void) printf("Error %d initializing license system\n", stat);
            exit(1);
        }

        lic = rlm_checkout(rh, product, "1.0", 1);

        stat = checkstat(rh, lic, product);

        if ((pass == 1) && (stat < 0))
        {
            /*
             *      Didn't get the license.  Try a 2nd time to activate it.
             */
            if (lic) rlm_checkin(lic);
            lic = (RLM_LICENSE) NULL;
            continue;
        }
        else if (stat == 0)
        {
            /*
             *          We got the license
             */
            break;
        }
    }

    if (stat == 0)
    {
        /*
         *      We got the license
         */
        if (lic) rlm_checkin(lic);
        printf("License valid.\n");
        stat = dodeactivation(rh, product);
        rlm_close(rh);
    }
    else if (pass == 2)
    {
        /*
         *      checkout and/or activation failed.
         */
        (void)
            printf("Unable to check out/activate \"actdemo\" license\n");
    }
    return(0);
}

static
    int
checkstat(RLM_HANDLE rh, RLM_LICENSE lic, const char *product)
{
    int stat;
    char errstring[RLM_ERRSTRING_MAX];

    stat = rlm_license_stat(lic);
    if (stat == 0)
        (void) printf("Checkout of %s license OK.\n", product);
    else
    {
        (void) printf("Error checking out %s license\n", product);
        (void) printf("%s\n", rlm_errstring(lic, rh, errstring));
    }
    return(stat);
}

#include <time.h>
#ifndef _WIN32
#include <sys/time.h>
#endif

/*
 *  write_license() - write the license file out.
 */
static
    int
write_license(char *license)
{
    char name[100];
    int try;
    int stat=0;
    FILE *f;
    /*
     *  Activation was successful.  Write the license out.
     *  Note in this example, we try the license file name
     *  aN.lic, and we only try 100 different names.  You
     *  should change this to whatever naming convention you
     *  want to use.
     */
    for (try=0; try<100; try++)
    {
        sprintf(name, "a%d.lic", try);
        f = fopen(name, "r");
        if (f == (FILE *) NULL)
        {
            struct tm *t;
#ifdef _WIN32
            time_t ltime;

            time(&ltime);
            t = localtime(&ltime);
#else
            struct timezone tz;
            struct timeval tv;
            time_t x;

            gettimeofday(&tv, &tz);
            x = tv.tv_sec;
            t = localtime((time_t *) &x);
#endif

            f = fopen(name, "w");
            if (f)
            {
                fprintf(f, "This license created by RLM Internet Activation\n");
                if (t)
                    fprintf(f, "Created on %02d/%02d/%04d at %02d:%02d\n",
                            t->tm_mon+1, t->tm_mday,
                            t->tm_year+1900, t->tm_hour,
                            t->tm_min);

                fprintf(f, "\n%s\n", license);
                fclose(f);
                printf(
                        "Activation successful, license file \"%s\" written\n",
                        name);
                break;
            }
            else
            {
                printf( "Error writing license file \"%s\"\n", name);
                stat = -1;
                break;
            }
        }
    }
    return(stat);
}

/*
 *  display_error() - display the error return from activation.
 */
static
    void
display_error(RLM_HANDLE rh, int stat)
{
    printf("\n");
    switch(stat)
    {
        char err[RLM_ERRSTRING_MAX+1];

        case RLM_EH_CANTCONNECT_URL:
        printf("You were unable to connect to %s.\n", URL);
        printf("Please make sure that this system is able to\n");
        printf("access the internet and try again.\n");
        break;

        case RLM_ACT_NO_KEY_MATCH:
        printf("The activation key you supplied (%s) was \n", akey);
        printf("not found.  Please check the key and ensure\n");
        printf("that you have entered it correctly.\n");
        break;

        case RLM_ACT_KEY_USED:
        printf("The activation key you supplied (%s)\n", akey);
        printf("has already been used to activate a license.\n");
        printf("Please check the key and ensure that you have entered it correctly.\n");
        break;

        case RLM_EH_BAD_HTTP:
        printf("Bad HTTP transaction\n%s\n", rlm_errstring(0, rh, err));
        break;

        case 0:
        break;

        default:
        printf("Error %d requesting activation\n%s\n", stat,
                RLM_ACT_ERR(stat) ?
                rlm_act_errstring(stat) :
                rlm_errstring(0, rh, err));
        break;

    }
    printf("\n");
}

/*
 *  Prompt for the activation key, if not entered already.
 */
static
    int
get_akey(const char *product)
{
    int len;

    if (*akey) return(0);

    (void) printf("\nWould you like to activate this license now? ");
    fgets(akey, RLM_MAX_LINE, stdin);
    if (*akey == 'y' || *akey == 'Y')
    {
        (void) printf("Enter Activation key for \"%s\": ", product);
        fgets(akey, RLM_MAX_LINE, stdin);
        len = ((int) strlen(akey)) - 1;
        if (akey[len] == '\n') akey[len] = '\0';
        return(0);
    }
    return(1);
}

/*
 *  doactivation() - Perform the activation request.
 */

static
    int
doactivation(RLM_HANDLE rh, const char *product)
{
    char license[3 * RLM_MAX_LINE + 1];   /* Allow for HOST, ISV, and LICENSE */
    int stat = RLM_EH_READ_NOLICENSE; /* If they say NO, no license */
    RLM_ACT_HANDLE act_handle;

    if (get_akey(product)) return(stat);
    /*
     *  Request the license.  First make a handle, and tell it we
     *  want a rehostable hostid to activate.
     */
    act_handle = rlm_act_new_handle(rh);
    rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_REHOST, (void *) 1);
    /*
     *  Note - you would normally never need to make this next call,
     *  but we do it here so that we can connect to the license
     *  generator on the reprise demo site.
     */
    rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_ISV, ISVNAME);

    stat = rlm_activate(
            rh,         /* RLM handle */
            URL,
            akey,       /* Activation key */
            1,      /* count - # of licenses */
            license,    /* Space for the returned license */
            act_handle  /* Activation handle data */
            );

    rlm_act_destroy_handle(act_handle); /* Done with this */

    if ((stat == 0) || (stat == 1)) stat = write_license(license);
    display_error(rh, stat);
    return(stat);
}

/*
 *  This routine handles the case where the license was activated, but
 *  the license file was lost/misplaced.  Get the hostid and re-activate
 *  using it.
 */

static
    int
retrieve_license(RLM_HANDLE rh, const char *product)
{
    char license[3 * RLM_MAX_LINE + 1];   /* Allow for HOST, ISV, and LICENSE */
    char hostid[RLM_MAX_HOSTID_STRING+1];
    int stat = RLM_EH_READ_NOLICENSE; /* If they say NO, no license */
    RLM_ACT_HANDLE act_handle;

    if (get_akey(product)) return(stat);
    /*
     *  Get the rehostable hostid for this product.
     */
    stat = rlm_get_rehost(rh, PRODUCT, hostid);
    if (stat)
    {
        printf("Rehostable hostid does not exist, error: %d\n", stat);
        return(stat);
    }
    printf("%s product has hostid %s\n", PRODUCT, hostid);
    /*
     *  Re-Request the license using the hostid we just retrieved.
     */
    act_handle = rlm_act_new_handle(rh);
    rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_HOSTID_LIST, hostid);
    /*
     *  Note - you would normally never need to make this next call,
     *  but we do it here so that we can connect to the license
     *  generator on the reprise demo site.
     */
    rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_ISV, ISVNAME);

    stat = rlm_activate(
            rh,         /* RLM handle */
            URL,
            akey,       /* Activation key */
            1,      /* count - # of licenses */
            license,    /* Space for the returned license */
            act_handle  /* Activation handle data */
            );

    rlm_act_destroy_handle(act_handle); /* Done with this */

    if ((stat == 0) || (stat == 1)) stat = write_license(license);
    display_error(rh, stat);
    return(stat);
}

static
    int
dodeactivation(RLM_HANDLE rh, const char *product)
{
    int stat = RLM_EH_READ_NOLICENSE; /* If they say NO, no license */
    char x[100];

    (void) printf("\nWould you like to deactivate the \"%s\" license now? ",
            product);
    fgets(x, RLM_MAX_LINE, stdin);
    if (*x == 'y' || *x == 'Y')
    {
        /*
         *      Request the deactivation.
         */
        stat = rlm_act_revoke(rh, URL, (char *) product);

        if (stat == 0)
        {
            /*
             *              revoke (Deactivation) was successful.
             */
            printf("License successfully revoked\n");
        }
        else
        {
            char err[RLM_ERRSTRING_MAX+1];

            printf("\n");
            printf("Error %d requesting license revoke\n%s\n",
                    stat,
                    RLM_ACT_ERR(stat) ?
                    rlm_act_errstring(stat) :
                    rlm_errstring(0, rh, err));
            printf("\n");
        }
    }
    return(stat);
}

Rehostable Hostids on Disconnected Systems

Sometimes it is desirable to create a rehostable hostid on a system with no access to the internet. RLM allows you to do this; it is a 3-step process, with the first step performed on the disconnected system, then the next on an internet-connected system. Finally, the resulting license must be transferred back to the disconnected system. Similarly, revoking a rehostable hostid on a disconnected system is a 2-step process – first the revoke is started on the disconnected system, then finished on a system with a connection to the internet.

Creating a Rehostable Hostid on a Disconnected System

Normally, a rehostable hostid is activated by calling rlm_activate() with a handle that specifies the RLM_ACT_HANDLE_REHOST parameter and the activation key. When the system is disconnected from the internet, the procedure is as follows:

Step 1: call rlm_activate() with a handle that specifies RLM_ACT_HANDLE_REHOST, RLM_ACT_HANDLE_DISCONN, and the product name in RLM_ACT_HANDLE_PRODUCT. You must specify the product name on the disconnected system. The activation key is not required. rlm_activate() will return a set of data (in the “license” parameter) which you must then transmit to the system that is connected to the internet.

Step 2: On the internet-connected system, call rlm_activate() with a handle that specifies RLM_ACT_HANDLE_REHOST, RLM_ACT_HANDLE_DISCONN, nothing in the product name, and the data from step 1 in RLM_ACT_HANDLE_HOSTID_LIST. The activation key is required in this call, and it must correspond to the product name specified in step 1. This call will return the license (in the “license” parameter).

Step 3: take the license from step 2, and install it on the disconnected system.

For example:

Run this on the disconnected system:

#define URL "your-activation-server-URL"
#include "license.h"
RLM_ACT_HANDLE act_handle;
char data[3*(RLM_MAX_LINE+1)];
int stat;

        rh = rlm_init();
        act_handle = rlm_act_new_handle(rh);
        stat = rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_REHOST, 1);
        stat = rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_DISCONN, 1);
        stat = rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_PRODUCT, "product-name");
        stat = rlm_activate(rh, URL, "", 0, data, act_handle);
        (void) rlm_act_destroy_handle(act_handle);

Take the result in “data” and run this on the internet-connected system:

#define URL "your-activation-server-URL"
#include "license.h"
RLM_ACT_HANDLE act_handle;
char license[3*(RLM_MAX_LINE+1)];
char activation_key; /* should be set to the activation key */
int stat;

        rh = rlm_init();
        act_handle = rlm_act_new_handle(rh);
        stat = rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_REHOST, 1);
        stat = rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_DISCONN, 1);
        stat = rlm_act_set_handle(act_handle, RLM_ACT_HANDLE_HOSTID_LIST, data);
        stat = rlm_activate(rh, URL, activation_key, 0, license, act_handle);
        (void) rlm_act_destroy_handle(act_handle);

Finally, take the result in “license” and install it in a license file on the disconnected system.

Revoking a Rehostable Hostid on a Disconnected System

Normally, a rehostable hostid is revoked by calling rlm_act_rehost_revoke() with the server’s URL and the product name. When the system is disconnected from the internet, the procedure is as follows:

Step 1: on the disconnected system, call rlm_act_revoke_disconn(), specifying a NULL URL and the name of the product in the third parameter. This will return a verification string in the last parameter.

Step 2: transfer the verification string to the the internet-connected system, call rlm_act_revoke_disconn() specifying the URL and the verification string in the third parameter.

For example:

Run this on the disconnected system:

#include "license.h"
char verification[3*(RLM_MAX_LINE+1)];
int stat;

        rh = rlm_init();
        stat = rlm_act_revoke_disconn(rh, "", "product-name", verification);

Take the result in “verification” and run this on the internet-connected system:

#define URL "your-activation-server-URL"
#include "license.h"
char license[3*(RLM_MAX_LINE+1)];
int stat;

        rh = rlm_init();
        stat = rlm_act_revoke_disconn(rh, URL, verification, (char *) NULL);