handle.c 22.4 KB
Newer Older
1 2 3 4
/*
 * Server-side handle management
 *
 * Copyright (C) 1998 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21 22 23
#include "config.h"
#include "wine/port.h"

24 25 26
#include <assert.h>
#include <limits.h>
#include <string.h>
27
#include <stdarg.h>
28 29 30
#include <stdio.h>
#include <stdlib.h>

31 32
#include "ntstatus.h"
#define WIN32_NO_STATUS
33
#include "windef.h"
34
#include "winternl.h"
35 36 37 38

#include "handle.h"
#include "process.h"
#include "thread.h"
39
#include "security.h"
40
#include "request.h"
41 42 43

struct handle_entry
{
44 45
    struct object *ptr;       /* object */
    unsigned int   access;    /* access rights */
46 47
};

48 49 50 51 52 53 54 55 56 57 58
struct handle_table
{
    struct object        obj;         /* object header */
    struct process      *process;     /* process owning this table */
    int                  count;       /* number of allocated entries */
    int                  last;        /* last used entry */
    int                  free;        /* first entry that may be free */
    struct handle_entry *entries;     /* handle entries */
};

static struct handle_table *global_table;
59 60

/* reserved handle access rights */
61
#define RESERVED_SHIFT         26
62 63 64 65 66
#define RESERVED_INHERIT       (HANDLE_FLAG_INHERIT << RESERVED_SHIFT)
#define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE << RESERVED_SHIFT)
#define RESERVED_ALL           (RESERVED_INHERIT | RESERVED_CLOSE_PROTECT)

#define MIN_HANDLE_ENTRIES  32
67
#define MAX_HANDLE_ENTRIES  0x00ffffff
68

69

70 71
/* handle to table index conversion */

72
/* handles are a multiple of 4 under NT; handle 0 is not used */
73
static inline obj_handle_t index_to_handle( int index )
74
{
75
    return (obj_handle_t)((index + 1) << 2);
76
}
77
static inline int handle_to_index( obj_handle_t handle )
78
{
79
    return (handle >> 2) - 1;
80 81 82 83 84 85
}

/* global handle conversion */

#define HANDLE_OBFUSCATOR 0x544a4def

86
static inline int handle_is_global( obj_handle_t handle)
87
{
88
    return (handle ^ HANDLE_OBFUSCATOR) <= (MAX_HANDLE_ENTRIES << 2);
89
}
90
static inline obj_handle_t handle_local_to_global( obj_handle_t handle )
91
{
92
    if (!handle) return 0;
93
    return handle ^ HANDLE_OBFUSCATOR;
94
}
95
static inline obj_handle_t handle_global_to_local( obj_handle_t handle )
96
{
97
    return handle ^ HANDLE_OBFUSCATOR;
98 99 100
}


101 102 103 104 105
static void handle_table_dump( struct object *obj, int verbose );
static void handle_table_destroy( struct object *obj );

static const struct object_ops handle_table_ops =
{
106 107
    sizeof(struct handle_table),     /* size */
    handle_table_dump,               /* dump */
108
    no_get_type,                     /* get_type */
109 110 111 112
    no_add_queue,                    /* add_queue */
    NULL,                            /* remove_queue */
    NULL,                            /* signaled */
    NULL,                            /* satisfied */
113
    no_signal,                       /* signal */
114
    no_get_fd,                       /* get_fd */
115
    no_map_access,                   /* map_access */
116 117
    default_get_sd,                  /* get_sd */
    default_set_sd,                  /* set_sd */
118
    no_lookup_name,                  /* lookup_name */
119
    no_open_file,                    /* open_file */
120
    no_close_handle,                 /* close_handle */
121
    handle_table_destroy             /* destroy */
122 123 124 125 126 127 128
};

/* dump a handle table */
static void handle_table_dump( struct object *obj, int verbose )
{
    int i;
    struct handle_table *table = (struct handle_table *)obj;
129
    struct handle_entry *entry;
130 131 132 133 134 135 136 137 138 139

    assert( obj->ops == &handle_table_ops );

    fprintf( stderr, "Handle table last=%d count=%d process=%p\n",
             table->last, table->count, table->process );
    if (!verbose) return;
    entry = table->entries;
    for (i = 0; i <= table->last; i++, entry++)
    {
        if (!entry->ptr) continue;
140
        fprintf( stderr, "    %04x: %p %08x ",
141
                 index_to_handle(i), entry->ptr, entry->access );
142 143 144 145 146 147 148 149 150
        entry->ptr->ops->dump( entry->ptr, 0 );
    }
}

