pam

Pluggable Authentication Modules (PAM) binding for Crystal

pam

Crystal Version License: GPL v3

Production-grade, type-safe Linux Pluggable Authentication Modules (PAM) binding for Crystal.

Provides C FFI bindings (libpam.so), memory safety contracts (allocating conversation responses via C malloc/free to prevent glibc invalid pointer crashes), type-safe enums and exceptions, customizable conversation handlers, and block-scoped RAII context handles.


Installation

Add the dependency to your shard.yml:

dependencies:
  pam:
    gitlab: renich/pam

Run shards install.


Quick Usage

1. Simple User Authentication

require "pam"

# Authenticate user credentials against PAM service ("login", "system-auth", "sudo")
if Pam.authenticate("system-auth", "alice", "secret123")
  puts "Access granted"
else
  puts "Access denied"
end

# Raising variant (raises Pam::AuthError on failure)
begin
  Pam.authenticate!("system-auth", "alice", "secret123")
  puts "Authentication successful"
rescue ex : Pam::AuthError
  puts "Authentication failed: #{ex.message}"
end

2. Block-Scoped Context & Session Management

Pam::Context.open guarantees pam_end is invoked upon block exit, preserving PAM transaction status.

require "pam"

Pam::Context.open("system-auth", "alice") do |ctx|
  # Authenticate credentials
  ctx.authenticate!

  # Verify account policy compliance (expiration, login hours)
  ctx.acct_mgmt!

  # Establish credentials and open PAM session
  ctx.setcred(Pam::Flags::EstablishCred)
  ctx.open_session

  # Access PAM transaction metadata items
  ctx.tty = "tty1"
  ctx.rhost = "192.168.1.50"
  puts "User: #{ctx.user}, TTY: #{ctx.tty}, Remote Host: #{ctx.rhost}"

  # Manage PAM environment variables
  ctx.setenv("XDG_RUNTIME_DIR", "/run/user/1000")
  env = ctx.env # Returns Hash(String, String)
  puts "PAM Environment: #{env}"

  # Perform authenticated system work...

  # Clean up session
  ctx.close_session
end

3. Password Token Change (pam_chauthtok)

require "pam"

Pam::Context.open("passwd", "alice") do |ctx|
  ctx.authenticate!
  ctx.chauthtok(Pam::Flags::None)
  puts "Password updated successfully"
end

4. Custom Conversation Handlers

Implement custom prompt handlers by inheriting from Pam::Conversation:

require "pam"

class ConsoleConversation < Pam::Conversation
  def call(messages : Array(Pam::Message)) : Array(Pam::Response)
    messages.map do |msg|
      case msg.style
      when Pam::MessageStyle::PromptEchoOff
        # Prompt for hidden input (password/PIN)
        print msg.text
        Response.new(STDIN.noecho(&.gets).try(&.chomp))
      when Pam::MessageStyle::PromptEchoOn
        # Prompt for visible text
        print msg.text
        Response.new(STDIN.gets.try(&.chomp))
      when Pam::MessageStyle::ErrorMsg, Pam::MessageStyle::TextInfo
        puts msg.text
        Response.new(nil, 0)
      else
        Response.new(nil, 0)
      end
    end
  end
end

Pam::Context.open("system-auth", "alice", ConsoleConversation.new) do |ctx|
  ctx.authenticate!
end

Build Targets (GNUmakefile)

make all      # Run specs and build API documentation in docs/technical/api
make docs     # Generate HTML API documentation via `crystal docs -o docs/technical/api`
make test     # Run Crystal test suite (`crystal spec`, use VERBOSE=1 for verbose output)
make check    # Run Ameba static code analysis
make clean    # Remove generated documentation & build caches

License

This project is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). See LICENSE for details.

Repository

pam

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 2
  • about 3 hours ago
  • August 13, 2026
License

GNU General Public License v3.0 only

Links
Synced at

Thu, 13 Aug 2026 14:19:21 GMT

Languages