minidlna support now Samsung TV C550/C650 (thx amir909)
[tomato.git] / release / src / router / ntfs-3g / libntfs-3g / security.c
blobb0bbe6ba0ce71c280a34a8ab296a299f0d4ed120
1 /**
2 * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project.
4 * Copyright (c) 2004 Anton Altaparmakov
5 * Copyright (c) 2005-2006 Szabolcs Szakacsits
6 * Copyright (c) 2006 Yura Pakhuchiy
7 * Copyright (c) 2007-2010 Jean-Pierre Andre
9 * This program/include file is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program/include file is distributed in the hope that it will be
15 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program (in the main directory of the NTFS-3G
21 * distribution in the file COPYING); if not, write to the Free Software
22 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #ifdef HAVE_STDIO_H
30 #include <stdio.h>
31 #endif
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38 #ifdef HAVE_ERRNO_H
39 #include <errno.h>
40 #endif
41 #ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 #endif
44 #ifdef HAVE_SETXATTR
45 #include <sys/xattr.h>
46 #endif
47 #ifdef HAVE_SYS_STAT_H
48 #include <sys/stat.h>
49 #endif
51 #include <unistd.h>
52 #include <pwd.h>
53 #include <grp.h>
55 #include "param.h"
56 #include "types.h"
57 #include "layout.h"
58 #include "attrib.h"
59 #include "index.h"
60 #include "dir.h"
61 #include "bitmap.h"
62 #include "security.h"
63 #include "acls.h"
64 #include "cache.h"
65 #include "misc.h"
68 * JPA NTFS constants or structs
69 * should be moved to layout.h
72 #define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */
73 #define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */
74 #define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */
75 #define FIRST_SECURITY_ID 0x100 /* Lowest security id */
77 /* Mask for attributes which can be forced */
78 #define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \
79 | FILE_ATTR_HIDDEN \
80 | FILE_ATTR_SYSTEM \
81 | FILE_ATTR_ARCHIVE \
82 | FILE_ATTR_TEMPORARY \
83 | FILE_ATTR_OFFLINE \
84 | FILE_ATTR_NOT_CONTENT_INDEXED )
86 struct SII { /* this is an image of an $SII index entry */
87 le16 offs;
88 le16 size;
89 le32 fill1;
90 le16 indexsz;
91 le16 indexksz;
92 le16 flags;
93 le16 fill2;
94 le32 keysecurid;
96 /* did not find official description for the following */
97 le32 hash;
98 le32 securid;
99 le32 dataoffsl; /* documented as badly aligned */
100 le32 dataoffsh;
101 le32 datasize;
104 struct SDH { /* this is an image of an $SDH index entry */
105 le16 offs;
106 le16 size;
107 le32 fill1;
108 le16 indexsz;
109 le16 indexksz;
110 le16 flags;
111 le16 fill2;
112 le32 keyhash;
113 le32 keysecurid;
115 /* did not find official description for the following */
116 le32 hash;
117 le32 securid;
118 le32 dataoffsl;
119 le32 dataoffsh;
120 le32 datasize;
121 le32 fill3;
125 * A few useful constants
128 static ntfschar sii_stream[] = { const_cpu_to_le16('$'),
129 const_cpu_to_le16('S'),
130 const_cpu_to_le16('I'),
131 const_cpu_to_le16('I'),
132 const_cpu_to_le16(0) };
133 static ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
134 const_cpu_to_le16('S'),
135 const_cpu_to_le16('D'),
136 const_cpu_to_le16('H'),
137 const_cpu_to_le16(0) };
140 * null SID (S-1-0-0)
143 extern const SID *nullsid;
146 * The zero GUID.
149 static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
150 const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
151 static const GUID *const zero_guid = &__zero_guid;
154 * ntfs_guid_is_zero - check if a GUID is zero
155 * @guid: [IN] guid to check
157 * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
158 * and FALSE otherwise.
160 BOOL ntfs_guid_is_zero(const GUID *guid)
162 return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
166 * ntfs_guid_to_mbs - convert a GUID to a multi byte string
167 * @guid: [IN] guid to convert
168 * @guid_str: [OUT] string in which to return the GUID (optional)
170 * Convert the GUID pointed to by @guid to a multi byte string of the form
171 * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL)
172 * needs to be able to store at least 37 bytes.
174 * If @guid_str is not NULL it will contain the converted GUID on return. If
175 * it is NULL a string will be allocated and this will be returned. The caller
176 * is responsible for free()ing the string in that case.
178 * On success return the converted string and on failure return NULL with errno
179 * set to the error code.
181 char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
183 char *_guid_str;
184 int res;
186 if (!guid) {
187 errno = EINVAL;
188 return NULL;
190 _guid_str = guid_str;
191 if (!_guid_str) {
192 _guid_str = (char*)ntfs_malloc(37);
193 if (!_guid_str)
194 return _guid_str;
196 res = snprintf(_guid_str, 37,
197 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
198 (unsigned int)le32_to_cpu(guid->data1),
199 le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
200 guid->data4[0], guid->data4[1],
201 guid->data4[2], guid->data4[3], guid->data4[4],
202 guid->data4[5], guid->data4[6], guid->data4[7]);
203 if (res == 36)
204 return _guid_str;
205 if (!guid_str)
206 free(_guid_str);
207 errno = EINVAL;
208 return NULL;
212 * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
213 * @sid: [IN] SID for which to determine the maximum string size
215 * Determine the maximum multi byte string size in bytes which is needed to
216 * store the standard textual representation of the SID pointed to by @sid.
217 * See ntfs_sid_to_mbs(), below.
219 * On success return the maximum number of bytes needed to store the multi byte
220 * string and on failure return -1 with errno set to the error code.
222 int ntfs_sid_to_mbs_size(const SID *sid)
224 int size, i;
226 if (!ntfs_sid_is_valid(sid)) {
227 errno = EINVAL;
228 return -1;
230 /* Start with "S-". */
231 size = 2;
233 * Add the SID_REVISION. Hopefully the compiler will optimize this
234 * away as SID_REVISION is a constant.
236 for (i = SID_REVISION; i > 0; i /= 10)
237 size++;
238 /* Add the "-". */
239 size++;
241 * Add the identifier authority. If it needs to be in decimal, the
242 * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be
243 * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
245 if (!sid->identifier_authority.high_part)
246 size += 10;
247 else
248 size += 14;
250 * Finally, add the sub authorities. For each we have a "-" followed
251 * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
253 size += (1 + 10) * sid->sub_authority_count;
254 /* We need the zero byte at the end, too. */
255 size++;
256 return size * sizeof(char);
260 * ntfs_sid_to_mbs - convert a SID to a multi byte string
261 * @sid: [IN] SID to convert
262 * @sid_str: [OUT] string in which to return the SID (optional)
263 * @sid_str_size: [IN] size in bytes of @sid_str
265 * Convert the SID pointed to by @sid to its standard textual representation.
266 * @sid_str (if not NULL) needs to be able to store at least
267 * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of
268 * @sid_str if @sid_str is not NULL.
270 * The standard textual representation of the SID is of the form:
271 * S-R-I-S-S...
272 * Where:
273 * - The first "S" is the literal character 'S' identifying the following
274 * digits as a SID.
275 * - R is the revision level of the SID expressed as a sequence of digits
276 * in decimal.
277 * - I is the 48-bit identifier_authority, expressed as digits in decimal,
278 * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
279 * - S... is one or more sub_authority values, expressed as digits in
280 * decimal.
282 * If @sid_str is not NULL it will contain the converted SUID on return. If it
283 * is NULL a string will be allocated and this will be returned. The caller is
284 * responsible for free()ing the string in that case.
286 * On success return the converted string and on failure return NULL with errno
287 * set to the error code.
289 char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
291 u64 u;
292 le32 leauth;
293 char *s;
294 int i, j, cnt;
297 * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
298 * check @sid, too. 8 is the minimum SID string size.
300 if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
301 errno = EINVAL;
302 return NULL;
304 /* Allocate string if not provided. */
305 if (!sid_str) {
306 cnt = ntfs_sid_to_mbs_size(sid);
307 if (cnt < 0)
308 return NULL;
309 s = (char*)ntfs_malloc(cnt);
310 if (!s)
311 return s;
312 sid_str = s;
313 /* So we know we allocated it. */
314 sid_str_size = 0;
315 } else {
316 s = sid_str;
317 cnt = sid_str_size;
319 /* Start with "S-R-". */
320 i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
321 if (i < 0 || i >= cnt)
322 goto err_out;
323 s += i;
324 cnt -= i;
325 /* Add the identifier authority. */
326 for (u = i = 0, j = 40; i < 6; i++, j -= 8)
327 u += (u64)sid->identifier_authority.value[i] << j;
328 if (!sid->identifier_authority.high_part)
329 i = snprintf(s, cnt, "%lu", (unsigned long)u);
330 else
331 i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
332 if (i < 0 || i >= cnt)
333 goto err_out;
334 s += i;
335 cnt -= i;
336 /* Finally, add the sub authorities. */
337 for (j = 0; j < sid->sub_authority_count; j++) {
338 leauth = sid->sub_authority[j];
339 i = snprintf(s, cnt, "-%u", (unsigned int)
340 le32_to_cpu(leauth));
341 if (i < 0 || i >= cnt)
342 goto err_out;
343 s += i;
344 cnt -= i;
346 return sid_str;
347 err_out:
348 if (i >= cnt)
349 i = EMSGSIZE;
350 else
351 i = errno;
352 if (!sid_str_size)
353 free(sid_str);
354 errno = i;
355 return NULL;
359 * ntfs_generate_guid - generatates a random current guid.
360 * @guid: [OUT] pointer to a GUID struct to hold the generated guid.
362 * perhaps not a very good random number generator though...
364 void ntfs_generate_guid(GUID *guid)
366 unsigned int i;
367 u8 *p = (u8 *)guid;
369 for (i = 0; i < sizeof(GUID); i++) {
370 p[i] = (u8)(random() & 0xFF);
371 if (i == 7)
372 p[7] = (p[7] & 0x0F) | 0x40;
373 if (i == 8)
374 p[8] = (p[8] & 0x3F) | 0x80;
379 * ntfs_security_hash - calculate the hash of a security descriptor
380 * @sd: self-relative security descriptor whose hash to calculate
381 * @length: size in bytes of the security descritor @sd
383 * Calculate the hash of the self-relative security descriptor @sd of length
384 * @length bytes.
386 * This hash is used in the $Secure system file as the primary key for the $SDH
387 * index and is also stored in the header of each security descriptor in the
388 * $SDS data stream as well as in the index data of both the $SII and $SDH
389 * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER
390 * structure.
392 * Return the calculated security hash in little endian.
394 le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
396 const le32 *pos = (const le32*)sd;
397 const le32 *end = pos + (len >> 2);
398 u32 hash = 0;
400 while (pos < end) {
401 hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
402 pos++;
404 return cpu_to_le32(hash);
408 * Get the first entry of current index block
409 * cut and pasted form ntfs_ie_get_first() in index.c
412 static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
414 return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
418 * Stuff a 256KB block into $SDS before writing descriptors
419 * into the block.
421 * This prevents $SDS from being automatically declared as sparse
422 * when the second copy of the first security descriptor is written
423 * 256KB further ahead.
425 * Having $SDS declared as a sparse file is not wrong by itself
426 * and chkdsk leaves it as a sparse file. It does however complain
427 * and add a sparse flag (0x0200) into field file_attributes of
428 * STANDARD_INFORMATION of $Secure. This probably means that a
429 * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
430 * files (FILE_ATTR_SPARSE_FILE).
432 * Windows normally does not convert to sparse attribute or sparse
433 * file. Stuffing is just a way to get to the same result.
436 static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
438 int res;
439 int written;
440 unsigned long total;
441 char *stuff;
443 res = 0;
444 total = 0;
445 stuff = (char*)ntfs_malloc(STUFFSZ);
446 if (stuff) {
447 memset(stuff, 0, STUFFSZ);
448 do {
449 written = ntfs_attr_data_write(vol->secure_ni,
450 STREAM_SDS, 4, stuff, STUFFSZ, offs);
451 if (written == STUFFSZ) {
452 total += STUFFSZ;
453 offs += STUFFSZ;
454 } else {
455 errno = ENOSPC;
456 res = -1;
458 } while (!res && (total < ALIGN_SDS_BLOCK));
459 free(stuff);
460 } else {
461 errno = ENOMEM;
462 res = -1;
464 return (res);
468 * Enter a new security descriptor into $Secure (data only)
469 * it has to be written twice with an offset of 256KB
471 * Should only be called by entersecurityattr() to ensure consistency
473 * Returns zero if sucessful
476 static int entersecurity_data(ntfs_volume *vol,
477 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
478 le32 hash, le32 keyid, off_t offs, int gap)
480 int res;
481 int written1;
482 int written2;
483 char *fullattr;
484 int fullsz;
485 SECURITY_DESCRIPTOR_HEADER *phsds;
487 res = -1;
488 fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
489 fullattr = (char*)ntfs_malloc(fullsz);
490 if (fullattr) {
492 * Clear the gap from previous descriptor
493 * this could be useful for appending the second
494 * copy to the end of file. When creating a new
495 * 256K block, the gap is cleared while writing
496 * the first copy
498 if (gap)
499 memset(fullattr,0,gap);
500 memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
501 attr,attrsz);
502 phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
503 phsds->hash = hash;
504 phsds->security_id = keyid;
505 phsds->offset = cpu_to_le64(offs);
506 phsds->length = cpu_to_le32(fullsz - gap);
507 written1 = ntfs_attr_data_write(vol->secure_ni,
508 STREAM_SDS, 4, fullattr, fullsz,
509 offs - gap);
510 written2 = ntfs_attr_data_write(vol->secure_ni,
511 STREAM_SDS, 4, fullattr, fullsz,
512 offs - gap + ALIGN_SDS_BLOCK);
513 if ((written1 == fullsz)
514 && (written2 == written1))
515 res = 0;
516 else
517 errno = ENOSPC;
518 free(fullattr);
519 } else
520 errno = ENOMEM;
521 return (res);
525 * Enter a new security descriptor in $Secure (indexes only)
527 * Should only be called by entersecurityattr() to ensure consistency
529 * Returns zero if sucessful
532 static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
533 le32 hash, le32 keyid, off_t offs)
535 union {
536 struct {
537 le32 dataoffsl;
538 le32 dataoffsh;
539 } parts;
540 le64 all;
541 } realign;
542 int res;
543 ntfs_index_context *xsii;
544 ntfs_index_context *xsdh;
545 struct SII newsii;
546 struct SDH newsdh;
548 res = -1;
549 /* enter a new $SII record */
551 xsii = vol->secure_xsii;
552 ntfs_index_ctx_reinit(xsii);
553 newsii.offs = const_cpu_to_le16(20);
554 newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
555 newsii.fill1 = const_cpu_to_le32(0);
556 newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
557 newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
558 newsii.flags = const_cpu_to_le16(0);
559 newsii.fill2 = const_cpu_to_le16(0);
560 newsii.keysecurid = keyid;
561 newsii.hash = hash;
562 newsii.securid = keyid;
563 realign.all = cpu_to_le64(offs);
564 newsii.dataoffsh = realign.parts.dataoffsh;
565 newsii.dataoffsl = realign.parts.dataoffsl;
566 newsii.datasize = cpu_to_le32(attrsz
567 + sizeof(SECURITY_DESCRIPTOR_HEADER));
568 if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
570 /* enter a new $SDH record */
572 xsdh = vol->secure_xsdh;
573 ntfs_index_ctx_reinit(xsdh);
574 newsdh.offs = const_cpu_to_le16(24);
575 newsdh.size = const_cpu_to_le16(
576 sizeof(SECURITY_DESCRIPTOR_HEADER));
577 newsdh.fill1 = const_cpu_to_le32(0);
578 newsdh.indexsz = const_cpu_to_le16(
579 sizeof(struct SDH));
580 newsdh.indexksz = const_cpu_to_le16(
581 sizeof(SDH_INDEX_KEY));
582 newsdh.flags = const_cpu_to_le16(0);
583 newsdh.fill2 = const_cpu_to_le16(0);
584 newsdh.keyhash = hash;
585 newsdh.keysecurid = keyid;
586 newsdh.hash = hash;
587 newsdh.securid = keyid;
588 newsdh.dataoffsh = realign.parts.dataoffsh;
589 newsdh.dataoffsl = realign.parts.dataoffsl;
590 newsdh.datasize = cpu_to_le32(attrsz
591 + sizeof(SECURITY_DESCRIPTOR_HEADER));
592 /* special filler value, Windows generally */
593 /* fills with 0x00490049, sometimes with zero */
594 newsdh.fill3 = const_cpu_to_le32(0x00490049);
595 if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
596 res = 0;
598 return (res);
602 * Enter a new security descriptor in $Secure (data and indexes)
603 * Returns id of entry, or zero if there is a problem.
604 * (should not be called for NTFS version < 3.0)
606 * important : calls have to be serialized, however no locking is
607 * needed while fuse is not multithreaded
610 static le32 entersecurityattr(ntfs_volume *vol,
611 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
612 le32 hash)
614 union {
615 struct {
616 le32 dataoffsl;
617 le32 dataoffsh;
618 } parts;
619 le64 all;
620 } realign;
621 le32 securid;
622 le32 keyid;
623 u32 newkey;
624 off_t offs;
625 int gap;
626 int size;
627 BOOL found;
628 struct SII *psii;
629 INDEX_ENTRY *entry;
630 INDEX_ENTRY *next;
631 ntfs_index_context *xsii;
632 int retries;
633 ntfs_attr *na;
634 int olderrno;
636 /* find the first available securid beyond the last key */
637 /* in $Secure:$SII. This also determines the first */
638 /* available location in $Secure:$SDS, as this stream */
639 /* is always appended to and the id's are allocated */
640 /* in sequence */
642 securid = const_cpu_to_le32(0);
643 xsii = vol->secure_xsii;
644 ntfs_index_ctx_reinit(xsii);
645 offs = size = 0;
646 keyid = const_cpu_to_le32(-1);
647 olderrno = errno;
648 found = !ntfs_index_lookup((char*)&keyid,
649 sizeof(SII_INDEX_KEY), xsii);
650 if (!found && (errno != ENOENT)) {
651 ntfs_log_perror("Inconsistency in index $SII");
652 psii = (struct SII*)NULL;
653 } else {
654 /* restore errno to avoid misinterpretation */
655 errno = olderrno;
656 entry = xsii->entry;
657 psii = (struct SII*)xsii->entry;
659 if (psii) {
661 * Get last entry in block, but must get first one
662 * one first, as we should already be beyond the
663 * last one. For some reason the search for the last
664 * entry sometimes does not return the last block...
665 * we assume this can only happen in root block
667 if (xsii->is_in_root)
668 entry = ntfs_ie_get_first
669 ((INDEX_HEADER*)&xsii->ir->index);
670 else
671 entry = ntfs_ie_get_first
672 ((INDEX_HEADER*)&xsii->ib->index);
674 * All index blocks should be at least half full
675 * so there always is a last entry but one,
676 * except when creating the first entry in index root.
677 * This was however found not to be true : chkdsk
678 * sometimes deletes all the (unused) keys in the last
679 * index block without rebalancing the tree.
680 * When this happens, a new search is restarted from
681 * the smallest key.
683 keyid = const_cpu_to_le32(0);
684 retries = 0;
685 while (entry) {
686 next = ntfs_index_next(entry,xsii);
687 if (next) {
688 psii = (struct SII*)next;
689 /* save last key and */
690 /* available position */
691 keyid = psii->keysecurid;
692 realign.parts.dataoffsh
693 = psii->dataoffsh;
694 realign.parts.dataoffsl
695 = psii->dataoffsl;
696 offs = le64_to_cpu(realign.all);
697 size = le32_to_cpu(psii->datasize);
699 entry = next;
700 if (!entry && !keyid && !retries) {
701 /* search failed, retry from smallest key */
702 ntfs_index_ctx_reinit(xsii);
703 found = !ntfs_index_lookup((char*)&keyid,
704 sizeof(SII_INDEX_KEY), xsii);
705 if (!found && (errno != ENOENT)) {
706 ntfs_log_perror("Index $SII is broken");
707 } else {
708 /* restore errno */
709 errno = olderrno;
710 entry = xsii->entry;
712 retries++;
716 if (!keyid) {
718 * could not find any entry, before creating the first
719 * entry, make a double check by making sure size of $SII
720 * is less than needed for one entry
722 securid = const_cpu_to_le32(0);
723 na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
724 if (na) {
725 if ((size_t)na->data_size < sizeof(struct SII)) {
726 ntfs_log_error("Creating the first security_id\n");
727 securid = const_cpu_to_le32(FIRST_SECURITY_ID);
729 ntfs_attr_close(na);
731 if (!securid) {
732 ntfs_log_error("Error creating a security_id\n");
733 errno = EIO;
735 } else {
736 newkey = le32_to_cpu(keyid) + 1;
737 securid = cpu_to_le32(newkey);
740 * The security attr has to be written twice 256KB
741 * apart. This implies that offsets like
742 * 0x40000*odd_integer must be left available for
743 * the second copy. So align to next block when
744 * the last byte overflows on a wrong block.
747 if (securid) {
748 gap = (-size) & (ALIGN_SDS_ENTRY - 1);
749 offs += gap + size;
750 if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
751 & ALIGN_SDS_BLOCK) {
752 offs = ((offs + attrsz
753 + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
754 | (ALIGN_SDS_BLOCK - 1)) + 1;
756 if (!(offs & (ALIGN_SDS_BLOCK - 1)))
757 entersecurity_stuff(vol, offs);
759 * now write the security attr to storage :
760 * first data, then SII, then SDH
761 * If failure occurs while writing SDS, data will never
762 * be accessed through indexes, and will be overwritten
763 * by the next allocated descriptor
764 * If failure occurs while writing SII, the id has not
765 * recorded and will be reallocated later
766 * If failure occurs while writing SDH, the space allocated
767 * in SDS or SII will not be reused, an inconsistency
768 * will persist with no significant consequence
770 if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
771 || entersecurity_indexes(vol, attrsz, hash, securid, offs))
772 securid = const_cpu_to_le32(0);
774 /* inode now is dirty, synchronize it all */
775 ntfs_index_entry_mark_dirty(vol->secure_xsii);
776 ntfs_index_ctx_reinit(vol->secure_xsii);
777 ntfs_index_entry_mark_dirty(vol->secure_xsdh);
778 ntfs_index_ctx_reinit(vol->secure_xsdh);
779 NInoSetDirty(vol->secure_ni);
780 if (ntfs_inode_sync(vol->secure_ni))
781 ntfs_log_perror("Could not sync $Secure\n");
782 return (securid);
786 * Find a matching security descriptor in $Secure,
787 * if none, allocate a new id and write the descriptor to storage
788 * Returns id of entry, or zero if there is a problem.
790 * important : calls have to be serialized, however no locking is
791 * needed while fuse is not multithreaded
794 static le32 setsecurityattr(ntfs_volume *vol,
795 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
797 struct SDH *psdh; /* this is an image of index (le) */
798 union {
799 struct {
800 le32 dataoffsl;
801 le32 dataoffsh;
802 } parts;
803 le64 all;
804 } realign;
805 BOOL found;
806 BOOL collision;
807 size_t size;
808 size_t rdsize;
809 s64 offs;
810 int res;
811 ntfs_index_context *xsdh;
812 char *oldattr;
813 SDH_INDEX_KEY key;
814 INDEX_ENTRY *entry;
815 le32 securid;
816 le32 hash;
817 int olderrno;
819 hash = ntfs_security_hash(attr,attrsz);
820 oldattr = (char*)NULL;
821 securid = const_cpu_to_le32(0);
822 res = 0;
823 xsdh = vol->secure_xsdh;
824 if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
825 ntfs_index_ctx_reinit(xsdh);
827 * find the nearest key as (hash,0)
828 * (do not search for partial key : in case of collision,
829 * it could return a key which is not the first one which
830 * collides)
832 key.hash = hash;
833 key.security_id = const_cpu_to_le32(0);
834 olderrno = errno;
835 found = !ntfs_index_lookup((char*)&key,
836 sizeof(SDH_INDEX_KEY), xsdh);
837 if (!found && (errno != ENOENT))
838 ntfs_log_perror("Inconsistency in index $SDH");
839 else {
840 /* restore errno to avoid misinterpretation */
841 errno = olderrno;
842 entry = xsdh->entry;
843 found = FALSE;
845 * lookup() may return a node with no data,
846 * if so get next
848 if (entry->ie_flags & INDEX_ENTRY_END)
849 entry = ntfs_index_next(entry,xsdh);
850 do {
851 collision = FALSE;
852 psdh = (struct SDH*)entry;
853 if (psdh)
854 size = (size_t) le32_to_cpu(psdh->datasize)
855 - sizeof(SECURITY_DESCRIPTOR_HEADER);
856 else size = 0;
857 /* if hash is not the same, the key is not present */
858 if (psdh && (size > 0)
859 && (psdh->keyhash == hash)) {
860 /* if hash is the same */
861 /* check the whole record */
862 realign.parts.dataoffsh = psdh->dataoffsh;
863 realign.parts.dataoffsl = psdh->dataoffsl;
864 offs = le64_to_cpu(realign.all)
865 + sizeof(SECURITY_DESCRIPTOR_HEADER);
866 oldattr = (char*)ntfs_malloc(size);
867 if (oldattr) {
868 rdsize = ntfs_attr_data_read(
869 vol->secure_ni,
870 STREAM_SDS, 4,
871 oldattr, size, offs);
872 found = (rdsize == size)
873 && !memcmp(oldattr,attr,size);
874 free(oldattr);
875 /* if the records do not compare */
876 /* (hash collision), try next one */
877 if (!found) {
878 entry = ntfs_index_next(
879 entry,xsdh);
880 collision = TRUE;
882 } else
883 res = ENOMEM;
885 } while (collision && entry);
886 if (found)
887 securid = psdh->keysecurid;
888 else {
889 if (res) {
890 errno = res;
891 securid = const_cpu_to_le32(0);
892 } else {
894 * no matching key :
895 * have to build a new one
897 securid = entersecurityattr(vol,
898 attr, attrsz, hash);
903 if (--vol->secure_reentry)
904 ntfs_log_perror("Reentry error, check no multithreading\n");
905 return (securid);
910 * Update the security descriptor of a file
911 * Either as an attribute (complying with pre v3.x NTFS version)
912 * or, when possible, as an entry in $Secure (for NTFS v3.x)
914 * returns 0 if success
917 static int update_secur_descr(ntfs_volume *vol,
918 char *newattr, ntfs_inode *ni)
920 int newattrsz;
921 int written;
922 int res;
923 ntfs_attr *na;
925 newattrsz = ntfs_attr_size(newattr);
927 #if !FORCE_FORMAT_v1x
928 if ((vol->major_ver < 3) || !vol->secure_ni) {
929 #endif
931 /* update for NTFS format v1.x */
933 /* update the old security attribute */
934 na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
935 if (na) {
936 /* resize attribute */
937 res = ntfs_attr_truncate(na, (s64) newattrsz);
938 /* overwrite value */
939 if (!res) {
940 written = (int)ntfs_attr_pwrite(na, (s64) 0,
941 (s64) newattrsz, newattr);
942 if (written != newattrsz) {
943 ntfs_log_error("Failed to update "
944 "a v1.x security descriptor\n");
945 errno = EIO;
946 res = -1;
950 ntfs_attr_close(na);
951 /* if old security attribute was found, also */
952 /* truncate standard information attribute to v1.x */
953 /* this is needed when security data is wanted */
954 /* as v1.x though volume is formatted for v3.x */
955 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
956 AT_UNNAMED, 0);
957 if (na) {
958 clear_nino_flag(ni, v3_Extensions);
960 * Truncating the record does not sweep extensions
961 * from copy in memory. Clear security_id to be safe
963 ni->security_id = const_cpu_to_le32(0);
964 res = ntfs_attr_truncate(na, (s64)48);
965 ntfs_attr_close(na);
966 clear_nino_flag(ni, v3_Extensions);
968 } else {
970 * insert the new security attribute if there
971 * were none
973 res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
974 AT_UNNAMED, 0, (u8*)newattr,
975 (s64) newattrsz);
977 #if !FORCE_FORMAT_v1x
978 } else {
980 /* update for NTFS format v3.x */
982 le32 securid;
984 securid = setsecurityattr(vol,
985 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
986 (s64)newattrsz);
987 if (securid) {
988 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
989 AT_UNNAMED, 0);
990 if (na) {
991 res = 0;
992 if (!test_nino_flag(ni, v3_Extensions)) {
993 /* expand standard information attribute to v3.x */
994 res = ntfs_attr_truncate(na,
995 (s64)sizeof(STANDARD_INFORMATION));
996 ni->owner_id = const_cpu_to_le32(0);
997 ni->quota_charged = const_cpu_to_le64(0);
998 ni->usn = const_cpu_to_le64(0);
999 ntfs_attr_remove(ni,
1000 AT_SECURITY_DESCRIPTOR,
1001 AT_UNNAMED, 0);
1003 set_nino_flag(ni, v3_Extensions);
1004 ni->security_id = securid;
1005 ntfs_attr_close(na);
1006 } else {
1007 ntfs_log_error("Failed to update "
1008 "standard informations\n");
1009 errno = EIO;
1010 res = -1;
1012 } else
1013 res = -1;
1015 #endif
1017 /* mark node as dirty */
1018 NInoSetDirty(ni);
1019 return (res);
1023 * Upgrade the security descriptor of a file
1024 * This is intended to allow graceful upgrades for files which
1025 * were created in previous versions, with a security attributes
1026 * and no security id.
1028 * It will allocate a security id and replace the individual
1029 * security attribute by a reference to the global one
1031 * Special files are not upgraded (currently / and files in
1032 * directories /$*)
1034 * Though most code is similar to update_secur_desc() it has
1035 * been kept apart to facilitate the further processing of
1036 * special cases or even to remove it if found dangerous.
1038 * returns 0 if success,
1039 * 1 if not upgradable. This is not an error.
1040 * -1 if there is a problem
1043 static int upgrade_secur_desc(ntfs_volume *vol,
1044 const char *attr, ntfs_inode *ni)
1046 int attrsz;
1047 int res;
1048 le32 securid;
1049 ntfs_attr *na;
1052 * upgrade requires NTFS format v3.x
1053 * also refuse upgrading for special files
1054 * whose number is less than FILE_first_user
1057 if ((vol->major_ver >= 3)
1058 && (ni->mft_no >= FILE_first_user)) {
1059 attrsz = ntfs_attr_size(attr);
1060 securid = setsecurityattr(vol,
1061 (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
1062 (s64)attrsz);
1063 if (securid) {
1064 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1065 AT_UNNAMED, 0);
1066 if (na) {
1067 res = 0;
1068 /* expand standard information attribute to v3.x */
1069 res = ntfs_attr_truncate(na,
1070 (s64)sizeof(STANDARD_INFORMATION));
1071 ni->owner_id = const_cpu_to_le32(0);
1072 ni->quota_charged = const_cpu_to_le64(0);
1073 ni->usn = const_cpu_to_le64(0);
1074 ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
1075 AT_UNNAMED, 0);
1076 set_nino_flag(ni, v3_Extensions);
1077 ni->security_id = securid;
1078 ntfs_attr_close(na);
1079 } else {
1080 ntfs_log_error("Failed to upgrade "
1081 "standard informations\n");
1082 errno = EIO;
1083 res = -1;
1085 } else
1086 res = -1;
1087 /* mark node as dirty */
1088 NInoSetDirty(ni);
1089 } else
1090 res = 1;
1092 return (res);
1096 * Optional simplified checking of group membership
1098 * This only takes into account the groups defined in
1099 * /etc/group at initialization time.
1100 * It does not take into account the groups dynamically set by
1101 * setgroups() nor the changes in /etc/group since initialization
1103 * This optional method could be useful if standard checking
1104 * leads to a performance concern.
1106 * Should not be called for user root, however the group may be root
1110 static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1112 BOOL ingroup;
1113 int grcnt;
1114 gid_t *groups;
1115 struct MAPPING *user;
1117 ingroup = FALSE;
1118 if (uid) {
1119 user = scx->mapping[MAPUSERS];
1120 while (user && ((uid_t)user->xid != uid))
1121 user = user->next;
1122 if (user) {
1123 groups = user->groups;
1124 grcnt = user->grcnt;
1125 while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
1126 ingroup = (grcnt >= 0);
1129 return (ingroup);
1134 * Check whether current thread owner is member of file group
1136 * Should not be called for user root, however the group may be root
1138 * As indicated by Miklos Szeredi :
1140 * The group list is available in
1142 * /proc/$PID/task/$TID/status
1144 * and fuse supplies TID in get_fuse_context()->pid. The only problem is
1145 * finding out PID, for which I have no good solution, except to iterate
1146 * through all processes. This is rather slow, but may be speeded up
1147 * with caching and heuristics (for single threaded programs PID = TID).
1149 * The following implementation gets the group list from
1150 * /proc/$TID/task/$TID/status which apparently exists and
1151 * contains the same data.
1154 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1156 static char key[] = "\nGroups:";
1157 char buf[BUFSZ+1];
1158 char filename[64];
1159 enum { INKEY, INSEP, INNUM, INEND } state;
1160 int fd;
1161 char c;
1162 int matched;
1163 BOOL ismember;
1164 int got;
1165 char *p;
1166 gid_t grp;
1167 pid_t tid;
1169 if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1170 ismember = staticgroupmember(scx, uid, gid);
1171 else {
1172 ismember = FALSE; /* default return */
1173 tid = scx->tid;
1174 sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
1175 fd = open(filename,O_RDONLY);
1176 if (fd >= 0) {
1177 got = read(fd, buf, BUFSZ);
1178 buf[got] = 0;
1179 state = INKEY;
1180 matched = 0;
1181 p = buf;
1182 grp = 0;
1184 * A simple automaton to process lines like
1185 * Groups: 14 500 513
1187 do {
1188 c = *p++;
1189 if (!c) {
1190 /* refill buffer */
1191 got = read(fd, buf, BUFSZ);
1192 buf[got] = 0;
1193 p = buf;
1194 c = *p++; /* 0 at end of file */
1196 switch (state) {
1197 case INKEY :
1198 if (key[matched] == c) {
1199 if (!key[++matched])
1200 state = INSEP;
1201 } else
1202 if (key[0] == c)
1203 matched = 1;
1204 else
1205 matched = 0;
1206 break;
1207 case INSEP :
1208 if ((c >= '0') && (c <= '9')) {
1209 grp = c - '0';
1210 state = INNUM;
1211 } else
1212 if ((c != ' ') && (c != '\t'))
1213 state = INEND;
1214 break;
1215 case INNUM :
1216 if ((c >= '0') && (c <= '9'))
1217 grp = grp*10 + c - '0';
1218 else {
1219 ismember = (grp == gid);
1220 if ((c != ' ') && (c != '\t'))
1221 state = INEND;
1222 else
1223 state = INSEP;
1225 default :
1226 break;
1228 } while (!ismember && c && (state != INEND));
1229 close(fd);
1230 if (!c)
1231 ntfs_log_error("No group record found in %s\n",filename);
1232 } else
1233 ntfs_log_error("Could not open %s\n",filename);
1235 return (ismember);
1239 * Cacheing is done two-way :
1240 * - from uid, gid and perm to securid (CACHED_SECURID)
1241 * - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
1243 * CACHED_SECURID data is kept in a most-recent-first list
1244 * which should not be too long to be efficient. Its optimal
1245 * size is depends on usage and is hard to determine.
1247 * CACHED_PERMISSIONS data is kept in a two-level indexed array. It
1248 * is optimal at the expense of storage. Use of a most-recent-first
1249 * list would save memory and provide similar performances for
1250 * standard usage, but not for file servers with too many file
1251 * owners
1253 * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
1254 * for legacy directories which were not allocated a security_id
1255 * it is organized in a most-recent-first list.
1257 * In main caches, data is never invalidated, as the meaning of
1258 * a security_id only changes when user mapping is changed, which
1259 * current implies remounting. However returned entries may be
1260 * overwritten at next update, so data has to be copied elsewhere
1261 * before another cache update is made.
1262 * In legacy cache, data has to be invalidated when protection is
1263 * changed.
1265 * Though the same data may be found in both list, they
1266 * must be kept separately : the interpretation of ACL
1267 * in both direction are approximations which could be non
1268 * reciprocal for some configuration of the user mapping data
1270 * During the process of recompiling ntfs-3g from a tgz archive,
1271 * security processing added 7.6% to the cpu time used by ntfs-3g
1272 * and 30% if the cache is disabled.
1275 static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
1276 u32 securindex)
1278 struct PERMISSIONS_CACHE *cache;
1279 unsigned int index1;
1280 unsigned int i;
1282 cache = (struct PERMISSIONS_CACHE*)NULL;
1283 /* create the first permissions blocks */
1284 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1285 cache = (struct PERMISSIONS_CACHE*)
1286 ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
1287 + index1*sizeof(struct CACHED_PERMISSIONS*));
1288 if (cache) {
1289 cache->head.last = index1;
1290 cache->head.p_reads = 0;
1291 cache->head.p_hits = 0;
1292 cache->head.p_writes = 0;
1293 *scx->pseccache = cache;
1294 for (i=0; i<=index1; i++)
1295 cache->cachetable[i]
1296 = (struct CACHED_PERMISSIONS*)NULL;
1298 return (cache);
1302 * Free memory used by caches
1303 * The only purpose is to facilitate the detection of memory leaks
1306 static void free_caches(struct SECURITY_CONTEXT *scx)
1308 unsigned int index1;
1309 struct PERMISSIONS_CACHE *pseccache;
1311 pseccache = *scx->pseccache;
1312 if (pseccache) {
1313 for (index1=0; index1<=pseccache->head.last; index1++)
1314 if (pseccache->cachetable[index1]) {
1315 #if POSIXACLS
1316 struct CACHED_PERMISSIONS *cacheentry;
1317 unsigned int index2;
1319 for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
1320 cacheentry = &pseccache->cachetable[index1][index2];
1321 if (cacheentry->valid
1322 && cacheentry->pxdesc)
1323 free(cacheentry->pxdesc);
1325 #endif
1326 free(pseccache->cachetable[index1]);
1328 free(pseccache);
1332 static int compare(const struct CACHED_SECURID *cached,
1333 const struct CACHED_SECURID *item)
1335 #if POSIXACLS
1336 size_t csize;
1337 size_t isize;
1339 /* only compare data and sizes */
1340 csize = (cached->variable ?
1341 sizeof(struct POSIX_ACL)
1342 + (((struct POSIX_SECURITY*)cached->variable)->acccnt
1343 + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
1344 *sizeof(struct POSIX_ACE) :
1346 isize = (item->variable ?
1347 sizeof(struct POSIX_ACL)
1348 + (((struct POSIX_SECURITY*)item->variable)->acccnt
1349 + ((struct POSIX_SECURITY*)item->variable)->defcnt)
1350 *sizeof(struct POSIX_ACE) :
1352 return ((cached->uid != item->uid)
1353 || (cached->gid != item->gid)
1354 || (cached->dmode != item->dmode)
1355 || (csize != isize)
1356 || (csize
1357 && isize
1358 && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
1359 &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
1360 #else
1361 return ((cached->uid != item->uid)
1362 || (cached->gid != item->gid)
1363 || (cached->dmode != item->dmode));
1364 #endif
1367 static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
1368 const struct CACHED_PERMISSIONS_LEGACY *item)
1370 return (cached->mft_no != item->mft_no);
1374 * Resize permission cache table
1375 * do not call unless resizing is needed
1377 * If allocation fails, the cache size is not updated
1378 * Lack of memory is not considered as an error, the cache is left
1379 * consistent and errno is not set.
1382 static void resize_cache(struct SECURITY_CONTEXT *scx,
1383 u32 securindex)
1385 struct PERMISSIONS_CACHE *oldcache;
1386 struct PERMISSIONS_CACHE *newcache;
1387 int newcnt;
1388 int oldcnt;
1389 unsigned int index1;
1390 unsigned int i;
1392 oldcache = *scx->pseccache;
1393 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1394 newcnt = index1 + 1;
1395 if (newcnt <= ((CACHE_PERMISSIONS_SIZE
1396 + (1 << CACHE_PERMISSIONS_BITS)
1397 - 1) >> CACHE_PERMISSIONS_BITS)) {
1398 /* expand cache beyond current end, do not use realloc() */
1399 /* to avoid losing data when there is no more memory */
1400 oldcnt = oldcache->head.last + 1;
1401 newcache = (struct PERMISSIONS_CACHE*)
1402 ntfs_malloc(
1403 sizeof(struct PERMISSIONS_CACHE)
1404 + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1405 if (newcache) {
1406 memcpy(newcache,oldcache,
1407 sizeof(struct PERMISSIONS_CACHE)
1408 + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1409 free(oldcache);
1410 /* mark new entries as not valid */
1411 for (i=newcache->head.last+1; i<=index1; i++)
1412 newcache->cachetable[i]
1413 = (struct CACHED_PERMISSIONS*)NULL;
1414 newcache->head.last = index1;
1415 *scx->pseccache = newcache;
1421 * Enter uid, gid and mode into cache, if possible
1423 * returns the updated or created cache entry,
1424 * or NULL if not possible (typically if there is no
1425 * security id associated)
1428 #if POSIXACLS
1429 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1430 ntfs_inode *ni, uid_t uid, gid_t gid,
1431 struct POSIX_SECURITY *pxdesc)
1432 #else
1433 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1434 ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
1435 #endif
1437 struct CACHED_PERMISSIONS *cacheentry;
1438 struct CACHED_PERMISSIONS *cacheblock;
1439 struct PERMISSIONS_CACHE *pcache;
1440 u32 securindex;
1441 #if POSIXACLS
1442 int pxsize;
1443 struct POSIX_SECURITY *pxcached;
1444 #endif
1445 unsigned int index1;
1446 unsigned int index2;
1447 int i;
1449 /* cacheing is only possible if a security_id has been defined */
1450 if (test_nino_flag(ni, v3_Extensions)
1451 && ni->security_id) {
1453 * Immediately test the most frequent situation
1454 * where the entry exists
1456 securindex = le32_to_cpu(ni->security_id);
1457 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1458 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1459 pcache = *scx->pseccache;
1460 if (pcache
1461 && (pcache->head.last >= index1)
1462 && pcache->cachetable[index1]) {
1463 cacheentry = &pcache->cachetable[index1][index2];
1464 cacheentry->uid = uid;
1465 cacheentry->gid = gid;
1466 #if POSIXACLS
1467 if (cacheentry->valid && cacheentry->pxdesc)
1468 free(cacheentry->pxdesc);
1469 if (pxdesc) {
1470 pxsize = sizeof(struct POSIX_SECURITY)
1471 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1472 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1473 if (pxcached) {
1474 memcpy(pxcached, pxdesc, pxsize);
1475 cacheentry->pxdesc = pxcached;
1476 } else {
1477 cacheentry->valid = 0;
1478 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1480 cacheentry->mode = pxdesc->mode & 07777;
1481 } else
1482 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1483 #else
1484 cacheentry->mode = mode & 07777;
1485 #endif
1486 cacheentry->inh_fileid = const_cpu_to_le32(0);
1487 cacheentry->inh_dirid = const_cpu_to_le32(0);
1488 cacheentry->valid = 1;
1489 pcache->head.p_writes++;
1490 } else {
1491 if (!pcache) {
1492 /* create the first cache block */
1493 pcache = create_caches(scx, securindex);
1494 } else {
1495 if (index1 > pcache->head.last) {
1496 resize_cache(scx, securindex);
1497 pcache = *scx->pseccache;
1500 /* allocate block, if cache table was allocated */
1501 if (pcache && (index1 <= pcache->head.last)) {
1502 cacheblock = (struct CACHED_PERMISSIONS*)
1503 malloc(sizeof(struct CACHED_PERMISSIONS)
1504 << CACHE_PERMISSIONS_BITS);
1505 pcache->cachetable[index1] = cacheblock;
1506 for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
1507 cacheblock[i].valid = 0;
1508 cacheentry = &cacheblock[index2];
1509 if (cacheentry) {
1510 cacheentry->uid = uid;
1511 cacheentry->gid = gid;
1512 #if POSIXACLS
1513 if (pxdesc) {
1514 pxsize = sizeof(struct POSIX_SECURITY)
1515 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1516 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1517 if (pxcached) {
1518 memcpy(pxcached, pxdesc, pxsize);
1519 cacheentry->pxdesc = pxcached;
1520 } else {
1521 cacheentry->valid = 0;
1522 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1524 cacheentry->mode = pxdesc->mode & 07777;
1525 } else
1526 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1527 #else
1528 cacheentry->mode = mode & 07777;
1529 #endif
1530 cacheentry->inh_fileid = const_cpu_to_le32(0);
1531 cacheentry->inh_dirid = const_cpu_to_le32(0);
1532 cacheentry->valid = 1;
1533 pcache->head.p_writes++;
1535 } else
1536 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1538 } else {
1539 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1540 #if CACHE_LEGACY_SIZE
1541 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1542 struct CACHED_PERMISSIONS_LEGACY wanted;
1543 struct CACHED_PERMISSIONS_LEGACY *legacy;
1545 wanted.perm.uid = uid;
1546 wanted.perm.gid = gid;
1547 #if POSIXACLS
1548 wanted.perm.mode = pxdesc->mode & 07777;
1549 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1550 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1551 wanted.mft_no = ni->mft_no;
1552 wanted.variable = (void*)pxdesc;
1553 wanted.varsize = sizeof(struct POSIX_SECURITY)
1554 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1555 #else
1556 wanted.perm.mode = mode & 07777;
1557 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1558 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1559 wanted.mft_no = ni->mft_no;
1560 wanted.variable = (void*)NULL;
1561 wanted.varsize = 0;
1562 #endif
1563 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
1564 scx->vol->legacy_cache, GENERIC(&wanted),
1565 (cache_compare)leg_compare);
1566 if (legacy) {
1567 cacheentry = &legacy->perm;
1568 #if POSIXACLS
1570 * give direct access to the cached pxdesc
1571 * in the permissions structure
1573 cacheentry->pxdesc = legacy->variable;
1574 #endif
1577 #endif
1579 return (cacheentry);
1583 * Fetch owner, group and permission of a file, if cached
1585 * Beware : do not use the returned entry after a cache update :
1586 * the cache may be relocated making the returned entry meaningless
1588 * returns the cache entry, or NULL if not available
1591 static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
1592 ntfs_inode *ni)
1594 struct CACHED_PERMISSIONS *cacheentry;
1595 struct PERMISSIONS_CACHE *pcache;
1596 u32 securindex;
1597 unsigned int index1;
1598 unsigned int index2;
1600 /* cacheing is only possible if a security_id has been defined */
1601 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1602 if (test_nino_flag(ni, v3_Extensions)
1603 && (ni->security_id)) {
1604 securindex = le32_to_cpu(ni->security_id);
1605 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1606 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1607 pcache = *scx->pseccache;
1608 if (pcache
1609 && (pcache->head.last >= index1)
1610 && pcache->cachetable[index1]) {
1611 cacheentry = &pcache->cachetable[index1][index2];
1612 /* reject if entry is not valid */
1613 if (!cacheentry->valid)
1614 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1615 else
1616 pcache->head.p_hits++;
1617 if (pcache)
1618 pcache->head.p_reads++;
1621 #if CACHE_LEGACY_SIZE
1622 else {
1623 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1624 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1625 struct CACHED_PERMISSIONS_LEGACY wanted;
1626 struct CACHED_PERMISSIONS_LEGACY *legacy;
1628 wanted.mft_no = ni->mft_no;
1629 wanted.variable = (void*)NULL;
1630 wanted.varsize = 0;
1631 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
1632 scx->vol->legacy_cache, GENERIC(&wanted),
1633 (cache_compare)leg_compare);
1634 if (legacy) cacheentry = &legacy->perm;
1637 #endif
1638 #if POSIXACLS
1639 if (cacheentry && !cacheentry->pxdesc) {
1640 ntfs_log_error("No Posix descriptor in cache\n");
1641 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1643 #endif
1644 return (cacheentry);
1648 * Retrieve a security attribute from $Secure
1651 static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
1653 struct SII *psii;
1654 union {
1655 struct {
1656 le32 dataoffsl;
1657 le32 dataoffsh;
1658 } parts;
1659 le64 all;
1660 } realign;
1661 int found;
1662 size_t size;
1663 size_t rdsize;
1664 s64 offs;
1665 ntfs_inode *ni;
1666 ntfs_index_context *xsii;
1667 char *securattr;
1669 securattr = (char*)NULL;
1670 ni = vol->secure_ni;
1671 xsii = vol->secure_xsii;
1672 if (ni && xsii) {
1673 ntfs_index_ctx_reinit(xsii);
1674 found =
1675 !ntfs_index_lookup((char*)&id,
1676 sizeof(SII_INDEX_KEY), xsii);
1677 if (found) {
1678 psii = (struct SII*)xsii->entry;
1679 size =
1680 (size_t) le32_to_cpu(psii->datasize)
1681 - sizeof(SECURITY_DESCRIPTOR_HEADER);
1682 /* work around bad alignment problem */
1683 realign.parts.dataoffsh = psii->dataoffsh;
1684 realign.parts.dataoffsl = psii->dataoffsl;
1685 offs = le64_to_cpu(realign.all)
1686 + sizeof(SECURITY_DESCRIPTOR_HEADER);
1688 securattr = (char*)ntfs_malloc(size);
1689 if (securattr) {
1690 rdsize = ntfs_attr_data_read(
1691 ni, STREAM_SDS, 4,
1692 securattr, size, offs);
1693 if ((rdsize != size)
1694 || !ntfs_valid_descr(securattr,
1695 rdsize)) {
1696 /* error to be logged by caller */
1697 free(securattr);
1698 securattr = (char*)NULL;
1701 } else
1702 if (errno != ENOENT)
1703 ntfs_log_perror("Inconsistency in index $SII");
1705 if (!securattr) {
1706 ntfs_log_error("Failed to retrieve a security descriptor\n");
1707 errno = EIO;
1709 return (securattr);
1713 * Get the security descriptor associated to a file
1715 * Either :
1716 * - read the security descriptor attribute (v1.x format)
1717 * - or find the descriptor in $Secure:$SDS (v3.x format)
1719 * in both case, sanity checks are done on the attribute and
1720 * the descriptor can be assumed safe
1722 * The returned descriptor is dynamically allocated and has to be freed
1725 static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
1727 SII_INDEX_KEY securid;
1728 char *securattr;
1729 s64 readallsz;
1732 * Warning : in some situations, after fixing by chkdsk,
1733 * v3_Extensions are marked present (long standard informations)
1734 * with a default security descriptor inserted in an
1735 * attribute
1737 if (test_nino_flag(ni, v3_Extensions)
1738 && vol->secure_ni && ni->security_id) {
1739 /* get v3.x descriptor in $Secure */
1740 securid.security_id = ni->security_id;
1741 securattr = retrievesecurityattr(vol,securid);
1742 if (!securattr)
1743 ntfs_log_error("Bad security descriptor for 0x%lx\n",
1744 (long)le32_to_cpu(ni->security_id));
1745 } else {
1746 /* get v1.x security attribute */
1747 readallsz = 0;
1748 securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
1749 AT_UNNAMED, 0, &readallsz);
1750 if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
1751 ntfs_log_error("Bad security descriptor for inode %lld\n",
1752 (long long)ni->mft_no);
1753 free(securattr);
1754 securattr = (char*)NULL;
1757 if (!securattr) {
1759 * in some situations, there is no security
1760 * descriptor, and chkdsk does not detect or fix
1761 * anything. This could be a normal situation.
1762 * When this happens, simulate a descriptor with
1763 * minimum rights, so that a real descriptor can
1764 * be created by chown or chmod
1766 ntfs_log_error("No security descriptor found for inode %lld\n",
1767 (long long)ni->mft_no);
1768 securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
1770 return (securattr);
1773 #if POSIXACLS
1776 * Determine which access types to a file are allowed
1777 * according to the relation of current process to the file
1779 * Do not call if default_permissions is set
1782 static int access_check_posix(struct SECURITY_CONTEXT *scx,
1783 struct POSIX_SECURITY *pxdesc, mode_t request,
1784 uid_t uid, gid_t gid)
1786 struct POSIX_ACE *pxace;
1787 int userperms;
1788 int groupperms;
1789 int mask;
1790 BOOL somegroup;
1791 BOOL needgroups;
1792 mode_t perms;
1793 int i;
1795 perms = pxdesc->mode;
1796 /* owner and root access */
1797 if (!scx->uid || (uid == scx->uid)) {
1798 if (!scx->uid) {
1799 /* root access if owner or other execution */
1800 if (perms & 0101)
1801 perms = 07777;
1802 else {
1803 /* root access if some group execution */
1804 groupperms = 0;
1805 mask = 7;
1806 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1807 pxace = &pxdesc->acl.ace[i];
1808 switch (pxace->tag) {
1809 case POSIX_ACL_USER_OBJ :
1810 case POSIX_ACL_GROUP_OBJ :
1811 case POSIX_ACL_GROUP :
1812 groupperms |= pxace->perms;
1813 break;
1814 case POSIX_ACL_MASK :
1815 mask = pxace->perms & 7;
1816 break;
1817 default :
1818 break;
1821 perms = (groupperms & mask & 1) | 6;
1823 } else
1824 perms &= 07700;
1825 } else {
1827 * analyze designated users, get mask
1828 * and identify whether we need to check
1829 * the group memberships. The groups are
1830 * not needed when all groups have the
1831 * same permissions as other for the
1832 * requested modes.
1834 userperms = -1;
1835 groupperms = -1;
1836 needgroups = FALSE;
1837 mask = 7;
1838 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1839 pxace = &pxdesc->acl.ace[i];
1840 switch (pxace->tag) {
1841 case POSIX_ACL_USER :
1842 if ((uid_t)pxace->id == scx->uid)
1843 userperms = pxace->perms;
1844 break;
1845 case POSIX_ACL_MASK :
1846 mask = pxace->perms & 7;
1847 break;
1848 case POSIX_ACL_GROUP_OBJ :
1849 case POSIX_ACL_GROUP :
1850 if (((pxace->perms & mask) ^ perms)
1851 & (request >> 6) & 7)
1852 needgroups = TRUE;
1853 break;
1854 default :
1855 break;
1858 /* designated users */
1859 if (userperms >= 0)
1860 perms = (perms & 07000) + (userperms & mask);
1861 else if (!needgroups)
1862 perms &= 07007;
1863 else {
1864 /* owning group */
1865 if (!(~(perms >> 3) & request & mask)
1866 && ((gid == scx->gid)
1867 || groupmember(scx, scx->uid, gid)))
1868 perms &= 07070;
1869 else {
1870 /* other groups */
1871 groupperms = -1;
1872 somegroup = FALSE;
1873 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1874 pxace = &pxdesc->acl.ace[i];
1875 if ((pxace->tag == POSIX_ACL_GROUP)
1876 && groupmember(scx, uid, pxace->id)) {
1877 if (!(~pxace->perms & request & mask))
1878 groupperms = pxace->perms;
1879 somegroup = TRUE;
1882 if (groupperms >= 0)
1883 perms = (perms & 07000) + (groupperms & mask);
1884 else
1885 if (somegroup)
1886 perms = 0;
1887 else
1888 perms &= 07007;
1892 return (perms);
1896 * Get permissions to access a file
1897 * Takes into account the relation of user to file (owner, group, ...)
1898 * Do no use as mode of the file
1899 * Do no call if default_permissions is set
1901 * returns -1 if there is a problem
1904 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
1905 ntfs_inode * ni, mode_t request)
1907 const SECURITY_DESCRIPTOR_RELATIVE *phead;
1908 const struct CACHED_PERMISSIONS *cached;
1909 char *securattr;
1910 const SID *usid; /* owner of file/directory */
1911 const SID *gsid; /* group of file/directory */
1912 uid_t uid;
1913 gid_t gid;
1914 int perm;
1915 BOOL isdir;
1916 struct POSIX_SECURITY *pxdesc;
1918 if (!scx->mapping[MAPUSERS])
1919 perm = 07777;
1920 else {
1921 /* check whether available in cache */
1922 cached = fetch_cache(scx,ni);
1923 if (cached) {
1924 uid = cached->uid;
1925 gid = cached->gid;
1926 perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
1927 } else {
1928 perm = 0; /* default to no permission */
1929 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
1930 != const_cpu_to_le16(0);
1931 securattr = getsecurityattr(scx->vol, ni);
1932 if (securattr) {
1933 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
1934 securattr;
1935 gsid = (const SID*)&
1936 securattr[le32_to_cpu(phead->group)];
1937 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
1938 #if OWNERFROMACL
1939 usid = ntfs_acl_owner(securattr);
1940 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
1941 usid, gsid, isdir);
1942 if (pxdesc)
1943 perm = pxdesc->mode & 07777;
1944 else
1945 perm = -1;
1946 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
1947 #else
1948 usid = (const SID*)&
1949 securattr[le32_to_cpu(phead->owner)];
1950 pxdesc = ntfs_build_permissions_posix(scx,securattr,
1951 usid, gsid, isdir);
1952 if (pxdesc)
1953 perm = pxdesc->mode & 07777;
1954 else
1955 perm = -1;
1956 if (!perm && ntfs_same_sid(usid, adminsid)) {
1957 uid = find_tenant(scx, securattr);
1958 if (uid)
1959 perm = 0700;
1960 } else
1961 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
1962 #endif
1964 * Create a security id if there were none
1965 * and upgrade option is selected
1967 if (!test_nino_flag(ni, v3_Extensions)
1968 && (perm >= 0)
1969 && (scx->vol->secure_flags
1970 & (1 << SECURITY_ADDSECURIDS))) {
1971 upgrade_secur_desc(scx->vol,
1972 securattr, ni);
1974 * fetch owner and group for cacheing
1975 * if there is a securid
1978 if (test_nino_flag(ni, v3_Extensions)
1979 && (perm >= 0)) {
1980 enter_cache(scx, ni, uid,
1981 gid, pxdesc);
1983 if (pxdesc) {
1984 perm = access_check_posix(scx,pxdesc,request,uid,gid);
1985 free(pxdesc);
1987 free(securattr);
1988 } else {
1989 perm = -1;
1990 uid = gid = 0;
1994 return (perm);
1998 * Get a Posix ACL
2000 * returns size or -errno if there is a problem
2001 * if size was too small, no copy is done and errno is not set,
2002 * the caller is expected to issue a new call
2005 int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2006 const char *name, char *value, size_t size)
2008 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2009 struct POSIX_SECURITY *pxdesc;
2010 const struct CACHED_PERMISSIONS *cached;
2011 char *securattr;
2012 const SID *usid; /* owner of file/directory */
2013 const SID *gsid; /* group of file/directory */
2014 uid_t uid;
2015 gid_t gid;
2016 int perm;
2017 BOOL isdir;
2018 size_t outsize;
2020 outsize = 0; /* default to error */
2021 if (!scx->mapping[MAPUSERS])
2022 errno = ENOTSUP;
2023 else {
2024 /* check whether available in cache */
2025 cached = fetch_cache(scx,ni);
2026 if (cached)
2027 pxdesc = cached->pxdesc;
2028 else {
2029 securattr = getsecurityattr(scx->vol, ni);
2030 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2031 != const_cpu_to_le16(0);
2032 if (securattr) {
2033 phead =
2034 (const SECURITY_DESCRIPTOR_RELATIVE*)
2035 securattr;
2036 gsid = (const SID*)&
2037 securattr[le32_to_cpu(phead->group)];
2038 #if OWNERFROMACL
2039 usid = ntfs_acl_owner(securattr);
2040 #else
2041 usid = (const SID*)&
2042 securattr[le32_to_cpu(phead->owner)];
2043 #endif
2044 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2045 usid, gsid, isdir);
2048 * fetch owner and group for cacheing
2050 if (pxdesc) {
2051 perm = pxdesc->mode & 07777;
2053 * Create a security id if there were none
2054 * and upgrade option is selected
2056 if (!test_nino_flag(ni, v3_Extensions)
2057 && (scx->vol->secure_flags
2058 & (1 << SECURITY_ADDSECURIDS))) {
2059 upgrade_secur_desc(scx->vol,
2060 securattr, ni);
2062 #if OWNERFROMACL
2063 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2064 #else
2065 if (!perm && ntfs_same_sid(usid, adminsid)) {
2066 uid = find_tenant(scx,
2067 securattr);
2068 if (uid)
2069 perm = 0700;
2070 } else
2071 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2072 #endif
2073 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2074 if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
2075 enter_cache(scx, ni, uid,
2076 gid, pxdesc);
2078 free(securattr);
2079 } else
2080 pxdesc = (struct POSIX_SECURITY*)NULL;
2083 if (pxdesc) {
2084 if (ntfs_valid_posix(pxdesc)) {
2085 if (!strcmp(name,"system.posix_acl_default")) {
2086 if (ni->mrec->flags
2087 & MFT_RECORD_IS_DIRECTORY)
2088 outsize = sizeof(struct POSIX_ACL)
2089 + pxdesc->defcnt*sizeof(struct POSIX_ACE);
2090 else {
2092 * getting default ACL from plain file :
2093 * return EACCES if size > 0 as
2094 * indicated in the man, but return ok
2095 * if size == 0, so that ls does not
2096 * display an error
2098 if (size > 0) {
2099 outsize = 0;
2100 errno = EACCES;
2101 } else
2102 outsize = sizeof(struct POSIX_ACL);
2104 if (outsize && (outsize <= size)) {
2105 memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
2106 memcpy(&value[sizeof(struct POSIX_ACL)],
2107 &pxdesc->acl.ace[pxdesc->firstdef],
2108 outsize-sizeof(struct POSIX_ACL));
2110 } else {
2111 outsize = sizeof(struct POSIX_ACL)
2112 + pxdesc->acccnt*sizeof(struct POSIX_ACE);
2113 if (outsize <= size)
2114 memcpy(value,&pxdesc->acl,outsize);
2116 } else {
2117 outsize = 0;
2118 errno = EIO;
2119 ntfs_log_error("Invalid Posix ACL built\n");
2121 if (!cached)
2122 free(pxdesc);
2123 } else
2124 outsize = 0;
2126 return (outsize ? (int)outsize : -errno);
2129 #else /* POSIXACLS */
2133 * Get permissions to access a file
2134 * Takes into account the relation of user to file (owner, group, ...)
2135 * Do no use as mode of the file
2137 * returns -1 if there is a problem
2140 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
2141 ntfs_inode *ni, mode_t request)
2143 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2144 const struct CACHED_PERMISSIONS *cached;
2145 char *securattr;
2146 const SID *usid; /* owner of file/directory */
2147 const SID *gsid; /* group of file/directory */
2148 BOOL isdir;
2149 uid_t uid;
2150 gid_t gid;
2151 int perm;
2153 if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
2154 perm = 07777;
2155 else {
2156 /* check whether available in cache */
2157 cached = fetch_cache(scx,ni);
2158 if (cached) {
2159 perm = cached->mode;
2160 uid = cached->uid;
2161 gid = cached->gid;
2162 } else {
2163 perm = 0; /* default to no permission */
2164 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2165 != const_cpu_to_le16(0);
2166 securattr = getsecurityattr(scx->vol, ni);
2167 if (securattr) {
2168 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2169 securattr;
2170 gsid = (const SID*)&
2171 securattr[le32_to_cpu(phead->group)];
2172 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2173 #if OWNERFROMACL
2174 usid = ntfs_acl_owner(securattr);
2175 perm = ntfs_build_permissions(securattr,
2176 usid, gsid, isdir);
2177 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2178 #else
2179 usid = (const SID*)&
2180 securattr[le32_to_cpu(phead->owner)];
2181 perm = ntfs_build_permissions(securattr,
2182 usid, gsid, isdir);
2183 if (!perm && ntfs_same_sid(usid, adminsid)) {
2184 uid = find_tenant(scx, securattr);
2185 if (uid)
2186 perm = 0700;
2187 } else
2188 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2189 #endif
2191 * Create a security id if there were none
2192 * and upgrade option is selected
2194 if (!test_nino_flag(ni, v3_Extensions)
2195 && (perm >= 0)
2196 && (scx->vol->secure_flags
2197 & (1 << SECURITY_ADDSECURIDS))) {
2198 upgrade_secur_desc(scx->vol,
2199 securattr, ni);
2201 * fetch owner and group for cacheing
2202 * if there is a securid
2205 if (test_nino_flag(ni, v3_Extensions)
2206 && (perm >= 0)) {
2207 enter_cache(scx, ni, uid,
2208 gid, perm);
2210 free(securattr);
2211 } else {
2212 perm = -1;
2213 uid = gid = 0;
2216 if (perm >= 0) {
2217 if (!scx->uid) {
2218 /* root access and execution */
2219 if (perm & 0111)
2220 perm = 07777;
2221 else
2222 perm = 0;
2223 } else
2224 if (uid == scx->uid)
2225 perm &= 07700;
2226 else
2228 * avoid checking group membership
2229 * when the requested perms for group
2230 * are the same as perms for other
2232 if ((gid == scx->gid)
2233 || ((((perm >> 3) ^ perm)
2234 & (request >> 6) & 7)
2235 && groupmember(scx, scx->uid, gid)))
2236 perm &= 07070;
2237 else
2238 perm &= 07007;
2241 return (perm);
2244 #endif /* POSIXACLS */
2247 * Get an NTFS ACL
2249 * Returns size or -errno if there is a problem
2250 * if size was too small, no copy is done and errno is not set,
2251 * the caller is expected to issue a new call
2254 int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2255 char *value, size_t size)
2257 char *securattr;
2258 size_t outsize;
2260 outsize = 0; /* default to no data and no error */
2261 securattr = getsecurityattr(scx->vol, ni);
2262 if (securattr) {
2263 outsize = ntfs_attr_size(securattr);
2264 if (outsize <= size) {
2265 memcpy(value,securattr,outsize);
2267 free(securattr);
2269 return (outsize ? (int)outsize : -errno);
2273 * Get owner, group and permissions in an stat structure
2274 * returns permissions, or -1 if there is a problem
2277 int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
2278 ntfs_inode * ni, struct stat *stbuf)
2280 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2281 char *securattr;
2282 const SID *usid; /* owner of file/directory */
2283 const SID *gsid; /* group of file/directory */
2284 const struct CACHED_PERMISSIONS *cached;
2285 int perm;
2286 BOOL isdir;
2287 #if POSIXACLS
2288 struct POSIX_SECURITY *pxdesc;
2289 #endif
2291 if (!scx->mapping[MAPUSERS])
2292 perm = 07777;
2293 else {
2294 /* check whether available in cache */
2295 cached = fetch_cache(scx,ni);
2296 if (cached) {
2297 perm = cached->mode;
2298 stbuf->st_uid = cached->uid;
2299 stbuf->st_gid = cached->gid;
2300 stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
2301 } else {
2302 perm = -1; /* default to error */
2303 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2304 != const_cpu_to_le16(0);
2305 securattr = getsecurityattr(scx->vol, ni);
2306 if (securattr) {
2307 phead =
2308 (const SECURITY_DESCRIPTOR_RELATIVE*)
2309 securattr;
2310 gsid = (const SID*)&
2311 securattr[le32_to_cpu(phead->group)];
2312 #if OWNERFROMACL
2313 usid = ntfs_acl_owner(securattr);
2314 #else
2315 usid = (const SID*)&
2316 securattr[le32_to_cpu(phead->owner)];
2317 #endif
2318 #if POSIXACLS
2319 pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr,
2320 usid, gsid, isdir);
2321 if (pxdesc)
2322 perm = pxdesc->mode & 07777;
2323 else
2324 perm = -1;
2325 #else
2326 perm = ntfs_build_permissions(securattr,
2327 usid, gsid, isdir);
2328 #endif
2330 * fetch owner and group for cacheing
2332 if (perm >= 0) {
2334 * Create a security id if there were none
2335 * and upgrade option is selected
2337 if (!test_nino_flag(ni, v3_Extensions)
2338 && (scx->vol->secure_flags
2339 & (1 << SECURITY_ADDSECURIDS))) {
2340 upgrade_secur_desc(scx->vol,
2341 securattr, ni);
2343 #if OWNERFROMACL
2344 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2345 #else
2346 if (!perm && ntfs_same_sid(usid, adminsid)) {
2347 stbuf->st_uid =
2348 find_tenant(scx,
2349 securattr);
2350 if (stbuf->st_uid)
2351 perm = 0700;
2352 } else
2353 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2354 #endif
2355 stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2356 stbuf->st_mode =
2357 (stbuf->st_mode & ~07777) + perm;
2358 #if POSIXACLS
2359 enter_cache(scx, ni, stbuf->st_uid,
2360 stbuf->st_gid, pxdesc);
2361 free(pxdesc);
2362 #else
2363 enter_cache(scx, ni, stbuf->st_uid,
2364 stbuf->st_gid, perm);
2365 #endif
2367 free(securattr);
2371 return (perm);
2374 #if POSIXACLS
2377 * Get the base for a Posix inheritance and
2378 * build an inherited Posix descriptor
2381 static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
2382 ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
2384 const struct CACHED_PERMISSIONS *cached;
2385 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2386 struct POSIX_SECURITY *pxdesc;
2387 struct POSIX_SECURITY *pydesc;
2388 char *securattr;
2389 const SID *usid;
2390 const SID *gsid;
2391 uid_t uid;
2392 gid_t gid;
2394 pydesc = (struct POSIX_SECURITY*)NULL;
2395 /* check whether parent directory is available in cache */
2396 cached = fetch_cache(scx,dir_ni);
2397 if (cached) {
2398 uid = cached->uid;
2399 gid = cached->gid;
2400 pxdesc = cached->pxdesc;
2401 if (pxdesc) {
2402 pydesc = ntfs_build_inherited_posix(pxdesc,mode,
2403 scx->umask,isdir);
2405 } else {
2406 securattr = getsecurityattr(scx->vol, dir_ni);
2407 if (securattr) {
2408 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2409 securattr;
2410 gsid = (const SID*)&
2411 securattr[le32_to_cpu(phead->group)];
2412 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2413 #if OWNERFROMACL
2414 usid = ntfs_acl_owner(securattr);
2415 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2416 usid, gsid, TRUE);
2417 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2418 #else
2419 usid = (const SID*)&
2420 securattr[le32_to_cpu(phead->owner)];
2421 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2422 usid, gsid, TRUE);
2423 if (pxdesc && ntfs_same_sid(usid, adminsid)) {
2424 uid = find_tenant(scx, securattr);
2425 } else
2426 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2427 #endif
2428 if (pxdesc) {
2430 * Create a security id if there were none
2431 * and upgrade option is selected
2433 if (!test_nino_flag(dir_ni, v3_Extensions)
2434 && (scx->vol->secure_flags
2435 & (1 << SECURITY_ADDSECURIDS))) {
2436 upgrade_secur_desc(scx->vol,
2437 securattr, dir_ni);
2439 * fetch owner and group for cacheing
2440 * if there is a securid
2443 if (test_nino_flag(dir_ni, v3_Extensions)) {
2444 enter_cache(scx, dir_ni, uid,
2445 gid, pxdesc);
2447 pydesc = ntfs_build_inherited_posix(pxdesc,
2448 mode, scx->umask, isdir);
2449 free(pxdesc);
2451 free(securattr);
2454 return (pydesc);
2458 * Allocate a security_id for a file being created
2460 * Returns zero if not possible (NTFS v3.x required)
2463 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2464 uid_t uid, gid_t gid, ntfs_inode *dir_ni,
2465 mode_t mode, BOOL isdir)
2467 #if !FORCE_FORMAT_v1x
2468 const struct CACHED_SECURID *cached;
2469 struct CACHED_SECURID wanted;
2470 struct POSIX_SECURITY *pxdesc;
2471 char *newattr;
2472 int newattrsz;
2473 const SID *usid;
2474 const SID *gsid;
2475 BIGSID defusid;
2476 BIGSID defgsid;
2477 le32 securid;
2478 #endif
2480 securid = const_cpu_to_le32(0);
2482 #if !FORCE_FORMAT_v1x
2484 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2485 if (pxdesc) {
2486 /* check whether target securid is known in cache */
2488 wanted.uid = uid;
2489 wanted.gid = gid;
2490 wanted.dmode = pxdesc->mode & mode & 07777;
2491 if (isdir) wanted.dmode |= 0x10000;
2492 wanted.variable = (void*)pxdesc;
2493 wanted.varsize = sizeof(struct POSIX_SECURITY)
2494 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2495 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2496 scx->vol->securid_cache, GENERIC(&wanted),
2497 (cache_compare)compare);
2498 /* quite simple, if we are lucky */
2499 if (cached)
2500 securid = cached->securid;
2502 /* not in cache : make sure we can create ids */
2504 if (!cached && (scx->vol->major_ver >= 3)) {
2505 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2506 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2507 if (!usid || !gsid) {
2508 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2509 (int)uid, (int)gid);
2510 usid = gsid = adminsid;
2512 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2513 isdir, usid, gsid);
2514 if (newattr) {
2515 newattrsz = ntfs_attr_size(newattr);
2516 securid = setsecurityattr(scx->vol,
2517 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2518 newattrsz);
2519 if (securid) {
2520 /* update cache, for subsequent use */
2521 wanted.securid = securid;
2522 ntfs_enter_cache(scx->vol->securid_cache,
2523 GENERIC(&wanted),
2524 (cache_compare)compare);
2526 free(newattr);
2527 } else {
2529 * could not build new security attribute
2530 * errno set by ntfs_build_descr()
2534 free(pxdesc);
2536 #endif
2537 return (securid);
2541 * Apply Posix inheritance to a newly created file
2542 * (for NTFS 1.x only : no securid)
2545 int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
2546 ntfs_inode *ni, uid_t uid, gid_t gid,
2547 ntfs_inode *dir_ni, mode_t mode)
2549 struct POSIX_SECURITY *pxdesc;
2550 char *newattr;
2551 const SID *usid;
2552 const SID *gsid;
2553 BIGSID defusid;
2554 BIGSID defgsid;
2555 BOOL isdir;
2556 int res;
2558 res = -1;
2559 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2560 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2561 if (pxdesc) {
2562 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2563 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2564 if (!usid || !gsid) {
2565 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2566 (int)uid, (int)gid);
2567 usid = gsid = adminsid;
2569 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2570 isdir, usid, gsid);
2571 if (newattr) {
2572 /* Adjust Windows read-only flag */
2573 res = update_secur_descr(scx->vol, newattr, ni);
2574 if (!res && !isdir) {
2575 if (mode & S_IWUSR)
2576 ni->flags &= ~FILE_ATTR_READONLY;
2577 else
2578 ni->flags |= FILE_ATTR_READONLY;
2580 #if CACHE_LEGACY_SIZE
2581 /* also invalidate legacy cache */
2582 if (isdir && !ni->security_id) {
2583 struct CACHED_PERMISSIONS_LEGACY legacy;
2585 legacy.mft_no = ni->mft_no;
2586 legacy.variable = pxdesc;
2587 legacy.varsize = sizeof(struct POSIX_SECURITY)
2588 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2589 ntfs_invalidate_cache(scx->vol->legacy_cache,
2590 GENERIC(&legacy),
2591 (cache_compare)leg_compare,0);
2593 #endif
2594 free(newattr);
2596 } else {
2598 * could not build new security attribute
2599 * errno set by ntfs_build_descr()
2603 return (res);
2606 #else
2608 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2609 uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
2611 #if !FORCE_FORMAT_v1x
2612 const struct CACHED_SECURID *cached;
2613 struct CACHED_SECURID wanted;
2614 char *newattr;
2615 int newattrsz;
2616 const SID *usid;
2617 const SID *gsid;
2618 BIGSID defusid;
2619 BIGSID defgsid;
2620 le32 securid;
2621 #endif
2623 securid = const_cpu_to_le32(0);
2625 #if !FORCE_FORMAT_v1x
2626 /* check whether target securid is known in cache */
2628 wanted.uid = uid;
2629 wanted.gid = gid;
2630 wanted.dmode = mode & 07777;
2631 if (isdir) wanted.dmode |= 0x10000;
2632 wanted.variable = (void*)NULL;
2633 wanted.varsize = 0;
2634 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2635 scx->vol->securid_cache, GENERIC(&wanted),
2636 (cache_compare)compare);
2637 /* quite simple, if we are lucky */
2638 if (cached)
2639 securid = cached->securid;
2641 /* not in cache : make sure we can create ids */
2643 if (!cached && (scx->vol->major_ver >= 3)) {
2644 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2645 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2646 if (!usid || !gsid) {
2647 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2648 (int)uid, (int)gid);
2649 usid = gsid = adminsid;
2651 newattr = ntfs_build_descr(mode, isdir, usid, gsid);
2652 if (newattr) {
2653 newattrsz = ntfs_attr_size(newattr);
2654 securid = setsecurityattr(scx->vol,
2655 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2656 newattrsz);
2657 if (securid) {
2658 /* update cache, for subsequent use */
2659 wanted.securid = securid;
2660 ntfs_enter_cache(scx->vol->securid_cache,
2661 GENERIC(&wanted),
2662 (cache_compare)compare);
2664 free(newattr);
2665 } else {
2667 * could not build new security attribute
2668 * errno set by ntfs_build_descr()
2672 #endif
2673 return (securid);
2676 #endif
2679 * Update ownership and mode of a file, reusing an existing
2680 * security descriptor when possible
2682 * Returns zero if successful
2685 #if POSIXACLS
2686 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2687 uid_t uid, gid_t gid, mode_t mode,
2688 struct POSIX_SECURITY *pxdesc)
2689 #else
2690 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2691 uid_t uid, gid_t gid, mode_t mode)
2692 #endif
2694 int res;
2695 const struct CACHED_SECURID *cached;
2696 struct CACHED_SECURID wanted;
2697 char *newattr;
2698 const SID *usid;
2699 const SID *gsid;
2700 BIGSID defusid;
2701 BIGSID defgsid;
2702 BOOL isdir;
2704 res = 0;
2706 /* check whether target securid is known in cache */
2708 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2709 wanted.uid = uid;
2710 wanted.gid = gid;
2711 wanted.dmode = mode & 07777;
2712 if (isdir) wanted.dmode |= 0x10000;
2713 #if POSIXACLS
2714 wanted.variable = (void*)pxdesc;
2715 if (pxdesc)
2716 wanted.varsize = sizeof(struct POSIX_SECURITY)
2717 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2718 else
2719 wanted.varsize = 0;
2720 #else
2721 wanted.variable = (void*)NULL;
2722 wanted.varsize = 0;
2723 #endif
2724 if (test_nino_flag(ni, v3_Extensions)) {
2725 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2726 scx->vol->securid_cache, GENERIC(&wanted),
2727 (cache_compare)compare);
2728 /* quite simple, if we are lucky */
2729 if (cached) {
2730 ni->security_id = cached->securid;
2731 NInoSetDirty(ni);
2733 } else cached = (struct CACHED_SECURID*)NULL;
2735 if (!cached) {
2737 * Do not use usid and gsid from former attributes,
2738 * but recompute them to get repeatable results
2739 * which can be kept in cache.
2741 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2742 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2743 if (!usid || !gsid) {
2744 ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
2745 uid, gid);
2746 usid = gsid = adminsid;
2748 #if POSIXACLS
2749 if (pxdesc)
2750 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2751 isdir, usid, gsid);
2752 else
2753 newattr = ntfs_build_descr(mode,
2754 isdir, usid, gsid);
2755 #else
2756 newattr = ntfs_build_descr(mode,
2757 isdir, usid, gsid);
2758 #endif
2759 if (newattr) {
2760 res = update_secur_descr(scx->vol, newattr, ni);
2761 if (!res) {
2762 /* adjust Windows read-only flag */
2763 if (!isdir) {
2764 if (mode & S_IWUSR)
2765 ni->flags &= ~FILE_ATTR_READONLY;
2766 else
2767 ni->flags |= FILE_ATTR_READONLY;
2768 NInoFileNameSetDirty(ni);
2770 /* update cache, for subsequent use */
2771 if (test_nino_flag(ni, v3_Extensions)) {
2772 wanted.securid = ni->security_id;
2773 ntfs_enter_cache(scx->vol->securid_cache,
2774 GENERIC(&wanted),
2775 (cache_compare)compare);
2777 #if CACHE_LEGACY_SIZE
2778 /* also invalidate legacy cache */
2779 if (isdir && !ni->security_id) {
2780 struct CACHED_PERMISSIONS_LEGACY legacy;
2782 legacy.mft_no = ni->mft_no;
2783 #if POSIXACLS
2784 legacy.variable = wanted.variable;
2785 legacy.varsize = wanted.varsize;
2786 #else
2787 legacy.variable = (void*)NULL;
2788 legacy.varsize = 0;
2789 #endif
2790 ntfs_invalidate_cache(scx->vol->legacy_cache,
2791 GENERIC(&legacy),
2792 (cache_compare)leg_compare,0);
2794 #endif
2796 free(newattr);
2797 } else {
2799 * could not build new security attribute
2800 * errno set by ntfs_build_descr()
2802 res = -1;
2805 return (res);
2809 * Check whether user has ownership rights on a file
2811 * Returns TRUE if allowed
2812 * if not, errno tells why
2815 BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
2817 const struct CACHED_PERMISSIONS *cached;
2818 char *oldattr;
2819 const SID *usid;
2820 uid_t processuid;
2821 uid_t uid;
2822 BOOL gotowner;
2823 int allowed;
2825 processuid = scx->uid;
2826 /* TODO : use CAP_FOWNER process capability */
2828 * Always allow for root
2829 * Also always allow if no mapping has been defined
2831 if (!scx->mapping[MAPUSERS] || !processuid)
2832 allowed = TRUE;
2833 else {
2834 gotowner = FALSE; /* default */
2835 /* get the owner, either from cache or from old attribute */
2836 cached = fetch_cache(scx, ni);
2837 if (cached) {
2838 uid = cached->uid;
2839 gotowner = TRUE;
2840 } else {
2841 oldattr = getsecurityattr(scx->vol, ni);
2842 if (oldattr) {
2843 #if OWNERFROMACL
2844 usid = ntfs_acl_owner(oldattr);
2845 #else
2846 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2848 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2849 oldattr;
2850 usid = (const SID*)&oldattr
2851 [le32_to_cpu(phead->owner)];
2852 #endif
2853 uid = ntfs_find_user(scx->mapping[MAPUSERS],
2854 usid);
2855 gotowner = TRUE;
2856 free(oldattr);
2859 allowed = FALSE;
2860 if (gotowner) {
2861 /* TODO : use CAP_FOWNER process capability */
2862 if (!processuid || (processuid == uid))
2863 allowed = TRUE;
2864 else
2865 errno = EPERM;
2868 return (allowed);
2871 #ifdef HAVE_SETXATTR /* extended attributes interface required */
2873 #if POSIXACLS
2876 * Set a new access or default Posix ACL to a file
2877 * (or remove ACL if no input data)
2878 * Validity of input data is checked after merging
2880 * Returns 0, or -1 if there is a problem which errno describes
2883 int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2884 const char *name, const char *value, size_t size,
2885 int flags)
2887 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2888 const struct CACHED_PERMISSIONS *cached;
2889 char *oldattr;
2890 uid_t processuid;
2891 const SID *usid;
2892 const SID *gsid;
2893 uid_t uid;
2894 uid_t gid;
2895 int res;
2896 mode_t mode;
2897 BOOL isdir;
2898 BOOL deflt;
2899 BOOL exist;
2900 int count;
2901 struct POSIX_SECURITY *oldpxdesc;
2902 struct POSIX_SECURITY *newpxdesc;
2904 /* get the current pxsec, either from cache or from old attribute */
2905 res = -1;
2906 deflt = !strcmp(name,"system.posix_acl_default");
2907 if (size)
2908 count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
2909 else
2910 count = 0;
2911 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2912 newpxdesc = (struct POSIX_SECURITY*)NULL;
2913 if ((!value
2914 || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
2915 && (!deflt || isdir || (!size && !value))) {
2916 cached = fetch_cache(scx, ni);
2917 if (cached) {
2918 uid = cached->uid;
2919 gid = cached->gid;
2920 oldpxdesc = cached->pxdesc;
2921 if (oldpxdesc) {
2922 mode = oldpxdesc->mode;
2923 newpxdesc = ntfs_replace_acl(oldpxdesc,
2924 (const struct POSIX_ACL*)value,count,deflt);
2926 } else {
2927 oldattr = getsecurityattr(scx->vol, ni);
2928 if (oldattr) {
2929 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
2930 #if OWNERFROMACL
2931 usid = ntfs_acl_owner(oldattr);
2932 #else
2933 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
2934 #endif
2935 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
2936 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2937 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2938 oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
2939 oldattr, usid, gsid, isdir);
2940 if (oldpxdesc) {
2941 if (deflt)
2942 exist = oldpxdesc->defcnt > 0;
2943 else
2944 exist = oldpxdesc->acccnt > 3;
2945 if ((exist && (flags & XATTR_CREATE))
2946 || (!exist && (flags & XATTR_REPLACE))) {
2947 errno = (exist ? EEXIST : ENODATA);
2948 } else {
2949 mode = oldpxdesc->mode;
2950 newpxdesc = ntfs_replace_acl(oldpxdesc,
2951 (const struct POSIX_ACL*)value,count,deflt);
2953 free(oldpxdesc);
2955 free(oldattr);
2958 } else
2959 errno = EINVAL;
2961 if (newpxdesc) {
2962 processuid = scx->uid;
2963 /* TODO : use CAP_FOWNER process capability */
2964 if (!processuid || (uid == processuid)) {
2966 * clear setgid if file group does
2967 * not match process group
2969 if (processuid && (gid != scx->gid)
2970 && !groupmember(scx, scx->uid, gid)) {
2971 newpxdesc->mode &= ~S_ISGID;
2973 res = ntfs_set_owner_mode(scx, ni, uid, gid,
2974 newpxdesc->mode, newpxdesc);
2975 } else
2976 errno = EPERM;
2977 free(newpxdesc);
2979 return (res ? -1 : 0);
2983 * Remove a default Posix ACL from a file
2985 * Returns 0, or -1 if there is a problem which errno describes
2988 int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2989 const char *name)
2991 return (ntfs_set_posix_acl(scx, ni, name,
2992 (const char*)NULL, 0, 0));
2995 #endif
2998 * Set a new NTFS ACL to a file
3000 * Returns 0, or -1 if there is a problem
3003 int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3004 const char *value, size_t size, int flags)
3006 char *attr;
3007 int res;
3009 res = -1;
3010 if ((size > 0)
3011 && !(flags & XATTR_CREATE)
3012 && ntfs_valid_descr(value,size)
3013 && (ntfs_attr_size(value) == size)) {
3014 /* need copying in order to write */
3015 attr = (char*)ntfs_malloc(size);
3016 if (attr) {
3017 memcpy(attr,value,size);
3018 res = update_secur_descr(scx->vol, attr, ni);
3020 * No need to invalidate standard caches :
3021 * the relation between a securid and
3022 * the associated protection is unchanged,
3023 * only the relation between a file and
3024 * its securid and protection is changed.
3026 #if CACHE_LEGACY_SIZE
3028 * we must however invalidate the legacy
3029 * cache, which is based on inode numbers.
3030 * For safety, invalidate even if updating
3031 * failed.
3033 if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3034 && !ni->security_id) {
3035 struct CACHED_PERMISSIONS_LEGACY legacy;
3037 legacy.mft_no = ni->mft_no;
3038 legacy.variable = (char*)NULL;
3039 legacy.varsize = 0;
3040 ntfs_invalidate_cache(scx->vol->legacy_cache,
3041 GENERIC(&legacy),
3042 (cache_compare)leg_compare,0);
3044 #endif
3045 free(attr);
3046 } else
3047 errno = ENOMEM;
3048 } else
3049 errno = EINVAL;
3050 return (res ? -1 : 0);
3053 #endif /* HAVE_SETXATTR */
3056 * Set new permissions to a file
3057 * Checks user mapping has been defined before request for setting
3059 * rejected if request is not originated by owner or root
3061 * returns 0 on success
3062 * -1 on failure, with errno = EIO
3065 int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
3067 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3068 const struct CACHED_PERMISSIONS *cached;
3069 char *oldattr;
3070 const SID *usid;
3071 const SID *gsid;
3072 uid_t processuid;
3073 uid_t uid;
3074 uid_t gid;
3075 int res;
3076 #if POSIXACLS
3077 BOOL isdir;
3078 int pxsize;
3079 const struct POSIX_SECURITY *oldpxdesc;
3080 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3081 #endif
3083 /* get the current owner, either from cache or from old attribute */
3084 res = 0;
3085 cached = fetch_cache(scx, ni);
3086 if (cached) {
3087 uid = cached->uid;
3088 gid = cached->gid;
3089 #if POSIXACLS
3090 oldpxdesc = cached->pxdesc;
3091 if (oldpxdesc) {
3092 /* must copy before merging */
3093 pxsize = sizeof(struct POSIX_SECURITY)
3094 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3095 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3096 if (newpxdesc) {
3097 memcpy(newpxdesc, oldpxdesc, pxsize);
3098 if (ntfs_merge_mode_posix(newpxdesc, mode))
3099 res = -1;
3100 } else
3101 res = -1;
3102 } else
3103 newpxdesc = (struct POSIX_SECURITY*)NULL;
3104 #endif
3105 } else {
3106 oldattr = getsecurityattr(scx->vol, ni);
3107 if (oldattr) {
3108 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3109 #if OWNERFROMACL
3110 usid = ntfs_acl_owner(oldattr);
3111 #else
3112 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3113 #endif
3114 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3115 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3116 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3117 #if POSIXACLS
3118 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
3119 newpxdesc = ntfs_build_permissions_posix(scx->mapping,
3120 oldattr, usid, gsid, isdir);
3121 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3122 res = -1;
3123 #endif
3124 free(oldattr);
3125 } else
3126 res = -1;
3129 if (!res) {
3130 processuid = scx->uid;
3131 /* TODO : use CAP_FOWNER process capability */
3132 if (!processuid || (uid == processuid)) {
3134 * clear setgid if file group does
3135 * not match process group
3137 if (processuid && (gid != scx->gid)
3138 && !groupmember(scx, scx->uid, gid))
3139 mode &= ~S_ISGID;
3140 #if POSIXACLS
3141 if (newpxdesc) {
3142 newpxdesc->mode = mode;
3143 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3144 mode, newpxdesc);
3145 } else
3146 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3147 mode, newpxdesc);
3148 #else
3149 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3150 #endif
3151 } else {
3152 errno = EPERM;
3153 res = -1; /* neither owner nor root */
3155 } else {
3157 * Should not happen : a default descriptor is generated
3158 * by getsecurityattr() when there are none
3160 ntfs_log_error("File has no security descriptor\n");
3161 res = -1;
3162 errno = EIO;
3164 #if POSIXACLS
3165 if (newpxdesc) free(newpxdesc);
3166 #endif
3167 return (res ? -1 : 0);
3171 * Create a default security descriptor for files whose descriptor
3172 * cannot be inherited
3175 int ntfs_sd_add_everyone(ntfs_inode *ni)
3177 /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
3178 SECURITY_DESCRIPTOR_RELATIVE *sd;
3179 ACL *acl;
3180 ACCESS_ALLOWED_ACE *ace;
3181 SID *sid;
3182 int ret, sd_len;
3184 /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
3186 * Calculate security descriptor length. We have 2 sub-authorities in
3187 * owner and group SIDs, but structure SID contain only one, so add
3188 * 4 bytes to every SID.
3190 sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
3191 sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
3192 sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
3193 if (!sd)
3194 return -1;
3196 sd->revision = SECURITY_DESCRIPTOR_REVISION;
3197 sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
3199 sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
3200 sid->revision = SID_REVISION;
3201 sid->sub_authority_count = 2;
3202 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3203 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3204 sid->identifier_authority.value[5] = 5;
3205 sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
3207 sid = (SID*)((u8*)sid + sizeof(SID) + 4);
3208 sid->revision = SID_REVISION;
3209 sid->sub_authority_count = 2;
3210 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3211 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3212 sid->identifier_authority.value[5] = 5;
3213 sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
3215 acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
3216 acl->revision = ACL_REVISION;
3217 acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
3218 acl->ace_count = const_cpu_to_le16(1);
3219 sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
3221 ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
3222 ace->type = ACCESS_ALLOWED_ACE_TYPE;
3223 ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
3224 ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
3225 ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
3226 ace->sid.revision = SID_REVISION;
3227 ace->sid.sub_authority_count = 1;
3228 ace->sid.sub_authority[0] = const_cpu_to_le32(0);
3229 ace->sid.identifier_authority.value[5] = 1;
3231 ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
3232 sd_len);
3233 if (ret)
3234 ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
3236 free(sd);
3237 return ret;
3241 * Check whether user can access a file in a specific way
3243 * Returns 1 if access is allowed, including user is root or no
3244 * user mapping defined
3245 * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
3246 * 0 and sets errno if there is a problem or if access
3247 * is not allowed
3249 * This is used for Posix ACL and checking creation of DOS file names
3252 int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
3253 ntfs_inode *ni,
3254 int accesstype) /* access type required (S_Ixxx values) */
3256 int perm;
3257 int res;
3258 int allow;
3259 struct stat stbuf;
3262 * Always allow for root unless execution is requested.
3263 * (was checked by fuse until kernel 2.6.29)
3264 * Also always allow if no mapping has been defined
3266 if (!scx->mapping[MAPUSERS]
3267 || (!scx->uid
3268 && (!(accesstype & S_IEXEC)
3269 || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
3270 allow = 1;
3271 else {
3272 perm = ntfs_get_perm(scx, ni, accesstype);
3273 if (perm >= 0) {
3274 res = EACCES;
3275 switch (accesstype) {
3276 case S_IEXEC:
3277 allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
3278 break;
3279 case S_IWRITE:
3280 allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
3281 break;
3282 case S_IWRITE + S_IEXEC:
3283 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3284 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3285 break;
3286 case S_IREAD:
3287 allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
3288 break;
3289 case S_IREAD + S_IEXEC:
3290 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3291 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3292 break;
3293 case S_IREAD + S_IWRITE:
3294 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3295 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
3296 break;
3297 case S_IWRITE + S_IEXEC + S_ISVTX:
3298 if (perm & S_ISVTX) {
3299 if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3300 && (stbuf.st_uid == scx->uid))
3301 allow = 1;
3302 else
3303 allow = 2;
3304 } else
3305 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3306 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3307 break;
3308 case S_IREAD + S_IWRITE + S_IEXEC:
3309 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3310 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3311 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3312 break;
3313 default :
3314 res = EINVAL;
3315 allow = 0;
3316 break;
3318 if (!allow)
3319 errno = res;
3320 } else
3321 allow = 0;
3323 return (allow);
3326 #if 0 /* not needed any more */
3329 * Check whether user can access the parent directory
3330 * of a file in a specific way
3332 * Returns true if access is allowed, including user is root and
3333 * no user mapping defined
3335 * Sets errno if there is a problem or if not allowed
3337 * This is used for Posix ACL and checking creation of DOS file names
3340 BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
3341 const char *path, int accesstype)
3343 int allow;
3344 char *dirpath;
3345 char *name;
3346 ntfs_inode *ni;
3347 ntfs_inode *dir_ni;
3348 struct stat stbuf;
3350 allow = 0;
3351 dirpath = strdup(path);
3352 if (dirpath) {
3353 /* the root of file system is seen as a parent of itself */
3354 /* is that correct ? */
3355 name = strrchr(dirpath, '/');
3356 *name = 0;
3357 dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
3358 if (dir_ni) {
3359 allow = ntfs_allowed_access(scx,
3360 dir_ni, accesstype);
3361 ntfs_inode_close(dir_ni);
3363 * for an not-owned sticky directory, have to
3364 * check whether file itself is owned
3366 if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
3367 && (allow == 2)) {
3368 ni = ntfs_pathname_to_inode(scx->vol, NULL,
3369 path);
3370 allow = FALSE;
3371 if (ni) {
3372 allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3373 && (stbuf.st_uid == scx->uid);
3374 ntfs_inode_close(ni);
3378 free(dirpath);
3380 return (allow); /* errno is set if not allowed */
3383 #endif
3386 * Define a new owner/group to a file
3388 * returns zero if successful
3391 int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3392 uid_t uid, gid_t gid)
3394 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3395 const struct CACHED_PERMISSIONS *cached;
3396 char *oldattr;
3397 const SID *usid;
3398 const SID *gsid;
3399 uid_t fileuid;
3400 uid_t filegid;
3401 mode_t mode;
3402 int perm;
3403 BOOL isdir;
3404 int res;
3405 #if POSIXACLS
3406 struct POSIX_SECURITY *pxdesc;
3407 BOOL pxdescbuilt = FALSE;
3408 #endif
3410 res = 0;
3411 /* get the current owner and mode from cache or security attributes */
3412 oldattr = (char*)NULL;
3413 cached = fetch_cache(scx,ni);
3414 if (cached) {
3415 fileuid = cached->uid;
3416 filegid = cached->gid;
3417 mode = cached->mode;
3418 #if POSIXACLS
3419 pxdesc = cached->pxdesc;
3420 if (!pxdesc)
3421 res = -1;
3422 #endif
3423 } else {
3424 fileuid = 0;
3425 filegid = 0;
3426 mode = 0;
3427 oldattr = getsecurityattr(scx->vol, ni);
3428 if (oldattr) {
3429 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3430 != const_cpu_to_le16(0);
3431 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3432 oldattr;
3433 gsid = (const SID*)
3434 &oldattr[le32_to_cpu(phead->group)];
3435 #if OWNERFROMACL
3436 usid = ntfs_acl_owner(oldattr);
3437 #else
3438 usid = (const SID*)
3439 &oldattr[le32_to_cpu(phead->owner)];
3440 #endif
3441 #if POSIXACLS
3442 pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3443 usid, gsid, isdir);
3444 if (pxdesc) {
3445 pxdescbuilt = TRUE;
3446 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3447 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3448 mode = perm = pxdesc->mode;
3449 } else
3450 res = -1;
3451 #else
3452 mode = perm = ntfs_build_permissions(oldattr,
3453 usid, gsid, isdir);
3454 if (perm >= 0) {
3455 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3456 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3457 } else
3458 res = -1;
3459 #endif
3460 free(oldattr);
3461 } else
3462 res = -1;
3464 if (!res) {
3465 /* check requested by root */
3466 /* or chgrp requested by owner to an owned group */
3467 if (!scx->uid
3468 || ((((int)uid < 0) || (uid == fileuid))
3469 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3470 && (fileuid == scx->uid))) {
3471 /* replace by the new usid and gsid */
3472 /* or reuse old gid and sid for cacheing */
3473 if ((int)uid < 0)
3474 uid = fileuid;
3475 if ((int)gid < 0)
3476 gid = filegid;
3477 /* clear setuid and setgid if owner has changed */
3478 /* unless request originated by root */
3479 if (uid && (fileuid != uid))
3480 mode &= 01777;
3481 #if POSIXACLS
3482 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3483 mode, pxdesc);
3484 #else
3485 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3486 #endif
3487 } else {
3488 res = -1; /* neither owner nor root */
3489 errno = EPERM;
3491 #if POSIXACLS
3492 if (pxdescbuilt)
3493 free(pxdesc);
3494 #endif
3495 } else {
3497 * Should not happen : a default descriptor is generated
3498 * by getsecurityattr() when there are none
3500 ntfs_log_error("File has no security descriptor\n");
3501 res = -1;
3502 errno = EIO;
3504 return (res ? -1 : 0);
3508 * Define new owner/group and mode to a file
3510 * returns zero if successful
3513 int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3514 uid_t uid, gid_t gid, const mode_t mode)
3516 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3517 const struct CACHED_PERMISSIONS *cached;
3518 char *oldattr;
3519 const SID *usid;
3520 const SID *gsid;
3521 uid_t fileuid;
3522 uid_t filegid;
3523 BOOL isdir;
3524 int res;
3525 #if POSIXACLS
3526 const struct POSIX_SECURITY *oldpxdesc;
3527 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3528 int pxsize;
3529 #endif
3531 res = 0;
3532 /* get the current owner and mode from cache or security attributes */
3533 oldattr = (char*)NULL;
3534 cached = fetch_cache(scx,ni);
3535 if (cached) {
3536 fileuid = cached->uid;
3537 filegid = cached->gid;
3538 #if POSIXACLS
3539 oldpxdesc = cached->pxdesc;
3540 if (oldpxdesc) {
3541 /* must copy before merging */
3542 pxsize = sizeof(struct POSIX_SECURITY)
3543 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3544 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3545 if (newpxdesc) {
3546 memcpy(newpxdesc, oldpxdesc, pxsize);
3547 if (ntfs_merge_mode_posix(newpxdesc, mode))
3548 res = -1;
3549 } else
3550 res = -1;
3552 #endif
3553 } else {
3554 fileuid = 0;
3555 filegid = 0;
3556 oldattr = getsecurityattr(scx->vol, ni);
3557 if (oldattr) {
3558 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3559 != const_cpu_to_le16(0);
3560 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3561 oldattr;
3562 gsid = (const SID*)
3563 &oldattr[le32_to_cpu(phead->group)];
3564 #if OWNERFROMACL
3565 usid = ntfs_acl_owner(oldattr);
3566 #else
3567 usid = (const SID*)
3568 &oldattr[le32_to_cpu(phead->owner)];
3569 #endif
3570 #if POSIXACLS
3571 newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3572 usid, gsid, isdir);
3573 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3574 res = -1;
3575 else {
3576 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3577 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3579 #endif
3580 free(oldattr);
3581 } else
3582 res = -1;
3584 if (!res) {
3585 /* check requested by root */
3586 /* or chgrp requested by owner to an owned group */
3587 if (!scx->uid
3588 || ((((int)uid < 0) || (uid == fileuid))
3589 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3590 && (fileuid == scx->uid))) {
3591 /* replace by the new usid and gsid */
3592 /* or reuse old gid and sid for cacheing */
3593 if ((int)uid < 0)
3594 uid = fileuid;
3595 if ((int)gid < 0)
3596 gid = filegid;
3597 #if POSIXACLS
3598 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3599 mode, newpxdesc);
3600 #else
3601 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3602 #endif
3603 } else {
3604 res = -1; /* neither owner nor root */
3605 errno = EPERM;
3607 } else {
3609 * Should not happen : a default descriptor is generated
3610 * by getsecurityattr() when there are none
3612 ntfs_log_error("File has no security descriptor\n");
3613 res = -1;
3614 errno = EIO;
3616 #if POSIXACLS
3617 free(newpxdesc);
3618 #endif
3619 return (res ? -1 : 0);
3623 * Build a security id for a descriptor inherited from
3624 * parent directory the Windows way
3627 static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
3628 const char *parentattr, BOOL fordir)
3630 const SECURITY_DESCRIPTOR_RELATIVE *pphead;
3631 const ACL *ppacl;
3632 const SID *usid;
3633 const SID *gsid;
3634 BIGSID defusid;
3635 BIGSID defgsid;
3636 int offpacl;
3637 int offowner;
3638 int offgroup;
3639 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
3640 ACL *pnacl;
3641 int parentattrsz;
3642 char *newattr;
3643 int newattrsz;
3644 int aclsz;
3645 int usidsz;
3646 int gsidsz;
3647 int pos;
3648 le32 securid;
3650 parentattrsz = ntfs_attr_size(parentattr);
3651 pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
3652 if (scx->mapping[MAPUSERS]) {
3653 usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
3654 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
3655 if (!usid)
3656 usid = adminsid;
3657 if (!gsid)
3658 gsid = adminsid;
3659 } else {
3661 * If there is no user mapping, we have to copy owner
3662 * and group from parent directory.
3663 * Windows never has to do that, because it can always
3664 * rely on a user mapping
3666 offowner = le32_to_cpu(pphead->owner);
3667 usid = (const SID*)&parentattr[offowner];
3668 offgroup = le32_to_cpu(pphead->group);
3669 gsid = (const SID*)&parentattr[offgroup];
3672 * new attribute is smaller than parent's
3673 * except for differences in SIDs which appear in
3674 * owner, group and possible grants and denials in
3675 * generic creator-owner and creator-group ACEs.
3676 * For directories, an ACE may be duplicated for
3677 * access and inheritance, so we double the count.
3679 usidsz = ntfs_sid_size(usid);
3680 gsidsz = ntfs_sid_size(gsid);
3681 newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
3682 if (fordir)
3683 newattrsz *= 2;
3684 newattr = (char*)ntfs_malloc(newattrsz);
3685 if (newattr) {
3686 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
3687 pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
3688 pnhead->alignment = 0;
3689 pnhead->control = SE_SELF_RELATIVE;
3690 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
3692 * locate and inherit DACL
3693 * do not test SE_DACL_PRESENT (wrong for "DR Watson")
3695 pnhead->dacl = const_cpu_to_le32(0);
3696 if (pphead->dacl) {
3697 offpacl = le32_to_cpu(pphead->dacl);
3698 ppacl = (const ACL*)&parentattr[offpacl];
3699 pnacl = (ACL*)&newattr[pos];
3700 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
3701 if (aclsz) {
3702 pnhead->dacl = cpu_to_le32(pos);
3703 pos += aclsz;
3704 pnhead->control |= SE_DACL_PRESENT;
3708 * locate and inherit SACL
3710 pnhead->sacl = const_cpu_to_le32(0);
3711 if (pphead->sacl) {
3712 offpacl = le32_to_cpu(pphead->sacl);
3713 ppacl = (const ACL*)&parentattr[offpacl];
3714 pnacl = (ACL*)&newattr[pos];
3715 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
3716 if (aclsz) {
3717 pnhead->sacl = cpu_to_le32(pos);
3718 pos += aclsz;
3719 pnhead->control |= SE_SACL_PRESENT;
3723 * inherit or redefine owner
3725 memcpy(&newattr[pos],usid,usidsz);
3726 pnhead->owner = cpu_to_le32(pos);
3727 pos += usidsz;
3729 * inherit or redefine group
3731 memcpy(&newattr[pos],gsid,gsidsz);
3732 pnhead->group = cpu_to_le32(pos);
3733 pos += usidsz;
3734 securid = setsecurityattr(scx->vol,
3735 (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
3736 free(newattr);
3737 } else
3738 securid = const_cpu_to_le32(0);
3739 return (securid);
3743 * Get an inherited security id
3745 * For Windows compatibility, the normal initial permission setting
3746 * may be inherited from the parent directory instead of being
3747 * defined by the creation arguments.
3749 * The following creates an inherited id for that purpose.
3751 * Note : the owner and group of parent directory are also
3752 * inherited (which is not the case on Windows) if no user mapping
3753 * is defined.
3755 * Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
3758 le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
3759 ntfs_inode *dir_ni, BOOL fordir)
3761 struct CACHED_PERMISSIONS *cached;
3762 char *parentattr;
3763 le32 securid;
3765 securid = const_cpu_to_le32(0);
3766 cached = (struct CACHED_PERMISSIONS*)NULL;
3768 * Try to get inherited id from cache
3770 if (test_nino_flag(dir_ni, v3_Extensions)
3771 && dir_ni->security_id) {
3772 cached = fetch_cache(scx, dir_ni);
3773 if (cached)
3774 securid = (fordir ? cached->inh_dirid
3775 : cached->inh_fileid);
3778 * Not cached or not available in cache, compute it all
3779 * Note : if parent directory has no id, it is not cacheable
3781 if (!securid) {
3782 parentattr = getsecurityattr(scx->vol, dir_ni);
3783 if (parentattr) {
3784 securid = build_inherited_id(scx,
3785 parentattr, fordir);
3786 free(parentattr);
3788 * Store the result into cache for further use
3790 if (securid) {
3791 cached = fetch_cache(scx, dir_ni);
3792 if (cached) {
3793 if (fordir)
3794 cached->inh_dirid = securid;
3795 else
3796 cached->inh_fileid = securid;
3801 return (securid);
3805 * Link a group to a member of group
3807 * Returns 0 if OK, -1 (and errno set) if error
3810 static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
3811 gid_t gid)
3813 struct group *group;
3814 char **grmem;
3815 int grcnt;
3816 gid_t *groups;
3817 int res;
3819 res = 0;
3820 group = getgrgid(gid);
3821 if (group && group->gr_mem) {
3822 grcnt = usermapping->grcnt;
3823 groups = usermapping->groups;
3824 grmem = group->gr_mem;
3825 while (*grmem && strcmp(user->pw_name, *grmem))
3826 grmem++;
3827 if (*grmem) {
3828 if (!grcnt)
3829 groups = (gid_t*)malloc(sizeof(gid_t));
3830 else
3831 groups = (gid_t*)realloc(groups,
3832 (grcnt+1)*sizeof(gid_t));
3833 if (groups)
3834 groups[grcnt++] = gid;
3835 else {
3836 res = -1;
3837 errno = ENOMEM;
3840 usermapping->grcnt = grcnt;
3841 usermapping->groups = groups;
3843 return (res);
3848 * Statically link group to users
3849 * This is based on groups defined in /etc/group and does not take
3850 * the groups dynamically set by setgroups() nor any changes in
3851 * /etc/group into account
3853 * Only mapped groups and root group are linked to mapped users
3855 * Returns 0 if OK, -1 (and errno set) if error
3859 static int link_group_members(struct SECURITY_CONTEXT *scx)
3861 struct MAPPING *usermapping;
3862 struct MAPPING *groupmapping;
3863 struct passwd *user;
3864 int res;
3866 res = 0;
3867 for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
3868 usermapping=usermapping->next) {
3869 usermapping->grcnt = 0;
3870 usermapping->groups = (gid_t*)NULL;
3871 user = getpwuid(usermapping->xid);
3872 if (user && user->pw_name) {
3873 for (groupmapping=scx->mapping[MAPGROUPS];
3874 groupmapping && !res;
3875 groupmapping=groupmapping->next) {
3876 if (link_single_group(usermapping, user,
3877 groupmapping->xid))
3878 res = -1;
3880 if (!res && link_single_group(usermapping,
3881 user, (gid_t)0))
3882 res = -1;
3885 return (res);
3889 * Apply default single user mapping
3890 * returns zero if successful
3893 static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
3894 uid_t uid, gid_t gid, const SID *usid)
3896 struct MAPPING *usermapping;
3897 struct MAPPING *groupmapping;
3898 SID *sid;
3899 int sidsz;
3900 int res;
3902 res = -1;
3903 sidsz = ntfs_sid_size(usid);
3904 sid = (SID*)ntfs_malloc(sidsz);
3905 if (sid) {
3906 memcpy(sid,usid,sidsz);
3907 usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
3908 if (usermapping) {
3909 groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
3910 if (groupmapping) {
3911 usermapping->sid = sid;
3912 usermapping->xid = uid;
3913 usermapping->next = (struct MAPPING*)NULL;
3914 groupmapping->sid = sid;
3915 groupmapping->xid = gid;
3916 groupmapping->next = (struct MAPPING*)NULL;
3917 scx->mapping[MAPUSERS] = usermapping;
3918 scx->mapping[MAPGROUPS] = groupmapping;
3919 res = 0;
3923 return (res);
3927 * Make sure there are no ambiguous mapping
3928 * Ambiguous mapping may lead to undesired configurations and
3929 * we had rather be safe until the consequences are understood
3932 #if 0 /* not activated for now */
3934 static BOOL check_mapping(const struct MAPPING *usermapping,
3935 const struct MAPPING *groupmapping)
3937 const struct MAPPING *mapping1;
3938 const struct MAPPING *mapping2;
3939 BOOL ambiguous;
3941 ambiguous = FALSE;
3942 for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
3943 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
3944 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
3945 if (mapping1->xid != mapping2->xid)
3946 ambiguous = TRUE;
3947 } else {
3948 if (mapping1->xid == mapping2->xid)
3949 ambiguous = TRUE;
3951 for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
3952 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
3953 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
3954 if (mapping1->xid != mapping2->xid)
3955 ambiguous = TRUE;
3956 } else {
3957 if (mapping1->xid == mapping2->xid)
3958 ambiguous = TRUE;
3960 return (ambiguous);
3963 #endif
3965 #if 0 /* not used any more */
3968 * Try and apply default single user mapping
3969 * returns zero if successful
3972 static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
3974 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3975 ntfs_inode *ni;
3976 char *securattr;
3977 const SID *usid;
3978 int res;
3980 res = -1;
3981 ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
3982 if (ni) {
3983 securattr = getsecurityattr(scx->vol, ni);
3984 if (securattr) {
3985 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
3986 usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
3987 if (ntfs_is_user_sid(usid))
3988 res = ntfs_do_default_mapping(scx,
3989 scx->uid, scx->gid, usid);
3990 free(securattr);
3992 ntfs_inode_close(ni);
3994 return (res);
3997 #endif
4000 * Basic read from a user mapping file on another volume
4003 static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
4005 return (read(*(int*)fileid, buf, size));
4010 * Read from a user mapping file on current NTFS partition
4013 static int localread(void *fileid, char *buf, size_t size, off_t offs)
4015 return (ntfs_attr_data_read((ntfs_inode*)fileid,
4016 AT_UNNAMED, 0, buf, size, offs));
4020 * Build the user mapping
4021 * - according to a mapping file if defined (or default present),
4022 * - or try default single user mapping if possible
4024 * The mapping is specific to a mounted device
4025 * No locking done, mounting assumed non multithreaded
4027 * returns zero if mapping is successful
4028 * (failure should not be interpreted as an error)
4031 int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
4032 BOOL allowdef)
4034 struct MAPLIST *item;
4035 struct MAPLIST *firstitem;
4036 struct MAPPING *usermapping;
4037 struct MAPPING *groupmapping;
4038 ntfs_inode *ni;
4039 int fd;
4040 static struct {
4041 u8 revision;
4042 u8 levels;
4043 be16 highbase;
4044 be32 lowbase;
4045 le32 level1;
4046 le32 level2;
4047 le32 level3;
4048 le32 level4;
4049 le32 level5;
4050 } defmap = {
4051 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
4052 const_cpu_to_le32(21),
4053 const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
4054 const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
4057 /* be sure not to map anything until done */
4058 scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
4059 scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
4061 if (!usermap_path) usermap_path = MAPPINGFILE;
4062 if (usermap_path[0] == '/') {
4063 fd = open(usermap_path,O_RDONLY);
4064 if (fd > 0) {
4065 firstitem = ntfs_read_mapping(basicread, (void*)&fd);
4066 close(fd);
4067 } else
4068 firstitem = (struct MAPLIST*)NULL;
4069 } else {
4070 ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
4071 if (ni) {
4072 firstitem = ntfs_read_mapping(localread, ni);
4073 ntfs_inode_close(ni);
4074 } else
4075 firstitem = (struct MAPLIST*)NULL;
4079 if (firstitem) {
4080 usermapping = ntfs_do_user_mapping(firstitem);
4081 groupmapping = ntfs_do_group_mapping(firstitem);
4082 if (usermapping && groupmapping) {
4083 scx->mapping[MAPUSERS] = usermapping;
4084 scx->mapping[MAPGROUPS] = groupmapping;
4085 } else
4086 ntfs_log_error("There were no valid user or no valid group\n");
4087 /* now we can free the memory copy of input text */
4088 /* and rely on internal representation */
4089 while (firstitem) {
4090 item = firstitem->next;
4091 free(firstitem);
4092 firstitem = item;
4094 } else {
4095 /* no mapping file, try a default mapping */
4096 if (allowdef) {
4097 if (!ntfs_do_default_mapping(scx,
4098 0, 0, (const SID*)&defmap))
4099 ntfs_log_info("Using default user mapping\n");
4102 return (!scx->mapping[MAPUSERS] || link_group_members(scx));
4105 #ifdef HAVE_SETXATTR /* extended attributes interface required */
4108 * Get the ntfs attribute into an extended attribute
4109 * The attribute is returned according to cpu endianness
4112 int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
4114 u32 attrib;
4115 size_t outsize;
4117 outsize = 0; /* default to no data and no error */
4118 if (ni) {
4119 attrib = le32_to_cpu(ni->flags);
4120 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4121 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4122 else
4123 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4124 if (!attrib)
4125 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4126 outsize = sizeof(FILE_ATTR_FLAGS);
4127 if (size >= outsize) {
4128 if (value)
4129 memcpy(value,&attrib,outsize);
4130 else
4131 errno = EINVAL;
4134 return (outsize ? (int)outsize : -errno);
4138 * Return the ntfs attribute into an extended attribute
4139 * The attribute is expected according to cpu endianness
4141 * Returns 0, or -1 if there is a problem
4144 int ntfs_set_ntfs_attrib(ntfs_inode *ni,
4145 const char *value, size_t size, int flags)
4147 u32 attrib;
4148 le32 settable;
4149 ATTR_FLAGS dirflags;
4150 int res;
4152 res = -1;
4153 if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
4154 if (!(flags & XATTR_CREATE)) {
4155 /* copy to avoid alignment problems */
4156 memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
4157 settable = FILE_ATTR_SETTABLE;
4158 res = 0;
4159 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4161 * Accept changing compression for a directory
4162 * and set index root accordingly
4164 settable |= FILE_ATTR_COMPRESSED;
4165 if ((ni->flags ^ cpu_to_le32(attrib))
4166 & FILE_ATTR_COMPRESSED) {
4167 if (ni->flags & FILE_ATTR_COMPRESSED)
4168 dirflags = const_cpu_to_le16(0);
4169 else
4170 dirflags = ATTR_IS_COMPRESSED;
4171 res = ntfs_attr_set_flags(ni,
4172 AT_INDEX_ROOT,
4173 NTFS_INDEX_I30, 4,
4174 dirflags,
4175 ATTR_COMPRESSION_MASK);
4178 if (!res) {
4179 ni->flags = (ni->flags & ~settable)
4180 | (cpu_to_le32(attrib) & settable);
4181 NInoFileNameSetDirty(ni);
4182 NInoSetDirty(ni);
4184 } else
4185 errno = EEXIST;
4186 } else
4187 errno = EINVAL;
4188 return (res ? -1 : 0);
4191 #endif /* HAVE_SETXATTR */
4194 * Open $Secure once for all
4195 * returns zero if it succeeds
4196 * non-zero if it fails. This is not an error (on NTFS v1.x)
4200 int ntfs_open_secure(ntfs_volume *vol)
4202 ntfs_inode *ni;
4203 int res;
4205 res = -1;
4206 vol->secure_ni = (ntfs_inode*)NULL;
4207 vol->secure_xsii = (ntfs_index_context*)NULL;
4208 vol->secure_xsdh = (ntfs_index_context*)NULL;
4209 if (vol->major_ver >= 3) {
4210 /* make sure this is a genuine $Secure inode 9 */
4211 ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
4212 if (ni && (ni->mft_no == 9)) {
4213 vol->secure_reentry = 0;
4214 vol->secure_xsii = ntfs_index_ctx_get(ni,
4215 sii_stream, 4);
4216 vol->secure_xsdh = ntfs_index_ctx_get(ni,
4217 sdh_stream, 4);
4218 if (ni && vol->secure_xsii && vol->secure_xsdh) {
4219 vol->secure_ni = ni;
4220 res = 0;
4224 return (res);
4228 * Final cleaning
4229 * Allocated memory is freed to facilitate the detection of memory leaks
4232 void ntfs_close_secure(struct SECURITY_CONTEXT *scx)
4234 ntfs_volume *vol;
4236 vol = scx->vol;
4237 if (vol->secure_ni) {
4238 ntfs_index_ctx_put(vol->secure_xsii);
4239 ntfs_index_ctx_put(vol->secure_xsdh);
4240 ntfs_inode_close(vol->secure_ni);
4243 ntfs_free_mapping(scx->mapping);
4244 free_caches(scx);
4248 * API for direct access to security descriptors
4249 * based on Win32 API
4254 * Selective feeding of a security descriptor into user buffer
4256 * Returns TRUE if successful
4259 static BOOL feedsecurityattr(const char *attr, u32 selection,
4260 char *buf, u32 buflen, u32 *psize)
4262 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4263 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
4264 const ACL *pdacl;
4265 const ACL *psacl;
4266 const SID *pusid;
4267 const SID *pgsid;
4268 unsigned int offdacl;
4269 unsigned int offsacl;
4270 unsigned int offowner;
4271 unsigned int offgroup;
4272 unsigned int daclsz;
4273 unsigned int saclsz;
4274 unsigned int usidsz;
4275 unsigned int gsidsz;
4276 unsigned int size; /* size of requested attributes */
4277 BOOL ok;
4278 unsigned int pos;
4279 unsigned int avail;
4280 le16 control;
4282 avail = 0;
4283 control = SE_SELF_RELATIVE;
4284 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4285 size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4287 /* locate DACL if requested and available */
4288 if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
4289 offdacl = le32_to_cpu(phead->dacl);
4290 pdacl = (const ACL*)&attr[offdacl];
4291 daclsz = le16_to_cpu(pdacl->size);
4292 size += daclsz;
4293 avail |= DACL_SECURITY_INFORMATION;
4294 } else
4295 offdacl = daclsz = 0;
4297 /* locate owner if requested and available */
4298 offowner = le32_to_cpu(phead->owner);
4299 if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
4300 /* find end of USID */
4301 pusid = (const SID*)&attr[offowner];
4302 usidsz = ntfs_sid_size(pusid);
4303 size += usidsz;
4304 avail |= OWNER_SECURITY_INFORMATION;
4305 } else
4306 offowner = usidsz = 0;
4308 /* locate group if requested and available */
4309 offgroup = le32_to_cpu(phead->group);
4310 if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
4311 /* find end of GSID */
4312 pgsid = (const SID*)&attr[offgroup];
4313 gsidsz = ntfs_sid_size(pgsid);
4314 size += gsidsz;
4315 avail |= GROUP_SECURITY_INFORMATION;
4316 } else
4317 offgroup = gsidsz = 0;
4319 /* locate SACL if requested and available */
4320 if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
4321 /* find end of SACL */
4322 offsacl = le32_to_cpu(phead->sacl);
4323 psacl = (const ACL*)&attr[offsacl];
4324 saclsz = le16_to_cpu(psacl->size);
4325 size += saclsz;
4326 avail |= SACL_SECURITY_INFORMATION;
4327 } else
4328 offsacl = saclsz = 0;
4331 * Check having enough size in destination buffer
4332 * (required size is returned nevertheless so that
4333 * the request can be reissued with adequate size)
4335 if (size > buflen) {
4336 *psize = size;
4337 errno = EINVAL;
4338 ok = FALSE;
4339 } else {
4340 if (selection & OWNER_SECURITY_INFORMATION)
4341 control |= phead->control & SE_OWNER_DEFAULTED;
4342 if (selection & GROUP_SECURITY_INFORMATION)
4343 control |= phead->control & SE_GROUP_DEFAULTED;
4344 if (selection & DACL_SECURITY_INFORMATION)
4345 control |= phead->control
4346 & (SE_DACL_PRESENT
4347 | SE_DACL_DEFAULTED
4348 | SE_DACL_AUTO_INHERITED
4349 | SE_DACL_PROTECTED);
4350 if (selection & SACL_SECURITY_INFORMATION)
4351 control |= phead->control
4352 & (SE_SACL_PRESENT
4353 | SE_SACL_DEFAULTED
4354 | SE_SACL_AUTO_INHERITED
4355 | SE_SACL_PROTECTED);
4357 * copy header and feed new flags, even if no detailed data
4359 memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
4360 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
4361 pnhead->control = control;
4362 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4364 /* copy DACL if requested and available */
4365 if (selection & avail & DACL_SECURITY_INFORMATION) {
4366 pnhead->dacl = cpu_to_le32(pos);
4367 memcpy(&buf[pos],&attr[offdacl],daclsz);
4368 pos += daclsz;
4369 } else
4370 pnhead->dacl = const_cpu_to_le32(0);
4372 /* copy SACL if requested and available */
4373 if (selection & avail & SACL_SECURITY_INFORMATION) {
4374 pnhead->sacl = cpu_to_le32(pos);
4375 memcpy(&buf[pos],&attr[offsacl],saclsz);
4376 pos += saclsz;
4377 } else
4378 pnhead->sacl = const_cpu_to_le32(0);
4380 /* copy owner if requested and available */
4381 if (selection & avail & OWNER_SECURITY_INFORMATION) {
4382 pnhead->owner = cpu_to_le32(pos);
4383 memcpy(&buf[pos],&attr[offowner],usidsz);
4384 pos += usidsz;
4385 } else
4386 pnhead->owner = const_cpu_to_le32(0);
4388 /* copy group if requested and available */
4389 if (selection & avail & GROUP_SECURITY_INFORMATION) {
4390 pnhead->group = cpu_to_le32(pos);
4391 memcpy(&buf[pos],&attr[offgroup],gsidsz);
4392 pos += gsidsz;
4393 } else
4394 pnhead->group = const_cpu_to_le32(0);
4395 if (pos != size)
4396 ntfs_log_error("Error in security descriptor size\n");
4397 *psize = size;
4398 ok = TRUE;
4401 return (ok);
4405 * Merge a new security descriptor into the old one
4406 * and assign to designated file
4408 * Returns TRUE if successful
4411 static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
4412 const char *newattr, u32 selection, ntfs_inode *ni)
4414 const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
4415 const SECURITY_DESCRIPTOR_RELATIVE *newhead;
4416 SECURITY_DESCRIPTOR_RELATIVE *targhead;
4417 const ACL *pdacl;
4418 const ACL *psacl;
4419 const SID *powner;
4420 const SID *pgroup;
4421 int offdacl;
4422 int offsacl;
4423 int offowner;
4424 int offgroup;
4425 unsigned int size;
4426 le16 control;
4427 char *target;
4428 int pos;
4429 int oldattrsz;
4430 int newattrsz;
4431 BOOL ok;
4433 ok = FALSE; /* default return */
4434 oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
4435 newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
4436 oldattrsz = ntfs_attr_size(oldattr);
4437 newattrsz = ntfs_attr_size(newattr);
4438 target = (char*)ntfs_malloc(oldattrsz + newattrsz);
4439 if (target) {
4440 targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
4441 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4442 control = SE_SELF_RELATIVE;
4444 * copy new DACL if selected
4445 * or keep old DACL if any
4447 if ((selection & DACL_SECURITY_INFORMATION) ?
4448 newhead->dacl : oldhead->dacl) {
4449 if (selection & DACL_SECURITY_INFORMATION) {
4450 offdacl = le32_to_cpu(newhead->dacl);
4451 pdacl = (const ACL*)&newattr[offdacl];
4452 } else {
4453 offdacl = le32_to_cpu(oldhead->dacl);
4454 pdacl = (const ACL*)&oldattr[offdacl];
4456 size = le16_to_cpu(pdacl->size);
4457 memcpy(&target[pos], pdacl, size);
4458 targhead->dacl = cpu_to_le32(pos);
4459 pos += size;
4460 } else
4461 targhead->dacl = const_cpu_to_le32(0);
4462 if (selection & DACL_SECURITY_INFORMATION) {
4463 control |= newhead->control
4464 & (SE_DACL_PRESENT
4465 | SE_DACL_DEFAULTED
4466 | SE_DACL_PROTECTED);
4467 if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
4468 control |= SE_DACL_AUTO_INHERITED;
4469 } else
4470 control |= oldhead->control
4471 & (SE_DACL_PRESENT
4472 | SE_DACL_DEFAULTED
4473 | SE_DACL_AUTO_INHERITED
4474 | SE_DACL_PROTECTED);
4476 * copy new SACL if selected
4477 * or keep old SACL if any
4479 if ((selection & SACL_SECURITY_INFORMATION) ?
4480 newhead->sacl : oldhead->sacl) {
4481 if (selection & SACL_SECURITY_INFORMATION) {
4482 offsacl = le32_to_cpu(newhead->sacl);
4483 psacl = (const ACL*)&newattr[offsacl];
4484 } else {
4485 offsacl = le32_to_cpu(oldhead->sacl);
4486 psacl = (const ACL*)&oldattr[offsacl];
4488 size = le16_to_cpu(psacl->size);
4489 memcpy(&target[pos], psacl, size);
4490 targhead->sacl = cpu_to_le32(pos);
4491 pos += size;
4492 } else
4493 targhead->sacl = const_cpu_to_le32(0);
4494 if (selection & SACL_SECURITY_INFORMATION) {
4495 control |= newhead->control
4496 & (SE_SACL_PRESENT
4497 | SE_SACL_DEFAULTED
4498 | SE_SACL_PROTECTED);
4499 if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
4500 control |= SE_SACL_AUTO_INHERITED;
4501 } else
4502 control |= oldhead->control
4503 & (SE_SACL_PRESENT
4504 | SE_SACL_DEFAULTED
4505 | SE_SACL_AUTO_INHERITED
4506 | SE_SACL_PROTECTED);
4508 * copy new OWNER if selected
4509 * or keep old OWNER if any
4511 if ((selection & OWNER_SECURITY_INFORMATION) ?
4512 newhead->owner : oldhead->owner) {
4513 if (selection & OWNER_SECURITY_INFORMATION) {
4514 offowner = le32_to_cpu(newhead->owner);
4515 powner = (const SID*)&newattr[offowner];
4516 } else {
4517 offowner = le32_to_cpu(oldhead->owner);
4518 powner = (const SID*)&oldattr[offowner];
4520 size = ntfs_sid_size(powner);
4521 memcpy(&target[pos], powner, size);
4522 targhead->owner = cpu_to_le32(pos);
4523 pos += size;
4524 } else
4525 targhead->owner = const_cpu_to_le32(0);
4526 if (selection & OWNER_SECURITY_INFORMATION)
4527 control |= newhead->control & SE_OWNER_DEFAULTED;
4528 else
4529 control |= oldhead->control & SE_OWNER_DEFAULTED;
4531 * copy new GROUP if selected
4532 * or keep old GROUP if any
4534 if ((selection & GROUP_SECURITY_INFORMATION) ?
4535 newhead->group : oldhead->group) {
4536 if (selection & GROUP_SECURITY_INFORMATION) {
4537 offgroup = le32_to_cpu(newhead->group);
4538 pgroup = (const SID*)&newattr[offgroup];
4539 control |= newhead->control
4540 & SE_GROUP_DEFAULTED;
4541 } else {
4542 offgroup = le32_to_cpu(oldhead->group);
4543 pgroup = (const SID*)&oldattr[offgroup];
4544 control |= oldhead->control
4545 & SE_GROUP_DEFAULTED;
4547 size = ntfs_sid_size(pgroup);
4548 memcpy(&target[pos], pgroup, size);
4549 targhead->group = cpu_to_le32(pos);
4550 pos += size;
4551 } else
4552 targhead->group = const_cpu_to_le32(0);
4553 if (selection & GROUP_SECURITY_INFORMATION)
4554 control |= newhead->control & SE_GROUP_DEFAULTED;
4555 else
4556 control |= oldhead->control & SE_GROUP_DEFAULTED;
4557 targhead->revision = SECURITY_DESCRIPTOR_REVISION;
4558 targhead->alignment = 0;
4559 targhead->control = control;
4560 ok = !update_secur_descr(vol, target, ni);
4561 free(target);
4563 return (ok);
4567 * Return the security descriptor of a file
4568 * This is intended to be similar to GetFileSecurity() from Win32
4569 * in order to facilitate the development of portable tools
4571 * returns zero if unsuccessful (following Win32 conventions)
4572 * -1 if no securid
4573 * the securid if any
4575 * The Win32 API is :
4577 * BOOL WINAPI GetFileSecurity(
4578 * __in LPCTSTR lpFileName,
4579 * __in SECURITY_INFORMATION RequestedInformation,
4580 * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor,
4581 * __in DWORD nLength,
4582 * __out LPDWORD lpnLengthNeeded
4583 * );
4587 int ntfs_get_file_security(struct SECURITY_API *scapi,
4588 const char *path, u32 selection,
4589 char *buf, u32 buflen, u32 *psize)
4591 ntfs_inode *ni;
4592 char *attr;
4593 int res;
4595 res = 0; /* default return */
4596 if (scapi && (scapi->magic == MAGIC_API)) {
4597 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4598 if (ni) {
4599 attr = getsecurityattr(scapi->security.vol, ni);
4600 if (attr) {
4601 if (feedsecurityattr(attr,selection,
4602 buf,buflen,psize)) {
4603 if (test_nino_flag(ni, v3_Extensions)
4604 && ni->security_id)
4605 res = le32_to_cpu(
4606 ni->security_id);
4607 else
4608 res = -1;
4610 free(attr);
4612 ntfs_inode_close(ni);
4613 } else
4614 errno = ENOENT;
4615 if (!res) *psize = 0;
4616 } else
4617 errno = EINVAL; /* do not clear *psize */
4618 return (res);
4623 * Set the security descriptor of a file or directory
4624 * This is intended to be similar to SetFileSecurity() from Win32
4625 * in order to facilitate the development of portable tools
4627 * returns zero if unsuccessful (following Win32 conventions)
4628 * -1 if no securid
4629 * the securid if any
4631 * The Win32 API is :
4633 * BOOL WINAPI SetFileSecurity(
4634 * __in LPCTSTR lpFileName,
4635 * __in SECURITY_INFORMATION SecurityInformation,
4636 * __in PSECURITY_DESCRIPTOR pSecurityDescriptor
4637 * );
4640 int ntfs_set_file_security(struct SECURITY_API *scapi,
4641 const char *path, u32 selection, const char *attr)
4643 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4644 ntfs_inode *ni;
4645 int attrsz;
4646 BOOL missing;
4647 char *oldattr;
4648 int res;
4650 res = 0; /* default return */
4651 if (scapi && (scapi->magic == MAGIC_API) && attr) {
4652 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4653 attrsz = ntfs_attr_size(attr);
4654 /* if selected, owner and group must be present or defaulted */
4655 missing = ((selection & OWNER_SECURITY_INFORMATION)
4656 && !phead->owner
4657 && !(phead->control & SE_OWNER_DEFAULTED))
4658 || ((selection & GROUP_SECURITY_INFORMATION)
4659 && !phead->group
4660 && !(phead->control & SE_GROUP_DEFAULTED));
4661 if (!missing
4662 && (phead->control & SE_SELF_RELATIVE)
4663 && ntfs_valid_descr(attr, attrsz)) {
4664 ni = ntfs_pathname_to_inode(scapi->security.vol,
4665 NULL, path);
4666 if (ni) {
4667 oldattr = getsecurityattr(scapi->security.vol,
4668 ni);
4669 if (oldattr) {
4670 if (mergesecurityattr(
4671 scapi->security.vol,
4672 oldattr, attr,
4673 selection, ni)) {
4674 if (test_nino_flag(ni,
4675 v3_Extensions))
4676 res = le32_to_cpu(
4677 ni->security_id);
4678 else
4679 res = -1;
4681 free(oldattr);
4683 ntfs_inode_close(ni);
4685 } else
4686 errno = EINVAL;
4687 } else
4688 errno = EINVAL;
4689 return (res);
4694 * Return the attributes of a file
4695 * This is intended to be similar to GetFileAttributes() from Win32
4696 * in order to facilitate the development of portable tools
4698 * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
4700 * The Win32 API is :
4702 * DWORD WINAPI GetFileAttributes(
4703 * __in LPCTSTR lpFileName
4704 * );
4707 int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
4709 ntfs_inode *ni;
4710 s32 attrib;
4712 attrib = -1; /* default return */
4713 if (scapi && (scapi->magic == MAGIC_API) && path) {
4714 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4715 if (ni) {
4716 attrib = le32_to_cpu(ni->flags);
4717 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4718 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4719 else
4720 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4721 if (!attrib)
4722 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4724 ntfs_inode_close(ni);
4725 } else
4726 errno = ENOENT;
4727 } else
4728 errno = EINVAL; /* do not clear *psize */
4729 return (attrib);
4734 * Set attributes to a file or directory
4735 * This is intended to be similar to SetFileAttributes() from Win32
4736 * in order to facilitate the development of portable tools
4738 * Only a few flags can be set (same list as Win32)
4740 * returns zero if unsuccessful (following Win32 conventions)
4741 * nonzero if successful
4743 * The Win32 API is :
4745 * BOOL WINAPI SetFileAttributes(
4746 * __in LPCTSTR lpFileName,
4747 * __in DWORD dwFileAttributes
4748 * );
4751 BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
4752 const char *path, s32 attrib)
4754 ntfs_inode *ni;
4755 le32 settable;
4756 ATTR_FLAGS dirflags;
4757 int res;
4759 res = 0; /* default return */
4760 if (scapi && (scapi->magic == MAGIC_API) && path) {
4761 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4762 if (ni) {
4763 settable = FILE_ATTR_SETTABLE;
4764 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4766 * Accept changing compression for a directory
4767 * and set index root accordingly
4769 settable |= FILE_ATTR_COMPRESSED;
4770 if ((ni->flags ^ cpu_to_le32(attrib))
4771 & FILE_ATTR_COMPRESSED) {
4772 if (ni->flags & FILE_ATTR_COMPRESSED)
4773 dirflags = const_cpu_to_le16(0);
4774 else
4775 dirflags = ATTR_IS_COMPRESSED;
4776 res = ntfs_attr_set_flags(ni,
4777 AT_INDEX_ROOT,
4778 NTFS_INDEX_I30, 4,
4779 dirflags,
4780 ATTR_COMPRESSION_MASK);
4783 if (!res) {
4784 ni->flags = (ni->flags & ~settable)
4785 | (cpu_to_le32(attrib) & settable);
4786 NInoSetDirty(ni);
4788 if (!ntfs_inode_close(ni))
4789 res = -1;
4790 } else
4791 errno = ENOENT;
4793 return (res);
4797 BOOL ntfs_read_directory(struct SECURITY_API *scapi,
4798 const char *path, ntfs_filldir_t callback, void *context)
4800 ntfs_inode *ni;
4801 BOOL ok;
4802 s64 pos;
4804 ok = FALSE; /* default return */
4805 if (scapi && (scapi->magic == MAGIC_API) && callback) {
4806 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4807 if (ni) {
4808 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4809 pos = 0;
4810 ntfs_readdir(ni,&pos,context,callback);
4811 ok = !ntfs_inode_close(ni);
4812 } else {
4813 ntfs_inode_close(ni);
4814 errno = ENOTDIR;
4816 } else
4817 errno = ENOENT;
4818 } else
4819 errno = EINVAL; /* do not clear *psize */
4820 return (ok);
4824 * read $SDS (for auditing security data)
4826 * Returns the number or read bytes, or -1 if there is an error
4829 int ntfs_read_sds(struct SECURITY_API *scapi,
4830 char *buf, u32 size, u32 offset)
4832 int got;
4834 got = -1; /* default return */
4835 if (scapi && (scapi->magic == MAGIC_API)) {
4836 if (scapi->security.vol->secure_ni)
4837 got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
4838 STREAM_SDS, 4, buf, size, offset);
4839 else
4840 errno = EOPNOTSUPP;
4841 } else
4842 errno = EINVAL;
4843 return (got);
4847 * read $SII (for auditing security data)
4849 * Returns next entry, or NULL if there is an error
4852 INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
4853 INDEX_ENTRY *entry)
4855 SII_INDEX_KEY key;
4856 INDEX_ENTRY *ret;
4857 BOOL found;
4858 ntfs_index_context *xsii;
4860 ret = (INDEX_ENTRY*)NULL; /* default return */
4861 if (scapi && (scapi->magic == MAGIC_API)) {
4862 xsii = scapi->security.vol->secure_xsii;
4863 if (xsii) {
4864 if (!entry) {
4865 key.security_id = const_cpu_to_le32(0);
4866 found = !ntfs_index_lookup((char*)&key,
4867 sizeof(SII_INDEX_KEY), xsii);
4868 /* not supposed to find */
4869 if (!found && (errno == ENOENT))
4870 ret = xsii->entry;
4871 } else
4872 ret = ntfs_index_next(entry,xsii);
4873 if (!ret)
4874 errno = ENODATA;
4875 } else
4876 errno = EOPNOTSUPP;
4877 } else
4878 errno = EINVAL;
4879 return (ret);
4883 * read $SDH (for auditing security data)
4885 * Returns next entry, or NULL if there is an error
4888 INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
4889 INDEX_ENTRY *entry)
4891 SDH_INDEX_KEY key;
4892 INDEX_ENTRY *ret;
4893 BOOL found;
4894 ntfs_index_context *xsdh;
4896 ret = (INDEX_ENTRY*)NULL; /* default return */
4897 if (scapi && (scapi->magic == MAGIC_API)) {
4898 xsdh = scapi->security.vol->secure_xsdh;
4899 if (xsdh) {
4900 if (!entry) {
4901 key.hash = const_cpu_to_le32(0);
4902 key.security_id = const_cpu_to_le32(0);
4903 found = !ntfs_index_lookup((char*)&key,
4904 sizeof(SDH_INDEX_KEY), xsdh);
4905 /* not supposed to find */
4906 if (!found && (errno == ENOENT))
4907 ret = xsdh->entry;
4908 } else
4909 ret = ntfs_index_next(entry,xsdh);
4910 if (!ret)
4911 errno = ENODATA;
4912 } else errno = ENOTSUP;
4913 } else
4914 errno = EINVAL;
4915 return (ret);
4919 * Get the mapped user SID
4920 * A buffer of 40 bytes has to be supplied
4922 * returns the size of the SID, or zero and errno set if not found
4925 int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
4927 const SID *usid;
4928 BIGSID defusid;
4929 int size;
4931 size = 0;
4932 if (scapi && (scapi->magic == MAGIC_API)) {
4933 usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
4934 if (usid) {
4935 size = ntfs_sid_size(usid);
4936 memcpy(buf,usid,size);
4937 } else
4938 errno = ENODATA;
4939 } else
4940 errno = EINVAL;
4941 return (size);
4945 * Get the mapped group SID
4946 * A buffer of 40 bytes has to be supplied
4948 * returns the size of the SID, or zero and errno set if not found
4951 int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
4953 const SID *gsid;
4954 BIGSID defgsid;
4955 int size;
4957 size = 0;
4958 if (scapi && (scapi->magic == MAGIC_API)) {
4959 gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
4960 if (gsid) {
4961 size = ntfs_sid_size(gsid);
4962 memcpy(buf,gsid,size);
4963 } else
4964 errno = ENODATA;
4965 } else
4966 errno = EINVAL;
4967 return (size);
4971 * Get the user mapped to a SID
4973 * returns the uid, or -1 if not found
4976 int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
4978 int uid;
4980 uid = -1;
4981 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
4982 if (ntfs_same_sid(usid,adminsid))
4983 uid = 0;
4984 else {
4985 uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
4986 if (!uid) {
4987 uid = -1;
4988 errno = ENODATA;
4991 } else
4992 errno = EINVAL;
4993 return (uid);
4997 * Get the group mapped to a SID
4999 * returns the uid, or -1 if not found
5002 int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
5004 int gid;
5006 gid = -1;
5007 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
5008 if (ntfs_same_sid(gsid,adminsid))
5009 gid = 0;
5010 else {
5011 gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
5012 if (!gid) {
5013 gid = -1;
5014 errno = ENODATA;
5017 } else
5018 errno = EINVAL;
5019 return (gid);
5023 * Initializations before calling ntfs_get_file_security()
5024 * ntfs_set_file_security() and ntfs_read_directory()
5026 * Only allowed for root
5028 * Returns an (obscured) struct SECURITY_API* needed for further calls
5029 * NULL if not root (EPERM) or device is mounted (EBUSY)
5032 struct SECURITY_API *ntfs_initialize_file_security(const char *device,
5033 unsigned long flags)
5035 ntfs_volume *vol;
5036 unsigned long mntflag;
5037 int mnt;
5038 struct SECURITY_API *scapi;
5039 struct SECURITY_CONTEXT *scx;
5041 scapi = (struct SECURITY_API*)NULL;
5042 mnt = ntfs_check_if_mounted(device, &mntflag);
5043 if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
5044 vol = ntfs_mount(device, flags);
5045 if (vol) {
5046 scapi = (struct SECURITY_API*)
5047 ntfs_malloc(sizeof(struct SECURITY_API));
5048 if (!ntfs_volume_get_free_space(vol)
5049 && scapi) {
5050 scapi->magic = MAGIC_API;
5051 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
5052 scx = &scapi->security;
5053 scx->vol = vol;
5054 scx->uid = getuid();
5055 scx->gid = getgid();
5056 scx->pseccache = &scapi->seccache;
5057 scx->vol->secure_flags = 0;
5058 /* accept no mapping and no $Secure */
5059 ntfs_build_mapping(scx,(const char*)NULL,TRUE);
5060 ntfs_open_secure(vol);
5061 } else {
5062 if (scapi)
5063 free(scapi);
5064 else
5065 errno = ENOMEM;
5066 mnt = ntfs_umount(vol,FALSE);
5067 scapi = (struct SECURITY_API*)NULL;
5070 } else
5071 if (getuid())
5072 errno = EPERM;
5073 else
5074 errno = EBUSY;
5075 return (scapi);
5079 * Leaving after ntfs_initialize_file_security()
5081 * Returns FALSE if FAILED
5084 BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
5086 int ok;
5087 ntfs_volume *vol;
5089 ok = FALSE;
5090 if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
5091 vol = scapi->security.vol;
5092 ntfs_close_secure(&scapi->security);
5093 free(scapi);
5094 if (!ntfs_umount(vol, 0))
5095 ok = TRUE;
5097 return (ok);