xref: /illumos-gate/usr/src/lib/libtsol/common/call_labeld.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <door.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <synch.h>
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include <sys/param.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 
41 #include "labeld.h"
42 
43 #ifndef	DEBUG
44 #define	perror(e)
45 #endif	/* !DEBUG */
46 
47 /*
48  *	Library prototypes to get away from lots of static build problems.
49  */
50 
51 extern int __nanosleep(const struct timespec *, struct timespec *);
52 
53 /*
54  *	This is cloned from _nsc_trydoorcall used by the nscd client.
55  *
56  * Routine that actually performs the door call.
57  * Note that we cache a file descriptor.  We do
58  * the following to prevent disasters:
59  *
60  * 1) Never use 0, 1 or 2; if we get this from the open
61  *    we dup it upwards.
62  *
63  * 2) Set the close on exec flags so descriptor remains available
64  *    to child processes.
65  *
66  * 3) Verify that the door is still the same one we had before
67  *    by using door_info on the client side.
68  *
69  *	Note that we never close the file descriptor if it isn't one
70  *	we allocated; we check this with door info.  The rather tricky
71  *	logic is designed to be fast in the normal case (fd is already
72  *	allocated and is ok) while handling the case where the application
73  *	closed it underneath us or where the nscd dies or re-execs itself
74  *	and we're a multi-threaded application.  Note that we cannot protect
75  *	the application if it closes the fd and it is multi-threaded.
76  *
77  *  int __call_labeld(label_door_op **dptr, int *ndata, int *adata);
78  *
79  *      *dptr	IN: points to arg buffer OUT: points to results buffer
80  *      *ndata	IN: overall size of buffer OUT: overall size of buffer
81  *      *adata	IN: size of call data OUT: size of return data
82  *
83  *  Note that *dptr may change if provided space as defined by *bufsize is
84  *  inadequate.  In this case the door call mmaps more space and places
85  *  the answer there and sets dptr to contain a pointer to the space, which
86  *  should be freed with munmap.
87  *
88  *  Returns 0 if the door call reached the server, -1 if contact was not made.
89  *
90  */
91 
92 
93 static mutex_t	_door_lock = DEFAULTMUTEX;
94 
95 int
96 __call_labeld(labeld_data_t **dptr, size_t *ndata, size_t *adata)
97 {
98 	static	int 		doorfd = -1;
99 	static	door_info_t 	real_door;
100 	struct stat		st;
101 	door_info_t 		my_door;
102 	door_arg_t		param;
103 	char			door_name[MAXPATHLEN];
104 	struct timespec		ts;
105 	int			busy = 0;	/* number of busy loops */
106 
107 #ifdef	DEBUG
108 	labeld_data_t		*callptr = *dptr;
109 	int			buf_size = *ndata;
110 	int			return_size = *adata;
111 #endif	/* DEBUG */
112 
113 	/*
114 	 * the first time in we try and open and validate the door.
115 	 * the validations are that the door must have been
116 	 * created with the label service door cookie and
117 	 * that it has the same door ID.  If any of these
118 	 * validations fail we refuse to use the door.
119 	 */
120 	ts.tv_sec = 0;		/* initialize nanosecond retry timer */
121 	ts.tv_nsec = 100;
122 	(void) mutex_lock(&_door_lock);
123 
124 try_again:
125 	if (doorfd == -1) {
126 		int	tbc[3];
127 		int	i;
128 
129 		(void) snprintf(door_name, sizeof (door_name), "%s%s",
130 		    DOOR_PATH, DOOR_NAME);
131 		if ((doorfd = open64(door_name, O_RDONLY, 0)) < 0) {
132 			(void) mutex_unlock(&_door_lock);
133 			perror("server door open");
134 			return (NOSERVER);
135 		}
136 
137 		/*
138 		 * dup up the file descriptor if we have 0 - 2
139 		 * to avoid problems with shells stdin/out/err
140 		 */
141 		i = 0;
142 		while (doorfd < 3) { /* we have a reserved fd */
143 			tbc[i++] = doorfd;
144 			if ((doorfd = dup(doorfd)) < 0) {
145 				perror("couldn't dup");
146 				while (i--)
147 				    (void) close(tbc[i]);
148 				doorfd = -1;
149 				(void) mutex_unlock(&_door_lock);
150 				return (NOSERVER);
151 			}
152 		}
153 		while (i--)
154 		    (void) close(tbc[i]);
155 
156 		/*
157 		 * mark this door descriptor as close on exec
158 		 */
159 		(void) fcntl(doorfd, F_SETFD, FD_CLOEXEC);
160 		if (door_info(doorfd, &real_door) < 0) {
161 			/*
162 			 * we should close doorfd because we just opened it
163 			 */
164 			perror("real door door_info");
165 			(void) close(doorfd);
166 			doorfd = -1;
167 			(void) mutex_unlock(&_door_lock);
168 			return (NOSERVER);
169 		}
170 		if (fstat(doorfd, &st) < 0) {
171 			perror("real door fstat");
172 			return (NOSERVER);
173 		}
174 #ifdef	DEBUG
175 		(void) printf("\treal door %s\n", door_name);
176 		(void) printf("\t\tuid = %d, gid = %d, mode = %o\n", st.st_uid,
177 		    st.st_gid, st.st_mode);
178 		(void) printf("\t\toutstanding requests = %d\n", st.st_nlink-1);
179 		(void) printf("\t\t pid = %d\n", real_door.di_target);
180 		(void) printf("\t\t procedure = %llx\n", real_door.di_proc);
181 		(void) printf("\t\t cookie = %llx\n",  real_door.di_data);
182 		(void) printf("\t\t attributes = %x\n",
183 		    real_door.di_attributes);
184 		if (real_door.di_attributes & DOOR_UNREF)
185 			(void) printf("\t\t\t UNREF\n");
186 		if (real_door.di_attributes & DOOR_PRIVATE)
187 			(void) printf("\t\t\t PRIVATE\n");
188 		if (real_door.di_attributes & DOOR_LOCAL)
189 			(void) printf("\t\t\t LOCAL\n");
190 		if (real_door.di_attributes & DOOR_REVOKED)
191 			(void) printf("\t\t\t REVOKED\n");
192 		if (real_door.di_attributes & DOOR_DESCRIPTOR)
193 			(void) printf("\t\t\t DESCRIPTOR\n");
194 		if (real_door.di_attributes & DOOR_RELEASE)
195 			(void) printf("\t\t\t RELEASE\n");
196 		if (real_door.di_attributes & DOOR_DELAY)
197 			(void) printf("\t\t\t DELAY\n");
198 		(void) printf("\t\t id = %llx\n", real_door.di_uniquifier);
199 #endif	/* DEBUG */
200 		if ((real_door.di_attributes & DOOR_REVOKED) ||
201 		    (real_door.di_data != (door_ptr_t)COOKIE)) {
202 #ifdef	DEBUG
203 			(void) printf("real door revoked\n");
204 #endif	/* DEBUG */
205 			(void) close(doorfd);
206 			doorfd = -1;
207 			(void) mutex_unlock(&_door_lock);
208 			return (NOSERVER);
209 		}
210 	} else {
211 		if ((door_info(doorfd, &my_door) < 0) ||
212 		    (my_door.di_data != (door_ptr_t)COOKIE) ||
213 			(my_door.di_uniquifier != real_door.di_uniquifier)) {
214 			perror("my door door_info");
215 			/*
216 			 * don't close it - someone else has clobbered fd
217 			 */
218 			doorfd = -1;
219 			goto try_again;
220 		}
221 		if (fstat(doorfd, &st) < 0) {
222 			perror("my door fstat");
223 			goto try_again;
224 		}
225 #ifdef	DEBUG
226 		(void) sprintf(door_name, "%s%s", DOOR_PATH, DOOR_NAME);
227 		(void) printf("\tmy door %s\n", door_name);
228 		(void) printf("\t\tuid = %d, gid = %d, mode = %o\n", st.st_uid,
229 		    st.st_gid, st.st_mode);
230 		(void) printf("\t\toutstanding requests = %d\n", st.st_nlink-1);
231 		(void) printf("\t\t pid = %d\n", my_door.di_target);
232 		(void) printf("\t\t procedure = %llx\n", my_door.di_proc);
233 		(void) printf("\t\t cookie = %llx\n",  my_door.di_data);
234 		(void) printf("\t\t attributes = %x\n", my_door.di_attributes);
235 		if (my_door.di_attributes & DOOR_UNREF)
236 			(void) printf("\t\t\t UNREF\n");
237 		if (my_door.di_attributes & DOOR_PRIVATE)
238 			(void) printf("\t\t\t PRIVATE\n");
239 		if (my_door.di_attributes & DOOR_LOCAL)
240 			(void) printf("\t\t\t LOCAL\n");
241 		if (my_door.di_attributes & DOOR_REVOKED)
242 			(void) printf("\t\t\t REVOKED\n");
243 		if (my_door.di_attributes & DOOR_DESCRIPTOR)
244 			(void) printf("\t\t\t DESCRIPTOR\n");
245 		if (my_door.di_attributes & DOOR_RELEASE)
246 			(void) printf("\t\t\t RELEASE\n");
247 		if (my_door.di_attributes & DOOR_DELAY)
248 			(void) printf("\t\t\t DELAY\n");
249 		(void) printf("\t\t id = %llx\n", my_door.di_uniquifier);
250 #endif	/* DEBUG */
251 		if (my_door.di_attributes & DOOR_REVOKED) {
252 #ifdef	DEBUG
253 			(void) printf("my door revoked\n");
254 #endif	/* DEBUG */
255 			(void) close(doorfd);	/* labeld exited .... */
256 			doorfd = -1;	/* try and restart connection */
257 			goto try_again;
258 		}
259 	}
260 	(void) mutex_unlock(&_door_lock);
261 
262 	param.data_ptr = (char *)*dptr;
263 	param.data_size = *adata;
264 	param.desc_ptr = NULL;
265 	param.desc_num = 0;
266 	param.rbuf = (char *)*dptr;
267 	param.rsize = *ndata;
268 
269 	if (door_call(doorfd, &param) < 0) {
270 		if (errno == EAGAIN && busy++ < 10) {
271 			/* adjust backoff */
272 			if ((ts.tv_nsec *= 10) >= NANOSEC) {
273 				ts.tv_sec++;
274 				ts.tv_nsec = 100;
275 			}
276 			(void) __nanosleep(&ts, NULL);
277 #ifdef	DEBUG
278 			(void) printf("door_call failed EAGAIN # %d\n", busy);
279 #endif	/* DEBUG */
280 			(void) mutex_lock(&_door_lock);
281 			goto try_again;
282 		}
283 		perror("door call");
284 		return (NOSERVER);
285 	}
286 
287 	*adata = (int)param.data_size;
288 	*ndata = (int)param.rsize;
289 	/*LINTED*/
290 	*dptr = (labeld_data_t *)param.data_ptr;
291 
292 	if (*adata == 0 || *dptr == NULL) {
293 #ifdef	DEBUG
294 		(void) printf("\tNo data returned, size = %lu, dptr = %p\n",
295 		    (unsigned long)*adata, (void *)*dptr);
296 #endif	/* DEBUG */
297 		return (NOSERVER);
298 	}
299 #ifdef	DEBUG
300 	(void) printf("call buf = %x, buf size  = %d, call size = %d\n",
301 	    callptr, buf_size, return_size);
302 	(void) printf("retn buf = %x, buf size  = %d, retn size = %d\n",
303 	    *dptr, *ndata, *adata);
304 	(void) printf("\treply status = %d\n", (*dptr)->param.aret.ret);
305 #endif	/* DEBUG */
306 	return ((*dptr)->param.aret.ret);
307 
308 }  /* __call_labeld */
309