Skip to content

Commit 63dbb99

Browse files
committed
Add mod_session support
By setting GssapiUseSessions we enable the module to store a bearer token with the user and gss names in the client, this way we can allow clients to perform authentication once but then remain authenticaed for the duration of the session or until the original credentials expire. The Secure cookie used to store the token is encrypted using a randomly generated AES key at process startup. This means multiple apache servers will not be able to use the same cookie, however the client will reauth transparently if the cookie cannot be read.
1 parent 342cea5 commit 63dbb99

File tree

7 files changed

+493
-23
lines changed

7 files changed

+493
-23
lines changed

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
all-local:
2-
@APXS@ -c ${LIBS} ${CFLAGS} mod_auth_gssapi.c
2+
@APXS@ -c ${LIBS} ${CFLAGS} mod_auth_gssapi.c sessions.c crypto.c
33

44
install-exec-local:
55
if test ! -d ${APXS_LIBEXECDIR}; then mkdir -p ${APXS_LIBEXECDIR}; fi

src/crypto.c

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/* Copyright (C) 2014 mod_auth_gssapi authors - See COPYING for (C) terms */
2+
3+
#include <openssl/evp.h>
4+
#include <openssl/hmac.h>
5+
#include <openssl/rand.h>
6+
#include <stdbool.h>
7+
#include "crypto.h"
8+
9+
struct seal_key {
10+
const EVP_CIPHER *cipher;
11+
const EVP_MD *md;
12+
unsigned char *ekey;
13+
unsigned char *hkey;
14+
};
15+
16+
apr_status_t SEAL_KEY_CREATE(struct seal_key **skey)
17+
{
18+
struct seal_key *n;
19+
int ret;
20+
21+
n = calloc(1, sizeof(*n));
22+
if (!n) return ENOMEM;
23+
24+
n->cipher = EVP_aes_128_cbc();
25+
if (!n->cipher) {
26+
free(n);
27+
return EFAULT;
28+
}
29+
30+
n->md = EVP_sha256();
31+
if (!n->md) {
32+
free(n);
33+
return EFAULT;
34+
}
35+
36+
n->ekey = malloc(n->cipher->key_len);
37+
if (!n->ekey) {
38+
free(n);
39+
return ENOMEM;
40+
}
41+
42+
n->hkey = malloc(n->cipher->key_len);
43+
if (!n->hkey) {
44+
free(n);
45+
return ENOMEM;
46+
}
47+
48+
ret = RAND_bytes(n->ekey, n->cipher->key_len);
49+
if (ret == 0) {
50+
free(n->ekey);
51+
free(n->hkey);
52+
free(n);
53+
return EFAULT;
54+
}
55+
56+
ret = RAND_bytes(n->hkey, n->cipher->key_len);
57+
if (ret == 0) {
58+
free(n->ekey);
59+
free(n->hkey);
60+
free(n);
61+
return EFAULT;
62+
}
63+
64+
*skey = n;
65+
return 0;
66+
}
67+
68+
apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
69+
struct databuf *plain, struct databuf *cipher)
70+
{
71+
apr_status_t err = EFAULT;
72+
EVP_CIPHER_CTX ctx = { 0 };
73+
HMAC_CTX hmac_ctx = { 0 };
74+
uint8_t rbuf[16];
75+
unsigned int len;
76+
int outlen, totlen;
77+
int ret;
78+
79+
EVP_CIPHER_CTX_init(&ctx);
80+
81+
/* confounder to avoid exposing random numbers directly to clients
82+
* as IVs */
83+
ret = RAND_bytes(rbuf, 16);
84+
if (ret == 0) goto done;
85+
86+
if (cipher->length == 0) {
87+
/* add space for confounder and padding and MAC */
88+
cipher->length = (plain->length / 16 + 2) * 16;
89+
cipher->value = apr_palloc(p, cipher->length + skey->md->md_size);
90+
if (!cipher->value) {
91+
err = ENOMEM;
92+
goto done;
93+
}
94+
}
95+
96+
ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
97+
if (ret == 0) goto done;
98+
totlen = 0;
99+
100+
outlen = cipher->length;
101+
ret = EVP_EncryptUpdate(&ctx, cipher->value, &outlen, rbuf, 16);
102+
if (ret == 0) goto done;
103+
totlen += outlen;
104+
105+
outlen = cipher->length - totlen;
106+
ret = EVP_EncryptUpdate(&ctx, &cipher->value[totlen], &outlen,
107+
plain->value, plain->length);
108+
if (ret == 0) goto done;
109+
totlen += outlen;
110+
111+
outlen = cipher->length - totlen;
112+
ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[totlen], &outlen);
113+
if (ret == 0) goto done;
114+
totlen += outlen;
115+
116+
/* now MAC the buffer */
117+
HMAC_CTX_init(&hmac_ctx);
118+
119+
ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
120+
skey->cipher->key_len, skey->md, NULL);
121+
if (ret == 0) goto done;
122+
123+
ret = HMAC_Update(&hmac_ctx, cipher->value, totlen);
124+
if (ret == 0) goto done;
125+
126+
ret = HMAC_Final(&hmac_ctx, &cipher->value[totlen], &len);
127+
if (ret == 0) goto done;
128+
129+
cipher->length = totlen + len;
130+
err = 0;
131+
132+
done:
133+
EVP_CIPHER_CTX_cleanup(&ctx);
134+
HMAC_CTX_cleanup(&hmac_ctx);
135+
return err;
136+
}
137+
138+
apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
139+
struct databuf *cipher, struct databuf *plain)
140+
{
141+
apr_status_t err = EFAULT;
142+
EVP_CIPHER_CTX ctx = { 0 };
143+
HMAC_CTX hmac_ctx = { 0 };
144+
unsigned char mac[skey->md->md_size];
145+
unsigned int len;
146+
int outlen, totlen;
147+
volatile bool equal = true;
148+
int ret, i;
149+
150+
/* check MAC first */
151+
HMAC_CTX_init(&hmac_ctx);
152+
153+
ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
154+
skey->cipher->key_len, skey->md, NULL);
155+
if (ret == 0) goto done;
156+
157+
cipher->length -= skey->md->md_size;
158+
159+
ret = HMAC_Update(&hmac_ctx, cipher->value, cipher->length);
160+
if (ret == 0) goto done;
161+
162+
ret = HMAC_Final(&hmac_ctx, mac, &len);
163+
if (ret == 0) goto done;
164+
165+
if (len != skey->md->md_size) goto done;
166+
for (i = 0; i < skey->md->md_size; i++) {
167+
if (cipher->value[cipher->length + i] != mac[i]) equal = false;
168+
/* not breaking intentionally,
169+
* or we would allow an oracle attack */
170+
}
171+
if (!equal) goto done;
172+
173+
EVP_CIPHER_CTX_init(&ctx);
174+
175+
if (plain->length == 0) {
176+
plain->length = cipher->length;
177+
plain->value = apr_palloc(p, plain->length);
178+
if (!plain->value) {
179+
err = ENOMEM;
180+
goto done;
181+
}
182+
}
183+
184+
ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
185+
if (ret == 0) goto done;
186+
187+
totlen = 0;
188+
outlen = plain->length;
189+
ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
190+
cipher->value, cipher->length);
191+
if (ret == 0) goto done;
192+
193+
totlen += outlen;
194+
outlen = plain->length - totlen;
195+
ret = EVP_DecryptFinal_ex(&ctx, plain->value, &outlen);
196+
if (ret == 0) goto done;
197+
198+
totlen += outlen;
199+
/* now remove the confounder */
200+
totlen -= 16;
201+
memmove(plain->value, plain->value + 16, totlen);
202+
203+
plain->length = totlen;
204+
err = 0;
205+
206+
done:
207+
EVP_CIPHER_CTX_cleanup(&ctx);
208+
HMAC_CTX_cleanup(&hmac_ctx);
209+
return err;
210+
}

