Skip to content

Commit 380f904

Browse files
authored
Merge pull request #37 from linagora/sprint-2
Sprint 2: SSH bastion
2 parents 58e59a3 + ea16fa0 commit 380f904

21 files changed

Lines changed: 2678 additions & 8586 deletions

.github/workflows/ci.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ on:
66
pull_request:
77
branches: [ main ]
88

9+
# Restrict GITHUB_TOKEN permissions to read-only by default
10+
permissions:
11+
contents: read
12+
913
jobs:
1014
build:
1115
name: Build and Test (${{ matrix.os }})
1216
runs-on: ${{ matrix.os }}
17+
permissions:
18+
contents: read
1319

1420
strategy:
1521
matrix:
@@ -54,6 +60,8 @@ jobs:
5460
build-debug:
5561
name: Build Debug with Sanitizers
5662
runs-on: ubuntu-latest
63+
permissions:
64+
contents: read
5765

5866
steps:
5967
- name: Checkout code
@@ -89,6 +97,8 @@ jobs:
8997
name: Build Debian Package
9098
runs-on: ubuntu-latest
9199
needs: build
100+
permissions:
101+
contents: read
92102

93103
steps:
94104
- name: Checkout code
@@ -125,6 +135,8 @@ jobs:
125135
name: Build RPM (Rocky Linux ${{ matrix.version }})
126136
runs-on: ubuntu-latest
127137
needs: build
138+
permissions:
139+
contents: read
128140
container: rockylinux:${{ matrix.version }}
129141

130142
strategy:
@@ -168,6 +180,8 @@ jobs:
168180
shellcheck:
169181
name: ShellCheck
170182
runs-on: ubuntu-latest
183+
permissions:
184+
contents: read
171185

172186
steps:
173187
- name: Checkout code

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ set(PAM_MODULE_SOURCES
3737
src/token_manager.c
3838
src/notify.c
3939
src/secret_store.c
40+
src/auth_cache.c
4041
)
4142

4243
if(ENABLE_CACHE)

include/auth_cache.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* auth_cache.h - Authorization cache for offline mode
3+
*
4+
* Caches authorization responses from LLNG server for offline use.
5+
* Uses AES-256-GCM encryption with machine-id derived key.
6+
*
7+
* Copyright (C) 2024 Linagora
8+
* License: AGPL-3.0
9+
*/
10+
11+
#ifndef AUTH_CACHE_H
12+
#define AUTH_CACHE_H
13+
14+
#include <stdbool.h>
15+
#include <time.h>
16+
17+
/* Authorization cache entry */
18+
typedef struct {
19+
int version; /* Cache format version (3) */
20+
time_t expires_at; /* Expiration timestamp */
21+
char *user; /* Username */
22+
bool authorized; /* Authorization result */
23+
char **groups; /* User groups */
24+
size_t groups_count; /* Number of groups */
25+
bool sudo_allowed; /* Sudo permission */
26+
bool sudo_nopasswd; /* Sudo without password */
27+
char *gecos; /* Full name / GECOS */
28+
char *shell; /* Login shell */
29+
char *home; /* Home directory */
30+
} auth_cache_entry_t;
31+
32+
/* Authorization cache handle */
33+
typedef struct auth_cache auth_cache_t;
34+
35+
/*
36+
* Initialize authorization cache
37+
* cache_dir: Directory for cache files
38+
* Returns NULL on failure
39+
*/
40+
auth_cache_t *auth_cache_init(const char *cache_dir);
41+
42+
/*
43+
* Destroy cache and free resources
44+
*/
45+
void auth_cache_destroy(auth_cache_t *cache);
46+
47+
/*
48+
* Look up cached authorization for a user
49+
* user: Username to look up
50+
* server_group: Server group (for cache key)
51+
* host: Hostname (for cache key binding)
52+
* entry: Output parameter for cached entry (caller must free with auth_cache_entry_free)
53+
* Returns true if valid cache entry found, false otherwise
54+
*/
55+
bool auth_cache_lookup(auth_cache_t *cache,
56+
const char *user,
57+
const char *server_group,
58+
const char *host,
59+
auth_cache_entry_t *entry);
60+
61+
/*
62+
* Store authorization result in cache
63+
* user: Username
64+
* server_group: Server group (for cache key)
65+
* host: Hostname (for cache key binding)
66+
* entry: Entry to store
67+
* ttl: Time-to-live in seconds
68+
* Returns 0 on success, -1 on error
69+
*/
70+
int auth_cache_store(auth_cache_t *cache,
71+
const char *user,
72+
const char *server_group,
73+
const char *host,
74+
const auth_cache_entry_t *entry,
75+
int ttl);
76+
77+
/*
78+
* Invalidate cache entry for a user
79+
*/
80+
void auth_cache_invalidate(auth_cache_t *cache,
81+
const char *user,
82+
const char *server_group,
83+
const char *host);
84+
85+
/*
86+
* Check if force-online file exists
87+
* If file contains usernames, returns true only if user is listed
88+
* force_online_file: Path to force-online file
89+
* user: Username to check
90+
* Returns true if user should skip cache
91+
*/
92+
bool auth_cache_force_online(const char *force_online_file, const char *user);
93+
94+
/*
95+
* Free cache entry contents
96+
*/
97+
void auth_cache_entry_free(auth_cache_entry_t *entry);
98+
99+
/*
100+
* Clean up expired entries
101+
* Returns number of entries removed
102+
*/
103+
int auth_cache_cleanup(auth_cache_t *cache);
104+
105+
#endif /* AUTH_CACHE_H */

