]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] generic auth support with groups and encrypted passwords
authorKrzysztof Piotr Oledzki <ole@ans.pl>
Fri, 29 Jan 2010 16:50:44 +0000 (17:50 +0100)
committerWilly Tarreau <w@1wt.eu>
Sun, 31 Jan 2010 18:14:07 +0000 (19:14 +0100)
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.

Makefile
include/common/cfgparse.h
include/common/uri_auth.h
include/proto/auth.h [new file with mode: 0644]
include/types/auth.h [new file with mode: 0644]
src/auth.c [new file with mode: 0644]
src/cfgparse.c
src/haproxy.c

index 0c9df3fb6230bc43d7982a44d83e9d9671c5641c..022731d8beb2851fdb5ddd8666e29d70c3e183c5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -108,7 +108,8 @@ SMALL_OPTS =
 #### 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 @@ ifeq ($(TARGET),linux22)
   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 @@ ifeq ($(TARGET),linux24)
   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 @@ ifeq ($(TARGET),linux24e)
   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 @@ ifeq ($(TARGET),linux26)
   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 @@ ifeq ($(TARGET),freebsd)
   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 @@ OPTIONS_CFLAGS += -DCONFIG_HAP_LINUX_TPROXY
 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 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
        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 \
index 3b376a09f31843d83f318188d29ff56556105c06..a67f0d1cf0eca0269312d804cde22d76cd61ea1a 100644 (file)
@@ -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 */
index 64f818b89e22b2cfbd9e017905809d4aa7f44133..b4c297c77511be17330e6cd32d7c33aee6d9d78b 100644 (file)
@@ -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 @@ struct uri_auth {
        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 (file)
index 0000000..9808621
--- /dev/null
@@ -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 (file)
index 0000000..d278de6
--- /dev/null
@@ -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 (file)
index 0000000..58fc5be
--- /dev/null
@@ -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;
+}
index 77e61d864d2032292b1edfea7529537e8c6655f2..5c181f16c4ffff9af06a657a327c57f8d9063f43 100644 (file)
@@ -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 @@ stats_error_parsing:
        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 @@ int readcfgfile(const char *file)
                        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 @@ int readcfgfile(const char *file)
                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 check_config_validity()
        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 @@ int check_config_validity()
                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.
         */
index 2fdde61414941e131461942ca80963027ef3c25c..0393ebfee13c2a06d11998fae7132e2c6d1527dd 100644 (file)
@@ -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 @@ void display_build_opts()
               "\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 @@ void deinit(void)
                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 @@ void deinit(void)
                free(uap);
        }
 
+       userlist_free(userlist);
+
        protocol_unbind_all();
 
        free(global.chroot);  global.chroot = NULL;