src/crypto.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* Copyright (C) 2014 mod_auth_gssapi authors - See COPYING for (C) terms */
2+
3+
#include <apr_errno.h>
4+
#include <apr_pools.h>
5+
6+
struct seal_key;
7+
8+
struct databuf {
9+
unsigned char *value;
10+
int length;
11+
};
12+
13+
apr_status_t SEAL_KEY_CREATE(struct seal_key **skey);
14+
apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
15+
struct databuf *plain, struct databuf *cipher);
16+
apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
17+
struct databuf *cipher, struct databuf *plain);

src/mod_auth_gssapi.c

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "mod_auth_gssapi.h"
2626

27+
2728
module AP_MODULE_DECLARE_DATA auth_gssapi_module;
2829

2930
APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
@@ -71,24 +72,16 @@ static char *mag_error(request_rec *req, const char *msg,
7172

7273
static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
7374

74-
static int mag_post_config(apr_pool_t *cfg, apr_pool_t *log,
75+
static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
7576
apr_pool_t *temp, server_rec *s)
7677
{
7778
/* FIXME: create mutex to deal with connections and contexts ? */
7879
mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
80+
mag_post_config_session();
7981

8082
return OK;
8183
}
8284

83-
84-
struct mag_conn {
85-
apr_pool_t *parent;
86-
gss_ctx_id_t ctx;
87-
bool established;
88-
char *user_name;
89-
char *gss_name;
90-
};
91-
9285
static int mag_pre_connection(conn_rec *c, void *csd)
9386
{
9487
struct mag_conn *mc;
@@ -138,6 +131,7 @@ static int mag_auth(request_rec *req)
138131
gss_name_t client = GSS_C_NO_NAME;
139132
gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
140133
uint32_t flags;
134+
uint32_t vtime;
141135
uint32_t maj, min;
142136
char *reply;
143137
size_t replen;
@@ -151,6 +145,11 @@ static int mag_auth(request_rec *req)
151145
return DECLINED;
152146
}
153147

148+
/* ignore auth for subrequests */
149+
if (!ap_is_initial_req(req)) {
150+
return OK;
151+
}
152+
154153
cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
155154

156155
if (cfg->ssl_only) {
@@ -166,11 +165,24 @@ static int mag_auth(request_rec *req)
166165
req->connection->conn_config,
167166
&auth_gssapi_module);
168167
if (!mc) {
169-
return DECLINED;
168+
ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
169+
"Failed to retrieve connection context!");
170+
goto done;
170171
}
172+
}
173+
174+
/* if available, session always supersedes connection bound data */
175+
mag_check_session(req, cfg, &mc);
176+
177+
if (mc) {
178+
/* register the context in the memory pool, so it can be freed
179+
* when the connection/request is terminated */
180+
apr_pool_userdata_set(mc, "mag_conn_ptr",
181+
mag_conn_destroy, mc->parent);
182+
171183
if (mc->established) {
172184
ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
173-
"Connection bound pre-authentication found.");
185+
"Already established context found!");
174186
apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
175187
req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate");
176188
req->user = apr_pstrdup(req->pool, mc->user_name);
@@ -199,7 +211,7 @@ static int mag_auth(request_rec *req)
199211