include/config.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ typedef struct {
2828
int min_tls_version; /* Minimum TLS version: 12=1.2, 13=1.3 (default: 13) */
2929
char *cert_pin; /* Certificate pin (sha256//base64 format, optional) */
3030

31-
/* Cache settings */
31+
/* Cache settings (for token cache) */
3232
bool cache_enabled; /* Enable token caching (default: true) */
3333
char *cache_dir; /* Cache directory (default: /var/cache/pam_llng) */
3434
int cache_ttl; /* Cache TTL in seconds (default: 300) */
@@ -37,6 +37,11 @@ typedef struct {
3737
bool cache_encrypted; /* Encrypt cache files with AES-256-GCM (default: true) */
3838
bool cache_invalidate_on_logout; /* Invalidate cache when session closes (default: true) */
3939

40+
/* Authorization cache settings (for offline mode) */
41+
bool auth_cache_enabled; /* Enable authorization caching (default: true) */
42+
char *auth_cache_dir; /* Auth cache directory (default: /var/cache/pam_llng/auth) */
43+
char *auth_cache_force_online; /* Force-online trigger file (default: /etc/security/pam_llng.force_online) */
44+
4045
/* Authorization mode */
4146
bool authorize_only; /* Only check authorization, no password (for SSH keys) */
4247

include/llng_client.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@
1111
#include <stdbool.h>
1212
#include <stddef.h>
1313

14+
/* Permissions structure from /pam/authorize response */
15+
typedef struct {
16+
bool sudo_allowed; /* User is allowed to use sudo */
17+
bool sudo_nopasswd; /* Sudo without password (future use) */
18+
} llng_permissions_t;
19+
20+
/* Offline settings from /pam/authorize response */
21+
typedef struct {
22+
bool enabled; /* Offline mode allowed for this user */
23+
int ttl; /* Cache TTL in seconds (0 = no caching) */
24+
} llng_offline_settings_t;
25+
26+
/* SSH certificate info extracted from environment */
27+
typedef struct {
28+
char *key_id; /* Certificate key ID */
29+
char *serial; /* Certificate serial number */
30+
char *principals; /* Comma-separated principals */
31+
char *ca_fingerprint; /* CA key fingerprint */
32+
bool valid; /* Certificate was validated */
33+
} llng_ssh_cert_info_t;
34+
1435
/* Response structure from LLNG server */
1536
typedef struct {
1637
bool authorized;
@@ -26,6 +47,14 @@ typedef struct {
2647
char *gecos; /* Full name / GECOS field */
2748
char *shell; /* Login shell */
2849
char *home; /* Home directory */
50+
51+
/* Permissions from /pam/authorize */
52+
llng_permissions_t permissions;
53+
bool has_permissions; /* True if permissions object was present */
54+
55+
/* Offline settings from /pam/authorize */
56+
llng_offline_settings_t offline;
57+
bool has_offline; /* True if offline object was present */
2958
} llng_response_t;
3059

3160
/* Client configuration */
@@ -87,6 +116,28 @@ int llng_authorize_user(llng_client_t *client,
87116
const char *service,
88117
llng_response_t *response);
89118

119+
/*
120+
* Check user authorization with SSH certificate info via /pam/authorize
121+
* Same as llng_authorize_user but includes SSH certificate details
122+
* Returns 0 on success, -1 on error
123+
*/
124+
int llng_authorize_user_with_cert(llng_client_t *client,
125+
const char *user,
126+
const char *host,
127+
const char *service,
128+
const llng_ssh_cert_info_t *ssh_cert,
129+
llng_response_t *response);
130+
131+
/*
132+
* Free SSH certificate info structure contents
133+
*/
134+
void llng_ssh_cert_info_free(llng_ssh_cert_info_t *cert_info);
135+
136+
/*
137+
* Initialize response structure to safe defaults (all zeros)
138+
*/
139+
void llng_response_init(llng_response_t *response);
140+
90141
/*
91142
* Free response structure contents
92143
*/

0 commit comments

Comments
 (0)