blob: 8ae4858640bb9069cc18160049df5f94cac1eb40 [file] [log] [blame]
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +01001/*
2 * User authentication & authorization
3 *
4 * Copyright 2010 Krzysztof Piotr Oledzki <ole@ans.pl>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau890a33e2010-03-04 19:10:14 +010013#ifdef CONFIG_HAP_CRYPT
14/* This is to have crypt() defined on Linux */
15#define _GNU_SOURCE
16
17#ifdef NEED_CRYPT_H
18/* some platforms such as Solaris need this */
19#include <crypt.h>
20#endif
21#endif /* CONFIG_HAP_CRYPT */
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010022
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include <common/config.h>
29
30#include <proto/acl.h>
31#include <proto/log.h>
32
33#include <types/auth.h>
34
35struct userlist *userlist = NULL; /* list of all existing userlists */
36
37/* find targets for selected gropus. The function returns pointer to
38 * the userlist struct ot NULL if name is NULL/empty or unresolvable.
39 */
40
41struct userlist *
42auth_find_userlist(char *name)
43{
44 struct userlist *l;
45
46 if (!name || !*name)
47 return NULL;
48
49 for (l = userlist; l; l = l->next)
50 if (!strcmp(l->name, name))
51 return l;
52
53 return NULL;
54}
55
56/* find group_mask for selected gropus. The function returns 1 if OK or nothing to do,
57 * 0 if case of unresolved groupname.
58 * WARING: the function destroys the list (strtok), so it can only be used once.
59 */
60
61unsigned int
62auth_resolve_groups(struct userlist *l, char *groups)
63{
64
65 char *group = NULL;
66 unsigned int g, group_mask = 0;
67
68 if (!groups || !*groups)
69 return 0;
70
71 while ((group = strtok(group?NULL:groups," "))) {
72 for (g = 0; g < l->grpcnt; g++)
73 if (!strcmp(l->groups[g], group))
74 break;
75
76 if (g == l->grpcnt) {
77 Alert("No such group '%s' in userlist '%s'.\n",
78 group, l->name);
79 return 0;
80 }
81
82 group_mask |= (1 << g);
83 }
84
85 return group_mask;
86}
87
88struct req_acl_rule *
Willy Tarreau9cc670f2010-02-01 10:43:44 +010089parse_auth_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010090{
91 struct req_acl_rule *req_acl;
92 int cur_arg;
93
94 req_acl = (struct req_acl_rule*)calloc(1, sizeof(struct req_acl_rule));
95 if (!req_acl) {
96 Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
97 return NULL;
98 }
99
100 if (!*args[0]) {
101 goto req_error_parsing;
102 } else if (!strcmp(args[0], "allow")) {
103 req_acl->action = PR_REQ_ACL_ACT_ALLOW;
104 cur_arg = 1;
105 } else if (!strcmp(args[0], "deny")) {
106 req_acl->action = PR_REQ_ACL_ACT_DENY;
107 cur_arg = 1;
108 } else if (!strcmp(args[0], "auth")) {
109 req_acl->action = PR_REQ_ACL_ACT_HTTP_AUTH;
110 cur_arg = 1;
111
112 while(*args[cur_arg]) {
113 if (!strcmp(args[cur_arg], "realm")) {
114 req_acl->http_auth.realm = strdup(args[cur_arg + 1]);
115 cur_arg+=2;
116 continue;
117 } else
118 break;
119 }
120 } else {
121req_error_parsing:
122 Alert("parsing [%s:%d]: %s '%s', expects 'allow', 'deny', 'auth'.\n",
123 file, linenum, *args[1]?"unknown parameter":"missing keyword in", args[*args[1]?1:0]);
124 return NULL;
125 }
126
Willy Tarreau9cc670f2010-02-01 10:43:44 +0100127 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100128 struct acl_cond *cond;
129
Willy Tarreau9cc670f2010-02-01 10:43:44 +0100130 if ((cond = build_acl_cond(file, linenum, proxy, args+cur_arg)) == NULL) {
131 Alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition.\n",
132 file, linenum, args[0]);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100133 return NULL;
134 }
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100135 req_acl->cond = cond;
136 }
Willy Tarreau9cc670f2010-02-01 10:43:44 +0100137 else if (*args[cur_arg]) {
138 Alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or"
139 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
140 file, linenum, args[0], args[cur_arg]);
141 return NULL;
142 }
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100143
144 return req_acl;
145}
146
147void
148userlist_free(struct userlist *ul)
149{
150 struct userlist *tul;
151 struct auth_users *au, *tau;
152 int i;
153
154 while (ul) {
155 au = ul->users;
156 while (au) {
157 tau = au;
158 au = au->next;
159 free(tau->user);
160 free(tau->pass);
161 free(tau);
162 }
163
164 tul = ul;
165 ul = ul->next;
166
167 for (i = 0; i < tul->grpcnt; i++)
168 free(tul->groups[i]);
169
170 free(tul->name);
171 free(tul);
172 };
173}
174
175void
176req_acl_free(struct list *r) {
177 struct req_acl_rule *tr, *pr;
178
179 list_for_each_entry_safe(pr, tr, r, list) {
180 LIST_DEL(&pr->list);
181 if (pr->action == PR_REQ_ACL_ACT_HTTP_AUTH)
182 free(pr->http_auth.realm);
183
184 free(pr);
185 }
186}
187
188/*
189 * Authenticate and authorize user; return 1 if OK, 0 if case of error.
190 */
191int
192check_user(struct userlist *ul, unsigned int group_mask, const char *user, const char *pass)
193{
194
195 struct auth_users *u;
196 const char *ep;
197
198#ifdef DEBUG_AUTH
199 fprintf(stderr, "req: userlist=%s, user=%s, pass=%s, group_mask=%u\n",
200 ul->name, user, pass, group_mask);
201#endif
202
203 for (u = ul->users; u; u = u->next)
204 if (!strcmp(user, u->user))
205 break;
206
207 if (!u)
208 return 0;
209
210#ifdef DEBUG_AUTH
211 fprintf(stderr, "cfg: user=%s, pass=%s, group_mask=%u, flags=%X",
212 u->user, u->pass, u->group_mask, u->flags);
213#endif
214
215 /*
216 * if user matches but group does not,
217 * it makes no sens to check passwords
218 */
Willy Tarreaub4c06b72010-02-02 11:28:20 +0100219 if (group_mask && !(group_mask & u->u.group_mask))
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100220 return 0;
221
222 if (!(u->flags & AU_O_INSECURE)) {
223#ifdef CONFIG_HAP_CRYPT
224 ep = crypt(pass, u->pass);
225#else
226 return 0;
227#endif
228 } else
229 ep = pass;
230
231#ifdef DEBUG_AUTH
232 fprintf(stderr, ", crypt=%s\n", ep);
233#endif
234
235 if (!strcmp(ep, u->pass))
236 return 1;
237 else
238 return 0;
239}
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100240
241int
242acl_match_auth(struct acl_test *test, struct acl_pattern *pattern)
243{
244
245 struct userlist *ul = test->ctx.a[0];
246 char *user = test->ctx.a[1];
247 char *pass = test->ctx.a[2];
248 unsigned int group_mask;
249
250 if (pattern)
251 group_mask = pattern->val.group_mask;
252 else
253 group_mask = 0;
254
255 if (check_user(ul, group_mask, user, pass))
256 return ACL_PAT_PASS;
257 else
258 return ACL_PAT_FAIL;
259}