Skip to content

Latest commit

 

History

History
866 lines (664 loc) · 33.9 KB

opencryptoki-howto.md

File metadata and controls

866 lines (664 loc) · 33.9 KB

PKCS #11 openCryptoki for Linux HOWTO

v1 - Kristin Thomas - [email protected]

v2 - Eduardo Barretto - [email protected]

This HOWTO describes the implementation of the RSA Security Inc./Organization for the Advancement of Structured Information Standards (OASIS) Public Key Cryptographic Standard #11 (PKCS #11) cryptoki application program interface (API) on Linux (openCryptoki). The HOWTO explains what services openCryptoki provides and how to build and install it. Additional resources and a simple sample program are also provided.

Table of contents

  1. Copyright Notice and Disclaimer
  2. Introduction
  3. What is openCryptoki?
  4. Architectural Overview
    4.1. Slot Manager
    4.2. Main API
    4.3. Slot Token Dynamic Link Libraries
    4.4. Shared Memory
  5. Getting Started with openCryptoki
    5.1. System Requirements
    5.2. Obtaining openCryptoki
    5.3. Compiling and Installing openCryptoki
  6. Configuring openCryptoki
  7. Components of openCryptoki
    7.1. Slot Manager Daemon
    7.2. libopencryptoki.so
    7.3. Slot Token DLLs
      7.3.1. Trusted Module Platform
      7.3.2. IBM Cryptographic Architecture (ICA)
      7.3.3. IBM Common Cryptographic Architecture (CCA)
      7.3.4. Software Token
      7.3.5. IBM Enterprise PKCS #11 (EP11)
      7.3.6. IBM Integrated Cryptographic Service Facility (ICSF)
  8. Applications and openCryptoki
    8.1. Making openCryptoki Available to Applications
    8.2. Writing an Application
  9. Resources
  10. Appendix A: Sample Program
    10.1. Sample Program
    10.2. Makefile

1. Copyright Notice and Disclaimer

Copyright © 2001 - 2017 IBM Corporation. All rights reserved.

This document may be reproduced or distributed in any form without prior permission provided the copyright notice is retained on all copies. Modified versions of this document may be freely distributed, provided that they are clearly identified as such, and this copyright is included intact.

This document is provided "AS IS," with no express or implied warranties. Use the information in this document at your own risk.

Special Notices

This publication/presentation was produced in the United States. IBM may not offer the products, programs, services or features discussed herein in other countries, and the information may be subject to change without notice. Consult your local IBM business contact for information on the products, programs, services, and features available in your area. Any reference to an IBM product, program, service, or feature is not intended to state or imply that only IBM’s product, program, service, or feature may be used. Any functionally equivalent product, program, service, or feature that does not infringe on IBM’s intellectual property rights may be used instead.

Questions on the capabilities of non-IBM products should be addressed to suppliers of those products. IBM may have patents or pending patent applications covering subject matter in this presentation. Furnishing this presentation does not give you any license to these patents. Send license inquiries, in writing, to IBM Director of Licensing, IBM Corporation, New Castle Drive, Armonk, NY 10504-1785 USA. All statements regarding IBM’s future direction and intent are subject to change or withdrawal without notice, and represent goals and objectives only. Contact your local IBM office or IBM authorized reseller for the full text of a specific Statement of General Direction.

The information contained in this presentation has not been submitted to any formal IBM test and is distributed "AS IS." While each item may have have been reviewed by IBM for accuracy in a specific situation, there is no guarantee that the same or similar results will be obtained elsewhere. The use of this information or the implementation of any techniques described herein is a customer responsibility and depends on the customer’s ability to evaluate and integrate them into the customer’s operational environment. Customers attempting to adapt these techniques to their own environments do so at their own risk.

The information contained in this document represents the current views of IBM on the issues discussed as of the date of publication. IBM cannot guarantee the accuracy of any information presented after the date of publication.

Any performance data in this document was determined in a controlled environment. Therefore, the results obtained in other operating environments may vary significantly. Some measurements quoted in this book may have been made on development-level systems. There is no guarantee these measurements will be the same on generally-available systems. Some measurements quoted in this book may have been estimated through extrapolation. Actual results may vary. Users of this book should verify the applicable data for their specific environment.

