1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_extensions.hxx"
30 
31 #include "ldapaccess.hxx"
32 
33 #include <rtl/ustrbuf.hxx>
34 #include <rtl/strbuf.hxx>
35 
36 
37 namespace extensions { namespace config { namespace ldap {
38 
39 oslModule		LdapConnection::s_Ldap_Module = NULL;
40 t_ldap_unbind_s          LdapConnection::s_p_unbind_s = NULL;
41 t_ldap_simple_bind_s	 LdapConnection::s_p_simple_bind_s = NULL;
42 t_ldap_set_option        LdapConnection::s_p_set_option = NULL;
43 t_ldap_err2string        LdapConnection::s_p_err2string = NULL;
44 t_ldap_init              LdapConnection::s_p_init = NULL;
45 t_ldap_msgfree           LdapConnection::s_p_msgfree = NULL;
46 t_ldap_get_dn            LdapConnection::s_p_get_dn = NULL;
47 t_ldap_first_entry       LdapConnection::s_p_first_entry = NULL;
48 t_ldap_first_attribute   LdapConnection::s_p_first_attribute = NULL;
49 t_ldap_next_attribute    LdapConnection::s_p_next_attribute = NULL;
50 t_ldap_search_s          LdapConnection::s_p_search_s = NULL;
51 t_ldap_value_free        LdapConnection::s_p_value_free = NULL;
52 t_ldap_get_values        LdapConnection::s_p_get_values = NULL;
53 t_ldap_memfree           LdapConnection::s_p_memfree = NULL;
54 //------------------------------------------------------------------------------
55 typedef int LdapErrCode;
56 //------------------------------------------------------------------------------
57 struct LdapMessageHolder
58 {
59     LdapMessageHolder() : msg(0) {}
60     ~LdapMessageHolder()
61     {
62         if (msg)
63             (*LdapConnection::s_p_msgfree)(msg);
64     }
65 
66     LDAPMessage * msg;
67 
68 private:
69     LdapMessageHolder(LdapMessageHolder const&);
70     void operator=(LdapMessageHolder const&);
71 };
72 //------------------------------------------------------------------------------
73 LdapConnection::~LdapConnection()
74 {
75     if (isValid()) disconnect();
76 }
77 //------------------------------------------------------------------------------
78 
79 void LdapConnection::disconnect()
80 {
81     if (mConnection != NULL)
82     {
83         (*s_p_unbind_s)(mConnection) ;
84         mConnection = NULL;
85     }
86 }
87 //------------------------------------------------------------------------------
88 
89 static void checkLdapReturnCode(const sal_Char *aOperation,
90                                 LdapErrCode aRetCode,
91                                 LDAP * /*aConnection*/)
92 {
93     if (aRetCode == LDAP_SUCCESS) { return ; }
94 
95     static const sal_Char *kNoSpecificMessage = "No additional information" ;
96     rtl::OUStringBuffer message ;
97 
98     if (aOperation != NULL)
99     {
100         message.appendAscii(aOperation).appendAscii(": ") ;
101     }
102     message.appendAscii((*LdapConnection::s_p_err2string)(aRetCode)).appendAscii(" (") ;
103     sal_Char *stub = NULL ;
104 
105 #ifndef LDAP_OPT_SIZELIMIT // for use with OpenLDAP
106     (*s_p_get_lderrno)(aConnection, NULL, &stub) ;
107 #endif
108     if (stub != NULL)
109     {
110         message.appendAscii(stub) ;
111         // It would seem the message returned is actually
112         // not a copy of a string but rather some static
113         // string itself. At any rate freeing it seems to
114         // cause some undue problems at least on Windows.
115         // This call is thus disabled for the moment.
116         //(*s_p_memfree)(stub) ;
117     }
118     else { message.appendAscii(kNoSpecificMessage) ; }
119     message.appendAscii(")") ;
120     throw ldap::LdapGenericException(message.makeStringAndClear(),
121                                      NULL, aRetCode) ;
122 }
123 //------------------------------------------------------------------------------
124 void  LdapConnection::connectSimple(const LdapDefinition& aDefinition)
125    throw (ldap::LdapConnectionException, ldap::LdapGenericException)
126 {
127     OSL_ENSURE(!isValid(), "Recoonecting an LDAP connection that is already established");
128     if (isValid()) disconnect();
129 
130     mLdapDefinition = aDefinition;
131     connectSimple();
132 }
133 //------------------------------------------------------------------------------
134 void  LdapConnection::connectSimple()
135    throw (ldap::LdapConnectionException, ldap::LdapGenericException)
136 {
137     if (!isValid())
138 	{
139 		// Connect to the server
140 		initConnection() ;
141 		// Set Protocol V3
142 		int version = LDAP_VERSION3;
143 		(*s_p_set_option)(mConnection,
144 						LDAP_OPT_PROTOCOL_VERSION,
145 						&version);
146 
147 #ifdef LDAP_X_OPT_CONNECT_TIMEOUT // OpenLDAP doesn't support this and the func
148         /* timeout is specified in milliseconds -> 4 seconds*/
149         int timeout = 4000;
150         (*s_p_set_option)( mConnection,
151                         LDAP_X_OPT_CONNECT_TIMEOUT,
152                         &timeout );
153 #endif
154 
155         // Do the bind
156 		LdapErrCode retCode = (*s_p_simple_bind_s)(mConnection,
157                                                mLdapDefinition.mAnonUser ,
158                                                mLdapDefinition.mAnonCredentials) ;
159 
160 		checkLdapReturnCode("SimpleBind", retCode, mConnection) ;
161 	}
162 }
163 //------------------------------------------------------------------------------
164 void LdapConnection::initConnection()
165     throw (ldap::LdapConnectionException)
166 {
167     if (mLdapDefinition.mServer.getLength() == 0)
168     {
169         rtl::OUStringBuffer message ;
170 
171         message.appendAscii("Cannot initialise connection to LDAP: No server specified.") ;
172         throw ldap::LdapConnectionException(message.makeStringAndClear(), NULL) ;
173     }
174 
175     if (mLdapDefinition.mPort == 0) mLdapDefinition.mPort = LDAP_PORT;
176 
177     mConnection = (*s_p_init)(mLdapDefinition.mServer,
178                             mLdapDefinition.mPort) ;
179     if (mConnection == NULL)
180     {
181         rtl::OUStringBuffer message ;
182 
183         message.appendAscii("Cannot initialise connection to LDAP server ") ;
184         message.appendAscii(mLdapDefinition.mServer) ;
185         message.appendAscii(":") ;
186         message.append(mLdapDefinition.mPort) ;
187         throw ldap::LdapConnectionException(message.makeStringAndClear(),
188 			                                NULL) ;
189     }
190 }
191 //------------------------------------------------------------------------------
192  void LdapConnection::getUserProfile(
193      const rtl::OUString& aUser, LdapData * data)
194     throw (lang::IllegalArgumentException,
195             ldap::LdapConnectionException, ldap::LdapGenericException)
196 {
197     OSL_ASSERT(data != 0);
198     if (!isValid()) { connectSimple(); }
199 
200     rtl::OString aUserDn =findUserDn( rtl::OUStringToOString(aUser, RTL_TEXTENCODING_ASCII_US));
201 
202     LdapMessageHolder result;
203     LdapErrCode retCode = (*s_p_search_s)(mConnection,
204 									  aUserDn,
205 									  LDAP_SCOPE_BASE,
206 									  "(objectclass=*)",
207 									  0,
208 									  0, // Attributes + values
209 									  &result.msg) ;
210 
211     checkLdapReturnCode("getUserProfile", retCode,mConnection) ;
212 
213     void * ptr;
214     char * attr = (*s_p_first_attribute)(mConnection, result.msg, &ptr);
215     while (attr != 0) {
216         char ** values = (*s_p_get_values)(mConnection, result.msg, attr);
217         if (values != 0) {
218             data->insert(
219                 LdapData::value_type(
220                     rtl::OStringToOUString(attr, RTL_TEXTENCODING_ASCII_US),
221                     rtl::OStringToOUString(*values, RTL_TEXTENCODING_UTF8)));
222             (*s_p_value_free)(values);
223         }
224         attr = (*s_p_next_attribute)(mConnection, result.msg, ptr);
225     }
226 }
227 //------------------------------------------------------------------------------
228  rtl::OString LdapConnection::findUserDn(const rtl::OString& aUser)
229     throw (lang::IllegalArgumentException,
230             ldap::LdapConnectionException, ldap::LdapGenericException)
231 {
232     if (!isValid()) { connectSimple(); }
233 
234     if (aUser.getLength() == 0)
235     {
236         throw lang::IllegalArgumentException(
237 			rtl::OUString(RTL_CONSTASCII_USTRINGPARAM
238 			("LdapConnection::findUserDn -User id is empty")),
239 				NULL, 0) ;
240     }
241 
242 
243 
244     rtl::OStringBuffer filter( "(&(objectclass=" );
245 
246     filter.append( mLdapDefinition.mUserObjectClass ).append(")(") ;
247     filter.append( mLdapDefinition.mUserUniqueAttr ).append("=").append(aUser).append("))") ;
248 
249     LdapMessageHolder result;
250     sal_Char * attributes [2];
251     attributes[0]= const_cast<sal_Char *>(LDAP_NO_ATTRS);
252     attributes[1]= NULL;
253     LdapErrCode retCode = (*s_p_search_s)(mConnection,
254                                       mLdapDefinition.mBaseDN,
255                                       LDAP_SCOPE_SUBTREE,
256                                       filter.makeStringAndClear(), attributes, 0, &result.msg) ;
257 
258     checkLdapReturnCode("FindUserDn", retCode,mConnection) ;
259     rtl::OString userDn ;
260     LDAPMessage *entry = (*s_p_first_entry)(mConnection, result.msg) ;
261 
262     if (entry != NULL)
263     {
264         sal_Char *charsDn = (*s_p_get_dn)(mConnection, entry) ;
265 
266         userDn = charsDn ;
267         (*s_p_memfree)(charsDn) ;
268     }
269     else
270     {
271         OSL_ENSURE( false, "LdapConnection::findUserDn-could not get DN for User ");
272     }
273 
274     return userDn ;
275 }
276 
277 extern "C" { static void SAL_CALL thisModule() {} }
278 void LdapConnection::loadModule()
279 {
280     if ( !s_Ldap_Module )
281     {
282 #if defined(WNT)
283 #       define LIBLDAP "nsldap32v50.dll"
284 #else
285 #   ifdef WITH_OPENLDAP
286 #       define xstr(s) str(s)
287 #       define str(s) #s
288 #       define LIBLDAP "libldap-" xstr(LDAP_VENDOR_VERSION_MAJOR) "." xstr(LDAP_VENDOR_VERSION_MINOR) ".so." xstr(LDAP_VENDOR_VERSION_MAJOR)
289 #   else
290 #       define LIBLDAP "libldap50.so"
291 #   endif
292 #endif
293         const ::rtl::OUString sModuleName(RTL_CONSTASCII_USTRINGPARAM(LIBLDAP));
294 
295 		// load the dbtools library
296 		s_Ldap_Module = osl_loadModuleRelative(&thisModule, sModuleName.pData, 0);
297         if ( s_Ldap_Module != NULL )
298         {
299             s_p_unbind_s = (t_ldap_unbind_s)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_unbind_s").pData));
300             s_p_simple_bind_s = (t_ldap_simple_bind_s)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_simple_bind_s").pData));
301             s_p_set_option = (t_ldap_set_option)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_set_option").pData));
302             s_p_err2string = (t_ldap_err2string)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_err2string").pData));
303             s_p_init = (t_ldap_init)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_init").pData));
304             s_p_msgfree = (t_ldap_msgfree)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_msgfree").pData));
305             s_p_get_dn = (t_ldap_get_dn)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_get_dn").pData));
306             s_p_first_entry = (t_ldap_first_entry)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_first_entry").pData));
307             s_p_first_attribute = (t_ldap_first_attribute)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_first_attribute").pData));
308             s_p_next_attribute = (t_ldap_next_attribute)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_next_attribute").pData));
309             s_p_search_s = (t_ldap_search_s)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_search_s").pData));
310             s_p_value_free = (t_ldap_value_free)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_value_free").pData));
311             s_p_get_values = (t_ldap_get_values)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_get_values").pData));
312             s_p_memfree = (t_ldap_memfree)(osl_getFunctionSymbol(s_Ldap_Module, ::rtl::OUString::createFromAscii("ldap_memfree").pData));
313         }
314     }
315 }
316 
317 //------------------------------------------------------------------------------
318 } } } // extensions.config.ldap
319 
320