/* destroy a handle table */
static void handle_table_destroy( struct object *obj )
{
    int i;
    struct handle_table *table = (struct handle_table *)obj;
151
    struct handle_entry *entry;
152 153 154

    assert( obj->ops == &handle_table_ops );

155 156 157 158 159 160 161 162 163 164 165
    /* first notify all objects that handles are being closed */
    if (table->process)
    {
        for (i = 0, entry = table->entries; i <= table->last; i++, entry++)
        {
            struct object *obj = entry->ptr;
            if (obj) obj->ops->close_handle( obj, table->process, index_to_handle(i) );
        }
    }

    for (i = 0, entry = table->entries; i <= table->last; i++, entry++)
166 167 168 169 170 171 172 173
    {
        struct object *obj = entry->ptr;
        entry->ptr = NULL;
        if (obj) release_object( obj );
    }
    free( table->entries );
}

174 175 176 177 178 179 180 181 182
/* close all the process handles and free the handle table */
void close_process_handles( struct process *process )
{
    struct handle_table *table = process->handles;

    process->handles = NULL;
    if (table) release_object( table );
}

183
/* allocate a new handle table */
184
struct handle_table *alloc_handle_table( struct process *process, int count )
185 186 187 188
{
    struct handle_table *table;

    if (count < MIN_HANDLE_ENTRIES) count = MIN_HANDLE_ENTRIES;
189
    if (!(table = alloc_object( &handle_table_ops )))
190 191 192 193 194
        return NULL;
    table->process = process;
    table->count   = count;
    table->last    = -1;
    table->free    = 0;
195
    if ((table->entries = mem_alloc( count * sizeof(*table->entries) ))) return table;
196 197 198 199
    release_object( table );
    return NULL;
}

200
/* grow a handle table */
201
static int grow_handle_table( struct handle_table *table )
202 203
{
    struct handle_entry *new_entries;
204
    int count = min( table->count * 2, MAX_HANDLE_ENTRIES );
205

206 207
    if (count == table->count ||
        !(new_entries = realloc( table->entries, count * sizeof(struct handle_entry) )))
208
    {
209
        set_error( STATUS_INSUFFICIENT_RESOURCES );
210 211
        return 0;
    }
212 213
    table->entries = new_entries;
    table->count   = count;
214 215 216
    return 1;
}

217
/* allocate the first free entry in the handle table */
218
static obj_handle_t alloc_entry( struct handle_table *table, void *obj, unsigned int access )
219 220
{
    struct handle_entry *entry = table->entries + table->free;
221
    int i;
222

223 224
    for (i = table->free; i <= table->last; i++, entry++) if (!entry->ptr) goto found;
    if (i >= table->count)
225
    {
226
        if (!grow_handle_table( table )) return 0;
227
        entry = table->entries + i;  /* the entries may have moved */
228
    }
229
    table->last = i;
230
 found:
231 232 233 234
    table->free = i + 1;
    entry->ptr    = grab_object( obj );
    entry->access = access;
    return index_to_handle(i);
235 236
}

237
/* allocate a handle for an object, incrementing its refcount */
238
/* return the handle, or 0 on error */
239
obj_handle_t alloc_handle_no_access_check( struct process *process, void *ptr, unsigned int access, unsigned int attr )
240
{
241 242
    struct object *obj = ptr;

243
    access &= ~RESERVED_ALL;
244
    if (attr & OBJ_INHERIT) access |= RESERVED_INHERIT;
245 246 247 248 249 250
    if (!process->handles)
    {
        set_error( STATUS_NO_MEMORY );
        return 0;
    }
    return alloc_entry( process->handles, obj, access );
251
}
252

253 254 255 256 257 258 259 260 261 262 263
/* allocate a handle for an object, checking the dacl allows the process to */
/* access it and incrementing its refcount */
/* return the handle, or 0 on error */
obj_handle_t alloc_handle( struct process *process, void *ptr, unsigned int access, unsigned int attr )
{
    struct object *obj = ptr;
    access = obj->ops->map_access( obj, access );
    if (access && !check_object_access( obj, &access )) return 0;
    return alloc_handle_no_access_check( process, ptr, access, attr );
}

