blob: 0031300bc58dd42c6b01246a72855e8b3e3111da [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 Tarreau4c7e4b72020-05-27 12:58:42 +020028#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020029#include <haproxy/auth-t.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020030#include <haproxy/errors.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020031#include <haproxy/global.h>
Willy Tarreaue8ceea12021-05-08 12:29:51 +020032#include <haproxy/list.h>
Willy Tarreau225a90a2020-06-04 15:06:28 +020033#include <haproxy/pattern-t.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020034#include <haproxy/sample-t.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020035#include <haproxy/thread.h>
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010036
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010037struct userlist *userlist = NULL; /* list of all existing userlists */
38
Willy Tarreaue5733232019-05-22 19:24:06 +020039#ifdef USE_LIBCRYPT
Victor Kislovec002512020-08-06 19:21:39 +030040#define CRYPT_STATE_MSG "yes"
Willy Tarreau943e7ec2018-10-29 19:16:27 +010041#ifdef HA_HAVE_CRYPT_R
42/* context for crypt_r() */
43static THREAD_LOCAL struct crypt_data crypt_data = { .initialized = 0 };
44#else
45/* lock for crypt() */
Willy Tarreauaf613e82020-06-05 08:40:51 +020046__decl_thread(static HA_SPINLOCK_T auth_lock);
Willy Tarreau34d4b522018-10-29 18:02:54 +010047#endif
Victor Kislovec002512020-08-06 19:21:39 +030048#else /* USE_LIBCRYPT */
49#define CRYPT_STATE_MSG "no"
Willy Tarreau943e7ec2018-10-29 19:16:27 +010050#endif
Willy Tarreau34d4b522018-10-29 18:02:54 +010051
David Carlier6c00eba2019-09-01 14:59:10 +010052/* find targets for selected groups. The function returns pointer to
Ilya Shipitsinb8888ab2021-01-06 21:20:16 +050053 * the userlist struct or NULL if name is NULL/empty or unresolvable.
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010054 */
55
56struct userlist *
57auth_find_userlist(char *name)
58{
59 struct userlist *l;
60
61 if (!name || !*name)
62 return NULL;
63
64 for (l = userlist; l; l = l->next)
Tim Duesterhuse5ff1412021-01-02 22:31:53 +010065 if (strcmp(l->name, name) == 0)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010066 return l;
67
68 return NULL;
69}
70
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010071int check_group(struct userlist *ul, char *name)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010072{
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010073 struct auth_groups *ag;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010074
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010075 for (ag = ul->groups; ag; ag = ag->next)
76 if (strcmp(name, ag->name) == 0)
77 return 1;
78 return 0;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010079}
80
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010081void
82userlist_free(struct userlist *ul)
83{
84 struct userlist *tul;
85 struct auth_users *au, *tau;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010086 struct auth_groups_list *agl, *tagl;
87 struct auth_groups *ag, *tag;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010088
89 while (ul) {
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010090 /* Free users. */
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010091 au = ul->users;
92 while (au) {
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010093 /* Free groups that own current user. */
94 agl = au->u.groups;
95 while (agl) {
96 tagl = agl;
97 agl = agl->next;
98 free(tagl);
99 }
100
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100101 tau = au;
102 au = au->next;
103 free(tau->user);
104 free(tau->pass);
105 free(tau);
106 }
107
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100108 /* Free grouplist. */
109 ag = ul->groups;
110 while (ag) {
111 tag = ag;
112 ag = ag->next;
113 free(tag->name);
114 free(tag);
115 }
116
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100117 tul = ul;
118 ul = ul->next;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100119 free(tul->name);
120 free(tul);
121 };
122}
123
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100124int userlist_postinit()
125{
126 struct userlist *curuserlist = NULL;
127
128 /* Resolve usernames and groupnames. */
129 for (curuserlist = userlist; curuserlist; curuserlist = curuserlist->next) {
130 struct auth_groups *ag;
131 struct auth_users *curuser;
132 struct auth_groups_list *grl;
133
134 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
135 char *group = NULL;
136 struct auth_groups_list *groups = NULL;
137
138 if (!curuser->u.groups_names)
139 continue;
140
141 while ((group = strtok(group?NULL:curuser->u.groups_names, ","))) {
142 for (ag = curuserlist->groups; ag; ag = ag->next) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100143 if (strcmp(ag->name, group) == 0)
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100144 break;
145 }
146
147 if (!ag) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100148 ha_alert("userlist '%s': no such group '%s' specified in user '%s'\n",
149 curuserlist->name, group, curuser->user);
Dirkjan Bussink07fcaaa2014-04-28 22:57:16 +0000150 free(groups);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100151 return ERR_ALERT | ERR_FATAL;
152 }
153
154 /* Add this group at the group userlist. */
155 grl = calloc(1, sizeof(*grl));
156 if (!grl) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100157 ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
158 curuserlist->name);
Dirkjan Bussink07fcaaa2014-04-28 22:57:16 +0000159 free(groups);
160 return ERR_ALERT | ERR_FATAL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100161 }
162
163 grl->group = ag;
164 grl->next = groups;
165 groups = grl;
166 }
167
168 free(curuser->u.groups);
169 curuser->u.groups = groups;
170 }
171
172 for (ag = curuserlist->groups; ag; ag = ag->next) {
173 char *user = NULL;
174
175 if (!ag->groupusers)
176 continue;
177
178 while ((user = strtok(user?NULL:ag->groupusers, ","))) {
179 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100180 if (strcmp(curuser->user, user) == 0)
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100181 break;
182 }
183
184 if (!curuser) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100185 ha_alert("userlist '%s': no such user '%s' specified in group '%s'\n",
186 curuserlist->name, user, ag->name);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100187 return ERR_ALERT | ERR_FATAL;
188 }
189
190 /* Add this group at the group userlist. */
191 grl = calloc(1, sizeof(*grl));
192 if (!grl) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100193 ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
194 curuserlist->name);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100195 return ERR_ALERT | ERR_FATAL;
196 }
197
198 grl->group = ag;
199 grl->next = curuser->u.groups;
200 curuser->u.groups = grl;
201 }
202
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100203 ha_free(&ag->groupusers);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100204 }
205
206#ifdef DEBUG_AUTH
207 for (ag = curuserlist->groups; ag; ag = ag->next) {
208 struct auth_groups_list *agl;
209
210 fprintf(stderr, "group %s, id %p, users:", ag->name, ag);
211 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
212 for (agl = curuser->u.groups; agl; agl = agl->next) {
213 if (agl->group == ag)
214 fprintf(stderr, " %s", curuser->user);
215 }
216 }
217 fprintf(stderr, "\n");
218 }
219#endif
220 }
221
222 return ERR_NONE;
223}
224
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100225/*
226 * Authenticate and authorize user; return 1 if OK, 0 if case of error.
227 */
228int
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100229check_user(struct userlist *ul, const char *user, const char *pass)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100230{
231
232 struct auth_users *u;
CJ Ess6eac32e2015-05-02 16:35:24 -0400233#ifdef DEBUG_AUTH
234 struct auth_groups_list *agl;
235#endif
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100236 const char *ep;
237
238#ifdef DEBUG_AUTH
CJ Ess6eac32e2015-05-02 16:35:24 -0400239 fprintf(stderr, "req: userlist=%s, user=%s, pass=%s\n",
240 ul->name, user, pass);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100241#endif
242
243 for (u = ul->users; u; u = u->next)
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100244 if (strcmp(user, u->user) == 0)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100245 break;
246
247 if (!u)
248 return 0;
249
250#ifdef DEBUG_AUTH
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100251 fprintf(stderr, "cfg: user=%s, pass=%s, flags=%X, groups=",
252 u->user, u->pass, u->flags);
253 for (agl = u->u.groups; agl; agl = agl->next)
254 fprintf(stderr, " %s", agl->group->name);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100255#endif
256
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100257 if (!(u->flags & AU_O_INSECURE)) {
Willy Tarreaue5733232019-05-22 19:24:06 +0200258#ifdef USE_LIBCRYPT
Willy Tarreau943e7ec2018-10-29 19:16:27 +0100259#ifdef HA_HAVE_CRYPT_R
260 ep = crypt_r(pass, u->pass, &crypt_data);
261#else
Willy Tarreau34d4b522018-10-29 18:02:54 +0100262 HA_SPIN_LOCK(AUTH_LOCK, &auth_lock);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100263 ep = crypt(pass, u->pass);
Willy Tarreau34d4b522018-10-29 18:02:54 +0100264 HA_SPIN_UNLOCK(AUTH_LOCK, &auth_lock);
Willy Tarreau943e7ec2018-10-29 19:16:27 +0100265#endif
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100266#else
267 return 0;
268#endif
269 } else
270 ep = pass;
271
272#ifdef DEBUG_AUTH
Aurelien DARRAGONa7dc2512022-11-24 08:37:13 +0100273 fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100274#endif
275
Cyril Bontéc82279c2014-08-29 20:20:01 +0200276 if (ep && strcmp(ep, u->pass) == 0)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100277 return 1;
278 else
279 return 0;
280}
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100281
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100282struct pattern *
283pat_match_auth(struct sample *smp, struct pattern_expr *expr, int fill)
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100284{
Willy Tarreau37406352012-04-23 16:16:37 +0200285 struct userlist *ul = smp->ctx.a[0];
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100286 struct pattern_list *lst;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100287 struct auth_users *u;
288 struct auth_groups_list *agl;
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100289 struct pattern *pattern;
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100290
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100291 /* Check if the userlist is present in the context data. */
292 if (!ul)
Willy Tarreau86e0fc12014-04-29 19:52:16 +0200293 return NULL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100294
295 /* Browse the userlist for searching user. */
296 for (u = ul->users; u; u = u->next) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200297 if (strcmp(smp->data.u.str.area, u->user) == 0)
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100298 break;
299 }
300 if (!u)
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100301 return NULL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100302
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100303 /* Browse each pattern. */
304 list_for_each_entry(lst, &expr->patterns, list) {
305 pattern = &lst->pat;
306
307 /* Browse each group for searching group name that match the pattern. */
308 for (agl = u->u.groups; agl; agl = agl->next) {
309 if (strcmp(agl->group->name, pattern->ptr.str) == 0)
310 return pattern;
311 }
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100312 }
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100313 return NULL;
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100314}
Willy Tarreaue8692b42016-12-21 19:36:25 +0100315
Victor Kislovec002512020-08-06 19:21:39 +0300316REGISTER_BUILD_OPTS("Encrypted password support via crypt(3): "CRYPT_STATE_MSG);