1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * The contents of this file are subject to the Netscape Public
8  * License Version 1.1 (the "License"); you may not use this file
9  * except in compliance with the License. You may obtain a copy of
10  * the License at http://www.mozilla.org/NPL/
11  *
12  * Software distributed under the License is distributed on an "AS
13  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14  * implied. See the License for the specific language governing
15  * rights and limitations under the License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is Netscape
21  * Communications Corporation. Portions created by Netscape are
22  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23  * Rights Reserved.
24  *
25  * Contributor(s):
26  */
27 
28 /*
29  * DNS callback functions for libldap that use the NSPR (Netscape
30  * Portable Runtime) thread API.
31  *
32  */
33 
34 #ifdef _SOLARIS_SDK
35 #include "solaris-int.h"
36 #include <libintl.h>
37 #include <syslog.h>
38 #include <nsswitch.h>
39 #include <synch.h>
40 #include <nss_dbdefs.h>
41 #include <netinet/in.h>
42 static char *host_service = NULL;
43 static DEFINE_NSS_DB_ROOT(db_root_hosts);
44 #endif
45 
46 #include "ldappr-int.h"
47 
48 static LDAPHostEnt *prldap_gethostbyname( const char *name,
49 	LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
50 	void *extradata );
51 static LDAPHostEnt *prldap_gethostbyaddr( const char *addr, int length,
52 	int type, LDAPHostEnt *result, char *buffer, int buflen,
53 	int *statusp, void *extradata );
54 static int prldap_getpeername( LDAP *ld, struct sockaddr *addr,
55 	char *buffer, int buflen );
56 static LDAPHostEnt *prldap_convert_hostent( LDAPHostEnt *ldhp,
57 	PRHostEnt *prhp );
58 
59 #ifdef _SOLARIS_SDK
60 static LDAPHostEnt *
61 prldap_gethostbyname1(const char *name, LDAPHostEnt *result,
62 	char *buffer, int buflen, int *statusp, void *extradata);
63 extern int
64 str2hostent(const char *instr, int lenstr, void *ent, char *buffer,
65 	int buflen);
66 #endif /* _SOLARIS_SDK */
67 
68 
69 /*
70  * Install NSPR DNS functions into ld (if ld is NULL, they are installed
71  * as the default functions for new LDAP * handles).
72  *
73  * Returns 0 if all goes well and -1 if not.
74  */
75 int
prldap_install_dns_functions(LDAP * ld)76 prldap_install_dns_functions( LDAP *ld )
77 {
78     struct ldap_dns_fns			dnsfns;
79 
80     memset( &dnsfns, '\0', sizeof(struct ldap_dns_fns) );
81     dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE;
82     dnsfns.lddnsfn_gethostbyname = prldap_gethostbyname;
83     dnsfns.lddnsfn_gethostbyaddr = prldap_gethostbyaddr;
84 	    dnsfns.lddnsfn_getpeername = prldap_getpeername;
85 	    if ( ldap_set_option( ld, LDAP_OPT_DNS_FN_PTRS, (void *)&dnsfns ) != 0 ) {
86 		return( -1 );
87 	    }
88 
89     return( 0 );
90 }
91 
92 
93 static LDAPHostEnt *
prldap_gethostbyname(const char * name,LDAPHostEnt * result,char * buffer,int buflen,int * statusp,void * extradata)94 prldap_gethostbyname( const char *name, LDAPHostEnt *result,
95 	char *buffer, int buflen, int *statusp, void *extradata )
96 {
97 	PRHostEnt	prhent;
98 
99 	if( !statusp || ( *statusp = (int)PR_GetIPNodeByName( name,
100 		PRLDAP_DEFAULT_ADDRESS_FAMILY, PR_AI_DEFAULT,
101 		buffer, buflen, &prhent )) == PR_FAILURE ) {
102 		return( NULL );
103 	}
104 
105 	return( prldap_convert_hostent( result, &prhent ));
106 }
107 
108 
109 static LDAPHostEnt *
prldap_gethostbyaddr(const char * addr,int length,int type,LDAPHostEnt * result,char * buffer,int buflen,int * statusp,void * extradata)110 prldap_gethostbyaddr( const char *addr, int length, int type,
111 	LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
112 	void *extradata )
113 {
114     PRHostEnt	prhent;
115     PRNetAddr	iaddr;
116 
117 	if ( PR_SetNetAddr(PR_IpAddrNull, PRLDAP_DEFAULT_ADDRESS_FAMILY,
118 		0, &iaddr) == PR_FAILURE
119  		|| PR_StringToNetAddr( addr, &iaddr ) == PR_FAILURE ) {
120 		return( NULL );
121 	}
122 
123     if( !statusp || (*statusp = PR_GetHostByAddr(&iaddr, buffer,
124 	     buflen, &prhent )) == PR_FAILURE ) {
125 	return( NULL );
126     }
127     return( prldap_convert_hostent( result, &prhent ));
128 }
129 
130 static int
prldap_getpeername(LDAP * ld,struct sockaddr * addr,char * buffer,int buflen)131 prldap_getpeername( LDAP *ld, struct sockaddr *addr, char *buffer, int buflen)
132 {
133     PRLDAPIOSocketArg *sa;
134     PRFileDesc	*fd;
135     PRNetAddr	iaddr;
136     int		ret;
137 
138     if (NULL != ld) {
139 	    ret = prldap_socket_arg_from_ld( ld, &sa );
140 	    if (ret != LDAP_SUCCESS) {
141 		return (-1);
142 	    }
143 	    ret = PR_GetPeerName(sa->prsock_prfd, &iaddr);
144 	    if( ret == PR_FAILURE ) {
145 		return( -1 );
146 	    }
147 	    *addr = *((struct sockaddr *)&iaddr.raw);
148 	    ret = PR_NetAddrToString(&iaddr, buffer, buflen);
149 	    if( ret == PR_FAILURE ) {
150 		return( -1 );
151 	    }
152 	    return (0);
153     }
154     return (-1);
155 }
156 
157 
158 /*
159  * Function: prldap_convert_hostent()
160  * Description: copy the fields of a PRHostEnt struct to an LDAPHostEnt
161  * Returns: the LDAPHostEnt pointer passed in.
162  */
163 static LDAPHostEnt *
prldap_convert_hostent(LDAPHostEnt * ldhp,PRHostEnt * prhp)164 prldap_convert_hostent( LDAPHostEnt *ldhp, PRHostEnt *prhp )
165 {
166 	ldhp->ldaphe_name = prhp->h_name;
167 	ldhp->ldaphe_aliases = prhp->h_aliases;
168 	ldhp->ldaphe_addrtype = prhp->h_addrtype;
169 	ldhp->ldaphe_length =  prhp->h_length;
170 	ldhp->ldaphe_addr_list =  prhp->h_addr_list;
171 	return( ldhp );
172 }
173 
174 #ifdef _SOLARIS_SDK
175 /*
176  * prldap_x_install_dns_skipdb attempts to prevent recursion in resolving
177  * the hostname to an IP address when a host name is given to LDAP user.
178  *
179  * For example, libsldap cannot use LDAP to resolve the host name to an
180  * address because of recursion. The caller is instructing libldap to skip
181  * the specified name service when resolving addresses for the specified
182  * ldap connection.
183  *
184  * Note:
185  *      This only supports ipv4 addresses currently.
186  *
187  *      Since host_service applies to all connections, calling
188  *      prldap_x_install_dns_skipdb with name services other than
189  *      ldap or what uses ldap (for example nis+ might use ldap) to
190  *      skip will lead to unpredictable results.
191  *
192  * Returns:
193  *      0       if success and data base found
194  *      -1      if failure
195  */
196 
197 int
prldap_x_install_dns_skipdb(LDAP * ld,const char * skip)198 prldap_x_install_dns_skipdb(LDAP *ld, const char *skip)
199 {
200 	enum __nsw_parse_err		pserr;
201 	struct __nsw_switchconfig       *conf;
202 	struct __nsw_lookup             *lkp;
203 	struct ldap_dns_fns             dns_fns;
204 	char                            *name_list = NULL;
205 	char                            *tmp;
206 	const char                      *name;
207 	int                             len;
208 	boolean_t                       got_skip = B_FALSE;
209 
210 	/*
211 	 * db_root_hosts.lock mutex is used to ensure that the name list
212 	 * is not in use by the name service switch while we are updating
213 	 * the host_service
214 	 */
215 
216         (void) mutex_lock(&db_root_hosts.lock);
217         conf = __nsw_getconfig("hosts", &pserr);
218         if (conf == NULL) {
219                 (void) mutex_unlock(&db_root_hosts.lock);
220                 return (0);
221         }
222 
223         /* check for skip and count other backends */
224         for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
225                 name = lkp->service_name;
226                 if (strcmp(name, skip) == 0) {
227                         got_skip = B_TRUE;
228                         continue;
229                 }
230                 if (name_list == NULL)
231                         name_list = strdup(name);
232                 else {
233                         len = strlen(name_list);
234                         tmp = realloc(name_list, len + strlen(name) + 2);
235                         if (tmp == NULL) {
236                                 free(name_list);
237                                 name_list = NULL;
238                         } else {
239                                 name_list = tmp;
240                                 name_list[len++] = ' ';
241                                 (void) strcpy(name_list+len, name);
242                         }
243                 }
244                 if (name_list == NULL) {        /* alloc error */
245                         (void) mutex_unlock(&db_root_hosts.lock);
246                         __nsw_freeconfig(conf);
247                         return (-1);
248                 }
249         }
250         __nsw_freeconfig(conf);
251         if (!got_skip) {
252 		/*
253 		 * Since skip name service not used for hosts, we do not need
254 		 * to install our private address resolution function
255 		 */
256                 (void) mutex_unlock(&db_root_hosts.lock);
257                 if (name_list != NULL)
258                         free(name_list);
259                 return (0);
260         }
261         if (host_service != NULL)
262                 free(host_service);
263         host_service = name_list;
264         (void) mutex_unlock(&db_root_hosts.lock);
265 
266         if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
267                 return (-1);
268         dns_fns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE;
269         dns_fns.lddnsfn_gethostbyname = prldap_gethostbyname1;
270         if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
271                 return (-1);
272         return (0);
273 }
274 
275 /*
276  * prldap_initf_hosts is passed to and called by nss_search() as a
277  * service routine.
278  *
279  * Returns:
280  *      None
281  */
282 
283 static void
prldap_initf_hosts(nss_db_params_t * p)284 prldap_initf_hosts(nss_db_params_t *p)
285 {
286         static char *no_service = "";
287 
288         p->name = NSS_DBNAM_HOSTS;
289         p->flags |= NSS_USE_DEFAULT_CONFIG;
290         p->default_config = host_service == NULL ? no_service : host_service;
291 }
292 
293 /*
294  * called by prldap_gethostbyname1()
295  */
296 /*
297  * prldap_switch_gethostbyname_r is called by prldap_gethostbyname1 as a
298  * substitute for gethostbyname_r(). A method which prevents recursion. see
299  * prldap_gethostbyname1() and prldap_x_install_dns_skipdb().
300  *
301  * Returns:
302  *      PR_SUCCESS                    if success
303  *      PR_FAILURE                    if failure
304  */
305 
306 static int
prldap_switch_gethostbyname_r(const char * name,struct hostent * result,char * buffer,int buflen,int * h_errnop)307 prldap_switch_gethostbyname_r(const char *name,
308         struct hostent *result, char *buffer, int buflen,
309         int *h_errnop)
310 {
311         nss_XbyY_args_t arg;
312         nss_status_t    res;
313 	struct hostent	*resp;
314 
315 	/*
316 	 * Log the information indicating that we are trying to
317 	 * resolve the LDAP server name.
318 	 */
319 	syslog(LOG_INFO, "libldap: Resolving server name \"%s\"", name);
320 
321         NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
322 
323         arg.key.name = name;
324         arg.stayopen = 0;
325 
326         res = nss_search(&db_root_hosts, prldap_initf_hosts,
327             NSS_DBOP_HOSTS_BYNAME, &arg);
328         arg.status = res;
329         *h_errnop = arg.h_errno;
330 	resp = (struct hostent *)NSS_XbyY_FINI(&arg);
331 
332 	return (resp != NULL ? PR_SUCCESS : PR_FAILURE);
333 }
334 
335 /*
336  * prldap_gethostbyname1 is used to be a substitute gethostbyname_r for
337  * libldap when it is unsafe to use the normal nameservice functions.
338  *
339  * Returns:
340  *      pointer to LDAPHostEnt:         if success contains the address
341  *      NULL pointer:                   if failure
342  */
343 
344 static LDAPHostEnt *
prldap_gethostbyname1(const char * name,LDAPHostEnt * result,char * buffer,int buflen,int * statusp,void * extradata)345 prldap_gethostbyname1(const char *name, LDAPHostEnt *result,
346 	char *buffer, int buflen, int *statusp, void *extradata)
347 {
348         int         h_errno;
349 	LDAPHostEnt prhent;
350 
351 	memset(&prhent, '\0', sizeof (prhent));
352         if (!statusp || ( *statusp = prldap_switch_gethostbyname_r(name,
353                         &prhent, buffer, buflen, &h_errno )) == PR_FAILURE) {
354 		/*
355 		 * If we got here, it means that we are not able to
356 		 * resolve the LDAP server name and so warn the system
357 		 * adminstrator accordingly.
358 		 */
359 		syslog(LOG_WARNING, "libldap: server name \"%s\" could not "
360 		"be resolved", name);
361 		return (NULL);
362         }
363 
364         return (prldap_convert_hostent(result, &prhent));
365 }
366 
367 #endif  /* _SOLARIS_SDK */
368