264
/* allocate a global handle for an object, incrementing its refcount */
265
/* return the handle, or 0 on error */
266
static obj_handle_t alloc_global_handle_no_access_check( void *obj, unsigned int access )
267 268
{
    if (!global_table)
269
    {
270
        if (!(global_table = alloc_handle_table( NULL, 0 )))
271
            return 0;
272
        make_object_static( &global_table->obj );
273
    }
274
    return handle_local_to_global( alloc_entry( global_table, obj, access ));
275 276
}

277 278 279 280 281 282 283 284 285
/* allocate a global handle for an object, checking the dacl allows the */
/* process to access it and incrementing its refcount and incrementing its refcount */
/* return the handle, or 0 on error */
static obj_handle_t alloc_global_handle( void *obj, unsigned int access )
{
    if (access && !check_object_access( obj, &access )) return 0;
    return alloc_global_handle_no_access_check( obj, access );
}

286
/* return a handle entry, or NULL if the handle is invalid */
287
static struct handle_entry *get_handle( struct process *process, obj_handle_t handle )
288
{
289
    struct handle_table *table = process->handles;
290
    struct handle_entry *entry;
291
    int index;
292

293
    if (handle_is_global(handle))
294
    {
295
        handle = handle_global_to_local(handle);
296
        table = global_table;
297
    }
298
    if (!table) return NULL;
299
    index = handle_to_index( handle );
300 301
    if (index < 0) return NULL;
    if (index > table->last) return NULL;
302
    entry = table->entries + index;
303
    if (!entry->ptr) return NULL;
304 305 306 307
    return entry;
}

/* attempt to shrink a table */
308
static void shrink_handle_table( struct handle_table *table )
309
{
310
    struct handle_entry *entry = table->entries + table->last;
311
    struct handle_entry *new_entries;
312
    int count = table->count;
313

314
    while (table->last >= 0)
315 316
    {
        if (entry->ptr) break;
317
        table->last--;
318 319
        entry--;
    }
320 321
    if (table->last >= count / 4) return;  /* no need to shrink */
    if (count < MIN_HANDLE_ENTRIES * 2) return;  /* too small to shrink */
322
    count /= 2;
323 324 325
    if (!(new_entries = realloc( table->entries, count * sizeof(*new_entries) ))) return;
    table->count   = count;
    table->entries = new_entries;
326 327 328 329
}

/* copy the handle table of the parent process */
/* return 1 if OK, 0 on error */
330
struct handle_table *copy_handle_table( struct process *process, struct process *parent )
331
{
332
    struct handle_table *parent_table = parent->handles;
333 334
    struct handle_table *table;
    int i;
335

336 337
    assert( parent_table );
    assert( parent_table->obj.ops == &handle_table_ops );
338

339
    if (!(table = alloc_handle_table( process, parent_table->count )))
340
        return NULL;
341

342
    if ((table->last = parent_table->last) >= 0)
343
    {
344 345 346
        struct handle_entry *ptr = table->entries;
        memcpy( ptr, parent_table->entries, (table->last + 1) * sizeof(struct handle_entry) );
        for (i = 0; i <= table->last; i++, ptr++)
347 348 349 350 351 352 353
        {
            if (!ptr->ptr) continue;
            if (ptr->access & RESERVED_INHERIT) grab_object( ptr->ptr );
            else ptr->ptr = NULL; /* don't inherit this entry */
        }
    }
    /* attempt to shrink the table */
354
    shrink_handle_table( table );
355
    return table;
356 357 358
}

/* close a handle and decrement the refcount of the associated object */
359
unsigned int close_handle( struct process *process, obj_handle_t handle )
360
{
361
    struct handle_table *table;
362 363 364
    struct handle_entry *entry;
    struct object *obj;

365 366
    if (!(entry = get_handle( process, handle ))) return STATUS_INVALID_HANDLE;
    if (entry->access & RESERVED_CLOSE_PROTECT) return STATUS_HANDLE_NOT_CLOSABLE;
367
    obj = entry->ptr;
368
    if (!obj->ops->close_handle( obj, process, handle )) return STATUS_HANDLE_NOT_CLOSABLE;
369
    entry->ptr = NULL;
370
    table = handle_is_global(handle) ? global_table : process->handles;
371 372
    if (entry < table->entries + table->free) table->free = entry - table->entries;
    if (entry == table->entries + table->last) shrink_handle_table( table );
373
    release_object( obj );
374
    return STATUS_SUCCESS;
375 376
}

