1 /* x3ldap.c - LDAP functionality for x3, by Rubin
2 * Copyright 2002-2007 x3 Development Team
4 * This file is part of x3.
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 * * get queries working in static existance, so i understand how it works
23 * * get ldap enabled in ./configure
24 * * x3.conf settings to enable/configure its use
25 * * generic functions to enable ldap
26 * * nickserv.c work to use said functions.
41 extern struct nickserv_config nickserv_conf
;
45 int admin_bind
= false;
49 if(!nickserv_conf
.ldap_enable
)
51 /* TODO: check here for all required config options and exit() out if not present */
52 //ld = ldap_init(nickserv_conf.ldap_host, nickserv_conf.ldap_port);
55 if(ldap_initialize(&ld
, nickserv_conf
.ldap_uri
)) {
56 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP initilization failed!\n");
59 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &nickserv_conf
.ldap_version
);
60 log_module(MAIN_LOG
, LOG_INFO
, "Success! ldap_init() was successfull in connecting to %s\n", nickserv_conf
.ldap_uri
);
65 /* Try to auth someone. If theres problems, try reconnecting
66 * once every 10 seconds for 1 minute.
67 * TODO: move this stuff to config file
69 unsigned int ldap_do_bind( const char *dn
, const char *pass
)
75 q
= ldap_simple_bind_s(ld
, dn
, pass
);
76 if(q
== LDAP_SUCCESS
) {
77 log_module(MAIN_LOG
, LOG_DEBUG
, "bind() successfull! You are bound as %s", dn
);
81 else if(q
== LDAP_INVALID_CREDENTIALS
) {
85 log_module(MAIN_LOG
, LOG_ERROR
, "Bind failed: %s/****** (%s)", dn
, ldap_err2string(q
));
86 /* ldap_perror(ld, "ldap"); */
90 /* TODO: return to the user that this is a connection error and not a problem
93 log_module(MAIN_LOG
, LOG_ERROR
, "Failing to reconnect to ldap server. Auth failing.");
97 log_module(MAIN_LOG
, LOG_ERROR
, "ldap_do_bind falling off the end. this shouldnt happen");
100 int ldap_do_admin_bind()
103 if(!(nickserv_conf
.ldap_admin_dn
&& *nickserv_conf
.ldap_admin_dn
&&
104 nickserv_conf
.ldap_admin_pass
&& *nickserv_conf
.ldap_admin_pass
)) {
105 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
106 return LDAP_OTHER
; /* not configured to do this */
108 rc
= ldap_do_bind(nickserv_conf
.ldap_admin_dn
, nickserv_conf
.ldap_admin_pass
);
109 if(rc
== LDAP_SUCCESS
)
115 unsigned int ldap_check_auth( const char *account
, const char *pass
)
119 if(!nickserv_conf
.ldap_enable
)
122 memset(buff
, 0, MAXLEN
);
123 snprintf(buff
, sizeof(buff
)-1, nickserv_conf
.ldap_dn_fmt
/*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account
);
125 return ldap_do_bind(buff
, pass
);
129 int ldap_search_user(char *account
, LDAPMessage
**entry
)
132 char filter
[MAXLEN
+1];
136 struct timeval timeout
;
138 memset(filter
, 0, MAXLEN
+1);
139 snprintf(filter
, MAXLEN
, "%s=%s", nickserv_conf
.ldap_field_account
, account
);
144 timeout
.tv_sec
= nickserv_conf
.ldap_timeout
;
145 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
146 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
149 if( (rc
= ldap_search_st(ld
, nickserv_conf
.ldap_base
, LDAP_SCOPE_ONELEVEL
, filter
, NULL
, 0, &timeout
, &res
)) != LDAP_SUCCESS
) {
150 log_module(MAIN_LOG
, LOG_ERROR
, "search failed: %s %s: %s", nickserv_conf
.ldap_base
, filter
, ldap_err2string(rc
));
153 log_module(MAIN_LOG
, LOG_DEBUG
, "Search successfull! %s %s\n", nickserv_conf
.ldap_base
, filter
);
154 if(ldap_count_entries(ld
, res
) != 1) {
155 log_module(MAIN_LOG
, LOG_DEBUG
, "LDAP search got %d entries when looking for %s", ldap_count_entries(ld
, res
), account
);
156 return(LDAP_OTHER
); /* Search was a success, but user not found.. */
158 log_module(MAIN_LOG
, LOG_DEBUG
, "LDAP search got %d entries", ldap_count_entries(ld
, res
));
159 *entry
= ldap_first_entry(ld
, res
);
163 /* queries the ldap server for account..
164 * if a single account match is found,
165 * email is allocated and set to the email address
166 * and returns LDAP_SUCCESS. returns LDAP_OTHER if
167 * 0 or 2+ entries are matched, or the proper ldap error
168 * code for other errors.
170 int ldap_get_user_info(const char *account
, char **email
)
173 struct berval
**value
;
174 LDAPMessage
*entry
, *res
;
177 if( (rc
= ldap_search_user(account
, &res
)) == LDAP_SUCCESS
) {
178 entry
= ldap_first_entry(ld
, res
);
179 value
= ldap_get_values_len(ld
, entry
, nickserv_conf
.ldap_field_email
);
184 *email
= strdup(value
[0]->bv_val
);
185 log_module(MAIN_LOG
, LOG_DEBUG
, "%s: %s\n", nickserv_conf
.ldap_field_email
, value
[0]->bv_val
);
186 ldap_value_free_len(value
);
188 value = ldap_get_values(ld, entry, "description");
189 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
190 value = ldap_get_values(ld, entry, "userPassword");
191 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
200 ldap_first_attribute();
203 ldap_next_attribute();
210 /* get errors with ldap_err2string(); */
213 /********* base64 stuff ***********/
215 unsigned char *pack(const char *str
, unsigned int *len
)
220 static unsigned char buf
[MAXLEN
+1];
223 memset(buf
, 0, MAXLEN
+1);
228 if((n
>= '0') && (n
<= '9')) {
230 } else if ((n
>= 'A') && (n
<= 'F')) {
232 } else if ((n
>= 'a') && (n
<= 'f')) {
235 printf("pack type H: illegal hex digit %c", n
);
240 buf
[++outputpos
] = 0;
245 buf
[outputpos
] |= (n
<< nibbleshift
);
246 nibbleshift
= (nibbleshift
+ 4) & 7;
253 /* from php5 sources */
254 static char base64_table
[] =
255 { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
256 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
257 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
258 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
259 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
261 static char base64_pad
= '=';
263 char *base64_encode(const unsigned char *str
, int length
, int *ret_length
)
265 const unsigned char *current
= str
;
269 if ((length
+ 2) < 0 || ((length
+ 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
270 if (ret_length
!= NULL
) {
276 result
= (char *)calloc((((length
+ 2) / 3) * 4)+1, sizeof(char));
279 while (length
> 2) { /* keep going until we have less than 24 bits */
280 *p
++ = base64_table
[current
[0] >> 2];
281 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
282 *p
++ = base64_table
[((current
[1] & 0x0f) << 2) + (current
[2] >> 6)];
283 *p
++ = base64_table
[current
[2] & 0x3f];
286 length
-= 3; /* we just handle 3 octets of data */
289 /* now deal with the tail end of things */
291 *p
++ = base64_table
[current
[0] >> 2];
293 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
294 *p
++ = base64_table
[(current
[1] & 0x0f) << 2];
297 *p
++ = base64_table
[(current
[0] & 0x03) << 4];
302 if (ret_length
!= NULL
) {
303 *ret_length
= (int)(p
- result
);
310 char **make_object_vals()
313 static char **object_vals
= NULL
;
318 object_vals
= malloc(sizeof( *object_vals
) * (nickserv_conf
.ldap_object_classes
->used
+1));
320 for(y
= 0; y
< nickserv_conf
.ldap_object_classes
->used
; y
++) {
321 object_vals
[y
] = nickserv_conf
.ldap_object_classes
->list
[y
];
323 object_vals
[y
] = NULL
;
325 /* NOTE: The return value of this is only good until the next call to it. */
328 char *make_password(const char *crypted
)
331 unsigned char *packed
;
335 packed
= pack(crypted
, &len
);
336 base64pass
= base64_encode(packed
, len
, NULL
);
337 passbuf
= malloc(strlen(base64pass
) + 1 + 5);
338 strcpy(passbuf
, "{MD5}");
339 strcat(passbuf
, base64pass
);
340 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
346 LDAPMod
**make_mods_add(const char *account
, const char *password
, const char *email
, int *num_mods_ret
)
348 static char *account_vals
[] = { NULL
, NULL
};
349 static char *password_vals
[] = { NULL
, NULL
};
350 static char *email_vals
[] = { NULL
, NULL
};
354 /* TODO: take this from nickserv_conf.ldap_add_objects */
356 static char **object_vals
;
358 account_vals
[0] = NULL
;
359 account_vals
[1] = NULL
;
360 password_vals
[0] = NULL
;
361 password_vals
[1] = NULL
;
362 email_vals
[0] = NULL
;
363 email_vals
[1] = NULL
;
364 object_vals
= make_object_vals();
366 account_vals
[0] = (char *) account
;
367 if (password
!= NULL
) {
368 password_vals
[0] = (char *) password
;
371 email_vals
[0] = (char *) email
;
373 if(!(nickserv_conf
.ldap_field_account
&& *nickserv_conf
.ldap_field_account
))
374 return 0; /* account required */
375 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
376 return 0; /* password required */
377 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
380 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
381 for( i
= 0; i
< num_mods
; i
++) {
382 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
383 memset(mods
[i
], 0, sizeof(LDAPMod
));
386 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
387 mods
[mod
]->mod_type
= strdup("objectclass");
388 mods
[mod
]->mod_values
= object_vals
;
391 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
392 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_account
);
393 mods
[mod
]->mod_values
= account_vals
;
396 if (password
!= NULL
) {
397 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
398 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
399 mods
[mod
]->mod_values
= password_vals
;
403 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
&& *email
) {
404 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
405 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
406 mods
[mod
]->mod_values
= email_vals
;
410 *num_mods_ret
= num_mods
;
414 int ldap_do_add(const char *account
, const char *crypted
, const char *email
)
422 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
423 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
428 passbuf
= make_password(crypted
);
429 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
430 mods
= make_mods_add(account
, (crypted
!= NULL
? passbuf
: crypted
), email
, &num_mods
);
432 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
435 rc
= ldap_add_ext_s(ld
, newdn
, mods
, NULL
, NULL
);
436 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_ALREADY_EXISTS
) {
437 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
441 for(i
= 0; i
< num_mods
; i
++) {
442 free(mods
[i
]->mod_type
);
451 int ldap_delete_account(char *account
)
456 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
457 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
461 memset(dn
, 0, MAXLEN
);
462 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
463 return(ldap_delete_s(ld
, dn
));
466 int ldap_rename_account(char *oldaccount
, char *newaccount
)
468 char dn
[MAXLEN
], newdn
[MAXLEN
];
471 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
472 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
476 memset(dn
, 0, MAXLEN
);
477 memset(newdn
, 0, MAXLEN
);
478 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
479 strcat(newdn
, nickserv_conf
.ldap_field_account
);
481 strcat(newdn
, newaccount
);
482 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
483 if(rc
!= LDAP_SUCCESS
) {
484 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
491 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
493 static char *password_vals
[] = { NULL
, NULL
};
494 static char *email_vals
[] = { NULL
, NULL
};
497 /* TODO: take this from nickserv_conf.ldap_add_objects */
500 password_vals
[0] = (char *) password
;
501 email_vals
[0] = (char *) email
;
503 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
504 return 0; /* password required */
506 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
514 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
515 for( i
= 0; i
< num_mods
; i
++) {
516 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
517 memset(mods
[i
], 0, sizeof(LDAPMod
));
521 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
523 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
524 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
525 mods
[i
]->mod_values
= password_vals
;
529 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
530 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
531 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
532 mods
[i
]->mod_values
= email_vals
;
536 *num_mods_ret
= num_mods
;
540 /* Save OpServ level to LDAP
542 * level - OpServ level
544 * A level of <0 will be treated as 0
546 int ldap_do_oslevel(const char *account
, int level
, int oldlevel
)
549 static char *oslevel_vals
[] = { NULL
, NULL
};
550 char dn
[MAXLEN
], temp
[MAXLEN
];
553 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
554 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
562 snprintf(temp
, MAXLEN
-1, "%d", (level
? level
: oldlevel
));
563 oslevel_vals
[0] = (char *) temp
;
565 if(!(nickserv_conf
.ldap_field_oslevel
&& *nickserv_conf
.ldap_field_oslevel
))
568 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
570 mods
= ( LDAPMod
** ) malloc(( 1 ) * sizeof( LDAPMod
* ));
571 mods
[0] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
572 memset(mods
[0], 0, sizeof(LDAPMod
));
574 mods
[0]->mod_op
= (level
? LDAP_MOD_REPLACE
: LDAP_MOD_DELETE
);
575 mods
[0]->mod_type
= strdup(nickserv_conf
.ldap_field_oslevel
);
576 mods
[0]->mod_values
= oslevel_vals
;
579 rc
= ldap_modify_s(ld
, dn
, mods
);
580 if(rc
!= LDAP_SUCCESS
) {
581 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap OpServ level: %s -- %s", account
, ldap_err2string(rc
));
584 free(mods
[0]->mod_type
);
591 /* Save email or password to server
593 * password - UNENCRYPTED password. This function encrypts if libs are available
594 * email - email address
596 * NULL to make no change
598 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
604 char *passbuf
= NULL
;
606 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
607 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
612 passbuf
= make_password(password
);
615 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
616 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
618 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_do_modify");
621 rc
= ldap_modify_s(ld
, dn
, mods
);
622 if(rc
!= LDAP_SUCCESS
) {
623 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", account
, ldap_err2string(rc
));
626 for(i
= 0; i
< num_mods
; i
++) {
627 free(mods
[i
]->mod_type
);
636 LDAPMod
**make_mods_group(const char *account
, int operation
, int *num_mods_ret
)
638 static char *uid_vals
[] = { NULL
, NULL
};
641 /* TODO: take this from nickserv_conf.ldap_add_objects */
644 uid_vals
[0] = (char *) account
;
646 if(!(nickserv_conf
.ldap_field_group_member
&& *nickserv_conf
.ldap_field_group_member
))
647 return 0; /* password required */
649 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
650 for( i
= 0; i
< num_mods
; i
++) {
651 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
652 memset(mods
[i
], 0, sizeof(LDAPMod
));
656 mods
[i
]->mod_op
= operation
;
657 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_group_member
);
658 mods
[i
]->mod_values
= uid_vals
;
661 *num_mods_ret
= num_mods
;
666 int ldap_add2group(char *account
, const char *group
)
672 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
673 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
676 mods
= make_mods_group(account
, LDAP_MOD_ADD
, &num_mods
);
678 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for add2group");
681 rc
= ldap_modify_s(ld
, group
, mods
);
682 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_TYPE_OR_VALUE_EXISTS
) {
683 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding %s to group %s: %s", account
, group
, ldap_err2string(rc
));
686 for(i
= 0; i
< num_mods
; i
++) {
687 free(mods
[i
]->mod_type
);
694 int ldap_delfromgroup(char *account
, const char *group
)
700 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
701 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
704 mods
= make_mods_group(account
, LDAP_MOD_DELETE
, &num_mods
);
706 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for delfromgroup");
709 rc
= ldap_modify_s(ld
, group
, mods
);
710 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_NO_SUCH_ATTRIBUTE
) {
711 log_module(MAIN_LOG
, LOG_ERROR
, "Error removing %s from group %s: %s", account
, group
, ldap_err2string(rc
));
714 for(i
= 0; i
< num_mods
; i
++) {
715 free(mods
[i
]->mod_type
);
726 ldap_unbind_ext(ld
, NULL
, NULL
);