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