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