2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 \brief Handling the LDAP directory. Implementation
27 #include "../util/util.h"
29 #define log_warn log_debug
30 #define log_error log_debug
36 /*! \brief Lifetime of an ephemeral connection, in seconds
37 The recall connections thread period.
39 #define V2L_CONN_LIFETIME 30
41 /*! How often thread checks for LDAP results, in seconds */
42 #define V2L_POLL_INTERVAL 1
44 #define LOG_ERROR_MEM log_error(ZONE, "Unable to allocate memory")
46 /*! Global hashtable of all currently active LDAP connections */
47 static xht V2L_CONN_LIST
= NULL
;
49 /*! \brief Creates new LDAP connection
50 \param host LDAP hostname.
51 \param port LDAP port.
52 \param binddn Bind DN.
53 \param user User name.
54 \param passwd User password.
55 \param expire Boolean, ephemeral connection?
56 \return LDAP connectior or NULL if error.
58 static v2l_LdapConn
*_v2l_create_conn (char *host
, int port
, const char *binddn
,
59 const char *user
, const char *passwd
, int expire
);
61 /*! \brief Gets user password
62 Retrieves user password from LDAP directory using master connection.
63 \note Allocated memory must be freed by the caller.
64 \param self Module config.
65 \param user User name.
66 \pre self is valid, user is not NULL
67 \return user password string or NULL if doesn't exist or error.
69 static char *_v2l_ldap_get_passwd (v2l_Config
*self
, const char *user
);
71 /*! \brief Closes and frees a connection.
72 Utility function, common code for two hash walker functions.
73 \param h Jabberd hash. Hash table "username"->connection.
74 \param user User name.
75 \param val deference pointer to connection.
77 \sa _v2l_free_expired_walker
79 static void _v2l_free_conn (xht h
, const char *user
, void *val
);
81 /*! \brief Gets the number or attributes in a LDAP request.
82 \param[in,out] req The SLL of LDAP requests.
83 \return number of attributes, 0 if req is NULL.
85 static int _v2l_count_attrs (v2l_LdapRequest
*req
);
87 /*! \brief Modifies an entry in the LDAP directory
89 \param dn Entry bind DN
90 \param attrs List of attributes changed.
91 \param[in,out] evt_res Control and info parameter.
92 \return LDAP error code.
94 static int _v2l_ldap_modify (char *dn
, LDAPMod
**attrs
, v2l_LdapEvt
*evt_res
);
96 /*! \brief Searchs and retrieves an entry from the LDAP directory
98 \param dn Entry bind DN.
99 \param suffix LDAP suffix
100 \param[out] attrs Entry attributes.
101 \param subtree boolean. Search in a one level or in entire subtree?
102 \param[in,out] evt_res Control and info parameter.
103 \return LDAP error code.
105 static int _v2l_ldap_search (char *dn
, char *suffix
, char **attrs
, int subtree
,
106 v2l_LdapEvt
*evt_res
);
108 /*! \brief Waits for LDAP results
109 Operations on the directory are asynchronous. This is a sync wait function.
110 \param \param[in,out] evt_res Control and info parameter.
112 static void _v2l_ldap_sync (v2l_LdapEvt
*evt_res
);
114 /*! \brief Adds LDAP attr to request.
115 Utility function, hides OpenLDAP API to upper level.
116 \param[in,out] req The SLL of LDAP requests.
117 \param attr The attr, LDAPMod pointer.
118 \return The list of requests + the last added. The list of request if error.
120 static v2l_LdapRequest
*_v2l_add_attr (v2l_LdapRequest
*req
, LDAPMod
*attr
);
122 /*! \brief Adds a conn to the global list.
123 If list is empty, initializes it and starts the thread who recalls expired
125 \param ldap_conn New connection.
127 static void _v2l_add_conn (v2l_LdapConn
*ldap_conn
);
129 /*! \brief Frees connection unconditionally.
130 Utility function. Hash walker function.
131 See jabberd API for details.
133 static void _v2l_free_walker (xht h
, const char *key
, void *val
,
136 /*! \brief Frees connections when its slice time expired (periodic thread)
140 node [shape = ellipse, fontname = Helvetica fontsize = 12]
141 {node[style = filled, fontsize = 16, bgcolor = grey] sleep}
142 {node[shape = circle, label = delete] free}
143 {node[label = "expired?" ] is_expired}
144 {node[label = "conn left?" ] any_conn}
146 sleep -> any_conn [label = " "]
147 any_conn -> sleep [label = no]
148 any_conn -> is_expired [label = yes]
149 is_expired -> free [label = yes]
150 is_expired -> any_conn [label = no]
156 static void *_v2l_purge_conn_callback (void *arg
);
158 /*! \brief Frees expired connections.
159 Utility function. Hash walker function.
160 See jabberd API for details.
162 static void _v2l_free_expired_walker (xht h
, const char *key
, void *val
,
165 /*! A thread for active wait for LDAP results */
166 static int _v2l_ldap_wait_callback(void *arg
);
169 static void *_v2l_ldap_wait_callback_g (void *arg
);
175 v2l_get_conn (v2l_Config
*self
, const char *user
)
177 v2l_LdapConn
*user_conn
;
179 user_conn
= (v2l_LdapConn
*) xhash_get (V2L_CONN_LIST
, user
);
181 if(user_conn
== NULL
)
183 char *passwd
, *binddn
;
185 /* Get the user password for connecting him to LDAP server */
186 passwd
= _v2l_ldap_get_passwd (self
, user
);
191 log_error (ZONE
, "User \"%s\" not found in the directory", user
);
195 binddn
= (char *) malloc (sizeof(char) * (strlen (self
->suffix
) +
196 strlen (self
->uniqattr
) + strlen (user
) + 3));
205 sprintf (binddn
, "%s=%s,%s", self
->uniqattr
, user
, self
->suffix
);
207 log_debug (ZONE
, "Attempting to connect with DN: %s", binddn
);
209 user_conn
= _v2l_create_conn (self
->host
, self
->port
, binddn
, user
,
215 } /* user_conn == NULL */
221 v2l_get_master_conn (v2l_Config
*self
)
223 return _v2l_create_conn (self
->host
, self
->port
, self
->binddn
, V2L_ADMIN
,
230 xhash_walk (V2L_CONN_LIST
, _v2l_free_walker
, NULL
);
231 xhash_free (V2L_CONN_LIST
);
235 v2l_ldap_get_entry (v2l_Config
*self
, v2l_LdapConn
*curr_conn
)
237 v2l_LdapEvt
*evt_res
;
240 evt_res
= (v2l_LdapEvt
*) malloc (sizeof (v2l_LdapEvt
));
248 evt_res
->ld
= curr_conn
->ld
;
250 rc
= _v2l_ldap_search (curr_conn
->entry
, self
->suffix
, NULL
, 1, evt_res
);
252 if (rc
!= LDAP_SUCCESS
)
254 log_error (ZONE
, "LDAP error attempting to retrieve user info: %s",
255 ldap_err2string (rc
));
260 _v2l_ldap_sync(evt_res
);
262 if (ldap_count_entries (evt_res
->ld
, evt_res
->result
) != 1)
264 log_warn (ZONE
, "Multiple users with the same dn?");
273 v2l_request_record (v2l_Config
*self
, v2l_LdapConn
*curr_conn
,
274 v2l_LdapRequest
*req
)
278 v2l_LdapRequest
*cur_req
, *cur_temp
;
279 v2l_LdapEvt
*evt_res
;
283 log_warn (ZONE
, "LDAP request is NULL? I cannot record anything");
287 nbmod
= _v2l_count_attrs (req
);
289 attrs
= (LDAPMod
**) malloc ((nbmod
+ 1) * sizeof (LDAPMod
*));
297 /* to wait for the results */
298 evt_res
= (v2l_LdapEvt
*) malloc (sizeof (v2l_LdapEvt
));
307 for (i
= 0; i
< nbmod
; i
++)
309 attrs
[i
] = (LDAPMod
*) malloc (sizeof (LDAPMod
));
311 if (attrs
[i
] == NULL
)
325 for (cur_req
= req
, i
= 0; i
< nbmod
; cur_req
= cur_req
->next
, i
++)
327 memcpy (attrs
[i
], cur_req
->attr
, sizeof (LDAPMod
));
328 log_debug (ZONE
, "Element \"%s\" (%d) in the LDAP request: %s",
329 attrs
[i
]->mod_type
, i
, attrs
[i
]->mod_values
[0]);
334 log_debug (ZONE
, "LDAP attempting to modify \"%s\" with dn \"%s\"",
335 curr_conn
->user
, curr_conn
->binddn
);
337 evt_res
->ld
= curr_conn
->ld
;
338 evt_res
->rc
= _v2l_ldap_modify (curr_conn
->binddn
, attrs
, evt_res
);
339 ret
= evt_res
->rc
== LDAP_SUCCESS
;
343 log_error (ZONE
, "LDAP error attempting to modify user info: %s",
344 ldap_err2string (evt_res
->rc
));
348 _v2l_ldap_sync (evt_res
);
351 ldap_msgfree (evt_res
->result
);
354 for (cur_req
= req
, i
= 0; i
< nbmod
; i
++)
357 cur_req
= cur_req
->next
;
360 free (cur_temp
->attr
->mod_values
[0]);
361 free (cur_temp
->attr
->mod_values
);
362 free (cur_temp
->attr
);
372 v2l_add_attr_str (v2l_LdapRequest
*req
, const char *attr
, const char *str
)
376 mod
= (LDAPMod
*) malloc (sizeof (LDAPMod
));
383 mod
->mod_op
= LDAP_MOD_REPLACE
;
384 mod
->mod_type
= (char *) attr
;
386 mod
->mod_values
= (char **) malloc (2 * sizeof (char *));
388 if (mod
->mod_values
== NULL
)
394 mod
->mod_values
[0] = (char *) malloc (strlen (str
) + 1);
396 if (mod
->mod_values
[0] == NULL
)
398 free (mod
->mod_values
);
403 memcpy (mod
->mod_values
[0], str
, strlen (str
) + 1);
404 mod
->mod_values
[1] = NULL
;
406 return _v2l_add_attr (req
, mod
);
409 /* public api ends here */
411 static v2l_LdapConn
*
412 _v2l_create_conn (char *host
, int port
, const char *binddn
, const char *user
,
413 const char *passwd
, int expire
)
416 v2l_LdapConn
*ldap_conn
;
417 int version
; /* LDAP protocol version */
421 ld
= ldap_init (host
, port
);
425 log_error (ZONE
, "Unable to init LDAP");
430 version
= LDAP_VERSION3
;
433 ldap_set_option (ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
434 rc
= ldap_simple_bind_s (ld
, binddn
, passwd
);
436 if (rc
!= LDAP_SUCCESS
)
439 "LDAP simple bind error : %s", ldap_err2string (rc
));
443 poolref
= pool_new ();
452 ldap_conn
= (v2l_LdapConn
*) pmalloc (poolref
, sizeof (v2l_LdapConn
));
454 if (ldap_conn
== NULL
)
462 ldap_conn
->poolref
= poolref
;
465 ldap_conn
->binddn
= (char *) pmalloc (ldap_conn
->poolref
,
466 strlen (binddn
) + 1);
468 if (ldap_conn
->binddn
== NULL
)
476 strcpy (ldap_conn
->binddn
, binddn
);
478 ldap_conn
->entry
= (char *) pmalloc (ldap_conn
->poolref
, strlen (binddn
) + 1);
480 if (ldap_conn
->entry
== NULL
)
488 strcpy (ldap_conn
->entry
, binddn
);
489 strtok (ldap_conn
->entry
, ", ");
491 ldap_conn
->user
= (char *) pmalloc (ldap_conn
->poolref
, strlen (user
) + 1);
493 if (ldap_conn
->user
== NULL
)
501 strcpy (ldap_conn
->user
, user
);
503 ldap_conn
->creation_time
= time (NULL
); /* timestamp */
505 if (expire
== 1) /* Add it to V2L_CONN_LIST */
507 _v2l_add_conn (ldap_conn
);
514 _v2l_ldap_get_passwd(v2l_Config
*self
, const char *user
)
517 v2l_LdapEvt
*evt_res
;
518 char *data
, *filter
, **vals
, *attrs
[2] = {"userPassword", NULL
};
520 evt_res
= (v2l_LdapEvt
*) malloc(sizeof(v2l_LdapEvt
));
528 filter
= (char *) malloc (strlen (self
->uniqattr
) + strlen (user
) + 2);
537 sprintf(filter
, "%s=%s", self
->uniqattr
, user
);
538 evt_res
->ld
= self
->master_conn
->ld
;
540 evt_res
->rc
= _v2l_ldap_search (filter
, self
->suffix
, attrs
, 0, evt_res
);
543 if (evt_res
->rc
!= LDAP_SUCCESS
)
546 "LDAP error attempting to retrieve \"%s\"'s password: %s",
547 user
, ldap_err2string (evt_res
->rc
));
552 _v2l_ldap_sync (evt_res
);
555 if (ldap_count_entries(evt_res
->ld
, evt_res
->result
) == 1)
557 e
= ldap_first_entry(evt_res
->ld
, evt_res
->result
);
558 vals
= ldap_get_values(evt_res
->ld
, e
, "userPassword");
562 log_debug(ZONE
, "User has no password!");
563 data
= (char *) malloc(sizeof(char));
568 ldap_msgfree(evt_res
->result
);
577 data
= (char *) malloc(sizeof(char) * (strlen(vals
[0]) + 1));
581 strcpy(data
, vals
[0]);
584 ldap_value_free(vals
);
587 ldap_msgfree(evt_res
->result
);
595 _v2l_ldap_modify (char *dn
, LDAPMod
**attrs
, v2l_LdapEvt
*evt_res
)
597 return ldap_modify_ext (evt_res
->ld
, dn
, attrs
, NULL
, NULL
,
602 _v2l_ldap_search (char *dn
, char *suffix
, char **attrs
, int subtree
,
603 v2l_LdapEvt
*evt_res
)
607 scope
= subtree
? LDAP_SCOPE_SUBTREE
: LDAP_SCOPE_ONELEVEL
;
609 return ldap_search_ext (evt_res
->ld
, suffix
, scope
,
610 dn
, attrs
, 0, NULL
, NULL
, NULL
, LDAP_NO_LIMIT
,
615 _v2l_ldap_sync (v2l_LdapEvt
*evt_res
)
620 evt
= pth_event (PTH_EVENT_FUNC
, &_v2l_ldap_wait_callback
,
621 (void *) evt_res
, pth_time (V2L_POLL_INTERVAL
, 0));
627 rc
= pthread_create(&thr
, NULL
, _v2l_ldap_wait_callback_g
, (void *) evt_res
);
631 log_error (ZONE
, "Thread create failed: %d", rc
);
635 pthread_join(thr
, NULL
);
639 /*! Count the number of LDAPMod in the structure */
641 _v2l_count_attrs (v2l_LdapRequest
*req
)
643 v2l_LdapRequest
*ptr
;
646 for (nbmod
= 0, ptr
= req
; ptr
!= NULL
; ptr
= ptr
->next
, nbmod
++);
651 static v2l_LdapRequest
*
652 _v2l_add_attr (v2l_LdapRequest
*req
, LDAPMod
*attr
)
656 log_warn (ZONE
, "LDAP attribute is NULL? I cannot add anything");
662 req
= (v2l_LdapRequest
*) malloc (sizeof (v2l_LdapRequest
));
675 v2l_LdapRequest
*ptr
;
676 v2l_LdapRequest
*new_req
;
678 new_req
= (v2l_LdapRequest
*) malloc (sizeof (v2l_LdapRequest
));
686 new_req
->attr
= attr
;
687 new_req
->next
= NULL
;
689 for (ptr
= req
; ptr
->next
!= NULL
; ptr
= ptr
->next
);
698 _v2l_ldap_wait_callback(void *arg
)
700 v2l_LdapEvt
*evt_res
= (v2l_LdapEvt
*) arg
;
704 rc
= ldap_result (evt_res
->ld
, evt_res
->msgid
, 1, NULL
, &result
);
708 log_error(ZONE
, "LDAP result error %s",
709 ldap_err2string(rc
));
710 evt_res
->result
= NULL
;
715 if ((rc
== LDAP_RES_ADD
)
716 || (rc
== LDAP_RES_MODIFY
)
717 || (rc
== LDAP_RES_SEARCH_RESULT
)
718 || (rc
== LDAP_RES_SEARCH_ENTRY
)
719 || (rc
== LDAP_RES_DELETE
))
721 evt_res
->result
= result
;
726 return 0; /* still waiting */
731 _v2l_ldap_wait_callback_g (void *arg
)
733 while (!_v2l_ldap_wait_callback (arg
))
735 sleep (V2L_POLL_INTERVAL
);
743 _v2l_free_walker (xht h
, const char *key
, void *val
, void *arg
)
745 _v2l_free_conn (h
, key
, val
);
749 _v2l_free_conn (xht h
, const char *user
, void *val
)
751 v2l_LdapConn
*temp_conn
= (v2l_LdapConn
*) val
;
753 log_debug (ZONE
, "Freeing LDAP connection for user \"%s\"", user
);
756 ldap_unbind_s (temp_conn
->ld
);
757 pool_free (temp_conn
->poolref
);
761 _v2l_free_expired_walker (xht h
, const char *key
, void *val
, void *arg
)
763 v2l_LdapConn
*temp_conn
= (v2l_LdapConn
*) val
;
765 /* kill connections older than V2L_CONN_LIFETIME */
766 if ((time (NULL
) - temp_conn
->creation_time
) > (time_t) V2L_CONN_LIFETIME
)
768 _v2l_free_conn (h
, key
, val
);
773 _v2l_purge_conn_callback (void *arg
)
775 log_debug (ZONE
, "Checking connections lifetime");
779 /* V2L_CONN_LIST has been freed? */
780 if (V2L_CONN_LIST
!= NULL
)
782 xhash_walk (V2L_CONN_LIST
, _v2l_free_expired_walker
, NULL
);
785 pth_sleep (V2L_CONN_LIFETIME
);
787 sleep (V2L_CONN_LIFETIME
);
795 _v2l_add_conn (v2l_LdapConn
*ldap_conn
)
805 if (V2L_CONN_LIST
== NULL
)
808 log_debug (ZONE
, "V2L_CONN_LIST hashtable is not initialized yet");
810 V2L_CONN_LIST
= xhash_new (509);
812 /* spawn the thread which deletes expired connections */
814 attr
= pth_attr_new ();
815 pth_attr_set (attr
, PTH_ATTR_JOINABLE
, FALSE
);
816 pth_spawn (attr
, _v2l_purge_conn_callback
, NULL
);
817 pth_attr_destroy (attr
);
819 pthread_attr_init(&attr
);
820 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
822 rc
= pthread_create (&thr
, &attr
, _v2l_purge_conn_callback
, NULL
);
823 pthread_attr_destroy(&attr
);
827 log_error (ZONE
, "Thread create failed: %d", rc
);
832 xhash_put (V2L_CONN_LIST
, ldap_conn
->user
, (void *) ldap_conn
);
836 v2l_ldap_for_all_attrs(v2l_AttrValueFunction value_func
,
837 v2l_AttrMatchFunction match_func
, void *pointer
, v2l_LdapEvt
*evt_res
)
839 LDAPMessage
*current_result
;
841 char *current_attr
, **vals
;
844 current_result
= ldap_first_entry (evt_res
->ld
, evt_res
->result
);
845 current_attr
= ldap_first_attribute (evt_res
->ld
, current_result
, &ber
);
847 /* step through each attribute in objectclass */
849 current_attr
!= NULL
;
850 current_attr
= ldap_next_attribute (evt_res
->ld
, current_result
, ber
))
853 if (match_func (current_attr
, &shrdata
))
855 vals
= ldap_get_values (evt_res
->ld
, current_result
, current_attr
);
856 value_func (current_attr
, (const char **) vals
, pointer
, shrdata
);
860 ldap_value_free (vals
);
864 ldap_memfree (current_attr
);
865 } /* attributes loop */
872 /* don't forget to free the next attribute */
873 ldap_memfree (current_attr
);
874 ldap_msgfree (evt_res
->result
);