A full list of U.S. trademarks owned by IBM may be found at http://www.ibm.com/legal/copytrade.shtml. Linux is a trademark of Linus Torvalds. Other company, product, and service names may be trademarks or service marks of others.

2. Introduction

Cryptography is rapidly becoming a critical part of our daily lives. However, the application of cryptographic technology adds a heavy computational burden to today's server platforms. More systems are beginning to use specialized hardware to offload the computations, as well as to help ensure the security of secret key material. In this HOWTO we will discuss openCryptoki, an API that is rapidly becoming the defacto, non-Windows-platform industry standard for interfacing between cryptographic hardware and user space applications. In particular we will introduce the specifics of the PKCS #11 implementation to IBM cryptographic hardware (openCryptoki).

3. What is openCryptoki?

openCryptoki is an implementation of the PKCS #11 API that allows interfacing to devices (such as a smart card, smart disk, or PCMCIA card) that hold cryptographic information and perform cryptographic functions. openCryptoki provides application portability by isolating the application from the details of the cryptographic device. Isolating the application also provides an added level of security because all cryptographic information stays within the device. The openCryptoki API provides a standard programming interface between applications and all kinds of portable cryptographic devices.

4. Architectural Overview

openCryptoki consists of a slot manager and an API for Slot Token Dynamic Link Libraries (STDLLs). The slot manager runs as a daemon to control the number of slots provided to applications, and it interacts with applications using a shared memory region. Each device that has a token associated with it places that token into a slot in the slot manager database. The shared memory region allows for proper sharing of state information between applications to help ensure conformance with the PKCS #11 specification.

4.1. Slot Manager

The Slot Manager Daemon (pkcsslotd) manages slots (and therefore tokens) in the system. A fixed number of processes can be attached to pkcsslotd, so a static table in shared memory is used. The current limit of the table is 1000 processes using the subsystem. The daemon sets up this shared memory upon initialization and acts as a garbage collector thereafter, helping to ensure that only active processes remain registered. When a process attaches to a slot and opens a session, pkcsslotd will make future processes aware that a process has a session open and will lock out certain function calls, if the they need exclusive access to the given token. The daemon will constantly search through its region of shared memory and make sure that when a process is attached to a token it is actually running. If an attached process terminates abnormally, pkcsslotd will "clean up" after the process and free the slot for use by other processes.

4.2. Main API

The main API for the STDLLs lies in /usr/lib/opencryptoki/libopencryptoki.so. This API includes all the functions as outlined in the PKCS #11 API specification. The main API provides each application with the slot management facility. The API also loads token specific modules (STDLLs) the provide the token specific operations (cryptographic operations and session and object management). STDLLs are customized for each token type and have specific functions, such as an initialization routine, to allow the token to work with the slot manager. When an application initializes the subsystem with the C_Initialize call, the API will load the STDLL shared objects for all the tokens that exist in the configuration (residing in the shared memory) and invoke the token specific initialization routines.

4.3. Slot Token Dynamic Link Libraries

STDLLs are plug-in modules to the main API. They provide token-specific functions beyond the main API functions. Specific devices can be supported by building an STDLLs for the device. Each STDLLs must provide at least a token specific initialization function. If the device is an intelligent device, such as a hardware adapter that supports multiple mechanisms, the STDLL can be thin because much of the session information can be stored on the device. If the device only performs a simple cryptographic function, all of the objects must be managed by the software. This flexibility allows the STDLLs to support any cryptographic device.

4.4. Shared Memory

The slot manager sets up its database in a region of shared memory. Since the maximum number of processes allowed to attach to pkcsslotd is finite, a fixed amount of memory can be set aside for token management. This fixed memory allotment management allows applications easier access to token state information and helps ensure conformance with the PKCS #11 specification.

5. Getting Started with openCryptoki

This section describes the system requirements for openCryptoki. It also explains where you can get openCryptoki and how to compile and install it.

5.1. System Requirements

openCryptoki installs by default a software token that relies on software to deliver the crypto functions. So it is possible to install it even if you don't have physical (hardware) token.

The following lists show the system requirements for running openCryptoki.

Hardware Requirements

  • openCryptoki is supported on ppc64, s390x and x86.

Software Requirements

  • Linux operating system running at least a 2.2.16 kernel
  • Device drivers and associated support libraries for the installed tokens (some of the header files from those distributions may also be required)

