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, ¶m) < 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