fetch function is not really useful outside of ACLs. Currently only http
basic auth is supported.
-http_auth_group(<userlist>) : group
- Returns a boolean indicating whether the authentication data received from
- the client match a username & password stored in the specified userlist, and
- whether that username belongs to one of the groups supplied in ACL patterns.
+http_auth_group(<userlist>) : string
+ Returns a string corresponding to the user name found in the authentication
+ data received from the client if both the user name and password are valid
+ according to the specified userlist. The main purpose is to use it in ACLs
+ where it is then checked whether the user belongs to any group within a list.
This fetch function is not really useful outside of ACLs. Currently only http
basic auth is supported.
ACL derivatives :
- http_auth_group(<userlist>) : user group match
+ http_auth_group(<userlist>) : group ...
+ Returns true when the user extracted from the request and whose password is
+ valid according to the specified userlist belongs to at least one of the
+ groups.
http_first_req : boolean
Returns true when the request being processed is the first one of the
struct userlist *auth_find_userlist(char *name);
unsigned int auth_resolve_groups(struct userlist *l, char *groups);
+int userlist_postinit();
void userlist_free(struct userlist *ul);
enum pat_match_res pat_match_auth(struct sample *smp, struct pattern *pattern);
-int check_user(struct userlist *ul, unsigned int group_mask, const char *user, const char *pass);
+int check_user(struct userlist *ul, const char *user, const char *pass);
+int check_group(struct userlist *ul, char *name);
#endif /* _PROTO_AUTH_H */
#include <types/auth.h>
-#define MAX_AUTH_GROUPS (unsigned int)(sizeof(int)*8)
-
#define AU_O_INSECURE 0x00000001 /* insecure, unencrypted password */
+struct auth_groups {
+ struct auth_groups *next;
+ char *name;
+ char *groupusers; /* Just used during the configuration parsing. */
+};
+
+struct auth_groups_list {
+ struct auth_groups_list *next;
+ struct auth_groups *group;
+};
+
struct auth_users {
struct auth_users *next;
unsigned int flags;
char *user, *pass;
union {
- char *groups;
- unsigned int group_mask;
+ char *groups_names; /* Just used during the configuration parsing. */
+ struct auth_groups_list *groups;
} u;
};
struct userlist *next;
char *name;
struct auth_users *users;
- int grpcnt;
- char *groups[MAX_AUTH_GROUPS];
- char **groupusers;
+ struct auth_groups *groups;
};
#endif /* _TYPES_AUTH_H */
unsigned char mask; /* number of bits */
} ipv6; /* IPv6 address/mask */
struct pat_time time; /* valid hours and days */
- unsigned int group_mask;
struct eb_root *tree; /* tree storing all values if any */
} val; /* direct value */
union {
continue;
}
+ /* For each pattern, check if the group exists. */
list_for_each_entry(pattern, &expr->pat.patterns, list) {
- /* this keyword only has one argument */
- pattern->val.group_mask = auth_resolve_groups(expr->smp->arg_p->data.usr, pattern->ptr.str);
-
- if (!pattern->val.group_mask) {
+ if (!check_group(expr->smp->arg_p->data.usr, pattern->ptr.str)) {
Alert("proxy %s: acl %s %s(): invalid group '%s'.\n",
p->id, acl->name, expr->kw, pattern->ptr.str);
cfgerr++;
}
- free(pattern->ptr.str);
- pattern->ptr.str = NULL;
- pattern->len = 0;
}
}
}
#include <unistd.h>
#include <common/config.h>
+#include <common/errors.h>
#include <proto/acl.h>
#include <proto/log.h>
return NULL;
}
-/* find group_mask for selected gropus. The function returns 1 if OK or nothing to do,
- * 0 if case of unresolved groupname.
- * WARING: the function destroys the list (strtok), so it can only be used once.
- */
-
-unsigned int
-auth_resolve_groups(struct userlist *l, char *groups)
+int check_group(struct userlist *ul, char *name)
{
+ struct auth_groups *ag;
- char *group = NULL;
- unsigned int g, group_mask = 0;
-
- if (!groups || !*groups)
- return 0;
-
- while ((group = strtok(group?NULL:groups," "))) {
- for (g = 0; g < l->grpcnt; g++)
- if (!strcmp(l->groups[g], group))
- break;
-
- if (g == l->grpcnt) {
- Alert("No such group '%s' in userlist '%s'.\n",
- group, l->name);
- return 0;
- }
-
- group_mask |= (1 << g);
- }
-
- return group_mask;
+ for (ag = ul->groups; ag; ag = ag->next)
+ if (strcmp(name, ag->name) == 0)
+ return 1;
+ return 0;
}
void
{
struct userlist *tul;
struct auth_users *au, *tau;
- int i;
+ struct auth_groups_list *agl, *tagl;
+ struct auth_groups *ag, *tag;
while (ul) {
+ /* Free users. */
au = ul->users;
while (au) {
+ /* Free groups that own current user. */
+ agl = au->u.groups;
+ while (agl) {
+ tagl = agl;
+ agl = agl->next;
+ free(tagl);
+ }
+
tau = au;
au = au->next;
free(tau->user);
free(tau);
}
+ /* Free grouplist. */
+ ag = ul->groups;
+ while (ag) {
+ tag = ag;
+ ag = ag->next;
+ free(tag->name);
+ free(tag);
+ }
+
tul = ul;
ul = ul->next;
-
- for (i = 0; i < tul->grpcnt; i++)
- free(tul->groups[i]);
-
free(tul->name);
free(tul);
};
}
+int userlist_postinit()
+{
+ struct userlist *curuserlist = NULL;
+
+ /* Resolve usernames and groupnames. */
+ for (curuserlist = userlist; curuserlist; curuserlist = curuserlist->next) {
+ struct auth_groups *ag;
+ struct auth_users *curuser;
+ struct auth_groups_list *grl;
+
+ for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
+ char *group = NULL;
+ struct auth_groups_list *groups = NULL;
+
+ if (!curuser->u.groups_names)
+ continue;
+
+ while ((group = strtok(group?NULL:curuser->u.groups_names, ","))) {
+ for (ag = curuserlist->groups; ag; ag = ag->next) {
+ if (!strcmp(ag->name, group))
+ break;
+ }
+
+ if (!ag) {
+ Alert("userlist '%s': no such group '%s' specified in user '%s'\n",
+ curuserlist->name, group, curuser->user);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ /* Add this group at the group userlist. */
+ grl = calloc(1, sizeof(*grl));
+ if (!grl) {
+ Alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
+ curuserlist->name);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ grl->group = ag;
+ grl->next = groups;
+ groups = grl;
+ }
+
+ free(curuser->u.groups);
+ curuser->u.groups = groups;
+ }
+
+ for (ag = curuserlist->groups; ag; ag = ag->next) {
+ char *user = NULL;
+
+ if (!ag->groupusers)
+ continue;
+
+ while ((user = strtok(user?NULL:ag->groupusers, ","))) {
+ for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
+ if (!strcmp(curuser->user, user))
+ break;
+ }
+
+ if (!curuser) {
+ Alert("userlist '%s': no such user '%s' specified in group '%s'\n",
+ curuserlist->name, user, ag->name);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ /* Add this group at the group userlist. */
+ grl = calloc(1, sizeof(*grl));
+ if (!grl) {
+ Alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
+ curuserlist->name);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ grl->group = ag;
+ grl->next = curuser->u.groups;
+ curuser->u.groups = grl;
+ }
+
+ free(ag->groupusers);
+ ag->groupusers = NULL;
+ }
+
+#ifdef DEBUG_AUTH
+ for (ag = curuserlist->groups; ag; ag = ag->next) {
+ struct auth_groups_list *agl;
+
+ fprintf(stderr, "group %s, id %p, users:", ag->name, ag);
+ for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
+ for (agl = curuser->u.groups; agl; agl = agl->next) {
+ if (agl->group == ag)
+ fprintf(stderr, " %s", curuser->user);
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+ }
+
+ return ERR_NONE;
+}
+
/*
* Authenticate and authorize user; return 1 if OK, 0 if case of error.
*/
int
-check_user(struct userlist *ul, unsigned int group_mask, const char *user, const char *pass)
+check_user(struct userlist *ul, const char *user, const char *pass)
{
struct auth_users *u;
const char *ep;
#ifdef DEBUG_AUTH
- fprintf(stderr, "req: userlist=%s, user=%s, pass=%s, group_mask=%u\n",
- ul->name, user, pass, group_mask);
+ fprintf(stderr, "req: userlist=%s, user=%s, pass=%s, group=%s\n",
+ ul->name, user, pass, group);
#endif
for (u = ul->users; u; u = u->next)
return 0;
#ifdef DEBUG_AUTH
- fprintf(stderr, "cfg: user=%s, pass=%s, group_mask=%u, flags=%X",
- u->user, u->pass, u->u.group_mask, u->flags);
+ fprintf(stderr, "cfg: user=%s, pass=%s, flags=%X, groups=",
+ u->user, u->pass, u->flags);
+ for (agl = u->u.groups; agl; agl = agl->next)
+ fprintf(stderr, " %s", agl->group->name);
#endif
- /*
- * if user matches but group does not,
- * it makes no sens to check passwords
- */
- if (group_mask && !(group_mask & u->u.group_mask))
- return 0;
-
if (!(u->flags & AU_O_INSECURE)) {
#ifdef CONFIG_HAP_CRYPT
ep = crypt(pass, u->pass);
enum pat_match_res
pat_match_auth(struct sample *smp, struct pattern *pattern)
{
-
struct userlist *ul = smp->ctx.a[0];
- char *user = smp->ctx.a[1];
- char *pass = smp->ctx.a[2];
- unsigned int group_mask = pattern->val.group_mask;
+ struct auth_users *u;
+ struct auth_groups_list *agl;
- if (check_user(ul, group_mask, user, pass))
- return PAT_MATCH;
- else
+ /* Check if the userlist is present in the context data. */
+ if (!ul)
+ return PAT_NOMATCH;
+
+ /* Browse the userlist for searching user. */
+ for (u = ul->users; u; u = u->next) {
+ if (strcmp(smp->data.str.str, u->user) == 0)
+ break;
+ }
+ if (!u)
+ return 0;
+
+ /* Browse each group for searching group name that match the pattern. */
+ for (agl = u->u.groups; agl; agl = agl->next) {
+ if (strcmp(agl->group->name, pattern->ptr.str) == 0)
+ break;
+ }
+ if (!agl)
return PAT_NOMATCH;
+ return PAT_MATCH;
}
goto out;
}
- newul->groupusers = calloc(MAX_AUTH_GROUPS, sizeof(char *));
newul->name = strdup(args[1]);
-
- if (!newul->groupusers | !newul->name) {
+ if (!newul->name) {
Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
err_code |= ERR_ALERT | ERR_ABORT;
goto out;
userlist = newul;
} else if (!strcmp(args[0], "group")) { /* new group */
- int cur_arg, i;
+ int cur_arg;
const char *err;
+ struct auth_groups *ag;
if (!*args[1]) {
Alert("parsing [%s:%d]: '%s' expects <name> as arguments.\n",
goto out;
}
- for(i = 0; i < userlist->grpcnt; i++)
- if (!strcmp(userlist->groups[i], args[1])) {
+ for (ag = userlist->groups; ag; ag = ag->next)
+ if (!strcmp(ag->name, args[1])) {
Warning("parsing [%s:%d]: ignoring duplicated group '%s' in userlist '%s'.\n",
file, linenum, args[1], userlist->name);
err_code |= ERR_ALERT;
goto out;
}
- if (userlist->grpcnt >= MAX_AUTH_GROUPS) {
- Alert("parsing [%s:%d]: too many groups (%u) in in userlist '%s' while adding group '%s'.\n",
- file, linenum, MAX_AUTH_GROUPS, userlist->name, args[1]);
- err_code |= ERR_ALERT | ERR_FATAL;
+ ag = calloc(1, sizeof(*ag));
+ if (!ag) {
+ Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_ABORT;
+ goto out;
+ }
+
+ ag->name = strdup(args[1]);
+ if (!ag) {
+ Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_ABORT;
goto out;
}
while (*args[cur_arg]) {
if (!strcmp(args[cur_arg], "users")) {
- userlist->groupusers[userlist->grpcnt] = strdup(args[cur_arg + 1]);
+ ag->groupusers = strdup(args[cur_arg + 1]);
cur_arg += 2;
continue;
} else {
}
}
- userlist->groups[userlist->grpcnt++] = strdup(args[1]);
+ ag->next = userlist->groups;
+ userlist->groups = ag;
+
} else if (!strcmp(args[0], "user")) { /* new user */
struct auth_users *newuser;
int cur_arg;
cur_arg += 2;
continue;
} else if (!strcmp(args[cur_arg], "groups")) {
- newuser->u.groups = strdup(args[cur_arg + 1]);
+ newuser->u.groups_names = strdup(args[cur_arg + 1]);
cur_arg += 2;
continue;
} else {
int cfgerr = 0;
struct proxy *curproxy = NULL;
struct server *newsrv = NULL;
- struct userlist *curuserlist = NULL;
int err_code = 0;
unsigned int next_pxid = 1;
struct bind_conf *bind_conf;
pool2_capture = create_pool("capture", global.tune.cookie_len, MEM_F_SHARED);
+ /* Post initialisation of the users and groups lists. */
+ err_code = userlist_postinit();
+ if (err_code != ERR_NONE)
+ goto out;
+
/* first, we will invert the proxy list order */
curproxy = NULL;
while (proxy) {
curproxy->conf.args.line = 0;
}
- /* only now we can check if some args remain unresolved */
+ /* only now we can check if some args remain unresolved.
+ * This must be done after the users and groups resolution.
+ */
cfgerr += smp_resolve_args(curproxy);
if (!cfgerr)
cfgerr += acl_find_targets(curproxy);
}
}
- for (curuserlist = userlist; curuserlist; curuserlist = curuserlist->next) {
- struct auth_users *curuser;
- int g;
-
- for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
- unsigned int group_mask = 0;
- char *group = NULL;
-
- if (!curuser->u.groups)
- continue;
-
- while ((group = strtok(group?NULL:curuser->u.groups, ","))) {
-
- for (g = 0; g < curuserlist->grpcnt; g++)
- if (!strcmp(curuserlist->groups[g], group))
- break;
-
- if (g == curuserlist->grpcnt) {
- Alert("userlist '%s': no such group '%s' specified in user '%s'\n",
- curuserlist->name, group, curuser->user);
- err_code |= ERR_ALERT | ERR_FATAL;
- goto out;
- }
-
- group_mask |= (1 << g);
- }
-
- free(curuser->u.groups);
- curuser->u.group_mask = group_mask;
- }
-
- for (g = 0; g < curuserlist->grpcnt; g++) {
- char *user = NULL;
-
- if (!curuserlist->groupusers[g])
- continue;
-
- while ((user = strtok(user?NULL:curuserlist->groupusers[g], ","))) {
- for (curuser = curuserlist->users; curuser; curuser = curuser->next)
- if (!strcmp(curuser->user, user))
- break;
-
- if (!curuser) {
- Alert("userlist '%s': no such user '%s' specified in group '%s'\n",
- curuserlist->name, user, curuserlist->groups[g]);
- err_code |= ERR_ALERT | ERR_FATAL;
- goto out;
- }
-
- curuser->u.group_mask |= (1 << g);
- }
-
- free(curuserlist->groupusers[g]);
- }
-
- free(curuserlist->groupusers);
-
-#ifdef DEBUG_AUTH
- for (g = 0; g < curuserlist->grpcnt; g++) {
- fprintf(stderr, "group %s, id %d, mask %08X, users:", curuserlist->groups[g], g , 1 << g);
-
- for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
- if (curuser->u.group_mask & (1 << g))
- fprintf(stderr, " %s", curuser->user);
- }
-
- fprintf(stderr, "\n");
- }
-#endif
-
- }
-
/* automatically compute fullconn if not set. We must not do it in the
* loop above because cross-references are not yet fully resolved.
*/
return !!parse_binary(*text, &pattern->ptr.str, &pattern->len, err);
}
-/* Parse and concatenate all further strings into one. */
-int
-pat_parse_strcat(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err)
-{
- int len = 0, i;
- char *s;
- struct chunk *trash;
-
- for (i = 0; *text[i]; i++)
- len += strlen(text[i])+1;
-
- pattern->type = SMP_T_CSTR;
- if (usage == PAT_U_COMPILE) {
- pattern->ptr.str = calloc(1, len);
- if (!pattern->ptr.str) {
- memprintf(err, "out of memory while loading pattern");
- return 0;
- }
- }
- else {
- trash = get_trash_chunk();
- if (trash->size < len) {
- memprintf(err, "no space avalaible in the buffer. expect %d, provides %d",
- len, trash->size);
- return 0;
- }
- pattern->ptr.str = trash->str;
- }
-
- s = pattern->ptr.str;
-
- for (i = 0; *text[i]; i++)
- s += sprintf(s, i?" %s":"%s", text[i]);
-
- pattern->len = len;
-
- return i;
-}
-
/* Parse a regex. It is allocated. */
int pat_parse_reg(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err)
{
* have the credentials overwritten by another session in parallel.
*/
+/* This bufffer is initialized in the file 'src/haproxy.c'. This length is
+ * set according to global.tune.bufsize.
+ */
char *get_http_auth_buff;
int
return 0;
smp->type = SMP_T_BOOL;
- smp->data.uint = check_user(args->data.usr, 0, l4->txn.auth.user, l4->txn.auth.pass);
+ smp->data.uint = check_user(args->data.usr, l4->txn.auth.user, l4->txn.auth.pass);
return 1;
}
if (!get_http_auth(l4))
return 0;
- /* pat_match_auth() will need several information at once */
- smp->ctx.a[0] = args->data.usr; /* user list */
- smp->ctx.a[1] = l4->txn.auth.user; /* user name */
- smp->ctx.a[2] = l4->txn.auth.pass; /* password */
-
/* if the user does not belong to the userlist or has a wrong password,
* report that it unconditionally does not match. Otherwise we return
- * a non-zero integer which will be ignored anyway since all the params
- * that pat_match_auth() will use are in test->ctx.a[0,1,2].
+ * a string containing the username.
*/
- smp->type = SMP_T_BOOL;
- smp->data.uint = check_user(args->data.usr, 0, l4->txn.auth.user, l4->txn.auth.pass);
- if (smp->data.uint)
- smp->type = SMP_T_UINT;
+ if (!check_user(args->data.usr, l4->txn.auth.user, l4->txn.auth.pass))
+ return 0;
+
+ /* pat_match_auth() will need the user list */
+ smp->ctx.a[0] = args->data.usr;
+
+ smp->type = SMP_T_CSTR;
+ smp->data.str.str = l4->txn.auth.user;
+ smp->data.str.len = strlen(l4->txn.auth.user);
return 1;
}
{ "hdr_reg", "req.hdr", pat_parse_reg, pat_match_reg },
{ "hdr_sub", "req.hdr", pat_parse_str, pat_match_sub },
- { "http_auth_group", NULL, pat_parse_strcat, pat_match_auth },
+ { "http_auth_group", NULL, pat_parse_str, pat_match_auth },
{ "method", NULL, pat_parse_meth, pat_match_meth },
{ "hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_UINT, SMP_USE_HRQHV },
{ "http_auth", smp_fetch_http_auth, ARG1(1,USR), NULL, SMP_T_BOOL, SMP_USE_HRQHV },
- { "http_auth_group", smp_fetch_http_auth_grp, ARG1(1,USR), NULL, SMP_T_BOOL, SMP_USE_HRQHV },
+ { "http_auth_group", smp_fetch_http_auth_grp, ARG1(1,USR), NULL, SMP_T_CSTR, SMP_USE_HRQHV },
{ "http_first_req", smp_fetch_http_first_req, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
{ "method", smp_fetch_meth, 0, NULL, SMP_T_UINT, SMP_USE_HRQHP },
{ "path", smp_fetch_path, 0, NULL, SMP_T_CSTR, SMP_USE_HRQHV },