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);
54 if(ldap_initialize(&ld
, nickserv_conf
.ldap_uri
)) {
55 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP initilization failed!\n");
58 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &nickserv_conf
.ldap_version
);
59 log_module(MAIN_LOG
, LOG_INFO
, "Success! ldap_init() was successfull in connecting to %s\n", nickserv_conf
.ldap_uri
);
64 /* Try to auth someone. If theres problems, try reconnecting
65 * once every 10 seconds for 1 minute.
66 * TODO: move this stuff to config file
68 unsigned int ldap_do_bind( const char *dn
, const char *pass
)
74 q
= ldap_simple_bind_s(ld
, dn
, pass
);
75 if(q
== LDAP_SUCCESS
) {
76 log_module(MAIN_LOG
, LOG_DEBUG
, "bind() successfull! You are bound as %s", dn
);
80 else if(q
== LDAP_INVALID_CREDENTIALS
) {
84 log_module(MAIN_LOG
, LOG_ERROR
, "Bind failed: %s/****** (%s)", dn
, ldap_err2string(q
));
85 /* ldap_perror(ld, "ldap"); */
89 /* TODO: return to the user that this is a connection error and not a problem
92 log_module(MAIN_LOG
, LOG_ERROR
, "Failing to reconnect to ldap server. Auth failing.");
96 log_module(MAIN_LOG
, LOG_ERROR
, "ldap_do_bind falling off the end. this shouldnt happen");
99 int ldap_do_admin_bind()
101 if(!(nickserv_conf
.ldap_admin_dn
&& *nickserv_conf
.ldap_admin_dn
&&
102 nickserv_conf
.ldap_admin_pass
&& *nickserv_conf
.ldap_admin_pass
)) {
103 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
104 return LDAP_OTHER
; /* not configured to do this */
106 return(ldap_do_bind(nickserv_conf
.ldap_admin_dn
, nickserv_conf
.ldap_admin_pass
));
110 unsigned int ldap_check_auth( char *account
, char *pass
)
114 if(!nickserv_conf
.ldap_enable
)
117 memset(buff
, 0, MAXLEN
);
118 snprintf(buff
, sizeof(buff
)-1, nickserv_conf
.ldap_dn_fmt
/*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account
);
119 return ldap_do_bind(buff
, pass
);
123 int ldap_search_user(char *account
, LDAPMessage
**entry
)
126 char filter
[MAXLEN
+1];
130 struct timeval timeout
;
132 memset(filter
, 0, MAXLEN
+1);
133 snprintf(filter
, MAXLEN
, "%s=%s", nickserv_conf
.ldap_field_account
, account
);
138 timeout
.tv_sec
= nickserv_conf
.ldap_timeout
;
139 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
140 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
143 if( (rc
= ldap_search_st(ld
, nickserv_conf
.ldap_base
, LDAP_SCOPE_ONELEVEL
, filter
, NULL
, 0, &timeout
, &res
)) != LDAP_SUCCESS
) {
144 log_module(MAIN_LOG
, LOG_ERROR
, "search failed: %s %s: %s", nickserv_conf
.ldap_base
, filter
, ldap_err2string(rc
));
147 log_module(MAIN_LOG
, LOG_DEBUG
, "Search successfull! %s %s\n", nickserv_conf
.ldap_base
, filter
);
148 if(ldap_count_entries(ld
, res
) != 1) {
149 log_module(MAIN_LOG
, LOG_ERROR
, "LDAP search got %d entries when looking for %s", ldap_count_entries(ld
, res
), account
);
150 return(LDAP_OTHER
); /* Search was a success, but user not found.. */
152 log_module(MAIN_LOG
, LOG_DEBUG
, "LDAP search got %d entries", ldap_count_entries(ld
, res
));
153 *entry
= ldap_first_entry(ld
, res
);
157 /* queries the ldap server for account..
158 * if a single account match is found,
159 * email is allocated and set to the email address
160 * and returns LDAP_SUCCESS. returns LDAP_OTHER if
161 * 0 or 2+ entries are matched, or the proper ldap error
162 * code for other errors.
164 int ldap_get_user_info(char *account
, char **email
)
168 LDAPMessage
*entry
, *res
;
170 if( (rc
= ldap_search_user(account
, &res
)) == LDAP_SUCCESS
) {
171 entry
= ldap_first_entry(ld
, res
);
172 value
= ldap_get_values(ld
, entry
, nickserv_conf
.ldap_field_email
);
176 *email
= strdup(value
[0]);
177 log_module(MAIN_LOG
, LOG_DEBUG
, "%s: %s\n", nickserv_conf
.ldap_field_email
, value
[0]);
179 value = ldap_get_values(ld, entry, "description");
180 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
181 value = ldap_get_values(ld, entry, "userPassword");
182 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
191 ldap_first_attribute();
194 ldap_next_attribute();
201 /* get errors with ldap_err2string(); */
204 /********* base64 stuff ***********/
206 unsigned char *pack(const char *str
, unsigned int *len
)
211 static unsigned char buf
[MAXLEN
+1];
214 memset(buf
, 0, MAXLEN
+1);
219 if((n
>= '0') && (n
<= '9')) {
221 } else if ((n
>= 'A') && (n
<= 'F')) {
223 } else if ((n
>= 'a') && (n
<= 'f')) {
226 printf("pack type H: illegal hex digit %c", n
);
231 buf
[++outputpos
] = 0;
236 buf
[outputpos
] |= (n
<< nibbleshift
);
237 nibbleshift
= (nibbleshift
+ 4) & 7;
244 /* from php5 sources */
245 static char base64_table
[] =
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 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
249 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
250 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
252 static char base64_pad
= '=';
254 char *base64_encode(const unsigned char *str
, int length
, int *ret_length
)
256 const unsigned char *current
= str
;
260 if ((length
+ 2) < 0 || ((length
+ 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
261 if (ret_length
!= NULL
) {
267 result
= (char *)calloc(((length
+ 2) / 3) * 4, sizeof(char));
270 while (length
> 2) { /* keep going until we have less than 24 bits */
271 *p
++ = base64_table
[current
[0] >> 2];
272 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
273 *p
++ = base64_table
[((current
[1] & 0x0f) << 2) + (current
[2] >> 6)];
274 *p
++ = base64_table
[current
[2] & 0x3f];
277 length
-= 3; /* we just handle 3 octets of data */
280 /* now deal with the tail end of things */
282 *p
++ = base64_table
[current
[0] >> 2];
284 *p
++ = base64_table
[((current
[0] & 0x03) << 4) + (current
[1] >> 4)];
285 *p
++ = base64_table
[(current
[1] & 0x0f) << 2];
288 *p
++ = base64_table
[(current
[0] & 0x03) << 4];
293 if (ret_length
!= NULL
) {
294 *ret_length
= (int)(p
- result
);
301 char **make_object_vals()
304 static char **object_vals
= NULL
;
309 object_vals
= malloc(sizeof( *object_vals
) * nickserv_conf
.ldap_object_classes
->used
);
311 for(y
= 0; y
< nickserv_conf
.ldap_object_classes
->used
; y
++) {
312 object_vals
[y
] = nickserv_conf
.ldap_object_classes
->list
[y
];
314 object_vals
[y
] = NULL
;
318 char *make_password(const char *crypted
)
321 unsigned char *packed
;
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 *crypted
, 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(crypted
);
403 snprintf(newdn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
404 mods
= make_mods_add(account
, passbuf
, 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
&& rc
!= LDAP_ALREADY_EXISTS
) {
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
)
429 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
430 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
434 memset(dn
, 0, MAXLEN
);
435 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
436 return(ldap_delete_s(ld
, dn
));
439 int ldap_rename_account(char *oldaccount
, char *newaccount
)
441 char dn
[MAXLEN
], newdn
[MAXLEN
];
444 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
445 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
449 memset(dn
, 0, MAXLEN
);
450 memset(newdn
, 0, MAXLEN
);
451 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, oldaccount
);
452 strcat(newdn
, nickserv_conf
.ldap_field_account
);
454 strcat(newdn
, newaccount
);
455 rc
= ldap_modrdn2_s(ld
, dn
, newdn
, true);
456 if(rc
!= LDAP_SUCCESS
) {
457 log_module(MAIN_LOG
, LOG_ERROR
, "Error modifying ldap account: %s -- %s", oldaccount
, ldap_err2string(rc
));
464 LDAPMod
**make_mods_modify(const char *password
, const char *email
, int *num_mods_ret
)
466 static char *password_vals
[] = { NULL
, NULL
};
467 static char *email_vals
[] = { NULL
, NULL
};
470 /* TODO: take this from nickserv_conf.ldap_add_objects */
473 password_vals
[0] = (char *) password
;
474 email_vals
[0] = (char *) email
;
476 if(!(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
))
477 return 0; /* password required */
479 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
487 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
488 for( i
= 0; i
< num_mods
; i
++) {
489 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
490 memset(mods
[i
], 0, sizeof(LDAPMod
));
494 if(nickserv_conf
.ldap_field_password
&& *nickserv_conf
.ldap_field_password
&&
496 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
497 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_password
);
498 mods
[i
]->mod_values
= password_vals
;
502 if(nickserv_conf
.ldap_field_email
&& *nickserv_conf
.ldap_field_email
&& email
) {
503 mods
[i
]->mod_op
= LDAP_MOD_REPLACE
;
504 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_email
);
505 mods
[i
]->mod_values
= email_vals
;
509 *num_mods_ret
= num_mods
;
514 /* Save email or password to server
516 * password - UNENCRYPTED password. This function encrypts if libs are available
517 * email - email address
519 * NULL to make no change
521 int ldap_do_modify(const char *account
, const char *password
, const char *email
)
527 char *passbuf
= NULL
;
529 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
530 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
535 passbuf
= make_password(password
);
538 snprintf(dn
, MAXLEN
-1, nickserv_conf
.ldap_dn_fmt
, account
);
539 mods
= make_mods_modify(passbuf
, email
, &num_mods
);
541 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for ldap_add");
544 rc
= ldap_modify_s(ld
, dn
, mods
);
545 if(rc
!= LDAP_SUCCESS
) {
546 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding ldap account: %s -- %s", account
, ldap_err2string(rc
));
549 for(i
= 0; i
< num_mods
; i
++) {
550 free(mods
[i
]->mod_type
);
559 LDAPMod
**make_mods_group(const char *account
, int operation
, int *num_mods_ret
)
561 static char *uid_vals
[] = { NULL
, NULL
};
564 /* TODO: take this from nickserv_conf.ldap_add_objects */
567 uid_vals
[0] = (char *) account
;
569 if(!(nickserv_conf
.ldap_field_group_member
&& *nickserv_conf
.ldap_field_group_member
))
570 return 0; /* password required */
572 mods
= ( LDAPMod
** ) malloc(( num_mods
+ 1 ) * sizeof( LDAPMod
* ));
573 for( i
= 0; i
< num_mods
; i
++) {
574 mods
[i
] = (LDAPMod
*) malloc(sizeof(LDAPMod
));
575 memset(mods
[i
], 0, sizeof(LDAPMod
));
579 mods
[i
]->mod_op
= operation
;
580 mods
[i
]->mod_type
= strdup(nickserv_conf
.ldap_field_group_member
);
581 mods
[i
]->mod_values
= uid_vals
;
584 *num_mods_ret
= num_mods
;
589 int ldap_add2group(char *account
, const char *group
)
595 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
596 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
599 mods
= make_mods_group(account
, LDAP_MOD_ADD
, &num_mods
);
601 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for add2group");
604 rc
= ldap_modify_s(ld
, group
, mods
);
605 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_ALREADY_EXISTS
) {
606 log_module(MAIN_LOG
, LOG_ERROR
, "Error adding %s to group %s: %s", account
, group
, ldap_err2string(rc
));
609 for(i
= 0; i
< num_mods
; i
++) {
610 free(mods
[i
]->mod_type
);
617 int ldap_delfromgroup(char *account
, const char *group
)
623 if(LDAP_SUCCESS
!= ( rc
= ldap_do_admin_bind())) {
624 log_module(MAIN_LOG
, LOG_ERROR
, "failed to bind as admin");
627 mods
= make_mods_group(account
, LDAP_MOD_DELETE
, &num_mods
);
629 log_module(MAIN_LOG
, LOG_ERROR
, "Error building mods for delfromgroup");
632 rc
= ldap_modify_s(ld
, group
, mods
);
633 if(rc
!= LDAP_SUCCESS
&& rc
!= LDAP_NO_SUCH_ATTRIBUTE
) {
634 log_module(MAIN_LOG
, LOG_ERROR
, "Error removing %s from group %s: %s", account
, group
, ldap_err2string(rc
));
637 for(i
= 0; i
< num_mods
; i
++) {
638 free(mods
[i
]->mod_type
);