# License server

## Introduction

{% hint style="success" %}
With the **IDA 9.3** release, we introduced several enhancements to the license server, including:

* [**JSON Configuration**](#json-configuration): A single configuration file for all server settings
* [**Access Control Lists**](#access-control-lists-acl): Fine-grained control over who can borrow licenses
* [**Access Logging**](#access-logging): HTTP-style logging of all license operations
* [**Enhanced lsadm**](#lsadm-command-line-tool): Improved output formatting and JSON export

Check how to [upgrade the server](#upgrading-the-server) to benefit from the improvements.
{% endhint %}

This manual describes the installation, management, and interaction with a Hex-Rays License Server deployment. It is primarily intended for administrators, and will focus on the setup and management of the Hex-Rays License Server.

While we will (at least superficially) make use of the command-line client used to access/manage the server, this manual will not offer a detailed explanation of its usage, although there is a dedicated section for [essential lsadm commands](#lsadm-tool-quick-guide)

## Installing the Hex-Rays License Server

The first step is to install the Hex-Rays License Server, which is the central component of the deployment.

### Prerequisites

After your purchase of a Hex-Rays product with floating licenses, go to the [customer portal](https://my.hex-rays.com/dashboard/download-center/downloads), where you will find:

* an installer for the Hex-Rays License Server
* the installer for the product you have purchased
* a `license_server_<LID>.hexlic` (where \<LID> is your license ID) and the certificate bundle will be available after License Server activation, under [Licenses](https://my.hex-rays.com/dashboard/licenses) tab

{% hint style="info" %}
The certificate bundle contains two files `hexlicsrv.crt` and `hexlicsrv.key` that will be used for the TLS encryption between the clients and the license server. You should not try to create them yourself.
{% endhint %}

All those will be necessary, so please go ahead and download them.

You will also need `root` access on the host where you will be installing the server.

### Installation

This chapter explains how to install the Hex-Rays License Server.

{% hint style="info" %}
To ensure proper functionality, we recommend that the **License Server version match the IDA Pro version** and that both are updated to the latest version. The 9.3 License Server has backward compatibility with the 9.2 IDA Pro version (client). However, the reverse is not supported. Ensure that the License Server version is equal to or higher than the client version to maintain compatibility. For the latest installers, visit the [Download Center](https://my.hex-rays.com/dashboard/download-center) in My Hex-Rays portal.
{% endhint %}

#### Installing clients

The command-line client `lsadm` is bundled with the Hex-Rays License Server installer. To install both Hex-Rays License Server and `lsadm`, simply run the installer and follow the instructions.

Every Hex-Rays product using floating licenses, such as IDA, is also a client of Hex-Rays License Server. For installation instructions for these products, please refer to their documentation.

#### Installing the server

The Hex-Rays License Server can be installed on x64 Linux servers. We have tested it on Debian and Ubuntu, but other major flavors of Linux should be fine too.

The installer is provided as an executable file. If needed, use `chmod` to ensure it has execute permissions.

To install the server, run the Hex-Rays License Server installer as `root` and follow the instructions (the server will not require `root` permissions; only the installer does.)

{% hint style="info" %}
If your Linux system is based on `systemd` (e.g., Debian/Ubuntu, Red-Hat, CentOS, ...​), it is recommended to let the installer create systemd units so that the server will start automatically at the next reboot.
{% endhint %}

{% hint style="info" %}
During server installation, you’ll be prompted to create the `hexlicsrv.conf` configuration file. **We recommend accepting the default option to create it automatically**, as this file is required to run the server.
{% endhint %}

#### Activating the server license

In order for the Hex-Rays License Server license to be activated, it must be bound to a Host ID (an Ethernet MAC address.) From a command prompt, run `/sbin/ifconfig`, and lookup the "ether" address for the network interface through which the server will be accessible.

```
    >/sbin/ifconfig
    enp4s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            [...snipped...]
            ether bf:e2:91:10:58:d2  txqueuelen 1000  (Ethernet)
            [...snipped...]
```

In this case, our MAC address is: `bf:e2:91:10:58:d2`

Go to [Hex-Rays customer portal](https://my.hex-rays.com) and [activate your license for license server](https://docs.hex-rays.com/getting-started/licensing#license-server-activation-for-admins). During that process, you will need to provide the MAC address of the device where the license server will be running. Once the activation is complete, you'll be able to download the following files:

* license server certificate bundle
* `license_server_<LID>.hexlic` (license key)

Those need to be copied in the Hex-Rays License Server installation directory. As `root`:

```
    >cd /opt/hexlicsrv
    >cp .../path/to/hexlicsrv.crt .
    >cp .../path/to/hexlicsrv.key .
    >cp .../path/to/license_server*.hexlic .
    >chown hexlicsrv:hexlicsrv hexlicsrv.crt hexlicsrv.key license_server*.hexlic
    >chmod 640 hexlicsrv.crt hexlicsrv.key license_server*.hexlic
```

#### Creating the initial database

At this point, the server should be ready to run.

{% hint style="info" %}
If your system is already in production, skip this section. Using the `--recreate-schema` option as in the example below, will re-create an empty database.
{% endhint %}

On the first install, you will need to initialize the database the server will use:

```
    >sudo -u hexlicsrv ./license_server --config-file hexlicsrv.conf \
                                        --recreate-schema
    >2024-04-14 14:30:28 License Server v1.0 Hex-Rays (c) 2024
    >2024-04-14 14:30:28 Database initialized; exiting.
```

#### Testing the server

Now that the server is installed and has a database to work with, we can test that it works:

```
    >sudo -u hexlicsrv ./license_server --config-file hexlicsrv.conf \
                                        --certchain-file hexlicsrv.crt \
                                        --privkey-file hexlicsrv.key \
                                        --license-file license_server_<LID>.hexlic
    >2024-04-14 14:35:47 License Server v1.0 Hex-Rays (c) 2024
    >2024-04-14 14:35:47 Using a license with 1 seats
    >2024-04-14 14:35:47 Listening on 0.0.0.0:65434...
```

Good, the server appears to run! If you are observing more worrying messages than this one, please refer to the [troubleshooting](#troubleshooting) section.

At this point, you may want to either let the server run, or stop it (`Ctrl+C` will do) and restart it using systemd:

```
    >systemctl restart hexlicsrv.service
```

...​and make sure it runs:

```
    >ps aux | grep license_server
    hexlicsrv  58246  0.0  0.0 ...
```

If you don't see a running `license_server` process, please refer to the `systemd` diagnostic tools (e.g., `journalctl`) for more info.

## Management

This chapter explains in detail how to perform regular administrator tasks.

### Backup and restore

Currently, there is no dedicated procedure to back up the Hex-Rays License Server database. It can be done by temporarily stopping the Hex-Rays License Server and making a copy of the sqlite3 database. The server must be stopped only during the backup of the sqlite3 database and then can be immediately restarted.

Alternatively, it is possible to use sqlite3 backup functionality to make a backup of the database.

### Upgrading the server

Switching to the newest versions of the Hex-Rays License Server is recommended in order for the team to benefit from its improvements and new features.

The upgrade procedure consists of the following steps:

1. Stop the server. For example, if you are using `systemd` to manage the server: `sudo systemctl stop hexlicsrv`
2. Perform a [backup](#backup-and-restore) of the database
3. Launch the installer
4. Follow the on-screen instructions
5. Upgrade the database with `./license_server --config-file hexlicsrv.conf --upgrade-schema`
6. Restart the server. E.g., `sudo systemctl start hexlicsrv`

### License Borrowing Limits

The server supports limiting the maximum duration for which licenses can be borrowed using the `MAX_BORROW_HOURS` environment variable.

| Example                       | Behavior                                          |
| ----------------------------- | ------------------------------------------------- |
| `MAX_BORROW_HOURS=0` or unset | No limit (default)                                |
| `MAX_BORROW_HOURS=-1`         | Borrowing is completely disabled                  |
| `MAX_BORROW_HOURS=<hours>`    | Limits borrowing to the specified number of hours |

**Note:** Changes to this environment variable require a server restart to take effect.

## Migrating to the latest version

### Migrating from .conf to JSON

The server automatically migrates `.conf` files to `.json` format on first run:

1. Server detects `hexlicsrv.conf` exists but `hexlicsrv.json` doesn't
2. Server reads the `.conf` file and creates equivalent `.json`
3. Server uses the new `.json` file
4. Original `.conf` file is preserved (not deleted)

**Manual migration:**

If automatic migration doesn't work, you can create a JSON file manually (check [troubleshooting](#automatic-migration-from-.conf-to-.json-failed) for details).

### Adding ACL to Existing Installation

1. Create `acl.json` in the server directory:

   ```json
   {
       "aliases": {},
       "acls": {
           "default_policy": "allow",
           "rules": []
       }
   }
   ```
2. Add the ACL file to your JSON config:

   ```json
   {
       "acl_file": "acl.json"
   }
   ```
3. Restart the license server
4. Gradually add aliases and rules as needed

## JSON Configuration

The license server now supports comprehensive JSON configuration files, replacing the legacy `.conf` format.

### Configuration File Location

The server looks for configuration in the following order:

1. Path specified with `-f <config_file>` command-line option
2. `hexlicsrv.json` in the current working directory

### Complete Configuration Reference

```json
{
    "connection_string": "sqlite3;Data Source=./hexlicsrv.sqlite3;",
    "license_file": "/absolute/path/to/license_server.hexlic",
    "log_file": "/path/to/server.log",
    "cert_file": "hexlicsrv.crt",
    "key_file": "hexlicsrv.key",
    "port": 65434,
    "ip_address": "0.0.0.0",
    "verbose": true,
    "no_tls": false,
    "badreq_dir": "/path/to/badreq",
    "access_log_file": "access.log",
    "acl_file": "acl.json",
    "recreate_schema": false,
    "upgrade_schema": false
}
```

### Configuration Options

#### Database Settings

| Option              | Type   | Required | Description                                                       |
| ------------------- | ------ | -------- | ----------------------------------------------------------------- |
| `connection_string` | string | Yes      | Database connection string. Format: `sqlite3;Data Source=<path>;` |

**Example:**

```json
{
    "connection_string": "sqlite3;Data Source=/var/lib/licsrv/hexlicsrv.sqlite3;"
}
```

#### License File

| Option         | Type   | Required | Description                                                                 |
| -------------- | ------ | -------- | --------------------------------------------------------------------------- |
| `license_file` | string | Yes      | Path to the `.hexlic` license file. **Use absolute paths for reliability.** |

**Example:**

```json
{
    "license_file": "/etc/hexrays/license_server.hexlic"
}
```

{% hint style="info" %}
Relative paths are resolved relative to the server binary location, not the current working directory. Always use absolute paths to avoid confusion.
{% endhint %}

#### TLS/Security Settings

| Option      | Type    | Default | Description                                             |
| ----------- | ------- | ------- | ------------------------------------------------------- |
| `cert_file` | string  | -       | Path to TLS certificate file (PEM format)               |
| `key_file`  | string  | -       | Path to TLS private key file (PEM format)               |
| `no_tls`    | boolean | `false` | Disable TLS encryption (not recommended for production) |

**Example:**

```json
{
    "cert_file": "/etc/hexrays/ssl/hexlicsrv.crt",
    "key_file": "/etc/hexrays/ssl/hexlicsrv.key",
    "no_tls": false
}
```

#### Network Settings

| Option       | Type    | Default   | Description                                              |
| ------------ | ------- | --------- | -------------------------------------------------------- |
| `port`       | integer | `65434`   | TCP port to listen on                                    |
| `ip_address` | string  | `0.0.0.0` | IP address to bind to. Use `0.0.0.0` for all interfaces. |

**Example:**

```json
{
    "port": 65434,
    "ip_address": "192.168.1.100"
}
```

#### Logging Settings

| Option            | Type    | Default | Description                                        |
| ----------------- | ------- | ------- | -------------------------------------------------- |
| `log_file`        | string  | -       | Path to server log file                            |
| `access_log_file` | string  | -       | Path to HTTP-style access log file                 |
| `verbose`         | boolean | `false` | Enable verbose logging                             |
| `badreq_dir`      | string  | -       | Directory to dump malformed requests for debugging |

**Example:**

```json
{
    "log_file": "/var/log/hexrays/server.log",
    "access_log_file": "/var/log/hexrays/access.log",
    "verbose": true
}
```

#### ACL Settings

| Option     | Type   | Default    | Description                    |
| ---------- | ------ | ---------- | ------------------------------ |
| `acl_file` | string | `acl.json` | Path to ACL configuration file |

**Example:**

```json
{
    "acl_file": "/etc/hexrays/acl.json"
}
```

#### Database Schema Options

| Option            | Type    | Default | Description                                  |
| ----------------- | ------- | ------- | -------------------------------------------- |
| `recreate_schema` | boolean | `false` | Drop and recreate database schema on startup |
| `upgrade_schema`  | boolean | `false` | Upgrade database schema to latest version    |

{% hint style="warning" %}
`recreate_schema` will delete all existing data. Use with caution.
{% endhint %}

### Configuration Precedence

Command-line arguments always override JSON configuration values:

```
Command-line > JSON configuration > Default values
```

**Example:**

```bash
# JSON has port: 65434, but command-line overrides to 65000
./license_server -f hexlicsrv.json -p 65000
```

### Minimal Configuration Example

```json
{
    "connection_string": "sqlite3;Data Source=./hexlicsrv.sqlite3;",
    "license_file": "/path/to/license_server.hexlic",
    "cert_file": "hexlicsrv.crt",
    "key_file": "hexlicsrv.key"
}
```

***

## Access Control Lists (ACL)

The ACL system provides fine-grained control over license borrowing operations.

### Overview

The ACL system supports:

* **User aliases**: Map machine identifiers to human-readable usernames
* **Permission rules**: Allow or deny borrowing based on user or machine
* **Product restrictions**: Limit access to specific products
* **Denial messages**: Provide clear explanations when access is denied

### ACL File Location

The server looks for ACL configuration in:

1. Path specified with `-a <acl_file>` or `--acl <acl_file>`
2. Path specified in JSON config: `"acl_file": "path/to/acl.json"`
3. `acl.json` in the current working directory

If no ACL file is found, the server operates without access restrictions.

### ACL File Format

```json
{
    "aliases": {
        "<machine_id>": "<username>",
        ...
    },
    "acls": {
        "default_policy": "allow|deny",
        "rules": [
            {
                "user": "<username>",
                "permission": "allow|deny",
                "products": ["<product1>", "<product2>"],
                "reason": "<explanation>"
            },
            {
                "machine_id": "<machine_id>",
                "permission": "allow|deny",
                "reason": "<explanation>"
            }
        ]
    }
}
```

### Aliases Section

The `aliases` section maps machine identifiers to human-readable usernames. This serves two purposes:

1. **Identification**: Makes logs and reports more readable
2. **Rule matching**: Allows rules to target users instead of machine IDs

```json
{
    "aliases": {
        "DESKTOP-ABC123": "john.doe",
        "LAPTOP-XYZ789": "jane.smith",
        "192.168.1.100": "build-server",
        "dev-workstation-01": "alice.williams"
    }
}
```

**Alias resolution:**

* When a client connects, the server looks up its machine ID in aliases
* If found, the username from aliases is used for rule matching
* If not found, the machine ID is used directly

### ACLs Section

#### Default Policy

The `default_policy` determines what happens when no rule matches:

| Policy  | Behavior                                  |
| ------- | ----------------------------------------- |
| `allow` | Permit borrowing unless explicitly denied |
| `deny`  | Block borrowing unless explicitly allowed |

**Recommended:** Use `"default_policy": "allow"` for open environments, `"default_policy": "deny"` for restricted environments.

#### Rules

Rules are evaluated in order. The first matching rule determines the outcome.

**Rule fields:**

| Field        | Type   | Required                | Description                                |
| ------------ | ------ | ----------------------- | ------------------------------------------ |
| `user`       | string | One of user/machine\_id | Username to match (from aliases or direct) |
| `machine_id` | string | One of user/machine\_id | Machine identifier to match                |
| `permission` | string | Yes                     | `"allow"` or `"deny"`                      |
| `products`   | array  | No                      | List of products this rule applies to      |
| `reason`     | string | No                      | Human-readable explanation                 |

### Rule Examples

#### Deny a Specific User

```json
{
    "user": "contractor.bob",
    "permission": "deny",
    "reason": "Contractors are not permitted to borrow licenses"
}
```

#### Allow with Product Restriction

```json
{
    "user": "intern.alice",
    "permission": "allow",
    "products": ["ida-home"],
    "reason": "Interns limited to IDA Home edition"
}
```

#### Deny Unregistered Machines

```json
{
    "machine_id": "UNKNOWN-*",
    "permission": "deny",
    "reason": "Unregistered machine - contact IT to register"
}
```

#### Allow Test Infrastructure

```json
{
    "machine_id": "CI-BUILD-*",
    "permission": "allow",
    "reason": "CI/CD build infrastructure"
}
```

### Complete ACL Example

```json
{
    "aliases": {
        "DESKTOP-DEV001": "john.doe",
        "DESKTOP-DEV002": "jane.smith",
        "LAPTOP-REMOTE01": "bob.contractor",
        "DESKTOP-INTERN01": "alice.intern",
        "CI-BUILD-001": "jenkins",
        "CI-BUILD-002": "jenkins"
    },
    "acls": {
        "default_policy": "allow",
        "rules": [
            {
                "user": "john.doe",
                "permission": "allow",
                "reason": "Senior developer - full access"
            },
            {
                "user": "jane.smith",
                "permission": "allow",
                "reason": "Developer - full access"
            },
            {
                "user": "bob.contractor",
                "permission": "deny",
                "reason": "External contractor - borrowing not permitted per security policy"
            },
            {
                "user": "alice.intern",
                "permission": "allow",
                "products": ["ida-home"],
                "reason": "Intern - limited to IDA Home"
            },
            {
                "user": "jenkins",
                "permission": "allow",
                "reason": "CI/CD automation account"
            },
            {
                "machine_id": "UNKNOWN_MACHINE",
                "permission": "deny",
                "reason": "Unregistered machine - please contact IT department"
            }
        ]
    }
}
```

### ACL Behavior

1. Client sends borrow request with machine ID and username
2. Server resolves machine ID to alias (if configured)
3. Server evaluates rules in order:
   * First matching rule determines outcome
   * If no rule matches, default policy applies
4. If denied, the `reason` is returned to the client

### Product Names

Use these product identifiers in the `products` array:

| Product ID  | Description |
| ----------- | ----------- |
| `ida-pro`   | IDA Pro     |
| `ida-home`  | IDA Home    |
| `ida-teams` | IDA Teams   |

## Access Logging

The access logging feature provides HTTP-style logging of all license operations.

### Enabling Access Logging

**Via command line:**

```bash
./license_server -o /var/log/hexrays/access.log
```

**Via JSON configuration:**

```json
{
    "access_log_file": "/var/log/hexrays/access.log"
}
```

### Log Format

```
[timestamp] user machine_id (alias) "OPERATION license_id" status_code duration_ms
```

**Fields:**

| Field         | Description                                                                   |
| ------------- | ----------------------------------------------------------------------------- |
| `timestamp`   | ISO 8601 format: `YYYY-MM-DD HH:MM:SS`                                        |
| `user`        | Username from request (or `-` if not provided)                                |
| `machine_id`  | Machine identifier from request                                               |
| `alias`       | Resolved username from ACL aliases (only shown if different from machine\_id) |
| `OPERATION`   | The license operation performed                                               |
| `license_id`  | The license ID involved (or `-` for operations without a specific license)    |
| `status_code` | HTTP-style status code                                                        |
| `duration_ms` | Operation duration in milliseconds                                            |

### Operations Logged

| Operation   | Description                      |
| ----------- | -------------------------------- |
| `HELO`      | Client handshake                 |
| `LIST`      | List available licenses          |
| `CHECKOUT`  | Checkout a license               |
| `CHECKIN`   | Return a checked-out license     |
| `BORROW`    | Borrow a license for offline use |
| `RETURN`    | Return a borrowed license        |
| `HEARTBEAT` | Keep-alive ping                  |

### Status Codes

| Code | Meaning       | License Error                                                                           |
| ---- | ------------- | --------------------------------------------------------------------------------------- |
| 200  | Success       | `LICERR_OK`, `LICERR_WARNING`, `LICERR_INFO`                                            |
| 400  | Bad Request   | `LICERR_BAD_PACKET`                                                                     |
| 403  | Forbidden     | `LICERR_LICENSE_NOT_ACQUIRED`, `LICERR_LICENSE_NOT_BORROWED`, `LICERR_LICENSE_BORROWED` |
| 404  | Not Found     | `LICERR_BAD_LICENSE`                                                                    |
| 410  | Gone          | `LICERR_LICENSE_EXPIRED`                                                                |
| 422  | Unprocessable | `LICERR_LICENSE_BAD_END_TIME`, `LICERR_LICENSE_BAD_BLOB`                                |
| 502  | Bad Gateway   | `LICERR_NETWORK_FAILURE`                                                                |
| 503  | Unavailable   | `LICERR_LICENSE_NO_AVAIL`                                                               |

### Example Log Output

```
[2025-01-07 09:00:01] john.doe DESKTOP-DEV001 (john.doe) "HELO -" 200 5
[2025-01-07 09:00:01] john.doe DESKTOP-DEV001 (john.doe) "LIST -" 200 12
[2025-01-07 09:00:02] john.doe DESKTOP-DEV001 (john.doe) "CHECKOUT 96-E7DA-50E5-82" 200 45
[2025-01-07 09:15:00] john.doe DESKTOP-DEV001 (john.doe) "HEARTBEAT -" 200 2
[2025-01-07 10:30:00] bob.contractor LAPTOP-REMOTE01 "BORROW 96-E7DA-50E5-82" 403 8
[2025-01-07 12:00:00] john.doe DESKTOP-DEV001 (john.doe) "CHECKIN 96-E7DA-50E5-82" 200 23
```

### Log Rotation

The access log file is opened in append mode. Use standard log rotation tools:

**logrotate example (`/etc/logrotate.d/hexlicsrv`):**

```
/var/log/hexrays/access.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}
```

## License Server Command Line

`license_server [options]`

| Option              | Description                  |
| ------------------- | ---------------------------- |
| `-f <file>`         | JSON configuration file      |
| `-C <string>`       | Database connection string   |
| `-L <file>`         | License file path            |
| `-c <file>`         | TLS certificate file         |
| `-k <file>`         | TLS private key file         |
| `-p <port>`         | Port number (default: 65434) |
| `-i <ip>`           | IP address to bind           |
| `-l <file>`         | Log file path                |
| `-o <file>`         | Access log file (HTTP-style) |
| `-a, --acl`         | ACL configuration file       |
| `-v`                | Verbose mode                 |
| `-t`                | Disable TLS                  |
| `-V, --version`     | Show version                 |
| `--recreate-schema` | Recreate database schema     |
| `--upgrade-schema`  | Upgrade database schema      |

## lsadm Command-Line Tool

The `lsadm` tool is the command-line interface for managing the license server. This quick manual presents the basic common commands for lsadm tool.

### Command-Line Options

| Option | Long Form   | Description                                   |
| ------ | ----------- | --------------------------------------------- |
| `-u`   | `--user`    | Specify custom `user@machine-id` for requests |
| `-j`   | `--json`    | Output results in JSON format                 |
| `-V`   | `--version` | Show version information                      |
| `-h`   | `--host`    | License server hostname (format: `host:port`) |
| `-v`   | `--verbose` | Enable verbose output                         |

### Commands

| Command    | Description             | Arguments                 |
| ---------- | ----------------------- | ------------------------- |
| `info`     | Show server information | -                         |
| `list`     | List all licenses       | -                         |
| `checkout` | Checkout a license      | `<license_id>`            |
| `checkin`  | Return a license        | `<license_id>`            |
| `borrow`   | Borrow for offline use  | `<license_id> <end_time>` |
| `return`   | Return borrowed license | `<license_id>`            |

### Usage Examples

#### List Licenses

```bash
# Standard output
lsadm -h server.example.com:65434 list

# JSON output
lsadm -h server.example.com:65434 -j list

# With custom identity
lsadm -h server.example.com:65434 -u john.doe@DESKTOP-DEV001 list
```

#### Checkout License

```bash
lsadm -h server:65434 checkout 96-E7DA-50E5-82
```

#### Borrow License

```bash
# Borrow until specific date
lsadm -h server:65434 borrow 96-E7DA-50E5-82 2025-01-15

# Borrow for 7 days
lsadm -h server:65434 borrow 96-E7DA-50E5-82 +7d

# Borrow for 2 weeks
lsadm -h server:65434 borrow 96-E7DA-50E5-82 +2w
```

#### Show Version

```bash
lsadm -V
# Output: lsadm version 2 (protocol version 2)
```

### Output Formats

#### Standard Table Output

```
Licenses (assigned to John Doe, <john@example.com>):

License ID       Product  Add-ons                        Expires     Used/Total
--------------- -------  -----------------------------  ----------  ----------
96-E7DA-50E5-82 IDA-PRO  DECOMPILER,ARM64,+2 more       2025-12-31  1/5
  └─ john.doe@DESKTOP-DEV001 (192.168.1.50) checked out
97-ABCD-1234-56 IDA-HOME -                              2025-06-30  0/2
```

#### JSON Output

```json
{
    "contact": {
        "name": "John Doe",
        "email": "john@example.com"
    },
    "licenses": [
        {
            "license_id": "96-E7DA-50E5-82",
            "product": "IDA-PRO",
            "add_ons": ["DECOMPILER", "ARM64", "ARM", "PPC"],
            "expires": "2025-12-31",
            "seats": {
                "used": 1,
                "total": 5
            },
            "clients": [
                {
                    "user": "john.doe",
                    "machine_id": "DESKTOP-DEV001",
                    "peer_address": "192.168.1.50",
                    "status": "checked_out"
                }
            ]
        }
    ]
}
```

## Troubleshooting

This chapter explains how to solve typical problems with the Hex-Rays License Server.

### Connection issues

By default, the Hex-Rays License Server listens on the TCP port 65434 on all interfaces. Please ensure that this port is enabled in your firewalls.

The Hex-Rays License Server uses secure TLS connections with the clients. The TLS layer requires the certificate (.crt) and private key (.key) files. Usually, they are attached to the email message with the activation information.

### The server complains about a "world-accessible" file, and exits

The following files shouldn't be readable by everyone on the system, but only by `root` and `hexlicsrv`:

* `hexlicsrv.conf`: this file file holds the connection string to the database the server will use, and might contain credentials.
* `hexlicsrv.crt`: the certificate chain
* `hexlicsrv.key`: the private key file
* `license_server_<LID>.hexlic`: the license file

As a precaution, the Hex-Rays License Server will refuse to start if these files are readable by unauthorized users.

Please make sure they:

* have `hexlicsrv:hexlicsrv` ownership: `chown hexlicsrv:hexlicsrv hexlicsrv.crt hexlicsrv.key licensesrv.hexlic hexlicsrv.conf`
* are not world-accessible: `chmod 640 hexlicsrv.crt hexlicsrv.key license_server_<LID>.hexlic hexlicsrv.conf`

### Licensing

The licensesrv.hexlic file is tied to the MAC address of the first network interface. If they do not match, the server will not start. To change the MAC address, please contact [support](mailto:support@hex-rays.com)

### "Error: Failed to list licenses"

This error means that the currently installed license server license file (hexlic) content is incorrect. More precisly, the "licenses" list does not contain an entry with the following items:

```
...
        "product_id": "IDAPRO",
        "edition_id": "ida-pro",
...
```

or that the activation end date in the same section has expired.

Make sure to activate all the floating license plans owned by the license server **before** downloading the license server hexlic file.

### "Error: stat() failed on "hexlicsrv.conf": No such file or directory"

This error occurs when the `hexlicsrv.conf` configuration file is missing.

During the server installation, the user is asked whether to automatically create this configuration file for pre-configuring the server. Replying "no" will require creating the configuration file manually (which may be a bit tedious).

**Solution:**\
The simplest approach is to reinstall the server:

1. Uninstall the server by executing the uninstall program in the installation directory.
2. Re-run the installer and accept the prompt to automatically create the configuration file.

### "Error: Failed to initialize the license manager: No valid license file could be found"

This error can occur if a recent hexlic file is loaded in the version 9.0 of the license server.

**Solution** Make sure to download and install the latest version of the license server.

### "Missing private key path" when upgrading the schema

This is a known regression in the version 9.2 of the license server.

**Solution** Use the following command line:

```
>sudo -u hexlicsrv ./license_server --config-file hexlicsrv.conf \
                                        --certchain-file hexlicsrv.crt \
                                        --privkey-file hexlicsrv.key \
                                        --upgrade-schema
```

### Restoring from backups

There are no special precautions to take: restoring the sqlite3 database from a backup should be enough.

### Common Issues

#### ACL File Not Found

**Symptom:** Server starts but ACL restrictions don't work

**Solution:** Verify the ACL file path in your configuration:

```bash
# Check if file exists
ls -la /path/to/acl.json

# Verify JSON syntax
python3 -m json.tool /path/to/acl.json
```

#### Access Denied Unexpectedly

**Symptom:** User receives "access denied" but should be allowed

**Troubleshooting steps:**

1. Check if user's machine ID has an alias:

   ```json
   "aliases": {
       "ACTUAL-MACHINE-ID": "username"
   }
   ```
2. Verify rule order (first match wins)
3. Check default policy
4. Review access log for the denial:

   ```bash
   grep "BORROW.*403" /var/log/hexrays/access.log
   ```

#### JSON Configuration Not Loading

**Symptom:** Server ignores JSON configuration

**Solution:**

1. Verify JSON syntax:

   ```bash
   python3 -m json.tool hexlicsrv.json
   ```
2. Check file permissions:

   ```bash
   ls -la hexlicsrv.json
   ```
3. Use absolute path with `-f`:

   ```bash
   ./license_server -f /full/path/to/hexlicsrv.json
   ```

#### Access Log Not Writing

**Symptom:** Access log file remains empty or doesn't exist

**Solution:**

1. Verify the directory exists and is writable
2. Check the path in configuration
3. Ensure the server has write permissions

#### Automatic Migration from `.conf` to `.json` Failed

**Symptom:** The server does not create `.json` automatically, or fails to migrate the existing `.conf` file.

**Solution:**

If automatic migration does not occur, create the JSON configuration file manually.

**Old format (`hexlicsrv.conf`):**

```
sqlite3;Data Source=./hexlicsrv.sqlite3;
```

**New format (`hexlicsrv.json`):**

```json
{
    "connection_string": "sqlite3;Data Source=./hexlicsrv.sqlite3;"
}
```

### Validating Configuration

#### Validate JSON Config

```bash
python3 -c "import json; json.load(open('hexlicsrv.json'))"
```

#### Validate ACL File

```bash
python3 -c "import json; json.load(open('acl.json'))"
```

#### Test ACL Rules

Start the server in verbose mode and attempt operations:

```bash
./license_server -f hexlicsrv.json -v
```

Then in another terminal:

```bash
lsadm -h localhost:65434 -u testuser@testmachine borrow 96-E7DA-50E5-82 +1d
```

Check the server output and access log for details.
