1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/types.h>
30#include <pwd.h>
31#include <stdio.h>
32#include <synch.h>
33#include <sys/param.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include "ns_cache_door.h"
37#include <door.h>
38
39#if defined(PIC) || defined(lint)
40
41/*
42 *
43 * Routines that actually performs the door call.
44 * Note that we cache a file descriptor.  We do
45 * the following to prevent disasters:
46 *
47 * 1) Never use 0,1 or 2; if we get this from the open
48 *    we dup it upwards.
49 *
50 * 2) Set the close on exec flags so descriptor remains available
51 *    to child processes.
52 *
53 * 3) Verify that the door is still the same one we had before
54 *    by using door_info on the client side.
55 *
56 *	Note that we never close the file descriptor if it isn't one
57 *	we allocated; we check this with door info.  The rather tricky
58 *	logic is designed to be fast in the normal case (fd is already
59 *	allocated and is ok) while handling the case where the application
60 *	closed it underneath us or where the nscd dies or re-execs itself
61 *	and we're a multi-threaded application.  Note that we cannot protect
62 *	the application if it closes the fd and it is multi-threaded.
63 *
64 *  int _cache_trydoorcall(void *dptr, int *bufsize, int *actualsize);
65 *
66 *      *dptr           IN: points to arg buffer OUT: points to results buffer
67 *      *bufsize        IN: overall size of buffer OUT: overall size of buffer
68 *      *actualsize     IN: size of call data OUT: size of return data
69 *
70 *  Note that *dptr may change if provided space as defined by *bufsize is
71 *  inadequate.  In this case the door call mmaps more space and places
72 *  the answer there and sets dptr to contain a pointer to the space, which
73 *  should be freed with munmap.
74 *
75 *  Returns 0 if the door call reached the server, -1 if contact was not made.
76 *
77 */
78
79extern int errno;
80static mutex_t	_door_lock = DEFAULTMUTEX;
81static	int 		doorfd = -1;
82
83/*
84 * This function does the first part: ensures a file descriptor is
85 * cached and usable.
86 */
87int
88__ns_ldap_trydoorcall_getfd()
89{
90	static	door_info_t 	real_door;
91	door_info_t 		my_door;
92
93	/*
94	 * the first time in we try and open and validate the door.
95	 * the validations are that the door must have been
96	 * created with the name service door cookie and
97	 * that the file attached to the door is owned by root
98	 * and readonly by user, group and other.  If any of these
99	 * validations fail we refuse to use the door.
100	 */
101
102	(void) mutex_lock(&_door_lock);
103
104try_again:
105
106	if (doorfd == -1) {
107
108		int		tbc[3];
109		int		i;
110		if ((doorfd = open(LDAP_CACHE_DOOR, O_RDONLY, 0))
111		    == -1) {
112			(void) mutex_unlock(&_door_lock);
113			return (NS_CACHE_NOSERVER);
114		}
115
116		/*
117		 * dup up the file descriptor if we have 0 - 2
118		 * to avoid problems with shells stdin/out/err
119		 */
120		i = 0;
121
122		while (doorfd < 3) { /* we have a reserved fd */
123			tbc[i++] = doorfd;
124			if ((doorfd = dup(doorfd)) < 0) {
125				while (i--)
126					(void) close(tbc[i]);
127				doorfd = -1;
128				(void) mutex_unlock(&_door_lock);
129				return (NS_CACHE_NOSERVER);
130			}
131		}
132
133		while (i--)
134			(void) close(tbc[i]);
135
136		/*
137		 * mark this door descriptor as close on exec
138		 */
139		(void) fcntl(doorfd, F_SETFD, FD_CLOEXEC);
140		if (door_info(doorfd, &real_door) == -1 ||
141		    (real_door.di_attributes & DOOR_REVOKED) ||
142		    real_door.di_data != (uintptr_t)LDAP_CACHE_DOOR_COOKIE) {
143			/*
144			 * we should close doorfd because we just opened it
145			 */
146			(void) close(doorfd);
147			doorfd = -1;
148			(void) mutex_unlock(&_door_lock);
149			return (NS_CACHE_NOSERVER);
150		}
151	} else {
152		if (door_info(doorfd, &my_door) == -1 ||
153		    my_door.di_data != (uintptr_t)LDAP_CACHE_DOOR_COOKIE ||
154		    my_door.di_uniquifier != real_door.di_uniquifier) {
155			/*
156			 * don't close it -
157			 * someone else has clobbered fd
158			 */
159			doorfd = -1;
160			goto try_again;
161		}
162
163		if (my_door.di_attributes & DOOR_REVOKED) {
164			(void) close(doorfd);
165			doorfd = -1;	/* try and restart connection */
166			goto try_again;
167		}
168	}
169
170	(void) mutex_unlock(&_door_lock);
171	return (NS_CACHE_SUCCESS);
172}
173
174/*
175 * This function does the second part: sends a door request to
176 * the ldap_cachemgr daemon.
177 */
178int
179__ns_ldap_trydoorcall_send(ldap_data_t **dptr, int *ndata, int *adata)
180{
181	door_arg_t		param;
182
183	param.rbuf = (char *)*dptr;
184	param.rsize = *ndata;
185	param.data_ptr = (char *)*dptr;
186	param.data_size = *adata;
187	param.desc_ptr = NULL;
188	param.desc_num = 0;
189	if (door_call(doorfd, &param) == -1) {
190		return (NS_CACHE_NOSERVER);
191	}
192	*adata = (int)param.data_size;
193	*ndata = (int)param.rsize;
194	*dptr = (ldap_data_t *)param.data_ptr;
195	if (*adata == 0 || *dptr == NULL) {
196		return (NS_CACHE_NOSERVER);
197	}
198
199	return ((*dptr)->ldap_ret.ldap_return_code);
200}
201
202/*
203 * This function does part 1 and 2: makes sure a file descriptor is
204 * available and sends a door request to the ldap_cachemgr daemon.
205 */
206int
207__ns_ldap_trydoorcall(ldap_data_t **dptr, int *ndata, int *adata)
208{
209	int rc;
210
211	if ((rc = __ns_ldap_trydoorcall_getfd()) == NS_CACHE_SUCCESS)
212		return (__ns_ldap_trydoorcall_send(dptr, ndata, adata));
213	else
214		return (rc);
215}
216
217void
218__ns_ldap_doorfd_close()
219{
220	(void) mutex_lock(&_door_lock);
221	if (doorfd != -1) {
222		(void) close(doorfd);
223	}
224	(void) mutex_unlock(&_door_lock);
225}
226
227/*
228 *  routine to check if server is already running
229 */
230
231int
232__ns_ldap_cache_ping()
233{
234	ldap_data_t data;
235	ldap_data_t *dptr;
236	int ndata;
237	int adata;
238
239	data.ldap_call.ldap_callnumber = NULLCALL;
240	ndata = sizeof (data);
241	adata = sizeof (data);
242	dptr = &data;
243	return (__ns_ldap_trydoorcall(&dptr, &ndata, &adata));
244}
245
246#endif /* PIC */
247