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
)
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
+1));
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
;
324 /* NOTE: The return value of this is only good until the next call to it. */
327 char *make_password(const char *crypted
)
330 unsigned char *packed
;
334 packed
= pack(crypted
, &len
);
335 base64pass
= base64_encode(packed
, len
, NULL
);
336 passbuf
= malloc(strlen(base64pass
) + 1 + 5);
337 strcpy(passbuf
, "{MD5}");
338 strcat(passbuf
, base64pass
);
339 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
345 LDAPMod
**make_mods_add(const char *account
, const char *password
, const char *email
, int *num_mods_ret
)
347 static char *account_vals
[] = { NULL
, NULL
};
348 static char *password_vals
[] = { NULL
, NULL
};
349 static char *email_vals
[] = { NULL
, NULL
};
353 /* TODO: take this from nickserv_conf.ldap_add_objects */
355 static char **object_vals
;
357 account_vals
[0] = NULL
;
358 account_vals
[1] = NULL
;
359 password_vals
[0] = NULL
;
360 password_vals
[1] = NULL
;
361 email_vals
[0] = NULL
;
362 email_vals
[1] = NULL
;
363 object_vals
= make_object_vals();
365 account_vals
[0] = (char *) account
;
366 if (password
!= NULL
) {
367 password_vals
[0] = (char *) password
;
370 email_vals
[0] = (char *) email
;
372 if(!(nickserv_conf
.ldap_field_account
&& *nickserv_conf
.ldap_field_account
))
373 return 0; /* account required */
374 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
375 return 0; /* password required */
376 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
379 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
380 for( i
= 0; i
< num_mods
; i
++) {
381 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
382 memset(mods
[i
], 0, sizeof(LDAPMod
));
385 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
386 mods
[mod
]->mod_type
= strdup("objectclass");
387 mods
[mod
]->mod_values
= object_vals
;
390 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
391 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_account
);
392 mods
[mod
]->mod_values
= account_vals
;
395 if (password
!= NULL
) {
396 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
397 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
398 mods
[mod
]->mod_values
= password_vals
;
402 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
&& *email
) {
403 mods
[mod
]->mod_op
= LDAP_MOD_ADD
;
404 mods
[mod
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
405 mods
[mod
]->mod_values
= email_vals
;
409 *num_mods_ret
= num_mods
;
413 int ldap_do_add(const char *account
, const char *crypted
, const char *email
)
421 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
422 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
427 passbuf
= make_password(crypted
);
428 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
429 mods
= make_mods_add(account
, (crypted
!= NULL
? passbuf
: crypted
), email
, &num_mods
);
431 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
434 rc
= ldap_add_ext_s(ld
, newdn
, mods
, NULL
, NULL
);
435 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_ALREADY_EXISTS
) {
436 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
440 for(i
= 0; i
< num_mods
; i
++) {
441 free(mods
[i
]->mod_type
);
450 int ldap_delete_account(char *account
)
455 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
456 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
460 memset(dn
, 0, MAXLEN
);
461 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
462 return(ldap_delete_s(ld
, dn
));
465 int ldap_rename_account(char *oldaccount
, char *newaccount
)
467 char dn
[MAXLEN
], newdn
[MAXLEN
];
470 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
471 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
475 memset(dn
, 0, MAXLEN
);
476 memset(newdn
, 0, MAXLEN
);
477 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
478 strcat(newdn
, nickserv_conf
.ldap_field_account
);
480 strcat(newdn
, newaccount
);
481 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
482 if(rc
!= LDAP_SUCCESS
) {
483 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
490 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
492 static char *password_vals
[] = { NULL
, NULL
};
493 static char *email_vals
[] = { NULL
, NULL
};
496 /* TODO: take this from nickserv_conf.ldap_add_objects */
499 password_vals
[0] = (char *) password
;
500 email_vals
[0] = (char *) email
;
502 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
503 return 0; /* password required */
505 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
513 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
514 for( i
= 0; i
< num_mods
; i
++) {
515 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
516 memset(mods
[i
], 0, sizeof(LDAPMod
));
520 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
522 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
523 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
524 mods
[i
]->mod_values
= password_vals
;
528 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
529 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
530 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
531 mods
[i
]->mod_values
= email_vals
;
535 *num_mods_ret
= num_mods
;
539 /* Save OpServ level to LDAP
541 * level - OpServ level
543 * A level of <0 will be treated as 0
545 int ldap_do_oslevel(const char *account
, int level
, int oldlevel
)
548 static char *oslevel_vals
[] = { NULL
, NULL
};
549 char dn
[MAXLEN
], temp
[MAXLEN
];
552 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
553 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
561 snprintf(temp
, MAXLEN
-1, "%d", (level
? level
: oldlevel
));
562 oslevel_vals
[0] = (char *) temp
;
564 if(!(nickserv_conf
.ldap_field_oslevel
&& *nickserv_conf
.ldap_field_oslevel
))
567 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
569 mods
= ( LDAPMod
** ) malloc(( 1 ) * sizeof( LDAPMod
* ));
570 mods
[0] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
571 memset(mods
[0], 0, sizeof(LDAPMod
));
573 mods
[0]->mod_op
= (level
? LDAP_MOD_REPLACE
: LDAP_MOD_DELETE
);
574 mods
[0]->mod_type
= strdup(nickserv_conf
.ldap_field_oslevel
);
575 mods
[0]->mod_values
= oslevel_vals
;
578 rc
= ldap_modify_s(ld
, dn
, mods
);
579 if(rc
!= LDAP_SUCCESS
) {
580 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap OpServ level: %s -- %s", account
, ldap_err2string(rc
));
583 free(mods
[0]->mod_type
);
590 /* Save email or password to server
592 * password - UNENCRYPTED password. This function encrypts if libs are available
593 * email - email address
595 * NULL to make no change
597 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
603 char *passbuf
= NULL
;
605 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
606 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
611 passbuf
= make_password(password
);
614 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
615 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
617 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_do_modify");
620 rc
= ldap_modify_s(ld
, dn
, mods
);
621 if(rc
!= LDAP_SUCCESS
) {
622 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", account
, ldap_err2string(rc
));
625 for(i
= 0; i
< num_mods
; i
++) {
626 free(mods
[i
]->mod_type
);
635 LDAPMod
**make_mods_group(const char *account
, int operation
, int *num_mods_ret
)
637 static char *uid_vals
[] = { NULL
, NULL
};
640 /* TODO: take this from nickserv_conf.ldap_add_objects */
643 uid_vals
[0] = (char *) account
;
645 if(!(nickserv_conf
.ldap_field_group_member
&& *nickserv_conf
.ldap_field_group_member
))
646 return 0; /* password required */
648 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
649 for( i
= 0; i
< num_mods
; i
++) {
650 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
651 memset(mods
[i
], 0, sizeof(LDAPMod
));
655 mods
[i
]->mod_op
= operation
;
656 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_group_member
);
657 mods
[i
]->mod_values
= uid_vals
;
660 *num_mods_ret
= num_mods
;
665 int ldap_add2group(char *account
, const char *group
)
671 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
672 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
675 mods
= make_mods_group(account
, LDAP_MOD_ADD
, &num_mods
);
677 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for add2group");
680 rc
= ldap_modify_s(ld
, group
, mods
);
681 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_TYPE_OR_VALUE_EXISTS
) {
682 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding %s to group %s: %s", account
, group
, ldap_err2string(rc
));
685 for(i
= 0; i
< num_mods
; i
++) {
686 free(mods
[i
]->mod_type
);
693 int ldap_delfromgroup(char *account
, const char *group
)
699 if(!admin_bind
&& LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
700 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
703 mods
= make_mods_group(account
, LDAP_MOD_DELETE
, &num_mods
);
705 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for delfromgroup");
708 rc
= ldap_modify_s(ld
, group
, mods
);
709 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_NO_SUCH_ATTRIBUTE
) {
710 log_module(MAIN_LOG
, LOG_ERROR
, "Error removing %s from group %s: %s", account
, group
, ldap_err2string(rc
));
713 for(i
= 0; i
< num_mods
; i
++) {
714 free(mods
[i
]->mod_type
);