Skip to content

Commit fe50dfe

Browse files
Matt Rogerssimo5
Matt Rogers
authored andcommitted
Add GssapiRequiredNameAttributes option
Allow setting the required name attributes for a session, using a flex and bison based parser to process the attributes specified. Reviewed-by: Simo Sorce <[email protected]> Closes #140
1 parent 7393674 commit fe50dfe

16 files changed

+890
-31
lines changed

README

+29
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ To run tests, you also need:
2828
`krb5-server` on Fedora)
2929
* Packages `mod_session`, `krb5-workstation`, `python-requests-kerberos`,
3030
and `python-gssapi` on Fedora
31+
* Some tests require `krb5-pkinit` package on fedora and krb5 >= 1.15.
3132
* [nss_wrapper](https://cwrap.org/nss_wrapper.html), packaged in Fedora
3233
* [socket_wrapper](https://cwrap.org/socket_wrapper.html), packaged in Fedora
3334

@@ -306,6 +307,34 @@ underscores for environment variable names.
306307
GssapiNameAttributes json
307308
GssapiNameAttributes RADIUS_NAME urn:ietf:params:gss:radius-attribute_1
308309

310+
### GssapiRequiredNameAttributes
311+
312+
This option allows specifying one or more Name Attributes that the client must
313+
possess in order to be authorized to access the location. The required Name
314+
Attributes are specified by name=value pairs (name being the ATTRIBUTE_NAME as
315+
mentioned above, and value being a Null-terminated string. Alternately, if a
316+
Name Attribute produces binary values or is expected to contain a space
317+
character, the desired value can be specified by a ':=' and a base64-encoded
318+
string).
319+
320+
A combination of Name Attributes (including multiple values from a single Name
321+
Attribute type) can be specified with an expression that separates each
322+
name=value pair with the "and" or "or" logical operators. Operator precedence
323+
can be influenced by parenthesized statements.
324+
325+
foo=bar
326+
foo:=YmFy
327+
foo=bar or foo=baz
328+
foo=bar and foo=baz and bar=baz
329+
(foo=bar and foo=baz) or bar:=YmFy
330+
331+
If the Name Attributes associated with the client do not satisfy the given
332+
expression, or no Name Attributes are present, a 403 response is returned.
333+
334+
#### Example
335+
GssapiRequiredNameAttributes "auth-indicators=high"
336+
GssapiRequiredNameAttributes "auth-indicators=high or other-attr=foo"
337+
GssapiRequiredNameAttributes "((auth-indicators=low and auth-indicators=med) or auth-indicators=high)"
309338

310339
### GssapiNegotiateOnce
311340

configure.ac

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ AC_CONFIG_HEADERS([src/config.h])
99

1010
# Checks for programs.
1111
AC_PROG_CC_STDC
12+
AC_PROG_YACC
13+
AC_PROG_LEX
14+
AC_DECL_YYTEXT
1215

1316
LT_INIT
1417
AC_SUBST(INCLTDL)

src/Makefile.am

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
AM_CPPFLAGS = -I $(srcdir)/asn1c
22

3+
BUILT_SOURCES = parser.h
4+
AM_YFLAGS = -d
5+
36
magdir = $(APXS_LIBEXECDIR)
47
mag_LTLIBRARIES = \
58
mod_auth_gssapi.la
69

710
dist_noinst_HEADERS = \
8-
mod_auth_gssapi.h crypto.h sessions.h environ.h
11+
mod_auth_gssapi.h crypto.h sessions.h environ.h mag_parse.h
912

1013
mod_auth_gssapi_la_SOURCES = \
11-
mod_auth_gssapi.c crypto.c sessions.c environ.c
14+
mod_auth_gssapi.c crypto.c sessions.c environ.c lex.l parser.y
1215
mod_auth_gssapi_la_CFLAGS = \
1316
$(MAG_CFLAGS)
1417
mod_auth_gssapi_la_LIBADD = \
@@ -17,7 +20,8 @@ mod_auth_gssapi_la_LDFLAGS = \
1720
$(MAG_LIBS) \
1821
-avoid-version \
1922
-module \
20-
-export-symbols-regex auth_gssapi_module
23+
-export-symbols-regex auth_gssapi_module \
24+
$(LEXLIB)
2125

2226
install-exec-local:
2327
test -d $(DESTDIR)$(APXS_LIBEXECDIR) || mkdir -p $(DESTDIR)$(APXS_LIBEXECDIR)

src/environ.c

+117-25
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,75 @@ static apr_status_t mag_mc_name_attrs_cleanup(void *data)
4343
return 0;
4444
}
4545

46+
static apr_status_t mag_mc_req_name_attrs_cleanup(void *data)
47+
{
48+
struct mag_conn *mc = (struct mag_conn *)data;
49+
50+
free(mc->required_name_attrs);
51+
free(mc->required_name_vals);
52+
mc->required_name_attrs = NULL;
53+
mc->required_name_vals = NULL;
54+
return 0;
55+
}
56+
57+
static void mag_set_required_name_attr(request_rec *req,
58+
struct mag_conn *mc,
59+
struct name_attr *attr)
60+
{
61+
size_t count, len;
62+
char *val = NULL, *attrval = NULL;
63+
64+
/* Count the existing name attribute and value pairs. Both
65+
* required_name_attrs and required_name_vals are allocated together and
66+
* hold the same number of strings, with pairs corresponding by index. */
67+
for (count = 0; mc->required_name_attrs != NULL &&
68+
mc->required_name_attrs[count] != NULL &&
69+
mc->required_name_vals != NULL &&
70+
mc->required_name_vals[count] != NULL; count++);
71+
72+
/* Prefer a display value string. */
73+
if (attr->display_value.length != 0) {
74+
len = attr->display_value.length;
75+
attrval = attr->display_value.value;
76+
} else if (attr->value.length != 0) {
77+
len = attr->value.length;
78+
attrval = attr->value.value;
79+
} else {
80+
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
81+
"no name attribute value available");
82+
return;
83+
}
84+
85+
/* Prepend the value length. */
86+
val = apr_pcalloc(mc->pool, sizeof(len) + len + 1);
87+
memcpy(val, &len, sizeof(len));
88+
memcpy(val + sizeof(len), attrval, len);
89+
90+
/* Allocate/realloc a new bunch. */
91+
if (count % 16 == 0) {
92+
size_t size = sizeof(*mc->required_name_attrs) * (count + 16 + 2);
93+
mc->required_name_attrs = realloc(mc->required_name_attrs, size);
94+
mc->required_name_vals = realloc(mc->required_name_vals, size);
95+
if (!mc->required_name_attrs || !mc->required_name_vals) {
96+
apr_pool_abort_get(mc->pool)(ENOMEM);
97+
}
98+
apr_pool_userdata_setn(mc, GSS_NAME_ATTR_USERDATA,
99+
mag_mc_req_name_attrs_cleanup, mc->pool);
100+
}
101+
102+
mc->required_name_attrs[count] = apr_pstrndup(mc->pool, attr->name.value,
103+
attr->name.length);
104+
mc->required_name_attrs[count + 1] = NULL;
105+
mc->required_name_vals[count] = val;
106+
mc->required_name_vals[count + 1] = NULL;
107+
108+
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
109+
"found name attribute '%s' with length %lu",
110+
mc->required_name_attrs[count], len);
111+
ap_log_rdata(APLOG_MARK, APLOG_DEBUG, req, mc->required_name_attrs[count],
112+
mc->required_name_vals[count] + sizeof(len), len, 0);
113+
}
114+
46115
static void mc_add_name_attribute(struct mag_conn *mc,
47116
const char *name, const char *value)
48117
{
@@ -221,17 +290,17 @@ gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
221290
void mag_get_name_attributes(request_rec *req, struct mag_config *cfg,
222291
gss_name_t name, struct mag_conn *mc)
223292
{
224-
if (!cfg->name_attributes) {
225-
return;
226-
}
227-
228293
uint32_t maj, min;
229294
gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
230295
struct name_attr attr;
231296
char *json = NULL;
232297
char *error;
233-
int count = 0;
234-
int i, j;
298+
int count = 0, map_count = 0;
299+
int i;
300+
301+
if (!cfg->name_attributes && !cfg->required_na_expr) {
302+
return;
303+
}
235304

236305
maj = gss_inquire_name(&min, name, NULL, NULL, &attrs);
237306
if (GSS_ERROR(maj)) {
@@ -242,62 +311,77 @@ void mag_get_name_attributes(request_rec *req, struct mag_config *cfg,
242311
}
243312

244313
if (!attrs || attrs->count == 0) {
245-
mc_add_name_attribute(mc, "GSS_NAME_ATTR_ERROR", "0 attributes found");
314+
if (cfg->name_attributes) {
315+
mc_add_name_attribute(mc, "GSS_NAME_ATTR_ERROR", "0 attributes found");
316+
}
317+
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "no name attributes found");
246318
}
247319

248-
if (cfg->name_attributes->output_json) {
320+
if (attrs) {
321+
count = attrs->count;
322+
}
249323

250-
if (attrs) count = attrs->count;
324+
if (cfg->name_attributes) {
325+
map_count = cfg->name_attributes->map_count;
326+
}
251327

328+
if (cfg->name_attributes && cfg->name_attributes->output_json) {
252329
json = apr_psprintf(req->pool,
253330
"{\"name\":\"%s\",\"attributes\":{",
254331
mc->gss_name);
255-
} else {
256-
count = cfg->name_attributes->map_count;
257332
}
258333

334+
/* Collect all name attributes */
259335
for (i = 0; i < count; i++) {
260-
261336
memset(&attr, 0, sizeof(struct name_attr));
262337

263-
if (cfg->name_attributes->output_json) {
264-
attr.name = attrs->elements[i];
265-
for (j = 0; j < cfg->name_attributes->map_count; j++) {
338+
attr.name = attrs->elements[i];
339+
if (map_count) {
340+
/* Use the environment variable name matching the attribute name
341+
* from the map. */
342+
for (int j = 0; j < map_count; j++) {
266343
if (strncmp(cfg->name_attributes->map[j].attr_name,
267-
attrs->elements[i].value,
268-
attrs->elements[i].length) == 0) {
344+
attr.name.value,
345+
attr.name.length) == 0) {
269346
attr.env_name = cfg->name_attributes->map[j].env_name;
270347
break;
271348
}
272349
}
273-
} else {
274-
attr.name.length = strlen(cfg->name_attributes->map[i].attr_name);
275-
attr.name.value = cfg->name_attributes->map[i].attr_name;
276-
attr.env_name = cfg->name_attributes->map[i].env_name;
277350
}
278-
279351
attr.number = 0;
280352
attr.more = -1;
281353
do {
282354
attr.number++;
283355
attr.value = empty_buffer;
284356
attr.display_value = empty_buffer;
285357

286-
if (!mag_get_name_attr(req, name, &attr)) break;
358+
/* Fetch the next attribute value. */
359+
if (!mag_get_name_attr(req, name, &attr)) {
360+
break;
361+
}
287362

288-
if (cfg->name_attributes->output_json) {
363+
/* Add as a JSON value if needed. */
364+
if (cfg->name_attributes && cfg->name_attributes->output_json) {
289365
mag_add_json_name_attr(req, i == 0, &attr, &json);
290366
}
367+
368+
/* Add as an environment variable if needed. */
291369
if (attr.env_name) {
292370
mag_set_env_name_attr(req, mc, &attr);
293371
}
294372

373+
/* Add to the list of name attributes lined up for the requirement
374+
* check if needed. */
375+
if (cfg->required_na_expr) {
376+
mag_set_required_name_attr(req, mc, &attr);
377+
}
378+
295379
gss_release_buffer(&min, &attr.value);
296380
gss_release_buffer(&min, &attr.display_value);
297381
} while (attr.more != 0);
298382
}
299383

300-
if (cfg->name_attributes->output_json) {
384+
if (cfg->name_attributes && cfg->name_attributes->output_json) {
301385
json = apr_psprintf(req->pool, "%s}}", json);
302386
mc_add_name_attribute(mc, "GSS_NAME_ATTRS_JSON", json);
303387
}
@@ -393,6 +477,14 @@ void mag_set_req_data(request_rec *req,
393477
mag_export_req_env(req, mc->env);
394478
}
395479

480+
void mag_set_req_attr_fail(request_rec *req, struct mag_config *cfg,
481+
struct mag_conn *mc)
482+
{
483+
apr_table_set(mc->env, "GSS_NAME_ATTR_ERROR",
484+
"required name attributes check unsatisfied");
485+
mag_set_req_data(req, cfg, mc);
486+
}
487+
396488
void mag_publish_error(request_rec *req, uint32_t maj, uint32_t min,
397489
const char *gss_err, const char *mag_err)
398490
{

src/environ.h

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ void mag_set_req_data(request_rec *req,
1616

1717
void mag_publish_error(request_rec *req, uint32_t maj, uint32_t min,
1818
const char *gss_err, const char *mag_err);
19+
void mag_set_req_attr_fail(request_rec *req, struct mag_config *cfg,
20+
struct mag_conn *mc);

src/lex.l

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* Copyright (C) 2017 mod_auth_gssapi contributors - See COPYING for (C) terms
2+
*
3+
* Scanner for the GssapiRequiredNameAttributes option parser.
4+
*/
5+
%{
6+
#include <string.h>
7+
#include "parser.h"
8+
%}
9+
10+
%option noyywrap noinput nounput
11+
12+
%%
13+
14+
"(" { return LPAREN; }
15+
")" { return RPAREN; }
16+
" " { return SPACE; }
17+
"or" { return OR; }
18+
"OR" { return OR; }
19+
"and" { return AND; }
20+
"AND" { return AND; }
21+
"=" { return EQUAL; }
22+
":=" { return EQUALBIN; }
23+
"*" { return AST; }
24+
[A-Za-z0-9_\-\[\]]+ {
25+
yylval.sval = strdup(yytext);
26+
return STRING;
27+
}
28+
29+
%%

src/mag_parse.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* Copyright (C) 2017 mod_auth_gssapi contributors - See COPYING for (C) terms */
2+
3+
#ifndef _MAG_PARSE_H_
4+
#define _MAG_PARSE_H_
5+
extern int mag_verify_name_attributes(const char *expr, const char **attrs,
6+
const char **vals);
7+
extern int mag_check_name_attr_expr(const char *expr);
8+
#endif /* _MAG_PARSE_H_ */

0 commit comments

Comments
 (0)