blob: d7eca60d910bd0992c417ef0b0f710214477ffe0 [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 Tarreaue5733232019-05-22 19:24:06 +020013#ifdef USE_LIBCRYPT
Willy Tarreau890a33e2010-03-04 19:10:14 +010014/* This is to have crypt() defined on Linux */
15#define _GNU_SOURCE
16
Willy Tarreaue5733232019-05-22 19:24:06 +020017#ifdef USE_CRYPT_H
Willy Tarreau890a33e2010-03-04 19:10:14 +010018/* some platforms such as Solaris need this */
19#include <crypt.h>
20#endif
Willy Tarreaue5733232019-05-22 19:24:06 +020021#endif /* USE_LIBCRYPT */
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
Willy Tarreaue8692b42016-12-21 19:36:25 +010028#include <types/global.h>
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010029#include <common/config.h>
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010030#include <common/errors.h>
Willy Tarreau34d4b522018-10-29 18:02:54 +010031#include <common/hathreads.h>
Willy Tarreau80713382018-11-26 10:19:54 +010032#include <common/initcall.h>
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010033
34#include <proto/acl.h>
35#include <proto/log.h>
36
37#include <types/auth.h>
Thierry FOURNIERa65b3432013-11-28 18:22:00 +010038#include <types/pattern.h>
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010039
40struct userlist *userlist = NULL; /* list of all existing userlists */
41
Willy Tarreaue5733232019-05-22 19:24:06 +020042#ifdef USE_LIBCRYPT
Victor Kislovbf2bc1e2020-08-06 19:21:39 +030043#define CRYPT_STATE_MSG "yes"
Willy Tarreau943e7ec2018-10-29 19:16:27 +010044#ifdef HA_HAVE_CRYPT_R
45/* context for crypt_r() */
46static THREAD_LOCAL struct crypt_data crypt_data = { .initialized = 0 };
47#else
48/* lock for crypt() */
Willy Tarreau34d4b522018-10-29 18:02:54 +010049__decl_hathreads(static HA_SPINLOCK_T auth_lock);
50#endif
Victor Kislovbf2bc1e2020-08-06 19:21:39 +030051#else /* USE_LIBCRYPT */
52#define CRYPT_STATE_MSG "no"
Willy Tarreau943e7ec2018-10-29 19:16:27 +010053#endif
Willy Tarreau34d4b522018-10-29 18:02:54 +010054
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010055/* find targets for selected gropus. The function returns pointer to
56 * the userlist struct ot NULL if name is NULL/empty or unresolvable.
57 */
58
59struct userlist *
60auth_find_userlist(char *name)
61{
62 struct userlist *l;
63
64 if (!name || !*name)
65 return NULL;
66
67 for (l = userlist; l; l = l->next)
68 if (!strcmp(l->name, name))
69 return l;
70
71 return NULL;
72}
73
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010074int check_group(struct userlist *ul, char *name)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010075{
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010076 struct auth_groups *ag;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010077
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010078 for (ag = ul->groups; ag; ag = ag->next)
79 if (strcmp(name, ag->name) == 0)
80 return 1;
81 return 0;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010082}
83
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010084void
85userlist_free(struct userlist *ul)
86{
87 struct userlist *tul;
88 struct auth_users *au, *tau;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010089 struct auth_groups_list *agl, *tagl;
90 struct auth_groups *ag, *tag;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010091
92 while (ul) {
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010093 /* Free users. */
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010094 au = ul->users;
95 while (au) {
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010096 /* Free groups that own current user. */
97 agl = au->u.groups;
98 while (agl) {
99 tagl = agl;
100 agl = agl->next;
101 free(tagl);
102 }
103
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100104 tau = au;
105 au = au->next;
106 free(tau->user);
107 free(tau->pass);
108 free(tau);
109 }
110
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100111 /* Free grouplist. */
112 ag = ul->groups;
113 while (ag) {
114 tag = ag;
115 ag = ag->next;
116 free(tag->name);
117 free(tag);
118 }
119
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100120 tul = ul;
121 ul = ul->next;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100122 free(tul->name);
123 free(tul);
124 };
125}
126
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100127int userlist_postinit()
128{
129 struct userlist *curuserlist = NULL;
130
131 /* Resolve usernames and groupnames. */
132 for (curuserlist = userlist; curuserlist; curuserlist = curuserlist->next) {
133 struct auth_groups *ag;
134 struct auth_users *curuser;
135 struct auth_groups_list *grl;
136
137 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
138 char *group = NULL;
139 struct auth_groups_list *groups = NULL;
140
141 if (!curuser->u.groups_names)
142 continue;
143
144 while ((group = strtok(group?NULL:curuser->u.groups_names, ","))) {
145 for (ag = curuserlist->groups; ag; ag = ag->next) {
146 if (!strcmp(ag->name, group))
147 break;
148 }
149
150 if (!ag) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100151 ha_alert("userlist '%s': no such group '%s' specified in user '%s'\n",
152 curuserlist->name, group, curuser->user);
Dirkjan Bussink07fcaaa2014-04-28 22:57:16 +0000153 free(groups);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100154 return ERR_ALERT | ERR_FATAL;
155 }
156
157 /* Add this group at the group userlist. */
158 grl = calloc(1, sizeof(*grl));
159 if (!grl) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100160 ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
161 curuserlist->name);
Dirkjan Bussink07fcaaa2014-04-28 22:57:16 +0000162 free(groups);
163 return ERR_ALERT | ERR_FATAL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100164 }
165
166 grl->group = ag;
167 grl->next = groups;
168 groups = grl;
169 }
170
171 free(curuser->u.groups);
172 curuser->u.groups = groups;
173 }
174
175 for (ag = curuserlist->groups; ag; ag = ag->next) {
176 char *user = NULL;
177
178 if (!ag->groupusers)
179 continue;
180
181 while ((user = strtok(user?NULL:ag->groupusers, ","))) {
182 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
183 if (!strcmp(curuser->user, user))
184 break;
185 }
186
187 if (!curuser) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100188 ha_alert("userlist '%s': no such user '%s' specified in group '%s'\n",
189 curuserlist->name, user, ag->name);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100190 return ERR_ALERT | ERR_FATAL;
191 }
192
193 /* Add this group at the group userlist. */
194 grl = calloc(1, sizeof(*grl));
195 if (!grl) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100196 ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
197 curuserlist->name);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100198 return ERR_ALERT | ERR_FATAL;
199 }
200
201 grl->group = ag;
202 grl->next = curuser->u.groups;
203 curuser->u.groups = grl;
204 }
205
206 free(ag->groupusers);
207 ag->groupusers = NULL;
208 }
209
210#ifdef DEBUG_AUTH
211 for (ag = curuserlist->groups; ag; ag = ag->next) {
212 struct auth_groups_list *agl;
213
214 fprintf(stderr, "group %s, id %p, users:", ag->name, ag);
215 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
216 for (agl = curuser->u.groups; agl; agl = agl->next) {
217 if (agl->group == ag)
218 fprintf(stderr, " %s", curuser->user);
219 }
220 }
221 fprintf(stderr, "\n");
222 }
223#endif
224 }
225
226 return ERR_NONE;
227}
228
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100229/*
230 * Authenticate and authorize user; return 1 if OK, 0 if case of error.
231 */
232int
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100233check_user(struct userlist *ul, const char *user, const char *pass)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100234{
235
236 struct auth_users *u;
CJ Ess6eac32e2015-05-02 16:35:24 -0400237#ifdef DEBUG_AUTH
238 struct auth_groups_list *agl;
239#endif
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100240 const char *ep;
241
242#ifdef DEBUG_AUTH
CJ Ess6eac32e2015-05-02 16:35:24 -0400243 fprintf(stderr, "req: userlist=%s, user=%s, pass=%s\n",
244 ul->name, user, pass);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100245#endif
246
247 for (u = ul->users; u; u = u->next)
248 if (!strcmp(user, u->user))
249 break;
250
251 if (!u)
252 return 0;
253
254#ifdef DEBUG_AUTH
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100255 fprintf(stderr, "cfg: user=%s, pass=%s, flags=%X, groups=",
256 u->user, u->pass, u->flags);
257 for (agl = u->u.groups; agl; agl = agl->next)
258 fprintf(stderr, " %s", agl->group->name);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100259#endif
260
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100261 if (!(u->flags & AU_O_INSECURE)) {
Willy Tarreaue5733232019-05-22 19:24:06 +0200262#ifdef USE_LIBCRYPT
Willy Tarreau943e7ec2018-10-29 19:16:27 +0100263#ifdef HA_HAVE_CRYPT_R
264 ep = crypt_r(pass, u->pass, &crypt_data);
265#else
Willy Tarreau34d4b522018-10-29 18:02:54 +0100266 HA_SPIN_LOCK(AUTH_LOCK, &auth_lock);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100267 ep = crypt(pass, u->pass);
Willy Tarreau34d4b522018-10-29 18:02:54 +0100268 HA_SPIN_UNLOCK(AUTH_LOCK, &auth_lock);
Willy Tarreau943e7ec2018-10-29 19:16:27 +0100269#endif
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100270#else
271 return 0;
272#endif
273 } else
274 ep = pass;
275
276#ifdef DEBUG_AUTH
277 fprintf(stderr, ", crypt=%s\n", ep);
278#endif
279
Cyril Bontéc82279c2014-08-29 20:20:01 +0200280 if (ep && strcmp(ep, u->pass) == 0)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100281 return 1;
282 else
283 return 0;
284}
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100285
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100286struct pattern *
287pat_match_auth(struct sample *smp, struct pattern_expr *expr, int fill)
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100288{
Willy Tarreau37406352012-04-23 16:16:37 +0200289 struct userlist *ul = smp->ctx.a[0];
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100290 struct pattern_list *lst;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100291 struct auth_users *u;
292 struct auth_groups_list *agl;
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100293 struct pattern *pattern;
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100294
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100295 /* Check if the userlist is present in the context data. */
296 if (!ul)
Willy Tarreau86e0fc12014-04-29 19:52:16 +0200297 return NULL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100298
299 /* Browse the userlist for searching user. */
300 for (u = ul->users; u; u = u->next) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200301 if (strcmp(smp->data.u.str.area, u->user) == 0)
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100302 break;
303 }
304 if (!u)
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100305 return NULL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100306
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100307 /* Browse each pattern. */
308 list_for_each_entry(lst, &expr->patterns, list) {
309 pattern = &lst->pat;
310
311 /* Browse each group for searching group name that match the pattern. */
312 for (agl = u->u.groups; agl; agl = agl->next) {
313 if (strcmp(agl->group->name, pattern->ptr.str) == 0)
314 return pattern;
315 }
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100316 }
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100317 return NULL;
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100318}
Willy Tarreaue8692b42016-12-21 19:36:25 +0100319
Victor Kislovbf2bc1e2020-08-06 19:21:39 +0300320REGISTER_BUILD_OPTS("Encrypted password support via crypt(3): "CRYPT_STATE_MSG);