200212
maj = gss_accept_sec_context(&min, pctx, GSS_C_NO_CREDENTIAL,
201213
&input, GSS_C_NO_CHANNEL_BINDINGS,
202-
&client, &mech_type, &output, &flags, NULL,
214+
&client, &mech_type, &output, &flags, &vtime,
203215
&delegated_cred);
204216
if (GSS_ERROR(maj)) {
205217
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
@@ -208,19 +220,17 @@ static int mag_auth(request_rec *req)
208220
goto done;
209221
}
210222

211-
/* register the context in the connection pool, so it can be freed
212-
* when the connection is terminated */
213-
apr_pool_userdata_set(mc, "mag_conn_ptr", mag_conn_destroy, mc->parent);
214-
215223
if (maj == GSS_S_CONTINUE_NEEDED) {
216-
if (!cfg->gss_conn_ctx) {
224+
if (!mc) {
217225
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
218-
"Mechanism needs continuation but "
219-
"GssapiConnectionBound is off.");
226+
"Mechanism needs continuation but neither "
227+
"GssapiConnectionBound nor "
228+
"GssapiUseSessions are available");
220229
gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
221230
gss_release_buffer(&min, &output);
222231
output.length = 0;
223232
}
233+
/* auth not complete send token and wait next packet */
224234
goto done;
225235
}
226236

@@ -263,6 +273,11 @@ static int mag_auth(request_rec *req)
263273
mc->user_name = apr_pstrdup(mc->parent, req->user);
264274
mc->gss_name = apr_pstrdup(mc->parent, clientname);
265275
mc->established = true;
276+
if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
277+
vtime = MIN_SESS_EXP_TIME;
278+
}
279+
mc->expiration = time(NULL) + vtime;
280+
mag_attempt_session(req, cfg, mc);
266281
}
267282

268283
ret = OK;
@@ -298,6 +313,7 @@ static void *mag_create_dir_config(apr_pool_t *p, char *dir)
298313

299314
cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
300315
if (!cfg) return NULL;
316+
cfg->pool = p;
301317

302318
return cfg;
303319
}
@@ -323,6 +339,13 @@ static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
323339
return NULL;
324340
}
325341

342+
static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
343+
{
344+
struct mag_config *cfg = (struct mag_config *)mconfig;
345+
cfg->use_sessions = on ? true : false;
346+
return NULL;
347+
}
348+
326349
static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
327350
const char *w)
328351
{
@@ -376,6 +399,8 @@ static const command_rec mag_commands[] = {
376399
"Work only if connection is SSL Secured"),
377400
AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
378401
"Authentication is bound to the TCP connection"),
402+
AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
403+
"Authentication uses mod_sessions to hold status"),
379404
AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
380405
"Credential Store"),
381406
{ NULL }

0 commit comments

Comments
 (0)