5.2. Obtaining openCryptoki

The openCryptoki project and source code is hosted on GitHub. You can find openCryptoki releases (tarball) on GitHub and, as well, on SourceForge. For any issue, questions or development related subjects, please contact us on the mailing list.

5.3. Compiling and Installing openCryptoki

Assuming that the device support (and header files) for the required devices are on the system, then you can build openCryptoki by entering the source code main directory and do the following:

  1. Run the bootstrap.sh script by typing:

$ ./bootstrap.sh

  1. Configure the source code by typing:

$ ./configure

If you're planning to install the package into your home directory or to a location other than /usr/local then add the flag --prefix=PATH to configure. For example, if your home directory is /home/luser you can configure the package to install itself there by invoking:

$ ./configure --prefix=/home/luser

If your stdll headers and libraries are not under any standard path, you will need to pass the paths to your files for the configure script. For instance:

$ CPPFLAGS="-L/path/lib" LDFLAGS="-I/path/include" ./configure

See ./configure --help for info on various options. The default behavior is to build a default token implicitly. For the s390 platform, the default token is ICA. For other platforms, the default token is the software token. Other tokens may be enabled using the corresponding --enable-<tok> configuration option provided the appropriate libraries are available.

While running, configure prints some messages telling which features is it checking for.

  1. Compile the package by typing:

$ make

  1. openCryptoki defaults to be usable by anyone who is in the group pkcs11, Add the pkcs11 group before installing it, by typing as root the command:

# groupadd pkcs11

