blob: faac16d4e2c63cd715a2bdc620e12bcae2367e87 [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 Tarreauac13aea2020-06-04 10:36:03 +020028#include <haproxy/auth-t.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020029#include <haproxy/api.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020030#include <haproxy/global.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020031#include <haproxy/errors.h>
Willy Tarreau225a90a2020-06-04 15:06:28 +020032#include <haproxy/pattern-t.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020033#include <haproxy/thread.h>
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010034
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010035#include <proto/log.h>
36
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
Willy Tarreau943e7ec2018-10-29 19:16:27 +010040#ifdef HA_HAVE_CRYPT_R
41/* context for crypt_r() */
42static THREAD_LOCAL struct crypt_data crypt_data = { .initialized = 0 };
43#else
44/* lock for crypt() */
Willy Tarreauaf613e82020-06-05 08:40:51 +020045__decl_thread(static HA_SPINLOCK_T auth_lock);
Willy Tarreau34d4b522018-10-29 18:02:54 +010046#endif
Willy Tarreau943e7ec2018-10-29 19:16:27 +010047#endif
Willy Tarreau34d4b522018-10-29 18:02:54 +010048
David Carlier6c00eba2019-09-01 14:59:10 +010049/* find targets for selected groups. The function returns pointer to
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010050 * the userlist struct ot NULL if name is NULL/empty or unresolvable.
51 */
52
53struct userlist *
54auth_find_userlist(char *name)
55{
56 struct userlist *l;
57
58 if (!name || !*name)
59 return NULL;
60
61 for (l = userlist; l; l = l->next)
62 if (!strcmp(l->name, name))
63 return l;
64
65 return NULL;
66}
67
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010068int check_group(struct userlist *ul, char *name)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010069{
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010070 struct auth_groups *ag;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010071
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010072 for (ag = ul->groups; ag; ag = ag->next)
73 if (strcmp(name, ag->name) == 0)
74 return 1;
75 return 0;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010076}
77
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010078void
79userlist_free(struct userlist *ul)
80{
81 struct userlist *tul;
82 struct auth_users *au, *tau;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010083 struct auth_groups_list *agl, *tagl;
84 struct auth_groups *ag, *tag;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010085
86 while (ul) {
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010087 /* Free users. */
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010088 au = ul->users;
89 while (au) {
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +010090 /* Free groups that own current user. */
91 agl = au->u.groups;
92 while (agl) {
93 tagl = agl;
94 agl = agl->next;
95 free(tagl);
96 }
97
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +010098 tau = au;
99 au = au->next;
100 free(tau->user);
101 free(tau->pass);
102 free(tau);
103 }
104
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100105 /* Free grouplist. */
106 ag = ul->groups;
107 while (ag) {
108 tag = ag;
109 ag = ag->next;
110 free(tag->name);
111 free(tag);
112 }
113
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100114 tul = ul;
115 ul = ul->next;
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100116 free(tul->name);
117 free(tul);
118 };
119}
120
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100121int userlist_postinit()
122{
123 struct userlist *curuserlist = NULL;
124
125 /* Resolve usernames and groupnames. */
126 for (curuserlist = userlist; curuserlist; curuserlist = curuserlist->next) {
127 struct auth_groups *ag;
128 struct auth_users *curuser;
129 struct auth_groups_list *grl;
130
131 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
132 char *group = NULL;
133 struct auth_groups_list *groups = NULL;
134
135 if (!curuser->u.groups_names)
136 continue;
137
138 while ((group = strtok(group?NULL:curuser->u.groups_names, ","))) {
139 for (ag = curuserlist->groups; ag; ag = ag->next) {
140 if (!strcmp(ag->name, group))
141 break;
142 }
143
144 if (!ag) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100145 ha_alert("userlist '%s': no such group '%s' specified in user '%s'\n",
146 curuserlist->name, group, curuser->user);
Dirkjan Bussink07fcaaa2014-04-28 22:57:16 +0000147 free(groups);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100148 return ERR_ALERT | ERR_FATAL;
149 }
150
151 /* Add this group at the group userlist. */
152 grl = calloc(1, sizeof(*grl));
153 if (!grl) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100154 ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
155 curuserlist->name);
Dirkjan Bussink07fcaaa2014-04-28 22:57:16 +0000156 free(groups);
157 return ERR_ALERT | ERR_FATAL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100158 }
159
160 grl->group = ag;
161 grl->next = groups;
162 groups = grl;
163 }
164
165 free(curuser->u.groups);
166 curuser->u.groups = groups;
167 }
168
169 for (ag = curuserlist->groups; ag; ag = ag->next) {
170 char *user = NULL;
171
172 if (!ag->groupusers)
173 continue;
174
175 while ((user = strtok(user?NULL:ag->groupusers, ","))) {
176 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
177 if (!strcmp(curuser->user, user))
178 break;
179 }
180
181 if (!curuser) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100182 ha_alert("userlist '%s': no such user '%s' specified in group '%s'\n",
183 curuserlist->name, user, ag->name);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100184 return ERR_ALERT | ERR_FATAL;
185 }
186
187 /* Add this group at the group userlist. */
188 grl = calloc(1, sizeof(*grl));
189 if (!grl) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100190 ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n",
191 curuserlist->name);
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100192 return ERR_ALERT | ERR_FATAL;
193 }
194
195 grl->group = ag;
196 grl->next = curuser->u.groups;
197 curuser->u.groups = grl;
198 }
199
200 free(ag->groupusers);
201 ag->groupusers = NULL;
202 }
203
204#ifdef DEBUG_AUTH
205 for (ag = curuserlist->groups; ag; ag = ag->next) {
206 struct auth_groups_list *agl;
207
208 fprintf(stderr, "group %s, id %p, users:", ag->name, ag);
209 for (curuser = curuserlist->users; curuser; curuser = curuser->next) {
210 for (agl = curuser->u.groups; agl; agl = agl->next) {
211 if (agl->group == ag)
212 fprintf(stderr, " %s", curuser->user);
213 }
214 }
215 fprintf(stderr, "\n");
216 }
217#endif
218 }
219
220 return ERR_NONE;
221}
222
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100223/*
224 * Authenticate and authorize user; return 1 if OK, 0 if case of error.
225 */
226int
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100227check_user(struct userlist *ul, const char *user, const char *pass)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100228{
229
230 struct auth_users *u;
CJ Ess6eac32e2015-05-02 16:35:24 -0400231#ifdef DEBUG_AUTH
232 struct auth_groups_list *agl;
233#endif
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100234 const char *ep;
235
236#ifdef DEBUG_AUTH
CJ Ess6eac32e2015-05-02 16:35:24 -0400237 fprintf(stderr, "req: userlist=%s, user=%s, pass=%s\n",
238 ul->name, user, pass);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100239#endif
240
241 for (u = ul->users; u; u = u->next)
242 if (!strcmp(user, u->user))
243 break;
244
245 if (!u)
246 return 0;
247
248#ifdef DEBUG_AUTH
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100249 fprintf(stderr, "cfg: user=%s, pass=%s, flags=%X, groups=",
250 u->user, u->pass, u->flags);
251 for (agl = u->u.groups; agl; agl = agl->next)
252 fprintf(stderr, " %s", agl->group->name);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100253#endif
254
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100255 if (!(u->flags & AU_O_INSECURE)) {
Willy Tarreaue5733232019-05-22 19:24:06 +0200256#ifdef USE_LIBCRYPT
Willy Tarreau943e7ec2018-10-29 19:16:27 +0100257#ifdef HA_HAVE_CRYPT_R
258 ep = crypt_r(pass, u->pass, &crypt_data);
259#else
Willy Tarreau34d4b522018-10-29 18:02:54 +0100260 HA_SPIN_LOCK(AUTH_LOCK, &auth_lock);
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100261 ep = crypt(pass, u->pass);
Willy Tarreau34d4b522018-10-29 18:02:54 +0100262 HA_SPIN_UNLOCK(AUTH_LOCK, &auth_lock);
Willy Tarreau943e7ec2018-10-29 19:16:27 +0100263#endif
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100264#else
265 return 0;
266#endif
267 } else
268 ep = pass;
269
270#ifdef DEBUG_AUTH
271 fprintf(stderr, ", crypt=%s\n", ep);
272#endif
273
Cyril Bontéc82279c2014-08-29 20:20:01 +0200274 if (ep && strcmp(ep, u->pass) == 0)
Krzysztof Piotr Oledzki96105042010-01-29 17:50:44 +0100275 return 1;
276 else
277 return 0;
278}
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100279
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100280struct pattern *
281pat_match_auth(struct sample *smp, struct pattern_expr *expr, int fill)
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100282{
Willy Tarreau37406352012-04-23 16:16:37 +0200283 struct userlist *ul = smp->ctx.a[0];
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100284 struct pattern_list *lst;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100285 struct auth_users *u;
286 struct auth_groups_list *agl;
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100287 struct pattern *pattern;
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100288
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100289 /* Check if the userlist is present in the context data. */
290 if (!ul)
Willy Tarreau86e0fc12014-04-29 19:52:16 +0200291 return NULL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100292
293 /* Browse the userlist for searching user. */
294 for (u = ul->users; u; u = u->next) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200295 if (strcmp(smp->data.u.str.area, u->user) == 0)
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100296 break;
297 }
298 if (!u)
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100299 return NULL;
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100300
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100301 /* Browse each pattern. */
302 list_for_each_entry(lst, &expr->patterns, list) {
303 pattern = &lst->pat;
304
305 /* Browse each group for searching group name that match the pattern. */
306 for (agl = u->u.groups; agl; agl = agl->next) {
307 if (strcmp(agl->group->name, pattern->ptr.str) == 0)
308 return pattern;
309 }
Thierry FOURNIER9eec0a62014-01-22 18:38:02 +0100310 }
Thierry FOURNIER5338eea2013-12-16 14:22:13 +0100311 return NULL;
Krzysztof Piotr Oledzkif9423ae2010-01-29 19:26:18 +0100312}
Willy Tarreaue8692b42016-12-21 19:36:25 +0100313
Willy Tarreau80713382018-11-26 10:19:54 +0100314REGISTER_BUILD_OPTS("Encrypted password support via crypt(3): yes");