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 * * Setup an ldap server. Add inetOrgAnon to the schema (it is in tools/ldap)
23 * * Make sure ldap is enabled at compile time (debian needs libldap2-dev package)
24 * * Enable ldap in x3.conf and set everything
25 * * Initial import: you can use the secret authserv search action add2ldap
26 * to get your users into ldap. /msg authserv search add2ldap account *
29 * * get queries working in static existance, so i understand how it works
30 * * get ldap enabled in ./configure
31 * * x3.conf settings to enable/configure its use
32 * * generic functions to enable ldap
33 * * nickserv.c work to use said functions.
39 #define LDAP_DEPRECATED 1
51 extern struct nickserv_config nickserv_conf
;
55 int admin_bind
= false;
59 if(!nickserv_conf
.ldap_enable
)
61 /* TODO: check here for all required config options and exit() out if not present */
62 //ld = ldap_init(nickserv_conf.ldap_host, nickserv_conf.ldap_port);
65 if(ldap_initialize(&ld
, nickserv_conf
.ldap_uri
)) {
66 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP initilization failed!\n");
69 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &nickserv_conf
.ldap_version
);
70 log_module(MAIN_LOG
, LOG_INFO
, "Success! ldap_init() was successfull in connecting to %s\n", nickserv_conf
.ldap_uri
);
75 /* Try to auth someone. If theres problems, try reconnecting
76 * once every 10 seconds for 1 minute.
77 * TODO: move this stuff to config file
79 unsigned int ldap_do_bind( const char *dn
, const char *pass
)
85 q
= ldap_simple_bind_s(ld
, dn
, pass
);
86 if(q
== LDAP_SUCCESS
) {
87 log_module(MAIN_LOG
, LOG_DEBUG
, "bind() successfull! You are bound as %s", dn
);
91 else if(q
== LDAP_INVALID_CREDENTIALS
) {
95 log_module(MAIN_LOG
, LOG_ERROR
, "Bind failed: %s/****** (%s)", dn
, ldap_err2string(q
));
96 /* ldap_perror(ld, "ldap"); */
100 /* TODO: return to the user that this is a connection error and not a problem
101 * with their password
103 log_module(MAIN_LOG
, LOG_ERROR
, "Failing to reconnect to ldap server. Auth failing.");
107 log_module(MAIN_LOG
, LOG_ERROR
, "ldap_do_bind falling off the end. this shouldnt happen");
110 int ldap_do_admin_bind()
113 if(!(nickserv_conf
.ldap_admin_dn
&& *nickserv_conf
.ldap_admin_dn
&&
114 nickserv_conf
.ldap_admin_pass
&& *nickserv_conf
.ldap_admin_pass
)) {
115 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
116 return LDAP_OTHER
; /* not configured to do this */
118 rc
= ldap_do_bind(nickserv_conf
.ldap_admin_dn
, nickserv_conf
.ldap_admin_pass
);
119 if(rc
== LDAP_SUCCESS
)
125 unsigned int ldap_check_auth( const char *account
, const char *pass
)
129 if(!nickserv_conf
.ldap_enable
)
132 memset(buff
, 0, MAXLEN
);
133 snprintf(buff
, sizeof(buff
)-1, nickserv_conf
.ldap_dn_fmt
/*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account
);
135 return ldap_do_bind(buff
, pass
);
139 int ldap_search_user(const char *account
, LDAPMessage
**entry
)
142 char filter
[MAXLEN
+1];
146 struct timeval timeout
;
148 memset(filter
, 0, MAXLEN
+1);
149 snprintf(filter
, MAXLEN
, "%s=%s", nickserv_conf
.ldap_field_account
, account
);
154 timeout
.tv_sec
= nickserv_conf
.ldap_timeout
;
155 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
156 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
159 if( (rc
= ldap_search_st(ld
, nickserv_conf
.ldap_base
, LDAP_SCOPE_ONELEVEL
, filter
, NULL
, 0, &timeout
, &res
)) != LDAP_SUCCESS
) {
160 log_module(MAIN_LOG
, LOG_ERROR
, "search failed: %s %s: %s", nickserv_conf
.ldap_base
, filter
, ldap_err2string(rc
));
163 log_module(MAIN_LOG
, LOG_DEBUG
, "Search successfull! %s %s\n", nickserv_conf
.ldap_base
, filter
);
164 if(ldap_count_entries(ld
, res
) != 1) {
165 log_module(MAIN_LOG
, LOG_DEBUG
, "LDAP search got %d entries when looking for %s", ldap_count_entries(ld
, res
), account
);
166 return(LDAP_OTHER
); /* Search was a success, but user not found.. */
168 log_module(MAIN_LOG
, LOG_DEBUG
, "LDAP search got %d entries", ldap_count_entries(ld
, res
));
169 *entry
= ldap_first_entry(ld
, res
);
173 /* queries the ldap server for account..
174 * if a single account match is found,
175 * email is allocated and set to the email address
176 * and returns LDAP_SUCCESS. returns LDAP_OTHER if
177 * 0 or 2+ entries are matched, or the proper ldap error
178 * code for other errors.
180 int ldap_get_user_info(const char *account
, char **email
)
183 struct berval
**value
;
184 LDAPMessage
*entry
, *res
;
187 if( (rc
= ldap_search_user(account
, &res
)) == LDAP_SUCCESS
) {
188 entry
= ldap_first_entry(ld
, res
);
189 value
= ldap_get_values_len(ld
, entry
, nickserv_conf
.ldap_field_email
);
194 *email
= strdup(value
[0]->bv_val
);
195 log_module(MAIN_LOG
, LOG_DEBUG
, "%s: %s\n", nickserv_conf
.ldap_field_email
, value
[0]->bv_val
);
196 ldap_value_free_len(value
);
198 value = ldap_get_values(ld, entry, "description");
199 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
200 value = ldap_get_values(ld, entry, "userPassword");
201 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
210 ldap_first_attribute();
213 ldap_next_attribute();
220 /* get errors with ldap_err2string(); */
223 /********* base64 stuff ***********/
225 unsigned char *pack(const char *str
, unsigned int *len
)
230 static unsigned char buf
[MAXLEN
+1];
233 memset(buf
, 0, MAXLEN
+1);
238 if((n
>= '0') && (n
<= '9')) {
240 } else if ((n
>= 'A') && (n
<= 'F')) {
242 } else if ((n
>= 'a') && (n
<= 'f')) {
245 printf("pack type H: illegal hex digit %c", n
);
250 buf
[++outputpos
] = 0;
255 buf
[outputpos
] |= (n
<< nibbleshift
);
256 nibbleshift
= (nibbleshift
+ 4) & 7;
262 char **make_object_vals()
265 static char **object_vals
= NULL
;
270 object_vals
= malloc(sizeof( *object_vals
) * (nickserv_conf
.ldap_object_classes
->used
+1));
272 for(y
= 0; y
< nickserv_conf
.ldap_object_classes
->used
; y
++) {
273 object_vals
[y
] = nickserv_conf
.ldap_object_classes
->list
[y
];
275 object_vals
[y
] = NULL
;
277 /* NOTE: The return value of this is only good until the next call to it. */
280 char *make_password(const char *crypted
)
283 unsigned char *packed
;
287 packed
= pack(crypted
, &len
);
288 base64_encode_alloc((char *)packed
, len
, &base64pass
);
289 passbuf
= malloc(strlen(base64pass
) + 1 + 5);
290 strcpy(passbuf
, "{MD5}");
291 strcat(passbuf
, base64pass
);
292 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
298 LDAPMod
**make_mods_add(const char *account
, const char *password
, const char *email
, int *num_mods_ret
)
300 static char *account_vals
[] = { NULL
, NULL
};
301 static char *password_vals
[] = { NULL
, NULL
};
302 static char *email_vals
[] = { NULL
, NULL
};
306 /* TODO: take this from nickserv_conf.ldap_add_objects */
308 static char **object_vals
;
310 account_vals
[0] = NULL
;
311 account_vals
[1] = NULL
;
312 password_vals
[0] = NULL
;
313 password_vals
[1] = NULL
;
314 email_vals
[0] = NULL
;
315 email_vals
[1] = NULL
;
316 object_vals
= make_object_vals();
318 account_vals
[0] = (char *) account
;
319 if (password
!= NULL
) {
320 password_vals
[0] = (char *) password
;
323 email_vals
[0] = (char *) email
;
325 if(!(nickserv_conf
.ldap_field_account
&& *nickserv_conf
.ldap_field_account
))
326 return 0; /* account required */
327 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
328 return 0; /* password required */
329 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
332 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
333 for( i
= 0; i
< num_mods
; i
++) {
334 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
335 memset(mods
[i
], 0, sizeof(LDAPMod
));
338 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
339 mods
[mod
]->mod_type
= strdup("objectclass");
340 mods
[mod
]->mod_values
= object_vals
;
343 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
344 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_account
);
345 mods
[mod
]->mod_values
= account_vals
;
348 if (password
!= NULL
) {
349 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
350 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
351 mods
[mod
]->mod_values
= password_vals
;
355 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
&& *email
) {
356 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
357 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
358 mods
[mod
]->mod_values
= email_vals
;
362 *num_mods_ret
= num_mods
;
366 int ldap_do_add(const char *account
, const char *crypted
, const char *email
)
372 char *passbuf
= NULL
;
374 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
375 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
380 passbuf
= make_password(crypted
);
381 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
382 mods
= make_mods_add(account
, (crypted
!= NULL
? passbuf
: crypted
), email
, &num_mods
);
384 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
387 rc
= ldap_add_ext_s(ld
, newdn
, mods
, NULL
, NULL
);
388 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_ALREADY_EXISTS
) {
389 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
393 for(i
= 0; i
< num_mods
; i
++) {
394 free(mods
[i
]->mod_type
);
403 int ldap_delete_account(char *account
)
408 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
409 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
413 memset(dn
, 0, MAXLEN
);
414 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
415 return(ldap_delete_s(ld
, dn
));
418 int ldap_rename_account(char *oldaccount
, char *newaccount
)
420 char dn
[MAXLEN
], newdn
[MAXLEN
];
423 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
424 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
428 memset(dn
, 0, MAXLEN
);
429 memset(newdn
, 0, MAXLEN
);
430 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
431 strcat(newdn
, nickserv_conf
.ldap_field_account
);
433 strcat(newdn
, newaccount
);
434 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
435 if(rc
!= LDAP_SUCCESS
) {
436 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
443 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
445 static char *password_vals
[] = { NULL
, NULL
};
446 static char *email_vals
[] = { NULL
, NULL
};
449 /* TODO: take this from nickserv_conf.ldap_add_objects */
452 password_vals
[0] = (char *) password
;
453 email_vals
[0] = (char *) email
;
455 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
456 return 0; /* password required */
458 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
466 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
467 for( i
= 0; i
< num_mods
; i
++) {
468 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
469 memset(mods
[i
], 0, sizeof(LDAPMod
));
473 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
475 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
476 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
477 mods
[i
]->mod_values
= password_vals
;
481 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
482 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
483 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
484 mods
[i
]->mod_values
= email_vals
;
488 *num_mods_ret
= num_mods
;
492 /* Save OpServ level to LDAP
494 * level - OpServ level
496 * A level of <0 will be treated as 0
498 int ldap_do_oslevel(const char *account
, int level
, int oldlevel
)
501 static char *oslevel_vals
[] = { NULL
, NULL
};
502 char dn
[MAXLEN
], temp
[MAXLEN
];
505 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
506 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
514 snprintf(temp
, MAXLEN
-1, "%d", (level
? level
: oldlevel
));
515 oslevel_vals
[0] = (char *) temp
;
517 if(!(nickserv_conf
.ldap_field_oslevel
&& *nickserv_conf
.ldap_field_oslevel
))
520 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
522 mods
= ( LDAPMod
** ) malloc(( 1 ) * sizeof( LDAPMod
* ));
523 mods
[0] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
524 memset(mods
[0], 0, sizeof(LDAPMod
));
526 mods
[0]->mod_op
= (level
? LDAP_MOD_REPLACE
: LDAP_MOD_DELETE
);
527 mods
[0]->mod_type
= strdup(nickserv_conf
.ldap_field_oslevel
);
528 mods
[0]->mod_values
= oslevel_vals
;
531 rc
= ldap_modify_s(ld
, dn
, mods
);
532 if(rc
!= LDAP_SUCCESS
) {
533 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap OpServ level: %s -- %s", account
, ldap_err2string(rc
));
536 free(mods
[0]->mod_type
);
543 /* Save email or password to server
545 * password - UNENCRYPTED password. This function encrypts if libs are available
546 * email - email address
548 * NULL to make no change
550 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
556 char *passbuf
= NULL
;
558 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
559 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
564 passbuf
= make_password(password
);
567 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
568 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
570 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_do_modify");
573 rc
= ldap_modify_s(ld
, dn
, mods
);
574 if(rc
!= LDAP_SUCCESS
) {
575 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", account
, ldap_err2string(rc
));
578 for(i
= 0; i
< num_mods
; i
++) {
579 free(mods
[i
]->mod_type
);
588 LDAPMod
**make_mods_group(const char *account
, int operation
, int *num_mods_ret
)
590 static char *uid_vals
[] = { NULL
, NULL
};
593 /* TODO: take this from nickserv_conf.ldap_add_objects */
596 uid_vals
[0] = (char *) account
;
598 if(!(nickserv_conf
.ldap_field_group_member
&& *nickserv_conf
.ldap_field_group_member
))
599 return 0; /* password required */
601 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
602 for( i
= 0; i
< num_mods
; i
++) {
603 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
604 memset(mods
[i
], 0, sizeof(LDAPMod
));
608 mods
[i
]->mod_op
= operation
;
609 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_group_member
);
610 mods
[i
]->mod_values
= uid_vals
;
613 *num_mods_ret
= num_mods
;
618 int ldap_add2group(char *account
, const char *group
)
624 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
625 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
628 mods
= make_mods_group(account
, LDAP_MOD_ADD
, &num_mods
);
630 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for add2group");
633 rc
= ldap_modify_s(ld
, group
, mods
);
634 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_TYPE_OR_VALUE_EXISTS
) {
635 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding %s to group %s: %s", account
, group
, ldap_err2string(rc
));
638 for(i
= 0; i
< num_mods
; i
++) {
639 free(mods
[i
]->mod_type
);
646 int ldap_delfromgroup(char *account
, const char *group
)
652 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
653 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
656 mods
= make_mods_group(account
, LDAP_MOD_DELETE
, &num_mods
);
658 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for delfromgroup");
661 rc
= ldap_modify_s(ld
, group
, mods
);
662 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_NO_SUCH_ATTRIBUTE
) {
663 log_module(MAIN_LOG
, LOG_ERROR
, "Error removing %s from group %s: %s", account
, group
, ldap_err2string(rc
));
666 for(i
= 0; i
< num_mods
; i
++) {
667 free(mods
[i
]->mod_type
);
678 ldap_unbind_ext(ld
, NULL
, NULL
);
681 /* queries the ldap server for account..
682 * returns LDAP_SUCCESS if a match is found
683 * returns LDAP_OTHER if no match is found
684 * on error returns the proper ldap error
686 int ldap_user_exists(const char *account
)
691 rc
= ldap_search_user(account
, &res
);