377
/* retrieve the object corresponding to one of the magic pseudo-handles */
378
static inline struct object *get_magic_handle( obj_handle_t handle )
379
{
380
    switch(handle)
381 382 383 384 385 386 387 388 389 390 391
    {
        case 0xfffffffe:  /* current thread pseudo-handle */
            return &current->obj;
        case 0x7fffffff:  /* current process pseudo-handle */
        case 0xffffffff:  /* current process pseudo-handle */
            return (struct object *)current->process;
        default:
            return NULL;
    }
}

392
/* retrieve the object corresponding to a handle, incrementing its refcount */
393
struct object *get_handle_obj( struct process *process, obj_handle_t handle,
394 395 396 397 398
                               unsigned int access, const struct object_ops *ops )
{
    struct handle_entry *entry;
    struct object *obj;

399
    if (!(obj = get_magic_handle( handle )))
400
    {
401 402 403 404 405
        if (!(entry = get_handle( process, handle )))
        {
            set_error( STATUS_INVALID_HANDLE );
            return NULL;
        }
406 407 408 409 410 411
        obj = entry->ptr;
        if (ops && (obj->ops != ops))
        {
            set_error( STATUS_OBJECT_TYPE_MISMATCH );  /* not the right type */
            return NULL;
        }
412 413
        if ((entry->access & access) != access)
        {
414
            set_error( STATUS_ACCESS_DENIED );
415 416 417
            return NULL;
        }
    }
418
    else if (ops && (obj->ops != ops))
419
    {
420
        set_error( STATUS_OBJECT_TYPE_MISMATCH );  /* not the right type */
421 422 423 424 425
        return NULL;
    }
    return grab_object( obj );
}

426 427 428 429 430
/* retrieve the access rights of a given handle */
unsigned int get_handle_access( struct process *process, obj_handle_t handle )
{
    struct handle_entry *entry;

431
    if (get_magic_handle( handle )) return ~RESERVED_ALL;  /* magic handles have all access rights */
432
    if (!(entry = get_handle( process, handle ))) return 0;
433
    return entry->access & ~RESERVED_ALL;
434 435
}

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
/* find the first inherited handle of the given type */
/* this is needed for window stations and desktops (don't ask...) */
obj_handle_t find_inherited_handle( struct process *process, const struct object_ops *ops )
{
    struct handle_table *table = process->handles;
    struct handle_entry *ptr;
    int i;

    if (!table) return 0;

    for (i = 0, ptr = table->entries; i <= table->last; i++, ptr++)
    {
        if (!ptr->ptr) continue;
        if (ptr->ptr->ops != ops) continue;
        if (ptr->access & RESERVED_INHERIT) return index_to_handle(i);
    }
    return 0;
}

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
/* enumerate handles of a given type */
/* this is needed for window stations and desktops */
obj_handle_t enumerate_handles( struct process *process, const struct object_ops *ops,
                                unsigned int *index )
{
    struct handle_table *table = process->handles;
    unsigned int i;
    struct handle_entry *entry;

    if (!table) return 0;

    for (i = *index, entry = &table->entries[i]; i <= table->last; i++, entry++)
    {
        if (!entry->ptr) continue;
        if (entry->ptr->ops != ops) continue;
        *index = i + 1;
        return index_to_handle(i);
    }
    return 0;
}

