Dev Docs User Authentication

Authentication System (UAMs)

Audited against Netatalk d63155e13 on 2026-10-10. This describes the current source checkout; older releases may differ.

Framework and source

AFP authentication is implemented through dynamically loaded User Authentication Modules. etc/afpd/auth.c parses login requests and establishes session identity; etc/afpd/uam.c loads modules, registers callbacks, and exposes server options. Public definitions are in include/atalk/uam.h, with internal callback objects in etc/afpd/uam_auth.h.

auth_load() reads uam list and uses uam_load() to open libraries and locate their uam_export symbol. Modules register a login, extended-login, password-change, or print-auth callback through uam_register(). Module build/install and generic-name aliases are in etc/uams/meson.build.

The default list is uams_dhx2.so; that default does not include every built module.

Login flow

  1. afp_login() or afp_login_ext() validates the requested AFP version and finds the requested UAM.
  2. The server creates session cryptographic state and calls the UAM’s login callback. Multi-round UAMs continue through afp_logincont().
  3. Internal login() verifies the returned Unix identity, rejects UID 0, applies groups/effective identity and configured policy, and enables post-authentication commands through set_auth_switch().
  4. The worker reports IPC_LOGINDONE to its parent. Logout closes forks and volumes and marks the DSI session logged out.

The worker process is forked before login. The idle worker is also initialized before login in afp_over_dsi(); PAM does not run in the AFP parent as a pre-fork workaround.

singleuser_admits_uid() additionally restricts a single-user server to the caller’s UID. The controller validates that this mode uses only SRP and has no admin/forced identity settings.

Implemented modules

AFP UAM name Source Credential/verification path
No User Authent etc/uams/uams_guest.c Configured Unix guest account; no password
Cleartxt Passwrd etc/uams/uams_pam.c, etc/uams/uams_passwd.c PAM or Unix password verification; 8-byte password field
DHCAST128 etc/uams/uams_dhx_pam.c, etc/uams/uams_dhx_passwd.c DHX exchange, CAST5, PAM or Unix verification
DHX2 etc/uams/uams_dhx2_pam.c, etc/uams/uams_dhx2_passwd.c Multi-round exchange with generated 1,024-bit DH parameters and 256-byte password field
Client Krb v2 etc/uams/uams_gss.c GSSAPI ticket authentication and wrapped session key
Randnum exchange, 2-Way Randnum exchange etc/uams/uams_randnum.c Legacy DES challenge/response and Randnum password file
SRP etc/uams/uams_srp.c SRP-6a proof exchange against per-user verifier records

Availability depends on build dependencies and the configured module list. The PAM and passwd variants are separate implementations; password-change support differs. Authentication exchanges do not provide ongoing DSI transport encryption.

PAM and Kerberos

PAM variants use service netatalk and perform authentication/account checks with their module-specific session and credential handling. Installed service configurations are in config/pam. Use the platform’s PAM stack; one Linux example is not a portable replacement for macOS/BSD configuration.

PAM password expiry can place AFP into a restricted command state so a supported password-change callback can run. Follow each UAM’s actual error mapping rather than assuming identical mappings for every module.

The GSS module uses k5 service, k5 realm, fqdn, and k5 keytab where configured. It obtains a service principal/keytab entry, accepts a GSS security context, maps the client principal to a local user, and can wrap the server’s session key for GetSessionToken type 8. Principal setup and bounds are implemented in etc/uams/uams_gss.c.

SRP

uams_srp.so registers SRP. The implementation uses SRP-6a, SHA-1, an MGF1-derived key, and RFC 5054 group #2 (1,536 bits), as defined in include/atalk/srp.h. Login returns group/salt/server-ephemeral data; continuation validates the client’s public value and proof, and returns a server proof. It verifies stored credentials independently of PAM.

[Global]
uam list = uams_srp.so
srp verifier path = /var/lib/netatalk/srp

srp verifier path names a directory, not a flat file. Each verifier file is named by the user’s numeric UID and contains exactly one record:

username:hex_salt:hex_verifier

The salt is 16 bytes; the verifier is 192 bytes, represented as 32 and 384 hexadecimal characters respectively. The UAM validates filename/account association, record length and fields, ownership, file mode, link count, and directory policy. Root-owned disabled placeholders represent users not enrolled; an active verifier belongs to its user. Single-user mode uses the caller’s private directory and verifier.

srp passwd file is a deprecated configuration alias whose value is now interpreted as a directory. A legacy flat store requires offline migration; merely renaming the option does not convert it.

afppasswd

bin/afppasswd/afppasswd.c now defaults to SRP. Legacy Randnum credential-file operations require -r.

Option Current purpose
-c Create/initialize a credential store
-a user Root adds/resets the named user’s credential
-d user Root disables the named user’s SRP verifier
-m Migrate a legacy flat SRP verifier file
-p path SRP verifier directory, or Randnum file with -r
-r Select legacy Randnum mode
-f Force the supported create/replace action
-u uid Minimum UID when initializing a system store
-n Disable cracklib checking, when compiled
-w string Supply password on the command line; interactive entry avoids putting it in command history

Regular users can update their own enrolled SRP verifier and can bootstrap a private store with -c -p directory; they cannot use administrator account-management options. Use the current tool’s usage for exact combinations. SRP verifier writes synchronize records and apply ownership only after durable content is prepared.

Randnum retains the older username:hex_password:last_login_date:failed_count format and optional master-key file. Do not use that format to describe SRP.

Session keys and reconnect tokens

struct session_info tracks a 64-byte session key, optional GSS-wrapped key, an 8-byte reconnect token, and client ID data. create_session_key() and create_session_token() use uam_random_string().

uam_random_string() reads /dev/urandom until the requested buffer is filled and fails if secure randomness cannot be obtained. There is no time-seeded random() fallback.

GetSessionToken publishes the random token to the parent through IPC_SESSIONTOKEN. server_child_transfer_session() in libatalk/util/server_child.c requires token and UID agreement before passing a socket to the old worker. Comparison uses the constant-time helper in libatalk/util/constant_time.c. The token is opaque, not derived from a PID.

Current limitations in etc/afpd/auth.c should not be mistaken for completed protocol support: legacy GetSessionToken types 1/2 validate but do not register their client ID, and the guest-account reconnect restriction in afp_disconnect() remains disabled. Token/UID checks are active, but the code does not separately enforce guest-authentication state.

LDAP, UUIDs, and ACLs

LDAP in libatalk/acl/ldap.c maps account names to UUIDs and back for ACL/identity operations. It uses configuration from libatalk/acl/ldap_config.c and include/atalk/ldapconfig.h. Its configured bind credentials are for directory lookup; this is not a login UAM or per-user password verifier. LDAP-backed login can instead be supplied through the deployed PAM/NSS infrastructure.

UUID mapping uses libatalk/acl/uuid.c; cached identity lookups use libatalk/acl/cache.c. AFP ACL handlers are in etc/afpd/acls.c, with platform operations in libatalk/acl/unix.c and libatalk/vfs/acl.c. UUID resolution, Unix account existence, and successful authentication are separate requirements.

Verification

test/uams covers malformed authentication requests and password bounds. test/afppasswd covers credential creation/migration and verifier lookup. test/testsuite/logintest.c, test/testsuite/afptest_srp.c, and test/testsuite/FPGetSessionToken.c cover client-facing paths; test/integration/singleuser_test.sh covers the restricted server mode.

Footnotes

Generated from the Netatalk GitHub Wiki

Last updated 2026-10-21