1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #ifdef __gnu_linux__
30 #define _GNU_SOURCE
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/time.h>
37
38 #include <cx/utils.h>
39 #include <cx/hash_map.h>
40 #include <cx/printf.h>
41
42 #include "../util/util.h"
43
44 #include "ldap_auth.h"
45 #include "ldap_resource.h"
46
47 static cxstring ws_ldap_default_uid_attr[] = {
48 CX_STR(
"uid")
49 };
50
51 static cxstring ws_ldap_default_member_attr[] = {
52 CX_STR(
"member"),
53 CX_STR(
"uniqueMember")
54 };
55
56 static LDAPConfig ws_ldap_default_config = {
57 NULL,
58 NULL,
59 NULL,
60 NULL,
61 "(&(objectclass=inetorgperson)(|(cn=%s)(uid=%s)))",
62 ws_ldap_default_uid_attr,
63 1,
64 "(&(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames))(cn=%s))",
65 ws_ldap_default_member_attr,
66 2,
67 WS_LDAP_GROUP_MEMBER_DN,
68 TRUE,
69 FALSE
70 };
71
72
73 static cxstring ws_ad_default_uid_attr[] = {
74 CX_STR(
"uid")
75 };
76
77 static cxstring ws_ad_default_member_attr[] = {
78 CX_STR(
"member"),
79 CX_STR(
"uniqueMember")
80 };
81
82 static LDAPConfig ws_ldap_ad_config = {
83 NULL,
84 NULL,
85 NULL,
86 NULL,
87 "(&(objectclass=inetorgperson)(|(cn=%s)(uid=%s)))",
88 ws_ad_default_uid_attr,
89 1,
90 "",
91 ws_ad_default_member_attr,
92 2,
93 WS_LDAP_GROUP_MEMBER_DN,
94 TRUE,
95 FALSE
96 };
97
98 static cxstring ws_posix_default_uid_attr[] = {
99 CX_STR(
"uid")
100 };
101
102 static cxstring ws_posix_default_member_attr[] = {
103 CX_STR(
"memberUid")
104 };
105
106 static LDAPConfig ws_ldap_posix_config = {
107 NULL,
108 NULL,
109 NULL,
110 NULL,
111 "(&(objectclass=posixAccount)(uid=%s))",
112 ws_posix_default_uid_attr,
113 1,
114 "(&(objectclass=posixGroup)(cn=%s))",
115 ws_posix_default_member_attr,
116 1,
117 WS_LDAP_GROUP_MEMBER_UID,
118 TRUE,
119 FALSE
120 };
121
122 AuthDB* create_ldap_authdb(ServerConfiguration *cfg,
const char *name, ConfigNode *node) {
123 LDAPAuthDB *authdb = cxMalloc(cfg->a,
sizeof(LDAPAuthDB));
124 if(!authdb) {
125 return NULL;
126 }
127 authdb->authdb.name = pool_strdup(cfg->pool, name);
128 if(!authdb->authdb.name) {
129 return NULL;
130 }
131 authdb->authdb.get_user = ldap_get_user;
132 authdb->authdb.use_cache =
0;
133
134
135 cxstring dirtype = serverconfig_object_directive_value(node, cx_str(
"DirectoryType"));
136 LDAPConfig *default_config;
137 if(!dirtype.ptr) {
138 default_config = &ws_ldap_default_config;
139 }
else if(!cx_strcmp(dirtype, cx_str(
"ldap"))) {
140 default_config = &ws_ldap_default_config;
141 }
else if(!cx_strcmp(dirtype, cx_str(
"posix"))) {
142 default_config = &ws_ldap_posix_config;
143 }
else if(!cx_strcmp(dirtype, cx_str(
"ad"))) {
144 default_config = &ws_ldap_ad_config;
145 }
else {
146 log_ereport(
LOG_FAILURE,
"cannot create ldap authdb %s: unknown directory type %s", name, dirtype.ptr);
147 }
148 memcpy(&authdb->config, default_config,
sizeof(LDAPConfig));
149
150
151 cxstring resource = serverconfig_object_directive_value(node, cx_str(
"Resource"));
152 cxstring basedn = serverconfig_object_directive_value(node, cx_str(
"Basedn"));
153 cxstring binddn = serverconfig_object_directive_value(node, cx_str(
"Binddn"));
154 cxstring bindpw = serverconfig_object_directive_value(node, cx_str(
"Bindpw"));
155 cxstring userSearchFilter = serverconfig_object_directive_value(node, cx_str(
"UserSearchFilter"));
156 cxstring uidAttributes = serverconfig_object_directive_value(node, cx_str(
"UidAttributes"));
157 cxstring groupSearchFilter = serverconfig_object_directive_value(node, cx_str(
"GroupSearchFilter"));
158 cxstring memberAttributes = serverconfig_object_directive_value(node, cx_str(
"MemberAttributes"));
159 cxstring memberType = serverconfig_object_directive_value(node, cx_str(
"MemberType"));
160 cxstring enableGroups = serverconfig_object_directive_value(node, cx_str(
"EnableGroups"));
161 cxstring userNameIsDn = serverconfig_object_directive_value(node, cx_str(
"UserNameIsDn"));
162
163 if(!resource.ptr) {
164
165 cxmutstr respool_name = cx_asprintf_a(cfg->a,
"_authdb_%s", name);
166 if(!respool_name.ptr) {
167 return NULL;
168 }
169 log_ereport(
170 LOG_INFORM,
171 "ldap authdb %s: no resource specified: create resource pool %s",
172 name,
173 respool_name.ptr);
174 if(resourcepool_new(cfg, cx_str(
"ldap"), cx_strcast(respool_name), node)) {
175 log_ereport(
176 LOG_FAILURE,
177 "ldap authdb %s: cannot create ldap resource pool",
178 name);
179 return NULL;
180 }
181 authdb->config.resource = respool_name.ptr;
182 }
else {
183 authdb->config.resource = cx_strdup_a(cfg->a, resource).ptr;
184 if(!authdb->config.resource)
return NULL;
185 }
186
187 if(!basedn.ptr) {
188 log_ereport(
LOG_FAILURE,
"ldap authdb %s: basedn is required", name);
189 return NULL;
190 }
191 authdb->config.basedn = cx_strdup_a(cfg->a, basedn).ptr;
192 if(!authdb->config.basedn)
return NULL;
193
194
195 if(binddn.ptr) {
196 if(!bindpw.ptr) {
197 log_ereport(
LOG_FAILURE,
"ldap authdb %s: binddn specified, but no bindpw", name);
198 return NULL;
199 }
200
201 authdb->config.binddn = cx_strdup_a(cfg->a, binddn).ptr;
202 authdb->config.bindpw = cx_strdup_a(cfg->a, bindpw).ptr;
203
204 if(!authdb->config.binddn || !authdb->config.bindpw) {
205 return NULL;
206 }
207 }
208
209
210 if(userSearchFilter.ptr) {
211 authdb->config.userSearchFilter = cx_strdup_a(cfg->a, userSearchFilter).ptr;
212 }
213 if(uidAttributes.ptr) {
214 cxmutstr uidAttributesCopy = cx_strdup_a(cfg->a, uidAttributes);
215 if(uidAttributesCopy.ptr) {
216 authdb->config.numUidAttributes = cx_strsplit_a(
217 cfg->a,
218 cx_strcast(uidAttributesCopy),
219 cx_str(
","),
220 1024,
221 &authdb->config.uidAttributes);
222 }
223 }
224 if(groupSearchFilter.ptr) {
225 authdb->config.groupSearchFilter = groupSearchFilter.ptr;
226 }
227 if(memberAttributes.ptr) {
228 cxmutstr memberAttributesCopy = cx_strdup_a(cfg->a, memberAttributes);
229 if(memberAttributesCopy.ptr) {
230 authdb->config.numMemberAttributes = cx_strsplit_a(
231 cfg->a,
232 cx_strcast(memberAttributesCopy),
233 cx_str(
","),
234 1024,
235 &authdb->config.memberAttributes);
236 }
237 }
238 if(memberType.ptr) {
239 if(!cx_strcmp(memberType, cx_str(
"dn"))) {
240 authdb->config.groupMemberType =
WS_LDAP_GROUP_MEMBER_DN;
241 }
else if(!cx_strcmp(memberType, cx_str(
"uid"))) {
242 authdb->config.groupMemberType =
WS_LDAP_GROUP_MEMBER_UID;
243 }
else {
244 log_ereport(
LOG_FAILURE,
"ldap authdb %s: unknown MemberType %s", name, memberType.ptr);
245 return NULL;
246 }
247 }
248 if(enableGroups.ptr) {
249 authdb->config.enableGroups = util_getboolean_s(enableGroups,
FALSE);
250 }
251 if(userNameIsDn.ptr) {
252 authdb->config.userNameIsDN = util_getboolean_s(userNameIsDn,
FALSE);
253 }
254
255
256
257 authdb->groups.first =
NULL;
258 authdb->groups.last =
NULL;
259 authdb->groups.map = cxHashMapCreate(cfg->a,
CX_STORE_POINTERS,
32);
260 if(!authdb->groups.map) {
261 return NULL;
262 }
263
264 log_ereport(
LOG_INFORM,
"create authdb name=%s type=ldap resource=%s", name, resource.ptr);
265
266 return (AuthDB*) authdb;
267 }
268
269 LDAP* get_ldap_session(Session *sn, Request *rq, LDAPAuthDB *authdb) {
270 ResourceData *res = resourcepool_lookup(sn, rq, authdb->config.resource,
0);
271 if(!res) {
272 log_ereport(
LOG_FAILURE,
"AuthDB %s: cannot get resource %s", authdb->authdb.name, authdb->config.resource);
273 return NULL;
274 }
275
276 LDAP *ldap = res->data;
277
278 if(authdb->config.binddn) {
279 struct berval *server_cred;
280 int r = ws_ldap_bind(ldap, authdb->config.binddn, authdb->config.bindpw, &server_cred);
281 if(r !=
LDAP_SUCCESS) {
282 log_ereport(
LOG_FAILURE,
"AuthDB %s: bind to %s failed: %s", authdb->config.binddn, ldap_err2string(r));
283 resourcepool_free(sn, rq, res);
284 return NULL;
285 }
286 }
287
288 return ldap;
289 }
290
291 static LDAPUser* ldap_msg_to_user(
292 Session *sn,
293 Request *rq,
294 LDAPAuthDB *authdb,
295 LDAP *ldap,
296 LDAPMessage *msg)
297 {
298 CxAllocator *a = pool_allocator(sn->pool);
299
300 LDAPUser *user = pool_malloc(sn->pool,
sizeof(LDAPUser));
301 if(!user) {
302 return NULL;
303 }
304
305
306 char *ldap_dn = ldap_get_dn(ldap, msg);
307 if(!ldap_dn) {
308 return NULL;
309 }
310 char *dn = pool_strdup(sn->pool, ldap_dn);
311 ldap_memfree(ldap_dn);
312 if(!dn) {
313 return NULL;
314 }
315
316
317 char *uid =
NULL;
318
319
320 size_t numUidAttributes = authdb->config.numUidAttributes;
321 cxmutstr *uid_values = pool_calloc(sn->pool, authdb->config.numUidAttributes,
sizeof(cxmutstr));
322 if(!uid_values) {
323 return NULL;
324 }
325
326
327 BerElement *ber =
NULL;
328 char *attribute = ldap_first_attribute(ldap, msg, &ber);
329 while(attribute) {
330 cxstring attr = cx_str(attribute);
331 for(
int i=
0;i<numUidAttributes;i++) {
332
333 if(!uid_values[i].ptr && !cx_strcmp(attr, authdb->config.uidAttributes[i])) {
334
335 struct berval **values = ldap_get_values_len(ldap, msg, attribute);
336 if(values) {
337 int count = ldap_count_values_len(values);
338 if(count >
0) {
339 cxstring attr_val = cx_strn(values[
0]->bv_val, values[
0]->bv_len);
340 uid_values[i] = cx_strdup_a(a, attr_val);
341 }
else {
342 log_ereport(
LOG_FAILURE,
"ldap user: dn: %s attribute %s: no values", dn, attribute);
343 }
344 ldap_value_free_len(values);
345 }
346 }
347 }
348
349 if(uid_values[
0].ptr) {
350
351 break;
352 }
353
354 ldap_memfree(attribute);
355 attribute = ldap_next_attribute(ldap, msg, ber);
356 }
357 if(ber) {
358 ber_free(ber,
0);
359 }
360
361
362
363
364 for(
int i=
0;i<numUidAttributes;i++) {
365 if(uid_values[i].ptr) {
366 if(!uid) {
367 uid = uid_values[i].ptr;
368 }
else {
369 cxFree(a, uid_values[i].ptr);
370 }
371 }
372 }
373 pool_free(sn->pool, uid_values);
374
375
376 char *username;
377 if(authdb->config.userNameIsDN) {
378 username = dn;
379 }
else {
380 username = uid;
381 }
382
383 if(!username) {
384 return NULL;
385 }
386
387 user->authdb = authdb;
388 user->user.verify_password = ldap_user_verify_password;
389 user->user.check_group = ldap_user_check_group;
390 user->user.free = ldap_user_free;
391 user->user.name = username;
392 user->sn = sn;
393 user->rq = rq;
394
395
396 user->user.uid = -
1;
397 user->user.gid = -
1;
398
399 user->ldap = ldap;
400 user->userdn = dn;
401 user->uid_attr = uid;
402
403 return user;
404 }
405
406 User* ldap_get_user(AuthDB *db, Session *sn, Request *rq,
const char *username) {
407 LDAPAuthDB *authdb = (LDAPAuthDB*) db;
408 LDAPConfig *config = &authdb->config;
409 CxAllocator *a = pool_allocator(sn->pool);
410
411 LDAP *ld = get_ldap_session(sn, rq, authdb);
412 if (ld ==
NULL) {
413 return NULL;
414 }
415
416
417 cxstring userSearch = cx_str(config->userSearchFilter);
418 cxmutstr filter = cx_strreplace_a(a, userSearch, cx_str(
"%s"), cx_str(username));
419 if(!filter.ptr) {
420 return NULL;
421 }
422
423 log_ereport(
LOG_DEBUG,
"ldap_get_user: filter: %s", filter.ptr);
424
425 LDAPMessage *result;
426 struct timeval timeout;
427 timeout.tv_sec =
8;
428 timeout.tv_usec =
0;
429 int r = ldap_search_ext_s(
430 ld,
431 config->basedn,
432 LDAP_SCOPE_SUBTREE,
433 filter.ptr,
434 NULL,
435 0,
436 NULL,
437 NULL,
438 &timeout,
439 2,
440 &result);
441 cxFree(a, filter.ptr);
442 if(r !=
LDAP_SUCCESS) {
443 if(result) {
444 ldap_msgfree(result);
445 }
446 log_ereport(
LOG_FAILURE,
"ldap_get_user: search failed: %s", ldap_err2string(r));
447 return NULL;
448 }
449 if(!result) {
450
451 log_ereport(
LOG_FAILURE,
"ldap_get_user: search failed: no result");
452 return NULL;
453 }
454
455 LDAPMessage *msg = ldap_first_entry(ld, result);
456 LDAPUser *user =
NULL;
457 if(msg) {
458 if(ldap_count_entries(ld, msg) >
1) {
459 log_ereport(
LOG_FAILURE,
"ldap_get_user: more than one search result");
460 }
else {
461 user = ldap_msg_to_user(sn, rq, authdb, ld, msg);
462 }
463 }
464 ldap_msgfree(result);
465
466 return (User*)user;
467 }
468
469
470 static int is_member_attribute(LDAPAuthDB *auth,
const char *attribute) {
471 LDAPConfig *config = &auth->config;
472 cxstring attr = cx_str(attribute);
473 for(
int i=
0;i<config->numMemberAttributes;i++) {
474 if(!cx_strcmp(config->memberAttributes[i], attr)) {
475 return 1;
476 }
477 }
478 return 0;
479 }
480
481 static int group_add_member(LDAPGroup *group,
LDAP *ldap, LDAPMessage *msg,
char *attribute) {
482 struct berval **values = ldap_get_values_len(ldap, msg, attribute);
483 int ret =
0;
484 if(values) {
485 int count = ldap_count_values_len(values);
486 for(
int i=
0;i<count;i++) {
487 cxstring memberValue = cx_strn(values[i]->bv_val, values[i]->bv_len);
488 CxHashKey key = cx_hash_key(memberValue.ptr, memberValue.length);
489 char *g_member = cxMapGet(group->members, key);
490 if(!g_member) {
491 cxmutstr member = cx_strdup_a(group->members->allocator, memberValue);
492 if(!member.ptr) {
493 ret =
1;
494 break;
495 }
496 if(cxMapPut(group->members, key, member.ptr)) {
497 ret =
1;
498 break;
499 }
500 }
501 }
502 ldap_value_free_len(values);
503 }
504 return ret;
505 }
506
507 static LDAPGroup* ldap_msg_to_group(
508 Session *sn,
509 Request *rq,
510 LDAPAuthDB *authdb,
511 LDAP *ldap,
512 LDAPMessage *msg,
513 const char *group_name)
514 {
515 CxAllocator *a = pool_allocator(sn->pool);
516
517 LDAPGroup *group = pool_malloc(sn->pool,
sizeof(LDAPGroup));
518 if(!group) {
519 return NULL;
520 }
521 group->members = cxHashMapCreate(a,
CX_STORE_POINTERS,
32);
522 if(!group->members) {
523 pool_free(sn->pool, group);
524 return NULL;
525 }
526 group->name = pool_strdup(sn->pool, group_name);
527
528 BerElement *ber =
NULL;
529 char *attribute = ldap_first_attribute(ldap, msg, &ber);
530 while(attribute) {
531 if(is_member_attribute(authdb, attribute)) {
532 if(group_add_member(group, ldap, msg, attribute)) {
533
534 ldap_memfree(attribute);
535
536 cxMapDestroy(group->members);
537 pool_free(sn->pool, group);
538 group =
NULL;
539 break;
540 }
541 }
542
543 ldap_memfree(attribute);
544 attribute = ldap_next_attribute(ldap, msg, ber);
545 }
546 if(ber) {
547 ber_free(ber,
0);
548 }
549
550 return group;
551 }
552
553 LDAPGroup* ldap_get_group(Session *sn, Request *rq, LDAPAuthDB *authdb,
const char *group) {
554 LDAPConfig *config = &authdb->config;
555 CxAllocator *a = pool_allocator(sn->pool);
556
557 LDAP *ld = get_ldap_session(sn, rq, authdb);
558 if (ld ==
NULL) {
559 return NULL;
560 }
561
562
563
564 char *filterStr;
565 const char *basedn;
566 int scope;
567 if(config->userNameIsDN) {
568 filterStr =
NULL;
569 basedn = group;
570 scope =
LDAP_SCOPE_BASE;
571 }
else {
572 cxstring groupSearch = cx_str(config->groupSearchFilter);
573 cxmutstr filter = cx_strreplace_a(a, groupSearch, cx_str(
"%s"), cx_str(group));
574 if(!filter.ptr) {
575 return NULL;
576 }
577 filterStr = filter.ptr;
578 basedn = config->basedn;
579 scope =
LDAP_SCOPE_SUBTREE;
580 }
581
582 log_ereport(
LOG_DEBUG,
"ldap_get_group: basedn: %s filter: %s", basedn, filterStr);
583
584 LDAPMessage *result;
585 struct timeval timeout;
586 timeout.tv_sec =
8;
587 timeout.tv_usec =
0;
588 int r = ldap_search_ext_s(
589 ld,
590 basedn,
591 scope,
592 filterStr,
593 NULL,
594 0,
595 NULL,
596 NULL,
597 &timeout,
598 2,
599 &result);
600 if(filterStr) {
601 cxFree(a, filterStr);
602 }
603
604 if (r !=
LDAP_SUCCESS) {
605 if(result) {
606 ldap_msgfree(result);
607 }
608 log_ereport(
LOG_FAILURE,
"ldap_get_group %s: search failed: %s", group, ldap_err2string(r));
609 return NULL;
610 }
611
612 LDAPMessage *msg = ldap_first_entry(ld, result);
613 LDAPGroup *wsgroup =
NULL;
614 if(msg) {
615 if(ldap_count_entries(ld, msg) >
1) {
616 log_ereport(
LOG_FAILURE,
"ldap_get_user: more than one search result");
617 }
else {
618 wsgroup = ldap_msg_to_group(sn, rq, authdb, ld, msg, group);
619 }
620 }
621 ldap_msgfree(result);
622
623 return wsgroup;
624 }
625
626 int ldap_user_verify_password(User *u,
const char *password) {
627 LDAPUser *user = (LDAPUser*)u;
628
629 struct berval cred;
630 cred.bv_val = (
char*)password;
631 cred.bv_len = strlen(password);
632 struct berval *server_cred;
633 int r = ldap_sasl_bind_s(
634 user->ldap,
635 user->userdn,
636 LDAP_SASL_SIMPLE,
637 &cred,
638 NULL,
639 NULL,
640 &server_cred);
641 if(r ==
LDAP_SUCCESS) {
642 log_ereport(
LOG_VERBOSE,
"ldap user %s password ok", user->userdn);
643 return 1;
644 }
else {
645 log_ereport(
LOG_VERBOSE,
"ldap user %s password not ok", user->userdn);
646 return 0;
647 }
648 }
649
650 int ldap_user_check_group(User *u,
const char *group_str) {
651 LDAPUser *user = (LDAPUser*)u;
652 LDAPAuthDB *authdb = user->authdb;
653 if(!authdb->config.enableGroups) {
654 log_ereport(
655 LOG_DEBUG,
656 "ldap_user_check_group: authdb %s: groups disabled",
657 authdb->authdb.name);
658 return 0;
659 }
660
661 int ret =
0;
662 LDAPGroup *group = ldap_get_group(user->sn, user->rq, authdb, group_str);
663 if(group) {
664 const char *usr = authdb->config.groupMemberType ==
WS_LDAP_GROUP_MEMBER_DN ? user->userdn : user->uid_attr;
665 char *member = cxMapGet(group->members, cx_hash_key_str(usr));
666 if(member) {
667 ret =
1;
668 }
669 }
670
671 return ret;
672 }
673
674 void ldap_user_free(User *u) {
675 LDAPUser *user = (LDAPUser*)u;
676 pool_free(user->sn->pool, user->userdn);
677 pool_free(user->sn->pool, user->uid_attr);
678 pool_free(user->sn->pool, user);
679 }
680