Line | Count | Source |
1 | | /* |
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 | | |
13 | | #ifdef USE_LIBCRYPT |
14 | | /* This is to have crypt() defined on Linux */ |
15 | | #define _GNU_SOURCE |
16 | | |
17 | | #ifdef USE_CRYPT_H |
18 | | /* some platforms such as Solaris need this */ |
19 | | #include <crypt.h> |
20 | | #endif |
21 | | #endif /* USE_LIBCRYPT */ |
22 | | |
23 | | #include <stdio.h> |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | #include <unistd.h> |
27 | | |
28 | | #include <haproxy/api.h> |
29 | | #include <haproxy/auth-t.h> |
30 | | #include <haproxy/errors.h> |
31 | | #include <haproxy/global.h> |
32 | | #include <haproxy/list.h> |
33 | | #include <haproxy/pattern-t.h> |
34 | | #include <haproxy/sample-t.h> |
35 | | #include <haproxy/thread.h> |
36 | | |
37 | | struct userlist *userlist = NULL; /* list of all existing userlists */ |
38 | | |
39 | | #ifdef USE_LIBCRYPT |
40 | | #define CRYPT_STATE_MSG "yes" |
41 | | #ifdef HA_HAVE_CRYPT_R |
42 | | /* context for crypt_r() */ |
43 | | static THREAD_LOCAL struct crypt_data crypt_data = { .initialized = 0 }; |
44 | | #else |
45 | | /* lock for crypt() */ |
46 | | __decl_thread(static HA_SPINLOCK_T auth_lock); |
47 | | #endif |
48 | | #else /* USE_LIBCRYPT */ |
49 | | #define CRYPT_STATE_MSG "no" |
50 | | #endif |
51 | | |
52 | | /* find targets for selected groups. The function returns pointer to |
53 | | * the userlist struct or NULL if name is NULL/empty or unresolvable. |
54 | | */ |
55 | | |
56 | | struct userlist * |
57 | | auth_find_userlist(char *name) |
58 | 0 | { |
59 | 0 | struct userlist *l; |
60 | |
|
61 | 0 | if (!name || !*name) |
62 | 0 | return NULL; |
63 | | |
64 | 0 | for (l = userlist; l; l = l->next) |
65 | 0 | if (strcmp(l->name, name) == 0) |
66 | 0 | return l; |
67 | | |
68 | 0 | return NULL; |
69 | 0 | } |
70 | | |
71 | | int check_group(struct userlist *ul, char *name) |
72 | 0 | { |
73 | 0 | struct auth_groups *ag; |
74 | |
|
75 | 0 | for (ag = ul->groups; ag; ag = ag->next) |
76 | 0 | if (strcmp(name, ag->name) == 0) |
77 | 0 | return 1; |
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | | void |
82 | | userlist_free(struct userlist *ul) |
83 | 0 | { |
84 | 0 | struct userlist *tul; |
85 | 0 | struct auth_users *au, *tau; |
86 | 0 | struct auth_groups_list *agl, *tagl; |
87 | 0 | struct auth_groups *ag, *tag; |
88 | |
|
89 | 0 | while (ul) { |
90 | | /* Free users. */ |
91 | 0 | au = ul->users; |
92 | 0 | while (au) { |
93 | | /* Free groups that own current user. */ |
94 | 0 | agl = au->u.groups; |
95 | 0 | while (agl) { |
96 | 0 | tagl = agl; |
97 | 0 | agl = agl->next; |
98 | 0 | free(tagl); |
99 | 0 | } |
100 | |
|
101 | 0 | tau = au; |
102 | 0 | au = au->next; |
103 | 0 | free(tau->user); |
104 | 0 | free(tau->pass); |
105 | 0 | free(tau); |
106 | 0 | } |
107 | | |
108 | | /* Free grouplist. */ |
109 | 0 | ag = ul->groups; |
110 | 0 | while (ag) { |
111 | 0 | tag = ag; |
112 | 0 | ag = ag->next; |
113 | 0 | free(tag->name); |
114 | 0 | free(tag); |
115 | 0 | } |
116 | |
|
117 | 0 | tul = ul; |
118 | 0 | ul = ul->next; |
119 | 0 | free(tul->name); |
120 | 0 | free(tul); |
121 | 0 | }; |
122 | 0 | } |
123 | | |
124 | | int userlist_postinit() |
125 | 0 | { |
126 | 0 | struct userlist *curuserlist = NULL; |
127 | | |
128 | | /* Resolve usernames and groupnames. */ |
129 | 0 | for (curuserlist = userlist; curuserlist; curuserlist = curuserlist->next) { |
130 | 0 | struct auth_groups *ag; |
131 | 0 | struct auth_users *curuser; |
132 | 0 | struct auth_groups_list *grl, *tmp; |
133 | |
|
134 | 0 | for (curuser = curuserlist->users; curuser; curuser = curuser->next) { |
135 | 0 | char *group = NULL; |
136 | 0 | struct auth_groups_list *groups = NULL; |
137 | |
|
138 | 0 | if (!curuser->u.groups_names) |
139 | 0 | continue; |
140 | | |
141 | 0 | while ((group = strtok(group?NULL:curuser->u.groups_names, ","))) { |
142 | 0 | for (ag = curuserlist->groups; ag; ag = ag->next) { |
143 | 0 | if (strcmp(ag->name, group) == 0) |
144 | 0 | break; |
145 | 0 | } |
146 | |
|
147 | 0 | if (!ag) { |
148 | 0 | ha_alert("userlist '%s': no such group '%s' specified in user '%s'\n", |
149 | 0 | curuserlist->name, group, curuser->user); |
150 | 0 | while (groups) { |
151 | 0 | grl = groups; |
152 | 0 | groups = groups->next; |
153 | 0 | free(grl); |
154 | 0 | } |
155 | 0 | goto free_groups; |
156 | 0 | } |
157 | | |
158 | | /* Add this group at the group userlist. */ |
159 | 0 | grl = calloc(1, sizeof(*grl)); |
160 | 0 | if (!grl) { |
161 | 0 | ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n", |
162 | 0 | curuserlist->name); |
163 | 0 | while (groups) { |
164 | 0 | grl = groups; |
165 | 0 | groups = groups->next; |
166 | 0 | free(grl); |
167 | 0 | } |
168 | 0 | goto free_groups; |
169 | 0 | } |
170 | | |
171 | 0 | grl->group = ag; |
172 | 0 | grl->next = groups; |
173 | 0 | groups = grl; |
174 | 0 | } |
175 | | |
176 | 0 | free(curuser->u.groups); |
177 | 0 | curuser->u.groups = groups; |
178 | 0 | } |
179 | | |
180 | 0 | for (ag = curuserlist->groups; ag; ag = ag->next) { |
181 | 0 | char *user = NULL; |
182 | |
|
183 | 0 | if (!ag->groupusers) |
184 | 0 | continue; |
185 | | |
186 | 0 | while ((user = strtok(user?NULL:ag->groupusers, ","))) { |
187 | 0 | for (curuser = curuserlist->users; curuser; curuser = curuser->next) { |
188 | 0 | if (strcmp(curuser->user, user) == 0) |
189 | 0 | break; |
190 | 0 | } |
191 | |
|
192 | 0 | if (!curuser) { |
193 | 0 | ha_alert("userlist '%s': no such user '%s' specified in group '%s'\n", |
194 | 0 | curuserlist->name, user, ag->name); |
195 | 0 | goto free_groups; |
196 | 0 | } |
197 | | |
198 | | /* Add this group at the group userlist. */ |
199 | 0 | grl = calloc(1, sizeof(*grl)); |
200 | 0 | if (!grl) { |
201 | 0 | ha_alert("userlist '%s': no more memory when trying to allocate the user groups.\n", |
202 | 0 | curuserlist->name); |
203 | 0 | goto free_groups; |
204 | 0 | } |
205 | | |
206 | 0 | grl->group = ag; |
207 | 0 | grl->next = curuser->u.groups; |
208 | 0 | curuser->u.groups = grl; |
209 | 0 | } |
210 | | |
211 | 0 | ha_free(&ag->groupusers); |
212 | 0 | } |
213 | | |
214 | 0 | goto next_userlist; |
215 | | |
216 | 0 | free_groups: |
217 | | /* Free already-assigned groups for all users in this userlist. */ |
218 | 0 | for (curuser = curuserlist->users; curuser; curuser = curuser->next) { |
219 | 0 | grl = curuser->u.groups; |
220 | 0 | while (grl) { |
221 | 0 | tmp = grl; |
222 | 0 | grl = grl->next; |
223 | 0 | free(tmp); |
224 | 0 | } |
225 | 0 | curuser->u.groups = NULL; |
226 | 0 | } |
227 | 0 | return ERR_ALERT | ERR_FATAL; |
228 | | |
229 | 0 | next_userlist:; |
230 | | #ifdef DEBUG_AUTH |
231 | | for (ag = curuserlist->groups; ag; ag = ag->next) { |
232 | | struct auth_groups_list *agl; |
233 | | |
234 | | fprintf(stderr, "group %s, id %p, users:", ag->name, ag); |
235 | | for (curuser = curuserlist->users; curuser; curuser = curuser->next) { |
236 | | for (agl = curuser->u.groups; agl; agl = agl->next) { |
237 | | if (agl->group == ag) |
238 | | fprintf(stderr, " %s", curuser->user); |
239 | | } |
240 | | } |
241 | | fprintf(stderr, "\n"); |
242 | | } |
243 | | #endif |
244 | 0 | } |
245 | | |
246 | 0 | return ERR_NONE; |
247 | 0 | } |
248 | | |
249 | | /* |
250 | | * Authenticate and authorize user; return 1 if OK, 0 if case of error. |
251 | | */ |
252 | | int |
253 | | check_user(struct userlist *ul, const char *user, const char *pass) |
254 | 0 | { |
255 | |
|
256 | 0 | struct auth_users *u; |
257 | | #ifdef DEBUG_AUTH |
258 | | struct auth_groups_list *agl; |
259 | | #endif |
260 | 0 | const char *ep; |
261 | |
|
262 | | #ifdef DEBUG_AUTH |
263 | | fprintf(stderr, "req: userlist=%s, user=%s, pass=%s\n", |
264 | | ul->name, user, pass); |
265 | | #endif |
266 | |
|
267 | 0 | for (u = ul->users; u; u = u->next) |
268 | 0 | if (strcmp(user, u->user) == 0) |
269 | 0 | break; |
270 | |
|
271 | 0 | if (!u) |
272 | 0 | return 0; |
273 | | |
274 | | #ifdef DEBUG_AUTH |
275 | | fprintf(stderr, "cfg: user=%s, pass=%s, flags=%X, groups=", |
276 | | u->user, u->pass, u->flags); |
277 | | for (agl = u->u.groups; agl; agl = agl->next) |
278 | | fprintf(stderr, " %s", agl->group->name); |
279 | | #endif |
280 | | |
281 | 0 | if (!(u->flags & AU_O_INSECURE)) { |
282 | | #ifdef USE_LIBCRYPT |
283 | | #ifdef HA_HAVE_CRYPT_R |
284 | | ep = crypt_r(pass, u->pass, &crypt_data); |
285 | | #else |
286 | | HA_SPIN_LOCK(AUTH_LOCK, &auth_lock); |
287 | | ep = crypt(pass, u->pass); |
288 | | HA_SPIN_UNLOCK(AUTH_LOCK, &auth_lock); |
289 | | #endif |
290 | | #else |
291 | 0 | return 0; |
292 | 0 | #endif |
293 | 0 | } else |
294 | 0 | ep = pass; |
295 | | |
296 | | #ifdef DEBUG_AUTH |
297 | | fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : "")); |
298 | | #endif |
299 | | |
300 | 0 | if (ep && u->pass && strcmp(ep, u->pass) == 0) |
301 | 0 | return 1; |
302 | 0 | else |
303 | 0 | return 0; |
304 | 0 | } |
305 | | |
306 | | struct pattern * |
307 | | pat_match_auth(struct sample *smp, struct pattern_expr *expr, int fill) |
308 | 0 | { |
309 | 0 | struct userlist *ul = smp->ctx.a[0]; |
310 | 0 | struct pattern_list *lst; |
311 | 0 | struct auth_users *u; |
312 | 0 | struct auth_groups_list *agl; |
313 | 0 | struct pattern *pattern; |
314 | | |
315 | | /* Check if the userlist is present in the context data. */ |
316 | 0 | if (!ul) |
317 | 0 | return NULL; |
318 | | |
319 | | /* Browse the userlist for searching user. */ |
320 | 0 | for (u = ul->users; u; u = u->next) { |
321 | 0 | if (strcmp(smp->data.u.str.area, u->user) == 0) |
322 | 0 | break; |
323 | 0 | } |
324 | 0 | if (!u) |
325 | 0 | return NULL; |
326 | | |
327 | | /* Browse each pattern. */ |
328 | 0 | list_for_each_entry(lst, &expr->patterns, list) { |
329 | 0 | pattern = &lst->pat; |
330 | | |
331 | | /* Browse each group for searching group name that match the pattern. */ |
332 | 0 | for (agl = u->u.groups; agl; agl = agl->next) { |
333 | 0 | if (strcmp(agl->group->name, pattern->ptr.str) == 0) |
334 | 0 | return pattern; |
335 | 0 | } |
336 | 0 | } |
337 | 0 | return NULL; |
338 | 0 | } |
339 | | |
340 | | REGISTER_BUILD_OPTS("Encrypted password support via crypt(3): "CRYPT_STATE_MSG); |