In addition, add the necessary user to the pkcs11 group (root doesn't need to be in the pkcs11 group):

# usermod -G pkcs11 <user>

  1. Type make install (as root) to install the programs and any data files and documentation. During installation, the following files go to the following directories:
    /prefix/sbin/pkcsconf
    /prefix/sbin/pkcsslotd
    /prefix/sbin/pkcsicsf
    /prefix/libdir/libopencryptoki.so
    /prefix/libdir/libopencryptoki.so.0
    /prefix/libdir/opencryptoki/libopencryptoki.so
    /prefix/libdir/opencryptoki/libopencryptoki.so.0
    /prefix/libdir/opencryptoki/libopencryptoki.so.0.0.0
    /prefix/var/lib/opencryptoki
    /prefix/etc/opencryptoki/opencryptoki.conf

Token objects, which may be optionally built, go to the following locations:

    /prefix/libdir/opencryptoki/stdll/libpkcs11_cca.so
    /prefix/libdir/opencryptoki/stdll/libpkcs11_cca.so.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_cca.so.0.0.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_ep11.so
    /prefix/libdir/opencryptoki/stdll/libpkcs11_ep11.so.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_ep11.so.0.0.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_ica.so
    /prefix/libdir/opencryptoki/stdll/libpkcs11_ica.so.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_ica.so.0.0.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_icsf.so
    /prefix/libdir/opencryptoki/stdll/libpkcs11_icsf.so.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_icsf.so.0.0.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_sw.so
    /prefix/libdir/opencryptoki/stdll/libpkcs11_sw.so.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_sw.so.0.0.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_tpm.so
    /prefix/libdir/opencryptoki/stdll/libpkcs11_tpm.so.0
    /prefix/libdir/opencryptoki/stdll/libpkcs11_tpm.so.0.0.0

where prefix is either /usr/local/ or the PATH that you specified in the --prefix flag. libdir is the name of the library directory, for 32-bit libraries it is usually lib and for 64-bit libraries it is usually lib64.

To maintain backwards compatibility, some additional symlinks are generated (note that these are deprecated and applications should migrate to use the LSB-compliant name and locations for libraries and executable):

    /prefix/lib/opencryptoki/PKCS11_API.so
    - Symlink to /prefix/lib/opencryptoki/libopencryptoki.so

    /prefix/lib/opencryptoki/stdll/PKCS11_CCA.so
    - Symlink to /prefix/lib/opencryptoki/stdll/libpkcs11_cca.so

    /prefix/lib/opencryptoki/stdll/PKCS11_EP11.so
    - Symlink to /prefix/lib/opencryptoki/stdll/libpkcs11_ep11.so

    /prefix/lib/opencryptoki/stdll/PKCS11_ICA.so
    - Symlink to /prefix/lib/opencryptoki/stdll/libpkcs11_ica.so

    /prefix/lib/opencryptoki/stdll/PKCS11_ICSF.so
    - Symlink to /prefix/lib/opencryptoki/stdll/libpkcs11_icsf.so

    /prefix/lib/opencryptoki/stdll/PKCS11_SW.so
    - Symlink to /prefix/lib/opencryptoki/stdll/libpkcs11_sw.so

    /prefix/lib/pkcs11/PKCS11_API.so
    - Symlink to /prefix/lib/opencryptoki/libopencryptoki.so

    /prefix/lib/pkcs11
    - Directory created if non-existent

    /prefix/lib/pkcs11/methods
    - Symlink to /prefix/sbin

    /prefix/lib/pkcs11/stdll
    - Symlink to /prefix/lib/opencryptoki/stdll

    /prefix/etc/pkcs11
    - Symlink to /prefix/var/lib/opencryptoki

If any of these directories do not presently exist, they will be created on demand. Note that if prefix is /usr, then /prefix/var and /prefix/etc resolve to /var and /etc. On the make install stage, if content exists in the old /prefix/etc/pkcs11 directory, it will be migrated to the new /prefix/var/lib/opencryptoki location.

If you are installing in your home directory make sure that /home/luser/bin is in your path. If you're using the bash shell add this line at the end of your .bashrc file:

    PATH="/home/luser/bin:${PATH}"
    export PATH

If you are using csh or tcsh, then use this line instead:

setenv PATH /home/luser/bin:${PATH}

By prepending your home directory to the rest of the PATH you can override systemwide installed software with your own custom installation.

6. Configuring openCryptoki

See: https://www.ibm.com/support/knowledgecenter/linuxonibm/com.ibm.linux.z.lxce/lxce_stackoverview.html

Prior to version 3, openCryptoki used pk_config_data as its configuration file. This file was created upon running pkcs11_startup. In version 3, pkcs11_startup and pk_config_data have been removed and replaced with a customizable config file named, opencryptoki.conf. It contains an entry for each token currently supported by openCryptoki. However, only those token, whose hardware and software requirements are available on the local system, will show up as present and available upon running the pkcsconf -t command.

Before using, each token must be first initialized. You can select the token with the -c command line option; refer to the documentation linked to above for further instructions.

Initialize a particular token by running pkcsconf:

$ pkcsconf -I -c

In this version of openCryptoki, the default SO PIN is 87654321. This should be changed to a different PIN value before use.

You can change the SO PIN by running pkcsconf :

$ pkcsconf -P -c

You can initialize and change the user PIN by typing:

$ pkcsconf -u -c

You can later change the user PIN again by typing:

$ pkcsconf -p -c

7. Components of openCryptoki

This section describes the different components of the openCryptoki subsystem.

7.1. Slot Manager Daemon

The slot manager daemon is an executable (/usr/sbin/pkcsslotd) that reads in /etc/opencryptoki/opencryptoki.conf, populating shared memory according to what devices have been found within the system. pkcsslotd then continues running as a daemon. Any other applications attempting to use the subsystem must first attach to the shared memory region and register as part of the API initialization process, so pkcsslotd is aware of the application. If /etc/opencryptoki/opencryptoki.conf/ is changed, pkcsslotd must be stopped and restarted to read in the new configuration file. The daemon can be stopped by issuing the pkill pkcsslotd command or through systemd systemctl stop pkcsslotd. The daemon will not terminate if there are any applications using the subsystem.

7.2. libopencryptoki.so

This library contains the main API (/usr/lib/opencryptoki/libopencryptoki.so) and is loaded by any application that uses any PKCS #11 token managed by the subsystem. Before an application uses a token, it must load the API and call C_Initialize, as per the PKCS #11 specification. The loading operation is performed by the application using the dlopen facilities.

7.3. Slot Token DLLs

Six STDLLs ship in the initial offering. These support Trusted Platfrom Module (TPM, <2.0), IBM Cryptographic Architecture (ICA), IBM Common Cryptographic Architecture (CCA), Soft Token, IBM Enterprise PKCS #11 (EP11) and IBM Integrated Cryptographic Service Facility (ICSF).

**Note**: The compilation process attempts to build all of the tokens that
are supported on the target platform, as well as all of the required support
programs. If some of the headers and libraries are not present, those
components will not be built.
7.3.1. Trusted Module Platform (TPM)

In order to be able to build the TPM stdll you first need:

  1. Enable tpm in BIOS settings.

  2. Install trousers, trousers-devel, tpm-tools and tpm-tools-pkcs11 as root. Package names can differ depending on the Linux distribution.

  3. As root run the following commands:

    Start the tcsd daemon
    # /etc/init.d/tcsd start  or # systemctl start tcsd

    Enter tpm passwords
    # tpm_takeownership
    Enter owner password:
    Confirm password:
    Enter SRK password:
    Confirm password:

    # tpm_setpresence
    Enter owner password:
    Physical Presence Status:
        Command Enable: true
        Hardware Enable: false
        Lifetime Lock: true
        Physical presence: false
        Lock: true

After setting up the TPM the openCryptoki compilation should automatically build the tpm stdll. If it doesn't, then please run:

./configure --enable-tpmtok

For more information check README.tpm_stdll

7.3.2. IBM Cryptographic Architecture (ICA)

The IBM Cryptographic Architecture (ICA) is a hardware token that is available only for s390 systems. If you are in this platform and have the necessary hardware, you can build openCryptoki with the ICA stdll. To achieve it you need first install the libica package. This package is available in the Linux distributions repositories.

7.3.3. IBM Common Cryptographic Architecture (CCA)

The IBM Common Cryptographic Architecture (CCA) is also a hardware token that is only available for the s390 architecture. If you are in this platform and have the necessary hardware then you can build openCryptoki with the CCA stdll. First, you need to install the csulcca library on your system. To get this package click here and be sure to choose the package corresponding to your crypto card version.

For more information about CCA, read README.cca_stdll and README.pkcscca_migrate.

7.3.4. Software Token

This token is a software emulation of a token. All the cryptographic operations needed will be run in a software implementation of such cryptographic algorithms. This implementation is given by OpenSSL and the Soft token is built by default with openCryptoki.

7.3.5. IBM Enterprise PKCS #11 (EP11)

This is another hardware token for the s390 architecture. In order to be able to build openCryptoki with EP11 stdll download the necessary library from here. Be sure to choose the driver corresponding to your crypto card version.

For more information about EP11, please refer to README.ep11_stdll.

7.3.6. IBM Integrated Cryptographic Service Facility (ICSF)

The ICSF token is a remote crypto token. The actual crypto operations are performed remotely on a s390 server and all the PKCS #11 key objects are stored remotely on the server. This calls to the remote server are done via LDAP.

So, to build openCryptoki with LDAP, you need to install on the client side: openldap, openldap-clients and openldap-devel.

For more information about ICSF, head over to README.icsf_stdll.

8. Application and openCryptoki

This section describes how to make openCryptoki available to applications and provides an example of how to write such an application.

8.1. Making openCryptoki Available to Applications

Many applications use PKCS #11 tokens. Most of these applications must be configured to load specific shared object (DLL) for the token. In the case of openCryptoki, only one module (/usr/lib/opencryptoki/libopencryptoki.so) must be loaded for access to all the tokens currently running in the subsystem. Multiple token types are supported, with each type taking up a slot in the subsystem according to the implementation specifics of the plug-in module.

If devices are added or removed, the PKCS #11 slot where the token resides may change. For this reason, applications should locate the specific token by the token label provided when the token is initialized and not assume that a specific slot always contains the desired token.

For application-specific configuration information relating to the exploitations of PKCS #11, refer to the application's documentation.

8.2. Writing an Application

To develop an application that uses openCryptoki, you must first load the shared object using the dynamic library calls. Then call C_GetFunctionList. For example, the following routines loads the shared library and gets the function list for subsequent calls.

CK_FUNCTION_LIST *funcs;

int do_GetFunctionList(void)
{
    CK_RV rc;
    CK_RV (*pfoo)();
    void *d;
    char *e;
    char f[]="/usr/lib/pkcs11/PKCS11_API.so"

    printf("do_GetFunctionList...\n");

    d = dlopen(f, RTLD_NOW);
    if (d == NULL)
        return FALSE;

    pfoo = (CK_RV (*)())dlsym(d, "C_GetFunctionList");
    if (pfoo == NULL)
        return FALSE;

    rc = pfoo(&funcs);

    if (rc != CKR_OK) {
        show_error("C_GetFunctionList rc=%d\n", rc);
        return FALSE;
    }

    printf("Looks okay...\n");
    return TRUE;
}

Once loaded, the application must call the C_Initialize function. In the previous example, the function would be invoked with the following lines:

CK_C_INITIALIZE_ARGS cinit_args;
memset(&cinit_args, 0x0, sizeof(cinit_args));
funcs->C_Initialize(&cinit_args);

Refer to the PKCS #11 specification available from the OASIS web site (https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=pkcs11) for more options.

Note: openCryptoki requires that operating systems threads be allowed. If other thread routines are passed in, they are ignored. If the no-os threads argument is set in the initialize arguments structure, the call to C_Initialize will fail.

9. Resources

For additional information about PKCS #11 and openCryptoki, see the following resources:

10. Appendix A: Sample Program

The following sample program prints out all of the current tokens and slots in use in the system. If you want to build the sample program, you will also need the Makefile after the sample.

10.1. Sample Program

#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <dlfcn.h>
#include <pkcs11types.h>

#define CFG_SLOT        0x0004
#define CFG_PKCS_INFO   0X0008
#define CFG_TOKEN_INFO  0x0010

CK_RV init(void);
CK_RV cleanup(void);
CK_RV get_slot_list(int, CK_CHAR_PTR);
CK_RV display_slot_info(void);
CK_RV display_token_info(void);

void *dll_ptr;
CK_FUNCTION_LIST_PTR    function_ptr = NULL;
CK_SLOT_ID_PTR          slot_list = NULL;
CK_ULONG                slot_count = 0;
int in_slot;

int main(int argc, char *argv[])
{
    CK_RV rc;                   /* Return Code */
    CK_FLAGS flags = 0;         /* Bit Mask for what options were passed in */
    CK_CHAR_PTR slot = NULL;    /* The PKCS slot number */

    /* Load the PKCS11 library */
    init();

    /* Get the slot list and indicate if a slot number was passed in or not */
    get_slot_list(flags, slot);

    /* Display the current token and slot info */
    display_token_info();
    display_slot_info();

    /* We are done, free the memory we may have allocated */
    free(slot);
    return rc;
}

CK_RV get_slot_list(int cond, CK_CHAR_PTR slot)
{
    CK_RV rc;   /* Return code */

    /* Find out how many tokens are present in the slots */
    rc = function_ptr->C_GetSlotList(TRUE, NULL_PTR, &slot_count);
    if (rc != CKR_OK) {
        printf("Error getting number of slots: 0x%X\n", rc);
        return rc;
    }

    /* Allocate enough space for the slots information */
    slot_list = (CK_SLOT_ID_PTR) malloc(slot_count*sizeof(CK_SLOT_ID));

    rc = function_ptr->C_GetSlotList(TRUE, slot_list, &slot_count);
    if (rc != CKR_OK) {
        printf("Error getting slot list: 0x%X\n", rc);
        return rc;
    }

    return rc;
}

CK_RV display_slot_info(void)
{
    CK_RV           rc;         /* Return Code */
    CK_SLOT_INFO    slot_info;   /* Structure to hold slot information */
    int             lcv;        /* Loop Control Variable */

    for (lcv = 0; lcv < slot_count; lcv++) {
        /* Get the info for the slot we are examining and store in slot_info */
        rc = function_ptr->C_GetSlotInfo(slot_list[lcv], &slot_info);
        if (rc != CKR_OK) {
            printf("Error getting the slot info: 0x%X\n", rc);
            return rc;
        }

        /* Display the slot information */
        printf("Slot #%d Info\n", slot_list[lcv]);
        printf("\tDescription: %.64s\n", slot_info.slotDescription);
        printf("\tManufacturer: %.32s\n", slot_info.manufacturerID);
        printf("\tFlags: 0x%X\n", slot_info.flags);
        printf("\tHardware Version: %d.%d\n", slot_info.hardwareVersion.major,
                                              slot_info.hardwareVersion.minor);
        printf("\tFirmware Version: %d.%d\n", slot_info.firmwareVersion.major,
                                              slot_info.firmwareVersion.minor);
    }
    return CKR_OK;
}

CK_RV display_token_info(void)
{
    CK_RV           rc;         /* Return Code */
    CK_TOKEN_INFO   token_info;  /* Structure to hold token information */
    int             lcv;        /* Loop Control Variable */

    for (lcv = 0; lcv < slot_count; lcv++) {
        /* Get the Token info for each slot in the system */
        rc = function_ptr->C_GetTokenInfo(slot_list[lcv], &token_info);
        if (rc != CKR_OK) {
            printf("Error getting token info: 0x%X\n", rc);
            return rc;
        }

        /* Display the token information */
        printf("Token #%d Info:\n", slot_list[lcv]);
        printf("\tLabel: %.32s\n", token_info.label);
        printf("\tManufacturer: %.32s\n", token_info.manufacturerID);
        printf("\tModel: %.16s\n", token_info.model);
        printf("\tSerial Number: %.16s\n", token_info.serialNumber);
        printf("\tFlags: 0x%X\n", token_info.flags);
        printf("\tSessions: %d/%d\n", token_info.ulSessionCount,
                                      token_info.ulMaxSessionCount);
        printf("\tR/W Sessions: %d/%d\n", token_info.ulRwSessionCount,
                                          token_info.ulMaxRwSessionCount);
        printf("\tPIN Length: %d-%d\n", token_info.ulMinPinLen,
                                        token_info.ulMaxPinLen);
        printf("\tPublic Memory: 0x%X/0x%X\n", token_info.ulFreePublicMemory,
                                               token_info.ulTotalPublicMemory);
        printf("\tPrivate Memory: 0x%X/0x%X\n", token_info.ulFreePrivateMemory,
                                               token_info.ulTotalPrivateMemory);
        printf("\tHardware Version: %d.%d\n", token_info.hardwareVersion.major,
                                              token_info.hardwareVersion.minor);
        printf("\tFirmware Version: %d.%d\n", token_info.firmwareVersion.major,
                                              token_info.firmwareVersion.minor);
        printf("\tTime: %.16s\n", token_info.utcTime);
    }
    return CKR_OK;
}

CK_RV init(void)
{
    CK_RV rc;           /* Return Code */
    void (*sym_ptr)();   /* Pointer for the DLL */

    /* Open the PKCS11 API Shared Library, and inform the user if there is an
     * error
     */
    dll_ptr = dlopen("/usr/lib/opencryptoki/libopencryptoki.so", RTLD_NOW);
    if (!dll_ptr) {
        rc = errno;
        printf("Error loading PKCS#11 library: 0x%X\n", rc);
        fflush(stdout);
        return rc;
    }

    /* Get the list of the PKCS11 functions this token supports */
    sym_ptr = (void (*) ())dlsym(dll_ptr, "C_GetFunctionList");
    if (!sym_ptr) {
        rc = errno;
        printf("Error getting function list: 0x%X\n", rc);
        fflush(stdout);
        cleanup();
    }

    sym_ptr(&function_ptr);

    /* If we get here, we know the slot manager is running and we can use PKCS11
     * calls, so we will execute the PKCS11 Initialize command.
     */
    rc = function_ptr->C_Initialize(NULL);
    if (rc != CKR_OK) {
        printf("Error initializing the PKCS11 library: 0x%X\n", rc);
        fflush(stdout);
        cleanup();
    }

    return CKR_OK;
}

CK_RV cleanup(void)
{
    CK_RV rc;   /* Return Code */

    /* To clean up we will free the slot list we create, call the Finalize
     * routine for PKCS11 and close the dynamically linked library
     */
    free(slot_list);
    rc = function_ptr->C_Finalize(NULL);
    if (dll_ptr)
        dlclose(dll_ptr);

    exit(rc);
}

10.2. Makefile

VPATH = ...

INCS = -I../. -I../../../../../include/pkcs11
CFLAGS = $(OPTLVL) $(INCS) -DAPI -DDEV -D_THREAD_SAFE -DLINUX -DDEBUG -DSPINXL

CC = gcc
LD = gcc

LIBS = -ldl -lpthread

OBJS = sample.o

.c.o: ; $(CC) -c $(CFLAGS) -o $@ $<

all: sample

sample: $(OBJS)
${CC} ${OBJS} $(LIBS) -o $@

TARGET = sample

build: $(TARGET)

clean:
rm -f *.so *.o $(TARGET)