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.
41 extern struct nickserv_config nickserv_conf
;
48 if(!nickserv_conf
.ldap_enable
)
50 /* TODO: check here for all required config options and exit() out if not present */
51 ld
= ldap_init(nickserv_conf
.ldap_host
, nickserv_conf
.ldap_port
);
53 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP initilization failed!\n");
56 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &nickserv_conf
.ldap_version
);
57 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
);
62 /* Try to auth someone. If theres problems, try reconnecting
63 * once every 10 seconds for 1 minute.
64 * TODO: move this stuff to config file
66 unsigned int ldap_do_bind( const char *dn
, const char *pass
)
72 q
= ldap_simple_bind_s(ld
, dn
, pass
);
73 if(q
== LDAP_SUCCESS
) {
74 log_module(MAIN_LOG
, LOG_DEBUG
, "bind() successfull! You are bound as %s", dn
);
78 else if(q
== LDAP_INVALID_CREDENTIALS
) {
82 log_module(MAIN_LOG
, LOG_ERROR
, "Bind failed: %s/****** (%d)", dn
, q
);
83 ldap_perror(ld
, "ldap");
87 /* TODO: return to the user that this is a connection error and not a problem
90 log_module(MAIN_LOG
, LOG_ERROR
, "Failing to reconnect to ldap server. Auth failing.");
94 log_module(MAIN_LOG
, LOG_ERROR
, "ldap_do_bind falling off the end. this shouldnt happen");
97 int ldap_do_admin_bind()
99 if(!(nickserv_conf
.ldap_admin_dn
&& *nickserv_conf
.ldap_admin_dn
&&
100 nickserv_conf
.ldap_admin_pass
&& *nickserv_conf
.ldap_admin_pass
)) {
101 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
102 return LDAP_OTHER
; /* not configured to do this */
104 return(ldap_do_bind(nickserv_conf
.ldap_admin_dn
, nickserv_conf
.ldap_admin_pass
));
108 unsigned int ldap_check_auth( char *account
, char *pass
)
112 if(!nickserv_conf
.ldap_enable
)
115 memset(buff
, 0, MAXLEN
);
116 snprintf(buff
, sizeof(buff
)-1, nickserv_conf
.ldap_dn_fmt
/*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account
);
117 return ldap_do_bind(buff
, pass
);
121 int ldap_search_user(char *account
, LDAPMessage
**entry
)
124 char filter
[MAXLEN
+1];
128 struct timeval timeout
;
130 memset(filter
, 0, MAXLEN
+1);
131 snprintf(filter
, MAXLEN
, "%s=%s", nickserv_conf
.ldap_field_account
, account
);
137 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
138 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
141 if( (rc
= ldap_search_st(ld
, nickserv_conf
.ldap_base
, LDAP_SCOPE_ONELEVEL
, filter
, NULL
, 0, &timeout
, &res
)) != LDAP_SUCCESS
) {
142 log_module(MAIN_LOG
, LOG_ERROR
, "search failed: %s %s: %s", nickserv_conf
.ldap_base
, filter
, ldap_err2string(rc
));
145 log_module(MAIN_LOG
, LOG_DEBUG
, "Search successfull! %s %s\n", nickserv_conf
.ldap_base
, filter
);
146 if(ldap_count_entries(ld
, res
) != 1) {
147 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP search got %d entries when looking for %s", ldap_count_entries(ld
, res
), account
);
148 return(LDAP_OTHER
); /* Search was a success, but user not found.. */
150 log_module(MAIN_LOG
, LOG_DEBUG
, "LDAP search got %d entries", ldap_count_entries(ld
, res
));
151 *entry
= ldap_first_entry(ld
, res
);
155 /* queries the ldap server for account..
156 * if a single account match is found,
157 * email is allocated and set to the email address
158 * and returns LDAP_SUCCESS. returns LDAP_OTHER if
159 * 0 or 2+ entries are matched, or the proper ldap error
160 * code for other errors.
162 int ldap_get_user_info(char *account
, char **email
)
166 LDAPMessage
*entry
, *res
;
168 if( (rc
= ldap_search_user(account
, &res
)) == LDAP_SUCCESS
) {
169 entry
= ldap_first_entry(ld
, res
);
170 value
= ldap_get_values(ld
, entry
, nickserv_conf
.ldap_field_email
);
174 *email
= strdup(value
[0]);
175 log_module(MAIN_LOG
, LOG_DEBUG
, "%s: %s\n", nickserv_conf
.ldap_field_email
, value
[0]);
177 value = ldap_get_values(ld, entry, "description");
178 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
179 value = ldap_get_values(ld, entry, "userPassword");
180 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
189 ldap_first_attribute();
192 ldap_next_attribute();
199 /* get errors with ldap_err2string(); */
202 /********* base64 stuff ***********/
204 unsigned char *pack(char *str
, unsigned int *len
)
209 static unsigned char buf
[MAXLEN
+1];
212 memset(buf
, 0, MAXLEN
+1);
217 if((n
>= '0') && (n
<= '9')) {
219 } else if ((n
>= 'A') && (n
<= 'F')) {
221 } else if ((n
>= 'a') && (n
<= 'f')) {
224 printf("pack type H: illegal hex digit %c", n
);
229 buf
[++outputpos
] = 0;
234 buf
[outputpos
] |= (n
<< nibbleshift
);
235 nibbleshift
= (nibbleshift
+ 4) & 7;
242 /* from php5 sources */
243 static char base64_table
[] =
244 { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
245 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
246 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
247 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
248 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
250 static char base64_pad
= '=';
252 char *base64_encode(const unsigned char *str
, int length
, int *ret_length
)
254 const unsigned char *current
= str
;
258 if ((length
+ 2) < 0 || ((length
+ 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
259 if (ret_length
!= NULL
) {
265 result
= (char *)calloc(((length
+ 2) / 3) * 4, sizeof(char));
268 while (length
> 2) { /* keep going until we have less than 24 bits */
269 *p
++ = base64_table
[current
[0] >> 2];
270 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
271 *p
++ = base64_table
[((current
[1] & 0x0f) << 2) + (current
[2] >> 6)];
272 *p
++ = base64_table
[current
[2] & 0x3f];
275 length
-= 3; /* we just handle 3 octets of data */
278 /* now deal with the tail end of things */
280 *p
++ = base64_table
[current
[0] >> 2];
282 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
283 *p
++ = base64_table
[(current
[1] & 0x0f) << 2];
286 *p
++ = base64_table
[(current
[0] & 0x03) << 4];
291 if (ret_length
!= NULL
) {
292 *ret_length
= (int)(p
- result
);
299 char **make_object_vals()
302 static char **object_vals
= NULL
;
307 object_vals
= malloc(sizeof( *object_vals
) * nickserv_conf
.ldap_object_classes
->used
);
309 for(y
= 0; y
< nickserv_conf
.ldap_object_classes
->used
; y
++) {
310 object_vals
[y
] = nickserv_conf
.ldap_object_classes
->list
[y
];
312 object_vals
[y
] = NULL
;
316 char *make_password(const char *password
)
319 char crypted
[MD5_CRYPT_LENGTH
+1];
320 unsigned char *packed
;
323 cryptpass(password
, crypted
);
325 packed
= pack(crypted
, &len
);
326 base64pass
= base64_encode(packed
, len
, NULL
);
327 passbuf
= malloc(strlen(base64pass
) + 1 + 5);
328 strcpy(passbuf
, "{MD5}");
329 strcat(passbuf
, base64pass
);
330 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
336 LDAPMod
**make_mods_add(const char *account
, const char *password
, const char *email
, int *num_mods_ret
)
338 static char *account_vals
[] = { NULL
, NULL
};
339 static char *password_vals
[] = { NULL
, NULL
};
340 static char *email_vals
[] = { NULL
, NULL
};
343 /* TODO: take this from nickserv_conf.ldap_add_objects */
345 static char **object_vals
;
346 object_vals
= make_object_vals();
348 account_vals
[0] = (char *) account
;
349 password_vals
[0] = (char *) password
;
350 email_vals
[0] = (char *) email
;
352 if(!(nickserv_conf
.ldap_field_account
&& *nickserv_conf
.ldap_field_account
))
353 return 0; /* account required */
354 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
355 return 0; /* password required */
356 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
359 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
360 for( i
= 0; i
< num_mods
; i
++) {
361 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
362 memset(mods
[i
], 0, sizeof(LDAPMod
));
365 mods
[0]->mod_op
= LDAP_MOD_ADD
;
366 mods
[0]->mod_type
= strdup("objectclass");
367 mods
[0]->mod_values
= object_vals
;
369 mods
[1]->mod_op
= LDAP_MOD_ADD
;
370 mods
[1]->mod_type
= strdup(nickserv_conf
.ldap_field_account
);
371 mods
[1]->mod_values
= account_vals
;
373 mods
[2]->mod_op
= LDAP_MOD_ADD
;
374 mods
[2]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
375 mods
[2]->mod_values
= password_vals
;
377 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
&& *email
) {
378 mods
[3]->mod_op
= LDAP_MOD_ADD
;
379 mods
[3]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
380 mods
[3]->mod_values
= email_vals
;
385 *num_mods_ret
= num_mods
;
389 int ldap_do_add(const char *account
, const char *password
, const char *email
)
397 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
398 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
402 passbuf
= make_password(password
);
403 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
404 mods
= make_mods_add(account
, password
, email
, &num_mods
);
406 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
409 rc
= ldap_add_ext_s(ld
, newdn
, mods
, NULL
, NULL
);
410 if(rc
!= LDAP_SUCCESS
) {
411 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
415 for(i
= 0; i
< num_mods
; i
++) {
416 free(mods
[i
]->mod_type
);
424 int ldap_delete_account(char *account
)
427 memset(dn
, 0, MAXLEN
);
428 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
429 return(ldap_delete_s(ld
, dn
));
432 int ldap_rename_account(char *oldaccount
, char *newaccount
)
434 char dn
[MAXLEN
], newdn
[MAXLEN
];
437 if(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 memset(newdn
, 0, MAXLEN
);
444 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
445 strcat(newdn
, nickserv_conf
.ldap_field_account
);
447 strcat(newdn
, newaccount
);
448 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
449 if(rc
!= LDAP_SUCCESS
) {
450 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
457 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
459 static char *password_vals
[] = { NULL
, NULL
};
460 static char *email_vals
[] = { NULL
, NULL
};
463 /* TODO: take this from nickserv_conf.ldap_add_objects */
466 password_vals
[0] = (char *) password
;
467 email_vals
[0] = (char *) email
;
469 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
470 return 0; /* password required */
471 if(email
&& *email
&& nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
)
474 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
475 for( i
= 0; i
< num_mods
; i
++) {
476 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
477 memset(mods
[i
], 0, sizeof(LDAPMod
));
481 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
483 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
484 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
485 mods
[i
]->mod_values
= password_vals
;
489 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
490 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
491 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
492 mods
[i
]->mod_values
= email_vals
;
496 *num_mods_ret
= num_mods
;
501 /* Save email or password to server
503 * password - UNENCRYPTED password. This function encrypts if libs are available
504 * email - email address
506 * NULL to make no change
508 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
514 char *passbuf
= NULL
;
516 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
517 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
522 passbuf
= make_password(password
);
525 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
526 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
528 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
531 rc
= ldap_modify_s(ld
, dn
, mods
);
532 if(rc
!= LDAP_SUCCESS
) {
533 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
536 for(i
= 0; i
< num_mods
; i
++) {
537 free(mods
[i
]->mod_type
);