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 2 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.
35 //#include <sys/select.h>
38 #include <openssl/bio.h>
39 #include <openssl/evp.h>
50 #ifdef HAVE_SYS_SELECT_H
51 #include <sys/select.h>
53 #ifdef HAVE_SYS_SOCKET_H
54 #include <sys/socket.h>
58 /* char dn[] = "uid=%s,ou=Users,dc=afternet,dc=org";
59 char password[] = "xxxxxxx";
60 char base[] = "ou=Users,dc=afternet,dc=org";
63 extern struct nickserv_config nickserv_conf
;
70 if(!nickserv_conf
.ldap_enable
)
72 /* TODO: check here for all required config options and exit() out if not present */
73 ld
= ldap_init(nickserv_conf
.ldap_host
, nickserv_conf
.ldap_port
);
75 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP initilization failed!\n");
78 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &nickserv_conf
.ldap_version
);
79 log_module(MAIN_LOG
, LOG_INFO
, "Success! ldap_init() was successfull in connecting to %s port %d\n", nickserv_conf
.ldap_host
, nickserv_conf
.ldap_port
);
84 /* Try to auth someone. If theres problems, try reconnecting
85 * once every 10 seconds for 1 minute.
86 * TODO: move this stuff to config file
88 unsigned int ldap_do_bind( const char *dn
, const char *pass
)
94 q
= ldap_simple_bind_s(ld
, dn
, pass
);
95 if(q
== LDAP_SUCCESS
) {
96 log_module(MAIN_LOG
, LOG_DEBUG
, "bind() successfull! You are bound as %s\n", dn
);
100 else if(q
== LDAP_INVALID_CREDENTIALS
) {
104 log_module(MAIN_LOG
, LOG_ERROR
, "Bind failed: %s/****** (%d)\n", dn
, q
);
105 ldap_perror(ld
, "ldap");
109 /* TODO: return to the user that this is a connection error and not a problem
110 * with their password
112 log_module(MAIN_LOG
, LOG_ERROR
, "Failing to reconnect to ldap server. Auth failing.");
116 log_module(MAIN_LOG
, LOG_ERROR
, "ldap_do_bind falling off the end. this shouldnt happen");
120 unsigned int ldap_check_auth( char *account
, char *pass
)
124 if(!nickserv_conf
.ldap_enable
)
127 memset(buff
, 0, MAXLEN
);
128 snprintf(buff
, sizeof(buff
)-1, nickserv_conf
.ldap_dn_fmt
/*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account
);
129 return ldap_do_bind(buff
, pass
);
133 #ifdef notdef /* not used yet - will be used to pull email etc out of ldap */
134 LDAPMessage
ldap_search_user(char uid
)
137 char filter
[] = "cn=admin";
139 struct timeval timeout
;
145 if( ldap_search_st(ld
, base
, LDAP_SCOPE_ONELEVEL
, filter
, NULL
, 0, &timeout
, &res
) != LDAP_SUCCESS
) {
146 log_module(MAIN_LOG
, LOG_ERROR
, "search failed: %s %s\n", base
, filter
);
149 log_module(MAIN_LOG
, LOG_DEBUG
, "Search successfull! %s %s\n", base
, filter
);
150 log_module(MAIN_LOG
, LOG_DEBUG
, "Got %d entries\n", ldap_count_entries(ld
, res
));
154 entry
= ldap_first_entry(ld
, res
);
155 value
= ldap_get_values(ld
, entry
, "cn");
156 log_module(MAIN_LOG
, LOG_DEBUG
, "cn: %s\n", value
[0]);
157 value
= ldap_get_values(ld
, entry
, "description");
158 log_module(MAIN_LOG
, LOG_DEBUG
, "Description: %s\n", value
[0]);
159 value
= ldap_get_values(ld
, entry
, "userPassword");
160 log_module(MAIN_LOG
, LOG_DEBUG
, "pass: %s\n", value
? value
[0] : "error");
165 ldap_first_attribute();
168 ldap_next_attribute();
176 /* get errors with ldap_err2string(); */
181 char **make_object_vals()
184 static char **object_vals
= NULL
;
189 object_vals
= malloc(sizeof( *object_vals
) * nickserv_conf
.ldap_object_classes
->used
);
191 for(y
= 0; y
< nickserv_conf
.ldap_object_classes
->used
; y
++) {
192 object_vals
[y
] = nickserv_conf
.ldap_object_classes
->list
[y
];
194 object_vals
[y
] = NULL
;
198 LDAPMod
**make_mods_add(const char *account
, const char *password
, const char *email
, int *num_mods_ret
)
200 static char *account_vals
[] = { NULL
, NULL
};
201 static char *password_vals
[] = { NULL
, NULL
};
202 static char *email_vals
[] = { NULL
, NULL
};
205 /* TODO: take this from nickserv_conf.ldap_add_objects */
207 static char **object_vals
;
208 object_vals
= make_object_vals();
210 account_vals
[0] = (char *) account
;
211 password_vals
[0] = (char *) password
;
212 email_vals
[0] = (char *) email
;
214 if(!(nickserv_conf
.ldap_field_account
&& *nickserv_conf
.ldap_field_account
))
215 return 0; /* account required */
216 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
217 return 0; /* password required */
218 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
221 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
222 for( i
= 0; i
< num_mods
; i
++) {
223 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
224 memset(mods
[i
], 0, sizeof(LDAPMod
));
227 mods
[0]->mod_op
= LDAP_MOD_ADD
;
228 mods
[0]->mod_type
= strdup("objectclass");
229 mods
[0]->mod_values
= object_vals
;
231 mods
[1]->mod_op
= LDAP_MOD_ADD
;
232 mods
[1]->mod_type
= strdup(nickserv_conf
.ldap_field_account
);
233 mods
[1]->mod_values
= account_vals
;
235 mods
[2]->mod_op
= LDAP_MOD_ADD
;
236 mods
[2]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
237 mods
[2]->mod_values
= password_vals
;
239 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
&& *email
) {
240 mods
[3]->mod_op
= LDAP_MOD_ADD
;
241 mods
[3]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
242 mods
[3]->mod_values
= email_vals
;
247 *num_mods_ret
= num_mods
;
251 int ldap_do_admin_bind()
253 if(!(nickserv_conf
.ldap_admin_dn
&& *nickserv_conf
.ldap_admin_dn
&&
254 nickserv_conf
.ldap_admin_pass
&& *nickserv_conf
.ldap_admin_pass
)) {
255 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
256 return LDAP_OTHER
; /* not configured to do this */
258 return(ldap_do_bind(nickserv_conf
.ldap_admin_dn
, nickserv_conf
.ldap_admin_pass
));
261 int ldap_do_add(const char *account
, const char *password
, const char *email
)
268 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
269 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
273 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
274 mods
= make_mods_add(account
, password
, email
, &num_mods
);
276 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
279 rc
= ldap_add_ext_s(ld
, newdn
, mods
, NULL
, NULL
);
280 if(rc
!= LDAP_SUCCESS
) {
281 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
285 for(i
= 0; i
< num_mods
; i
++) {
286 free(mods
[i
]->mod_type
);
293 int ldap_delete_account(char *account
)
296 memset(dn
, 0, MAXLEN
);
297 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
298 return(ldap_delete_s(ld
, dn
));
301 int ldap_rename_account(char *oldaccount
, char *newaccount
)
303 char dn
[MAXLEN
], newdn
[MAXLEN
];
306 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
307 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
311 memset(dn
, 0, MAXLEN
);
312 memset(newdn
, 0, MAXLEN
);
313 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
314 strcat(newdn
, nickserv_conf
.ldap_field_account
);
316 strcat(newdn
, newaccount
);
317 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
318 if(rc
!= LDAP_SUCCESS
) {
319 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
326 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
328 static char *password_vals
[] = { NULL
, NULL
};
329 static char *email_vals
[] = { NULL
, NULL
};
332 /* TODO: take this from nickserv_conf.ldap_add_objects */
335 password_vals
[0] = (char *) password
;
336 email_vals
[0] = (char *) email
;
338 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
339 return 0; /* password required */
340 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
343 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
344 for( i
= 0; i
< num_mods
; i
++) {
345 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
346 memset(mods
[i
], 0, sizeof(LDAPMod
));
350 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
352 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
353 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
354 mods
[i
]->mod_values
= password_vals
;
358 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
359 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
360 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
361 mods
[i
]->mod_values
= email_vals
;
365 *num_mods_ret
= num_mods
;
369 /********* base64 stuff ***********/
371 unsigned char *pack(char *str
, unsigned int *len
)
376 static unsigned char buf
[MAXLEN
+1];
379 memset(buf
, 0, MAXLEN
+1);
384 if((n
>= '0') && (n
<= '9')) {
386 } else if ((n
>= 'A') && (n
<= 'F')) {
388 } else if ((n
>= 'a') && (n
<= 'f')) {
391 printf("pack type H: illegal hex digit %c", n
);
396 buf
[++outputpos
] = 0;
401 buf
[outputpos
] |= (n
<< nibbleshift
);
402 nibbleshift
= (nibbleshift
+ 4) & 7;
409 /* from php5 sources */
410 static char base64_table
[] =
411 { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
412 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
413 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
414 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
415 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
417 static char base64_pad
= '=';
419 char *base64_encode(const unsigned char *str
, int length
, int *ret_length
)
421 const unsigned char *current
= str
;
425 if ((length
+ 2) < 0 || ((length
+ 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
426 if (ret_length
!= NULL
) {
432 result
= (char *)calloc(((length
+ 2) / 3) * 4, sizeof(char));
435 while (length
> 2) { /* keep going until we have less than 24 bits */
436 *p
++ = base64_table
[current
[0] >> 2];
437 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
438 *p
++ = base64_table
[((current
[1] & 0x0f) << 2) + (current
[2] >> 6)];
439 *p
++ = base64_table
[current
[2] & 0x3f];
442 length
-= 3; /* we just handle 3 octets of data */
445 /* now deal with the tail end of things */
447 *p
++ = base64_table
[current
[0] >> 2];
449 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
450 *p
++ = base64_table
[(current
[1] & 0x0f) << 2];
453 *p
++ = base64_table
[(current
[0] & 0x03) << 4];
458 if (ret_length
!= NULL
) {
459 *ret_length
= (int)(p
- result
);
466 /* Save email or password to server
468 * password - UNENCRYPTED password. This function encrypts if libs are available
469 * email - email address
471 * NULL to make no change
473 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
479 char *passbuf
= NULL
;
481 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
482 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
487 /* If we have the ssl lib, (and thus the base64 libraries) save the passwords as ldap md5 */
489 char crypted
[MD5_CRYPT_LENGTH
+1];
490 unsigned char *packed
;
493 cryptpass(password
, crypted
);
495 // printf("Crypted pass is: '%s'\n", crypted);
496 packed
= pack(crypted
, &len
);
497 base64pass
= base64_encode(packed
, len
, NULL
);
498 // printf("base64pass is: '%s'\n", base64pass);
499 // for(i = 0; i<=len; i++) {
501 // printf("%d ", packed[i]);
504 passbuf
= malloc(strlen(base64pass
) + 1 + 5);
505 strcpy(passbuf
, "{MD5}");
506 strcat(passbuf
, base64pass
);
507 log_module(MAIN_LOG
, LOG_DEBUG
, "Encoded password is: '%s'", passbuf
);
511 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
512 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
514 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
517 rc
= ldap_modify_s(ld
, dn
, mods
);
518 if(rc
!= LDAP_SUCCESS
) {
519 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
522 for(i
= 0; i
< num_mods
; i
++) {
523 free(mods
[i
]->mod_type
);