author | Jon Tibble <meths@btinternet.com> |
Thu, 09 Dec 2010 22:32:39 +0100 | |
changeset 13255 | 4afa820d78b9 |
parent 6812 | febeba71273d |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* CDDL HEADER START |
|
3 |
* |
|
4 |
* The contents of this file are subject to the terms of the |
|
2830 | 5 |
* Common Development and Distribution License (the "License"). |
6 |
* You may not use this file except in compliance with the License. |
|
0 | 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 |
*/ |
|
1016 | 21 |
|
0 | 22 |
/* |
6351 | 23 |
* Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
0 | 24 |
* Use is subject to license terms. |
25 |
*/ |
|
26 |
||
27 |
#pragma ident "%Z%%M% %I% %E% SMI" |
|
28 |
||
6812 | 29 |
#include "lint.h" |
0 | 30 |
#include <mtlib.h> |
31 |
#include <sys/types.h> |
|
2830 | 32 |
#include <errno.h> |
0 | 33 |
#include <pwd.h> |
34 |
#include <nss_dbdefs.h> |
|
35 |
#include <stdio.h> |
|
2830 | 36 |
#include <string.h> |
0 | 37 |
#include <synch.h> |
38 |
#include <sys/param.h> |
|
39 |
#include <fcntl.h> |
|
40 |
#include <unistd.h> |
|
2830 | 41 |
#include <stdlib.h> |
0 | 42 |
#include <getxby_door.h> |
43 |
#include <sys/door.h> |
|
2830 | 44 |
#include <procfs.h> |
45 |
#include <door.h> |
|
4142
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
46 |
#include <sys/mman.h> |
0 | 47 |
#include "libc.h" |
2830 | 48 |
#include "tsd.h" |
0 | 49 |
#include "base_conversion.h" |
50 |
||
2830 | 51 |
/* nss<->door hints */ |
52 |
static mutex_t hints_lock = DEFAULTMUTEX; |
|
53 |
static size_t door_bsize = 0; |
|
54 |
static size_t door_nbsize = 0; |
|
55 |
static int proc_is_cache = -1; |
|
56 |
||
57 |
/* library<->nscd door interaction apis */ |
|
58 |
||
0 | 59 |
/* |
60 |
* |
|
61 |
* Routine that actually performs the door call. |
|
62 |
* Note that we cache a file descriptor. We do |
|
63 |
* the following to prevent disasters: |
|
64 |
* |
|
65 |
* 1) Never use 0,1 or 2; if we get this from the open |
|
66 |
* we dup it upwards. |
|
67 |
* |
|
68 |
* 2) Set the close on exec flags so descriptor remains available |
|
69 |
* to child processes. |
|
70 |
* |
|
71 |
* 3) Verify that the door is still the same one we had before |
|
72 |
* by using door_info on the client side. |
|
73 |
* |
|
74 |
* Note that we never close the file descriptor if it isn't one |
|
75 |
* we allocated; we check this with door info. The rather tricky |
|
76 |
* logic is designed to be fast in the normal case (fd is already |
|
77 |
* allocated and is ok) while handling the case where the application |
|
78 |
* closed it underneath us or where the nscd dies or re-execs itself |
|
79 |
* and we're a multi-threaded application. Note that we cannot protect |
|
80 |
* the application if it closes the fd and it is multi-threaded. |
|
81 |
* |
|
2830 | 82 |
* int _nsc_trydoorcall(void *dptr, size_t *bufsize, size_t *actualsize); |
0 | 83 |
* |
84 |
* *dptr IN: points to arg buffer OUT: points to results buffer |
|
85 |
* *bufsize IN: overall size of buffer OUT: overall size of buffer |
|
86 |
* *actualsize IN: size of call data OUT: size of return data |
|
87 |
* |
|
88 |
* Note that *dptr may change if provided space as defined by *bufsize is |
|
89 |
* inadequate. In this case the door call mmaps more space and places |
|
90 |
* the answer there and sets dptr to contain a pointer to the space, which |
|
91 |
* should be freed with munmap. |
|
92 |
* |
|
93 |
* Returns 0 if the door call reached the server, -1 if contact was not made. |
|
94 |
* |
|
95 |
*/ |
|
96 |
||
2830 | 97 |
/* |
98 |
* Max size for list of db names supported by the private nscd |
|
99 |
* No implied max here, any size will do, fixed size chosen to |
|
100 |
* reduce yet another malloc |
|
101 |
*/ |
|
102 |
||
103 |
#define BD_BUFSIZE 1024 |
|
104 |
#define BD_SEP ',' |
|
105 |
||
106 |
typedef struct _nsc_door_t { |
|
107 |
int doorfd; |
|
108 |
mutex_t door_lock; |
|
109 |
door_info_t doori; |
|
110 |
} nsc_door_t; |
|
111 |
||
112 |
static nsc_door_t nsc_door[2] = { |
|
113 |
{ -1, DEFAULTMUTEX, { 0 } }, /* front (fattached) door */ |
|
114 |
{ -1, DEFAULTMUTEX, { 0 } }, /* back (private) door */ |
|
115 |
}; |
|
116 |
||
117 |
/* assumed to be locked by using nsc_door[1] mutex */ |
|
118 |
static char *nsc_db_buf = NULL; |
|
119 |
static char **nsc_db_list = NULL; |
|
0 | 120 |
|
2830 | 121 |
/* |
122 |
* Check for a valid and matching db in the list. |
|
123 |
* assume list is in the locked state. |
|
124 |
*/ |
|
125 |
||
126 |
static int |
|
127 |
_nsc_use_backdoor(char *db) |
|
128 |
{ |
|
129 |
char **ndb; |
|
130 |
||
131 |
if (db && nsc_db_buf != NULL && nsc_db_list != NULL) { |
|
132 |
for (ndb = nsc_db_list; *ndb; ndb++) { |
|
133 |
if (strcmp(db, *ndb) == 0) |
|
134 |
return (1); |
|
135 |
} |
|
136 |
} |
|
137 |
return (0); |
|
138 |
} |
|
139 |
||
140 |
/* |
|
141 |
* flush private db lists |
|
142 |
*/ |
|
143 |
static void |
|
144 |
_nsc_flush_private_db() |
|
0 | 145 |
{ |
2830 | 146 |
if (nsc_db_buf != NULL) { |
147 |
libc_free((void *)nsc_db_buf); |
|
148 |
nsc_db_buf = NULL; |
|
149 |
} |
|
150 |
if (nsc_db_list != NULL) { |
|
151 |
libc_free((void *)nsc_db_list); |
|
152 |
nsc_db_list = NULL; |
|
153 |
} |
|
154 |
} |
|
155 |
||
156 |
/* |
|
157 |
* init/update nsc_db_buf given buff containing list of |
|
158 |
* db's to be processed by a private nscd. |
|
159 |
* This function assumes it has a well formed string from nscd. |
|
160 |
*/ |
|
161 |
||
162 |
static int |
|
163 |
_nsc_init_private_db(char *dblist) |
|
164 |
{ |
|
165 |
char *cp, **lp; |
|
166 |
int buflen = 0; |
|
167 |
int arrlen = 0; |
|
168 |
||
169 |
if (dblist == NULL) |
|
170 |
return (0); |
|
171 |
||
172 |
/* reset db list */ |
|
173 |
_nsc_flush_private_db(); |
|
174 |
||
175 |
/* rebuild fresh list */ |
|
176 |
buflen = strlen(dblist) + 1; |
|
177 |
for (cp = dblist; *cp; cp++) |
|
178 |
if (*cp == BD_SEP) |
|
179 |
arrlen++; |
|
180 |
if (cp == dblist) |
|
181 |
return (0); |
|
182 |
arrlen += 2; |
|
183 |
nsc_db_buf = (char *)libc_malloc(buflen); |
|
184 |
if (nsc_db_buf == (char *)NULL) |
|
185 |
return (0); |
|
186 |
nsc_db_list = (char **)libc_malloc(arrlen * sizeof (char *)); |
|
187 |
if (nsc_db_list == (char **)NULL) { |
|
188 |
libc_free((void *)nsc_db_buf); |
|
189 |
nsc_db_buf = NULL; |
|
190 |
return (0); |
|
191 |
} |
|
192 |
(void) memcpy(nsc_db_buf, dblist, buflen); |
|
193 |
lp = nsc_db_list; |
|
194 |
*lp++ = nsc_db_buf; |
|
195 |
for (cp = nsc_db_buf; *cp; ) { |
|
196 |
if (*cp == BD_SEP) { |
|
197 |
*cp++ = '\0'; |
|
198 |
*lp++ = cp; |
|
199 |
} else |
|
200 |
cp++; |
|
201 |
} |
|
202 |
*lp = NULL; |
|
203 |
return (1); |
|
204 |
} |
|
205 |
||
206 |
/* |
|
207 |
* _nsc_initdoor_fp attempts to validate the given door and |
|
208 |
* confirm that it is still available for use. The options are: |
|
209 |
* Front door: |
|
210 |
* If it's not open, attempt to open or error |
|
211 |
* If it's open attempt to validate. |
|
212 |
* If it's not validatable, reset fd and try again. |
|
213 |
* Other wise it open and validated, return success |
|
214 |
* Per user (back) door: |
|
215 |
* This door is passed to the client through th front door |
|
216 |
* attempt to validate it. If it can't be validated, it |
|
217 |
* must be reset. Then send a NSS_ALTRESET error, so nscd can |
|
218 |
* forward another fd if desired. |
|
219 |
*/ |
|
220 |
||
221 |
static nss_status_t |
|
222 |
_nsc_initdoor_fp(nsc_door_t *dp) |
|
223 |
{ |
|
224 |
||
0 | 225 |
door_info_t my_door; |
2830 | 226 |
|
227 |
if (dp == NULL) { |
|
228 |
errno = ENOTCONN; |
|
229 |
return (NSS_ERROR); |
|
230 |
} |
|
0 | 231 |
|
232 |
/* |
|
2830 | 233 |
* the first time in we try and open and validate the front door. |
234 |
* A front door request may return an alternate private back door |
|
235 |
* that the client should use instead. |
|
236 |
* |
|
237 |
* To validate a door the door must have been created with |
|
238 |
* the name service door cookie. The front door is file |
|
239 |
* attached, owned by root and readonly by user, group and |
|
240 |
* other. If any of these validations fail we refuse to use |
|
241 |
* the door. A back door is delivered from the front door |
|
242 |
* via a door_desc_t, and have the same cooke notification. |
|
0 | 243 |
*/ |
244 |
||
2830 | 245 |
lmutex_lock(&dp->door_lock); |
0 | 246 |
|
247 |
try_again: |
|
248 |
||
2830 | 249 |
if (dp->doorfd == -1 && dp == &nsc_door[0]) { /* open front door */ |
0 | 250 |
int tbc[3]; |
251 |
int i; |
|
1016 | 252 |
|
2830 | 253 |
dp->doorfd = open64(NAME_SERVICE_DOOR, O_RDONLY, 0); |
254 |
if (dp->doorfd == -1) { |
|
255 |
lmutex_unlock(&dp->door_lock); |
|
256 |
return (NSS_ERROR); |
|
0 | 257 |
} |
258 |
||
259 |
/* |
|
260 |
* dup up the file descriptor if we have 0 - 2 |
|
261 |
* to avoid problems with shells stdin/out/err |
|
262 |
*/ |
|
263 |
i = 0; |
|
264 |
||
2830 | 265 |
while (dp->doorfd < 3) { /* we have a reserved fd */ |
266 |
tbc[i++] = dp->doorfd; |
|
267 |
if ((dp->doorfd = dup(dp->doorfd)) < 0) { |
|
0 | 268 |
while (i--) |
6351 | 269 |
(void) close(tbc[i]); |
2830 | 270 |
dp->doorfd = -1; |
271 |
lmutex_unlock(&dp->door_lock); |
|
272 |
return (NSS_ERROR); |
|
0 | 273 |
} |
274 |
} |
|
275 |
||
276 |
while (i--) |
|
6351 | 277 |
(void) close(tbc[i]); |
0 | 278 |
|
279 |
/* |
|
280 |
* mark this door descriptor as close on exec |
|
281 |
*/ |
|
2830 | 282 |
(void) fcntl(dp->doorfd, F_SETFD, FD_CLOEXEC); |
283 |
if (__door_info(dp->doorfd, &dp->doori) < 0 || |
|
284 |
(dp->doori.di_attributes & DOOR_REVOKED) || |
|
285 |
dp->doori.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE) { |
|
0 | 286 |
/* |
287 |
* we should close doorfd because we just opened it |
|
288 |
*/ |
|
2830 | 289 |
(void) close(dp->doorfd); |
290 |
dp->doorfd = -1; |
|
291 |
(void) memset((void *)&dp->doori, |
|
6351 | 292 |
'\0', sizeof (door_info_t)); |
2830 | 293 |
lmutex_unlock(&dp->door_lock); |
294 |
errno = ECONNREFUSED; |
|
295 |
return (NSS_ERROR); |
|
0 | 296 |
} |
1016 | 297 |
} else { |
2830 | 298 |
if (__door_info(dp->doorfd, &my_door) < 0 || |
1016 | 299 |
my_door.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE || |
2830 | 300 |
my_door.di_uniquifier != dp->doori.di_uniquifier) { |
1016 | 301 |
/* |
302 |
* don't close it - |
|
303 |
* someone else has clobbered fd |
|
304 |
*/ |
|
2830 | 305 |
dp->doorfd = -1; |
306 |
(void) memset((void *)&dp->doori, |
|
6351 | 307 |
'\0', sizeof (door_info_t)); |
2830 | 308 |
if (dp == &nsc_door[1]) { /* reset back door */ |
309 |
/* flush invalid db list */ |
|
310 |
_nsc_flush_private_db(); |
|
311 |
lmutex_unlock(&dp->door_lock); |
|
312 |
return (NSS_ALTRESET); |
|
313 |
} |
|
1016 | 314 |
goto try_again; |
0 | 315 |
} |
316 |
||
317 |
if (my_door.di_attributes & DOOR_REVOKED) { |
|
2830 | 318 |
(void) close(dp->doorfd); /* nscd exited .... */ |
319 |
dp->doorfd = -1; /* try and restart connection */ |
|
320 |
(void) memset((void *)&dp->doori, |
|
6351 | 321 |
'\0', sizeof (door_info_t)); |
2830 | 322 |
if (dp == &nsc_door[1]) { /* back door reset */ |
323 |
/* flush invalid db list */ |
|
324 |
_nsc_flush_private_db(); |
|
325 |
lmutex_unlock(&dp->door_lock); |
|
326 |
return (NSS_ALTRESET); |
|
327 |
} |
|
0 | 328 |
goto try_again; |
329 |
} |
|
330 |
} |
|
331 |
||
2830 | 332 |
lmutex_unlock(&dp->door_lock); |
333 |
return (NSS_SUCCESS); |
|
334 |
} |
|
335 |
||
336 |
/* |
|
337 |
* Try the door request once only, to the specified connection. |
|
338 |
* return the results or error. |
|
339 |
*/ |
|
340 |
||
341 |
static nss_status_t |
|
342 |
_nsc_try1door(nsc_door_t *dp, void **dptr, size_t *ndata, |
|
343 |
size_t *adata, int *pdesc) |
|
344 |
{ |
|
345 |
door_arg_t param; |
|
346 |
int ret; |
|
347 |
nss_pheader_t *rp; |
|
348 |
||
349 |
ret = _nsc_initdoor_fp(dp); |
|
350 |
if (ret != NSS_SUCCESS) |
|
351 |
return (ret); |
|
0 | 352 |
|
353 |
param.rbuf = (char *)*dptr; |
|
354 |
param.rsize = *ndata; |
|
355 |
param.data_ptr = (char *)*dptr; |
|
356 |
param.data_size = *adata; |
|
357 |
param.desc_ptr = NULL; |
|
358 |
param.desc_num = 0; |
|
2830 | 359 |
ret = __door_call(dp->doorfd, ¶m); |
360 |
if (ret < 0) { |
|
361 |
return (NSS_ERROR); |
|
0 | 362 |
} |
2830 | 363 |
*adata = param.data_size; |
364 |
*ndata = param.rsize; |
|
365 |
*dptr = (void *)param.data_ptr; |
|
366 |
rp = (nss_pheader_t *)((void *)param.rbuf); |
|
367 |
if (pdesc != NULL && rp && rp->p_status == NSS_ALTRETRY && |
|
368 |
param.desc_ptr != NULL && param.desc_num > 0) { |
|
369 |
if ((param.desc_ptr->d_attributes & DOOR_DESCRIPTOR) && |
|
370 |
param.desc_ptr->d_data.d_desc.d_descriptor >= 0 && |
|
371 |
param.desc_ptr->d_data.d_desc.d_id != 0) { |
|
372 |
/* have an alt descriptor */ |
|
373 |
*pdesc = param.desc_ptr->d_data.d_desc.d_descriptor; |
|
374 |
/* got a NSS_ALTRETRY command */ |
|
375 |
return (NSS_ALTRETRY); |
|
376 |
} |
|
377 |
errno = EINVAL; |
|
378 |
return (NSS_ERROR); /* other error? */ |
|
379 |
} |
|
0 | 380 |
if (*adata == 0 || *dptr == NULL) { |
2830 | 381 |
errno = ENOTCONN; |
382 |
return (NSS_ERROR); |
|
0 | 383 |
} |
384 |
||
2830 | 385 |
if (rp->p_status == NSS_ALTRESET || |
6351 | 386 |
rp->p_status == NSS_ALTRETRY || |
387 |
rp->p_status == NSS_TRYLOCAL) |
|
2830 | 388 |
return (rp->p_status); |
389 |
||
390 |
return (NSS_SUCCESS); |
|
391 |
} |
|
392 |
||
393 |
/* |
|
394 |
* Backwards compatible API |
|
395 |
*/ |
|
396 |
||
397 |
nss_status_t |
|
398 |
_nsc_trydoorcall(void **dptr, size_t *ndata, size_t *adata) |
|
399 |
{ |
|
400 |
return (_nsc_try1door(&nsc_door[0], dptr, ndata, adata, NULL)); |
|
0 | 401 |
} |
2830 | 402 |
|
403 |
/* |
|
404 |
* Send the request to the designated door, based on the supplied db |
|
405 |
* Retry on the alternate door fd if possible. |
|
406 |
*/ |
|
407 |
||
408 |
nss_status_t |
|
409 |
_nsc_trydoorcall_ext(void **dptr, size_t *ndata, size_t *adata) |
|
410 |
{ |
|
411 |
int ret = NSS_ALTRETRY; |
|
412 |
nsc_door_t *frontd = &nsc_door[0]; |
|
413 |
nsc_door_t *backd = &nsc_door[1]; |
|
414 |
int fd; |
|
415 |
||
416 |
nss_pheader_t *ph, ph_save; |
|
417 |
char *dbl; |
|
418 |
char *db = NULL; |
|
419 |
nss_dbd_t *dbd; |
|
420 |
int fb2frontd = 0; |
|
421 |
int reset_frontd = 0; |
|
4142
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
422 |
size_t ndata_save = *ndata, adata_save = *adata; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
423 |
void *dptr_save = *dptr; |
2830 | 424 |
|
425 |
ph = (nss_pheader_t *)*dptr; |
|
426 |
dbd = (nss_dbd_t *)((void *)((char *)ph + ph->dbd_off)); |
|
427 |
if (dbd->o_name != 0) |
|
428 |
db = (char *)dbd + dbd->o_name; |
|
4142
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
429 |
|
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
430 |
/* |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
431 |
* save away a copy of the header, in case the request needs |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
432 |
* to be sent to nscd more than once. In that case, this |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
433 |
* original header can be copied back to the door buffer |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
434 |
* to replace the possibly changed header |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
435 |
*/ |
2830 | 436 |
ph_save = *ph; |
437 |
||
438 |
while (ret == NSS_ALTRETRY || ret == NSS_ALTRESET) { |
|
439 |
/* try private (back) door first if it exists and applies */ |
|
440 |
if (db != NULL && backd->doorfd > 0 && fb2frontd == 0 && |
|
6351 | 441 |
_nsc_use_backdoor(db)) { |
2830 | 442 |
ret = _nsc_try1door(backd, dptr, ndata, adata, NULL); |
443 |
if (ret == NSS_ALTRESET) { |
|
444 |
/* |
|
445 |
* received NSS_ALTRESET command, |
|
446 |
* retry on front door |
|
447 |
*/ |
|
448 |
lmutex_lock(&backd->door_lock); |
|
449 |
backd->doorfd = -1; |
|
450 |
(void) memset((void *)&backd->doori, |
|
6351 | 451 |
'\0', sizeof (door_info_t)); |
2830 | 452 |
/* flush now invalid db list */ |
453 |
_nsc_flush_private_db(); |
|
454 |
lmutex_unlock(&backd->door_lock); |
|
455 |
continue; |
|
456 |
} else if (ret == NSS_ALTRETRY) { |
|
457 |
/* |
|
458 |
* received NSS_ALTRETRY command, |
|
459 |
* fall back and retry on front door |
|
460 |
*/ |
|
461 |
fb2frontd = 1; |
|
4142
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
462 |
if (*dptr != dptr_save) |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
463 |
(void) munmap((void *)*dptr, *ndata); |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
464 |
|
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
465 |
/* |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
466 |
* restore the buffer size and header |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
467 |
* data so that the front door will |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
468 |
* see the original request |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
469 |
*/ |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
470 |
*ndata = ndata_save; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
471 |
*adata = adata_save; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
472 |
*dptr = dptr_save; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
473 |
ph = (nss_pheader_t *)*dptr; |
2830 | 474 |
*ph = ph_save; |
475 |
/* |
|
476 |
* tell the front door server, this is |
|
477 |
* a fallback call |
|
478 |
*/ |
|
479 |
ph->p_status = NSS_ALTRETRY; |
|
480 |
continue; |
|
481 |
} |
|
482 |
||
483 |
/* return the result or error */ |
|
484 |
break; |
|
485 |
} |
|
486 |
||
487 |
/* try the front door */ |
|
488 |
fd = -1; |
|
489 |
ret = _nsc_try1door(frontd, dptr, ndata, adata, &fd); |
|
490 |
||
491 |
if (ret != NSS_ALTRETRY) { |
|
492 |
/* |
|
493 |
* got a success or failure result. |
|
494 |
* but front door should never send NSS_ALTRESET |
|
495 |
*/ |
|
496 |
if (ret == NSS_ALTRESET) |
|
497 |
/* reset the front door */ |
|
498 |
reset_frontd = 1; |
|
499 |
else |
|
500 |
/* |
|
501 |
* not NSS_ALTRETRY and not NSS_ALTRESET |
|
502 |
* return the result or error |
|
503 |
*/ |
|
504 |
break; |
|
505 |
} else if (fb2frontd == 1) { |
|
506 |
/* |
|
507 |
* front door should never send NSS_ALTRETRY |
|
508 |
* in a fallback call. Reset the front door. |
|
509 |
*/ |
|
510 |
reset_frontd = 1; |
|
511 |
} |
|
512 |
||
513 |
if (reset_frontd == 1) { |
|
514 |
lmutex_lock(&frontd->door_lock); |
|
515 |
frontd->doorfd = -1; |
|
516 |
(void) memset((void *)&frontd->doori, |
|
6351 | 517 |
'\0', sizeof (door_info_t)); |
2830 | 518 |
lmutex_unlock(&frontd->door_lock); |
519 |
/* error out */ |
|
520 |
ret = NSS_ERROR; |
|
521 |
break; |
|
522 |
} |
|
523 |
||
524 |
/* process NSS_ALTRETRY request from front door */ |
|
525 |
if (fd < 0) |
|
526 |
continue; /* no new door given, try again */ |
|
527 |
||
528 |
/* update and try alternate door */ |
|
529 |
lmutex_lock(&backd->door_lock); |
|
530 |
if (backd->doorfd >= 0) { |
|
531 |
/* unexpected open alt door - clean up, continue */ |
|
532 |
_nsc_flush_private_db(); |
|
533 |
(void) close(backd->doorfd); |
|
534 |
} |
|
535 |
||
536 |
/* set up back door fd */ |
|
537 |
backd->doorfd = fd; |
|
538 |
||
539 |
/* set up back door db list */ |
|
540 |
ph = (nss_pheader_t *)*dptr; |
|
541 |
dbl = ((char *)ph) + ph->data_off; |
|
542 |
||
543 |
if (_nsc_init_private_db(dbl) == 0) { |
|
544 |
/* could not init db list, try again */ |
|
545 |
(void) close(backd->doorfd); |
|
546 |
backd->doorfd = -1; |
|
547 |
lmutex_unlock(&backd->door_lock); |
|
548 |
continue; |
|
549 |
} |
|
550 |
if (door_info(backd->doorfd, &backd->doori) < 0 || |
|
6351 | 551 |
(backd->doori.di_attributes & DOOR_REVOKED) || |
552 |
backd->doori.di_data != |
|
553 |
(uintptr_t)NAME_SERVICE_DOOR_COOKIE) { |
|
2830 | 554 |
/* doorfd bad, or must not really be open */ |
555 |
(void) close(backd->doorfd); |
|
556 |
backd->doorfd = -1; |
|
557 |
(void) memset((void *)&backd->doori, |
|
6351 | 558 |
'\0', sizeof (door_info_t)); |
2830 | 559 |
} |
560 |
(void) fcntl(backd->doorfd, F_SETFD, FD_CLOEXEC); |
|
561 |
lmutex_unlock(&backd->door_lock); |
|
562 |
/* NSS_ALTRETRY new back door */ |
|
4142
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
563 |
if (*dptr != dptr_save) |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
564 |
(void) munmap((void *)*dptr, *ndata); |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
565 |
|
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
566 |
/* |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
567 |
* restore the buffer size and header |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
568 |
* data so that the back door will |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
569 |
* see the original request |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
570 |
*/ |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
571 |
*ndata = ndata_save; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
572 |
*adata = adata_save; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
573 |
*dptr = dptr_save; |
0d7fa83a8b00
6548196 Multiple memory corruption vulnerabilities in nscd(1M)
michen
parents:
2830
diff
changeset
|
574 |
ph = (nss_pheader_t *)*dptr; |
2830 | 575 |
*ph = ph_save; |
576 |
} |
|
577 |
return (ret); |
|
578 |
} |
|
579 |
||
580 |
/* |
|
581 |
* Get the current (but growable) buffer size for a NSS2 packet. |
|
582 |
* Heuristic algorithm used: |
|
583 |
* 1) Make sure it's at least NSS_BUFLEN_DOOR in length (16k default) |
|
584 |
* 2) if an incoming user buffer is > larger than the current size |
|
585 |
* Make the buffer at least NSS_BUFLEN_DOOR/2+user buffer size |
|
586 |
* This should account for any reasonable nss_pheader, keys |
|
587 |
* extended area etc. |
|
588 |
* 3) keep the prototype/debugging (private)NSS_BUFLEN option |
|
589 |
* to change any preconfigured value if needed(?) |
|
590 |
*/ |
|
591 |
||
592 |
static size_t |
|
593 |
_nsc_getdoorbsize(size_t min_size) |
|
594 |
{ |
|
595 |
if (!door_bsize) { |
|
596 |
lmutex_lock(&hints_lock); |
|
597 |
if (!door_bsize) { |
|
598 |
/* future work - get nscd hint & use hint size */ |
|
599 |
door_bsize = ROUND_UP(door_bsize, NSS_BUFSIZ); |
|
600 |
if (door_bsize < NSS_BUFLEN_DOOR) { |
|
601 |
door_bsize = NSS_BUFLEN_DOOR; |
|
602 |
} |
|
603 |
} |
|
604 |
lmutex_unlock(&hints_lock); |
|
605 |
} |
|
606 |
if (min_size && door_bsize < (min_size + NSS_BUFLEN_DOOR/2)) { |
|
607 |
lmutex_lock(&hints_lock); |
|
608 |
if (door_bsize < (min_size + NSS_BUFLEN_DOOR/2)) { |
|
609 |
min_size += NSS_BUFLEN_DOOR; |
|
610 |
door_bsize = ROUND_UP(min_size, NSS_BUFSIZ); |
|
611 |
} |
|
612 |
lmutex_unlock(&hints_lock); |
|
613 |
} |
|
614 |
return (door_bsize); |
|
615 |
} |
|
616 |
||
617 |
static void |
|
618 |
_nsc_freedbuf(void *arg) |
|
619 |
{ |
|
620 |
nss_XbyY_buf_t *tsdbuf = arg; |
|
621 |
||
622 |
if (tsdbuf != NULL && tsdbuf->buffer != NULL) { |
|
623 |
lfree(tsdbuf->buffer, (size_t)tsdbuf->buflen); |
|
624 |
tsdbuf->result = NULL; |
|
625 |
tsdbuf->buffer = NULL; |
|
626 |
tsdbuf->buflen = 0; |
|
627 |
} |
|
628 |
} |
|
629 |
||
630 |
/* |
|
631 |
* _nsc_getdoorbuf - return the client side per thread door buffer |
|
632 |
* Elsewhere, it is assumed that the header is 0'd upon return from here. |
|
633 |
*/ |
|
634 |
||
635 |
int |
|
636 |
_nsc_getdoorbuf(void **doorptr, size_t *bufsize) |
|
637 |
{ |
|
638 |
nss_XbyY_buf_t *tsdbuf; |
|
639 |
char *bp; |
|
640 |
size_t dsize; |
|
641 |
||
642 |
if (doorptr == NULL || bufsize == NULL) |
|
643 |
return (-1); |
|
644 |
||
645 |
/* Get thread specific pointer to door buffer */ |
|
646 |
tsdbuf = tsdalloc(_T_DOORBUF, sizeof (nss_XbyY_buf_t), _nsc_freedbuf); |
|
647 |
if (tsdbuf == NULL) |
|
648 |
return (-1); |
|
649 |
||
650 |
/* if door buffer does not exist create it */ |
|
651 |
if (tsdbuf->buffer == NULL) { |
|
652 |
dsize = _nsc_getdoorbsize(*bufsize); |
|
653 |
||
654 |
/* setup a door buffer with a total length of dsize */ |
|
655 |
bp = lmalloc(dsize); |
|
656 |
if (bp == NULL) |
|
657 |
return (-1); |
|
658 |
tsdbuf->buffer = bp; |
|
659 |
tsdbuf->buflen = dsize; |
|
660 |
} else { |
|
661 |
/* check old buffer size and resize if needed */ |
|
662 |
if (*bufsize) { |
|
663 |
dsize = _nsc_getdoorbsize(*bufsize); |
|
664 |
if (tsdbuf->buflen < dsize) { |
|
665 |
lfree(tsdbuf->buffer, (size_t)tsdbuf->buflen); |
|
666 |
bp = lmalloc(dsize); |
|
667 |
if (bp == NULL) |
|
668 |
return (-1); |
|
669 |
tsdbuf->buffer = bp; |
|
670 |
tsdbuf->buflen = dsize; |
|
671 |
} |
|
672 |
} |
|
673 |
/* freshly malloc'd door bufs are 0'd */ |
|
674 |
/* 0 header for now. Zero entire buf(?) TDB */ |
|
675 |
(void) memset((void *)tsdbuf->buffer, 0, |
|
6351 | 676 |
(size_t)sizeof (nss_pheader_t)); |
2830 | 677 |
|
678 |
} |
|
679 |
*doorptr = (void *)tsdbuf->buffer; |
|
680 |
*bufsize = tsdbuf->buflen; |
|
681 |
return (0); |
|
682 |
} |
|
683 |
||
684 |
void |
|
685 |
_nsc_resizedoorbuf(size_t bsize) |
|
686 |
{ |
|
687 |
/* signal to update if new door size is desired */ |
|
688 |
lmutex_lock(&hints_lock); |
|
689 |
if (bsize > door_bsize && door_nbsize < bsize) |
|
690 |
door_nbsize = bsize; |
|
691 |
lmutex_unlock(&hints_lock); |
|
692 |
} |
|
693 |
||
694 |
/* |
|
695 |
* Check uid and /proc/PID/psinfo to see if this process is nscd |
|
696 |
* If it is set the appropriate flags and allow policy reconfiguration. |
|
697 |
*/ |
|
698 |
int |
|
699 |
_nsc_proc_is_cache() |
|
700 |
{ |
|
701 |
psinfo_t pinfo; |
|
702 |
char fname[128]; |
|
703 |
int ret; |
|
704 |
int fd; |
|
705 |
||
706 |
if (proc_is_cache >= 0) |
|
707 |
return (proc_is_cache); |
|
708 |
lmutex_lock(&hints_lock); |
|
709 |
if (proc_is_cache >= 0) { |
|
710 |
lmutex_unlock(&hints_lock); |
|
711 |
return (proc_is_cache); |
|
712 |
} |
|
713 |
proc_is_cache = 0; |
|
714 |
/* It can't be nscd if it's not running as root... */ |
|
715 |
if (getuid() != 0) { |
|
716 |
lmutex_unlock(&hints_lock); |
|
717 |
return (0); |
|
718 |
} |
|
719 |
ret = snprintf(fname, 128, "/proc/%d/psinfo", getpid()); |
|
720 |
if (ret > 0 && ret < 128) { |
|
6351 | 721 |
if ((fd = open(fname, O_RDONLY)) >= 0) { |
2830 | 722 |
ret = read(fd, &pinfo, sizeof (psinfo_t)); |
723 |
(void) close(fd); |
|
724 |
if (ret == sizeof (psinfo_t) && |
|
725 |
(strcmp(pinfo.pr_fname, "nscd") == 0)) { |
|
726 |
/* process runs as root and is named nscd */ |
|
727 |
/* that's good enough for now */ |
|
728 |
proc_is_cache = 1; |
|
729 |
} |
|
730 |
} |
|
731 |
} |
|
732 |
lmutex_unlock(&hints_lock); |
|
733 |
return (proc_is_cache); |
|
734 |
} |