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( char *account
, 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(char *account
, char **email
)
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(ld
, entry
, nickserv_conf
.ldap_field_email
);
184 *email
= strdup(value
[0]);
185 log_module(MAIN_LOG
, LOG_DEBUG
, "%s: %s\n", nickserv_conf
.ldap_field_email
, value
[0]);
187 value = ldap_get_values(ld, entry, "description");
188 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
189 value = ldap_get_values(ld, entry, "userPassword");
190 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
199 ldap_first_attribute();
202 ldap_next_attribute();
209 /* get errors with ldap_err2string(); */
212 /********* base64 stuff ***********/
214 unsigned char *pack(const char *str
, unsigned int *len
)
219 static unsigned char buf
[MAXLEN
+1];
222 memset(buf
, 0, MAXLEN
+1);
227 if((n
>= '0') && (n
<= '9')) {
229 } else if ((n
>= 'A') && (n
<= 'F')) {
231 } else if ((n
>= 'a') && (n
<= 'f')) {
234 printf("pack type H: illegal hex digit %c", n
);
239 buf
[++outputpos
] = 0;
244 buf
[outputpos
] |= (n
<< nibbleshift
);
245 nibbleshift
= (nibbleshift
+ 4) & 7;
252 /* from php5 sources */
253 static char base64_table
[] =
254 { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
255 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
256 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
257 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
258 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
260 static char base64_pad
= '=';
262 char *base64_encode(const unsigned char *str
, int length
, int *ret_length
)
264 const unsigned char *current
= str
;
268 if ((length
+ 2) < 0 || ((length
+ 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
269 if (ret_length
!= NULL
) {
275 result
= (char *)calloc((((length
+ 2) / 3) * 4)+1, sizeof(char));
278 while (length
> 2) { /* keep going until we have less than 24 bits */
279 *p
++ = base64_table
[current
[0] >> 2];
280 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
281 *p
++ = base64_table
[((current
[1] & 0x0f) << 2) + (current
[2] >> 6)];
282 *p
++ = base64_table
[current
[2] & 0x3f];
285 length
-= 3; /* we just handle 3 octets of data */
288 /* now deal with the tail end of things */
290 *p
++ = base64_table
[current
[0] >> 2];
292 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
293 *p
++ = base64_table
[(current
[1] & 0x0f) << 2];
296 *p
++ = base64_table
[(current
[0] & 0x03) << 4];
301 if (ret_length
!= NULL
) {
302 *ret_length
= (int)(p
- result
);
309 char **make_object_vals()
312 static char **object_vals
= NULL
;
317 object_vals
= malloc(sizeof( *object_vals
) * nickserv_conf
.ldap_object_classes
->used
);
319 for(y
= 0; y
< nickserv_conf
.ldap_object_classes
->used
; y
++) {
320 object_vals
[y
] = nickserv_conf
.ldap_object_classes
->list
[y
];
322 object_vals
[y
] = NULL
;
326 char *make_password(const char *crypted
)
329 unsigned char *packed
;
333 packed
= pack(crypted
, &len
);
334 base64pass
= base64_encode(packed
, len
, NULL
);
335 passbuf
= malloc(strlen(base64pass
) + 1 + 5);
336 strcpy(passbuf
, "{MD5}");
337 strcat(passbuf
, base64pass
);
338 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
344 LDAPMod
**make_mods_add(const char *account
, const char *password
, const char *email
, int *num_mods_ret
)
346 static char *account_vals
[] = { NULL
, NULL
};
347 static char *password_vals
[] = { NULL
, NULL
};
348 static char *email_vals
[] = { NULL
, NULL
};
351 /* TODO: take this from nickserv_conf.ldap_add_objects */
353 static char **object_vals
;
354 object_vals
= make_object_vals();
356 account_vals
[0] = (char *) account
;
357 password_vals
[0] = (char *) password
;
358 email_vals
[0] = (char *) email
;
360 if(!(nickserv_conf
.ldap_field_account
&& *nickserv_conf
.ldap_field_account
))
361 return 0; /* account required */
362 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
363 return 0; /* password required */
364 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
367 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
368 for( i
= 0; i
< num_mods
; i
++) {
369 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
370 memset(mods
[i
], 0, sizeof(LDAPMod
));
373 mods
[0]->mod_op
= LDAP_MOD_ADD
;
374 mods
[0]->mod_type
= strdup("objectclass");
375 mods
[0]->mod_values
= object_vals
;
377 mods
[1]->mod_op
= LDAP_MOD_ADD
;
378 mods
[1]->mod_type
= strdup(nickserv_conf
.ldap_field_account
);
379 mods
[1]->mod_values
= account_vals
;
381 mods
[2]->mod_op
= LDAP_MOD_ADD
;
382 mods
[2]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
383 mods
[2]->mod_values
= password_vals
;
385 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
&& *email
) {
386 mods
[3]->mod_op
= LDAP_MOD_ADD
;
387 mods
[3]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
388 mods
[3]->mod_values
= email_vals
;
393 *num_mods_ret
= num_mods
;
397 int ldap_do_add(const char *account
, const char *crypted
, const char *email
)
405 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
406 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
410 passbuf
= make_password(crypted
);
411 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
412 mods
= make_mods_add(account
, passbuf
, email
, &num_mods
);
414 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
417 rc
= ldap_add_ext_s(ld
, newdn
, mods
, NULL
, NULL
);
418 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_ALREADY_EXISTS
) {
419 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
423 for(i
= 0; i
< num_mods
; i
++) {
424 free(mods
[i
]->mod_type
);
432 int ldap_delete_account(char *account
)
437 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
438 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
442 memset(dn
, 0, MAXLEN
);
443 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
444 return(ldap_delete_s(ld
, dn
));
447 int ldap_rename_account(char *oldaccount
, char *newaccount
)
449 char dn
[MAXLEN
], newdn
[MAXLEN
];
452 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
453 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
457 memset(dn
, 0, MAXLEN
);
458 memset(newdn
, 0, MAXLEN
);
459 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
460 strcat(newdn
, nickserv_conf
.ldap_field_account
);
462 strcat(newdn
, newaccount
);
463 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
464 if(rc
!= LDAP_SUCCESS
) {
465 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
472 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
474 static char *password_vals
[] = { NULL
, NULL
};
475 static char *email_vals
[] = { NULL
, NULL
};
478 /* TODO: take this from nickserv_conf.ldap_add_objects */
481 password_vals
[0] = (char *) password
;
482 email_vals
[0] = (char *) email
;
484 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
485 return 0; /* password required */
487 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
495 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
496 for( i
= 0; i
< num_mods
; i
++) {
497 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
498 memset(mods
[i
], 0, sizeof(LDAPMod
));
502 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
504 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
505 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
506 mods
[i
]->mod_values
= password_vals
;
510 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
511 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
512 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
513 mods
[i
]->mod_values
= email_vals
;
517 *num_mods_ret
= num_mods
;
522 /* Save email or password to server
524 * password - UNENCRYPTED password. This function encrypts if libs are available
525 * email - email address
527 * NULL to make no change
529 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
535 char *passbuf
= NULL
;
537 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
538 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
543 passbuf
= make_password(password
);
546 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
547 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
549 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_do_modify");
552 rc
= ldap_modify_s(ld
, dn
, mods
);
553 if(rc
!= LDAP_SUCCESS
) {
554 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", account
, ldap_err2string(rc
));
557 for(i
= 0; i
< num_mods
; i
++) {
558 free(mods
[i
]->mod_type
);
567 LDAPMod
**make_mods_group(const char *account
, int operation
, int *num_mods_ret
)
569 static char *uid_vals
[] = { NULL
, NULL
};
572 /* TODO: take this from nickserv_conf.ldap_add_objects */
575 uid_vals
[0] = (char *) account
;
577 if(!(nickserv_conf
.ldap_field_group_member
&& *nickserv_conf
.ldap_field_group_member
))
578 return 0; /* password required */
580 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
581 for( i
= 0; i
< num_mods
; i
++) {
582 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
583 memset(mods
[i
], 0, sizeof(LDAPMod
));
587 mods
[i
]->mod_op
= operation
;
588 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_group_member
);
589 mods
[i
]->mod_values
= uid_vals
;
592 *num_mods_ret
= num_mods
;
597 int ldap_add2group(char *account
, const char *group
)
603 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
604 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
607 mods
= make_mods_group(account
, LDAP_MOD_ADD
, &num_mods
);
609 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for add2group");
612 rc
= ldap_modify_s(ld
, group
, mods
);
613 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_TYPE_OR_VALUE_EXISTS
) {
614 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding %s to group %s: %s", account
, group
, ldap_err2string(rc
));
617 for(i
= 0; i
< num_mods
; i
++) {
618 free(mods
[i
]->mod_type
);
625 int ldap_delfromgroup(char *account
, const char *group
)
631 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
632 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
635 mods
= make_mods_group(account
, LDAP_MOD_DELETE
, &num_mods
);
637 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for delfromgroup");
640 rc
= ldap_modify_s(ld
, group
, mods
);
641 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_NO_SUCH_ATTRIBUTE
) {
642 log_module(MAIN_LOG
, LOG_ERROR
, "Error removing %s from group %s: %s", account
, group
, ldap_err2string(rc
));
645 for(i
= 0; i
< num_mods
; i
++) {
646 free(mods
[i
]->mod_type
);