[MINOR] generic auth support with groups and encrypted passwords
Add generic authentication & authorization support.
Groups are implemented as bitmaps so the count is limited to
sizeof(int)*8 == 32.
Encrypted passwords are supported with libcrypt and crypt(3), so it is
possible to use any method supported by your system. For example modern
Linux/glibc instalations support MD5/SHA-256/SHA-512 and of course classic,
DES-based encryption.
diff --git a/Makefile b/Makefile
index 0c9df3f..022731d 100644
--- a/Makefile
+++ b/Makefile
@@ -108,7 +108,8 @@
#### Debug settings
# You can enable debugging on specific code parts by setting DEBUG=-DDEBUG_xxx.
# Currently defined DEBUG macros include DEBUG_FULL, DEBUG_MEMORY, DEBUG_FSM,
-# and DEBUG_HASH. Please check sources for exact meaning or do not use at all.
+# DEBUG_HASH and DEBUG_AUTH. Please check sources for exact meaning or do not
+# use at all.
DEBUG =
#### Additional include and library dirs
@@ -170,6 +171,7 @@
USE_GETSOCKNAME = implicit
USE_POLL = implicit
USE_TPROXY = implicit
+ USE_LIBCRYPT = implicit
else
ifeq ($(TARGET),linux24)
# This is for standard Linux 2.4 with netfilter but without epoll()
@@ -177,6 +179,7 @@
USE_NETFILTER = implicit
USE_POLL = implicit
USE_TPROXY = implicit
+ USE_LIBCRYPT = implicit
else
ifeq ($(TARGET),linux24e)
# This is for enhanced Linux 2.4 with netfilter and epoll() patch > 0.21
@@ -187,6 +190,7 @@
USE_SEPOLL = implicit
USE_MY_EPOLL = implicit
USE_TPROXY = implicit
+ USE_LIBCRYPT = implicit
else
ifeq ($(TARGET),linux26)
# This is for standard Linux 2.6 with netfilter and standard epoll()
@@ -196,6 +200,7 @@
USE_EPOLL = implicit
USE_SEPOLL = implicit
USE_TPROXY = implicit
+ USE_LIBCRYPT = implicit
else
ifeq ($(TARGET),solaris)
# This is for Solaris 8
@@ -209,6 +214,7 @@
USE_POLL = implicit
USE_KQUEUE = implicit
USE_TPROXY = implicit
+ USE_LIBCRYPT = implicit
else
ifeq ($(TARGET),openbsd)
# This is for OpenBSD >= 3.0
@@ -324,6 +330,12 @@
BUILD_OPTIONS += $(call ignore_implicit,USE_LINUX_TPROXY)
endif
+ifneq ($(USE_LIBCRYPT),)
+OPTIONS_CFLAGS += -DCONFIG_HAP_CRYPT
+BUILD_OPTIONS += $(call ignore_implicit,USE_LIBCRYPT)
+OPTIONS_LDFLAGS += -lcrypt
+endif
+
ifneq ($(USE_POLL),)
OPTIONS_CFLAGS += -DENABLE_POLL
OPTIONS_OBJS += src/ev_poll.o
@@ -464,7 +476,7 @@
src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o \
src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
- src/acl.o src/pattern.o src/memory.o src/freq_ctr.o
+ src/acl.o src/pattern.o src/memory.o src/freq_ctr.o src/auth.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
$(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
diff --git a/include/common/cfgparse.h b/include/common/cfgparse.h
index 3b376a0..a67f0d1 100644
--- a/include/common/cfgparse.h
+++ b/include/common/cfgparse.h
@@ -32,6 +32,7 @@
#define CFG_NONE 0
#define CFG_GLOBAL 1
#define CFG_LISTEN 2
+#define CFG_USERLIST 3
struct cfg_keyword {
int section; /* section type for this keyword */
diff --git a/include/common/uri_auth.h b/include/common/uri_auth.h
index 64f818b..b4c297c 100644
--- a/include/common/uri_auth.h
+++ b/include/common/uri_auth.h
@@ -15,6 +15,8 @@
#include <common/config.h>
+#include <types/auth.h>
+
/* here we find a very basic list of base64-encoded 'user:passwd' strings */
struct user_auth {
struct user_auth *next; /* next entry, NULL if none */
@@ -46,6 +48,7 @@
int flags; /* some flags describing the statistics page */
struct user_auth *users; /* linked list of valid user:passwd couples */
struct stat_scope *scope; /* linked list of authorized proxies */
+ struct list req_acl; /* */
struct uri_auth *next; /* Used at deinit() to build a list of unique elements */
};
diff --git a/include/proto/auth.h b/include/proto/auth.h
new file mode 100644
index 0000000..9808621
--- /dev/null
+++ b/include/proto/auth.h
@@ -0,0 +1,36 @@
+/*
+ * User authentication & authorization.
+ *
+ * Copyright 2010 Krzysztof Piotr Oledzki <ole@ans.pl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _PROTO_AUTH_H
+#define _PROTO_AUTH_H
+
+#include <common/config.h>
+#include <types/auth.h>
+
+extern struct userlist *userlist;
+
+struct userlist *auth_find_userlist(char *name);
+unsigned int auth_resolve_groups(struct userlist *l, char *groups);
+struct req_acl_rule *parse_auth_cond(const char **args, const char *file, int linenum, struct list *known_acl, int *acl_requires);
+void userlist_free(struct userlist *ul);
+void req_acl_free(struct list *r);
+int acl_match_auth(struct acl_test *test, struct acl_pattern *pattern);
+
+#endif /* _PROTO_AUTH_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/include/types/auth.h b/include/types/auth.h
new file mode 100644
index 0000000..d278de6
--- /dev/null
+++ b/include/types/auth.h
@@ -0,0 +1,73 @@
+/*
+ * User authentication & authorization.
+ *
+ * Copyright 2010 Krzysztof Piotr Oledzki <ole@ans.pl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _TYPES_AUTH_H
+#define _TYPES_AUTH_H
+
+#include <common/config.h>
+#include <common/mini-clist.h>
+
+#include <types/auth.h>
+
+#define MAX_AUTH_GROUPS (unsigned int)(sizeof(int)*8)
+
+#define AU_O_INSECURE 0x00000001 /* insecure, unencrypted password */
+
+enum {
+ PR_REQ_ACL_ACT_UNKNOWN = 0,
+ PR_REQ_ACL_ACT_ALLOW,
+ PR_REQ_ACL_ACT_DENY,
+ PR_REQ_ACL_ACT_HTTP_AUTH,
+
+ PR_REQ_ACL_ACT_MAX
+};
+
+
+struct req_acl_rule {
+ struct list list;
+ struct acl_cond *cond; /* acl condition to meet */
+ unsigned int action;
+ union {
+ struct {
+ char *realm;
+ } http_auth;
+ };
+};
+
+struct auth_users {
+ struct auth_users *next;
+ unsigned int flags;
+ char *user, *pass;
+ union {
+ char *groups;
+ unsigned int group_mask;
+ };
+};
+
+struct userlist {
+ struct userlist *next;
+ char *name;
+ struct auth_users *users;
+ int grpcnt;
+ char *groups[MAX_AUTH_GROUPS];
+ char **groupusers;
+};
+
+#endif /* _TYPES_AUTH_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 0000000..58fc5be
--- /dev/null
+++ b/src/auth.c
@@ -0,0 +1,241 @@
+/*
+ * User authentication & authorization
+ *
+ * Copyright 2010 Krzysztof Piotr Oledzki <ole@ans.pl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define _XOPEN_SOURCE 500
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/config.h>
+
+#include <proto/acl.h>
+#include <proto/log.h>
+
+#include <types/auth.h>
+
+struct userlist *userlist = NULL; /* list of all existing userlists */
+
+/* find targets for selected gropus. The function returns pointer to
+ * the userlist struct ot NULL if name is NULL/empty or unresolvable.
+ */
+
+struct userlist *
+auth_find_userlist(char *name)
+{
+ struct userlist *l;
+
+ if (!name || !*name)
+ return NULL;
+
+ for (l = userlist; l; l = l->next)
+ if (!strcmp(l->name, name))
+ return l;
+
+ 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)
+{
+
+ 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;
+}
+
+struct req_acl_rule *
+parse_auth_cond(const char **args, const char *file, int linenum, struct list *known_acl, int *acl_requires)
+{
+ struct req_acl_rule *req_acl;
+ int cur_arg;
+
+ req_acl = (struct req_acl_rule*)calloc(1, sizeof(struct req_acl_rule));
+ if (!req_acl) {
+ Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ return NULL;
+ }
+
+ if (!*args[0]) {
+ goto req_error_parsing;
+ } else if (!strcmp(args[0], "allow")) {
+ req_acl->action = PR_REQ_ACL_ACT_ALLOW;
+ cur_arg = 1;
+ } else if (!strcmp(args[0], "deny")) {
+ req_acl->action = PR_REQ_ACL_ACT_DENY;
+ cur_arg = 1;
+ } else if (!strcmp(args[0], "auth")) {
+ req_acl->action = PR_REQ_ACL_ACT_HTTP_AUTH;
+ cur_arg = 1;
+
+ while(*args[cur_arg]) {
+ if (!strcmp(args[cur_arg], "realm")) {
+ req_acl->http_auth.realm = strdup(args[cur_arg + 1]);
+ cur_arg+=2;
+ continue;
+ } else
+ break;
+ }
+ } else {
+req_error_parsing:
+ Alert("parsing [%s:%d]: %s '%s', expects 'allow', 'deny', 'auth'.\n",
+ file, linenum, *args[1]?"unknown parameter":"missing keyword in", args[*args[1]?1:0]);
+ return NULL;
+ }
+
+ if (*args[cur_arg]) {
+ int pol = ACL_COND_NONE;
+ struct acl_cond *cond;
+
+ if (!strcmp(args[cur_arg], "if"))
+ pol = ACL_COND_IF;
+ else if (!strcmp(args[cur_arg], "unless"))
+ pol = ACL_COND_UNLESS;
+ else {
+ Alert("parsing [%s:%d]: '%s' expects 'realm' for 'auth' or"
+ " either 'if' or 'unless' followed by a condition but found '%s'.\n",
+ file, linenum, args[0], args[cur_arg]);
+ return NULL;
+ }
+
+ if ((cond = parse_acl_cond((const char **)args + cur_arg + 1, known_acl, pol)) == NULL) {
+ Alert("parsing [%s:%d]: error detected while parsing 'req' condition.\n",
+ file, linenum);
+ return NULL;
+ }
+
+ cond->file = file;
+ cond->line = linenum;
+ *acl_requires |= cond->requires;
+ req_acl->cond = cond;
+ }
+
+ return req_acl;
+}
+
+void
+userlist_free(struct userlist *ul)
+{
+ struct userlist *tul;
+ struct auth_users *au, *tau;
+ int i;
+
+ while (ul) {
+ au = ul->users;
+ while (au) {
+ tau = au;
+ au = au->next;
+ free(tau->user);
+ free(tau->pass);
+ free(tau);
+ }
+
+ tul = ul;
+ ul = ul->next;
+
+ for (i = 0; i < tul->grpcnt; i++)
+ free(tul->groups[i]);
+
+ free(tul->name);
+ free(tul);
+ };
+}
+
+void
+req_acl_free(struct list *r) {
+ struct req_acl_rule *tr, *pr;
+
+ list_for_each_entry_safe(pr, tr, r, list) {
+ LIST_DEL(&pr->list);
+ if (pr->action == PR_REQ_ACL_ACT_HTTP_AUTH)
+ free(pr->http_auth.realm);
+
+ free(pr);
+ }
+}
+
+/*
+ * 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)
+{
+
+ 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);
+#endif
+
+ for (u = ul->users; u; u = u->next)
+ if (!strcmp(user, u->user))
+ break;
+
+ if (!u)
+ return 0;
+
+#ifdef DEBUG_AUTH
+ fprintf(stderr, "cfg: user=%s, pass=%s, group_mask=%u, flags=%X",
+ u->user, u->pass, u->group_mask, u->flags);
+#endif
+
+ /*
+ * if user matches but group does not,
+ * it makes no sens to check passwords
+ */
+ if (group_mask && !(group_mask & u->group_mask))
+ return 0;
+
+ if (!(u->flags & AU_O_INSECURE)) {
+#ifdef CONFIG_HAP_CRYPT
+ ep = crypt(pass, u->pass);
+#else
+ return 0;
+#endif
+ } else
+ ep = pass;
+
+#ifdef DEBUG_AUTH
+ fprintf(stderr, ", crypt=%s\n", ep);
+#endif
+
+ if (!strcmp(ep, u->pass))
+ return 1;
+ else
+ return 0;
+}
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 77e61d8..5c181f1 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -37,6 +37,7 @@
#include <types/global.h>
#include <proto/acl.h>
+#include <proto/auth.h>
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/checks.h>
@@ -4008,6 +4009,175 @@
return err_code;
}
+int
+cfg_parse_users(const char *file, int linenum, char **args, int kwm)
+{
+
+ int err_code = 0;
+ const char *err;
+
+ if (!strcmp(args[0], "userlist")) { /* new userlist */
+ struct userlist *newul;
+
+ if (!*args[1]) {
+ Alert("parsing [%s:%d]: '%s' expects <name> as arguments.\n",
+ file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ err = invalid_char(args[1]);
+ if (err) {
+ Alert("parsing [%s:%d]: character '%c' is not permitted in '%s' name '%s'.\n",
+ file, linenum, *err, args[0], args[1]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ for (newul = userlist; newul; newul = newul->next)
+ if (!strcmp(newul->name, args[1])) {
+ Warning("parsing [%s:%d]: ignoring duplicated userlist '%s'.\n",
+ file, linenum, args[1]);
+ err_code |= ERR_WARN;
+ goto out;
+ }
+
+ newul = (struct userlist *)calloc(1, sizeof(struct userlist));
+ if (!newul) {
+ Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_ABORT;
+ goto out;
+ }
+
+ newul->groupusers = calloc(MAX_AUTH_GROUPS, sizeof(char *));
+ newul->name = strdup(args[1]);
+
+ if (!newul->groupusers | !newul->name) {
+ Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_ABORT;
+ goto out;
+ }
+
+ newul->next = userlist;
+ userlist = newul;
+
+ } else if (!strcmp(args[0], "group")) { /* new group */
+ int cur_arg, i;
+ const char *err;
+
+ if (!*args[1]) {
+ Alert("parsing [%s:%d]: '%s' expects <name> as arguments.\n",
+ file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ err = invalid_char(args[1]);
+ if (err) {
+ Alert("parsing [%s:%d]: character '%c' is not permitted in '%s' name '%s'.\n",
+ file, linenum, *err, args[0], args[1]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ for(i = 0; i < userlist->grpcnt; i++)
+ if (!strcmp(userlist->groups[i], 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;
+ goto out;
+ }
+
+ cur_arg = 2;
+
+ while (*args[cur_arg]) {
+ if (!strcmp(args[cur_arg], "users")) {
+ userlist->groupusers[userlist->grpcnt] = strdup(args[cur_arg + 1]);
+ cur_arg += 2;
+ continue;
+ } else {
+ Alert("parsing [%s:%d]: '%s' only supports 'users' option.\n",
+ file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ }
+
+ userlist->groups[userlist->grpcnt++] = strdup(args[1]);
+ } else if (!strcmp(args[0], "user")) { /* new user */
+ struct auth_users *newuser;
+ int cur_arg;
+
+ if (!*args[1]) {
+ Alert("parsing [%s:%d]: '%s' expects <name> as arguments.\n",
+ file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ for (newuser = userlist->users; newuser; newuser = newuser->next)
+ if (!strcmp(newuser->user, args[1])) {
+ Warning("parsing [%s:%d]: ignoring duplicated user '%s' in userlist '%s'.\n",
+ file, linenum, args[1], userlist->name);
+ err_code |= ERR_ALERT;
+ goto out;
+ }
+
+ newuser = (struct auth_users *)calloc(1, sizeof(struct auth_users));
+ if (!newuser) {
+ Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_ABORT;
+ goto out;
+ }
+
+ newuser->user = strdup(args[1]);
+
+ newuser->next = userlist->users;
+ userlist->users = newuser;
+
+ cur_arg = 2;
+
+ while (*args[cur_arg]) {
+ if (!strcmp(args[cur_arg], "password")) {
+#ifndef CONFIG_HAP_CRYPT
+ Warning("parsing [%s:%d]: no crypt(3) support compiled, encrypted passwords will not work.\n",
+ file, linenum);
+ err_code |= ERR_ALERT;
+#endif
+ newuser->pass = strdup(args[cur_arg + 1]);
+ cur_arg += 2;
+ continue;
+ } else if (!strcmp(args[cur_arg], "insecure-password")) {
+ newuser->pass = strdup(args[cur_arg + 1]);
+ newuser->flags |= AU_O_INSECURE;
+ cur_arg += 2;
+ continue;
+ } else if (!strcmp(args[cur_arg], "groups")) {
+ newuser->groups = strdup(args[cur_arg + 1]);
+ cur_arg += 2;
+ continue;
+ } else {
+ Alert("parsing [%s:%d]: '%s' only supports 'password', 'insecure-password' and 'groups' options.\n",
+ file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ }
+ } else {
+ Alert("parsing [%s:%d]: unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "users");
+ err_code |= ERR_ALERT | ERR_FATAL;
+ }
+
+out:
+ return err_code;
+}
/*
* This function reads and parses the configuration file given in the argument.
@@ -4172,6 +4342,10 @@
confsect = CFG_GLOBAL;
free(cursection);
cursection = strdup(args[0]);
+ } else if (!strcmp(args[0], "userlist")) {
+ confsect = CFG_USERLIST;
+ free(cursection);
+ cursection = strdup(args[0]);
}
/* else it's a section keyword */
@@ -4182,8 +4356,11 @@
case CFG_GLOBAL:
err_code |= cfg_parse_global(file, linenum, args, kwm);
break;
+ case CFG_USERLIST:
+ err_code |= cfg_parse_users(file, linenum, args, kwm);
+ break;
default:
- Alert("parsing [%s:%d] : unknown keyword '%s' out of section.\n", file, linenum, args[0]);
+ Alert("parsing [%s:%d]: unknown keyword '%s' out of section.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
}
@@ -4210,6 +4387,7 @@
int cfgerr = 0;
struct proxy *curproxy = NULL;
struct server *newsrv = NULL;
+ struct userlist *curuserlist = NULL;
int err_code = 0;
unsigned int next_pxid = 1;
@@ -4817,6 +4995,78 @@
curproxy = curproxy->next;
}
+ 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->groups)
+ continue;
+
+ while ((group = strtok(group?NULL:curuser->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->groups);
+ curuser->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->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->group_mask & (1 << g))
+ fprintf(stderr, " %s", curuser->user);
+ }
+
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ }
+
/*
* Recount currently required checks.
*/
diff --git a/src/haproxy.c b/src/haproxy.c
index 2fdde61..0393ebf 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -67,6 +67,7 @@
#include <types/capture.h>
#include <types/global.h>
+#include <proto/auth.h>
#include <proto/acl.h>
#include <proto/backend.h>
#include <proto/buffers.h>
@@ -177,6 +178,16 @@
"\n\n",
DEFAULT_MAXCONN, BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS);
+ printf("Encrypted password support via crypt(3): "
+#ifdef CONFIG_HAP_CRYPT
+ "yes"
+#else
+ "no"
+#endif
+ "\n");
+
+ putchar('\n');
+
list_pollers(stdout);
putchar('\n');
}
@@ -851,6 +862,7 @@
pool_destroy2(p->req_cap_pool);
pool_destroy2(p->rsp_cap_pool);
pool_destroy2(p->hdr_idx_pool);
+
p0 = p;
p = p->next;
free(p0);
@@ -874,6 +886,8 @@
free(uap);
}
+ userlist_free(userlist);
+
protocol_unbind_all();
free(global.chroot); global.chroot = NULL;