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