476
/* get/set the handle reserved flags */
477
/* return the old flags (or -1 on error) */
478
static int set_handle_flags( struct process *process, obj_handle_t handle, int mask, int flags )
479 480
{
    struct handle_entry *entry;
481
    unsigned int old_access;
482

483 484 485
    if (get_magic_handle( handle ))
    {
        /* we can retrieve but not set info for magic handles */
486
        if (mask) set_error( STATUS_ACCESS_DENIED );
487 488
        return 0;
    }
489 490 491 492 493
    if (!(entry = get_handle( process, handle )))
    {
        set_error( STATUS_INVALID_HANDLE );
        return -1;
    }
494
    old_access = entry->access;
495 496 497
    mask  = (mask << RESERVED_SHIFT) & RESERVED_ALL;
    flags = (flags << RESERVED_SHIFT) & mask;
    entry->access = (entry->access & ~mask) | flags;
498
    return (old_access & RESERVED_ALL) >> RESERVED_SHIFT;
499 500 501
}

/* duplicate a handle */
502
obj_handle_t duplicate_handle( struct process *src, obj_handle_t src_handle, struct process *dst,
503
                               unsigned int access, unsigned int attr, unsigned int options )
504
{
505
    obj_handle_t res;
506 507
    struct handle_entry *entry;
    unsigned int src_access;
508
    struct object *obj = get_handle_obj( src, src_handle, 0, NULL );
509

510
    if (!obj) return 0;
511 512 513 514 515 516
    if ((entry = get_handle( src, src_handle )))
        src_access = entry->access;
    else  /* pseudo-handle, give it full access */
        src_access = obj->ops->map_access( obj, GENERIC_ALL );
    src_access &= ~RESERVED_ALL;

517
    if (options & DUP_HANDLE_SAME_ACCESS)
518 519 520 521 522 523
        access = src_access;
    else
        access = obj->ops->map_access( obj, access ) & ~RESERVED_ALL;

    /* asking for the more access rights than src_access? */
    if (access & ~src_access)
524
    {
525 526 527 528
        if (options & DUP_HANDLE_MAKE_GLOBAL)
            res = alloc_global_handle( obj, access );
        else
            res = alloc_handle( dst, obj, access, attr );
529
    }
530
    else
531 532 533 534 535 536 537
    {
        if (options & DUP_HANDLE_MAKE_GLOBAL)
            res = alloc_global_handle_no_access_check( obj, access );
        else
            res = alloc_handle_no_access_check( dst, obj, access, attr );
    }

538
    release_object( obj );
539 540 541 542
    return res;
}

/* open a new handle to an existing object */
543
obj_handle_t open_object( const struct namespace *namespace, const struct unicode_str *name,
544
                          const struct object_ops *ops, unsigned int access, unsigned int attr )
545
{
546
    obj_handle_t handle = 0;
547
    struct object *obj = find_object( namespace, name, attr );
548
    if (obj)
549
    {
550
        if (ops && obj->ops != ops)
551
            set_error( STATUS_OBJECT_TYPE_MISMATCH );
552
        else
553
            handle = alloc_handle( current->process, obj, access, attr );
554 555
        release_object( obj );
    }
556
    else
557
        set_error( STATUS_OBJECT_NAME_NOT_FOUND );
558
    return handle;
559 560
}

561 562 563
/* return the size of the handle table of a given process */
unsigned int get_handle_table_count( struct process *process )
{
564
    if (!process->handles) return 0;
565 566 567
    return process->handles->count;
}

568 569 570
/* close a handle */
DECL_HANDLER(close_handle)
{
571 572
    unsigned int err = close_handle( current->process, req->handle );
    set_error( err );
573 574 575 576
}

/* set a handle information */
DECL_HANDLER(set_handle_info)
577 578 579 580
{
    reply->old_flags = set_handle_flags( current->process, req->handle, req->mask, req->flags );
}

581 582 583 584 585
/* duplicate a handle */
DECL_HANDLER(dup_handle)
{
    struct process *src, *dst;

586
    reply->handle = 0;
587 588 589 590
    if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
    {
        if (req->options & DUP_HANDLE_MAKE_GLOBAL)
        {
591
            reply->handle = duplicate_handle( src, req->src_handle, NULL,
592
                                              req->access, req->attributes, req->options );
593 594 595
        }
        else if ((dst = get_process_from_handle( req->dst_process, PROCESS_DUP_HANDLE )))
        {
596
            reply->handle = duplicate_handle( src, req->src_handle, dst,
597
                                              req->access, req->attributes, req->options );
598 599 600
            release_object( dst );
        }
        /* close the handle no matter what happened */
601
        if (req->options & DUP_HANDLE_CLOSE_SOURCE) reply->closed = !close_handle( src, req->src_handle );
602
        reply->self = (src == current->process);
603 604 605
        release_object( src );
    }
}
606 607 608 609

DECL_HANDLER(get_object_info)
{
    struct object *obj;
610
    WCHAR *name;
611 612 613 614 615

    if (!(obj = get_handle_obj( current->process, req->handle, 0, NULL ))) return;

    reply->access = get_handle_access( current->process, req->handle );
    reply->ref_count = obj->refcount;
616 617
    if ((name = get_object_full_name( obj, &reply->total )))
        set_reply_data_ptr( name, min( reply->total, get_reply_max_size() ));
618 619
    release_object( obj );
}
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

DECL_HANDLER(set_security_object)
{
    data_size_t sd_size = get_req_data_size();
    const struct security_descriptor *sd = get_req_data();
    struct object *obj;
    unsigned int access = 0;

    if (!sd_is_valid( sd, sd_size ))
    {
        set_error( STATUS_ACCESS_VIOLATION );
        return;
    }

    if (req->security_info & OWNER_SECURITY_INFORMATION ||
        req->security_info & GROUP_SECURITY_INFORMATION)
        access |= WRITE_OWNER;
    if (req->security_info & SACL_SECURITY_INFORMATION)
        access |= ACCESS_SYSTEM_SECURITY;
    if (req->security_info & DACL_SECURITY_INFORMATION)
        access |= WRITE_DAC;

    if (!(obj = get_handle_obj( current->process, req->handle, access, NULL ))) return;

644
    obj->ops->set_sd( obj, sd, req->security_info );
645 646
    release_object( obj );
}
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662

DECL_HANDLER(get_security_object)
{
    const struct security_descriptor *sd;
    struct object *obj;
    unsigned int access = READ_CONTROL;
    struct security_descriptor req_sd;
    int present;
    const SID *owner, *group;
    const ACL *sacl, *dacl;

    if (req->security_info & SACL_SECURITY_INFORMATION)
        access |= ACCESS_SYSTEM_SECURITY;

    if (!(obj = get_handle_obj( current->process, req->handle, access, NULL ))) return;

663
    sd = obj->ops->get_sd( obj );
664 665 666 667 668 669 670
    if (sd)
    {
        req_sd.control = sd->control & ~SE_SELF_RELATIVE;

        owner = sd_get_owner( sd );
        if (req->security_info & OWNER_SECURITY_INFORMATION)
            req_sd.owner_len = sd->owner_len;
671 672
        else
            req_sd.owner_len = 0;
673 674 675 676

        group = sd_get_group( sd );
        if (req->security_info & GROUP_SECURITY_INFORMATION)
            req_sd.group_len = sd->group_len;
677 678
        else
            req_sd.group_len = 0;
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715

        req_sd.control |= SE_SACL_PRESENT;
        sacl = sd_get_sacl( sd, &present );
        if (req->security_info & SACL_SECURITY_INFORMATION && present)
            req_sd.sacl_len = sd->sacl_len;
        else
            req_sd.sacl_len = 0;

        req_sd.control |= SE_DACL_PRESENT;
        dacl = sd_get_dacl( sd, &present );
        if (req->security_info & DACL_SECURITY_INFORMATION && present)
            req_sd.dacl_len = sd->dacl_len;
        else
            req_sd.dacl_len = 0;

        reply->sd_len = sizeof(req_sd) + req_sd.owner_len + req_sd.group_len +
            req_sd.sacl_len + req_sd.dacl_len;
        if (reply->sd_len <= get_reply_max_size())
        {
            char *ptr = set_reply_data_size(reply->sd_len);

            memcpy( ptr, &req_sd, sizeof(req_sd) );
            ptr += sizeof(req_sd);
            memcpy( ptr, owner, req_sd.owner_len );
            ptr += req_sd.owner_len;
            memcpy( ptr, group, req_sd.group_len );
            ptr += req_sd.group_len;
            memcpy( ptr, sacl, req_sd.sacl_len );
            ptr += req_sd.sacl_len;
            memcpy( ptr, dacl, req_sd.dacl_len );
        }
        else
            set_error(STATUS_BUFFER_TOO_SMALL);
    }

    release_object( obj );
}